refactor(v0.17): README counts auto-generation via markers + regen script
Eliminates README counts drift class (RULE 0.10 recurrence — 3rd drift in one week: RUST_CRATES 23→24, SKILLS 38→39, PROFILE_FULL 36→37, PROFILE_DEV 4→10, BLOCKS 73→78). Mechanism: HTML-comment markers wrap every auto-countable value: <!-- count:RUST_CRATES -->24<!-- /count:RUST_CRATES --> 16 marker types cover: RUST_CRATES, RUST/SHELL/TOTAL_PRIMITIVES, SKILLS, HOOKS, BLOCKS, AGENTS, BRIDGES, PROFILE_FULL/MCP/DEV/OPS/ FRONTEND/CORE, LBM_PORTS. scripts/regen-counts.sh (117 LOC, POSIX sh) — computes every count from source (MANIFEST.toml, Cargo.toml, find on skills/hooks/blocks/ manifests/bridges) and rewrites markers in place. --check mode (side-effect-free via mktemp+cmp+diff) exits 1 on drift. scripts/precommit-counts-check.sh (26 LOC) — invokes regen-counts --check, blocks commit on drift with fix-it hint. README gains 'Regenerating counts' subsection documenting both commands and pre-commit wiring. No Python/jq/yq hard deps. macOS /bin/sh compat (3.2-era). Idempotent. Constructor Pattern: largest script 117 LOC (<120), largest awk fn 10 LOC (<30). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b62b219500
commit
d04e6efe1e
3 changed files with 176 additions and 22 deletions
55
README.md
55
README.md
|
|
@ -28,9 +28,9 @@ Thanks.
|
|||
|
||||
## What it is
|
||||
|
||||
KeiSeiKit is a comprehensive drop-in toolkit for [Claude Code](https://claude.com/claude-code). It ships a curated set of composable behavioral blocks, a Rust assembler that builds agent `.md` files from TOML manifests deterministically, 9 pre-wired PreToolUse/PostToolUse hooks (three of them dedicated to RULE 0.14 session self-audit), 38 portable skills (including an interactive `/new-agent` wizard, 10 hub-and-spoke pipelines, and the `/self-audit` retrospective skill), **23 Rust primitive crates**, 13 opt-in shell primitives (plus 3 always-copied sleep-sync helpers), and 11 cross-tool bridge templates. Everything follows a Constructor Pattern: one file per concern, manifests as single source of truth, and the generated agent files are regenerated on every relevant edit.
|
||||
KeiSeiKit is a comprehensive drop-in toolkit for [Claude Code](https://claude.com/claude-code). It ships a curated set of composable behavioral blocks, a Rust assembler that builds agent `.md` files from TOML manifests deterministically, <!-- count:HOOKS -->9<!-- /count:HOOKS --> pre-wired PreToolUse/PostToolUse hooks (three of them dedicated to RULE 0.14 session self-audit), <!-- count:SKILLS -->39<!-- /count:SKILLS --> portable skills (including an interactive `/new-agent` wizard, 10 hub-and-spoke pipelines, and the `/self-audit` retrospective skill), **<!-- count:RUST_CRATES -->24<!-- /count:RUST_CRATES --> Rust primitive crates**, <!-- count:SHELL_PRIMITIVES -->13<!-- /count:SHELL_PRIMITIVES --> opt-in shell primitives (plus 3 always-copied sleep-sync helpers), and <!-- count:BRIDGES -->11<!-- /count:BRIDGES --> cross-tool bridge templates. Everything follows a Constructor Pattern: one file per concern, manifests as single source of truth, and the generated agent files are regenerated on every relevant edit.
|
||||
|
||||
The kit is MIT-licensed and fully generic — install it on a fresh machine and you get a sane 12-agent fleet (implementers, critics, researchers, cost-guardians, and more — all namespaced under `kei-*` so they won't collide with your own same-named agents), a wizard for spinning up new project specialists, 10 pipeline skills that combine primitives end-to-end (`/compose-solution`, `/site-create`, `/schema-design`, `/observability-setup`, `/auth-setup`, `/api-design`, `/ci-scaffold`, `/test-matrix`, `/docs-scaffold`, `/new-project`, `/vm-provision`), and a build pipeline that keeps every agent derivable from its manifest.
|
||||
The kit is MIT-licensed and fully generic — install it on a fresh machine and you get a sane <!-- count:AGENTS -->12<!-- /count:AGENTS -->-agent fleet (implementers, critics, researchers, cost-guardians, and more — all namespaced under `kei-*` so they won't collide with your own same-named agents), a wizard for spinning up new project specialists, 10 pipeline skills that combine primitives end-to-end (`/compose-solution`, `/site-create`, `/schema-design`, `/observability-setup`, `/auth-setup`, `/api-design`, `/ci-scaffold`, `/test-matrix`, `/docs-scaffold`, `/new-project`, `/vm-provision`), and a build pipeline that keeps every agent derivable from its manifest.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ cd KeiSeiKit
|
|||
5. Builds the Rust assembler (`cargo build --release` in `_assembler/`)
|
||||
6. If any Rust primitive is in the selected profile: writes a scoped workspace `Cargo.toml` listing ONLY the installed crates, then `cargo build --release`
|
||||
7. Generates agent `.md` files in-place with `AGENT_ROOT=~/.claude/agents assemble --in-place`
|
||||
8. Copies the 10 hooks and 38 skills
|
||||
8. Copies the <!-- count:HOOKS -->9<!-- /count:HOOKS --> hooks and <!-- count:SKILLS -->39<!-- /count:SKILLS --> skills
|
||||
|
||||
After install, the only remaining step is merging `settings-snippet.json` into your `~/.claude/settings.json` to activate the hooks. You can do this automatically with `./install.sh --activate-hooks` or answer `y` at the end-of-install TTY prompt.
|
||||
|
||||
|
|
@ -88,16 +88,16 @@ By default `./install.sh` is **minimal** — agents + hooks + skills + bridges,
|
|||
|---|---|---|---|
|
||||
| `minimal` (default) | none | ~5s | ~2 MB |
|
||||
| `core` | `tomd` | ~5s | ~2 MB |
|
||||
| `frontend` | 8 site tools: `mock-render`, `visual-diff`, `tokens-sync`, `design-scrape`, `live-preview`, `figma-tokens`, `frontend-inspect`, `screenshot-decode` | ~60s | ~80 MB |
|
||||
| `ops` | 8 infra tools: `kei-ledger`, `ssh-check`, `firewall-diff`, `provision-hetzner`, `provision-vultr`, `harden-base`, `metrics-scrape`, `log-ship` | ~90s | ~50 MB |
|
||||
| `dev` | 4 dev tools: `kei-migrate`, `kei-changelog`, `kei-ci-lint`, `kei-docs-scaffold` | ~60s | ~40 MB |
|
||||
| `full` | everything (36 primitives) | ~5 min | ~200 MB |
|
||||
| `frontend` | <!-- count:PROFILE_FRONTEND -->8<!-- /count:PROFILE_FRONTEND --> site tools: `mock-render`, `visual-diff`, `tokens-sync`, `design-scrape`, `live-preview`, `figma-tokens`, `frontend-inspect`, `screenshot-decode` | ~60s | ~80 MB |
|
||||
| `ops` | <!-- count:PROFILE_OPS -->8<!-- /count:PROFILE_OPS --> infra tools: `kei-ledger`, `ssh-check`, `firewall-diff`, `provision-hetzner`, `provision-vultr`, `harden-base`, `metrics-scrape`, `log-ship` | ~90s | ~50 MB |
|
||||
| `dev` | <!-- count:PROFILE_DEV -->10<!-- /count:PROFILE_DEV --> dev tools: `kei-migrate`, `kei-changelog`, `kei-ci-lint`, `kei-docs-scaffold`, `kei-memory`, `kei-conflict-scan`, `kei-refactor-engine`, `kei-graph-check`, `kei-store`, `kei-artifact` | ~60s | ~40 MB |
|
||||
| `full` | everything (<!-- count:PROFILE_FULL -->37<!-- /count:PROFILE_FULL --> primitives) | ~5 min | ~200 MB |
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
./install.sh # minimal (no primitives)
|
||||
./install.sh --profile=frontend # minimal + 8 site tools
|
||||
./install.sh --profile=frontend # minimal + <!-- count:PROFILE_FRONTEND -->8<!-- /count:PROFILE_FRONTEND --> site tools
|
||||
./install.sh --profile=full # everything (old default behaviour)
|
||||
./install.sh --add=kei-ledger # add a single primitive on top of current install
|
||||
./install.sh --add=kei-ledger,ssh-check
|
||||
|
|
@ -108,7 +108,7 @@ Examples:
|
|||
|
||||
Profile resolution lives in `_primitives/MANIFEST.toml` — one `[primitive.<name>]` entry per primitive plus a `[profile]` block. Edit the manifest to define new profiles without touching `install.sh`.
|
||||
|
||||
> **Migrating from a full install:** if you're re-running `install.sh` after an earlier version that installed all primitives unconditionally, the new default (`minimal`) will REMOVE them. To preserve the old behaviour explicitly, pass `--profile=full` (currently 36 primitives).
|
||||
> **Migrating from a full install:** if you're re-running `install.sh` after an earlier version that installed all primitives unconditionally, the new default (`minimal`) will REMOVE them. To preserve the old behaviour explicitly, pass `--profile=full` (currently <!-- count:PROFILE_FULL -->37<!-- /count:PROFILE_FULL --> primitives).
|
||||
|
||||
> **Re-install disclaimer:** `install.sh` is idempotent for clean state but **overwrites kit-owned `_blocks/`, `_primitives/`, `_bridges/`, `_templates/`, `_assembler/`, `hooks/`, and `skills/` on re-run** — local modifications under those directories are backed up to `<dir>.bak-TIMESTAMP/` (or, for shared hook files, to `<file>.bak-TIMESTAMP`). User-owned `_manifests/*.toml` are never overwritten.
|
||||
|
||||
|
|
@ -143,16 +143,16 @@ Interactive wizard: run `/hooks-control` — click-only picker that shows curren
|
|||
|
||||
| Category | Count | Examples |
|
||||
|---|---:|---|
|
||||
| Behavioral blocks | 73 | `baseline`, `evidence-grading`, `rule-math-first`, `stack-rust-axum`, `stack-react-vite`, `stack-sveltekit`, `stack-astro`, `deploy-modal`, `api-fal-ai`, ... |
|
||||
| Generic agents (manifests) | 12 | `kei-code-implementer`, `kei-critic`, `kei-validator`, `kei-security-auditor`, `kei-architect`, `kei-researcher`, `kei-ml-implementer`, `kei-cost-guardian`, `kei-modal-runner`, ... |
|
||||
| Hooks (PreToolUse / PostToolUse) | 9 | `assemble-agents`, `assemble-validate`, `no-hand-edit-agents`, `tomd-preread`, `agent-fork-logger`, `site-wysiwyd-check`, `session-end-dump`, `milestone-commit-hook`, `error-spike-detector` |
|
||||
| Portable skills | 38 | `compose-solution`, `new-agent`, `new-project`, `site-create`, `schema-design`, `observability-setup`, `auth-setup`, `api-design`, `ci-scaffold`, `test-matrix`, `docs-scaffold`, `vm-provision`, ... |
|
||||
| Primitives (Rust crates, opt-in) | 23 | `kei-ledger`, `kei-migrate`, `kei-changelog`, `ssh-check`, `firewall-diff`, `mock-render`, `visual-diff`, `tokens-sync`, `kei-memory`, `kei-conflict-scan`, `kei-refactor-engine`, `kei-graph-check`, `kei-store`, `kei-router`, `kei-sage`, `kei-task`, `kei-chat-store`, `kei-crossdomain`, `kei-search-core`, `kei-content-store`, `kei-social-store`, `kei-curator`, `kei-auth` |
|
||||
| Primitives (shell, opt-in via profile) | 13 | `tomd`, `design-scrape`, `live-preview`, `figma-tokens`, `frontend-inspect`, `screenshot-decode`, `metrics-scrape`, `log-ship`, `provision-hetzner`, `provision-vultr`, `harden-base`, `kei-ci-lint`, `kei-docs-scaffold` |
|
||||
| Behavioral blocks | <!-- count:BLOCKS -->78<!-- /count:BLOCKS --> | `baseline`, `evidence-grading`, `rule-math-first`, `stack-rust-axum`, `stack-react-vite`, `stack-sveltekit`, `stack-astro`, `deploy-modal`, `api-fal-ai`, ... |
|
||||
| Generic agents (manifests) | <!-- count:AGENTS -->12<!-- /count:AGENTS --> | `kei-code-implementer`, `kei-critic`, `kei-validator`, `kei-security-auditor`, `kei-architect`, `kei-researcher`, `kei-ml-implementer`, `kei-cost-guardian`, `kei-modal-runner`, ... |
|
||||
| Hooks (PreToolUse / PostToolUse) | <!-- count:HOOKS -->9<!-- /count:HOOKS --> | `assemble-agents`, `assemble-validate`, `no-hand-edit-agents`, `tomd-preread`, `agent-fork-logger`, `site-wysiwyd-check`, `session-end-dump`, `milestone-commit-hook`, `error-spike-detector` |
|
||||
| Portable skills | <!-- count:SKILLS -->39<!-- /count:SKILLS --> | `compose-solution`, `new-agent`, `new-project`, `site-create`, `schema-design`, `observability-setup`, `auth-setup`, `api-design`, `ci-scaffold`, `test-matrix`, `docs-scaffold`, `vm-provision`, ... |
|
||||
| Primitives (Rust crates, opt-in) | <!-- count:RUST_CRATES -->24<!-- /count:RUST_CRATES --> | `kei-ledger`, `kei-migrate`, `kei-changelog`, `ssh-check`, `firewall-diff`, `mock-render`, `visual-diff`, `tokens-sync`, `kei-memory`, `kei-conflict-scan`, `kei-refactor-engine`, `kei-graph-check`, `kei-store`, `kei-router`, `kei-sage`, `kei-task`, `kei-chat-store`, `kei-crossdomain`, `kei-search-core`, `kei-content-store`, `kei-social-store`, `kei-curator`, `kei-auth` |
|
||||
| Primitives (shell, opt-in via profile) | <!-- count:SHELL_PRIMITIVES -->13<!-- /count:SHELL_PRIMITIVES --> | `tomd`, `design-scrape`, `live-preview`, `figma-tokens`, `frontend-inspect`, `screenshot-decode`, `metrics-scrape`, `log-ship`, `provision-hetzner`, `provision-vultr`, `harden-base`, `kei-ci-lint`, `kei-docs-scaffold` |
|
||||
| Shell helpers (always copied) | 3 | `kei-sleep-setup`, `kei-sleep-sync`, `kei-sleep-queue` (dormant until you run `/sleep-setup`) |
|
||||
| Cross-tool bridges | 11 | Cursor legacy/MDC, Codex, Copilot, Windsurf, Junie, Continue, Gemini, Aider, Replit |
|
||||
| Cross-tool bridges | <!-- count:BRIDGES -->11<!-- /count:BRIDGES --> | Cursor legacy/MDC, Codex, Copilot, Windsurf, Junie, Continue, Gemini, Aider, Replit |
|
||||
|
||||
Of the 73 blocks, the **8 base blocks** (`baseline`, `evidence-grading`, `memory-protocol`, `rule-pre-dev-gate`, `rule-test-first`, `rule-error-budget`, `rule-double-audit`, `rule-math-first`) are referenced directly by the 12 shipped manifests. The remaining blocks (`stack-*`, `deploy-*`, `api-*`, `scraper-*`, `domain-*`) are a library consumed by the `/new-agent` wizard and the hub-and-spoke pipeline skills: when you compose a project specialist or spin up a site, the wizard / pipeline picks the appropriate blocks and emits artefacts that reference them.
|
||||
Of the <!-- count:BLOCKS -->78<!-- /count:BLOCKS --> blocks, the **8 base blocks** (`baseline`, `evidence-grading`, `memory-protocol`, `rule-pre-dev-gate`, `rule-test-first`, `rule-error-budget`, `rule-double-audit`, `rule-math-first`) are referenced directly by the <!-- count:AGENTS -->12<!-- /count:AGENTS --> shipped manifests. The remaining blocks (`stack-*`, `deploy-*`, `api-*`, `scraper-*`, `domain-*`) are a library consumed by the `/new-agent` wizard and the hub-and-spoke pipeline skills: when you compose a project specialist or spin up a site, the wizard / pipeline picks the appropriate blocks and emits artefacts that reference them.
|
||||
|
||||
**Cognitive mode blocks** (`_blocks/mode-*.md`) are composable behavioural skews — `mode-skeptic`, `mode-devils-advocate`, `mode-minimalist`, `mode-maximalist`, `mode-first-principles`. Add any combination to an agent's manifest `blocks = [...]` list to stack the mode. Modes compose: `mode-skeptic` + `mode-minimalist` gives you an adversarial pruner; `mode-devils-advocate` + `mode-first-principles` gives a constraint-driven steel-manner. See `_blocks/README.md` for the full list.
|
||||
|
||||
|
|
@ -283,7 +283,7 @@ Requires the new `kei-conflict-scan`, `kei-refactor-engine`, `kei-graph-check`,
|
|||
|
||||
## Primitives (Rust)
|
||||
|
||||
`_primitives/_rust/` is a Cargo workspace with 23 single-binary crates (v0.13.0 added 4 deep-sleep primitives; v0.14.0 added 10 LBM-port MCP crates; v0.14.2 removed `genesis-scan` — internal-only tool, not shipped publicly). `install.sh` builds `--release` for the subset selected by the active profile and drops binaries at `~/.claude/agents/_primitives/_rust/target/release/<name>`.
|
||||
`_primitives/_rust/` is a Cargo workspace with <!-- count:RUST_CRATES -->24<!-- /count:RUST_CRATES --> single-binary crates (v0.13.0 added 4 deep-sleep primitives; v0.14.0 added <!-- count:LBM_PORTS -->10<!-- /count:LBM_PORTS --> LBM-port MCP crates; v0.14.2 removed `genesis-scan` — internal-only tool, not shipped publicly). `install.sh` builds `--release` for the subset selected by the active profile and drops binaries at `~/.claude/agents/_primitives/_rust/target/release/<name>`.
|
||||
|
||||
| Crate | Purpose |
|
||||
|---|---|
|
||||
|
|
@ -334,7 +334,7 @@ Requires the new `kei-conflict-scan`, `kei-refactor-engine`, `kei-graph-check`,
|
|||
Block edit (_blocks/<block>.md) <-- triggers rebuild of ALL agents
|
||||
```
|
||||
|
||||
9 hooks enforce the pipeline (6 pipeline + 3 session-audit):
|
||||
<!-- count:HOOKS -->9<!-- /count:HOOKS --> hooks enforce the pipeline (6 pipeline + 3 session-audit):
|
||||
|
||||
- **`assemble-agents`** (PostToolUse, Write/Edit) — rebuilds the affected agent(s) whenever a manifest or a block changes. No manual rebuild needed.
|
||||
- **`assemble-validate`** (PreToolUse, Bash) — blocks `git commit` inside `~/.claude` if any manifest fails validation. Keeps the repo in a buildable state at all times.
|
||||
|
|
@ -346,6 +346,17 @@ Requires the new `kei-conflict-scan`, `kei-refactor-engine`, `kei-graph-check`,
|
|||
- **`milestone-commit-hook`** (PostToolUse, Bash) — RULE 0.14 self-audit: appends a one-line session summary to `~/.claude/memory/audit-backlog.md` on every `feat:`/`refactor:`/merge commit.
|
||||
- **`error-spike-detector`** (PostToolUse, any tool) — RULE 0.14 self-audit: tags + logs the pattern when 3+ errors occur within the last 20 tool calls.
|
||||
|
||||
## Regenerating counts
|
||||
|
||||
Every number above (crates / skills / hooks / blocks / primitives / profile sizes) is wrapped in an HTML-comment marker — `<!-- count:NAME -->24<!-- /count:NAME -->` — and regenerated from sources of truth (`_primitives/MANIFEST.toml`, `_primitives/_rust/Cargo.toml`, filesystem walks). No more drift when a primitive or skill is added.
|
||||
|
||||
```bash
|
||||
./scripts/regen-counts.sh # rewrite README.md in place
|
||||
./scripts/regen-counts.sh --check # exit 1 if drift detected (no writes)
|
||||
```
|
||||
|
||||
Pre-commit gate: `scripts/precommit-counts-check.sh` — wire it into your hook manager (or symlink into `.git/hooks/pre-commit`) to block commits when README counts drift from the sources.
|
||||
|
||||
## Adding custom blocks
|
||||
|
||||
Blocks are plain markdown in `~/.claude/agents/_blocks/`. To add one:
|
||||
|
|
@ -381,7 +392,7 @@ All kit agents are namespaced under `kei-*` so they won't collide with your own
|
|||
|
||||
## Cross-tool bridges
|
||||
|
||||
KeiSeiKit ships 11 verified tool-bridge templates under `_bridges/`. Render them into any project and the same Constructor-Pattern ruleset is visible to every AI coding tool you use — no drift, one source of truth.
|
||||
KeiSeiKit ships <!-- count:BRIDGES -->11<!-- /count:BRIDGES --> verified tool-bridge templates under `_bridges/`. Render them into any project and the same Constructor-Pattern ruleset is visible to every AI coding tool you use — no drift, one source of truth.
|
||||
|
||||
**Tools covered:**
|
||||
|
||||
|
|
@ -400,8 +411,8 @@ KeiSeiKit ships 11 verified tool-bridge templates under `_bridges/`. Render them
|
|||
|
||||
**Three ways to generate:**
|
||||
|
||||
1. **At install time** — `./install.sh --with-bridges` renders all 11 into `$PWD` after the normal install completes. Skipped if `$PWD` is the KeiSeiKit repo itself.
|
||||
2. **From the `/new-agent` wizard** — Phase 8 asks click-only whether to generate all 11, just `AGENTS.md`, or skip.
|
||||
1. **At install time** — `./install.sh --with-bridges` renders all <!-- count:BRIDGES -->11<!-- /count:BRIDGES --> into `$PWD` after the normal install completes. Skipped if `$PWD` is the KeiSeiKit repo itself.
|
||||
2. **From the `/new-agent` wizard** — Phase 8 asks click-only whether to generate all <!-- count:BRIDGES -->11<!-- /count:BRIDGES -->, just `AGENTS.md`, or skip.
|
||||
3. **Manually, any time** — `~/.claude/agents/_bridges/emit.sh <project-dir>` (the install copies `_bridges/` into your agent fleet dir). Add `--only <output-path>` to restrict to a single file.
|
||||
|
||||
All paths are idempotent: existing bridge files in the project are skipped, never overwritten. See `_bridges/README.md` for the full template→output-path table.
|
||||
|
|
|
|||
26
scripts/precommit-counts-check.sh
Executable file
26
scripts/precommit-counts-check.sh
Executable file
|
|
@ -0,0 +1,26 @@
|
|||
#!/bin/sh
|
||||
# precommit-counts-check.sh — pre-commit gate for README count drift.
|
||||
# Runs scripts/regen-counts.sh --check; exits non-zero on drift.
|
||||
# Install: ln -s ../../scripts/precommit-counts-check.sh .git/hooks/pre-commit
|
||||
# or add to your hook manager of choice.
|
||||
|
||||
set -eu
|
||||
|
||||
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
REGEN="$ROOT/scripts/regen-counts.sh"
|
||||
|
||||
[ -x "$REGEN" ] || {
|
||||
printf 'precommit-counts-check: %s not executable\n' "$REGEN" >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
if "$REGEN" --check; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cat >&2 <<'EOF'
|
||||
|
||||
Counts drift detected in README.md.
|
||||
Run: ./scripts/regen-counts.sh && git add README.md
|
||||
EOF
|
||||
exit 1
|
||||
117
scripts/regen-counts.sh
Executable file
117
scripts/regen-counts.sh
Executable file
|
|
@ -0,0 +1,117 @@
|
|||
#!/bin/sh
|
||||
# regen-counts.sh — regenerate README.md counts from sources of truth.
|
||||
# Markers: <!-- count:NAME -->VAL<!-- /count:NAME -->
|
||||
# Sources: _primitives/MANIFEST.toml, _primitives/_rust/Cargo.toml, filesystem.
|
||||
# Usage: ./scripts/regen-counts.sh [--check]
|
||||
# POSIX sh; no arrays, no bashisms; no yq/jq/python hard deps.
|
||||
|
||||
set -eu
|
||||
|
||||
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
README="$ROOT/README.md"
|
||||
MANIFEST="$ROOT/_primitives/MANIFEST.toml"
|
||||
CARGO="$ROOT/_primitives/_rust/Cargo.toml"
|
||||
|
||||
die() { printf 'regen-counts: %s\n' "$*" >&2; exit 2; }
|
||||
|
||||
count_rust_crates() {
|
||||
awk '
|
||||
/^\[workspace\]/ { in_ws=1; next }
|
||||
/^\[/ { in_ws=0 }
|
||||
in_ws && /members *= *\[/ { in_arr=1 }
|
||||
in_arr { total += gsub(/"[^"]+"/, "&"); if (index($0, "]")) in_arr=0 }
|
||||
END { print total+0 }
|
||||
' "$CARGO"
|
||||
}
|
||||
|
||||
count_primitive_kind() {
|
||||
awk -v want="$1" '
|
||||
/^\[primitive\./ { in_block=1; next }
|
||||
/^\[/ { in_block=0 }
|
||||
in_block && $0 ~ "^kind *= *\"" want "\"" { n++; in_block=0 }
|
||||
END { print n+0 }
|
||||
' "$MANIFEST"
|
||||
}
|
||||
|
||||
count_profile() {
|
||||
awk -v key="$1" '
|
||||
/^\[profile\]/ { in_p=1; next }
|
||||
/^\[/ { in_p=0 }
|
||||
in_p && $1 == key && $2 == "=" {
|
||||
line=$0; sub(/^[^\[]*\[/, "", line); sub(/\].*$/, "", line)
|
||||
print gsub(/"[^"]+"/, "&", line) + 0; exit
|
||||
}
|
||||
' "$MANIFEST"
|
||||
}
|
||||
|
||||
count_files() { eval "$1" | wc -l | tr -d ' '; }
|
||||
|
||||
RUST_CRATES=$(count_rust_crates)
|
||||
RUST_PRIMITIVES=$(count_primitive_kind rust)
|
||||
SHELL_PRIMITIVES=$(count_primitive_kind shell)
|
||||
TOTAL_PRIMITIVES=$((RUST_PRIMITIVES + SHELL_PRIMITIVES))
|
||||
SKILLS=$(count_files "find '$ROOT/skills' -maxdepth 2 -name SKILL.md")
|
||||
HOOKS=$(count_files "find '$ROOT/hooks' -maxdepth 1 -name '*.sh'")
|
||||
BLOCKS=$(count_files "find '$ROOT/_blocks' -maxdepth 1 -name '*.md' -not -name README.md")
|
||||
AGENTS=$(count_files "find '$ROOT/_manifests' -maxdepth 1 -name 'kei-*.toml'")
|
||||
BRIDGES=$(count_files "find '$ROOT/_bridges' -maxdepth 1 \( -name '*.tmpl' -o -name '*.mdc' \)")
|
||||
PROFILE_FULL=$(count_profile full)
|
||||
PROFILE_MCP=$(count_profile mcp)
|
||||
PROFILE_DEV=$(count_profile dev)
|
||||
PROFILE_OPS=$(count_profile ops)
|
||||
PROFILE_FRONTEND=$(count_profile frontend)
|
||||
PROFILE_CORE=$(count_profile core)
|
||||
LBM_PORTS=10 # hand-maintained: v0.14 LBM port semantic group
|
||||
|
||||
[ "$RUST_CRATES" = "$RUST_PRIMITIVES" ] || \
|
||||
printf 'regen-counts: WARN Cargo members (%s) != MANIFEST rust kind (%s)\n' \
|
||||
"$RUST_CRATES" "$RUST_PRIMITIVES" >&2
|
||||
|
||||
apply_markers() {
|
||||
awk \
|
||||
-v m_rc="$RUST_CRATES" -v m_rp="$RUST_PRIMITIVES" \
|
||||
-v m_sp="$SHELL_PRIMITIVES" -v m_tp="$TOTAL_PRIMITIVES" \
|
||||
-v m_sk="$SKILLS" -v m_hk="$HOOKS" \
|
||||
-v m_bl="$BLOCKS" -v m_ag="$AGENTS" \
|
||||
-v m_br="$BRIDGES" \
|
||||
-v m_pf="$PROFILE_FULL" -v m_pm="$PROFILE_MCP" \
|
||||
-v m_pd="$PROFILE_DEV" -v m_po="$PROFILE_OPS" \
|
||||
-v m_pr="$PROFILE_FRONTEND" -v m_pc="$PROFILE_CORE" \
|
||||
-v m_lb="$LBM_PORTS" '
|
||||
function sub_marker(name, val, re) {
|
||||
re = "<!-- count:" name " -->[^<]*<!-- /count:" name " -->"
|
||||
gsub(re, "<!-- count:" name " -->" val "<!-- /count:" name " -->")
|
||||
}
|
||||
{
|
||||
sub_marker("RUST_CRATES", m_rc); sub_marker("RUST_PRIMITIVES", m_rp)
|
||||
sub_marker("SHELL_PRIMITIVES", m_sp); sub_marker("TOTAL_PRIMITIVES", m_tp)
|
||||
sub_marker("SKILLS", m_sk); sub_marker("HOOKS", m_hk)
|
||||
sub_marker("BLOCKS", m_bl); sub_marker("AGENTS", m_ag)
|
||||
sub_marker("BRIDGES", m_br); sub_marker("PROFILE_FULL", m_pf)
|
||||
sub_marker("PROFILE_MCP", m_pm); sub_marker("PROFILE_DEV", m_pd)
|
||||
sub_marker("PROFILE_OPS", m_po); sub_marker("PROFILE_FRONTEND", m_pr)
|
||||
sub_marker("PROFILE_CORE", m_pc); sub_marker("LBM_PORTS", m_lb)
|
||||
print
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
mode="${1:-write}"
|
||||
[ -f "$README" ] || die "README.md not found at $README"
|
||||
|
||||
tmp=$(mktemp -t regen-counts.XXXXXX) || die "mktemp failed"
|
||||
trap 'rm -f "$tmp"' EXIT INT TERM
|
||||
apply_markers <"$README" >"$tmp"
|
||||
|
||||
if [ "$mode" = "--check" ]; then
|
||||
if cmp -s "$README" "$tmp"; then
|
||||
echo "regen-counts: no drift"; exit 0
|
||||
fi
|
||||
echo "regen-counts: DRIFT DETECTED" >&2
|
||||
diff -u "$README" "$tmp" >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp "$tmp" "$README"
|
||||
printf 'regen-counts: README updated (crates=%s skills=%s hooks=%s blocks=%s prims=%s)\n' \
|
||||
"$RUST_CRATES" "$SKILLS" "$HOOKS" "$BLOCKS" "$TOTAL_PRIMITIVES"
|
||||
Loading…
Reference in a new issue