@softspark/ai-toolkit 4.2.0 → 4.2.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 CHANGED
@@ -7,6 +7,51 @@ Versioning follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## v4.2.2 - Legacy hook duplicate cleanup (2026-05-12)
11
+
12
+ Patch release. Cleans up legacy untagged hook duplicates found during post-release verification of v4.2.1.
13
+
14
+ ### Fixed
15
+
16
+ - **Legacy notification hook cleanup** - `scripts/merge-hooks.py` now removes old direct `osascript` notification hooks from pre-script installs while preserving user-owned notification hooks.
17
+ - **External hook duplicate cleanup** - `scripts/inject_hook_cli.py` now removes legacy untagged entries matching the same event, matcher, and handler payload as the external hook being re-injected.
18
+
19
+ ### Verification
20
+
21
+ - `bats tests/test_merge_hooks_statusline.bats tests/test_inject_hook.bats`: 42/42 passing.
22
+ - Local `~/.claude/settings.json`: 0 behavior duplicates, 0 exact duplicates, 0 untagged hook groups after cleanup.
23
+
24
+ ---
25
+
26
+ ## v4.2.1 - Claude Code hook enforcement and README cleanup (2026-05-12)
27
+
28
+ Patch release. Tightens Claude Code runtime enforcement, fixes duplicated hook cleanup during install/update, and removes the README hero image for release.
29
+
30
+ ### Changed
31
+
32
+ - **Stop quality gate** - Registered `quality-gate.sh` for `Stop` in addition to `TaskCompleted`, so final Claude Code responses are blocked when supported lint/type checks fail.
33
+ - **README release surface** - Removed the README hero image block while keeping badges and release notes intact.
34
+ - **Runtime guidance** - Clarified in `CLAUDE.md` that Claude Code consumes `CLAUDE.md`, settings, hooks, skills, and agents, while `AGENTS.md` is generated for Codex/OpenCode/Gemini compatibility.
35
+
36
+ ### Fixed
37
+
38
+ - **Quality gate exit codes** - Reworked shell pipelines so `ruff`, `tsc`, `dart analyze`, `phpstan`, and `go test` failures are not masked by output truncation.
39
+ - **Hook merge cleanup** - `scripts/merge-hooks.py` now removes legacy untagged ai-toolkit hook entries matching current toolkit hook signatures while preserving custom hooks.
40
+
41
+ ### Ecosystem
42
+
43
+ - Refreshed `benchmarks/ecosystem-doctor-snapshot.json` for current editor/runtime docs. Claude Code now exposes `Stop` in docs; the toolkit already supported `Stop` hooks, and this release registers the strict quality gate there.
44
+
45
+ ### Verification
46
+
47
+ - `python3 scripts/ecosystem_doctor.py --offline --check`: no drift.
48
+ - `python3 scripts/validate.py --strict`: 0 errors, 0 warnings.
49
+ - `python3 scripts/audit_skills.py --ci`: HIGH 0, WARN 0.
50
+ - `npm test`: 1049/1049 passing.
51
+ - `npm --cache /tmp/ai-toolkit-npm-cache pack --dry-run`: `softspark-ai-toolkit-4.2.1.tgz`.
52
+
53
+ ---
54
+
10
55
  ## v4.2.0 - SEO GEO pipeline and release gate hardening (2026-05-07)
11
56
 
12
57
  Minor release. Adds AI pipeline, content citability, and topical authority guidance to `seo-validate` from PR #9 by @fakenso, and keeps the release gate fixes prepared for v4.1.1 in the same unreleased train.
