@caddis/cli 0.2.3 → 0.2.5

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 (46) hide show
  1. package/README.md +11 -0
  2. package/bundles/antigravity-plugin/agents/anchor.md +60 -0
  3. package/bundles/antigravity-plugin/agents/claude-md-curator.md +144 -0
  4. package/bundles/antigravity-plugin/agents/codebase-audit.md +53 -0
  5. package/bundles/antigravity-plugin/agents/data-engineer.md +51 -0
  6. package/bundles/antigravity-plugin/agents/debug.md +48 -0
  7. package/bundles/antigravity-plugin/agents/knowledge-transfer.md +139 -0
  8. package/bundles/antigravity-plugin/agents/security-analyst.md +49 -0
  9. package/bundles/antigravity-plugin/agents/sql-expert.md +46 -0
  10. package/bundles/antigravity-plugin/agents/tester.md +45 -0
  11. package/bundles/antigravity-plugin/agents/ui-design-reviewer.md +110 -0
  12. package/bundles/antigravity-plugin/plugin.json +1 -1
  13. package/bundles/antigravity-plugin/skills/_registry.md +6 -0
  14. package/bundles/antigravity-plugin/skills/advisory-hub/SKILL.md +183 -0
  15. package/bundles/antigravity-plugin/skills/advisory-hub/reference/context-template.md +214 -0
  16. package/bundles/antigravity-plugin/skills/advisory-hub/reference/hub-handbook.md +233 -0
  17. package/bundles/antigravity-plugin/skills/advisory-hub/reference/report-contract.md +94 -0
  18. package/bundles/antigravity-plugin/skills/advisory-hub/reference/succession.md +96 -0
  19. package/bundles/antigravity-plugin/skills/brainstorming/SKILL.md +1 -1
  20. package/bundles/antigravity-plugin/skills/code-review/SKILL.md +7 -0
  21. package/bundles/antigravity-plugin/skills/context-curator/SKILL.md +1 -1
  22. package/bundles/antigravity-plugin/skills/cross-review/SKILL.md +71 -14
  23. package/bundles/antigravity-plugin/skills/dashboard-perf-sweep/SKILL.md +18 -18
  24. package/bundles/antigravity-plugin/skills/feature-plan/SKILL.md +164 -9
  25. package/bundles/antigravity-plugin/skills/gate-preflight/SKILL.md +34 -0
  26. package/bundles/antigravity-plugin/skills/gate-review/SKILL.md +36 -0
  27. package/bundles/antigravity-plugin/skills/golden-plan/SKILL.md +22 -13
  28. package/bundles/antigravity-plugin/skills/handoff/SKILL.md +78 -0
  29. package/bundles/antigravity-plugin/skills/implement/SKILL.md +182 -3
  30. package/bundles/antigravity-plugin/skills/mermaid-diagrams/SKILL.md +57 -1
  31. package/bundles/antigravity-plugin/skills/park/SKILL.md +126 -0
  32. package/bundles/antigravity-plugin/skills/prd/SKILL.md +4 -2
  33. package/bundles/antigravity-plugin/skills/preflight/SKILL.md +5 -0
  34. package/bundles/antigravity-plugin/skills/spawn-hub/SKILL.md +287 -0
  35. package/bundles/antigravity-plugin/skills/validate-phase/SKILL.md +112 -0
  36. package/bundles/antigravity-plugin/skills/writing-plans/SKILL.md +7 -4
  37. package/bundles/antigravity-plugin/warm_start_agy.py +35 -6
  38. package/bundles/antigravity-plugin-extras/plugin.json +1 -1
  39. package/bundles/antigravity-plugin-extras/skills/agent-orchestration/SKILL.md +9 -9
  40. package/bundles/antigravity-plugin-extras/skills/cross-review/SKILL.md +56 -6
  41. package/bundles/antigravity-plugin-extras/skills/requesting-code-review/SKILL.md +1 -1
  42. package/bundles/antigravity-plugin-extras/skills/schema-migration/SKILL.md +2 -2
  43. package/bundles/antigravity-plugin-extras/skills/state-tracking/SKILL.md +4 -4
  44. package/bundles/manifest.json +3 -3
  45. package/dist/cli.js +143 -59
  46. package/package.json +1 -1
