gm-skill 2.0.1117 → 2.0.1118
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.json +1 -1
- package/lib/skill-bootstrap.js +184 -3
- package/package.json +2 -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.1118` — 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.json
CHANGED
package/lib/skill-bootstrap.js
CHANGED
|
@@ -10,6 +10,9 @@ 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';
|
|
13
16
|
const BOOTSTRAP_STATUS_FILE = path.join(os.homedir(), '.gm', 'bootstrap-status.json');
|
|
14
17
|
const BOOTSTRAP_ERROR_FILE = path.join(os.homedir(), '.gm', 'bootstrap-error.json');
|
|
15
18
|
const LOG_DIR = path.join(os.homedir(), '.claude', 'gm-log');
|
|
@@ -100,6 +103,172 @@ function computeFileHash(filePath) {
|
|
|
100
103
|
return crypto.createHash('sha256').update(content).digest('hex');
|
|
101
104
|
}
|
|
102
105
|
|
|
106
|
+
function httpGet(url, timeoutMs) {
|
|
107
|
+
return new Promise((resolve, reject) => {
|
|
108
|
+
const req = https.get(url, { timeout: timeoutMs, headers: { 'accept': 'application/json', 'user-agent': 'gm-skill-bootstrap' } }, (res) => {
|
|
109
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
110
|
+
res.resume();
|
|
111
|
+
httpGet(res.headers.location, timeoutMs).then(resolve, reject);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (res.statusCode !== 200) {
|
|
115
|
+
res.resume();
|
|
116
|
+
reject(new Error(`HTTP ${res.statusCode} ${url}`));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const chunks = [];
|
|
120
|
+
res.on('data', (c) => chunks.push(c));
|
|
121
|
+
res.on('end', () => resolve(Buffer.concat(chunks)));
|
|
122
|
+
res.on('error', reject);
|
|
123
|
+
});
|
|
124
|
+
req.on('timeout', () => { req.destroy(new Error(`timeout ${timeoutMs}ms ${url}`)); });
|
|
125
|
+
req.on('error', reject);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
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
|
+
let version = null;
|
|
141
|
+
let source = null;
|
|
142
|
+
try {
|
|
143
|
+
const buf = await httpGet('https://api.github.com/repos/AnEntrypoint/plugkit-bin/releases/latest', 3000);
|
|
144
|
+
const rel = JSON.parse(buf.toString('utf-8'));
|
|
145
|
+
const tag = rel && rel.tag_name;
|
|
146
|
+
if (tag) {
|
|
147
|
+
version = tag.replace(/^v/, '');
|
|
148
|
+
source = 'github-releases';
|
|
149
|
+
}
|
|
150
|
+
} catch (e) {
|
|
151
|
+
emitBootstrapEvent('warn', 'GitHub Releases lookup failed', { error: e.message });
|
|
152
|
+
}
|
|
153
|
+
if (!version) {
|
|
154
|
+
try {
|
|
155
|
+
const buf = await httpGet('https://registry.npmjs.org/gm-plugkit/latest', 3000);
|
|
156
|
+
const pkg = JSON.parse(buf.toString('utf-8'));
|
|
157
|
+
if (pkg && pkg.plugkitVersion) {
|
|
158
|
+
version = pkg.plugkitVersion;
|
|
159
|
+
source = 'npm-gm-plugkit';
|
|
160
|
+
} else if (pkg && pkg.version) {
|
|
161
|
+
version = pkg.version;
|
|
162
|
+
source = 'npm-gm-plugkit-fallback';
|
|
163
|
+
}
|
|
164
|
+
} catch (e) {
|
|
165
|
+
emitBootstrapEvent('warn', 'npm fallback lookup failed', { error: e.message });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (!version) {
|
|
169
|
+
emitBootstrapEvent('warn', 'All latest-version lookups failed; falling back to manifest');
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
let sha = '';
|
|
173
|
+
try {
|
|
174
|
+
const shaBuf = await httpGet(`https://github.com/AnEntrypoint/plugkit-bin/releases/download/v${version}/plugkit.wasm.sha256`, 3000);
|
|
175
|
+
sha = shaBuf.toString('utf-8').trim().split(/\s+/)[0];
|
|
176
|
+
} catch (e) {
|
|
177
|
+
emitBootstrapEvent('warn', 'sha fetch failed; will verify after download', { error: e.message, version });
|
|
178
|
+
}
|
|
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
|
+
emitBootstrapEvent('info', 'Resolved latest plugkit version', { version, source, hasSha: Boolean(sha) });
|
|
185
|
+
return payload;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function gitignorePath(cwd) { return path.join(cwd, '.gitignore'); }
|
|
189
|
+
|
|
190
|
+
function getManagedGitignoreEntries() {
|
|
191
|
+
return [
|
|
192
|
+
'.gm/exec-spool/',
|
|
193
|
+
'.gm/gm-fired-*',
|
|
194
|
+
'.gm/needs-gm',
|
|
195
|
+
'.gm/lastskill',
|
|
196
|
+
'.gm/turn-state.json',
|
|
197
|
+
'.gm/turn-state.json.corrupted-*',
|
|
198
|
+
'.gm/residual-check-fired',
|
|
199
|
+
'.gm/bootstrap-status.json',
|
|
200
|
+
'.gm/bootstrap-error.json',
|
|
201
|
+
'.gm/rslearn-counter.json',
|
|
202
|
+
'.gm/trajectory-drafts/',
|
|
203
|
+
'.gm/ingest-drafts/',
|
|
204
|
+
'.gm/prd-state.json',
|
|
205
|
+
'.gm/subagent-*.json',
|
|
206
|
+
'.plugkit-browser-profile/',
|
|
207
|
+
'.plugkit-browser-profile-*/',
|
|
208
|
+
];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function getMustStayTracked() {
|
|
212
|
+
return [
|
|
213
|
+
'.gm/rs-learn.db',
|
|
214
|
+
'.gm/code-search/',
|
|
215
|
+
'.gm/disciplines/',
|
|
216
|
+
'.gm/prd.yml',
|
|
217
|
+
'.gm/mutables.yml',
|
|
218
|
+
'gm-data/rs-learn.db',
|
|
219
|
+
'gm-data/code-search/',
|
|
220
|
+
'gm-data/disciplines/',
|
|
221
|
+
];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function ensureManagedGitignore(cwd) {
|
|
225
|
+
try {
|
|
226
|
+
const gi = gitignorePath(cwd);
|
|
227
|
+
let content = '';
|
|
228
|
+
try { content = fs.readFileSync(gi, 'utf-8'); } catch (_) {}
|
|
229
|
+
const START = '# >>> plugkit managed';
|
|
230
|
+
const END = '# <<< plugkit managed';
|
|
231
|
+
const entries = getManagedGitignoreEntries();
|
|
232
|
+
const block = [START, ...entries, END].join('\n');
|
|
233
|
+
const startIdx = content.indexOf(START);
|
|
234
|
+
const endIdx = content.indexOf(END);
|
|
235
|
+
let cleaned;
|
|
236
|
+
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
237
|
+
const before = content.slice(0, startIdx).replace(/\n+$/, '');
|
|
238
|
+
const after = content.slice(endIdx + END.length).replace(/^\n+/, '');
|
|
239
|
+
cleaned = [before, block, after].filter(Boolean).join('\n');
|
|
240
|
+
} else {
|
|
241
|
+
cleaned = content.replace(/\n+$/, '');
|
|
242
|
+
cleaned = cleaned ? `${cleaned}\n\n${block}` : block;
|
|
243
|
+
}
|
|
244
|
+
if (!cleaned.endsWith('\n')) cleaned += '\n';
|
|
245
|
+
if (cleaned !== content) {
|
|
246
|
+
fs.writeFileSync(gi, cleaned);
|
|
247
|
+
emitBootstrapEvent('info', 'Managed .gitignore block updated', { path: gi, entries: entries.length });
|
|
248
|
+
}
|
|
249
|
+
const mustTrack = getMustStayTracked();
|
|
250
|
+
const lines = cleaned.split(/\r?\n/);
|
|
251
|
+
const inManaged = (idx) => {
|
|
252
|
+
let inside = false;
|
|
253
|
+
for (let i = 0; i <= idx; i++) {
|
|
254
|
+
if (lines[i] === START) inside = true;
|
|
255
|
+
else if (lines[i] === END) inside = false;
|
|
256
|
+
}
|
|
257
|
+
return inside;
|
|
258
|
+
};
|
|
259
|
+
for (let i = 0; i < lines.length; i++) {
|
|
260
|
+
const t = lines[i].trim();
|
|
261
|
+
if (!t || t.startsWith('#')) continue;
|
|
262
|
+
if (inManaged(i)) continue;
|
|
263
|
+
if (mustTrack.includes(t)) {
|
|
264
|
+
emitBootstrapEvent('warn', 'Hostile .gitignore entry — must stay tracked', { entry: t, line: i + 1 });
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
} catch (e) {
|
|
268
|
+
emitBootstrapEvent('warn', 'ensureManagedGitignore failed', { error: e.message });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
103
272
|
async function downloadPlugkitBinary(version) {
|
|
104
273
|
const binaryName = 'plugkit.wasm';
|
|
105
274
|
const url = `https://github.com/AnEntrypoint/plugkit-bin/releases/download/v${version}/${binaryName}`;
|
|
@@ -297,11 +466,20 @@ async function bootstrapPlugkit(sessionId) {
|
|
|
297
466
|
try {
|
|
298
467
|
emitBootstrapEvent('info', 'Bootstrap started');
|
|
299
468
|
|
|
300
|
-
|
|
469
|
+
ensureManagedGitignore(process.cwd());
|
|
470
|
+
|
|
471
|
+
const manifest = readManifest();
|
|
472
|
+
const latest = await getLatestRemoteVersion();
|
|
473
|
+
const targetVersion = (latest && latest.version) || manifest.version;
|
|
474
|
+
const expectedHash = (latest && latest.sha) || manifest.expectedHash;
|
|
475
|
+
if (latest && latest.version !== manifest.version) {
|
|
476
|
+
emitBootstrapEvent('info', 'Latest plugkit newer than manifest pin', { latest: latest.version, manifest: manifest.version });
|
|
477
|
+
}
|
|
301
478
|
const installedVersion = getInstalledVersion();
|
|
302
479
|
const plugkitPath = getPlugkitPath();
|
|
303
480
|
|
|
304
|
-
const
|
|
481
|
+
const manifestVersion = targetVersion;
|
|
482
|
+
const versionMismatch = installedVersion !== targetVersion;
|
|
305
483
|
const binaryMissing = !fs.existsSync(plugkitPath);
|
|
306
484
|
|
|
307
485
|
if (!binaryMissing && !versionMismatch) {
|
|
@@ -347,9 +525,12 @@ async function bootstrapPlugkit(sessionId) {
|
|
|
347
525
|
|
|
348
526
|
if (binaryData) {
|
|
349
527
|
const downloadedHash = crypto.createHash('sha256').update(binaryData).digest('hex');
|
|
350
|
-
if (downloadedHash !== expectedHash) {
|
|
528
|
+
if (expectedHash && downloadedHash !== expectedHash) {
|
|
351
529
|
throw new Error(`Hash mismatch: got ${downloadedHash}, expected ${expectedHash}`);
|
|
352
530
|
}
|
|
531
|
+
if (!expectedHash) {
|
|
532
|
+
emitBootstrapEvent('warn', 'No expected hash; trusting npm-resolved download', { sha: downloadedHash, version: manifestVersion });
|
|
533
|
+
}
|
|
353
534
|
|
|
354
535
|
killExistingPlugkit();
|
|
355
536
|
await writeBinaryWithRetry(plugkitPath, binaryData);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-skill",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1118",
|
|
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.1118"
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|
|
45
45
|
"node": ">=16.0.0"
|