gm-skill 2.0.1109 → 2.0.1111

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/README.md CHANGED
@@ -28,7 +28,7 @@ npx gm-skill-bootstrap
28
28
 
29
29
  ## Version
30
30
 
31
- `2.0.1109` — auto-bumped from the canonical `gm` repo. Every push to `AnEntrypoint/gm` republishes this package alongside all 15 platform packages.
31
+ `2.0.1111` — auto-bumped from the canonical `gm` repo. Every push to `AnEntrypoint/gm` republishes this package alongside all 15 platform packages.
32
32
 
33
33
  ## Source of truth
34
34
 
@@ -1,6 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
+ import crypto from 'crypto';
4
5
  import { watch } from 'fs';
5
6
  import { spawn, spawnSync } from 'child_process';
6
7
 
@@ -23,14 +24,64 @@ function cosineSim(a, b) {
23
24
  const browserSessions = new Map();
24
25
  let nextBrowserSessionId = 1;
25
26
 
26
- function createWasiShim() {
27
- return new Proxy({}, {
27
+ function createWasiShim(instanceRef) {
28
+ const getMemory = () => instanceRef.value.exports.memory.buffer;
29
+ const shim = {
30
+ proc_exit: (code) => process.exit(code),
31
+ fd_write: (fd, iovs_ptr, iovs_len, nwritten_ptr) => {
32
+ try {
33
+ const buf = getMemory();
34
+ const dv = new DataView(buf);
35
+ const chunks = [];
36
+ let total = 0;
37
+ for (let i = 0; i < iovs_len; i++) {
38
+ const base = iovs_ptr + i * 8;
39
+ const ptr = dv.getUint32(base, true);
40
+ const len = dv.getUint32(base + 4, true);
41
+ if (len > 0) {
42
+ chunks.push(new Uint8Array(buf, ptr, len).slice());
43
+ total += len;
44
+ }
45
+ }
46
+ const merged = new Uint8Array(total);
47
+ let off = 0;
48
+ for (const c of chunks) { merged.set(c, off); off += c.length; }
49
+ const text = new TextDecoder('utf-8').decode(merged);
50
+ if (fd === 2) process.stderr.write(text);
51
+ else process.stdout.write(text);
52
+ new DataView(getMemory()).setUint32(nwritten_ptr, total, true);
53
+ return 0;
54
+ } catch (e) {
55
+ return 28;
56
+ }
57
+ },
58
+ random_get: (buf_ptr, buf_len) => {
59
+ try {
60
+ crypto.randomFillSync(new Uint8Array(getMemory(), buf_ptr, buf_len));
61
+ return 0;
62
+ } catch (e) {
63
+ return 28;
64
+ }
65
+ },
66
+ clock_time_get: (clock_id, precision, time_ptr) => {
67
+ try {
68
+ const ns = BigInt(Date.now()) * 1000000n;
69
+ new DataView(getMemory()).setBigUint64(time_ptr, ns, true);
70
+ return 0;
71
+ } catch (e) {
72
+ return 28;
73
+ }
74
+ },
75
+ environ_get: () => 0,
76
+ environ_sizes_get: () => 0,
77
+ };
78
+ return new Proxy(shim, {
28
79
  get(target, prop) {
29
- if (prop === 'proc_exit') return (code) => process.exit(code);
30
- if (prop === 'fd_write') return () => 0;
31
- if (prop === 'environ_get') return () => 0;
32
- if (prop === 'environ_sizes_get') return () => 0;
33
- return () => 0;
80
+ if (prop in target) return target[prop];
81
+ return (...args) => {
82
+ console.error(`[plugkit-wasm] unimplemented WASI call: ${String(prop)} args=${args.length}`);
83
+ return 52;
84
+ };
34
85
  }
35
86
  });
36
87
  }
@@ -374,6 +425,42 @@ async function runSpoolWatcher(instance, spoolDir) {
374
425
  fs.mkdirSync(inDir, { recursive: true });
375
426
  fs.mkdirSync(outDir, { recursive: true });
376
427
 
428
+ const LOCK_PATH = path.join(spoolDir, '.watcher.lock');
429
+ function acquireLock() {
430
+ try {
431
+ if (fs.existsSync(LOCK_PATH)) {
432
+ const content = fs.readFileSync(LOCK_PATH, 'utf-8').trim();
433
+ const [pidStr, tsStr] = content.split('|');
434
+ const lockTs = parseInt(tsStr, 10);
435
+ const age = Date.now() - lockTs;
436
+ if (age < 15000) {
437
+ console.error(`[plugkit-wasm] another watcher active (pid=${pidStr}, age=${age}ms); refusing to start`);
438
+ process.exit(1);
439
+ }
440
+ console.error(`[plugkit-wasm] stale lock (age=${age}ms); taking over`);
441
+ }
442
+ fs.writeFileSync(LOCK_PATH, `${process.pid}|${Date.now()}`);
443
+ } catch (e) {
444
+ console.error(`[plugkit-wasm] lock acquire failed: ${e.message}`);
445
+ process.exit(1);
446
+ }
447
+ }
448
+ function refreshLock() {
449
+ try { fs.writeFileSync(LOCK_PATH, `${process.pid}|${Date.now()}`); } catch (_) {}
450
+ }
451
+ function releaseLock() {
452
+ try {
453
+ const content = fs.readFileSync(LOCK_PATH, 'utf-8').trim();
454
+ const [pidStr] = content.split('|');
455
+ if (pidStr === String(process.pid)) fs.unlinkSync(LOCK_PATH);
456
+ } catch (_) {}
457
+ }
458
+ acquireLock();
459
+ setInterval(refreshLock, 5000);
460
+ process.on('SIGINT', () => { releaseLock(); process.exit(0); });
461
+ process.on('SIGTERM', () => { releaseLock(); process.exit(0); });
462
+ process.on('exit', releaseLock);
463
+
377
464
  console.log(`[plugkit-wasm] plugkit v${resolveVersion(instance)} (wasm)`);
378
465
  console.log(`[plugkit-wasm] watching ${inDir}`);
379
466
 
@@ -549,7 +636,7 @@ async function runSpoolWatcher(instance, spoolDir) {
549
636
 
550
637
  const importObject = {
551
638
  env: hostFunctions,
552
- wasi_snapshot_preview1: createWasiShim(),
639
+ wasi_snapshot_preview1: createWasiShim(instanceRef),
553
640
  };
554
641
 
555
642
  const instance = new WebAssembly.Instance(wasmModule, importObject);
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1109",
3
+ "version": "2.0.1111",
4
4
  "description": "Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
@@ -11,12 +11,6 @@
11
11
  "automation"
12
12
  ],
13
13
  "homepage": "https://github.com/AnEntrypoint/gm",
14
- "agents": [
15
- {
16
- "name": "gm",
17
- "description": "Agent (not skill) - immutable programming state machine. Always invoke for all work coordination."
18
- }
19
- ],
20
14
  "engines": {
21
15
  "node": ">=16.0.0"
22
16
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-skill",
3
- "version": "2.0.1109",
3
+ "version": "2.0.1111",
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",
@@ -39,7 +39,7 @@
39
39
  "gm.json"
40
40
  ],
41
41
  "dependencies": {
42
- "gm-plugkit": "^2.0.1109"
42
+ "gm-plugkit": "^2.0.1111"
43
43
  },
44
44
  "engines": {
45
45
  "node": ">=16.0.0"
@@ -1,19 +1,27 @@
1
1
  ---
2
2
  name: gm-skill
3
- description: Canonical universal harness — AI-native software engineering. Plugkit serves all instructions, state, guardrails on demand via the spool.
3
+ description: AI-native software engineering harness. plugkit serves all instructions, state, guardrails via the spool.
4
4
  allowed-tools: Skill, Read, Write
5
5
  ---
6
6
 
7
7
  # gm — single entry point
8
8
 
9
- Plugkit owns every instruction, every phase transition, every guardrail. The skill body is the only thing the agent reads from disk; everything else flows from plugkit verbs.
9
+ The wasm artifact lives at `~/.claude/gm-tools/plugkit.wasm`; the spool watcher runs it. If `.gm/exec-spool/.status.json` is stale or absent, bootstrap has not seeded the watcher yet re-invoke the skill or start it manually (`node plugkit-wasm-wrapper.js spool &`).
10
+
11
+ ## Dispatch ABI
12
+
13
+ Write request body to `.gm/exec-spool/in/<verb>/<N>.txt`. Read response from `.gm/exec-spool/out/<verb>-<N>.json` for nested verbs, `.gm/exec-spool/out/<N>.json` for root verbs.
10
14
 
11
15
  ## The loop
12
16
 
13
- 1. Write `.gm/exec-spool/in/instruction/<N>.txt` with empty body (or `phase=<override>` to force a phase). Read `.gm/exec-spool/out/instruction-<N>.json`.
14
- 2. The response contains `phase`, `instruction` (prose to follow), `mutables_pending`, `prd_pending_count`, `next_phase_hint`.
15
- 3. Follow the `instruction` body imperatively. Resolve mutables, execute work, dispatch other verbs (`recall`, `codesearch`, `memorize`, `mutable-resolve`, `transition`, all language stems) as the instruction directs.
16
- 4. When the phase's exit condition is met, dispatch `in/transition/<N>.txt` to advance. Then re-enter step 1 with the new phase.
17
- 5. Loop until `next_phase_hint` is null (phase=COMPLETE) the chain is done.
17
+ Dispatch `instruction` (empty body for current phase; `phase=<NAME>` line, `{"phase":"<NAME>"}`, or a raw phase name to override). The response carries `{phase, instruction, mutables_pending, prd_pending_count, next_phase_hint}`. Follow the `instruction` prose imperatively — it is the operative guidance for this phase. Resolve every `mutables_pending` entry through `mutable-resolve` before transitioning; the gate will refuse otherwise. When the phase's exit condition is met, dispatch `transition` (body: a phase name from `EXECUTE`/`EMIT`/`VERIFY`/`COMPLETE`, or empty to auto-advance), then re-enter with the new phase. Stop when `next_phase_hint` is null or phase is `COMPLETE`.
18
+
19
+ ## Orchestrator verbs
20
+
21
+ `instruction`, `transition`, `phase-status`, `mutable-resolve`, `memorize-fire`, `residual-scan`, `auto-recall`.
22
+
23
+ ## Host verbs
24
+
25
+ `fs_read`, `fs_write`, `fs_stat`, `fs_readdir`, `kv_get`, `kv_put`, `kv_query`, `fetch`, `exec_js`, `env_get`, `recall`, `codesearch`, `memorize`, `health`.
18
26
 
19
- No other skill exists. There is no `gm:planning`, no `gm:gm-execute`, no `gm:gm-emit`. Plugkit serves what those used to be, on demand, per phase.
27
+ Plugkit serves what prior skills (`gm:planning`, `gm:gm-execute`) used to serve, on demand, per phase. There is no other skill.