gm-skill 2.0.1161 → 2.0.1162
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/README.md +1 -1
- package/gm-plugkit/bootstrap.js +32 -0
- package/gm-plugkit/supervisor.js +1 -0
- package/gm.json +1 -1
- package/lib/spool-dispatch.js +30 -0
- package/package.json +2 -2
- package/skills/gm-skill/SKILL.md +4 -0
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ An earlier generation fanned out fifteen per-platform downstream repos (gm-cc, g
|
|
|
35
35
|
|
|
36
36
|
## Version
|
|
37
37
|
|
|
38
|
-
`2.0.
|
|
38
|
+
`2.0.1162` — auto-bumped from the canonical `gm` repo. Every push to `AnEntrypoint/gm` (or any cascading sibling crate) republishes this package.
|
|
39
39
|
|
|
40
40
|
## Source of truth
|
|
41
41
|
|
package/gm-plugkit/bootstrap.js
CHANGED
|
@@ -811,6 +811,37 @@ function getBinaryPath() {
|
|
|
811
811
|
return getWasmPath();
|
|
812
812
|
}
|
|
813
813
|
|
|
814
|
+
function probeUnsupervisedWatcher(spoolDir) {
|
|
815
|
+
try {
|
|
816
|
+
const statusPath = path.join(spoolDir, '.status.json');
|
|
817
|
+
const supervisorPath = path.join(spoolDir, '.supervisor.json');
|
|
818
|
+
const markerPath = path.join(spoolDir, '.pre-supervised-watcher.json');
|
|
819
|
+
if (!fs.existsSync(statusPath)) {
|
|
820
|
+
try { fs.unlinkSync(markerPath); } catch (_) {}
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
const status = JSON.parse(fs.readFileSync(statusPath, 'utf-8'));
|
|
824
|
+
const age = Date.now() - (status && status.ts || 0);
|
|
825
|
+
if (age > 30_000) {
|
|
826
|
+
try { fs.unlinkSync(markerPath); } catch (_) {}
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
if (fs.existsSync(supervisorPath)) {
|
|
830
|
+
try { fs.unlinkSync(markerPath); } catch (_) {}
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
const marker = {
|
|
834
|
+
ts: Date.now(),
|
|
835
|
+
reason: 'running-watcher-has-no-supervisor',
|
|
836
|
+
watcher_pid: status.pid,
|
|
837
|
+
watcher_version: status.version,
|
|
838
|
+
severity: 'warn',
|
|
839
|
+
instruction: 'A running watcher was started under an older bootstrap that did not spawn a supervisor. Unplanned-restart recovery and idle-teardown coordination are dormant. To migrate, stop the current watcher (taskkill /F /T /PID <watcher_pid> on Windows or kill <watcher_pid> on POSIX) and let the next bootstrap re-spawn it under supervisor.js.',
|
|
840
|
+
};
|
|
841
|
+
fs.writeFileSync(markerPath, JSON.stringify(marker, null, 2));
|
|
842
|
+
} catch (_) {}
|
|
843
|
+
}
|
|
844
|
+
|
|
814
845
|
function startSpoolDaemon() {
|
|
815
846
|
try {
|
|
816
847
|
const wrapper = path.join(gmToolsDir(), 'plugkit-wasm-wrapper.js');
|
|
@@ -821,6 +852,7 @@ function startSpoolDaemon() {
|
|
|
821
852
|
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
822
853
|
const spoolDir = path.join(projectDir, '.gm', 'exec-spool');
|
|
823
854
|
fs.mkdirSync(spoolDir, { recursive: true });
|
|
855
|
+
probeUnsupervisedWatcher(spoolDir);
|
|
824
856
|
const logPath = path.join(spoolDir, '.watcher.log');
|
|
825
857
|
try {
|
|
826
858
|
const stat = fs.statSync(logPath);
|
package/gm-plugkit/supervisor.js
CHANGED
|
@@ -216,6 +216,7 @@ process.on('SIGTERM', () => {
|
|
|
216
216
|
|
|
217
217
|
writeSupervisorStatus('starting', {});
|
|
218
218
|
logEvent('supervisor.starting', { spool_dir: spoolDir });
|
|
219
|
+
try { fs.unlinkSync(path.join(spoolDir, '.pre-supervised-watcher.json')); } catch (_) {}
|
|
219
220
|
spawnWatcher('initial');
|
|
220
221
|
setInterval(checkWatcherHealth, POLL_INTERVAL_MS);
|
|
221
222
|
setInterval(() => writeSupervisorStatus('watching', { watcher_pid: currentChildPid, boot_reason: currentBootReason }), 10_000);
|
package/gm.json
CHANGED
package/lib/spool-dispatch.js
CHANGED
|
@@ -170,6 +170,25 @@ function markInstructionSeen(sessionId) {
|
|
|
170
170
|
} catch (_) {}
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
const DEFER_MARKERS = [
|
|
174
|
+
'next pass', 'next session', 'next turn',
|
|
175
|
+
'defer to later', 'deferred to later', 'deferred for later',
|
|
176
|
+
'future pass', 'future session', 'future turn',
|
|
177
|
+
'address it next', 'address this next', 'leave for next',
|
|
178
|
+
'documented for next', 'documented for future',
|
|
179
|
+
'below criticality', 'skip for now', 'punt for now',
|
|
180
|
+
'do later', 'fix later', 'later pass',
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
function deferMarkerIn(text) {
|
|
184
|
+
if (!text) return null;
|
|
185
|
+
const lower = String(text).toLowerCase();
|
|
186
|
+
for (const m of DEFER_MARKERS) {
|
|
187
|
+
if (lower.includes(m)) return m;
|
|
188
|
+
}
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
|
|
173
192
|
function checkDispatchGates(sessionId, operation, extra) {
|
|
174
193
|
const cwd = process.cwd();
|
|
175
194
|
const gm = path.join(cwd, '.gm');
|
|
@@ -226,6 +245,17 @@ function checkDispatchGates(sessionId, operation, extra) {
|
|
|
226
245
|
logDeviation('deviation.mutable-without-evidence', { mutable_id: extra.id || null });
|
|
227
246
|
}
|
|
228
247
|
|
|
248
|
+
if (operation === 'git' && extra && extra.commit_message) {
|
|
249
|
+
const marker = deferMarkerIn(extra.commit_message);
|
|
250
|
+
if (marker) {
|
|
251
|
+
logDeviation('deviation.commit-message-defer', { marker, operation });
|
|
252
|
+
return {
|
|
253
|
+
allowed: false,
|
|
254
|
+
reason: `commit message rejected: deferral phrase '${marker}' detected. Per paper §22 Fix on Sight, defer markers are forced closure. Either inline-fix and re-witness, or split the deferred work as a separate PRD item with blockedBy: [external] before committing. Rewrite the commit message and retry.`,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
229
259
|
if (!['write', 'edit', 'git'].includes(operation)) return { allowed: true };
|
|
230
260
|
|
|
231
261
|
if (fs.existsSync(prdPath) && fs.existsSync(needsGmPath) && !fs.existsSync(gmFiredPath)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-skill",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1162",
|
|
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",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"gm.json"
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"gm-plugkit": "^2.0.
|
|
42
|
+
"gm-plugkit": "^2.0.1162"
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|
|
45
45
|
"node": ">=16.0.0"
|
package/skills/gm-skill/SKILL.md
CHANGED
|
@@ -20,6 +20,10 @@ If the `instruction` response carries a non-null `update_available`, plugkit dri
|
|
|
20
20
|
|
|
21
21
|
If `running_tasks` is non-empty, you own them — every entry is a subprocess you started that's still consuming CPU/memory. Stop ones that have outlived their purpose with `task-stop` (write `.gm/exec-spool/in/task-stop/<N>.txt` with `{id: "t<n>"}`). The 15-min idle reaper is the last resort, not a substitute for hygiene. If `stuck_spool` is non-empty, dispatches are wedged — the watcher's host_exec_js is synchronous and a stuck body blocks every other verb until it returns. Diagnose via `.watcher.log` and consider rebootstrapping if it doesn't clear; spool bodies are Turing-complete and can loop forever. Long-running work goes through `task-spawn` (returns a `task_id` immediately, body runs detached), not through the sync `nodejs`/`bash`/`python` language verbs.
|
|
22
22
|
|
|
23
|
+
If `should_residual_scan` is true, dispatch `residual-scan` before any text response that reads like a stop. The marker is one-shot per stopping window — re-checking after you've fired it is fine; firing it for the first time in a session that has pending work is non-negotiable. If `unsupervised_watcher` is non-null, the running watcher predates supervisor.js and has no unplanned-restart recovery; stop the named PID and let the next bootstrap re-spawn it under supervision.
|
|
24
|
+
|
|
25
|
+
No deferral phrasing. "Next pass", "next session", "future work", "defer to later", "address it next", "below criticality", "do later", "fix later" are all forced closure dressed up. `prd-add` rejects items whose description/subject/notes contain these markers unless the item carries `blockedBy: [external]` or `blockedBy: [out-of-reach]`. The dispatch gate also rejects git commits whose message contains them. Per §22 Fix on Sight: in-spirit reachable work is executed this turn, not deferred. If the work is genuinely unreachable, declare it with blockedBy; that's an auditable refusal, not a defer.
|
|
26
|
+
|
|
23
27
|
The wasm artifact lives at `~/.claude/gm-tools/plugkit.wasm`; the spool watcher runs it. The watcher's own stdout/stderr is appended to `.gm/exec-spool/.watcher.log` — Read it to see plugkit's internal trace, dispatch timings, sweep actions, errors.
|
|
24
28
|
|
|
25
29
|
The watcher self-shuts-down after 15 minutes idle (no spool I/O, no live browser session) and is restarted on next agent activity by a detached supervisor. `.gm/exec-spool/.unplanned-restart.json` is a critical-failure marker — present means a prior watcher died without a planned shutdown. Treat as a PRD-worthy incident on sight: diagnose via `.watcher.log` and `gm-log/<day>/plugkit.jsonl` events `supervisor.watcher-exited-unexpectedly` and `supervisor.heartbeat-stale` around the prior_status.ts timestamp, then delete the marker once root cause is named.
|