@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.
Files changed (60) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +11 -0
  3. package/README.md +214 -8
  4. package/agent-plugins/amp.js +38 -0
  5. package/agent-plugins/hermes/__init__.py +53 -0
  6. package/agent-plugins/hermes/plugin.yaml +5 -0
  7. package/agent-plugins/kilo.js +72 -0
  8. package/agent-plugins/omp.js +32 -0
  9. package/agent-plugins/openclaw/index.js +58 -0
  10. package/agent-plugins/openclaw/openclaw.plugin.json +12 -0
  11. package/agent-plugins/openclaw/package.json +11 -0
  12. package/agent-plugins/opencode.js +72 -0
  13. package/agent-plugins/pi.js +30 -0
  14. package/codex-hooks/hooks.json +16 -0
  15. package/command-templates/tkr-gain.md +7 -2
  16. package/command-templates/tkr-status.md +5 -1
  17. package/command-templates/tkr-telemetry.md +3 -3
  18. package/command-templates/tkr-unlink.md +2 -2
  19. package/dist/bin/tkr-agent-hook.js +13 -0
  20. package/dist/bin/tkr-codex-hook.js +134 -40
  21. package/dist/bin/tkr-context-hook.js +4 -0
  22. package/dist/bin/tkr-copilot-hook.js +13 -0
  23. package/dist/bin/tkr-crush-hook.js +13 -0
  24. package/dist/bin/tkr-cursor-hook.js +13 -0
  25. package/dist/bin/tkr-daemon.js +194 -56
  26. package/dist/bin/tkr-droid-hook.js +13 -0
  27. package/dist/bin/tkr-exec +136 -42
  28. package/dist/bin/tkr-exec.js +136 -42
  29. package/dist/bin/tkr-gemini-hook.js +13 -0
  30. package/dist/bin/tkr-grok-hook.js +13 -0
  31. package/dist/bin/tkr-junie-hook.js +13 -0
  32. package/dist/bin/tkr-processor.js +166 -0
  33. package/dist/bin/tkr-qwen-hook.js +13 -0
  34. package/dist/bin/tkr-shim.js +138 -37
  35. package/dist/bin/tkr-shim.stable +1 -0
  36. package/dist/bin/tkr-trae-hook.js +13 -0
  37. package/dist/bin/tkr-zcode-hook.js +13 -0
  38. package/dist/bin/tkr.js +425 -157
  39. package/dist/hooks-host/post-tool-use.js +150 -49
  40. package/dist/hooks-host/pre-tool-use.js +10 -7
  41. package/dist/hooks-host/reducer-prompt-submit.js +2 -1
  42. package/dist/hooks-host/reducer-session-start.js +2 -1
  43. package/dist/hooks-host/reducer-stop.js +25 -16
  44. package/dist/hooks-host/session-recap.js +4 -3
  45. package/dist/index.js +160 -41
  46. package/dist/native/manifest.json +18 -8
  47. package/dist/native/single.json +38 -0
  48. package/dist/native/tkr-exec-darwin-arm64 +0 -0
  49. package/dist/native/tkr-exec-darwin-x64 +0 -0
  50. package/dist/native/tkr-exec-linux-arm64 +0 -0
  51. package/dist/native/tkr-exec-linux-x64 +0 -0
  52. package/dist/native/tkr-exec-win32-arm64.exe +0 -0
  53. package/dist/native/tkr-exec-win32-x64.exe +0 -0
  54. package/hooks/on-session-start.d.mts +1 -0
  55. package/hooks/on-session-start.mjs +37 -34
  56. package/package.json +26 -3
  57. package/scripts/README-INSTALL.md +50 -14
  58. package/scripts/install-mac-linux.sh +77 -7
  59. package/scripts/install-native.mjs +177 -25
  60. package/codex-plugin/plugin.json +0 -7
