feat(settings-audit): Add Claude Code settings optimizer skill
- 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>
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user