gm-skill 2.0.1911 → 2.0.1912

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
@@ -24,9 +24,11 @@ Skills encode environment-specific constraints that override general knowledge.
24
24
 
25
25
  Repo root = package root = published `gm-skill` npm package; no factory, no separate build-output dir. Entry: `skills/gm/SKILL.md`. Orchestration lives in rs-plugkit, served on-demand via `instruction`. Agent-facing prose (phase instruction, gate/residual text) externalizes to editable `gm-plugkit/instructions/`: prose edits = gm-plugkit republish, zero Rust rebuild. Mechanism (prose.rs per-key fallback to compiled const; sync-instruction-consts.mjs byte-aligns .md<->rs-plugkit consts) in the recall store (`recall: string-externalization project`).
26
26
 
27
- ## WASM-only
27
+ ## WASM-only, now with an optional native runner
28
28
 
29
- Plugkit = wasm cdylib, loaded by `plugkit-wasm-wrapper.js` under Node/bun; zero native binaries built/downloaded/published. `plugkit.wasm` fetched at bootstrap from `plugkit-wasm` npm / `plugkit-bin` gh-releases, sha256-pinned. Size + embedded-model (offline in-wasm embeddings) mechanics: the recall store (`recall: WASM-only plugkit size mechanics`).
29
+ Plugkit = wasm cdylib. Two host loaders exist and coexist: `plugkit-wasm-wrapper.js` under Node/bun (original, still the default) and `gm-runner` (rs-plugkit `crates/gm-runner/`, a real native cross-platform binary built by CI and published to `AnEntrypoint/gm-runner-bin` releases) -- gm-runner links `wasmtime` and hosts the same wasm module directly, no Node/bun dependency for the boot step. Both loaders serve the byte-identical spool ABI (`in/`/`out/` layout, verb names), so a gm-driven session works the same regardless of which loader booted it. `bin/install.js` best-effort-downloads gm-runner (sha256-verified against its release's `.sha256` sidecar) but never hard-requires it -- a platform with no published gm-runner binary, or a failed download, silently falls back to bun/npx exactly as before. `plugkit.wasm` itself is fetched at bootstrap from `plugkit-wasm` npm / `plugkit-bin` gh-releases, sha256-pinned, by whichever loader is running (gm-runner's own `download.rs::bootstrap_plugkit_wasm` mirrors the JS wrapper's fetch+verify). Size + embedded-model (offline in-wasm embeddings) mechanics: the recall store (`recall: WASM-only plugkit size mechanics`).
30
+
31
+ **gm-runner auto-updates the wasm it serves, not itself.** `spool.rs::run_spool_watcher` polls `plugkit-bin`'s GitHub Releases API every 600s for a newer tag (`download.rs::fetch_latest_plugkit_version`); on a mismatch it downloads+verifies the new wasm and the existing 30s local-version-skew check picks up the on-disk change and triggers a clean in-process reload (`StopReason::Reload`). The gm-runner *executable* itself has no such remote self-update loop yet -- a stale installed gm-runner binary keeps running until the next `bin/install.js` re-run re-checks `gm-runner-bin`'s latest release tag.
30
32
 
31
33
  Wasm host-import link-module rule (`#[link(wasm_import_module="env")]` on every host-import extern block, every dep crate): the recall store (`recall: wasm host-import link-module trap`).
32
34
 
@@ -152,6 +154,8 @@ A task that reduces to read/investigate/report, or a change confined to files th
152
154
 
153
155
  Push to any rs-* sibling -> `cascade.yml` -> rs-plugkit `release.yml` -> single `plugkit.wasm` (npm `plugkit-wasm` + `plugkit-bin` Releases) -> auto-bump `gm.json::plugkitVersion` -> `publish.yml` ships gm-skill+gm-plugkit+SKILL.md mirror. Step sequence + PUBLISHER_TOKEN: the recall store (`recall: cascade pipeline`).
154
156
 
157
+ A push touching `rs-plugkit/crates/gm-runner/**` additionally triggers its own `gm-runner.yml` workflow (separate from `release.yml`, matrix-builds all 6 platform targets, publishes to `AnEntrypoint/gm-runner-bin` Releases) -- a stuck/unschedulable single matrix leg (observed: `macos-13` queued 18h+ with zero GH-assigned runner) must never block the `release` job from shipping the other 5 platforms; that job runs `if: always()` and tolerates partial artifacts rather than a blanket `needs: build` hard-gate.
158
+
155
159
  **Repos involved (push to any triggers cascade):** `AnEntrypoint/{rs-codeinsight, rs-search, rs-plugkit, gm}`. rs-learn and rs-exec are retired (crates removed from / never depended on by rs-plugkit; their spool-dispatch and memory surfaces reimplemented natively in rs-plugkit wasm_dispatch; repos archived as tombstones, README points at rs-plugkit). Roles, npm package names, legacy-retirement detail: the recall store (`recall: cascade repos involved roles`, `recall: legacy gm-skill variants retired`).
156
160
 
157
161
  **To update every possible thing**: push to the relevant repo. No manual version bumps, no local `cargo update`/`cargo build` -- push, let CI build.
package/bin/install.js CHANGED
@@ -4,6 +4,8 @@
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
6
  const os = require('os');
7
+ const crypto = require('crypto');
8
+ const https = require('https');
7
9
  const readline = require('readline');
8
10
 
9
11
  function out(msg) { process.stdout.write(msg + '\n'); }
@@ -170,6 +172,119 @@ function runPlugkitBootstrap() {
170
172
  return Promise.resolve(false);
171
173
  }
172
174
 
175
+ // Maps process.platform/process.arch to the exact release-asset basename
176
+ // gm-runner's CI (rs-plugkit/.github/workflows/gm-runner.yml) publishes to
177
+ // AnEntrypoint/gm-runner-bin -- must stay byte-identical to that workflow's
178
+ // `artifact:` matrix values, this is the only other place that name is
179
+ // spelled. Returns null for a host combination CI does not build (no crash,
180
+ // caller falls back to the existing bun/npx path).
181
+ function gmRunnerAssetName() {
182
+ const plat = process.platform;
183
+ const arch = process.arch;
184
+ if (plat === 'win32') {
185
+ if (arch === 'x64') return 'gm-runner-windows-x64.exe';
186
+ if (arch === 'arm64') return 'gm-runner-windows-arm64.exe';
187
+ return null;
188
+ }
189
+ if (plat === 'darwin') {
190
+ if (arch === 'x64') return 'gm-runner-macos-x64';
191
+ if (arch === 'arm64') return 'gm-runner-macos-arm64';
192
+ return null;
193
+ }
194
+ if (plat === 'linux') {
195
+ if (arch === 'x64') return 'gm-runner-linux-x64';
196
+ if (arch === 'arm64') return 'gm-runner-linux-arm64';
197
+ return null;
198
+ }
199
+ return null;
200
+ }
201
+
202
+ function httpsGetBuffer(url, redirectsLeft) {
203
+ if (redirectsLeft === undefined) redirectsLeft = 5;
204
+ return new Promise((resolve, reject) => {
205
+ https.get(url, { headers: { 'User-Agent': 'gm-skill-installer' } }, res => {
206
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
207
+ res.resume();
208
+ if (redirectsLeft <= 0) { reject(new Error('too many redirects fetching ' + url)); return; }
209
+ httpsGetBuffer(res.headers.location, redirectsLeft - 1).then(resolve, reject);
210
+ return;
211
+ }
212
+ if (res.statusCode !== 200) {
213
+ res.resume();
214
+ reject(new Error(`GET ${url} -> HTTP ${res.statusCode}`));
215
+ return;
216
+ }
217
+ const chunks = [];
218
+ res.on('data', c => chunks.push(c));
219
+ res.on('end', () => resolve(Buffer.concat(chunks)));
220
+ res.on('error', reject);
221
+ }).on('error', reject);
222
+ });
223
+ }
224
+
225
+ function sha256Hex(buf) {
226
+ return crypto.createHash('sha256').update(buf).digest('hex');
227
+ }
228
+
229
+ function gmToolsDir() {
230
+ const home = homeDir();
231
+ return path.join(home, '.gm-tools');
232
+ }
233
+
234
+ // Downloads the platform-matched gm-runner binary from gm-runner-bin's
235
+ // GitHub Releases (native runner replacing the bun/node spool-boot path),
236
+ // verified against the release's own .sha256 sidecar before the atomic
237
+ // rename lands -- same trust model as gm-runner's own
238
+ // download::download_and_verify for plugkit.wasm, so a corrupt/partial
239
+ // download is never silently accepted. Best-effort: any failure (offline,
240
+ // asset missing for this host, network error) resolves false rather than
241
+ // throwing, so install never hard-fails over a runner-binary fetch -- the
242
+ // bun/npx spool path remains the fallback until remove-node-bun-from-native-path
243
+ // lands.
244
+ async function downloadGmRunner({ silent } = {}) {
245
+ const assetName = gmRunnerAssetName();
246
+ if (!assetName) {
247
+ if (!silent) err(`gm-runner: no published binary for platform=${process.platform} arch=${process.arch}, skipping native runner install`);
248
+ return false;
249
+ }
250
+ const destDir = gmToolsDir();
251
+ const destPath = path.join(destDir, assetName.endsWith('.exe') ? 'gm-runner.exe' : 'gm-runner');
252
+ try {
253
+ const releaseInfo = JSON.parse((await httpsGetBuffer('https://api.github.com/repos/AnEntrypoint/gm-runner-bin/releases/latest')).toString('utf8'));
254
+ const tag = releaseInfo && releaseInfo.tag_name;
255
+ if (!tag) { if (!silent) err('gm-runner: no releases published yet at AnEntrypoint/gm-runner-bin, skipping'); return false; }
256
+ const base = `https://github.com/AnEntrypoint/gm-runner-bin/releases/download/${tag}`;
257
+ const binUrl = `${base}/${assetName}`;
258
+ const shaUrl = `${binUrl}.sha256`;
259
+
260
+ const versionFile = path.join(destDir, 'gm-runner.version');
261
+ if (fs.existsSync(destPath) && fs.existsSync(versionFile)) {
262
+ const installed = fs.readFileSync(versionFile, 'utf8').trim();
263
+ if (installed === tag) { if (!silent) out(`gm-runner ${tag} already installed at ${destPath}`); return true; }
264
+ }
265
+
266
+ const [binBuf, shaBuf] = await Promise.all([httpsGetBuffer(binUrl), httpsGetBuffer(shaUrl)]);
267
+ const expectedSha = shaBuf.toString('utf8').trim().split(/\s+/)[0];
268
+ const actualSha = sha256Hex(binBuf);
269
+ if (!expectedSha || actualSha.toLowerCase() !== expectedSha.toLowerCase()) {
270
+ if (!silent) err(`gm-runner: sha256 mismatch downloading ${binUrl} (expected ${expectedSha}, got ${actualSha}), not installing`);
271
+ return false;
272
+ }
273
+
274
+ fs.mkdirSync(destDir, { recursive: true });
275
+ const tmp = destPath + '.tmp' + process.pid;
276
+ fs.writeFileSync(tmp, binBuf);
277
+ if (process.platform !== 'win32') { try { fs.chmodSync(tmp, 0o755); } catch (_) {} }
278
+ fs.renameSync(tmp, destPath);
279
+ fs.writeFileSync(versionFile, tag);
280
+ if (!silent) out(`Installed gm-runner ${tag} to ${destPath}`);
281
+ return true;
282
+ } catch (e) {
283
+ if (!silent) err(`gm-runner download skipped: ${e && e.message || e}`);
284
+ return false;
285
+ }
286
+ }
287
+
173
288
  function printHelp() {
174
289
  out('gm installer');
175
290
  out('');
@@ -220,6 +335,7 @@ async function main() {
220
335
  }
221
336
 
222
337
  await runPlugkitBootstrap();
338
+ await downloadGmRunner({ silent: false });
223
339
 
224
340
  out('');
225
341
  out('Done. Open Claude Code and run /gm. New top-level skill dirs may need one restart to register.');
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1911",
3
+ "version": "2.0.1912",
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": {
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1911",
3
+ "version": "2.0.1912",
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-skill",
3
- "version": "2.0.1911",
3
+ "version": "2.0.1912",
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",
@@ -60,6 +60,8 @@ bun x gm-plugkit@latest spool
60
60
 
61
61
  (`npx -y gm-plugkit@latest spool` if no `bun`.) Fire-and-forget: spawns the detached daemon and returns immediately (already-alive watcher also returns at once, unchanged) -- it does NOT wait for the watcher to confirm serving. No `&`, no `sleep`, no re-`cat`; write your first verb to `in/` right after it returns. A first-read "file does not exist" on that verb is normal (the just-spawned watcher hasn't noticed the file yet) -- re-Read next message, same as any dead-watcher-adjacent recheck. If you need to actively confirm serving before dispatching (rare), read `.gm/exec-spool/.status.json` yourself and check `ts` freshness.
62
62
 
63
+ **A native `gm-runner` binary at `~/.gm-tools/gm-runner` (`gm-runner.exe` on Windows) is a drop-in replacement for the bun/npx boot line above, when present** -- `gm-runner spool` serves the identical spool ABI (same `in/`/`out/` layout, same verb names) directly from a compiled binary, no bun/npx/node_modules dependency for the boot step. It is optional and additive right now, not a required cutover: `bin/install.js`'s installer best-effort-downloads it (sha256-verified, from `AnEntrypoint/gm-runner-bin` releases) but silently no-ops if the platform has no published binary yet or the download fails, and gm-runner itself only auto-updates its own served `plugkit.wasm` (a background 600s remote-version poll), not its own executable. If `~/.gm-tools/gm-runner` (or `.exe`) exists, prefer it for the boot line; otherwise fall back to `bun x gm-plugkit@latest spool` exactly as before -- both are equally valid, check-for-binary-then-fallback, never a hard requirement on one or the other.
64
+
63
65
  The `Resolving dependencies` / `Saved lockfile` chatter before the JSON payload is `bunx` re-resolving the `@latest` tag against the registry, not gm-plugkit hanging -- the daemon already spawns detached+unref'd and the CLI itself exits the instant that happens; the visible delay is entirely bunx's own network round-trip, unavoidable on `@latest` (a pinned exact version, once bunx-cached, skips it). `GM_PLUGKIT_SKIP_SELF_STALE_CHECK=1` skips the CLI's own redundant npm-registry version probe (already covered by `@latest`'s resolution) for a faster boot on repeat same-session invocations. In PowerShell 5.1, never `2>&1`-redirect this command into another cmdlet (e.g. `| Select-Object`) -- PowerShell wraps every stderr line from a native exe in a `NativeCommandError` record and reports failure even on exit 0, turning bun's routine stderr progress output into a misleading red error block; run it bare or capture stdout only.
64
66
 
65
67
  **Dispatch shape: Write request + Read response, SAME tool-call block.** Never proceed/narrate/begin work before reading the response and following its `instruction` field. First-read "file does not exist" mid-verb = normal, re-Read next message. Never poll with `sleep && ls` -- plugkit is synchronous; missing response = dead watcher (recheck `ts`) or slow verb, never "still processing."