package/README.md CHANGED
@@ -6,22 +6,15 @@
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
7
  [![Skills](https://img.shields.io/badge/skills-107-brightgreen)](app/skills/)
8
8
  [![Agents](https://img.shields.io/badge/agents-44-blue)](app/agents/)
9
- [![Tests](https://img.shields.io/badge/tests-1047%20passing-success)](tests/)
9
+ [![Tests](https://img.shields.io/badge/tests-1051%20passing-success)](tests/)
10
10
 
11
- <p align="center">
12
- <img src="assets/ai-toolkit-readme-hero.png" alt="ai-toolkit 3 README hero showing one toolkit for 12 AI editors" width="900">
13
- </p>
11
+ ## What's New in v4.2.2
14
12
 
15
- ---
16
-
17
- ## What's New in v4.2.0
18
-
19
- SEO/GEO release focused on AI citation readiness and stricter release gates.
13
+ Patch release focused on cleaning legacy hook duplicates found during post-release verification.
20
14
 
21
- - **AI pipeline guidance**: `seo-validate` now includes Google AI pipeline, Query Fan Out, and content citability references.
22
- - **Topical authority checks**: new `--scope topical` covers cluster structure, internal links, orphan pages, slugs, and cannibalization.
23
- - **Expanded GEO heuristics**: Category 6 now checks chunk boundaries, hedging, decision frameworks, freshness, and author quality.
24
- - **Release gate parity**: CI and `prepublishOnly` now run strict validation, and the URL rule test is hermetic.
15
+ - **Legacy notification cleanup**: `merge-hooks.py` now removes old direct `osascript` notification hooks from pre-script installs.
16
+ - **External hook cleanup**: `inject-hook` now removes legacy untagged duplicates that match the re-injected external hook payload.
17
+ - **Regression coverage**: added tests for both cleanup paths so repeated updates keep hook settings idempotent.
25
18
 
26
19
  See [CHANGELOG.md](CHANGELOG.md) for full history.
27
20
 
@@ -131,7 +124,7 @@ See [CLI Reference](kb/reference/cli-reference.md) for all commands and options.
131
124
  | `skills/` (hybrid) | 30 | Slash commands with agent knowledge base |
132
125
  | `skills/` (knowledge) | 45 | Domain knowledge auto-loaded by agents (includes 13 `<lang>-rules` skills) |
133
126
  | `agents/` | 44 | Specialized agents across 10 categories |
134
- | `hooks/` | 21 global + 5 skill-scoped | Quality gates, path safety, prompt governance, session lifecycle |
127
+ | `hooks/` | 22 global + 5 skill-scoped | Quality gates, path safety, prompt governance, session lifecycle |
135
128
  | `plugins/` | 11 packs | Opt-in domain bundles (security, research, frontend, enterprise, 6 language packs) |
136
129
  | `constitution.md` | 6 articles | Machine-enforced safety rules |
137
130
  | `rules/` | auto-injected | Language-specific and custom rules injected into your configs |
@@ -147,14 +140,14 @@ ai-toolkit/
147
140
  │ ├── agents/ # 44 agent definitions
148
141
  │ ├── skills/ # 107 skills (task / hybrid / knowledge)
149
142
  │ ├── rules/ # Auto-injected into your CLAUDE.md
150
- │ ├── hooks/ # Hook scripts (21 entries, 12 lifecycle events)
143
+ │ ├── hooks/ # Hook scripts (22 entries, 12 lifecycle events)
151
144
  │ ├── plugins/ # 11 experimental plugin packs (opt-in)
152
145
  │ ├── output-styles/ # System prompt output style overrides
153
146
  │ ├── constitution.md # 6 immutable safety articles
154
147
  │ └── ARCHITECTURE.md # Full system design
155
148
  ├── kb/ # Reference docs, procedures, plans
156
149
  ├── scripts/ # Validation, install, evaluation scripts
157
- ├── tests/ # Bats test suite (1047 tests)
150
+ ├── tests/ # Bats test suite (1051 tests)
158
151
  └── CHANGELOG.md
159
152
  ```
160
153
 
@@ -166,7 +159,7 @@ ai-toolkit/
166
159
 
167
160
  **Machine-enforced constitution** — 6-article safety constitution enforced via `PreToolUse` hooks that actually block `rm -rf`, `DROP TABLE`, and irreversible operations. Not just documentation.
168
161
 
169
- **21 lifecycle hooks** — Executable scripts across 12 events (SessionStart → SessionEnd). Guards, governance, quality gates, session persistence, MCP health checks. See [Hooks Catalog](kb/reference/hooks-catalog.md).
162
+ **22 lifecycle hooks** — Executable scripts across 12 events (SessionStart → SessionEnd). Guards, governance, quality gates, session persistence, MCP health checks. See [Hooks Catalog](kb/reference/hooks-catalog.md).
170
163
 
171
164
  **Security scanning** — `/skill-audit` for code-level risks, `/cve-scan` for dependency CVEs. Both CI-ready with exit codes.
172
165
 
@@ -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.2.0",
4
+ "version": "4.2.2",
5
5
  "author": {
6
6
  "name": "SoftSpark",
7
7
  "url": "https://github.com/softspark"
@@ -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 | 12 events / 21 entries (SessionStart ×3, Notification ×1, PreToolUse ×4, UserPromptSubmit ×2, PostToolUse ×2, Stop ×2, TaskCompleted ×1, TeammateIdle ×1, SubagentStart ×1, SubagentStop ×1, PreCompact ×2, SessionEnd ×1) |
11
+ | Hooks | 12 events / 22 entries (SessionStart ×3, Notification ×1, PreToolUse ×4, UserPromptSubmit ×2, PostToolUse ×2, Stop ×3, TaskCompleted ×1, TeammateIdle ×1, SubagentStart ×1, SubagentStop ×1, PreCompact ×2, SessionEnd ×1) |
12
12
 
13
13
  ---
14
14
 
@@ -1,48 +1,60 @@
1
1
  #!/usr/bin/env bash
2
2
  # quality-gate.sh — Block task completion if lint/type errors found.
3
3
  #
4
- # Fires on: TaskCompleted
5
- # Exit 2 = block completion. Skipped when TOOLKIT_HOOK_PROFILE=minimal.
4
+ # Fires on: Stop, TaskCompleted
5
+ # Exit 2 = block completion/stop. Skipped when TOOLKIT_HOOK_PROFILE=minimal.
6
6
  # Strict profile also runs mypy.
7
7
 
8
8
  # shellcheck source=_profile-check.sh
9
9
  source "$(dirname "$0")/_profile-check.sh"
10
10
 
11
- if [ -f pyproject.toml ] || [ -f setup.py ]; then
12
- ruff check . 2>&1 | head -30
13
- exit_code=$?
11
+ run_required() {
12
+ local label="$1"
13
+ local preview_cmd="$2"
14
+ shift 2
15
+
16
+ local tmp
17
+ tmp="$(mktemp "${TMPDIR:-/tmp}/ai-toolkit-quality.XXXXXX")"
18
+ "$@" >"$tmp" 2>&1
19
+ local exit_code=$?
20
+ eval "$preview_cmd" <"$tmp"
21
+ rm -f "$tmp"
22
+
14
23
  if [ $exit_code -ne 0 ]; then
15
- echo "QUALITY GATE FAILED: ruff found errors. Fix them before completing." >&2
24
+ echo "QUALITY GATE FAILED: ${label}. Fix it before completing." >&2
16
25
  exit 2
17
26
  fi
27
+ }
28
+
29
+ require_command() {
30
+ local command_name="$1"
31
+ if command -v "$command_name" >/dev/null 2>&1; then
32
+ return 0
33
+ fi
34
+ echo "QUALITY GATE SKIPPED: ${command_name} is not installed." >&2
35
+ return 1
36
+ }
37
+
38
+ if [ -f pyproject.toml ] || [ -f setup.py ]; then
39
+ if require_command ruff; then
40
+ run_required "ruff found errors" "head -30" ruff check .
41
+ fi
18
42
  if [ -d src ] && [ "$PROFILE" = "strict" ]; then
19
- mypy --strict src/ 2>&1 | tail -5
20
- exit_code=$?
21
- if [ $exit_code -ne 0 ]; then
22
- echo "QUALITY GATE FAILED: mypy found type errors." >&2
23
- exit 2
43
+ if require_command mypy; then
44
+ run_required "mypy found type errors" "tail -5" mypy --strict src/
24
45
  fi
25
46
  fi
26
47
  elif [ -f package.json ] && [ -f tsconfig.json ]; then
27
- npx tsc --noEmit 2>&1 | tail -10
28
- exit_code=$?
29
- if [ $exit_code -ne 0 ]; then
30
- echo "QUALITY GATE FAILED: TypeScript compilation errors." >&2
31
- exit 2
48
+ if require_command npx; then
49
+ run_required "TypeScript compilation errors" "tail -10" npx tsc --noEmit
32
50
  fi
33
51
  elif [ -f pubspec.yaml ]; then
34
- dart analyze 2>&1 | tail -10
35
- exit_code=$?
36
- if [ $exit_code -ne 0 ]; then
37
- echo "QUALITY GATE FAILED: Dart analysis issues." >&2
38
- exit 2
52
+ if require_command dart; then
53
+ run_required "Dart analysis issues" "tail -10" dart analyze
39
54
  fi
40
55
  elif [ -f go.mod ]; then
41
- go vet ./... 2>&1 | tail -10
42
- exit_code=$?
43
- if [ $exit_code -ne 0 ]; then
44
- echo "QUALITY GATE FAILED: Go vet issues." >&2
45
- exit 2
56
+ if require_command go; then
57
+ run_required "Go vet issues" "tail -10" go vet ./...
46
58
  fi
47
59
  fi
48
60
 
package/app/hooks.json CHANGED
@@ -150,6 +150,16 @@
150
150
  "command": "\"$HOME/.softspark/ai-toolkit/hooks/save-session.sh\""
151
151
  }
152
152
  ]
153
+ },
154
+ {
155
+ "_source": "ai-toolkit",
156
+ "matcher": "",
157
+ "hooks": [
158
+ {
159
+ "type": "command",
160
+ "command": "\"$HOME/.softspark/ai-toolkit/hooks/quality-gate.sh\""
161
+ }
162
+ ]
153
163
  }
154
164
  ],
155
165
  "TaskCompleted": [
@@ -1,5 +1,5 @@
1
1
  {
2
- "last_run": "2026-05-06T08:17:20Z",
2
+ "last_run": "2026-05-12T10:01:38Z",
3
3
  "schema_version": 1,
4
4
  "tools": {
5
5
  "aider": {
@@ -24,10 +24,10 @@
24
24
  }
25
25
  },
26
26
  "augment": {
27
- "docs_hash": "ac138d99235e1f3e",
27
+ "docs_hash": "b746d5dbbc5a2936",
28
28
  "headings": [
29
29
  "Agent",
30
- "Code Completions",
30
+ "Chat",
31
31
  "Documentation Index",
32
32
  "Introduction",
33
33
  "\u200bAuggie CLI",
@@ -57,7 +57,7 @@
57
57
  }
58
58
  },
59
59
  "claude-code": {
60
- "docs_hash": "789113e1bc4cd34c",
60
+ "docs_hash": "9e0292a9f92392e0",
61
61
  "headings": [
62
62
  "Claude Code overview",
63
63
  "Documentation Index",
@@ -85,7 +85,7 @@
85
85
  "SessionEnd": false,
86
86
  "SessionStart": false,
87
87
  "Setup": true,
88
- "Stop": false,
88
+ "Stop": true,
89
89
  "StopFailure": false,
90
90
  "SubagentStart": false,
91
91
  "SubagentStop": false,
@@ -103,10 +103,10 @@
103
103
  "slash command": true,
104
104
  "sub-agent": true
105
105
  },
106
- "version": "2.1.131 (Claude Code)"
106
+ "version": "2.1.139 (Claude Code)"
107
107
  },
108
108
  "cline": {
109
- "docs_hash": "b4009dc8e6d40af9",
109
+ "docs_hash": "b59e8836b3217cdb",
110
110
  "headings": [
111
111
  "Cline CLI",
112
112
  "Cline Documentation",
@@ -137,7 +137,7 @@
137
137
  }
138
138
  },
139
139
  "codex-cli": {
140
- "docs_hash": "c6b51936294d38e7",
140
+ "docs_hash": "3f963da56d00add9",
141
141
  "headings": [
142
142
  "About",
143
143
  "Contributing",
@@ -156,7 +156,7 @@
156
156
  "Packages 0",
157
157
  "Provide feedback",
158
158
  "Quickstart",
159
- "Releases 765",
159
+ "Releases 784",
160
160
  "Repository files navigation",
161
161
  "Resources",
162
162
  "Saved searches",
@@ -186,10 +186,10 @@
186
186
  "mcp_servers": false,
187
187
  "sandbox": true
188
188
  },
189
- "version": "codex-cli 0.128.0"
189
+ "version": "codex-cli 0.130.0"
190
190
  },
191
191
  "cursor": {
192
- "docs_hash": "8ed1f5cf7441b6c0",
192
+ "docs_hash": "705adfd617433088",
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": "21a2f14121ff27fa",
208
+ "docs_hash": "93694630a3d04327",
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": "99a5ab7bd5f7556f",
247
+ "docs_hash": "d9b9fcdfe3b67469",
248
248
  "headings": [
249
249
  "About Copilot auto model selection",
250
250
  "About Copilot integrations",
@@ -292,7 +292,7 @@
292
292
  }
293
293
  },
294
294
  "opencode": {
295
- "docs_hash": "fe845a4bffb6371b",
295
+ "docs_hash": "c1c8ba8f6b2e7ddf",
296
296
  "headings": [
297
297
  "Add features",
298
298
  "Ask questions",
@@ -353,7 +353,7 @@
353
353
  }
354
354
  },
355
355
  "windsurf": {
356
- "docs_hash": "9b2879646f041aea",
356
+ "docs_hash": "66295add1d0f0645",
357
357
  "headings": [
358
358
  "Advanced",
359
359
  "App Deploys",
@@ -3,9 +3,9 @@ title: "AI Toolkit - Architecture Overview"
3
3
  category: reference
4
4
  service: ai-toolkit
5
5
  tags: [architecture, overview, design, structure]
6
- version: "1.4.4"
6
+ version: "1.4.5"
7
7
  created: "2026-03-23"
8
- last_updated: "2026-04-15"
8
+ last_updated: "2026-05-12"
9
9
  description: "Architecture of ai-toolkit: directory layout, global install model, editor-aware MCP install, Codex translation layer, skill tiers, and integration with projects."
10
10
  ---
11
11
 
@@ -264,7 +264,7 @@ Agents (code-reviewer, debugger, devops-implementer, ...)
264
264
 
265
265
  ## Quality Hooks
266
266
 
267
- 21 entries across 12 lifecycle events. See [hooks-catalog.md](hooks-catalog.md) for full details.
267
+ 22 entries across 12 lifecycle events. See [hooks-catalog.md](hooks-catalog.md) for full details.
268
268
 
269
269
  | Hook | Trigger | Script | Action |
270
270
  |------|---------|--------|--------|
@@ -282,6 +282,7 @@ Agents (code-reviewer, debugger, devops-implementer, ...)
282
282
  | PostToolUse | After any tool | `governance-capture.sh` | Log security-sensitive operations |
283
283
  | Stop | After response | `quality-check.sh` | Multi-language lint |
284
284
  | Stop | After response | `save-session.sh` | Persist session context |
285
+ | Stop | Before final stop | `quality-gate.sh` | Block final response on lint/type errors |
285
286
  | TaskCompleted | Agent Teams: task done | `quality-gate.sh` | Block completion on errors |
286
287
  | TeammateIdle | Agent Teams: idle | *(inline)* | Completeness reminder |
287
288
  | SubagentStart | Subagent spawn | `subagent-start.sh` | Scope reminder for subagents |
@@ -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.0"
6
+ version: "1.5.1"
7
7
  created: "2026-03-27"
8
- last_updated: "2026-04-24"
8
+ last_updated: "2026-05-12"
9
9
  description: "Complete reference of all ai-toolkit hooks: events, scripts, installation, and runtime behavior."
10
10
  ---
11
11
 
@@ -13,7 +13,7 @@ description: "Complete reference of all ai-toolkit hooks: events, scripts, insta
13
13
 
14
14
  ## Overview
15
15
 
16
- ai-toolkit provides 21 global hook entries across 12 lifecycle events that enforce quality, safety, and workflow rules across all Claude Code sessions. Hooks are merged into `~/.claude/settings.json` on install, with logic in standalone scripts at `~/.softspark/ai-toolkit/hooks/`.
16
+ ai-toolkit provides 22 global hook entries across 12 lifecycle events that enforce quality, safety, and workflow rules across all Claude Code sessions. Hooks are merged into `~/.claude/settings.json` on install, with logic in standalone scripts at `~/.softspark/ai-toolkit/hooks/`.
17
17
 
18
18
  ## Supported Surface
19
19
 
@@ -164,6 +164,19 @@ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
164
164
 
165
165
  Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
166
166
 
167
+ ### Stop (quality gate) — `quality-gate.sh`
168
+
169
+ | Field | Value |
170
+ |-------|-------|
171
+ | Event | `Stop` |
172
+ | Matcher | *(all)* |
173
+ | Script | `~/.softspark/ai-toolkit/hooks/quality-gate.sh` |
174
+ | Fires | Before Claude is allowed to finish a response |
175
+
176
+ **Action:** Runs lint/typecheck. **Blocks stopping (exit 2)** if errors found, so Claude must continue and fix the issues. Missing local tooling is reported as skipped rather than blocking the session.
177
+
178
+ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
179
+
167
180
  ### TaskCompleted — `quality-gate.sh`
168
181
 
169
182
  | Field | Value |
@@ -362,7 +375,7 @@ Set in `.claude/settings.local.json`:
362
375
  ├── PreToolUse → guard-destructive.sh, guard-path.sh, guard-config.sh, commit-quality.sh
363
376
  ├── UserPromptSubmit → user-prompt-submit.sh, track-usage.sh
364
377
  ├── PostToolUse → post-tool-use.sh, governance-capture.sh
365
- ├── Stop → quality-check.sh, save-session.sh
378
+ ├── Stop → quality-check.sh, save-session.sh, quality-gate.sh
366
379
  ├── TaskCompleted → quality-gate.sh
367
380
  ├── TeammateIdle → echo (inline)
368
381
  ├── SubagentStart → subagent-start.sh
@@ -4,7 +4,7 @@ category: reference
4
4
  service: ai-toolkit
5
5
  tags: [features, differentiators, constitution, hooks, security, tdd, memory]
6
6
  created: "2026-04-13"
7
- last_updated: "2026-04-13"
7
+ last_updated: "2026-05-12"
8
8
  description: "Detailed description of ai-toolkit's unique features: constitution enforcement, hooks system, security scanning, effort budgeting, quality gates, and more."
9
9
  ---
10
10
 
@@ -12,7 +12,7 @@ description: "Detailed description of ai-toolkit's unique features: constitution
12
12
 
13
13
  ## 1. Machine-Enforced Constitution
14
14
 
15
- Unlike other toolkits that put safety rules in documentation only, ai-toolkit enforces a 5-article constitution via `PreToolUse` hooks. The hook actually **blocks** execution of:
15
+ Unlike other toolkits that put safety rules in documentation only, ai-toolkit enforces a 6-article constitution via hooks. The hooks actually **block** execution of:
16
16
  - Mass deletion (`rm -rf`, `DROP TABLE`)
17
17
  - Blind overwrites of uncommitted work
18
18
  - Any action that could cause irreversible data loss
@@ -21,7 +21,7 @@ Unlike other toolkits that put safety rules in documentation only, ai-toolkit en
21
21
 
22
22
  Hook logic lives in `app/hooks/*.sh` — not inline JSON one-liners. Scripts are copied to `~/.softspark/ai-toolkit/hooks/` on install and referenced from `~/.claude/settings.json`. Easy to read, debug, and extend.
23
23
 
24
- **12 lifecycle events / 21 global hook entries:**
24
+ **12 lifecycle events / 22 global hook entries:**
25
25
 
26
26
  | Event | Script | Action |
27
27
  |-------|--------|--------|
@@ -39,6 +39,7 @@ Hook logic lives in `app/hooks/*.sh` — not inline JSON one-liners. Scripts are
39
39
  | PostToolUse | `governance-capture.sh` | Log security-sensitive operations to JSONL |
40
40
  | Stop | `quality-check.sh` | Multi-language lint (ruff/tsc/phpstan/dart/go) |
41
41
  | Stop | `save-session.sh` | Persist session context for cross-session continuity |
42
+ | Stop | `quality-gate.sh` | Block final response on lint/type errors |
42
43
  | TaskCompleted | `quality-gate.sh` | Block task completion on lint/type errors |
43
44
  | SubagentStart | `subagent-start.sh` | Narrow-scope reminder for spawned subagents |
44
45
  | SubagentStop | `subagent-stop.sh` | Completion checklist for subagent handoff |
package/llms-full.txt CHANGED
@@ -6091,9 +6091,9 @@ title: "AI Toolkit - Architecture Overview"
6091
6091
  category: reference
6092
6092
  service: ai-toolkit
6093
6093
  tags: [architecture, overview, design, structure]
6094
- version: "1.4.4"
6094
+ version: "1.4.5"
6095
6095
  created: "2026-03-23"
6096
- last_updated: "2026-04-15"
6096
+ last_updated: "2026-05-12"
6097
6097
  description: "Architecture of ai-toolkit: directory layout, global install model, editor-aware MCP install, Codex translation layer, skill tiers, and integration with projects."
6098
6098
  ---
6099
6099
 
@@ -6352,7 +6352,7 @@ Agents (code-reviewer, debugger, devops-implementer, ...)
6352
6352
 
6353
6353
  ## Quality Hooks
6354
6354
 
6355
- 21 entries across 12 lifecycle events. See [hooks-catalog.md](hooks-catalog.md) for full details.
6355
+ 22 entries across 12 lifecycle events. See [hooks-catalog.md](hooks-catalog.md) for full details.
6356
6356
 
6357
6357
  | Hook | Trigger | Script | Action |
6358
6358
  |------|---------|--------|--------|
@@ -6370,6 +6370,7 @@ Agents (code-reviewer, debugger, devops-implementer, ...)
6370
6370
  | PostToolUse | After any tool | `governance-capture.sh` | Log security-sensitive operations |
6371
6371
  | Stop | After response | `quality-check.sh` | Multi-language lint |
6372
6372
  | Stop | After response | `save-session.sh` | Persist session context |
6373
+ | Stop | Before final stop | `quality-gate.sh` | Block final response on lint/type errors |
6373
6374
  | TaskCompleted | Agent Teams: task done | `quality-gate.sh` | Block completion on errors |
6374
6375
  | TeammateIdle | Agent Teams: idle | *(inline)* | Completeness reminder |
6375
6376
  | SubagentStart | Subagent spawn | `subagent-start.sh` | Scope reminder for subagents |
@@ -8757,9 +8758,9 @@ title: "Hooks Catalog"
8757
8758
  category: reference
8758
8759
  service: ai-toolkit
8759
8760
  tags: [hooks, quality, safety, enforcement, settings.json]
8760
- version: "1.5.0"
8761
+ version: "1.5.1"
8761
8762
  created: "2026-03-27"
8762
- last_updated: "2026-04-24"
8763
+ last_updated: "2026-05-12"
8763
8764
  description: "Complete reference of all ai-toolkit hooks: events, scripts, installation, and runtime behavior."
8764
8765
  ---
8765
8766
 
@@ -8767,7 +8768,7 @@ description: "Complete reference of all ai-toolkit hooks: events, scripts, insta
8767
8768
 
8768
8769
  ## Overview
8769
8770
 
8770
- ai-toolkit provides 21 global hook entries across 12 lifecycle events that enforce quality, safety, and workflow rules across all Claude Code sessions. Hooks are merged into `~/.claude/settings.json` on install, with logic in standalone scripts at `~/.softspark/ai-toolkit/hooks/`.
8771
+ ai-toolkit provides 22 global hook entries across 12 lifecycle events that enforce quality, safety, and workflow rules across all Claude Code sessions. Hooks are merged into `~/.claude/settings.json` on install, with logic in standalone scripts at `~/.softspark/ai-toolkit/hooks/`.
8771
8772
 
8772
8773
  ## Supported Surface
8773
8774
 
@@ -8918,6 +8919,19 @@ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
8918
8919
 
8919
8920
  Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
8920
8921
 
8922
+ ### Stop (quality gate) — `quality-gate.sh`
8923
+
8924
+ | Field | Value |
8925
+ |-------|-------|
8926
+ | Event | `Stop` |
8927
+ | Matcher | *(all)* |
8928
+ | Script | `~/.softspark/ai-toolkit/hooks/quality-gate.sh` |
8929
+ | Fires | Before Claude is allowed to finish a response |
8930
+
8931
+ **Action:** Runs lint/typecheck. **Blocks stopping (exit 2)** if errors found, so Claude must continue and fix the issues. Missing local tooling is reported as skipped rather than blocking the session.
8932
+
8933
+ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
8934
+
8921
8935
  ### TaskCompleted — `quality-gate.sh`
8922
8936
 
8923
8937
  | Field | Value |
@@ -9116,7 +9130,7 @@ Set in `.claude/settings.local.json`:
9116
9130
  ├── PreToolUse → guard-destructive.sh, guard-path.sh, guard-config.sh, commit-quality.sh
9117
9131
  ├── UserPromptSubmit → user-prompt-submit.sh, track-usage.sh
9118
9132
  ├── PostToolUse → post-tool-use.sh, governance-capture.sh
9119
- ├── Stop → quality-check.sh, save-session.sh
9133
+ ├── Stop → quality-check.sh, save-session.sh, quality-gate.sh
9120
9134
  ├── TaskCompleted → quality-gate.sh
9121
9135
  ├── TeammateIdle → echo (inline)
9122
9136
  ├── SubagentStart → subagent-start.sh
@@ -11749,7 +11763,7 @@ category: reference
11749
11763
  service: ai-toolkit
11750
11764
  tags: [features, differentiators, constitution, hooks, security, tdd, memory]
11751
11765
  created: "2026-04-13"
11752
- last_updated: "2026-04-13"
11766
+ last_updated: "2026-05-12"
11753
11767
  description: "Detailed description of ai-toolkit's unique features: constitution enforcement, hooks system, security scanning, effort budgeting, quality gates, and more."
11754
11768
  ---
11755
11769
 
@@ -11757,7 +11771,7 @@ description: "Detailed description of ai-toolkit's unique features: constitution
11757
11771
 
11758
11772
  ## 1. Machine-Enforced Constitution
11759
11773
 
11760
- Unlike other toolkits that put safety rules in documentation only, ai-toolkit enforces a 5-article constitution via `PreToolUse` hooks. The hook actually **blocks** execution of:
11774
+ Unlike other toolkits that put safety rules in documentation only, ai-toolkit enforces a 6-article constitution via hooks. The hooks actually **block** execution of:
11761
11775
  - Mass deletion (`rm -rf`, `DROP TABLE`)
11762
11776
  - Blind overwrites of uncommitted work
11763
11777
  - Any action that could cause irreversible data loss
@@ -11766,7 +11780,7 @@ Unlike other toolkits that put safety rules in documentation only, ai-toolkit en
11766
11780
 
11767
11781
  Hook logic lives in `app/hooks/*.sh` — not inline JSON one-liners. Scripts are copied to `~/.softspark/ai-toolkit/hooks/` on install and referenced from `~/.claude/settings.json`. Easy to read, debug, and extend.
11768
11782
 
11769
- **12 lifecycle events / 21 global hook entries:**
11783
+ **12 lifecycle events / 22 global hook entries:**
11770
11784
 
11771
11785
  | Event | Script | Action |
11772
11786
  |-------|--------|--------|
@@ -11784,6 +11798,7 @@ Hook logic lives in `app/hooks/*.sh` — not inline JSON one-liners. Scripts are
11784
11798
  | PostToolUse | `governance-capture.sh` | Log security-sensitive operations to JSONL |
11785
11799
  | Stop | `quality-check.sh` | Multi-language lint (ruff/tsc/phpstan/dart/go) |
11786
11800
  | Stop | `save-session.sh` | Persist session context for cross-session continuity |
11801
+ | Stop | `quality-gate.sh` | Block final response on lint/type errors |
11787
11802
  | TaskCompleted | `quality-gate.sh` | Block task completion on lint/type errors |
11788
11803
  | SubagentStart | `subagent-start.sh` | Narrow-scope reminder for spawned subagents |
11789
11804
  | SubagentStop | `subagent-stop.sh` | Completion checklist for subagent handoff |
package/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.2.0",
2
+ "version": "4.2.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": "21 global hook entries across 12 lifecycle events + 5 skill-scoped lifecycle hooks",
19
+ "description": "22 global hook entries across 12 lifecycle events + 5 skill-scoped lifecycle hooks",
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",
@@ -30,7 +30,7 @@
30
30
  "tags": ["plugins", "packs", "experimental"]
31
31
  },
32
32
  "constitution": {
33
- "description": "5-article machine-enforced safety constitution",
33
+ "description": "6-article machine-enforced safety constitution",
34
34
  "path": "app/constitution.md",
35
35
  "target": ".claude/constitution.md",
36
36
  "type": "symlink",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softspark/ai-toolkit",
3
- "version": "4.2.0",
3
+ "version": "4.2.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",
@@ -120,20 +120,56 @@ def _entry_source(entry: dict) -> str | None:
120
120
  return None
121
121
 
122
122
 
123
- def strip_source(hooks: dict, source: str) -> dict:
123
+ def _entry_signature(entry: dict) -> tuple:
124
+ """Return behavior-defining hook fields without source tags."""
125
+ handlers = []
126
+ for hook in entry.get("hooks", []):
127
+ if not isinstance(hook, dict):
128
+ handlers.append(hook)
129
+ continue
130
+ handlers.append(tuple(sorted(
131
+ (key, value)
132
+ for key, value in hook.items()
133
+ if key != "_source"
134
+ )))
135
+ return (entry.get("matcher", ""), tuple(handlers))
136
+
137
+
138
+ def strip_source(hooks: dict, source: str, replacement_hooks: dict | None = None) -> dict:
124
139
  """Remove all entries whose ``_source`` matches *source*.
125
140
 
126
141
  Args:
127
142
  hooks: Existing hooks dict (event-name -> list of entries).
128
143
  source: Source tag to strip.
144
+ replacement_hooks: Optional hook entries being re-injected. When
145
+ provided, untagged legacy entries with the same event/matcher/
146
+ handler payload are stripped as belonging to the same source.
129
147
 
130
148
  Returns:
131
149
  New hooks dict with matching entries removed. Empty event lists
132
150
  are omitted.
133
151
  """
152
+ legacy_signatures: dict[str, set[tuple]] = {}
153
+ if replacement_hooks:
154
+ for event, entries in replacement_hooks.items():
155
+ legacy_signatures[event] = {
156
+ _entry_signature(entry)
157
+ for entry in entries
158
+ if isinstance(entry, dict)
159
+ }
160
+
134
161
  result: dict = {}
135
162
  for event, entries in hooks.items():
136
- filtered = [e for e in entries if _entry_source(e) != source]
163
+ signatures = legacy_signatures.get(event, set())
164
+ filtered = [
165
+ e for e in entries
166
+ if _entry_source(e) != source
167
+ and not (
168
+ isinstance(e, dict)
169
+ and _entry_source(e) is None
170
+ and _entry_signature(e) in signatures
171
+ )
172
+ ]
137
173
  if filtered:
138
174
  result[event] = filtered
139
175
  return result
@@ -174,7 +210,7 @@ def merge_hooks(new_hooks: dict, existing_hooks: dict, source: str) -> dict:
174
210
  Returns:
175
211
  Merged hooks dict.
176
212
  """
177
- merged = strip_source(existing_hooks, source)
213
+ merged = strip_source(existing_hooks, source, new_hooks)
178
214
  for event, entries in new_hooks.items():
179
215
  if event not in merged:
180
216
  merged[event] = []
@@ -27,6 +27,23 @@ import os
27
27
  import sys
28
28
 
29
29
  SOURCE_TAG = "ai-toolkit"
30
+ LEGACY_TOOLKIT_HOOKS = {
31
+ "Notification": [
32
+ {
33
+ "matcher": "",
34
+ "hooks": [
35
+ {
36
+ "type": "command",
37
+ "command": (
38
+ "osascript -e 'display notification "
39
+ '"Claude Code needs your attention" with title "Claude Code"'
40
+ "'"
41
+ ),
42
+ }
43
+ ],
44
+ }
45
+ ],
46
+ }
30
47
 
31
48
 
32
49
  def load_json(path: str) -> dict:
@@ -65,11 +82,55 @@ def _is_toolkit_entry(entry: dict) -> bool:
65
82
  return False
66
83
 
67
84
 
68
- def strip_toolkit(hooks: dict) -> dict:
69
- """Remove all entries tagged with _source == SOURCE_TAG."""
85
+ def _entry_signature(entry: dict) -> tuple:
86
+ """Return the behavior-defining parts of a hook entry.
87
+
88
+ Older ai-toolkit installs wrote hook entries without ``_source``. Matching
89
+ on the event, matcher, and handler payload lets current installs remove
90
+ those legacy duplicates while preserving unrelated user hooks.
91
+ """
92
+ handlers = []
93
+ for hook in entry.get("hooks", []):
94
+ if not isinstance(hook, dict):
95
+ handlers.append(hook)
96
+ continue
97
+ handlers.append(tuple(sorted(
98
+ (key, value)
99
+ for key, value in hook.items()
100
+ if key != "_source"
101
+ )))
102
+ return (entry.get("matcher", ""), tuple(handlers))
103
+
104
+
105
+ def strip_toolkit(hooks: dict, toolkit_hooks: dict | None = None) -> dict:
106
+ """Remove entries tagged with ai-toolkit or matching legacy toolkit hooks."""
107
+ legacy_signatures: dict[str, set[tuple]] = {}
108
+ if toolkit_hooks:
109
+ for event, entries in toolkit_hooks.items():
110
+ legacy_signatures[event] = {
111
+ _entry_signature(entry)
112
+ for entry in entries
113
+ if isinstance(entry, dict)
114
+ }
115
+ for event, entries in LEGACY_TOOLKIT_HOOKS.items():
116
+ legacy_signatures.setdefault(event, set()).update(
117
+ _entry_signature(entry)
118
+ for entry in entries
119
+ if isinstance(entry, dict)
120
+ )
121
+
70
122
  result = {}
71
123
  for event, entries in hooks.items():
72
- filtered = [e for e in entries if not _is_toolkit_entry(e)]
124
+ signatures = legacy_signatures.get(event, set())
125
+ filtered = [
126
+ e
127
+ for e in entries
128
+ if not _is_toolkit_entry(e)
129
+ and not (
130
+ isinstance(e, dict)
131
+ and _entry_signature(e) in signatures
132
+ )
133
+ ]
73
134
  if filtered:
74
135
  result[event] = filtered
75
136
  return result
@@ -85,7 +146,7 @@ def merge(toolkit_hooks: dict, target_hooks: dict) -> dict:
85
146
  Returns:
86
147
  Merged hooks dictionary with old toolkit entries replaced by new ones.
87
148
  """
88
- merged = strip_toolkit(target_hooks)
149
+ merged = strip_toolkit(target_hooks, toolkit_hooks)
89
150
  for event, entries in toolkit_hooks.items():
90
151
  if event not in merged:
91
152
  merged[event] = []