Coverage for src / moai_adk / core / diagnostics / slash_commands.py: 0.00%
41 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-20 20:52 +0900
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-20 20:52 +0900
1"""Slash command diagnostics
3Diagnose and validate Claude Code slash command loading issues.
5Functions:
6- validate_command_file: Validate YAML front matter and required fields
7- scan_command_files: Recursively scan for .md files
8- diagnose_slash_commands: Comprehensive diagnostic report
9"""
11from pathlib import Path
13import yaml # type: ignore[import-untyped]
16def validate_command_file(file_path: Path) -> dict:
17 """Validate slash command file format
19 Checks:
20 1. File exists and readable
21 2. YAML front matter present (starts with ---)
22 3. Valid YAML syntax
23 4. Required fields: name, description
25 Args:
26 file_path: Path to command file (.md)
28 Returns:
29 dict with 'valid' (bool) and optional 'errors' (list[str])
31 Example:
32 >>> result = validate_command_file(Path("cmd.md"))
33 >>> if result["valid"]:
34 ... print("Valid command file")
35 ... else:
36 ... print(f"Errors: {result['errors']}")
37 """
38 try:
39 # Check file exists
40 if not file_path.exists():
41 return {"valid": False, "errors": ["File not found"]}
43 # Read file content
44 content = file_path.read_text(encoding="utf-8")
46 # Check front matter delimiter
47 if not content.startswith("---"):
48 return {
49 "valid": False,
50 "errors": ["Missing YAML front matter (must start with ---)"],
51 }
53 # Split by --- delimiter
54 parts = content.split("---", 2)
55 if len(parts) < 3:
56 return {
57 "valid": False,
58 "errors": ["Invalid front matter format (missing closing ---)"],
59 }
61 # Parse YAML
62 try:
63 metadata = yaml.safe_load(parts[1])
64 except yaml.YAMLError as e:
65 return {"valid": False, "errors": [f"YAML parsing error: {e}"]}
67 # Check required fields
68 required_fields = ["name", "description"]
69 missing_fields = [field for field in required_fields if field not in metadata]
71 if missing_fields:
72 return {
73 "valid": False,
74 "errors": [f"Missing required field: {', '.join(missing_fields)}"],
75 }
77 return {"valid": True}
79 except Exception as e:
80 return {"valid": False, "errors": [str(e)]}
83def scan_command_files(commands_dir: Path) -> list[Path]:
84 """Scan directory for all .md files
86 Recursively searches for .md files in the given directory.
88 Args:
89 commands_dir: Directory to scan (e.g., .claude/commands)
91 Returns:
92 List of Path objects for found .md files
94 Example:
95 >>> files = scan_command_files(Path(".claude/commands"))
96 >>> print(f"Found {len(files)} command files")
97 """
98 if not commands_dir.exists():
99 return []
101 try:
102 return list(commands_dir.glob("**/*.md"))
103 except Exception:
104 return []
107def diagnose_slash_commands() -> dict:
108 """Diagnose slash command loading issues
110 Comprehensive diagnostic that:
111 1. Checks if .claude/commands directory exists
112 2. Scans for all .md files
113 3. Validates each file's format
114 4. Returns detailed report
116 Returns:
117 dict with diagnostic results:
118 - total_files: Number of .md files found
119 - valid_commands: Number of valid command files
120 - details: List of per-file validation results
121 OR
122 - error: Error message if directory not found
124 Example:
125 >>> result = diagnose_slash_commands()
126 >>> if "error" in result:
127 ... print(f"Error: {result['error']}")
128 ... else:
129 ... print(f"{result['valid_commands']}/{result['total_files']} valid")
130 """
131 commands_dir = Path(".claude/commands")
133 # Check if directory exists
134 if not commands_dir.exists():
135 return {"error": "Commands directory not found"}
137 # Scan for .md files
138 md_files = scan_command_files(commands_dir)
140 # Validate each file
141 details = []
142 for file_path in md_files:
143 validation = validate_command_file(file_path)
144 details.append(
145 {
146 "file": str(file_path.relative_to(commands_dir)),
147 "valid": validation["valid"],
148 "errors": validation.get("errors", []),
149 }
150 )
152 # Count valid commands
153 valid_count = sum(1 for detail in details if detail["valid"])
155 return {
156 "total_files": len(md_files),
157 "valid_commands": valid_count,
158 "details": details,
159 }