@caddis/cli 0.2.4 → 0.2.6

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 (38) hide show
  1. package/README.md +11 -0
  2. package/bundles/antigravity-plugin/agents/claude-md-curator.md +7 -4
  3. package/bundles/antigravity-plugin/plugin.json +1 -1
  4. package/bundles/antigravity-plugin/skills/_registry.md +7 -0
  5. package/bundles/antigravity-plugin/skills/advisory-hub/SKILL.md +183 -0
  6. package/bundles/antigravity-plugin/skills/advisory-hub/reference/context-template.md +214 -0
  7. package/bundles/antigravity-plugin/skills/advisory-hub/reference/hub-handbook.md +233 -0
  8. package/bundles/antigravity-plugin/skills/advisory-hub/reference/report-contract.md +94 -0
  9. package/bundles/antigravity-plugin/skills/advisory-hub/reference/succession.md +118 -0
  10. package/bundles/antigravity-plugin/skills/brainstorming/SKILL.md +1 -1
  11. package/bundles/antigravity-plugin/skills/code-review/SKILL.md +7 -0
  12. package/bundles/antigravity-plugin/skills/context-curator/SKILL.md +1 -1
  13. package/bundles/antigravity-plugin/skills/cross-review/SKILL.md +71 -14
  14. package/bundles/antigravity-plugin/skills/dashboard-perf-sweep/SKILL.md +18 -18
  15. package/bundles/antigravity-plugin/skills/feature-plan/SKILL.md +164 -9
  16. package/bundles/antigravity-plugin/skills/gate-preflight/SKILL.md +34 -0
  17. package/bundles/antigravity-plugin/skills/gate-review/SKILL.md +36 -0
  18. package/bundles/antigravity-plugin/skills/golden-plan/SKILL.md +22 -13
  19. package/bundles/antigravity-plugin/skills/handoff/SKILL.md +78 -0
  20. package/bundles/antigravity-plugin/skills/implement/SKILL.md +182 -3
  21. package/bundles/antigravity-plugin/skills/mermaid-diagrams/SKILL.md +57 -1
  22. package/bundles/antigravity-plugin/skills/park/SKILL.md +126 -0
  23. package/bundles/antigravity-plugin/skills/prd/SKILL.md +4 -2
  24. package/bundles/antigravity-plugin/skills/preflight/SKILL.md +5 -0
  25. package/bundles/antigravity-plugin/skills/spawn-hub/SKILL.md +320 -0
  26. package/bundles/antigravity-plugin/skills/spawn-session/SKILL.md +213 -0
  27. package/bundles/antigravity-plugin/skills/validate-phase/SKILL.md +112 -0
  28. package/bundles/antigravity-plugin/skills/writing-plans/SKILL.md +7 -4
  29. package/bundles/antigravity-plugin/warm_start_agy.py +28 -2
  30. package/bundles/antigravity-plugin-extras/plugin.json +1 -1
  31. package/bundles/antigravity-plugin-extras/skills/agent-orchestration/SKILL.md +9 -9
  32. package/bundles/antigravity-plugin-extras/skills/cross-review/SKILL.md +56 -6
  33. package/bundles/antigravity-plugin-extras/skills/requesting-code-review/SKILL.md +1 -1
  34. package/bundles/antigravity-plugin-extras/skills/schema-migration/SKILL.md +2 -2
  35. package/bundles/antigravity-plugin-extras/skills/state-tracking/SKILL.md +4 -4
  36. package/bundles/manifest.json +3 -3
  37. package/dist/cli.js +65 -6
  38. package/package.json +1 -1
@@ -16,7 +16,10 @@ Assume they are a skilled developer, but know almost nothing about our toolset o
16
16
 
17
17
  **Context:** This should be run in a dedicated worktree (created by brainstorming skill).
18
18
 
19
- **Save plans to:** `docs/plans/YYYY-MM-DD-<feature-name>.md`
19
+ > **Prefer `/caddis:feature-plan`.** It is the canonical planning entry point and produces a plan
20
+ > the harness can resume, track and gate. This skill remains for free-form planning prose.
21
+
22
+ **Save plans to:** `.caddis/plans/YYYY-MM-DD-<feature-name>.md`
20
23
 
