diff --git a/README.md b/README.md index c623d41..ac4ee45 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # KeiSeiKit — Constructor-Pattern Agent Kit for Claude Code -KeiSeiKit is a drop-in agent fleet 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, three pre-wired hooks, and six portable skills including an interactive `/new-agent` wizard. 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 drop-in agent fleet 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, four pre-wired hooks, and seven portable skills including an interactive `/new-agent` wizard. 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, and a build pipeline that keeps every agent derivable from its manifest. ## Prerequisites - **Rust** (stable toolchain) — the assembler is a small Cargo binary -- **jq** — used by the three shell hooks for JSON parsing (`brew install jq` / `apt install jq`) +- **jq** — used by the four shell hooks for JSON parsing (`brew install jq` / `apt install jq`) - **Claude Code** — the agents, hooks, and skills target Claude Code's agent / skill / hook surface ## Install @@ -25,7 +25,7 @@ cd KeiSeiKit 3. Copies generic manifests (skips if you already have a manifest with that name) 4. Builds the Rust assembler (`cargo build --release`) 5. Generates agent `.md` files in-place with `AGENT_ROOT=~/.claude/agents assemble --in-place` -6. Copies the three hooks and six skills +6. Copies the four hooks and seven 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. @@ -37,11 +37,13 @@ After install, the only remaining step is merging `settings-snippet.json` into y |---|---:|---| | Behavioral blocks | 33 | `baseline`, `evidence-grading`, `rule-math-first`, `stack-rust-axum`, `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 | 3 | `assemble-agents` (PostToolUse), `assemble-validate` (PreToolUse Bash), `no-hand-edit-agents` (PreToolUse Edit/Write) | -| Skills | 6 | `new-agent`, `research`, `test-gen`, `pr-review`, `refactor`, `debug-deep` | +| Hooks | 4 | `assemble-agents` (PostToolUse), `assemble-validate` (PreToolUse Bash), `no-hand-edit-agents` (PreToolUse Edit/Write), `tomd-preread` (PreToolUse Read) | +| Skills | 7 | `new-agent`, `research`, `test-gen`, `pr-review`, `refactor`, `debug-deep`, `compose-solution` | Of the 33 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 **25 blocks** (`stack-*`, `deploy-*`, `api-*`, `scraper-*`, `domain-*`) are a library consumed by the `/new-agent` wizard: when you compose a project specialist, the wizard picks the appropriate stack / deploy / API / scraper / domain blocks and emits a manifest that references them. The kit's generic 12 agents do not import them by default. +> **Frontend-stack coverage gap.** The shipped `stack-*` blocks currently cover Next.js and Flutter. Pure frontend frameworks (React-Vite, Vue-Nuxt, SvelteKit, Astro, Angular, plain-web) are planned as a follow-up; contributions are welcome via PR. Blocks stay in a single `_blocks/` directory — no opt-in split planned. + ## Creating a new agent Run the wizard in Claude Code: @@ -50,7 +52,7 @@ Run the wizard in Claude Code: /new-agent ``` -You'll be asked (via option-pickers, not free-text): +You'll be asked (via multiple option-picker batches, not free-text) — each batch groups several click-only questions into a single `AskUserQuestion` call: 1. Project stack (Rust CLI / axum / SwiftUI / Flutter / FastAPI / Next.js / Go / Embedded / Python ML) 2. Deploy target (local-only / EC2 / Cloudflare / Modal / Docker / none) @@ -74,11 +76,12 @@ Then one free-text prompt for slug + description + path + gotchas. The wizard co Block edit (_blocks/.md) <-- triggers rebuild of ALL agents ``` -Three hooks enforce the pipeline: +Four hooks enforce the pipeline: - **`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. - **`no-hand-edit-agents`** (PreToolUse, Edit/Write) — refuses edits to any `.md` under `~/.claude/agents/` that starts with the `` marker, pointing you at the manifest instead. Override with `AGENT_MIGRATION=1` for emergencies only. +- **`tomd-preread`** (PreToolUse, Read) — auto-converts opaque binary formats (`.docx`, `.doc`, `.xlsx`, `.pptx`, `.csv`) to markdown via the `tomd` primitive and redirects Claude to read the cached `.md` instead. Cache under `$KEISEI_TOMD_CACHE` (default `/tmp/keisei-tomd-cache`). Degrades silently if `jq` or the primitive is absent. ## Adding custom blocks @@ -144,7 +147,7 @@ All paths are idempotent: existing bridge files in the project are skipped, neve `_primitives/` holds first-class building blocks that agents and pipelines depend on — executable utilities, not behavioral markdown. Currently one primitive ships with the kit: -- `tomd` — universal non-native-format → markdown converter (PDF, DOCX, XLSX, PPTX, CSV, code, images with OCR). Ported from [KeiAgent](https://example.invalid/KeiAgent) with KeiSeiKit-style error tags and a configurable cache directory (`KEISEI_TOMD_CACHE`, default `/tmp/keisei-tomd-cache`). +- `tomd` — universal non-native-format → markdown converter (PDF, DOCX, XLSX, PPTX, CSV, code, images with OCR). Ported from the KeiAgent project (user's personal CLI predecessor) with KeiSeiKit-style error tags and a configurable cache directory (`KEISEI_TOMD_CACHE`, default `/tmp/keisei-tomd-cache`). The matching hook `hooks/tomd-preread.sh` is a `PreToolUse(Read)` entry that auto-redirects Claude to a cached markdown conversion when a Read targets an opaque binary format — no agent has to know about `tomd` explicitly, but any agent that *does* need to shell out can invoke `~/.claude/agents/_primitives/tomd.sh report.pdf > report.md` directly. diff --git a/_primitives/README.md b/_primitives/README.md index 7383e19..dcc1548 100644 --- a/_primitives/README.md +++ b/_primitives/README.md @@ -11,9 +11,9 @@ programs installed at `$HOME/.claude/agents/_primitives/` by `install.sh`. |---|---|---| | `tomd.sh` | Universal non-native-format → markdown converter (PDF, DOCX, XLSX, PPTX, CSV, images, code). | `~/.claude/agents/_primitives/tomd.sh ` | -`tomd.sh` is ported from [KeiAgent](https://…/KeiAgent) `bin/keiagent-tomd` — -same format matrix, KeiSeiKit-style error tags (`[tomd]`), configurable -cache directory (`KEISEI_TOMD_CACHE`). +`tomd.sh` is ported from the KeiAgent project (user's personal CLI +predecessor) `bin/keiagent-tomd` — same format matrix, KeiSeiKit-style +error tags (`[tomd]`), configurable cache directory (`KEISEI_TOMD_CACHE`). ## Hook integration diff --git a/hooks/no-hand-edit-agents.sh b/hooks/no-hand-edit-agents.sh index 92b89bd..b12456d 100755 --- a/hooks/no-hand-edit-agents.sh +++ b/hooks/no-hand-edit-agents.sh @@ -21,13 +21,21 @@ FILE=$(jq -r '.tool_input.file_path // empty') # Only care about files directly under ~/.claude/agents/*.md # (not blocks/, manifests/, assembler/, template, generated preview) +# +# NOTE on staleness: we use the `` marker +# on line 1 as the SOLE SOURCE OF TRUTH for "is this file generated?". +# Legacy agent .md files that were produced before the assembler existed +# (and therefore lack the marker) will pass this hook silently. That is +# intentional — the marker is how the assembler self-declares ownership, +# and any file without it is assumed hand-authored. Re-run the assembler +# to adopt an older file into the managed set. case "$FILE" in "$HOME/.claude/agents/_"*) exit 0 ;; "$HOME/.claude/agents/"*.md) ;; *) exit 0 ;; esac -# Detect generated marker in the first 10 lines +# Detect generated marker in the first 10 lines (sole truth — see NOTE above) if [ -f "$FILE" ] && head -10 "$FILE" | grep -q 'GENERATED by _assembler'; then NAME=$(basename "$FILE" .md) echo "[no-hand-edit-agents] BLOCKED: $FILE is generated." >&2 diff --git a/hooks/tomd-preread.sh b/hooks/tomd-preread.sh index e8ceb17..362cf2a 100755 --- a/hooks/tomd-preread.sh +++ b/hooks/tomd-preread.sh @@ -32,10 +32,15 @@ esac mkdir -p "$CACHE_DIR" -# Cache key: basename + mtime. Portable stat for macOS + Linux. +# Cache key: basename + mtime + short path-hash. Path-hash disambiguates +# two files with the same basename+mtime at different paths (otherwise they +# would collide and Claude would silently read the wrong conversion). +# Portable stat for macOS + Linux; portable shasum shim. BASENAME=$(basename "$FILE") MTIME=$(stat -f %m "$FILE" 2>/dev/null || stat -c %Y "$FILE" 2>/dev/null || echo 0) -MD_FILE="$CACHE_DIR/${BASENAME%.*}-${MTIME}.md" +PATH_HASH=$(printf '%s' "$FILE" | shasum 2>/dev/null | cut -c1-8) +[ -n "$PATH_HASH" ] || PATH_HASH="nohash" +MD_FILE="$CACHE_DIR/${BASENAME%.*}-${MTIME}-${PATH_HASH}.md" if [ ! -s "$MD_FILE" ]; then "$TOMD" "$FILE" > "$MD_FILE" 2>/dev/null || true diff --git a/install.sh b/install.sh index 40c271b..b7ed068 100755 --- a/install.sh +++ b/install.sh @@ -80,7 +80,11 @@ rollback() { orig="${pair%%|*}" bak="${pair#*|}" if [ -e "$bak" ]; then - rm -rf "$orig" + # Guard rm -rf: only remove $orig if it actually exists as a file or + # directory. Harmless either way, but explicit is safer than brittle. + if [ -d "$orig" ] || [ -f "$orig" ]; then + rm -rf "$orig" + fi mv "$bak" "$orig" say " restored $orig from $bak" fi @@ -142,6 +146,10 @@ activate_hooks() { say "created $target from snippet (no prior settings.json)" return 0 fi + # Merge path: back up the pre-merge settings.json so rollback can restore + # it if a later step ERR-traps. The "create new" path above exits before + # reaching here, so backup_file is only invoked when $target exists. + backup_file "$target" # Merge: walk each matcher-group in PostToolUse / PreToolUse, append hooks, # unique_by command. jq filter is written for readability, not golf. tmp="$(mktemp "$target.XXXXXX")" @@ -349,6 +357,12 @@ elif [ -t 0 ] && [ -t 1 ]; then fi # --- optional: render cross-tool bridges into $PWD ------------------------- +# If a prior step ERR-trapped into rollback(), we MUST NOT keep writing into +# $PWD — the install is now aborted, and bridges should not land as +# collateral on a failed run. rollback() sets ROLLED_BACK=1 before returning. +if [ "${ROLLED_BACK:-0}" = "1" ]; then + exit 2 +fi if [[ "$WITH_BRIDGES" == "1" ]]; then if [[ -f "./install.sh" && -d "./_bridges" ]]; then warn "not generating bridges — you are in the KeiSeiKit repo, not a project directory" @@ -369,7 +383,7 @@ if [ "$DID_ACTIVATE" = "1" ]; then ========================================================================== To verify install: - ls $AGENTS_DIR/*.md # should show ~14 generated agents + ls $AGENTS_DIR/*.md # should show 12 generated agents $AGENTS_DIR/_assembler/target/release/assemble --validate To create a new project-specialist agent: @@ -383,7 +397,7 @@ else NEXT STEP: merge settings-snippet.json into ~/.claude/settings.json ========================================================================== - KeiSeiKit ships 3 hooks (assemble-agents, assemble-validate, no-hand-edit). + KeiSeiKit ships 4 hooks (assemble-agents, assemble-validate, no-hand-edit, tomd-preread). To activate them, merge entries from: $KIT_DIR/settings-snippet.json into your: @@ -393,7 +407,7 @@ else ./install.sh --activate-hooks To verify install: - ls $AGENTS_DIR/*.md # should show ~14 generated agents + ls $AGENTS_DIR/*.md # should show 12 generated agents $AGENTS_DIR/_assembler/target/release/assemble --validate To create a new project-specialist agent: diff --git a/skills/compose-solution/SKILL.md b/skills/compose-solution/SKILL.md index 2595e0a..192e50d 100644 --- a/skills/compose-solution/SKILL.md +++ b/skills/compose-solution/SKILL.md @@ -116,6 +116,6 @@ Future invocations benefit from the K new blocks — kit is now smarter by K blo architectural decomposition if `research` is overkill - `_blocks/baseline.md`, `_blocks/rule-math-first.md` — block templates (Phase 6a shape references) -- `_manifests/kei-*.toml` — 14 kit agents (Phase 7b handoff references) +- `_manifests/kei-*.toml` — 12 kit agents (Phase 7b handoff references) - `_bridges/*.tmpl` — 11 tool bridges (architecture Phase 5 may reference them for agent-creation flows) diff --git a/skills/compose-solution/phase-2-decompose.md b/skills/compose-solution/phase-2-decompose.md index 00fe324..d7f6cf1 100644 --- a/skills/compose-solution/phase-2-decompose.md +++ b/skills/compose-solution/phase-2-decompose.md @@ -8,9 +8,11 @@ independently researched and composed. For heavy / deep-domain / unfamiliar-domain tasks, delegate to the `research` skill (`skills/research/SKILL.md`, Variant C "Deep decomposition" is the pattern — Wave 0 decomposition, then Wave 1 per-component -exploration). Invoke via the Agent tool with `subagent_type: kei-researcher` -(or `researcher` if that agent is present in the user's global fleet). Pass -`DESC` as the research question with the constraint: +exploration). Invoke via the Agent tool with `subagent_type: kei-researcher`. +Always prefer `kei-researcher` when it exists in the kit; bare `researcher` +matches only the user's personal fleet and may have divergent handoffs — do +not fall back to it silently. Pass `DESC` as the research question with the +constraint: > Decompose into 2-5 orthogonal components, each with a 1-line description > and 3-5 distinctive keywords suitable for grep prior-art search. diff --git a/skills/research/SKILL.md b/skills/research/SKILL.md index 319f5dc..ca72a6d 100644 --- a/skills/research/SKILL.md +++ b/skills/research/SKILL.md @@ -4,6 +4,13 @@ description: Deep research on any topic using parallel agents, web search, and c argument-hint: --- +> **Role-tag convention.** Names like `web-researcher`, `meta-critic`, +> `arch-analyst`, `{component}-critic` that appear later in this skill are +> ad-hoc role tags passed to the generic `kei-researcher` subagent inside +> its prompt — they are NOT separate manifests in the kit. Do not grep for +> them in `_manifests/`; they will not be found. The only manifest behind +> every research teammate is `kei-researcher`. + # Deep Research Skill You are conducting deep research on: $ARGUMENTS