- 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>
174 lines
6.7 KiB
Python
174 lines
6.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Auto-Fix Script
|
|
Applies safe fixes to Claude Code configuration with backup.
|
|
"""
|
|
|
|
import json
|
|
import shutil
|
|
import sys
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
# serverInstructions templates for common MCP servers
|
|
SERVER_INSTRUCTIONS = {
|
|
"playwright": "Browser automation for web interaction. Use for: SEO audits, page analysis, screenshots, form testing, Core Web Vitals. Keywords: browser, page, screenshot, click, navigate, DOM, selector",
|
|
"puppeteer": "Chrome automation for web testing. Use for: SEO audits, page rendering, JavaScript site testing. Keywords: browser, chrome, headless, screenshot, page",
|
|
"notion": "Notion workspace integration. Use for: saving research, documentation, project notes, knowledge base. Keywords: notion, page, database, wiki, notes, save",
|
|
"github": "GitHub repository management. Use for: commits, PRs, issues, code review. Keywords: git, github, commit, pull request, issue, repository",
|
|
"postgres": "PostgreSQL database queries. Use for: data analysis, SQL queries, analytics. Keywords: sql, query, database, table, select, analytics",
|
|
"postgresql": "PostgreSQL database queries. Use for: data analysis, SQL queries, analytics. Keywords: sql, query, database, table, select, analytics",
|
|
"bigquery": "Google BigQuery for large-scale analysis. Use for: analytics queries, data warehouse. Keywords: bigquery, sql, analytics, data warehouse",
|
|
"firecrawl": "Web scraping and crawling. Use for: site crawling, content extraction, competitor analysis. Keywords: crawl, scrape, extract, spider, sitemap",
|
|
"slack": "Slack workspace integration. Use for: messages, notifications, team communication. Keywords: slack, message, channel, notification",
|
|
"linear": "Linear issue tracking. Use for: issue management, project tracking. Keywords: linear, issue, task, project, sprint",
|
|
"memory": "Persistent memory across sessions. Use for: storing preferences, context recall. Keywords: remember, memory, store, recall",
|
|
"filesystem": "Local file operations. Use for: file reading/writing, directory management. Keywords: file, directory, read, write, path",
|
|
"chrome-devtools": "Chrome DevTools for debugging. Use for: GTM debugging, network analysis, console logs. Keywords: devtools, chrome, debug, network, console",
|
|
}
|
|
|
|
|
|
class AutoFixer:
|
|
def __init__(self, dry_run: bool = True):
|
|
self.dry_run = dry_run
|
|
self.fixes = []
|
|
self.backup_dir = Path.home() / ".claude" / "backups" / datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
|
|
def backup_file(self, path: Path) -> bool:
|
|
"""Create backup before modifying."""
|
|
if not path.exists():
|
|
return True
|
|
|
|
try:
|
|
backup_path = self.backup_dir / path.relative_to(Path.home())
|
|
backup_path.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copy2(path, backup_path)
|
|
return True
|
|
except Exception as e:
|
|
print(f"Warning: Could not backup {path}: {e}", file=sys.stderr)
|
|
return False
|
|
|
|
def fix_mcp_instructions(self, settings_path: Path) -> list:
|
|
"""Add serverInstructions to MCP servers."""
|
|
fixes = []
|
|
|
|
try:
|
|
with open(settings_path) as f:
|
|
settings = json.load(f)
|
|
except (json.JSONDecodeError, IOError) as e:
|
|
return [f"Error reading {settings_path}: {e}"]
|
|
|
|
servers = settings.get("mcpServers", {})
|
|
modified = False
|
|
|
|
for name, config in servers.items():
|
|
if not isinstance(config, dict):
|
|
continue
|
|
|
|
if "serverInstructions" in config:
|
|
continue
|
|
|
|
# Find matching template
|
|
instructions = None
|
|
name_lower = name.lower()
|
|
for key, template in SERVER_INSTRUCTIONS.items():
|
|
if key in name_lower:
|
|
instructions = template
|
|
break
|
|
|
|
if not instructions:
|
|
instructions = f"External tool: {name}. Use when this functionality is needed."
|
|
|
|
if self.dry_run:
|
|
fixes.append(f"[DRY RUN] Would add serverInstructions to '{name}'")
|
|
else:
|
|
config["serverInstructions"] = instructions
|
|
modified = True
|
|
fixes.append(f"Added serverInstructions to '{name}'")
|
|
|
|
if modified and not self.dry_run:
|
|
self.backup_file(settings_path)
|
|
with open(settings_path, 'w') as f:
|
|
json.dump(settings, f, indent=2)
|
|
|
|
return fixes
|
|
|
|
def fix_command_frontmatter(self, cmd_path: Path) -> str | None:
|
|
"""Add frontmatter to command missing it."""
|
|
try:
|
|
content = cmd_path.read_text()
|
|
except IOError:
|
|
return None
|
|
|
|
if content.startswith('---'):
|
|
return None
|
|
|
|
new_content = f'''---
|
|
description: {cmd_path.stem.replace('-', ' ').title()} command
|
|
---
|
|
|
|
{content}'''
|
|
|
|
if self.dry_run:
|
|
return f"[DRY RUN] Would add frontmatter to {cmd_path.name}"
|
|
|
|
self.backup_file(cmd_path)
|
|
cmd_path.write_text(new_content)
|
|
return f"Added frontmatter to {cmd_path.name}"
|
|
|
|
def run(self) -> dict:
|
|
"""Apply all fixes."""
|
|
results = {"applied": [], "skipped": [], "errors": []}
|
|
|
|
# Fix MCP settings
|
|
settings_paths = [
|
|
Path.home() / ".claude" / "settings.json",
|
|
Path.cwd() / ".claude" / "settings.json"
|
|
]
|
|
|
|
for path in settings_paths:
|
|
if path.exists():
|
|
fixes = self.fix_mcp_instructions(path)
|
|
results["applied"].extend(fixes)
|
|
|
|
# Fix commands without frontmatter
|
|
cmd_dirs = [
|
|
Path.home() / ".claude" / "commands",
|
|
Path.cwd() / ".claude" / "commands"
|
|
]
|
|
|
|
for cmd_dir in cmd_dirs:
|
|
if cmd_dir.exists():
|
|
for cmd_file in cmd_dir.glob("*.md"):
|
|
fix = self.fix_command_frontmatter(cmd_file)
|
|
if fix:
|
|
results["applied"].append(fix)
|
|
|
|
return results
|
|
|
|
|
|
def main():
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description="Auto-fix Claude Code settings")
|
|
parser.add_argument("--apply", action="store_true", help="Apply fixes (default is dry-run)")
|
|
args = parser.parse_args()
|
|
|
|
fixer = AutoFixer(dry_run=not args.apply)
|
|
results = fixer.run()
|
|
|
|
print(json.dumps(results, indent=2))
|
|
|
|
if fixer.dry_run:
|
|
print("\n[DRY RUN] No changes applied. Use --apply to apply fixes.", file=sys.stderr)
|
|
else:
|
|
print(f"\n[APPLIED] {len(results['applied'])} fixes.", file=sys.stderr)
|
|
if fixer.backup_dir.exists():
|
|
print(f"Backups: {fixer.backup_dir}", file=sys.stderr)
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|