@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
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,90 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## v4.18.0 — rtk-pack, an opt-in command rewriter (2026-07-26)
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`rtk-pack`**, an opt-in plugin pack that rewrites Bash commands at
|
|
15
|
+
`PreToolUse` via [rtk](https://github.com/rtk-ai/rtk) (Apache-2.0). Nothing
|
|
16
|
+
installs it for you. `ai-toolkit plugin install rtk-pack` fetches a
|
|
17
|
+
platform-specific binary from an ai-toolkit GitHub Release and verifies its
|
|
18
|
+
SHA-256 before installing anything; a mismatch aborts and leaves nothing
|
|
19
|
+
behind. Read `app/plugins/rtk-pack/README.md` before enabling it: the rewrite
|
|
20
|
+
means what runs is not literally what the model asked for, and rtk applies
|
|
21
|
+
your permission verdict for the original command to the rewritten one.
|
|
22
|
+
- **`.github/workflows/rtk-build.yml`**, a manual-dispatch pipeline that
|
|
23
|
+
cross-builds rtk from upstream source for five targets with the compile-time
|
|
24
|
+
telemetry endpoint left undefined, proves each artifact starts, writes no
|
|
25
|
+
telemetry state and (on Linux) behaves identically with no network route,
|
|
26
|
+
then publishes checksummed artifacts with LICENSE and NOTICE.
|
|
27
|
+
- **`scripts/verify_rtk_binary.py`**, the silence verifier that pipeline uses.
|
|
28
|
+
A target it cannot start reports `inconclusive`, never `pass`.
|
|
29
|
+
- **`kb/procedures/rtk-upstream-sync-sop.md`**, the procedure for moving to a
|
|
30
|
+
newer upstream tag, including a re-measure gate and a published kill number.
|
|
31
|
+
- Plugin packs can now ship `scripts/status.py` and have `plugin status` pick it
|
|
32
|
+
up. This replaces a hardcoded `if name == "memory-pack"` branch, so any pack
|
|
33
|
+
can report its own health.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- **`ai-toolkit update` now updates installed plugin packs.** It reads
|
|
38
|
+
`plugins.json` and runs the equivalent of `plugin update --all` for every
|
|
39
|
+
installed pack. `--local` leaves packs alone, since they are global.
|
|
40
|
+
- **`plugin update` is version-aware.** `plugins.json` records the pack version
|
|
41
|
+
installed per editor, and an update whose manifest version matches is a silent
|
|
42
|
+
no-op. Previously every update removed and reinstalled unconditionally, which
|
|
43
|
+
for a pack that downloads a binary meant refetching it every time. `--force`
|
|
44
|
+
overrides; `--dry-run` reports without acting. State written before versions
|
|
45
|
+
were tracked updates each pack exactly once.
|
|
46
|
+
- Plugin packs are now covered by `audit_skills.py --ci` and by the ShellCheck
|
|
47
|
+
gate in CI. Both previously scanned only `app/skills`, `app/agents` and
|
|
48
|
+
`app/hooks`, exempting the highest-risk code in the repo.
|
|
49
|
+
- A pack's install-time script may be named `init.py`; `init_db.py` still works.
|
|
50
|
+
Previously only the latter ran, so a pack using any other name installed
|
|
51
|
+
successfully while doing nothing.
|
|
52
|
+
|
|
53
|
+
### Fixed
|
|
54
|
+
|
|
55
|
+
- `plugin install` no longer aborts with `IsADirectoryError` when a pack ships a
|
|
56
|
+
subdirectory under `scripts/`.
|
|
57
|
+
- A failing pack init script now always reports, instead of printing nothing
|
|
58
|
+
when it exited non-zero with empty stderr.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## v4.17.0 — Native tool-output filter removed (2026-07-26)
|
|
63
|
+
|
|
64
|
+
### Removed
|
|
65
|
+
|
|
66
|
+
- **BREAKING for anyone who opted in.** The native tool-output filter shipped
|
|
67
|
+
in v4.16.0 is gone: the `PostToolUse` hook `filter-tool-output.sh`, the
|
|
68
|
+
`scripts/tool_output_filter/` runtime, the `ai-toolkit output-filter` CLI,
|
|
69
|
+
the benchmark corpus, and the `toolOutputFilter` key in
|
|
70
|
+
`.softspark-toolkit.json`. The key is accepted and ignored rather than
|
|
71
|
+
rejected, so an existing config still validates; `ai-toolkit config validate`
|
|
72
|
+
reports it as a retired key you can delete.
|
|
73
|
+
- Why: measured whole-session token saving was **0.0000%** on real traffic.
|
|
74
|
+
Across 134 session transcripts spanning 22 projects, 7600 successful Bash
|
|
75
|
+
results contained 145 that parsed as simple command shapes, 18 that matched a
|
|
76
|
+
registered shape, and 0 that any filter accepted. The filter was correct;
|
|
77
|
+
its premise was not. Agent-issued commands are overwhelmingly compound
|
|
78
|
+
(`&&` and `;` chains, pipelines, heredocs), and the design accepted only
|
|
79
|
+
simple registered shapes. See
|
|
80
|
+
`kb/history/completed/output-filter-retirement-20260726.md`.
|
|
81
|
+
|
|
82
|
+
### Changed
|
|
83
|
+
|
|
84
|
+
- `ai-toolkit install` and `ai-toolkit update` now remove the artifacts left by
|
|
85
|
+
v4.16.x: the installed hook script, the global
|
|
86
|
+
`hooks/output-filter-policy.json`, the managed project policy and owner
|
|
87
|
+
marker under `.claude/`, and stored recovery trees under
|
|
88
|
+
`sessions/<repo-key>/output-filter/`. Cleanup is idempotent, verifies
|
|
89
|
+
ai-toolkit ownership before deleting, and leaves foreign files untouched.
|
|
90
|
+
- The stale `PostToolUse` entry is removed from `~/.claude/settings.json` by
|
|
91
|
+
the existing hook strip-and-remerge cycle, so no manual edit is needed.
|
|
92
|
+
- Hook counts drop from 29 entries to 28 across the same 14 events.
|
|
93
|
+
|
|
10
94
|
## v4.16.1 — Copilot skill remnant recovery (2026-07-24)
|
|
11
95
|
|
|
12
96
|
### Fixed
|
package/README.md
CHANGED
|
@@ -6,18 +6,17 @@
|
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
[](app/skills/)
|
|
8
8
|
[](app/agents/)
|
|
9
|
-
[](tests/)
|
|
10
10
|
|
|
11
|
-
## What's New in v4.
|
|
11
|
+
## What's New in v4.18.0
|
|
12
12
|
|
|
13
|
-
v4.
|
|
13
|
+
v4.18.0 adds `rtk-pack`, an opt-in command rewriter, and makes `ai-toolkit update` maintain installed packs.
|
|
14
14
|
|
|
15
|
-
-
|
|
16
|
-
- **
|
|
17
|
-
-
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
20
|
-
- **Copilot remnant recovery**: asset-only `.github/skills/ai-toolkit-*` leftovers from older cleanups are rebuilt in place instead of aborting `install --local` / `update`.
|
|
15
|
+
- **`rtk-pack`, opt-in only**: rewrites Bash commands at `PreToolUse` via [rtk](https://github.com/rtk-ai/rtk) (Apache-2.0). Nothing installs it for you. `plugin install` fetches a platform binary from an ai-toolkit Release and verifies its SHA-256 before installing anything. Measured saving on the reference workload is 0.0615% of input tokens; the pack README states the trust boundary and the numbers up front.
|
|
16
|
+
- **Binaries built from source, telemetry compiled out**: a manual-dispatch workflow cross-builds five targets with the compile-time telemetry endpoint undefined, proves each artifact starts, writes no telemetry state, and on Linux behaves identically with no network route.
|
|
17
|
+
- **`ai-toolkit update` maintains installed packs**: it now runs the equivalent of `plugin update --all`. Updates are version-aware, so a pack whose manifest has not moved is a silent no-op instead of a remove-and-reinstall.
|
|
18
|
+
- **Packs can report their own health**: `plugin status` picks up a pack's `scripts/status.py`, replacing a hardcoded per-pack branch.
|
|
19
|
+
- **Security gates cover packs**: `audit_skills.py --ci` and the ShellCheck gate now scan `app/plugins/`, which they previously skipped.
|
|
21
20
|
|
|
22
21
|
See [CHANGELOG.md](CHANGELOG.md) for full history.
|
|
23
22
|
|
|
@@ -92,10 +91,7 @@ ai-toolkit claude-app export --verify
|
|
|
92
91
|
```
|
|
93
92
|
|
|
94
93
|
Re-export and re-upload after toolkit or registered-rule updates. Skills work
|
|
95
|
-
in Chat and Cowork; hooks and sub-agents are active only in Cowork.
|
|
96
|
-
tool-output replacement hook remains Claude Code-only and is excluded from the
|
|
97
|
-
Claude app archive until that runtime has an independently verified replacement
|
|
98
|
-
contract.
|
|
94
|
+
in Chat and Cowork; hooks and sub-agents are active only in Cowork.
|
|
99
95
|
|
|
100
96
|
### Install Profiles
|
|
101
97
|
|
|
@@ -146,8 +142,8 @@ See [CLI Reference](kb/reference/cli-reference.md) for all commands and options.
|
|
|
146
142
|
| `skills/` (hybrid) | 30 | Slash commands with agent knowledge base |
|
|
147
143
|
| `skills/` (knowledge) | 46 | Domain knowledge auto-loaded by agents (includes 13 `<lang>-rules` skills) |
|
|
148
144
|
| `agents/` | 44 | Specialized agents across 10 categories |
|
|
149
|
-
| `hooks/` |
|
|
150
|
-
| `plugins/` |
|
|
145
|
+
| `hooks/` | 28 entries / 14 events + statusLine | Quality gates, path safety, prompt governance, loop guard, session lifecycle |
|
|
146
|
+
| `plugins/` | 12 packs | Opt-in domain bundles (security, research, frontend, enterprise, memory, rtk, 6 language packs) |
|
|
151
147
|
| `constitution.md` | 7 articles | Machine-enforced safety rules |
|
|
152
148
|
| `rules/` | auto-synced | Global/project rule files for Claude and other editors |
|
|
153
149
|
| `kb/` | reference docs | Architecture, procedures, and best practices |
|
|
@@ -170,7 +166,7 @@ ai-toolkit/
|
|
|
170
166
|
│ └── ARCHITECTURE.md # Full system design
|
|
171
167
|
├── kb/ # Reference docs, procedures, plans
|
|
172
168
|
├── scripts/ # Validation, install, evaluation scripts
|
|
173
|
-
├── tests/ # Bats and Python test suite (
|
|
169
|
+
├── tests/ # Bats and Python test suite (1484 tests)
|
|
174
170
|
└── CHANGELOG.md
|
|
175
171
|
```
|
|
176
172
|
|
|
@@ -182,9 +178,7 @@ ai-toolkit/
|
|
|
182
178
|
|
|
183
179
|
**Machine-enforced constitution** — 7-article safety constitution enforced via `PreToolUse` hooks that actually block `rm -rf`, `DROP TABLE`, and irreversible operations. Not just documentation.
|
|
184
180
|
|
|
185
|
-
**
|
|
186
|
-
|
|
187
|
-
**Native tool-output filtering** — dependency-free, post-execution filtering for successful Claude Code Bash output, shipped **disabled by default** (`off`). Neither `off` nor `observe` ever replaces output; opt-in `safe` mode replaces only validated `repeat-lines` or `tap-success` results after exact ephemeral recovery is available. Failures, diagnostics, unsupported payloads, and unavailable recovery always pass through unchanged. See [Tool Output Filter](kb/reference/tool-output-filter.md).
|
|
181
|
+
**28 lifecycle hook entries:** Executable handlers across 14 events (SessionStart → SessionEnd, plus InstructionsLoaded + ConfigChange). Guards, governance, quality gates, session persistence, MCP health checks, revert protection, test-cohesion enforcement, loop guard, and search-first discipline. See [Hooks Catalog](kb/reference/hooks-catalog.md).
|
|
188
182
|
|
|
189
183
|
**Security scanning** — `/skill-audit` for code-level risks, `/cve-scan` for dependency CVEs. Both CI-ready with exit codes.
|
|
190
184
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "ai-toolkit",
|
|
4
4
|
"displayName": "AI Toolkit",
|
|
5
5
|
"description": "Professional-grade engineering skills, agents, rules, and lifecycle guardrails for Claude Code, Claude Chat, and Cowork.",
|
|
6
|
-
"version": "4.
|
|
6
|
+
"version": "4.18.0",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "SoftSpark",
|
|
9
9
|
"url": "https://github.com/softspark"
|
package/app/hooks/session-end.sh
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
#
|
|
4
4
|
# Fires on: SessionEnd
|
|
5
5
|
# Matcher: all
|
|
6
|
-
# The handoff snapshot is skipped for the minimal profile.
|
|
6
|
+
# The handoff snapshot is skipped for the minimal profile. Session-state
|
|
7
7
|
# cleanup still runs for every profile.
|
|
8
8
|
|
|
9
9
|
# shellcheck source=_session-paths.sh
|
|
@@ -18,18 +18,6 @@ if [ ! -t 0 ]; then
|
|
|
18
18
|
INPUT=$(cat)
|
|
19
19
|
fi
|
|
20
20
|
SESSION_ID=$(hook_session_id)
|
|
21
|
-
RECOVERY_ROOT="$SESSION_DIR/output-filter"
|
|
22
|
-
OUTPUT_FILTER_CLI="${AI_TOOLKIT_OUTPUT_FILTER_CLI:-$HOME/.softspark/ai-toolkit/scripts/output_filter_cli.py}"
|
|
23
|
-
if [ "$SESSION_ID" != "default" ] &&
|
|
24
|
-
[ -d "$RECOVERY_ROOT" ] &&
|
|
25
|
-
[ ! -L "$RECOVERY_ROOT" ] &&
|
|
26
|
-
[ -f "$OUTPUT_FILTER_CLI" ] &&
|
|
27
|
-
[ ! -L "$OUTPUT_FILTER_CLI" ] &&
|
|
28
|
-
command -v python3 >/dev/null 2>&1; then
|
|
29
|
-
python3 -S "$OUTPUT_FILTER_CLI" clean \
|
|
30
|
-
--base-directory "$SESSION_DIR" \
|
|
31
|
-
--session-id "$SESSION_ID" >/dev/null 2>&1 || true
|
|
32
|
-
fi
|
|
33
21
|
|
|
34
22
|
SESSION_STATE_CLI="${AI_TOOLKIT_SESSION_STATE_CLI:-$HOME/.softspark/ai-toolkit/scripts/session_state.py}"
|
|
35
23
|
if [ "$SESSION_ID" != "default" ] &&
|
package/app/hooks.json
CHANGED
|
@@ -158,16 +158,6 @@
|
|
|
158
158
|
"command": "\"$HOME/.softspark/ai-toolkit/hooks/search-tracker.sh\""
|
|
159
159
|
}
|
|
160
160
|
]
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
"_source": "ai-toolkit",
|
|
164
|
-
"matcher": "Bash",
|
|
165
|
-
"hooks": [
|
|
166
|
-
{
|
|
167
|
-
"type": "command",
|
|
168
|
-
"command": "\"$HOME/.softspark/ai-toolkit/hooks/filter-tool-output.sh\""
|
|
169
|
-
}
|
|
170
|
-
]
|
|
171
161
|
}
|
|
172
162
|
],
|
|
173
163
|
"Stop": [
|
package/app/plugins/README.md
CHANGED
|
@@ -17,10 +17,22 @@ Plugin packs can target:
|
|
|
17
17
|
These packs are not directly uploadable Claude app plugins. For Claude Chat,
|
|
18
18
|
Desktop, and Cowork use `ai-toolkit claude-app export`.
|
|
19
19
|
|
|
20
|
-
Current packs:
|
|
21
|
-
- `
|
|
22
|
-
- `research-pack`
|
|
23
|
-
- `frontend-pack`
|
|
20
|
+
Current packs (12):
|
|
21
|
+
- `csharp-pack`
|
|
24
22
|
- `enterprise-pack`
|
|
23
|
+
- `frontend-pack`
|
|
24
|
+
- `java-pack`
|
|
25
|
+
- `kotlin-pack`
|
|
26
|
+
- `memory-pack`
|
|
27
|
+
- `research-pack`
|
|
28
|
+
- `rtk-pack`
|
|
29
|
+
- `ruby-pack`
|
|
30
|
+
- `rust-pack`
|
|
31
|
+
- `security-pack`
|
|
32
|
+
- `swift-pack`
|
|
33
|
+
|
|
34
|
+
`rtk-pack` is the only one that fetches a native binary at install time and the
|
|
35
|
+
only one that rewrites commands before they run. Read its README before
|
|
36
|
+
installing it.
|
|
25
37
|
|
|
26
38
|
See `kb/reference/plugin-pack-conventions.md` for pack rules, validation, and adoption guidance.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# rtk-pack
|
|
2
|
+
|
|
3
|
+
Opt-in command rewriting via [rtk](https://github.com/rtk-ai/rtk), Apache-2.0.
|
|
4
|
+
|
|
5
|
+
Install: `ai-toolkit plugin install rtk-pack`. Remove: `ai-toolkit plugin remove rtk-pack`.
|
|
6
|
+
Nothing installs it for you, and nothing in the core depends on it.
|
|
7
|
+
|
|
8
|
+
## Read this before installing
|
|
9
|
+
|
|
10
|
+
**This pack rewrites your commands before they run.** A `PreToolUse` hook hands
|
|
11
|
+
each command to `rtk`, which may replace it with an `rtk` equivalent that
|
|
12
|
+
produces shorter output. What executes is therefore not literally what the model
|
|
13
|
+
asked for.
|
|
14
|
+
|
|
15
|
+
That is a real trade-off and the reason this ships as an opt-in pack rather than
|
|
16
|
+
a core feature. ai-toolkit's own retired output filter explicitly refused to do
|
|
17
|
+
this. Adopting rtk is a deliberate reversal, made with the mechanism understood
|
|
18
|
+
rather than assumed.
|
|
19
|
+
|
|
20
|
+
**Your permission rules are evaluated against the original command, and the
|
|
21
|
+
verdict is applied to the rewritten one.** rtk checks what the model asked for
|
|
22
|
+
against your `settings.json`, and if a rule allows it, emits
|
|
23
|
+
`permissionDecision: allow` for the `rtk …` form it substituted. So an allowlist
|
|
24
|
+
entry for `git status` transitively authorises `rtk git status`, a command you
|
|
25
|
+
never wrote a rule for. With no matching allow rule, no decision is emitted and
|
|
26
|
+
your normal prompt appears. Verified against upstream's own test at
|
|
27
|
+
`src/hooks/hook_cmd.rs:1272-1283`.
|
|
28
|
+
|
|
29
|
+
If your allowlist is broad, review it before installing. If it is narrow, expect
|
|
30
|
+
prompts on commands that used to run without one.
|
|
31
|
+
|
|
32
|
+
Every failure path degrades to passthrough: no binary, a non-zero exit, a
|
|
33
|
+
timeout, or unparseable output all result in the original command running
|
|
34
|
+
unchanged. Removing the pack strips the hook from `settings.json`.
|
|
35
|
+
|
|
36
|
+
## What you get, measured
|
|
37
|
+
|
|
38
|
+
On the reference workload (1224 local transcripts, 26.6 MB of successful Bash
|
|
39
|
+
output) rtk addresses **35.2%** of Bash bytes, which is **8.8%** of all
|
|
40
|
+
tool-result bytes. The projected saving is **0.12% to 0.16%** of session input
|
|
41
|
+
tokens, or 0.24% to 0.36% if rtk's own 60-90% claim holds on your traffic.
|
|
42
|
+
|
|
43
|
+
That is small, and it is stated up front on purpose. The arithmetic ceiling for
|
|
44
|
+
any tool-output mechanism on that workload is 4.5%, because tool results are
|
|
45
|
+
that share of input token volume, and `Read` results are 63% of them. If your
|
|
46
|
+
work is dominated by `cargo test`, `mvn` or `jest` output rather than file
|
|
47
|
+
reading, rtk is calibrated for you and will do better. Measure before believing
|
|
48
|
+
either number.
|
|
49
|
+
|
|
50
|
+
Full method and caveats: `kb/history/completed/rtk-pack-integration-20260726.md`.
|
|
51
|
+
|
|
52
|
+
## The binary
|
|
53
|
+
|
|
54
|
+
Not bundled. `plugin install` fetches the artifact for your platform from an
|
|
55
|
+
ai-toolkit GitHub Release and verifies its SHA-256 against `plugin.json` before
|
|
56
|
+
installing anything. A digest mismatch aborts and leaves nothing behind. This is
|
|
57
|
+
the only moment the pack uses the network.
|
|
58
|
+
|
|
59
|
+
Binaries are cross-built from upstream source in ai-toolkit CI with
|
|
60
|
+
`RTK_TELEMETRY_URL` and `RTK_TELEMETRY_TOKEN` left undefined, so the
|
|
61
|
+
compile-time telemetry endpoint is absent and cannot be supplied at runtime.
|
|
62
|
+
Each build asserts that, runs the artifact, checks no telemetry state is
|
|
63
|
+
written, and on Linux confirms behaviour is unchanged with no network route.
|
|
64
|
+
|
|
65
|
+
| Platform | Artifact |
|
|
66
|
+
|---|---|
|
|
67
|
+
| macOS arm64 | `rtk-aarch64-apple-darwin.tar.gz` |
|
|
68
|
+
| macOS x86_64 | `rtk-x86_64-apple-darwin.tar.gz` |
|
|
69
|
+
| Linux x86_64 | `rtk-x86_64-unknown-linux-musl.tar.gz` (static, runs on glibc) |
|
|
70
|
+
| Linux aarch64 | `rtk-aarch64-unknown-linux-gnu.tar.gz` |
|
|
71
|
+
| Windows x86_64 | `rtk-x86_64-pc-windows-msvc.zip` |
|
|
72
|
+
|
|
73
|
+
## One thing the binary does reach the network for
|
|
74
|
+
|
|
75
|
+
`rtk cc` (Claude economics) shells out to `npx --yes ccusage` when `ccusage` is
|
|
76
|
+
not already on `PATH`, which fetches and executes a third-party npm package.
|
|
77
|
+
That is not telemetry and never happens automatically, but it is outbound
|
|
78
|
+
network from a binary otherwise described as silent. Do not run `rtk cc` in an
|
|
79
|
+
air-gapped or policy-restricted environment.
|
|
80
|
+
|
|
81
|
+
Related: `rtk telemetry forget` on these builds prints a failure telling you to
|
|
82
|
+
email upstream to complete erasure. Nothing was ever sent; the message is
|
|
83
|
+
upstream's and does not apply here.
|
|
84
|
+
|
|
85
|
+
## Layout
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
~/.softspark/ai-toolkit/
|
|
89
|
+
├── hooks/plugin-rtk-pack-rewrite.sh # the PreToolUse hook
|
|
90
|
+
└── plugin-scripts/rtk-pack/
|
|
91
|
+
├── bin/rtk # verified binary
|
|
92
|
+
└── version.json # pinned versions and recorded digest
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Installing from a mirror
|
|
96
|
+
|
|
97
|
+
Set `RTK_PACK_RELEASE_BASE_URL` to a base URL holding the release assets and the
|
|
98
|
+
install fetches from there instead of GitHub, for networks that cannot reach it
|
|
99
|
+
directly:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
RTK_PACK_RELEASE_BASE_URL=https://mirror.internal/rtk/v0.44.0-1 \
|
|
103
|
+
ai-toolkit plugin install rtk-pack
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The digest check is unchanged. A mirror serving different bytes is rejected
|
|
107
|
+
exactly like a corrupt download, so this widens where the artifact comes from
|
|
108
|
+
without widening what is accepted.
|
|
109
|
+
|
|
110
|
+
## Disabling without removing
|
|
111
|
+
|
|
112
|
+
`rtk` honours `RTK_DISABLED=1` as a per-command prefix. To turn the pack off
|
|
113
|
+
entirely, remove it: `ai-toolkit plugin remove rtk-pack`.
|
|
114
|
+
|
|
115
|
+
## Licence
|
|
116
|
+
|
|
117
|
+
rtk is Apache-2.0. `LICENSE` and `NOTICE` ship with every release artifact. The
|
|
118
|
+
NOTICE records the two build-time differences from an upstream build: the
|
|
119
|
+
undefined telemetry endpoint, and in-range lockfile security updates for
|
|
120
|
+
`anyhow` and `crossbeam-epoch`. No upstream source is modified.
|
|
121
|
+
|
|
122
|
+
Upstream `DISCLAIMER.md` is deliberately not redistributed: it states that
|
|
123
|
+
telemetry is collected by default, which is not true of these builds.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# rewrite.sh — delegate PreToolUse command rewriting to the rtk binary.
|
|
3
|
+
#
|
|
4
|
+
# Fires on: PreToolUse
|
|
5
|
+
# Reads the Claude Code hook JSON from stdin and hands it to `rtk hook claude`,
|
|
6
|
+
# which emits hookSpecificOutput with permissionDecision and updatedInput.
|
|
7
|
+
#
|
|
8
|
+
# This hook rewrites the command before it runs, so what executes is not
|
|
9
|
+
# literally what the model asked for. That is the pack's stated trade-off; see
|
|
10
|
+
# the pack README. Everything here is built so that any failure degrades to
|
|
11
|
+
# passthrough rather than to a broken or altered command:
|
|
12
|
+
#
|
|
13
|
+
# no binary -> no output, exit 0 (command runs unchanged)
|
|
14
|
+
# rtk fails -> no output, exit 0
|
|
15
|
+
# rtk times out -> no output, exit 0
|
|
16
|
+
# rtk prints junk -> no output, exit 0
|
|
17
|
+
#
|
|
18
|
+
# Emitting nothing is the safe default: Claude Code treats an empty hook
|
|
19
|
+
# response as "no opinion" and runs the original command.
|
|
20
|
+
|
|
21
|
+
set -u
|
|
22
|
+
|
|
23
|
+
PACK_BIN_DIR="${HOME}/.softspark/ai-toolkit/plugin-scripts/rtk-pack/bin"
|
|
24
|
+
TIMEOUT_SECONDS=5
|
|
25
|
+
|
|
26
|
+
# Read stdin once; it is not replayable.
|
|
27
|
+
INPUT=$(cat)
|
|
28
|
+
[ -n "$INPUT" ] || exit 0
|
|
29
|
+
|
|
30
|
+
# jq is needed both to gate on the tool name and to check rtk's reply. Without
|
|
31
|
+
# it we cannot do either safely, so stay out of the way entirely.
|
|
32
|
+
command -v jq >/dev/null 2>&1 || exit 0
|
|
33
|
+
|
|
34
|
+
# A pack hook cannot declare its own matcher: plugin.py:408 looks the basename
|
|
35
|
+
# up in core app/hooks.json and falls back to "", so this registers against
|
|
36
|
+
# every tool rather than Bash alone. Gate here instead, otherwise every Read,
|
|
37
|
+
# Edit and Grep would spawn a subprocess for nothing.
|
|
38
|
+
TOOL=$(printf '%s' "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
39
|
+
[ "$TOOL" = "Bash" ] || exit 0
|
|
40
|
+
|
|
41
|
+
RTK=""
|
|
42
|
+
for candidate in "${PACK_BIN_DIR}/rtk" "${PACK_BIN_DIR}/rtk.exe"; do
|
|
43
|
+
if [ -x "$candidate" ]; then
|
|
44
|
+
RTK="$candidate"
|
|
45
|
+
break
|
|
46
|
+
fi
|
|
47
|
+
done
|
|
48
|
+
|
|
49
|
+
# Inert when init.py could not install a binary. The pack stays wired so that a
|
|
50
|
+
# later `ai-toolkit plugin install rtk-pack` fixes it without touching settings.
|
|
51
|
+
[ -n "$RTK" ] || exit 0
|
|
52
|
+
|
|
53
|
+
# GNU timeout is `timeout`, Homebrew coreutils installs it as `gtimeout`, and
|
|
54
|
+
# on a bare macOS it is absent. A hung rewriter would hang the session, so use
|
|
55
|
+
# it when we can and accept the risk when we cannot.
|
|
56
|
+
TIMEOUT_CMD=""
|
|
57
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
58
|
+
TIMEOUT_CMD="timeout"
|
|
59
|
+
elif command -v gtimeout >/dev/null 2>&1; then
|
|
60
|
+
TIMEOUT_CMD="gtimeout"
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
if [ -n "$TIMEOUT_CMD" ]; then
|
|
64
|
+
OUTPUT=$(printf '%s' "$INPUT" | "$TIMEOUT_CMD" "$TIMEOUT_SECONDS" "$RTK" hook claude 2>/dev/null)
|
|
65
|
+
else
|
|
66
|
+
OUTPUT=$(printf '%s' "$INPUT" | "$RTK" hook claude 2>/dev/null)
|
|
67
|
+
fi
|
|
68
|
+
STATUS=$?
|
|
69
|
+
|
|
70
|
+
[ "$STATUS" -eq 0 ] || exit 0
|
|
71
|
+
[ -n "$OUTPUT" ] || exit 0
|
|
72
|
+
|
|
73
|
+
# Only forward something that parses as JSON. A partial write or a stray
|
|
74
|
+
# diagnostic on stdout would otherwise reach Claude Code as a malformed hook
|
|
75
|
+
# response.
|
|
76
|
+
printf '%s' "$OUTPUT" | jq -e . >/dev/null 2>&1 || exit 0
|
|
77
|
+
|
|
78
|
+
printf '%s\n' "$OUTPUT"
|
|
79
|
+
exit 0
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rtk-pack",
|
|
3
|
+
"description": "Opt-in command rewriting via rtk, cross-built from source with telemetry disabled and fetched from a checksum-pinned release",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"domain": "token-reduction",
|
|
6
|
+
"type": "plugin-pack",
|
|
7
|
+
"status": "experimental",
|
|
8
|
+
"requires": {
|
|
9
|
+
"ai-toolkit": ">=4.17.0",
|
|
10
|
+
"claude-code": ">=1.0.33"
|
|
11
|
+
},
|
|
12
|
+
"includes": {
|
|
13
|
+
"agents": [],
|
|
14
|
+
"skills": [],
|
|
15
|
+
"rules": [],
|
|
16
|
+
"hooks": ["rewrite.sh"]
|
|
17
|
+
},
|
|
18
|
+
"hook_events": {
|
|
19
|
+
"rewrite.sh": "PreToolUse"
|
|
20
|
+
},
|
|
21
|
+
"upstream": {
|
|
22
|
+
"project": "rtk",
|
|
23
|
+
"repository": "https://github.com/rtk-ai/rtk",
|
|
24
|
+
"license": "Apache-2.0",
|
|
25
|
+
"version": "v0.44.0",
|
|
26
|
+
"note": "Built from source in our CI with RTK_TELEMETRY_URL and RTK_TELEMETRY_TOKEN undefined. Cargo.lock carries in-range security updates for anyhow and crossbeam-epoch. No source modifications."
|
|
27
|
+
},
|
|
28
|
+
"binary": {
|
|
29
|
+
"release_repo": "softspark/ai-toolkit",
|
|
30
|
+
"release_tag": "softspark-rtk-v0.44.0-1",
|
|
31
|
+
"install_name": "rtk",
|
|
32
|
+
"assets": {
|
|
33
|
+
"darwin-arm64": {
|
|
34
|
+
"file": "rtk-aarch64-apple-darwin.tar.gz",
|
|
35
|
+
"sha256": "a69b8ccb7c516a1d6e06aff714bdc8bf0afcb2af240c5b2f6c83122743c93c56",
|
|
36
|
+
"member": "rtk"
|
|
37
|
+
},
|
|
38
|
+
"darwin-x86_64": {
|
|
39
|
+
"file": "rtk-x86_64-apple-darwin.tar.gz",
|
|
40
|
+
"sha256": "bda9f4a6acf87be859394d5da3a064db211a869cf331ed2c485e174f5fd81dcb",
|
|
41
|
+
"member": "rtk"
|
|
42
|
+
},
|
|
43
|
+
"linux-aarch64": {
|
|
44
|
+
"file": "rtk-aarch64-unknown-linux-gnu.tar.gz",
|
|
45
|
+
"sha256": "fd5f4b10ca2e96e674b437f9a00639f376b215e7d15373327142f5f8e9642795",
|
|
46
|
+
"member": "rtk"
|
|
47
|
+
},
|
|
48
|
+
"linux-x86_64": {
|
|
49
|
+
"file": "rtk-x86_64-unknown-linux-musl.tar.gz",
|
|
50
|
+
"sha256": "1d886b748181b78e11ea99c2f2ff60ad04a73dbc29ca20ca807f01642ebcea32",
|
|
51
|
+
"member": "rtk"
|
|
52
|
+
},
|
|
53
|
+
"windows-x86_64": {
|
|
54
|
+
"file": "rtk-x86_64-pc-windows-msvc.zip",
|
|
55
|
+
"sha256": "83ebf53af4f15d860cef16e8ff6fe9a5cf25fa10ed4c48a1544d359a2796ef38",
|
|
56
|
+
"member": "rtk.exe"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|