gm-skill 2.0.1547 → 2.0.1548

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/AGENTS.md CHANGED
@@ -140,9 +140,7 @@ Every possible skill's `allowed-tools:` frontmatter is reduced to `Skill, Read,
140
140
 
141
141
  **rs-learn observability**: every learning-pipeline state change emits a structured `evt:{event,sess,ts,...}` line into `.gm/exec-spool/.watcher.log` + gm-log; recall replies carry `mode`/`namespace`/`derived_query`/per-hit `score`; gmsniff/ccsniff expose the taxonomy. Learning quality is observable, not a black box. Full event taxonomy + flag list in rs-learn (`recall: rs-learn observability taxonomy`).
142
142
 
143
- **SKILL.md auto-refresh**: `bootstrapPlugkit` sha256-compares the bundled SKILL.md against installed copies and atomically rewrites on mismatch, so the agent always sees the latest prose. Mechanism detail in rs-learn (`recall: SKILL.md auto-refresh`).
144
-
145
- **Skill-initiated bootstrap contract**: `lib/skill-bootstrap.js::bootstrapPlugkit(sessionId)` initializes wasm for skill-driven dispatch without hooks; failures are non-fatal (degraded fallback). Mechanism detail in rs-learn (`recall: skill-initiated bootstrap contract`).
143
+ **Bootstrap contract (skill-init + SKILL.md auto-refresh + project wiring)**: `bootstrapPlugkit`/`ensureReady` initialize wasm hook-free (failures non-fatal), sha256-rewrite stale installed SKILL.md, and seed per-project `CLAUDE.md` (`@AGENTS.md`) + `.gm/next-step.md` the wiring must live in `gm-plugkit/bootstrap.js::ensureReady` (the consumer-project watcher boot path), not only repo-root `bin`/`lib`. Detail in rs-learn (`recall: skill-initiated bootstrap contract`, `recall: SKILL.md auto-refresh`).
146
144
 
147
145
  ## Cascade pipeline
148
146
 
@@ -166,7 +164,7 @@ Orchestration state is tracked via `.gm/` marker files, not hook events; the CLI
166
164
 
167
165
  **Dead-watcher recovery uses `bun x gm-plugkit@latest spool`, never direct-node boot** (mechanism + wrapper-deploy verification in rs-learn: `recall: dead-watcher recovery bun x not direct-node`).
168
166
 
169
- **The first verb after a genuine multi-minute IDLE is `instruction`, to reset the long-gap clock** (mechanism in rs-learn: `recall: first verb after multi-minute wait instruction long-gap`). The gate fires on genuine idle only -- both >300s since the last instruction AND >300s since the previous dispatch of any verb -- so active back-to-back work verbs (a browser-heavy debugging stretch, a fan-out of exec/codesearch) keep the chain alive without an interleaved `instruction`; you do not inject defensive instruction dispatches between active work. A true wait (version download, overnight, long external CI watch with no dispatches) still trips it, and there the first verb back is `instruction`.
167
+ **The first verb after a genuine multi-minute IDLE is `instruction`, to reset the long-gap clock** (mechanism in rs-learn: `recall: first verb after multi-minute wait instruction long-gap`). The gate fires on genuine idle only -- both >300s since the last instruction AND >300s since the previous dispatch of any verb -- so active back-to-back work verbs (a browser-heavy debugging stretch, a fan-out of exec/codesearch) keep the chain alive without an interleaved `instruction`; you do not inject defensive instruction dispatches between active work. A true wait (version download, overnight, long external CI watch with no dispatches) still trips it, and there the first verb back is `instruction`. When the wait is self-inflicted and predictable -- a blocking `TaskOutput`/`gh run watch` on a multi-minute workflow or CI run you yourself launched -- dispatch `instruction` immediately BEFORE entering the wait, not only after: prevention resets the clock so a sub-300s tail never trips the gate, where recovery-after only cleans up a deviation that already fired.
170
168
 
171
169
  **A stop-hook firing on a terminal chain does not authorize re-polling**: when a stop-hook or unsatisfiable condition fires while the chain is already at `phase=COMPLETE` AND `prd_pending_count=0`, re-dispatching `instruction` or `phase-status` to "re-confirm" terminality is itself a deviation, it emits `deviation.complete-chain-poll` (`instructions/mod.rs`) and marks the agent as polling a closed chain. COMPLETE already authorizes the prose-only turn; the hook cannot be satisfied by more poll dispatches over elapsed work, and re-running already-committed work to manufacture skill-driven activity is the fabrication `Nothing Fake` forbids. Two admissible responses only: (a) a prose-only turn (the COMPLETE pronouncement is in hand), or (b) genuinely new planned work opened with a FRESH `{"prompt":...}` body, which resets phase to PLAN and is driven through the skill from inception. Repeatedly answering the same already-acknowledged hook is a loop; state the terminal facts once and stop, or open new work.
