gm-skill 2.0.1120 → 2.0.1121
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 +17 -8
- package/gm-plugkit/plugkit-wasm-wrapper.js +147 -12
- package/gm.json +1 -1
- package/package.json +2 -2
- package/skills/gm-skill/SKILL.md +6 -2
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.1121` — 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
|
|
package/gm-plugkit/bootstrap.js
CHANGED
|
@@ -597,22 +597,31 @@ function copyWasmToGmTools(wasmPath, version) {
|
|
|
597
597
|
const dst = gmToolsDir();
|
|
598
598
|
fs.mkdirSync(dst, { recursive: true });
|
|
599
599
|
const target = path.join(dst, 'plugkit.wasm');
|
|
600
|
+
const wrapperSrc = path.join(__dirname, 'plugkit-wasm-wrapper.js');
|
|
601
|
+
const wrapperDst = path.join(dst, 'plugkit-wasm-wrapper.js');
|
|
600
602
|
|
|
603
|
+
let wasmFresh = false;
|
|
601
604
|
if (fs.existsSync(target)) {
|
|
602
|
-
let needsRefresh = true;
|
|
603
605
|
try {
|
|
604
606
|
const cur = sha256OfFileSync(target);
|
|
605
607
|
const src = sha256OfFileSync(wasmPath);
|
|
606
|
-
if (cur === src)
|
|
608
|
+
if (cur === src) wasmFresh = true;
|
|
607
609
|
} catch (_) {}
|
|
608
|
-
if (!needsRefresh) {
|
|
609
|
-
try { fs.writeFileSync(path.join(dst, 'plugkit.version'), version); } catch (_) {}
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
612
610
|
}
|
|
613
|
-
|
|
614
|
-
fs.copyFileSync(wasmPath, target);
|
|
611
|
+
if (!wasmFresh) fs.copyFileSync(wasmPath, target);
|
|
615
612
|
fs.writeFileSync(path.join(dst, 'plugkit.version'), version);
|
|
613
|
+
|
|
614
|
+
if (fs.existsSync(wrapperSrc)) {
|
|
615
|
+
let wrapperFresh = false;
|
|
616
|
+
if (fs.existsSync(wrapperDst)) {
|
|
617
|
+
try {
|
|
618
|
+
const cur = sha256OfFileSync(wrapperDst);
|
|
619
|
+
const src = sha256OfFileSync(wrapperSrc);
|
|
620
|
+
if (cur === src) wrapperFresh = true;
|
|
621
|
+
} catch (_) {}
|
|
622
|
+
}
|
|
623
|
+
if (!wrapperFresh) fs.copyFileSync(wrapperSrc, wrapperDst);
|
|
624
|
+
}
|
|
616
625
|
}
|
|
617
626
|
|
|
618
627
|
function getWasmPath() {
|
|
@@ -6,6 +6,10 @@ import https from 'https';
|
|
|
6
6
|
import { watch } from 'fs';
|
|
7
7
|
import { spawn, spawnSync } from 'child_process';
|
|
8
8
|
import net from 'net';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = path.dirname(__filename);
|
|
9
13
|
|
|
10
14
|
const KV_DIR = path.join(os.homedir(), '.claude', 'gm-tools', 'kv');
|
|
11
15
|
fs.mkdirSync(KV_DIR, { recursive: true });
|
|
@@ -632,6 +636,24 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
632
636
|
process.on('SIGTERM', () => { releaseLock(); process.exit(0); });
|
|
633
637
|
process.on('exit', releaseLock);
|
|
634
638
|
|
|
639
|
+
try {
|
|
640
|
+
const wrapperDst = path.join(os.homedir(), '.claude', 'gm-tools', 'plugkit-wasm-wrapper.js');
|
|
641
|
+
if (path.resolve(__filename) !== path.resolve(wrapperDst)) {
|
|
642
|
+
let same = false;
|
|
643
|
+
if (fs.existsSync(wrapperDst)) {
|
|
644
|
+
try {
|
|
645
|
+
const a = fs.readFileSync(__filename);
|
|
646
|
+
const b = fs.readFileSync(wrapperDst);
|
|
647
|
+
if (a.length === b.length && crypto.createHash('sha256').update(a).digest('hex') === crypto.createHash('sha256').update(b).digest('hex')) same = true;
|
|
648
|
+
} catch (_) {}
|
|
649
|
+
}
|
|
650
|
+
if (!same) {
|
|
651
|
+
fs.copyFileSync(__filename, wrapperDst);
|
|
652
|
+
console.log(`[plugkit-wasm] installed wrapper at ${wrapperDst}`);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
} catch (e) { console.error(`[plugkit-wasm] wrapper self-install failed: ${e.message}`); }
|
|
656
|
+
|
|
635
657
|
console.log(`[plugkit-wasm] plugkit v${resolveVersion(instance)} (wasm)`);
|
|
636
658
|
console.log(`[plugkit-wasm] watching ${inDir}`);
|
|
637
659
|
|
|
@@ -853,22 +875,116 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
853
875
|
await new Promise(() => {});
|
|
854
876
|
}
|
|
855
877
|
|
|
856
|
-
|
|
878
|
+
async function selfHealFromGithubReleases() {
|
|
879
|
+
return new Promise((resolve, reject) => {
|
|
880
|
+
const fetchJson = (url) => new Promise((res, rej) => {
|
|
881
|
+
const req = https.get(url, { timeout: 5000, headers: { 'user-agent': 'plugkit-wasm-wrapper', 'accept': 'application/json' } }, (r) => {
|
|
882
|
+
if (r.statusCode >= 300 && r.statusCode < 400 && r.headers.location) {
|
|
883
|
+
r.resume(); fetchJson(r.headers.location).then(res, rej); return;
|
|
884
|
+
}
|
|
885
|
+
if (r.statusCode !== 200) { r.resume(); rej(new Error(`HTTP ${r.statusCode} ${url}`)); return; }
|
|
886
|
+
const chunks = []; r.on('data', c => chunks.push(c));
|
|
887
|
+
r.on('end', () => { try { res(JSON.parse(Buffer.concat(chunks).toString('utf-8'))); } catch (e) { rej(e); } });
|
|
888
|
+
r.on('error', rej);
|
|
889
|
+
});
|
|
890
|
+
req.on('timeout', () => req.destroy(new Error('timeout')));
|
|
891
|
+
req.on('error', rej);
|
|
892
|
+
});
|
|
893
|
+
const fetchBuf = (url) => new Promise((res, rej) => {
|
|
894
|
+
const req = https.get(url, { timeout: 30000, headers: { 'user-agent': 'plugkit-wasm-wrapper' } }, (r) => {
|
|
895
|
+
if (r.statusCode >= 300 && r.statusCode < 400 && r.headers.location) {
|
|
896
|
+
r.resume(); fetchBuf(r.headers.location).then(res, rej); return;
|
|
897
|
+
}
|
|
898
|
+
if (r.statusCode !== 200) { r.resume(); rej(new Error(`HTTP ${r.statusCode} ${url}`)); return; }
|
|
899
|
+
const chunks = []; r.on('data', c => chunks.push(c));
|
|
900
|
+
r.on('end', () => res(Buffer.concat(chunks)));
|
|
901
|
+
r.on('error', rej);
|
|
902
|
+
});
|
|
903
|
+
req.on('timeout', () => req.destroy(new Error('timeout')));
|
|
904
|
+
req.on('error', rej);
|
|
905
|
+
});
|
|
906
|
+
(async () => {
|
|
907
|
+
try {
|
|
908
|
+
const rel = await fetchJson('https://api.github.com/repos/AnEntrypoint/plugkit-bin/releases/latest');
|
|
909
|
+
const tag = rel.tag_name;
|
|
910
|
+
if (!tag) throw new Error('no tag_name from GH Releases');
|
|
911
|
+
const version = tag.replace(/^v/, '');
|
|
912
|
+
const base = `https://github.com/AnEntrypoint/plugkit-bin/releases/download/${tag}`;
|
|
913
|
+
const [wasm, sha] = await Promise.all([
|
|
914
|
+
fetchBuf(`${base}/plugkit.wasm`),
|
|
915
|
+
fetchBuf(`${base}/plugkit.wasm.sha256`).then(b => b.toString('utf-8').trim().split(/\s+/)[0]).catch(() => ''),
|
|
916
|
+
]);
|
|
917
|
+
if (sha) {
|
|
918
|
+
const got = crypto.createHash('sha256').update(wasm).digest('hex');
|
|
919
|
+
if (got !== sha) throw new Error(`sha mismatch: got ${got}, expected ${sha}`);
|
|
920
|
+
}
|
|
921
|
+
const toolsDir = path.join(os.homedir(), '.claude', 'gm-tools');
|
|
922
|
+
fs.mkdirSync(toolsDir, { recursive: true });
|
|
923
|
+
fs.writeFileSync(path.join(toolsDir, 'plugkit.wasm'), wasm);
|
|
924
|
+
fs.writeFileSync(path.join(toolsDir, 'plugkit.version'), version);
|
|
925
|
+
const wrapperSrc = __filename;
|
|
926
|
+
const wrapperDst = path.join(toolsDir, 'plugkit-wasm-wrapper.js');
|
|
927
|
+
if (path.resolve(wrapperSrc) !== path.resolve(wrapperDst) && fs.existsSync(wrapperSrc)) {
|
|
928
|
+
try { fs.copyFileSync(wrapperSrc, wrapperDst); } catch (_) {}
|
|
929
|
+
}
|
|
930
|
+
resolve({ ok: true, version, sha });
|
|
931
|
+
} catch (e) { reject(e); }
|
|
932
|
+
})();
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
async function selfHeal(reason) {
|
|
937
|
+
console.error(`[plugkit-wasm] self-heal: ${reason}`);
|
|
857
938
|
try {
|
|
858
|
-
const
|
|
859
|
-
|
|
860
|
-
|
|
939
|
+
const r = await selfHealFromGithubReleases();
|
|
940
|
+
console.error(`[plugkit-wasm] self-heal: installed v${r.version} from GH Releases`);
|
|
941
|
+
return true;
|
|
942
|
+
} catch (e) {
|
|
943
|
+
console.error(`[plugkit-wasm] self-heal GH fetch failed: ${e.message}`);
|
|
944
|
+
}
|
|
945
|
+
console.error('[plugkit-wasm] self-heal: run `bun x gm-plugkit@latest spool` to recover manually');
|
|
946
|
+
return false;
|
|
947
|
+
}
|
|
861
948
|
|
|
862
|
-
|
|
863
|
-
|
|
949
|
+
async function tryInstantiate(wasmPath) {
|
|
950
|
+
const wasmBuffer = fs.readFileSync(wasmPath);
|
|
951
|
+
const wasmModule = new WebAssembly.Module(wasmBuffer);
|
|
952
|
+
const instanceRef = { value: null };
|
|
953
|
+
const hostFunctions = makeHostFunctions(instanceRef);
|
|
954
|
+
const importObject = {
|
|
955
|
+
env: hostFunctions,
|
|
956
|
+
wasi_snapshot_preview1: createWasiShim(instanceRef),
|
|
957
|
+
};
|
|
958
|
+
const instance = new WebAssembly.Instance(wasmModule, importObject);
|
|
959
|
+
instanceRef.value = instance;
|
|
960
|
+
return { instance, instanceRef };
|
|
961
|
+
}
|
|
864
962
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
};
|
|
963
|
+
(async () => {
|
|
964
|
+
try {
|
|
965
|
+
const wasmPath = path.join(os.homedir(), '.claude', 'gm-tools', 'plugkit.wasm');
|
|
869
966
|
|
|
870
|
-
|
|
871
|
-
|
|
967
|
+
let instance, instanceRef;
|
|
968
|
+
if (!fs.existsSync(wasmPath)) {
|
|
969
|
+
const healed = await selfHeal('wasm not installed');
|
|
970
|
+
if (!healed) process.exit(1);
|
|
971
|
+
}
|
|
972
|
+
try {
|
|
973
|
+
({ instance, instanceRef } = await tryInstantiate(wasmPath));
|
|
974
|
+
} catch (e) {
|
|
975
|
+
const isLink = e && (e.name === 'LinkError' || /Import/i.test(e.message || ''));
|
|
976
|
+
const isCompile = e && (e.name === 'CompileError' || /WebAssembly/i.test(e.message || ''));
|
|
977
|
+
if (isLink || isCompile) {
|
|
978
|
+
const healed = await selfHeal(`${e.name || 'instantiate'}: ${e.message}`);
|
|
979
|
+
if (!healed) {
|
|
980
|
+
console.error('[plugkit-wasm] wrapper/wasm version skew — run: bun x gm-plugkit@latest spool');
|
|
981
|
+
process.exit(1);
|
|
982
|
+
}
|
|
983
|
+
({ instance, instanceRef } = await tryInstantiate(wasmPath));
|
|
984
|
+
} else {
|
|
985
|
+
throw e;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
872
988
|
|
|
873
989
|
const args = process.argv.slice(2);
|
|
874
990
|
if (args.includes('--version')) {
|
|
@@ -876,6 +992,25 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
876
992
|
process.exit(0);
|
|
877
993
|
}
|
|
878
994
|
|
|
995
|
+
if (args[0] === 'bootstrap' || args.includes('--ensure-latest')) {
|
|
996
|
+
try {
|
|
997
|
+
const bootstrapPath = path.join(__dirname, 'bootstrap.js');
|
|
998
|
+
if (fs.existsSync(bootstrapPath)) {
|
|
999
|
+
const bootstrap = await import('file://' + bootstrapPath.replace(/\\/g, '/'));
|
|
1000
|
+
if (bootstrap && typeof bootstrap.ensureReady === 'function') {
|
|
1001
|
+
const r = await bootstrap.ensureReady({ forceLatest: true });
|
|
1002
|
+
console.log(JSON.stringify(r || { ok: true }));
|
|
1003
|
+
process.exit(0);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
console.error('bootstrap.js not callable');
|
|
1007
|
+
process.exit(1);
|
|
1008
|
+
} catch (e) {
|
|
1009
|
+
console.error('bootstrap error:', e.message);
|
|
1010
|
+
process.exit(1);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
|
|
879
1014
|
if (args[0] === 'spool') {
|
|
880
1015
|
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
881
1016
|
const spoolDir = path.join(projectDir, '.gm', 'exec-spool');
|
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.1121",
|
|
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.1121"
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|
|
45
45
|
"node": ">=16.0.0"
|
package/skills/gm-skill/SKILL.md
CHANGED
|
@@ -10,9 +10,13 @@ The wasm artifact lives at `~/.claude/gm-tools/plugkit.wasm`; the spool watcher
|
|
|
10
10
|
|
|
11
11
|
## Boot the spool watcher (first turn only)
|
|
12
12
|
|
|
13
|
-
Check `.gm/exec-spool/.status.json`. If absent or `ts` > 15s old:
|
|
13
|
+
Check `.gm/exec-spool/.status.json`. If absent or `ts` > 15s old, boot via the npm package — `bun x gm-plugkit@latest spool` fetches the freshest plugkit (wasm + wrapper), copies them into `~/.claude/gm-tools/`, then enters spool mode:
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
```
|
|
16
|
+
bun x gm-plugkit@latest spool > /dev/null 2>&1 &
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
If `bun` is not available, fall back to `npx -y gm-plugkit@latest spool > /dev/null 2>&1 &` or to the local wrapper if it's already installed: `node ~/.claude/gm-tools/plugkit-wasm-wrapper.js spool > /dev/null 2>&1 &`. The wrapper has a self-heal: if it detects a `LinkError` or missing wasm at instantiation, it re-runs bootstrap automatically and retries.
|
|
16
20
|
|
|
17
21
|
Wait 2 seconds, verify `.status.json` is fresh. Then proceed.
|
|
18
22
|
|