feat(plugin): command metadata, scoped skills, local-tier support, layered hooks, Stop audit

Wave 3 - adoption hardening. Patterns adapted (in original prose, with
attribution) from MIT-licensed shanraisshan/claude-code-best-practice.

Commands (command/enhance-claude-md.md, command/sync-claude-md.md):
- Add allowed-tools / disallowedTools / argument-hint / when_to_use so the
  commands auto-suggest in the slash menu and avoid permission prompts.
- disallowedTools blocks WebFetch + WebSearch on both commands.
- Drop the previous broken hooks block (array-of-{matcher, commands} shape
  did not match canonical schema; was never firing).

Skills:
- skill/karpathy-guidelines/SKILL.md: paths: glob over 23 code-file
  extensions, so the guardrails auto-load only when editing source, not
  markdown or data.
- skill/SKILL.md: model: haiku, effort: medium, paths: scoped to CLAUDE.md
  + AGENTS.md + .claude/rules/*.md so validator/generator passes run
  cheaply without changing the user-facing model.

CLAUDE.local.md personal tier:
- skill/validator.py BestPracticesValidator now accepts filename=; any
  *.local.md basename waives the 150-line cap.
- hooks/validate-claude-md.py reads the exempt suffix from hooks-config.
- .gitignore covers CLAUDE.local.md, **/CLAUDE.local.md,
  .claude/settings.local.json, hooks/hooks-config.local.json.

Layered hook config:
- hooks/hooks-config.json: committed defaults
  (validateClaudeMd.enabled/maxLines/exemptFilenameSuffix/exitCodeOnViolation,
  stopAuditLine.enabled).
- hooks/validate-claude-md.py merges hooks-config.json +
  hooks-config.local.json key-by-key; honours enabled=false (silent
  exit 0), configurable cap, configurable exit code.

Stop audit hook:
- hooks/audit-claude-md.py walks the project tree, prints one stderr
  line: total tracked / OVER cap / near cap (>=80%). Respects
  stopAuditLine.enabled from config.
- hooks/hooks.json registers Stop event with matcher "".

Guardian fail-closed contract:
- agent/claude-md-guardian.md Safety & Validation section now explicitly
  requires Skill-tool invocation (no inline paraphrase of SKILL.md),
  abort on missing validated output, never auto-commit, and respect
  local hook config.

Verified (8/8 smoke tests):
- Both commands parse with new fields and no broken hooks block.
- karpathy paths: 23 globs, includes .py/.ts/.go/.rs.
- skill model=haiku effort=medium with CLAUDE.md path scope.
- Validator: *.local.md (300 lines) -> pass; CLAUDE.md (300) -> fail;
  legacy ctor without filename -> default behavior preserved.
- hooks-config.json valid; validateClaudeMd.enabled=true, maxLines=150.
- Hook validator: default rc=2 on bloated, rc=0 when local override
  disables it, rc=0 on *.local.md (exempt).
- Stop hook entry present; audit script: rc=0 with "5 CLAUDE.md tracked".
- Regression: large-fullstack root still 52 lines with chain imports.
This commit is contained in:
Claude
2026-05-19 01:07:11 +00:00
parent 0a34178e22
commit e33fa8326b
12 changed files with 317 additions and 36 deletions
+22 -3
View File
@@ -1,6 +1,25 @@
---
name: claude-md-enhancer
description: Analyzes, generates, and enhances CLAUDE.md files for any project type using best practices, modular architecture support, and tech stack customization. Use when setting up new projects, improving existing CLAUDE.md files, or establishing AI-assisted development standards.
model: haiku
effort: medium
paths:
- "**/CLAUDE.md"
- "**/CLAUDE.local.md"
- "**/AGENTS.md"
- "**/.cursorrules"
- "**/.windsurfrules"
- "**/.claude/rules/*.md"
allowed-tools:
- Read
- Write
- Edit
- Glob
- Grep
- "Bash(ls:*)"
- "Bash(find:*)"
- "Bash(git:*)"
- "Bash(wc:*)"
permissions:
allow:
- Read
@@ -8,9 +27,9 @@ permissions:
- Edit
- Glob
- Grep
- Bash(ls:*)
- Bash(find:*)
- Bash(git:*)
- "Bash(ls:*)"
- "Bash(find:*)"
- "Bash(git:*)"
---
# CLAUDE.md File Enhancer
+28
View File
@@ -2,6 +2,34 @@
name: karpathy-guidelines
description: Behavioral guardrails for LLM-assisted coding. Use when writing, reviewing, or refactoring code in any project to avoid overcomplication, keep changes surgical, surface assumptions early, and execute against verifiable success criteria.
license: MIT
paths:
- "**/*.py"
- "**/*.ts"
- "**/*.tsx"
- "**/*.js"
- "**/*.jsx"
- "**/*.go"
- "**/*.rs"
- "**/*.java"
- "**/*.kt"
- "**/*.rb"
- "**/*.php"
- "**/*.swift"
- "**/*.c"
- "**/*.cc"
- "**/*.cpp"
- "**/*.h"
- "**/*.hpp"
- "**/*.cs"
- "**/*.scala"
- "**/*.sh"
- "**/*.bash"
- "**/*.zsh"
- "**/*.sql"
allowed-tools:
- Read
- Glob
- Grep
permissions:
allow:
- Read
+26 -1
View File
@@ -66,18 +66,24 @@ class BestPracticesValidator:
}
]
def __init__(self, content: str, project_context: Dict[str, Any] = None):
def __init__(self, content: str, project_context: Dict[str, Any] = None, filename: str = None):
"""
Initialize validator with CLAUDE.md content.
Args:
content: Full text content of CLAUDE.md file
project_context: Optional project context for advanced validation
filename: Optional path or basename. When the basename ends with
``.local.md`` (e.g. ``CLAUDE.local.md``), the 150-line cap is
relaxed because the file is a personal/gitignored override
outside the chained team-shared tree.
"""
self.content = content
self.lines = content.split('\n')
self.line_count = len(self.lines)
self.project_context = project_context or {}
self.filename = filename or ""
self.is_local_override = self.filename.endswith('.local.md')
def validate_all(self) -> Dict[str, Any]:
"""
@@ -112,6 +118,25 @@ class BestPracticesValidator:
message = f"File length is appropriate ({self.line_count} lines)"
severity = "info"
# CLAUDE.local.md (and any *.local.md sibling) is a personal,
# gitignored override outside the chained team-shared tree. Skip the
# 150-line cap — only flag underuse.
if self.is_local_override:
if self.line_count < self.MIN_LINES:
status = "fail"
message = f"Personal override is too short ({self.line_count} lines, minimum {self.MIN_LINES})"
severity = "low"
else:
message = f"Personal override ({self.line_count} lines, cap waived)"
return {
"check": "file_length",
"status": status,
"message": message,
"severity": severity,
"actual_value": self.line_count,
"expected_range": f"{self.MIN_LINES}+ lines (cap waived for *.local.md)",
}
if self.line_count > self.MAX_RECOMMENDED_LINES:
status = "fail"
message = f"File exceeds maximum recommended length ({self.line_count} > {self.MAX_RECOMMENDED_LINES} lines)"