@caddis/cli 0.2.6 → 0.3.0

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.
Files changed (51) hide show
  1. package/bundles/antigravity-plugin/agents/claude-md-curator.md +1 -1
  2. package/bundles/antigravity-plugin/caddis_statusline.py +803 -0
  3. package/bundles/antigravity-plugin/claude-md/folder-agents.md.tmpl +21 -0
  4. package/bundles/antigravity-plugin/guard_agy.py +23 -4
  5. package/bundles/antigravity-plugin/plugin.json +1 -1
  6. package/bundles/antigravity-plugin/scripts/caddis_gate.py +524 -0
  7. package/bundles/antigravity-plugin/scripts/caddis_inventory.py +369 -0
  8. package/bundles/antigravity-plugin/scripts/caddis_migrate_dir.py +270 -0
  9. package/bundles/antigravity-plugin/scripts/caddis_spawn.py +300 -0
  10. package/bundles/antigravity-plugin/scripts/caddis_statusline.py +803 -0
  11. package/bundles/antigravity-plugin/scripts/caddis_tidy.py +366 -0
  12. package/bundles/antigravity-plugin/scripts/check_doc_coverage.py +924 -0
  13. package/bundles/antigravity-plugin/scripts/claude-deepseek.ps1 +4 -0
  14. package/bundles/antigravity-plugin/scripts/claude-deepseek.sh +3 -0
  15. package/bundles/antigravity-plugin/scripts/claude-glm.ps1 +4 -0
  16. package/bundles/antigravity-plugin/scripts/claude-glm.sh +3 -0
  17. package/bundles/antigravity-plugin/scripts/claude-oss.ps1 +64 -0
  18. package/bundles/antigravity-plugin/scripts/claude-oss.sh +60 -0
  19. package/bundles/antigravity-plugin/scripts/claudster_config.py +126 -0
  20. package/bundles/antigravity-plugin/scripts/claudster_doctor.py +356 -0
  21. package/bundles/antigravity-plugin/scripts/claudster_init.py +400 -0
  22. package/bundles/antigravity-plugin/scripts/claudster_migrate_rules.py +300 -0
  23. package/bundles/antigravity-plugin/scripts/dream_capture.py +249 -0
  24. package/bundles/antigravity-plugin/scripts/dream_memory.py +438 -0
  25. package/bundles/antigravity-plugin/scripts/enforcement_inventory.py +261 -0
  26. package/bundles/antigravity-plugin/scripts/hook_log.py +156 -0
  27. package/bundles/antigravity-plugin/scripts/oss_ask.py +98 -0
  28. package/bundles/antigravity-plugin/scripts/oss_model.py +166 -0
  29. package/bundles/antigravity-plugin/scripts/oss_review.py +718 -0
  30. package/bundles/antigravity-plugin/scripts/setup_project_ai.py +1429 -0
  31. package/bundles/antigravity-plugin/scripts/usage_review.py +1323 -0
  32. package/bundles/antigravity-plugin/session_end_agy.py +21 -2
  33. package/bundles/antigravity-plugin/skills/_registry.md +1 -0
  34. package/bundles/antigravity-plugin/skills/fastapi-dev/SKILL.md +1 -1
  35. package/bundles/antigravity-plugin/skills/ship/SKILL.md +1 -1
  36. package/bundles/antigravity-plugin/skills/ship-merge/SKILL.md +1 -1
  37. package/bundles/antigravity-plugin/skills/ship-pr/SKILL.md +1 -1
  38. package/bundles/antigravity-plugin/skills/statusline/SKILL.md +105 -0
  39. package/bundles/antigravity-plugin/skills/use-model/SKILL.md +15 -3
  40. package/bundles/antigravity-plugin/warm_start_agy.py +21 -2
  41. package/bundles/antigravity-plugin-extras/plugin.json +1 -1
  42. package/bundles/antigravity-plugin-extras/skills/architecture-design/SKILL.md +1 -1
  43. package/bundles/antigravity-plugin-extras/skills/caching-patterns/SKILL.md +1 -1
  44. package/bundles/antigravity-plugin-extras/skills/webapp-development/SKILL.md +1 -1
  45. package/bundles/manifest.json +3 -3
  46. package/dist/claude-deepseek.js +544 -0
  47. package/dist/claude-glm.js +544 -0
  48. package/dist/claude-oss.js +544 -0
  49. package/dist/cli.js +365 -44
  50. package/package.json +5 -2
  51. package/bundles/antigravity-plugin/statusline-command-agy.sh +0 -91