@@ -14,6 +14,27 @@ issue. This skill runs that second reviewer over the current changes and acts on
14
14
  - On a risky diff (auth, crypto, data-touching, concurrency) where a second opinion is cheap insurance.
15
15
  - NOT a replacement for the in-repo `/caddis:code-review` — this is the *cross-vendor* complement.
16
16
 
17
+ ## Check the key FIRST, and ask if it is missing
18
+
19
+ ```bash
20
+ python "$TOOL" --check-config
21
+ ```
22
+
23
+ Exit 0 prints which providers have a key. **Exit 3 means nothing is configured — stop and ask the
24
+ user before reading any diff.** Do not proceed, and do not silently skip the review.
25
+
26
+ Ask with `AskUserQuestion` which provider to set up, then write the key to `~/.caddis/keys.env` as
27
+ `DEEPSEEK_API_KEY=<key>` (or the matching variable). That file sits outside every git repo, so it
28
+ cannot be committed by accident. **Never put a key in the repo, in a command, or in a reply.**
29
+
30
+ This used to fail mid-task with a commit pending — the worst moment to go hunting for a credential.
31
+ A command that fails once at an inconvenient moment does not get retried, so the safety check is
32
+ quietly lost and nothing records the loss. The script stays non-interactive because it also runs
33
+ headless, where a stdin prompt hangs instead of asking; the asking is the agent's job.
34
+
35
+ If a key exists for a *different* provider than the one requested, the error names it and gives the
36
+ exact flag (`--provider glm`). Read the message before concluding anything is missing.
37
+
17
38
  ## Prerequisites (one-time)
18
39
  Only the key is mandatory — the tool ships **provider presets** (default `deepseek`, the cheapest + most
19
40
  architecturally distinct-from-Claude option; a review costs well under a cent):
@@ -28,16 +49,41 @@ churn — the preset table in `oss_review.py` is the one place to edit a rename,
28
49
  `REVIEW_BASE_URL` always win, so you can point at any new id without touching code.
29
50
 
30
51
  ## How to run
31
- From the repo root. Locate the tool, in order:
32
- - `.github/tools/oss_review.py` — this project vendors its own copy.
33
- - `${CLAUDE_PLUGIN_ROOT}/scripts/oss_review.py` — the plugin-bundled copy (every Claude Code
34
- install has this; use it whenever the project has no local copy).
52
+ From the repo root. Resolve the tool path deterministically — check only these two exact
53
+ locations, in order, and **never search the filesystem for it** (see Rules below):
54
+ ```bash
55
+ # THE PLUGIN COPY WINS. It is the one `caddis update` refreshes; a vendored copy is a snapshot
56
+ # of whatever caddis shipped the day someone copied it, and it never updates again.
57
+ if [ -f "${CLAUDE_PLUGIN_ROOT}/scripts/oss_review.py" ]; then
58
+ TOOL="${CLAUDE_PLUGIN_ROOT}/scripts/oss_review.py"
59
+ # A vendored copy that DIFFERS is a live hazard, not a curiosity — say so, do not stay silent.
60
+ if [ -f ".github/tools/oss_review.py" ] && ! diff -q ".github/tools/oss_review.py" "$TOOL" >/dev/null 2>&1; then
61
+ echo "[cross-review] WARNING: .github/tools/oss_review.py differs from the caddis copy and is"
62
+ echo " being IGNORED. Delete it, or keep the local change deliberately."
63
+ fi
64
+ elif [ -f ".github/tools/oss_review.py" ]; then
65
+ TOOL=".github/tools/oss_review.py" # no plugin present — the vendored copy is all there is
66
+ else
67
+ echo "oss_review.py not found at either known location — report this and stop. Do NOT search for it."
68
+ exit 3
69
+ fi
35
70
  ```
