gm-thebird 2.0.1069 → 2.0.1070

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
@@ -461,6 +461,11 @@ async function bootstrap(opts) {
461
461
  const okSentinel = path.join(verDir, '.ok');
462
462
  const partialPath = `${finalPath}.partial`;
463
463
 
464
+ const wasmName = 'plugkit.wasm';
465
+ const wasmExpectedSha = shaManifest ? shaManifest[wasmName] : null;
466
+ const wasmFinalPath = path.join(verDir, wasmName);
467
+ const wasmPartialPath = `${wasmFinalPath}.partial`;
468
+
464
469
  if (fs.existsSync(finalPath) && fs.existsSync(okSentinel)) {
465
470
  if (expectedSha) {
466
471
  const actualSha = sha256OfFileSync(finalPath);
@@ -570,6 +575,34 @@ async function bootstrap(opts) {
570
575
  pruneOldVersions(root, version, readRtkVersion(wrapperDir));
571
576
  spawnDetachedRtkFetch(wrapperDir);
572
577
  copyToGmTools(finalPath, wrapperDir, version);
578
+
579
+ try {
580
+ if (healIfShaMatches(wasmFinalPath, wasmExpectedSha, path.join(verDir, '.wasm-ok'), wasmPartialPath, 'wasm')) {
581
+ obsEvent('bootstrap', 'wasm.heal', { path: wasmFinalPath });
582
+ } else {
583
+ const wasmUrl = `https://github.com/${RELEASE_REPO}/releases/download/v${version}/${wasmName}`;
584
+ try {
585
+ await downloadWithRetry(wasmUrl, wasmPartialPath);
586
+ if (wasmExpectedSha) {
587
+ const wasmGot = await sha256OfFile(wasmPartialPath);
588
+ if (wasmGot !== wasmExpectedSha) {
589
+ try { fs.unlinkSync(wasmPartialPath); } catch (_) {}
590
+ log(`wasm sha256 mismatch: expected ${wasmExpectedSha}, got ${wasmGot}`);
591
+ } else {
592
+ try { fs.renameSync(wasmPartialPath, wasmFinalPath); } catch (e) {
593
+ if (e.code === 'EEXIST') { try { fs.unlinkSync(wasmFinalPath); } catch (_) {} fs.renameSync(wasmPartialPath, wasmFinalPath); }
594
+ }
595
+ fs.writeFileSync(path.join(verDir, '.wasm-ok'), new Date().toISOString());
596
+ obsEvent('bootstrap', 'wasm.install', { path: wasmFinalPath });
597
+ }
598
+ }
599
+ } catch (err) {
600
+ log(`wasm download failed (non-fatal): ${err.message}`);
601
+ }
602
+ }
603
+ try { fs.copyFileSync(wasmFinalPath, path.join(gmToolsDir(), wasmName)); } catch (_) {}
604
+ } catch (_) {}
605
+
573
606
  clearBootstrapError();
574
607
  return finalPath;
575
608
  } finally {
package/bin/plugkit.js CHANGED
@@ -81,13 +81,40 @@ function ensureReady(silent) {
81
81
  return r.status === 0 && isReady();
82
82
  }
83
83
 
84
+ function wasmPath() {
85
+ return path.join(wrapperDir, 'plugkit.wasm');
86
+ }
87
+
88
+ function shouldUseWasm() {
89
+ if (process.env.GM_USE_WASM === '1') return true;
90
+ if (fs.existsSync(wasmPath()) && !fs.existsSync(toolsBin())) return true;
91
+ return false;
92
+ }
93
+
94
+ async function runWasm(args) {
95
+ try {
96
+ const WasmHost = require('../lib/wasm-host');
97
+ const host = new WasmHost(wasmPath());
98
+ const verb = args[0] || 'health';
99
+ const body = args.slice(1).join(' ') || '{}';
100
+ const result = await host.dispatch(verb, body);
101
+ console.log(JSON.stringify(result, null, 2));
102
+ process.exit(result.ok ? 0 : 1);
103
+ } catch (err) {
104
+ console.error('[plugkit wasm]', err.message);
105
+ process.exit(1);
106
+ }
107
+ }
108
+
84
109
  function main() {
85
110
  const args = process.argv.slice(2);
86
111
  const isHook = args[0] === 'hook';
87
112
  const hookSubcmd = isHook ? (args[1] || '') : '';
88
113
 
89
- // Synchronous readiness check on these hooks. Hot path: isReady() is sha-match
90
- // against pinned manifest, returns true in <50ms with no network.
114
+ if (shouldUseWasm()) {
115
+ return runWasm(args);
116
+ }
117
+
91
118
  const blocksUntilReady = hookSubcmd === 'session-start' || hookSubcmd === 'prompt-submit';
92
119
 
93
120
  if (blocksUntilReady) {
@@ -96,8 +123,6 @@ function main() {
96
123
  process.exit(1);
97
124
  }
98
125
  } else if (!fs.existsSync(toolsBin())) {
99
- // For non-blocking hooks (pre-tool-use, post-tool-use, stop, etc.): if the
100
- // binary doesn't exist yet, exit cleanly — session-start will populate it.
101
126
  if (isHook) process.exit(0);
102
127
  process.exit(1);
103
128
  }
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1069",
3
+ "version": "2.0.1070",
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.1069",
3
+ "version": "2.0.1070",
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.1069",
3
+ "version": "2.0.1070",
4
4
  "description": "Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement",
5
5
  "author": {
6
6
  "name": "AnEntrypoint",
@@ -5,7 +5,7 @@ description: Mandatory codebase search workflow. Use whenever you need to find a
5
5
 
6
6
  # Codebase search
7
7
 
8
- `exec:codesearch` is the only codebase search tool. Grep, Glob, Find, Explore, raw `grep`/`rg`/`find` inside `exec:bash` are all hook-blocked. No fallback.
8
+ `exec:codesearch` is the only codebase search tool. Never use Grep, Glob, Find, Explore, raw `grep`/`rg`/`find` inside `exec:bash`. No fallback.
9
9
 
10
10
  A `@<discipline>` first-token after the verb scopes the search to that discipline's index; absent the sigil, results fan across default plus enabled disciplines, prefixed by source.
11
11
 
@@ -26,7 +26,7 @@ Failure triage: broken output to EMIT, wrong logic to EXECUTE, new unknown to PL
26
26
  - `git_clean` — `git status --porcelain` returns empty
27
27
  - `git_pushed` — `git log origin/main..HEAD --oneline` returns empty
28
28
  - `ci_passed` — every GitHub Actions run reaches `conclusion: success`
29
- - `mutables_resolved` — `.gm/mutables.yml` deleted OR every entry `status: witnessed`. Stop hook hard-blocks turn-stop while any entry is `status: unknown`.
29
+ - `mutables_resolved` — `.gm/mutables.yml` deleted OR every entry `status: witnessed`. Never stop the turn while any entry is `status: unknown` — this gate is self-enforced.
30
30
  - `prd_empty` — `.gm/prd.yml` deleted AFTER residual scan: enumerate every in-spirit reachable residual surfaced this session; any hit re-enters `planning`, appends PRD items, executes. Empty PRD is necessary, not sufficient — done = empty PRD AND zero reachable in-spirit residuals. Out-of-spirit-or-unreachable residuals are named in the response and skipped; everything else is this turn's work.
31
31
  - `stress_suite_clear` — change walked through M1–D1 (governance), none flunked
32
32
  - `hidden_decision_posture` — open → down_weighted → closed only when CI is green AND stress suite is clear
@@ -81,7 +81,7 @@ Both must return empty. Local commit without push is not complete.
81
81
 
82
82
  ## CI is automated
83
83
 
84
- The Stop hook watches Actions for the pushed HEAD. Do not call `gh run list` manually. All-green Stop approves with CI summary in next-turn context. FailureStop blocks with run names + IDs; investigate via `gh run view <id> --log-failed`, fix, push, hook re-watches. Deadline 180s (override `GM_CI_WATCH_SECS`); slow jobs get a "still in progress" approve.
84
+ After `git push`, poll `gh run list --branch main --limit 5 --json status,name,databaseId` until all runs reach a terminal state. Greencontinue; 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.
85
85
 
86
86
  ## Hygiene sweep
87
87
 
@@ -49,7 +49,7 @@ Re-import from disk — in-memory state is stale and inadmissible. Run identical
49
49
 
50
50
  ## Mutables gate
51
51
 
52
- Before pre-emit run, read `.gm/mutables.yml`. Any entry with `status: unknown` → regress to `gm-execute`. The pre-tool-use hook hard-blocks Write/Edit/NotebookEdit while unresolved entries exist; trying to emit anyway returns deny. Zero unresolved is the precondition for every legitimacy question below.
52
+ Before pre-emit run, read `.gm/mutables.yml`. Any entry with `status: unknown` → regress to `gm-execute`. Never use Write/Edit/NotebookEdit while unresolved entries exist this gate is self-enforced. Zero unresolved is the precondition for every legitimacy question below.
53
53
 
54
54
  ## Gate (all true at once)
55
55
 
@@ -30,7 +30,7 @@ Resolves to KNOWN only when all four pass:
30
30
 
31
31
  Unresolved after 2 passes regresses to `planning`. Never narrate past an unresolved mutable.
32
32
 
33
- Every witness that resolves a mutable writes back to `.gm/mutables.yml` the same step: set `status: witnessed` and fill `witness_evidence` with concrete proof (file:line, codesearch hit, exec output snippet). No write-back = the mutable stays unknown and the EMIT-gate stays closed. The hook reads this file; the agent's memory of "I resolved it" does not unblock anything.
33
+ Every witness that resolves a mutable writes back to `.gm/mutables.yml` the same step: set `status: witnessed` and fill `witness_evidence` with concrete proof (file:line, codesearch hit, exec output snippet). No write-back = the mutable stays unknown and the EMIT-gate stays closed. The file is the record; the agent's memory of "I resolved it" does not count.
34
34
 
35
35
  Route candidates from PLAN are `weak_prior` only. Plausibility is the right to test, not the right to believe. A claim with no witness in the current session is a hypothesis — say so when stating it, and say what would settle it. The next reader (you, next turn) needs to know which lines were earned and which were carried forward.
36
36
 
@@ -44,13 +44,13 @@ Code AND utility verbs both run through the file-spool. Write a file to `.gm/exe
44
44
 
45
45
  Pack runs: `Promise.allSettled`, each idea own try/catch, under 12s per call. Runner: write `in/runner/<N>.txt` with body `start` | `stop` | `status`.
46
46
 
47
- Every exec daemonizes. The hook tails the task logfile up to 30s wall-clock and returns whatever is there — short tasks complete inside the window and look synchronous; long tasks return a task_id with partial output. Continue with `exec:tail` (drain, bounded), `exec:watch` (resume blocking until match or timeout), or `exec:close` (terminate). Never re-spawn a long task to check on it — that orphans the first one. `exec:wait` is a pure timer; `exec:sleep` blocks on a specific task's output; `exec:watch` is the match-or-timeout primitive. Every execution-platform RPC returns the live list of running tasks for this session — close stragglers via `exec:close\n<id>` so the list stays scannable. Session-end (clear/logout/prompt_input_exit) kills the session's tasks; compaction/handoff preserves them.
47
+ Every exec daemonizes. The spool watcher tails the task logfile up to 30s wall-clock and returns whatever is there — short tasks complete inside the window and look synchronous; long tasks return a task_id with partial output. Continue with `exec:tail` (drain, bounded), `exec:watch` (resume blocking until match or timeout), or `exec:close` (terminate). Never re-spawn a long task to check on it — that orphans the first one. `exec:wait` is a pure timer; `exec:sleep` blocks on a specific task's output; `exec:watch` is the match-or-timeout primitive. Every execution-platform RPC returns the live list of running tasks for this session — close stragglers via `exec:close\n<id>` so the list stays scannable. Session-end (clear/logout/prompt_input_exit) kills the session's tasks; compaction/handoff preserves them.
48
48
 
49
- Every utility verb dispatches via `in/<verb>/<N>.txt`; the body of the file is the verb's argument. There is no inline form and no Bash-prefix form — both are denied by the hook.
49
+ Every utility verb dispatches via `in/<verb>/<N>.txt`; the body of the file is the verb's argument. There is no inline form and no Bash-prefix form — use only the spool path.
50
50
 
51
51
  ## Codebase search
52
52
 
53
- Codesearch only. Grep, Glob, Find, Explore, raw grep/rg/find inside Bash are all hook-blocked. Write query to `.gm/exec-spool/in/codesearch/<N>.txt`. Read result from `.gm/exec-spool/out/<N>.out`.
53
+ Codesearch only. Never use Grep, Glob, Find, Explore, raw grep/rg/find inside Bash. Write query to `.gm/exec-spool/in/codesearch/<N>.txt`. Read result from `.gm/exec-spool/out/<N>.out`.
54
54
 
55
55
  Start two words, change/add one per pass, minimum four attempts before concluding absent. Known absolute path → `Read`. Known directory → nodejs spool file + `fs.readdirSync`.
56
56
 
@@ -80,4 +80,4 @@ Up to 3 `gm:gm` subagents for independent items in one message. Browser escalati
80
80
 
81
81
  ## CI is automated
82
82
 
83
- `git push` triggers the Stop hook to watch Actions for the pushed HEAD on the same repo (downstream cascades are not auto-watched). Green → Stop approves with summary; failure → run names + IDs surfaced, investigate via `gh run view <id> --log-failed`. Deadline 180s (override `GM_CI_WATCH_SECS`).
83
+ 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.
@@ -77,7 +77,7 @@ Plan exits when zero new unknowns surfaced last pass AND every item has acceptan
77
77
 
78
78
  ## .gm/mutables.yml — co-equal with .gm/prd.yml
79
79
 
80
- Every unknown surfaced during PLAN lands as an entry in `.gm/mutables.yml` the same pass. Live during work, deleted when empty. Hook-gated: Write/Edit/NotebookEdit and `git commit`/`git push` are hard-blocked while any entry has `status: unknown`; turn-stop is hard-blocked the same way.
80
+ Every unknown surfaced during PLAN lands as an entry in `.gm/mutables.yml` the same pass. Live during work, deleted when empty. Self-enforced: never use Write/Edit/NotebookEdit, never run `git commit`/`git push`, never stop the turn while any entry has `status: unknown`. This discipline is owned by the agent — not by external infrastructure.
81
81
 
82
82
  ```yaml
83
83
  - id: kebab-id
@@ -143,7 +143,7 @@ The 200 lines are a *budget* for maximum surface coverage, not a target. Subsyst
143
143
 
144
144
  Code execution AND utility verbs both write to `.gm/exec-spool/in/<lang-or-verb>/<N>.<ext>`. Languages live under `in/<lang>/` (nodejs, python, bash, typescript, go, rust, c, cpp, java, deno); verbs live under `in/<verb>/` (codesearch, recall, memorize, wait, sleep, status, close, browser, runner, type, kill-port, forget, feedback, learn-status, learn-debug, learn-build, discipline, pause, health). The spool watcher runs the file and streams to `out/<N>.out` (stdout) + `out/<N>.err` (stderr) line-by-line, then writes `out/<N>.json` metadata (exitCode, durationMs, timedOut, startedAt, endedAt) at completion. Both streams return as systemMessage with `--- stdout ---` / `--- stderr ---` separators. `in/` and `out/` are wiped at session start and at real-exit session end. Only `git` (and `gh`) run directly via Bash; never `Bash(node/npm/npx/bun)`, never `Bash(exec:<anything>)`. Spool paths in nodejs files are platform-literal — use `os.tmpdir()` and `path.join`. The spool enforces per-task timeouts; on timeout, partial output is preserved and the watcher emits `[exec timed out after Nms; partial output above]`.
145
145
 
146
- Codesearch only — Grep/Glob/Find/Explore are hook-blocked. Write to `.gm/exec-spool/in/codesearch/<N>.txt`. Start two words, change/add one per pass, minimum four attempts before concluding absent.
146
+ Codesearch only — never use Grep/Glob/Find/Explore. Write to `.gm/exec-spool/in/codesearch/<N>.txt`. Start two words, change/add one per pass, minimum four attempts before concluding absent.
147
147
 
148
148
  Pack runs use `Promise.allSettled`, each idea its own try/catch, under 12s per call.
149
149