gm-plugkit 2.0.1493 → 2.0.1495
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/SKILL.md +7 -5
- package/bootstrap.js +1 -1
- package/package.json +1 -1
- package/plugkit-wasm-wrapper.js +6 -6
package/SKILL.md
CHANGED
|
@@ -14,7 +14,7 @@ allowed-tools: Skill, Read, Write, Bash(bun *), Bash(npx *)
|
|
|
14
14
|
|
|
15
15
|
**Every possible action begins and ends with `instruction`.** When in doubt, dispatch instruction. When denied, dispatch instruction. When the next move is unclear, dispatch instruction. There is no other recovery primitive and there is no situation in which improvising beats re-reading the prose.
|
|
16
16
|
|
|
17
|
-
**You are the state machine.** Plugkit is the durable memory and gate-checker you write into; you are the actor that walks PLAN
|
|
17
|
+
**You are the state machine.** Plugkit is the durable memory and gate-checker you write into; you are the actor that walks PLAN -> EXECUTE -> EMIT -> VERIFY -> COMPLETE. Every possible phase transition, every possible PRD resolution, every possible mutable witness, every possible residual scan is a verb YOU dispatch by writing `.gm/exec-spool/in/<verb>/<N>.txt`. Plugkit does not advance. Plugkit does not validate in the background. Plugkit does not "process" while you wait. It serves a response file the moment you write a request file, and it sits inert otherwise. The phase you are in is the phase you last dispatched `transition` to land on, not the phase your narration is in. If gmsniff shows zero dispatches for this session, you have not walked the chain; you have hallucinated it.
|
|
18
18
|
|
|
19
19
|
This is the only thing that makes the discipline work. Drop this and every possible other rule collapses: mutables get resolved without witness, COMPLETE gets claimed without VERIFY, residuals get narrated away instead of scanned, and the chain becomes a story you tell instead of work you ship.
|
|
20
20
|
|
|
@@ -28,7 +28,7 @@ Every turn: dispatch `instruction` (you are the one dispatching it), read the re
|
|
|
28
28
|
|
|
29
29
|
**Search routes through the spool, never a platform search agent.** For any code, file, or symbol search — whereabouts, "where is X defined", "what calls Y", grepping the tree — you dispatch the `codesearch` verb (`.gm/exec-spool/in/codesearch/<N>.txt` with `{"query":"..."}`), and for prior-knowledge you dispatch `recall`. You do NOT reach for the platform's Explore agent, a Task/general-purpose search subagent, raw `grep`/`Glob`, or any host-native code-search; those are not substitutes for `codesearch`, exactly as puppeteer is not a substitute for the `browser` verb. They bypass the spool, the committed code-search index, and the recall-grounded discipline — the search becomes invisible to gmsniff, ungrounded in what the project already learned, and non-portable across harnesses. The orient fan-out at PLAN is `recall` + `codesearch` in parallel; every ad-hoc lookup mid-EXECUTE is a `codesearch` dispatch too. Reaching outside the spool for search is the same drift as reaching outside it for the browser: the capability exists as a verb, so you use the verb.
|
|
30
30
|
|
|
31
|
-
**This is one instance of a class rule: every platform-native capability that has a plugkit verb is forbidden in favor of the verb.** Your `allowed-tools` already blocks raw shell beyond the boot commands, but a harness can still offer the capability as its own first-class tool or subagent that slips past that restriction — a search/Explore agent, a web-fetch or web-search tool, a plan/architect subagent, a notebook editor. For each, the plugkit verb is the only admissible surface: code/file/symbol search
|
|
31
|
+
**This is one instance of a class rule: every platform-native capability that has a plugkit verb is forbidden in favor of the verb.** Your `allowed-tools` already blocks raw shell beyond the boot commands, but a harness can still offer the capability as its own first-class tool or subagent that slips past that restriction — a search/Explore agent, a web-fetch or web-search tool, a plan/architect subagent, a notebook editor. For each, the plugkit verb is the only admissible surface: code/file/symbol search -> `codesearch`; prior knowledge -> `recall`; fetching a URL or searching the web -> the `fetch` verb (`.gm/exec-spool/in/fetch/`); running code -> `exec_js` / the exec spool; a real browser -> the `browser` verb; persisting memory -> `memorize-fire`; **any git operation -> the git verbs** (`git_status`/`git_log`/`git_diff`/`git_show`/`git_branch` to inspect, `git_add`/`git_commit`/`git_finalize`/`git_push` to stage-commit-push, `git_checkout`/`git_fetch`/`git_rm`/`git_revert`/`git_reset` to mutate) — `git_finalize {message}` bundles add->commit->porcelain-gate->push in ONE dispatch and is the COMPLETE-phase push surface, so you never shell `git` via Bash and never spend 4 tool-use events on what is one verb; a `bash`/`sh`/`powershell` body that invokes git is gated (`deviation.bash-git-bypass`). The native tool is never the substitute, for the same three reasons every time: it bypasses the spool (invisible to the ledger), it bypasses the project's committed index and learned memory (ungrounded), and it is non-portable across harnesses (a different agent host has a different native tool, so a discipline built on the native tool does not transport — only the verb does). When you reach for any capability, the question is not "what tool does my platform give me" but "what verb does plugkit expose for this"; if a verb exists, the native tool is off-limits, and if no verb exists the gap is a missing verb to add, not a license to reach around the spool.
|
|
32
32
|
|
|
33
33
|
**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.
|
|
34
34
|
|
|
@@ -52,13 +52,13 @@ bun x gm-plugkit@latest spool > /dev/null 2>&1 &
|
|
|
52
52
|
|
|
53
53
|
Never poll the spool dir with `sleep && ls` or `Start-Sleep && Test-Path`, plugkit is synchronous from your view; if the response is not there, the watcher is dead (re-check `.status.json` mtime) or the verb is slow (check `.gm/exec-spool/.watcher.log`), not "still processing."
|
|
54
54
|
|
|
55
|
-
**Dead-watcher recovery is mandatory, never abandon the dispatch.** If two consecutive re-Reads return "file does not exist" AND `.status.json` ts is stale (>15s gap from current epoch) AND `busy_until` is absent or in the past, the watcher is dead. (A future `busy_until` means a long synchronous verb is running, the response will land when it finishes; wait, do not boot.) Your next call is `bun x gm-plugkit@latest spool` to boot a fresh watcher (the wrapper has self-respawn paths now, one boot deploys every queued fix to disk). Then re-dispatch the original verb. Do NOT reach for an alternative tool, puppeteer-core, agent-browser, WebFetch, raw `chrome.exe`, none of these substitute for the `browser` verb. Reaching outside plugkit when the spool surface is reachable orphans state the next session cannot reap, bypasses paper §23 witness gates, and ages the project's discipline. The recovery is always: notice dead
|
|
55
|
+
**Dead-watcher recovery is mandatory, never abandon the dispatch.** If two consecutive re-Reads return "file does not exist" AND `.status.json` ts is stale (>15s gap from current epoch) AND `busy_until` is absent or in the past, the watcher is dead. (A future `busy_until` means a long synchronous verb is running, the response will land when it finishes; wait, do not boot.) Your next call is `bun x gm-plugkit@latest spool` to boot a fresh watcher (the wrapper has self-respawn paths now, one boot deploys every queued fix to disk). Then re-dispatch the original verb. Do NOT reach for an alternative tool, puppeteer-core, agent-browser, WebFetch, raw `chrome.exe`, none of these substitute for the `browser` verb. Reaching outside plugkit when the spool surface is reachable orphans state the next session cannot reap, bypasses paper §23 witness gates, and ages the project's discipline. The recovery is always: notice dead -> boot -> re-dispatch. The full chain from spool-write to disk-Read-success is the only admissible loop; any short-circuit produces unreconcilable state.
|
|
56
56
|
|
|
57
57
|
When writing the spool input from PowerShell, pass `-Encoding utf8` (or use `[System.IO.File]::WriteAllText($path, $body)` which defaults to UTF-8 no-BOM). PowerShell 5.1's default `Out-File` / `Set-Content` write UTF-16 LE with BOM, which the watcher detects and re-decodes (`spool.body-encoding-recoded` event in gmsniff), but the deviation is a fingerprint of an instruction you missed. Use `bash -c "echo -n '...' > ..."` or `Write` tool instead when the body is structured JSON.
|
|
58
58
|
|
|
59
59
|
First turn body must be `{"prompt":"<user request>"}` so orient_nouns and recall_hits derive from the request; subsequent turns in the same conversation may use empty body `{}`.
|
|
60
60
|
|
|
61
|
-
**Batch writes, waits, and reads together.** Each agent turn costs cycles; the dispatch shape `Write request
|
|
61
|
+
**Batch writes, waits, and reads together.** Each agent turn costs cycles; the dispatch shape `Write request -> wait -> Read response` is one logical step, not three. Issue all three in a single message, the Write tool call and the Read tool call go in the same `<function_calls>` block. The Read may return "file does not exist" if plugkit is mid-verb; that's fine, retry with one more Read in the next message rather than spreading the cycle across three turns. Fan-out is the same shape, dispatching three independent verbs (`prd-add g1`, `prd-add g2`, `prd-add g3`) means three Write tool calls in one block, then three Read tool calls in one block. Serial dispatch when you could be parallel is wasted cycles. The only sequencing constraint is real data dependency: if verb B needs the response of verb A, those go in separate turns; otherwise batch.
|
|
62
62
|
|
|
63
63
|
The chain is not COMPLETE until your changes are on origin. Commit and push at the end of every possible session that touched tracked files, you do not ask the user whether to push, you dispatch it. Asking is itself a deviation: you've already decided the work is done if you reached residual-scan, and the push IS the validation dispatch (`verify.rs`). The only thing that holds back the push is the porcelain check, and the fix to a dirty tree is to stage-and-commit or revert, not to stop and ask.
|
|
64
64
|
|
|
@@ -70,11 +70,13 @@ The chain is not COMPLETE until your changes are on origin. Commit and push at t
|
|
|
70
70
|
|
|
71
71
|
**One tell-tale AI design element spawns a full-codebase sweep.** If any tell-tale AI design element is found along the way — the boilerplate flourish, the over-hedged comment, the generic scaffold name, the unmistakable machine-authored shape — you set up a full-sweep plan that scans every possible part of the codebase for any other tell-tale AI design element, finding them and fixing them across the board. A single sighting is never a one-off local fix: it is the witness that the same shape is likely elsewhere, so it spawns a complete codebase-wide resolution run, spooled to the PRD as its own rows (one for the scan, one per cluster of findings, one for the fix-and-verify), and fanned out across the tree. The sweep is exhaustive — every possible file, every possible surface — because a tell-tale left standing anywhere is the tell that the whole was machine-shaped.
|
|
72
72
|
|
|
73
|
+
**Graphical symbols are forbidden; convert them to industry-standard text on sight.** Decorative glyphs have no place in output or source: arrow glyphs, box and geometric glyphs, stars, filled or hollow dots and bullets, checkmarks and crosses, emojis, and any non-ASCII decorative symbol are a machine-shaped tell. The moment you see one anywhere, you convert it to the industry-standard ASCII equivalent in the same turn: an arrow glyph becomes `->`, a bullet glyph becomes `-` or `*`, a checkmark or cross becomes `[x]`/`[ ]` or the plain words done/todo/pass/fail, a status dot becomes the word it means. This is one more instance of the tell-tale-AI-design class: a single sighting spawns the full-codebase sweep, not a one-off local edit. The exemptions are narrow and concrete: functional code operators (`=>`, `??`, `?.`, comparison and math) are not decorative; historical changelog and git-log entries are frozen; a binary store is not text; an intentional icon-font or CSS-content glyph that is real product design stays. Everything else converts the instant it is found.
|
|
74
|
+
|
|
73
75
|
**Treat the architecture as pliable.** `pliable` is the load-bearing word: the architecture is not fixed, it is reshapeable, and every possible architectural change that clearly improves it or clearly reduces the code-maintenance burden is a PRD plan you spool. Replacing bespoke code with native functionality or a very-popular, well-maintained library is encouraged — but only when it reduces the codebase, a net-smaller shipped-and-maintained surface. Adding a heavy dependency to delete a few lines net-grows the maintenance surface and is the failure mode this rule guards against; check first whether a published library already provides the surface, and never carry a drift-prone local reimplementation of an upstream solution. You make every improvement that is clearly outstanding.
|
|
74
76
|
|
|
75
77
|
**Noticing is a planning event.** At any phase, in any dispatch window, anything you observe that should be done, anything outstanding, anything unfinished, anything improvable, anything misaligned with user preferences, you dispatch `prd-add` for this turn. Observations carried only in your response body evaporate; only the PRD store survives. The default response to noticing is to convert. "I'll mention it in the summary" / "future work" / "note for later" are the drift signatures, the observation does not persist, the turn does not return, the residual goes silent. Density grows along the walk, not just at PLAN-time. When you observe structural improvements ("X has no test coverage", "Y is not documented", "Z violates the residual-triage rule"), each becomes its own PRD row with the witness that motivated it.
|
|
76
78
|
|
|
77
|
-
`git push` is admissible only when `git status --porcelain` reads empty, and the porcelain probe must be its own Bash **tool-use event** before the push, a separate `Bash(...)` invocation, not a `&&`-chained command within one Bash call. ccsniff `--git-discipline` scans the last 20 Bash tool-use events (not shell commands within those events) for an explicit `git status --porcelain` (or `-s`); putting `add && commit && push` into one Bash call counts as one event with no porcelain witness, even though `git push` is technically the third shell command. The witness lives in the tool-call stream, not the shell stream. The discipline is **three Bash tool-use events** visible in the transcript: `Bash(git status --porcelain)`
|
|
79
|
+
`git push` is admissible only when `git status --porcelain` reads empty, and the porcelain probe must be its own Bash **tool-use event** before the push, a separate `Bash(...)` invocation, not a `&&`-chained command within one Bash call. ccsniff `--git-discipline` scans the last 20 Bash tool-use events (not shell commands within those events) for an explicit `git status --porcelain` (or `-s`); putting `add && commit && push` into one Bash call counts as one event with no porcelain witness, even though `git push` is technically the third shell command. The witness lives in the tool-call stream, not the shell stream. The discipline is **three Bash tool-use events** visible in the transcript: `Bash(git status --porcelain)` -> read empty -> `Bash(git push)`. You dispatch the `git_push` verb (not raw Bash) when possible, it gates on the porcelain probe internally, refuses dirty, and emits `deviation.push-dirty`. A raw `git push` Bash event without a preceding porcelain-probe Bash event is itself a deviation, regardless of how clean the worktree is by construction. Witness clean via `git_status`; witness pushed-to-remote via `branch_status` (ahead==0). The residual-scan and COMPLETE gate both refuse a dirty tree or a missing residual-check marker.
|
|
78
80
|
|
|
79
81
|
**Memory is project-resident, never platform-resident.** The agent platform may advertise its own auto-memory directory (`~/.claude/projects/*/memory/`, `~/.codex/memory/`, `~/.cursor/*`, etc.) and prompt you to write facts there. **Refuse**. Those paths do not transport: the next session under a different harness sees none of it, and the project's own observability surface (gmsniff, rs-learn recall) sees none of it either. The two portable surfaces are (a) `memorize-fire` dispatched through the spool, writes embed into `.gm/rs-learn.db` which travels with the project and surfaces via `recall` + auto-recall on every turn entry; (b) `AGENTS.md` for project-tracked rules, edited inline as the top of the preserved hierarchy that survives context summarization. memorize-fire and inline-AGENTS.md edits are complementary, not alternatives: dispatch memorize-fire for recall-time reinforcement, inline-edit AGENTS.md for the hard rule. If you find yourself about to `Write` into a path under `~/.claude/`, `~/.codex/`, `~/.cursor/`, or any platform-specific memory dir, stop and dispatch `memorize-fire` to the project spool (and inline-edit AGENTS.md if the fact is structural). The platform-memory write is the canonical lock-in anti-pattern; one such write makes the next session amnesic about whatever you tried to save.
|
|
80
82
|
|
package/bootstrap.js
CHANGED
|
@@ -237,7 +237,7 @@ async function extractNpmPackageWasm(destPath, version) {
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
fs.copyFileSync(nodeModulesPath, destPath);
|
|
240
|
-
log(`extracted ${nodeModulesPath}
|
|
240
|
+
log(`extracted ${nodeModulesPath} -> ${destPath}`);
|
|
241
241
|
obsEvent('bootstrap', 'npm.extract.end', { dur_ms: Date.now() - startMs, ok: true });
|
|
242
242
|
} finally {
|
|
243
243
|
try { fs.rmSync(tempDir, { recursive: true, force: true, maxRetries: 1, retryDelay: 50 }); } catch (_) {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-plugkit",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1495",
|
|
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/plugkit-wasm-wrapper.js
CHANGED
|
@@ -2279,7 +2279,7 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
2279
2279
|
if (!_selfStaleLoggedOnce) {
|
|
2280
2280
|
_selfStaleLoggedOnce = true;
|
|
2281
2281
|
try { logEvent('plugkit', 'gm-plugkit.self-stale', { running_version: own, latest_version: latest, detected_by: 'watcher-periodic-probe' }); } catch (_) {}
|
|
2282
|
-
console.error(`[plugkit-wasm] gm-plugkit self-stale: running ${own}, latest npm ${latest}
|
|
2282
|
+
console.error(`[plugkit-wasm] gm-plugkit self-stale: running ${own}, latest npm ${latest} -> spawning replacement via bun x gm-plugkit@latest spool and exiting`);
|
|
2283
2283
|
try {
|
|
2284
2284
|
const cp = require('child_process');
|
|
2285
2285
|
const bunPath = process.env.GM_BUN_PATH || 'bun';
|
|
@@ -2402,7 +2402,7 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
2402
2402
|
file_version: fileV,
|
|
2403
2403
|
action: 'exit-for-respawn',
|
|
2404
2404
|
});
|
|
2405
|
-
console.error(`[plugkit-wasm] version drift detected: instance=${instV} file=${fileV}
|
|
2405
|
+
console.error(`[plugkit-wasm] version drift detected: instance=${instV} file=${fileV} -> exiting so supervisor reloads fresh wasm`);
|
|
2406
2406
|
try {
|
|
2407
2407
|
fs.writeFileSync(path.join(spoolDir, '.shutdown-reason.json'), JSON.stringify({
|
|
2408
2408
|
reason: 'version-change',
|
|
@@ -2485,7 +2485,7 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
2485
2485
|
file_sha: cur.slice(0, 12),
|
|
2486
2486
|
action: 'exit-for-respawn',
|
|
2487
2487
|
});
|
|
2488
|
-
console.error(`[plugkit-wasm] wrapper.js drift detected
|
|
2488
|
+
console.error(`[plugkit-wasm] wrapper.js drift detected -> exiting so supervisor reloads fresh wrapper`);
|
|
2489
2489
|
try {
|
|
2490
2490
|
fs.writeFileSync(path.join(spoolDir, '.shutdown-reason.json'), JSON.stringify({
|
|
2491
2491
|
reason: 'wrapper-change',
|
|
@@ -2793,7 +2793,7 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
2793
2793
|
const bodyBytes = new TextEncoder().encode(body);
|
|
2794
2794
|
|
|
2795
2795
|
const t0 = Date.now();
|
|
2796
|
-
console.log(`[dispatch]
|
|
2796
|
+
console.log(`[dispatch] -> verb=${verb} task=${taskBase} body=${bodyBytes.length}b`);
|
|
2797
2797
|
logEvent('plugkit', 'dispatch.start', { verb, task: taskBase, body_bytes: bodyBytes.length, cwd: process.cwd() });
|
|
2798
2798
|
|
|
2799
2799
|
// Network-bound git verbs block the event loop for the duration of a push/fetch (~30s),
|
|
@@ -2852,7 +2852,7 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
2852
2852
|
const outName = dir === '.' ? `${taskBase}.json` : `${verb}-${taskBase}.json`;
|
|
2853
2853
|
fs.writeFileSync(path.join(outDir, outName), resultStr);
|
|
2854
2854
|
const dur_ms = Date.now() - t0;
|
|
2855
|
-
console.log(`[dispatch]
|
|
2855
|
+
console.log(`[dispatch] <- verb=${verb} task=${taskBase} ms=${dur_ms} out=${resultStr.length}b`);
|
|
2856
2856
|
logEvent('plugkit', 'dispatch.end', { verb, task: taskBase, dur_ms, out_bytes: resultStr.length });
|
|
2857
2857
|
emitOrchestratorEvents(verb, taskBase, resultStr);
|
|
2858
2858
|
|
|
@@ -3225,7 +3225,7 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
3225
3225
|
instruction: 'plugkit is out of date. Update with: bun x gm-plugkit@latest --kill-stale-watchers; bun x gm-plugkit@latest spool. A fresh boot downloads the new wasm and respawns; an in-place running watcher does not self-download.',
|
|
3226
3226
|
update_url,
|
|
3227
3227
|
}, null, 2));
|
|
3228
|
-
console.log(`[update] available: installed=${installed} latest=${latest}
|
|
3228
|
+
console.log(`[update] available: installed=${installed} latest=${latest} -> wrote ${UPDATE_AVAILABLE_PATH}`);
|
|
3229
3229
|
if (_lastKnownDrift !== latest) {
|
|
3230
3230
|
logEvent('plugkit', 'update.available', { installed, latest, update_url });
|
|
3231
3231
|
_lastKnownDrift = latest;
|