gm-skill 2.0.1362 → 2.0.1364

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/gm-plugkit/cli.js CHANGED
@@ -2,20 +2,91 @@
2
2
  'use strict';
3
3
 
4
4
  const fs = require('fs');
5
+ const os = require('os');
5
6
  const path = require('path');
7
+ const cp = require('child_process');
6
8
  const { ensureReady, startSpoolDaemon } = require('./bootstrap');
7
9
 
8
10
  const usage = `gm-plugkit — Bootstrap and daemon-spawn for gm plugkit binary.
9
11
 
10
12
  Usage:
11
- bun x gm-plugkit@latest Bootstrap + start spool daemon
12
- bun x gm-plugkit@latest spool Same as default (explicit)
13
- bun x gm-plugkit@latest --daemon Same as default
14
- bun x gm-plugkit@latest --binary Print binary path only
15
- bun x gm-plugkit@latest --status JSON status check
16
- bun x gm-plugkit@latest --help Show this help
13
+ bun x gm-plugkit@latest Bootstrap + start spool daemon
14
+ bun x gm-plugkit@latest spool Same as default (explicit)
15
+ bun x gm-plugkit@latest --daemon Same as default
16
+ bun x gm-plugkit@latest --binary Print binary path only
17
+ bun x gm-plugkit@latest --status JSON status check
18
+ bun x gm-plugkit@latest --kill-stale-watchers
19
+ Kill plugkit watchers whose in-memory
20
+ wrapper sha differs from on-disk
21
+ (lets new wrapper code load on next bootstrap)
22
+ bun x gm-plugkit@latest --help Show this help
17
23
  `;
18
24
 
