gm-skill 2.0.1781 → 2.0.1793

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
@@ -606,6 +606,29 @@ function writeDaemonVersion(v) {
606
606
  try { fs.writeFileSync(daemonVersionSentinel(), String(v)); } catch (_) {}
607
607
  }
608
608
 
609
+ function pidCommandLineForKillGuard(pid) {
610
+ try {
611
+ const { spawnSync } = require('child_process');
612
+ if (process.platform === 'win32') {
613
+ const r = spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', `(Get-CimInstance Win32_Process -Filter "ProcessId=${Number(pid)}").CommandLine`], { encoding: 'utf8', windowsHide: true, timeout: 5000 });
614
+ return String((r && r.stdout) || '');
615
+ }
616
+ const r = spawnSync('ps', ['-p', String(pid), '-o', 'args='], { encoding: 'utf8', timeout: 5000 });
617
+ return String((r && r.stdout) || '');
618
+ } catch (_) { return ''; }
619
+ }
620
+
621
+ function pidIsPlugkitProcess(pid) {
622
+ return /plugkit-wasm-wrapper\.js|plugkit-supervisor\.js|gm-plugkit[\\\/]supervisor\.js/i.test(pidCommandLineForKillGuard(pid));
623
+ }
624
+
625
+ function writeKillAttribution(targetSpoolDir, info) {
626
+ try {
627
+ fs.mkdirSync(targetSpoolDir, { recursive: true });
628
+ fs.writeFileSync(path.join(targetSpoolDir, '.kill-attribution.json'), JSON.stringify({ killer_pid: process.pid, killer_cwd: process.cwd(), killer_script: __filename, ts: Date.now(), ...info }, null, 2));
629
+ } catch (_) {}
630
+ }
631
+
609
632
  function killPid(pid) {
610
633
  if (!Number.isFinite(pid) || pid === process.pid || !pidAlive(pid)) return false;
611
634
  try { process.kill(pid, 'SIGTERM'); }
@@ -624,6 +647,12 @@ function killSpoolWatcherInCwd(reason) {
624
647
  const pidPath = path.join(process.cwd(), '.gm', 'exec-spool', '.watcher.pid');
625
648
  if (!fs.existsSync(pidPath)) return null;
626
649
  const pid = parseInt(fs.readFileSync(pidPath, 'utf8').trim(), 10);
650
+ if (pidAlive(pid) && !pidIsPlugkitProcess(pid)) {
651
+ obsEvent('bootstrap', 'watcher.kill-skipped-pid-reused', { pid, reason });
652
+ try { fs.unlinkSync(pidPath); } catch (_) {}
653
+ return null;
654
+ }
655
+ writeKillAttribution(path.join(process.cwd(), '.gm', 'exec-spool'), { reason, target_pid: pid, via: 'killSpoolWatcherInCwd' });
627
656
  if (killPid(pid)) {
628
657
  obsEvent('bootstrap', 'watcher.killed', { pid, reason });
629
658
  try { fs.unlinkSync(pidPath); } catch (_) {}
@@ -1 +1 @@
1
- 0.1.808
1
+ 0.1.818
@@ -1 +1 @@
1
- 14ca32983cb5e377ec053e0aad070fc1b469d79b8cf21dfb19ab5e9815ffe8a9 plugkit.wasm
1
+ 5cfcad879b84f2443f28e5ce9182d725bc6ebfeae1bf2c84a5b21d5a84a55a1e plugkit.wasm
@@ -450,6 +450,28 @@ function writeDaemonVersion(v) {
450
450
  try { fs.writeFileSync(daemonVersionSentinel(), String(v)); } catch (_) {}
451
451
  }
452
452
 
453
+ function pidCommandLineForKillGuard(pid) {
454
+ try {
455
+ if (process.platform === 'win32') {
456
+ const r = spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', `(Get-CimInstance Win32_Process -Filter "ProcessId=${Number(pid)}").CommandLine`], { encoding: 'utf8', windowsHide: true, timeout: 5000 });
457
+ return String((r && r.stdout) || '');
458
+ }
459
+ const r = spawnSync('ps', ['-p', String(pid), '-o', 'args='], { encoding: 'utf8', timeout: 5000 });
460
+ return String((r && r.stdout) || '');
461
+ } catch (_) { return ''; }
462
+ }
463
+
464
+ function pidIsPlugkitProcess(pid) {
465
+ return /plugkit-wasm-wrapper\.js|plugkit-supervisor\.js|gm-plugkit[\\\/]supervisor\.js/i.test(pidCommandLineForKillGuard(pid));
466
+ }
467
+
468
+ function writeKillAttribution(targetSpoolDir, info) {
469
+ try {
470
+ fs.mkdirSync(targetSpoolDir, { recursive: true });
471
+ fs.writeFileSync(path.join(targetSpoolDir, '.kill-attribution.json'), JSON.stringify({ killer_pid: process.pid, killer_cwd: process.cwd(), killer_script: __filename, ts: Date.now(), ...info }, null, 2));
472
+ } catch (_) {}
473
+ }
474
+
453
475
  function killPid(pid) {
454
476
  if (!Number.isFinite(pid) || pid === process.pid || !pidAlive(pid)) return false;
455
477
  try { process.kill(pid, 'SIGTERM'); }
@@ -465,6 +487,12 @@ function killSpoolWatcherInCwd(reason) {
465
487
  const pidPath = path.join(process.cwd(), '.gm', 'exec-spool', '.watcher.pid');
466
488
  if (!fs.existsSync(pidPath)) return null;
467
489
  const pid = parseInt(fs.readFileSync(pidPath, 'utf8').trim(), 10);
490
+ if (pidAlive(pid) && !pidIsPlugkitProcess(pid)) {
491
+ obsEvent('bootstrap', 'watcher.kill-skipped-pid-reused', { pid, reason });
492
+ try { fs.unlinkSync(pidPath); } catch (_) {}
493
+ return null;
494
+ }
495
+ writeKillAttribution(path.join(process.cwd(), '.gm', 'exec-spool'), { reason, target_pid: pid, via: 'killSpoolWatcherInCwd' });
468
496
  if (killPid(pid)) {
469
497
  obsEvent('bootstrap', 'watcher.killed', { pid, reason });
470
498
  try { fs.unlinkSync(pidPath); } catch (_) {}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1781",
3
+ "version": "2.0.1793",
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": {
@@ -74,6 +74,28 @@ function emitShutdownReason(reason, err) {
74
74
  } catch (_) {}
75
75
  }
76
76
 
77
+ function pidCommandLineForKillGuard(pid) {
78
+ try {
79
+ if (process.platform === 'win32') {
80
+ const r = spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', `(Get-CimInstance Win32_Process -Filter "ProcessId=${Number(pid)}").CommandLine`], { encoding: 'utf8', windowsHide: true, timeout: 5000 });
81
+ return String((r && r.stdout) || '');
82
+ }
83
+ const r = spawnSync('ps', ['-p', String(pid), '-o', 'args='], { encoding: 'utf8', timeout: 5000 });
84
+ return String((r && r.stdout) || '');
85
+ } catch (_) { return ''; }
86
+ }
87
+
88
+ function pidIsPlugkitProcess(pid) {
89
+ return /plugkit-wasm-wrapper\.js|plugkit-supervisor\.js|gm-plugkit[\\\/]supervisor\.js/i.test(pidCommandLineForKillGuard(pid));
90
+ }
91
+
92
+ function writeKillAttribution(targetSpoolDir, info) {
93
+ try {
94
+ fs.mkdirSync(targetSpoolDir, { recursive: true });
95
+ fs.writeFileSync(path.join(targetSpoolDir, '.kill-attribution.json'), JSON.stringify({ killer_pid: process.pid, killer_cwd: process.cwd(), killer_script: 'plugkit-wasm-wrapper', ts: Date.now(), ...info }, null, 2));
96
+ } catch (_) {}
97
+ }
98
+
77
99
  function writeVerbActive(verb, task) {
78
100
  __currentVerbContext = { verb, task, started_at_ms: Date.now(), pid: process.pid };
79
101
  try {
@@ -1558,7 +1580,8 @@ function cosineSim(a, b) {
1558
1580
  }
1559
1581
 
1560
1582
  let __wasmAbortFlag = { aborted: false, code: 0 };
1561
- const WASI_FILESYSTEM_ROOT = path.join(GM_TOOLS_ROOT, 'wasi-fs');
1583
+ const WASI_PROJECT_SLUG = crypto.createHash('sha256').update(String(process.env.CLAUDE_PROJECT_DIR || process.cwd()).toLowerCase().replace(/\\/g, '/')).digest('hex').slice(0, 16);
1584
+ const WASI_FILESYSTEM_ROOT = path.join(GM_TOOLS_ROOT, 'wasi-fs', WASI_PROJECT_SLUG);
1562
1585
  const wasiOpenFiles = new Map();
1563
1586
  let wasiNextFd = 100;
1564
1587
 
@@ -2256,6 +2279,19 @@ function makeHostFunctions(instanceRef) {
2256
2279
  }
2257
2280
  },
2258
2281
 
2282
+ host_fs_remove: (pathPtr, pathLen) => {
2283
+ try {
2284
+ const filePath = readWasmStr(instanceRef.value, pathPtr, pathLen);
2285
+ if (!filePath) return 0;
2286
+ const st = fs.statSync(filePath);
2287
+ if (st.isDirectory()) return 0;
2288
+ fs.unlinkSync(filePath);
2289
+ return 1;
2290
+ } catch (e) {
2291
+ return 0;
2292
+ }
2293
+ },
2294
+
2259
2295
  host_fs_readdir: (pathPtr, pathLen) => {
2260
2296
  try {
2261
2297
  const dirPath = readWasmStr(instanceRef.value, pathPtr, pathLen);
@@ -3038,7 +3074,12 @@ async function runSpoolWatcher(instance, spoolDir) {
3038
3074
  holder_sha: holderSha,
3039
3075
  }));
3040
3076
  } catch (_) {}
3041
- try { process.kill(parseInt(pidStr, 10), 'SIGTERM'); } catch (_) {}
3077
+ if (pidIsPlugkitProcess(holderPidNum)) {
3078
+ writeKillAttribution(spoolDir, { reason: 'peer-stale-takeover', target_pid: holderPidNum, via: 'lock-takeover', holder_sha: holderSha });
3079
+ try { process.kill(holderPidNum, 'SIGTERM'); } catch (_) {}
3080
+ } else {
3081
+ try { logEvent('plugkit', 'takeover-kill-skipped-pid-reused', { holder_pid: pidStr }); } catch (_) {}
3082
+ }
3042
3083
  return 'takeover';
3043
3084
  } else {
3044
3085
  const msg = JSON.stringify({ ok: false, reason: 'another-watcher-active', pid: pidStr, age_ms: age });
@@ -3249,6 +3290,11 @@ async function runSpoolWatcher(instance, spoolDir) {
3249
3290
  try {
3250
3291
  process.kill(peerPid, 0);
3251
3292
  } catch (_) { continue; }
3293
+ if (!pidIsPlugkitProcess(peerPid)) {
3294
+ logEvent('plugkit', 'peer.kill-skipped-pid-reused', { peer_cwd: peerCwd, peer_pid: peerPid });
3295
+ continue;
3296
+ }
3297
+ writeKillAttribution(path.join(peerCwd, '.gm', 'exec-spool'), { reason: 'peer-stale-takeover', target_pid: peerPid, via: 'peer-sweep', peer_sha: peerSha, own_sha: _ownWrapperSha12 });
3252
3298
  logEvent('plugkit', 'peer.stale-wrapper-killed', { peer_cwd: peerCwd, peer_pid: peerPid, peer_sha: peerSha, own_sha: _ownWrapperSha12, lock_age_ms: age });
3253
3299
  console.error(`[plugkit-wasm] peer-sweep killing stale-wrapper watcher pid=${peerPid} cwd=${peerCwd} sha=${peerSha} (own=${_ownWrapperSha12})`);
3254
3300
  try {
@@ -1 +1 @@
1
- 0.1.808
1
+ 0.1.818
@@ -64,6 +64,28 @@ function writeSupervisorStatus(state, extra) {
64
64
  } catch (_) {}
65
65
  }
66
66
 
67
+ function pidCommandLineForKillGuard(pid) {
68
+ try {
69
+ if (process.platform === 'win32') {
70
+ const r = spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', `(Get-CimInstance Win32_Process -Filter "ProcessId=${Number(pid)}").CommandLine`], { encoding: 'utf8', windowsHide: true, timeout: 5000 });
71
+ return String((r && r.stdout) || '');
72
+ }
73
+ const r = spawnSync('ps', ['-p', String(pid), '-o', 'args='], { encoding: 'utf8', timeout: 5000 });
74
+ return String((r && r.stdout) || '');
75
+ } catch (_) { return ''; }
76
+ }
77
+
78
+ function pidIsPlugkitProcess(pid) {
79
+ return /plugkit-wasm-wrapper\.js|plugkit-supervisor\.js|gm-plugkit[\\\/]supervisor\.js/i.test(pidCommandLineForKillGuard(pid));
80
+ }
81
+
82
+ function writeKillAttribution(targetSpoolDir, info) {
83
+ try {
84
+ fs.mkdirSync(targetSpoolDir, { recursive: true });
85
+ fs.writeFileSync(path.join(targetSpoolDir, '.kill-attribution.json'), JSON.stringify({ killer_pid: process.pid, killer_cwd: process.cwd(), killer_script: __filename, ts: Date.now(), ...info }, null, 2));
86
+ } catch (_) {}
87
+ }
88
+
67
89
  function pidAlive(pid) {
68
90
  if (!Number.isFinite(pid) || pid <= 0) return false;
69
91
  try { process.kill(pid, 0); return true; } catch (_) { return false; }
@@ -100,9 +122,14 @@ function acquireSingleInstance() {
100
122
  spool_status_age_ms: now - spoolTs,
101
123
  severity: 'critical',
102
124
  });
103
- try { process.kill(other, 'SIGTERM'); } catch (_) {}
104
- if (process.platform === 'win32') {
105
- try { spawnSync('taskkill', ['/F', '/T', '/PID', String(other)], { stdio: 'ignore', windowsHide: true, timeout: 3000 }); } catch (_) {}
125
+ if (pidIsPlugkitProcess(other)) {
126
+ writeKillAttribution(spoolDir, { reason: 'supervisor-takeover-wedged', target_pid: other, via: 'supervisor-duel' });
127
+ try { process.kill(other, 'SIGTERM'); } catch (_) {}
128
+ if (process.platform === 'win32') {
129
+ try { spawnSync('taskkill', ['/F', '/T', '/PID', String(other)], { stdio: 'ignore', windowsHide: true, timeout: 3000 }); } catch (_) {}
130
+ }
131
+ } else {
132
+ logEvent('supervisor.takeover-kill-skipped-pid-reused', { existing_pid: other, severity: 'warn' });
106
133
  }
107
134
  }
108
135
  try { fs.unlinkSync(SUPERVISOR_PID_PATH); } catch (_) {}
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1781",
3
+ "version": "2.0.1793",
4
4
  "description": "Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
@@ -17,5 +17,5 @@
17
17
  "publishConfig": {
18
18
  "access": "public"
19
19
  },
20
- "plugkitVersion": "0.1.808"
20
+ "plugkitVersion": "0.1.818"
21
21
  }
@@ -406,6 +406,26 @@ function isProcessRunning() {
406
406
  return findPlugkitWasmPids().length > 0;
407
407
  }
408
408
 
409
+ function pidCommandLineForKillGuard(pid) {
410
+ try {
411
+ if (process.platform === 'win32') {
412
+ return execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', `(Get-CimInstance Win32_Process -Filter "ProcessId=${Number(pid)}").CommandLine`], { encoding: 'utf8', windowsHide: true, timeout: 5000 }) || '';
413
+ }
414
+ return execFileSync('ps', ['-p', String(pid), '-o', 'args='], { encoding: 'utf8', timeout: 5000 }) || '';
415
+ } catch (_) { return ''; }
416
+ }
417
+
418
+ function pidIsPlugkitProcess(pid) {
419
+ return /plugkit-wasm-wrapper\.js|plugkit-supervisor\.js|gm-plugkit[\\\/]supervisor\.js/i.test(pidCommandLineForKillGuard(pid));
420
+ }
421
+
422
+ function writeKillAttribution(targetSpoolDir, info) {
423
+ try {
424
+ fs.mkdirSync(targetSpoolDir, { recursive: true });
425
+ fs.writeFileSync(path.join(targetSpoolDir, '.kill-attribution.json'), JSON.stringify({ killer_pid: process.pid, killer_cwd: process.cwd(), killer_script: __filename, ts: Date.now(), ...info }, null, 2));
426
+ } catch (_) {}
427
+ }
428
+
409
429
  function killExistingPlugkit() {
410
430
  try {
411
431
  const pids = findPlugkitWasmPids();
@@ -413,17 +433,24 @@ function killExistingPlugkit() {
413
433
  emitBootstrapEvent('info', 'No existing plugkit WASM watcher to kill');
414
434
  return;
415
435
  }
436
+ const killed = [];
416
437
  for (const pid of pids) {
438
+ if (!pidIsPlugkitProcess(pid)) {
439
+ emitBootstrapEvent('warn', 'Kill skipped: pid is not a plugkit process (stale or reused pid)', { pid });
440
+ continue;
441
+ }
442
+ writeKillAttribution(path.join(process.cwd(), '.gm', 'exec-spool'), { reason: 'bootstrap-refresh', target_pid: Number(pid), via: 'killExistingPlugkit' });
417
443
  try {
418
444
  if (process.platform === 'win32') {
419
445
  execFileSync('taskkill', ['/F', '/T', '/PID', String(pid)], { stdio: 'ignore', windowsHide: true });
420
446
  } else {
421
447
  execFileSync('kill', ['-9', String(pid)], { stdio: 'ignore' });
422
448
  }
449
+ killed.push(pid);
423
450
  } catch (e) {
424
451
  }
425
452
  }
426
- emitBootstrapEvent('info', 'Killed existing plugkit WASM watcher', { pids });
453
+ emitBootstrapEvent('info', 'Killed existing plugkit WASM watcher', { pids: killed });
427
454
  } catch (e) {
428
455
  emitBootstrapEvent('warn', 'Failed to kill existing plugkit', { error: e.message });
429
456
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-skill",
3
- "version": "2.0.1781",
3
+ "version": "2.0.1793",
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",