@softspark/ai-toolkit 4.3.0 → 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 +56 -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 +17 -0
- package/app/hooks/_search-capability.sh +52 -4
- package/app/hooks/commit-quality.sh +1 -0
- package/app/hooks/governance-capture.sh +1 -0
- package/app/hooks/guard-config.sh +1 -0
- package/app/hooks/guard-destructive.sh +1 -0
- package/app/hooks/mcp-health.sh +3 -1
- package/app/hooks/revert-guard.sh +1 -0
- package/app/hooks/search-tracker.sh +6 -2
- package/app/hooks/session-start.sh +22 -12
- package/app/hooks/stop-search-check.sh +56 -1
- package/app/hooks/test-cohesion.sh +1 -0
- package/app/hooks/track-usage.sh +1 -0
- package/app/hooks/user-prompt-submit.sh +2 -1
- 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 +21 -8
- package/llms-full.txt +122 -30
- package/manifest.json +2 -2
- package/package.json +1 -1
- package/scripts/generate_codex_hooks.py +1 -1
- package/scripts/test_cohesion.py +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,62 @@ 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
|
+
|
|
44
|
+
## v4.3.1 - per-session search-first flag (2026-05-19)
|
|
45
|
+
|
|
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.
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- **Search-first flag is now per-session** - `user-prompt-submit.sh`, `search-tracker.sh`, and `stop-search-check.sh` key the flag by `session_id` from the hook stdin payload (`search-required-<session_id>.flag`), falling back to `transcript_path` basename, then `default`. Parallel sessions no longer interfere with each other. Each Stop check reads only its own session's flag.
|
|
51
|
+
- **`bats 1.13` regression in test-cohesion runner** - `scripts/test_cohesion.py` no longer passes `--no-parallelize-within-files` (now requires `--jobs 2` in bats 1.13). Default sequential mode is used, which is what the hook expected anyway.
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- **`hook_session_id()` helper in `_hook-io.sh`** - Resolves and sanitizes the per-session key for any hook that needs to scope state to a single Claude Code window.
|
|
56
|
+
- **`session-start.sh` GC** - Stale per-session search-required flags older than 60 minutes are deleted on every `SessionStart`, so crashed sessions do not leave residue.
|
|
57
|
+
- **Two new bats tests** in `tests/test_search_first_flow.bats` verifying per-session isolation and that `search-tracker` only clears its own session's flag.
|
|
58
|
+
|
|
59
|
+
### Changed
|
|
60
|
+
|
|
61
|
+
- **`kb/reference/hooks-catalog.md`** - Documents the new per-session flag layout, GC behavior, and isolation guarantee.
|
|
62
|
+
- **`README.md`** - Test count badge 1131 → 1133.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
10
66
|
## v4.3.0 - inject-mcp extension API (2026-05-12)
|
|
11
67
|
|
|
12
68
|
Minor release. Closes the asymmetry between `inject-rule` / `inject-hook` and MCP servers by adding `inject-mcp` and `remove-mcp` as first-class members of the extension API. External tools (rag-mcp, jira-mcp, custom integrations) can now register their MCP templates the same way they register rules and hooks -- from a local file or HTTPS URL, with full editor propagation and auto-refresh on `ai-toolkit update`.
|
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
|
-
|
|
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
|
@@ -19,6 +19,20 @@ hook_prompt() {
|
|
|
19
19
|
hook_json '.prompt // .tool_info.user_prompt // .user_prompt // .input // empty'
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
hook_session_id() {
|
|
23
|
+
local sid
|
|
24
|
+
sid=$(hook_json '.session_id // empty')
|
|
25
|
+
if [ -z "$sid" ] || [ "$sid" = "null" ]; then
|
|
26
|
+
local tp
|
|
27
|
+
tp=$(hook_json '.transcript_path // empty')
|
|
28
|
+
if [ -n "$tp" ] && [ "$tp" != "null" ]; then
|
|
29
|
+
sid=$(basename "$tp" .jsonl)
|
|
30
|
+
fi
|
|
31
|
+
fi
|
|
32
|
+
[ -z "$sid" ] && sid="default"
|
|
33
|
+
printf '%s' "$sid" | LC_ALL=C tr -c 'a-zA-Z0-9_-' '_'
|
|
34
|
+
}
|
|
35
|
+
|
|
22
36
|
hook_command() {
|
|
23
37
|
hook_json '.tool_input.command // .tool_input.command_line // .tool_info.command // .tool_info.command_line // .command // empty'
|
|
24
38
|
}
|
|
@@ -44,6 +58,9 @@ hook_new_content() {
|
|
|
44
58
|
|
|
45
59
|
hook_emit_context() {
|
|
46
60
|
local message="$1"
|
|
61
|
+
if [ "${AI_TOOLKIT_HOOK_QUIET:-0}" = "1" ]; then
|
|
62
|
+
return 0
|
|
63
|
+
fi
|
|
47
64
|
if [ "${AI_TOOLKIT_HOOK_FORMAT:-}" = "json" ]; then
|
|
48
65
|
jq -nc --arg msg "$message" \
|
|
49
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
|
+
}
|
|
@@ -13,6 +13,7 @@ LOG_FILE="$LOG_DIR/governance.log"
|
|
|
13
13
|
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
14
14
|
SESSION="${CLAUDE_SESSION_ID:-unknown}"
|
|
15
15
|
|
|
16
|
+
# shellcheck disable=SC2034 # INPUT is consumed via sourced _hook-io.sh
|
|
16
17
|
INPUT=$(cat)
|
|
17
18
|
# shellcheck source=_hook-io.sh
|
|
18
19
|
source "$(dirname "$0")/_hook-io.sh"
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
# Fires on: PreToolUse (Edit|Write|MultiEdit)
|
|
5
5
|
# Exit 2 = block the tool call. Stderr message goes to Claude as feedback.
|
|
6
6
|
|
|
7
|
+
# shellcheck disable=SC2034 # INPUT is consumed via sourced _hook-io.sh
|
|
7
8
|
INPUT=$(cat)
|
|
8
9
|
# shellcheck source=_hook-io.sh
|
|
9
10
|
source "$(dirname "$0")/_hook-io.sh"
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
# Fires on: PreToolUse (Bash)
|
|
5
5
|
# Exit 2 = block the command. Stderr message goes to Claude as feedback.
|
|
6
6
|
|
|
7
|
+
# shellcheck disable=SC2034 # INPUT is consumed via sourced _hook-io.sh
|
|
7
8
|
INPUT=$(cat)
|
|
8
9
|
# shellcheck source=_hook-io.sh
|
|
9
10
|
source "$(dirname "$0")/_hook-io.sh"
|
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
|
#
|
|
@@ -12,7 +12,11 @@
|
|
|
12
12
|
|
|
13
13
|
# shellcheck source=_profile-check.sh
|
|
14
14
|
source "$(dirname "$0")/_profile-check.sh"
|
|
15
|
+
# shellcheck source=_hook-io.sh
|
|
16
|
+
source "$(dirname "$0")/_hook-io.sh"
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
# shellcheck disable=SC2034 # INPUT is consumed via sourced _hook-io.sh
|
|
19
|
+
INPUT=$(cat 2>/dev/null || true)
|
|
20
|
+
FLAG="$HOME/.softspark/ai-toolkit/state/search-required-$(hook_session_id).flag"
|
|
17
21
|
rm -f "$FLAG" 2>/dev/null
|
|
18
22
|
exit 0
|
|
@@ -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
|
|
@@ -22,10 +28,14 @@ if [ -n "$TOOLKIT_DIR" ] && command -v python3 >/dev/null 2>&1; then
|
|
|
22
28
|
${SESSION_ID_INPUT:+--session-id "$SESSION_ID_INPUT"} >/dev/null 2>&1 || true
|
|
23
29
|
fi
|
|
24
30
|
|
|
31
|
+
# 1b. GC stale per-session search-required flags (older than 60 min)
|
|
32
|
+
find "$HOME/.softspark/ai-toolkit/state" -maxdepth 1 -name 'search-required-*.flag' \
|
|
33
|
+
-type f -mmin +60 -delete 2>/dev/null || true
|
|
34
|
+
|
|
25
35
|
# 2. Check for updates (cached, max once per 24h, non-blocking)
|
|
26
36
|
VERSION_MSG=$(python3 "$TOOLKIT_DIR/scripts/version_check.py" 2>/dev/null)
|
|
27
37
|
if [ -n "$VERSION_MSG" ]; then
|
|
28
|
-
|
|
38
|
+
emit_context "$VERSION_MSG"
|
|
29
39
|
# Strip shell/AppleScript/PowerShell metacharacters before interpolating into
|
|
30
40
|
# notification commands. VERSION_MSG is version_check.py output which should
|
|
31
41
|
# be plain ASCII, but sanitize anyway as defense in depth.
|
|
@@ -42,20 +52,20 @@ fi
|
|
|
42
52
|
|
|
43
53
|
# 3. Load session context (if available)
|
|
44
54
|
SESSION_FILE=".claude/session-context.md"
|
|
45
|
-
if [ -f "$SESSION_FILE" ]; then
|
|
46
|
-
|
|
55
|
+
if [ -f "$SESSION_FILE" ] && [ "${AI_TOOLKIT_HOOK_QUIET:-0}" != "1" ]; then
|
|
56
|
+
printf '%s\n' "=== Session Context ==="
|
|
47
57
|
cat "$SESSION_FILE"
|
|
48
|
-
|
|
58
|
+
printf '%s\n' "====================="
|
|
49
59
|
fi
|
|
50
60
|
|
|
51
61
|
# 3. Load active instincts (if any)
|
|
52
62
|
INSTINCTS_DIR=".claude/instincts"
|
|
53
|
-
if [ -d "$INSTINCTS_DIR" ] && ls "$INSTINCTS_DIR"/*.md >/dev/null 2>&1; then
|
|
54
|
-
|
|
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 ==="
|
|
55
65
|
for f in "$INSTINCTS_DIR"/*.md; do
|
|
56
|
-
|
|
66
|
+
printf '%s\n' "- $(head -1 "$f")"
|
|
57
67
|
done
|
|
58
|
-
|
|
68
|
+
printf '%s\n' "========================"
|
|
59
69
|
fi
|
|
60
70
|
|
|
61
71
|
exit 0
|
|
@@ -21,7 +21,9 @@ source "$(dirname "$0")/_search-capability.sh"
|
|
|
21
21
|
|
|
22
22
|
[ "${CLAUDE_SKIP_SEARCH_FIRST:-0}" = "1" ] && exit 0
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
# shellcheck disable=SC2034 # INPUT is consumed via sourced _hook-io.sh
|
|
25
|
+
INPUT=$(cat 2>/dev/null || true)
|
|
26
|
+
FLAG="$HOME/.softspark/ai-toolkit/state/search-required-$(hook_session_id).flag"
|
|
25
27
|
[ -f "$FLAG" ] || exit 0
|
|
26
28
|
|
|
27
29
|
if ! ai_toolkit_has_search_provider; then
|
|
@@ -29,6 +31,59 @@ if ! ai_toolkit_has_search_provider; then
|
|
|
29
31
|
exit 0
|
|
30
32
|
fi
|
|
31
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
|
+
|
|
32
87
|
# Read the original prompt (line 2 of the flag file) for the reminder.
|
|
33
88
|
PROMPT_PREVIEW=$(sed -n '2p' "$FLAG" 2>/dev/null | head -c 200)
|
|
34
89
|
rm -f "$FLAG" 2>/dev/null # one-shot; do not loop forever
|
|
@@ -32,6 +32,7 @@ source "$(dirname "$0")/_hook-io.sh"
|
|
|
32
32
|
[ "${CLAUDE_HOOK_BOOTSTRAP:-0}" = "1" ] && exit 0
|
|
33
33
|
[ "${CLAUDE_SKIP_COHESION:-0}" = "1" ] && exit 0
|
|
34
34
|
|
|
35
|
+
# shellcheck disable=SC2034 # INPUT is consumed via sourced _hook-io.sh
|
|
35
36
|
INPUT=$(cat)
|
|
36
37
|
FILE_PATH=$(hook_file_path)
|
|
37
38
|
|
package/app/hooks/track-usage.sh
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
STATS_FILE="${HOME}/.softspark/ai-toolkit/stats.json"
|
|
11
11
|
|
|
12
12
|
# Read prompt from stdin (Claude Code passes JSON with .prompt field)
|
|
13
|
+
# shellcheck disable=SC2034 # INPUT is consumed via sourced _hook-io.sh
|
|
13
14
|
INPUT=$(cat)
|
|
14
15
|
# shellcheck source=_hook-io.sh
|
|
15
16
|
source "$(dirname "$0")/_hook-io.sh"
|
|
@@ -13,6 +13,7 @@ source "$(dirname "$0")/_hook-io.sh"
|
|
|
13
13
|
source "$(dirname "$0")/_search-capability.sh"
|
|
14
14
|
|
|
15
15
|
# Read prompt from stdin (Claude Code passes JSON with .prompt field)
|
|
16
|
+
# shellcheck disable=SC2034 # INPUT is consumed via sourced _hook-io.sh
|
|
16
17
|
INPUT=$(cat)
|
|
17
18
|
PROMPT_TEXT=$(hook_prompt)
|
|
18
19
|
LOWERED="$(printf '%s' "$PROMPT_TEXT" | tr '[:upper:]' '[:lower:]')"
|
|
@@ -22,7 +23,7 @@ LOWERED="$(printf '%s' "$PROMPT_TEXT" | tr '[:upper:]' '[:lower:]')"
|
|
|
22
23
|
# Cleared by search-tracker.sh after smart_query / hybrid_search_kb / web runs.
|
|
23
24
|
PROMPT_LEN=${#PROMPT_TEXT}
|
|
24
25
|
STATE_DIR="$HOME/.softspark/ai-toolkit/state"
|
|
25
|
-
FLAG="$STATE_DIR/search-required.flag"
|
|
26
|
+
FLAG="$STATE_DIR/search-required-$(hook_session_id).flag"
|
|
26
27
|
mkdir -p "$STATE_DIR" 2>/dev/null
|
|
27
28
|
if [ "$PROMPT_LEN" -gt 30 ] && \
|
|
28
29
|
[ "${CLAUDE_SKIP_SEARCH_FIRST:-0}" != "1" ] && \
|
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",
|