36
- python <resolved-path>/oss_review.py # review the working tree (staged+unstaged)
37
- python <resolved-path>/oss_review.py --range origin/main..HEAD # review a branch's commits
71
+
72
+ > **Why the plugin copy is first.** It used to be second. On 2026-08-10 a repo ran a stale vendored
73
+ > `oss_review.py` and got `REVIEW: CLEAN` on a database write path that was never reviewed. The bug
74
+ > had been fixed on 2026-08-01 — the fix just never reached the caller, because a file someone
75
+ > copied weeks earlier silently won. `caddis_gate.py vendor-drift` now fails on the same condition.
76
+ ```
77
+ python "$TOOL" # review the working tree (staged+unstaged)
78
+ python "$TOOL" --range origin/main..HEAD # review a branch's commits
38
79
  ```
39
80
  Optional flags: `--cwd <repo>`, `--base-url <url>`, `--model <id>` (override the env).
40
81
 
82
+ **Diff-size ceiling.** A diff over `REVIEW_MAX_DIFF_CHARS` (default 60,000 chars) is **split into batches** on whole-file boundaries and each batch reviewed separately (verdict is CLEAN only if every batch is). Exit 2 now means a *single unsplittable file* over the ceiling, or too many batches — not an ordinary large diff. Previously it was refused with exit 2
83
+ *before* any LLM call — an oversized diff has been observed to come back either an empty response, or
84
+ worse, a `REVIEW: CLEAN` with zero real engagement. If you hit this, narrow `--range` or review in
85
+ smaller chunks; don't just raise `--max-diff-chars` without knowing the endpoint's real limit.
86
+
41
87
  ## Interpret the exit code
42
88
  - **0 — REVIEW: CLEAN** → no blocking issues. Proceed.
43
89
  - **1 — REVIEW: BLOCKING** → the reviewer found blocking issues. **Read the printed findings, then FIX
