Constructor Pattern (RULE ZERO). Zero behaviour change, zero flag
drift — all original CLI flags preserved verbatim.
Before: install.sh — 1238 LOC monolith
After: install.sh — 138 LOC dispatcher (sources libs in order)
install/lib-*.sh — 16 cubes, max 183 LOC (lib-menu)
Cubes:
lib-log 21 LOC — logging primitives
lib-backup 63 LOC — rollback trap + BACKUP_PAIRS
lib-profile 115 LOC — MANIFEST.toml profile resolution
lib-args 92 LOC — CLI parsing + --help heredoc
lib-menu 183 LOC — whiptail/dialog/plain-text interactive picker
lib-plan 150 LOC — dry-run --no-execute output
lib-prereqs 91 LOC — hard + soft dependency checks
lib-primitives 131 LOC — primitive copy + MANIFEST drive
lib-rust 114 LOC — cargo workspace build + pre-built support
lib-scaffold 144 LOC — agent/skill/block scaffolding
lib-bridges 31 LOC — project-bridge install
lib-hooks 104 LOC — settings.json jq merge
lib-agents 77 LOC — assembled agent output
lib-skills 23 LOC — skill copy
lib-wizard 20 LOC — sleep-setup wizard invocation
lib-summary 59 LOC — post-install summary
Invariants preserved:
- macOS bash 3.2 compat (no associative arrays, no [[ ]], no ${,,})
- rollback trap wired via setup_backup_trap early in dispatcher
- jq-merge behaviour verbatim in lib-hooks
- scoped Cargo.toml regeneration in lib-rust
Function LOC limits: largest non-heredoc fn 22 LOC (check_soft_prereqs).
Three functions kept >30 LOC because heredoc-dominated (print_help,
print_summary, profile_members); splitting would fragment logical unit.
62 unique function names across cubes, zero duplicates (grep-verified).
bash -n passes on all 17 files. Runtime smoke test deferred to user's
shell (bash-readonly sandbox constraint).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- TUI via whiptail (preferred) or dialog; plain-text fallback with zero deps
- Install Plan confirm screen: primitives, soft-deps status (✓/✗), estimates
- Skip menu on --profile/--add/--remove/--list or non-TTY (CI-safe)
- --yes skips confirm; --no-execute dry-run
- install.sh 844 → 1195 LOC, 10 new functions all <30 LOC
- README +8 LOC Interactive install section
- L1: install.sh post-install banners '~14 generated agents' → '12 generated agents'
(both the activated-path and the manual-merge-path copies).
- L2: skills/compose-solution/SKILL.md handoff reference '14 kit agents' → '12 kit agents'.
- L3: README /new-agent section rephrases 'via option-pickers' to call out that
the 6 questions are grouped into multiple option-picker batches (two
AskUserQuestion calls) rather than six separate prompts.
- M1 (RULE 0.4): replace fabricated URLs 'https://example.invalid/PROJECT-D'
and 'https://…/PROJECT-D' with plain text ('user's personal CLI predecessor').
- M2: tomd-preread cache key = basename + mtime + 8-char shasum of full path,
so two files with the same basename+mtime at different paths no longer
collide. Portable shasum shim; falls back to 'nohash' if shasum absent.
- M3: install.sh --with-bridges gated on ROLLED_BACK=0 so bridges are NOT
emitted into $PWD after an ERR-trap rollback.
- M4: rollback() guards rm -rf "$orig" behind an existence check.
- M5: skills/research/SKILL.md front-matter note — role tags like
'web-researcher' / 'meta-critic' are ad-hoc prompt labels for the generic
kei-researcher subagent, NOT separate manifests. Prevents fruitless
grep in _manifests/.
- M6: README adds a 'Frontend-stack coverage gap' callout listing the
planned-but-not-shipped frameworks (React-Vite, Vue-Nuxt, SvelteKit,
Astro, Angular, plain-web).
- M7: no-hand-edit-agents.sh documents at case block that the GENERATED
marker is the SOLE source of truth — legacy unmarked .md files pass
silently by design; re-run the assembler to adopt them.
- phase-2-decompose.md: delete 'or researcher if that agent is present in the user's
global fleet' clause that silently bypassed the kei-namespace. Replace with an
explicit prefer-kei note warning that bare 'researcher' matches only the user's
personal fleet and may have divergent handoffs.
- install.sh activate_hooks(): call backup_file "$target" on the merge path
(after the 'create new' early-return) so ERR-trap rollback can restore the
pre-merge settings.json. Previously an ERR in the jq-merge or mv left no
backup pair to restore.
- install.sh:
- mkdir $AGENTS_DIR/_primitives
- copy *.sh + README.md from kit _primitives/ with backup_dir guard
- chmod +x for the primitive scripts
- extend hooks-copy loop to include tomd-preread.sh (skips if absent,
preserving back-compat with kits that predate the primitive)
- soft-warn (not hard-fail) if pandoc is missing — tomd primitive is
opt-in and works without pandoc for CSV/code/JSON/images
- settings-snippet.json: add PreToolUse(Read) entry for tomd-preread.sh
with statusMessage. New matcher block (no existing Read matcher).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Copy _bridges/ into ~/.claude/agents/_bridges/ so new-agent
skill Phase 8 can invoke the renderer from the user fleet dir
- Add --with-bridges flag: after agents/hooks/skills install,
invoke _bridges/emit.sh "$PWD" to render 11 bridge files
- Guard: if $PWD is the KeiSeiKit source repo itself (detected
via ./install.sh + ./_bridges), warn and skip — avoid
polluting the kit's own working tree
- --help prints usage block; flag parser loop supports future
--activate-hooks coexistence without refactor
$HOOKS_DIR ($HOME/.claude/hooks) is shared with other kits. Backing up
the whole directory cumulatively bloats foreign hooks across re-runs.
Switch to backup_file per KeiSeiKit-owned hook so only the 3 files we
actually overwrite get a .bak-TIMESTAMP sibling. backup_dir remains for
KeiSeiKit-owned directories (_blocks, _templates, _assembler, skills).
say/warn/err now detect isatty(1) and the NO_COLOR env convention. When
stdout is redirected to a log file or NO_COLOR is set, ANSI escape codes
are suppressed. Interactive activation prompt also gated.
Offline build is attempted first so a fresh clone with a warm registry
cache can install without network. On failure, fall back to a regular
cargo build --release which will fetch from crates.io.
Adds --activate-hooks flag for non-interactive hook activation, plus a
TTY prompt at end-of-install. Merge is jq-based, groups by matcher, and
de-dupes hook entries by command — idempotent across re-runs. Existing
user hooks on the same matcher are preserved. Non-TTY without the flag
keeps the manual instructions.
If cargo build or any later step fails, the ERR trap walks the list of
backups created during this run and atomically swaps each .bak-TIMESTAMP
back onto its original. Idempotent via ROLLED_BACK guard. On success
nothing is rolled back — backups remain as the user's recovery copy.
_blocks/memory-protocol.md references ~/.claude/memory/MEMORY.md, but the
installer previously only scaffolded agents/ hooks/ skills/ — so the first
agent that followed the block would fail reading a non-existent index.
Now mkdir -p ~/.claude/memory and, only when MEMORY.md is absent, write a
minimal placeholder (frontmatter + pointer to the block). User-edited
MEMORY.md is never overwritten.
All three hooks used `set -eu` + `cat | jq …`. Without jq installed, jq
would fail and `-e` would abort the hook → non-zero exit → Claude Code
refuses Edit/Write/Bash system-wide. Now each hook probes for jq BEFORE
`set -eu` and exits 0 silently if absent. Also dropped the useless `cat |`
pipe — `jq -r` reads stdin directly.
Companion: install.sh jq check upgraded from warn to hard `exit 1` because
without jq the hooks are dead weight; message states jq is required on
any machine that will activate the hooks.
Previously cp -f clobbered user-edited _blocks/, _templates/, _assembler/,
hooks/, and skills/ silently — violating the README's "idempotent" claim
on re-run. Now each of those targets is snapshotted to a timestamped
<target>.bak-$(date +%s)/ sibling before overwrite, but only when the
target actually contains regular files (freshly-mkdir'd scaffolds are
skipped). _manifests/ unchanged — already uses per-file skip-if-exists.
Kit source ($KIT_DIR) is never backed up.