agy-plugin-cc 0.6.0 → 0.6.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/README.md +74 -19
- package/package.json +8 -2
- package/scripts/lib/process.mjs +109 -0
- package/scripts/npx-install.mjs +52 -22
package/README.md
CHANGED
|
@@ -4,6 +4,9 @@ Use Google's Antigravity CLI (`agy`) from inside Claude Code: delegate tasks to
|
|
|
4
4
|
|
|
5
5
|
This plugin is for Claude Code users who want an easy way to start using Antigravity from the workflow they already have.
|
|
6
6
|
|
|
7
|
+
> [!NOTE]
|
|
8
|
+
> This is an open-source, third-party community project. It is not affiliated with, endorsed by, or sponsored by Anthropic or Google.
|
|
9
|
+
|
|
7
10
|
## What You Get
|
|
8
11
|
|
|
9
12
|
- `/agy:review` for standard read-only code review
|
|
@@ -20,6 +23,7 @@ This plugin is for Claude Code users who want an easy way to start using Antigra
|
|
|
20
23
|
- **Google account for Antigravity sign-in.**
|
|
21
24
|
- Delegations contribute to your Antigravity usage limits. Check them anytime with `/agy:quota`.
|
|
22
25
|
- **Node.js 18 or later**
|
|
26
|
+
- **git**, for the review commands. The companion script collects the diff itself, so a missing git is reported as `git is not installed or not on PATH` rather than an empty review.
|
|
23
27
|
|
|
24
28
|
## Install
|
|
25
29
|
|
|
@@ -65,10 +69,12 @@ On Windows (PowerShell):
|
|
|
65
69
|
irm https://antigravity.google/cli/install.ps1 | iex
|
|
66
70
|
```
|
|
67
71
|
|
|
72
|
+
The plugin's own test suite runs on Windows as well as Linux in CI, including a check that it can actually execute the `.cmd` shims npm installs there.
|
|
73
|
+
|
|
68
74
|
If agy is installed but not signed in yet, run it once interactively:
|
|
69
75
|
|
|
70
76
|
```bash
|
|
71
|
-
!agy
|
|
77
|
+
! agy
|
|
72
78
|
```
|
|
73
79
|
|
|
74
80
|
After install, you should see:
|
|
@@ -156,6 +162,7 @@ Examples:
|
|
|
156
162
|
**Notes:**
|
|
157
163
|
|
|
158
164
|
- if you do not pass `--model` or `--effort`, agy chooses its own defaults
|
|
165
|
+
- some models reject `--effort` outright (agy 1.2.4 refuses it for `claude-opus-4-6-thinking` before running anything); the rescue agent then reruns once without the flag and says so
|
|
159
166
|
- write-capable runs use `--mode accept-edits` on the agy side; review and diagnosis runs stay read-only
|
|
160
167
|
- follow-up rescue requests can continue the latest agy conversation
|
|
161
168
|
|
|
@@ -197,7 +204,7 @@ Afterwards, continue in a terminal with `agy --conversation <id>`, or from Claud
|
|
|
197
204
|
|
|
198
205
|
### `/agy:status`
|
|
199
206
|
|
|
200
|
-
Shows active and recent background agy
|
|
207
|
+
Shows active and recent background agy work in this session. Two kinds appear, and both are listed: `agy:agy-rescue` subagent delegations from `/agy:rescue --background`, and backgrounded companion runs from `/agy:adversarial-review --background`, which are Claude Code background tasks rather than subagents.
|
|
201
208
|
|
|
202
209
|
Examples:
|
|
203
210
|
|
|
@@ -214,7 +221,7 @@ Use it to:
|
|
|
214
221
|
|
|
215
222
|
### `/agy:result`
|
|
216
223
|
|
|
217
|
-
Shows the stored final output of a finished background delegation, including the `conversation_id` so you can reopen that run with `/agy:continue` or `agy --conversation <id>`.
|
|
224
|
+
Shows the stored final output of a finished background run, subagent delegation or companion review alike, including the `conversation_id` so you can reopen that run with `/agy:continue` or `agy --conversation <id>`.
|
|
218
225
|
|
|
219
226
|
Examples:
|
|
220
227
|
|
|
@@ -236,7 +243,11 @@ Examples:
|
|
|
236
243
|
|
|
237
244
|
### `/agy:setup`
|
|
238
245
|
|
|
239
|
-
Checks agy readiness through a companion script (`scripts/agy-setup.mjs`) that runs every check itself (agy on PATH, auth probe,
|
|
246
|
+
Checks agy readiness through a companion script (`scripts/agy-setup.mjs`) that runs every check itself (agy on PATH, auth probe, a command probe and a file-read probe for headless permission denial, stop-review gate state) and prints a single JSON report with `ready`, per-check sections, and `nextSteps`.
|
|
247
|
+
|
|
248
|
+
Headless agy auto-denies any tool its `permissions.allow` rules do not cover, and it reports the refusal as `denied_actions` on an otherwise successful-looking result. The two probes exercise a terminal command and a file read, because the two need different rules (`command(...)` and `read_file(*)`) and `--mode accept-edits` covers neither. The report names each denied tool and the rule for it. That settings edit is yours to make by hand, in your own terminal: in a Claude Code auto mode session the classifier blocks the agent from editing the file, from passing `--dangerously-skip-permissions`, and even from querying `agy -p "/permissions"`.
|
|
249
|
+
|
|
250
|
+
When a probe fails, the report says which of three things went wrong rather than assuming a login problem: a real authentication failure, a restricted environment where the invoking shell blocked a syscall agy needs, or an unknown cause. Only the first is fixed by signing in again.
|
|
240
251
|
|
|
241
252
|
You can also use `/agy:setup` to manage the optional stop-review gate.
|
|
242
253
|
|
|
@@ -287,7 +298,11 @@ Then check in with:
|
|
|
287
298
|
/agy:transfer
|
|
288
299
|
```
|
|
289
300
|
|
|
290
|
-
Then continue in a terminal with
|
|
301
|
+
Then continue in a terminal with:
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
agy --conversation <id>
|
|
305
|
+
```
|
|
291
306
|
|
|
292
307
|
## How to Delegate
|
|
293
308
|
|
|
@@ -325,7 +340,7 @@ Either way, the result comes back with a `conversation_id`, so the thread stays
|
|
|
325
340
|
|
|
326
341
|
Off by default. When enabled, a Stop hook runs a read-only `agy -p` review of the previous Claude turn before the session is allowed to end. The reviewer answers `ALLOW:` or `BLOCK:` on its first line; a block keeps the session open with the reviewer's reason. Turns without code changes are allowed through immediately.
|
|
327
342
|
|
|
328
|
-
- Toggle per
|
|
343
|
+
- Toggle per workspace with `/agy:setup gate on` / `gate off`. The flag is stored outside the repository, under `CLAUDE_PLUGIN_DATA` and keyed by a hash of the workspace root, so there is nothing in your project to commit by mistake. A gate enabled under the older in-repository `.claude/agy.local.md` is still honoured until you set it through the command.
|
|
329
344
|
- If agy is missing, the gate skips with a note instead of blocking.
|
|
330
345
|
- Review failures and timeouts block with guidance to run `/agy:review` manually or turn the gate off.
|
|
331
346
|
|
|
@@ -338,15 +353,55 @@ Job control stays thin too: `/agy:status`, `/agy:result`, and `/agy:cancel` read
|
|
|
338
353
|
## Layout
|
|
339
354
|
|
|
340
355
|
```
|
|
341
|
-
.
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
356
|
+
.
|
|
357
|
+
├── .claude-plugin/
|
|
358
|
+
│ ├── marketplace.json marketplace catalog
|
|
359
|
+
│ └── plugin.json manifest (plugin name: agy)
|
|
360
|
+
├── agents/
|
|
361
|
+
│ └── agy-rescue.md forwarding subagent
|
|
362
|
+
├── commands/
|
|
363
|
+
│ ├── adversarial-review.md
|
|
364
|
+
│ ├── cancel.md
|
|
365
|
+
│ ├── continue.md
|
|
366
|
+
│ ├── quota.md
|
|
367
|
+
│ ├── rescue.md
|
|
368
|
+
│ ├── result.md
|
|
369
|
+
│ ├── review.md
|
|
370
|
+
│ ├── setup.md
|
|
371
|
+
│ ├── status.md
|
|
372
|
+
│ └── transfer.md
|
|
373
|
+
├── hooks/
|
|
374
|
+
│ └── hooks.json Stop hook wiring for the stop-review gate
|
|
375
|
+
├── prompts/
|
|
376
|
+
│ ├── adversarial-review.md challenge-review prompt
|
|
377
|
+
│ ├── review.md code-review prompt
|
|
378
|
+
│ ├── stop-review-gate.md stop-gate prompt
|
|
379
|
+
│ └── transfer.md session handoff prompt
|
|
380
|
+
├── schemas/
|
|
381
|
+
│ └── review-output.schema.json adversarial review output shape
|
|
382
|
+
├── scripts/
|
|
383
|
+
│ ├── lib/
|
|
384
|
+
│ │ ├── agy.mjs agy invocation and result normalizing
|
|
385
|
+
│ │ ├── git.mjs diff collection
|
|
386
|
+
│ │ ├── process.mjs spawn without a shell, Windows shims
|
|
387
|
+
│ │ ├── prompts.mjs prompt loader
|
|
388
|
+
│ │ ├── state.mjs per-workspace state
|
|
389
|
+
│ │ ├── stop-review.mjs stop-review gate decision logic
|
|
390
|
+
│ │ └── workspace.mjs repository root resolution
|
|
391
|
+
│ ├── agy-companion.mjs review, transfer, quota, gate subcommands
|
|
392
|
+
│ ├── agy-setup.mjs /agy:setup readiness report
|
|
393
|
+
│ ├── bump-version.mjs version metadata check and bump
|
|
394
|
+
│ ├── npx-install.mjs npx agy-plugin-cc installer
|
|
395
|
+
│ ├── prepack-check.mjs refuses to npm pack with .bak files present
|
|
396
|
+
│ └── stop-review-gate-hook.mjs stop-review gate (dependency-free node)
|
|
397
|
+
├── skills/
|
|
398
|
+
│ ├── agy-cli-runtime/ CLI call contract
|
|
399
|
+
│ └── agy-result-handling/ output presentation rules
|
|
400
|
+
├── tests/ node --test suite, run with npm test
|
|
401
|
+
├── CHANGELOG.md
|
|
402
|
+
├── LICENSE
|
|
403
|
+
├── README.md
|
|
404
|
+
└── package.json npm package (agy-plugin-cc) for the npx installer
|
|
350
405
|
```
|
|
351
406
|
|
|
352
407
|
## FAQ
|
|
@@ -355,7 +410,7 @@ package.json npm package (agy-plugin-cc) for the npx installer
|
|
|
355
410
|
|
|
356
411
|
If you are already signed into agy on this machine, that account works immediately here too. This plugin uses your local Antigravity CLI authentication.
|
|
357
412
|
|
|
358
|
-
If you have not used Antigravity yet, install the CLI and run `!agy` once to sign in with your Google account, then run `/agy:setup` to confirm everything is ready.
|
|
413
|
+
If you have not used Antigravity yet, [install the CLI](#install) and run `! agy` once to sign in with your Google account, then run [`/agy:setup`](#agysetup) to confirm everything is ready. Interactive agy needs a TTY, so the `! agy` form only works where the host gives the command a real terminal; otherwise sign in from a terminal of your own.
|
|
359
414
|
|
|
360
415
|
### Does the plugin use a separate Antigravity runtime?
|
|
361
416
|
|
|
@@ -363,12 +418,12 @@ No. Every delegation is a call to the same local `agy` binary you would use dire
|
|
|
363
418
|
|
|
364
419
|
### Will it use the same agy settings I already have?
|
|
365
420
|
|
|
366
|
-
Yes. The plugin picks up your existing configuration in `~/.gemini/antigravity-cli/settings.json`, including `permissions.allow` rules. Headless runs auto-deny tools not covered by those rules; `/agy:setup` detects
|
|
421
|
+
Yes. The plugin picks up your existing configuration in `~/.gemini/antigravity-cli/settings.json`, including `permissions.allow` rules. Headless runs auto-deny tools not covered by those rules, file reads (`read_file(*)`) as well as commands (`command(*)` or a narrower target); [`/agy:setup`](#agysetup) detects both and shows the rule to add. You make that edit by hand, outside the agent session: an auto mode session cannot apply it for you.
|
|
367
422
|
|
|
368
423
|
### Does it spend my Antigravity quota?
|
|
369
424
|
|
|
370
|
-
Yes. Delegations, reviews, and stop-review gate runs all contribute to your Antigravity usage limits. `/agy:quota` shows what is left per bucket and is itself quota-free.
|
|
425
|
+
Yes. Delegations, reviews, and stop-review gate runs all contribute to your Antigravity usage limits. [`/agy:quota`](#agyquota) shows what is left per bucket and is itself quota-free.
|
|
371
426
|
|
|
372
427
|
### What does `npx agy-plugin-cc` actually do?
|
|
373
428
|
|
|
374
|
-
It runs `claude plugin marketplace add Eakkapoom-Name/antigravity-plugin-cc` and `claude plugin install agy@antigravity-plugin-cc` through your local `claude` CLI, then points you at `/agy:setup
|
|
429
|
+
It runs `claude plugin marketplace add Eakkapoom-Name/antigravity-plugin-cc` and `claude plugin install agy@antigravity-plugin-cc` through your local `claude` CLI, then points you at [`/agy:setup`](#agysetup). It is safe to rerun. See [Install](#install) for the manual steps.
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agy-plugin-cc",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"description": "Installer for the agy Claude Code plugin: use Google's Antigravity CLI (agy) from Claude Code to delegate tasks or review code.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"agy-plugin-cc": "scripts/npx-install.mjs"
|
|
7
7
|
},
|
|
8
8
|
"files": [
|
|
9
9
|
"scripts/npx-install.mjs",
|
|
10
|
+
"scripts/lib/process.mjs",
|
|
10
11
|
"README.md",
|
|
11
12
|
"LICENSE"
|
|
12
13
|
],
|
|
@@ -24,5 +25,10 @@
|
|
|
24
25
|
"agy"
|
|
25
26
|
],
|
|
26
27
|
"author": "toaster",
|
|
27
|
-
"license": "MIT"
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"scripts": {
|
|
30
|
+
"test": "node --test",
|
|
31
|
+
"check-version": "node scripts/bump-version.mjs --check",
|
|
32
|
+
"prepack": "node scripts/prepack-check.mjs"
|
|
33
|
+
}
|
|
28
34
|
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
|
|
6
|
+
const DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD";
|
|
7
|
+
|
|
8
|
+
// npm installs `agy` and `claude` on Windows as `.cmd` and `.ps1` shims, which
|
|
9
|
+
// `spawnSync("agy", ...)` cannot execute: there is no `agy` file to exec.
|
|
10
|
+
//
|
|
11
|
+
// `shell: true` would fix it and must not be used. These commands are handed
|
|
12
|
+
// prompt text, diffs, and file paths; routing any of that through a shell turns
|
|
13
|
+
// argument data into shell syntax. Resolving the real file and spawning it
|
|
14
|
+
// directly keeps arguments as arguments.
|
|
15
|
+
//
|
|
16
|
+
// This also replaces the `which` call the setup script used, which only existed
|
|
17
|
+
// on Unix in the first place.
|
|
18
|
+
export function resolveCommand(name, env = process.env) {
|
|
19
|
+
const raw = String(name ?? "");
|
|
20
|
+
if (!raw) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const isWindows = process.platform === "win32";
|
|
25
|
+
const extensions = isWindows
|
|
26
|
+
? ["", ...String(env.PATHEXT || DEFAULT_PATHEXT).split(";").filter(Boolean)]
|
|
27
|
+
: [""];
|
|
28
|
+
|
|
29
|
+
const exists = (candidate) => {
|
|
30
|
+
try {
|
|
31
|
+
return fs.statSync(candidate).isFile();
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// An explicit path is used as given; only PATH entries get the extension walk.
|
|
38
|
+
if (raw.includes("/") || raw.includes("\\")) {
|
|
39
|
+
const absolute = path.resolve(raw);
|
|
40
|
+
for (const extension of extensions) {
|
|
41
|
+
if (exists(absolute + extension)) {
|
|
42
|
+
return absolute + extension;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
for (const dir of String(env.PATH || "").split(path.delimiter).filter(Boolean)) {
|
|
49
|
+
for (const extension of extensions) {
|
|
50
|
+
const candidate = path.join(dir, raw + extension);
|
|
51
|
+
if (exists(candidate)) {
|
|
52
|
+
return candidate;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function commandAvailable(name, env = process.env) {
|
|
60
|
+
return resolveCommand(name, env) !== null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const WINDOWS_BATCH = /\.(cmd|bat)$/i;
|
|
64
|
+
|
|
65
|
+
// Resolving the shim is only half the job: Windows cannot exec a .cmd or .bat
|
|
66
|
+
// image at all, and since the fix for CVE-2024-27980 Node refuses to try,
|
|
67
|
+
// throwing EINVAL. Such a file has to be handed to cmd.exe.
|
|
68
|
+
//
|
|
69
|
+
// cmd.exe is invoked explicitly rather than through `shell: true` because the
|
|
70
|
+
// difference is what stays fixed. Here the interpreter and the command file are
|
|
71
|
+
// decided by this function and only the arguments are quoted. With
|
|
72
|
+
// `shell: true` the whole line, command included, would be assembled from
|
|
73
|
+
// caller-supplied text.
|
|
74
|
+
export function quoteForCmd(value) {
|
|
75
|
+
const text = String(value ?? "");
|
|
76
|
+
// Windows argument rules: backslashes are literal unless they precede a
|
|
77
|
+
// quote, where they must be doubled.
|
|
78
|
+
const escaped = text.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\*)$/, "$1$1");
|
|
79
|
+
return `"${escaped}"`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function buildCmdInvocation(target, args, env = process.env) {
|
|
83
|
+
// /d skips AutoRun commands from the registry, /s fixes how the outer quotes
|
|
84
|
+
// around the whole command are parsed, /c runs it and exits.
|
|
85
|
+
return {
|
|
86
|
+
file: env.ComSpec || "cmd.exe",
|
|
87
|
+
args: ["/d", "/s", "/c", `"${[target, ...args].map(quoteForCmd).join(" ")}"`]
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Spawns without a shell, always. When the command cannot be resolved the raw
|
|
92
|
+
// name is passed through so the caller still gets a normal ENOENT result rather
|
|
93
|
+
// than a different error shape from this layer.
|
|
94
|
+
export function runCommand(name, args, options = {}) {
|
|
95
|
+
const resolved = resolveCommand(name) ?? name;
|
|
96
|
+
|
|
97
|
+
if (process.platform === "win32" && WINDOWS_BATCH.test(resolved)) {
|
|
98
|
+
const invocation = buildCmdInvocation(resolved, args);
|
|
99
|
+
return spawnSync(invocation.file, invocation.args, {
|
|
100
|
+
...options,
|
|
101
|
+
shell: false,
|
|
102
|
+
// The argument vector is already quoted for cmd.exe above; letting Node
|
|
103
|
+
// quote it again would double the escaping.
|
|
104
|
+
windowsVerbatimArguments: true
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return spawnSync(resolved, args, { ...options, shell: false });
|
|
109
|
+
}
|
package/scripts/npx-install.mjs
CHANGED
|
@@ -4,14 +4,19 @@
|
|
|
4
4
|
// `npx agy-plugin-cc` registers the marketplace and installs the plugin
|
|
5
5
|
// through the user's local `claude` CLI. Safe to rerun.
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import fs from "node:fs";
|
|
8
8
|
import process from "node:process";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
import { runCommand } from "./lib/process.mjs";
|
|
9
12
|
|
|
10
13
|
const MARKETPLACE = "Eakkapoom-Name/antigravity-plugin-cc";
|
|
11
14
|
const PLUGIN = "agy@antigravity-plugin-cc";
|
|
12
15
|
|
|
13
16
|
function run(args) {
|
|
14
|
-
|
|
17
|
+
// Resolved through PATH and PATHEXT so the claude.cmd shim npm installs on
|
|
18
|
+
// Windows is found; a bare spawn of "claude" cannot execute it.
|
|
19
|
+
const result = runCommand("claude", args, { encoding: "utf8" });
|
|
15
20
|
return {
|
|
16
21
|
ok: result.status === 0,
|
|
17
22
|
enoent: result.error?.code === "ENOENT",
|
|
@@ -19,8 +24,16 @@ function run(args) {
|
|
|
19
24
|
};
|
|
20
25
|
}
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
// A rerun is a no-op, not a failure, but a bare /already/i over the combined
|
|
28
|
+
// stdout and stderr also swallows any genuine failure whose message happens to
|
|
29
|
+
// contain the word. Match the shape of a no-op instead. The two observed
|
|
30
|
+
// messages are `Marketplace '<name>' already on disk` and
|
|
31
|
+
// `Plugin "<name>" is already installed`; the rest are near neighbours.
|
|
32
|
+
const ALREADY_DONE =
|
|
33
|
+
/\balready\s+(?:on disk|installed|added|exists?|present|registered|configured|up[- ]to[- ]date)\b/i;
|
|
34
|
+
|
|
35
|
+
export function alreadyDone(output) {
|
|
36
|
+
return ALREADY_DONE.test(String(output ?? ""));
|
|
24
37
|
}
|
|
25
38
|
|
|
26
39
|
function fail(message) {
|
|
@@ -28,27 +41,44 @@ function fail(message) {
|
|
|
28
41
|
process.exit(1);
|
|
29
42
|
}
|
|
30
43
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
function main() {
|
|
45
|
+
console.log(`Adding marketplace ${MARKETPLACE} ...`);
|
|
46
|
+
const add = run(["plugin", "marketplace", "add", MARKETPLACE]);
|
|
47
|
+
if (add.enoent) {
|
|
48
|
+
fail(
|
|
49
|
+
"The `claude` CLI was not found on PATH. Install Claude Code first: https://code.claude.com/docs"
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
// Kept for claude CLI releases where a duplicate add exits non-zero. On the
|
|
53
|
+
// version this was measured against, both duplicate cases exit 0, so this
|
|
54
|
+
// branch is unreachable there and the message below is what reports the
|
|
55
|
+
// no-op. Removing it would break older installs for no gain.
|
|
56
|
+
if (!add.ok && !alreadyDone(add.output)) {
|
|
57
|
+
fail(`Could not add the marketplace:\n${add.output}`);
|
|
58
|
+
}
|
|
59
|
+
// A rerun succeeds rather than failing, so the exit status alone does not say
|
|
60
|
+
// whether anything changed; the message does.
|
|
61
|
+
console.log(alreadyDone(add.output) ? "Marketplace already added." : "Marketplace added.");
|
|
42
62
|
|
|
43
|
-
console.log(`Installing ${PLUGIN} ...`);
|
|
44
|
-
const install = run(["plugin", "install", PLUGIN]);
|
|
45
|
-
if (!install.ok && !alreadyDone(install.output)) {
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
console.log(install.
|
|
63
|
+
console.log(`Installing ${PLUGIN} ...`);
|
|
64
|
+
const install = run(["plugin", "install", PLUGIN]);
|
|
65
|
+
if (!install.ok && !alreadyDone(install.output)) {
|
|
66
|
+
fail(`Could not install the plugin:\n${install.output}`);
|
|
67
|
+
}
|
|
68
|
+
console.log(alreadyDone(install.output) ? "Plugin already installed." : "Plugin installed.");
|
|
49
69
|
|
|
50
|
-
console.log(`
|
|
70
|
+
console.log(`
|
|
51
71
|
Done. Next steps:
|
|
52
72
|
1. Start (or restart) Claude Code.
|
|
53
73
|
2. Run /agy:setup to check that the Antigravity CLI (agy) is ready.
|
|
54
74
|
If agy is not installed yet, see https://antigravity.google/docs/cli/install/`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Only install when invoked as a script; importing this module (for tests) must
|
|
78
|
+
// not shell out to the claude CLI.
|
|
79
|
+
if (
|
|
80
|
+
process.argv[1] &&
|
|
81
|
+
fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url))
|
|
82
|
+
) {
|
|
83
|
+
main();
|
|
84
|
+
}
|