@softspark/ai-toolkit 4.16.1 → 4.18.0
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 +84 -0
- package/README.md +13 -19
- package/app/.claude-plugin/plugin.json +1 -1
- package/app/hooks/session-end.sh +1 -13
- package/app/hooks.json +0 -10
- package/app/plugins/README.md +16 -4
- package/app/plugins/rtk-pack/README.md +123 -0
- package/app/plugins/rtk-pack/hooks/rewrite.sh +79 -0
- package/app/plugins/rtk-pack/plugin.json +60 -0
- package/app/plugins/rtk-pack/scripts/init.py +252 -0
- package/app/plugins/rtk-pack/scripts/status.py +105 -0
- package/benchmarks/ecosystem-doctor-snapshot.json +14 -15
- package/bin/ai-toolkit.js +15 -2
- package/kb/history/completed/output-filter-retirement-20260726.md +128 -0
- package/kb/history/completed/rtk-pack-integration-20260726.md +710 -0
- package/kb/procedures/maintenance-sop.md +1 -1
- package/kb/procedures/release-preparation-sop.md +8 -3
- package/kb/procedures/rtk-upstream-sync-sop.md +279 -0
- package/kb/reference/architecture-overview.md +2 -3
- package/kb/reference/cli-reference.md +4 -14
- package/kb/reference/enterprise-config-guide.md +1 -21
- package/kb/reference/hooks-catalog.md +3 -60
- package/kb/reference/plugin-pack-conventions.md +17 -2
- package/kb/reference/supported-tools-registry.md +0 -4
- package/llms-full.txt +1171 -402
- package/llms.txt +3 -1
- package/manifest.json +147 -36
- package/package.json +1 -2
- package/scripts/audit_skills.py +21 -0
- package/scripts/claude_app.py +2 -21
- package/scripts/config_cli.py +4 -0
- package/scripts/config_merger.py +0 -17
- package/scripts/config_validator.py +11 -138
- package/scripts/doctor.py +3 -20
- package/scripts/install.py +2 -1
- package/scripts/install_steps/ai_tools.py +28 -99
- package/scripts/install_steps/hooks.py +26 -24
- package/scripts/merge-hooks.py +33 -2
- package/scripts/output_filter_retirement.py +395 -0
- package/scripts/plugin.py +136 -16
- package/scripts/schemas/ai-toolkit-config.schema.json +0 -60
- package/scripts/uninstall.py +13 -27
- package/scripts/verify_rtk_binary.py +335 -0
- package/app/hooks/filter-tool-output.sh +0 -76
- package/app/output-filter-policy.json +0 -15
- package/benchmarks/output-filter/README.md +0 -11
- package/benchmarks/output-filter/scenarios.json +0 -25
- package/kb/reference/tool-output-filter.md +0 -288
- package/scripts/benchmark_output_filter.py +0 -343
- package/scripts/output_filter_cli.py +0 -347
- package/scripts/output_filter_hook.py +0 -23
- package/scripts/tool_output_filter/__init__.py +0 -33
- package/scripts/tool_output_filter/contracts.py +0 -173
- package/scripts/tool_output_filter/engine.py +0 -260
- package/scripts/tool_output_filter/hook_runtime.py +0 -369
- package/scripts/tool_output_filter/input.py +0 -56
- package/scripts/tool_output_filter/invariants.py +0 -40
- package/scripts/tool_output_filter/policy.py +0 -153
- package/scripts/tool_output_filter/profiles/__init__.py +0 -68
- package/scripts/tool_output_filter/profiles/repeat_lines.py +0 -71
- package/scripts/tool_output_filter/profiles/tap_success.py +0 -154
- package/scripts/tool_output_filter/recovery.py +0 -846
- package/scripts/tool_output_filter/telemetry.py +0 -13
|
@@ -291,7 +291,7 @@ python3 scripts/validate.py --strict
|
|
|
291
291
|
ai-toolkit plugin list # show available packs
|
|
292
292
|
ai-toolkit plugin install --editor claude <name> # install for Claude Code global target
|
|
293
293
|
ai-toolkit plugin install --editor codex <name> # install for Codex global target
|
|
294
|
-
ai-toolkit plugin install --editor all --all # install all
|
|
294
|
+
ai-toolkit plugin install --editor all --all # install all 12 packs for both runtimes
|
|
295
295
|
ai-toolkit plugin update --editor all --all # re-apply all installed packs after toolkit updates
|
|
296
296
|
ai-toolkit plugin clean <name> # prune data older than 90 days
|
|
297
297
|
ai-toolkit plugin clean <name> --days 30 # custom retention
|
|
@@ -250,12 +250,17 @@ Add entry at the top of `CHANGELOG.md` (after the header, before previous releas
|
|
|
250
250
|
|
|
251
251
|
## Phase 4: Regenerate Artifacts
|
|
252
252
|
|
|
253
|
+
Use the npm scripts, not the generators directly:
|
|
254
|
+
|
|
253
255
|
```bash
|
|
254
|
-
python3 scripts/generate_agents_md.py > AGENTS.md
|
|
255
|
-
|
|
256
|
-
python3 scripts/generate_llms_txt.py --full > llms-full.txt
|
|
256
|
+
npm run generate:agents # AI_TOOLKIT_NO_CUSTOM_RULES=1 python3 scripts/generate_agents_md.py > AGENTS.md
|
|
257
|
+
npm run generate:llms # llms.txt + llms-full.txt
|
|
257
258
|
```
|
|
258
259
|
|
|
260
|
+
`generate:agents` sets `AI_TOOLKIT_NO_CUSTOM_RULES=1`. Running
|
|
261
|
+
`generate_agents_md.py` bare picks up whatever is registered in the maintainer's
|
|
262
|
+
own `~/.softspark/ai-toolkit/rules/`, which then ships inside `AGENTS.md`.
|
|
263
|
+
|
|
259
264
|
Check if anything actually changed:
|
|
260
265
|
|
|
261
266
|
```bash
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "SOP: rtk Upstream Sync"
|
|
3
|
+
category: procedures
|
|
4
|
+
service: ai-toolkit
|
|
5
|
+
tags: [sop, rtk, rtk-pack, upstream, cross-build, telemetry, checksum, port-validation, advisory]
|
|
6
|
+
version: "1.0.0"
|
|
7
|
+
created: "2026-07-26"
|
|
8
|
+
last_updated: "2026-07-26"
|
|
9
|
+
description: "Procedure for moving rtk-pack to a newer upstream rtk release: detect the tag, review the files we depend on, re-validate the Python port that every coverage number rests on, rebuild five targets with telemetry undefined, verify silence, publish to our own release namespace, and bump the pack. Written after the v0.43.0 to v0.44.0 bump, which changed every file this SOP names."
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# SOP: rtk Upstream Sync
|
|
13
|
+
|
|
14
|
+
Moves `rtk-pack` from one pinned upstream tag to the next.
|
|
15
|
+
|
|
16
|
+
Currently pinned: **v0.44.0**, shipped as
|
|
17
|
+
`softspark-rtk-v0.44.0-1`. The pin lives in
|
|
18
|
+
`app/plugins/rtk-pack/plugin.json` under `upstream.version`.
|
|
19
|
+
|
|
20
|
+
Upstream ships stable tags roughly every two to four weeks behind a long
|
|
21
|
+
release-candidate train (300+ RCs preceded v0.44.0). Do not track RCs.
|
|
22
|
+
|
|
23
|
+
Background and the measured numbers: `kb/history/completed/rtk-pack-integration-20260726.md`.
|
|
24
|
+
|
|
25
|
+
## Why this SOP is not "just rebuild"
|
|
26
|
+
|
|
27
|
+
The v0.43.0 to v0.44.0 bump was 200 commits and touched **every file listed in
|
|
28
|
+
Phase 2 below**. It also inverted pipeline rewriting: v0.43.0 rewrote the first
|
|
29
|
+
stage of a pipeline, v0.44.0 rewrites the last. A rebuild without the review
|
|
30
|
+
step would have shipped that silently, and the coverage numbers quoted to users
|
|
31
|
+
would have described a version we no longer ship.
|
|
32
|
+
|
|
33
|
+
## Phase 1: Detect
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
gh api repos/rtk-ai/rtk/releases --paginate \
|
|
37
|
+
--jq '.[] | select(.tag_name | test("^v[0-9]")) | "\(.tag_name)\t\(.published_at)"' | head -5
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Compare against `upstream.version` in `app/plugins/rtk-pack/plugin.json`.
|
|
41
|
+
|
|
42
|
+
Cadence: on demand, plus a check folded into release preparation.
|
|
43
|
+
|
|
44
|
+
## Phase 2: Review before building
|
|
45
|
+
|
|
46
|
+
Fetch the diff for the areas the pack depends on:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
gh api repos/rtk-ai/rtk/compare/<pinned>...<new> \
|
|
50
|
+
--jq '{ahead: .ahead_by, files: [.files[] | {f: .filename, add: .additions, del: .deletions}]}'
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Read the changelog, then diff these specifically:
|
|
54
|
+
|
|
55
|
+
| File | Why it matters |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `src/discover/registry.rs` | rewrite eligibility, pipeline handling, the TOML call sites |
|
|
58
|
+
| `src/discover/rules.rs` | the rule table and `IGNORED_PREFIXES` |
|
|
59
|
+
| `src/discover/lexer.rs` | tokenisation and `contains_unattestable_construct` |
|
|
60
|
+
| `src/core/toml_filter.rs` | the filter DSL, which affects users who write their own filters |
|
|
61
|
+
| `src/hooks/trust.rs` | the trust gate and its paths |
|
|
62
|
+
| `src/hooks/hook_cmd.rs` | the Claude hook contract and permission handling |
|
|
63
|
+
| `src/core/telemetry.rs`, `src/core/telemetry_cmd.rs` | the compile-time endpoint gate |
|
|
64
|
+
| `Cargo.toml`, `Cargo.lock` | native deps, the MSRV, and new advisories |
|
|
65
|
+
|
|
66
|
+
**A change to the DSL, to trust handling, or to the permission flow is a
|
|
67
|
+
stop-and-think, not a rebuild.** In particular:
|
|
68
|
+
|
|
69
|
+
- The pack ships no filters of its own, so a DSL change cannot break us. It can
|
|
70
|
+
still break a **user's** `filters.toml`, which upstream then skips silently
|
|
71
|
+
(`toml_filter.rs:220-221`). Worth a release-note line, not a code change.
|
|
72
|
+
- If `hook_cmd.rs` changes when `permissionDecision` is emitted, the trust
|
|
73
|
+
boundary documented in the pack README changes with it.
|
|
74
|
+
- If `IGNORED_PREFIXES` or the `pipeline_final_safe` rule set moves, every
|
|
75
|
+
coverage number is stale.
|
|
76
|
+
|
|
77
|
+
## Phase 3: Re-validate the port
|
|
78
|
+
|
|
79
|
+
Every coverage and saving figure the pack quotes comes from `rtk_port.py`, a
|
|
80
|
+
Python model of rtk's rewrite pipeline. **Anything short of full agreement
|
|
81
|
+
invalidates those numbers until the port is fixed.** Tooling lives in
|
|
82
|
+
`~/rtk-measurement-archive/`.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
git clone --depth 1 --branch <new-tag> https://github.com/rtk-ai/rtk.git /tmp/rtk-new
|
|
86
|
+
cd ~/rtk-measurement-archive
|
|
87
|
+
|
|
88
|
+
# Rewrite assertions from the tag's own test block.
|
|
89
|
+
python3 extract_cases.py /tmp/rtk-new/src/discover/registry.rs cases.json
|
|
90
|
+
python3 validate_port.py cases.json
|
|
91
|
+
|
|
92
|
+
# The entry gate both real hook paths apply before rewrite_command.
|
|
93
|
+
# Its assertions live in lexer.rs; re-extract if that block moved.
|
|
94
|
+
python3 -c "import entry_gate; print('gate import ok')"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Baseline at v0.44.0: **203/203** rewrite assertions, **35/35** gate assertions.
|
|
98
|
+
|
|
99
|
+
If the port diverges, fix the port first, then re-measure:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
python3 measure_gated.py gated 0:1224
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Measure over the whole transcript pool, never the default 134-file window: at
|
|
106
|
+
that size the projection swings 8.8x on an unchanged mechanism.
|
|
107
|
+
|
|
108
|
+
### 3.1 Re-measure, do not just re-validate
|
|
109
|
+
|
|
110
|
+
Port agreement proves rtk still rewrites the same commands. It says nothing
|
|
111
|
+
about how much each rewrite saves, and that is where the value actually sits.
|
|
112
|
+
|
|
113
|
+
**`rtk grep` carries 4.04 MB of the 5.66 MB measurable saving: 71% of the total
|
|
114
|
+
rests on one family.** If upstream changes that one filter, the headline number
|
|
115
|
+
moves even with the port at full agreement. Measured effectiveness has already
|
|
116
|
+
diverged from expectation in both directions once: `rtk grep` measured 22.3%
|
|
117
|
+
against 9.0% modelled, `rtk rg` 7.0% against 30% modelled.
|
|
118
|
+
|
|
119
|
+
So on every bump, after the port passes, replay against the newly built binary:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
python3 replay_rtk.py --pool 1300 --rtk <path-to-the-new-binary>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Compare per family against the recorded baseline:
|
|
126
|
+
|
|
127
|
+
| Family | Measured at v0.44.0 | Share of total saving |
|
|
128
|
+
|---|---:|---:|
|
|
129
|
+
| `rtk grep` | 22.3% | 71% |
|
|
130
|
+
| `rtk git` | 33.1% | 21% |
|
|
131
|
+
| `rtk find` | 35.6% | 8% |
|
|
132
|
+
|
|
133
|
+
**Act on the result, do not just record it.** If the total lands below the
|
|
134
|
+
published kill number of **0.05% of input tokens**, the pack has stopped earning
|
|
135
|
+
its supply-chain surface and retiring it is the correct outcome, exactly as the
|
|
136
|
+
in-house filter was retired. The margin at v0.44.0 is 0.0615%, which is 1.23x
|
|
137
|
+
the kill number, so a single-family regression is enough to cross it.
|
|
138
|
+
|
|
139
|
+
## Phase 4: Rebuild
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
gh workflow run rtk-build.yml --ref main \
|
|
143
|
+
-f upstream_tag=<new-tag> -f build_revision=1 -f rust_version=<pinned> -f publish=false
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Five targets: `x86_64-apple-darwin`, `aarch64-apple-darwin`,
|
|
147
|
+
`x86_64-unknown-linux-musl`, `aarch64-unknown-linux-gnu`,
|
|
148
|
+
`x86_64-pc-windows-msvc`. A target that will not build is dropped, not faked.
|
|
149
|
+
|
|
150
|
+
Pin `rust_version` explicitly. Upstream uses unpinned `stable` with
|
|
151
|
+
`warnings = "deny"`, so a new rustc lint can turn the build red with no change
|
|
152
|
+
on either side.
|
|
153
|
+
|
|
154
|
+
**Advisories.** The `audit` job blocks. Re-derive the disposition rather than
|
|
155
|
+
carrying the previous one forward:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
# Cross-reference the new lockfile against OSV without waiting for CI.
|
|
159
|
+
python3 - <<'PY'
|
|
160
|
+
import json, re, urllib.request, pathlib
|
|
161
|
+
lock = pathlib.Path("/tmp/rtk-new/Cargo.lock").read_text()
|
|
162
|
+
pkgs = [(re.search(r'^name = "([^"]+)"', b, re.M).group(1),
|
|
163
|
+
re.search(r'^version = "([^"]+)"', b, re.M).group(1))
|
|
164
|
+
for b in lock.split("[[package]]")[1:]
|
|
165
|
+
if re.search(r'^name = ', b, re.M) and re.search(r'^version = ', b, re.M)]
|
|
166
|
+
q = [{"package": {"name": n, "ecosystem": "crates.io"}, "version": v} for n, v in pkgs]
|
|
167
|
+
req = urllib.request.Request("https://api.osv.dev/v1/querybatch",
|
|
168
|
+
data=json.dumps({"queries": q}).encode(), headers={"Content-Type": "application/json"})
|
|
169
|
+
res = json.load(urllib.request.urlopen(req, timeout=60))
|
|
170
|
+
for (n, v), r in zip(pkgs, res["results"]):
|
|
171
|
+
if r.get("vulns"):
|
|
172
|
+
print(n, v, [x["id"] for x in r["vulns"]])
|
|
173
|
+
PY
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
For each advisory decide, and record the reason in the workflow:
|
|
177
|
+
|
|
178
|
+
- **In-range fix** (`cargo update -p <crate>` works): add the crate to
|
|
179
|
+
`RTK_CARGO_UPDATES` in `.github/workflows/rtk-build.yml`. Both the audit job
|
|
180
|
+
and every build job apply it, so we audit what we ship.
|
|
181
|
+
- **Needs a `Cargo.toml` change**: that is a source modification and breaks the
|
|
182
|
+
NOTICE claim. Ignore with a written reason, or escalate.
|
|
183
|
+
- **Carried-forward ignores**: re-check every `--ignore` still applies. The
|
|
184
|
+
quick-xml pair exists only because upstream pins `"0.37"`; **delete both the
|
|
185
|
+
moment upstream moves to 0.41 or later** rather than carrying them.
|
|
186
|
+
|
|
187
|
+
## Phase 5: Verify
|
|
188
|
+
|
|
189
|
+
CI asserts this per target and the run fails on any `fail` verdict:
|
|
190
|
+
|
|
191
|
+
1. `RTK_TELEMETRY_URL` and `RTK_TELEMETRY_TOKEN` unset at build time
|
|
192
|
+
2. the artifact starts and reports the expected version
|
|
193
|
+
3. no telemetry state written into a sandboxed home
|
|
194
|
+
4. on Linux, identical behaviour with no network route
|
|
195
|
+
5. every archive holds exactly one flat entry
|
|
196
|
+
|
|
197
|
+
Then check by hand:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
gh run download <run-id> --dir /tmp/rtk-verify
|
|
201
|
+
cd /tmp/rtk-verify && shasum -a 256 -c checksums.txt
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Three things CI cannot tell you:
|
|
205
|
+
|
|
206
|
+
- **`strings` markers.** Compare `tls_markers_present` per target against the
|
|
207
|
+
previous build. Their absence is an LLVM outcome under LTO, not a guarantee,
|
|
208
|
+
so a sudden appearance means the telemetry stack survived and is worth
|
|
209
|
+
understanding before shipping.
|
|
210
|
+
- **Reproducibility.** Four of five targets are bit-reproducible; compare
|
|
211
|
+
extracted binaries, never the tarballs, because gzip records a timestamp.
|
|
212
|
+
`x86_64-pc-windows-msvc` differs by 24 bytes per link (MSVC timestamp plus a
|
|
213
|
+
CodeView GUID), so a changed Windows digest proves nothing on its own.
|
|
214
|
+
- **Upstream has no test asserting network silence.** That property can regress
|
|
215
|
+
on any bump without turning their CI red. We own it.
|
|
216
|
+
|
|
217
|
+
## Phase 6: Publish
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
gh workflow run rtk-build.yml --ref main \
|
|
221
|
+
-f upstream_tag=<new-tag> -f build_revision=1 -f rust_version=<pinned> -f publish=true
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Creates `softspark-rtk-<upstream>-<revision>`. Bump the revision, not the
|
|
225
|
+
upstream part, when rebuilding the same upstream tag.
|
|
226
|
+
|
|
227
|
+
## Phase 7: Bump the pack
|
|
228
|
+
|
|
229
|
+
In `app/plugins/rtk-pack/plugin.json`:
|
|
230
|
+
|
|
231
|
+
- `upstream.version` to the new tag
|
|
232
|
+
- `binary.release_tag` to the new release
|
|
233
|
+
- every `assets.*.sha256` from the published `checksums.txt`
|
|
234
|
+
- `version` (the pack's own) — bump it whether or not the upstream tag moved,
|
|
235
|
+
because `plugin update` skips a pack whose recorded version still matches, so
|
|
236
|
+
an unbumped pack never reaches installed users. The two fields are separate so
|
|
237
|
+
a pack-only fix does not pretend to be an upstream bump
|
|
238
|
+
|
|
239
|
+
Then:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
npm test # tests/test_rtk_pack.bats asserts digest shape and layout
|
|
243
|
+
python3 scripts/validate.py --strict
|
|
244
|
+
python3 scripts/audit_skills.py --ci
|
|
245
|
+
shellcheck --severity=warning app/hooks/*.sh app/plugins/*/hooks/*.sh
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Verify a real install end to end, against the published release rather than a
|
|
249
|
+
mirror:
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
H=$(mktemp -d)
|
|
253
|
+
AI_TOOLKIT_DATA_DIR="$H" python3 app/plugins/rtk-pack/scripts/init.py
|
|
254
|
+
AI_TOOLKIT_DATA_DIR="$H" python3 app/plugins/rtk-pack/scripts/status.py
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Phase 8: Ship
|
|
258
|
+
|
|
259
|
+
The pack version bump reaches installed users through `ai-toolkit update`,
|
|
260
|
+
which runs `plugin update --editor all --all`. A pack whose recorded version
|
|
261
|
+
matches its manifest is skipped silently, so the bump in Phase 7 is what makes
|
|
262
|
+
the update fire at all. Forgetting it means nobody gets the new binary.
|
|
263
|
+
|
|
264
|
+
## Phase 9: Record the licence position
|
|
265
|
+
|
|
266
|
+
If upstream relicenses, adds a `NOTICE`, or changes `DISCLAIMER.md`, update the
|
|
267
|
+
NOTICE generated in `.github/workflows/rtk-build.yml`. It currently records two
|
|
268
|
+
build-time differences: the undefined telemetry endpoint, and in-range lockfile
|
|
269
|
+
security updates. If Phase 4 changes that set, the NOTICE text changes with it.
|
|
270
|
+
|
|
271
|
+
`DISCLAIMER.md` is deliberately not redistributed because it states telemetry is
|
|
272
|
+
collected by default, which our builds contradict. Re-check that this is still
|
|
273
|
+
the reason before changing the decision.
|
|
274
|
+
|
|
275
|
+
## Related
|
|
276
|
+
|
|
277
|
+
- `kb/history/completed/rtk-pack-integration-20260726.md` — decisions, measurements, pre-mortem
|
|
278
|
+
- `kb/history/completed/output-filter-retirement-20260726.md` — why premise validation comes first
|
|
279
|
+
- `app/plugins/rtk-pack/README.md` — the user-facing trust boundary
|
|
@@ -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.
|
|
6
|
+
version: "1.6.0"
|
|
7
7
|
created: "2026-03-23"
|
|
8
|
-
last_updated: "2026-07-
|
|
8
|
+
last_updated: "2026-07-26"
|
|
9
9
|
description: "Architecture of ai-toolkit: directory layout, Claude app export, global install model, editor-aware MCP install, Codex translation layer, skill tiers, and integration with projects."
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -300,7 +300,6 @@ Agents (code-reviewer, debugger, devops-implementer, ...)
|
|
|
300
300
|
| UserPromptSubmit | Before user prompt execution | `track-usage.sh` | Record skill invocations to stats.json |
|
|
301
301
|
| PostToolUse | After edit/write tools | `post-tool-use.sh` | Lightweight validation reminders |
|
|
302
302
|
| PostToolUse | After any tool | `governance-capture.sh` | Log security-sensitive operations |
|
|
303
|
-
| PostToolUse | After successful Bash | `filter-tool-output.sh` | Observe eligible text or replace it only after exact recovery succeeds; Claude Code only |
|
|
304
303
|
| Stop | After response | `quality-check.sh` | Multi-language lint |
|
|
305
304
|
| Stop | After response | `save-session.sh` | Persist session context |
|
|
306
305
|
| Stop | Before final stop | `quality-gate.sh` | Block final response on lint/type errors |
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
title: "CLI Reference"
|
|
3
3
|
category: reference
|
|
4
4
|
service: ai-toolkit
|
|
5
|
-
tags: [cli, commands, reference, install, update, plugin, mcp, telemetry
|
|
5
|
+
tags: [cli, commands, reference, install, update, plugin, mcp, telemetry]
|
|
6
6
|
created: "2026-04-13"
|
|
7
|
-
last_updated: "2026-07-
|
|
7
|
+
last_updated: "2026-07-26"
|
|
8
8
|
description: "Complete CLI reference for all ai-toolkit commands, options, and flags."
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -58,7 +58,7 @@ Usage: ai-toolkit <command> [options]
|
|
|
58
58
|
|---------|-------------|
|
|
59
59
|
| `plugin list` | Show available plugin packs with install status |
|
|
60
60
|
| `plugin install <name> [--editor claude\|codex\|all]` | Install a plugin pack for Claude Code and/or Codex (`claude` means Claude Code, not the Claude app) |
|
|
61
|
-
| `plugin install --all [--editor claude\|codex\|all]` | Install all
|
|
61
|
+
| `plugin install --all [--editor claude\|codex\|all]` | Install all 12 plugin packs |
|
|
62
62
|
| `plugin update <name> [--editor claude\|codex\|all]` | Update a plugin pack (remove + reinstall, preserves data) |
|
|
63
63
|
| `plugin update --all [--editor claude\|codex\|all]` | Update all installed plugin packs |
|
|
64
64
|
| `plugin clean <name> [--days N]` | Prune old plugin data (default: 90 days) |
|
|
@@ -79,18 +79,8 @@ Usage: ai-toolkit <command> [options]
|
|
|
79
79
|
|
|
80
80
|
| Command | Description |
|
|
81
81
|
|---------|-------------|
|
|
82
|
-
|
|
83
|
-
| `output-filter inspect --profile repeat-lines\|tap-success` | Read stdin and print content-free candidate metadata without changing or echoing the input |
|
|
84
|
-
| `output-filter recover <handle> [--base-directory PATH] [--session-id ID]` | Print the exact stored native response object for an opaque recovery handle |
|
|
85
|
-
| `output-filter clean [--base-directory PATH] [--session-id ID]` | Remove owned filter artifacts for one session or all sessions in the current repository |
|
|
86
|
-
| `output-filter clean --session-id ID --expired` | Remove expired exact-response artifacts for one native session |
|
|
87
|
-
| `output-filter hook --policy PATH` | **Internal, not for manual use.** `PostToolUse` entry point that reads the native hook payload on stdin and emits any replacement. The installed hook `filter-tool-output.sh` calls the same runtime through the lean `output_filter_hook.py` process instead |
|
|
88
|
-
|
|
89
|
-
`off` is the default. Configure `toolOutputFilter.mode` in
|
|
90
|
-
`.softspark-toolkit.json`, then run `install --local` or `update --local`.
|
|
82
|
+
|
|
91
83
|
`AI_TOOLKIT_OUTPUT_FILTER_DISABLE=1` bypasses active filtering immediately.
|
|
92
|
-
See [Native Tool Output Filter](tool-output-filter.md) for eligibility,
|
|
93
|
-
privacy, recovery, and runtime support.
|
|
94
84
|
|
|
95
85
|
## Project Registry
|
|
96
86
|
|
|
@@ -10,7 +10,7 @@ tags:
|
|
|
10
10
|
- multi-repo
|
|
11
11
|
doc_type: reference
|
|
12
12
|
created: "2026-04-11"
|
|
13
|
-
last_updated: "2026-07-
|
|
13
|
+
last_updated: "2026-07-26"
|
|
14
14
|
description: "Comprehensive guide for setting up and using ai-toolkit configuration inheritance. Covers base config creation, project setup, enforcement rules, CI integration, and troubleshooting."
|
|
15
15
|
---
|
|
16
16
|
|
|
@@ -77,7 +77,6 @@ ai-toolkit config check # CI enforcement check
|
|
|
77
77
|
|-------|------|-------------|
|
|
78
78
|
| `extends` | string | Base config source (npm, git URL, local path) |
|
|
79
79
|
| `profile` | enum | `minimal`, `standard`, `strict`, `full`, `offline-slm` |
|
|
80
|
-
| `toolOutputFilter` | object | Native Bash result filtering policy: mode, profiles, savings thresholds, and recovery limits |
|
|
81
80
|
| `agents` | object | `enabled`, `disabled`, `custom` arrays |
|
|
82
81
|
| `plugins` | object | Resolved `enabled` and `disabled` plugin intent |
|
|
83
82
|
| `rules` | object | `inject`, `remove` arrays |
|
|
@@ -157,25 +156,6 @@ effective intent explicit and causes validation to reject a conflicting
|
|
|
157
156
|
`plugins.disabled` entry. Install the named plugin pack separately with
|
|
158
157
|
`ai-toolkit plugin install <name>` or your organization deployment workflow.
|
|
159
158
|
|
|
160
|
-
### Native tool-output filtering
|
|
161
|
-
|
|
162
|
-
Projects can opt into the dependency-free output filter without changing the
|
|
163
|
-
organization-wide default:
|
|
164
|
-
|
|
165
|
-
```json
|
|
166
|
-
{
|
|
167
|
-
"toolOutputFilter": {
|
|
168
|
-
"mode": "observe",
|
|
169
|
-
"profiles": ["repeat-lines", "tap-success"]
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
`ai-toolkit install --local` materializes the effective policy as the managed
|
|
175
|
-
`.claude/ai-toolkit-output-filter.json` file. `off` is the default, `observe`
|
|
176
|
-
collects metadata without changing model-visible output, and `safe` permits
|
|
177
|
-
replacement only when every invariant and exact-recovery check succeeds.
|
|
178
|
-
|
|
179
159
|
### Overrides
|
|
180
160
|
|
|
181
161
|
Projects can override base settings, but must declare intent:
|
|
@@ -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.
|
|
6
|
+
version: "1.9.0"
|
|
7
7
|
created: "2026-03-27"
|
|
8
|
-
last_updated: "2026-07-
|
|
8
|
+
last_updated: "2026-07-26"
|
|
9
9
|
description: "Complete reference of all ai-toolkit hooks: events, scripts, installation, and runtime behavior."
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -31,9 +31,6 @@ ai-toolkit update # re-copies scripts, re-merges (idempotent)
|
|
|
31
31
|
**File locations:**
|
|
32
32
|
|
|
33
33
|
- Scripts: `~/.softspark/ai-toolkit/hooks/*.sh`
|
|
34
|
-
- Output-filter runtime: `~/.softspark/ai-toolkit/scripts/output_filter_hook.py`, `output_filter_cli.py`, and `tool_output_filter/`
|
|
35
|
-
- Global output-filter policy: `~/.softspark/ai-toolkit/hooks/output-filter-policy.json`
|
|
36
|
-
- Managed project policy: `<project>/.claude/ai-toolkit-output-filter.json`
|
|
37
34
|
- Config: `~/.claude/settings.json` → `hooks` key
|
|
38
35
|
- Source: `ai-toolkit/app/hooks/*.sh` + `app/hooks.json`
|
|
39
36
|
|
|
@@ -178,59 +175,6 @@ arms the corrective Stop hook.
|
|
|
178
175
|
|
|
179
176
|
Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
|
|
180
177
|
|
|
181
|
-
### PostToolUse (native Bash output filter): `filter-tool-output.sh`
|
|
182
|
-
|
|
183
|
-
| Field | Value |
|
|
184
|
-
|-------|-------|
|
|
185
|
-
| Event | `PostToolUse` |
|
|
186
|
-
| Matcher | `Bash` |
|
|
187
|
-
| Script | `~/.softspark/ai-toolkit/hooks/filter-tool-output.sh` |
|
|
188
|
-
| Fires | After a successful Claude Code Bash tool call |
|
|
189
|
-
|
|
190
|
-
The hook is disabled by default and runs last among the installed
|
|
191
|
-
`PostToolUse` handlers. Its modes are:
|
|
192
|
-
|
|
193
|
-
| Mode | Behavior |
|
|
194
|
-
|------|----------|
|
|
195
|
-
| `off` | Shell fast path exits before Python starts |
|
|
196
|
-
| `observe` | Evaluates eligible output and writes content-free metadata, but emits no replacement |
|
|
197
|
-
| `safe` | Replaces eligible output only after invariants pass and the exact native response is stored for recovery |
|
|
198
|
-
|
|
199
|
-
Only explicitly allowlisted test, lint, typecheck, and validation command
|
|
200
|
-
shapes are eligible. Failed or interrupted tools, non-empty stderr, image or
|
|
201
|
-
binary results, pipes, redirects, deployment, migrations, audits, security
|
|
202
|
-
scanners, malformed payloads, unavailable recovery, and any uncertain case
|
|
203
|
-
remain unchanged.
|
|
204
|
-
|
|
205
|
-
The per-project policy `<project>/.claude/ai-toolkit-output-filter.json` is
|
|
206
|
-
honored only when **both** checks pass: the project root is registered in
|
|
207
|
-
`~/.softspark/ai-toolkit/projects.json`, and the sibling regular file
|
|
208
|
-
`<project>/.claude/.ai-toolkit-output-filter.owner` holds the ai-toolkit owner
|
|
209
|
-
marker. `ai-toolkit install --local` writes both. Registration is required
|
|
210
|
-
because the owner marker is a public constant, so a cloned or untrusted
|
|
211
|
-
checkout must never be able to self-enable filtering by shipping its own
|
|
212
|
-
marker. An unregistered project, a missing or foreign marker, or a symlinked
|
|
213
|
-
project root or `.claude` directory falls back to the installed global policy
|
|
214
|
-
at `~/.softspark/ai-toolkit/hooks/output-filter-policy.json`, which ships as
|
|
215
|
-
`off`.
|
|
216
|
-
|
|
217
|
-
`jq` is a required system dependency (`python3 scripts/check_deps.py`
|
|
218
|
-
verifies it alongside `python3`, `git`, and `node`); without it `guard-path.sh`
|
|
219
|
-
blocks file tools rather than skipping path validation.
|
|
220
|
-
|
|
221
|
-
Recovery data is private and session-scoped under
|
|
222
|
-
`~/.softspark/ai-toolkit/sessions/<repo-key>/output-filter/`. Telemetry records
|
|
223
|
-
only profile/version, byte and line counts, latency, outcome, and fallback
|
|
224
|
-
reason. Recovery-backed modes require a bounded native session ID containing
|
|
225
|
-
only ASCII letters, digits, underscores, or hyphens. Three consecutive
|
|
226
|
-
profile, invariant, or recovery safety failures open a session circuit
|
|
227
|
-
breaker. Set `AI_TOOLKIT_OUTPUT_FILTER_DISABLE=1` for an immediate bypass.
|
|
228
|
-
|
|
229
|
-
The replacement adapter is Claude Code-specific. Claude Chat/Cowork exports
|
|
230
|
-
exclude it, and the generated hooks for other editors do not activate it.
|
|
231
|
-
The `minimal` hook profile and `AI_TOOLKIT_DISABLED_HOOKS=filter-tool-output`
|
|
232
|
-
bypass it before the Python runtime starts.
|
|
233
|
-
|
|
234
178
|
### Stop (quality check) — `quality-check.sh`
|
|
235
179
|
|
|
236
180
|
| Field | Value |
|
|
@@ -344,8 +288,7 @@ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
|
|
|
344
288
|
| Script | `~/.softspark/ai-toolkit/hooks/session-end.sh` |
|
|
345
289
|
| Fires | When a Claude session ends |
|
|
346
290
|
|
|
347
|
-
**Action:** Removes
|
|
348
|
-
the ending native session, then writes `session-end.md` to the per-repo session store
|
|
291
|
+
**Action:** Removes isolated edit state for the ending native session, then writes `session-end.md` to the per-repo session store
|
|
349
292
|
(`~/.softspark/ai-toolkit/sessions/<repo-key>/`) with a lightweight handoff note
|
|
350
293
|
for the next session.
|
|
351
294
|
|
|
@@ -72,10 +72,10 @@ Required keys:
|
|
|
72
72
|
## CLI Management
|
|
73
73
|
|
|
74
74
|
```bash
|
|
75
|
-
ai-toolkit plugin list # show all
|
|
75
|
+
ai-toolkit plugin list # show all 12 packs with install status
|
|
76
76
|
ai-toolkit plugin install --editor claude <name> # Claude Code global target
|
|
77
77
|
ai-toolkit plugin install --editor codex <name> # Codex global target
|
|
78
|
-
ai-toolkit plugin install --editor all --all # install all
|
|
78
|
+
ai-toolkit plugin install --editor all --all # install all 12 packs for both runtimes
|
|
79
79
|
ai-toolkit plugin update --editor all --all # update all installed packs
|
|
80
80
|
ai-toolkit plugin clean <name> # prune data older than 90 days (default)
|
|
81
81
|
ai-toolkit plugin clean <name> --days 30 # prune data older than 30 days
|
|
@@ -138,6 +138,21 @@ ai-toolkit plugin status --editor all # show installed packs with r
|
|
|
138
138
|
| `kotlin-pack` | kotlin | 0 | 1 | 0 | Kotlin patterns |
|
|
139
139
|
| `swift-pack` | swift | 0 | 1 | 0 | Swift patterns |
|
|
140
140
|
| `ruby-pack` | ruby | 0 | 1 | 0 | Ruby patterns |
|
|
141
|
+
| `rtk-pack` | token-reduction | 0 | 0 | 1 | Command rewriting via a checksum-pinned rtk binary fetched at install |
|
|
142
|
+
|
|
143
|
+
`rtk-pack` is the first pack to break three assumptions the others share, so it
|
|
144
|
+
is the one to read when extending the contract:
|
|
145
|
+
|
|
146
|
+
- **It fetches from the network at install time.** `scripts/init.py` downloads a
|
|
147
|
+
platform-specific artifact and verifies its SHA-256 against `plugin.json`
|
|
148
|
+
before installing anything. A mismatch aborts and leaves nothing behind.
|
|
149
|
+
- **It declares platform assets and digests in `plugin.json`.** The manifest
|
|
150
|
+
schema tolerates extra keys, so `upstream` and `binary` are additive; nothing
|
|
151
|
+
validates them, which means a malformed block fails at install rather than in
|
|
152
|
+
`validate.py --strict`.
|
|
153
|
+
- **It reports its own health.** `scripts/status.py` is picked up generically by
|
|
154
|
+
`plugin status`, replacing what used to be a hardcoded `if name == "memory-pack"`
|
|
155
|
+
branch. Any pack can now ship one.
|
|
141
156
|
|
|
142
157
|
## Optional Hook Modules
|
|
143
158
|
|
|
@@ -33,7 +33,6 @@ The canonical data lives in **`scripts/ecosystem_tools.json`** and is consumed b
|
|
|
33
33
|
| Our generators | — (Claude Code is the primary target; toolkit content ships directly as `.md` files and `settings.json` merges) |
|
|
34
34
|
| Tracked hook events | Core: `SessionStart`, `SessionEnd`, `UserPromptSubmit`, `Notification`, `MessageDisplay`. Tool: `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PostToolBatch`. Turn: `Stop`, `StopFailure`, `UserPromptExpansion`. Subagent: `SubagentStart`, `SubagentStop`. Compaction: `PreCompact`, `PostCompact`. Permissions: `PermissionRequest`, `PermissionDenied`. Elicitation: `Elicitation`, `ElicitationResult`. Teams: `TaskCreated`, `TaskCompleted`, `TeammateIdle`. Worktrees/env: `WorktreeCreate`, `WorktreeRemove`, `CwdChanged`, `FileChanged`, `ConfigChange`. Setup: `Setup`, `InstructionsLoaded` |
|
|
35
35
|
| Tracked handler types | `command`, `prompt`, `agent`, `mcp_tool`, `http` (POST event JSON to allowlisted URLs via `allowedHttpHookUrls`) |
|
|
36
|
-
| Tool-output replacement | Native `PostToolUse.updatedToolOutput`; ai-toolkit exposes dependency-free `off`, byte-identical `observe`, and opt-in recoverable `safe` modes for eligible successful Bash text |
|
|
37
36
|
| Other capabilities | slash commands, MCP server/client, sub-agent, output style, `SKILL.md` (≥500 lines warn) |
|
|
38
37
|
| Version probe | `claude --version` |
|
|
39
38
|
| Notes | v2.1.169 added `disableBundledSkills` setting + `CLAUDE_CODE_DISABLE_BUNDLED_SKILLS` env var (hides bundled skills/built-in slash commands from the model; toolkit skills in `.claude/skills/` are unaffected — useful when toolkit skills overlap built-ins) and `claude --safe-mode` / `CLAUDE_CODE_SAFE_MODE` (starts with hooks, skills, agents, and CLAUDE.md disabled — first isolation step when debugging toolkit rule enforcement). `fallbackModel` settings key (v2.1.166) noted as not-adopted (class C, no toolkit surface writes model settings). |
|
|
@@ -53,7 +52,6 @@ The canonical data lives in **`scripts/ecosystem_tools.json`** and is consumed b
|
|
|
53
52
|
| Plugin layout | `.claude-plugin/plugin.json`, `skills/*/SKILL.md`, `agents/*.md`, `hooks/hooks.json`; ai-toolkit uses manifest paths under `claude-app/` for its generated app-only rules and hooks |
|
|
54
53
|
| Our generator | `scripts/claude_app.py` (`ai-toolkit claude-app export`) |
|
|
55
54
|
| Runtime split | Skills work in Chat (web/Desktop) and Cowork. Hooks and sub-agents run only in Cowork. Claude app does **not** scan Claude Code's `~/.claude/rules/`, `CLAUDE.md`, or `~/.claude/settings.json`. |
|
|
56
|
-
| Tool-output replacement | Not shipped. The Claude Code-only filter hook and runtime are excluded from the app archive until Cowork has an independently verified replacement contract. |
|
|
57
55
|
| Install/update | Export the ZIP, upload it from `Customize > Plugins`, then paste the generated global-instructions file into `Settings > Cowork > Global instructions`. Re-export/re-upload after toolkit updates. |
|
|
58
56
|
|
|
59
57
|
---
|
|
@@ -201,7 +199,6 @@ The canonical data lives in **`scripts/ecosystem_tools.json`** and is consumed b
|
|
|
201
199
|
| Our generators | `scripts/generate_codex.py`, `scripts/generate_codex_agents.py` (native custom-agent TOML), `scripts/generate_codex_hooks.py`, `scripts/generate_codex_skills.py` (opt-in via `--codex-skills`) |
|
|
202
200
|
| Rules delivery | Universal coding rules are inlined into `AGENTS.md` (Codex reads instructions only from AGENTS.md, not `.agents/rules/`); language rules ship as `<lang>-rules` skills under `.agents/skills/`. Global install writes `~/.codex/AGENTS.md` (not `~/AGENTS.md`, which Codex never loads globally); plugin-pack rules are marker-injected into the same file. `project_doc_max_bytes` default is 32 KiB and Codex silently truncates AGENTS.md past that (see codex-cli-compatibility.md). |
|
|
203
201
|
| Tracked hook events | Upstream canonical (codex-rs `HookEventName` enum): `PreToolUse`, `PostToolUse`, `PermissionRequest`, `PreCompact`, `PostCompact`, `SessionStart`, `UserPromptSubmit`, `SubagentStart`, `SubagentStop`, `Stop` (10 events). We wire 9 through an explicit Codex map, including destructive-command and wrong-home path guards on both Bash `PreToolUse` and `PermissionRequest`. `PostCompact` is not wired (its only hook was the removed environment-snapshot probe). |
|
|
204
|
-
| Tool-output replacement | Manual `ai-toolkit output-filter inspect` only. The Claude-specific `updatedToolOutput` adapter is not emitted into Codex hooks. |
|
|
205
202
|
| Tracked handler types | `command` (emitted by default; the only handler Codex actually runs). `prompt` and `agent` are parsed by Codex but NOT yet executed, so hand-authored handlers of those types are inert. |
|
|
206
203
|
| Other capabilities | `AGENTS.md`, `config.toml`, `mcp_servers`, sandbox policies, `.agents/skills/*/SKILL.md` (native Codex skill discovery path), `.codex/agents/*.toml` (native custom agents) |
|
|
207
204
|
| Version probe | `codex --version` |
|
|
@@ -216,7 +213,6 @@ The canonical data lives in **`scripts/ecosystem_tools.json`** and is consumed b
|
|
|
216
213
|
| Config paths | `opencode.json`, `.opencode/agents/*.md`, `.opencode/commands/*.md`, `.opencode/plugins/*`, `.opencode/skills/*/SKILL.md` (v1.14+), `AGENTS.md`; skill fallback discovery: `.claude/skills/`, `.agents/skills/`, `~/.config/opencode/skills/`, `~/.claude/skills/`, `~/.agents/skills/` |
|
|
217
214
|
| Our generators | `scripts/generate_opencode.py`, `scripts/generate_opencode_agents.py`, `scripts/generate_opencode_commands.py`, `scripts/generate_opencode_json.py`, `scripts/generate_opencode_plugin.py` |
|
|
218
215
|
| Hook isolation | Tool hooks preserve native `sessionID` as normalized `session_id`; exit code 2 from a blocking pre-tool guard is raised back to OpenCode instead of being ignored. |
|
|
219
|
-
| Tool-output replacement | Manual `ai-toolkit output-filter inspect` only. No active OpenCode output adapter is shipped. |
|
|
220
216
|
| Tracked plugin events | `session.created`, `session.compacted`, `session.deleted`, `message.updated`, `tool.execute.before`, `tool.execute.after`, `permission.asked`, `command.executed` |
|
|
221
217
|
| Other capabilities | `opencode.json` config, primary + subagent modes, `@`-mention subagents, `/`-invocation commands, MCP (local + remote), plugin hooks in JS/TS, native `SKILL.md` discovery with Claude-compatible fallback, `permission.skill.*` matrix |
|
|
222
218
|
| Version probe | `opencode --version` |
|