- Add new 00-claude-code-setting skill for token usage optimization - Includes audit scripts for MCP servers, CLAUDE.md, and extensions - Auto-fix capability with backup for serverInstructions and frontmatter - Add YAML frontmatter to 17 command files - Target: keep baseline under 30% of 200K context limit Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
233 lines
7.3 KiB
Python
233 lines
7.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Extensions Analyzer
|
|
Analyzes Claude Code commands, skills, and agents.
|
|
"""
|
|
|
|
import json
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
try:
|
|
import yaml
|
|
HAS_YAML = True
|
|
except ImportError:
|
|
HAS_YAML = False
|
|
|
|
MAX_COMMAND_LINES = 100
|
|
MAX_SKILL_LINES = 500
|
|
|
|
|
|
class ExtensionsAnalyzer:
|
|
def __init__(self):
|
|
self.findings = {
|
|
"critical": [],
|
|
"warnings": [],
|
|
"passing": [],
|
|
"recommendations": []
|
|
}
|
|
self.commands = {}
|
|
self.skills = {}
|
|
self.agents = {}
|
|
|
|
def find_extension_dirs(self) -> dict:
|
|
"""Find extension directories."""
|
|
base_paths = [
|
|
Path.home() / ".claude",
|
|
Path.cwd() / ".claude",
|
|
]
|
|
|
|
dirs = {"commands": [], "skills": [], "agents": []}
|
|
|
|
for base in base_paths:
|
|
for ext_type in dirs.keys():
|
|
path = base / ext_type
|
|
if path.exists() and path.is_dir():
|
|
dirs[ext_type].append(path)
|
|
|
|
return dirs
|
|
|
|
def parse_frontmatter(self, content: str) -> dict | None:
|
|
"""Parse YAML frontmatter."""
|
|
if not content.startswith('---'):
|
|
return None
|
|
|
|
try:
|
|
end = content.find('---', 3)
|
|
if end == -1:
|
|
return None
|
|
yaml_content = content[3:end].strip()
|
|
|
|
if HAS_YAML:
|
|
return yaml.safe_load(yaml_content)
|
|
else:
|
|
# Basic parsing without yaml
|
|
result = {}
|
|
for line in yaml_content.split('\n'):
|
|
if ':' in line:
|
|
key, value = line.split(':', 1)
|
|
result[key.strip()] = value.strip()
|
|
return result
|
|
except Exception:
|
|
return None
|
|
|
|
def analyze_command(self, path: Path) -> dict:
|
|
"""Analyze a command file."""
|
|
try:
|
|
content = path.read_text()
|
|
except IOError:
|
|
return {"name": path.stem, "error": "Could not read"}
|
|
|
|
lines = len(content.split('\n'))
|
|
frontmatter = self.parse_frontmatter(content)
|
|
|
|
analysis = {
|
|
"name": path.stem,
|
|
"lines": lines,
|
|
"has_frontmatter": frontmatter is not None,
|
|
"has_description": frontmatter and "description" in frontmatter,
|
|
"issues": []
|
|
}
|
|
|
|
if not analysis["has_frontmatter"]:
|
|
analysis["issues"].append("Missing YAML frontmatter")
|
|
elif not analysis["has_description"]:
|
|
analysis["issues"].append("Missing description")
|
|
|
|
if lines > MAX_COMMAND_LINES:
|
|
analysis["issues"].append(f"Too long: {lines} lines (max {MAX_COMMAND_LINES})")
|
|
|
|
if not re.match(r'^[a-z][a-z0-9-]*$', analysis["name"]):
|
|
analysis["issues"].append("Name should be kebab-case")
|
|
|
|
return analysis
|
|
|
|
def analyze_skill(self, path: Path) -> dict:
|
|
"""Analyze a skill directory."""
|
|
skill_md = path / "SKILL.md"
|
|
|
|
if not skill_md.exists():
|
|
return {
|
|
"name": path.name,
|
|
"error": "Missing SKILL.md",
|
|
"issues": ["Missing SKILL.md"]
|
|
}
|
|
|
|
try:
|
|
content = skill_md.read_text()
|
|
except IOError:
|
|
return {"name": path.name, "error": "Could not read SKILL.md", "issues": []}
|
|
|
|
lines = len(content.split('\n'))
|
|
frontmatter = self.parse_frontmatter(content)
|
|
|
|
analysis = {
|
|
"name": path.name,
|
|
"lines": lines,
|
|
"has_frontmatter": frontmatter is not None,
|
|
"has_description": frontmatter and "description" in frontmatter,
|
|
"issues": []
|
|
}
|
|
|
|
if not analysis["has_frontmatter"]:
|
|
analysis["issues"].append("Missing frontmatter in SKILL.md")
|
|
|
|
if lines > MAX_SKILL_LINES:
|
|
analysis["issues"].append(f"Too long: {lines} lines (max {MAX_SKILL_LINES})")
|
|
|
|
return analysis
|
|
|
|
def analyze_agent(self, path: Path) -> dict:
|
|
"""Analyze an agent file."""
|
|
try:
|
|
content = path.read_text()
|
|
except IOError:
|
|
return {"name": path.stem, "error": "Could not read", "issues": []}
|
|
|
|
frontmatter = self.parse_frontmatter(content)
|
|
|
|
analysis = {
|
|
"name": path.stem,
|
|
"has_frontmatter": frontmatter is not None,
|
|
"tools_restricted": False,
|
|
"issues": []
|
|
}
|
|
|
|
if frontmatter:
|
|
tools = frontmatter.get("tools", "*")
|
|
analysis["tools_restricted"] = tools != "*" and tools
|
|
|
|
if not analysis["has_frontmatter"]:
|
|
analysis["issues"].append("Missing frontmatter")
|
|
|
|
if not analysis["tools_restricted"]:
|
|
analysis["issues"].append("Tools not restricted (consider limiting)")
|
|
|
|
return analysis
|
|
|
|
def analyze(self) -> dict:
|
|
"""Run full analysis."""
|
|
dirs = self.find_extension_dirs()
|
|
|
|
# Analyze commands
|
|
for cmd_dir in dirs["commands"]:
|
|
for cmd_file in cmd_dir.glob("*.md"):
|
|
analysis = self.analyze_command(cmd_file)
|
|
self.commands[analysis["name"]] = analysis
|
|
|
|
if analysis.get("issues"):
|
|
for issue in analysis["issues"]:
|
|
self.findings["warnings"].append(f"Command '{analysis['name']}': {issue}")
|
|
else:
|
|
self.findings["passing"].append(f"Command '{analysis['name']}': OK")
|
|
|
|
# Analyze skills
|
|
for skill_dir in dirs["skills"]:
|
|
for skill_path in skill_dir.iterdir():
|
|
if skill_path.is_dir():
|
|
analysis = self.analyze_skill(skill_path)
|
|
self.skills[analysis["name"]] = analysis
|
|
|
|
if analysis.get("issues"):
|
|
for issue in analysis["issues"]:
|
|
if "Missing SKILL.md" in issue:
|
|
self.findings["critical"].append(f"Skill '{analysis['name']}': {issue}")
|
|
else:
|
|
self.findings["warnings"].append(f"Skill '{analysis['name']}': {issue}")
|
|
else:
|
|
self.findings["passing"].append(f"Skill '{analysis['name']}': OK")
|
|
|
|
# Analyze agents
|
|
for agent_dir in dirs["agents"]:
|
|
for agent_file in agent_dir.glob("*.md"):
|
|
analysis = self.analyze_agent(agent_file)
|
|
self.agents[analysis["name"]] = analysis
|
|
|
|
if analysis.get("issues"):
|
|
for issue in analysis["issues"]:
|
|
self.findings["warnings"].append(f"Agent '{analysis['name']}': {issue}")
|
|
else:
|
|
self.findings["passing"].append(f"Agent '{analysis['name']}': OK")
|
|
|
|
return {
|
|
"commands_count": len(self.commands),
|
|
"skills_count": len(self.skills),
|
|
"agents_count": len(self.agents),
|
|
"commands": self.commands,
|
|
"skills": self.skills,
|
|
"agents": self.agents,
|
|
"findings": self.findings
|
|
}
|
|
|
|
|
|
def main():
|
|
analyzer = ExtensionsAnalyzer()
|
|
report = analyzer.analyze()
|
|
print(json.dumps(report, indent=2, default=str))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|