--- name: escalate-recurrence description: Triggered when Claude detects the same mistake ≥2× in one session, or the user says "опять / again / second time / already said". Interactively codifies the pattern into a rule + hook + wiki entry via option-picker questions. Pure-click flow — no free-text decisions. Writes rule file, generates hook scaffold at user-chosen severity, registers hook in settings.json via update-config. See ~/.claude/rules/recurrence-escalate.md. --- # Escalate Recurrence — Interactive Codifier You are converting an observed repeating mistake into a permanent guardrail: a rule file, a wiki entry, and (optionally) an enforcement hook. The entire decision chain is AskUserQuestion — the user clicks, you generate. --- ## Phase 0 — Sanity check (do this BEFORE asking anything) 1. State in ONE paragraph what you observed: - The mistake pattern (abstract, not specific instance) - Two or more concrete instances with file:line or tool-call evidence - Your root-cause hypothesis 2. Grep existing rules for coverage: ```bash grep -rinE "<3-5 distinctive keywords from the pattern>" ~/.claude/rules/ 2>/dev/null | head -10 ``` 3. If an existing rule ALREADY covers the pattern → offer `(A) extend that rule / (B) new sibling rule / (C) cancel` via AskUserQuestion and branch. Do NOT duplicate rules. --- ## Phase 1 — Pure-click decisions (AskUserQuestion, SINGLE call) Emit all four questions in one `AskUserQuestion` invocation. Use `multiSelect: false`. ```json { "questions": [ { "question": "Codify this pattern as a rule?", "header": "Rule", "multiSelect": false, "options": [ {"label": "Yes — write rule file", "description": "New file at ~/.claude/rules/.md"}, {"label": "Extend existing rule", "description": "Append section to the closest matching rule"}, {"label": "No — session-only", "description": "Note in chat, don't persist"}, {"label": "Later — remind me", "description": "Skip now, flag in recurrence-log for next session"} ] }, { "question": "Wikify (add to indices)?", "header": "Wiki", "multiSelect": false, "options": [ {"label": "Yes — full (RULES.md + MEMORY.md + CLAUDE.md Rules Index)", "description": "Discoverable from all three entry points"}, {"label": "Partial — RULES.md only", "description": "Registry entry only, no MEMORY.md pointer"}, {"label": "No", "description": "Rule file exists but not indexed"} ] }, { "question": "Create enforcement hook?", "header": "Hook severity", "multiSelect": false, "options": [ {"label": "block (exit 2)", "description": "Hard deny. Reserve for data-loss / secret / IP patterns."}, {"label": "enforce (exit 1)", "description": "Error — Claude must fix before retry"}, {"label": "warn (exit 0 + stderr)", "description": "Advisory; tool call proceeds. Safest first step."}, {"label": "remind (UserPromptSubmit)", "description": "Passive reminder on every user turn"}, {"label": "No hook", "description": "Rule only, no automation"} ] }, { "question": "Hook event type?", "header": "Hook event", "multiSelect": false, "options": [ {"label": "PreToolUse:Bash", "description": "Gate on shell commands — cmd-string pattern match"}, {"label": "PreToolUse:Edit|Write", "description": "Gate on file modifications — path / content pattern match"}, {"label": "PostToolUse:*", "description": "React AFTER a tool call (e.g. auto-rebuild, auto-log)"}, {"label": "UserPromptSubmit", "description": "Runs on every user turn — for remind-level hooks"}, {"label": "Stop", "description": "Runs when Claude finishes — for end-of-turn reminders"}, {"label": "N/A — no hook chosen", "description": "If previous answer was 'No hook'"} ] } ] } ``` Store as `R`, `W`, `S`, `E` (rule / wiki / severity / event). If `S == "No hook"` and `E != "N/A — no hook chosen"` → re-ask just the event question with a reminder. Never silently fix user inconsistency. --- ## Phase 2 — Generate artefacts (in memory — do NOT Write yet) Compose all files the user will see in the diff preview: ### 2.1 — Rule file (if `R` is Yes or Extend) Path: `~/.claude/rules/.md` (slug = kebab-case short pattern name, 2–4 words). Template: ```markdown # RULE — (YYYY-MM-DD ADDED) > . ## Incident ## The Rule ## Triggers - - ## Enforcement - Hook: `~/.claude/hooks/-guard.sh` (, severity ) - Bypass: ## Why this and not "remember to check" ## Rule lock YYYY-MM-DD. Never override without explicit user revocation. ``` Fill every `<...>` placeholder from Phase 0 + Phase 1 answers. Literal `{{placeholder}}` is forbidden — the assembler `validator.rs` rejects it. ### 2.2 — Hook scaffold (if `S != "No hook"`) Path: `~/.claude/hooks/-guard.sh`. **Template per event type:** `PreToolUse:Bash`: ```bash #!/bin/sh # -guard — # Severity: ; Event: PreToolUse:Bash # Rule: ~/.claude/rules/.md command -v jq >/dev/null 2>&1 || exit 0 set -eu CMD=$(jq -r '.tool_input.command // empty') [ -n "$CMD" ] || exit 0 # Trigger pattern — tighten during dogfooding case "$CMD" in **) echo "[] . See ~/.claude/rules/.md" >&2 exit ;; esac exit 0 ``` `PreToolUse:Edit|Write`: ```bash #!/bin/sh command -v jq >/dev/null 2>&1 || exit 0 set -eu FILE=$(jq -r '.tool_input.file_path // empty') CONTENT=$(jq -r '.tool_input.content // .tool_input.new_string // empty') [ -n "$FILE" ] || exit 0 case "$FILE" in **) printf '%s' "$CONTENT" | grep -qE '' || exit 0 echo "[] " >&2 exit ;; esac exit 0 ``` `UserPromptSubmit`: ```bash #!/bin/sh command -v jq >/dev/null 2>&1 || exit 0 PROMPT=$(jq -r '.prompt // empty') case "$PROMPT" in **) echo "[] reminder: . See ~/.claude/rules/.md" >&2 ;; esac exit 0 ``` **Exit code → severity map:** - block → `exit 2` - enforce → `exit 1` - warn → `exit 0` (but still echo to stderr) - remind → `exit 0` (UserPromptSubmit only — echo to stderr) Replace every `<...>` with the pattern-specific literal extracted from the Phase 0 evidence. Narrow match preferred over broad match — false-positive fatigue kills a hook faster than any bug. ### 2.3 — Wikification diffs (if `W != No`) Compute the diff lines to add: **`~/.claude/rules/RULES.md`** (append to table body, keep alphabetical order if easy; otherwise end): ``` | .md | | Rules Index (added YYYY-MM-DD) | ~/.claude/hooks/-guard.sh | ``` **`~/.claude/memory/MEMORY.md`** (one-liner under `## Rules & Feedback`): ``` - [[../../../../rules/]] — ``` **`~/.claude/CLAUDE.md` Rules Index table** (if `W == "Yes — full"`): ``` | | [[]] | | ``` ### 2.4 — Settings registration (if `S != "No hook"`) Compute the JSON hook entry: ```json { "matcher": "", "hooks": [ { "type": "command", "command": "~/.claude/hooks/-guard.sh", "statusMessage": " check..." } ] } ``` This gets merged into `~/.claude/settings.json` under `hooks.PreToolUse` / `hooks.UserPromptSubmit` / etc. --- ## Phase 3 — Confirm via diff preview (AskUserQuestion — keep it click-only) Show all generated artefacts inline in ONE message, THEN emit a single `AskUserQuestion`: ``` === GENERATED — REVIEW BEFORE WRITE === 1. New rule: ~/.claude/rules/.md 2. New hook: ~/.claude/hooks/-guard.sh (severity: , event: ) 3. RULES.md append: 4. MEMORY.md append: 5. settings.json merge (PreToolUse / UserPromptSubmit / etc): ``` Then: ```json { "questions": [ { "question": "Write these artefacts + register hook?", "header": "Confirm", "multiSelect": false, "options": [ {"label": "Confirm — write all", "description": "Phase 4 runs: files written, hook registered via update-config"}, {"label": "Edit rule (1)", "description": "Regenerate the rule file with changes you specify"}, {"label": "Edit hook (2)", "description": "Regenerate the hook scaffold with changes you specify"}, {"label": "Edit wiki diffs (3-4)", "description": "Regenerate RULES.md / MEMORY.md entries"}, {"label": "Edit settings (5)", "description": "Regenerate the settings.json merge block"}, {"label": "Abort", "description": "Stop — nothing gets written, no files touched"} ] } ] } ``` On `Confirm` → Phase 4. On any `Edit ` → ask ONE free-text line for what to change in that artefact (unavoidable — arbitrary content edit), regenerate, re-preview. On `Abort` → stop, write nothing, do NOT touch `~/.claude/`. --- ## Phase 4 — Write + register (on confirm only) Execute in order (each via its right tool — do NOT shell out when a tool exists): 1. `Write` → `~/.claude/rules/.md` 2. If hook: `Write` → `~/.claude/hooks/-guard.sh`, then `Bash` → `chmod +x ~/.claude/hooks/-guard.sh` 3. `Edit` → `~/.claude/rules/RULES.md` (append the row) 4. `Edit` → `~/.claude/memory/MEMORY.md` (append the line) 5. `Edit` → `~/.claude/CLAUDE.md` (add Rules Index row) — only if `W == "Yes — full"` 6. If hook: invoke the `update-config` skill with the settings-merge spec. Do NOT directly hand-edit `settings.json` — the skill knows how to merge without clobbering foreign entries. 7. `Edit` → append one line to `~/.claude/memory/recurrence-log.md` (create if absent): ``` YYYY-MM-DD | | severity= | event= | pattern="" | instances= ``` --- ## Phase 5 — Report ``` ✓ Rule codified: ~/.claude/rules/.md ✓ Hook registered: ~/.claude/hooks/-guard.sh (severity , event ) ✓ Wikified: RULES.md, MEMORY.md<, CLAUDE.md if full> ✓ Log entry: ~/.claude/memory/recurrence-log.md Next time this pattern triggers, the hook . To upgrade severity: invoke /escalate-recurrence again on the same pattern. To disable: remove the entry from settings.json (or set "enabled": false). ``` --- ## Rules (apply throughout) - **Pure click wherever possible.** Only slug and pattern-literal require typing. Everything structural is AskUserQuestion. - **Narrow match preferred.** A hook with false-positives gets ignored, then removed. Start narrow, widen on next recurrence. - **Start at lowest severity that solves the pain.** Upgrade on re-trigger, never auto-promote. - **Never write `{{placeholder}}` literals.** The assembler `validator.rs` rejects them; same hygiene applies here. - **No Patching.** A new rule codifies a pattern, it doesn't paper over an existing rule's gap. If the gap is in an existing rule → extend that rule (Phase 1 option "Extend existing"). - **Escape hatch.** Every hook MUST document its bypass mechanism (env var like `_BYPASS=1` or marker string). Without escape, emergency work stalls.