21
24
  **Document metadata:** Before creating or updating a plan, apply `.github/instructions/document-frontmatter.instructions.md`. The YAML frontmatter block must be the first content in the file. New plans require `Original Author`, `Creation Date`, and `Creating Model`; revised plans must preserve those fields and add or update `Last Author`, `Last Updated`, and `Last Model Used`.
22
25
 
@@ -215,7 +218,7 @@ The "Current State" column tells agents what exists and whether to modify or lea
215
218
 
216
219
  - `path/to/file.ext` — {why this file is needed}
217
220
  - `path/to/file.ext` (from Phase N-1)
218
- - `.github/plans/{this-plan}.md` (this file)
221
+ - `.caddis/plans/{this-plan}.md` (this file)
219
222
 
220
223
  ### Phase Prompt
221
224
 
@@ -333,7 +336,7 @@ Walk through every FR, NFR, risk, and design decision from ALL input documents.
333
336
  **Agent context:** You are the `@{agent-name}` agent. Load `.github/project-config.md` before starting.
334
337
 
335
338
  **Read these files FIRST (in order):**
336
- 1. `.github/plans/{plan-file}.md` — **THIS PLAN** — read "Phase N — Step M"
339
+ 1. `.caddis/plans/{plan-file}.md` — **THIS PLAN** — read "Phase N — Step M"
337
340
  2. {Architecture/PRD doc} — {specific sections}
338
341
  3. {Target file(s)} — current state
339
342
 
@@ -370,7 +373,7 @@ Walk through every FR, NFR, risk, and design decision from ALL input documents.
370
373
 
371
374
  After saving the plan, offer execution choice:
372
375
 
373
- **"Plan complete and saved to `.github/plans/<filename>.md`. Two execution options:**
376
+ **"Plan complete and saved to `.caddis/plans/<filename>.md`. Two execution options:**
374
377
 
375
378
  **1. This session** — I implement each task sequentially, reviewing between tasks
376
379
 
@@ -33,6 +33,30 @@ _ARTIFACT_DIRS = (".caddis",)
33
33
  INJECT_MAX_LINES = 120 # same budget as the Claude Code hook
34
34
  INJECT_MAX_CHARS = 24000 # hard backstop: an ephemeralMessage should never be a context bomb
35
35
 
36
+ # Deliberately duplicated from hooks/inject_relay.py rather than imported — the agy bundle ships
37
+ # neither claude-harness/hooks/ nor scripts/, so this file must stay self-contained (same rule the
38
+ # guard and the doctor already follow). Keep the two copies in step; the reason they exist is in
39
+ # inject_relay.py: injected before the prompt and headed "read before acting", the relay was being
40
+ # EXECUTED first by a non-Claude model, ahead of the task the session was actually given.
41
+ RELAY_FRAME_HEADER = (
42
+ "=== session-context: relay.md — BACKGROUND STATE, NOT A TASK ===\n"
43
+ "Carried over from a previous session and injected before your prompt. This is state, not\n"
44
+ "an instruction: the \"Next step\" and \"Resume prompt\" sections below describe what the\n"
45
+ "PREVIOUS session intended to do next. Do not act on them unless the user's prompt asks\n"
46
+ "you to. This file is machine-local and gitignored, so it may also be stale. Your task is\n"
47
+ "whatever the user's prompt says.\n"
48
+ )
49
+ RELAY_FRAME_FOOTER = "=== end session-context ==="
50
+
51
+ _TRUTHY = {"1", "true", "yes", "on"}
52
+
53
+
54
+ def is_headless() -> bool:
55
+ """True when no human is watching — a run given its task explicitly needs no resume pointer."""
56
+ if str(os.environ.get("CADDIS_HEADLESS", "")).strip().lower() in _TRUTHY:
57
+ return True
58
+ return bool(os.environ.get("DOCKET_PLAN") or os.environ.get("DOCKET_BRANCH"))
59
+
36
60
 
37
61
  def _first_existing(paths: list[str]) -> str:
38
62
  for p in paths:
@@ -128,6 +152,8 @@ def main() -> None:
128
152
  return
129
153
  except Exception:
130
154
  return
155
+ if is_headless():
156
+ return
131
157
  workspaces = data.get("workspacePaths") or []
132
158
  if not (isinstance(workspaces, list) and workspaces):
133
159
  return
