gm-skill 2.0.1118 → 2.0.1119

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 CHANGED
@@ -28,7 +28,7 @@ npx gm-skill-bootstrap
28
28
 
29
29
  ## Version
30
30
 
31
- `2.0.1118` — auto-bumped from the canonical `gm` repo. Every push to `AnEntrypoint/gm` republishes this package alongside all 15 platform packages.
31
+ `2.0.1119` — 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1118",
3
+ "version": "2.0.1119",
4
4
  "description": "Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
@@ -10,9 +10,6 @@ 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');
@@ -127,16 +124,6 @@ function httpGet(url, timeoutMs) {
127
124
  }
128
125
 
129
126
  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
127
  let version = null;
141
128
  let source = null;
142
129
  try {
@@ -176,13 +163,8 @@ async function getLatestRemoteVersion() {
176
163
  } catch (e) {
177
164
  emitBootstrapEvent('warn', 'sha fetch failed; will verify after download', { error: e.message, version });
178
165
  }
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
166
  emitBootstrapEvent('info', 'Resolved latest plugkit version', { version, source, hasSha: Boolean(sha) });
185
- return payload;
167
+ return { version, sha, source };
186
168
  }
187
169
 
188
170
  function gitignorePath(cwd) { return path.join(cwd, '.gitignore'); }
@@ -460,21 +442,31 @@ async function spawnPlugkitWatcher(wasmPath) {
460
442
  }
461
443
  }
462
444
 
463
- async function bootstrapPlugkit(sessionId) {
445
+ async function bootstrapPlugkit(sessionId, options) {
464
446
  const startTime = Date.now();
447
+ const opts = options || {};
448
+ const forceLatest = Boolean(opts.latest);
465
449
 
466
450
  try {
467
- emitBootstrapEvent('info', 'Bootstrap started');
451
+ emitBootstrapEvent('info', 'Bootstrap started', { forceLatest });
468
452
 
469
453
  ensureManagedGitignore(process.cwd());
470
454
 
471
455
  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 });
456
+ let targetVersion = manifest.version;
457
+ let expectedHash = manifest.expectedHash;
458
+
459
+ if (forceLatest) {
460
+ const latest = await getLatestRemoteVersion();
461
+ if (latest && latest.version) {
462
+ targetVersion = latest.version;
463
+ expectedHash = latest.sha || expectedHash;
464
+ if (latest.version !== manifest.version) {
465
+ emitBootstrapEvent('info', 'forceLatest: using newer remote version', { latest: latest.version, manifest: manifest.version });
466
+ }
467
+ }
477
468
  }
469
+
478
470
  const installedVersion = getInstalledVersion();
479
471
  const plugkitPath = getPlugkitPath();
480
472
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-skill",
3
- "version": "2.0.1118",
3
+ "version": "2.0.1119",
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.1118"
42
+ "gm-plugkit": "^2.0.1119"
43
43
  },
44
44
  "engines": {
45
45
  "node": ">=16.0.0"
@@ -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.