gm-skill 2.0.1513 → 2.0.1514

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.
@@ -5,6 +5,13 @@ const fs = require('fs');
5
5
  const path = require('path');
6
6
  const os = require('os');
7
7
  const { spawn, spawnSync } = require('child_process');
8
+ const crypto = require('crypto');
9
+
10
+ function wrapperSha12OnDisk() {
11
+ try {
12
+ return crypto.createHash('sha256').update(fs.readFileSync(resolveWrapper())).digest('hex').slice(0, 12);
13
+ } catch (_) { return null; }
14
+ }
8
15
 
9
16
  const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
10
17
  const spoolDir = path.join(projectDir, '.gm', 'exec-spool');
@@ -248,6 +255,25 @@ function checkWatcherHealth() {
248
255
  severity: 'critical',
249
256
  });
250
257
  killChild('supervisor-killed-stale-heartbeat');
258
+ return;
259
+ }
260
+ // A published wrapper-only fix (no wasm version bump) is copied to ~/.gm-tools by the next
261
+ // bootstrap's ensureWrapperFresh, but a healthy running watcher keeps the old wrapper until it
262
+ // restarts. Compare the watcher's reported wrapper_sha against the on-disk wrapper; on drift,
263
+ // recycle so the fix goes live without a manual kill. Skip while busy (a long verb is running).
264
+ const status = readStatus();
265
+ if (status && !(status.busy_until && status.busy_until > Date.now())) {
266
+ const reported = status.wrapper_sha || null;
267
+ const onDisk = wrapperSha12OnDisk();
268
+ if (reported && onDisk && reported !== onDisk) {
269
+ logEvent('supervisor.wrapper-sha-drift', {
270
+ watcher_pid: currentChildPid,
271
+ reported_sha: reported,
272
+ on_disk_sha: onDisk,
273
+ severity: 'info',
274
+ });
275
+ killChild('supervisor-killed-wrapper-sha-drift');
276
+ }
251
277
  }
252
278
  }
253
279
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1513",
3
+ "version": "2.0.1514",
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": {
@@ -15,6 +15,10 @@ let _writeStatusBusy = () => {};
15
15
  // Latest busy_until epoch ms stamped by a long synchronous verb (codesearch rebuild, chromium
16
16
  // spawn). scanStalledTurns reads it so a busy watcher is not mis-flagged as an idle stall.
17
17
  let _lastBusyUntil = 0;
18
+ // First 12 hex of sha256 of this watcher's own gmTools wrapper. Module-scoped so writeStatus
19
+ // (a different function scope) can stamp status.wrapper_sha, which the supervisor compares
20
+ // against the on-disk wrapper to recycle a watcher running a stale wrapper-only fix.
21
+ let _ownWrapperSha12 = '';
18
22
 
19
23
  function spawnSync(cmd, args, opts) {
20
24
  return _rawSpawnSync(cmd, args, { windowsHide: true, ...(opts || {}) });
@@ -2078,7 +2082,6 @@ async function runSpoolWatcher(instance, spoolDir) {
2078
2082
 
2079
2083
 
2080
2084
  const LOCK_PATH = path.join(spoolDir, '.watcher.lock');
2081
- let _ownWrapperSha12 = '';
2082
2085
  try {
2083
2086
  const _crypto = require('crypto');
2084
2087
  const _wp = path.join(GM_TOOLS_ROOT, 'plugkit-wasm-wrapper.js');
@@ -4,8 +4,18 @@
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
6
  const os = require('os');
7
+ const crypto = require('crypto');
7
8
  const { spawn, spawnSync } = require('child_process');
8
9
 
10
+ function wrapperSha12OnDisk() {
11
+ try {
12
+ const primary = path.join(os.homedir(), '.gm-tools', 'plugkit-wasm-wrapper.js');
13
+ const fallback = path.join(os.homedir(), '.claude', 'gm-tools', 'plugkit-wasm-wrapper.js');
14
+ const wp = fs.existsSync(primary) ? primary : fallback;
15
+ return crypto.createHash('sha256').update(fs.readFileSync(wp)).digest('hex').slice(0, 12);
16
+ } catch (_) { return null; }
17
+ }
18
+
9
19
  const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
10
20
  const spoolDir = path.join(projectDir, '.gm', 'exec-spool');
11
21
  fs.mkdirSync(spoolDir, { recursive: true });
@@ -227,6 +237,25 @@ function checkWatcherHealth() {
227
237
  if (process.platform === 'win32') {
228
238
  try { spawnSync('taskkill', ['/F', '/T', '/PID', String(currentChildPid)], { stdio: 'ignore', windowsHide: true, timeout: 3000 }); } catch (_) {}
229
239
  }
240
+ return;
241
+ }
242
+ // A published wrapper-only fix (no wasm version bump) lands in ~/.gm-tools via the next
243
+ // bootstrap's ensureWrapperFresh, but a healthy running watcher keeps the old wrapper until it
244
+ // restarts. On wrapper_sha drift (watcher's reported sha != on-disk), recycle so the fix goes
245
+ // live without a manual kill. busy_until already returned above, so the watcher is not mid-verb.
246
+ const reported = status.wrapper_sha || null;
247
+ const onDisk = wrapperSha12OnDisk();
248
+ if (reported && onDisk && reported !== onDisk) {
249
+ logEvent('supervisor.wrapper-sha-drift', {
250
+ watcher_pid: currentChildPid,
251
+ reported_sha: reported,
252
+ on_disk_sha: onDisk,
253
+ severity: 'info',
254
+ });
255
+ try { process.kill(currentChildPid, 'SIGTERM'); } catch (_) {}
256
+ if (process.platform === 'win32') {
257
+ try { spawnSync('taskkill', ['/F', '/T', '/PID', String(currentChildPid)], { stdio: 'ignore', windowsHide: true, timeout: 3000 }); } catch (_) {}
258
+ }
230
259
  }
231
260
  }
232
261
 
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1513",
3
+ "version": "2.0.1514",
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.1513",
3
+ "version": "2.0.1514",
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",