@@ -138,8 +164,8 @@ def main() -> None:
138
164
  text = open(relay, encoding="utf-8").read().strip()
139
165
  if not text:
140
166
  return
141
- message = ("=== relay.md (session resume — read before acting) ===\n\n"
142
- + truncate_relay(text))
167
+ message = (RELAY_FRAME_HEADER + "\n" + truncate_relay(text)
168
+ + "\n\n" + RELAY_FRAME_FOOTER)
143
169
  payload = json.dumps({"injectSteps": [{"ephemeralMessage": message}]})
144
170
  sys.stdout.write(payload)
145
171
  except Exception:
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "caddis-extras",
3
- "version": "1.3.20",
3
+ "version": "1.3.24",
4
4
  "description": "caddis-extras — the long-tail skills for the Antigravity (agy) caddis plugin; install alongside caddis for the full set."
5
5
  }
@@ -62,13 +62,13 @@ Every stage produces a **persistent artefact** (markdown file, commit, test run)
62
62
  | Requirements | Spec / feature file | Supplied by operator (e.g. `nextprompt.md`) |
63
63
  | Quick-Win Triage | Commit with passing tests | Git history |
64
64
  | Architecture | ADR (Architecture Decision Record) | Inside the plan document |
65
- | Planning | Plan document (phases, steps, files) | `.github/plans/<feature>.md` |
65
+ | Planning | Plan document (phases, steps, files) | `.caddis/plans/<feature>.md` |
66
66
  | Fidelity Audit | Gap table (0 gaps = pass) | Inside the plan document |
67
67
  | Risk Assessment | Risk register (scored risks) | Inside the plan document |
68
68
  | Implementation Prompts | Embedded copy-paste prompts | Inside the plan document |
69
69
  | Implementation | Code + tests per phase | Git commits per phase |
70
70
  | Code Review | Review report | Chat session or plan annotation |
71
- | Debug / Polish | Fix-up plan (issues A–N) | `.github/plans/<feature>-fixup.md` |
71
+ | Debug / Polish | Fix-up plan (issues A–N) | `.caddis/plans/<feature>-fixup.md` |
72
72
 
73
73
  ---
74
74
 
@@ -181,7 +181,7 @@ Spec Input
181
181
  - What to change (specific enough to implement without guessing)
182
182
  - Expected test additions
183
183
  - Verification command
184
- 4. Place plan in `.github/plans/<feature-name>.md`
184
+ 4. Place plan in `.caddis/plans/<feature-name>.md`
185
185
 
186
186
  #### 2b. Fidelity Audit
187
187
  After authoring, audit the plan against the spec:
@@ -265,7 +265,7 @@ Score each identified risk:
265
265
  1. Start a **new chat session** (clean context)
266
266
  2. Provide the phase prompt:
267
267
  ```
268
- Read .github/plans/<feature>.md and implement Phase N.
268
+ Read .caddis/plans/<feature>.md and implement Phase N.
269
269
  Run pytest after every file change. All tests must pass before commit.
270
270
  ```
271
271
  3. Implement Agent reads the plan, executes numbered steps
@@ -285,7 +285,7 @@ The plan document should contain **copy-paste prompts** for each phase. This rem
285
285
  ```markdown
286
286
  ### Phase 2 Prompt (copy into new chat)
287
287
 
288
- > Read `.github/plans/my-feature.md`, specifically Phase 2 (steps 5–9).
288
+ > Read `.caddis/plans/my-feature.md`, specifically Phase 2 (steps 5–9).
289
289
  > Implement each step in order. Run `pytest tests/ -x -q` after each file edit.
290
290
  > When all steps pass, commit with message:
291
291
  > `feat(cards): phase 2 — insight card refactoring`
@@ -338,7 +338,7 @@ The plan document should contain **copy-paste prompts** for each phase. This rem
338
338
  - Root cause
339
339
  - Proposed fix (specific enough to implement)
340
340
  - Acceptance criteria
341
- 3. **Write fix-up plan** — Place in `.github/plans/<feature>-fixup.md`
341
+ 3. **Write fix-up plan** — Place in `.caddis/plans/<feature>-fixup.md`
342
342
  4. **Execute** — Implement Agent works through issues sequentially
343
343
  5. **Verify** — Run full test suite + visual inspection
344
344
  6. **Commit** — `fix(<scope>): post-track cross-component polish`
@@ -562,7 +562,7 @@ Load .github/skills/workflow/agent-orchestration/SKILL.md for the methodology."
562
562
  ```
