diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92b4bfe..ededde0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - node: ['18', '20', '22'] + node: ['20', '22'] steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -73,4 +73,7 @@ jobs: - run: sudo apt-get update && sudo apt-get install -y shellcheck - name: shellcheck (advisory) run: find hooks _primitives -name '*.sh' -exec shellcheck -S warning {} + - continue-on-error: true # warnings are advisory initially + # v0.15.1: kept advisory because local shellcheck sweep not yet clean + # (quoted-var nits in hooks). Flip to fatal once the sweep is committed; + # planned for v0.16. + continue-on-error: true diff --git a/_manifests/kei-architect.toml b/_manifests/kei-architect.toml index 349db4f..49747ec 100644 --- a/_manifests/kei-architect.toml +++ b/_manifests/kei-architect.toml @@ -73,7 +73,6 @@ produces_artifact = "spec" [[handoff]] target = "kei-code-implementer" trigger = "structural finding implies a concrete refactor / extraction / module split" -produces_artifact = "spec" [[handoff]] target = "kei-critic" diff --git a/_manifests/kei-code-implementer.toml b/_manifests/kei-code-implementer.toml index 8842778..eebe231 100644 --- a/_manifests/kei-code-implementer.toml +++ b/_manifests/kei-code-implementer.toml @@ -79,7 +79,6 @@ trigger = "task involves deploy / CI/CD / secrets / IaC / credentials / public-s [[handoff]] target = "kei-critic" trigger = "anti-pattern sweep / code smell review on large diff (>500 LOC) or long function chains" -produces_artifact = "patch" [[handoff]] target = "kei-security-auditor" diff --git a/_manifests/kei-critic.toml b/_manifests/kei-critic.toml index 3cd2214..41ca16f 100644 --- a/_manifests/kei-critic.toml +++ b/_manifests/kei-critic.toml @@ -62,7 +62,6 @@ produces_artifact = "review" target = "kei-code-implementer" trigger = "confirmed findings need code edits (user approves fix plan first)" expects_artifact = "patch" -produces_artifact = "review" [[handoff]] target = "kei-security-auditor" diff --git a/_manifests/kei-security-auditor.toml b/_manifests/kei-security-auditor.toml index 3309384..eff1af4 100644 --- a/_manifests/kei-security-auditor.toml +++ b/_manifests/kei-security-auditor.toml @@ -54,6 +54,10 @@ output_extra_fields = [ "9-point checklist coverage: [x]/[ ] per item", ] +# v0.15.1: typed-artifact handoff — security-auditor emits a `review` artifact +# with severity-sorted findings (schema: kei-artifact://review). +produces_artifact = "review" + # Handoffs MUST come after all top-level keys (TOML array-of-tables scope rule) [[handoff]] target = "kei-code-implementer" diff --git a/_manifests/kei-validator.toml b/_manifests/kei-validator.toml index 0fff293..83aa9b9 100644 --- a/_manifests/kei-validator.toml +++ b/_manifests/kei-validator.toml @@ -55,6 +55,10 @@ output_extra_fields = [ "Overall verdict: ALL VERIFIED | PARTIAL (fix list) | BLOCK (FALSE findings present)", ] +# v0.15.1: typed-artifact handoff — validator emits a `review` artifact +# with severity-sorted findings (schema: kei-artifact://review). +produces_artifact = "review" + # Handoffs MUST come after all top-level keys (TOML array-of-tables scope rule) [[handoff]] target = "kei-ml-researcher" diff --git a/_primitives/_rust/kei-artifact/schemas/review.json b/_primitives/_rust/kei-artifact/schemas/review.json index 8d76070..5fa0279 100644 --- a/_primitives/_rust/kei-artifact/schemas/review.json +++ b/_primitives/_rust/kei-artifact/schemas/review.json @@ -17,6 +17,7 @@ }, "findings": { "type": "array", + "minItems": 1, "items": { "type": "object", "additionalProperties": false, diff --git a/_primitives/_rust/kei-artifact/src/validate.rs b/_primitives/_rust/kei-artifact/src/validate.rs index 15cde5f..841561f 100644 --- a/_primitives/_rust/kei-artifact/src/validate.rs +++ b/_primitives/_rust/kei-artifact/src/validate.rs @@ -27,6 +27,54 @@ pub fn validate_content(schema: &Value, content: &Value) -> Result<(), String> { check(schema, content, "$") } +/// Keywords the minimal validator knows about. Used by `warn_unsupported_keywords` +/// to flag — but not reject — schemas that lean on unsupported features (so an +/// operator writing human-readable docs in a schema still sees them stored, +/// while being warned they do not actually enforce anything). +const KNOWN_KEYWORDS: &[&str] = &[ + "$schema", + "$id", + "title", + "description", + "type", + "required", + "properties", + "additionalProperties", + "enum", + "items", + "minLength", + "minItems", + "minimum", +]; + +/// Emit a stderr warning for each schema keyword this validator does not +/// enforce. Non-fatal: the schema is still accepted and stored verbatim — +/// operators can keep `pattern` / `format` / `oneOf` etc. as human-readable +/// hints without expecting runtime validation of them. +/// +/// Walks the schema recursively so a nested `items` / `properties` sub-schema +/// with an unsupported keyword is caught too. +pub fn warn_unsupported_keywords(schema: &Value) { + fn walk(v: &Value, path: &str) { + if let Value::Object(map) = v { + for (k, sub) in map { + if !KNOWN_KEYWORDS.contains(&k.as_str()) { + eprintln!( + "[kei-artifact] schema warning: unsupported keyword '{k}' at {path} — \ +stored but not enforced by the minimal validator (see validate.rs KNOWN_KEYWORDS)" + ); + } + walk(sub, &format!("{path}.{k}")); + } + } else if let Value::Array(arr) = v { + for (i, el) in arr.iter().enumerate() { + walk(el, &format!("{path}[{i}]")); + } + } + } + walk(schema, "$"); +} + fn check(schema: &Value, value: &Value, path: &str) -> Result<(), String> { if let Some(t) = schema.get("type") { check_type(t, value, path)?; @@ -195,4 +243,26 @@ mod tests { let err = validate_content(&schema, &json!(["nope"])).unwrap_err(); assert!(err.contains("enum")); } + + #[test] + fn warn_unsupported_keywords_does_not_panic_or_mutate() { + // Smoke test — the warn function prints to stderr but returns unit and + // never mutates the schema. We cannot portably capture stderr without + // a gag-style helper, so we just assert execution is stable and the + // schema is still usable by `validate_content` afterwards. + let schema = json!({ + "type": "object", + "required": ["k"], + "properties": { + "k": {"type": "string", "pattern": "^[a-z]+$", "format": "email"} + }, + "oneOf": [{"type": "object"}], + "patternProperties": {"^x_": {"type": "string"}} + }); + warn_unsupported_keywords(&schema); + // Validator is still callable and still enforces the supported subset. + assert!(validate_content(&schema, &json!({"k": "hi"})).is_ok()); + let err = validate_content(&schema, &json!({})).unwrap_err(); + assert!(err.contains("k")); + } } diff --git a/_primitives/_rust/kei-artifact/tests/validation.rs b/_primitives/_rust/kei-artifact/tests/validation.rs index ea10f8b..9fb423d 100644 --- a/_primitives/_rust/kei-artifact/tests/validation.rs +++ b/_primitives/_rust/kei-artifact/tests/validation.rs @@ -137,3 +137,22 @@ fn patch_schema_rejects_invalid_op_enum() { let msg = format!("{err:#}"); assert!(msg.contains("enum"), "unexpected: {msg}"); } + +#[test] +fn review_schema_rejects_empty_findings() { + // v0.15.1 HIGH fix: review artifacts must list ≥ 1 finding so a `reject` + // or `request_changes` verdict cannot ship with nothing to point at. + let s = seed(); + let bad = serde_json::to_vec(&json!({ + "reviewer": "kei-critic", + "findings": [], + "verdict": "reject" + })) + .unwrap(); + let err = emit(&s, "review", "kei-critic", &bad, None, None).unwrap_err(); + let msg = format!("{err:#}"); + assert!( + msg.contains("array") || msg.contains("min"), + "unexpected: {msg}" + ); +} diff --git a/hooks/agent-fork-logger.sh b/hooks/agent-fork-logger.sh index 8d5e435..86e8e13 100755 --- a/hooks/agent-fork-logger.sh +++ b/hooks/agent-fork-logger.sh @@ -8,16 +8,21 @@ command -v jq >/dev/null 2>&1 || exit 0 -# --- RUNTIME CONTROLS (v0.14.2) --- +# --- RUNTIME CONTROLS (v0.15.1) --- +# KEI_DISABLED_HOOKS: tokenized exact-match list (comma- or space-separated). +# Repro of pre-v0.15.1 substring bypass (CVE-class): KEI_DISABLED_HOOKS="foo-all-bar" +# previously disabled every hook via `*all*`. v0.15.1 requires token equality. _hook_name="$(basename "$0" .sh)" -case "${KEI_DISABLED_HOOKS:-}" in - *"$_hook_name"*|*all*) exit 0 ;; +_disabled=" $(printf '%s' "${KEI_DISABLED_HOOKS:-}" | tr ',' ' ') " +case "$_disabled" in + *" $_hook_name "*|*" all "*) unset _disabled; exit 0 ;; esac +unset _disabled case "${KEI_HOOK_PROFILE:-full}" in off) exit 0 ;; minimal) case "$_hook_name" in - no-github-push|genesis-leak-guard|no-hand-edit-agents|secrets-guard|assemble-validate|git-pre-commit-genesis) ;; + no-hand-edit-agents|assemble-validate|agent-fork-logger|session-end-dump) ;; *) exit 0 ;; esac ;; diff --git a/hooks/assemble-agents.sh b/hooks/assemble-agents.sh index 7577726..d7e1aa3 100755 --- a/hooks/assemble-agents.sh +++ b/hooks/assemble-agents.sh @@ -13,16 +13,21 @@ # Claude Code would refuse the tool call system-wide. command -v jq >/dev/null 2>&1 || exit 0 -# --- RUNTIME CONTROLS (v0.14.2) --- +# --- RUNTIME CONTROLS (v0.15.1) --- +# KEI_DISABLED_HOOKS: tokenized exact-match list (comma- or space-separated). +# Repro of pre-v0.15.1 substring bypass (CVE-class): KEI_DISABLED_HOOKS="foo-all-bar" +# previously disabled every hook via `*all*`. v0.15.1 requires token equality. _hook_name="$(basename "$0" .sh)" -case "${KEI_DISABLED_HOOKS:-}" in - *"$_hook_name"*|*all*) exit 0 ;; +_disabled=" $(printf '%s' "${KEI_DISABLED_HOOKS:-}" | tr ',' ' ') " +case "$_disabled" in + *" $_hook_name "*|*" all "*) unset _disabled; exit 0 ;; esac +unset _disabled case "${KEI_HOOK_PROFILE:-full}" in off) exit 0 ;; minimal) case "$_hook_name" in - no-github-push|genesis-leak-guard|no-hand-edit-agents|secrets-guard|assemble-validate|git-pre-commit-genesis) ;; + no-hand-edit-agents|assemble-validate|agent-fork-logger|session-end-dump) ;; *) exit 0 ;; esac ;; diff --git a/hooks/assemble-validate.sh b/hooks/assemble-validate.sh index 6b4906f..b20e778 100755 --- a/hooks/assemble-validate.sh +++ b/hooks/assemble-validate.sh @@ -10,16 +10,21 @@ # Claude Code would refuse the tool call system-wide. command -v jq >/dev/null 2>&1 || exit 0 -# --- RUNTIME CONTROLS (v0.14.2) --- +# --- RUNTIME CONTROLS (v0.15.1) --- +# KEI_DISABLED_HOOKS: tokenized exact-match list (comma- or space-separated). +# Repro of pre-v0.15.1 substring bypass (CVE-class): KEI_DISABLED_HOOKS="foo-all-bar" +# previously disabled every hook via `*all*`. v0.15.1 requires token equality. _hook_name="$(basename "$0" .sh)" -case "${KEI_DISABLED_HOOKS:-}" in - *"$_hook_name"*|*all*) exit 0 ;; +_disabled=" $(printf '%s' "${KEI_DISABLED_HOOKS:-}" | tr ',' ' ') " +case "$_disabled" in + *" $_hook_name "*|*" all "*) unset _disabled; exit 0 ;; esac +unset _disabled case "${KEI_HOOK_PROFILE:-full}" in off) exit 0 ;; minimal) case "$_hook_name" in - no-github-push|genesis-leak-guard|no-hand-edit-agents|secrets-guard|assemble-validate|git-pre-commit-genesis) ;; + no-hand-edit-agents|assemble-validate|agent-fork-logger|session-end-dump) ;; *) exit 0 ;; esac ;; diff --git a/hooks/error-spike-detector.sh b/hooks/error-spike-detector.sh index 40deaee..78bd257 100755 --- a/hooks/error-spike-detector.sh +++ b/hooks/error-spike-detector.sh @@ -8,16 +8,21 @@ command -v jq >/dev/null 2>&1 || exit 0 -# --- RUNTIME CONTROLS (v0.14.2) --- +# --- RUNTIME CONTROLS (v0.15.1) --- +# KEI_DISABLED_HOOKS: tokenized exact-match list (comma- or space-separated). +# Repro of pre-v0.15.1 substring bypass (CVE-class): KEI_DISABLED_HOOKS="foo-all-bar" +# previously disabled every hook via `*all*`. v0.15.1 requires token equality. _hook_name="$(basename "$0" .sh)" -case "${KEI_DISABLED_HOOKS:-}" in - *"$_hook_name"*|*all*) exit 0 ;; +_disabled=" $(printf '%s' "${KEI_DISABLED_HOOKS:-}" | tr ',' ' ') " +case "$_disabled" in + *" $_hook_name "*|*" all "*) unset _disabled; exit 0 ;; esac +unset _disabled case "${KEI_HOOK_PROFILE:-full}" in off) exit 0 ;; minimal) case "$_hook_name" in - no-github-push|genesis-leak-guard|no-hand-edit-agents|secrets-guard|assemble-validate|git-pre-commit-genesis) ;; + no-hand-edit-agents|assemble-validate|agent-fork-logger|session-end-dump) ;; *) exit 0 ;; esac ;; diff --git a/hooks/milestone-commit-hook.sh b/hooks/milestone-commit-hook.sh index ff5fa64..ee24bf3 100755 --- a/hooks/milestone-commit-hook.sh +++ b/hooks/milestone-commit-hook.sh @@ -8,16 +8,21 @@ command -v jq >/dev/null 2>&1 || exit 0 -# --- RUNTIME CONTROLS (v0.14.2) --- +# --- RUNTIME CONTROLS (v0.15.1) --- +# KEI_DISABLED_HOOKS: tokenized exact-match list (comma- or space-separated). +# Repro of pre-v0.15.1 substring bypass (CVE-class): KEI_DISABLED_HOOKS="foo-all-bar" +# previously disabled every hook via `*all*`. v0.15.1 requires token equality. _hook_name="$(basename "$0" .sh)" -case "${KEI_DISABLED_HOOKS:-}" in - *"$_hook_name"*|*all*) exit 0 ;; +_disabled=" $(printf '%s' "${KEI_DISABLED_HOOKS:-}" | tr ',' ' ') " +case "$_disabled" in + *" $_hook_name "*|*" all "*) unset _disabled; exit 0 ;; esac +unset _disabled case "${KEI_HOOK_PROFILE:-full}" in off) exit 0 ;; minimal) case "$_hook_name" in - no-github-push|genesis-leak-guard|no-hand-edit-agents|secrets-guard|assemble-validate|git-pre-commit-genesis) ;; + no-hand-edit-agents|assemble-validate|agent-fork-logger|session-end-dump) ;; *) exit 0 ;; esac ;; diff --git a/hooks/no-hand-edit-agents.sh b/hooks/no-hand-edit-agents.sh index 49cf476..b4f63f4 100755 --- a/hooks/no-hand-edit-agents.sh +++ b/hooks/no-hand-edit-agents.sh @@ -12,16 +12,21 @@ # Claude Code would refuse Edit/Write system-wide. command -v jq >/dev/null 2>&1 || exit 0 -# --- RUNTIME CONTROLS (v0.14.2) --- +# --- RUNTIME CONTROLS (v0.15.1) --- +# KEI_DISABLED_HOOKS: tokenized exact-match list (comma- or space-separated). +# Repro of pre-v0.15.1 substring bypass (CVE-class): KEI_DISABLED_HOOKS="foo-all-bar" +# previously disabled every hook via `*all*`. v0.15.1 requires token equality. _hook_name="$(basename "$0" .sh)" -case "${KEI_DISABLED_HOOKS:-}" in - *"$_hook_name"*|*all*) exit 0 ;; +_disabled=" $(printf '%s' "${KEI_DISABLED_HOOKS:-}" | tr ',' ' ') " +case "$_disabled" in + *" $_hook_name "*|*" all "*) unset _disabled; exit 0 ;; esac +unset _disabled case "${KEI_HOOK_PROFILE:-full}" in off) exit 0 ;; minimal) case "$_hook_name" in - no-github-push|genesis-leak-guard|no-hand-edit-agents|secrets-guard|assemble-validate|git-pre-commit-genesis) ;; + no-hand-edit-agents|assemble-validate|agent-fork-logger|session-end-dump) ;; *) exit 0 ;; esac ;; diff --git a/hooks/session-end-dump.sh b/hooks/session-end-dump.sh index 574bb38..bcf87db 100755 --- a/hooks/session-end-dump.sh +++ b/hooks/session-end-dump.sh @@ -8,16 +8,21 @@ command -v jq >/dev/null 2>&1 || exit 0 -# --- RUNTIME CONTROLS (v0.14.2) --- +# --- RUNTIME CONTROLS (v0.15.1) --- +# KEI_DISABLED_HOOKS: tokenized exact-match list (comma- or space-separated). +# Repro of pre-v0.15.1 substring bypass (CVE-class): KEI_DISABLED_HOOKS="foo-all-bar" +# previously disabled every hook via `*all*`. v0.15.1 requires token equality. _hook_name="$(basename "$0" .sh)" -case "${KEI_DISABLED_HOOKS:-}" in - *"$_hook_name"*|*all*) exit 0 ;; +_disabled=" $(printf '%s' "${KEI_DISABLED_HOOKS:-}" | tr ',' ' ') " +case "$_disabled" in + *" $_hook_name "*|*" all "*) unset _disabled; exit 0 ;; esac +unset _disabled case "${KEI_HOOK_PROFILE:-full}" in off) exit 0 ;; minimal) case "$_hook_name" in - no-github-push|genesis-leak-guard|no-hand-edit-agents|secrets-guard|assemble-validate|git-pre-commit-genesis) ;; + no-hand-edit-agents|assemble-validate|agent-fork-logger|session-end-dump) ;; *) exit 0 ;; esac ;; diff --git a/hooks/site-wysiwyd-check.sh b/hooks/site-wysiwyd-check.sh index 1bc1f98..c954a4c 100755 --- a/hooks/site-wysiwyd-check.sh +++ b/hooks/site-wysiwyd-check.sh @@ -13,16 +13,21 @@ command -v jq >/dev/null 2>&1 || exit 0 -# --- RUNTIME CONTROLS (v0.14.2) --- +# --- RUNTIME CONTROLS (v0.15.1) --- +# KEI_DISABLED_HOOKS: tokenized exact-match list (comma- or space-separated). +# Repro of pre-v0.15.1 substring bypass (CVE-class): KEI_DISABLED_HOOKS="foo-all-bar" +# previously disabled every hook via `*all*`. v0.15.1 requires token equality. _hook_name="$(basename "$0" .sh)" -case "${KEI_DISABLED_HOOKS:-}" in - *"$_hook_name"*|*all*) exit 0 ;; +_disabled=" $(printf '%s' "${KEI_DISABLED_HOOKS:-}" | tr ',' ' ') " +case "$_disabled" in + *" $_hook_name "*|*" all "*) unset _disabled; exit 0 ;; esac +unset _disabled case "${KEI_HOOK_PROFILE:-full}" in off) exit 0 ;; minimal) case "$_hook_name" in - no-github-push|genesis-leak-guard|no-hand-edit-agents|secrets-guard|assemble-validate|git-pre-commit-genesis) ;; + no-hand-edit-agents|assemble-validate|agent-fork-logger|session-end-dump) ;; *) exit 0 ;; esac ;; diff --git a/hooks/tomd-preread.sh b/hooks/tomd-preread.sh index 834ef4a..a10198c 100755 --- a/hooks/tomd-preread.sh +++ b/hooks/tomd-preread.sh @@ -11,16 +11,21 @@ # Claude Code would refuse Read system-wide. command -v jq >/dev/null 2>&1 || exit 0 -# --- RUNTIME CONTROLS (v0.14.2) --- +# --- RUNTIME CONTROLS (v0.15.1) --- +# KEI_DISABLED_HOOKS: tokenized exact-match list (comma- or space-separated). +# Repro of pre-v0.15.1 substring bypass (CVE-class): KEI_DISABLED_HOOKS="foo-all-bar" +# previously disabled every hook via `*all*`. v0.15.1 requires token equality. _hook_name="$(basename "$0" .sh)" -case "${KEI_DISABLED_HOOKS:-}" in - *"$_hook_name"*|*all*) exit 0 ;; +_disabled=" $(printf '%s' "${KEI_DISABLED_HOOKS:-}" | tr ',' ' ') " +case "$_disabled" in + *" $_hook_name "*|*" all "*) unset _disabled; exit 0 ;; esac +unset _disabled case "${KEI_HOOK_PROFILE:-full}" in off) exit 0 ;; minimal) case "$_hook_name" in - no-github-push|genesis-leak-guard|no-hand-edit-agents|secrets-guard|assemble-validate|git-pre-commit-genesis) ;; + no-hand-edit-agents|assemble-validate|agent-fork-logger|session-end-dump) ;; *) exit 0 ;; esac ;; diff --git a/skills/hooks-control/SKILL.md b/skills/hooks-control/SKILL.md index fc7f1ac..024bef9 100644 --- a/skills/hooks-control/SKILL.md +++ b/skills/hooks-control/SKILL.md @@ -1,6 +1,6 @@ --- name: hooks-control -description: Runtime enable/disable of KeiSeiKit hooks via env vars (v0.14.2). Click-only wizard that emits shell `export` / `unset` commands for the user to paste. Supports per-hook disable, profile switch (full / advisory-off / minimal / off), or full re-enable. Does NOT execute anything — user controls their shell. +description: Runtime enable/disable of KeiSeiKit hooks via env vars (v0.15.1). Click-only wizard that emits shell `export` / `unset` commands for the user to paste. Supports per-hook disable, profile switch (full / advisory-off / minimal / off), or full re-enable. Does NOT execute anything — user controls their shell. argument-hint: (none — fully click-driven) --- @@ -10,18 +10,18 @@ Click-only wizard. Helps you toggle KeiSeiKit hooks **for the current shell session** via env vars, without editing `~/.claude/settings.json`. The skill emits shell commands; it NEVER runs them. -Two env vars are honoured by every kit-shipped hook (v0.14.2+): +Two env vars are honoured by every kit-shipped hook (v0.15.1+): | Var | Meaning | |---|---| -| `KEI_DISABLED_HOOKS` | Comma- or space-list of hook base names (no `.sh`). `all` disables every hook. | +| `KEI_DISABLED_HOOKS` | Comma- or space-list of hook base names (no `.sh`). Matching is **tokenized exact-match** (v0.15.1 fix — earlier versions used substring-glob, which let `foo-all-bar` disable every hook). The literal `all` token still disables every hook. | | `KEI_HOOK_PROFILE` | One of `full` (default), `advisory-off`, `minimal`, `off`. | | Profile | What stays on | |---|---| | `full` (default) | Every hook | | `advisory-off` | Disables pure-stderr advisories: `recurrence-suggest`, `citation-verify`, `error-spike-detector`, `milestone-commit-hook`. | -| `minimal` | Safety-only: `no-github-push`, `genesis-leak-guard`, `no-hand-edit-agents`, `secrets-guard`, `assemble-validate`, `git-pre-commit-genesis`. | +| `minimal` | Only the four kit-shipped hooks needed for structural integrity or observability: `no-hand-edit-agents`, `assemble-validate`, `agent-fork-logger`, `session-end-dump`. User-global safety hooks (`no-github-push`, `secrets-guard`, `genesis-leak-guard`, `git-pre-commit-genesis`) are not shipped by the kit but are respected when present in `~/.claude/hooks/`. | | `off` | Every hook off (escape hatch — use when debugging hook interactions). | --- @@ -34,7 +34,7 @@ Print current state: ``` Current KEI_DISABLED_HOOKS: Current KEI_HOOK_PROFILE: -Active kit-shipped hooks: +Active kit-shipped hooks: ``` `AskUserQuestion` — **What do you want to do?** @@ -45,10 +45,10 @@ Active kit-shipped hooks: ### Phase 2a — Hook multi-select (if picked 1) -`AskUserQuestion` multi-select over the 10 kit-shipped hook names: +`AskUserQuestion` multi-select over the 9 kit-shipped hook names: `assemble-agents`, `assemble-validate`, `no-hand-edit-agents`, `tomd-preread`, `agent-fork-logger`, `site-wysiwyd-check`, `error-spike-detector`, -`milestone-commit-hook`, `session-end-dump`, `git-pre-commit-genesis`. +`milestone-commit-hook`, `session-end-dump`. Emit: ```sh @@ -88,7 +88,7 @@ Stop after the state block. exit — the shell running hooks is a subshell. - **No rc edits.** If the user wants persistence, we say "paste into your shell rc". The skill MUST NOT modify `~/.zshrc` / `~/.bashrc`. -- **RULE 0.4 — no invented hook names.** Only the 10 names in Phase 2a +- **RULE 0.4 — no invented hook names.** Only the 9 names in Phase 2a are valid choices. Never suggest a name not in the kit. - **RULE -1 — NO DOWNGRADE.** If the user asks "can I silence all safety hooks?", present tradeoffs; point at `KEI_HOOK_PROFILE=off` with a @@ -111,7 +111,7 @@ Undo: unset KEI_DISABLED_HOOKS KEI_HOOK_PROFILE ## References -- `hooks/*.sh` — each kit hook sources the v0.14.2 runtime-controls block +- `hooks/*.sh` — each kit hook sources the v0.15.1 runtime-controls block - `README.md` → "Runtime hook controls" section - `~/.claude/rules/recurrence-escalate.md` — severity ladder notes that hooks can be silenced at runtime, no rule deletion required