gm-skill 2.0.1526 → 2.0.1527

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
@@ -32,9 +32,7 @@ The plugkit stack runs as a wasm cdylib loaded by `plugkit-wasm-wrapper.js` unde
32
32
 
33
33
  **`plugkit-wasm-wrapper.js` is ESM; never use inline `require()` for a node builtin -- import it at module scope.** The wrapper runs under both node and bun, and the supervisor's `resolveRuntime()` prefers bun. Under bun's ESM, `require` is not a global, so an inline `const x = require('crypto'|'net'|'http'|'https'|'child_process')` throws `require is not defined` -- and because those calls sit in `catch(_){}` blocks, the failure is silent: it broke `_ownWrapperSha12` (status.wrapper_sha stayed null, leaving the supervisor wrapper-sha-drift recycle inert), `_wrapperShaAtBoot` and its self-drift-restart, the synthetic-session cwd-hash, and the file-index sha -- all only under the bun watcher, which is why it hid for so long (node-run watchers have `require` via CJS interop). Every node builtin is imported once at the top (`import crypto from 'crypto'`, etc.); inline `require` of a builtin is forbidden. Full incident in rs-learn (`recall: wrapper require not defined under bun`).
34
34
 
35
- **Every single-instance / lock guard is atomic, never check-then-act.** A guard that does `existsSync` -> read -> decide -> `writeFileSync` is TOCTOU: under a concurrent burst (the bootstrap spawns several supervisors in the same millisecond per skill-load) every caller passes the check before any writes, so all proceed and the duplicate it was meant to prevent happens anyway. The supervisor single-instance guard, the `.watcher.lock`, and any future pid/lock file all acquire via an atomic primitive -- `fs.openSync(path, 'wx')` (O_EXCL exclusive-create, succeeds for exactly one racer) or atomic-rename -- then on `EEXIST` read the holder and refuse-if-alive / take-over-if-stale. The trap: a check-then-write guard passes sequential testing (a later boot sees the prior holder) and silently fails only under concurrency, so when a guard is in place and the duplicate STILL occurs, suspect non-atomicity before suspecting absence. Full incident (three mis-diagnoses) in rs-learn (`recall: supervisor churn TOCTOU atomic guard`).
36
-
37
- **Count plugkit processes by executable Name, never by command-line substring.** A `Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like '*plugkit-wasm-wrapper*' }` (or `-match 'plugkit-supervisor'`) also matches the bash/powershell command running the query itself -- its eval string contains those literals -- so it fabricates phantom processes and inflates the count (it reported `8 watchers + 4 supervisors` when the truth was `4 watchers, 0 supervisors`). Always constrain to the real runtime: `Where-Object { ($_.Name -eq 'node.exe' -or $_.Name -eq 'bun.exe') -and $_.CommandLine -match 'plugkit-wasm-wrapper\.js' }`. The phantom count made a working atomic-guard fix look unconverged across two fires; a wrong measurement is as costly as a wrong diagnosis. Full incident in rs-learn (`recall: supervisor churn TOCTOU atomic guard`).
35
+ **Every single-instance / lock guard is atomic (`fs.openSync(path,'wx')` O_EXCL or atomic-rename), never check-then-act; count plugkit processes by executable Name not command-line substring.** Both are Windows concurrency mechanics whose full incident lives in rs-learn (`recall: supervisor churn TOCTOU atomic guard`).
38
36
 
39
37
  ## Spool dispatch ABI
40
38
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1526",
3
+ "version": "2.0.1527",
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": {
@@ -3472,78 +3472,7 @@ async function runSpoolWatcher(instance, spoolDir) {
3472
3472
  applyUpdateCheckResult(installed, cached.latest, cached.status || 200);
3473
3473
  return;
3474
3474
  }
