gm-plugkit 2.0.1628 → 2.0.1629

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.
Files changed (3) hide show
  1. package/SKILL.md +3 -3
  2. package/cli.js +36 -2
  3. package/package.json +1 -1
package/SKILL.md CHANGED
@@ -34,15 +34,15 @@ Every turn: dispatch `instruction`, read it, follow the imperative, dispatch the
34
34
  cat .gm/exec-spool/.status.json 2>/dev/null; echo ---; cat .gm/exec-spool/.turn-summary.json 2>/dev/null; echo ---; date +%s%3N
35
35
  ```
36
36
 
37
- `.turn-summary.json` carries `phase`, `last_skill`, `prd_pending`, `last_instruction_ts`, `last_instruction_age_ms`, `long_gap_threshold_ms`, `browser_sessions_alive`, `update_available`, `deviations_30m`, `watcher_uptime_ms`. Age over threshold: your next non-orienting verb is gated, dispatch `instruction` first. `update_available` non-null: eager-upgrade with `bun x gm-plugkit@latest --kill-stale-watchers; bun x gm-plugkit@latest spool > /dev/null 2>&1 &`, then re-read `.status.json` after 8s to confirm `version`. `deviations_30m` non-zero indicates active drift to investigate before continuing.
37
+ `.turn-summary.json` carries `phase`, `last_skill`, `prd_pending`, `last_instruction_ts`, `last_instruction_age_ms`, `long_gap_threshold_ms`, `browser_sessions_alive`, `update_available`, `deviations_30m`, `watcher_uptime_ms`. Age over threshold: your next non-orienting verb is gated, dispatch `instruction` first. `update_available` non-null: eager-upgrade with `bun x gm-plugkit@latest --kill-stale-watchers; bun x gm-plugkit@latest spool` (the spool call blocks until serving), then read `.status.json` to confirm `version`. `deviations_30m` non-zero indicates active drift to investigate before continuing.
38
38
 
39
39
  Compare `.status.json` `ts` to the printed epoch: gap > 15000 = dead, boot it. Exception: a future `busy_until` means a long verb (browser/chromium spawn blocks the heartbeat ~15-18s) -- wait, do not boot a second watcher.
40
40
 
41
41
  ```bash
42
- bun x gm-plugkit@latest spool > /dev/null 2>&1 &
42
+ bun x gm-plugkit@latest spool
43
43
  ```
44
44
 
45
- (`npx -y gm-plugkit@latest spool > /dev/null 2>&1 &` if `bun` missing.) Wait ~8s, re-`cat .status.json` for a fresh `ts`, and only then write to `instruction/`.
45
+ (`npx -y gm-plugkit@latest spool` if `bun` missing.) This call is atomic: it daemonizes the watcher and blocks until `.status.json` reports a fresh heartbeat, returning only once the spool is serving (exit 0) or failing loud on timeout. No `&`, no `sleep`, no re-`cat` -- when it returns you write to `instruction/` directly. (An already-alive watcher makes it return at once.)
46
46
 
47
47
  **Dispatch shape: Write request + Read response in the SAME tool-call block.** The shape is `Write .gm/exec-spool/in/instruction/<N>.txt` AND `Read .gm/exec-spool/out/instruction-<N>.json` (or `out/<N>.json` for nested verbs) in one block. A first-read "file does not exist" while plugkit is mid-verb is normal -- re-Read next message. Do not proceed, narrate readiness, or begin work before reading the response and following its `instruction` field. Never poll with `sleep && ls`: plugkit is synchronous, so a missing response means dead watcher (re-check `ts`) or slow verb (check `.gm/exec-spool/.watcher.log`), not "still processing."
48
48
 
package/cli.js CHANGED
@@ -132,6 +132,31 @@ function spoolDir() {
132
132
  return path.join(projectDir, '.gm', 'exec-spool');
133
133
  }
134
134
 
135
+ function readStatus(dir) {
136
+ try { return JSON.parse(fs.readFileSync(path.join(dir, '.status.json'), 'utf-8')); } catch (_) { return null; }
137
+ }
138
+
139
+ function statusServing(st, freshMs) {
140
+ if (!st || !st.pid) return false;
141
+ const now = Date.now();
142
+ if (Number.isFinite(st.busy_until) && st.busy_until > now) return true;
143
+ return Number.isFinite(st.ts) && (now - st.ts) < freshMs;
144
+ }
145
+
146
+ function sleepSync(ms) {
147
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
148
+ }
149
+
150
+ function waitForWatcherHeartbeat(dir, deadlineMs, freshMs) {
151
+ const t0 = Date.now();
152
+ while (Date.now() - t0 < deadlineMs) {
153
+ const st = readStatus(dir);
154
+ if (statusServing(st, freshMs)) return st;
155
+ sleepSync(300);
156
+ }
157
+ return null;
158
+ }
159
+
135
160
  function ensureSpoolDir() {
136
161
  try { fs.mkdirSync(spoolDir(), { recursive: true }); } catch (_) {}
137
162
  }
@@ -207,13 +232,22 @@ function writeCliError(phase, err) {
207
232
  process.exit(1);
208
233
  }
209
234
 
210
- writeCliStatus({ phase: 'ready', version: bootstrapResult.version, daemon_pid: daemon.pid, log: daemon.logPath });
235
+ const serving = waitForWatcherHeartbeat(spoolDir(), 30000, 12000);
236
+ if (!serving) {
237
+ const errMsg = `watcher spawned (pid=${daemon.pid}) but .status.json heartbeat not fresh within 30s`;
238
+ writeCliError('watcher-heartbeat-timeout', new Error(errMsg));
239
+ console.error('Daemon start failed:', errMsg);
240
+ process.exit(1);
241
+ }
242
+
243
+ writeCliStatus({ phase: 'ready', version: bootstrapResult.version, daemon_pid: daemon.pid, watcher_pid: serving.pid, log: daemon.logPath });
211
244
 
212
245
  console.log(JSON.stringify({
213
246
  ok: true,
214
247
  binary: bootstrapResult.binaryPath,
215
248
  daemon,
216
- message: 'plugkit ready, spool watcher running'
249
+ watcher_pid: serving.pid,
250
+ message: 'plugkit ready, spool watcher serving'
217
251
  }));
218
252
  process.exit(0);
219
253
  })().catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1628",
3
+ "version": "2.0.1629",
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": {