gm-thebird 2.0.1072 → 2.0.1074

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
@@ -224,17 +224,17 @@ function sha256OfFile(filePath) {
224
224
  });
225
225
  }
226
226
 
227
- async function extractNpmPackageWasm(destPath) {
227
+ async function extractNpmPackageWasm(destPath, version) {
228
228
  const tempDir = path.join(path.dirname(destPath), '.npm-extract-' + Date.now());
229
229
  try {
230
230
  ensureDir(tempDir);
231
231
  const startMs = Date.now();
232
- log(`extracting npm package ${NPM_PACKAGE}@latest to ${tempDir}`);
233
- obsEvent('bootstrap', 'npm.extract.start', { package: NPM_PACKAGE, version: 'latest' });
232
+ log(`extracting npm package ${NPM_PACKAGE}@${version} to ${tempDir}`);
233
+ obsEvent('bootstrap', 'npm.extract.start', { package: NPM_PACKAGE, version });
234
234
 
235
235
  const result = spawnSync(
236
236
  process.platform === 'win32' ? 'npx.cmd' : 'npx',
237
- [NPM_PACKAGE + '@latest', '--prefix', tempDir],
237
+ [NPM_PACKAGE + '@' + version, '--prefix', tempDir],
238
238
  {
239
239
  stdio: ['ignore', 'pipe', 'pipe'],
240
240
  timeout: ATTEMPT_TIMEOUT_MS,
@@ -245,7 +245,7 @@ async function extractNpmPackageWasm(destPath) {
245
245
 
246
246
  if (result.error) throw result.error;
247
247
  if (result.status !== 0) {
248
- throw new Error(`bunx extraction failed: ${result.stderr || result.stdout || 'unknown error'}`);
248
+ throw new Error(`npx extraction failed: ${result.stderr || result.stdout || 'unknown error'}`);
249
249
  }
250
250
 
251
251
  const nodeModulesPath = path.join(tempDir, 'node_modules', NPM_PACKAGE, 'plugkit.wasm');
@@ -261,12 +261,12 @@ async function extractNpmPackageWasm(destPath) {
261
261
  }
262
262
  }
263
263
 
264
- async function extractNpmPackageWithRetry(destPath) {
264
+ async function extractNpmPackageWithRetry(destPath, version) {
265
265
  let lastErr;
266
266
  for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
267
267
  try {
268
- log(`npm extract attempt ${attempt}/${MAX_ATTEMPTS}: ${NPM_PACKAGE}@latest`);
269
- await extractNpmPackageWasm(destPath);
268
+ log(`npm extract attempt ${attempt}/${MAX_ATTEMPTS}: ${NPM_PACKAGE}@${version}`);
269
+ await extractNpmPackageWasm(destPath, version);
270
270
  return;
271
271
  } catch (err) {
272
272
  lastErr = err;
@@ -393,7 +393,7 @@ async function bootstrap(opts) {
393
393
  } catch (_) {}
394
394
  }
395
395
  try {
396
- await extractNpmPackageWithRetry(wasmPartialPath);
396
+ await extractNpmPackageWithRetry(wasmPartialPath, version);
397
397
  } catch (extractErr) {
398
398
  writeBootstrapError({
399
399
  expected_version: version,
package/bin/plugkit.js CHANGED
@@ -47,10 +47,9 @@ function readPinnedVersion() {
47
47
  function readExpectedSha() {
48
48
  try {
49
49
  const manifest = fs.readFileSync(path.join(wrapperDir, 'plugkit.sha256'), 'utf8');
50
- const asset = platformAsset();
51
50
  for (const line of manifest.split(/\r?\n/)) {
52
51
  const parts = line.trim().split(/\s+/);
53
- if (parts.length >= 2 && parts[parts.length - 1].replace(/^\*/, '') === asset) {
52
+ if (parts.length >= 2 && parts[parts.length - 1].replace(/^\*/, '') === 'plugkit.wasm') {
54
53
  return parts[0].toLowerCase();
55
54
  }
56
55
  }
@@ -58,13 +57,13 @@ function readExpectedSha() {
58
57
  return null;
59
58
  }
60
59
 
61
- // Returns true if gm-tools binary matches pinned version by sha. Fast: no network.
60
+ // Returns true if gm-tools WASM matches pinned version by sha. Fast: no network.
62
61
  function isReady() {
63
- const bin = toolsBin();
64
- if (!fs.existsSync(bin)) return false;
62
+ const wasmBin = path.join(process.env.USERPROFILE || process.env.HOME || os.homedir(), '.claude', 'gm-tools', 'plugkit.wasm');
63
+ if (!fs.existsSync(wasmBin)) return false;
65
64
  const expected = readExpectedSha();
66
- if (!expected) return true; // no manifest to compare against — trust existence
67
- const actual = sha256OfFileSync(bin);
65
+ if (!expected) return true;
66
+ const actual = sha256OfFileSync(wasmBin);
68
67
  return actual && actual.toLowerCase() === expected;
69
68
  }
70
69
 
@@ -87,7 +86,7 @@ function wasmPath() {
87
86
 
88
87
  function shouldUseWasm() {
89
88
  if (process.env.GM_USE_WASM === '1') return true;
90
- if (fs.existsSync(wasmPath()) && !fs.existsSync(toolsBin())) return true;
89
+ if (fs.existsSync(wasmPath())) return true;
91
90
  return false;
92
91
  }
93
92
 
@@ -122,14 +121,16 @@ function main() {
122
121
  process.stderr.write('[plugkit] bootstrap failed; aborting hook\n');
123
122
  process.exit(1);
124
123
  }
125
- } else if (!fs.existsSync(toolsBin())) {
124
+ return runWasm(args);
125
+ }
126
+
127
+ const wasmBin = path.join(process.env.USERPROFILE || process.env.HOME || os.homedir(), '.claude', 'gm-tools', 'plugkit.wasm');
128
+ if (!fs.existsSync(wasmBin)) {
126
129
  if (isHook) process.exit(0);
127
130
  process.exit(1);
128
131
  }
129
132
 
130
- const bin = toolsBin();
131
- const r = spawnSync(bin, args, { stdio: 'inherit', windowsHide: true });
132
- process.exit(r.status ?? 1);
133
+ runWasm(args);
133
134
  }
134
135
 
135
136
  main();
@@ -1,3 +1,4 @@
1
+ 4d744898fef1f053e8bd3deb25aa20f6dd0ccda639c835ba4132762c8bfb9ec2 plugkit.wasm
1
2
  e5569efe81e4ef06c8349678253ca2845571495e619babb0a79be9268ea83c2a plugkit-win32-x64.exe
2
3
  ce2f09f8ea0dd522345a9d9c3e5b04ba44bc37b2046d311d7ff1737f1b3fbf1a plugkit-win32-arm64.exe
3
4
  a1a1d376986551828e5a39e4ae931accf66f00663aceac1439b2778cb4fffd27 plugkit-darwin-x64
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1072",
3
+ "version": "2.0.1074",
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-thebird",
3
- "version": "2.0.1072",
3
+ "version": "2.0.1074",
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/plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1072",
3
+ "version": "2.0.1074",
4
4
  "description": "Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement",
5
5
  "author": {
6
6
  "name": "AnEntrypoint",
@@ -58,3 +58,15 @@ Platform adapters (vscode, cursor, zed, jetbrains) that support `end-to-end: tru
58
58
  4. Repeat until `nextSkill` is null
59
59
 
60
60
  This collapses 5 manual skill invocations into 1 user invocation + 4 transparent auto-dispatches, achieving perceived single-flow parity with gm-cc's subagent orchestration.
61
+
62
+ ## Skill Transition Protocol
63
+
64
+ Skill-to-skill transitions NEVER stop for approval or ask the user. Each skill completes its phase and invokes the next skill via `Skill()` tool call directly. Platform adapters auto-detect the transition from stdout JSON and fire the next skill without user interaction. Tool-use approvals are pre-authorized by the initial user request — re-asking defeats the cascade and breaks autonomy. Skills must assume subsequent tools are approved once the PRD is written by PLAN.
65
+
66
+ ## Gate enforcement (spool-dispatch layer)
67
+
68
+ The file-spool (`lib/spool-dispatch.js::checkDispatchGates()`) blocks Write/Edit/git operations when:
69
+ 1. `.gm/prd.yml` exists AND `.gm/needs-gm` exists AND `.gm/gm-fired-<sessionId>` does NOT exist → reason: "gm orchestration in progress; skills must complete work before tools execute"
70
+ 2. `.gm/mutables.yml` has entries with `status: unknown` → reason: "unresolved mutables block tool execution; resolve all mutables before proceeding"
71
+
72
+ Gate 1 auto-clears: PLAN writes THREE markers (`.gm/prd.yml`, `.gm/needs-gm`, `.gm/gm-fired-<sessionId>`) at session start BEFORE transitioning to EXECUTE. The marker proves planning has run and authorized tool use. Gate 2 auto-clears: EXECUTE resolves mutables by updating `.gm/mutables.yml` entries to `status: witnessed`, or the file is deleted when empty by gm-complete. Tool denials surface the reason text to the agent, which adjusts behavior (e.g., resolve remaining mutables before retrying). Tool denials never mutate command arguments — they surface policy as imperative instruction.
@@ -104,3 +104,18 @@ After `git push`, poll `gh run list --branch main --limit 5 --json status,name,d
104
104
  ## Completion
105
105
 
106
106
  All true at once: witnessed e2e | browser_validated when client work touched | failure paths exercised | test.js passes | `.prd` deleted | git clean and pushed | CI green | hygiene sweep clean | TODO.md gone | CHANGELOG.md updated.
107
+
108
+ ## Marker file protocol
109
+
110
+ On transition to `update-docs`, delete all gm orchestration markers for the next cycle:
111
+ ```
112
+ const fs = require('fs');
113
+ const path = require('path');
114
+ const sessionId = process.env.SESSION_ID || 'default';
115
+ const gm = path.join(process.cwd(), '.gm');
116
+ ['.gm/prd.yml', '.gm/mutables.yml', '.gm/needs-gm', `.gm/gm-fired-${sessionId}`].forEach(m => {
117
+ try { fs.unlinkSync(path.join(gm, m.split('/')[1])); } catch (_) {}
118
+ });
119
+ ```
120
+
121
+ The `.gm/gm-fired-<sessionId>` marker was written by PLAN at session start and proves gm orchestration has completed. Cleanup before next cycle resets gates so the next PLAN run can write fresh markers.
@@ -5,16 +5,18 @@ description: EMIT phase. Pre-emit debug, write files, post-emit verify from disk
5
5
 
6
6
  # GM EMIT — Write and verify from disk
7
7
 
8
- Entry: every mutable KNOWN, from `gm-execute` or re-entered from VERIFY. Exit: gates clear → `gm-complete`.
8
+ Entry: every mutable KNOWN, from `gm-execute` or re-entered from VERIFY. Exit: gates clear → `gm-complete` (automatic, no approval).
9
9
 
10
10
  Cross-cutting dispositions live in `gm` SKILL.md.
11
11
 
12
+ **Skill Transition Guarantee**: When this skill completes, invoke `gm-complete` directly via `Skill()` — do not ask the user. Tool denials from gates (checkDispatchGates) surface as imperative reason text; the agent adjusts behavior and retries, never re-asks the user.
13
+
12
14
  ## Transitions
13
15
 
14
- - All gates clear → `gm-complete`
15
- - Post-emit variance with known cause → fix in-band, re-verify, stay in EMIT
16
- - Pre-emit reveals known logic error → `gm-execute`
17
- - Pre-emit reveals new unknown OR post-emit variance with unknown cause OR scope changed → `planning`
16
+ - All gates clear → `gm-complete` (invoke via `Skill(skill="gm:gm-complete")` immediately, no stop)
17
+ - Post-emit variance with known cause → fix in-band, re-verify, stay in EMIT (self-loop, no transition)
18
+ - Pre-emit reveals known logic error → `gm-execute` (invoke via `Skill(skill="gm:gm-execute")` immediately, no stop)
19
+ - Pre-emit reveals new unknown OR post-emit variance with unknown cause OR scope changed → `planning` (invoke via `Skill(skill="planning")` immediately, no stop)
18
20
 
19
21
  ## Legitimacy gate (before pre-emit run)
20
22
 
@@ -68,3 +70,7 @@ Before pre-emit run, read `.gm/mutables.yml`. Any entry with `status: unknown`
68
70
  - Structure: no if/else where dispatch suffices; no one-liners that obscure; no reinvented APIs
69
71
  - Every fact resolved this phase memorized via background `Agent(memorize)`
70
72
  - CHANGELOG.md updated; TODO.md cleared or deleted
73
+
74
+ ## Marker file protocol
75
+
76
+ EMIT phase operates after EXECUTE resolves all mutables. `.gm/prd.yml` and `.gm/mutables.yml` remain live (deleted by gm-complete when work finishes). Gate enforcement does not block EMIT — the `.gm/gm-fired-<sessionId>` marker was already written by PLAN at session start, so tool use (Write/Edit) is fully authorized. EMIT does not write or read markers; it only invokes Write/Edit on files and runs post-emit verifications. On transition to gm-complete (all gates clear), invoke the skill immediately (no marker write needed by EMIT).
@@ -5,17 +5,19 @@ description: EXECUTE phase AND the foundational execution contract for every ski
5
5
 
6
6
  # GM EXECUTE — Resolve every unknown by witness
7
7
 
8
- Entry: `.prd` with named unknowns. Exit: every mutable KNOWN → invoke `gm-emit`.
8
+ Entry: `.prd` with named unknowns. Exit: every mutable KNOWN → invoke `gm-emit` (automatic, no approval).
9
9
 
10
10
  A `@<discipline>` sigil propagates from PLAN through every recall, codesearch, and memorize call; reads without one fan across default plus enabled disciplines, writes without one go to default only.
11
11
 
12
12
  This skill is the execution contract for ALL phases — pre-emit witnesses, post-emit verifies, e2e checks all run on this discipline. Cross-cutting dispositions live in `gm` SKILL.md.
13
13
 
14
+ **Skill Transition Guarantee**: Tool use is pre-authorized by the initial user request. When this skill completes, invoke `gm-emit` directly via `Skill()` — do not ask the user. Tool denials from gates (checkDispatchGates) surface as imperative reason text; the agent adjusts behavior and retries, never re-asks the user.
15
+
14
16
  ## Transitions
15
17
 
16
- - All mutables KNOWN → `gm-emit`
18
+ - All mutables KNOWN → `gm-emit` (invoke via `Skill(skill="gm:emit")` immediately, no stop)
17
19
  - Still UNKNOWN → re-run from a different angle (max 2 passes)
18
- - New unknown OR unresolvable after 2 passes → `planning`
20
+ - New unknown OR unresolvable after 2 passes → `planning` (invoke via `Skill(skill="planning")` immediately, no stop)
19
21
 
20
22
  ## Mutable discipline
21
23
 
@@ -81,3 +83,7 @@ Up to 3 `gm:gm` subagents for independent items in one message. Browser escalati
81
83
  ## CI is automated
82
84
 
83
85
  After `git push`, poll `gh run list --branch main --limit 5 --json status,name,databaseId` until all runs reach a terminal state. Green → continue; failure → investigate via `gh run view <id> --log-failed`, fix, push again. Deadline 180s (override `GM_CI_WATCH_SECS`). Poll every 10s via a nodejs spool file with a `setInterval` loop writing results to stdout.
86
+
87
+ ## Marker file protocol
88
+
89
+ EXECUTE phase works against `.gm/prd.yml` and `.gm/mutables.yml` written by PLAN. Both files live for the duration of work and are deleted by gm-complete when work finishes. Gate enforcement (spool-dispatch layer) checks: if `.gm/prd.yml` + `.gm/needs-gm` exist BUT `.gm/gm-fired-<sessionId>` marker is missing, tool use blocks. PLAN writes all three markers at session start before transitioning to EXECUTE, so the gate is already clear when EXECUTE runs. EXECUTE does not write or read markers — it only reads and updates mutables. On transition to gm-emit, invoke the skill immediately (no marker write needed by EXECUTE).
@@ -31,7 +31,7 @@ const { spawn, spawnSync } = require('child_process');
31
31
  const fs = require('fs');
32
32
  const path = require('path');
33
33
  const os = require('os');
34
- const bin = path.join(os.homedir(), '.claude', 'gm-tools', process.platform === 'win32' ? 'plugkit.exe' : 'plugkit');
34
+ const bin = path.join(os.homedir(), '.claude', 'gm-tools', 'plugkit.wasm');
35
35
  const root = process.cwd();
36
36
  const spoolIn = path.join(root, '.gm', 'exec-spool', 'in');
37
37
  const spoolOut = path.join(root, '.gm', 'exec-spool', 'out');
@@ -42,13 +42,13 @@ if (fs.existsSync(pidFile)) {
42
42
  try { fs.unlinkSync(pidFile); } catch (_) {}
43
43
  }
44
44
  if (process.platform === 'win32') {
45
- try { spawnSync('taskkill', ['/F', '/IM', 'plugkit.exe'], { windowsHide: true, timeout: 3000, stdio: 'ignore' }); } catch (_) {}
45
+ try { spawnSync('taskkill', ['/F', '/IM', 'node.exe'], { windowsHide: true, timeout: 3000, stdio: 'ignore' }); } catch (_) {}
46
46
  } else {
47
47
  try { spawnSync('pkill', ['-f', 'plugkit'], { timeout: 3000, stdio: 'ignore' }); } catch (_) {}
48
48
  }
49
49
  fs.mkdirSync(spoolIn, { recursive: true });
50
50
  fs.mkdirSync(spoolOut, { recursive: true });
51
- const proc = spawn(bin, ['runner', '--watch', spoolIn, '--out', spoolOut], {
51
+ const proc = spawn('node', [bin, 'runner', '--watch', spoolIn, '--out', spoolOut], {
52
52
  detached: true, stdio: 'ignore', windowsHide: true, cwd: root,
53
53
  });
54
54
  proc.unref();
@@ -152,3 +152,24 @@ Pack runs use `Promise.allSettled`, each idea its own try/catch, under 12s per c
152
152
  No comments. 200-line per-file cap. Fail loud. No duplication. Scan before edit. AGENTS.md edits route through the memorize sub-agent only. CHANGELOG.md gets one entry per commit.
153
153
 
154
154
  Minimal-code process, stop at the first that resolves: native → library → structure (map / pipeline) → write.
155
+
156
+ ## Marker File Protocol
157
+
158
+ PLAN phase writes THREE marker files before transitioning to EXECUTE:
159
+ 1. `.gm/prd.yml` — the work items (already written per PRD format above)
160
+ 2. `.gm/needs-gm` — empty marker file signaling PRD is ready for orchestration
161
+ 3. `.gm/gm-fired-<sessionId>` — signals that gm orchestration (planning) has run and cleared the gate
162
+
163
+ When `.gm/prd.yml` and `.gm/needs-gm` both exist, downstream tools check `.gm/gm-fired-<sessionId>` marker. If missing, tool execution blocks with reason: "gm orchestration in progress; skills must complete work before tools execute." This gate prevents tool use from tools that run BEFORE the orchestration phase is complete. Writing the marker clears this gate.
164
+
165
+ Write all three markers as final step of PLAN:
166
+ ```
167
+ const fs = require('fs');
168
+ const path = require('path');
169
+ const sessionId = process.env.SESSION_ID || 'default';
170
+ fs.mkdirSync(path.join(process.cwd(), '.gm'), { recursive: true });
171
+ fs.writeFileSync(path.join(process.cwd(), '.gm', 'needs-gm'), '');
172
+ fs.writeFileSync(path.join(process.cwd(), '.gm', `gm-fired-${sessionId}`), '');
173
+ ```
174
+
175
+ Transition to `gm-execute` (or `gm-gm` subagent) immediately after writing all files. No stop-for-approval; the transition is automatic.