@kairyou/agent-tools 0.23.0 → 0.23.1

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.
@@ -4,10 +4,12 @@
4
4
  agent-tools/
5
5
  ├── .claude-plugin/ # Claude Code/plugin ecosystem manifest.
6
6
  ├── .codex-plugin/ # Codex plugin manifest.
7
+ ├── .zcode-plugin/ # ZCode plugin manifest.
7
8
  ├── capabilities/ # Runtime capabilities, one directory each.
8
9
  │ ├── statusline/ # Agent status line: branch, model, usage.
9
10
  │ ├── usage/ # Provider balance / quota display.
10
- └── vision/ # Cross-model image understanding.
11
+ ├── vision/ # Cross-model image understanding and host adapters.
12
+ │ └── log/ # Session work-log hooks and plugins.
11
13
  ├── skills/ # Reusable Agent Skills.
12
14
  │ ├── workflow/ # Workflow-oriented skills.
13
15
  │ │ ├── at-commit/ # Conventional Commit message skill.
@@ -4,10 +4,12 @@
4
4
  agent-tools/
5
5
  ├── .claude-plugin/ # Claude Code/plugin 生态的 manifest.
6
6
  ├── .codex-plugin/ # Codex plugin manifest.
7
+ ├── .zcode-plugin/ # ZCode plugin manifest.
7
8
  ├── capabilities/ # Runtime capabilities, 一个一目录.
8
9
  │ ├── statusline/ # Agent 状态栏: 分支, 模型, 用量.
9
10
  │ ├── usage/ # Provider 余额/额度显示.
10
- └── vision/ # 跨模型识图.
11
+ ├── vision/ # 跨模型识图与各 Agent adapter.
12
+ │ └── log/ # Session work log hooks 与 plugins.
11
13
  ├── skills/ # 可复用的 Agent Skills.
12
14
  │ ├── workflow/ # 工作流类 skills.
13
15
  │ │ ├── at-commit/ # 生成 Conventional Commits message.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kairyou/agent-tools",
3
- "version": "0.23.0",
4
- "description": "Reusable Agent Skills, plus runtime capabilities (statusline, provider usage, vision) for Codex, Claude Code, and opencode.",
3
+ "version": "0.23.1",
4
+ "description": "Reusable Agent Skills plus per-agent runtime capabilities for compatible coding agents.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -15,6 +15,9 @@
15
15
  },
16
16
  "files": [
17
17
  "config.default.jsonc",
18
+ ".claude-plugin/",
19
+ ".codex-plugin/",
20
+ ".zcode-plugin/",
18
21
  "docs/",
19
22
  "dist/",
20
23
  "capabilities/",
@@ -25,14 +28,16 @@
25
28
  "jsonc-parser": "3.3.1"
26
29
  },
27
30
  "devDependencies": {
28
- "@modelcontextprotocol/sdk": "1.29.0",
29
- "esbuild": "0.28.1",
31
+ "@modelcontextprotocol/sdk": "1.30.0",
32
+ "esbuild": "0.28.2",
33
+ "typebox": "1.3.7",
30
34
  "zod": "4.4.3"
31
35
  },
