@caddis/cli 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundles/antigravity-plugin/agents/anchor.md +60 -0
- package/bundles/antigravity-plugin/agents/claude-md-curator.md +141 -0
- package/bundles/antigravity-plugin/agents/codebase-audit.md +53 -0
- package/bundles/antigravity-plugin/agents/data-engineer.md +51 -0
- package/bundles/antigravity-plugin/agents/debug.md +48 -0
- package/bundles/antigravity-plugin/agents/knowledge-transfer.md +139 -0
- package/bundles/antigravity-plugin/agents/security-analyst.md +49 -0
- package/bundles/antigravity-plugin/agents/sql-expert.md +46 -0
- package/bundles/antigravity-plugin/agents/tester.md +45 -0
- package/bundles/antigravity-plugin/agents/ui-design-reviewer.md +110 -0
- package/bundles/antigravity-plugin/plugin.json +1 -1
- package/bundles/antigravity-plugin/skills/_registry.md +2 -0
- package/bundles/antigravity-plugin/skills/cross-review/SKILL.md +7 -4
- package/bundles/antigravity-plugin/skills/dashboard-perf-sweep/SKILL.md +132 -0
- package/bundles/antigravity-plugin/skills/dashboard-perf-sweep/scripts/perf_sweep.py +249 -0
- package/bundles/antigravity-plugin/skills/explainer-doc/SKILL.md +97 -0
- package/bundles/antigravity-plugin/warm_start_agy.py +7 -4
- package/bundles/antigravity-plugin-extras/plugin.json +1 -1
- package/bundles/antigravity-plugin-extras/skills/cross-review/SKILL.md +6 -3
- package/bundles/manifest.json +3 -3
- package/dist/cli.js +132 -71
- package/package.json +1 -1
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
"""caddis warm-start hook for agy (Antigravity) — PreInvocation event.
|
|
3
3
|
|
|
4
4
|
Ports the relay half of claude-harness/hooks/inject_relay.py. agy has NO SessionStart event, so the
|
|
5
|
-
nearest equivalent is PreInvocation with `invocationNum ==
|
|
5
|
+
nearest equivalent is PreInvocation with `invocationNum == 0` — the first model turn of a session.
|
|
6
|
+
**agy's invocations are 0-indexed** (confirmed via a live-fire session against the real `agy` binary
|
|
7
|
+
2026-07-30 -- the original code checked `== 1`, which is agy's SECOND turn, so this hook silently
|
|
8
|
+
never fired at all until the live check caught it; see .caddis/plans/agy-hooks-port.md step 5).
|
|
6
9
|
On that turn only, the workspace's relay doc is injected as an ephemeral step so a fresh agy session
|
|
7
10
|
resumes with zero re-discovery. Every later invocation emits nothing (re-injecting the relay on every
|
|
8
11
|
turn would burn context and drown the conversation).
|
|
@@ -118,10 +121,10 @@ def main() -> None:
|
|
|
118
121
|
return
|
|
119
122
|
if not isinstance(data, dict):
|
|
120
123
|
return
|
|
121
|
-
# SessionStart equivalent: the FIRST invocation only
|
|
122
|
-
# invocationNum) injects nothing.
|
|
124
|
+
# SessionStart equivalent: the FIRST invocation only (agy 0-indexes invocationNum, so the
|
|
125
|
+
# first turn is 0, not 1). Anything else (or an unparseable invocationNum) injects nothing.
|
|
123
126
|
try:
|
|
124
|
-
if int(data.get("invocationNum")) !=
|
|
127
|
+
if int(data.get("invocationNum")) != 0:
|
|
125
128
|
return
|
|
126
129
|
except Exception:
|
|
127
130
|
return
|
|
@@ -28,10 +28,13 @@ churn — the preset table in `oss_review.py` is the one place to edit a rename,
|
|
|
28
28
|
`REVIEW_BASE_URL` always win, so you can point at any new id without touching code.
|
|
29
29
|
|
|
30
30
|
## How to run
|
|
31
|
-
From the repo root
|
|
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).
|
|
32
35
|
```
|
|
33
|
-
python
|
|
34
|
-
python
|
|
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
|
|
35
38
|
```
|
|
36
39
|
Optional flags: `--cwd <repo>`, `--base-url <url>`, `--model <id>` (override the env).
|
|
37
40
|
|
package/bundles/manifest.json
CHANGED
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) {
|
|
@@ -4483,6 +4528,13 @@ var init_drive = __esm({
|
|
|
4483
4528
|
});
|
|
4484
4529
|
|
|
4485
4530
|
// src/commands/report.ts
|
|
4531
|
+
async function checkCliUpdate(currentVersion) {
|
|
4532
|
+
const result = await run("npm", ["view", "@caddis/cli", "version"], { timeout: 5e3 });
|
|
4533
|
+
if (!result.ok) return null;
|
|
4534
|
+
const latest = result.stdout.trim();
|
|
4535
|
+
if (!latest || latest === currentVersion) return null;
|
|
4536
|
+
return { current: currentVersion, latest };
|
|
4537
|
+
}
|
|
4486
4538
|
function classify(adapter, detection, status4, poolVersion) {
|
|
4487
4539
|
if (!detection.present) return "absent";
|
|
4488
4540
|
if (!adapter.supported) return "unsupported";
|
|
@@ -4491,24 +4543,28 @@ function classify(adapter, detection, status4, poolVersion) {
|
|
|
4491
4543
|
if (poolVersion === "unknown") return "unknown";
|
|
4492
4544
|
return status4.version === poolVersion ? "current" : "stale";
|
|
4493
4545
|
}
|
|
4494
|
-
async function gather(adapters) {
|
|
4546
|
+
async function gather(adapters, options = {}) {
|
|
4495
4547
|
const manifest = bundleManifest();
|
|
4496
4548
|
const poolVersion = manifest.poolVersion;
|
|
4497
4549
|
const extrasVersion = manifest.bundles["antigravity-plugin-extras"];
|
|
4498
|
-
const
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
adapter,
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4550
|
+
const cliVersion = packageInfo().version;
|
|
4551
|
+
const [agents, cliUpdate] = await Promise.all([
|
|
4552
|
+
Promise.all(
|
|
4553
|
+
adapters.map(async (adapter) => {
|
|
4554
|
+
const detection = await adapter.detect();
|
|
4555
|
+
const status4 = detection.present ? await adapter.status() : { installed: false, note: "agent not installed" };
|
|
4556
|
+
return {
|
|
4557
|
+
adapter,
|
|
4558
|
+
detection,
|
|
4559
|
+
status: status4,
|
|
4560
|
+
drift: classify(adapter, detection, status4, poolVersion),
|
|
4561
|
+
extrasDrift: classifyExtras(adapter, detection, status4, extrasVersion)
|
|
4562
|
+
};
|
|
4563
|
+
})
|
|
4564
|
+
),
|
|
4565
|
+
options.checkCliUpdate ? checkCliUpdate(cliVersion) : Promise.resolve(null)
|
|
4566
|
+
]);
|
|
4567
|
+
return { cliVersion, poolVersion, extrasVersion, agents, cliUpdate: cliUpdate ?? void 0 };
|
|
4512
4568
|
}
|
|
4513
4569
|
function classifyExtras(adapter, detection, status4, extrasVersion) {
|
|
4514
4570
|
if (!status4.extras) return void 0;
|
|
@@ -4528,6 +4584,7 @@ function actionable(report, includeCurrent = false) {
|
|
|
4528
4584
|
var init_report = __esm({
|
|
4529
4585
|
"src/commands/report.ts"() {
|
|
4530
4586
|
"use strict";
|
|
4587
|
+
init_exec();
|
|
4531
4588
|
init_pkg();
|
|
4532
4589
|
}
|
|
4533
4590
|
});
|
|
@@ -4646,7 +4703,7 @@ __export(doctor_exports, {
|
|
|
4646
4703
|
import os3 from "os";
|
|
4647
4704
|
import process2 from "process";
|
|
4648
4705
|
async function doctor(options) {
|
|
4649
|
-
const report = await gather(options.adapters);
|
|
4706
|
+
const report = await gather(options.adapters, { checkCliUpdate: true });
|
|
4650
4707
|
const findings = [];
|
|
4651
4708
|
if (options.json) {
|
|
4652
4709
|
line(JSON.stringify(toJson(report), null, 2));
|
|
@@ -4667,7 +4724,17 @@ function renderEnvironment(report, problems) {
|
|
|
4667
4724
|
problems.push({ kind: "problem", text: `node ${process2.versions.node} is below the required ${REQUIRED_NODE_MAJOR}.${REQUIRED_NODE_MINOR}`, fix: "upgrade node" });
|
|
4668
4725
|
}
|
|
4669
4726
|
item("info", `platform ${process2.platform} ${process2.arch} (${os3.release()})`);
|
|
4670
|
-
|
|
4727
|
+
if (report.cliUpdate) {
|
|
4728
|
+
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");
|
|
4730
|
+
problems.push({
|
|
4731
|
+
kind: "problem",
|
|
4732
|
+
text: `@caddis/cli itself is behind: ${report.cliUpdate.current} installed, ${report.cliUpdate.latest} published`,
|
|
4733
|
+
fix: "npm i -g @caddis/cli@latest"
|
|
4734
|
+
});
|
|
4735
|
+
} else {
|
|
4736
|
+
item("ok", `@caddis/cli ${report.cliVersion}`);
|
|
4737
|
+
}
|
|
4671
4738
|
const manifest = bundleManifest();
|
|
4672
4739
|
if (report.poolVersion === "unknown") {
|
|
4673
4740
|
item("fail", "shipped pool version unknown");
|
|
@@ -4837,6 +4904,7 @@ function problemFor(entry, poolVersion) {
|
|
|
4837
4904
|
function toJson(report) {
|
|
4838
4905
|
return {
|
|
4839
4906
|
cliVersion: report.cliVersion,
|
|
4907
|
+
cliUpdate: report.cliUpdate ?? null,
|
|
4840
4908
|
poolVersion: report.poolVersion,
|
|
4841
4909
|
extrasVersion: report.extrasVersion ?? null,
|
|
4842
4910
|
node: process2.versions.node,
|
|
@@ -4990,44 +5058,8 @@ var {
|
|
|
4990
5058
|
Help
|
|
4991
5059
|
} = import_index.default;
|
|
4992
5060
|
|
|
4993
|
-
// src/
|
|
4994
|
-
|
|
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
|
-
}
|
|
5061
|
+
// src/agents/claude.ts
|
|
5062
|
+
init_exec();
|
|
5031
5063
|
|
|
5032
5064
|
// src/util/which.ts
|
|
5033
5065
|
var import_which = __toESM(require_lib(), 1);
|
|
@@ -5167,7 +5199,8 @@ var claudeAdapter = {
|
|
|
5167
5199
|
};
|
|
5168
5200
|
|
|
5169
5201
|
// src/agents/agy.ts
|
|
5170
|
-
|
|
5202
|
+
init_exec();
|
|
5203
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2, mkdtempSync, cpSync, rmSync } from "fs";
|
|
5171
5204
|
import os from "os";
|
|
5172
5205
|
import path2 from "path";
|
|
5173
5206
|
init_pkg();
|
|
@@ -5239,6 +5272,25 @@ function shortenHome(target) {
|
|
|
5239
5272
|
const home = os.homedir();
|
|
5240
5273
|
return target.startsWith(home) ? path2.join("~", target.slice(home.length)) : target;
|
|
5241
5274
|
}
|
|
5275
|
+
function stageForAgy(bundleDir) {
|
|
5276
|
+
if (!bundleDir.includes("@")) {
|
|
5277
|
+
return { installPath: bundleDir, cleanup: () => {
|
|
5278
|
+
} };
|
|
5279
|
+
}
|
|
5280
|
+
const staged = mkdtempSync(path2.join(os.tmpdir(), "caddis-agy-"));
|
|
5281
|
+
const dest = path2.join(staged, path2.basename(bundleDir));
|
|
5282
|
+
cpSync(bundleDir, dest, { recursive: true });
|
|
5283
|
+
return {
|
|
5284
|
+
installPath: dest,
|
|
5285
|
+
// Best-effort: a stray temp dir left behind is harmless clutter, not a failure.
|
|
5286
|
+
cleanup: () => {
|
|
5287
|
+
try {
|
|
5288
|
+
rmSync(staged, { recursive: true, force: true });
|
|
5289
|
+
} catch {
|
|
5290
|
+
}
|
|
5291
|
+
}
|
|
5292
|
+
};
|
|
5293
|
+
}
|
|
5242
5294
|
async function drive2(_action, options) {
|
|
5243
5295
|
const detected = await detect2();
|
|
5244
5296
|
if (!detected.present) {
|
|
@@ -5253,12 +5305,12 @@ async function drive2(_action, options) {
|
|
|
5253
5305
|
message: `the ${BUNDLE} bundle is missing from this package \u2014 reinstall @caddis/cli`
|
|
5254
5306
|
};
|
|
5255
5307
|
}
|
|
5256
|
-
const planned = [{ cmd: BIN2, args: ["plugin", "install", bundle] }];
|
|
5308
|
+
const planned = [{ cmd: BIN2, args: ["plugin", "install", bundle], bundleDir: bundle }];
|
|
5257
5309
|
const extrasInstalled = extrasStatusFromHome(os.homedir()).installed;
|
|
5258
5310
|
if (options.extras === true || extrasInstalled) {
|
|
5259
5311
|
const extrasBundle = bundlePath(EXTRAS_BUNDLE);
|
|
5260
5312
|
if (extrasBundle) {
|
|
5261
|
-
planned.push({ cmd: BIN2, args: ["plugin", "install", extrasBundle] });
|
|
5313
|
+
planned.push({ cmd: BIN2, args: ["plugin", "install", extrasBundle], bundleDir: extrasBundle });
|
|
5262
5314
|
} else if (options.extras === true) {
|
|
5263
5315
|
return {
|
|
5264
5316
|
ok: false,
|
|
@@ -5272,25 +5324,34 @@ async function drive2(_action, options) {
|
|
|
5272
5324
|
return {
|
|
5273
5325
|
ok: true,
|
|
5274
5326
|
skipped: true,
|
|
5327
|
+
// Show the logical bundle path, not a staged temp copy that was never created.
|
|
5275
5328
|
steps: planned.map((s) => ({ command: formatCommand(s.cmd, s.args), ok: true, code: null })),
|
|
5276
5329
|
message: "dry run \u2014 nothing executed"
|
|
5277
5330
|
};
|
|
5278
5331
|
}
|
|
5279
|
-
const
|
|
5280
|
-
|
|
5281
|
-
const
|
|
5282
|
-
const
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5332
|
+
const cleanups = [];
|
|
5333
|
+
try {
|
|
5334
|
+
const steps2 = [];
|
|
5335
|
+
for (const step of planned) {
|
|
5336
|
+
const command = formatCommand(step.cmd, step.args);
|
|
5337
|
+
const staged = stageForAgy(step.bundleDir);
|
|
5338
|
+
cleanups.push(staged.cleanup);
|
|
5339
|
+
const execArgs = [...step.args.slice(0, -1), staged.installPath];
|
|
5340
|
+
const result = await run(step.cmd, execArgs);
|
|
5341
|
+
steps2.push({
|
|
5342
|
+
command,
|
|
5343
|
+
ok: result.ok,
|
|
5344
|
+
code: result.code,
|
|
5345
|
+
output: result.ok ? void 0 : (result.stderr || result.stdout || result.failure || "").trim().split(/\r?\n/).slice(-3).join("\n")
|
|
5346
|
+
});
|
|
5347
|
+
if (!result.ok) {
|
|
5348
|
+
return { ok: false, skipped: false, steps: steps2, message: `\`${command}\` failed` };
|
|
5349
|
+
}
|
|
5291
5350
|
}
|
|
5351
|
+
return { ok: true, skipped: false, steps: steps2 };
|
|
5352
|
+
} finally {
|
|
5353
|
+
cleanups.forEach((fn) => fn());
|
|
5292
5354
|
}
|
|
5293
|
-
return { ok: true, skipped: false, steps: steps2 };
|
|
5294
5355
|
}
|
|
5295
5356
|
var agyAdapter = {
|
|
5296
5357
|
id: "agy",
|
package/package.json
CHANGED