package/dist/cli.js CHANGED
@@ -2419,8 +2419,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
2419
2419
  if (index < this.args.length) {
2420
2420
  value = this.args.slice(index);
2421
2421
  if (declaredArg.parseArg) {
2422
- value = value.reduce((processed, v) => {
2423
- return myParseArg(declaredArg, v, processed);
2422
+ value = value.reduce((processed, v2) => {
2423
+ return myParseArg(declaredArg, v2, processed);
2424
2424
  }, declaredArg.defaultValue);
2425
2425
  }
2426
2426
  } else if (value === void 0) {
@@ -3596,7 +3596,7 @@ var require_index_min = __commonJS({
3596
3596
  "use strict";
3597
3597
  Object.defineProperty(h2, "__esModule", { value: true });
3598
3598
  });
3599
- var v = exports && exports.__createBinding || (Object.create ? (function(t, e2, r2, s) {
3599
+ var v2 = exports && exports.__createBinding || (Object.create ? (function(t, e2, r2, s) {
3600
3600
  s === void 0 && (s = r2);
3601
3601
  var n = Object.getOwnPropertyDescriptor(e2, r2);
3602
3602
  (!n || ("get" in n ? !e2.__esModule : n.writable || n.configurable)) && (n = { enumerable: true, get: function() {
@@ -3621,12 +3621,12 @@ var require_index_min = __commonJS({
3621
3621
  return function(e2) {
3622
3622
  if (e2 && e2.__esModule) return e2;
3623
3623
  var r2 = {};
3624
- if (e2 != null) for (var s = t(e2), n = 0; n < s.length; n++) s[n] !== "default" && v(r2, e2, s[n]);
3624
+ if (e2 != null) for (var s = t(e2), n = 0; n < s.length; n++) s[n] !== "default" && v2(r2, e2, s[n]);
3625
3625
  return G2(r2, e2), r2;
3626
3626
  };
3627
3627
  })();
3628
3628
  var X2 = exports && exports.__exportStar || function(t, e2) {
3629
- for (var r2 in t) r2 !== "default" && !Object.prototype.hasOwnProperty.call(e2, r2) && v(e2, t, r2);
3629
+ for (var r2 in t) r2 !== "default" && !Object.prototype.hasOwnProperty.call(e2, r2) && v2(e2, t, r2);
3630
3630
  };
3631
3631
  Object.defineProperty(exports, "__esModule", { value: true });
3632
3632
  exports.sync = exports.isexe = exports.posix = exports.win32 = void 0;
@@ -3647,7 +3647,7 @@ var require_lib = __commonJS({
3647
3647
  "node_modules/which/lib/index.js"(exports, module) {
3648
3648
  "use strict";
3649
3649
  var { isexe, sync: isexeSync } = require_index_min();
3650
- var { join, delimiter, sep, posix } = __require("path");
3650
+ var { join: join2, delimiter, sep, posix } = __require("path");
3651
3651
  var isWindows = process.platform === "win32";
3652
3652
  var rSlash = new RegExp(`[${posix.sep}${sep === posix.sep ? "" : sep}]`.replace(/(\\)/g, "\\$1"));
3653
3653
  var rRel = new RegExp(`^\\.${rSlash.source}`);
@@ -3676,7 +3676,7 @@ var require_lib = __commonJS({
3676
3676
  var getPathPart = (raw, cmd) => {
3677
3677
  const pathPart = /^".*"$/.test(raw) ? raw.slice(1, -1) : raw;
3678
3678
  const prefix = !pathPart && rRel.test(cmd) ? cmd.slice(0, 2) : "";
3679
- return prefix + join(pathPart, cmd);
3679
+ return prefix + join2(pathPart, cmd);
3680
3680
  };
3681
3681
  var which = async (cmd, opt = {}) => {
3682
3682
  const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
@@ -4087,11 +4087,12 @@ function fD({ input: e2 = j, output: u2 = M, overwrite: t = true, hideCursor: F2
4087
4087
  e2.off("keypress", i), F2 && u2.write(import_sisteransi.cursor.show), e2.isTTY && !AD && e2.setRawMode(false), s.terminal = false, s.close();
4088
4088
  };
4089
4089
  }
4090
- var import_sisteransi, uD, W, tD, eD, FD, sD, w, N, I, R, r, iD, CD, ED, d, oD, y, V, nD, G, _, z, K, aD, k, hD, lD, xD, B, AD, S, gD, vD, h, x, dD, A;
4090
+ var import_sisteransi, import_picocolors2, uD, W, tD, eD, FD, sD, w, N, I, R, r, iD, CD, ED, d, oD, y, V, nD, G, _, z, K, aD, k, hD, lD, xD, B, AD, S, gD, vD, h, x, dD, A, TD, jD, U, MD;
4091
4091
  var init_dist = __esm({
4092
4092
  "node_modules/@clack/core/dist/index.mjs"() {
4093
4093
  "use strict";
4094
4094
  import_sisteransi = __toESM(require_src(), 1);
4095
+ import_picocolors2 = __toESM(require_picocolors(), 1);
4095
4096
  uD = DD();
4096
4097
  W = { exports: {} };
4097
4098
  (function(e2) {
@@ -4324,6 +4325,28 @@ var init_dist = __esm({
4324
4325
  }
4325
4326
  };
4326
4327
  A = /* @__PURE__ */ new WeakMap();
4328
+ TD = Object.defineProperty;
4329
+ jD = (e2, u2, t) => u2 in e2 ? TD(e2, u2, { enumerable: true, configurable: true, writable: true, value: t }) : e2[u2] = t;
4330
+ U = (e2, u2, t) => (jD(e2, typeof u2 != "symbol" ? u2 + "" : u2, t), t);
4331
+ MD = class extends x {
4332
+ constructor({ mask: u2, ...t }) {
4333
+ super(t), U(this, "valueWithCursor", ""), U(this, "_mask", "\u2022"), this._mask = u2 ?? "\u2022", this.on("finalize", () => {
4334
+ this.valueWithCursor = this.masked;
4335
+ }), this.on("value", () => {
4336
+ if (this.cursor >= this.value.length) this.valueWithCursor = `${this.masked}${import_picocolors2.default.inverse(import_picocolors2.default.hidden("_"))}`;
4337
+ else {
4338
+ const F2 = this.masked.slice(0, this.cursor), s = this.masked.slice(this.cursor);
4339
+ this.valueWithCursor = `${F2}${import_picocolors2.default.inverse(s[0])}${s.slice(1)}`;
4340
+ }
4341
+ });
4342
+ }
4343
+ get cursor() {
4344
+ return this._cursor;
4345
+ }
4346
+ get masked() {
4347
+ return this.value.replaceAll(/./g, this._mask);
4348
+ }
4349
+ };
4327
4350
  }
4328
4351
  });
4329
4352
 
@@ -4333,13 +4356,13 @@ import y2 from "process";
4333
4356
  function ce() {
4334
4357
  return y2.platform !== "win32" ? y2.env.TERM !== "linux" : !!y2.env.CI || !!y2.env.WT_SESSION || !!y2.env.TERMINUS_SUBLIME || y2.env.ConEmuTask === "{cmd::Cmder}" || y2.env.TERM_PROGRAM === "Terminus-Sublime" || y2.env.TERM_PROGRAM === "vscode" || y2.env.TERM === "xterm-256color" || y2.env.TERM === "alacritty" || y2.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
4335
4358
  }
4336
- var import_picocolors2, import_sisteransi2, V2, u, le, L2, W2, C, ue, o, d2, k2, P2, A2, T, F, $e, _2, me, de, pe, q, D, U, K2, b2, ye, xe, Ie, Se, J, Y2;
4359
+ var import_picocolors3, import_sisteransi2, V2, u, le, L2, W2, C, ue, o, d2, k2, P2, A2, T, F, $e, _2, me, de, pe, q, D, U2, K2, b2, ge, ye, xe, Ie, Se, J, Y2;
4337
4360
  var init_dist2 = __esm({
4338
4361
  "node_modules/@clack/prompts/dist/index.mjs"() {
4339
4362
  "use strict";
4340
4363
  init_dist();
4341
4364
  init_dist();
4342
- import_picocolors2 = __toESM(require_picocolors(), 1);
4365
+ import_picocolors3 = __toESM(require_picocolors(), 1);
4343
4366
  import_sisteransi2 = __toESM(require_src(), 1);
4344
4367
  V2 = ce();
4345
4368
  u = (t, n) => V2 ? t : n;
@@ -4362,66 +4385,87 @@ var init_dist2 = __esm({
4362
4385
  pe = u("\u256F", "+");
4363
4386
  q = u("\u25CF", "\u2022");
4364
4387
  D = u("\u25C6", "*");
4365
- U = u("\u25B2", "!");
4388
+ U2 = u("\u25B2", "!");
4366
4389
  K2 = u("\u25A0", "x");
4367
4390
  b2 = (t) => {
4368
4391
  switch (t) {
4369
4392
  case "initial":
4370
4393
  case "active":
4371
- return import_picocolors2.default.cyan(le);
4394
+ return import_picocolors3.default.cyan(le);
4372
4395
  case "cancel":
4373
- return import_picocolors2.default.red(L2);
4396
+ return import_picocolors3.default.red(L2);
4374
4397
  case "error":
4375
- return import_picocolors2.default.yellow(W2);
4398
+ return import_picocolors3.default.yellow(W2);
4376
4399
  case "submit":
4377
- return import_picocolors2.default.green(C);
4400
+ return import_picocolors3.default.green(C);
4378
4401
  }
4379
4402
  };
4403
+ ge = (t) => new MD({ validate: t.validate, mask: t.mask ?? $e, render() {
4404
+ const n = `${import_picocolors3.default.gray(o)}
4405
+ ${b2(this.state)} ${t.message}
4406
+ `, r2 = this.valueWithCursor, i = this.masked;
4407
+ switch (this.state) {
4408
+ case "error":
4409
+ return `${n.trim()}
4410
+ ${import_picocolors3.default.yellow(o)} ${i}
4411
+ ${import_picocolors3.default.yellow(d2)} ${import_picocolors3.default.yellow(this.error)}
4412
+ `;
4413
+ case "submit":
4414
+ return `${n}${import_picocolors3.default.gray(o)} ${import_picocolors3.default.dim(i)}`;
4415
+ case "cancel":
4416
+ return `${n}${import_picocolors3.default.gray(o)} ${import_picocolors3.default.strikethrough(import_picocolors3.default.dim(i ?? ""))}${i ? `
4417
+ ${import_picocolors3.default.gray(o)}` : ""}`;
4418
+ default:
4419
+ return `${n}${import_picocolors3.default.cyan(o)} ${r2}
4420
+ ${import_picocolors3.default.cyan(d2)}
4421
+ `;
4422
+ }
4423
+ } }).prompt();
4380
4424
  ye = (t) => {
4381
4425
  const n = t.active ?? "Yes", r2 = t.inactive ?? "No";
4382
4426
  return new dD({ active: n, inactive: r2, initialValue: t.initialValue ?? true, render() {
4383
- const i = `${import_picocolors2.default.gray(o)}
4427
+ const i = `${import_picocolors3.default.gray(o)}
4384
4428
  ${b2(this.state)} ${t.message}
4385
4429
  `, s = this.value ? n : r2;
4386
4430
  switch (this.state) {
4387
4431
  case "submit":
4388
- return `${i}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.dim(s)}`;
4432
+ return `${i}${import_picocolors3.default.gray(o)} ${import_picocolors3.default.dim(s)}`;
4389
4433
  case "cancel":
4390
- return `${i}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(s))}
4391
- ${import_picocolors2.default.gray(o)}`;
4434
+ return `${i}${import_picocolors3.default.gray(o)} ${import_picocolors3.default.strikethrough(import_picocolors3.default.dim(s))}
4435
+ ${import_picocolors3.default.gray(o)}`;
4392
4436
  default:
4393
- return `${i}${import_picocolors2.default.cyan(o)} ${this.value ? `${import_picocolors2.default.green(k2)} ${n}` : `${import_picocolors2.default.dim(P2)} ${import_picocolors2.default.dim(n)}`} ${import_picocolors2.default.dim("/")} ${this.value ? `${import_picocolors2.default.dim(P2)} ${import_picocolors2.default.dim(r2)}` : `${import_picocolors2.default.green(k2)} ${r2}`}
4394
- ${import_picocolors2.default.cyan(d2)}
4437
+ return `${i}${import_picocolors3.default.cyan(o)} ${this.value ? `${import_picocolors3.default.green(k2)} ${n}` : `${import_picocolors3.default.dim(P2)} ${import_picocolors3.default.dim(n)}`} ${import_picocolors3.default.dim("/")} ${this.value ? `${import_picocolors3.default.dim(P2)} ${import_picocolors3.default.dim(r2)}` : `${import_picocolors3.default.green(k2)} ${r2}`}
4438
+ ${import_picocolors3.default.cyan(d2)}
4395
4439
  `;
4396
4440
  }
4397
4441
  } }).prompt();
4398
4442
  };
4399
4443
  xe = (t = "") => {
4400
- process.stdout.write(`${import_picocolors2.default.gray(d2)} ${import_picocolors2.default.red(t)}
4444
+ process.stdout.write(`${import_picocolors3.default.gray(d2)} ${import_picocolors3.default.red(t)}
4401
4445
 
4402
4446
  `);
4403
4447
  };
4404
4448
  Ie = (t = "") => {
4405
- process.stdout.write(`${import_picocolors2.default.gray(ue)} ${t}
4449
+ process.stdout.write(`${import_picocolors3.default.gray(ue)} ${t}
4406
4450
  `);
4407
4451
  };
4408
4452
  Se = (t = "") => {
4409
- process.stdout.write(`${import_picocolors2.default.gray(o)}
4410
- ${import_picocolors2.default.gray(d2)} ${t}
4453
+ process.stdout.write(`${import_picocolors3.default.gray(o)}
4454
+ ${import_picocolors3.default.gray(d2)} ${t}
4411
4455
 
4412
4456
  `);
4413
4457
  };
4414
- J = `${import_picocolors2.default.gray(o)} `;
4458
+ J = `${import_picocolors3.default.gray(o)} `;
4415
4459
  Y2 = ({ indicator: t = "dots" } = {}) => {
4416
4460
  const n = V2 ? ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] : ["\u2022", "o", "O", "0"], r2 = V2 ? 80 : 120, i = process.env.CI === "true";
4417
4461
  let s, c, a = false, l2 = "", $2, g2 = performance.now();
4418
4462
  const p2 = (m2) => {
4419
4463
  const h2 = m2 > 1 ? "Something went wrong" : "Canceled";
4420
4464
  a && N2(h2, m2);
4421
- }, v = () => p2(2), f = () => p2(1), j2 = () => {
4422
- process.on("uncaughtExceptionMonitor", v), process.on("unhandledRejection", v), process.on("SIGINT", f), process.on("SIGTERM", f), process.on("exit", p2);
4465
+ }, v2 = () => p2(2), f = () => p2(1), j2 = () => {
4466
+ process.on("uncaughtExceptionMonitor", v2), process.on("unhandledRejection", v2), process.on("SIGINT", f), process.on("SIGTERM", f), process.on("exit", p2);
4423
4467
  }, E = () => {
4424
- process.removeListener("uncaughtExceptionMonitor", v), process.removeListener("unhandledRejection", v), process.removeListener("SIGINT", f), process.removeListener("SIGTERM", f), process.removeListener("exit", p2);
4468
+ process.removeListener("uncaughtExceptionMonitor", v2), process.removeListener("unhandledRejection", v2), process.removeListener("SIGINT", f), process.removeListener("SIGTERM", f), process.removeListener("exit", p2);
4425
4469
  }, B2 = () => {
4426
4470
  if ($2 === void 0) return;
4427
4471
  i && process.stdout.write(`
@@ -4433,13 +4477,13 @@ ${import_picocolors2.default.gray(d2)} ${t}
4433
4477
  const h2 = (performance.now() - m2) / 1e3, w2 = Math.floor(h2 / 60), I2 = Math.floor(h2 % 60);
4434
4478
  return w2 > 0 ? `[${w2}m ${I2}s]` : `[${I2}s]`;
4435
4479
  }, H = (m2 = "") => {
4436
- a = true, s = fD(), l2 = R2(m2), g2 = performance.now(), process.stdout.write(`${import_picocolors2.default.gray(o)}
4480
+ a = true, s = fD(), l2 = R2(m2), g2 = performance.now(), process.stdout.write(`${import_picocolors3.default.gray(o)}
4437
4481
  `);
4438
4482
  let h2 = 0, w2 = 0;
4439
4483
  j2(), c = setInterval(() => {
4440
4484
  if (i && l2 === $2) return;
4441
4485
  B2(), $2 = l2;
4442
- const I2 = import_picocolors2.default.magenta(n[h2]);
4486
+ const I2 = import_picocolors3.default.magenta(n[h2]);
4443
4487
  if (i) process.stdout.write(`${I2} ${l2}...`);
4444
4488
  else if (t === "timer") process.stdout.write(`${I2} ${l2} ${O2(g2)}`);
4445
4489
  else {
@@ -4450,7 +4494,7 @@ ${import_picocolors2.default.gray(d2)} ${t}
4450
4494
  }, r2);
4451
4495
  }, N2 = (m2 = "", h2 = 0) => {
4452
4496
  a = false, clearInterval(c), B2();
4453
- const w2 = h2 === 0 ? import_picocolors2.default.green(C) : h2 === 1 ? import_picocolors2.default.red(L2) : import_picocolors2.default.red(W2);
4497
+ const w2 = h2 === 0 ? import_picocolors3.default.green(C) : h2 === 1 ? import_picocolors3.default.red(L2) : import_picocolors3.default.red(W2);
4454
4498
  l2 = R2(m2 ?? l2), t === "timer" ? process.stdout.write(`${w2} ${l2} ${O2(g2)}
4455
4499
  `) : process.stdout.write(`${w2} ${l2}
4456
4500
  `), E(), s();
@@ -4540,9 +4584,9 @@ function compareVersions(a, b3) {
4540
4584
  }
4541
4585
  return 0;
4542
4586
  }
4543
- function parse(v) {
4544
- if (!v || v === "unknown") return null;
4545
- const core = v.trim().replace(/^v/, "").split(/[-+]/)[0] ?? "";
4587
+ function parse(v2) {
4588
+ if (!v2 || v2 === "unknown") return null;
4589
+ const core = v2.trim().replace(/^v/, "").split(/[-+]/)[0] ?? "";
4546
4590
  const parts = core.split(".");
4547
4591
  if (parts.length === 0 || parts.some((p2) => p2 === "" || !/^\d+$/.test(p2))) return null;
4548
4592
  return parts.map((p2) => Number.parseInt(p2, 10));
@@ -4601,13 +4645,18 @@ function classifyExtras(adapter, detection, status4, extrasVersion) {
4601
4645
  if (!adapter.supported) return "unsupported";
4602
4646
  if (!status4.extras.installed) return "missing";
4603
4647
  if (!status4.extras.version || !extrasVersion) return "unknown";
4604
- return status4.extras.version === extrasVersion ? "current" : "stale";
4648
+ const order = compareVersions(status4.extras.version, extrasVersion);
4649
+ if (order === null) return status4.extras.version === extrasVersion ? "current" : "stale";
4650
+ if (order === 0) return "current";
4651
+ return order > 0 ? "ahead" : "stale";
4605
4652
  }
4606
- function actionable(report, includeCurrent = false) {
4653
+ function actionable(report, options = {}) {
4654
+ const { includeCurrent = false, allowDowngrade = false } = options;
4607
4655
  return report.agents.filter((entry) => {
4608
4656
  if (!entry.detection.present || !entry.adapter.supported) return false;
4657
+ if (entry.drift === "ahead" && !allowDowngrade) return false;
4658
+ if (entry.extrasDrift === "ahead" && !allowDowngrade) return false;
4609
4659
  if (includeCurrent) return true;
4610
- if (entry.drift === "ahead") return false;
4611
4660
  return entry.drift !== "current" || entry.extrasDrift === "stale" || entry.extrasDrift === "unknown";
4612
4661
  });
4613
4662
  }
@@ -4641,7 +4690,7 @@ async function init(options) {
4641
4690
  const current = entry.drift === "current";
4642
4691
  item(current ? "ok" : "warn", `${entry.adapter.name} \u2014 ${current ? `caddis ${entry.status.version} (current)` : entry.status.installed ? `caddis ${entry.status.version ?? "?"} \u2192 ${report.poolVersion}` : "caddis not installed"}`);
4643
4692
  }
4644
- const targets = actionable(report, true);
4693
+ const targets = actionable(report, { includeCurrent: true });
4645
4694
  if (targets.length === 0) {
4646
4695
  Se(
4647
4696
  `${color.yellow("No supported agent found.")} caddis v0.1 drives Claude Code and agy \u2014 install one, then re-run.`
@@ -4684,6 +4733,266 @@ var init_init = __esm({
4684
4733
  }
4685
4734
  });
4686
4735
 
4736
+ // src/providers/resolve.ts
4737
+ import { readFileSync as readFileSync3 } from "fs";
4738
+ import { homedir } from "os";
4739
+ import { isAbsolute, join } from "path";
4740
+ function expandHome(p2) {
4741
+ if (p2 === "~") return homedir();
4742
+ if (p2.startsWith("~/") || p2.startsWith("~\\")) return join(homedir(), p2.slice(2));
4743
+ return p2;
4744
+ }
4745
+ function keysFilePath(env) {
4746
+ const configured = (env[KEYS_FILE_ENV] || "").trim() || DEFAULT_KEYS_FILE;
4747
+ const expanded = expandHome(configured);
4748
+ return isAbsolute(expanded) ? expanded : expanded;
4749
+ }
4750
+ function parseKeysFile(path5) {
4751
+ let text;
4752
+ try {
4753
+ text = readFileSync3(path5, "utf8");
4754
+ } catch {
4755
+ return {};
4756
+ }
4757
+ const out = {};
4758
+ for (const raw of text.split(/\r?\n/)) {
4759
+ const line2 = raw.trim();
4760
+ if (!line2 || line2.startsWith("#") || !line2.includes("=")) continue;
4761
+ const idx = line2.indexOf("=");
4762
+ const key = line2.slice(0, idx).trim();
4763
+ let val = line2.slice(idx + 1).trim();
4764
+ if (val.length >= 2 && (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'"))) {
4765
+ val = val.slice(1, -1);
4766
+ }
4767
+ out[key] = val;
4768
+ }
4769
+ return out;
4770
+ }
4771
+ function maskKey(key) {
4772
+ if (!key) return "(none)";
4773
+ return key.length <= 4 ? "*".repeat(key.length) : `${"*".repeat(8)}${key.slice(-4)}`;
4774
+ }
4775
+ var PROVIDERS, DEFAULT_KEYS_FILE, KEYS_FILE_ENV;
4776
+ var init_resolve = __esm({
4777
+ "src/providers/resolve.ts"() {
4778
+ "use strict";
4779
+ PROVIDERS = {
4780
+ deepseek: {
4781
+ baseUrl: "https://api.deepseek.com/anthropic",
4782
+ model: "deepseek-v4-flash",
4783
+ keyEnv: "DEEPSEEK_API_KEY"
4784
+ },
4785
+ glm: {
4786
+ baseUrl: "https://api.z.ai/api/anthropic",
4787
+ model: "glm-5.2",
4788
+ keyEnv: "GLM_API_KEY"
4789
+ },
4790
+ // OpenRouter has no Anthropic-protocol endpoint. It works only through an
4791
+ // Anthropic-compatible gateway (e.g. LiteLLM — see claude-harness/LOCAL-MODELS.md)
4792
+ // whose URL you supply via OSS_BASE_URL.
4793
+ openrouter: {
4794
+ baseUrl: "",
4795
+ model: "deepseek/deepseek-v4-flash",
4796
+ keyEnv: "OPENROUTER_API_KEY"
4797
+ }
4798
+ };
4799
+ DEFAULT_KEYS_FILE = "~/.caddis/keys.env";
4800
+ KEYS_FILE_ENV = "CADDIS_KEYS_FILE";
4801
+ }
4802
+ });
4803
+
4804
+ // src/commands/keys.ts
4805
+ var keys_exports = {};
4806
+ __export(keys_exports, {
4807
+ keys: () => keys,
4808
+ probeKey: () => probeKey,
4809
+ upsertKeyLines: () => upsertKeyLines
4810
+ });
4811
+ import { chmodSync, mkdirSync, readFileSync as readFileSync4, writeFileSync } from "fs";
4812
+ import { dirname } from "path";
4813
+ async function probeKey(baseUrl, model, apiKey) {
4814
+ if (!baseUrl) return { ok: false, rejected: false, detail: "no endpoint for this provider" };
4815
+ const controller = new AbortController();
4816
+ const timer = setTimeout(() => controller.abort(), 15e3);
4817
+ try {
4818
+ const res = await fetch(`${baseUrl.replace(/\/$/, "")}/v1/messages`, {
4819
+ method: "POST",
4820
+ headers: {
4821
+ "content-type": "application/json",
4822
+ "anthropic-version": "2023-06-01",
4823
+ authorization: `Bearer ${apiKey}`,
4824
+ "x-api-key": apiKey
4825
+ },
4826
+ body: JSON.stringify({ model, max_tokens: 1, messages: [{ role: "user", content: "ping" }] }),
4827
+ signal: controller.signal
4828
+ });
4829
+ if (res.ok) return { ok: true, rejected: false, detail: "ok" };
4830
+ if (res.status === 401 || res.status === 403) {
4831
+ return { ok: false, rejected: true, detail: `rejected (HTTP ${res.status})` };
4832
+ }
4833
+ return { ok: false, rejected: false, detail: `could not validate (HTTP ${res.status})` };
4834
+ } catch (error) {
4835
+ const message = error instanceof Error ? error.message : String(error);
4836
+ return { ok: false, rejected: false, detail: `could not validate (${message})` };
4837
+ } finally {
4838
+ clearTimeout(timer);
4839
+ }
4840
+ }
4841
+ function upsertKeyLines(existing, updates) {
4842
+ const lines = existing ? existing.split(/\r?\n/) : [];
4843
+ const remaining = { ...updates };
4844
+ const out = lines.map((line2) => {
4845
+ const trimmed = line2.trim();
4846
+ if (!trimmed || trimmed.startsWith("#") || !trimmed.includes("=")) return line2;
4847
+ const name = trimmed.slice(0, trimmed.indexOf("=")).trim();
4848
+ if (name in remaining) {
4849
+ const value = remaining[name];
4850
+ delete remaining[name];
4851
+ return `${name}=${value}`;
4852
+ }
4853
+ return line2;
4854
+ });
4855
+ const added = Object.entries(remaining).map(([k3, v2]) => `${k3}=${v2}`);
4856
+ if (added.length) {
4857
+ if (out.length && out[out.length - 1].trim() !== "") out.push("");
4858
+ out.push("# added by `caddis keys`");
4859
+ out.push(...added);
4860
+ }
4861
+ let text = out.join("\n");
4862
+ if (!text.endsWith("\n")) text += "\n";
4863
+ return text;
4864
+ }
4865
+ function restrictPermissions(path5) {
4866
+ if (process.platform === "win32") return null;
4867
+ try {
4868
+ chmodSync(path5, 384);
4869
+ return null;
4870
+ } catch (error) {
4871
+ return error instanceof Error ? error.message : String(error);
4872
+ }
4873
+ }
4874
+ async function keys(options = {}) {
4875
+ const path5 = keysFilePath(process.env);
4876
+ const fileKeys = parseKeysFile(path5);
4877
+ const names = Object.keys(PROVIDERS).sort();
4878
+ const current = names.map((name) => {
4879
+ const preset = PROVIDERS[name];
4880
+ const fromEnv = (process.env[preset.keyEnv] || "").trim();
4881
+ const fromFile = (fileKeys[preset.keyEnv] || "").trim();
4882
+ return {
4883
+ name,
4884
+ preset,
4885
+ value: fromEnv || fromFile,
4886
+ source: fromEnv ? "env" : fromFile ? "file" : null
4887
+ };
4888
+ });
4889
+ if (options.check) {
4890
+ const configured = current.filter((entry) => entry.value);
4891
+ if (!configured.length) {
4892
+ process.stdout.write(`no provider keys configured (looked in $ENV and ${path5})
4893
+ `);
4894
+ return 1;
4895
+ }
4896
+ let bad = 0;
4897
+ for (const entry of configured) {
4898
+ const probe = await probeKey(entry.preset.baseUrl, entry.preset.model, entry.value);
4899
+ const label = probe.ok ? color.green("ok") : probe.rejected ? color.red("REJECTED") : color.yellow("unverified");
4900
+ process.stdout.write(` ${entry.name.padEnd(12)} ${label} ${probe.detail}
4901
+ `);
4902
+ if (probe.rejected) bad += 1;
4903
+ }
4904
+ return bad ? 1 : 0;
4905
+ }
4906
+ Ie("caddis keys");
4907
+ process.stdout.write(` keys file: ${path5}
4908
+
4909
+ `);
4910
+ for (const entry of current) {
4911
+ const state = entry.source ? `${color.green("present")} (${entry.source}) ${color.dim(maskKey(entry.value))}` : color.dim("missing");
4912
+ process.stdout.write(` ${entry.name.padEnd(12)} ${state}
4913
+ `);
4914
+ }
4915
+ process.stdout.write("\n");
4916
+ const collected = {};
4917
+ for (const entry of current) {
4918
+ if (entry.source === "env") {
4919
+ process.stdout.write(
4920
+ color.dim(` ${entry.name}: set via $${entry.preset.keyEnv}; the environment wins, skipping
4921
+ `)
4922
+ );
4923
+ continue;
4924
+ }
4925
+ if (entry.value && !options.yes) {
4926
+ const replace = await ye({
4927
+ message: `${entry.name}: a key is already saved. Replace it?`,
4928
+ initialValue: false
4929
+ });
4930
+ if (pD(replace)) {
4931
+ xe("cancelled \u2014 nothing written");
4932
+ return 1;
4933
+ }
4934
+ if (!replace) continue;
4935
+ } else if (entry.value) {
4936
+ continue;
4937
+ }
4938
+ const entered = await ge({
4939
+ message: `${entry.name} key (${entry.preset.keyEnv}) \u2014 Enter to skip`
4940
+ });
4941
+ if (pD(entered)) {
4942
+ xe("cancelled \u2014 nothing written");
4943
+ return 1;
4944
+ }
4945
+ const value = String(entered || "").trim();
4946
+ if (!value) continue;
4947
+ const probe = await probeKey(entry.preset.baseUrl, entry.preset.model, value);
4948
+ if (probe.rejected) {
4949
+ process.stdout.write(color.red(` ${entry.name}: ${probe.detail} \u2014 not saved
4950
+ `));
4951
+ continue;
4952
+ }
4953
+ if (!probe.ok) {
4954
+ process.stdout.write(color.yellow(` ${entry.name}: ${probe.detail} \u2014 saving anyway
4955
+ `));
4956
+ } else {
4957
+ process.stdout.write(color.green(` ${entry.name}: validated
4958
+ `));
4959
+ }
4960
+ collected[entry.preset.keyEnv] = value;
4961
+ }
4962
+ if (!Object.keys(collected).length) {
4963
+ Se("nothing to write");
4964
+ return 0;
4965
+ }
4966
+ if (options.dryRun) {
4967
+ Se(`dry run \u2014 would write ${Object.keys(collected).join(", ")} to ${path5}`);
4968
+ return 0;
4969
+ }
4970
+ let existing = "";
4971
+ try {
4972
+ existing = readFileSync4(path5, "utf8");
4973
+ } catch {
4974
+ existing = "";
4975
+ }
4976
+ mkdirSync(dirname(path5), { recursive: true });
4977
+ writeFileSync(path5, upsertKeyLines(existing, collected), { encoding: "utf8" });
4978
+ const permWarning = restrictPermissions(path5);
4979
+ process.stdout.write(`
4980
+ wrote ${Object.keys(collected).join(", ")} to ${path5}
4981
+ `);
4982
+ if (permWarning) process.stdout.write(color.yellow(` could not set 0600 on the keys file: ${permWarning}
4983
+ `));
4984
+ Se("lanes ready \u2014 try `claude-glm --print-config`");
4985
+ return 0;
4986
+ }
4987
+ var init_keys = __esm({
4988
+ "src/commands/keys.ts"() {
4989
+ "use strict";
4990
+ init_dist2();
4991
+ init_log();
4992
+ init_resolve();
4993
+ }
4994
+ });
4995
+
4687
4996
  // src/commands/update.ts
4688
4997
  var update_exports = {};
4689
4998
  __export(update_exports, {
@@ -4699,7 +5008,10 @@ async function update(options) {
4699
5008
  item("info", `${entry.adapter.name} \u2014 detected, not yet supported (v0.2)`);
4700
5009
  }
4701
5010
  }
4702
- const targets = actionable(report, options.force === true);
5011
+ const targets = actionable(report, {
5012
+ includeCurrent: options.force === true,
5013
+ allowDowngrade: options.force === true
5014
+ });
4703
5015
  if (targets.length === 0) {
4704
5016
  const supportedPresent = report.agents.filter((e2) => e2.detection.present && e2.adapter.supported);
4705
5017
  const ahead = report.agents.filter((e2) => e2.drift === "ahead");
@@ -4991,18 +5303,18 @@ function renderTable(headers, rows) {
4991
5303
  (header, i) => Math.max(visibleWidth(header), ...rows.map((row) => visibleWidth(row[i] ?? "")))
4992
5304
  );
4993
5305
  const out = [];
4994
- out.push(` ${headers.map((h2, i) => pad(import_picocolors3.default.bold(h2), widths[i] ?? 0)).join(" ")}`.trimEnd());
4995
- out.push(` ${widths.map((w2) => import_picocolors3.default.dim("\u2500".repeat(w2))).join(" ")}`);
5306
+ out.push(` ${headers.map((h2, i) => pad(import_picocolors4.default.bold(h2), widths[i] ?? 0)).join(" ")}`.trimEnd());
5307
+ out.push(` ${widths.map((w2) => import_picocolors4.default.dim("\u2500".repeat(w2))).join(" ")}`);
4996
5308
  for (const row of rows) {
4997
5309
  out.push(` ${row.map((cell, i) => pad(cell ?? "", widths[i] ?? 0)).join(" ")}`.trimEnd());
4998
5310
  }
4999
5311
  return out.join("\n");
5000
5312
  }
5001
- var import_picocolors3, ANSI;
5313
+ var import_picocolors4, ANSI;
5002
5314
  var init_table = __esm({
5003
5315
  "src/util/table.ts"() {
5004
5316
  "use strict";
5005
- import_picocolors3 = __toESM(require_picocolors(), 1);
5317
+ import_picocolors4 = __toESM(require_picocolors(), 1);
5006
5318
  ANSI = /\[[0-9;]*m/g;
5007
5319
  }
5008
5320
  });
@@ -5531,6 +5843,15 @@ withGlobalFlags(program2.command("init")).description("first run: detect your ag
5531
5843
  })
5532
5844
  );
5533
5845
  });
5846
+ withGlobalFlags(program2.command("keys")).description("set up provider API keys for the model lanes (GLM, DeepSeek)").option("--check", "validate configured keys and exit non-zero on failure; never prompts", false).action(async (options) => {
5847
+ const flags = mergeFlags(options);
5848
+ const { keys: keys2 } = await Promise.resolve().then(() => (init_keys(), keys_exports));
5849
+ process.exitCode = await keys2({
5850
+ check: options.check === true,
5851
+ dryRun: flags.dryRun,
5852
+ yes: flags.yes
5853
+ });
5854
+ });
5534
5855
  withGlobalFlags(program2.command("update")).description("update every detected agent to the caddis version shipped in this CLI").option("-f, --force", "re-drive agents that already report the shipped version", false).option("--extras", "also install the optional caddis-extras long-tail skills", false).action(async (options) => {
5535
5856
  const flags = mergeFlags(options);
5536
5857
  const { update: update2 } = await Promise.resolve().then(() => (init_update(), update_exports));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caddis/cli",
3
- "version": "0.2.6",
3
+ "version": "0.3.0",
4
4
  "description": "Install, update and diagnose the caddis dev harness across every coding agent you use (Claude Code, agy/Antigravity, and more).",
5
5
  "keywords": [
6
6
  "caddis",
@@ -19,7 +19,10 @@
19
19
  },
20
20
  "bin": {
21
21
  "caddis": "./dist/cli.js",
22
- "caddis-cli": "./dist/cli.js"
22
+ "caddis-cli": "./dist/cli.js",
23
+ "claude-oss": "./dist/claude-oss.js",
24
+ "claude-glm": "./dist/claude-glm.js",
25
+ "claude-deepseek": "./dist/claude-deepseek.js"
23
26
  },
24
27
  "files": [
25
28
  "dist",