gm-skill 2.0.1971 → 2.0.1973
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/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/supervisor.js +6 -2
- package/gm.json +1 -1
- package/package.json +1 -1
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.1973",
|
|
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 {
|
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
|
|
package/gm.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-skill",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1973",
|
|
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",
|