@softspark/ai-toolkit 4.3.1 → 4.3.2
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.
- package/CHANGELOG.md +34 -0
- package/README.md +10 -9
- package/app/.claude-plugin/plugin.json +1 -1
- package/app/ARCHITECTURE.md +6 -1
- package/app/hooks/_hook-io.sh +3 -0
- package/app/hooks/_search-capability.sh +52 -4
- package/app/hooks/mcp-health.sh +3 -1
- package/app/hooks/search-tracker.sh +1 -1
- package/app/hooks/session-start.sh +18 -12
- package/app/hooks/stop-search-check.sh +53 -0
- package/app/hooks.json +2 -2
- package/benchmarks/ecosystem-doctor-snapshot.json +42 -37
- package/kb/procedures/release-verification-sop.md +7 -5
- package/kb/reference/codex-cli-compatibility.md +11 -2
- package/kb/reference/hooks-catalog.md +20 -7
- package/llms-full.txt +38 -14
- package/manifest.json +2 -2
- package/package.json +1 -1
- package/scripts/generate_codex_hooks.py +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,40 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## v4.3.2 - quiet hooks and no-RAG search-first hardening (2026-05-21)
|
|
11
|
+
|
|
12
|
+
Patch release. Fixes noisy lifecycle hook output in Codex and Claude prompt-submit flows while preserving search-first enforcement and blocking decisions.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **Quiet Codex startup hooks** - `scripts/generate_codex_hooks.py` now prefixes generated Codex hook commands with `AI_TOOLKIT_HOOK_QUIET=1`, preventing informational `SessionStart` and prompt reminders from appearing as visible hook context.
|
|
17
|
+
- **Quiet Claude prompt-submit hook** - `app/hooks.json` now installs `user-prompt-submit.sh` with `AI_TOOLKIT_HOOK_QUIET=1`, suppressing non-blocking prompt governance output while still arming the per-session search-first flag.
|
|
18
|
+
- **No-RAG false positives** - `_search-capability.sh` now detects search providers from actual MCP server definitions only, so hook matchers and permission allowlists no longer make no-RAG installs block incorrectly.
|
|
19
|
+
- **Codex search tracking gap** - `stop-search-check.sh` now checks the Codex TUI log for search tool calls after the search-first flag timestamp before blocking, covering Codex MCP calls that do not fire the shared `PostToolUse` tracker.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- **Hook output helper** - `_hook-io.sh` supports `AI_TOOLKIT_HOOK_QUIET=1` for non-blocking context output.
|
|
24
|
+
- **Runtime hook docs** - `kb/reference/hooks-catalog.md` and `kb/reference/codex-cli-compatibility.md` document quiet mode and the Codex search-first fallback.
|
|
25
|
+
- **Manifest hook count** - `manifest.json` now describes the current 28 hook entries across 14 lifecycle events.
|
|
26
|
+
|
|
27
|
+
### Tests
|
|
28
|
+
|
|
29
|
+
- **Hook quiet-mode coverage** - `tests/test_hooks.bats`, `tests/test_codex.bats`, and `tests/test_install.bats` cover quiet mode for `SessionStart`, `UserPromptSubmit`, `PostToolUse`, Codex hook generation, and installed Claude hook configuration.
|
|
30
|
+
- **Search-first flow coverage** - `tests/test_search_first_flow.bats` covers real-provider detection, no-RAG behavior, custom `customer-rag` style providers, and Codex log fallback.
|
|
31
|
+
|
|
32
|
+
### Ecosystem
|
|
33
|
+
|
|
34
|
+
- **Snapshot refresh** - `benchmarks/ecosystem-doctor-snapshot.json` refreshed after class A/C upstream documentation and version drift review. No generator contract changes were required.
|
|
35
|
+
|
|
36
|
+
### Verification
|
|
37
|
+
|
|
38
|
+
- `npm test` - 1142 passing.
|
|
39
|
+
- `python3 scripts/validate.py --strict` - passed.
|
|
40
|
+
- `python3 scripts/audit_skills.py --ci` - passed with 0 HIGH / 0 WARN.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
10
44
|
## v4.3.1 - per-session search-first flag (2026-05-19)
|
|
11
45
|
|
|
12
46
|
Patch release. Fixes a cross-session race condition in the search-first enforcement trio (`user-prompt-submit.sh` + `search-tracker.sh` + `stop-search-check.sh`): the single global flag file `~/.softspark/ai-toolkit/state/search-required.flag` was shared by every parallel Claude Code window, so a Stop in session B could consume session A's flag (or vice versa), blocking unrelated turns with someone else's prompt. Also unblocks `bats 1.13` regression in the test-cohesion runner default.
|
package/README.md
CHANGED
|
@@ -6,16 +6,17 @@
|
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
[](app/skills/)
|
|
8
8
|
[](app/agents/)
|
|
9
|
-
[](tests/)
|
|
10
10
|
|
|
11
|
-
## What's New in v4.3.
|
|
11
|
+
## What's New in v4.3.2
|
|
12
12
|
|
|
13
|
-
Patch release. Fixes
|
|
13
|
+
Patch release. Fixes noisy hook output and hardens search-first behavior across Claude and Codex.
|
|
14
14
|
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
-
|
|
18
|
-
- **
|
|
15
|
+
- **Quiet hook context**: Codex startup hooks and Claude/Codex `UserPromptSubmit` now suppress non-blocking reminder output while preserving side effects and blocking decisions.
|
|
16
|
+
- **Search provider detection**: search-first hooks now count only real MCP server definitions, not hook matchers or permission allowlists, so no-RAG installs stay advisory instead of blocked.
|
|
17
|
+
- **Codex search-first fallback**: `stop-search-check.sh` recognizes search calls recorded in the Codex TUI log when MCP tool calls do not trigger `PostToolUse`.
|
|
18
|
+
- **Runtime refresh coverage**: installer tests now assert that Claude `UserPromptSubmit` is installed with quiet mode enabled.
|
|
19
|
+
- **Ecosystem snapshot refresh**: release prep refreshed upstream editor/tool drift baselines after class A/C documentation and version drift review.
|
|
19
20
|
|
|
20
21
|
See [CHANGELOG.md](CHANGELOG.md) for full history.
|
|
21
22
|
|
|
@@ -125,7 +126,7 @@ See [CLI Reference](kb/reference/cli-reference.md) for all commands and options.
|
|
|
125
126
|
| `skills/` (hybrid) | 30 | Slash commands with agent knowledge base |
|
|
126
127
|
| `skills/` (knowledge) | 45 | Domain knowledge auto-loaded by agents (includes 13 `<lang>-rules` skills) |
|
|
127
128
|
| `agents/` | 44 | Specialized agents across 10 categories |
|
|
128
|
-
| `hooks/` |
|
|
129
|
+
| `hooks/` | 28 entries / 14 events | Quality gates, path safety, prompt governance, session lifecycle |
|
|
129
130
|
| `plugins/` | 11 packs | Opt-in domain bundles (security, research, frontend, enterprise, 6 language packs) |
|
|
130
131
|
| `constitution.md` | 6 articles | Machine-enforced safety rules |
|
|
131
132
|
| `rules/` | auto-injected | Language-specific and custom rules injected into your configs |
|
|
@@ -148,7 +149,7 @@ ai-toolkit/
|
|
|
148
149
|
│ └── ARCHITECTURE.md # Full system design
|
|
149
150
|
├── kb/ # Reference docs, procedures, plans
|
|
150
151
|
├── scripts/ # Validation, install, evaluation scripts
|
|
151
|
-
├── tests/ # Bats test suite (
|
|
152
|
+
├── tests/ # Bats test suite (1142 tests)
|
|
152
153
|
└── CHANGELOG.md
|
|
153
154
|
```
|
|
154
155
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-toolkit",
|
|
3
3
|
"description": "Professional-grade Claude Code toolkit with persona presets, skill security auditor, expanded lifecycle hooks, experimental opt-in plugin packs, benchmark harvesting, and multi-tool support.",
|
|
4
|
-
"version": "4.3.
|
|
4
|
+
"version": "4.3.2",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "SoftSpark",
|
|
7
7
|
"url": "https://github.com/softspark"
|
package/app/ARCHITECTURE.md
CHANGED
|
@@ -8,7 +8,7 @@ Universal multi-agent system for software development. Works across all reposito
|
|
|
8
8
|
|-----------|-------|
|
|
9
9
|
| Agents | See agents catalog |
|
|
10
10
|
| Skills | See skills catalog |
|
|
11
|
-
| Hooks |
|
|
11
|
+
| Hooks | 14 events / 28 entries (SessionStart ×3, Notification ×1, PreToolUse ×5, UserPromptSubmit ×2, PostToolUse ×4, Stop ×4, TaskCompleted ×1, TeammateIdle ×1, SubagentStart ×1, SubagentStop ×1, PreCompact ×2, SessionEnd ×1, InstructionsLoaded ×1, ConfigChange ×1) |
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -332,7 +332,9 @@ Lead Session (You)
|
|
|
332
332
|
| `UserPromptSubmit` | Before prompt execution | Prompt governance reminder |
|
|
333
333
|
| `UserPromptSubmit` | Before prompt execution | Usage tracking (skill invocations) |
|
|
334
334
|
| `PostToolUse` | After edit/write tools | Lightweight validation reminder |
|
|
335
|
+
| `PostToolUse` | After search tools | Clears the per-session search-first flag |
|
|
335
336
|
| `Stop` | After Claude response | Multi-language quality check + saves session context |
|
|
337
|
+
| `Stop` | After Claude response | Blocks skipped search-first only when a real MCP/Web search provider is configured, with Codex log fallback |
|
|
336
338
|
| `TaskCompleted` | Teammate marks task done | Multi-language lint + type check (blocking) |
|
|
337
339
|
| `TeammateIdle` | Teammate goes idle | Reminds to verify completeness |
|
|
338
340
|
| `SubagentStart` | Subagent starts | Scope reminder for spawned subagents |
|
|
@@ -353,6 +355,9 @@ Lead Session (You)
|
|
|
353
355
|
|
|
354
356
|
Language rules are propagated to **all configured editors** — not just Claude. `dir_rules_shared.build_language_rules()` reads `app/rules/<lang>/*.md`, strips frontmatter, and returns combined content per language. Each directory-based generator (Cursor, Windsurf, Cline, Roo, Augment, Antigravity, Codex) emits `ai-toolkit-lang-<lang>` files in its native format. Registered custom rules (`~/.softspark/ai-toolkit/rules/`) are similarly propagated as `ai-toolkit-custom-<name>` files via `build_registered_rules()`.
|
|
355
357
|
|
|
358
|
+
### Codex Integration
|
|
359
|
+
Codex receives `AGENTS.md`, `.agents/rules/*.md`, optional `.agents/skills/*`, and `.codex/hooks.json`. `generate_codex_hooks.py` emits only Codex-supported lifecycle events and prefixes commands with `AI_TOOLKIT_HOOK_QUIET=1`, so informational hook context is not shown at session start or prompt submit while side effects and blocking Stop decisions still run. Claude's bundled `UserPromptSubmit` hook also runs in quiet mode for the same reason.
|
|
360
|
+
|
|
356
361
|
### opencode Integration (v2.5.0+)
|
|
357
362
|
opencode is the 11th supported editor. Five generators handle its integration surface:
|
|
358
363
|
|
package/app/hooks/_hook-io.sh
CHANGED
|
@@ -58,6 +58,9 @@ hook_new_content() {
|
|
|
58
58
|
|
|
59
59
|
hook_emit_context() {
|
|
60
60
|
local message="$1"
|
|
61
|
+
if [ "${AI_TOOLKIT_HOOK_QUIET:-0}" = "1" ]; then
|
|
62
|
+
return 0
|
|
63
|
+
fi
|
|
61
64
|
if [ "${AI_TOOLKIT_HOOK_FORMAT:-}" = "json" ]; then
|
|
62
65
|
jq -nc --arg msg "$message" \
|
|
63
66
|
'{"hookSpecificOutput":{"additionalContext":$msg},"suppressOutput":true}'
|
|
@@ -24,23 +24,71 @@ ai_toolkit_has_search_provider() {
|
|
|
24
24
|
[ -n "${AI_TOOLKIT_SEARCH_PROVIDER:-}" ] && return 0
|
|
25
25
|
[ -n "${CLAUDE_SEARCH_PROVIDER:-}" ] && return 0
|
|
26
26
|
|
|
27
|
-
local
|
|
27
|
+
local json_candidates=(
|
|
28
28
|
"$PWD/.mcp.json"
|
|
29
|
+
"$PWD/.claude/settings.local.json"
|
|
30
|
+
"$PWD/.claude/settings.json"
|
|
29
31
|
"$PWD/.claude/mcp.json"
|
|
30
32
|
"$PWD/.cursor/mcp.json"
|
|
31
33
|
"$PWD/.gemini/settings.json"
|
|
34
|
+
"$HOME/.mcp.json"
|
|
32
35
|
"$HOME/.claude.json"
|
|
33
36
|
"$HOME/.claude/settings.json"
|
|
34
|
-
"$HOME/.
|
|
37
|
+
"$HOME/.cursor/mcp.json"
|
|
35
38
|
"$HOME/.gemini/settings.json"
|
|
36
39
|
)
|
|
37
40
|
local path
|
|
38
|
-
for path in "${
|
|
41
|
+
for path in "${json_candidates[@]}"; do
|
|
42
|
+
[ -f "$path" ] || continue
|
|
43
|
+
if ai_toolkit_json_has_search_provider "$path"; then
|
|
44
|
+
return 0
|
|
45
|
+
fi
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
local toml_candidates=(
|
|
49
|
+
"$HOME/.codex/config.toml"
|
|
50
|
+
)
|
|
51
|
+
for path in "${toml_candidates[@]}"; do
|
|
39
52
|
[ -f "$path" ] || continue
|
|
40
|
-
if
|
|
53
|
+
if ai_toolkit_toml_has_search_provider "$path"; then
|
|
41
54
|
return 0
|
|
42
55
|
fi
|
|
43
56
|
done
|
|
44
57
|
|
|
45
58
|
return 1
|
|
46
59
|
}
|
|
60
|
+
|
|
61
|
+
ai_toolkit_search_provider_pattern() {
|
|
62
|
+
printf '%s\n' '(^|[^[:alnum:]])(([^[:space:]]*[-_])?rag([-_][^[:space:]]*)?|rag[-_]?mcp|web[-_]?search|search)([^[:alnum:]]|$)'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
ai_toolkit_json_has_search_provider() {
|
|
66
|
+
local path="$1"
|
|
67
|
+
local pattern
|
|
68
|
+
pattern="$(ai_toolkit_search_provider_pattern)"
|
|
69
|
+
|
|
70
|
+
command -v jq >/dev/null 2>&1 || return 1
|
|
71
|
+
jq -er '
|
|
72
|
+
[
|
|
73
|
+
(.mcpServers // {} | to_entries[]? | .key),
|
|
74
|
+
(.mcp_servers // {} | to_entries[]? | .key),
|
|
75
|
+
(.mcp // {} | to_entries[]? | .key)
|
|
76
|
+
] | join(" ")
|
|
77
|
+
' "$path" 2>/dev/null | grep -Eiq "$pattern"
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
ai_toolkit_toml_has_search_provider() {
|
|
81
|
+
local path="$1"
|
|
82
|
+
local pattern
|
|
83
|
+
pattern="$(ai_toolkit_search_provider_pattern)"
|
|
84
|
+
|
|
85
|
+
awk '
|
|
86
|
+
/^\[mcp_servers[."]/ {
|
|
87
|
+
line=$0
|
|
88
|
+
sub(/^\[mcp_servers[."]?/, "", line)
|
|
89
|
+
sub(/"\]$/, "", line)
|
|
90
|
+
sub(/\]$/, "", line)
|
|
91
|
+
print line
|
|
92
|
+
}
|
|
93
|
+
' "$path" 2>/dev/null | grep -Eiq "$pattern"
|
|
94
|
+
}
|
package/app/hooks/mcp-health.sh
CHANGED
|
@@ -45,7 +45,9 @@ for CONFIG in "${CONFIG_FILES[@]}"; do
|
|
|
45
45
|
[ -z "$CMD" ] && continue
|
|
46
46
|
if ! command -v "$CMD" >/dev/null 2>&1; then
|
|
47
47
|
HINT=$(get_install_hint "$CMD")
|
|
48
|
-
|
|
48
|
+
if [ "${AI_TOOLKIT_HOOK_QUIET:-0}" != "1" ]; then
|
|
49
|
+
echo "MCP health: ${SERVER_NAME} command not found (${CMD}). Install with: ${HINT}"
|
|
50
|
+
fi
|
|
49
51
|
fi
|
|
50
52
|
done <<< "$SERVERS"
|
|
51
53
|
done
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# search-tracker.sh — Clear search-required flag when a search tool runs.
|
|
3
3
|
#
|
|
4
4
|
# Fires on: PostToolUse
|
|
5
|
-
# Matcher:
|
|
5
|
+
# Matcher: mcp__.*__(smart_query|hybrid_search_kb|crag_search|multi_hop_search|verify_answer)|WebSearch|WebFetch
|
|
6
6
|
# Non-blocking: always exits 0.
|
|
7
7
|
# Skipped when TOOLKIT_HOOK_PROFILE=minimal.
|
|
8
8
|
#
|
|
@@ -4,13 +4,19 @@
|
|
|
4
4
|
# Fires on: SessionStart (startup|compact)
|
|
5
5
|
# Output goes to Claude's context as plain text.
|
|
6
6
|
|
|
7
|
-
# 1. Mandatory rules reminder
|
|
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
|
-
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
|
-
|
|
11
7
|
# shellcheck source=_locate-toolkit.sh
|
|
12
8
|
source "$(dirname "$0")/_locate-toolkit.sh"
|
|
13
9
|
|
|
10
|
+
emit_context() {
|
|
11
|
+
if [ "${AI_TOOLKIT_HOOK_QUIET:-0}" != "1" ]; then
|
|
12
|
+
printf '%s\n' "$1"
|
|
13
|
+
fi
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
# 1. Mandatory rules reminder
|
|
17
|
+
emit_context "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."
|
|
18
|
+
emit_context "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."
|
|
19
|
+
|
|
14
20
|
# 1a. Reset per-session edit state (used by revert-guard, test-cohesion, quality-gate)
|
|
15
21
|
SESSION_ID_INPUT=""
|
|
16
22
|
if [ ! -t 0 ]; then
|
|
@@ -29,7 +35,7 @@ find "$HOME/.softspark/ai-toolkit/state" -maxdepth 1 -name 'search-required-*.fl
|
|
|
29
35
|
# 2. Check for updates (cached, max once per 24h, non-blocking)
|
|
30
36
|
VERSION_MSG=$(python3 "$TOOLKIT_DIR/scripts/version_check.py" 2>/dev/null)
|
|
31
37
|
if [ -n "$VERSION_MSG" ]; then
|
|
32
|
-
|
|
38
|
+
emit_context "$VERSION_MSG"
|
|
33
39
|
# Strip shell/AppleScript/PowerShell metacharacters before interpolating into
|
|
34
40
|
# notification commands. VERSION_MSG is version_check.py output which should
|
|
35
41
|
# be plain ASCII, but sanitize anyway as defense in depth.
|
|
@@ -46,20 +52,20 @@ fi
|
|
|
46
52
|
|
|
47
53
|
# 3. Load session context (if available)
|
|
48
54
|
SESSION_FILE=".claude/session-context.md"
|
|
49
|
-
if [ -f "$SESSION_FILE" ]; then
|
|
50
|
-
|
|
55
|
+
if [ -f "$SESSION_FILE" ] && [ "${AI_TOOLKIT_HOOK_QUIET:-0}" != "1" ]; then
|
|
56
|
+
printf '%s\n' "=== Session Context ==="
|
|
51
57
|
cat "$SESSION_FILE"
|
|
52
|
-
|
|
58
|
+
printf '%s\n' "====================="
|
|
53
59
|
fi
|
|
54
60
|
|
|
55
61
|
# 3. Load active instincts (if any)
|
|
56
62
|
INSTINCTS_DIR=".claude/instincts"
|
|
57
|
-
if [ -d "$INSTINCTS_DIR" ] && ls "$INSTINCTS_DIR"/*.md >/dev/null 2>&1; then
|
|
58
|
-
|
|
63
|
+
if [ -d "$INSTINCTS_DIR" ] && [ "${AI_TOOLKIT_HOOK_QUIET:-0}" != "1" ] && ls "$INSTINCTS_DIR"/*.md >/dev/null 2>&1; then
|
|
64
|
+
printf '%s\n' "=== Active Instincts ==="
|
|
59
65
|
for f in "$INSTINCTS_DIR"/*.md; do
|
|
60
|
-
|
|
66
|
+
printf '%s\n' "- $(head -1 "$f")"
|
|
61
67
|
done
|
|
62
|
-
|
|
68
|
+
printf '%s\n' "========================"
|
|
63
69
|
fi
|
|
64
70
|
|
|
65
71
|
exit 0
|
|
@@ -31,6 +31,59 @@ if ! ai_toolkit_has_search_provider; then
|
|
|
31
31
|
exit 0
|
|
32
32
|
fi
|
|
33
33
|
|
|
34
|
+
ai_toolkit_codex_log_has_search_since() {
|
|
35
|
+
local flag="$1"
|
|
36
|
+
local log="$HOME/.codex/log/codex-tui.log"
|
|
37
|
+
[ -f "$log" ] || return 1
|
|
38
|
+
|
|
39
|
+
python3 - "$flag" "$log" <<'PY' 2>/dev/null
|
|
40
|
+
import re
|
|
41
|
+
import sys
|
|
42
|
+
from datetime import datetime
|
|
43
|
+
from pathlib import Path
|
|
44
|
+
|
|
45
|
+
flag_path = Path(sys.argv[1])
|
|
46
|
+
log_path = Path(sys.argv[2])
|
|
47
|
+
try:
|
|
48
|
+
since = int(flag_path.read_text(encoding="utf-8").splitlines()[0])
|
|
49
|
+
except (OSError, ValueError, IndexError):
|
|
50
|
+
sys.exit(1)
|
|
51
|
+
|
|
52
|
+
tool_pattern = re.compile(
|
|
53
|
+
r"(ToolCall: (mcp__[^ ]*__(smart_query|hybrid_search_kb|crag_search|multi_hop_search|verify_answer)|web_(search|fetch))|"
|
|
54
|
+
r'tool\.name="(smart_query|hybrid_search_kb|crag_search|multi_hop_search|verify_answer)")',
|
|
55
|
+
re.IGNORECASE,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
with log_path.open("rb") as handle:
|
|
60
|
+
handle.seek(0, 2)
|
|
61
|
+
size = handle.tell()
|
|
62
|
+
handle.seek(max(0, size - 2_000_000))
|
|
63
|
+
lines = handle.read().decode("utf-8", errors="replace").splitlines()
|
|
64
|
+
except OSError:
|
|
65
|
+
sys.exit(1)
|
|
66
|
+
|
|
67
|
+
for line in lines:
|
|
68
|
+
if not tool_pattern.search(line):
|
|
69
|
+
continue
|
|
70
|
+
raw_ts = line.split(" ", 1)[0]
|
|
71
|
+
try:
|
|
72
|
+
ts = datetime.fromisoformat(raw_ts.replace("Z", "+00:00")).timestamp()
|
|
73
|
+
except ValueError:
|
|
74
|
+
continue
|
|
75
|
+
if ts >= since:
|
|
76
|
+
sys.exit(0)
|
|
77
|
+
|
|
78
|
+
sys.exit(1)
|
|
79
|
+
PY
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if ai_toolkit_codex_log_has_search_since "$FLAG"; then
|
|
83
|
+
rm -f "$FLAG" 2>/dev/null
|
|
84
|
+
exit 0
|
|
85
|
+
fi
|
|
86
|
+
|
|
34
87
|
# Read the original prompt (line 2 of the flag file) for the reminder.
|
|
35
88
|
PROMPT_PREVIEW=$(sed -n '2p' "$FLAG" 2>/dev/null | head -c 200)
|
|
36
89
|
rm -f "$FLAG" 2>/dev/null # one-shot; do not loop forever
|
package/app/hooks.json
CHANGED
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"hooks": [
|
|
104
104
|
{
|
|
105
105
|
"type": "command",
|
|
106
|
-
"command": "\"$HOME/.softspark/ai-toolkit/hooks/user-prompt-submit.sh\""
|
|
106
|
+
"command": "AI_TOOLKIT_HOOK_QUIET=1 \"$HOME/.softspark/ai-toolkit/hooks/user-prompt-submit.sh\""
|
|
107
107
|
}
|
|
108
108
|
]
|
|
109
109
|
},
|
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
},
|
|
152
152
|
{
|
|
153
153
|
"_source": "ai-toolkit",
|
|
154
|
-
"matcher": "
|
|
154
|
+
"matcher": "mcp__.*__(smart_query|hybrid_search_kb|crag_search|multi_hop_search|verify_answer)|WebSearch|WebFetch",
|
|
155
155
|
"hooks": [
|
|
156
156
|
{
|
|
157
157
|
"type": "command",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"last_run": "2026-05-
|
|
2
|
+
"last_run": "2026-05-21T08:27:23Z",
|
|
3
3
|
"schema_version": 1,
|
|
4
4
|
"tools": {
|
|
5
5
|
"aider": {
|
|
@@ -24,17 +24,18 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"augment": {
|
|
27
|
-
"docs_hash": "
|
|
27
|
+
"docs_hash": "8be23f4fb78816c3",
|
|
28
28
|
"headings": [
|
|
29
|
-
"
|
|
30
|
-
"Chat",
|
|
29
|
+
"Code",
|
|
31
30
|
"Documentation Index",
|
|
32
31
|
"Introduction",
|
|
32
|
+
"On this page",
|
|
33
|
+
"Other automation",
|
|
34
|
+
"Review",
|
|
33
35
|
"\u200bAuggie CLI",
|
|
34
|
-
"\
|
|
35
|
-
"\
|
|
36
|
-
"\
|
|
37
|
-
"\u200bVisual Studio Code"
|
|
36
|
+
"\u200bAutomate your SDLC",
|
|
37
|
+
"\u200bCosmos",
|
|
38
|
+
"\u200bGet started in minutes"
|
|
38
39
|
],
|
|
39
40
|
"markers": {
|
|
40
41
|
".augment": true,
|
|
@@ -57,10 +58,11 @@
|
|
|
57
58
|
}
|
|
58
59
|
},
|
|
59
60
|
"claude-code": {
|
|
60
|
-
"docs_hash": "
|
|
61
|
+
"docs_hash": "0518086bd4527849",
|
|
61
62
|
"headings": [
|
|
62
|
-
"Claude Code overview",
|
|
63
63
|
"Documentation Index",
|
|
64
|
+
"On this page",
|
|
65
|
+
"Overview",
|
|
64
66
|
"\u200bGet started",
|
|
65
67
|
"\u200bNext steps",
|
|
66
68
|
"\u200bUse Claude Code everywhere",
|
|
@@ -103,24 +105,27 @@
|
|
|
103
105
|
"slash command": true,
|
|
104
106
|
"sub-agent": true
|
|
105
107
|
},
|
|
106
|
-
"version": "2.1.
|
|
108
|
+
"version": "2.1.146 (Claude Code)"
|
|
107
109
|
},
|
|
108
110
|
"cline": {
|
|
109
|
-
"docs_hash": "
|
|
111
|
+
"docs_hash": "c2ce2443743365fa",
|
|
110
112
|
"headings": [
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
"Customization",
|
|
113
|
+
"API Reference",
|
|
114
|
+
"CLI",
|
|
115
|
+
"Cline Overview",
|
|
115
116
|
"Documentation Index",
|
|
116
|
-
"
|
|
117
|
-
"
|
|
118
|
-
"
|
|
119
|
-
"
|
|
120
|
-
"
|
|
121
|
-
"
|
|
122
|
-
"
|
|
123
|
-
"
|
|
117
|
+
"JetBrains Plugin",
|
|
118
|
+
"Kanban",
|
|
119
|
+
"Observability",
|
|
120
|
+
"On this page",
|
|
121
|
+
"SDK",
|
|
122
|
+
"Security & Governance",
|
|
123
|
+
"Team Management",
|
|
124
|
+
"VS Code Extension",
|
|
125
|
+
"\u200bAgent Core (SDK)",
|
|
126
|
+
"\u200bApplications",
|
|
127
|
+
"\u200bEnterprise Solutions",
|
|
128
|
+
"\u200bOther IDE Supports",
|
|
124
129
|
"\u200bWhat is Cline?"
|
|
125
130
|
],
|
|
126
131
|
"markers": {
|
|
@@ -137,7 +142,7 @@
|
|
|
137
142
|
}
|
|
138
143
|
},
|
|
139
144
|
"codex-cli": {
|
|
140
|
-
"docs_hash": "
|
|
145
|
+
"docs_hash": "70f184b6cdd1f765",
|
|
141
146
|
"headings": [
|
|
142
147
|
"About",
|
|
143
148
|
"Contributing",
|
|
@@ -156,7 +161,7 @@
|
|
|
156
161
|
"Packages 0",
|
|
157
162
|
"Provide feedback",
|
|
158
163
|
"Quickstart",
|
|
159
|
-
"Releases
|
|
164
|
+
"Releases 795",
|
|
160
165
|
"Repository files navigation",
|
|
161
166
|
"Resources",
|
|
162
167
|
"Saved searches",
|
|
@@ -186,10 +191,10 @@
|
|
|
186
191
|
"mcp_servers": false,
|
|
187
192
|
"sandbox": true
|
|
188
193
|
},
|
|
189
|
-
"version": "codex-cli 0.
|
|
194
|
+
"version": "codex-cli 0.132.0"
|
|
190
195
|
},
|
|
191
196
|
"cursor": {
|
|
192
|
-
"docs_hash": "
|
|
197
|
+
"docs_hash": "60edb30c9ddcdadd",
|
|
193
198
|
"headings": [],
|
|
194
199
|
"markers": {
|
|
195
200
|
".cursor/rules": false,
|
|
@@ -205,7 +210,7 @@
|
|
|
205
210
|
}
|
|
206
211
|
},
|
|
207
212
|
"gemini-cli": {
|
|
208
|
-
"docs_hash": "
|
|
213
|
+
"docs_hash": "c6aab087667f7717",
|
|
209
214
|
"headings": [
|
|
210
215
|
"Breadcrumbs",
|
|
211
216
|
"Directory actions",
|
|
@@ -244,16 +249,16 @@
|
|
|
244
249
|
}
|
|
245
250
|
},
|
|
246
251
|
"github-copilot": {
|
|
247
|
-
"docs_hash": "
|
|
252
|
+
"docs_hash": "ab274112faeec90f",
|
|
248
253
|
"headings": [
|
|
249
254
|
"About Copilot auto model selection",
|
|
250
255
|
"About Copilot integrations",
|
|
251
256
|
"About agent management",
|
|
252
257
|
"About agent skills",
|
|
253
|
-
"About agentic memory for GitHub Copilot",
|
|
254
258
|
"About billing for GitHub Copilot in organizations and enterprises",
|
|
255
259
|
"About billing for individual GitHub Copilot plans",
|
|
256
260
|
"About custom agents",
|
|
261
|
+
"About customizing GitHub Copilot responses",
|
|
257
262
|
"Articles",
|
|
258
263
|
"Did you find what you needed?",
|
|
259
264
|
"GitHub Copilot",
|
|
@@ -276,7 +281,7 @@
|
|
|
276
281
|
}
|
|
277
282
|
},
|
|
278
283
|
"google-antigravity": {
|
|
279
|
-
"docs_hash": "
|
|
284
|
+
"docs_hash": "2c48c702b9e592a7",
|
|
280
285
|
"headings": [],
|
|
281
286
|
"markers": {
|
|
282
287
|
"AGENTS.md": false,
|
|
@@ -292,7 +297,7 @@
|
|
|
292
297
|
}
|
|
293
298
|
},
|
|
294
299
|
"opencode": {
|
|
295
|
-
"docs_hash": "
|
|
300
|
+
"docs_hash": "5277673e235393f7",
|
|
296
301
|
"headings": [
|
|
297
302
|
"Add features",
|
|
298
303
|
"Ask questions",
|
|
@@ -331,20 +336,19 @@
|
|
|
331
336
|
}
|
|
332
337
|
},
|
|
333
338
|
"roo-code": {
|
|
334
|
-
"docs_hash": "
|
|
339
|
+
"docs_hash": "54908ae23136b646",
|
|
335
340
|
"headings": [
|
|
336
341
|
"A mindset for success\u200b",
|
|
337
342
|
"Additional Resources\u200b",
|
|
338
343
|
"Getting Started\u200b",
|
|
339
344
|
"Roo Code Docs",
|
|
340
|
-
"Tutorial & Feature Videos\u200b",
|
|
341
345
|
"What can Roo Code do for you\u200b"
|
|
342
346
|
],
|
|
343
347
|
"markers": {
|
|
344
348
|
"Code Actions": false,
|
|
345
349
|
"MCP": true,
|
|
346
350
|
"Orchestrator mode": false,
|
|
347
|
-
"custom modes":
|
|
351
|
+
"custom modes": false,
|
|
348
352
|
"description": true,
|
|
349
353
|
"groups": false,
|
|
350
354
|
"roleDefinition": false,
|
|
@@ -353,7 +357,7 @@
|
|
|
353
357
|
}
|
|
354
358
|
},
|
|
355
359
|
"windsurf": {
|
|
356
|
-
"docs_hash": "
|
|
360
|
+
"docs_hash": "fa29ba621f291d81",
|
|
357
361
|
"headings": [
|
|
358
362
|
"Advanced",
|
|
359
363
|
"App Deploys",
|
|
@@ -362,6 +366,7 @@
|
|
|
362
366
|
"Documentation Index",
|
|
363
367
|
"MCP",
|
|
364
368
|
"Memories",
|
|
369
|
+
"On this page",
|
|
365
370
|
"Recommended Plugins",
|
|
366
371
|
"Terminal",
|
|
367
372
|
"Usage",
|
|
@@ -3,10 +3,10 @@ title: "SOP: Release Verification"
|
|
|
3
3
|
category: procedures
|
|
4
4
|
service: ai-toolkit
|
|
5
5
|
tags: [sop, verification, release, smoke-test, install, update, qa, provenance, sarif]
|
|
6
|
-
version: "1.4.
|
|
6
|
+
version: "1.4.3"
|
|
7
7
|
created: "2026-04-08"
|
|
8
|
-
last_updated: "2026-
|
|
9
|
-
description: "End-to-end smoke test after installing or updating @softspark/ai-toolkit — verifies CLI, install, doctor, validation, tests, eject, npm provenance attestation, SARIF audit, and per-skill permissions. Reflects the v2.8.0 supply-chain standard. v1.3.0 added the single-run npm test discipline; v1.4.0 adds v3.0.0 deep-coverage checks (--profile full, --codex-skills, breaking-change surfaces, idempotence, registry drift, live-JSON parse) and refreshes stale thresholds. v1.4.2 makes the Phase 9.4 idempotence check deterministic by sorting file paths before hashing."
|
|
8
|
+
last_updated: "2026-05-19"
|
|
9
|
+
description: "End-to-end smoke test after installing or updating @softspark/ai-toolkit — verifies CLI, install, doctor, validation, tests, eject, npm provenance attestation, SARIF audit, and per-skill permissions. Reflects the v2.8.0 supply-chain standard. v1.3.0 added the single-run npm test discipline; v1.4.0 adds v3.0.0 deep-coverage checks (--profile full, --codex-skills, breaking-change surfaces, idempotence, registry drift, live-JSON parse) and refreshes stale thresholds. v1.4.2 makes the Phase 9.4 idempotence check deterministic by sorting file paths before hashing. v1.4.3 tightens the Phase 8.4 URL pin check so the success-message count includes only entries with a `url:` field, not local `path:` entries, and documents the `sources.json` envelope shape."
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# SOP: Release Verification
|
|
@@ -303,11 +303,13 @@ python3 scripts/audit_skills.py --permissions | head -40
|
|
|
303
303
|
|
|
304
304
|
### 8.4 URL-sourced rules/hooks are checksum-pinned
|
|
305
305
|
|
|
306
|
+
`sources.json` is an envelope of the form `{"schema_version": 1, "rules"|"hooks": {...}}`, so the jq filter must pick the nested map before piping into the pin assertion. The URL-count in the success message ignores local `path:`-only entries — only entries with a `url:` field are pinned and counted.
|
|
307
|
+
|
|
306
308
|
```bash
|
|
307
309
|
jq '.rules // .hooks // {}' ~/.softspark/ai-toolkit/rules/sources.json 2>/dev/null \
|
|
308
|
-
| python3 -c "import json, sys; d=json.load(sys.stdin) or {}; bad=[n for n,v in d.items() if v.get('url') and not v.get('sha256')]; assert not bad, f'UNPINNED: {bad}'; print(f'RULE PIN OK: {
|
|
310
|
+
| python3 -c "import json, sys; d=json.load(sys.stdin) or {}; bad=[n for n,v in d.items() if v.get('url') and not v.get('sha256')]; assert not bad, f'UNPINNED: {bad}'; url_n=sum(1 for v in d.values() if v.get('url')); print(f'RULE PIN OK: {url_n} URL rules, all with sha256')"
|
|
309
311
|
jq '.hooks // {}' ~/.softspark/ai-toolkit/hooks/external/sources.json 2>/dev/null \
|
|
310
|
-
| python3 -c "import json, sys; d=json.load(sys.stdin) or {}; bad=[n for n,v in d.items() if v.get('url') and not v.get('sha256')]; assert not bad, f'UNPINNED: {bad}'; print(f'HOOK PIN OK: {
|
|
312
|
+
| python3 -c "import json, sys; d=json.load(sys.stdin) or {}; bad=[n for n,v in d.items() if v.get('url') and not v.get('sha256')]; assert not bad, f'UNPINNED: {bad}'; url_n=sum(1 for v in d.values() if v.get('url')); print(f'HOOK PIN OK: {url_n} URL hooks, all with sha256')"
|
|
311
313
|
```
|
|
312
314
|
|
|
313
315
|
**Verify:**
|
|
@@ -3,9 +3,9 @@ title: "AI Toolkit - Codex CLI Compatibility"
|
|
|
3
3
|
category: reference
|
|
4
4
|
service: ai-toolkit
|
|
5
5
|
tags: [codex, compatibility, install, skills, hooks]
|
|
6
|
-
version: "1.0.
|
|
6
|
+
version: "1.0.1"
|
|
7
7
|
created: "2026-04-12"
|
|
8
|
-
last_updated: "2026-
|
|
8
|
+
last_updated: "2026-05-21"
|
|
9
9
|
description: "Reference for how ai-toolkit maps Claude-oriented skills, hooks, and plugin packs to Codex CLI."
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -110,6 +110,11 @@ This means Claude-only events such as `TaskCompleted`, `TeammateIdle`,
|
|
|
110
110
|
`~/.codex/hooks.json` (global layer). Non-Codex events are silently skipped.
|
|
111
111
|
`remove-hook` cleans both Claude and Codex targets.
|
|
112
112
|
|
|
113
|
+
Generated Codex hook commands include `AI_TOOLKIT_HOOK_QUIET=1`. This keeps
|
|
114
|
+
non-blocking reminders and startup context out of the visible Codex hook output
|
|
115
|
+
while preserving hook side effects and blocking decisions such as search-first
|
|
116
|
+
Stop enforcement.
|
|
117
|
+
|
|
113
118
|
## Behavioral Limits
|
|
114
119
|
|
|
115
120
|
Codex wrappers preserve workflow intent, but not every Claude runtime behavior
|
|
@@ -120,6 +125,10 @@ Known limits:
|
|
|
120
125
|
- No native Codex equivalent of tmux-backed Agent Teams lifecycle events
|
|
121
126
|
- No separate task object model equivalent to Claude `Task*` APIs
|
|
122
127
|
- Hook event coverage is narrower than Claude Code
|
|
128
|
+
- MCP search tool calls may not fire the shared `PostToolUse` search tracker,
|
|
129
|
+
so `stop-search-check.sh` also checks `~/.codex/log/codex-tui.log` for
|
|
130
|
+
`smart_query`, `hybrid_search_kb`, `crag_search`, `multi_hop_search`, and
|
|
131
|
+
`verify_answer` calls after the search-first flag timestamp before blocking.
|
|
123
132
|
|
|
124
133
|
These are runtime platform limits, not installation defects.
|
|
125
134
|
|
|
@@ -3,9 +3,9 @@ title: "Hooks Catalog"
|
|
|
3
3
|
category: reference
|
|
4
4
|
service: ai-toolkit
|
|
5
5
|
tags: [hooks, quality, safety, enforcement, settings.json]
|
|
6
|
-
version: "1.5.
|
|
6
|
+
version: "1.5.2"
|
|
7
7
|
created: "2026-03-27"
|
|
8
|
-
last_updated: "2026-05-
|
|
8
|
+
last_updated: "2026-05-21"
|
|
9
9
|
description: "Complete reference of all ai-toolkit hooks: events, scripts, installation, and runtime behavior."
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -50,6 +50,11 @@ ai-toolkit update # re-copies scripts, re-merges (idempotent)
|
|
|
50
50
|
3. Loads session context from `.claude/session-context.md` (if exists)
|
|
51
51
|
4. Loads active instincts from `.claude/instincts/*.md` (if any)
|
|
52
52
|
|
|
53
|
+
When `AI_TOOLKIT_HOOK_QUIET=1`, the hook still performs session-state reset,
|
|
54
|
+
stale search-flag cleanup, and update notification side effects, but suppresses
|
|
55
|
+
all informational stdout so runtimes such as Codex do not show startup hook
|
|
56
|
+
context in the UI.
|
|
57
|
+
|
|
53
58
|
### Notification — `notify-waiting.sh`
|
|
54
59
|
|
|
55
60
|
| Field | Value |
|
|
@@ -104,7 +109,10 @@ ai-toolkit update # re-copies scripts, re-merges (idempotent)
|
|
|
104
109
|
|
|
105
110
|
**Action:** Adds a lightweight governance reminder: plan mode for architectural work, evidence-first debugging, KB-first research, and validation expectations.
|
|
106
111
|
|
|
107
|
-
Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
|
|
112
|
+
Skipped when `TOOLKIT_HOOK_PROFILE=minimal`. The bundled `app/hooks.json`
|
|
113
|
+
registers this command with `AI_TOOLKIT_HOOK_QUIET=1`, so it still arms or
|
|
114
|
+
clears the per-session search-first flag but suppresses the informational
|
|
115
|
+
reminder output.
|
|
108
116
|
|
|
109
117
|
### UserPromptSubmit (usage tracking) — `track-usage.sh`
|
|
110
118
|
|
|
@@ -377,11 +385,11 @@ First-match-wins per file. Built-in runners: `bats`, `pytest`, `vitest`, `jest`.
|
|
|
377
385
|
| Field | Value |
|
|
378
386
|
|-------|-------|
|
|
379
387
|
| Event | `PostToolUse` |
|
|
380
|
-
| Matcher | `
|
|
388
|
+
| Matcher | `mcp__.*__(smart_query\|hybrid_search_kb\|crag_search\|multi_hop_search\|verify_answer)\|WebSearch\|WebFetch` |
|
|
381
389
|
| Script | `~/.softspark/ai-toolkit/hooks/search-tracker.sh` |
|
|
382
390
|
| Fires | After any search-style tool call |
|
|
383
391
|
|
|
384
|
-
**Action:** Clears `~/.softspark/ai-toolkit/state/search-required-<session_id>.flag` (per-session, keyed by `session_id` from the hook stdin payload, falling back to `transcript_path` basename, then `default`). Pairs with `user-prompt-submit.sh` (sets the flag on long technical prompts only when a search provider is detected or strict mode is enabled) and `stop-search-check.sh` (blocks Stop if the calling session's flag is still set). Together
|
|
392
|
+
**Action:** Clears `~/.softspark/ai-toolkit/state/search-required-<session_id>.flag` (per-session, keyed by `session_id` from the hook stdin payload, falling back to `transcript_path` basename, then `default`). Pairs with `user-prompt-submit.sh` (sets the flag on long technical prompts only when a search provider is detected or strict mode is enabled) and `stop-search-check.sh` (blocks Stop if the calling session's flag is still set). Search provider detection parses actual MCP server names from `mcpServers`, `mcp_servers`, or `mcp` config blocks; hook matchers and permission allowlists do not count as providers. Together the hooks enforce the global CLAUDE.md GOLDEN RULE without breaking offline/no-RAG installs and without cross-session interference when multiple Claude Code windows run in parallel.
|
|
385
393
|
|
|
386
394
|
Non-blocking (exit 0). Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
|
|
387
395
|
|
|
@@ -394,7 +402,7 @@ Non-blocking (exit 0). Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
|
|
|
394
402
|
| Script | `~/.softspark/ai-toolkit/hooks/stop-search-check.sh` |
|
|
395
403
|
| Fires | When Claude finishes a response |
|
|
396
404
|
|
|
397
|
-
**Action:** If `search-required-<session_id>.flag` for the calling session is still present (no search tool ran during this turn) and a search provider is still detectable, emits `{"decision":"block","reason":"..."}` to continue the conversation with a search-first reminder. If no RAG/Web provider is detected, it clears the stale flag and exits 0, so offline/no-MCP users are not blocked. Flags are scoped by `session_id` from the hook stdin payload so a Stop in session B never consumes session A's flag (and vice versa). Stale per-session flags older than 60 minutes are GC'd on the next `SessionStart`.
|
|
405
|
+
**Action:** If `search-required-<session_id>.flag` for the calling session is still present (no search tool ran during this turn) and a search provider is still detectable, emits `{"decision":"block","reason":"..."}` to continue the conversation with a search-first reminder. If no RAG/Web provider is detected, it clears the stale flag and exits 0, so offline/no-MCP users are not blocked. On Codex, where MCP search tools may not trigger the shared `PostToolUse` tracker, the hook also checks `~/.codex/log/codex-tui.log` for search tool calls after the flag timestamp before blocking. Flags are scoped by `session_id` from the hook stdin payload so a Stop in session B never consumes session A's flag (and vice versa). Stale per-session flags older than 60 minutes are GC'd on the next `SessionStart`.
|
|
398
406
|
|
|
399
407
|
**Overrides:** `CLAUDE_SKIP_SEARCH_FIRST=1`, `AI_TOOLKIT_SEARCH_FIRST=off`, or `AI_TOOLKIT_SEARCH_FIRST=strict` to force enforcement. Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
|
|
400
408
|
|
|
@@ -432,7 +440,7 @@ Non-blocking (exit 0). Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
|
|
|
432
440
|
| `scripts/test_cohesion.py` | Resolves changed paths → test commands via cohesion map. First-match-wins. Stdlib-only. |
|
|
433
441
|
| `app/hooks/test-cohesion-map.json` | Toolkit-default path → tests mapping (used when no project map exists). |
|
|
434
442
|
| `app/hooks/_locate-toolkit.sh` | Shared bash helper that exports `$TOOLKIT_DIR` for hooks needing scripts/. |
|
|
435
|
-
| `app/hooks/_hook-io.sh` | Shared bash helper that normalizes hook payloads across Claude, Augment, Gemini, Windsurf, and Cursor-style JSON. |
|
|
443
|
+
| `app/hooks/_hook-io.sh` | Shared bash helper that normalizes hook payloads across Claude, Augment, Gemini, Windsurf, and Cursor-style JSON. Honors `AI_TOOLKIT_HOOK_QUIET=1` for non-blocking context output. |
|
|
436
444
|
| `app/hooks/_search-capability.sh` | Shared bash helper that enables search-first blocking only when RAG/Web is configured or strict mode is requested. |
|
|
437
445
|
|
|
438
446
|
## Runtime Profiles
|
|
@@ -449,6 +457,11 @@ Set in `.claude/settings.local.json`:
|
|
|
449
457
|
| `standard` | All hooks (default) |
|
|
450
458
|
| `strict` | Standard + mypy --strict on task completion |
|
|
451
459
|
|
|
460
|
+
Set `AI_TOOLKIT_HOOK_QUIET=1` on hook commands to suppress non-blocking
|
|
461
|
+
informational context while preserving side effects and blocking decisions.
|
|
462
|
+
Codex-generated hooks use this mode by default, and Claude's bundled
|
|
463
|
+
`UserPromptSubmit` entry uses it to avoid visible prompt hook context.
|
|
464
|
+
|
|
452
465
|
## Architecture
|
|
453
466
|
|
|
454
467
|
```
|
package/llms-full.txt
CHANGED
|
@@ -5250,10 +5250,10 @@ title: "SOP: Release Verification"
|
|
|
5250
5250
|
category: procedures
|
|
5251
5251
|
service: ai-toolkit
|
|
5252
5252
|
tags: [sop, verification, release, smoke-test, install, update, qa, provenance, sarif]
|
|
5253
|
-
version: "1.4.
|
|
5253
|
+
version: "1.4.3"
|
|
5254
5254
|
created: "2026-04-08"
|
|
5255
|
-
last_updated: "2026-
|
|
5256
|
-
description: "End-to-end smoke test after installing or updating @softspark/ai-toolkit — verifies CLI, install, doctor, validation, tests, eject, npm provenance attestation, SARIF audit, and per-skill permissions. Reflects the v2.8.0 supply-chain standard. v1.3.0 added the single-run npm test discipline; v1.4.0 adds v3.0.0 deep-coverage checks (--profile full, --codex-skills, breaking-change surfaces, idempotence, registry drift, live-JSON parse) and refreshes stale thresholds. v1.4.2 makes the Phase 9.4 idempotence check deterministic by sorting file paths before hashing."
|
|
5255
|
+
last_updated: "2026-05-19"
|
|
5256
|
+
description: "End-to-end smoke test after installing or updating @softspark/ai-toolkit — verifies CLI, install, doctor, validation, tests, eject, npm provenance attestation, SARIF audit, and per-skill permissions. Reflects the v2.8.0 supply-chain standard. v1.3.0 added the single-run npm test discipline; v1.4.0 adds v3.0.0 deep-coverage checks (--profile full, --codex-skills, breaking-change surfaces, idempotence, registry drift, live-JSON parse) and refreshes stale thresholds. v1.4.2 makes the Phase 9.4 idempotence check deterministic by sorting file paths before hashing. v1.4.3 tightens the Phase 8.4 URL pin check so the success-message count includes only entries with a `url:` field, not local `path:` entries, and documents the `sources.json` envelope shape."
|
|
5257
5257
|
---
|
|
5258
5258
|
|
|
5259
5259
|
# SOP: Release Verification
|
|
@@ -5550,11 +5550,13 @@ python3 scripts/audit_skills.py --permissions | head -40
|
|
|
5550
5550
|
|
|
5551
5551
|
### 8.4 URL-sourced rules/hooks are checksum-pinned
|
|
5552
5552
|
|
|
5553
|
+
`sources.json` is an envelope of the form `{"schema_version": 1, "rules"|"hooks": {...}}`, so the jq filter must pick the nested map before piping into the pin assertion. The URL-count in the success message ignores local `path:`-only entries — only entries with a `url:` field are pinned and counted.
|
|
5554
|
+
|
|
5553
5555
|
```bash
|
|
5554
5556
|
jq '.rules // .hooks // {}' ~/.softspark/ai-toolkit/rules/sources.json 2>/dev/null \
|
|
5555
|
-
| python3 -c "import json, sys; d=json.load(sys.stdin) or {}; bad=[n for n,v in d.items() if v.get('url') and not v.get('sha256')]; assert not bad, f'UNPINNED: {bad}'; print(f'RULE PIN OK: {
|
|
5557
|
+
| python3 -c "import json, sys; d=json.load(sys.stdin) or {}; bad=[n for n,v in d.items() if v.get('url') and not v.get('sha256')]; assert not bad, f'UNPINNED: {bad}'; url_n=sum(1 for v in d.values() if v.get('url')); print(f'RULE PIN OK: {url_n} URL rules, all with sha256')"
|
|
5556
5558
|
jq '.hooks // {}' ~/.softspark/ai-toolkit/hooks/external/sources.json 2>/dev/null \
|
|
5557
|
-
| python3 -c "import json, sys; d=json.load(sys.stdin) or {}; bad=[n for n,v in d.items() if v.get('url') and not v.get('sha256')]; assert not bad, f'UNPINNED: {bad}'; print(f'HOOK PIN OK: {
|
|
5559
|
+
| python3 -c "import json, sys; d=json.load(sys.stdin) or {}; bad=[n for n,v in d.items() if v.get('url') and not v.get('sha256')]; assert not bad, f'UNPINNED: {bad}'; url_n=sum(1 for v in d.values() if v.get('url')); print(f'HOOK PIN OK: {url_n} URL hooks, all with sha256')"
|
|
5558
5560
|
```
|
|
5559
5561
|
|
|
5560
5562
|
**Verify:**
|
|
@@ -6926,9 +6928,9 @@ title: "AI Toolkit - Codex CLI Compatibility"
|
|
|
6926
6928
|
category: reference
|
|
6927
6929
|
service: ai-toolkit
|
|
6928
6930
|
tags: [codex, compatibility, install, skills, hooks]
|
|
6929
|
-
version: "1.0.
|
|
6931
|
+
version: "1.0.1"
|
|
6930
6932
|
created: "2026-04-12"
|
|
6931
|
-
last_updated: "2026-
|
|
6933
|
+
last_updated: "2026-05-21"
|
|
6932
6934
|
description: "Reference for how ai-toolkit maps Claude-oriented skills, hooks, and plugin packs to Codex CLI."
|
|
6933
6935
|
---
|
|
6934
6936
|
|
|
@@ -7033,6 +7035,11 @@ This means Claude-only events such as `TaskCompleted`, `TeammateIdle`,
|
|
|
7033
7035
|
`~/.codex/hooks.json` (global layer). Non-Codex events are silently skipped.
|
|
7034
7036
|
`remove-hook` cleans both Claude and Codex targets.
|
|
7035
7037
|
|
|
7038
|
+
Generated Codex hook commands include `AI_TOOLKIT_HOOK_QUIET=1`. This keeps
|
|
7039
|
+
non-blocking reminders and startup context out of the visible Codex hook output
|
|
7040
|
+
while preserving hook side effects and blocking decisions such as search-first
|
|
7041
|
+
Stop enforcement.
|
|
7042
|
+
|
|
7036
7043
|
## Behavioral Limits
|
|
7037
7044
|
|
|
7038
7045
|
Codex wrappers preserve workflow intent, but not every Claude runtime behavior
|
|
@@ -7043,6 +7050,10 @@ Known limits:
|
|
|
7043
7050
|
- No native Codex equivalent of tmux-backed Agent Teams lifecycle events
|
|
7044
7051
|
- No separate task object model equivalent to Claude `Task*` APIs
|
|
7045
7052
|
- Hook event coverage is narrower than Claude Code
|
|
7053
|
+
- MCP search tool calls may not fire the shared `PostToolUse` search tracker,
|
|
7054
|
+
so `stop-search-check.sh` also checks `~/.codex/log/codex-tui.log` for
|
|
7055
|
+
`smart_query`, `hybrid_search_kb`, `crag_search`, `multi_hop_search`, and
|
|
7056
|
+
`verify_answer` calls after the search-first flag timestamp before blocking.
|
|
7046
7057
|
|
|
7047
7058
|
These are runtime platform limits, not installation defects.
|
|
7048
7059
|
|
|
@@ -8824,9 +8835,9 @@ title: "Hooks Catalog"
|
|
|
8824
8835
|
category: reference
|
|
8825
8836
|
service: ai-toolkit
|
|
8826
8837
|
tags: [hooks, quality, safety, enforcement, settings.json]
|
|
8827
|
-
version: "1.5.
|
|
8838
|
+
version: "1.5.2"
|
|
8828
8839
|
created: "2026-03-27"
|
|
8829
|
-
last_updated: "2026-05-
|
|
8840
|
+
last_updated: "2026-05-21"
|
|
8830
8841
|
description: "Complete reference of all ai-toolkit hooks: events, scripts, installation, and runtime behavior."
|
|
8831
8842
|
---
|
|
8832
8843
|
|
|
@@ -8871,6 +8882,11 @@ ai-toolkit update # re-copies scripts, re-merges (idempotent)
|
|
|
8871
8882
|
3. Loads session context from `.claude/session-context.md` (if exists)
|
|
8872
8883
|
4. Loads active instincts from `.claude/instincts/*.md` (if any)
|
|
8873
8884
|
|
|
8885
|
+
When `AI_TOOLKIT_HOOK_QUIET=1`, the hook still performs session-state reset,
|
|
8886
|
+
stale search-flag cleanup, and update notification side effects, but suppresses
|
|
8887
|
+
all informational stdout so runtimes such as Codex do not show startup hook
|
|
8888
|
+
context in the UI.
|
|
8889
|
+
|
|
8874
8890
|
### Notification — `notify-waiting.sh`
|
|
8875
8891
|
|
|
8876
8892
|
| Field | Value |
|
|
@@ -8925,7 +8941,10 @@ ai-toolkit update # re-copies scripts, re-merges (idempotent)
|
|
|
8925
8941
|
|
|
8926
8942
|
**Action:** Adds a lightweight governance reminder: plan mode for architectural work, evidence-first debugging, KB-first research, and validation expectations.
|
|
8927
8943
|
|
|
8928
|
-
Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
|
|
8944
|
+
Skipped when `TOOLKIT_HOOK_PROFILE=minimal`. The bundled `app/hooks.json`
|
|
8945
|
+
registers this command with `AI_TOOLKIT_HOOK_QUIET=1`, so it still arms or
|
|
8946
|
+
clears the per-session search-first flag but suppresses the informational
|
|
8947
|
+
reminder output.
|
|
8929
8948
|
|
|
8930
8949
|
### UserPromptSubmit (usage tracking) — `track-usage.sh`
|
|
8931
8950
|
|
|
@@ -9198,11 +9217,11 @@ First-match-wins per file. Built-in runners: `bats`, `pytest`, `vitest`, `jest`.
|
|
|
9198
9217
|
| Field | Value |
|
|
9199
9218
|
|-------|-------|
|
|
9200
9219
|
| Event | `PostToolUse` |
|
|
9201
|
-
| Matcher | `
|
|
9220
|
+
| Matcher | `mcp__.*__(smart_query\|hybrid_search_kb\|crag_search\|multi_hop_search\|verify_answer)\|WebSearch\|WebFetch` |
|
|
9202
9221
|
| Script | `~/.softspark/ai-toolkit/hooks/search-tracker.sh` |
|
|
9203
9222
|
| Fires | After any search-style tool call |
|
|
9204
9223
|
|
|
9205
|
-
**Action:** Clears `~/.softspark/ai-toolkit/state/search-required-<session_id>.flag` (per-session, keyed by `session_id` from the hook stdin payload, falling back to `transcript_path` basename, then `default`). Pairs with `user-prompt-submit.sh` (sets the flag on long technical prompts only when a search provider is detected or strict mode is enabled) and `stop-search-check.sh` (blocks Stop if the calling session's flag is still set). Together
|
|
9224
|
+
**Action:** Clears `~/.softspark/ai-toolkit/state/search-required-<session_id>.flag` (per-session, keyed by `session_id` from the hook stdin payload, falling back to `transcript_path` basename, then `default`). Pairs with `user-prompt-submit.sh` (sets the flag on long technical prompts only when a search provider is detected or strict mode is enabled) and `stop-search-check.sh` (blocks Stop if the calling session's flag is still set). Search provider detection parses actual MCP server names from `mcpServers`, `mcp_servers`, or `mcp` config blocks; hook matchers and permission allowlists do not count as providers. Together the hooks enforce the global CLAUDE.md GOLDEN RULE without breaking offline/no-RAG installs and without cross-session interference when multiple Claude Code windows run in parallel.
|
|
9206
9225
|
|
|
9207
9226
|
Non-blocking (exit 0). Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
|
|
9208
9227
|
|
|
@@ -9215,7 +9234,7 @@ Non-blocking (exit 0). Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
|
|
|
9215
9234
|
| Script | `~/.softspark/ai-toolkit/hooks/stop-search-check.sh` |
|
|
9216
9235
|
| Fires | When Claude finishes a response |
|
|
9217
9236
|
|
|
9218
|
-
**Action:** If `search-required-<session_id>.flag` for the calling session is still present (no search tool ran during this turn) and a search provider is still detectable, emits `{"decision":"block","reason":"..."}` to continue the conversation with a search-first reminder. If no RAG/Web provider is detected, it clears the stale flag and exits 0, so offline/no-MCP users are not blocked. Flags are scoped by `session_id` from the hook stdin payload so a Stop in session B never consumes session A's flag (and vice versa). Stale per-session flags older than 60 minutes are GC'd on the next `SessionStart`.
|
|
9237
|
+
**Action:** If `search-required-<session_id>.flag` for the calling session is still present (no search tool ran during this turn) and a search provider is still detectable, emits `{"decision":"block","reason":"..."}` to continue the conversation with a search-first reminder. If no RAG/Web provider is detected, it clears the stale flag and exits 0, so offline/no-MCP users are not blocked. On Codex, where MCP search tools may not trigger the shared `PostToolUse` tracker, the hook also checks `~/.codex/log/codex-tui.log` for search tool calls after the flag timestamp before blocking. Flags are scoped by `session_id` from the hook stdin payload so a Stop in session B never consumes session A's flag (and vice versa). Stale per-session flags older than 60 minutes are GC'd on the next `SessionStart`.
|
|
9219
9238
|
|
|
9220
9239
|
**Overrides:** `CLAUDE_SKIP_SEARCH_FIRST=1`, `AI_TOOLKIT_SEARCH_FIRST=off`, or `AI_TOOLKIT_SEARCH_FIRST=strict` to force enforcement. Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
|
|
9221
9240
|
|
|
@@ -9253,7 +9272,7 @@ Non-blocking (exit 0). Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
|
|
|
9253
9272
|
| `scripts/test_cohesion.py` | Resolves changed paths → test commands via cohesion map. First-match-wins. Stdlib-only. |
|
|
9254
9273
|
| `app/hooks/test-cohesion-map.json` | Toolkit-default path → tests mapping (used when no project map exists). |
|
|
9255
9274
|
| `app/hooks/_locate-toolkit.sh` | Shared bash helper that exports `$TOOLKIT_DIR` for hooks needing scripts/. |
|
|
9256
|
-
| `app/hooks/_hook-io.sh` | Shared bash helper that normalizes hook payloads across Claude, Augment, Gemini, Windsurf, and Cursor-style JSON. |
|
|
9275
|
+
| `app/hooks/_hook-io.sh` | Shared bash helper that normalizes hook payloads across Claude, Augment, Gemini, Windsurf, and Cursor-style JSON. Honors `AI_TOOLKIT_HOOK_QUIET=1` for non-blocking context output. |
|
|
9257
9276
|
| `app/hooks/_search-capability.sh` | Shared bash helper that enables search-first blocking only when RAG/Web is configured or strict mode is requested. |
|
|
9258
9277
|
|
|
9259
9278
|
## Runtime Profiles
|
|
@@ -9270,6 +9289,11 @@ Set in `.claude/settings.local.json`:
|
|
|
9270
9289
|
| `standard` | All hooks (default) |
|
|
9271
9290
|
| `strict` | Standard + mypy --strict on task completion |
|
|
9272
9291
|
|
|
9292
|
+
Set `AI_TOOLKIT_HOOK_QUIET=1` on hook commands to suppress non-blocking
|
|
9293
|
+
informational context while preserving side effects and blocking decisions.
|
|
9294
|
+
Codex-generated hooks use this mode by default, and Claude's bundled
|
|
9295
|
+
`UserPromptSubmit` entry uses it to avoid visible prompt hook context.
|
|
9296
|
+
|
|
9273
9297
|
## Architecture
|
|
9274
9298
|
|
|
9275
9299
|
```
|
package/manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "4.3.
|
|
2
|
+
"version": "4.3.2",
|
|
3
3
|
"components": {
|
|
4
4
|
"agents": {
|
|
5
5
|
"description": "44 specialized agents (orchestrator, backend, frontend, security, devops, etc.)",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"tags": ["core", "skills", "commands"]
|
|
17
17
|
},
|
|
18
18
|
"hooks": {
|
|
19
|
-
"description": "
|
|
19
|
+
"description": "28 hook entries across 14 lifecycle events",
|
|
20
20
|
"path": "app/hooks.json + app/hooks/*.sh",
|
|
21
21
|
"target": "~/.claude/settings.json (merge) + ~/.softspark/ai-toolkit/hooks/ (copy)",
|
|
22
22
|
"type": "merge+copy",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softspark/ai-toolkit",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.2",
|
|
4
4
|
"description": "AI coding toolkit: 107 skills, 44 agents, 12-editor write-through (Claude, Cursor, Windsurf, Copilot, Gemini, Cline, Roo, Aider, Augment, Antigravity, Codex, opencode), machine-enforced safety constitution, SARIF audit, signed npm provenance.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -24,7 +24,7 @@ import sys
|
|
|
24
24
|
from pathlib import Path
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
HOOKS_PREFIX = '"$HOME/.softspark/ai-toolkit/hooks/'
|
|
27
|
+
HOOKS_PREFIX = 'AI_TOOLKIT_HOOK_QUIET=1 "$HOME/.softspark/ai-toolkit/hooks/'
|
|
28
28
|
|
|
29
29
|
# Hooks compatible with Codex, grouped by event.
|
|
30
30
|
# Format: (matcher, script_name)
|