gm-copilot-cli 2.0.1069 → 2.0.1071

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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: gm
3
- version: 2.0.1069
3
+ version: 2.0.1071
4
4
  description: Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement
5
5
  author: AnEntrypoint
6
6
  repository: https://github.com/AnEntrypoint/gm-copilot-cli
package/index.html CHANGED
@@ -74,7 +74,7 @@ body { display: flex; flex-direction: column; min-height: 100vh; }
74
74
  <section>
75
75
  <div class="gm-section-label"><span class="slash">//</span>status</div>
76
76
  <div class="panel">
77
- <div class="panel-head"><span>release · v2.0.1069</span><span>probably emerging</span></div>
77
+ <div class="panel-head"><span>release · v2.0.1071</span><span>probably emerging</span></div>
78
78
  <div class="panel-body">
79
79
  <div class="row">
80
80
  <span class="code"><span style="color:var(--panel-accent)">●</span></span>
@@ -0,0 +1,241 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { execSync, spawnSync } = require('child_process');
4
+ const fetch = (...args) => import('node-fetch').then(({ default: fetch }) => fetch(...args));
5
+
6
+ class WasmHost {
7
+ constructor(wasmPath) {
8
+ this.wasmPath = wasmPath;
9
+ this.instance = null;
10
+ this.memory = null;
11
+ }
12
+
13
+ async init() {
14
+ try {
15
+ const wasmBuffer = fs.readFileSync(this.wasmPath);
16
+ const wasmModule = new WebAssembly.Module(wasmBuffer);
17
+
18
+ const importObject = {
19
+ host: {
20
+ host_fs_read: this.hostFsRead.bind(this),
21
+ host_fs_write: this.hostFsWrite.bind(this),
22
+ host_fs_readdir: this.hostFsReaddir.bind(this),
23
+ host_fs_stat: this.hostFsStat.bind(this),
24
+ host_kv_get: this.hostKvGet.bind(this),
25
+ host_kv_put: this.hostKvPut.bind(this),
26
+ host_kv_query: this.hostKvQuery.bind(this),
27
+ host_fetch: this.hostFetch.bind(this),
28
+ host_vec_search: this.hostVecSearch.bind(this),
29
+ host_vec_embed: this.hostVecEmbed.bind(this),
30
+ host_browser_spawn: this.hostBrowserSpawn.bind(this),
31
+ host_browser_eval: this.hostBrowserEval.bind(this),
32
+ host_browser_close: this.hostBrowserClose.bind(this),
33
+ host_exec_js: this.hostExecJs.bind(this),
34
+ host_log: this.hostLog.bind(this),
35
+ host_now_ms: this.hostNowMs.bind(this),
36
+ host_env_get: this.hostEnvGet.bind(this),
37
+ },
38
+ env: {
39
+ memory: new WebAssembly.Memory({ initial: 256, maximum: 512 }),
40
+ }
41
+ };
42
+
43
+ this.instance = new WebAssembly.Instance(wasmModule, importObject);
44
+ this.memory = importObject.env.memory;
45
+ return { ok: true };
46
+ } catch (err) {
47
+ return { ok: false, error: err.message };
48
+ }
49
+ }
50
+
51
+ readString(offset, len) {
52
+ const buf = new Uint8Array(this.memory.buffer, offset, len);
53
+ return new TextDecoder().decode(buf);
54
+ }
55
+
56
+ writeString(str) {
57
+ const encoder = new TextEncoder();
58
+ const encoded = encoder.encode(str);
59
+ const len = encoded.length;
60
+ const offset = this.instance.exports.plugkit_alloc(len);
61
+ const buf = new Uint8Array(this.memory.buffer, offset, len);
62
+ buf.set(encoded);
63
+ return [offset, len];
64
+ }
65
+
66
+ hostFsRead(pathPtr, pathLen) {
67
+ try {
68
+ const pathStr = this.readString(pathPtr, pathLen);
69
+ const content = fs.readFileSync(pathStr, 'utf8');
70
+ const [offset, len] = this.writeString(content);
71
+ return offset;
72
+ } catch (err) {
73
+ return 0;
74
+ }
75
+ }
76
+
77
+ hostFsWrite(pathPtr, pathLen, dataPtr, dataLen) {
78
+ try {
79
+ const pathStr = this.readString(pathPtr, pathLen);
80
+ const data = this.readString(dataPtr, dataLen);
81
+ fs.writeFileSync(pathStr, data, 'utf8');
82
+ return 1;
83
+ } catch (err) {
84
+ return 0;
85
+ }
86
+ }
87
+
88
+ hostFsReaddir(pathPtr, pathLen) {
89
+ try {
90
+ const pathStr = this.readString(pathPtr, pathLen);
91
+ const entries = fs.readdirSync(pathStr);
92
+ const result = JSON.stringify(entries);
93
+ const [offset] = this.writeString(result);
94
+ return offset;
95
+ } catch (err) {
96
+ return 0;
97
+ }
98
+ }
99
+
100
+ hostFsStat(pathPtr, pathLen) {
101
+ try {
102
+ const pathStr = this.readString(pathPtr, pathLen);
103
+ const stat = fs.statSync(pathStr);
104
+ const result = JSON.stringify({
105
+ isFile: stat.isFile(),
106
+ isDirectory: stat.isDirectory(),
107
+ size: stat.size,
108
+ mtime: stat.mtime.getTime(),
109
+ });
110
+ const [offset] = this.writeString(result);
111
+ return offset;
112
+ } catch (err) {
113
+ return 0;
114
+ }
115
+ }
116
+
117
+ hostKvGet(keyPtr, keyLen) {
118
+ return 0;
119
+ }
120
+
121
+ hostKvPut(keyPtr, keyLen, valPtr, valLen) {
122
+ return 1;
123
+ }
124
+
125
+ hostKvQuery(queryPtr, queryLen) {
126
+ return 0;
127
+ }
128
+
129
+ hostFetch(urlPtr, urlLen, optsPtr, optsLen) {
130
+ return 0;
131
+ }
132
+
133
+ hostVecSearch(queryPtr, queryLen) {
134
+ return 0;
135
+ }
136
+
137
+ hostVecEmbed(textPtr, textLen) {
138
+ return 0;
139
+ }
140
+
141
+ hostBrowserSpawn(urlPtr, urlLen) {
142
+ return 0;
143
+ }
144
+
145
+ hostBrowserEval(sessionPtr, sessionLen, jsPtr, jsLen) {
146
+ return 0;
147
+ }
148
+
149
+ hostBrowserClose(sessionPtr, sessionLen) {
150
+ return 1;
151
+ }
152
+
153
+ hostExecJs(codePtr, codeLen) {
154
+ try {
155
+ const code = this.readString(codePtr, codeLen);
156
+ const result = eval(`(${code})`);
157
+ const [offset] = this.writeString(JSON.stringify(result));
158
+ return offset;
159
+ } catch (err) {
160
+ return 0;
161
+ }
162
+ }
163
+
164
+ hostLog(msgPtr, msgLen) {
165
+ const msg = this.readString(msgPtr, msgLen);
166
+ console.log(msg);
167
+ return 1;
168
+ }
169
+
170
+ hostNowMs() {
171
+ return Date.now();
172
+ }
173
+
174
+ hostEnvGet(keyPtr, keyLen) {
175
+ const key = this.readString(keyPtr, keyLen);
176
+ const val = process.env[key] || '';
177
+ const [offset] = this.writeString(val);
178
+ return offset;
179
+ }
180
+
181
+ async dispatch(verb, body) {
182
+ if (!this.instance) {
183
+ const initResult = await this.init();
184
+ if (!initResult.ok) return initResult;
185
+ }
186
+
187
+ try {
188
+ const bodyStr = typeof body === 'string' ? body : JSON.stringify(body);
189
+ const [verbOffset, verbLen] = this.writeString(verb);
190
+ const [bodyOffset, bodyLen] = this.writeString(bodyStr);
191
+
192
+ const resultPtr = this.instance.exports.dispatch_verb(verbOffset, verbLen, bodyOffset, bodyLen);
193
+ if (resultPtr === 0) {
194
+ return { ok: false, error: 'dispatch_verb returned null' };
195
+ }
196
+
197
+ const resultStr = this.readString(resultPtr, 1024);
198
+ try {
199
+ return JSON.parse(resultStr);
200
+ } catch {
201
+ return { ok: true, output: resultStr };
202
+ }
203
+ } catch (err) {
204
+ return { ok: false, error: err.message };
205
+ }
206
+ }
207
+
208
+ async hook(name, body) {
209
+ if (!this.instance) {
210
+ const initResult = await this.init();
211
+ if (!initResult.ok) return initResult;
212
+ }
213
+
214
+ try {
215
+ const bodyStr = typeof body === 'string' ? body : JSON.stringify(body);
216
+ const [nameOffset, nameLen] = this.writeString(name);
217
+ const [bodyOffset, bodyLen] = this.writeString(bodyStr);
218
+
219
+ const hookFn = this.instance.exports[`hook_${name}`];
220
+ if (!hookFn) {
221
+ return { ok: false, error: `hook_${name} not found` };
222
+ }
223
+
224
+ const resultPtr = hookFn(bodyOffset, bodyLen);
225
+ if (resultPtr === 0) {
226
+ return { ok: false, error: `hook_${name} returned null` };
227
+ }
228
+
229
+ const resultStr = this.readString(resultPtr, 1024);
230
+ try {
231
+ return JSON.parse(resultStr);
232
+ } catch {
233
+ return { ok: true, output: resultStr };
234
+ }
235
+ } catch (err) {
236
+ return { ok: false, error: err.message };
237
+ }
238
+ }
239
+ }
240
+
241
+ module.exports = WasmHost;
package/manifest.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  name: gm
2
- version: 2.0.1069
2
+ version: 2.0.1071
3
3
  description: Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement
4
4
  author: AnEntrypoint
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-copilot-cli",
3
- "version": "2.0.1069",
3
+ "version": "2.0.1071",
4
4
  "description": "Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
@@ -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
 
package/tools.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1069",
3
+ "version": "2.0.1071",
4
4
  "description": "Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement",
5
5
  "tools": [
6
6
  {