gm-skill 2.0.1467 → 2.0.1469

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/AGENTS.md CHANGED
@@ -40,7 +40,7 @@ Agents dispatch verbs by writing to `.gm/exec-spool/in/<verb>/<N>.txt` (request
40
40
 
41
41
  **git verbs**: `git_status` returns `{dirty, modified, untracked, deleted, staged}` from `git status --porcelain`. `branch_status` returns `{branch, ahead, behind, remote}`, the `remote-pushed` witness. `git_push` is the ONLY admissible push surface, it gates on `git_porcelain()` non-empty (refuses dirty), emits `deviation.push-dirty` on attempt, and shells the push only when clean. A raw `git push` via Bash bypasses the gate and is itself a deviation; ccsniff `--git-discipline` flags it.
42
42
 
43
- **filter verb**: pure stdout → compact-stdout transformation. Body `{kind, input, ...opts}` where kind is one of `grep`, `ls`, `tree`, `json`, `diff`, `git-status`, `log`. Returns `{output, stats:{bytes_in, bytes_out, saved_pct, ...}}`. Pipe raw command output through filter before letting it enter context, rtk's role, in-wasm, no subprocess. Replaces the legacy detached rtk binary download in bootstrap.
43
+ **filter verb**: pure stdout → compact-stdout transformation. Body `{kind, input, ...opts}` where kind is one of `grep`, `ls`, `tree`, `json`, `diff`, `git-status`, `log`. Returns `{output, stats:{bytes_in, bytes_out, saved_pct, ...}}`. Pipe raw command output through filter before letting it enter context, in-wasm, no subprocess. The bootstrap fetches only `plugkit.wasm`, there is no separate filter/rtk binary download.
44
44
 
45
45
  ## Documentation Policy
46
46
 
package/bin/bootstrap.js CHANGED
@@ -187,11 +187,6 @@ function obsEvent(subsystem, event, fields) {
187
187
  }
188
188
 
189
189
 
190
- function rtkBinaryName() {
191
- const key = platformKey();
192
- return key.startsWith('win32') ? `rtk-${key}.exe` : `rtk-${key}`;
193
- }
194
-
195
190
  function cacheRoot() {
196
191
  const home = os.homedir();
197
192
  if (process.env.PLUGKIT_CACHE_DIR) return process.env.PLUGKIT_CACHE_DIR;
@@ -256,12 +251,6 @@ function readVersionFile(wrapperDir) {
256
251
  return fs.readFileSync(p, 'utf8').trim();
257
252
  }
258
253
 
259
- function readRtkVersion(wrapperDir) {
260
- const p = path.join(wrapperDir, 'rtk.version');
261
- if (!fs.existsSync(p)) return null;
262
- const v = fs.readFileSync(p, 'utf8').trim();
263
- return v || null;
264
- }
265
254
 
266
255
  function sha256OfFileSync(filePath) {
267
256
  const h = crypto.createHash('sha256');
@@ -432,15 +421,12 @@ function isLockStale(lockPath) {
432
421
  return false;
433
422
  }
434
423
 
435
- function pruneOldVersions(root, keepVersion, keepRtkVersion) {
424
+ function pruneOldVersions(root, keepVersion) {
436
425
  try {
437
426
  const entries = fs.readdirSync(root);
438
427
  for (const e of entries) {
439
- const isPlugkit = e.startsWith('v') && !e.startsWith('rtk-');
440
- const isRtk = e.startsWith('rtk-v');
441
- if (!isPlugkit && !isRtk) continue;
442
- if (isPlugkit && e === `v${keepVersion}`) continue;
443
- if (isRtk && keepRtkVersion && e === `rtk-v${keepRtkVersion}`) continue;
428
+ if (!e.startsWith('v')) continue;
429
+ if (e === `v${keepVersion}`) continue;
444
430
  const dir = path.join(root, e);
445
431
  const lock = path.join(dir, '.lock');
446
432
  if (fs.existsSync(lock) && !isLockStale(lock)) continue;
@@ -502,7 +488,6 @@ async function bootstrap(opts) {
502
488
 
503
489
  if (healIfShaMatches(wasmFinalPath, wasmExpectedSha, wasmOkSentinel, wasmPartialPath, 'wasm')) {
504
490
  obsEvent('bootstrap', 'decision.heal', { reason: 'sha-match', path: wasmFinalPath });
505
- spawnDetachedRtkFetch(wrapperDir);
506
491
  copyWasmToGmTools(wasmFinalPath, wrapperDir, version);
507
492
  clearBootstrapError();
508
493
  return wasmFinalPath;
@@ -519,7 +504,6 @@ async function bootstrap(opts) {
519
504
  }
520
505
  if (healIfShaMatches(wasmFinalPath, wasmExpectedSha, wasmOkSentinel, wasmPartialPath, 'wasm')) {
521
506
  obsEvent('bootstrap', 'decision.heal', { reason: 'sha-match-under-lock', path: wasmFinalPath });
522
- spawnDetachedRtkFetch(wrapperDir);
523
507
  copyWasmToGmTools(wasmFinalPath, wrapperDir, version);
524
508
  clearBootstrapError();
525
509
  return wasmFinalPath;
@@ -574,8 +558,7 @@ async function bootstrap(opts) {
574
558
  fs.writeFileSync(wasmOkSentinel, new Date().toISOString());
575
559
  log(`decision: fetch reason: install-complete (${wasmFinalPath})`);
576
560
  obsEvent('bootstrap', 'install.done', { path: wasmFinalPath, version, kind: 'wasm' });
577
- pruneOldVersions(root, version, readRtkVersion(wrapperDir));
578
- spawnDetachedRtkFetch(wrapperDir);
561
+ pruneOldVersions(root, version);
579
562
  copyWasmToGmTools(wasmFinalPath, wrapperDir, version);
580
563
 
581
564
  clearBootstrapError();
@@ -585,109 +568,6 @@ async function bootstrap(opts) {
585
568
  }
586
569
  }
587
570
 
588
- function spawnDetachedRtkFetch(wrapperDir) {
589
- try {
590
- const { spawn } = require('child_process');
591
- const child = spawn(process.execPath, [__filename, '--rtk-only', '--wrapper-dir', wrapperDir], {
592
- detached: true,
593
- stdio: 'ignore',
594
- windowsHide: true,
595
- });
596
- child.unref();
597
- obsEvent('bootstrap', 'rtk.detached.spawned', { pid: child.pid, wrapperDir });
598
- } catch (err) {
599
- log(`rtk detach spawn failed: ${err.message}`);
600
- }
601
- }
602
-
603
- function rtkCacheDir(root, wrapperDir, plugkitVerDir) {
604
- const rtkVer = readRtkVersion(wrapperDir);
605
- if (!rtkVer) return plugkitVerDir;
606
- const dir = path.join(root, `rtk-v${rtkVer}`);
607
- ensureDir(dir);
608
- return dir;
609
- }
610
-
611
- async function bootstrapRtk(plugkitVerDir, plugkitVersion, wrapperDir, silent, root) {
612
- const rtkName = rtkBinaryName();
613
- const cacheDir = rtkCacheDir(root || cacheRoot(), wrapperDir, plugkitVerDir);
614
- const rtkPath = path.join(cacheDir, rtkName);
615
- const rtkOk = path.join(cacheDir, '.rtk-ok');
616
- if (fs.existsSync(rtkPath) && fs.existsSync(rtkOk)) {
617
- if (!silent) log(`rtk cache hit: ${rtkPath}`);
618
- return rtkPath;
619
- }
620
- const rtkSha = readShaManifest(wrapperDir, 'rtk.sha256');
621
- const expected = rtkSha ? rtkSha[rtkName] : null;
622
- const tmp = `${rtkPath}.partial`;
623
- if (healIfShaMatches(rtkPath, expected, rtkOk, tmp, 'rtk')) {
624
- if (!silent) log(`rtk cache heal (sha match): ${rtkPath}`);
625
- return rtkPath;
626
- }
627
- const RTKS_RELEASE_REPO = 'AnEntrypoint/plugkit-bin';
628
- const url = `https://github.com/${RTKS_RELEASE_REPO}/releases/download/v${plugkitVersion}/${rtkName}`;
629
- const startMs = Date.now();
630
- let lastErr;
631
- for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
632
- try {
633
- log(`rtk download attempt ${attempt}/${MAX_ATTEMPTS}: ${url}`);
634
- const result = spawnSync(
635
- 'curl',
636
- ['-fSL', '--max-time', String(Math.floor(ATTEMPT_TIMEOUT_MS / 1000)), '-o', tmp, url],
637
- { stdio: 'pipe', timeout: ATTEMPT_TIMEOUT_MS + 5000, windowsHide: true }
638
- );
639
- if (result.error) throw result.error;
640
- if (result.status !== 0) throw new Error(`curl failed with status ${result.status}`);
641
- break;
642
- } catch (err) {
643
- lastErr = err;
644
- log(`rtk attempt ${attempt} failed: ${err.message}`);
645
- obsEvent('bootstrap', 'rtk.download.attempt_failed', { attempt, max: MAX_ATTEMPTS, err: String(err.message || err) });
646
- if (attempt < MAX_ATTEMPTS) {
647
- const wait = BACKOFF_MS[attempt - 1] || 120000;
648
- log(`backing off ${wait}ms`);
649
- await new Promise(r => setTimeout(r, wait));
650
- }
651
- }
652
- }
653
- if (lastErr) throw lastErr;
654
- if (expected) {
655
- const got = await sha256OfFile(tmp);
656
- if (got !== expected) {
657
- try { fs.unlinkSync(tmp); } catch (_) {}
658
- throw new Error(`rtk sha256 mismatch: expected ${expected}, got ${got}`);
659
- }
660
- }
661
- try { fs.renameSync(tmp, rtkPath); }
662
- catch (err) {
663
- if (err.code === 'EEXIST' || err.code === 'EPERM') {
664
- try { fs.unlinkSync(rtkPath); } catch (_) {}
665
- fs.renameSync(tmp, rtkPath);
666
- } else throw err;
667
- }
668
- if (os.platform() !== 'win32') { try { fs.chmodSync(rtkPath, 0o755); } catch (_) {} }
669
- fs.writeFileSync(rtkOk, new Date().toISOString());
670
- log(`installed ${rtkPath}`);
671
- obsEvent('bootstrap', 'install.done', { path: rtkPath, plugkit_version: plugkitVersion, rtk_version: readRtkVersion(wrapperDir) || plugkitVersion, kind: 'rtk', dur_ms: Date.now() - startMs });
672
- return rtkPath;
673
- }
674
-
675
- function resolveCachedRtk(opts) {
676
- opts = opts || {};
677
- const wrapperDir = opts.wrapperDir || __dirname;
678
- const version = opts.version || readVersionFile(wrapperDir);
679
- const root = (() => {
680
- try { const r = cacheRoot(); ensureDir(r); return r; }
681
- catch (_) { const r = fallbackCacheRoot(); ensureDir(r); return r; }
682
- })();
683
- const plugkitVerDir = path.join(root, `v${version}`);
684
- const cacheDir = rtkCacheDir(root, wrapperDir, plugkitVerDir);
685
- const rtkPath = path.join(cacheDir, rtkBinaryName());
686
- const rtkOk = path.join(cacheDir, '.rtk-ok');
687
- if (fs.existsSync(rtkPath) && fs.existsSync(rtkOk)) return rtkPath;
688
- return null;
689
- }
690
-
691
571
  function getWasmPath(opts) {
692
572
  opts = opts || {};
693
573
  const wrapperDir = opts.wrapperDir || __dirname;
@@ -801,44 +681,24 @@ function killStaleDaemonIfVersionChanged(wrapperDir) {
801
681
  writeDaemonVersion(currentVersion);
802
682
  }
803
683
 
804
- module.exports = { bootstrap, getWasmPath, resolveCachedRtk, rtkBinaryName, cacheRoot, obsEvent, killRunningDaemons, killStaleDaemonIfVersionChanged, killSpoolWatcherInCwd, proactiveKillForNewInstall };
684
+ module.exports = { bootstrap, getWasmPath, cacheRoot, obsEvent, killRunningDaemons, killStaleDaemonIfVersionChanged, killSpoolWatcherInCwd, proactiveKillForNewInstall };
805
685
 
806
686
  if (require.main === module) {
807
687
  const argv = process.argv.slice(2);
808
- if (argv.includes('--rtk-only')) {
809
- const wIdx = argv.indexOf('--wrapper-dir');
810
- const wrapperDir = wIdx >= 0 ? argv[wIdx + 1] : __dirname;
811
- (async () => {
688
+ bootstrap({ silent: false })
689
+ .then(p => { process.stdout.write(p + '\n'); process.exit(0); })
690
+ .catch(err => {
691
+ log(`FATAL: ${err.message}`);
692
+ obsEvent('bootstrap', 'fatal', { err: String(err.message || err) });
812
693
  try {
813
- const version = readVersionFile(wrapperDir);
814
- let root = cacheRoot();
815
- try { ensureDir(root); }
816
- catch (_) { root = fallbackCacheRoot(); ensureDir(root); }
817
- const verDir = path.join(root, `v${version}`);
818
- ensureDir(verDir);
819
- await bootstrapRtk(verDir, version, wrapperDir, true, root);
820
- process.exit(0);
821
- } catch (err) {
822
- obsEvent('bootstrap', 'rtk.detached.failed', { err: String(err.message || err) });
823
- process.exit(1);
824
- }
825
- })();
826
- } else {
827
- bootstrap({ silent: false })
828
- .then(p => { process.stdout.write(p + '\n'); process.exit(0); })
829
- .catch(err => {
830
- log(`FATAL: ${err.message}`);
831
- obsEvent('bootstrap', 'fatal', { err: String(err.message || err) });
832
- try {
833
- const pinned = (() => { try { return readVersionFile(__dirname); } catch (_) { return null; } })();
834
- writeBootstrapError({
835
- expected_version: pinned,
836
- cached_version: null,
837
- error_phase: 'fatal',
838
- error_message: String(err && err.message || err),
839
- });
840
- } catch (_) {}
841
- process.exit(1);
842
- });
843
- }
694
+ const pinned = (() => { try { return readVersionFile(__dirname); } catch (_) { return null; } })();
695
+ writeBootstrapError({
696
+ expected_version: pinned,
697
+ cached_version: null,
698
+ error_phase: 'fatal',
699
+ error_message: String(err && err.message || err),
700
+ });
701
+ } catch (_) {}
702
+ process.exit(1);
703
+ });
844
704
  }
@@ -329,14 +329,6 @@ async function extractNpmPackageWithRetry(destPath, version) {
329
329
  }
330
330
 
331
331
 
332
- function platformKey() {
333
- const p = os.platform();
334
- const a = os.arch();
335
- if (p === 'win32') return a === 'arm64' ? 'win32-arm64' : 'win32-x64';
336
- if (p === 'darwin') return a === 'arm64' ? 'darwin-arm64' : 'darwin-x64';
337
- return (a === 'arm64' || a === 'aarch64') ? 'linux-arm64' : 'linux-x64';
338
- }
339
-
340
332
  function healIfShaMatches(binPath, expectedSha, sentinelPath, partialPath, kind) {
341
333
  if (!fs.existsSync(binPath)) return false;
342
334
  if (partialPath) { try { if (fs.existsSync(partialPath)) fs.unlinkSync(partialPath); } catch (_) {} }
@@ -428,10 +420,8 @@ function pruneOldVersions(root, keepVersion) {
428
420
  try {
429
421
  const entries = fs.readdirSync(root);
430
422
  for (const e of entries) {
431
- const isPlugkit = e.startsWith('v') && !e.startsWith('rtk-');
432
- const isRtk = e.startsWith('rtk-v');
433
- if (!isPlugkit && !isRtk) continue;
434
- if (isPlugkit && e === `v${keepVersion}`) continue;
423
+ if (!e.startsWith('v')) continue;
424
+ if (e === `v${keepVersion}`) continue;
435
425
  const dir = path.join(root, e);
436
426
  const lock = path.join(dir, '.lock');
437
427
  if (fs.existsSync(lock) && !isLockStale(lock)) continue;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1467",
3
+ "version": "2.0.1469",
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": {
@@ -3109,6 +3109,30 @@ async function runSpoolWatcher(instance, spoolDir) {
3109
3109
  // file ahead of a verified binary download poisons installedVersionAtTools() and causes an infinite
3110
3110
  // drift-respawn thrash. Auto-update is notify-only until a sha-verified force-download path exists.
3111
3111
  }
3112
+ function checkUpdateViaNpm(installed) {
3113
+ const req = https.get({
3114
+ host: 'registry.npmjs.org',
3115
+ path: '/plugkit-wasm/latest',
3116
+ headers: { 'user-agent': 'plugkit-watcher', 'accept': 'application/json' },
3117
+ timeout: 5000,
3118
+ }, (res) => {
3119
+ if (res.statusCode !== 200) { res.resume(); return; }
3120
+ const chunks = [];
3121
+ res.on('data', c => chunks.push(c));
3122
+ res.on('end', () => {
3123
+ try {
3124
+ const meta = JSON.parse(Buffer.concat(chunks).toString('utf-8'));
3125
+ const latest = meta && meta.version;
3126
+ if (!latest) return;
3127
+ writeSharedUpdateCache(latest, 200);
3128
+ applyUpdateCheckResult(installed, latest, 200);
3129
+ } catch (_) {}
3130
+ });
3131
+ });
3132
+ req.on('timeout', () => { try { req.destroy(); } catch (_) {} });
3133
+ req.on('error', () => {});
3134
+ }
3135
+
3112
3136
  function checkForUpdate() {
3113
3137
  const installed = resolveVersion(instance);
3114
3138
  const cached = readSharedUpdateCache();
@@ -3126,6 +3150,7 @@ async function runSpoolWatcher(instance, spoolDir) {
3126
3150
  res.resume();
3127
3151
  writeSharedUpdateCache(null, res.statusCode);
3128
3152
  applyUpdateCheckResult(installed, null, res.statusCode);
3153
+ checkUpdateViaNpm(installed);
3129
3154
  return;
3130
3155
  }
3131
3156
  const chunks = [];
@@ -3178,12 +3203,14 @@ async function runSpoolWatcher(instance, spoolDir) {
3178
3203
  try { req.destroy(); } catch (_) {}
3179
3204
  writeSharedUpdateCache(null, -1);
3180
3205
  logUpdateCheckError({ error: 'timeout' });
3206
+ checkUpdateViaNpm(installed);
3181
3207
  });
3182
3208
  req.on('error', (e) => {
3183
3209
  if (_checkErrored) return;
3184
3210
  _checkErrored = true;
3185
3211
  writeSharedUpdateCache(null, -2);
3186
3212
  logUpdateCheckError({ error: String(e && e.message || e) });
3213
+ checkUpdateViaNpm(installed);
3187
3214
  });
3188
3215
  }
3189
3216
  setTimeout(checkForUpdate, 10_000);
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1467",
3
+ "version": "2.0.1469",
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.1467",
3
+ "version": "2.0.1469",
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",
package/bin/rtk.sha256 DELETED
@@ -1,6 +0,0 @@
1
- 53224d66572e937507a8d2877c768e4e6cc3da66aa6f8d0f132afaab2edc2a10 rtk-win32-x64.exe
2
- dedabc1d89641c60c91f09570353b6270dba4f5d53f8597018a708e515265d53 rtk-win32-arm64.exe
3
- cf3190554b82c7395948b7a478c78bbe2241549b00777e660deff4cbb9e0c4b6 rtk-darwin-x64
4
- c815bad459b4eaccc8be4a5d74dba397fdfe7d3716e0b6023b188d2351128b82 rtk-darwin-arm64
5
- 7d60dd5abc15f6d46ffd89b5de7253a067e2a3ef6f1cd8ae5a236eda05a504f4 rtk-linux-x64
6
- cd5dd78035845eef4b362927c61f61e23925af3c12779131024d8334bad87a6b rtk-linux-arm64