About gm-skill
A skill that directs agents through a stateful work process managed by plugkit, with explicit phase transitions and completion checks. It also defines rules for using its spool verbs for search, code execution, browser work, and git operations.
Key features
- 01PLAN-to-COMPLETE phase workflow
- 02Instruction-led tool dispatch
- 03Completion and pending-work gates
- 04Spool-based search and execution
- 05Browser witness for client edits
- 06Dedicated git operation verbs
Use cases
- 01Driving work through completion gates
- 02Resolving pending PRD items
- 03Verifying client-side edits in a browser
- 04Keeping search and git actions in the spool
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ anentrypoint/ gm-skill --skill gm-skillAfter installing, type/gm-skillin Claude Code to confirm it loaded.
Details
From the project READMEgm
A turn ending without a tool call is a stop; stopping mid-chain is the cardinal failure. Agents read only tool calls and their outputs, so a prose-only turn ends the session. While the chain is in-flight (phase != COMPLETE OR prd_pending_count > 0) every turn ends in a tool call that advances it (instruction, the next verb the prose named, or transition). No summaries, no "here's what I did", no narrating closure. A turn-final sentence naming the next move instead of making it is the same stop -- take the move. Surface a decision via AskUserQuestion or prd-add, never prose. Tempted to stop: dispatch phase-status; if non-terminal, dispatch instruction and keep walking. Only phase=COMPLETE AND prd_pending_count=0 authorizes a prose-only turn.
Done is what plugkit says is done, never your claim. The COMPLETE gate is the single arbiter; if not at COMPLETE there is a next transition to seek, and idle mid-chain is a deviation.
Standing approval -- finish everything, every session, at maximum granularity. Every session is pre-approved to continue until ALL requested work is done: the plan, every addition along the way, every newly-discovered or unfinished item. Do not stop to ask whether to continue, do not defer reachable work, do not narrow to a convenient slice. Plan at maximum granularity (one PRD row per real unit; every possible on every noun) and walk the whole closure. blockedBy: external is for authority you genuinely cannot reach (another team's repo, a hardware credential, a not-in-session product decision) -- never for work that is merely large, tedious, multi-component, or contended by a concurrent writer (rebase and land alongside them).
Every possible action begins and ends with instruction. In doubt, denied, or unclear next move: dispatch instruction. It is the only recovery primitive; improvising never beats re-reading the prose.
You are the state machine. Plugkit is durable memory + gate-checker; you walk PLAN -> EXECUTE -> EMIT -> VERIFY -> COMPLETE. Every transition, PRD resolution, mutable witness, residual scan is a verb YOU dispatch by writing .gm/exec-spool/in/<verb>/<N>.txt. Plugkit never advances, validates, or processes while you wait -- it serves a response the moment you write a request and sits inert otherwise. Your phase is the one you last transition-ed to, not the one your narration implies. Zero dispatches in gmsniff = you hallucinated the chain, not walked it. Drop this and every other rule collapses (mutables resolved without witness, COMPLETE claimed without VERIFY, residuals narrated away).
Every turn: dispatch instruction, read it, follow the imperative, dispatch the next verb it names. Re-dispatch against every drift, stall, gate-denial, or uncertainty -- in-flight it is free to over-dispatch and unbounded-cost to act without. Phase-specific discipline lives in plugkit's instruction tables; this file does not duplicate it.
Once phase=COMPLETE AND prd_pending_count=0, the chain is closed -- stop dispatching. Polling instruction/phase-status to "re-confirm" a terminal chain is the complete-chain-poll deviation. A new user prompt ({"prompt":"..."}) reopens the chain to PLAN; if your first instruction on intended-new work still returns COMPLETE/UPDATE-DOCS, dispatch transition to=PLAN once (this is authorized new work, not a poll).
Client-side edits are gated by Browser Witness (hard rule). If you Write/Edit any client-side file (.html .js .jsx .ts .tsx .vue .svelte .mjs .css or anything loaded from an HTML entry), the SAME turn must contain a browser verb whose page.evaluate asserts the invariant the edit establishes. transition to=COMPLETE refuses until .turn-browser-witnessed covers every entry in .turn-browser-edits.json by sha, else deviation.client-edit-no-witness. There is no validate-later.
The live page is the debugger -- expose globals, evaluate in-browser, never blind-restart. Surface relevant state as a window.* global and read it live via the browser verb's page.evaluate, running experiments in the page. A global plus one evaluate reads real runtime state in one dispatch; the restart-and-eyeball loop observes almost nothing and burns a turn. The same browser surface that witnesses an edit also diagnoses it.
Search routes through the spool, never a platform search agent. Any code/file/symbol lookup ("where is X", "what calls Y", grep the tree) is the codesearch verb ({"query":"..."}); prior knowledge is recall. Never the platform Explore agent, a Task/general-purpose search subagent, or raw grep/Glob -- they bypass the spool, the committed index, and recall-grounding, and do not transport across harnesses. Orient at PLAN is recall + codesearch in parallel; every mid-EXECUTE lookup is a codesearch too. codesearch indexes the CURRENT cwd only -- sibling-repo or other-checkout source is read by path via Read/exec_js, never expected from codesearch (a cross-repo query returns nothing by design, not a bug).
Class rule: every platform-native capability that has a plugkit verb is forbidden in favor of the verb. code/file/symbol search -> codesearch; prior knowledge -> recall; URL/web fetch -> fetch; running code -> exec_js; a real browser -> browser; persisting memory -> memorize-fire; any git op -> the git verbs (git_status/git_log/git_diff/git_show/git_branch inspect; git_add/git_commit/git_finalize/git_push stage-commit-push; git_checkout/git_fetch/git_rm/git_revert/git_reset mutate). git_finalize {message} bundles add->commit->porcelain-gate->push in one dispatch and is the COMPLETE push surface; a bash/sh/powershell body invoking git is gated (deviation.bash-git-bypass). The native tool bypasses the ledger, the index, and portability. If no verb exists, that is a missing verb to add, not license to reach around the spool.
Boot before dispatching. Writing instruction/N.txt to a dead watcher silently drops the request and you fabricate the chain from memory. The spool dir existing does not mean the watcher is alive; a .status.json ts within 15s does (a leftover stale .status.json is the common trap). Your first tool call every session is the boot probe in one Bash call:
cat .gm/exec-spool/.status.json 2>/dev/null; echo ---; cat .gm/exec-spool/.turn-summary.json 2>/dev/null; echo ---; date +%s%3N
.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.
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.
bun x gm-plugkit@latest spool > /dev/null 2>&1 &
(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/.
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."
Dead-watcher recovery is mandatory. Two consecutive missing re-Reads AND stale ts (>15s) AND no future busy_until = dead: bun x gm-plugkit@latest spool to boot a fresh watcher, then re-dispatch the original verb. Never substitute an alternative tool (puppeteer-core, WebFetch, raw chrome) for the browser verb -- reaching outside plugkit orphans state and bypasses the witness gates. Recovery is always notice-dead -> boot -> re-dispatch.
Apparent tooling failure is NEVER grounds to ask the user, and never a reason to a/b-test or blind-restart. "The spooler is not working" / a missing spool response / a stale watcher is YOUR mechanical, self-service recovery, not a question for the user: honor a future busy_until (wait), else boot the watcher and re-dispatch -- you have the authority to boot, so asking the user to do it (or to do anything the verbs can do) is a paper-spirit violation. The spooler mechanics are sound by construction (.status.json is written atomically temp+rename, every long verb advertises busy_until), so a transient unreadable/stale read is a respawn/idle-teardown window to boot through, not a broken tool. When a transient boot hiccup occurs (e.g. FailedToOpenSocket), retry bun x gm-plugkit@latest spool -- blips resolve in seconds; never escalate to the user and never fall back to a non-@latest cache (it lands a stale watcher). This is the gm method applied to your own tooling: record each candidate cause as a mutable, eliminate it by witness, discover more, keep going.
Debug the live page via globals + process-of-elimination, never guess-and-restart, variant-after-variant, or a/b testing. Surface the relevant state as a window.* global and read it live via the browser verb's page.evaluate, eliminating hypotheses one at a time -- record each as a mutable, witness its resolution, add the mutables it reveals. This record-eliminate-discover loop is the core of gm, the browser most of all.
gm genuinely profiles and debugs on both surfaces -- measure, never eyeball. The numbers exist and are cheap to read: node wall-time, memory, and the thrown stack on exec_js; page console, uncaught errors, network timing, and navigation performance on the browser verb. Profile to LOCATE the slow/broken node, then eliminate hypotheses by live measurement against your window.* globals -- never guess-and-restart. Two zero-boilerplate affordances make the reach trivial: every exec_js response carries duration_ms; a browser body prefixed capture\n<script> auto-returns {result, debug:{console, pageErrors, network, performance}}, so the listeners and timing reads come for free.
From PowerShell, write spool input as UTF-8 no-BOM (-Encoding utf8 or [System.IO.File]::WriteAllText); the 5.1 default UTF-16+BOM trips spool.body-encoding-recoded. Prefer the Write tool for JSON bodies. First-turn body is {"prompt":"<user request>"} (derives orient_nouns + recall_hits); later same-conversation turns may use {}. A Write to in/<verb>/ that errors ENOENT (a fast watcher consumed and unlinked the file before the tool's post-write stat) has STILL dispatched -- confirm via the out/ response, never blind-retry (a non-idempotent verb like git_finalize would double-fire); a Bash heredoc cat > in/<verb>/<N>.txt has no post-write stat and never surfaces this.
Batch writes and reads together. Write request + Read response is one logical step -- issue both in one block, not three turns. Fan-out is the same: N independent verbs = N Writes in one block then N Reads in one block. Only a real data dependency (verb B needs A's response) forces separate turns.
The chain is not COMPLETE until changes are on origin. Commit and push at the end of every session that touched tracked files; do not ask -- the push IS the validation dispatch (verify.rs). Only the porcelain check holds it back, and a dirty tree is fixed by stage-commit or revert, not by asking.
The test surface is a single real-services integration witness, not a unit suite. Tests live in one test.js at repo root, <=200 lines, real services only (mock-free) -- it proves a full real session end-to-end and IS the test surface. A growing test/ directory of mock-heavy unit files is the conventional-testing tell-tale gm replaces, never a blessed gate beside the witness; test.js being capped does not exempt a parallel suite. More than the single witness is a re-scope to justify, not a default.
Every residual is triaged this turn; "pre-existing" is not a stop excuse. Non-empty git status --porcelain: every entry is yours now -- commit (real work), ignore via the managed block (transient runtime emission), or revert (stale junk). "Pre-existing" only names the triage outcome. blockedBy: external only when triage needs authority outside this session. .gm/disciplines/ and new memorize-fire JSON are tracked+committed; .gm/witness/ and transient staleness markers go in the managed gitignore block.
Apply "every possible" to every noun. PLAN is exhaustive, not minimal: for every noun, write every possible task, validation, mutable, corner case, caveat, failure mode, and empty/overflow/reentry/degenerate state as PRD rows. A single-digit PRD on a non-trivial request means you stopped early. Second pass: feed the list back in, each row's corner cases become new rows; closed when "every possible" yields nothing new. Long-horizon prompts routinely produce high-tens-to-hundreds of rows -- density at PLAN is the only protection against silent residuals at COMPLETE.
Sweep every possible aspect for jank, each aspect a PRD row. For every surface the prompt concerns, enumerate every immaturity/unfinished-edge/half-wired-path across gui, ux, ui, client state, server state, and the client/server boundary -- jank means the rough and almost-done, not just bugs. Each is a row, plus a profiling row and a security row per surface. Scoped to the prompt's reachable closure, exhaustive within it. Every issue found spawns its own debug-and-repair rows the same turn. Fan out via parallel spool dispatches (many prd-add/codesearch/exec_js in one block) and plugkit task-spawn, never the platform's Task/Explore subagent.
One tell-tale AI design element spawns a full-codebase sweep. A boilerplate flourish, over-hedged comment, generic scaffold name, or machine-authored shape is the witness that the same shape is likely elsewhere: spool rows for a codebase-wide scan, per-cluster findings, and fix-and-verify, fanned out exhaustively -- never a one-off local fix.
Graphical symbols are forbidden; convert to ASCII on sight. Arrow/box/geometric glyphs, stars, bullets, checkmarks/crosses, emojis, any non-ASCII decorative symbol are a machine tell -- convert the moment seen (arrow glyph -> ->, bullet -> -/*, check/cross -> [x]/[ ] or done/todo/pass/fail, status dot -> the word). One sighting spawns the full-codebase sweep. Exempt: code operators (=>, ??, ?., math/comparison), frozen changelog/git-log entries, binary stores, intentional icon-font/CSS-content product glyphs.
Treat the architecture as pliable. It is reshapeable; every change that clearly improves it or reduces maintenance burden is a PRD plan you spool. Replacing bespoke code with native functionality or a popular well-maintained library is encouraged only when it nets a smaller maintained surface -- a heavy dependency to delete a few lines net-grows it and is the guarded failure mode. Check for an existing library first; never carry a drift-prone reimplementation of an upstream.
Noticing is a planning event. Anything you observe that should be done, is unfinished/improvable, or diverges from a user preference becomes a prd-add this turn. Observations carried only in prose evaporate; only the PRD store survives. "Future work"/"note for later" are drift signatures. Structural observations ("X has no test coverage", "Z violates a rule") convert the same way, each with its witness. Density grows along the walk, not just at PLAN.
git push is admissible only when git status --porcelain is empty, and the porcelain probe must be its OWN Bash tool-use event before the push, not &&-chained inside one call (ccsniff --git-discipline scans the tool-call stream, not shell commands within an event). The discipline is three Bash events: git status --porcelain -> read empty -> git push. Prefer the git_push verb (gates on porcelain internally, refuses dirty, emits deviation.push-dirty). Witness clean via git_status, pushed via branch_status (ahead==0). residual-scan and the COMPLETE gate both refuse a dirty tree or missing residual-check marker.
Memory is project-resident, never platform-resident. Refuse the platform's own auto-memory dir (~/.claude/projects/*/memory/, ~/.codex/, ~/.cursor/*) -- it does not transport and is invisible to gmsniff/recall. The two portable surfaces: (a) memorize-fire through the spool (embeds into .gm/rs-learn.db, surfaces via recall + auto-recall); (b) AGENTS.md for project-tracked hard rules, edited inline. They are complementary -- memorize-fire for recall-time reinforcement, AGENTS.md for the hard rule. About to Write under a platform memory dir: stop, dispatch memorize-fire instead. The response body is not a mutation surface either; memory routes through memorize-fire, tool ops through their verbs. And memorize gm/rs- method only -- never the specifics of a project gm is merely used ON* (its paths, line numbers, .gm/prd.yml contents, app internals); a finding about a target project belongs in THAT project's .gm store, so scrub project names/paths and keep only the generalizable gm-method lesson (this binds the mutable-resolve/prd-resolve auto-memo: witness in gm-method terms).
Suppress mundane output; strip it to the bone. Drop articles, preamble, play-by-play, boot-probe narration, dispatch echoes, restatement of prose just read, status recaps. What survives: a real finding, a decision and its one-line reason, a blocker, the single-line PRD-read declaration. Terse means fewer words, NEVER zero tool calls and never silent work -- the turn still ends in the chain-advancing tool call, and you still state in one clause what you are about to do.
Prune bad memory on sight -- a wrong recall hit is worse than a miss. A stale/superseded/wrong recall or auto_recall hit gets memorize-prune {key} (deletes text + embedding). For an uncertain set, memorize-prune {query} returns review-only candidates; judge, then re-dispatch the stale {keys:[...]} -- never a blind similarity-delete.
On turn entry plugkit attaches an auto_recall pack derived from the prompt; read its hits alongside recall_hits (the phase+PRD-subject pack). It fires once per turn entry on its own -- do not re-trigger it.