gm-qwen 2.0.980 → 2.0.982

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/bin/bootstrap.js CHANGED
@@ -99,6 +99,42 @@ function fallbackCacheRoot() {
99
99
  return path.join(os.tmpdir(), 'plugkit-cache', 'bin');
100
100
  }
101
101
 
102
+ function gmToolsDir() {
103
+ const home = process.env.USERPROFILE || process.env.HOME || os.homedir();
104
+ return path.join(home, '.claude', 'gm-tools');
105
+ }
106
+
107
+ // Copy the freshly-resolved plugkit binary + its version+sha manifests to
108
+ // ~/.claude/gm-tools so hooks.json can invoke plugkit directly without going
109
+ // through node. Self-update inside the Rust binary keeps gm-tools fresh from
110
+ // here on. Skipped silently on any error — the next session-start hook will
111
+ // retry via ensure_tools_current.
112
+ function copyToGmTools(finalPath, wrapperDir, version) {
113
+ try {
114
+ const dst = gmToolsDir();
115
+ fs.mkdirSync(dst, { recursive: true });
116
+ const exeName = process.platform === 'win32' ? 'plugkit.exe' : 'plugkit';
117
+ const target = path.join(dst, exeName);
118
+ const targetTmp = target + '.new';
119
+ fs.copyFileSync(finalPath, targetTmp);
120
+ try { fs.renameSync(targetTmp, target); }
121
+ catch (err) {
122
+ if (err.code === 'EEXIST' || err.code === 'EPERM' || err.code === 'EBUSY') {
123
+ // target may be locked by a running plugkit; the .new file persists
124
+ // and the in-Rust self-update will eventually swap it. Leave it.
125
+ } else { throw err; }
126
+ }
127
+ if (process.platform !== 'win32') {
128
+ try { fs.chmodSync(target, 0o755); } catch (_) {}
129
+ }
130
+ fs.writeFileSync(path.join(dst, 'plugkit.version'), version);
131
+ try {
132
+ const srcSha = path.join(wrapperDir, 'plugkit.sha256');
133
+ if (fs.existsSync(srcSha)) fs.copyFileSync(srcSha, path.join(dst, 'plugkit.sha256'));
134
+ } catch (_) {}
135
+ } catch (_) {}
136
+ }
137
+
102
138
  function ensureDir(dir) {
103
139
  fs.mkdirSync(dir, { recursive: true });
104
140
  }
@@ -349,6 +385,7 @@ async function bootstrap(opts) {
349
385
  const actualSha = sha256OfFileSync(finalPath);
350
386
  if (actualSha === expectedSha) {
351
387
  if (!opts.silent) log(`decision: hit reason: sha-match v${version} (${finalPath})`);
388
+ copyToGmTools(finalPath, wrapperDir, version);
352
389
  return finalPath;
353
390
  }
354
391
  log(`decision: fetch reason: cache-hit-sha-mismatch (dir=v${version} expected ${expectedSha.slice(0,12)}… got ${(actualSha||'').slice(0,12)}…)`);
@@ -362,6 +399,7 @@ async function bootstrap(opts) {
362
399
  try { fs.unlinkSync(okSentinel); } catch (_) {}
363
400
  } else {
364
401
  if (!opts.silent) log(`decision: hit reason: sentinel+no-sha-manifest (${finalPath})`);
402
+ copyToGmTools(finalPath, wrapperDir, version);
365
403
  return finalPath;
366
404
  }
367
405
  }
