feat: Add installation tool, Claude.ai export, and skill standardization (#1)

## Summary

- Add portable installation tool (`install.sh`) for cross-machine setup
- Add Claude.ai export files with proper YAML frontmatter
- Add multi-agent-guide v2.0 with consolidated framework template
- Rename `00-claude-code-setting` → `00-our-settings-audit` (avoid reserved word)
- Add YAML frontmatter to 25+ SKILL.md files for Claude Desktop compatibility

## Commits Included

- `93f604a` feat: Add portable installation tool for cross-machine setup
- `9b84104` feat: Add Claude.ai export for portable skill installation
- `f7ab973` fix: Add YAML frontmatter to Claude.ai export files
- `3fed49a` feat(multi-agent-guide): Add v2.0 with consolidated framework
- `3be26ef` refactor: Rename settings-audit skill and add YAML frontmatter

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Andrew Yim
2026-02-03 16:48:06 +07:00
committed by GitHub
parent 0bc24d00b9
commit b6a478e1df
72 changed files with 4770 additions and 803 deletions

View File

@@ -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())