gm-skill 2.0.1218 → 2.0.1219
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/bin/plugkit.version +1 -1
- package/bin/plugkit.wasm +0 -0
- package/bin/plugkit.wasm.sha256 +1 -1
- package/gm-plugkit/plugkit-wasm-wrapper.js +27 -1
- package/gm.json +2 -2
- package/lib/spool-dispatch.js +71 -1
- package/lib/spool-poll-gate.js +19 -1
- package/package.json +2 -2
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.1219` — 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/bin/plugkit.version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.447
|
package/bin/plugkit.wasm
CHANGED
|
Binary file
|
package/bin/plugkit.wasm.sha256
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
0ce920f00138b9524a0a1ad062dab691e4959497853644a7d06403c2b77ed480 plugkit.wasm
|
|
@@ -1223,7 +1223,17 @@ function makeHostFunctions(instanceRef) {
|
|
|
1223
1223
|
const opts = optsStr ? JSON.parse(optsStr) : {};
|
|
1224
1224
|
const lang = opts.lang || 'nodejs';
|
|
1225
1225
|
const cwd = opts.cwd || process.cwd();
|
|
1226
|
-
const
|
|
1226
|
+
const rawTimeout = opts.timeoutMs;
|
|
1227
|
+
if (rawTimeout === undefined || rawTimeout === null || typeof rawTimeout !== 'number' || !Number.isFinite(rawTimeout) || rawTimeout <= 0 || !Number.isInteger(rawTimeout)) {
|
|
1228
|
+
return writeWasmJson(instanceRef.value, {
|
|
1229
|
+
ok: false,
|
|
1230
|
+
error: 'missing timeoutMs',
|
|
1231
|
+
required: 'positive integer milliseconds',
|
|
1232
|
+
paper_ref: '§20',
|
|
1233
|
+
received: rawTimeout === undefined ? null : rawTimeout,
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
const timeoutMs = rawTimeout;
|
|
1227
1237
|
let cmd, args;
|
|
1228
1238
|
if (lang === 'nodejs' || lang === 'js') { cmd = process.execPath; args = ['-e', code]; }
|
|
1229
1239
|
else if (lang === 'python') { cmd = 'python'; args = ['-c', code]; }
|
|
@@ -1755,6 +1765,13 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
1755
1765
|
const sessForRecall = readCurrentSess();
|
|
1756
1766
|
if (isInstructionTurnStart(sessForRecall)) {
|
|
1757
1767
|
autoRecallPayload = tryAutoRecallForTurnEntry(instance, sessForRecall, process.cwd());
|
|
1768
|
+
try {
|
|
1769
|
+
const _spoolDir = path.join(process.cwd(), '.gm', 'exec-spool');
|
|
1770
|
+
for (const _f of ['.turn-browser-edits.json', '.turn-browser-witnessed']) {
|
|
1771
|
+
const _p = path.join(_spoolDir, _f);
|
|
1772
|
+
if (fs.existsSync(_p)) fs.unlinkSync(_p);
|
|
1773
|
+
}
|
|
1774
|
+
} catch (_) {}
|
|
1758
1775
|
}
|
|
1759
1776
|
}
|
|
1760
1777
|
|
|
@@ -1781,6 +1798,15 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
1781
1798
|
logEvent('plugkit', 'dispatch.end', { verb, task: taskBase, dur_ms, out_bytes: resultStr.length });
|
|
1782
1799
|
emitOrchestratorEvents(verb, taskBase, resultStr);
|
|
1783
1800
|
|
|
1801
|
+
if (verb === 'browser') {
|
|
1802
|
+
try {
|
|
1803
|
+
const witnessFile = path.join(process.cwd(), '.gm', 'exec-spool', '.turn-browser-witnessed');
|
|
1804
|
+
fs.mkdirSync(path.dirname(witnessFile), { recursive: true });
|
|
1805
|
+
fs.writeFileSync(witnessFile, JSON.stringify({ ts: Date.now(), task: taskBase, dur_ms }));
|
|
1806
|
+
logEvent('plugkit', 'browser.witness-marked', { task: taskBase });
|
|
1807
|
+
} catch (_) {}
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1784
1810
|
try { instance.exports.plugkit_free(verbPtr, verbBytes.length); } catch (_) {}
|
|
1785
1811
|
try { instance.exports.plugkit_free(bodyPtr, bodyBytes.length); } catch (_) {}
|
|
1786
1812
|
try { instance.exports.plugkit_free(ptr, len); } catch (_) {}
|
package/gm.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1219",
|
|
4
4
|
"description": "Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement",
|
|
5
5
|
"author": "AnEntrypoint",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,5 +17,5 @@
|
|
|
17
17
|
"publishConfig": {
|
|
18
18
|
"access": "public"
|
|
19
19
|
},
|
|
20
|
-
"plugkitVersion": "0.1.
|
|
20
|
+
"plugkitVersion": "0.1.447"
|
|
21
21
|
}
|
package/lib/spool-dispatch.js
CHANGED
|
@@ -59,6 +59,69 @@ function hasUnpushedCommits(cwd) {
|
|
|
59
59
|
|
|
60
60
|
const TOPLEVEL_DOC_ALLOWLIST = new Set(['AGENTS.md', 'CLAUDE.md', 'README.md', 'SKILLS.md', 'CHANGELOG.md', 'LICENSE', 'LICENSE.md']);
|
|
61
61
|
|
|
62
|
+
const BROWSER_FILE_EXT_RE = /\.(html?|tsx|jsx|vue|svelte|mjs|cjs|js|ts|css|scss|sass)$/i;
|
|
63
|
+
const BROWSER_FILE_DIR_RE = /^(src|public|site|app|pages|components|client|web)[\\/]/i;
|
|
64
|
+
|
|
65
|
+
function isBrowserRunningFile(rel) {
|
|
66
|
+
if (!rel) return false;
|
|
67
|
+
const norm = String(rel).replace(/\\/g, '/');
|
|
68
|
+
if (/\.(html?|tsx|jsx|vue|svelte)$/i.test(norm)) return true;
|
|
69
|
+
if (/\.(mjs|cjs|js|ts|css|scss|sass)$/i.test(norm) && BROWSER_FILE_DIR_RE.test(norm)) return true;
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function browserEditsFile(cwd) {
|
|
74
|
+
return path.join(cwd || process.cwd(), '.gm', 'exec-spool', '.turn-browser-edits.json');
|
|
75
|
+
}
|
|
76
|
+
function browserWitnessFile(cwd) {
|
|
77
|
+
return path.join(cwd || process.cwd(), '.gm', 'exec-spool', '.turn-browser-witnessed');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function recordBrowserEdit(cwd, filePath) {
|
|
81
|
+
try {
|
|
82
|
+
const root = cwd || process.cwd();
|
|
83
|
+
let rel = filePath;
|
|
84
|
+
try { rel = path.relative(root, filePath); } catch (_) {}
|
|
85
|
+
if (!isBrowserRunningFile(rel)) return false;
|
|
86
|
+
const f = browserEditsFile(root);
|
|
87
|
+
fs.mkdirSync(path.dirname(f), { recursive: true });
|
|
88
|
+
let list = [];
|
|
89
|
+
try { list = JSON.parse(fs.readFileSync(f, 'utf8')); if (!Array.isArray(list)) list = []; } catch (_) {}
|
|
90
|
+
const entry = { file: rel.replace(/\\/g, '/'), ts: Date.now() };
|
|
91
|
+
if (!list.some(e => e && e.file === entry.file)) list.push(entry);
|
|
92
|
+
fs.writeFileSync(f, JSON.stringify(list));
|
|
93
|
+
return true;
|
|
94
|
+
} catch (_) { return false; }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function clearBrowserTurnMarkers(cwd) {
|
|
98
|
+
const root = cwd || process.cwd();
|
|
99
|
+
for (const p of [browserEditsFile(root), browserWitnessFile(root)]) {
|
|
100
|
+
try { if (fs.existsSync(p)) fs.unlinkSync(p); } catch (_) {}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function markBrowserWitnessed(cwd, meta) {
|
|
105
|
+
try {
|
|
106
|
+
const f = browserWitnessFile(cwd);
|
|
107
|
+
fs.mkdirSync(path.dirname(f), { recursive: true });
|
|
108
|
+
fs.writeFileSync(f, JSON.stringify({ ts: Date.now(), ...(meta || {}) }));
|
|
109
|
+
} catch (_) {}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function readBrowserEdits(cwd) {
|
|
113
|
+
try {
|
|
114
|
+
const f = browserEditsFile(cwd);
|
|
115
|
+
if (!fs.existsSync(f)) return [];
|
|
116
|
+
const list = JSON.parse(fs.readFileSync(f, 'utf8'));
|
|
117
|
+
return Array.isArray(list) ? list : [];
|
|
118
|
+
} catch (_) { return []; }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function isBrowserWitnessed(cwd) {
|
|
122
|
+
try { return fs.existsSync(browserWitnessFile(cwd)); } catch (_) { return false; }
|
|
123
|
+
}
|
|
124
|
+
|
|
62
125
|
function unsolicitedDocs(cwd) {
|
|
63
126
|
try {
|
|
64
127
|
const r = spawnSync('git', ['status', '--porcelain'], {
|
|
@@ -285,6 +348,13 @@ function checkDispatchGates(sessionId, operation, extra) {
|
|
|
285
348
|
logDeviation('deviation.unsolicited-doc-created', { file: f, operation });
|
|
286
349
|
}
|
|
287
350
|
}
|
|
351
|
+
const browserEdits = readBrowserEdits(cwd);
|
|
352
|
+
if (browserEdits.length > 0 && !isBrowserWitnessed(cwd)) {
|
|
353
|
+
const files = browserEdits.map(e => e.file);
|
|
354
|
+
const shown = files.slice(0, 5).join(', ') + (files.length > 5 ? `, +${files.length - 5} more` : '');
|
|
355
|
+
residuals.push(`Browser Witness required: you edited ${shown} without dispatching the browser verb to witness the change in a live page. Per paper §23 this is non-negotiable. Either dispatch browser to verify the edit works in-browser, or revert the changes.`);
|
|
356
|
+
logDeviation('deviation.browser-witness-missing', { files, operation });
|
|
357
|
+
}
|
|
288
358
|
if (residuals.length > 0) {
|
|
289
359
|
logDeviation('deviation.gate-deny', { operation, reason: 'stop-gate residuals', residuals });
|
|
290
360
|
return { allowed: false, reason: `stop-gate residuals: ${residuals.join('; ')}`, residuals };
|
|
@@ -339,4 +409,4 @@ function checkDispatchGates(sessionId, operation, extra) {
|
|
|
339
409
|
return { allowed: true };
|
|
340
410
|
}
|
|
341
411
|
|
|
342
|
-
module.exports = { dispatchSpool, checkDispatchGates, isWorktreeDirty, hasUnpushedCommits, unsolicitedDocs, logDeviation, markInstructionSeen, hasDispatchedInstruction, isSpoolPollCommand, SPOOL_POLL_REASON };
|
|
412
|
+
module.exports = { dispatchSpool, checkDispatchGates, isWorktreeDirty, hasUnpushedCommits, unsolicitedDocs, logDeviation, markInstructionSeen, hasDispatchedInstruction, isSpoolPollCommand, SPOOL_POLL_REASON, recordBrowserEdit, markBrowserWitnessed, clearBrowserTurnMarkers, isBrowserRunningFile, readBrowserEdits, isBrowserWitnessed };
|
package/lib/spool-poll-gate.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const { isSpoolPollCommand, SPOOL_POLL_REASON, logDeviation } = require('./spool-dispatch.js');
|
|
2
|
+
const { isSpoolPollCommand, SPOOL_POLL_REASON, logDeviation, recordBrowserEdit, isBrowserRunningFile } = require('./spool-dispatch.js');
|
|
3
3
|
|
|
4
4
|
let raw = '';
|
|
5
5
|
process.stdin.setEncoding('utf8');
|
|
@@ -9,6 +9,24 @@ process.stdin.on('end', () => {
|
|
|
9
9
|
try { event = JSON.parse(raw || '{}'); } catch (_) { event = {}; }
|
|
10
10
|
const tool = event.tool_name || event.tool || '';
|
|
11
11
|
const input = event.tool_input || event.input || {};
|
|
12
|
+
const cwd = event.cwd || process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
13
|
+
|
|
14
|
+
if (tool === 'Write' || tool === 'Edit' || tool === 'MultiEdit') {
|
|
15
|
+
const fp = input.file_path || input.filePath || input.path || '';
|
|
16
|
+
if (fp && isBrowserRunningFile(require('path').relative(cwd, fp))) {
|
|
17
|
+
try { recordBrowserEdit(cwd, fp); } catch (_) {}
|
|
18
|
+
try {
|
|
19
|
+
logDeviation('browser-edit.recorded', {
|
|
20
|
+
operation: tool.toLowerCase(),
|
|
21
|
+
file: fp,
|
|
22
|
+
sess: event.session_id || process.env.CLAUDE_SESSION_ID || process.env.GM_SESSION_ID || '',
|
|
23
|
+
});
|
|
24
|
+
} catch (_) {}
|
|
25
|
+
}
|
|
26
|
+
process.stdout.write(JSON.stringify({ continue: true }));
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
|
|
12
30
|
if (tool !== 'Bash') {
|
|
13
31
|
process.stdout.write(JSON.stringify({ continue: true }));
|
|
14
32
|
process.exit(0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-skill",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1219",
|
|
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.1219"
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|
|
45
45
|
"node": ">=16.0.0"
|