gm-skill 2.0.1118 → 2.0.1120
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/plugkit-wasm-wrapper.js +41 -0
- package/gm.json +1 -1
- package/lib/skill-bootstrap.js +27 -57
- package/package.json +2 -2
- package/skills/gm-skill/SKILL.md +4 -0
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ npx gm-skill-bootstrap
|
|
|
28
28
|
|
|
29
29
|
## Version
|
|
30
30
|
|
|
31
|
-
`2.0.
|
|
31
|
+
`2.0.1120` — auto-bumped from the canonical `gm` repo. Every push to `AnEntrypoint/gm` republishes this package alongside all 15 platform packages.
|
|
32
32
|
|
|
33
33
|
## Source of truth
|
|
34
34
|
|
|
@@ -2,6 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import os from 'os';
|
|
4
4
|
import crypto from 'crypto';
|
|
5
|
+
import https from 'https';
|
|
5
6
|
import { watch } from 'fs';
|
|
6
7
|
import { spawn, spawnSync } from 'child_process';
|
|
7
8
|
import net from 'net';
|
|
@@ -736,6 +737,46 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
736
737
|
setInterval(writeStatus, 5000);
|
|
737
738
|
writeStatus();
|
|
738
739
|
|
|
740
|
+
const UPDATE_AVAILABLE_PATH = path.join(spoolDir, '.update-available.json');
|
|
741
|
+
const UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1000;
|
|
742
|
+
function checkForUpdate() {
|
|
743
|
+
const installed = resolveVersion(instance);
|
|
744
|
+
const req = https.get({
|
|
745
|
+
host: 'api.github.com',
|
|
746
|
+
path: '/repos/AnEntrypoint/plugkit-bin/releases/latest',
|
|
747
|
+
headers: { 'user-agent': 'plugkit-watcher', 'accept': 'application/json' },
|
|
748
|
+
timeout: 5000,
|
|
749
|
+
}, (res) => {
|
|
750
|
+
if (res.statusCode !== 200) { res.resume(); return; }
|
|
751
|
+
const chunks = [];
|
|
752
|
+
res.on('data', c => chunks.push(c));
|
|
753
|
+
res.on('end', () => {
|
|
754
|
+
try {
|
|
755
|
+
const rel = JSON.parse(Buffer.concat(chunks).toString('utf-8'));
|
|
756
|
+
const tag = rel && rel.tag_name;
|
|
757
|
+
if (!tag) return;
|
|
758
|
+
const latest = tag.replace(/^v/, '');
|
|
759
|
+
if (latest === installed) {
|
|
760
|
+
try { fs.unlinkSync(UPDATE_AVAILABLE_PATH); } catch (_) {}
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
fs.writeFileSync(UPDATE_AVAILABLE_PATH, JSON.stringify({
|
|
764
|
+
installed,
|
|
765
|
+
latest,
|
|
766
|
+
checked_at_ms: Date.now(),
|
|
767
|
+
instruction: 'plugkit is out of date. To update, close the running watcher and re-bootstrap with the @latest flag, e.g. node ~/.claude/gm-tools/plugkit-wasm-wrapper.js spool & after running bootstrap with {latest: true}.',
|
|
768
|
+
update_url: `https://github.com/AnEntrypoint/plugkit-bin/releases/tag/v${latest}`,
|
|
769
|
+
}, null, 2));
|
|
770
|
+
console.log(`[update] available: installed=${installed} latest=${latest} → wrote ${UPDATE_AVAILABLE_PATH}`);
|
|
771
|
+
} catch (_) {}
|
|
772
|
+
});
|
|
773
|
+
});
|
|
774
|
+
req.on('timeout', () => req.destroy());
|
|
775
|
+
req.on('error', () => {});
|
|
776
|
+
}
|
|
777
|
+
setTimeout(checkForUpdate, 10_000);
|
|
778
|
+
setInterval(checkForUpdate, UPDATE_CHECK_INTERVAL_MS);
|
|
779
|
+
|
|
739
780
|
const pollInterval = setInterval(async () => {
|
|
740
781
|
const existing = walkDir(inDir);
|
|
741
782
|
for (const fullPath of existing) {
|
package/gm.json
CHANGED
package/lib/skill-bootstrap.js
CHANGED
|
@@ -10,18 +10,12 @@ 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
11
|
const PLUGKIT_WASM_PATH = path.join(PLUGKIT_TOOLS_DIR, 'plugkit.wasm');
|
|
12
12
|
const PLUGKIT_WASM_WRAPPER = path.join(PLUGKIT_TOOLS_DIR, 'plugkit-wasm-wrapper.js');
|
|
13
|
-
const PLUGKIT_LATEST_CACHE = path.join(PLUGKIT_TOOLS_DIR, 'plugkit-latest.json');
|
|
14
|
-
const PLUGKIT_LATEST_TTL_MS = 60 * 60 * 1000;
|
|
15
|
-
const NPM_PACKAGE = '@anentrypoint/plugkit-wasm';
|
|
16
13
|
const BOOTSTRAP_STATUS_FILE = path.join(os.homedir(), '.gm', 'bootstrap-status.json');
|
|
17
14
|
const BOOTSTRAP_ERROR_FILE = path.join(os.homedir(), '.gm', 'bootstrap-error.json');
|
|
18
15
|
const LOG_DIR = path.join(os.homedir(), '.claude', 'gm-log');
|
|
19
16
|
|
|
20
17
|
function getPlugkitPath() {
|
|
21
|
-
|
|
22
|
-
return PLUGKIT_WASM_WRAPPER;
|
|
23
|
-
}
|
|
24
|
-
throw new Error(`plugkit WASM not found at ${PLUGKIT_WASM_PATH}`);
|
|
18
|
+
return PLUGKIT_WASM_PATH;
|
|
25
19
|
}
|
|
26
20
|
|
|
27
21
|
function emitBootstrapEvent(severity, message, details) {
|
|
@@ -127,16 +121,6 @@ function httpGet(url, timeoutMs) {
|
|
|
127
121
|
}
|
|
128
122
|
|
|
129
123
|
async function getLatestRemoteVersion() {
|
|
130
|
-
try {
|
|
131
|
-
const stat = fs.statSync(PLUGKIT_LATEST_CACHE);
|
|
132
|
-
if (Date.now() - stat.mtimeMs < PLUGKIT_LATEST_TTL_MS) {
|
|
133
|
-
const cached = JSON.parse(fs.readFileSync(PLUGKIT_LATEST_CACHE, 'utf-8'));
|
|
134
|
-
if (cached && cached.version) {
|
|
135
|
-
emitBootstrapEvent('info', 'Using cached latest version', { version: cached.version, ageMs: Date.now() - stat.mtimeMs });
|
|
136
|
-
return cached;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
} catch (_) {}
|
|
140
124
|
let version = null;
|
|
141
125
|
let source = null;
|
|
142
126
|
try {
|
|
@@ -176,13 +160,8 @@ async function getLatestRemoteVersion() {
|
|
|
176
160
|
} catch (e) {
|
|
177
161
|
emitBootstrapEvent('warn', 'sha fetch failed; will verify after download', { error: e.message, version });
|
|
178
162
|
}
|
|
179
|
-
const payload = { version, sha, source, fetchedAt: Date.now() };
|
|
180
|
-
try {
|
|
181
|
-
fs.mkdirSync(PLUGKIT_TOOLS_DIR, { recursive: true });
|
|
182
|
-
fs.writeFileSync(PLUGKIT_LATEST_CACHE, JSON.stringify(payload, null, 2));
|
|
183
|
-
} catch (_) {}
|
|
184
163
|
emitBootstrapEvent('info', 'Resolved latest plugkit version', { version, source, hasSha: Boolean(sha) });
|
|
185
|
-
return
|
|
164
|
+
return { version, sha, source };
|
|
186
165
|
}
|
|
187
166
|
|
|
188
167
|
function gitignorePath(cwd) { return path.join(cwd, '.gitignore'); }
|
|
@@ -272,34 +251,15 @@ function ensureManagedGitignore(cwd) {
|
|
|
272
251
|
async function downloadPlugkitBinary(version) {
|
|
273
252
|
const binaryName = 'plugkit.wasm';
|
|
274
253
|
const url = `https://github.com/AnEntrypoint/plugkit-bin/releases/download/v${version}/${binaryName}`;
|
|
275
|
-
|
|
276
254
|
emitBootstrapEvent('info', 'Starting WASM download', { version, url });
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
if (res.statusCode !== 200) {
|
|
286
|
-
reject(new Error(`HTTP ${res.statusCode} downloading plugkit.wasm`));
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const chunks = [];
|
|
291
|
-
res.on('data', (chunk) => chunks.push(chunk));
|
|
292
|
-
res.on('end', () => {
|
|
293
|
-
const data = Buffer.concat(chunks);
|
|
294
|
-
emitBootstrapEvent('info', 'WASM download complete', { bytes: data.length });
|
|
295
|
-
resolve(data);
|
|
296
|
-
});
|
|
297
|
-
})
|
|
298
|
-
.on('error', (e) => {
|
|
299
|
-
emitBootstrapEvent('error', 'Download failed', { error: e.message });
|
|
300
|
-
reject(e);
|
|
301
|
-
});
|
|
302
|
-
});
|
|
255
|
+
try {
|
|
256
|
+
const buf = await httpGet(url, 30000);
|
|
257
|
+
emitBootstrapEvent('info', 'WASM download complete', { bytes: buf.length });
|
|
258
|
+
return buf;
|
|
259
|
+
} catch (e) {
|
|
260
|
+
emitBootstrapEvent('error', 'Download failed', { error: e.message });
|
|
261
|
+
throw e;
|
|
262
|
+
}
|
|
303
263
|
}
|
|
304
264
|
|
|
305
265
|
function findPlugkitWasmPids() {
|
|
@@ -460,21 +420,31 @@ async function spawnPlugkitWatcher(wasmPath) {
|
|
|
460
420
|
}
|
|
461
421
|
}
|
|
462
422
|
|
|
463
|
-
async function bootstrapPlugkit(sessionId) {
|
|
423
|
+
async function bootstrapPlugkit(sessionId, options) {
|
|
464
424
|
const startTime = Date.now();
|
|
425
|
+
const opts = options || {};
|
|
426
|
+
const forceLatest = Boolean(opts.latest);
|
|
465
427
|
|
|
466
428
|
try {
|
|
467
|
-
emitBootstrapEvent('info', 'Bootstrap started');
|
|
429
|
+
emitBootstrapEvent('info', 'Bootstrap started', { forceLatest });
|
|
468
430
|
|
|
469
431
|
ensureManagedGitignore(process.cwd());
|
|
470
432
|
|
|
471
433
|
const manifest = readManifest();
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
if (
|
|
476
|
-
|
|
434
|
+
let targetVersion = manifest.version;
|
|
435
|
+
let expectedHash = manifest.expectedHash;
|
|
436
|
+
|
|
437
|
+
if (forceLatest) {
|
|
438
|
+
const latest = await getLatestRemoteVersion();
|
|
439
|
+
if (latest && latest.version) {
|
|
440
|
+
targetVersion = latest.version;
|
|
441
|
+
expectedHash = latest.sha || expectedHash;
|
|
442
|
+
if (latest.version !== manifest.version) {
|
|
443
|
+
emitBootstrapEvent('info', 'forceLatest: using newer remote version', { latest: latest.version, manifest: manifest.version });
|
|
444
|
+
}
|
|
445
|
+
}
|
|
477
446
|
}
|
|
447
|
+
|
|
478
448
|
const installedVersion = getInstalledVersion();
|
|
479
449
|
const plugkitPath = getPlugkitPath();
|
|
480
450
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-skill",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1120",
|
|
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.1120"
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|
|
45
45
|
"node": ">=16.0.0"
|
package/skills/gm-skill/SKILL.md
CHANGED
|
@@ -16,6 +16,10 @@ Check `.gm/exec-spool/.status.json`. If absent or `ts` > 15s old:
|
|
|
16
16
|
|
|
17
17
|
Wait 2 seconds, verify `.status.json` is fresh. Then proceed.
|
|
18
18
|
|
|
19
|
+
## Plugkit version updates
|
|
20
|
+
|
|
21
|
+
The watcher checks GitHub Releases every 5 minutes for a newer plugkit. If drift is detected, it writes `.gm/exec-spool/.update-available.json` with `{installed, latest, instruction, update_url}`; if no drift, the file is removed. Read this file at session start (and occasionally afterward); if present, kill the current watcher, run `bootstrapPlugkit({latest: true})` once to fetch the new wasm, then restart the watcher. Default bootstrap never hits the network — only `{latest: true}` fetches the newest binary.
|
|
22
|
+
|
|
19
23
|
## Dispatch ABI
|
|
20
24
|
|
|
21
25
|
Write request body to `.gm/exec-spool/in/<verb>/<N>.txt`. Read response from `.gm/exec-spool/out/<verb>-<N>.json` (nested verbs) or `out/<N>.json` (root verbs). Bodies are JSON, raw code, or a single phase name depending on the verb.
|