@@ -52,3 +98,7 @@ Optional flags: `--cwd <repo>`, `--base-url <url>`, `--model <id>` (override the
52
98
  the main thread after reading the findings.
53
99
  - Treat exit 2/3 as blocking-unknown, never as approval (the tool is fail-closed by design).
54
100
  - Different vendor ⇒ different style; weigh its findings on merit, don't cargo-cult them.
101
+ - **Never use `find`, `Get-ChildItem -Recurse`, or any other filesystem-wide search to locate
102
+ `oss_review.py`.** Check only the two paths above. On Git Bash under Windows, `find /` (or any
103
+ search rooted at `/`) walks every mounted drive, not just the repo — it can hang or take minutes.
104
+ If neither path exists, stop and report it; don't go hunting for the file.
@@ -58,7 +58,7 @@ HEAD_SHA=$(git rev-parse HEAD)
58
58
 
59
59
  [Dispatch superpowers:code-reviewer subagent]
60
60
  WHAT_WAS_IMPLEMENTED: Verification and repair functions for conversation index
61
- PLAN_OR_REQUIREMENTS: Task 2 from docs/plans/deployment-plan.md
61
+ PLAN_OR_REQUIREMENTS: Task 2 from .caddis/plans/deployment-plan.md
62
62
  BASE_SHA: a7981ec
63
63
  HEAD_SHA: 3df7661
64
64
  DESCRIPTION: Added verifyIndex() and repairIndex() with 4 issue types
@@ -24,7 +24,7 @@ Systematically migrate an application from a legacy database schema to a new sch
24
24
  1. **Read-Only** — Never execute DDL or DML (`CREATE`, `ALTER`, `INSERT`, `UPDATE`, `DELETE`) against the database. All analysis is SELECT-only.
25
25
  2. **No SQL Aliases** — Proposed queries must use direct `Table.Column` notation. Aliases obscure the mapping and make future changes harder to trace.
26
26
  3. **No Assumptions** — Every column mapping must be verified against the actual schema DDL. If a mapping is ambiguous or a source column has no clear target, flag it as a **GAP** rather than guessing.
27
- 4. **Scratchpad Protocol** — If context becomes large, save intermediate findings to `.github/plans/temp_migration_scratchpad.md` to preserve state across reasoning steps.
27
+ 4. **Scratchpad Protocol** — If context becomes large, save intermediate findings to `.caddis/plans/temp_migration_scratchpad.md` to preserve state across reasoning steps.
28
28
 
29
29
  ---
30
30
 
@@ -255,7 +255,7 @@ Produce a single, comprehensive document suitable for use as an **Implementation
255
255
  ```
256
256
 
257
257
  ### Output Location
258
- Place the final report at the path specified by the user (default: `.github/plans/backlog/migration_report.md`).
258
+ Place the final report at the path specified by the user (default: `.caddis/agent-docs/migration_report.md` — it is an agent-produced report, not a backlog item).
259
259
 
260
260
  ---
261
261
 
@@ -23,7 +23,7 @@ State tracking uses two distinct documents with different write semantics:
23
23
 
24
24
  | Document | Write mode | Purpose |
25
25
  |----------|-----------|---------|
26
- | **Plan Tracker** (`.github/plans/tracker/<slug>-tracker.md`) | Overwrite each row | Per-phase completion record, linked to commits |
26
+ | **Plan Tracker** (`.caddis/plans/tracker/<slug>-tracker.md`) | Overwrite each row | Per-phase completion record, linked to commits |
27
27
  | **CURRENT_STATE** (`.github/agent-docs/CURRENT_STATE.md`) | Overwrite head section | Single-view of right now: what phase, what's done, what's blocked |
28
28
  | **Session Log** (inline in CURRENT_STATE, append-only) | Append only | Chronological audit trail — never rewrite past entries |
29
29
 
@@ -42,8 +42,8 @@ Replace this section wholesale each time:
42
42
  ```markdown
43
43
  ## Current Status
44
44
 
45
- **Plan:** `.github/plans/<feature-slug>.md`
46
- **Tracker:** `.github/plans/tracker/<feature-slug>-tracker.md`
45
+ **Plan:** `.caddis/plans/<feature-slug>.md`
46
+ **Tracker:** `.caddis/plans/tracker/<feature-slug>-tracker.md`
47
47
  **Last updated:** YYYY-MM-DDTHH:MM:SSZ
48
48
  **Active agent:** @[Agent] (model: [Model])
49
49
  **Current phase:** Phase N — [Name]
@@ -116,7 +116,7 @@ The canonical template is at `.github/agent-docs/CURRENT_STATE.md`. Copy it for
116
116
  ## Integration with Golden-Plan
117
117
 
118
118
  When running a golden-plan:
119
- - The plan tracker (`.github/plans/tracker/<slug>-tracker.md`) is the commitment ledger.
119
+ - The plan tracker (`.caddis/plans/tracker/<slug>-tracker.md`) is the commitment ledger.
120
120
  - CURRENT_STATE.md is the working scratch-pad for the current session.
121
121
  - After completing a phase, update BOTH the tracker row AND the CURRENT_STATE current-status section.
122
122
  - When all tracker rows are `Complete`, follow the Plan Completion Protocol in golden-plan: move the plan to `plans/done/`, set `status: done`.
@@ -1,7 +1,7 @@
1
1
  {
2
- "poolVersion": "1.3.47",
2
+ "poolVersion": "1.3.74",
3
3
  "bundles": {
4
- "antigravity-plugin": "1.3.47",
5
- "antigravity-plugin-extras": "1.3.20"
4
+ "antigravity-plugin": "1.3.74",
5
+ "antigravity-plugin-extras": "1.3.24"
6
6
  }
7
7
  }
package/dist/cli.js CHANGED
@@ -3473,6 +3473,51 @@ var require_commander = __commonJS({
3473
3473
  }
3474
3474
  });
3475
3475
 