563
563
  Operator opens new chat:
564
564
 
565
- "Read .github/plans/<feature>.md and implement Phase N.
565
+ "Read .caddis/plans/<feature>.md and implement Phase N.
566
566
  Run pytest after each file change. Commit when all tests pass.
567
567
  Use message: feat(<scope>): phase N — <summary>"
568
568
  ```
@@ -575,7 +575,7 @@ Operator opens new chat:
575
575
  "All implementation phases for <feature> are complete.
576
576
  Audit all changed files for cross-component issues.
577
577
  Catalogue each issue (A–N) with root cause, fix, and acceptance criteria.
578
- Write the fix-up plan to .github/plans/<feature>-fixup.md"
578
+ Write the fix-up plan to .caddis/plans/<feature>-fixup.md"
579
579
  ```
580
580
 
581
581
  ### Absorbing Mid-Flight Changes
@@ -584,7 +584,7 @@ Write the fix-up plan to .github/plans/<feature>-fixup.md"
584
584
  Operator opens new chat:
585
585
 
586
586
  "I independently added <change> to <file(s)>.
587
- Read .github/plans/<feature>.md and update it to absorb this change.
587
+ Read .caddis/plans/<feature>.md and update it to absorb this change.
588
588
  Trace every affected step, update embedded prompts, re-verify fidelity."
589
589
  ```
590
590
 
@@ -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.54",
2
+ "poolVersion": "1.3.75",
3
3
  "bundles": {
4
- "antigravity-plugin": "1.3.54",
5
- "antigravity-plugin-extras": "1.3.20"
4
+ "antigravity-plugin": "1.3.75",
5
+ "antigravity-plugin-extras": "1.3.24"
6
6
  }
7
7
  }
package/dist/cli.js CHANGED
@@ -4527,6 +4527,32 @@ var init_drive = __esm({
4527
4527
  }
4528
4528
  });
4529
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
+
4530
4556
  // src/commands/report.ts
4531
4557
  async function checkCliUpdate(currentVersion) {
4532
4558
  const result = await run("npm", ["view", "@caddis/cli", "version"], { timeout: 5e3 });
@@ -4541,7 +4567,10 @@ function classify(adapter, detection, status4, poolVersion) {
4541
4567
  if (!status4.installed) return "missing";
4542
4568
  if (!status4.version) return "unknown";
4543
4569
  if (poolVersion === "unknown") return "unknown";
4544
- 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";
4545
4574
  }
4546
4575
  async function gather(adapters, options = {}) {
4547
4576
  const manifest = bundleManifest();
@@ -4578,6 +4607,7 @@ function actionable(report, includeCurrent = false) {
4578
4607
  return report.agents.filter((entry) => {
4579
4608
  if (!entry.detection.present || !entry.adapter.supported) return false;
4580
4609
  if (includeCurrent) return true;
4610
+ if (entry.drift === "ahead") return false;
4581
4611
  return entry.drift !== "current" || entry.extrasDrift === "stale" || entry.extrasDrift === "unknown";
4582
4612
  });
4583
4613
  }
@@ -4586,6 +4616,7 @@ var init_report = __esm({
4586
4616
  "use strict";
4587
4617
  init_exec();
4588
4618
  init_pkg();
4619
+ init_semver();
4589
4620
  }
4590
4621
  });
4591
4622
 
@@ -4671,10 +4702,18 @@ async function update(options) {
4671
4702
  const targets = actionable(report, options.force === true);
4672
4703
  if (targets.length === 0) {
4673
4704
  const supportedPresent = report.agents.filter((e2) => e2.detection.present && e2.adapter.supported);
4705
+ const ahead = report.agents.filter((e2) => e2.drift === "ahead");
4674
4706
  line("");
4675
- line(
4676
- 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.`
4677
- );
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
+ }
4678
4717
  return 0;
4679
4718
  }
4680
4719
  line("");