32
36
  "scripts": {
33
37
  "build": "node scripts/build.mjs",
34
38
  "prepare": "npm run build",
35
- "test": "node --test tests/*.test.mjs",
39
+ "test": "node --test --test-concurrency=1 tests/*.test.mjs",
40
+ "version": "node scripts/sync-plugin-version.mjs",
36
41
  "claude-skills:fetch": "node tools/claude-skill-sync/cli.mjs fetch",
37
42
  "claude-skills:inspect": "node tools/claude-skill-sync/cli.mjs inspect",
38
43
  "claude-skills:apply": "node tools/claude-skill-sync/cli.mjs apply",
package/scripts/build.mjs CHANGED
@@ -33,6 +33,7 @@ const TARGETS = {
33
33
  entryPoints: {
34
34
  "mcp-server": path.join(ROOT, "capabilities", "vision", "mcp-server.mjs"),
35
35
  cli: path.join(ROOT, "capabilities", "vision", "lib", "cli.mjs"),
36
+ "pi-extension": path.join(ROOT, "capabilities", "vision", "pi-extension.mjs"),
36
37
  },
37
38
  },
38
39
  log: {
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  // agent-tools installer: wires statusline / usage into each agent's config.
3
3
  // Runtime-dependent skills are installed with their capability. Standalone
4
- // Standalone skills are still handled by `npx skills add`.
4
+ // skills are still handled by `npx skills add`.
5
5
  //
6
6
  // Capabilities (all global for now — they target the user-level config):
7
7
  // statusline Claude Code statusLine script (claude only).
8
- // usage Active API provider quota/balance (all agents).
9
- // vision inspect_image MCP server + at-vision skill (all agents).
10
- // log AI session work log via agent hooks (all agents).
8
+ // usage Active API provider quota/balance (Claude/Codex/OpenCode).
9
+ // vision inspect_image host adapter + at-vision skill.
10
+ // log AI session work log via agent hooks (Claude/Codex/OpenCode).
11
11
  //
12
12
  // Standalone commands (dispatched before capability parsing):
13
13
  // inspect-image <path|url> --question "..." Human diagnostic for vision.
@@ -22,6 +22,12 @@
22
22
  // ~/.codex/config.toml (vision MCP) + ~/.agents/skills (at-vision)
23
23
  // opencode -> ~/.config/opencode/ (usage + log plugins,
24
24
  // vision MCP in opencode.json + skills/at-vision)
25
+ // pi -> ~/.pi/agent/extensions/ (native vision tool)
26
+ // + ~/.pi/agent/skills/at-vision
27
+ // zcode -> ~/.zcode/cli/config.json (vision MCP)
28
+ // + ~/.zcode/skills/at-vision
29
+ // dsh -> ~/.dsh/cordis.patch.yml (official MCP client row)
30
+ // + ~/.dsh/skills/at-vision
25
31
  // Runtime scripts are copied into ~/.agent-tools so this installer can be
26
32
  // run via npx from GitHub without requiring a persistent local clone.
27
33
  //
@@ -32,7 +38,7 @@
32
38
  // agent-tools <capabilities> [options]
33
39
  //
34
40
  // Options:
35
- // -a, --agent <names> Target agents: claude | codex | opencode.
41
+ // -a, --agent <names> Target agents: claude | codex | opencode | pi | zcode | dsh.
36
42
  // Default: claude.
37
43
  // --settings <path> Override the Claude settings.json (for testing).
38
44
  // --codex-hooks <path> Override the Codex hooks.json (for testing).
@@ -41,6 +47,10 @@
41
47
  // --claude-skills-dir <p> Override ~/.claude/skills (for testing).
42
48
  // --codex-config <path> Override ~/.codex/config.toml (for testing).
43
49
  // --codex-skills-dir <p> Override ~/.agents/skills (for testing).
50
+ // --pi-agent-dir <path> Override ~/.pi/agent (for testing).
51
+ // --zcode-config <path> Override ~/.zcode/cli/config.json (for testing).
52
+ // --zcode-skills-dir <path> Override ~/.zcode/skills (for testing).
53
+ // --dsh-home <path> Override ~/.dsh (for testing).
44
54
  // --uninstall Remove what this installer added, restoring backups.
45
55
  // --dry-run Print planned changes without writing anything.
46
56
  // -h, --help Show this help.
@@ -92,11 +102,14 @@ const RUNTIME = {
92
102
  opencodeUsageTui: path.join(INSTALL_ROOT, "dist", "usage", "opencode-tui.mjs"),
93
103
  };
94
104
  const ALL_CAPS = ["statusline", "usage", "vision", "log"];
95
- const ALL_AGENTS = ["claude", "codex", "opencode"];
105
+ const ALL_AGENTS = ["claude", "codex", "opencode", "pi", "zcode", "dsh"];
96
106
  const AGENT_CAPS = {
97
107
  claude: ["statusline", "usage", "vision", "log"],
98
108
  codex: ["usage", "vision", "log"],
99
109
  opencode: ["usage", "vision", "log"],
110
+ pi: ["vision"],
111
+ zcode: ["vision"],
112
+ dsh: ["vision"],
100
113
  };
101
114
  const VISION_MCP_NAME = "agent-tools-vision";
102
115
  const VISION_SKILL_NAME = "at-vision";
@@ -300,6 +313,10 @@ function parseArgs(argv) {
300
313
  claudeSkillsDir: null,
301
314
  codexConfig: null,
302
315
  codexSkillsDir: null,
316
+ piAgentDir: null,
317
+ zcodeConfig: null,
318
+ zcodeSkillsDir: null,
319
+ dshHome: null,
303
320
  uninstall: false,
304
321
  dryRun: false,
305
322
  help: false,
@@ -334,6 +351,10 @@ function parseArgs(argv) {
334
351
  case "--claude-skills-dir": opts.claudeSkillsDir = argv[++i]; break;
335
352
  case "--codex-config": opts.codexConfig = argv[++i]; break;
336
353
  case "--codex-skills-dir": opts.codexSkillsDir = argv[++i]; break;
354
+ case "--pi-agent-dir": opts.piAgentDir = argv[++i]; break;
355
+ case "--zcode-config": opts.zcodeConfig = argv[++i]; break;
356
+ case "--zcode-skills-dir": opts.zcodeSkillsDir = argv[++i]; break;
357
+ case "--dsh-home": opts.dshHome = argv[++i]; break;
337
358
  case "--uninstall": opts.uninstall = true; break;
338
359
  case "--dry-run": opts.dryRun = true; break;
339
360
  case "-h":
@@ -535,6 +556,7 @@ const VISION_RATE_LIMIT_STATE = path.join(INSTALL_ROOT, "cache", "vision-rate-li
535
556
  const VISION_DIST_DIR = path.join(REPO_ROOT, "dist", "vision");
536
557
  const VISION_RUNTIME_SERVER = path.join(VISION_RUNTIME_DIR, "mcp-server.mjs");
537
558
  const VISION_RUNTIME_CLI = path.join(VISION_RUNTIME_DIR, "cli.mjs");
559
+ const VISION_RUNTIME_PI_EXTENSION = path.join(VISION_RUNTIME_DIR, "pi-extension.mjs");
538
560
  const VISION_SKILL_IDENTITY = Object.freeze({
539
561
  owner: "@kairyou/agent-tools",
540
562
  capability: "vision",
@@ -545,7 +567,7 @@ const USAGE_SKILL_IDENTITY = Object.freeze({
545
567
  capability: "usage",
546
568
  artifact: "skill",
547
569
  });
548
- // Vision is bundled at release time. Install atomically swaps two self-contained
570
+ // Vision is bundled at release time. Install atomically swaps self-contained
549
571
  // entry files into ~/.agent-tools/dist/vision, so hosts never run npm and a
550
572
  // failed update cannot destroy the previously working runtime.
551
573
  function visionMcpCommand() {
@@ -591,6 +613,16 @@ function isOpenCodeVisionMcp(value, mcp = visionMcpCommand()) {
591
613
  );
592
614
  }
593
615
 
616
+ function isZcodeVisionMcp(value, mcp = visionMcpCommand()) {
617
+ return Boolean(
618
+ value &&
619
+ value.command === mcp.command &&
620
+ Array.isArray(value.args) &&
621
+ value.args.length === 1 &&
622
+ isVisionServerPath(value.args[0])
623
+ );
624
+ }
625
+
594
626
  function installVisionRuntime(opts) {
595
627
  if (opts.uninstall) return;
596
628
  console.log(`vision runtime: ${VISION_RUNTIME_DIR}`);
@@ -598,7 +630,7 @@ function installVisionRuntime(opts) {
598
630
  console.log(` [dry-run] would atomically install ${VISION_DIST_DIR} -> ${VISION_RUNTIME_DIR}`);
599
631
  return;
600
632
  }
601
- for (const name of ["mcp-server.mjs", "cli.mjs"]) {
633
+ for (const name of ["mcp-server.mjs", "cli.mjs", "pi-extension.mjs"]) {
602
634
  if (!fs.existsSync(path.join(VISION_DIST_DIR, name))) {
603
635
  throw new Error(`Missing bundled vision runtime ${path.join(VISION_DIST_DIR, name)}. Run npm run build.`);
604
636
  }
@@ -616,7 +648,7 @@ function installVisionRuntime(opts) {
616
648
  JSON.stringify({ owner: "@kairyou/agent-tools", capability: "vision", version: 1 }, null, 2) + "\n",
617
649
  false
618
650
  );
619
- for (const name of ["mcp-server.mjs", "cli.mjs"]) {
651
+ for (const name of ["mcp-server.mjs", "cli.mjs", "pi-extension.mjs"]) {
620
652
  const check = spawnSync(process.execPath, ["--check", path.join(stage, name)], {
621
653
  encoding: "utf8",
622
654
  });
@@ -1123,6 +1155,192 @@ function runOpencode(opts) {
1123
1155
  if (!opts.dryRun) console.log(" NOTE: restart opencode to load the agent-tools usage plugin.");
1124
1156
  }
1125
1157
 
1158
+ // ---- Pi: native extension tool plus the capability-owned skill. ----
1159
+
1160
+ const PI_VISION_STUB_NAME = "agent-tools-vision.js";
1161
+
1162
+ function piAgentDir(opts) {
1163
+ return opts.piAgentDir || path.join(os.homedir(), ".pi", "agent");
1164
+ }
1165
+
1166
+ function piVisionStubContents() {
1167
+ return (
1168
+ "// Generated by agent-tools installer; do not edit.\n" +
1169
+ `export { default } from ${JSON.stringify(pathToFileURL(VISION_RUNTIME_PI_EXTENSION).href)};\n`
1170
+ );
1171
+ }
1172
+
1173
+ function isManagedPiVisionStub(file) {
1174
+ return fs.existsSync(file) && fs.readFileSync(file, "utf8") === piVisionStubContents();
1175
+ }
1176
+
1177
+ function runPi(opts) {
1178
+ if (!wants(opts, "vision")) return;
1179
+ const agentDir = piAgentDir(opts);
1180
+ const stub = path.join(agentDir, "extensions", PI_VISION_STUB_NAME);
1181
+ const skillsDir = path.join(agentDir, "skills");
1182
+ console.log(`pi vision: ${stub}`);
1183
+
1184
+ if (opts.uninstall) {
1185
+ if (isManagedPiVisionStub(stub)) removeFile(stub, opts.dryRun);
1186
+ else if (fs.existsSync(stub)) console.log(` kept unmanaged ${stub}`);
1187
+ else console.log(" no agent-tools vision extension found; nothing to remove.");
1188
+ installVisionSkillDir(skillsDir, { remove: true, dryRun: opts.dryRun });
1189
+ console.log(" - vision");
1190
+ return;
1191
+ }
1192
+
1193
+ if (fs.existsSync(stub) && !isManagedPiVisionStub(stub)) {
1194
+ throw new Error(
1195
+ `Refusing to overwrite existing unmanaged Pi extension ${stub}. ` +
1196
+ "Move or remove it, then re-run the install."
1197
+ );
1198
+ }
1199
+ writeText(stub, piVisionStubContents(), opts.dryRun);
1200
+ installVisionSkillDir(skillsDir, { remove: false, dryRun: opts.dryRun });
1201
+ console.log(" + vision (native inspect_image extension + at-vision skill)");
1202
+ if (!opts.dryRun) console.log(" NOTE: restart Pi or run /reload to load the agent-tools vision extension.");
1203
+ }
1204
+
1205
+ // ---- ZCode: native user MCP config plus the user-level skill. ----
1206
+
1207
+ function updateZcodeVisionMcp(file, { remove, dryRun, mcp }) {
1208
+ const exists = fs.existsSync(file);
1209
+ const currentText = exists ? fs.readFileSync(file, "utf8") : "{}\n";
1210
+ const errors = [];
1211
+ const current = parseJsonc(currentText, errors, { allowTrailingComma: true }) || {};
1212
+ if (errors.length > 0 || typeof current !== "object" || Array.isArray(current)) {
1213
+ throw new Error(`Cannot parse ${file} as JSONC`);
1214
+ }
1215
+ const existing = current.mcp?.servers?.[VISION_MCP_NAME];
1216
+ if (existing && !isZcodeVisionMcp(existing, mcp)) {
1217
+ if (remove) {
1218
+ console.log(` kept unmanaged ${VISION_MCP_NAME} entry in ${file}`);
1219
+ return;
1220
+ }
1221
+ throw new Error(
1222
+ `Found an existing unmanaged ${VISION_MCP_NAME} entry in ${file}. ` +
1223
+ "Remove or rename it, then re-run the install."
1224
+ );
1225
+ }
1226
+ const desired = remove ? undefined : { command: mcp.command, args: mcp.args, env: {} };
1227
+ if (JSON.stringify(existing) === JSON.stringify(desired)) {
1228
+ console.log(` kept existing ${file}`);
1229
+ return;
1230
+ }
1231
+ const eol = currentText.includes("\r\n") ? "\r\n" : "\n";
1232
+ const edits = modify(currentText, ["mcp", "servers", VISION_MCP_NAME], desired, {
1233
+ formattingOptions: { insertSpaces: true, tabSize: 2, eol },
1234
+ });
1235
+ const updated = applyEdits(currentText, edits).replace(/\s*$/, "") + eol;
1236
+ writeText(file, updated, dryRun);
1237
+ }
1238
+
1239
+ function runZcode(opts) {
1240
+ if (!wants(opts, "vision")) return;
1241
+ const file = opts.zcodeConfig || path.join(os.homedir(), ".zcode", "cli", "config.json");
1242
+ const skillsDir = opts.zcodeSkillsDir || path.join(os.homedir(), ".zcode", "skills");
1243
+ console.log(`zcode vision: ${file}`);
1244
+ updateZcodeVisionMcp(file, {
1245
+ remove: opts.uninstall,
1246
+ dryRun: opts.dryRun,
1247
+ mcp: visionMcpCommand(),
1248
+ });
1249
+ installVisionSkillDir(skillsDir, { remove: opts.uninstall, dryRun: opts.dryRun });
1250
+ console.log(opts.uninstall ? " - vision" : " + vision (inspect_image MCP + at-vision skill)");
1251
+ if (!opts.uninstall && !opts.dryRun) {
1252
+ console.log(" NOTE: start a new ZCode session to load the agent-tools vision MCP server.");
1253
+ }
1254
+ }
1255
+
1256
+ // ---- DeepSeek Harness: official MCP client row in the global Cordis patch. ----
1257
+
1258
+ const DSH_VISION_BEGIN = "# >>> agent-tools vision >>>";
1259
+ const DSH_VISION_END = "# <<< agent-tools vision <<<";
1260
+ const DSH_VISION_ROW_ID = "agent-tools-vision";
1261
+
1262
+ function dshHome(opts) {
1263
+ return opts.dshHome || process.env.DSH_HOME || path.join(os.homedir(), ".dsh");
1264
+ }
1265
+
1266
+ function dshVisionBlock(mcp) {
1267
+ return [
1268
+ DSH_VISION_BEGIN,
1269
+ "- insert:",
1270
+ ` - id: ${DSH_VISION_ROW_ID}`,
1271
+ " name: '@deepseek-ai/dsh-mcp-client'",
1272
+ " config:",
1273
+ ` serverName: ${DSH_VISION_ROW_ID}`,
1274
+ " transport: stdio",
1275
+ ` command: ${JSON.stringify(mcp.command)}`,
1276
+ ` args: [${mcp.args.map((arg) => JSON.stringify(arg)).join(", ")}]`,
1277
+ " env: {}",
1278
+ DSH_VISION_END,
1279
+ "",
1280
+ ].join("\n");
1281
+ }
1282
+
1283
+ function updateDshVisionPatch(file, { remove, dryRun, mcp }) {
1284
+ const current = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "[]\n";
1285
+ const escape = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1286
+ const blockRe = new RegExp(
1287
+ `\\r?\\n?${escape(DSH_VISION_BEGIN)}[\\s\\S]*?${escape(DSH_VISION_END)}\\r?\\n?`
1288
+ );
1289
+ const hadManagedBlock = blockRe.test(current);
1290
+ let base = current.replace(blockRe, "\n").trim();
1291
+ if (base !== "" && base !== "[]" && !base.startsWith("-")) {
1292
+ throw new Error(`Cannot update ${file}: the Cordis patch must be a top-level YAML array`);
1293
+ }
1294
+
1295
+ if (!remove && !hadManagedBlock) {
1296
+ const conflict = new RegExp(
1297
+ `^\\s*(?:-\\s*)?(?:id|serverName):\\s*['\"]?${escape(DSH_VISION_ROW_ID)}['\"]?\\s*$`,
1298
+ "m"
1299
+ );
1300
+ if (conflict.test(base)) {
1301
+ throw new Error(
1302
+ `Found an existing unmanaged ${DSH_VISION_ROW_ID} Cordis row in ${file}. ` +
1303
+ "Remove or rename it, then re-run the install."
1304
+ );
1305
+ }
1306
+ }
1307
+
1308
+ let next;
1309
+ if (remove) {
1310
+ if (!hadManagedBlock) {
1311
+ console.log(` no managed ${DSH_VISION_ROW_ID} Cordis row found; nothing to remove.`);
1312
+ return;
1313
+ }
1314
+ next = base === "" ? "[]\n" : `${base}\n`;
1315
+ } else {
1316
+ const block = dshVisionBlock(mcp);
1317
+ next = base === "" || base === "[]" ? block : `${base}\n\n${block}`;
1318
+ }
1319
+ if (next === current) {
1320
+ console.log(` kept existing ${file}`);
1321
+ return;
1322
+ }
1323
+ writeText(file, next, dryRun);
1324
+ }
1325
+
1326
+ function runDsh(opts) {
1327
+ if (!wants(opts, "vision")) return;
1328
+ const home = dshHome(opts);
1329
+ const patchFile = path.join(home, "cordis.patch.yml");
1330
+ const skillsDir = path.join(home, "skills");
1331
+ console.log(`dsh vision: ${patchFile}`);
1332
+ updateDshVisionPatch(patchFile, {
1333
+ remove: opts.uninstall,
1334
+ dryRun: opts.dryRun,
1335
+ mcp: visionMcpCommand(),
1336
+ });
1337
+ installVisionSkillDir(skillsDir, { remove: opts.uninstall, dryRun: opts.dryRun });
1338
+ console.log(opts.uninstall ? " - vision" : " + vision (official MCP client row + at-vision skill)");
1339
+ if (!opts.uninstall && !opts.dryRun) {
1340
+ console.log(" NOTE: restart DSH and wait for mcp__agent-tools-vision__inspect_image to appear.");
1341
+ }
1342
+ }
1343
+
1126
1344
  function visionRuntimeHasRemainingReference(opts) {
1127
1345
  const removed = new Set(opts.agents);
1128
1346
  if (!removed.has("claude")) {
@@ -1162,6 +1380,36 @@ function visionRuntimeHasRemainingReference(opts) {
1162
1380
  return true;
1163
1381
  }
1164
1382
  }
1383
+ if (!removed.has("pi")) {
1384
+ const stub = path.join(piAgentDir(opts), "extensions", PI_VISION_STUB_NAME);
1385
+ try {
1386
+ if (isManagedPiVisionStub(stub)) return true;
1387
+ } catch {
1388
+ return true;
1389
+ }
1390
+ }
1391
+ if (!removed.has("zcode")) {
1392
+ const file = opts.zcodeConfig || path.join(os.homedir(), ".zcode", "cli", "config.json");
1393
+ try {
1394
+ if (isZcodeVisionMcp(readJsonc(file).mcp?.servers?.[VISION_MCP_NAME])) return true;
1395
+ } catch {
1396
+ return true;
1397
+ }
1398
+ }
1399
+ if (!removed.has("dsh")) {
1400
+ const file = path.join(dshHome(opts), "cordis.patch.yml");
1401
+ try {
1402
+ const text = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "";
1403
+ const start = text.indexOf(DSH_VISION_BEGIN);
1404
+ const end = text.indexOf(DSH_VISION_END, start + DSH_VISION_BEGIN.length);
1405
+ if (start !== -1 && end !== -1) {
1406
+ const block = text.slice(start, end);
1407
+ if (block.includes(fwd(VISION_RUNTIME_SERVER))) return true;
1408
+ }
1409
+ } catch {
1410
+ return true;
1411
+ }
1412
+ }
1165
1413
  return false;
1166
1414
  }
1167
1415
 
@@ -1187,7 +1435,14 @@ function cleanupVisionRuntimeIfUnused(opts) {
1187
1435
  console.log(` removed unused vision runtime ${VISION_RUNTIME_DIR}`);
1188
1436
  }
1189
1437
 
1190
- const AGENTS = { claude: runClaude, codex: runCodex, opencode: runOpencode };
1438
+ const AGENTS = {
1439
+ claude: runClaude,
1440
+ codex: runCodex,
1441
+ opencode: runOpencode,
1442
+ pi: runPi,
1443
+ zcode: runZcode,
1444
+ dsh: runDsh,
1445
+ };
1191
1446
 
1192
1447
  function main() {
1193
1448
  const opts = parseArgs(process.argv.slice(2));
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const ROOT = fileURLToPath(new URL("..", import.meta.url));
8
+ const version = JSON.parse(fs.readFileSync(path.join(ROOT, "package.json"), "utf8")).version;
9
+ const targets = [
10
+ [".claude-plugin/plugin.json", []],
11
+ [".codex-plugin/plugin.json", []],
12
+ [".zcode-plugin/plugin.json", []],
13
+ ];
14
+
15
+ for (const [relativePath, parentPath] of targets) {
16
+ const file = path.join(ROOT, relativePath);
17
+ const document = JSON.parse(fs.readFileSync(file, "utf8"));
18
+ let target = document;
19
+ for (const key of parentPath) target = target[key];
20
+ target.version = version;
21
+ fs.writeFileSync(file, `${JSON.stringify(document, null, 2)}\n`);
22
+ }