gm-skill 2.0.1970 → 2.0.1971

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.
@@ -849,6 +849,63 @@ function ensureGmPlugkitVersionFresh() {
849
849
  } catch (_) { return false; }
850
850
  }
851
851
 
852
+ const SEMVER_RE = /^\d+\.\d+\.\d+$/;
853
+
854
+ function readPinnedGmPlugkitVersion() {
855
+ try {
856
+ const p = path.join(gmToolsDir(), 'gm-plugkit.version');
857
+ if (!fs.existsSync(p)) return null;
858
+ const v = fs.readFileSync(p, 'utf-8').trim();
859
+ if (!v || !SEMVER_RE.test(v)) return null;
860
+ return v;
861
+ } catch (_) { return null; }
862
+ }
863
+
864
+ function resolveBunRuntime() {
865
+ const candidates = process.platform === 'win32' ? ['bun.exe', 'bun'] : ['bun'];
866
+ for (const c of candidates) {
867
+ try {
868
+ const r = spawnSync('where', [c], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true, timeout: 800 });
869
+ if (r.status === 0) {
870
+ const lines = (r.stdout || '').split(/\r?\n/).map(l => l.trim()).filter(Boolean);
871
+ const exe = lines.find(l => /\.exe$/i.test(l)) || lines[0];
872
+ if (exe) return exe;
873
+ }
874
+ } catch (_) {}
875
+ }
876
+ return process.platform === 'win32' ? 'bun.exe' : 'bun';
877
+ }
878
+
879
+ function spawnPinnedBoot(extraArgs) {
880
+ const args = Array.isArray(extraArgs) ? extraArgs : [];
881
+ const pinned = readPinnedGmPlugkitVersion();
882
+ if (!pinned) {
883
+ return { ok: false, reason: 'no-pin-file', fallback: '@latest' };
884
+ }
885
+ const runtime = resolveBunRuntime();
886
+ const bunxArgs = ['x', `gm-plugkit@${pinned}`, ...args];
887
+ const startedMs = Date.now();
888
+ let result;
889
+ try {
890
+ result = spawnSync(runtime, bunxArgs, {
891
+ stdio: 'inherit',
892
+ windowsHide: true,
893
+ shell: false,
894
+ env: { ...process.env, GM_PLUGKIT_PINNED_REEXEC: '1' },
895
+ });
896
+ } catch (e) {
897
+ return { ok: false, reason: 'spawn-failed', error: e.message, pinned_version: pinned, fallback: '@latest' };
898
+ }
899
+ const durationMs = Date.now() - startedMs;
900
+ if (result.error) {
901
+ return { ok: false, reason: 'spawn-error', error: result.error.message, pinned_version: pinned, fallback: '@latest' };
902
+ }
903
+ if (typeof result.status === 'number' && result.status !== 0) {
904
+ return { ok: false, reason: 'pinned-invocation-nonzero-exit', status: result.status, pinned_version: pinned, duration_ms: durationMs, fallback: '@latest' };
905
+ }
906
+ return { ok: true, pinned_version: pinned, duration_ms: durationMs, status: result.status };
907
+ }
908
+
852
909
  function discoverBundledSkillsAndSources() {
853
910
  const found = new Map();
854
911
  found.set('gm', path.join(__dirname, 'SKILL.md'));
@@ -1213,6 +1270,9 @@ module.exports = {
1213
1270
  ensureGmPlugkitVersionFresh,
1214
1271
  ensureSkillMdFresh,
1215
1272
  ensureWrapperFresh,
1273
+ readPinnedGmPlugkitVersion,
1274
+ resolveBunRuntime,
1275
+ spawnPinnedBoot,
1216
1276
  };
1217
1277
 
1218
1278
  if (require.main === module) {
package/gm-plugkit/cli.js CHANGED
@@ -5,7 +5,7 @@ const fs = require('fs');
5
5
  const os = require('os');
6
6
  const path = require('path');
7
7
  const cp = require('child_process');
8
- const { ensureReady, startSpoolDaemon, gmToolsDir, readVersionFile, ensureGmPlugkitVersionFresh, ensureSkillMdFresh, ensureWrapperFresh, isReady, getWasmPath } = require('./bootstrap');
8
+ const { ensureReady, startSpoolDaemon, gmToolsDir, readVersionFile, ensureGmPlugkitVersionFresh, ensureSkillMdFresh, ensureWrapperFresh, isReady, getWasmPath, readPinnedGmPlugkitVersion, spawnPinnedBoot } = require('./bootstrap');
9
9
 
10
10
  function getWasmPathSafe() {
11
11
  try { return getWasmPath(); } catch (_) { return null; }
@@ -77,6 +77,22 @@ Usage:
77
77
  wrapper sha differs from on-disk
78
78
  (lets new wrapper code load on next bootstrap)
79
79
  bun x gm-plugkit@latest --help Show this help
80
+
81
+ Opt-in pinned fast-path (skips bunx's own @latest npm-registry resolution):
82
+ GM_PLUGKIT_PREFER_PINNED=1 bun x gm-plugkit@latest spool
83
+ bun x gm-plugkit@latest spool --pinned
84
+ Reads ~/.gm-tools/gm-plugkit.version
85
+ (written by every successful boot) and
86
+ re-execs 'bun x gm-plugkit@<pinned-exact-version>'
87
+ with the remaining args -- bunx skips registry
88
+ resolution for an exact version once cached, so
89
+ this is genuinely faster than @latest on repeat
90
+ boots. Falls back to normal @latest-driven
91
+ behavior automatically when no pin file exists
92
+ yet (first-ever boot) or the pinned invocation
93
+ fails (stale/unpublished pinned version).
94
+ Opt-in only -- the default boot line above is
95
+ unaffected.
80
96
  `;
81
97
 
82
98
  function readDiskWasmVersion() {
@@ -238,6 +254,25 @@ function writeCliError(phase, err) {
238
254
  process.exit(killStaleWatchers());
239
255
  }
240
256
 
257
+ const wantsPinned = process.env.GM_PLUGKIT_PREFER_PINNED === '1' || args.includes('--pinned');
258
+ const alreadyReexecedPinned = process.env.GM_PLUGKIT_PINNED_REEXEC === '1';
259
+ if (wantsPinned && !alreadyReexecedPinned) {
260
+ const passArgs = args.filter(a => a !== '--pinned');
261
+ const pinned = readPinnedGmPlugkitVersion();
262
+ if (pinned) {
263
+ writeCliStatus({ phase: 'pinned-fast-path', pinned_version: pinned });
264
+ const result = spawnPinnedBoot(passArgs);
265
+ if (result.ok) {
266
+ process.exit(typeof result.status === 'number' ? result.status : 0);
267
+ }
268
+ writeCliStatus({ phase: 'pinned-fast-path-fallback', reason: result.reason, pinned_version: result.pinned_version || null });
269
+ console.error(`[gm-plugkit] pinned fast-path failed (${result.reason}), falling back to @latest resolution`);
270
+ } else {
271
+ writeCliStatus({ phase: 'pinned-fast-path-no-pin-file' });
272
+ console.error('[gm-plugkit] GM_PLUGKIT_PREFER_PINNED set but no ~/.gm-tools/gm-plugkit.version pin file yet -- falling back to @latest resolution (this boot will write the pin for next time)');
273
+ }
274
+ }
275
+
241
276
  ensureSpoolDir();
242
277
  writeCliStatus({ phase: 'starting', args });
243
278
 
@@ -5,7 +5,7 @@ const { spawnSync } = require('child_process');
5
5
  function pidCommandLineForKillGuard(pid) {
6
6
  try {
7
7
  if (process.platform === 'win32') {
8
- const r = spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', `(Get-CimInstance Win32_Process -Filter "ProcessId=${Number(pid)}").CommandLine`], { encoding: 'utf8', windowsHide: true, timeout: 5000 });
8
+ const r = spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', `(Get-WmiObject Win32_Process -Filter "ProcessId=${Number(pid)}").CommandLine`], { encoding: 'utf8', windowsHide: true, timeout: 5000 });
9
9
  return String((r && r.stdout) || '');
10
10
  }
11
11
  const r = spawnSync('ps', ['-p', String(pid), '-o', 'args='], { encoding: 'utf8', timeout: 5000 });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1970",
3
+ "version": "2.0.1971",
4
4
  "description": "Bootstrap and daemon-spawn tool for gm plugkit binary. Downloads the correct platform binary, verifies SHA256, and starts the spool watcher daemon. Includes plugkit-wasm-wrapper for WASM-based spool watching.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -2504,6 +2504,69 @@ function hostTaskProc(action, params) {
2504
2504
  }
2505
2505
  }
2506
2506
 
2507
+ let _gmRunnerEmbedBinPath;
2508
+ function resolveGmRunnerEmbedBin() {
2509
+ if (_gmRunnerEmbedBinPath !== undefined) return _gmRunnerEmbedBinPath;
2510
+ const exe = path.join(GM_TOOLS_ROOT, process.platform === 'win32' ? 'gm-runner.exe' : 'gm-runner');
2511
+ _gmRunnerEmbedBinPath = fs.existsSync(exe) ? exe : null;
2512
+ return _gmRunnerEmbedBinPath;
2513
+ }
2514
+
2515
+ // Slim-build support: plugkit-core's embed.rs probes host_vec_embed BEFORE
2516
+ // ever loading the 133MB wasm-embedded safetensors fallback (see
2517
+ // rs-plugkit/crates/plugkit-core/src/embed.rs::init_ctx) -- if this function
2518
+ // returns a real embedding, the wasm-side model never loads at all. Wired
2519
+ // here to gm-runner's OWN native candle path (crates/gm-runner/src/embed.rs)
2520
+ // via its `embed-text` one-shot subcommand (stdin=text,
2521
+ // stdout={"embedding":[...]}), a synchronous spawnSync call so the wasm-side
2522
+ // caller (a synchronous extern "C" import) can block on it correctly. Used
2523
+ // ONLY when a real ~/.gm-tools/gm-runner(.exe) binary is present on this
2524
+ // host; if absent, returns null immediately and the caller (host_vec_embed
2525
+ // below) falls through to -1, which makes plugkit-core's own probe fail and
2526
+ // fall back to loading the wasm-embedded fat-build safetensors model exactly
2527
+ // as before -- embedding capability is never silently lost on a host with no
2528
+ // gm-runner installed, by design (this is the real fallback the
2529
+ // slim-wasm-default-flip PRD row required be live before any default-feature
2530
+ // flip).
2531
+ function hostEmbedViaGmRunner(text) {
2532
+ const bin = resolveGmRunnerEmbedBin();
2533
+ if (!bin) return null;
2534
+ try {
2535
+ const r = spawnSync(bin, ['embed-text'], {
2536
+ input: text,
2537
+ encoding: 'utf-8',
2538
+ windowsHide: true,
2539
+ timeout: 30000,
2540
+ maxBuffer: 16 * 1024 * 1024,
2541
+ });
2542
+ if (r.error || r.status !== 0) return null;
2543
+ const parsed = JSON.parse(r.stdout || '{}');
2544
+ if (!Array.isArray(parsed.embedding)) return null;
2545
+ return parsed.embedding;
2546
+ } catch (_) {
2547
+ return null;
2548
+ }
2549
+ }
2550
+
2551
+ globalThis.__hostEmbedSync = function __hostEmbedSync(textPtr, textLen, outPtr, outLen, instance) {
2552
+ try {
2553
+ const text = readWasmStr(instance, textPtr, textLen);
2554
+ if (!text) return -1;
2555
+ const values = hostEmbedViaGmRunner(text);
2556
+ if (!values || values.length === 0) return -1;
2557
+ const dim = Math.min(values.length, outLen >>> 0);
2558
+ const bytes = new Uint8Array(dim * 4);
2559
+ const view = new DataView(bytes.buffer);
2560
+ for (let i = 0; i < dim; i++) view.setFloat32(i * 4, values[i], true);
2561
+ const buffer = instance.exports.memory.buffer;
2562
+ guardWasmRange(buffer, outPtr, dim * 4, '__hostEmbedSync:write');
2563
+ new Uint8Array(buffer, outPtr, dim * 4).set(bytes);
2564
+ return dim;
2565
+ } catch (_) {
2566
+ return -1;
2567
+ }
2568
+ };
2569
+
2507
2570
  function makeHostFunctions(instanceRef) {
2508
2571
  return {
2509
2572
  host_fs_read: (pathPtr, pathLen) => {
@@ -4445,6 +4508,7 @@ async function runSpoolWatcher(instance, spoolDir) {
4445
4508
  wasm_aborted: true,
4446
4509
  }));
4447
4510
  fs.renameSync(abortTmpPath, abortOutPath);
4511
+ try { fs.writeFileSync(abortOutPath + '.ready', ''); } catch (_) {}
4448
4512
  try { fs.unlinkSync(filePath); } catch (_) {}
4449
4513
  } catch (_) {}
4450
4514
  unmarkProcessed(key);
@@ -4566,6 +4630,7 @@ async function runSpoolWatcher(instance, spoolDir) {
4566
4630
  const outTmpPath = outPath + '.tmp.' + process.pid;
4567
4631
  fs.writeFileSync(outTmpPath, resultStr);
4568
4632
  fs.renameSync(outTmpPath, outPath);
4633
+ try { fs.writeFileSync(outPath + '.ready', ''); } catch (_) {}
4569
4634
  const dur_ms = Date.now() - t0;
4570
4635
  console.log(`[dispatch] <- verb=${verb} task=${taskBase} ms=${dur_ms} out=${resultStr.length}b`);
4571
4636
  logEvent('plugkit', 'dispatch.end', { verb, task: taskBase, dur_ms, out_bytes: resultStr.length });
@@ -4611,6 +4676,7 @@ async function runSpoolWatcher(instance, spoolDir) {
4611
4676
  const errTmpPath = errOutPath + '.tmp.' + process.pid;
4612
4677
  fs.writeFileSync(errTmpPath, JSON.stringify({ ok: false, error: e.message }));
4613
4678
  fs.renameSync(errTmpPath, errOutPath);
4679
+ try { fs.writeFileSync(errOutPath + '.ready', ''); } catch (_) {}
4614
4680
  } catch (_) {}
4615
4681
  try { fs.unlinkSync(filePath); } catch (_) {}
4616
4682
  unmarkProcessed(key);
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1970",
3
+ "version": "2.0.1971",
4
4
  "description": "Spool-dispatch orchestration engine with unified state machine, 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-skill",
3
- "version": "2.0.1970",
3
+ "version": "2.0.1971",
4
4
  "description": "Canonical universal harness — AI-native software engineering via skill-driven orchestration; bootstraps plugkit for task execution and session isolation. Install in any AI coding agent host.",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",