@@ -1,26 +1,88 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import { execFileSync } from "node:child_process";
3
- import { chmodSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
3
+ import { chmodSync, existsSync, linkSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
4
+ import { createRequire } from "node:module";
4
5
  import { dirname, join, resolve } from "node:path";
5
6
  import { fileURLToPath } from "node:url";
6
7
 
7
8
  const packageDirectory = resolve(dirname(fileURLToPath(import.meta.url)), "..");
9
+ const sessionStartCommand = '"${CLAUDE_PLUGIN_ROOT}/dist/bin/tkr-exec" --session-start';
10
+ const windowsCommand = "${CLAUDE_PLUGIN_ROOT}/dist/bin/tkr-exec.exe";
8
11
 
9
- function configureHook(directory, native) {
12
+ function sessionStartHook(hook) {
13
+ return hook.command === sessionStartCommand || (hook.command === windowsCommand && Array.isArray(hook.args) && hook.args.length === 1 && hook.args[0] === "--session-start");
14
+ }
15
+
16
+ function configureSessionStart(manifest, mode) {
17
+ const groups = manifest.hooks?.SessionStart;
18
+ if (!Array.isArray(groups) || groups.length === 0) return false;
19
+ const wanted = mode === "windows" ? { type: "command", command: windowsCommand, args: ["--session-start"], timeout: 5 } : mode === "unix" ? { type: "command", command: sessionStartCommand, timeout: 5 } : null;
20
+ const present = groups.flatMap((group) => (group.hooks ?? []).filter(sessionStartHook));
21
+ if (wanted && present.length === 1 && present[0].command === wanted.command) return false;
22
+ if (!wanted && present.length === 0) return false;
23
+ for (const group of groups) if (group.hooks) group.hooks = group.hooks.filter((hook) => !sessionStartHook(hook));
24
+ if (wanted) (groups[0].hooks ??= []).unshift(wanted);
25
+ return true;
26
+ }
27
+
28
+ const root = "${CLAUDE_PLUGIN_ROOT}";
29
+ const knownHooks = [
30
+ { event: "PreToolUse", node: `node "${root}/dist/hooks-host/pre-tool-use.js"`, words: ["hook", "pre-tool-use"], flag: "--pre-tool-use" },
31
+ { event: "PostToolUse", node: `node "${root}/dist/hooks-host/post-tool-use.js"`, words: ["hook", "post-tool-use"], flag: "--post-tool-use" },
32
+ { event: "SessionStart", node: `node "${root}/hooks/on-session-start.mjs"`, words: ["hook", "session-start"] },
33
+ { event: "SessionStart", node: `node "${root}/dist/hooks-host/reducer-session-start.js"`, words: ["hook", "reducer-session-start"] },
34
+ { event: "SessionStart", node: `node "${root}/dist/hooks-host/session-recap.js"`, words: ["hook", "session-recap"] },
35
+ { event: "UserPromptSubmit", node: `node "${root}/dist/hooks-host/reducer-prompt-submit.js"`, words: ["hook", "reducer-prompt-submit"] },
36
+ { event: "Stop", node: `node "${root}/dist/hooks-host/reducer-stop.js"`, words: ["hook", "reducer-stop"] }
37
+ ];
38
+
39
+ function hookForms(spec) {
40
+ const forms = {
41
+ node: { command: spec.node },
42
+ singleUnix: { command: `"${root}/dist/bin/tkr" ${spec.words.join(" ")}` },
43
+ singleWindows: { command: `${root}/dist/bin/tkr.exe`, args: spec.words }
44
+ };
45
+ if (spec.flag) {
46
+ forms.nativeUnix = { command: `"${root}/dist/bin/tkr-exec" ${spec.flag}` };
47
+ forms.nativeWindows = { command: windowsCommand, args: [spec.flag] };
48
+ }
49
+ return forms;
50
+ }
51
+
52
+ function sameArgs(left, right) {
53
+ return Array.isArray(left) && left.length === right.length && left.every((value, index) => value === right[index]);
54
+ }
55
+
56
+ function matchesForm(hook, form) {
57
+ return hook.command === form.command && (!form.args || sameArgs(hook.args, form.args));
58
+ }
59
+
60
+ function wantedForm(forms, mode, single, windows) {
61
+ if (forms.nativeUnix && mode === "unix") return forms.nativeUnix;
62
+ if (forms.nativeWindows && mode === "windows") return forms.nativeWindows;
63
+ if (single) return windows ? forms.singleWindows : forms.singleUnix;
64
+ return forms.node;
65
+ }
66
+
67
+ function configureHook(directory, mode, single = false, windows = mode === "windows") {
10
68
  const path = join(directory, "hooks", "hooks.json");
11
69
  let manifest;
12
70
  try { manifest = JSON.parse(readFileSync(path, "utf8")); } catch { return; }
13
- const nodeCommand = 'node "${CLAUDE_PLUGIN_ROOT}/dist/hooks-host/pre-tool-use.js"';
14
- const nativeCommand = '"${CLAUDE_PLUGIN_ROOT}/dist/bin/tkr-exec" --pre-tool-use';
15
- let changed = false;
16
- for (const group of manifest.hooks?.PreToolUse ?? []) {
17
- for (const hook of group.hooks ?? []) {
18
- if (hook.command !== nodeCommand && hook.command !== nativeCommand) continue;
19
- const command = native ? nativeCommand : nodeCommand;
20
- if (hook.command !== command) { hook.command = command; changed = true; }
71
+ const before = JSON.stringify(manifest);
72
+ configureSessionStart(manifest, mode);
73
+ for (const spec of knownHooks) {
74
+ const forms = hookForms(spec);
75
+ const wanted = wantedForm(forms, mode, single, windows);
76
+ for (const group of manifest.hooks?.[spec.event] ?? []) {
77
+ for (const hook of group.hooks ?? []) {
78
+ if (!Object.values(forms).some((form) => matchesForm(hook, form))) continue;
79
+ hook.command = wanted.command;
80
+ if (wanted.args) hook.args = [...wanted.args];
81
+ else delete hook.args;
82
+ }
21
83
  }
22
84
  }
23
- if (!changed) return;
85
+ if (JSON.stringify(manifest) === before) return;
24
86
  const temporary = `${path}.${randomUUID()}.tmp`;
25
87
  try {
26
88
  writeFileSync(temporary, JSON.stringify(manifest, null, 2) + "\n", { flag: "wx", mode: 0o644 });
@@ -30,37 +92,127 @@ function configureHook(directory, native) {
30
92
  }
31
93
  }
32
94
 
33
- export function installNative(directory = packageDirectory, platform = process.platform, arch = process.arch) {
34
- const target = join(directory, "dist", "bin", "tkr-exec");
95
+ function singlePath(directory, platform) {
96
+ return join(directory, "dist", "bin", platform === "win32" ? "tkr.exe" : "tkr");
97
+ }
98
+
99
+ function probeSingle(path, version) {
100
+ try {
101
+ return execFileSync(path, ["--single-check"], { encoding: "utf8", timeout: 5000, maxBuffer: 1024, stdio: ["ignore", "pipe", "ignore"], windowsHide: true }).trim() === `tokmizer-single ${version}`;
102
+ } catch {
103
+ return false;
104
+ }
105
+ }
106
+
107
+ function packageVersion(directory) {
108
+ return JSON.parse(readFileSync(join(directory, "package.json"), "utf8")).version;
109
+ }
110
+
111
+ export function singleInstalled(directory = packageDirectory, platform = process.platform) {
112
+ if (process.env.TOKMIZER_SINGLE_EXEC === "0") return false;
113
+ const path = singlePath(directory, platform);
114
+ try {
115
+ return existsSync(path) && probeSingle(path, packageVersion(directory));
116
+ } catch {
117
+ return false;
118
+ }
119
+ }
120
+
121
+ export function installSingle(directory = packageDirectory, platform = process.platform, arch = process.arch) {
122
+ const target = singlePath(directory, platform);
35
123
  const temporary = `${target}.${randomUUID()}.tmp`;
36
- const replace = (bytes) => {
37
- writeFileSync(temporary, bytes, { flag: "wx", mode: 0o755 });
124
+ const discard = () => {
125
+ try { rmSync(target, { force: true }); } catch {}
126
+ return false;
127
+ };
128
+ try {
129
+ if (process.env.TOKMIZER_SINGLE_EXEC === "0" || !["darwin", "linux", "win32"].includes(platform) || !["arm64", "x64"].includes(arch)) return discard();
130
+ const version = packageVersion(directory);
131
+ const key = `${platform}-${arch}`;
132
+ const entry = JSON.parse(readFileSync(join(directory, "dist", "native", "single.json"), "utf8"))[key];
133
+ if (!entry || entry.version !== version || entry.package !== `@tokmizer/plugin-${key}` || entry.file !== (platform === "win32" ? "tkr.exe" : "tkr") || !/^[0-9a-f]{64}$/.test(entry.sha256)) return discard();
134
+ const source = join(dirname(createRequire(join(directory, "package.json")).resolve(`${entry.package}/package.json`)), entry.file);
135
+ const bytes = readFileSync(source);
136
+ if (createHash("sha256").update(bytes).digest("hex") !== entry.sha256) return discard();
137
+ try { chmodSync(source, 0o755); } catch {}
138
+ if (!probeSingle(source, version)) return discard();
139
+ try {
140
+ linkSync(source, temporary);
141
+ } catch {
142
+ writeFileSync(temporary, bytes, { flag: "wx", mode: 0o755 });
143
+ }
38
144
  chmodSync(temporary, 0o755);
145
+ if (createHash("sha256").update(readFileSync(temporary)).digest("hex") !== entry.sha256) return discard();
39
146
  renameSync(temporary, target);
147
+ return probeSingle(target, version) || discard();
148
+ } catch {
149
+ return discard();
150
+ } finally {
151
+ try { rmSync(temporary, { force: true }); } catch {}
152
+ }
153
+ }
154
+
155
+ export function installNative(directory = packageDirectory, platform = process.platform, arch = process.arch, single = singleInstalled(directory, platform)) {
156
+ const windows = platform === "win32";
157
+ const launcher = join(directory, "dist", "bin", "tkr-exec");
158
+ const target = windows ? `${launcher}.exe` : launcher;
159
+ const temporaries = [];
160
+ const replace = (destination, bytes) => {
161
+ const temporary = `${destination}.${randomUUID()}.tmp`;
162
+ temporaries.push(temporary);
163
+ writeFileSync(temporary, bytes, { flag: "wx", mode: 0o755 });
164
+ chmodSync(temporary, 0o755);
165
+ renameSync(temporary, destination);
40
166
  };
41
167
  try {
42
- configureHook(directory, false);
43
- replace(readFileSync(join(directory, "dist", "bin", "tkr-exec.js")));
44
- if (process.env.TOKMIZER_NATIVE_EXEC === "0" || !["darwin", "linux"].includes(platform) || !["arm64", "x64"].includes(arch)) return false;
168
+ configureHook(directory, "node", single, windows);
169
+ replace(launcher, readFileSync(join(directory, "dist", "bin", "tkr-exec.js")));
170
+ if (windows) rmSync(target, { force: true });
171
+ if (process.env.TOKMIZER_NATIVE_EXEC === "0" || !["darwin", "linux", "win32"].includes(platform) || !["arm64", "x64"].includes(arch)) return false;
45
172
  const { version } = JSON.parse(readFileSync(join(directory, "package.json"), "utf8"));
46
173
  const manifest = JSON.parse(readFileSync(join(directory, "dist", "native", "manifest.json"), "utf8"));
47
174
  const key = `${platform}-${arch}`;
48
175
  const entry = manifest[key];
49
- if (!entry || entry.version !== version || entry.file !== `tkr-exec-${key}` || !/^[0-9a-f]{64}$/.test(entry.sha256)) return false;
176
+ if (!entry || entry.version !== version || entry.file !== `tkr-exec-${key}${windows ? ".exe" : ""}` || !/^[0-9a-f]{64}$/.test(entry.sha256)) return false;
50
177
  const source = join(directory, "dist", "native", entry.file);
51
178
  const bytes = readFileSync(source);
52
179
  if (createHash("sha256").update(bytes).digest("hex") !== entry.sha256) return false;
53
180
  chmodSync(source, 0o755);
54
- const probe = execFileSync(source, ["--native-check"], { encoding: "utf8", timeout: 2000, maxBuffer: 1024, stdio: ["ignore", "pipe", "ignore"] });
55
- if (probe.trim() !== `tokmizer-native ${version} protocol=2`) return false;
56
- replace(bytes);
57
- configureHook(directory, true);
181
+ const probe = (path) => execFileSync(path, ["--native-check"], { encoding: "utf8", timeout: 2000, maxBuffer: 1024, stdio: ["ignore", "pipe", "ignore"], windowsHide: true }).trim() === `tokmizer-native ${version} protocol=2`;
182
+ if (!probe(source)) return false;
183
+ replace(target, bytes);
184
+ let installed = false;
185
+ try { installed = probe(target); } catch {}
186
+ if (!installed) {
187
+ if (windows) rmSync(target, { force: true });
188
+ else replace(launcher, readFileSync(join(directory, "dist", "bin", "tkr-exec.js")));
189
+ return false;
190
+ }
191
+ configureHook(directory, windows ? "windows" : "unix", single, windows);
58
192
  return true;
59
193
  } catch {
60
194
  return false;
61
195
  } finally {
62
- try { rmSync(temporary, { force: true }); } catch {}
196
+ for (const temporary of temporaries) {
197
+ try { rmSync(temporary, { force: true }); } catch {}
198
+ }
199
+ }
200
+ }
201
+
202
+ export function healShims(directory = packageDirectory) {
203
+ if (process.env.TOKMIZER_POSTINSTALL_HEAL === "0" || !directory.split(/[\\/]/).includes("node_modules")) return false;
204
+ const entry = join(directory, "dist", "bin", "tkr.js");
205
+ if (!existsSync(entry)) return false;
206
+ try {
207
+ execFileSync(process.execPath, [entry, "shims-heal"], { stdio: "ignore", timeout: 5000, windowsHide: true });
208
+ return true;
209
+ } catch {
210
+ return false;
63
211
  }
64
212
  }
65
213
 
66
- if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) installNative();
214
+ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
215
+ installSingle();
216
+ installNative();
217
+ healShims();
218
+ }
@@ -1,7 +0,0 @@
1
- {
2
- "name": "tokmizer",
3
- "version": "0.9.57",
4
- "description": "Tokmizer reduces token consumption for OpenAI Codex.",
5
- "author": "Tokmizer",
6
- "homepage": "https://tokmizer.com"
7
- }