@@ -4726,7 +4765,7 @@ function renderEnvironment(report, problems) {
4726
4765
  item("info", `platform ${process2.platform} ${process2.arch} (${os3.release()})`);
4727
4766
  if (report.cliUpdate) {
4728
4767
  item("warn", `@caddis/cli ${report.cliVersion} ${color.dim("\u2192")} ${color.bold(report.cliUpdate.latest)} available`);
4729
- detail("everything below is compared against the pool THIS install carries, which is now behind the published one");
4768
+ detail('so EVERY "current" below may be wrong \u2014 it only means "matches the pool this install carries"');
4730
4769
  problems.push({
4731
4770
  kind: "problem",
4732
4771
  text: `@caddis/cli itself is behind: ${report.cliUpdate.current} installed, ${report.cliUpdate.latest} published`,
@@ -4805,6 +4844,7 @@ function renderSummary(report, findings) {
4805
4844
  function markFor(drift) {
4806
4845
  switch (drift) {
4807
4846
  case "current":
4847
+ case "ahead":
4808
4848
  return "ok";
4809
4849
  case "stale":
4810
4850
  case "missing":
@@ -4820,6 +4860,8 @@ function describe(entry, poolVersion) {
4820
4860
  switch (entry.drift) {
4821
4861
  case "current":
4822
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`)}`;
4823
4865
  case "stale":
4824
4866
  return `caddis ${color.yellow(entry.status.version ?? "?")} ${color.dim("\u2192")} ${color.bold(poolVersion)} available`;
4825
4867
  case "unknown":
@@ -4978,7 +5020,15 @@ async function status3(options) {
4978
5020
  }
4979
5021
  line("");
4980
5022
  line(
4981
- ` ${color.bold("caddis")} cli ${color.bold(report.cliVersion)} \xB7 pool ${color.bold(report.poolVersion)}` + (report.extrasVersion ? ` \xB7 extras ${color.bold(report.extrasVersion)}` : "")
5023
+ // The pool is named as the YARDSTICK, in the header, because that is what every verdict below
5024
+ // is measured against and status cannot see the marketplace — it is deliberately network-free.
5025
+ // A tool that cannot know something must not print a verdict that implies it does.
5026
+ //
5027
+ // Deliberately NOT a footer line. The first attempt added one, and it broke the existing
5028
+ // "stays quiet when everything is current" test — correctly: a qualifier phrased like a warning
5029
+ // fires on every healthy run, and a warning that always fires is the one nobody reads. doctor
5030
+ // carries the strong version, because doctor is where the network check actually happens.
5031
+ ` ${color.bold("caddis")} cli ${color.bold(report.cliVersion)} \xB7 pool ${color.bold(report.poolVersion)} ${color.dim('(what "current" is measured against)')}` + (report.extrasVersion ? ` \xB7 extras ${color.bold(report.extrasVersion)}` : "")
4982
5032
  );
4983
5033
  line("");
4984
5034
  line(renderTable(["AGENT", "DETECTED", "CADDIS", "EXTRAS", "STATE"], report.agents.map(toRow)));
@@ -4986,10 +5036,17 @@ async function status3(options) {
4986
5036
  const stale = report.agents.filter(
4987
5037
  (entry) => entry.drift === "stale" || entry.drift === "missing" || entry.extrasDrift === "stale"
4988
5038
  );
5039
+ const ahead = report.agents.filter((entry) => entry.drift === "ahead");
4989
5040
  if (stale.length > 0) {
4990
5041
  line(` ${color.yellow(`${stale.length} agent(s) behind`)} \u2014 run ${color.cyan("caddis update")} (or ${color.cyan("caddis doctor")} for detail)`);
4991
5042
  line("");
4992
5043
  }
5044
+ if (ahead.length > 0) {
5045
+ line(
5046
+ ` ${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.`
5047
+ );
5048
+ line("");
5049
+ }
4993
5050
  return 0;
4994
5051
  }
4995
5052
  function toRow(entry) {
@@ -5010,6 +5067,8 @@ function stateLabel(drift, disabled) {
5010
5067
  return disabled ? color.yellow("current, disabled") : color.green("current");
5011
5068
  case "stale":
5012
5069
  return color.yellow("update available");
5070
+ case "ahead":
5071
+ return color.green("newer than CLI pool");
5013
5072
  case "unknown":
5014
5073
  return color.yellow("version unknown");
5015
5074
  case "missing":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caddis/cli",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
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",