3476
+ // src/util/exec.ts
3477
+ async function run(cmd, args, opts = {}) {
3478
+ const { execa } = await import("execa");
3479
+ try {
3480
+ const result = await execa(cmd, args, {
3481
+ cwd: opts.cwd,
3482
+ timeout: opts.timeout ?? DEFAULT_TIMEOUT_MS,
3483
+ env: opts.env,
3484
+ reject: false,
3485
+ all: false,
3486
+ stripFinalNewline: true,
3487
+ // Windows: resolve .cmd/.ps1 shims through the shell-less path lookup
3488
+ // execa already does, but be explicit that we are not using a shell.
3489
+ shell: false,
3490
+ windowsHide: true
3491
+ });
3492
+ return {
3493
+ ok: result.exitCode === 0 && !result.failed,
3494
+ code: result.exitCode ?? null,
3495
+ stdout: String(result.stdout ?? ""),
3496
+ stderr: String(result.stderr ?? ""),
3497
+ failure: result.failed && result.exitCode === void 0 ? result.shortMessage : void 0
3498
+ };
3499
+ } catch (error) {
3500
+ return {
3501
+ ok: false,
3502
+ code: null,
3503
+ stdout: "",
3504
+ stderr: "",
3505
+ failure: error instanceof Error ? error.message : String(error)
3506
+ };
3507
+ }
3508
+ }
3509
+ function formatCommand(cmd, args) {
3510
+ const quoted = args.map((a) => /\s/.test(a) ? `"${a}"` : a);
3511
+ return [cmd, ...quoted].join(" ");
3512
+ }
3513
+ var DEFAULT_TIMEOUT_MS;
3514
+ var init_exec = __esm({
3515
+ "src/util/exec.ts"() {
3516
+ "use strict";
3517
+ DEFAULT_TIMEOUT_MS = 12e4;
3518
+ }
3519
+ });
3520
+
3476
3521
  // node_modules/isexe/dist/commonjs/index.min.js
