@tokmizer/plugin 0.9.57 → 0.9.59
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +11 -0
- package/README.md +214 -8
- package/agent-plugins/amp.js +38 -0
- package/agent-plugins/hermes/__init__.py +53 -0
- package/agent-plugins/hermes/plugin.yaml +5 -0
- package/agent-plugins/kilo.js +72 -0
- package/agent-plugins/omp.js +32 -0
- package/agent-plugins/openclaw/index.js +58 -0
- package/agent-plugins/openclaw/openclaw.plugin.json +12 -0
- package/agent-plugins/openclaw/package.json +11 -0
- package/agent-plugins/opencode.js +72 -0
- package/agent-plugins/pi.js +30 -0
- package/codex-hooks/hooks.json +16 -0
- package/command-templates/tkr-gain.md +7 -2
- package/command-templates/tkr-status.md +5 -1
- package/command-templates/tkr-telemetry.md +3 -3
- package/command-templates/tkr-unlink.md +2 -2
- package/dist/bin/tkr-agent-hook.js +13 -0
- package/dist/bin/tkr-codex-hook.js +134 -40
- package/dist/bin/tkr-context-hook.js +4 -0
- package/dist/bin/tkr-copilot-hook.js +13 -0
- package/dist/bin/tkr-crush-hook.js +13 -0
- package/dist/bin/tkr-cursor-hook.js +13 -0
- package/dist/bin/tkr-daemon.js +194 -56
- package/dist/bin/tkr-droid-hook.js +13 -0
- package/dist/bin/tkr-exec +136 -42
- package/dist/bin/tkr-exec.js +136 -42
- package/dist/bin/tkr-gemini-hook.js +13 -0
- package/dist/bin/tkr-grok-hook.js +13 -0
- package/dist/bin/tkr-junie-hook.js +13 -0
- package/dist/bin/tkr-processor.js +166 -0
- package/dist/bin/tkr-qwen-hook.js +13 -0
- package/dist/bin/tkr-shim.js +138 -37
- package/dist/bin/tkr-shim.stable +1 -0
- package/dist/bin/tkr-trae-hook.js +13 -0
- package/dist/bin/tkr-zcode-hook.js +13 -0
- package/dist/bin/tkr.js +425 -157
- package/dist/hooks-host/post-tool-use.js +150 -49
- package/dist/hooks-host/pre-tool-use.js +10 -7
- package/dist/hooks-host/reducer-prompt-submit.js +2 -1
- package/dist/hooks-host/reducer-session-start.js +2 -1
- package/dist/hooks-host/reducer-stop.js +25 -16
- package/dist/hooks-host/session-recap.js +4 -3
- package/dist/index.js +160 -41
- package/dist/native/manifest.json +18 -8
- package/dist/native/single.json +38 -0
- package/dist/native/tkr-exec-darwin-arm64 +0 -0
- package/dist/native/tkr-exec-darwin-x64 +0 -0
- package/dist/native/tkr-exec-linux-arm64 +0 -0
- package/dist/native/tkr-exec-linux-x64 +0 -0
- package/dist/native/tkr-exec-win32-arm64.exe +0 -0
- package/dist/native/tkr-exec-win32-x64.exe +0 -0
- package/hooks/on-session-start.d.mts +1 -0
- package/hooks/on-session-start.mjs +37 -34
- package/package.json +26 -3
- package/scripts/README-INSTALL.md +50 -14
- package/scripts/install-mac-linux.sh +77 -7
- package/scripts/install-native.mjs +177 -25
- package/codex-plugin/plugin.json +0 -7
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tokmizer",
|
|
3
|
+
"version": "0.9.59",
|
|
4
|
+
"description": "Tokmizer reduces token consumption for OpenAI Codex.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Tokmizer",
|
|
7
|
+
"email": "contact@tokmizer.com"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://tokmizer.com",
|
|
10
|
+
"hooks": "./codex-hooks/hooks.json"
|
|
11
|
+
}
|
package/README.md
CHANGED
|
@@ -38,6 +38,75 @@ tkr setup-codex
|
|
|
38
38
|
|
|
39
39
|
Restart Codex, then approve the hook in `/hooks`.
|
|
40
40
|
|
|
41
|
+
If Codex reports `PreToolUse hook returned updatedInput without permissionDecision:allow`, it is running the Claude Code hooks: an older Tokmizer plugin installed in Codex, or hooks copied over by Codex `/import`. Run `tkr doctor --fix`, then approve the Tokmizer hook in `/hooks` and restart Codex.
|
|
42
|
+
|
|
43
|
+
Cursor or Gemini CLI, after installing with npm or Homebrew:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
tkr setup-cursor
|
|
47
|
+
tkr setup-gemini
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`tkr setup-cursor` adds a `preToolUse` hook to `~/.cursor/hooks.json`; `tkr setup-gemini` adds a `BeforeTool` hook to `~/.gemini/settings.json`. Each hook routes the agent's shell commands through Tokmizer before they run, keeps your other hooks and settings, and saves the file as it was before each change in `~/.tokmizer/backup/config/` (latest copy only). Remove either with `--remove`. Cursor's `afterShellExecution` and Gemini's `AfterTool` events cannot replace command output, so these hooks rewrite the command itself.
|
|
51
|
+
|
|
52
|
+
Other agents with plugin or hook support:
|
|
53
|
+
|
|
54
|
+
| Agent | Command | What it installs |
|
|
55
|
+
| --- | --- | --- |
|
|
56
|
+
| OpenCode | `tkr setup-opencode` | plugin `~/.config/opencode/plugins/tokmizer.js` |
|
|
57
|
+
| Kilo Code (CLI and VS Code extension) | `tkr setup-kilo` | plugin `~/.config/kilo/plugin/tokmizer.js` |
|
|
58
|
+
| Amp | `tkr setup-amp` | plugin `~/.config/amp/plugins/tokmizer.js` |
|
|
59
|
+
| Mistral Vibe | `tkr setup-vibe` | `pre_tool` hook in `~/.vibe/hooks.toml` |
|
|
60
|
+
| Antigravity | `tkr setup-antigravity` | `PreToolUse` hook in `~/.gemini/config/hooks.json` |
|
|
61
|
+
| OpenClaw | `tkr setup-openclaw` | plugin `~/.openclaw/extensions/tokmizer/`, then `openclaw plugins enable tokmizer` |
|
|
62
|
+
| Pi | `tkr setup-pi` | extension `~/.pi/agent/extensions/tokmizer.js` |
|
|
63
|
+
| Oh My Pi | `tkr setup-omp` | extension `~/.omp/agent/extensions/tokmizer.js` |
|
|
64
|
+
| Hermes | `tkr setup-hermes` | plugin `~/.hermes/plugins/tokmizer/`, then `hermes plugins enable tokmizer` (Hermes v2026.8.16 or newer) |
|
|
65
|
+
|
|
66
|
+
Each plugin asks `tkr rewrite` for the Tokmizer form of a shell command just before the agent runs it. If `tkr` is missing, slow or fails, the command runs as typed. `XDG_CONFIG_HOME`, `VIBE_HOME`, `PI_CODING_AGENT_DIR` and `HERMES_HOME` are respected. Run the same command with `--remove` to take it out; your other plugins, hooks and settings are kept.
|
|
67
|
+
|
|
68
|
+
GitHub Copilot, Factory Droid, Trae, Qwen Code, Crush, Junie CLI, ZCode or Grok Build, after installing with npm or Homebrew:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
tkr setup-copilot
|
|
72
|
+
tkr setup-droid
|
|
73
|
+
tkr setup-trae
|
|
74
|
+
tkr setup-qwen
|
|
75
|
+
tkr setup-crush
|
|
76
|
+
tkr setup-junie
|
|
77
|
+
tkr setup-zcode
|
|
78
|
+
tkr setup-grok
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
| Command | Agent | File it edits |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `tkr setup-copilot` | Copilot in VS Code and Copilot CLI | `~/.copilot/hooks/tokmizer.json` (or `$COPILOT_HOME/hooks/`) |
|
|
84
|
+
| `tkr setup-droid` | Factory Droid | `~/.factory/hooks.json`, or `~/.factory/settings.json` when your own `PreToolUse` hooks live there |
|
|
85
|
+
| `tkr setup-trae` | Trae | `~/.trae/hooks.json`, plus `~/.trae-cn/hooks.json` when Trae CN is installed |
|
|
86
|
+
| `tkr setup-qwen` | Qwen Code | `~/.qwen/settings.json` (or `$QWEN_HOME`) |
|
|
87
|
+
| `tkr setup-crush` | Crush | `~/.config/crush/crush.json` (or `$XDG_CONFIG_HOME/crush/`) |
|
|
88
|
+
| `tkr setup-junie` | Junie CLI | `~/.junie/config.json` |
|
|
89
|
+
| `tkr setup-zcode` | ZCode | `~/.zcode/cli/config.json` (`hooks.events.PreToolUse`) |
|
|
90
|
+
| `tkr setup-grok` | Grok Build 1.0.4 or newer | `~/.grok/hooks/tokmizer.json` |
|
|
91
|
+
|
|
92
|
+
Each command adds one `PreToolUse` hook for the agent's shell tool. It rewrites the command before it runs, never approves it: the agent keeps asking for permission as usual. Your other hooks and settings stay as they are, the file as it was before each change is saved in `~/.tokmizer/backup/config/` (latest copy only), and `--remove` takes the hook out again. Restart the agent after setup. Crush still reads `crush.json` but now prefers `crushrc`; a `crush.json` with comments is left untouched and reported.
|
|
93
|
+
|
|
94
|
+
Permission rules stay in force. Agents match their allow, ask and deny rules against the command text, so Tokmizer leaves a command untouched whenever one of your deny or ask rules matches it. Tokmizer reads those rules for Claude Code (`permissions.deny` and `permissions.ask` in user, project, local and managed settings; a command matched by one of your `permissions.allow` rules is left untouched too, so that rule keeps approving it), OpenCode (`permission` in `opencode.json`), Cursor CLI (`permissions.deny`), Gemini CLI (policy files and `tools.exclude`) and Antigravity CLI (`permissions.deny` and `permissions.ask`). For the other agents, and on top of the Cursor, Gemini and Antigravity rules, only inspection, build and test commands (`cat`, `rg`, `git log`, `npm test`, `cargo build` and similar; inspection only for Codex) are routed; anything else, such as `git push` or `rm`, runs exactly as typed. If a settings file cannot be read, nothing is routed. Shell functions, aliases, builtins and anything that is not a program on your `PATH` always run exactly as typed.
|
|
95
|
+
|
|
96
|
+
In Claude Code, a command is rewritten only when that adds no permission prompt: in `bypassPermissions` mode, or when a plain `Bash` allow rule in your user or managed settings applies and the session is in default, `acceptEdits` or `dontAsk` mode. Otherwise it runs exactly as typed, so Claude Code still approves read-only commands such as `ls`, `cat` or `git status` on its own, and Tokmizer shortens the output once the command has succeeded.
|
|
97
|
+
|
|
98
|
+
Every agent found on this machine at once (each `tkr setup-…` above runs in turn, agents that are not installed are skipped, and a summary ends the run):
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
tkr setup --all
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`tkr setup` without `--all` lists the agents it found and asks which ones to set up.
|
|
105
|
+
|
|
106
|
+
### Importing your setup from one agent to another
|
|
107
|
+
|
|
108
|
+
Some agents import another agent's settings (Codex `/import` copies your Claude Code hooks), and config files get copied by hand. Tokmizer hooks are safe to bring along: each one reads which agent is calling it from the hook input and answers in that agent's format; if the input mixes fields of two agents, it changes nothing. Afterwards, run `tkr setup --all` or `tkr doctor --fix`: each agent gets its own Tokmizer hook, the copied ones are removed, your own hooks stay and every changed file is backed up in `~/.tokmizer/backup/config/`. `tkr doctor` also checks a project's `.codex/hooks.json` and `.codex/config.toml`. Junie CLI and ZCode send the same hook input as Claude Code, so a hook copied between those three answers in the format of the agent it was set up for.
|
|
109
|
+
|
|
41
110
|
Homebrew:
|
|
42
111
|
|
|
43
112
|
```bash
|
|
@@ -66,8 +135,8 @@ In Claude Code:
|
|
|
66
135
|
- `/tkr-status` shows your account and engine status
|
|
67
136
|
- `/tkr-compression` picks the compression level (light, balanced, max)
|
|
68
137
|
- `/tkr-yolo on` explicitly enables YOLO on this machine; `/tkr-yolo off` disables it
|
|
69
|
-
- `/tkr-reducer` makes the assistant's replies shorter (off, light, balanced, max)
|
|
70
|
-
- `/tkr-telemetry` views or changes
|
|
138
|
+
- `/tkr-reducer` makes the assistant's replies shorter (off, light, balanced, max), in Claude Code and in every agent set up with `tkr setup-<agent>`
|
|
139
|
+
- `/tkr-telemetry` views or changes usage stats (command names and token counts; on by default, off in one command)
|
|
71
140
|
- `/tkr-update` updates the plugin when a new version is out
|
|
72
141
|
|
|
73
142
|
From any terminal (after the shell-shim install, or `npm install -g @tokmizer/plugin`):
|
|
@@ -80,6 +149,39 @@ From any terminal (after the shell-shim install, or `npm install -g @tokmizer/pl
|
|
|
80
149
|
- `tkr yolo on|off|status` enables, disables, or checks the local YOLO mode
|
|
81
150
|
- `tkr reducer off|light|balanced|max` makes the assistant's replies shorter (default off)
|
|
82
151
|
- `tkr settings show|set|configure` manages settings and syncs them across your machines
|
|
152
|
+
- `tkr network off|on|status` blocks or allows every connection to tokmizer.com
|
|
153
|
+
- `tkr migrate rtk` moves your rtk filters, settings and history over and turns off rtk hooks where Tokmizer runs (see [Switching from rtk to Tokmizer](#switching-from-rtk-to-tokmizer))
|
|
154
|
+
|
|
155
|
+
## /power and the reducer in other agents
|
|
156
|
+
|
|
157
|
+
`tkr setup-<agent>` also installs `/power` and the reducer where the agent offers a documented way to do it; `tkr setup-<agent> --remove` and `tkr uninstall` take them out again. `/power` reads the same synced instructions as in Claude Code (`~/.tokmizer/skills/power.md`, refreshed by `tkr heartbeat`) and runs only the steps the agent's tools allow: it says what is left when it cannot run tests, commit or open a pull request. The reducer uses the same ruleset, word for word, in every agent and follows the single `tkr reducer off|light|balanced|max` setting.
|
|
158
|
+
|
|
159
|
+
| Agent | `/power` | Reducer |
|
|
160
|
+
| --- | --- | --- |
|
|
161
|
+
| Claude Code | native: `/power` command | native: SessionStart and UserPromptSubmit hooks |
|
|
162
|
+
| Codex | skill `~/.agents/skills/power`, type `$power <request>` | native: SessionStart and UserPromptSubmit hooks (approve them in `/hooks`) |
|
|
163
|
+
| Gemini CLI | native: `~/.gemini/commands/power.toml` | native: SessionStart and BeforeAgent hooks |
|
|
164
|
+
| Cursor | skill `/power` (`~/.agents/skills`) | native: sessionStart hook (no per-prompt context hook exists) |
|
|
165
|
+
| GitHub Copilot CLI | skill `/power` (`~/.agents/skills`) | native: sessionStart hook (per-prompt hook output is dropped by Copilot) |
|
|
166
|
+
| Factory Droid | skill `/power` (`~/.agents/skills`) | native: SessionStart and UserPromptSubmit hooks |
|
|
167
|
+
| Qwen Code | native: `~/.qwen/commands/power.md` | native: SessionStart and UserPromptSubmit hooks |
|
|
168
|
+
| ZCode | skill `/power` (`~/.zcode/skills`) | native: SessionStart and UserPromptSubmit hooks |
|
|
169
|
+
| Trae | not possible: no documented location for custom commands or skills | native: SessionStart and UserPromptSubmit hooks |
|
|
170
|
+
| Antigravity | skill `/power` (`~/.gemini/config/skills`) | native: SessionStart hook (system message) |
|
|
171
|
+
| Crush | skill `user:power` in the command palette (`~/.agents/skills`) | via rules: managed block in `~/.config/crush/CRUSH.md` (no session or prompt hook) |
|
|
172
|
+
| Junie CLI | native: `~/.junie/commands/power.md` | via rules: managed block in `~/.junie/AGENTS.md` (session-start context is ignored) |
|
|
173
|
+
| Grok Build | skill `/power` (`~/.grok/skills`) | via rules: managed block in `~/.grok/AGENTS.md` (hook output is ignored) |
|
|
174
|
+
| Mistral Vibe | skill `/power` (`~/.vibe/skills`) | via rules: managed block in `~/.vibe/AGENTS.md` (no session or prompt hook) |
|
|
175
|
+
| OpenCode | native: `~/.config/opencode/commands/power.md` | via plugin: system prompt, experimental `experimental.chat.system.transform` hook |
|
|
176
|
+
| Kilo Code | native: `~/.config/kilo/commands/power.md` | via plugin: system prompt, experimental `experimental.chat.system.transform` hook |
|
|
177
|
+
| Amp | skill `power` (`~/.agents/skills`); ask Amp to use it, Amp has no custom slash commands | via rules: managed block in `~/.config/amp/AGENTS.md` |
|
|
178
|
+
| Pi | native: `~/.pi/agent/prompts/power.md` | via rules: managed block in `~/.pi/agent/APPEND_SYSTEM.md` |
|
|
179
|
+
| Oh My Pi | native: `~/.omp/agent/commands/power.md` | via rules: managed block in `~/.omp/agent/APPEND_SYSTEM.md` |
|
|
180
|
+
| Hermes | skill `/power` (`~/.hermes/skills`) | via plugin: system prompt section |
|
|
181
|
+
| OpenClaw | skill `/power` (`~/.openclaw/skills`) | via plugin: `before_prompt_build`, once you set `plugins.entries.tokmizer.hooks.allowConversationAccess` to `true` |
|
|
182
|
+
| Cline, Windsurf, Aider | not installed: these agents only get command shims and have no `tkr setup-<agent>` command | not installed |
|
|
183
|
+
|
|
184
|
+
Never overwritten: an existing `power` command or skill that Tokmizer did not write is left as it is and reported. Rules files keep your text; Tokmizer only edits the block between `<!-- tokmizer:begin managed-by: tokmizer -->` and `<!-- tokmizer:end -->`, saves the file as it was before each change in `~/.tokmizer/backup/config/`, and rewrites that block when the reducer setting or its ruleset changes (`tkr reducer`, `tkr settings`, or a heartbeat). With the reducer off the block is removed, and it comes back when you turn the reducer on. When several agents read the same rules file through symlinks, the block stays until the last of them is removed. The shared `~/.agents/skills/power` skill stays while another agent that reads it is still set up. The OpenCode, Kilo Code, Hermes and OpenClaw plugins read the active ruleset from `~/.tokmizer/reducer-context.txt` (under `TOKMIZER_HOME` when set) without starting a process; Tokmizer rewrites that file when the reducer changes and deletes it when the reducer is off. `tkr uninstall` also deletes these backups.
|
|
83
185
|
|
|
84
186
|
## Choosing a compression mode
|
|
85
187
|
|
|
@@ -91,7 +193,7 @@ Since 0.9.52, short code reads and small explicit excerpts stay complete by defa
|
|
|
91
193
|
|
|
92
194
|
Without a linked account, standard settings stay on this machine and work offline after initial activation. With a linked account, changes require a connection and sync across machines. Linking an account restores its server settings.
|
|
93
195
|
|
|
94
|
-
The separate `reducer` setting provides response-style instructions
|
|
196
|
+
The separate `reducer` setting provides response-style instructions, starting with the next session, for Claude Code and for every agent set up with its `tkr setup-<agent>` command (see the table below). The instructions ask the model to preserve code, errors, and security information; compliance and actual savings depend on the model. If the ruleset is unavailable, the CLI reports it instead of claiming activation. Reconnect and run `tkr heartbeat` before starting a new session.
|
|
95
197
|
|
|
96
198
|
YOLO is off by default and requires an explicit `on`. It applies only to this machine, is not synchronized, and cannot be enabled from the dashboard.
|
|
97
199
|
|
|
@@ -112,8 +214,24 @@ Terminal commands managed by Tokmizer use the mode selected when execution start
|
|
|
112
214
|
|
|
113
215
|
In the next release, successful `tkr restore` calls are counted as additional output in the local gain history, even with telemetry disabled. They reduce the net saving; repeated restorations count again and can make it negative. The display shows restored tokens separately. `gain --json` exposes the net estimate in `observed.savedTokens` and restoration totals in `restoration`; the existing top-level `savedTokens` remains the total for reduced outputs only. Earlier restorations cannot be reconstructed from local history. Manually repeated commands, prompts, replies and replayed conversation context are not attributed to restoration costs.
|
|
114
216
|
|
|
217
|
+
Other local views: `tkr gain --by-rule` groups savings by rule (your custom filters by name, built-in rules by command), `tkr gain --failures` lists commands whose output went through unchanged and why (no rule, nothing to cut, failed run kept in full), and `tkr gain --quota [pro|5x|20x]` estimates the share of a Claude plan budget the savings represent. The quota view is an estimate, not a plan limit: tokens saved per day × 30, divided by an assumed 44,000 tokens per 5-hour window × 144 windows × the plan multiplier (1, 5 or 20). Anthropic does not publish plan limits in tokens. All three combine with a period, `--project` and `--json`.
|
|
218
|
+
|
|
115
219
|
The percentages in `tkr gain` measure byte reduction. A byte percentage can differ substantially from the reduction counted by a model's tokenizer. Restoring a full file after reading its outline can cost more tokens than reading it once. Compression is not a promise of faster task completion or a correct answer.
|
|
116
220
|
|
|
221
|
+
## Restoring shortened output
|
|
222
|
+
|
|
223
|
+
When Tokmizer shortens an output it can add a `[tkr restore <hash>]` hint. `tkr restore <hash>` prints the complete original output when it is at most 8 MB; for a larger output, the entry holds only the part that was left out and `tkr restore` says so. A unique prefix of the hash is enough.
|
|
224
|
+
|
|
225
|
+
| Command | Effect |
|
|
226
|
+
| --- | --- |
|
|
227
|
+
| `tkr restore <hash>` | Print the cached output |
|
|
228
|
+
| `tkr restore <hash> --lines 120:180` | Print only lines 120 to 180 (`120:`, `:40` and `57` also work) |
|
|
229
|
+
| `tkr restore <hash> --grep <pattern>` | Print matching lines with their line numbers |
|
|
230
|
+
| `tkr restore --grep <pattern>` | Search every cached output |
|
|
231
|
+
| `tkr restore --list` | List cached outputs with age, size, line count and command |
|
|
232
|
+
|
|
233
|
+
Entries are kept 30 days, up to 8 MB each and 200 MB in total; the oldest go first. `TOKMIZER_CCR_MAX_AGE_MS`, `TOKMIZER_CCR_ENTRY_MAX_BYTES` and `TOKMIZER_CCR_DIR_CAP_BYTES` change these limits, `TOKMIZER_CCR_DISABLED=1` turns copies off. `tkr gain` shows the restored tokens and the cache size.
|
|
234
|
+
|
|
117
235
|
## Measured comparison
|
|
118
236
|
|
|
119
237
|
On October 9, 2026, a fixed set of 30 real and synthetic command outputs contained 774,863 tokens before compression. The table counts tokens with the cl100k tokenizer, excluding restoration notices. It is a test corpus, not a sample of customer usage.
|
|
@@ -137,6 +255,51 @@ In a separate controlled test for 0.9.52, a repetitive failure log went from 1,0
|
|
|
137
255
|
|
|
138
256
|
These tokenizer counts are separate from the byte-based estimates in `tkr gain`. They measure output volume, not your bill, an agent's task success, or execution speed. See the [mode guide](https://tokmizer.com/en/docs#configuration) for usage and limits.
|
|
139
257
|
|
|
258
|
+
## Custom filters
|
|
259
|
+
|
|
260
|
+
Add your own output rules as `*.toml` files in `~/.tokmizer/filters/` (or `$TOKMIZER_HOME/filters/`). They load next to the built-in rules and win over them for the commands they match. They stay on your machine and are never uploaded. A file or rule that fails validation is skipped; `tkr doctor` shows what was loaded and why something was skipped.
|
|
261
|
+
|
|
262
|
+
A file is used only after you trust it: review it, then run `tkr filters trust` (all files) or `tkr filters trust <file>`. Tokmizer records the file's SHA-256; editing the file stops it from being used until you trust it again. `tkr filters untrust [file]` removes the trust.
|
|
263
|
+
|
|
264
|
+
```toml
|
|
265
|
+
schema_version = 1
|
|
266
|
+
|
|
267
|
+
[filters.make-check]
|
|
268
|
+
match_command = "^make\\s+check(\\s|$)"
|
|
269
|
+
strip_lines_matching = ["^make\\[\\d+\\]: (Entering|Leaving) directory"]
|
|
270
|
+
max_lines = 120
|
|
271
|
+
on_empty = "make check: no output"
|
|
272
|
+
|
|
273
|
+
[[filters.make-check.tests]]
|
|
274
|
+
name = "drops directory noise"
|
|
275
|
+
input = "make[1]: Entering directory '/src'\nok\n"
|
|
276
|
+
expected = "ok"
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
`schema_version` is optional; a version this tkr cannot read makes the file skipped with a message. `tkr filters test [rule]` runs the inline tests, including on files you have not trusted yet, and exits with 1 when one fails. Tests check the rule as written (the Light level) and ignore trailing whitespace; Balanced and Max shorten `max_lines` further.
|
|
280
|
+
|
|
281
|
+
`match_command` is a regular expression tested against the command line. `strip_lines_matching` drops matching lines, `max_lines` caps the number of lines kept and `on_empty` replaces an empty result. Also available: `strip_ansi = true`, `keep_failures = true` (failure lines survive the cap), `truncate_lines_at = 400`, and `priority = 60` to pick between several of your rules that match the same command.
|
|
282
|
+
|
|
283
|
+
## Switching from rtk to Tokmizer
|
|
284
|
+
|
|
285
|
+
If you already use rtk, one command brings over what can be brought over and stops commands from going through two hooks:
|
|
286
|
+
|
|
287
|
+
```sh
|
|
288
|
+
tkr migrate rtk # show the plan, then ask before changing anything
|
|
289
|
+
tkr migrate rtk --dry-run # only show the plan
|
|
290
|
+
tkr migrate rtk --yes # apply without asking
|
|
291
|
+
tkr migrate rtk --undo # put back every file the migrations changed, newest first
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
- **Filters.** rtk's global `filters.toml` (`~/.config/rtk/` on Linux, `~/Library/Application Support/rtk/` on macOS, `%APPDATA%\rtk\` on Windows) and the project's `.rtk/filters.toml` become files in `~/.tokmizer/filters/`. Their `[[tests]]` run again on Tokmizer and each result is printed. A filter that uses an option Tokmizer filters do not have (`replace`, `match_output`, `keep_lines_matching`, `head_lines`, `tail_lines`, `filter_stderr`) is not imported, and the plan says why. A file rtk had trusted stays trusted; the others, and project files (Tokmizer filters apply in every project), wait for `tkr filters trust`.
|
|
295
|
+
- **Settings.** From `config.toml`: `exclude_commands` (the output of those commands is left unchanged), the `rtk recall` retention or its `disabled` mode (`~/.tokmizer/restore.json`), and telemetry turned off (`tkr telemetry off`). Every other setting is listed as not imported.
|
|
296
|
+
- **History.** The totals in rtk's `history.db` are read without changing it. `tkr gain` shows them on their own "Imported from rtk" line, never added to the savings Tokmizer measured. An unreadable file is skipped.
|
|
297
|
+
- **Agent hooks.** For each agent where both rtk and Tokmizer are installed (Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, Factory Droid, Trae, Mistral Vibe, Antigravity, OpenCode, Pi, Oh My Pi), the rtk hook is removed so commands are not wrapped twice. Where Tokmizer is not installed yet, the rtk hook stays and the plan says which `tkr setup-…` to run first. rtk's own proxy setting (`ANTHROPIC_BASE_URL`) is left alone. `tkr doctor` reports any agent where both still hook the same commands.
|
|
298
|
+
|
|
299
|
+
Every changed file is backed up under `~/.tokmizer/migrate-rtk/`. Running `--yes` again changes only what is new since the last migration and says `Already migrated` when nothing is; `--undo` reverts every migration still in place, newest first, and does not overwrite a file you changed after the migration: it tells you where its backup is.
|
|
300
|
+
|
|
301
|
+
`tkr migrate rtk --shim` also adds an `rtk` command to `~/.tokmizer/shims` for scripts and habits: `rtk gain` runs `tkr gain`, `rtk recall <hash>` runs `tkr restore <hash>`, `rtk bypass <cmd>` (or `rtk proxy <cmd>`) runs the command unchanged, and any other subcommand explains what to run instead. It takes over once rtk itself is uninstalled and `~/.tokmizer/shims` is on your `PATH`.
|
|
302
|
+
|
|
140
303
|
## Distribution
|
|
141
304
|
|
|
142
305
|
This README is shared by the npm package and the public GitHub repository.
|
|
@@ -146,12 +309,55 @@ This README is shared by the npm package and the public GitHub repository.
|
|
|
146
309
|
|
|
147
310
|
The optimization engine and API client ship compiled with [`@tokmizer/plugin`](https://www.npmjs.com/package/@tokmizer/plugin). Their source code is not in the public repository.
|
|
148
311
|
|
|
149
|
-
##
|
|
312
|
+
## Network & privacy
|
|
313
|
+
|
|
314
|
+
Tokmizer works without an account or email. It contacts `tokmizer.com` to keep its rules up to date and to improve the product. One command turns all of it off: `tkr network off`.
|
|
315
|
+
|
|
316
|
+
What leaves your machine, and when:
|
|
317
|
+
|
|
318
|
+
| When | What is sent | Why |
|
|
319
|
+
|---|---|---|
|
|
320
|
+
| First online run, no account | A random install ID, your computer name, OS and version, CPU model and core count, a SHA-256 hash of your network card (MAC) address, a SHA-256 hash of your user name, a fingerprint (SHA-256 of all of these), your time zone and the plugin version | Create the install token used to download rules |
|
|
321
|
+
| `tkr link <key>` | The same, plus your key, and your email only if you pass `--email` | Attach this machine to your account |
|
|
322
|
+
| Regular check-in (heartbeat) | Install ID, fingerprint, plugin version, local date and time zone | Refresh access and learn that a new version is out |
|
|
323
|
+
| Rule downloads | Install ID, fingerprint, plugin version | Get the latest compression rules and the `/power` and reducer instructions |
|
|
324
|
+
| Settings sync, linked accounts only | Your Tokmizer settings | Keep them in sync with the dashboard |
|
|
325
|
+
| Usage stats, only while they are on (with or without an account) | Command names such as `npm install` (never arguments; Git and GitHub commands excluded), token counts, rule IDs with output sizes, session ID, agent name | See which commands to optimize next |
|
|
326
|
+
| `tkr unlink` | Install ID, fingerprint | Remove this device from your account |
|
|
327
|
+
| CLI self-update, after a check-in reports a new version | Runs `npm i -g @tokmizer/plugin@<version>` or `brew upgrade tokmizer`, which contact the npm registry, Homebrew and GitHub | Keep the CLI current (`tkr auto-update off` stops only this) |
|
|
328
|
+
|
|
329
|
+
The hashes are not anonymous: the same machine always gives the same values, and a user name hash can be guessed. Like any web server, `tokmizer.com` sees your IP address and keeps it in its security log. Tokmizer never sends command arguments, command output, file contents or file paths.
|
|
330
|
+
|
|
331
|
+
Usage stats are on by default and off in one command: `tkr telemetry off` (or `tkr network off` to stop every connection). The choice is saved on this machine and survives updates; an install that turned them off stays off. Before anything is sent, Tokmizer says so once, in the terminal or in the Claude Code session notice. Check with `tkr telemetry status`.
|
|
332
|
+
|
|
333
|
+
### Usage stats without an account
|
|
334
|
+
|
|
335
|
+
Usage stats do not need an account. Without one, they are stored against this install's random key, not against an email or a person. They are still not anonymous: the computer name and the other device details listed above are sent when the install registers and are kept with that key.
|
|
336
|
+
|
|
337
|
+
- **Sent:** for each command, its name, the token count before compression, tokens saved and restored, whether it was compressed, the session ID and the agent name. Never arguments, output, file contents or paths.
|
|
338
|
+
- **Only with your consent:** the plugin uploads them only while `tkr telemetry status` says `on`, and tells the server so with each upload. Nothing is accepted from an install that is off or whose network is off.
|
|
339
|
+
- **Once per install, before the first upload:** Tokmizer says so once (first run, setup, session start or upgrade) and sends nothing until that notice has been shown, then stays quiet.
|
|
340
|
+
- **Kept:** detailed rows for 90 days, then only a running total of tokens saved per key.
|
|
341
|
+
- **Linking later:** `tkr link` moves the install's past usage stats to your account, so your dashboard includes them.
|
|
342
|
+
- **Turn off:** `tkr telemetry off`. To stop every connection: `tkr network off`.
|
|
343
|
+
|
|
344
|
+
### Turn the network off
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
tkr network off # nothing leaves this machine
|
|
348
|
+
tkr network status
|
|
349
|
+
tkr network on
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
In CI or for one shell, set `TOKMIZER_NETWORK=0`. `tkr status` and `tkr doctor` show the current state.
|
|
353
|
+
|
|
354
|
+
While it is off there is no sign-up, heartbeat, rule download, settings sync, usage stats or update check. Tokmizer keeps compressing locally: with the last downloaded rules for up to 7 days, then with its core built-in rules. Settings are saved on this machine only. `tkr link` and `tkr heartbeat` explain how to turn the network back on.
|
|
355
|
+
|
|
356
|
+
The switch does not cover Claude Code's own marketplace auto-update, which `/tkr-update` turns on (set `extraKnownMarketplaces.tokmizer.autoUpdate` to `false` in `~/.claude/settings.json` to stop it), nor commands you run yourself such as the installer, `/tkr-update`, npm or brew.
|
|
357
|
+
|
|
358
|
+
### Local data
|
|
150
359
|
|
|
151
|
-
-
|
|
152
|
-
- In 0.9.52, telemetry defaults to on unless setup or saved settings turn it off. Check `tkr telemetry status`; using Tokmizer without an account does not itself disable telemetry.
|
|
153
|
-
- Turning telemetry off stops optional usage reporting. Initial activation, rule downloads, update checks and account synchronization are separate network operations. Activation includes device and installation metadata. Offline use requires previously downloaded rules and is subject to cache availability and expiry.
|
|
154
|
-
- Restoration copies are local and temporary. A restore hint identifies available cached content; there is no guarantee that every removed detail has a saved copy. YOLO creates no new restoration copies.
|
|
360
|
+
- Restoration copies are local and kept up to 30 days. A restore hint identifies available cached content; there is no guarantee that every removed detail has a saved copy. YOLO creates no new restoration copies.
|
|
155
361
|
- The public [redaction helpers](https://github.com/norwelltw/Tokmizer/blob/main/src/learn/redact.ts) are available for inspection.
|
|
156
362
|
- Local state lives in `~/.tokmizer/`.
|
|
157
363
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
const TKR = __TKR_ARGV__;
|
|
4
|
+
const ARGS = ["rewrite", "--host", "amp"];
|
|
5
|
+
|
|
6
|
+
export const description = "Routes shell commands through tokmizer to cut tool-output tokens.";
|
|
7
|
+
|
|
8
|
+
function rewrite(command, session) {
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
if (typeof command !== "string" || !command.trim()) return resolve(null);
|
|
11
|
+
try {
|
|
12
|
+
const args = [...TKR.slice(1), ...ARGS, ...(typeof session === "string" && session ? ["--session", session] : [])];
|
|
13
|
+
const child = execFile(TKR[0], args, { timeout: 3000, windowsHide: true, encoding: "utf8", maxBuffer: 1 << 20 }, (error, stdout) => {
|
|
14
|
+
resolve(!error && typeof stdout === "string" && stdout.trim() ? stdout : null);
|
|
15
|
+
});
|
|
16
|
+
child.stdin?.on("error", () => {});
|
|
17
|
+
child.stdin?.end(command);
|
|
18
|
+
} catch {
|
|
19
|
+
resolve(null);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default function (amp) {
|
|
25
|
+
amp.on("tool.call", async (event) => {
|
|
26
|
+
try {
|
|
27
|
+
const shell = amp.helpers?.shellCommandFromToolCall?.(event);
|
|
28
|
+
const input = event?.input;
|
|
29
|
+
if (!shell || !input || typeof input !== "object") return { action: "allow" };
|
|
30
|
+
const key = input.command === shell.command ? "command" : input.cmd === shell.command ? "cmd" : null;
|
|
31
|
+
if (!key) return { action: "allow" };
|
|
32
|
+
const rewritten = await rewrite(shell.command, event?.thread?.id);
|
|
33
|
+
return rewritten ? { action: "modify", input: { ...input, [key]: rewritten } } : { action: "allow" };
|
|
34
|
+
} catch {
|
|
35
|
+
return { action: "allow" };
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
|
|
3
|
+
TKR = __TKR_ARGV__
|
|
4
|
+
CONTEXT_FILE = __TKR_CONTEXT_FILE__
|
|
5
|
+
ARGS = ["rewrite", "--host", "hermes"]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _rewrite(command, session):
|
|
9
|
+
extra = ["--session", session] if isinstance(session, str) and session else []
|
|
10
|
+
result = subprocess.run(
|
|
11
|
+
TKR + ARGS + extra,
|
|
12
|
+
input=command,
|
|
13
|
+
capture_output=True,
|
|
14
|
+
text=True,
|
|
15
|
+
encoding="utf-8",
|
|
16
|
+
timeout=3,
|
|
17
|
+
shell=False,
|
|
18
|
+
)
|
|
19
|
+
if result.returncode != 0 or not result.stdout.strip():
|
|
20
|
+
return None
|
|
21
|
+
return result.stdout
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _pre_tool_call(tool_name=None, args=None, **kwargs):
|
|
25
|
+
try:
|
|
26
|
+
if tool_name != "terminal" or not isinstance(args, dict):
|
|
27
|
+
return None
|
|
28
|
+
command = args.get("command")
|
|
29
|
+
if not isinstance(command, str) or not command.strip():
|
|
30
|
+
return None
|
|
31
|
+
rewritten = _rewrite(command, kwargs.get("session_id"))
|
|
32
|
+
if not rewritten:
|
|
33
|
+
return None
|
|
34
|
+
return {"action": "modify", "args": {"command": rewritten}}
|
|
35
|
+
except Exception:
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _reducer_section(*args, **kwargs):
|
|
40
|
+
try:
|
|
41
|
+
with open(CONTEXT_FILE, encoding="utf-8") as handle:
|
|
42
|
+
return handle.read().strip()
|
|
43
|
+
except Exception:
|
|
44
|
+
return ""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def register(ctx):
|
|
48
|
+
ctx.register_hook("pre_tool_call", _pre_tool_call)
|
|
49
|
+
if hasattr(ctx, "register_system_prompt_section"):
|
|
50
|
+
try:
|
|
51
|
+
ctx.register_system_prompt_section("tokmizer.reducer", _reducer_section, position="after_memory", max_chars=8000)
|
|
52
|
+
except Exception:
|
|
53
|
+
pass
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
|
|
4
|
+
const TKR = __TKR_ARGV__;
|
|
5
|
+
const CONTEXT_FILE = __TKR_CONTEXT_FILE__;
|
|
6
|
+
const ARGS = ["rewrite", "--host", "kilo"];
|
|
7
|
+
|
|
8
|
+
function rewrite(command, session) {
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
if (typeof command !== "string" || !command.trim()) return resolve(null);
|
|
11
|
+
try {
|
|
12
|
+
const args = [...TKR.slice(1), ...ARGS, ...(typeof session === "string" && session ? ["--session", session] : [])];
|
|
13
|
+
const child = execFile(TKR[0], args, { timeout: 3000, windowsHide: true, encoding: "utf8", maxBuffer: 1 << 20 }, (error, stdout) => {
|
|
14
|
+
resolve(!error && typeof stdout === "string" && stdout.trim() ? stdout : null);
|
|
15
|
+
});
|
|
16
|
+
child.stdin?.on("error", () => {});
|
|
17
|
+
child.stdin?.end(command);
|
|
18
|
+
} catch {
|
|
19
|
+
resolve(null);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const contexts = new Map();
|
|
25
|
+
|
|
26
|
+
function readContext() {
|
|
27
|
+
try {
|
|
28
|
+
const text = readFileSync(CONTEXT_FILE, "utf8").trim();
|
|
29
|
+
return text || null;
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function reducerContext(session) {
|
|
36
|
+
const key = typeof session === "string" && session ? session : "";
|
|
37
|
+
if (contexts.has(key)) return contexts.get(key);
|
|
38
|
+
const text = readContext();
|
|
39
|
+
if (text === null) return null;
|
|
40
|
+
if (contexts.size >= 64) contexts.delete(contexts.keys().next().value);
|
|
41
|
+
contexts.set(key, text);
|
|
42
|
+
return text;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function addReducer(session, output) {
|
|
46
|
+
try {
|
|
47
|
+
if (!output || !Array.isArray(output.system)) return;
|
|
48
|
+
const text = reducerContext(session);
|
|
49
|
+
if (text) output.system.push(text);
|
|
50
|
+
} catch {}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function apply(tool, container, session) {
|
|
54
|
+
try {
|
|
55
|
+
if ((tool !== "bash" && tool !== "shell") || !container || typeof container !== "object") return;
|
|
56
|
+
const rewritten = await rewrite(container.command, session);
|
|
57
|
+
if (rewritten) container.command = rewritten;
|
|
58
|
+
} catch {}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export default {
|
|
62
|
+
id: "tokmizer",
|
|
63
|
+
async setup(ctx) {
|
|
64
|
+
await ctx?.tool?.hook?.("execute.before", (event) => apply(event?.tool, event?.input, event?.sessionID));
|
|
65
|
+
},
|
|
66
|
+
async server() {
|
|
67
|
+
return {
|
|
68
|
+
"tool.execute.before": (input, output) => apply(input?.tool, output?.args, input?.sessionID),
|
|
69
|
+
"experimental.chat.system.transform": (input, output) => addReducer(input?.sessionID, output)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
const TKR = __TKR_ARGV__;
|
|
4
|
+
const ARGS = ["rewrite", "--host", "omp"];
|
|
5
|
+
|
|
6
|
+
function rewrite(command, session) {
|
|
7
|
+
return new Promise((resolve) => {
|
|
8
|
+
if (typeof command !== "string" || !command.trim()) return resolve(null);
|
|
9
|
+
try {
|
|
10
|
+
const args = [...TKR.slice(1), ...ARGS, ...(typeof session === "string" && session ? ["--session", session] : [])];
|
|
11
|
+
const child = execFile(TKR[0], args, { timeout: 3000, windowsHide: true, encoding: "utf8", maxBuffer: 1 << 20 }, (error, stdout) => {
|
|
12
|
+
resolve(!error && typeof stdout === "string" && stdout.trim() ? stdout : null);
|
|
13
|
+
});
|
|
14
|
+
child.stdin?.on("error", () => {});
|
|
15
|
+
child.stdin?.end(command);
|
|
16
|
+
} catch {
|
|
17
|
+
resolve(null);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default function (pi) {
|
|
23
|
+
pi.on("tool_call", async (event) => {
|
|
24
|
+
try {
|
|
25
|
+
if (event?.toolName !== "bash" || !event.input || typeof event.input !== "object") return;
|
|
26
|
+
const rewritten = await rewrite(event.input.command);
|
|
27
|
+
return rewritten ? { input: { ...event.input, command: rewritten } } : undefined;
|
|
28
|
+
} catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
|
|
4
|
+
const TKR = __TKR_ARGV__;
|
|
5
|
+
const CONTEXT_FILE = __TKR_CONTEXT_FILE__;
|
|
6
|
+
const ARGS = ["rewrite", "--host", "openclaw"];
|
|
7
|
+
|
|
8
|
+
function rewrite(command, session) {
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
if (typeof command !== "string" || !command.trim()) return resolve(null);
|
|
11
|
+
try {
|
|
12
|
+
const args = [...TKR.slice(1), ...ARGS, ...(typeof session === "string" && session ? ["--session", session] : [])];
|
|
13
|
+
const child = execFile(TKR[0], args, { timeout: 3000, windowsHide: true, encoding: "utf8", maxBuffer: 1 << 20 }, (error, stdout) => {
|
|
14
|
+
resolve(!error && typeof stdout === "string" && stdout.trim() ? stdout : null);
|
|
15
|
+
});
|
|
16
|
+
child.stdin?.on("error", () => {});
|
|
17
|
+
child.stdin?.end(command);
|
|
18
|
+
} catch {
|
|
19
|
+
resolve(null);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function reducerContext() {
|
|
25
|
+
try {
|
|
26
|
+
const text = readFileSync(CONTEXT_FILE, "utf8").trim();
|
|
27
|
+
return text || null;
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default {
|
|
34
|
+
id: "tokmizer",
|
|
35
|
+
name: "Tokmizer",
|
|
36
|
+
description: "Routes exec commands through tokmizer to cut tool-output tokens.",
|
|
37
|
+
register(api) {
|
|
38
|
+
api.on("before_tool_call", async (event) => {
|
|
39
|
+
try {
|
|
40
|
+
if (event?.toolName !== "exec" || event.toolKind === "code_mode_exec") return undefined;
|
|
41
|
+
const params = event.params;
|
|
42
|
+
if (!params || typeof params !== "object") return undefined;
|
|
43
|
+
const rewritten = await rewrite(params.command);
|
|
44
|
+
return rewritten ? { params: { ...params, command: rewritten } } : undefined;
|
|
45
|
+
} catch {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
api.on("before_prompt_build", async () => {
|
|
50
|
+
try {
|
|
51
|
+
const text = reducerContext();
|
|
52
|
+
return text ? { appendSystemContext: text } : undefined;
|
|
53
|
+
} catch {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
};
|