@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
|
@@ -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
|
|
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
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
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 (
|
|
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
|
-
|
|
34
|
-
|
|
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
|
|
37
|
-
|
|
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,
|
|
43
|
-
replace(readFileSync(join(directory, "dist", "bin", "tkr-exec.js")));
|
|
44
|
-
if (
|
|
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(
|
|
55
|
-
if (probe
|
|
56
|
-
replace(bytes);
|
|
57
|
-
|
|
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
|
-
|
|
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))
|
|
214
|
+
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
215
|
+
installSingle();
|
|
216
|
+
installNative();
|
|
217
|
+
healShims();
|
|
218
|
+
}
|