@softspark/ai-toolkit 4.2.4 → 4.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +10 -9
  3. package/app/.claude-plugin/plugin.json +1 -1
  4. package/app/hooks/_hook-io.sh +64 -0
  5. package/app/hooks/_locate-toolkit.sh +39 -0
  6. package/app/hooks/_search-capability.sh +46 -0
  7. package/app/hooks/commit-quality.sh +3 -1
  8. package/app/hooks/config-desync-guard.sh +95 -0
  9. package/app/hooks/governance-capture.sh +5 -3
  10. package/app/hooks/guard-config.sh +5 -3
  11. package/app/hooks/guard-destructive.sh +3 -1
  12. package/app/hooks/guard-path.sh +8 -1
  13. package/app/hooks/instructions-audit.sh +43 -0
  14. package/app/hooks/post-tool-use.sh +20 -8
  15. package/app/hooks/quality-gate.sh +47 -0
  16. package/app/hooks/revert-guard.sh +84 -0
  17. package/app/hooks/search-tracker.sh +18 -0
  18. package/app/hooks/session-start.sh +14 -2
  19. package/app/hooks/stop-search-check.sh +37 -0
  20. package/app/hooks/test-cohesion-map.json +77 -0
  21. package/app/hooks/test-cohesion.sh +93 -0
  22. package/app/hooks/track-usage.sh +3 -1
  23. package/app/hooks/user-prompt-submit.sh +33 -6
  24. package/app/hooks.json +65 -1
  25. package/benchmarks/ecosystem-doctor-snapshot.json +8 -8
  26. package/kb/procedures/release-verification-sop.md +2 -2
  27. package/kb/reference/architecture-overview.md +1 -1
  28. package/kb/reference/hooks-catalog.md +149 -24
  29. package/kb/reference/unique-features.md +11 -5
  30. package/llms-full.txt +163 -32
  31. package/manifest.json +1 -1
  32. package/package.json +1 -1
  33. package/scripts/doctor.py +13 -0
  34. package/scripts/generate_augment_hooks.py +5 -1
  35. package/scripts/generate_codex_hooks.py +2 -0
  36. package/scripts/generate_cursor_hooks.py +6 -0
  37. package/scripts/generate_gemini_hooks.py +5 -1
  38. package/scripts/generate_windsurf_hooks.py +6 -0
  39. package/scripts/install_steps/hooks.py +5 -0
  40. package/scripts/merge-hooks.py +10 -1
  41. package/scripts/plugin_schema.py +4 -0
  42. package/scripts/session_state.py +150 -0
  43. package/scripts/test_cohesion.py +133 -0
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env bash
2
+ # revert-guard.sh — Block git revert/restore on files edited in this session.
3
+ #
4
+ # Fires on: PreToolUse
5
+ # Matcher: Bash
6
+ # Skipped when TOOLKIT_HOOK_PROFILE=minimal.
7
+ #
8
+ # Constitution Art. VI.2 ("Fix Every Found Bug"): when a session has produced
9
+ # edits, a `git checkout/restore/reset --hard/clean -fd` on those files is
10
+ # almost always discarding work-in-progress instead of fixing the root cause.
11
+ # Block it unless the user explicitly opted in.
12
+ #
13
+ # Escape hatch: set CLAUDE_REVERT_OK=1 (per-call) to bypass.
14
+ # Exit codes:
15
+ # 0 allow command
16
+ # 2 block (per Claude Code hooks spec)
17
+
18
+ # shellcheck source=_profile-check.sh
19
+ source "$(dirname "$0")/_profile-check.sh"
20
+ # shellcheck source=_locate-toolkit.sh
21
+ source "$(dirname "$0")/_locate-toolkit.sh"
22
+ # shellcheck source=_hook-io.sh
23
+ source "$(dirname "$0")/_hook-io.sh"
24
+
25
+ INPUT=$(cat)
26
+ COMMAND=$(hook_command)
27
+
28
+ [ -z "$COMMAND" ] && exit 0
29
+ [ "${CLAUDE_REVERT_OK:-0}" = "1" ] && exit 0
30
+ [ -z "$TOOLKIT_DIR" ] && exit 0
31
+ command -v python3 >/dev/null 2>&1 || exit 0
32
+
33
+ # Strip leading words so we can match the git verb.
34
+ # Only act on commands whose first token is `git`.
35
+ read -r FIRST _rest <<<"$COMMAND"
36
+ [ "$FIRST" = "git" ] || exit 0
37
+
38
+ # Helper: emit block message on stderr and exit 2.
39
+ _block() {
40
+ local reason="$1"
41
+ local detail="$2"
42
+ cat >&2 <<EOF
43
+ revert-guard: BLOCKED — $reason
44
+ Constitution Art. VI.2: do not revert work-in-progress. Fix the root cause instead.
45
+
46
+ Affected: $detail
47
+
48
+ If you really need to discard changes, re-run with:
49
+ CLAUDE_REVERT_OK=1 $COMMAND
50
+ EOF
51
+ exit 2
52
+ }
53
+
54
+ # git reset --hard / git clean -fd: clobber-style, scope = all session edits.
55
+ if printf '%s' "$COMMAND" | grep -Eq 'reset[[:space:]]+(-+[A-Za-z-]+[[:space:]]+)*--hard|clean[[:space:]]+-[A-Za-z]*[df]'; then
56
+ edited=$(python3 "$TOOLKIT_DIR/scripts/session_state.py" list 2>/dev/null | head -10)
57
+ if [ -n "$edited" ]; then
58
+ _block "destructive 'git reset --hard' / 'git clean' with session edits in tree" "$edited"
59
+ fi
60
+ exit 0
61
+ fi
62
+
63
+ # git checkout / git restore: only block when `--` separator is present
64
+ # (file-restore form). Bare `git checkout branch-name` is allowed.
65
+ if printf '%s' "$COMMAND" | grep -Eq '(checkout|restore)([[:space:]].*)?[[:space:]]--[[:space:]]'; then
66
+ files=$(printf '%s' "$COMMAND" | awk -F' -- ' '{print $2}')
67
+ [ -z "$files" ] && exit 0
68
+ blocked=""
69
+ for f in $files; do
70
+ # Resolve relative paths against $PWD.
71
+ case "$f" in
72
+ /*) abs="$f" ;;
73
+ *) abs="$PWD/$f" ;;
74
+ esac
75
+ if python3 "$TOOLKIT_DIR/scripts/session_state.py" was-edited "$abs" >/dev/null 2>&1; then
76
+ blocked="$blocked $f"
77
+ fi
78
+ done
79
+ if [ -n "$blocked" ]; then
80
+ _block "restoring file(s) edited in this session" "$blocked"
81
+ fi
82
+ fi
83
+
84
+ exit 0
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env bash
2
+ # search-tracker.sh — Clear search-required flag when a search tool runs.
3
+ #
4
+ # Fires on: PostToolUse
5
+ # Matcher: mcp__rag-mcp__smart_query|mcp__rag-mcp__hybrid_search_kb|mcp__rag-mcp__crag_search|mcp__rag-mcp__multi_hop_search|WebSearch|WebFetch
6
+ # Non-blocking: always exits 0.
7
+ # Skipped when TOOLKIT_HOOK_PROFILE=minimal.
8
+ #
9
+ # Paired with user-prompt-submit.sh (sets the flag) and stop-search-check.sh
10
+ # (blocks Stop when flag still present). Together they implement the Step 0
11
+ # "search before answering" rule from global CLAUDE.md.
12
+
13
+ # shellcheck source=_profile-check.sh
14
+ source "$(dirname "$0")/_profile-check.sh"
15
+
16
+ FLAG="$HOME/.softspark/ai-toolkit/state/search-required.flag"
17
+ rm -f "$FLAG" 2>/dev/null
18
+ exit 0
@@ -8,9 +8,21 @@
8
8
  echo "MANDATORY: Before answering ANY technical question, apply ALL rules from your CLAUDE.md files (global + project). Follow the exact order of operations defined there. Do NOT skip mandatory steps even if you think you already know the answer."
9
9
  echo "REMINDER: When writing features or fixing bugs, ensure tests cover the changes. When modifying API, config, or setup, update relevant documentation. Propose these steps to the user — do not silently skip them."
10
10
 
11
+ # shellcheck source=_locate-toolkit.sh
12
+ source "$(dirname "$0")/_locate-toolkit.sh"
13
+
14
+ # 1a. Reset per-session edit state (used by revert-guard, test-cohesion, quality-gate)
15
+ SESSION_ID_INPUT=""
16
+ if [ ! -t 0 ]; then
17
+ STDIN_PAYLOAD="$(cat)"
18
+ SESSION_ID_INPUT="$(printf '%s' "$STDIN_PAYLOAD" | jq -r '.session_id // empty' 2>/dev/null)"
19
+ fi
20
+ if [ -n "$TOOLKIT_DIR" ] && command -v python3 >/dev/null 2>&1; then
21
+ python3 "$TOOLKIT_DIR/scripts/session_state.py" reset \
22
+ ${SESSION_ID_INPUT:+--session-id "$SESSION_ID_INPUT"} >/dev/null 2>&1 || true
23
+ fi
24
+
11
25
  # 2. Check for updates (cached, max once per 24h, non-blocking)
12
- TOOLKIT_DIR="$(npm root -g 2>/dev/null)/@softspark/ai-toolkit"
13
- [ ! -d "$TOOLKIT_DIR" ] && TOOLKIT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
14
26
  VERSION_MSG=$(python3 "$TOOLKIT_DIR/scripts/version_check.py" 2>/dev/null)
15
27
  if [ -n "$VERSION_MSG" ]; then
16
28
  echo "$VERSION_MSG"
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env bash
2
+ # stop-search-check.sh — Block Stop when search-first rule was skipped.
3
+ #
4
+ # Fires on: Stop
5
+ # Matcher: all
6
+ # Skipped when TOOLKIT_HOOK_PROFILE=minimal.
7
+ #
8
+ # If user-prompt-submit.sh set the search-required flag and no search tool
9
+ # was invoked (search-tracker.sh would have cleared it), this hook continues
10
+ # the conversation with a JSON `decision: block` payload so Claude reads the
11
+ # reminder and calls smart_query() before the next response.
12
+ #
13
+ # Override (one-off): CLAUDE_SKIP_SEARCH_FIRST=1
14
+
15
+ # shellcheck source=_profile-check.sh
16
+ source "$(dirname "$0")/_profile-check.sh"
17
+ # shellcheck source=_hook-io.sh
18
+ source "$(dirname "$0")/_hook-io.sh"
19
+ # shellcheck source=_search-capability.sh
20
+ source "$(dirname "$0")/_search-capability.sh"
21
+
22
+ [ "${CLAUDE_SKIP_SEARCH_FIRST:-0}" = "1" ] && exit 0
23
+
24
+ FLAG="$HOME/.softspark/ai-toolkit/state/search-required.flag"
25
+ [ -f "$FLAG" ] || exit 0
26
+
27
+ if ! ai_toolkit_has_search_provider; then
28
+ rm -f "$FLAG" 2>/dev/null
29
+ exit 0
30
+ fi
31
+
32
+ # Read the original prompt (line 2 of the flag file) for the reminder.
33
+ PROMPT_PREVIEW=$(sed -n '2p' "$FLAG" 2>/dev/null | head -c 200)
34
+ rm -f "$FLAG" 2>/dev/null # one-shot; do not loop forever
35
+
36
+ hook_emit_block "Stop" "Step 0 violated: you responded to a technical prompt without calling smart_query() or hybrid_search_kb() first. Per global CLAUDE.md GOLDEN RULE: search KB BEFORE answering. Prompt was: \"${PROMPT_PREVIEW}...\". Now: call the search tool, then continue your reply. Override (one-off): CLAUDE_SKIP_SEARCH_FIRST=1."
37
+ exit 0
@@ -0,0 +1,77 @@
1
+ [
2
+ {
3
+ "match": "app/hooks/quality-gate.sh",
4
+ "tests": ["tests/test_hooks.bats"],
5
+ "runner": "bats"
6
+ },
7
+ {
8
+ "match": "app/hooks/quality-check.sh",
9
+ "tests": ["tests/test_hooks.bats"],
10
+ "runner": "bats"
11
+ },
12
+ {
13
+ "match": "app/hooks/revert-guard.sh",
14
+ "tests": ["tests/test_revert_guard_hook.bats"],
15
+ "runner": "bats"
16
+ },
17
+ {
18
+ "match": "app/hooks/test-cohesion.sh",
19
+ "tests": ["tests/test_test_cohesion_hook.bats"],
20
+ "runner": "bats"
21
+ },
22
+ {
23
+ "match": "app/hooks/config-desync-guard.sh",
24
+ "tests": ["tests/test_config_desync_guard.bats"],
25
+ "runner": "bats"
26
+ },
27
+ {
28
+ "match": "app/hooks/instructions-audit.sh",
29
+ "tests": ["tests/test_instructions_audit.bats"],
30
+ "runner": "bats"
31
+ },
32
+ {
33
+ "match": "app/hooks/user-prompt-submit.sh",
34
+ "tests": ["tests/test_hooks.bats"],
35
+ "runner": "bats"
36
+ },
37
+ {
38
+ "match": "app/hooks/*.sh",
39
+ "tests": ["tests/test_hooks.bats", "tests/test_hooks_per_editor.bats"],
40
+ "runner": "bats"
41
+ },
42
+ {
43
+ "match": "app/hooks.json",
44
+ "tests": ["tests/test_hooks.bats", "tests/test_merge_hooks_statusline.bats", "tests/test_hooks_per_editor.bats"],
45
+ "runner": "bats"
46
+ },
47
+ {
48
+ "match": "scripts/merge-hooks.py",
49
+ "tests": ["tests/test_merge_hooks_statusline.bats", "tests/test_inject_hook.bats"],
50
+ "runner": "bats"
51
+ },
52
+ {
53
+ "match": "scripts/session_state.py",
54
+ "tests": ["tests/test_session_state.bats"],
55
+ "runner": "bats"
56
+ },
57
+ {
58
+ "match": "scripts/test_cohesion.py",
59
+ "tests": ["tests/test_test_cohesion_resolver.bats"],
60
+ "runner": "bats"
61
+ },
62
+ {
63
+ "match": "scripts/inject_hook_cli.py",
64
+ "tests": ["tests/test_inject_hook.bats"],
65
+ "runner": "bats"
66
+ },
67
+ {
68
+ "match": "scripts/inject_rule_cli.py",
69
+ "tests": ["tests/test_inject.bats"],
70
+ "runner": "bats"
71
+ },
72
+ {
73
+ "match": "scripts/validate.py",
74
+ "tests": ["tests/test_validate.bats", "tests/test_validate_negative.bats"],
75
+ "runner": "bats"
76
+ }
77
+ ]
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env bash
2
+ # test-cohesion.sh — Run related tests after edits, block if they fail.
3
+ #
4
+ # Fires on: PostToolUse
5
+ # Matcher: Edit|MultiEdit|Write
6
+ # Skipped when TOOLKIT_HOOK_PROFILE=minimal or CLAUDE_HOOK_BOOTSTRAP=1.
7
+ #
8
+ # Constitution Art. VI.3 ("Tests and Docs Follow Behavior"): when source code
9
+ # changes, the matching tests must pass before the agent can continue. This
10
+ # hook resolves a per-project test-cohesion map and runs only the related
11
+ # test files (NOT the full suite).
12
+ #
13
+ # Map lookup (first found wins):
14
+ # 1. $PWD/.claude/test-cohesion-map.json (project-local)
15
+ # 2. $TOOLKIT_DIR/app/hooks/test-cohesion-map.json (toolkit default)
16
+ #
17
+ # Escape hatches:
18
+ # CLAUDE_HOOK_BOOTSTRAP=1 bypass (used when editing the hook itself)
19
+ # CLAUDE_SKIP_COHESION=1 one-off bypass
20
+ #
21
+ # Exit codes:
22
+ # 0 no map / no match / tests passed
23
+ # 2 related test command failed
24
+
25
+ # shellcheck source=_profile-check.sh
26
+ source "$(dirname "$0")/_profile-check.sh"
27
+ # shellcheck source=_locate-toolkit.sh
28
+ source "$(dirname "$0")/_locate-toolkit.sh"
29
+ # shellcheck source=_hook-io.sh
30
+ source "$(dirname "$0")/_hook-io.sh"
31
+
32
+ [ "${CLAUDE_HOOK_BOOTSTRAP:-0}" = "1" ] && exit 0
33
+ [ "${CLAUDE_SKIP_COHESION:-0}" = "1" ] && exit 0
34
+
35
+ INPUT=$(cat)
36
+ FILE_PATH=$(hook_file_path)
37
+
38
+ [ -z "$FILE_PATH" ] && exit 0
39
+ [ -z "$TOOLKIT_DIR" ] && exit 0
40
+ command -v python3 >/dev/null 2>&1 || exit 0
41
+
42
+ REPO_ROOT="$PWD"
43
+ # Prefer git repo root when available; fall back to PWD.
44
+ if command -v git >/dev/null 2>&1; then
45
+ git_root=$(git rev-parse --show-toplevel 2>/dev/null)
46
+ [ -n "$git_root" ] && REPO_ROOT="$git_root"
47
+ fi
48
+
49
+ # Resolve symlinks on both sides so the cohesion-map glob can match files
50
+ # under symlinked roots (e.g. /tmp -> /private/tmp on macOS).
51
+ REAL_FILE=$(python3 -c "import os,sys; print(os.path.realpath(sys.argv[1]))" "$FILE_PATH" 2>/dev/null)
52
+ REAL_REPO=$(python3 -c "import os,sys; print(os.path.realpath(sys.argv[1]))" "$REPO_ROOT" 2>/dev/null)
53
+ [ -z "$REAL_FILE" ] && REAL_FILE="$FILE_PATH"
54
+ [ -z "$REAL_REPO" ] && REAL_REPO="$REPO_ROOT"
55
+
56
+ COMMANDS=$(python3 "$TOOLKIT_DIR/scripts/test_cohesion.py" resolve \
57
+ --changed-paths "$REAL_FILE" --repo-root "$REAL_REPO" 2>/dev/null)
58
+ [ -z "$COMMANDS" ] && exit 0
59
+
60
+ # Run each resolved command. Block on first failure.
61
+ LOG_DIR="$HOME/.softspark/ai-toolkit/state"
62
+ mkdir -p "$LOG_DIR" 2>/dev/null
63
+ LOG_FILE="$LOG_DIR/test-cohesion-last.log"
64
+
65
+ cd "$REAL_REPO" || exit 0
66
+ FAILED=""
67
+ while IFS= read -r cmd; do
68
+ [ -z "$cmd" ] && continue
69
+ if ! bash -c "$cmd" >"$LOG_FILE" 2>&1; then
70
+ FAILED="$cmd"
71
+ break
72
+ fi
73
+ done <<<"$COMMANDS"
74
+
75
+ if [ -n "$FAILED" ]; then
76
+ cat >&2 <<EOF
77
+ test-cohesion: BLOCKED — tests for ${FILE_PATH} failed.
78
+ Constitution Art. VI.3: tests must pass before continuing.
79
+
80
+ Command:
81
+ $FAILED
82
+
83
+ Last log (truncated):
84
+ $(tail -20 "$LOG_FILE")
85
+
86
+ Fix the failing test OR update it to match the new behavior (Art. VI.3).
87
+ Override (one-off): CLAUDE_SKIP_COHESION=1
88
+ EOF
89
+ exit 2
90
+ fi
91
+
92
+ [ "${AI_TOOLKIT_HOOK_FORMAT:-}" = "json" ] || echo "test-cohesion: related tests passed for ${FILE_PATH}"
93
+ exit 0
@@ -11,7 +11,9 @@ STATS_FILE="${HOME}/.softspark/ai-toolkit/stats.json"
11
11
 
12
12
  # Read prompt from stdin (Claude Code passes JSON with .prompt field)
13
13
  INPUT=$(cat)
14
- PROMPT_TEXT=$(echo "$INPUT" | jq -r '.prompt // empty' 2>/dev/null)
14
+ # shellcheck source=_hook-io.sh
15
+ source "$(dirname "$0")/_hook-io.sh"
16
+ PROMPT_TEXT=$(hook_prompt)
15
17
  [ -z "$PROMPT_TEXT" ] && exit 0
16
18
 
17
19
  # Only track if prompt starts with a slash command
@@ -7,21 +7,48 @@
7
7
 
8
8
  # shellcheck source=_profile-check.sh
9
9
  source "$(dirname "$0")/_profile-check.sh"
10
+ # shellcheck source=_hook-io.sh
11
+ source "$(dirname "$0")/_hook-io.sh"
12
+ # shellcheck source=_search-capability.sh
13
+ source "$(dirname "$0")/_search-capability.sh"
10
14
 
11
15
  # Read prompt from stdin (Claude Code passes JSON with .prompt field)
12
16
  INPUT=$(cat)
13
- PROMPT_TEXT=$(echo "$INPUT" | jq -r '.prompt // empty' 2>/dev/null)
17
+ PROMPT_TEXT=$(hook_prompt)
14
18
  LOWERED="$(printf '%s' "$PROMPT_TEXT" | tr '[:upper:]' '[:lower:]')"
15
19
 
16
- echo "STOP. Execute Step 0 before responding: check your CLAUDE.md for search-first rules. If search-first rules exist, call the required search tool NOW — before any other tool or text output. If you skip this step, the user will interrupt you."
20
+ # Heuristic search-required flag (paired with search-tracker.sh + Stop check).
21
+ # Set only when a search provider is detectable or strict mode is requested.
22
+ # Cleared by search-tracker.sh after smart_query / hybrid_search_kb / web runs.
23
+ PROMPT_LEN=${#PROMPT_TEXT}
24
+ STATE_DIR="$HOME/.softspark/ai-toolkit/state"
25
+ FLAG="$STATE_DIR/search-required.flag"
26
+ mkdir -p "$STATE_DIR" 2>/dev/null
27
+ if [ "$PROMPT_LEN" -gt 30 ] && \
28
+ [ "${CLAUDE_SKIP_SEARCH_FIRST:-0}" != "1" ] && \
29
+ ai_toolkit_has_search_provider; then
30
+ printf '%s\n%s\n' "$(date -u +%s)" "$PROMPT_TEXT" > "$FLAG" 2>/dev/null
31
+ else
32
+ rm -f "$FLAG" 2>/dev/null
33
+ fi
34
+
35
+ if ai_toolkit_has_search_provider; then
36
+ CONTEXT_MSG="STOP. Execute Step 0 before responding: check your CLAUDE.md for search-first rules. If search-first rules exist and a search tool is available, call the required search tool NOW — before any other tool or text output."
37
+ else
38
+ CONTEXT_MSG="Search-first note: no ai-toolkit search provider was detected, so search-first enforcement is advisory only. Use available local evidence; do not call unavailable RAG/MCP tools."
39
+ fi
17
40
 
18
41
  if printf '%s' "$LOWERED" | grep -Eq 'architecture|design|migration|deploy|rollback|refactor|plugin|workflow'; then
19
- echo "UserPromptSubmit: task looks architectural or multi-step. Use plan mode, define success criteria, and validate before marking done."
42
+ CONTEXT_MSG="$CONTEXT_MSG
43
+ UserPromptSubmit: task looks architectural or multi-step. Use plan mode, define success criteria, and validate before marking done."
20
44
  elif printf '%s' "$LOWERED" | grep -Eq 'bug|error|fail|failing|incident|outage|debug'; then
21
- echo "UserPromptSubmit: debugging request detected. Gather evidence first, then propose the smallest safe fix and targeted tests."
45
+ CONTEXT_MSG="$CONTEXT_MSG
46
+ UserPromptSubmit: debugging request detected. Gather evidence first, then propose the smallest safe fix and targeted tests."
22
47
  else
23
- echo "UserPromptSubmit: apply KB-first research, keep changes minimal, and update tests/docs when behavior changes."
48
+ CONTEXT_MSG="$CONTEXT_MSG
49
+ UserPromptSubmit: apply KB-first research, keep changes minimal, and update tests/docs when behavior changes."
24
50
  fi
25
51
 
26
- exit 0
52
+ hook_emit_context "$CONTEXT_MSG"
27
53
 
54
+ exit 0
package/app/hooks.json CHANGED
@@ -39,7 +39,7 @@
39
39
  "hooks": [
40
40
  {
41
41
  "type": "command",
42
- "command": "bash ~/.softspark/ai-toolkit/hooks/notify-waiting.sh"
42
+ "command": "\"$HOME/.softspark/ai-toolkit/hooks/notify-waiting.sh\""
43
43
  }
44
44
  ]
45
45
  }
@@ -84,6 +84,16 @@
84
84
  "command": "\"$HOME/.softspark/ai-toolkit/hooks/commit-quality.sh\""
85
85
  }
86
86
  ]
87
+ },
88
+ {
89
+ "_source": "ai-toolkit",
90
+ "matcher": "Bash",
91
+ "hooks": [
92
+ {
93
+ "type": "command",
94
+ "command": "\"$HOME/.softspark/ai-toolkit/hooks/revert-guard.sh\""
95
+ }
96
+ ]
87
97
  }
88
98
  ],
89
99
  "UserPromptSubmit": [
@@ -128,6 +138,26 @@
128
138
  "command": "\"$HOME/.softspark/ai-toolkit/hooks/governance-capture.sh\""
129
139
  }
130
140
  ]
141
+ },
142
+ {
143
+ "_source": "ai-toolkit",
144
+ "matcher": "Edit|MultiEdit|Write",
145
+ "hooks": [
146
+ {
147
+ "type": "command",
148
+ "command": "\"$HOME/.softspark/ai-toolkit/hooks/test-cohesion.sh\""
149
+ }
150
+ ]
151
+ },
152
+ {
153
+ "_source": "ai-toolkit",
154
+ "matcher": "mcp__rag-mcp__smart_query|mcp__rag-mcp__hybrid_search_kb|mcp__rag-mcp__crag_search|mcp__rag-mcp__multi_hop_search|mcp__rag-mcp__verify_answer|WebSearch|WebFetch",
155
+ "hooks": [
156
+ {
157
+ "type": "command",
158
+ "command": "\"$HOME/.softspark/ai-toolkit/hooks/search-tracker.sh\""
159
+ }
160
+ ]
131
161
  }
132
162
  ],
133
163
  "Stop": [
@@ -160,6 +190,16 @@
160
190
  "command": "\"$HOME/.softspark/ai-toolkit/hooks/quality-gate.sh\""
161
191
  }
162
192
  ]
193
+ },
194
+ {
195
+ "_source": "ai-toolkit",
196
+ "matcher": "",
197
+ "hooks": [
198
+ {
199
+ "type": "command",
200
+ "command": "\"$HOME/.softspark/ai-toolkit/hooks/stop-search-check.sh\""
201
+ }
202
+ ]
163
203
  }
164
204
  ],
165
205
  "TaskCompleted": [
@@ -243,6 +283,30 @@
243
283
  }
244
284
  ]
245
285
  }
286
+ ],
287
+ "InstructionsLoaded": [
288
+ {
289
+ "_source": "ai-toolkit",
290
+ "matcher": "",
291
+ "hooks": [
292
+ {
293
+ "type": "command",
294
+ "command": "\"$HOME/.softspark/ai-toolkit/hooks/instructions-audit.sh\""
295
+ }
296
+ ]
297
+ }
298
+ ],
299
+ "ConfigChange": [
300
+ {
301
+ "_source": "ai-toolkit",
302
+ "matcher": "user_settings",
303
+ "hooks": [
304
+ {
305
+ "type": "command",
306
+ "command": "\"$HOME/.softspark/ai-toolkit/hooks/config-desync-guard.sh\""
307
+ }
308
+ ]
309
+ }
246
310
  ]
247
311
  },
248
312
  "statusLine": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "last_run": "2026-05-12T10:01:38Z",
2
+ "last_run": "2026-05-12T19:04:22Z",
3
3
  "schema_version": 1,
4
4
  "tools": {
5
5
  "aider": {
@@ -24,7 +24,7 @@
24
24
  }
25
25
  },
26
26
  "augment": {
27
- "docs_hash": "b746d5dbbc5a2936",
27
+ "docs_hash": "ceaf27b7126f44dd",
28
28
  "headings": [
29
29
  "Agent",
30
30
  "Chat",
@@ -57,7 +57,7 @@
57
57
  }
58
58
  },
59
59
  "claude-code": {
60
- "docs_hash": "9e0292a9f92392e0",
60
+ "docs_hash": "6ea2ec6ef023c1bd",
61
61
  "headings": [
62
62
  "Claude Code overview",
63
63
  "Documentation Index",
@@ -137,7 +137,7 @@
137
137
  }
138
138
  },
139
139
  "codex-cli": {
140
- "docs_hash": "3f963da56d00add9",
140
+ "docs_hash": "c8fb328bf1954fc2",
141
141
  "headings": [
142
142
  "About",
143
143
  "Contributing",
@@ -189,7 +189,7 @@
189
189
  "version": "codex-cli 0.130.0"
190
190
  },
191
191
  "cursor": {
192
- "docs_hash": "705adfd617433088",
192
+ "docs_hash": "787203a4eee076f7",
193
193
  "headings": [],
194
194
  "markers": {
195
195
  ".cursor/rules": false,
@@ -205,7 +205,7 @@
205
205
  }
206
206
  },
207
207
  "gemini-cli": {
208
- "docs_hash": "93694630a3d04327",
208
+ "docs_hash": "26555623f610ff8e",
209
209
  "headings": [
210
210
  "Breadcrumbs",
211
211
  "Directory actions",
@@ -244,7 +244,7 @@
244
244
  }
245
245
  },
246
246
  "github-copilot": {
247
- "docs_hash": "d9b9fcdfe3b67469",
247
+ "docs_hash": "1566c05f84f5c1a7",
248
248
  "headings": [
249
249
  "About Copilot auto model selection",
250
250
  "About Copilot integrations",
@@ -353,7 +353,7 @@
353
353
  }
354
354
  },
355
355
  "windsurf": {
356
- "docs_hash": "66295add1d0f0645",
356
+ "docs_hash": "7a8a7569f81eba1b",
357
357
  "headings": [
358
358
  "Advanced",
359
359
  "App Deploys",
@@ -144,7 +144,7 @@ ai-toolkit doctor
144
144
  - Environment: node, bash, python3, bats
145
145
  - Global Install: .claude exists, agents/skills symlinks (0 broken), settings.json hooks
146
146
  - Hook Scripts: all present and executable
147
- - Hook Configuration: 12 events registered
147
+ - Hook Configuration: 14 events registered
148
148
  - Generated Artifacts: AGENTS.md, llms.txt, llms-full.txt
149
149
  - Planned Assets: plugin.json, benchmarks, plugin packs
150
150
  - Benchmark Freshness: < 30 days
@@ -194,7 +194,7 @@ python3 scripts/audit_skills.py --ci
194
194
 
195
195
  **Verify validate.py:**
196
196
  - [ ] Agents >= 44, Skills >= 99, Tests >= 900
197
- - [ ] Hook events: 12, Hook scripts: >= 20
197
+ - [ ] Hook events: 14, Hook scripts: >= 30
198
198
  - [ ] Plugin packs >= 10, KB documents >= 20
199
199
  - [ ] `Errors: 0 | Warnings: 0` → `VALIDATION PASSED`
200
200
 
@@ -264,7 +264,7 @@ Agents (code-reviewer, debugger, devops-implementer, ...)
264
264
 
265
265
  ## Quality Hooks
266
266
 
267
- 22 entries across 12 lifecycle events. See [hooks-catalog.md](hooks-catalog.md) for full details.
267
+ 28 entries across 14 lifecycle events. See [hooks-catalog.md](hooks-catalog.md) for full details.
268
268
 
269
269
  | Hook | Trigger | Script | Action |
270
270
  |------|---------|--------|--------|