3475
- const req = https.get({
3476
- host: 'api.github.com',
3477
- path: '/repos/AnEntrypoint/plugkit-bin/releases/latest',
3478
- headers: { 'user-agent': 'plugkit-watcher', 'accept': 'application/json' },
3479
- timeout: 5000,
3480
- }, (res) => {
3481
- if (res.statusCode !== 200) {
3482
- res.resume();
3483
- writeSharedUpdateCache(null, res.statusCode);
3484
- applyUpdateCheckResult(installed, null, res.statusCode);
3485
- checkUpdateViaNpm(installed);
3486
- return;
3487
- }
3488
- const chunks = [];
3489
- res.on('data', c => chunks.push(c));
3490
- res.on('end', () => {
3491
- try {
3492
- const rel = JSON.parse(Buffer.concat(chunks).toString('utf-8'));
3493
- const tag = rel && rel.tag_name;
3494
- if (!tag) return;
3495
- const latest = tag.replace(/^v/, '');
3496
- writeSharedUpdateCache(latest, 200);
3497
- if (latest === installed) {
3498
- try { fs.unlinkSync(UPDATE_AVAILABLE_PATH); } catch (_) {}
3499
- if (_lastKnownDrift) {
3500
- logEvent('plugkit', 'update.cleared', { installed, was: _lastKnownDrift });
3501
- _lastKnownDrift = null;
3502
- }
3503
- return;
3504
- }
3505
- const update_url = `https://github.com/AnEntrypoint/plugkit-bin/releases/tag/v${latest}`;
3506
- fs.writeFileSync(UPDATE_AVAILABLE_PATH, JSON.stringify({
3507
- installed,
3508
- latest,
3509
- checked_at_ms: Date.now(),
3510
- instruction: 'plugkit is out of date. Update with: bun x gm-plugkit@latest --kill-stale-watchers; bun x gm-plugkit@latest spool. A fresh boot downloads the new wasm and respawns; an in-place running watcher does not self-download.',
3511
- update_url,
3512
- }, null, 2));
3513
- console.log(`[update] available: installed=${installed} latest=${latest} -> wrote ${UPDATE_AVAILABLE_PATH}`);
3514
- if (_lastKnownDrift !== latest) {
3515
- logEvent('plugkit', 'update.available', { installed, latest, update_url });
3516
- _lastKnownDrift = latest;
3517
- }
3518
- // NOTE: do NOT bump the disk version file here to "arm" a drift-respawn. installedVersionAtTools()
3519
- // reads that file as the source of truth for the installed version; bumping it ahead of the actual
3520
- // wasm download makes ensureReady compute versionDrift=false (file==target) and isReady()=true, so it
3521
- // returns already-ready WITHOUT downloading the new binary -- while the running instance is still the
3522
- // old version. The drift-check then sees instance(old) != file(new) forever and self-respawns in an
3523
- // infinite loop, each respawn reloading the same old wasm. The version file must only advance AFTER
3524
- // a verified binary download (bootstrap's job). Auto-update stays notify-only until ensureReady gains
3525
- // a sha-verified force-download path; see PRD watcher-autoupdate-thrash-fix.
3526
- } catch (e) {
3527
- logUpdateCheckError({ error: String(e && e.message || e) });
3528
- }
3529
- });
3530
- });
3531
- let _checkErrored = false;
3532
- req.on('timeout', () => {
3533
- if (_checkErrored) { try { req.destroy(); } catch (_) {} return; }
3534
- _checkErrored = true;
3535
- try { req.destroy(); } catch (_) {}
3536
- writeSharedUpdateCache(null, -1);
3537
- logUpdateCheckError({ error: 'timeout' });
3538
- checkUpdateViaNpm(installed);
3539
- });
3540
- req.on('error', (e) => {
3541
- if (_checkErrored) return;
3542
- _checkErrored = true;
3543
- writeSharedUpdateCache(null, -2);
3544
- logUpdateCheckError({ error: String(e && e.message || e) });
3545
- checkUpdateViaNpm(installed);
3546
- });
3475
+ checkUpdateViaNpm(installed);
3547
3476
  }
3548
3477
  setTimeout(checkForUpdate, 10_000);
3549
3478
  setInterval(checkForUpdate, UPDATE_CHECK_INTERVAL_MS);
@@ -3739,11 +3668,10 @@ async function selfHealFromGithubReleases() {
3739
3668
  });
3740
3669
  (async () => {
3741
3670
  try {
3742
- const rel = await fetchJson('https://api.github.com/repos/AnEntrypoint/plugkit-bin/releases/latest');
3743
- const tag = rel.tag_name;
3744
- if (!tag) throw new Error('no tag_name from GH Releases');
3745
- const version = tag.replace(/^v/, '');
3746
- const base = `https://github.com/AnEntrypoint/plugkit-bin/releases/download/${tag}`;
3671
+ const meta = await fetchJson('https://registry.npmjs.org/plugkit-wasm/latest');
3672
+ const version = meta && meta.version;
3673
+ if (!version) throw new Error('no version from npm plugkit-wasm');
3674
+ const base = `https://github.com/AnEntrypoint/plugkit-bin/releases/download/v${version}`;
3747
3675
  const [wasm, sha] = await Promise.all([
3748
3676
  fetchBuf(`${base}/plugkit.wasm`),
3749
3677
  fetchBuf(`${base}/plugkit.wasm.sha256`).then(b => b.toString('utf-8').trim().split(/\s+/)[0]).catch(() => ''),
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1526",
3
+ "version": "2.0.1527",
4
4
  "description": "Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
@@ -132,15 +132,14 @@ async function getLatestRemoteVersion() {
132
132
  let version = null;
133
133
  let source = null;
134
134
  try {
135
- const buf = await httpGet('https://api.github.com/repos/AnEntrypoint/plugkit-bin/releases/latest', 3000);
136
- const rel = JSON.parse(buf.toString('utf-8'));
137
- const tag = rel && rel.tag_name;
138
- if (tag) {
139
- version = tag.replace(/^v/, '');
140
- source = 'github-releases';
135
+ const buf = await httpGet('https://registry.npmjs.org/plugkit-wasm/latest', 3000);
136
+ const pkg = JSON.parse(buf.toString('utf-8'));
137
+ if (pkg && pkg.version) {
138
+ version = pkg.version;
139
+ source = 'npm-plugkit-wasm';
141
140
  }
142
141
  } catch (e) {
143
- emitBootstrapEvent('warn', 'GitHub Releases lookup failed', { error: e.message });
142
+ emitBootstrapEvent('warn', 'npm plugkit-wasm lookup failed', { error: e.message });
144
143
  }
145
144
  if (!version) {
146
145
  try {
@@ -154,7 +153,7 @@ async function getLatestRemoteVersion() {
154
153
  source = 'npm-gm-plugkit-fallback';
155
154
  }
156
155
  } catch (e) {
157
- emitBootstrapEvent('warn', 'npm fallback lookup failed', { error: e.message });
156
+ emitBootstrapEvent('warn', 'npm gm-plugkit fallback lookup failed', { error: e.message });
158
157
  }
159
158
  }
160
159
  if (!version) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-skill",
3
- "version": "2.0.1526",
3
+ "version": "2.0.1527",
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",