gm-skill 2.0.1544 → 2.0.1546

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
@@ -158,11 +158,7 @@ Three npm packages publish from this repo: `gm-skill` (the skill harness), `gm-p
158
158
 
159
159
  ## Spool-dispatch architecture replaces hooks
160
160
 
161
- Orchestration state is tracked via marker files in `.gm/` instead of hook events. `SpoolDispatcher` reads these markers via `checkDispatchGates(sessionId, operation)` and gates tool use, writes, and git operations:
162
-
163
- **Marker files**: `.gm/{prd.yml, mutables.yml, needs-gm, gm-fired-<sessionId>, residual-check-fired}` gate Write/Edit/git via `checkDispatchGates`. Per-marker semantics in rs-learn (`recall: spool dispatch gates marker files`).
164
-
165
- **Gate enforcement**: the CLI layer calls `checkDispatchGates()` before tool execution; marker-driven dispatch replaces the hook event pump entirely. Detail in rs-learn (`recall: gate enforcement layer`).
161
+ Orchestration state is tracked via `.gm/` marker files, not hook events; the CLI layer calls `checkDispatchGates()` before tool execution to gate Write/Edit/git. Marker set (`prd.yml, mutables.yml, needs-gm, gm-fired-<sessionId>, residual-check-fired`), per-marker semantics, and the SpoolDispatcher CLI mechanism live in rs-learn (`recall: gate enforcement layer`, `recall: spool dispatch gates marker files`).
166
162
 
167
163
  **gm-skill tool-use sequencing**: Invoking `Skill(skill="gm-skill")` writes `.gm/gm-fired-<sessionId>` to clear the needs-gm gate. The marker is cleared at turn start to reset the gate. There is one shipped skill; no subagent variant exists.
168
164
 
package/bin/bootstrap.js CHANGED
@@ -27,7 +27,8 @@ function ensureSkillMdCurrent(wrapperDir) {
27
27
  const bundledPath = candidates.find(p => { try { return fs.existsSync(p); } catch (_) { return false; } });
28
28
  if (!bundledPath) return { skipped: 'bundled-not-found' };
29
29
  const bundled = fs.readFileSync(bundledPath, 'utf8');
30
- const bundledHash = crypto.createHash('sha256').update(bundled).digest('hex');
30
+ const _norm = s => s.replace(/\r\n/g, '\n');
31
+ const bundledHash = crypto.createHash('sha256').update(_norm(bundled)).digest('hex');
31
32
  const home = os.homedir();
32
33
  const targets = [
33
34
  path.join(home, '.agents', 'skills', 'gm-skill', 'SKILL.md'),
@@ -39,7 +40,7 @@ function ensureSkillMdCurrent(wrapperDir) {
39
40
  let needsWrite = true;
40
41
  if (fs.existsSync(target)) {
41
42
  const existing = fs.readFileSync(target, 'utf8');
42
- const existingHash = crypto.createHash('sha256').update(existing).digest('hex');
43
+ const existingHash = crypto.createHash('sha256').update(_norm(existing)).digest('hex');
43
44
  if (existingHash === bundledHash) needsWrite = false;
44
45
  }
45
46
  if (needsWrite) {
@@ -1 +1 @@
1
- 0.1.641
1
+ 0.1.642
@@ -1 +1 @@
1
- ae93638360e50b10ccdc581f26c1eeb927465b5896ace833de61cf4e26a3dc1b plugkit.wasm
1
+ a6532f8efc7b865cc2a4ce2ed714169e7f6110255b01cd709f94a51ca4b70e48 plugkit.wasm
@@ -716,7 +716,8 @@ function ensureSkillMdFresh() {
716
716
  }
717
717
  const bundled = fs.readFileSync(bundledPath, 'utf-8');
718
718
  const crypto = require('crypto');
719
- const bundledHash = crypto.createHash('sha256').update(bundled).digest('hex');
719
+ const _norm = s => s.replace(/\r\n/g, '\n');
720
+ const bundledHash = crypto.createHash('sha256').update(_norm(bundled)).digest('hex');
720
721
  const home = process.env.HOME || process.env.USERPROFILE || require('os').homedir();
721
722
  const targets = [
722
723
  path.join(home, '.agents', 'skills', 'gm-skill', 'SKILL.md'),
@@ -728,7 +729,7 @@ function ensureSkillMdFresh() {
728
729
  let needsWrite = true;
729
730
  if (fs.existsSync(target)) {
730
731
  const existing = fs.readFileSync(target, 'utf-8');
731
- const existingHash = crypto.createHash('sha256').update(existing).digest('hex');
732
+ const existingHash = crypto.createHash('sha256').update(_norm(existing)).digest('hex');
732
733
  if (existingHash === bundledHash) needsWrite = false;
733
734
  }
734
735
  if (needsWrite) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1544",
3
+ "version": "2.0.1546",
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": {
@@ -2560,6 +2560,17 @@ async function runSpoolWatcher(instance, spoolDir) {
2560
2560
  setTimeout(probeGmPlugkitSelfStale, 5000);
2561
2561
  setInterval(probeGmPlugkitSelfStale, 300_000);
2562
2562
 
2563
+ // A supervised watcher self-exits on drift assuming the supervisor respawns it. If the
2564
+ // supervisor has died, that bare exit leaves the spool dead (worse than staying up). Treat a
2565
+ // dead/absent supervisor as unsupervised so the drift loops take the self-respawn-and-wait path
2566
+ // (spawn replacement, wait for its heartbeat, then exit) instead. False-negative is self-correcting:
2567
+ // if both the supervisor and this watcher respawn, the single-instance lock admits exactly one.
2568
+ function _supervisorIsDead() {
2569
+ try {
2570
+ const sp = parseInt(fs.readFileSync(path.join(spoolDir, '.supervisor.pid'), 'utf8').trim(), 10);
2571
+ return !(Number.isFinite(sp) && isProcessAliveSync(sp));
2572
+ } catch (_) { return true; }
2573
+ }
2563
2574
  const _instanceVersionAtBoot = readInstanceVersion(instance);
2564
2575
  let _driftLoggedOnce = false;
2565
2576
  setInterval(() => {
@@ -2568,7 +2579,7 @@ async function runSpoolWatcher(instance, spoolDir) {
2568
2579
  const instV = _instanceVersionAtBoot;
2569
2580
  if (!fileV || !instV || fileV === instV) return;
2570
2581
  const bootReason = process.env.PLUGKIT_BOOT_REASON || 'unknown';
2571
- const unsupervised = bootReason === 'direct-no-supervisor';
2582
+ const unsupervised = bootReason === 'direct-no-supervisor' || _supervisorIsDead();
2572
2583
  if (unsupervised) {
2573
2584
  if (_driftLoggedOnce) return;
2574
2585
  _driftLoggedOnce = true;
@@ -2665,7 +2676,7 @@ async function runSpoolWatcher(instance, spoolDir) {
2665
2676
  const cur = crypto.createHash('sha256').update(fs.readFileSync(_wrapperPathInstalled)).digest('hex');
2666
2677
  if (cur === _wrapperShaAtBoot) return;
2667
2678
  const bootReason = process.env.PLUGKIT_BOOT_REASON || 'unknown';
2668
- const unsupervised = bootReason === 'direct-no-supervisor';
2679
+ const unsupervised = bootReason === 'direct-no-supervisor' || _supervisorIsDead();
2669
2680
  if (unsupervised) {
2670
2681
  if (_wrapperDriftLoggedOnce) return;
2671
2682
  _wrapperDriftLoggedOnce = true;
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1544",
3
+ "version": "2.0.1546",
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.641"
20
+ "plugkitVersion": "0.1.642"
21
21
  }
@@ -300,7 +300,8 @@ function ensureSkillMdCurrent() {
300
300
  return { refreshed: [], skipped: true };
301
301
  }
302
302
  const bundled = fs.readFileSync(bundledPath, 'utf8');
303
- const bundledHash = crypto.createHash('sha256').update(bundled).digest('hex');
303
+ const _norm = s => s.replace(/\r\n/g, '\n');
304
+ const bundledHash = crypto.createHash('sha256').update(_norm(bundled)).digest('hex');
304
305
  const targets = [
305
306
  path.join(os.homedir(), '.agents', 'skills', 'gm-skill', 'SKILL.md'),
306
307
  path.join(os.homedir(), '.claude', 'skills', 'gm-skill', 'SKILL.md'),
@@ -311,7 +312,7 @@ function ensureSkillMdCurrent() {
311
312
  let needsWrite = true;
312
313
  if (fs.existsSync(target)) {
313
314
  const existing = fs.readFileSync(target, 'utf8');
314
- const existingHash = crypto.createHash('sha256').update(existing).digest('hex');
315
+ const existingHash = crypto.createHash('sha256').update(_norm(existing)).digest('hex');
315
316
  if (existingHash === bundledHash) needsWrite = false;
316
317
  }
317
318
  if (needsWrite) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-skill",
3
- "version": "2.0.1544",
3
+ "version": "2.0.1546",
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",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: gm-skill
3
- description: Plugkit-served instruction stream. Three-layer admission (cost, bounds, direction) over every possible mutation. Closure on first emit; partial = non-monotonic.
3
+ description: Plugkit-served instruction stream. Three-layer admission (witness, single-writer, direction) over every possible mutation; effort unbounded, never gated on cost. Closure on first emit; partial = non-monotonic.
4
4
  allowed-tools: Skill, Read, Write, Bash(bun *), Bash(npx *)
5
5
  ---
6
6