mirror of
https://github.com/alirezarezvani/claude-skills.git
synced 2026-08-28 04:24:58 +00:00
- derive_counters.py: python_tools condition simplified to the equivalent parts[0] != 'scripts' (reviewer M1); dead root_scripts variable removed; --check still passes with identical values - fda-consultant-specialist quick-start: 820.30 example annotated as a legacy checklist key mapping to ISO 13485 §7.3 (reviewer m3 — note: switching the example to '--section 7.3' as suggested would break; the checker's CLI keys are intentionally the legacy 820.x checklist indices, documented in --help) - CLAUDE.md: audit/ directory documented as an intentional public audit record, distinct from the gitignored AUDIT_REPORT.md (reviewer m2) Reviewer m1 (agents/CLAUDE.md 'engineering-team/' link) is a false positive: agents/engineering-team/ exists as an agents subfolder containing exactly the two linked files; check_paths.py confirms 0 unresolvable references. https://claude.ai/code/session_019AJddAL1NADWMXsy1qNPQF
238 lines
8.1 KiB
Python
238 lines
8.1 KiB
Python
#!/usr/bin/env python3
|
|
"""derive_counters.py — single source of truth for repository headline counters.
|
|
|
|
Walks the canonical tree (excluding sync copies, docs site, audit workspace,
|
|
and VCS/CI internals) and derives the headline numbers that README.md,
|
|
CLAUDE.md, and .claude-plugin/marketplace.json claim:
|
|
|
|
skills count of SKILL.md files
|
|
plugins_on_disk count of **/.claude-plugin/plugin.json manifests
|
|
plugins_registered entries in .claude-plugin/marketplace.json `plugins` array
|
|
python_tools .py files in the canonical tree, excluding repo-root scripts/
|
|
(i.e. automation tools shipped inside skill/plugin folders)
|
|
references .md files under any references/ directory
|
|
agents .md files under any agents/ directory (excl. CLAUDE.md/README.md)
|
|
commands .md files under any commands/ directory (excl. CLAUDE.md/README.md)
|
|
domains top-level folders containing at least one SKILL.md
|
|
|
|
Modes:
|
|
(default) print a human-readable table
|
|
--json print the derived counters as JSON
|
|
--check exit 1 listing mismatches if the headline counters claimed in
|
|
README.md, root CLAUDE.md ("Current Scope" line), and
|
|
marketplace.json metadata.description disagree with derived
|
|
values. CI gate G3.
|
|
|
|
Stdlib only. No writes ever.
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
|
|
# Top-level directories excluded from the canonical tree.
|
|
EXCLUDED_TOP_LEVEL = {
|
|
".git",
|
|
".codex",
|
|
".codex-plugin",
|
|
".gemini",
|
|
".hermes",
|
|
".vibe",
|
|
"docs",
|
|
"audit",
|
|
"node_modules",
|
|
".github",
|
|
".claude",
|
|
}
|
|
|
|
DOC_FILENAMES = {"CLAUDE.md", "README.md"}
|
|
|
|
|
|
def canonical_walk(root: Path):
|
|
"""Yield every file in the canonical tree (excluded top-level dirs pruned)."""
|
|
stack = [root]
|
|
while stack:
|
|
current = stack.pop()
|
|
try:
|
|
entries = sorted(current.iterdir())
|
|
except OSError:
|
|
continue
|
|
for entry in entries:
|
|
if entry.is_symlink():
|
|
continue
|
|
if entry.is_dir():
|
|
if current == root and entry.name in EXCLUDED_TOP_LEVEL:
|
|
continue
|
|
stack.append(entry)
|
|
elif entry.is_file():
|
|
yield entry
|
|
|
|
|
|
def derive(root: Path) -> dict:
|
|
counters = {
|
|
"skills": 0,
|
|
"plugins_on_disk": 0,
|
|
"plugins_registered": 0,
|
|
"python_tools": 0,
|
|
"references": 0,
|
|
"agents": 0,
|
|
"commands": 0,
|
|
"domains": 0,
|
|
}
|
|
domains = set()
|
|
|
|
for path in canonical_walk(root):
|
|
rel = path.relative_to(root)
|
|
parts = rel.parts
|
|
name = path.name
|
|
|
|
if name == "SKILL.md":
|
|
counters["skills"] += 1
|
|
if len(parts) > 1:
|
|
domains.add(parts[0])
|
|
elif name == "plugin.json" and path.parent.name == ".claude-plugin":
|
|
counters["plugins_on_disk"] += 1
|
|
elif path.suffix == ".py":
|
|
# Skill/plugin automation tools: every .py except repo-root scripts/.
|
|
if parts[0] != "scripts":
|
|
counters["python_tools"] += 1
|
|
elif path.suffix == ".md":
|
|
if "references" in parts[:-1]:
|
|
counters["references"] += 1
|
|
elif "agents" in parts[:-1] and name not in DOC_FILENAMES:
|
|
counters["agents"] += 1
|
|
elif "commands" in parts[:-1] and name not in DOC_FILENAMES:
|
|
counters["commands"] += 1
|
|
|
|
counters["domains"] = len(domains)
|
|
counters["_domain_list"] = sorted(domains)
|
|
|
|
marketplace = root / ".claude-plugin" / "marketplace.json"
|
|
if marketplace.is_file():
|
|
try:
|
|
data = json.loads(marketplace.read_text(encoding="utf-8"))
|
|
counters["plugins_registered"] = len(data.get("plugins", []))
|
|
except (json.JSONDecodeError, OSError):
|
|
counters["plugins_registered"] = -1
|
|
|
|
return counters
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# --check: parse the standardized claim patterns in the three headline files.
|
|
#
|
|
# Standardized claim patterns (keep docs in lockstep with these):
|
|
# "<N> production-ready skills" (or "production-ready Claude Code skills")
|
|
# "skills across <D> domains"
|
|
# "<T> Python automation tools" or "<T> Python tools"
|
|
# "<R> reference guides"
|
|
# "<P> marketplace plugins"
|
|
# ---------------------------------------------------------------------------
|
|
|
|
CLAIM_PATTERNS = {
|
|
"skills": re.compile(r"(\d+)\s+production-ready (?:Claude Code )?skills"),
|
|
"domains": re.compile(r"skills across\s+(\d+)\s+domains"),
|
|
"python_tools": re.compile(r"(\d+)\s+Python (?:automation )?tools"),
|
|
"references": re.compile(r"(\d+)\s+reference guides"),
|
|
"plugins_registered": re.compile(r"(\d+)\s+marketplace plugins"),
|
|
}
|
|
|
|
|
|
def extract_claims(text: str) -> dict:
|
|
"""Return {counter_name: first claimed int} for every pattern found in text."""
|
|
claims = {}
|
|
for key, pattern in CLAIM_PATTERNS.items():
|
|
match = pattern.search(text)
|
|
if match:
|
|
claims[key] = int(match.group(1))
|
|
return claims
|
|
|
|
|
|
def run_check(root: Path, derived: dict) -> int:
|
|
sources = []
|
|
|
|
readme = root / "README.md"
|
|
if readme.is_file():
|
|
sources.append(("README.md", readme.read_text(encoding="utf-8")))
|
|
|
|
claude_md = root / "CLAUDE.md"
|
|
if claude_md.is_file():
|
|
text = claude_md.read_text(encoding="utf-8")
|
|
# Restrict to the "Current Scope" line so history sections don't trip the gate.
|
|
scope_lines = [ln for ln in text.splitlines() if ln.startswith("**Current Scope:**")]
|
|
sources.append(("CLAUDE.md (Current Scope line)", "\n".join(scope_lines)))
|
|
|
|
marketplace = root / ".claude-plugin" / "marketplace.json"
|
|
if marketplace.is_file():
|
|
try:
|
|
data = json.loads(marketplace.read_text(encoding="utf-8"))
|
|
desc = data.get("metadata", {}).get("description", "")
|
|
sources.append(("marketplace.json metadata.description", desc))
|
|
except (json.JSONDecodeError, OSError) as exc:
|
|
print(f"FAIL: cannot parse marketplace.json: {exc}")
|
|
return 1
|
|
|
|
mismatches = []
|
|
for label, text in sources:
|
|
claims = extract_claims(text)
|
|
if not claims:
|
|
mismatches.append(f"{label}: no recognizable counter claims found")
|
|
continue
|
|
for key, claimed in claims.items():
|
|
actual = derived[key]
|
|
if claimed != actual:
|
|
mismatches.append(
|
|
f"{label}: claims {key}={claimed}, derived {key}={actual}"
|
|
)
|
|
|
|
if mismatches:
|
|
print("COUNTER CHECK FAILED — headline claims disagree with derived values:")
|
|
for line in mismatches:
|
|
print(f" - {line}")
|
|
print("\nRun `python3 scripts/derive_counters.py` for the ground-truth table.")
|
|
return 1
|
|
|
|
print("Counter check passed: README.md, CLAUDE.md, marketplace.json match derived values.")
|
|
return 0
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(
|
|
description="Derive repository headline counters from the canonical tree."
|
|
)
|
|
parser.add_argument("--json", action="store_true", help="emit counters as JSON")
|
|
parser.add_argument(
|
|
"--check",
|
|
action="store_true",
|
|
help="exit 1 if README.md / CLAUDE.md / marketplace.json claims drift from derived values",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
derived = derive(REPO_ROOT)
|
|
domain_list = derived.pop("_domain_list")
|
|
|
|
if args.json:
|
|
out = dict(derived)
|
|
out["domain_list"] = domain_list
|
|
print(json.dumps(out, indent=2))
|
|
else:
|
|
width = max(len(k) for k in derived)
|
|
print("Derived repository counters (canonical tree)")
|
|
print("-" * 46)
|
|
for key, value in derived.items():
|
|
print(f"{key.ljust(width)} {value}")
|
|
print("-" * 46)
|
|
print("domains: " + ", ".join(domain_list))
|
|
|
|
if args.check:
|
|
derived["_domain_list"] = domain_list
|
|
return run_check(REPO_ROOT, derived)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|