gm-skill 2.0.1391 → 2.0.1393
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/gm-plugkit/package.json +1 -1
- package/gm-plugkit/plugkit-wasm-wrapper.js +57 -1
- package/gm.json +1 -1
- package/package.json +1 -1
- package/skills/gm-skill/SKILL.md +5 -3
package/gm-plugkit/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-plugkit",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1393",
|
|
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": {
|
|
@@ -1868,7 +1868,9 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
1868
1868
|
const holderSha = parts[2] || '';
|
|
1869
1869
|
const lockTs = parseInt(tsStr, 10);
|
|
1870
1870
|
const age = Date.now() - lockTs;
|
|
1871
|
-
|
|
1871
|
+
const holderPidNum = parseInt(pidStr, 10);
|
|
1872
|
+
const holderAlive = Number.isFinite(holderPidNum) && isProcessAliveSync(holderPidNum);
|
|
1873
|
+
if (age < 15000 && holderAlive) {
|
|
1872
1874
|
if (_ownWrapperSha12 && holderSha && holderSha !== _ownWrapperSha12) {
|
|
1873
1875
|
try { logEvent('plugkit', 'peer.stale-wrapper-takeover', { holder_pid: pidStr, holder_sha: holderSha, own_sha: _ownWrapperSha12, lock_age_ms: age }); } catch (_) {}
|
|
1874
1876
|
console.error(`[plugkit-wasm] peer wrapper-sha mismatch (holder=${holderSha} own=${_ownWrapperSha12}); killing pid=${pidStr} and taking over`);
|
|
@@ -1896,6 +1898,9 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
1896
1898
|
} catch (_) {}
|
|
1897
1899
|
process.exit(75);
|
|
1898
1900
|
}
|
|
1901
|
+
} else if (!holderAlive) {
|
|
1902
|
+
console.error(`[plugkit-wasm] stale lock (holder pid=${pidStr} dead, age=${age}ms); taking over`);
|
|
1903
|
+
try { logEvent('plugkit', 'watcher.lock-pid-dead-takeover', { stale_pid: pidStr, lock_age_ms: age }); } catch (_) {}
|
|
1899
1904
|
} else {
|
|
1900
1905
|
console.error(`[plugkit-wasm] stale lock (age=${age}ms); taking over`);
|
|
1901
1906
|
}
|
|
@@ -2742,6 +2747,57 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
2742
2747
|
setInterval(writeStatus, 5000);
|
|
2743
2748
|
writeStatus();
|
|
2744
2749
|
|
|
2750
|
+
const TURN_SUMMARY_PATH = path.join(spoolDir, '.turn-summary.json');
|
|
2751
|
+
function writeTurnSummary() {
|
|
2752
|
+
try {
|
|
2753
|
+
const cwd = process.cwd();
|
|
2754
|
+
const gmDir = path.join(cwd, '.gm');
|
|
2755
|
+
let phase = null, lastSkill = null, prdPending = 0, browserSessions = 0;
|
|
2756
|
+
let lastInstructionTs = null, lastInstructionAgeMs = null;
|
|
2757
|
+
try {
|
|
2758
|
+
const ts = JSON.parse(fs.readFileSync(path.join(gmDir, 'turn-state.json'), 'utf-8'));
|
|
2759
|
+
phase = ts.phase || null;
|
|
2760
|
+
lastSkill = ts.last_skill || null;
|
|
2761
|
+
} catch (_) {}
|
|
2762
|
+
try {
|
|
2763
|
+
const prdRaw = fs.readFileSync(path.join(gmDir, 'prd.yml'), 'utf-8');
|
|
2764
|
+
const openRe = /\n\s*status:\s*(pending|in_progress|unknown)\b/g;
|
|
2765
|
+
const matches = prdRaw.match(openRe);
|
|
2766
|
+
prdPending = matches ? matches.length : 0;
|
|
2767
|
+
} catch (_) {}
|
|
2768
|
+
try {
|
|
2769
|
+
const tsRaw = fs.readFileSync(path.join(gmDir, 'last-instruction-ts'), 'utf-8');
|
|
2770
|
+
const n = parseInt(tsRaw.trim(), 10);
|
|
2771
|
+
if (Number.isFinite(n) && n > 0) {
|
|
2772
|
+
lastInstructionTs = n;
|
|
2773
|
+
lastInstructionAgeMs = Date.now() - n;
|
|
2774
|
+
}
|
|
2775
|
+
} catch (_) {}
|
|
2776
|
+
try {
|
|
2777
|
+
const ports = readJsonFile(browserPortsFile(cwd), {});
|
|
2778
|
+
for (const entry of Object.values(ports)) {
|
|
2779
|
+
if (entry && Number.isFinite(entry.pid) && isProcessAliveSync(entry.pid)) browserSessions++;
|
|
2780
|
+
}
|
|
2781
|
+
} catch (_) {}
|
|
2782
|
+
const fileV = readFileVersionOnly() || null;
|
|
2783
|
+
const instV = _instanceVersionAtBoot || null;
|
|
2784
|
+
fs.writeFileSync(TURN_SUMMARY_PATH, JSON.stringify({
|
|
2785
|
+
ts: Date.now(),
|
|
2786
|
+
watcher_pid: process.pid,
|
|
2787
|
+
watcher_version: instV || fileV,
|
|
2788
|
+
phase,
|
|
2789
|
+
last_skill: lastSkill,
|
|
2790
|
+
prd_pending: prdPending,
|
|
2791
|
+
last_instruction_ts: lastInstructionTs,
|
|
2792
|
+
last_instruction_age_ms: lastInstructionAgeMs,
|
|
2793
|
+
long_gap_threshold_ms: 300000,
|
|
2794
|
+
browser_sessions_alive: browserSessions,
|
|
2795
|
+
}));
|
|
2796
|
+
} catch (_) {}
|
|
2797
|
+
}
|
|
2798
|
+
setInterval(writeTurnSummary, 5000);
|
|
2799
|
+
writeTurnSummary();
|
|
2800
|
+
|
|
2745
2801
|
const UPDATE_AVAILABLE_PATH = path.join(spoolDir, '.update-available.json');
|
|
2746
2802
|
const UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1000;
|
|
2747
2803
|
const UPDATE_CHECK_SHARED_CACHE = path.join(GM_TOOLS_ROOT, '.update-check-cache.json');
|
package/gm.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-skill",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1393",
|
|
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",
|
package/skills/gm-skill/SKILL.md
CHANGED
|
@@ -22,13 +22,15 @@ Every turn: dispatch `instruction` (you are the one dispatching it), read the re
|
|
|
22
22
|
|
|
23
23
|
**Boot before dispatching. Always check first.** Writing to `.gm/exec-spool/in/instruction/N.txt` while the watcher is dead is the canonical cold-start failure — the request sits forever, you read no response, you fabricate the chain from memory of the prose. The spool directory's existence does NOT mean the watcher is alive; `.status.json` mtime within the last 15s does. The leftover `.status.json` from yesterday's dead watcher is the most common trap.
|
|
24
24
|
|
|
25
|
-
Your first tool call of every session is the boot probe, in one Bash invocation:
|
|
25
|
+
Your first tool call of every session is the boot probe, in one Bash invocation. Read `.status.json` (liveness) and `.turn-summary.json` (orientation) together — the watcher precomputes both every 5s, so the two files give you everything you'd otherwise extract via gmsniff queries, git, yaml-parse, and ports-file scans:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
cat .gm/exec-spool/.status.json 2>/dev/null; date +%s%3N
|
|
28
|
+
cat .gm/exec-spool/.status.json 2>/dev/null; echo ---; cat .gm/exec-spool/.turn-summary.json 2>/dev/null; echo ---; date +%s%3N
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
`.turn-summary.json` carries `phase`, `last_skill`, `prd_pending`, `last_instruction_ts`, `last_instruction_age_ms`, `long_gap_threshold_ms`, `browser_sessions_alive`. When age exceeds the threshold, your next non-orienting verb will be gated — dispatch `instruction` first.
|
|
32
|
+
|
|
33
|
+
Compare `.status.json` `ts` field to the printed epoch ms. If the gap is >15000, the watcher is dead — boot it:
|
|
32
34
|
|
|
33
35
|
```bash
|
|
34
36
|
bun x gm-plugkit@latest spool > /dev/null 2>&1 &
|