3477
3522
  var require_index_min = __commonJS({
3478
3523
  "node_modules/isexe/dist/commonjs/index.min.js"(exports) {
@@ -4482,33 +4527,73 @@ var init_drive = __esm({
4482
4527
  }
4483
4528
  });
4484
4529
 
4530
+ // src/util/semver.ts
4531
+ function compareVersions(a, b3) {
4532
+ const pa = parse(a);
4533
+ const pb = parse(b3);
4534
+ if (!pa || !pb) return null;
4535
+ for (let i = 0; i < Math.max(pa.length, pb.length); i += 1) {
4536
+ const da = pa[i] ?? 0;
4537
+ const db = pb[i] ?? 0;
4538
+ if (da > db) return 1;
4539
+ if (da < db) return -1;
4540
+ }
4541
+ return 0;
4542
+ }
4543
+ function parse(v) {
4544
+ if (!v || v === "unknown") return null;
4545
+ const core = v.trim().replace(/^v/, "").split(/[-+]/)[0] ?? "";
4546
+ const parts = core.split(".");
4547
+ if (parts.length === 0 || parts.some((p2) => p2 === "" || !/^\d+$/.test(p2))) return null;
4548
+ return parts.map((p2) => Number.parseInt(p2, 10));
4549
+ }
4550
+ var init_semver = __esm({
4551
+ "src/util/semver.ts"() {
4552
+ "use strict";
4553
+ }
4554
+ });
4555
+
4485
4556
  // src/commands/report.ts
4557
+ async function checkCliUpdate(currentVersion) {
4558
+ const result = await run("npm", ["view", "@caddis/cli", "version"], { timeout: 5e3 });
4559
+ if (!result.ok) return null;
4560
+ const latest = result.stdout.trim();
4561
+ if (!latest || latest === currentVersion) return null;
4562
+ return { current: currentVersion, latest };
4563
+ }
4486
4564
  function classify(adapter, detection, status4, poolVersion) {
4487
4565
  if (!detection.present) return "absent";
4488
4566
  if (!adapter.supported) return "unsupported";
4489
4567
  if (!status4.installed) return "missing";
4490
4568
  if (!status4.version) return "unknown";
4491
4569
  if (poolVersion === "unknown") return "unknown";
4492
- return status4.version === poolVersion ? "current" : "stale";
4570
+ const order = compareVersions(status4.version, poolVersion);
4571
+ if (order === null) return status4.version === poolVersion ? "current" : "stale";
4572
+ if (order === 0) return "current";
4573
+ return order > 0 ? "ahead" : "stale";
4493
4574
  }
4494
- async function gather(adapters) {
4575
+ async function gather(adapters, options = {}) {
4495
4576
  const manifest = bundleManifest();
4496
4577
  const poolVersion = manifest.poolVersion;
4497
4578
  const extrasVersion = manifest.bundles["antigravity-plugin-extras"];
4498
- const agents = await Promise.all(
4499
- adapters.map(async (adapter) => {
4500
- const detection = await adapter.detect();
4501
- const status4 = detection.present ? await adapter.status() : { installed: false, note: "agent not installed" };
4502
- return {
4503
- adapter,
4504
- detection,
4505
- status: status4,
4506
- drift: classify(adapter, detection, status4, poolVersion),
4507
- extrasDrift: classifyExtras(adapter, detection, status4, extrasVersion)
4508
- };
4509
- })
4510
- );
4511
- return { cliVersion: packageInfo().version, poolVersion, extrasVersion, agents };
4579
+ const cliVersion = packageInfo().version;
4580
+ const [agents, cliUpdate] = await Promise.all([
4581
+ Promise.all(
4582
+ adapters.map(async (adapter) => {
4583
+ const detection = await adapter.detect();
4584
+ const status4 = detection.present ? await adapter.status() : { installed: false, note: "agent not installed" };
4585
+ return {
4586
+ adapter,
4587
+ detection,
4588
+ status: status4,
4589
+ drift: classify(adapter, detection, status4, poolVersion),
4590
+ extrasDrift: classifyExtras(adapter, detection, status4, extrasVersion)
4591
+ };
4592
+ })
4593
+ ),
4594
+ options.checkCliUpdate ? checkCliUpdate(cliVersion) : Promise.resolve(null)
4595
+ ]);
4596
+ return { cliVersion, poolVersion, extrasVersion, agents, cliUpdate: cliUpdate ?? void 0 };
4512
4597
  }
4513
4598
  function classifyExtras(adapter, detection, status4, extrasVersion) {
4514
4599
  if (!status4.extras) return void 0;
@@ -4522,13 +4607,16 @@ function actionable(report, includeCurrent = false) {
4522
4607
  return report.agents.filter((entry) => {
4523
4608
  if (!entry.detection.present || !entry.adapter.supported) return false;
4524
4609
  if (includeCurrent) return true;
4610
+ if (entry.drift === "ahead") return false;
4525
4611
  return entry.drift !== "current" || entry.extrasDrift === "stale" || entry.extrasDrift === "unknown";
4526
4612
  });
4527
4613
  }
4528
4614
  var init_report = __esm({
4529
4615
  "src/commands/report.ts"() {
4530
4616
  "use strict";
4617
+ init_exec();
4531
4618
  init_pkg();
4619
+ init_semver();
4532
4620
  }
4533
4621
  });
4534
4622
 
@@ -4614,10 +4702,18 @@ async function update(options) {
4614
4702
  const targets = actionable(report, options.force === true);
4615
4703
  if (targets.length === 0) {
4616
4704
  const supportedPresent = report.agents.filter((e2) => e2.detection.present && e2.adapter.supported);
4705
+ const ahead = report.agents.filter((e2) => e2.drift === "ahead");
4617
4706
  line("");
4618
- line(
4619
- supportedPresent.length === 0 ? ` ${color.yellow("No supported agent found.")} caddis v0.1 drives Claude Code and agy.` : ` ${color.green("Everything is already current.")} Use --force to re-drive anyway.`
4620
- );
4707
+ if (supportedPresent.length === 0) {
4708
+ line(` ${color.yellow("No supported agent found.")} caddis v0.1 drives Claude Code and agy.`);
4709
+ } else if (ahead.length > 0) {
4710
+ line(
4711
+ ` ${color.green("Nothing to do.")} ${ahead.length} agent(s) are NEWER than this package's bundled pool (${report.poolVersion}) \u2014 driving them would be a DOWNGRADE, so they were left alone.`
4712
+ );
4713
+ line(` Update the CLI itself (${color.cyan("npm i -g @caddis/cli")}), or ${color.cyan("--force")} to roll back deliberately.`);
4714
+ } else {
4715
+ line(` ${color.green("Everything is already current.")} Use --force to re-drive anyway.`);
4716
+ }
4621
4717
  return 0;
4622
4718
  }
4623
4719
  line("");
@@ -4646,7 +4742,7 @@ __export(doctor_exports, {
4646
4742
  import os3 from "os";
4647
4743
  import process2 from "process";
4648
4744
  async function doctor(options) {
4649
- const report = await gather(options.adapters);
4745
+ const report = await gather(options.adapters, { checkCliUpdate: true });
4650
4746
  const findings = [];
4651
4747
  if (options.json) {
4652
4748
  line(JSON.stringify(toJson(report), null, 2));
@@ -4667,7 +4763,17 @@ function renderEnvironment(report, problems) {
4667
4763
  problems.push({ kind: "problem", text: `node ${process2.versions.node} is below the required ${REQUIRED_NODE_MAJOR}.${REQUIRED_NODE_MINOR}`, fix: "upgrade node" });
4668
4764
  }
4669
4765
  item("info", `platform ${process2.platform} ${process2.arch} (${os3.release()})`);
4670
- item("info", `@caddis/cli ${report.cliVersion}`);
4766
+ if (report.cliUpdate) {
4767
+ item("warn", `@caddis/cli ${report.cliVersion} ${color.dim("\u2192")} ${color.bold(report.cliUpdate.latest)} available`);
4768
+ detail("everything below is compared against the pool THIS install carries, which is now behind the published one");
4769
+ problems.push({
4770
+ kind: "problem",
4771
+ text: `@caddis/cli itself is behind: ${report.cliUpdate.current} installed, ${report.cliUpdate.latest} published`,
4772
+ fix: "npm i -g @caddis/cli@latest"
4773
+ });
4774
+ } else {
4775
+ item("ok", `@caddis/cli ${report.cliVersion}`);
4776
+ }
4671
4777
  const manifest = bundleManifest();
4672
4778
  if (report.poolVersion === "unknown") {
4673
4779
  item("fail", "shipped pool version unknown");
@@ -4738,6 +4844,7 @@ function renderSummary(report, findings) {
4738
4844
  function markFor(drift) {
4739
4845
  switch (drift) {
4740
4846
  case "current":
4847
+ case "ahead":
4741
4848
  return "ok";
4742
4849
  case "stale":
4743
4850
  case "missing":
@@ -4753,6 +4860,8 @@ function describe(entry, poolVersion) {
4753
4860
  switch (entry.drift) {
4754
4861
  case "current":
4755
4862
  return `caddis ${entry.status.version} ${color.dim("(current)")}`;
4863
+ case "ahead":
4864
+ return `caddis ${color.green(entry.status.version ?? "?")} ${color.dim("\u2014 newer than this CLI bundles")} ${color.dim(`(${poolVersion}); left alone, update @caddis/cli to catch up`)}`;
4756
4865
  case "stale":
4757
4866
  return `caddis ${color.yellow(entry.status.version ?? "?")} ${color.dim("\u2192")} ${color.bold(poolVersion)} available`;
4758
4867
  case "unknown":
@@ -4837,6 +4946,7 @@ function problemFor(entry, poolVersion) {
4837
4946
  function toJson(report) {
4838
4947
  return {
4839
4948
  cliVersion: report.cliVersion,
4949
+ cliUpdate: report.cliUpdate ?? null,
4840
4950
  poolVersion: report.poolVersion,
4841
4951
  extrasVersion: report.extrasVersion ?? null,
4842
4952
  node: process2.versions.node,
@@ -4918,10 +5028,17 @@ async function status3(options) {
4918
5028
  const stale = report.agents.filter(
4919
5029
  (entry) => entry.drift === "stale" || entry.drift === "missing" || entry.extrasDrift === "stale"
4920
5030
  );
5031
+ const ahead = report.agents.filter((entry) => entry.drift === "ahead");
4921
5032
  if (stale.length > 0) {
4922
5033
  line(` ${color.yellow(`${stale.length} agent(s) behind`)} \u2014 run ${color.cyan("caddis update")} (or ${color.cyan("caddis doctor")} for detail)`);
4923
5034
  line("");
4924
5035
  }
5036
+ if (ahead.length > 0) {
5037
+ line(
5038
+ ` ${ahead.length} agent(s) are NEWER than this CLI's bundled pool (${report.poolVersion}) \u2014 left alone. Update the CLI itself (${color.cyan("npm i -g @caddis/cli")}) to catch up.`
5039
+ );
5040
+ line("");
5041
+ }
4925
5042
  return 0;
4926
5043
  }
4927
5044
  function toRow(entry) {
@@ -4942,6 +5059,8 @@ function stateLabel(drift, disabled) {
4942
5059
  return disabled ? color.yellow("current, disabled") : color.green("current");
4943
5060
  case "stale":
4944
5061
  return color.yellow("update available");
5062
+ case "ahead":
5063
+ return color.green("newer than CLI pool");
4945
5064
  case "unknown":
4946
5065
  return color.yellow("version unknown");
4947
5066
  case "missing":
@@ -4990,44 +5109,8 @@ var {
4990
5109
  Help
4991
5110
  } = import_index.default;
4992
5111
 
4993
- // src/util/exec.ts
4994
- var DEFAULT_TIMEOUT_MS = 12e4;
4995
- async function run(cmd, args, opts = {}) {
4996
- const { execa } = await import("execa");
4997
- try {
4998
- const result = await execa(cmd, args, {
4999
- cwd: opts.cwd,
5000
- timeout: opts.timeout ?? DEFAULT_TIMEOUT_MS,
5001
- env: opts.env,
5002
- reject: false,
5003
- all: false,
5004
- stripFinalNewline: true,
5005
- // Windows: resolve .cmd/.ps1 shims through the shell-less path lookup
5006
- // execa already does, but be explicit that we are not using a shell.
5007
- shell: false,
5008
- windowsHide: true
5009
- });
5010
- return {
5011
- ok: result.exitCode === 0 && !result.failed,
5012
- code: result.exitCode ?? null,
5013
- stdout: String(result.stdout ?? ""),
5014
- stderr: String(result.stderr ?? ""),
5015
- failure: result.failed && result.exitCode === void 0 ? result.shortMessage : void 0
5016
- };
5017
- } catch (error) {
5018
- return {
5019
- ok: false,
5020
- code: null,
5021
- stdout: "",
5022
- stderr: "",
5023
- failure: error instanceof Error ? error.message : String(error)
5024
- };
5025
- }
5026
- }
5027
- function formatCommand(cmd, args) {
5028
- const quoted = args.map((a) => /\s/.test(a) ? `"${a}"` : a);
5029
- return [cmd, ...quoted].join(" ");
5030
- }
5112
+ // src/agents/claude.ts
5113
+ init_exec();
5031
5114
 
5032
5115
  // src/util/which.ts
5033
5116
  var import_which = __toESM(require_lib(), 1);
@@ -5167,6 +5250,7 @@ var claudeAdapter = {
5167
5250
  };
5168
5251
 
5169
5252
  // src/agents/agy.ts
5253
+ init_exec();
5170
5254
  import { readFileSync as readFileSync2, existsSync as existsSync2, mkdtempSync, cpSync, rmSync } from "fs";
5171
5255
  import os from "os";
5172
5256
  import path2 from "path";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caddis/cli",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
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",