gm-copilot-cli 2.0.1073 → 2.0.1075
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/copilot-profile.md +1 -1
- package/index.html +1 -1
- package/lib/skill-bootstrap.js +43 -47
- package/lib/spool-dispatch.js +26 -1
- package/manifest.yml +1 -1
- package/package.json +1 -1
- package/skills/gm/SKILL.md +12 -0
- package/skills/gm-complete/SKILL.md +15 -0
- package/skills/gm-emit/SKILL.md +11 -5
- package/skills/gm-execute/SKILL.md +9 -3
- package/skills/planning/SKILL.md +24 -3
- package/tools.json +1 -1
package/copilot-profile.md
CHANGED
package/index.html
CHANGED
|
@@ -74,7 +74,7 @@ body { display: flex; flex-direction: column; min-height: 100vh; }
|
|
|
74
74
|
<section>
|
|
75
75
|
<div class="gm-section-label"><span class="slash">//</span>status</div>
|
|
76
76
|
<div class="panel">
|
|
77
|
-
<div class="panel-head"><span>release · v2.0.
|
|
77
|
+
<div class="panel-head"><span>release · v2.0.1075</span><span>probably emerging</span></div>
|
|
78
78
|
<div class="panel-body">
|
|
79
79
|
<div class="row">
|
|
80
80
|
<span class="code"><span style="color:var(--panel-accent)">●</span></span>
|
package/lib/skill-bootstrap.js
CHANGED
|
@@ -8,32 +8,17 @@ const spool = require('./spool.js');
|
|
|
8
8
|
|
|
9
9
|
const PLUGKIT_TOOLS_DIR = path.join(os.homedir(), '.claude', 'gm-tools');
|
|
10
10
|
const PLUGKIT_VERSION_FILE = path.join(PLUGKIT_TOOLS_DIR, 'plugkit.version');
|
|
11
|
+
const PLUGKIT_WASM_PATH = path.join(PLUGKIT_TOOLS_DIR, 'plugkit.wasm');
|
|
12
|
+
const PLUGKIT_WASM_WRAPPER = path.join(PLUGKIT_TOOLS_DIR, 'plugkit-wasm-wrapper.js');
|
|
11
13
|
const BOOTSTRAP_STATUS_FILE = path.join(os.homedir(), '.gm', 'bootstrap-status.json');
|
|
12
14
|
const BOOTSTRAP_ERROR_FILE = path.join(os.homedir(), '.gm', 'bootstrap-error.json');
|
|
13
15
|
const LOG_DIR = path.join(os.homedir(), '.claude', 'gm-log');
|
|
14
|
-
const PLATFORM_MAP = {
|
|
15
|
-
win32: { suffix: '-win32-x64.exe', altSuffix: '-win32-arm64.exe' },
|
|
16
|
-
darwin: { suffix: '-darwin-x64', altSuffix: '-darwin-arm64' },
|
|
17
|
-
linux: { suffix: '-linux-x64', altSuffix: '-linux-arm64' },
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
function getPlatformKey() {
|
|
21
|
-
const plat = process.platform;
|
|
22
|
-
if (plat === 'win32') return plat;
|
|
23
|
-
if (plat === 'darwin') return plat;
|
|
24
|
-
if (plat === 'linux') return plat;
|
|
25
|
-
throw new Error(`Unsupported platform: ${plat}`);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function getExpectedBinaryName() {
|
|
29
|
-
const plat = getPlatformKey();
|
|
30
|
-
const suffix = PLATFORM_MAP[plat].suffix;
|
|
31
|
-
return `plugkit${suffix}`;
|
|
32
|
-
}
|
|
33
16
|
|
|
34
17
|
function getPlugkitPath() {
|
|
35
|
-
|
|
36
|
-
|
|
18
|
+
if (fs.existsSync(PLUGKIT_WASM_WRAPPER) && fs.existsSync(PLUGKIT_WASM_PATH)) {
|
|
19
|
+
return PLUGKIT_WASM_WRAPPER;
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`plugkit WASM not found at ${PLUGKIT_WASM_PATH}`);
|
|
37
22
|
}
|
|
38
23
|
|
|
39
24
|
function emitBootstrapEvent(severity, message, details) {
|
|
@@ -65,17 +50,12 @@ function readManifest() {
|
|
|
65
50
|
const gm = JSON.parse(fs.readFileSync(gmJsonPath, 'utf8'));
|
|
66
51
|
const version = gm.plugkitVersion;
|
|
67
52
|
|
|
68
|
-
const sha256Path = path.join(process.cwd(), 'gm-starter', 'bin', 'plugkit.sha256');
|
|
53
|
+
const sha256Path = path.join(process.cwd(), 'gm-starter', 'bin', 'plugkit.wasm.sha256');
|
|
69
54
|
if (!fs.existsSync(sha256Path)) {
|
|
70
|
-
throw new Error('gm-starter/bin/plugkit.sha256 not found');
|
|
55
|
+
throw new Error('gm-starter/bin/plugkit.wasm.sha256 not found');
|
|
71
56
|
}
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
const hashLine = sha256Lines.find(line => line.includes(binaryName));
|
|
75
|
-
if (!hashLine) {
|
|
76
|
-
throw new Error(`No hash found for ${binaryName}`);
|
|
77
|
-
}
|
|
78
|
-
const expectedHash = hashLine.split(/\s+/)[0];
|
|
57
|
+
const sha256Content = fs.readFileSync(sha256Path, 'utf8').trim();
|
|
58
|
+
const expectedHash = sha256Content.split(/\s+/)[0];
|
|
79
59
|
|
|
80
60
|
return { version, expectedHash };
|
|
81
61
|
} catch (e) {
|
|
@@ -101,20 +81,20 @@ function computeFileHash(filePath) {
|
|
|
101
81
|
}
|
|
102
82
|
|
|
103
83
|
async function downloadPlugkitBinary(version) {
|
|
104
|
-
const binaryName =
|
|
105
|
-
const url = `https://github.com/AnEntrypoint/plugkit-bin/releases/download
|
|
84
|
+
const binaryName = 'plugkit.wasm';
|
|
85
|
+
const url = `https://github.com/AnEntrypoint/plugkit-bin/releases/download/v${version}/${binaryName}`;
|
|
106
86
|
|
|
107
|
-
emitBootstrapEvent('info', 'Starting
|
|
87
|
+
emitBootstrapEvent('info', 'Starting WASM download', { version, url });
|
|
108
88
|
|
|
109
89
|
return new Promise((resolve, reject) => {
|
|
110
90
|
https
|
|
111
91
|
.get(url, { timeout: 30000 }, (res) => {
|
|
112
92
|
if (res.statusCode === 404) {
|
|
113
|
-
reject(new Error(`
|
|
93
|
+
reject(new Error(`WASM not found: v${version}`));
|
|
114
94
|
return;
|
|
115
95
|
}
|
|
116
96
|
if (res.statusCode !== 200) {
|
|
117
|
-
reject(new Error(`HTTP ${res.statusCode} downloading
|
|
97
|
+
reject(new Error(`HTTP ${res.statusCode} downloading plugkit.wasm`));
|
|
118
98
|
return;
|
|
119
99
|
}
|
|
120
100
|
|
|
@@ -122,7 +102,7 @@ async function downloadPlugkitBinary(version) {
|
|
|
122
102
|
res.on('data', (chunk) => chunks.push(chunk));
|
|
123
103
|
res.on('end', () => {
|
|
124
104
|
const data = Buffer.concat(chunks);
|
|
125
|
-
emitBootstrapEvent('info', '
|
|
105
|
+
emitBootstrapEvent('info', 'WASM download complete', { bytes: data.length });
|
|
126
106
|
resolve(data);
|
|
127
107
|
});
|
|
128
108
|
})
|
|
@@ -204,12 +184,14 @@ async function writeBinaryWithRetry(filePath, data, maxRetries = 3) {
|
|
|
204
184
|
|
|
205
185
|
async function verifyBinaryHealth(filePath) {
|
|
206
186
|
try {
|
|
207
|
-
if (
|
|
208
|
-
|
|
209
|
-
} else {
|
|
210
|
-
execSync(`"${filePath}" health > /dev/null 2>&1`, { timeout: 5000 });
|
|
187
|
+
if (!fs.existsSync(filePath)) {
|
|
188
|
+
throw new Error(`File not found: ${filePath}`);
|
|
211
189
|
}
|
|
212
|
-
|
|
190
|
+
const stat = fs.statSync(filePath);
|
|
191
|
+
if (stat.size < 1024) {
|
|
192
|
+
throw new Error(`File too small: ${stat.size} bytes`);
|
|
193
|
+
}
|
|
194
|
+
emitBootstrapEvent('info', 'Binary health check passed', { size: stat.size });
|
|
213
195
|
return true;
|
|
214
196
|
} catch (e) {
|
|
215
197
|
emitBootstrapEvent('warn', 'Binary health check failed', { error: e.message });
|
|
@@ -217,24 +199,38 @@ async function verifyBinaryHealth(filePath) {
|
|
|
217
199
|
}
|
|
218
200
|
}
|
|
219
201
|
|
|
220
|
-
async function spawnPlugkitWatcher(
|
|
202
|
+
async function spawnPlugkitWatcher(wasmPath) {
|
|
221
203
|
try {
|
|
222
|
-
emitBootstrapEvent('info', 'Spawning plugkit watcher daemon');
|
|
204
|
+
emitBootstrapEvent('info', 'Spawning plugkit WASM watcher daemon');
|
|
205
|
+
|
|
206
|
+
let wrapperPath;
|
|
207
|
+
try {
|
|
208
|
+
const gmPlugkit = require('gm-plugkit');
|
|
209
|
+
wrapperPath = path.join(path.dirname(gmPlugkit.getPath ? gmPlugkit.getPath() : require.resolve('gm-plugkit')), 'plugkit-wasm-wrapper.js');
|
|
210
|
+
} catch (e) {
|
|
211
|
+
emitBootstrapEvent('warn', 'gm-plugkit npm not available, using bundled wrapper', { error: e.message });
|
|
212
|
+
wrapperPath = path.join(path.dirname(wasmPath), 'plugkit-wasm-wrapper.js');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (!fs.existsSync(wrapperPath)) {
|
|
216
|
+
throw new Error(`WASM wrapper not found at ${wrapperPath}`);
|
|
217
|
+
}
|
|
223
218
|
|
|
224
|
-
const
|
|
225
|
-
const proc = spawn(
|
|
219
|
+
const runtime = process.platform === 'win32' ? 'bun.exe' : 'bun';
|
|
220
|
+
const proc = spawn(runtime, [wrapperPath, 'spool'], {
|
|
226
221
|
detached: true,
|
|
227
222
|
stdio: 'ignore',
|
|
228
223
|
windowsHide: true,
|
|
224
|
+
env: { ...process.env, CLAUDE_PROJECT_DIR: process.cwd() },
|
|
229
225
|
});
|
|
230
226
|
|
|
231
227
|
const pid = proc.pid;
|
|
232
228
|
proc.unref();
|
|
233
229
|
|
|
234
|
-
emitBootstrapEvent('info', 'Plugkit watcher spawned', { pid });
|
|
230
|
+
emitBootstrapEvent('info', 'Plugkit WASM watcher spawned', { pid });
|
|
235
231
|
return pid;
|
|
236
232
|
} catch (e) {
|
|
237
|
-
emitBootstrapEvent('error', 'Failed to spawn plugkit watcher', { error: e.message });
|
|
233
|
+
emitBootstrapEvent('error', 'Failed to spawn plugkit WASM watcher', { error: e.message });
|
|
238
234
|
throw e;
|
|
239
235
|
}
|
|
240
236
|
}
|
package/lib/spool-dispatch.js
CHANGED
|
@@ -72,4 +72,29 @@ async function pollForCompletion(jsonFile, timeoutMs, taskId) {
|
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
function checkDispatchGates(sessionId, operation) {
|
|
76
|
+
const gm = path.join(process.cwd(), '.gm');
|
|
77
|
+
const prdPath = path.join(gm, 'prd.yml');
|
|
78
|
+
const mutsPath = path.join(gm, 'mutables.yml');
|
|
79
|
+
const needsGmPath = path.join(gm, 'needs-gm');
|
|
80
|
+
const gmFiredPath = path.join(gm, `gm-fired-${sessionId}`);
|
|
81
|
+
|
|
82
|
+
if (!['write', 'edit', 'git'].includes(operation)) return { allowed: true };
|
|
83
|
+
|
|
84
|
+
if (fs.existsSync(prdPath) && fs.existsSync(needsGmPath) && !fs.existsSync(gmFiredPath)) {
|
|
85
|
+
return { allowed: false, reason: 'gm orchestration in progress; skills must complete work before tools execute' };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (fs.existsSync(mutsPath)) {
|
|
89
|
+
try {
|
|
90
|
+
const content = fs.readFileSync(mutsPath, 'utf8');
|
|
91
|
+
if (content.includes('status: unknown')) {
|
|
92
|
+
return { allowed: false, reason: 'unresolved mutables block tool execution; resolve all mutables before proceeding' };
|
|
93
|
+
}
|
|
94
|
+
} catch (_) {}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return { allowed: true };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = { dispatchSpool, checkDispatchGates };
|
package/manifest.yml
CHANGED
package/package.json
CHANGED
package/skills/gm/SKILL.md
CHANGED
|
@@ -58,3 +58,15 @@ Platform adapters (vscode, cursor, zed, jetbrains) that support `end-to-end: tru
|
|
|
58
58
|
4. Repeat until `nextSkill` is null
|
|
59
59
|
|
|
60
60
|
This collapses 5 manual skill invocations into 1 user invocation + 4 transparent auto-dispatches, achieving perceived single-flow parity with gm-cc's subagent orchestration.
|
|
61
|
+
|
|
62
|
+
## Skill Transition Protocol
|
|
63
|
+
|
|
64
|
+
Skill-to-skill transitions NEVER stop for approval or ask the user. Each skill completes its phase and invokes the next skill via `Skill()` tool call directly. Platform adapters auto-detect the transition from stdout JSON and fire the next skill without user interaction. Tool-use approvals are pre-authorized by the initial user request — re-asking defeats the cascade and breaks autonomy. Skills must assume subsequent tools are approved once the PRD is written by PLAN.
|
|
65
|
+
|
|
66
|
+
## Gate enforcement (spool-dispatch layer)
|
|
67
|
+
|
|
68
|
+
The file-spool (`lib/spool-dispatch.js::checkDispatchGates()`) blocks Write/Edit/git operations when:
|
|
69
|
+
1. `.gm/prd.yml` exists AND `.gm/needs-gm` exists AND `.gm/gm-fired-<sessionId>` does NOT exist → reason: "gm orchestration in progress; skills must complete work before tools execute"
|
|
70
|
+
2. `.gm/mutables.yml` has entries with `status: unknown` → reason: "unresolved mutables block tool execution; resolve all mutables before proceeding"
|
|
71
|
+
|
|
72
|
+
Gate 1 auto-clears: PLAN writes THREE markers (`.gm/prd.yml`, `.gm/needs-gm`, `.gm/gm-fired-<sessionId>`) at session start BEFORE transitioning to EXECUTE. The marker proves planning has run and authorized tool use. Gate 2 auto-clears: EXECUTE resolves mutables by updating `.gm/mutables.yml` entries to `status: witnessed`, or the file is deleted when empty by gm-complete. Tool denials surface the reason text to the agent, which adjusts behavior (e.g., resolve remaining mutables before retrying). Tool denials never mutate command arguments — they surface policy as imperative instruction.
|
|
@@ -104,3 +104,18 @@ After `git push`, poll `gh run list --branch main --limit 5 --json status,name,d
|
|
|
104
104
|
## Completion
|
|
105
105
|
|
|
106
106
|
All true at once: witnessed e2e | browser_validated when client work touched | failure paths exercised | test.js passes | `.prd` deleted | git clean and pushed | CI green | hygiene sweep clean | TODO.md gone | CHANGELOG.md updated.
|
|
107
|
+
|
|
108
|
+
## Marker file protocol
|
|
109
|
+
|
|
110
|
+
On transition to `update-docs`, delete all gm orchestration markers for the next cycle:
|
|
111
|
+
```
|
|
112
|
+
const fs = require('fs');
|
|
113
|
+
const path = require('path');
|
|
114
|
+
const sessionId = process.env.SESSION_ID || 'default';
|
|
115
|
+
const gm = path.join(process.cwd(), '.gm');
|
|
116
|
+
['.gm/prd.yml', '.gm/mutables.yml', '.gm/needs-gm', `.gm/gm-fired-${sessionId}`].forEach(m => {
|
|
117
|
+
try { fs.unlinkSync(path.join(gm, m.split('/')[1])); } catch (_) {}
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The `.gm/gm-fired-<sessionId>` marker was written by PLAN at session start and proves gm orchestration has completed. Cleanup before next cycle resets gates so the next PLAN run can write fresh markers.
|
package/skills/gm-emit/SKILL.md
CHANGED
|
@@ -5,16 +5,18 @@ description: EMIT phase. Pre-emit debug, write files, post-emit verify from disk
|
|
|
5
5
|
|
|
6
6
|
# GM EMIT — Write and verify from disk
|
|
7
7
|
|
|
8
|
-
Entry: every mutable KNOWN, from `gm-execute` or re-entered from VERIFY. Exit: gates clear → `gm-complete
|
|
8
|
+
Entry: every mutable KNOWN, from `gm-execute` or re-entered from VERIFY. Exit: gates clear → `gm-complete` (automatic, no approval).
|
|
9
9
|
|
|
10
10
|
Cross-cutting dispositions live in `gm` SKILL.md.
|
|
11
11
|
|
|
12
|
+
**Skill Transition Guarantee**: When this skill completes, invoke `gm-complete` directly via `Skill()` — do not ask the user. Tool denials from gates (checkDispatchGates) surface as imperative reason text; the agent adjusts behavior and retries, never re-asks the user.
|
|
13
|
+
|
|
12
14
|
## Transitions
|
|
13
15
|
|
|
14
|
-
- All gates clear → `gm-complete`
|
|
15
|
-
- Post-emit variance with known cause → fix in-band, re-verify, stay in EMIT
|
|
16
|
-
- Pre-emit reveals known logic error → `gm-execute`
|
|
17
|
-
- Pre-emit reveals new unknown OR post-emit variance with unknown cause OR scope changed → `planning`
|
|
16
|
+
- All gates clear → `gm-complete` (invoke via `Skill(skill="gm:gm-complete")` immediately, no stop)
|
|
17
|
+
- Post-emit variance with known cause → fix in-band, re-verify, stay in EMIT (self-loop, no transition)
|
|
18
|
+
- Pre-emit reveals known logic error → `gm-execute` (invoke via `Skill(skill="gm:gm-execute")` immediately, no stop)
|
|
19
|
+
- Pre-emit reveals new unknown OR post-emit variance with unknown cause OR scope changed → `planning` (invoke via `Skill(skill="planning")` immediately, no stop)
|
|
18
20
|
|
|
19
21
|
## Legitimacy gate (before pre-emit run)
|
|
20
22
|
|
|
@@ -68,3 +70,7 @@ Before pre-emit run, read `.gm/mutables.yml`. Any entry with `status: unknown`
|
|
|
68
70
|
- Structure: no if/else where dispatch suffices; no one-liners that obscure; no reinvented APIs
|
|
69
71
|
- Every fact resolved this phase memorized via background `Agent(memorize)`
|
|
70
72
|
- CHANGELOG.md updated; TODO.md cleared or deleted
|
|
73
|
+
|
|
74
|
+
## Marker file protocol
|
|
75
|
+
|
|
76
|
+
EMIT phase operates after EXECUTE resolves all mutables. `.gm/prd.yml` and `.gm/mutables.yml` remain live (deleted by gm-complete when work finishes). Gate enforcement does not block EMIT — the `.gm/gm-fired-<sessionId>` marker was already written by PLAN at session start, so tool use (Write/Edit) is fully authorized. EMIT does not write or read markers; it only invokes Write/Edit on files and runs post-emit verifications. On transition to gm-complete (all gates clear), invoke the skill immediately (no marker write needed by EMIT).
|
|
@@ -5,17 +5,19 @@ description: EXECUTE phase AND the foundational execution contract for every ski
|
|
|
5
5
|
|
|
6
6
|
# GM EXECUTE — Resolve every unknown by witness
|
|
7
7
|
|
|
8
|
-
Entry: `.prd` with named unknowns. Exit: every mutable KNOWN → invoke `gm-emit
|
|
8
|
+
Entry: `.prd` with named unknowns. Exit: every mutable KNOWN → invoke `gm-emit` (automatic, no approval).
|
|
9
9
|
|
|
10
10
|
A `@<discipline>` sigil propagates from PLAN through every recall, codesearch, and memorize call; reads without one fan across default plus enabled disciplines, writes without one go to default only.
|
|
11
11
|
|
|
12
12
|
This skill is the execution contract for ALL phases — pre-emit witnesses, post-emit verifies, e2e checks all run on this discipline. Cross-cutting dispositions live in `gm` SKILL.md.
|
|
13
13
|
|
|
14
|
+
**Skill Transition Guarantee**: Tool use is pre-authorized by the initial user request. When this skill completes, invoke `gm-emit` directly via `Skill()` — do not ask the user. Tool denials from gates (checkDispatchGates) surface as imperative reason text; the agent adjusts behavior and retries, never re-asks the user.
|
|
15
|
+
|
|
14
16
|
## Transitions
|
|
15
17
|
|
|
16
|
-
- All mutables KNOWN → `gm-emit`
|
|
18
|
+
- All mutables KNOWN → `gm-emit` (invoke via `Skill(skill="gm:emit")` immediately, no stop)
|
|
17
19
|
- Still UNKNOWN → re-run from a different angle (max 2 passes)
|
|
18
|
-
- New unknown OR unresolvable after 2 passes → `planning`
|
|
20
|
+
- New unknown OR unresolvable after 2 passes → `planning` (invoke via `Skill(skill="planning")` immediately, no stop)
|
|
19
21
|
|
|
20
22
|
## Mutable discipline
|
|
21
23
|
|
|
@@ -81,3 +83,7 @@ Up to 3 `gm:gm` subagents for independent items in one message. Browser escalati
|
|
|
81
83
|
## CI is automated
|
|
82
84
|
|
|
83
85
|
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.
|
|
86
|
+
|
|
87
|
+
## Marker file protocol
|
|
88
|
+
|
|
89
|
+
EXECUTE phase works against `.gm/prd.yml` and `.gm/mutables.yml` written by PLAN. Both files live for the duration of work and are deleted by gm-complete when work finishes. Gate enforcement (spool-dispatch layer) checks: if `.gm/prd.yml` + `.gm/needs-gm` exist BUT `.gm/gm-fired-<sessionId>` marker is missing, tool use blocks. PLAN writes all three markers at session start before transitioning to EXECUTE, so the gate is already clear when EXECUTE runs. EXECUTE does not write or read markers — it only reads and updates mutables. On transition to gm-emit, invoke the skill immediately (no marker write needed by EXECUTE).
|
package/skills/planning/SKILL.md
CHANGED
|
@@ -31,7 +31,7 @@ const { spawn, spawnSync } = require('child_process');
|
|
|
31
31
|
const fs = require('fs');
|
|
32
32
|
const path = require('path');
|
|
33
33
|
const os = require('os');
|
|
34
|
-
const bin = path.join(os.homedir(), '.claude', 'gm-tools',
|
|
34
|
+
const bin = path.join(os.homedir(), '.claude', 'gm-tools', 'plugkit.wasm');
|
|
35
35
|
const root = process.cwd();
|
|
36
36
|
const spoolIn = path.join(root, '.gm', 'exec-spool', 'in');
|
|
37
37
|
const spoolOut = path.join(root, '.gm', 'exec-spool', 'out');
|
|
@@ -42,13 +42,13 @@ if (fs.existsSync(pidFile)) {
|
|
|
42
42
|
try { fs.unlinkSync(pidFile); } catch (_) {}
|
|
43
43
|
}
|
|
44
44
|
if (process.platform === 'win32') {
|
|
45
|
-
try { spawnSync('taskkill', ['/F', '/IM', '
|
|
45
|
+
try { spawnSync('taskkill', ['/F', '/IM', 'node.exe'], { windowsHide: true, timeout: 3000, stdio: 'ignore' }); } catch (_) {}
|
|
46
46
|
} else {
|
|
47
47
|
try { spawnSync('pkill', ['-f', 'plugkit'], { timeout: 3000, stdio: 'ignore' }); } catch (_) {}
|
|
48
48
|
}
|
|
49
49
|
fs.mkdirSync(spoolIn, { recursive: true });
|
|
50
50
|
fs.mkdirSync(spoolOut, { recursive: true });
|
|
51
|
-
const proc = spawn(bin,
|
|
51
|
+
const proc = spawn('node', [bin, 'runner', '--watch', spoolIn, '--out', spoolOut], {
|
|
52
52
|
detached: true, stdio: 'ignore', windowsHide: true, cwd: root,
|
|
53
53
|
});
|
|
54
54
|
proc.unref();
|
|
@@ -152,3 +152,24 @@ Pack runs use `Promise.allSettled`, each idea its own try/catch, under 12s per c
|
|
|
152
152
|
No comments. 200-line per-file cap. Fail loud. No duplication. Scan before edit. AGENTS.md edits route through the memorize sub-agent only. CHANGELOG.md gets one entry per commit.
|
|
153
153
|
|
|
154
154
|
Minimal-code process, stop at the first that resolves: native → library → structure (map / pipeline) → write.
|
|
155
|
+
|
|
156
|
+
## Marker File Protocol
|
|
157
|
+
|
|
158
|
+
PLAN phase writes THREE marker files before transitioning to EXECUTE:
|
|
159
|
+
1. `.gm/prd.yml` — the work items (already written per PRD format above)
|
|
160
|
+
2. `.gm/needs-gm` — empty marker file signaling PRD is ready for orchestration
|
|
161
|
+
3. `.gm/gm-fired-<sessionId>` — signals that gm orchestration (planning) has run and cleared the gate
|
|
162
|
+
|
|
163
|
+
When `.gm/prd.yml` and `.gm/needs-gm` both exist, downstream tools check `.gm/gm-fired-<sessionId>` marker. If missing, tool execution blocks with reason: "gm orchestration in progress; skills must complete work before tools execute." This gate prevents tool use from tools that run BEFORE the orchestration phase is complete. Writing the marker clears this gate.
|
|
164
|
+
|
|
165
|
+
Write all three markers as final step of PLAN:
|
|
166
|
+
```
|
|
167
|
+
const fs = require('fs');
|
|
168
|
+
const path = require('path');
|
|
169
|
+
const sessionId = process.env.SESSION_ID || 'default';
|
|
170
|
+
fs.mkdirSync(path.join(process.cwd(), '.gm'), { recursive: true });
|
|
171
|
+
fs.writeFileSync(path.join(process.cwd(), '.gm', 'needs-gm'), '');
|
|
172
|
+
fs.writeFileSync(path.join(process.cwd(), '.gm', `gm-fired-${sessionId}`), '');
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Transition to `gm-execute` (or `gm-gm` subagent) immediately after writing all files. No stop-for-approval; the transition is automatic.
|