gm-thebird 2.0.1069 → 2.0.1070
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/bin/bootstrap.js +33 -0
- package/bin/plugkit.js +29 -4
- package/gm.json +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/skills/code-search/SKILL.md +1 -1
- package/skills/gm-complete/SKILL.md +2 -2
- package/skills/gm-emit/SKILL.md +1 -1
- package/skills/gm-execute/SKILL.md +5 -5
- package/skills/planning/SKILL.md +2 -2
package/bin/bootstrap.js
CHANGED
|
@@ -461,6 +461,11 @@ async function bootstrap(opts) {
|
|
|
461
461
|
const okSentinel = path.join(verDir, '.ok');
|
|
462
462
|
const partialPath = `${finalPath}.partial`;
|
|
463
463
|
|
|
464
|
+
const wasmName = 'plugkit.wasm';
|
|
465
|
+
const wasmExpectedSha = shaManifest ? shaManifest[wasmName] : null;
|
|
466
|
+
const wasmFinalPath = path.join(verDir, wasmName);
|
|
467
|
+
const wasmPartialPath = `${wasmFinalPath}.partial`;
|
|
468
|
+
|
|
464
469
|
if (fs.existsSync(finalPath) && fs.existsSync(okSentinel)) {
|
|
465
470
|
if (expectedSha) {
|
|
466
471
|
const actualSha = sha256OfFileSync(finalPath);
|
|
@@ -570,6 +575,34 @@ async function bootstrap(opts) {
|
|
|
570
575
|
pruneOldVersions(root, version, readRtkVersion(wrapperDir));
|
|
571
576
|
spawnDetachedRtkFetch(wrapperDir);
|
|
572
577
|
copyToGmTools(finalPath, wrapperDir, version);
|
|
578
|
+
|
|
579
|
+
try {
|
|
580
|
+
if (healIfShaMatches(wasmFinalPath, wasmExpectedSha, path.join(verDir, '.wasm-ok'), wasmPartialPath, 'wasm')) {
|
|
581
|
+
obsEvent('bootstrap', 'wasm.heal', { path: wasmFinalPath });
|
|
582
|
+
} else {
|
|
583
|
+
const wasmUrl = `https://github.com/${RELEASE_REPO}/releases/download/v${version}/${wasmName}`;
|
|
584
|
+
try {
|
|
585
|
+
await downloadWithRetry(wasmUrl, wasmPartialPath);
|
|
586
|
+
if (wasmExpectedSha) {
|
|
587
|
+
const wasmGot = await sha256OfFile(wasmPartialPath);
|
|
588
|
+
if (wasmGot !== wasmExpectedSha) {
|
|
589
|
+
try { fs.unlinkSync(wasmPartialPath); } catch (_) {}
|
|
590
|
+
log(`wasm sha256 mismatch: expected ${wasmExpectedSha}, got ${wasmGot}`);
|
|
591
|
+
} else {
|
|
592
|
+
try { fs.renameSync(wasmPartialPath, wasmFinalPath); } catch (e) {
|
|
593
|
+
if (e.code === 'EEXIST') { try { fs.unlinkSync(wasmFinalPath); } catch (_) {} fs.renameSync(wasmPartialPath, wasmFinalPath); }
|
|
594
|
+
}
|
|
595
|
+
fs.writeFileSync(path.join(verDir, '.wasm-ok'), new Date().toISOString());
|
|
596
|
+
obsEvent('bootstrap', 'wasm.install', { path: wasmFinalPath });
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
} catch (err) {
|
|
600
|
+
log(`wasm download failed (non-fatal): ${err.message}`);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
try { fs.copyFileSync(wasmFinalPath, path.join(gmToolsDir(), wasmName)); } catch (_) {}
|
|
604
|
+
} catch (_) {}
|
|
605
|
+
|
|
573
606
|
clearBootstrapError();
|
|
574
607
|
return finalPath;
|
|
575
608
|
} finally {
|
package/bin/plugkit.js
CHANGED
|
@@ -81,13 +81,40 @@ function ensureReady(silent) {
|
|
|
81
81
|
return r.status === 0 && isReady();
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
function wasmPath() {
|
|
85
|
+
return path.join(wrapperDir, 'plugkit.wasm');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function shouldUseWasm() {
|
|
89
|
+
if (process.env.GM_USE_WASM === '1') return true;
|
|
90
|
+
if (fs.existsSync(wasmPath()) && !fs.existsSync(toolsBin())) return true;
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function runWasm(args) {
|
|
95
|
+
try {
|
|
96
|
+
const WasmHost = require('../lib/wasm-host');
|
|
97
|
+
const host = new WasmHost(wasmPath());
|
|
98
|
+
const verb = args[0] || 'health';
|
|
99
|
+
const body = args.slice(1).join(' ') || '{}';
|
|
100
|
+
const result = await host.dispatch(verb, body);
|
|
101
|
+
console.log(JSON.stringify(result, null, 2));
|
|
102
|
+
process.exit(result.ok ? 0 : 1);
|
|
103
|
+
} catch (err) {
|
|
104
|
+
console.error('[plugkit wasm]', err.message);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
84
109
|
function main() {
|
|
85
110
|
const args = process.argv.slice(2);
|
|
86
111
|
const isHook = args[0] === 'hook';
|
|
87
112
|
const hookSubcmd = isHook ? (args[1] || '') : '';
|
|
88
113
|
|
|
89
|
-
|
|
90
|
-
|
|
114
|
+
if (shouldUseWasm()) {
|
|
115
|
+
return runWasm(args);
|
|
116
|
+
}
|
|
117
|
+
|
|
91
118
|
const blocksUntilReady = hookSubcmd === 'session-start' || hookSubcmd === 'prompt-submit';
|
|
92
119
|
|
|
93
120
|
if (blocksUntilReady) {
|
|
@@ -96,8 +123,6 @@ function main() {
|
|
|
96
123
|
process.exit(1);
|
|
97
124
|
}
|
|
98
125
|
} else if (!fs.existsSync(toolsBin())) {
|
|
99
|
-
// For non-blocking hooks (pre-tool-use, post-tool-use, stop, etc.): if the
|
|
100
|
-
// binary doesn't exist yet, exit cleanly — session-start will populate it.
|
|
101
126
|
if (isHook) process.exit(0);
|
|
102
127
|
process.exit(1);
|
|
103
128
|
}
|
package/gm.json
CHANGED
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -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
|
|
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`.
|
|
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
|
-
|
|
84
|
+
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.
|
|
85
85
|
|
|
86
86
|
## Hygiene sweep
|
|
87
87
|
|
package/skills/gm-emit/SKILL.md
CHANGED
|
@@ -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`.
|
|
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
|
|
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
|
|
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 —
|
|
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
|
|
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
|
|
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.
|
package/skills/planning/SKILL.md
CHANGED
|
@@ -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.
|
|
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
|
|
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
|
|