@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 +45 -0
- package/README.md +10 -17
- package/app/.claude-plugin/plugin.json +1 -1
- package/app/ARCHITECTURE.md +1 -1
- package/app/hooks/quality-gate.sh +38 -26
- package/app/hooks.json +10 -0
- package/benchmarks/ecosystem-doctor-snapshot.json +15 -15
- package/kb/reference/architecture-overview.md +4 -3
- package/kb/reference/hooks-catalog.md +17 -4
- package/kb/reference/unique-features.md +4 -3
- package/llms-full.txt +25 -10
- package/manifest.json +3 -3
- package/package.json +1 -1
- package/scripts/inject_hook_cli.py +39 -3
- package/scripts/merge-hooks.py +65 -4
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)
|
|
7
7
|
[](app/skills/)
|
|
8
8
|
[](app/agents/)
|
|
9
|
-
[](tests/)
|
|
10
10
|
|
|
11
|
-
|
|
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
|
-
- **
|
|
22
|
-
- **
|
|
23
|
-
- **
|
|
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/` |
|
|
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 (
|
|
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 (
|
|
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
|
-
**
|
|
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.
|
|
4
|
+
"version": "4.2.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 | 12 events /
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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:
|
|
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
|
-
|
|
20
|
-
|
|
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
|
-
|
|
28
|
-
|
|
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
|
-
|
|
35
|
-
|
|
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
|
-
|
|
42
|
-
|
|
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-
|
|
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": "
|
|
27
|
+
"docs_hash": "b746d5dbbc5a2936",
|
|
28
28
|
"headings": [
|
|
29
29
|
"Agent",
|
|
30
|
-
"
|
|
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": "
|
|
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":
|
|
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.
|
|
106
|
+
"version": "2.1.139 (Claude Code)"
|
|
107
107
|
},
|
|
108
108
|
"cline": {
|
|
109
|
-
"docs_hash": "
|
|
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": "
|
|
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
|
|
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.
|
|
189
|
+
"version": "codex-cli 0.130.0"
|
|
190
190
|
},
|
|
191
191
|
"cursor": {
|
|
192
|
-
"docs_hash": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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.
|
|
6
|
+
version: "1.4.5"
|
|
7
7
|
created: "2026-03-23"
|
|
8
|
-
last_updated: "2026-
|
|
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
|
-
|
|
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.
|
|
6
|
+
version: "1.5.1"
|
|
7
7
|
created: "2026-03-27"
|
|
8
|
-
last_updated: "2026-
|
|
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
|
|
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-
|
|
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
|
|
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 /
|
|
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.
|
|
6094
|
+
version: "1.4.5"
|
|
6095
6095
|
created: "2026-03-23"
|
|
6096
|
-
last_updated: "2026-
|
|
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
|
-
|
|
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.
|
|
8761
|
+
version: "1.5.1"
|
|
8761
8762
|
created: "2026-03-27"
|
|
8762
|
-
last_updated: "2026-
|
|
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
|
|
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-
|
|
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
|
|
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 /
|
|
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.
|
|
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": "
|
|
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": "
|
|
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.
|
|
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
|
|
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
|
-
|
|
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] = []
|
package/scripts/merge-hooks.py
CHANGED
|
@@ -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
|
|
69
|
-
"""
|
|
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
|
-
|
|
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] = []
|