gm-skill 2.0.1972 → 2.0.1974
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/bin/plugkit.version +1 -1
- package/bin/plugkit.wasm.sha256 +1 -1
- package/gm-plugkit/bootstrap.js +36 -2
- package/gm-plugkit/cli.js +27 -2
- package/gm-plugkit/package.json +1 -1
- package/gm-plugkit/plugkit-wasm-wrapper.js +15 -2
- package/gm-plugkit/plugkit.version +1 -1
- package/gm-plugkit/supervisor.js +7 -22
- package/gm.json +2 -2
- package/package.json +1 -1
package/bin/plugkit.version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.905
|
package/bin/plugkit.wasm.sha256
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
5e637cfefaddd54c2fdf06d6e718bc8834b6c026fed6e542ef1d18de2b5bebfc plugkit.wasm
|
package/gm-plugkit/bootstrap.js
CHANGED
|
@@ -46,7 +46,7 @@ function obsEvent(subsystem, event, fields) {
|
|
|
46
46
|
|
|
47
47
|
function writeBootstrapError(spec) {
|
|
48
48
|
try {
|
|
49
|
-
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
49
|
+
const projectDir = resolveProjectRoot(process.env.CLAUDE_PROJECT_DIR || process.cwd());
|
|
50
50
|
const spoolDir = path.join(projectDir, '.gm', 'exec-spool');
|
|
51
51
|
fs.mkdirSync(spoolDir, { recursive: true });
|
|
52
52
|
fs.writeFileSync(path.join(spoolDir, '.bootstrap-error.json'), JSON.stringify({ ts: new Date().toISOString(), ...spec }, null, 2));
|
|
@@ -55,7 +55,7 @@ function writeBootstrapError(spec) {
|
|
|
55
55
|
|
|
56
56
|
function clearBootstrapError() {
|
|
57
57
|
try {
|
|
58
|
-
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
58
|
+
const projectDir = resolveProjectRoot(process.env.CLAUDE_PROJECT_DIR || process.cwd());
|
|
59
59
|
fs.unlinkSync(path.join(projectDir, '.gm', 'exec-spool', '.bootstrap-error.json'));
|
|
60
60
|
} catch (_) {}
|
|
61
61
|
}
|
|
@@ -223,6 +223,39 @@ function gmToolsDir() {
|
|
|
223
223
|
return primary;
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
+
// Root a project-dir resolution at the git COMMON dir, not the raw cwd/
|
|
227
|
+
// CLAUDE_PROJECT_DIR -- a worktree (e.g. Workflow's isolation:'worktree'
|
|
228
|
+
// agents, each `git worktree add`-ing a fresh physical directory) shares the
|
|
229
|
+
// SAME underlying repo as its main checkout but has its own separate
|
|
230
|
+
// directory tree. Every cwd-derived project-dir computation in this file and
|
|
231
|
+
// in supervisor.js/cli.js must funnel through this so a worktree-spawned
|
|
232
|
+
// process resolves to the SAME .gm/exec-spool/ as its main-repo sibling --
|
|
233
|
+
// otherwise the single-instance supervisor lock can never see a sibling
|
|
234
|
+
// worktree's already-running watcher, and every worktree cold-boots its own
|
|
235
|
+
// independent watcher (each loading the full embed model + running its own
|
|
236
|
+
// cold reindex of what is, conceptually, the same project). Live-measured
|
|
237
|
+
// this session under real concurrent multi-agent load: this was the actual
|
|
238
|
+
// root cause of a user-flagged "memory grows to ~2GB then clears" churn
|
|
239
|
+
// pattern -- N worktrees of one repo each independently paying full
|
|
240
|
+
// cold-embed cost instead of sharing one already-warm watcher, and the
|
|
241
|
+
// resulting CPU contention pushed genuinely-busy processes past their
|
|
242
|
+
// heartbeat deadline into a restart cycle that produces the sawtooth memory
|
|
243
|
+
// pattern. Mirrors the existing browserRootDir() resolution already used for
|
|
244
|
+
// browser-session state in plugkit-wasm-wrapper.js (same fix, same reason,
|
|
245
|
+
// applied one layer up at the process-boot-dedup level).
|
|
246
|
+
function resolveProjectRoot(start) {
|
|
247
|
+
const resolved = path.resolve(start);
|
|
248
|
+
try {
|
|
249
|
+
const r = spawnSync('git', ['rev-parse', '--git-common-dir'], { cwd: resolved, encoding: 'utf-8', windowsHide: true, timeout: 1500 });
|
|
250
|
+
if (r.status === 0 && r.stdout && r.stdout.trim()) {
|
|
251
|
+
let commonDir = r.stdout.trim();
|
|
252
|
+
if (!path.isAbsolute(commonDir)) commonDir = path.resolve(resolved, commonDir);
|
|
253
|
+
if (/(^|[\\/])\.git$/.test(commonDir)) return path.dirname(commonDir);
|
|
254
|
+
}
|
|
255
|
+
} catch (_) {}
|
|
256
|
+
return resolved;
|
|
257
|
+
}
|
|
258
|
+
|
|
226
259
|
function readVersionFile() {
|
|
227
260
|
const p = path.join(wrapperDir, 'plugkit.version');
|
|
228
261
|
if (!fs.existsSync(p)) throw new Error(`plugkit.version not found at ${p}`);
|
|
@@ -1254,6 +1287,7 @@ module.exports = {
|
|
|
1254
1287
|
ensureNextStepWiring,
|
|
1255
1288
|
ensureInstructionsBundle,
|
|
1256
1289
|
gmToolsDir,
|
|
1290
|
+
resolveProjectRoot,
|
|
1257
1291
|
getWasmPath,
|
|
1258
1292
|
getBinaryPath,
|
|
1259
1293
|
startSpoolDaemon,
|
package/gm-plugkit/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ const fs = require('fs');
|
|
|
5
5
|
const os = require('os');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const cp = require('child_process');
|
|
8
|
-
const { ensureReady, startSpoolDaemon, gmToolsDir, readVersionFile, ensureGmPlugkitVersionFresh, ensureSkillMdFresh, ensureWrapperFresh, isReady, getWasmPath, readPinnedGmPlugkitVersion, spawnPinnedBoot } = require('./bootstrap');
|
|
8
|
+
const { ensureReady, startSpoolDaemon, gmToolsDir, readVersionFile, ensureGmPlugkitVersionFresh, ensureSkillMdFresh, ensureWrapperFresh, isReady, getWasmPath, readPinnedGmPlugkitVersion, spawnPinnedBoot, resolveProjectRoot } = require('./bootstrap');
|
|
9
9
|
|
|
10
10
|
function getWasmPathSafe() {
|
|
11
11
|
try { return getWasmPath(); } catch (_) { return null; }
|
|
@@ -201,7 +201,7 @@ function killStaleWatchers() {
|
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
function spoolDir() {
|
|
204
|
-
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
204
|
+
const projectDir = resolveProjectRoot(process.env.CLAUDE_PROJECT_DIR || process.cwd());
|
|
205
205
|
return path.join(projectDir, '.gm', 'exec-spool');
|
|
206
206
|
}
|
|
207
207
|
|
|
@@ -242,6 +242,29 @@ function writeCliError(phase, err) {
|
|
|
242
242
|
} catch (_) {}
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
+
// gm-runner is a genuine, sha256-verified drop-in replacement for this
|
|
246
|
+
// entire bun/node boot path -- when it's installed, delegate to it
|
|
247
|
+
// immediately and exit, before any bun/node-specific bootstrap logic runs.
|
|
248
|
+
// This is the actual code-level enforcement of what was previously only a
|
|
249
|
+
// documented convention (SKILL.md telling an LLM agent to manually prefer
|
|
250
|
+
// gm-runner) -- with this in place bun/node are only ever exercised on a
|
|
251
|
+
// platform gm-runner-bin has no published binary for, or during the
|
|
252
|
+
// one-time install before gm-runner itself lands on disk.
|
|
253
|
+
function tryDelegateToGmRunner(args) {
|
|
254
|
+
if (process.env.GM_PLUGKIT_NO_RUNNER_DELEGATE === '1') return false;
|
|
255
|
+
const exeName = process.platform === 'win32' ? 'gm-runner.exe' : 'gm-runner';
|
|
256
|
+
const runnerPath = path.join(gmToolsDir(), exeName);
|
|
257
|
+
if (!fs.existsSync(runnerPath)) return false;
|
|
258
|
+
try {
|
|
259
|
+
const result = cp.spawnSync(runnerPath, args, { stdio: 'inherit', windowsHide: true });
|
|
260
|
+
if (result.error) return false; // exec genuinely failed to start -- fall through to bun/node path
|
|
261
|
+
process.exit(typeof result.status === 'number' ? result.status : 0);
|
|
262
|
+
} catch (_) {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
|
|
245
268
|
(async () => {
|
|
246
269
|
const args = process.argv.slice(2);
|
|
247
270
|
|
|
@@ -250,6 +273,8 @@ function writeCliError(phase, err) {
|
|
|
250
273
|
process.exit(0);
|
|
251
274
|
}
|
|
252
275
|
|
|
276
|
+
tryDelegateToGmRunner(args);
|
|
277
|
+
|
|
253
278
|
if (args.includes('--kill-stale-watchers')) {
|
|
254
279
|
process.exit(killStaleWatchers());
|
|
255
280
|
}
|
package/gm-plugkit/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-plugkit",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1974",
|
|
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": {
|
|
@@ -4732,9 +4732,22 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
4732
4732
|
} catch (_) {}
|
|
4733
4733
|
}
|
|
4734
4734
|
_writeStatusBusy = (ms) => { try { writeStatus(ms); } catch (_) {} };
|
|
4735
|
-
|
|
4735
|
+
// Every regular heartbeat tick also carries a modest rolling busy_until
|
|
4736
|
+
// (45s, comfortably above the supervisor's 30s STATUS_STALE_MS) -- not
|
|
4737
|
+
// because this tick itself is doing long work, but because under heavy
|
|
4738
|
+
// real multi-process contention (many concurrent gm-driven watchers on
|
|
4739
|
+
// one machine, live-observed: 13 concurrent agent watchers each competing
|
|
4740
|
+
// for CPU, all doing real embed/codesearch work) the OS scheduler can
|
|
4741
|
+
// starve THIS process of any time slice for well over 30s between ticks
|
|
4742
|
+
// even though the process is genuinely alive and will resume once
|
|
4743
|
+
// scheduled -- no in-process yield/setImmediate can fix a starvation that
|
|
4744
|
+
// happens entirely outside this process's own event loop. A missed tick
|
|
4745
|
+
// or two under real contention should not itself be a kill signal; a
|
|
4746
|
+
// GENUINELY dead process still gets caught because nothing here ever
|
|
4747
|
+
// refreshes busy_until once the process actually exits.
|
|
4748
|
+
setInterval(() => writeStatus(45000), 5000);
|
|
4736
4749
|
setInterval(() => { try { scanStalledTurns(); } catch (_) {} }, 30000);
|
|
4737
|
-
writeStatus();
|
|
4750
|
+
writeStatus(45000);
|
|
4738
4751
|
|
|
4739
4752
|
setTimeout(async () => {
|
|
4740
4753
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.905
|
package/gm-plugkit/supervisor.js
CHANGED
|
@@ -6,7 +6,7 @@ const path = require('path');
|
|
|
6
6
|
const os = require('os');
|
|
7
7
|
const crypto = require('crypto');
|
|
8
8
|
const { spawn, spawnSync } = require('child_process');
|
|
9
|
-
const { gmToolsDir } = require('./bootstrap');
|
|
9
|
+
const { gmToolsDir, resolveProjectRoot } = require('./bootstrap');
|
|
10
10
|
const { logEvent: _sharedLogEvent, GM_LOG_ROOT: _sharedGmLogRoot } = require('./gm-log');
|
|
11
11
|
const { pidCommandLineForKillGuard: _sharedPidCommandLine } = require('./gm-process');
|
|
12
12
|
|
|
@@ -17,7 +17,11 @@ function wrapperSha12OnDisk() {
|
|
|
17
17
|
} catch (_) { return null; }
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
// Root the spool/lock state at the git common dir (see resolveProjectRoot's
|
|
21
|
+
// own doc comment in bootstrap.js for the full worktree-dedup rationale) so
|
|
22
|
+
// a worktree-spawned supervisor shares its lock/spoolDir with the main-repo
|
|
23
|
+
// checkout instead of always cold-booting its own independent watcher.
|
|
24
|
+
const projectDir = resolveProjectRoot(process.env.CLAUDE_PROJECT_DIR || process.cwd());
|
|
21
25
|
const spoolDir = path.join(projectDir, '.gm', 'exec-spool');
|
|
22
26
|
fs.mkdirSync(spoolDir, { recursive: true });
|
|
23
27
|
|
|
@@ -29,26 +33,7 @@ const LOG_PATH = path.join(spoolDir, '.watcher.log');
|
|
|
29
33
|
const GM_LOG_ROOT = _sharedGmLogRoot;
|
|
30
34
|
|
|
31
35
|
const POLL_INTERVAL_MS = 10_000;
|
|
32
|
-
|
|
33
|
-
// (13 parallel gm-skill-driven agents, each booting its own watcher on the
|
|
34
|
-
// same machine): 74 of 94 total watcher restarts logged in one session were
|
|
35
|
-
// heartbeat-stale kills, the dominant restart cause by a wide margin -- each
|
|
36
|
-
// restart drops the process's memory back to near-zero then it climbs again
|
|
37
|
-
// through a fresh cold-embed pass, which is the actual mechanism behind the
|
|
38
|
-
// "memory grows to ~2GB then clears" pattern a user observed and correctly
|
|
39
|
-
// flagged as inefficient. The busy_until shielding mechanism (see writeStatus
|
|
40
|
-
// call sites in plugkit-wasm-wrapper.js) already protects genuinely-busy
|
|
41
|
-
// single dispatches, but under real CPU contention from many concurrent
|
|
42
|
-
// processes, even the setInterval-based busy_until REFRESH callback itself
|
|
43
|
-
// can be delayed past the old 30s window before it fires -- a starvation
|
|
44
|
-
// problem one level above the per-dispatch busy-window fixes already shipped
|
|
45
|
-
// this session. Raising the stale threshold gives real contention more slack
|
|
46
|
-
// before concluding a process is dead, trading a slightly slower true-dead
|
|
47
|
-
// detection for far fewer false-positive kills-and-restarts (each restart is
|
|
48
|
-
// far more expensive than the extra wait -- a full cold embed pass observed
|
|
49
|
-
// taking 90-120s+ under load) -- a clear net win under the contention this
|
|
50
|
-
// session repeatedly measured.
|
|
51
|
-
const STATUS_STALE_MS = 90_000;
|
|
36
|
+
const STATUS_STALE_MS = 30_000;
|
|
52
37
|
const MAX_RESTART_BURST = 5;
|
|
53
38
|
const RESTART_WINDOW_MS = 60_000;
|
|
54
39
|
const BURST_BACKOFF_MS = 60_000;
|
package/gm.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1974",
|
|
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.905"
|
|
21
21
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-skill",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1974",
|
|
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",
|