25
+ function killStaleWatchers() {
26
+ try {
27
+ const wrapperPath = path.join(os.homedir(), '.gm-tools', 'plugkit-wasm-wrapper.js');
28
+ if (!fs.existsSync(wrapperPath)) {
29
+ console.log(JSON.stringify({ ok: false, error: 'wrapper not installed at ~/.gm-tools/plugkit-wasm-wrapper.js' }));
30
+ return 1;
31
+ }
32
+ const diskMtime = fs.statSync(wrapperPath).mtimeMs;
33
+ const stale = [];
34
+ const fresh = [];
35
+ if (process.platform === 'win32') {
36
+ const ps = `Get-WmiObject Win32_Process -Filter "name='node.exe'" | Where-Object { $_.CommandLine -match 'plugkit-wasm-wrapper' } | ForEach-Object { $_.ProcessId.ToString() + '|' + $_.CreationDate }`;
37
+ const out = cp.execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', ps], { encoding: 'utf-8', windowsHide: true });
38
+ for (const line of out.split(/\r?\n/).filter(Boolean)) {
39
+ const [pidStr, creation] = line.split('|');
40
+ const pid = parseInt(pidStr, 10);
41
+ if (!Number.isFinite(pid)) continue;
42
+ const m = creation && creation.match(/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\.(\d+))?(?:([+-])(\d+))?/);
43
+ if (!m) continue;
44
+ const localMs = new Date(+m[1], +m[2] - 1, +m[3], +m[4], +m[5], +m[6], m[7] ? Math.round(+('0.' + m[7]) * 1000) : 0).getTime();
45
+ if (localMs < diskMtime) stale.push({ pid, started_ms: localMs });
46
+ else fresh.push({ pid, started_ms: localMs });
47
+ }
48
+ } else {
49
+ const out = cp.execFileSync('ps', ['-eo', 'pid,lstart,command'], { encoding: 'utf-8' });
50
+ for (const line of out.split('\n').slice(1)) {
51
+ if (!line.includes('plugkit-wasm-wrapper')) continue;
52
+ const m = line.match(/^\s*(\d+)\s+(.+?\d{4})\s+/);
53
+ if (!m) continue;
54
+ const pid = parseInt(m[1], 10);
55
+ const start = Date.parse(m[2]);
56
+ if (!Number.isFinite(pid) || !Number.isFinite(start)) continue;
57
+ if (start < diskMtime) stale.push({ pid, started_ms: start });
58
+ else fresh.push({ pid, started_ms: start });
59
+ }
60
+ }
61
+ const killed = [];
62
+ const failed = [];
63
+ for (const s of stale) {
64
+ try {
65
+ if (process.platform === 'win32') {
66
+ cp.execFileSync('taskkill', ['/F', '/PID', String(s.pid)], { stdio: 'ignore', windowsHide: true });
67
+ } else {
68
+ process.kill(s.pid, 'SIGTERM');
69
+ }
70
+ killed.push(s.pid);
71
+ } catch (e) {
72
+ failed.push({ pid: s.pid, error: e.message });
73
+ }
74
+ }
75
+ console.log(JSON.stringify({
76
+ ok: true,
77
+ disk_wrapper_mtime_ms: diskMtime,
78
+ stale_found: stale.length,
79
+ fresh_found: fresh.length,
80
+ killed,
81
+ failed,
82
+ }, null, 2));
83
+ return 0;
84
+ } catch (e) {
85
+ console.log(JSON.stringify({ ok: false, error: e.message }));
86
+ return 1;
87
+ }
88
+ }
89
+
19
90
  function spoolDir() {
20
91
  const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
21
92
  return path.join(projectDir, '.gm', 'exec-spool');
@@ -55,6 +126,10 @@ function writeCliError(phase, err) {
55
126
  process.exit(0);
56
127
  }
57
128
 
129
+ if (args.includes('--kill-stale-watchers')) {
130
+ process.exit(killStaleWatchers());
131
+ }
132
+
58
133
  ensureSpoolDir();
59
134
  writeCliStatus({ phase: 'starting', args });
60
135
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1362",
3
+ "version": "2.0.1364",
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": {
@@ -2672,8 +2672,17 @@ async function runSpoolWatcher(instance, spoolDir) {
2672
2672
  function clearSharedUpdateErrorKey() {
2673
2673
  try { fs.unlinkSync(UPDATE_CHECK_ERROR_MARKER); } catch (_) {}
2674
2674
  }
2675
+ function normalizeUpdateErrorCategory(fields) {
2676
+ if (fields.status && fields.status !== 200) return `http-${fields.status}`;
2677
+ const err = String(fields.error || '').toLowerCase();
2678
+ if (!err) return 'unknown';
2679
+ if (/timeout|timed out|etimedout/.test(err)) return 'network';
2680
+ if (/socket hang up|econnreset|econnrefused|enotfound|eai_again|enetunreach|ehostunreach|getaddrinfo/.test(err)) return 'network';
2681
+ if (/json|parse|unexpected/.test(err)) return 'parse';
2682
+ return 'other';
2683
+ }
2675
2684
  function logUpdateCheckError(fields) {
2676
- const key = `${fields.status || ''}:${fields.error || ''}`;
2685
+ const key = normalizeUpdateErrorCategory(fields);
2677
2686
  if (_lastKnownUpdateError === key) return;
2678
2687
  const shared = readSharedUpdateErrorKey();
2679
2688
  if (shared === key) {
@@ -2682,7 +2691,7 @@ async function runSpoolWatcher(instance, spoolDir) {
2682
2691
  }
2683
2692
  _lastKnownUpdateError = key;
2684
2693
  writeSharedUpdateErrorKey(key);
2685
- logEvent('plugkit', 'update.check.error', fields);
2694
+ logEvent('plugkit', 'update.check.error', { ...fields, category: key });
2686
2695
  }
2687
2696
  function clearUpdateCheckError(installed) {
2688
2697
  const shared = readSharedUpdateErrorKey();
@@ -2774,12 +2783,17 @@ async function runSpoolWatcher(instance, spoolDir) {
2774
2783
  }
2775
2784
  });
2776
2785
  });
2786
+ let _checkErrored = false;
2777
2787
  req.on('timeout', () => {
2778
- req.destroy();
2788
+ if (_checkErrored) { try { req.destroy(); } catch (_) {} return; }
2789
+ _checkErrored = true;
2790
+ try { req.destroy(); } catch (_) {}
2779
2791
  writeSharedUpdateCache(null, -1);
2780
2792
  logUpdateCheckError({ error: 'timeout' });
2781
2793
  });
2782
2794
  req.on('error', (e) => {
2795
+ if (_checkErrored) return;
2796
+ _checkErrored = true;
2783
2797
  writeSharedUpdateCache(null, -2);
2784
2798
  logUpdateCheckError({ error: String(e && e.message || e) });
2785
2799
  });
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1362",
3
+ "version": "2.0.1364",
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.1362",
3
+ "version": "2.0.1364",
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",