172
170
 
@@ -74,6 +74,55 @@ function clearBootstrapError() {
74
74
  } catch (_) {}
75
75
  }
76
76
 
77
+ function ensureNextStepWiring(cwd) {
78
+ const changes = [];
79
+ const gmDir = path.join(cwd, '.gm');
80
+ try { fs.mkdirSync(gmDir, { recursive: true }); }
81
+ catch (e) { obsEvent('bootstrap', 'next-step.wiring.target-failed', { target: gmDir, error: e.message }); }
82
+
83
+ const nextStepPath = path.join(gmDir, 'next-step.md');
84
+ const nextStepBody = '# Next step\n\nDispatch the `instruction` verb to plugkit by writing `.gm/exec-spool/in/instruction/<N>.txt` (any unique N) with body `{}` (or `{"prompt":"<user request>"}` on the first dispatch of the turn). Read the response from `.gm/exec-spool/out/<N>.json` and follow the imperative in the `instruction` field.\n\nThis file is auto-rewritten by plugkit on every instruction dispatch.\n';
85
+ try {
86
+ if (!fs.existsSync(nextStepPath)) {
87
+ fs.writeFileSync(nextStepPath, nextStepBody);
88
+ changes.push('seeded .gm/next-step.md');
89
+ }
90
+ } catch (e) { obsEvent('bootstrap', 'next-step.wiring.target-failed', { target: nextStepPath, error: e.message }); }
91
+
92
+ const claudeMdPath = path.join(cwd, 'CLAUDE.md');
93
+ try {
94
+ if (!fs.existsSync(claudeMdPath)) {
95
+ fs.writeFileSync(claudeMdPath, '@AGENTS.md\n');
96
+ changes.push('created CLAUDE.md');
97
+ } else {
98
+ const cur = fs.readFileSync(claudeMdPath, 'utf8');
99
+ const hasLine = cur.split(/\r?\n/).some(l => l.trim() === '@AGENTS.md');
100
+ if (!hasLine) {
101
+ fs.writeFileSync(claudeMdPath, '@AGENTS.md\n' + cur);
102
+ changes.push('prepended @AGENTS.md to CLAUDE.md');
103
+ }
104
+ }
105
+ } catch (e) { obsEvent('bootstrap', 'next-step.wiring.target-failed', { target: claudeMdPath, error: e.message }); }
106
+
107
+ const agentsMdPath = path.join(cwd, 'AGENTS.md');
108
+ try {
109
+ if (fs.existsSync(agentsMdPath)) {
110
+ const cur = fs.readFileSync(agentsMdPath, 'utf8');
111
+ const hasLine = cur.split(/\r?\n/).some(l => l.trim() === '@.gm/next-step.md');
112
+ if (!hasLine) {
113
+ const sep = cur.endsWith('\n') ? '' : '\n';
114
+ fs.writeFileSync(agentsMdPath, cur + sep + '\n@.gm/next-step.md\n');
115
+ changes.push('appended @.gm/next-step.md to AGENTS.md');
116
+ }
117
+ }
118
+ } catch (e) { obsEvent('bootstrap', 'next-step.wiring.target-failed', { target: agentsMdPath, error: e.message }); }
119
+
120
+ if (changes.length > 0) {
121
+ log(`next-step wiring: ${changes.join(', ')}`);
122
+ obsEvent('bootstrap', 'next-step.wiring.applied', { changes });
123
+ }
124
+ }
125
+
77
126
 
78
127
  function cacheRoot() {
79
128
  const home = os.homedir();
@@ -812,6 +861,8 @@ async function ensureReady(opts) {
812
861
  opts = opts || {};
813
862
  const offline = opts.offline === true;
814
863
 
864
+ try { ensureNextStepWiring(process.env.CLAUDE_PROJECT_DIR || process.cwd()); } catch (_) {}
865
+
815
866
  if (!offline) {
816
867
  try {
817
868
  const selfStale = await probeSelfStaleness(2500);
@@ -1000,6 +1051,7 @@ function startSpoolDaemon() {
1000
1051
  module.exports = {
1001
1052
  bootstrap,
1002
1053
  ensureReady,
1054
+ ensureNextStepWiring,
1003
1055
  gmToolsDir,
1004
1056
  getWasmPath,
1005
1057
  getBinaryPath,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1547",
3
+ "version": "2.0.1548",
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/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1547",
3
+ "version": "2.0.1548",
4
4
  "description": "Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-skill",
3
- "version": "2.0.1547",
3
+ "version": "2.0.1548",
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",