@@ -369,6 +407,7 @@ async function bootstrap(opts) {
369
407
  if (healIfShaMatches(finalPath, expectedSha, okSentinel, partialPath, 'plugkit')) {
370
408
  if (!opts.silent) log(`decision: heal reason: sha-match (${finalPath})`);
371
409
  spawnDetachedRtkFetch(wrapperDir);
410
+ copyToGmTools(finalPath, wrapperDir, version);
372
411
  return finalPath;
373
412
  }
374
413
 
@@ -377,11 +416,13 @@ async function bootstrap(opts) {
377
416
  try {
378
417
  if (fs.existsSync(finalPath) && fs.existsSync(okSentinel)) {
379
418
  log(`decision: hit reason: lock-race-resolved (${finalPath})`);
419
+ copyToGmTools(finalPath, wrapperDir, version);
380
420
  return finalPath;
381
421
  }
382
422
  if (healIfShaMatches(finalPath, expectedSha, okSentinel, partialPath, 'plugkit')) {
383
423
  log(`decision: heal reason: sha-match-under-lock (${finalPath})`);
384
424
  spawnDetachedRtkFetch(wrapperDir);
425
+ copyToGmTools(finalPath, wrapperDir, version);
385
426
  return finalPath;
386
427
  }
387
428
 
@@ -442,6 +483,7 @@ async function bootstrap(opts) {
442
483
  proactiveKillForNewInstall(version, finalPath);
443
484
  pruneOldVersions(root, version, readRtkVersion(wrapperDir));
444
485
  spawnDetachedRtkFetch(wrapperDir);
486
+ copyToGmTools(finalPath, wrapperDir, version);
445
487
  return finalPath;
446
488
  } finally {
447
489
  releaseLock(lockPath);
package/bin/plugkit.js CHANGED
@@ -1,159 +1,50 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
- const { spawn, spawnSync } = require('child_process');
3
+ // Minimal exec wrapper. ZERO bootstrap, ZERO version-probe, ZERO async work.
4
+ // Just shell out to ~/.claude/gm-tools/plugkit{.exe} with inherited stdio.
5
+ // The Rust binary handles its own self-update at startup (detached); first-time
6
+ // bootstrap is done by gm-cc postinstall.js. Hot path is one spawnSync, ~150ms
7
+ // of node startup overhead.
8
+
9
+ const { spawnSync } = require('child_process');
4
10
  const path = require('path');
5
11
  const fs = require('fs');
6
- const { bootstrap, resolveCachedBinary, resolveCachedRtk, obsEvent, killStaleDaemonIfVersionChanged } = require('./bootstrap');
7
-
8
- const dir = __dirname;
9
-
10
- function readPinnedVersion() {
11
- try { return fs.readFileSync(path.join(dir, 'plugkit.version'), 'utf8').trim(); } catch (_) { return null; }
12
- }
12
+ const os = require('os');
13
13
 
14
- function probeCachedVersion(binPath) {
15
- try {
16
- const r = spawnSync(binPath, ['--version'], { timeout: 3000, encoding: 'utf8', windowsHide: true });
17
- if (r.error) return null;
18
- const text = `${r.stdout || ''} ${r.stderr || ''}`.trim();
19
- const m = text.match(/(\d+\.\d+\.\d+)/);
20
- return m ? m[1] : null;
21
- } catch (_) { return null; }
14
+ function toolsBin() {
15
+ const home = process.env.USERPROFILE || process.env.HOME || os.homedir();
16
+ const exe = process.platform === 'win32' ? 'plugkit.exe' : 'plugkit';
17
+ return path.join(home, '.claude', 'gm-tools', exe);
22
18
  }
23
19
 
24
- async function resolveBinaryWithPinCheck() {
25
- const cached = resolveCachedBinary({ wrapperDir: dir });
26
- if (!cached) return null;
27
- const pin = readPinnedVersion();
28
- if (!pin) return cached;
29
- const got = probeCachedVersion(cached);
30
- if (got && got === pin) return cached;
31
- try { return await bootstrap({ wrapperDir: dir, silent: true }); } catch (_) { return cached; }
32
- }
33
-
34
- function envWithRtkOnPath() {
35
- const rtkPath = resolveCachedRtk({ wrapperDir: dir });
36
- if (!rtkPath) return process.env;
37
- const rtkDir = path.dirname(rtkPath);
38
- const sep = process.platform === 'win32' ? ';' : ':';
39
- return { ...process.env, PATH: `${rtkDir}${sep}${process.env.PATH || ''}` };
40
- }
41
-
42
- async function resolveBinary() {
43
- const cached = resolveCachedBinary({ wrapperDir: dir });
44
- if (cached) return cached;
45
- return await bootstrap({ wrapperDir: dir });
20
+ function legacyBesideWrapper() {
21
+ const dir = __dirname;
22
+ const p = os.platform();
23
+ const a = os.arch();
24
+ let candidates = [];
25
+ if (p === 'win32') candidates = [path.join(dir, a === 'arm64' ? 'plugkit-win32-arm64.exe' : 'plugkit-win32-x64.exe')];
26
+ else if (p === 'darwin') candidates = [path.join(dir, a === 'arm64' ? 'plugkit-darwin-arm64' : 'plugkit-darwin-x64')];
27
+ else candidates = [path.join(dir, (a === 'arm64' || a === 'aarch64') ? 'plugkit-linux-arm64' : 'plugkit-linux-x64')];
28
+ for (const c of candidates) if (fs.existsSync(c)) return c;
29
+ return null;
46
30
  }
47
31
 
48
- async function main() {
32
+ function main() {
49
33
  const args = process.argv.slice(2);
50
- // Detached bootstrap entry: just run bootstrap() and exit. Used by session-start
51
- // to avoid blocking CC startup on a slow GitHub download.
52
- if (args[0] === '__rtk_only__') {
53
- try { await bootstrap({ wrapperDir: dir, silent: false }); }
54
- catch (e) { try { process.stderr.write(`[plugkit-bootstrap-detached] ${e.message}\n`); } catch (_) {} }
55
- process.exit(0);
56
- }
57
34
  const isHook = args[0] === 'hook';
58
- const startedAt = Date.now();
59
- obsEvent('plugkit_wrapper', 'invoke', { argv: args.slice(0, 4), is_hook: isHook });
60
- // If the plugin tarball updated `plugkit.version` since the runner daemon
61
- // was last started, kill the daemon so the next `runner start` picks up
62
- // the freshly-installed binary instead of serving stale RPCs.
63
- try { killStaleDaemonIfVersionChanged(dir); } catch (_) {}
64
- let bin;
65
- try {
66
- const hookSubcmd = isHook ? (args[1] || '') : '';
67
- // session-start ALWAYS bootstraps: this is the once-per-session moment
68
- // where we guarantee the cached binary matches the wrapper-pinned version.
69
- // If the bootstrap fails (offline) we fall through to whatever the cache
70
- // currently has — the hook itself isn't blocking, just refreshing.
71
- if (isHook && hookSubcmd === 'session-start') {
72
- obsEvent('plugkit_wrapper', 'hook_bootstrap_session_start', { argv: args.slice(0, 4) });
73
- // Bootstrap can stall 60s+ on a slow GitHub mirror — never block CC startup
74
- // on it. Detach into a background child; this hook returns immediately
75
- // with whatever cached binary exists. Subsequent hooks pick up the new
76
- // binary once the detached bootstrap completes.
77
- bin = resolveCachedBinary({ wrapperDir: dir }) || legacyFallback();
78
- try {
79
- const child = spawn(process.execPath, [__filename, '__rtk_only__'], {
80
- detached: true,
81
- stdio: 'ignore',
82
- windowsHide: true,
83
- env: { ...process.env, PLUGKIT_BOOTSTRAP_DETACHED: '1' },
84
- });
85
- child.unref();
86
- obsEvent('plugkit_wrapper', 'session_start_bootstrap_detached', { pid: child.pid });
87
- } catch (e) {
88
- process.stderr.write(`[plugkit] detached bootstrap spawn failed: ${e.message}\n`);
89
- }
90
- // If no cached binary yet (fresh install with bootstrap still downloading),
91
- // skip running the session-start handler this turn — it will fire next
92
- // session-start once binary is in place.
93
- if (!bin) {
94
- process.stderr.write(`[plugkit] session-start skipped: binary not yet installed (bootstrap running in background).\n`);
95
- process.exit(0);
96
- }
97
- } else if (isHook) {
98
- bin = (await resolveBinaryWithPinCheck()) || legacyFallback();
99
- if (!bin) {
100
- process.stderr.write(`[plugkit] hook ${hookSubcmd} skipped: binary not yet installed. Bootstrap will run on session-start.\n`);
101
- obsEvent('plugkit_wrapper', 'hook_skip_uncached', { argv: args.slice(0, 4), dur_ms: Date.now() - startedAt });
102
- process.exit(0);
103
- }
104
- } else {
105
- bin = await resolveBinary();
35
+ let bin = toolsBin();
36
+ if (!fs.existsSync(bin)) {
37
+ bin = legacyBesideWrapper();
38
+ if (!bin) {
39
+ // Binary not yet installed. If this is a hook, exit cleanly so CC doesn't
40
+ // see an error; postinstall will populate gm-tools on /plugin install.
41
+ if (isHook) process.exit(0);
42
+ process.stderr.write('[plugkit] binary not found at ~/.claude/gm-tools/plugkit — run postinstall\n');
43
+ process.exit(1);
106
44
  }
107
- } catch (err) {
108
- process.stderr.write(`[plugkit] bootstrap failed: ${err.message}\n`);
109
- obsEvent('plugkit_wrapper', 'bootstrap_failed', { err: err.message, dur_ms: Date.now() - startedAt, argv: args.slice(0, 4), is_hook: isHook });
110
- const legacy = legacyFallback();
111
- if (legacy) { bin = legacy; }
112
- else if (isHook) { process.exit(0); }
113
- else process.exit(1);
114
- }
115
-
116
- const env = envWithRtkOnPath();
117
-
118
- if (isHook && !process.stdin.isTTY) {
119
- const chunks = [];
120
- process.stdin.on('data', c => chunks.push(c));
121
- process.stdin.on('end', () => {
122
- const child = spawn(bin, args, { stdio: ['pipe', 'inherit', 'inherit'], windowsHide: true, env });
123
- child.stdin.end(Buffer.concat(chunks));
124
- child.on('close', code => process.exit(code ?? 1));
125
- child.on('error', () => process.exit(1));
126
- });
127
- process.stdin.on('error', () => process.exit(1));
128
- } else {
129
- const result = spawnSync(bin, args, { stdio: 'inherit', windowsHide: true, env });
130
- obsEvent('plugkit_wrapper', 'exit', { dur_ms: Date.now() - startedAt, code: result.status ?? -1 });
131
- process.exit(result.status ?? 1);
132
- }
133
- }
134
-
135
- // legacyFallback only returns a binary that lives next to the wrapper. We
136
- // never reach across to ~/.claude/gm-tools/plugkit.exe or other ambient
137
- // install dirs — those have proven to mask bootstrap failures by serving a
138
- // stale version whose hooks silently mismatch the active wrapper code (see
139
- // the v0.1.292-vs-v0.1.294 incident).
140
- function legacyFallback() {
141
- const os = require('os');
142
- const p = os.platform();
143
- const a = os.arch();
144
- let candidates = [];
145
- if (p === 'win32') {
146
- candidates = [path.join(dir, a === 'arm64' ? 'plugkit-win32-arm64.exe' : 'plugkit-win32-x64.exe')];
147
- } else if (p === 'darwin') {
148
- candidates = [path.join(dir, a === 'arm64' ? 'plugkit-darwin-arm64' : 'plugkit-darwin-x64')];
149
- } else {
150
- candidates = [path.join(dir, (a === 'arm64' || a === 'aarch64') ? 'plugkit-linux-arm64' : 'plugkit-linux-x64')];
151
45
  }
152
- for (const c of candidates) if (fs.existsSync(c)) return c;
153
- return null;
46
+ const r = spawnSync(bin, args, { stdio: 'inherit', windowsHide: true });
47
+ process.exit(r.status ?? 1);
154
48
  }
155
49
 
156
- main().catch(err => {
157
- process.stderr.write(`[plugkit] fatal: ${err.message}\n`);
158
- process.exit(1);
159
- });
50
+ main();
@@ -1,6 +1,6 @@
1
- 33bc1a2ab629980f179cf2c05d4110f80b5ebcfd5ffe9755f68012ba613bc84d plugkit-win32-x64.exe
2
- c4ea7265c95e34e2d422dd00b04ba4af2ca94747ffc9472ac9d3fdcc4244dc3c plugkit-win32-arm64.exe
3
- 90817eeaef96757e471702b43065aec16d4e14e2edb1a7e1727edc5c5d975118 plugkit-darwin-x64
4
- 5149b4208f4709dcf0aa473b561668e8dc0b53a5011aed8d35712a34d315b9c9 plugkit-darwin-arm64
5
- cabce25953909952cd554b7555a65d3334026604fe455b88dbb42bdc1c1430ca plugkit-linux-x64
6
- 54b2916f151f2cdef080022573805e712a6cfa4b268dc3902092a94233f7cc35 plugkit-linux-arm64
1
+ 8d0063971b24bb316258d997351e1d2f7873567efb2d5e5d67c0812b3aafa3b2 plugkit-win32-x64.exe
2
+ a3ae6e3dc06119779b6906d864aa5153a6b0263d02cd804256744ce87b418bd1 plugkit-win32-arm64.exe
3
+ 0026e9c08fa50816f7c9c4574fc69177d982dd4c78195e7b9205d1f0a0cd0e4d plugkit-darwin-x64
4
+ 6b953f803a0c07f5a8ff1d505158482e2feae8a062d8d16459ed52d4685feec6 plugkit-darwin-arm64
5
+ 0dd17b76a720db81d7b8142b3783fcbeae8759b3741fe9ce37c776149b03ba6f plugkit-linux-x64
6
+ 05d91f175c3a6ae78a11687b78aedcd0c5faeac6499b9f292848108a420c21c3 plugkit-linux-arm64
package/bin/rtk.sha256 CHANGED
@@ -1,5 +1,5 @@
1
- 0ee2e856f1a29164fdbf542ece089df5e065f5c68684cfd211e31c7b71219aa0 rtk-win32-x64.exe
2
- 8d8e2dcf1dd8bf27e88f3e82f37a76b957f41b1bcbd761adee7b5e833c607fcc rtk-win32-arm64.exe
1
+ ea121977fa8760f46a46a5eca0cdcd45dc1fe336586ab583a515bce5cd0737e6 rtk-win32-x64.exe
2
+ 232f7342e365178ad0c3732c4ff0f746539f1f999f328164eb7307595305a8ba rtk-win32-arm64.exe
3
3
  1b1e792767ed0e1e6ca0e2f0a8de02e77b06dea2f5ae667278b94baf239fcdc3 rtk-darwin-x64
4
4
  9717978d9d6216ea50c94444e00e359479b6315a17bd48c16064b267c8b0b60d rtk-darwin-arm64
5
5
  a100d3defac54194144e5723aec57e6f286b42298c67145c8428815246c9ee56 rtk-linux-x64
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.980",
3
+ "version": "2.0.982",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-qwen",
3
- "version": "2.0.980",
3
+ "version": "2.0.982",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",