agy-plugin-cc 0.6.1 → 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 +32 -7
- package/package.json +8 -2
- package/scripts/lib/process.mjs +109 -0
- package/scripts/npx-install.mjs +52 -22
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ This plugin is for Claude Code users who want an easy way to start using Antigra
|
|
|
23
23
|
- **Google account for Antigravity sign-in.**
|
|
24
24
|
- Delegations contribute to your Antigravity usage limits. Check them anytime with `/agy:quota`.
|
|
25
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.
|
|
26
27
|
|
|
27
28
|
## Install
|
|
28
29
|
|
|
@@ -68,10 +69,12 @@ On Windows (PowerShell):
|
|
|
68
69
|
irm https://antigravity.google/cli/install.ps1 | iex
|
|
69
70
|
```
|
|
70
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
|
+
|
|
71
74
|
If agy is installed but not signed in yet, run it once interactively:
|
|
72
75
|
|
|
73
76
|
```bash
|
|
74
|
-
!agy
|
|
77
|
+
! agy
|
|
75
78
|
```
|
|
76
79
|
|
|
77
80
|
After install, you should see:
|
|
@@ -159,6 +162,7 @@ Examples:
|
|
|
159
162
|
**Notes:**
|
|
160
163
|
|
|
161
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
|
|
162
166
|
- write-capable runs use `--mode accept-edits` on the agy side; review and diagnosis runs stay read-only
|
|
163
167
|
- follow-up rescue requests can continue the latest agy conversation
|
|
164
168
|
|
|
@@ -200,7 +204,7 @@ Afterwards, continue in a terminal with `agy --conversation <id>`, or from Claud
|
|
|
200
204
|
|
|
201
205
|
### `/agy:status`
|
|
202
206
|
|
|
203
|
-
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.
|
|
204
208
|
|
|
205
209
|
Examples:
|
|
206
210
|
|
|
@@ -217,7 +221,7 @@ Use it to:
|
|
|
217
221
|
|
|
218
222
|
### `/agy:result`
|
|
219
223
|
|
|
220
|
-
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>`.
|
|
221
225
|
|
|
222
226
|
Examples:
|
|
223
227
|
|
|
@@ -239,7 +243,11 @@ Examples:
|
|
|
239
243
|
|
|
240
244
|
### `/agy:setup`
|
|
241
245
|
|
|
242
|
-
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.
|
|
243
251
|
|
|
244
252
|
You can also use `/agy:setup` to manage the optional stop-review gate.
|
|
245
253
|
|
|
@@ -332,7 +340,7 @@ Either way, the result comes back with a `conversation_id`, so the thread stays
|
|
|
332
340
|
|
|
333
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.
|
|
334
342
|
|
|
335
|
-
- 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.
|
|
336
344
|
- If agy is missing, the gate skips with a note instead of blocking.
|
|
337
345
|
- Review failures and timeouts block with guidance to run `/agy:review` manually or turn the gate off.
|
|
338
346
|
|
|
@@ -364,15 +372,32 @@ Job control stays thin too: `/agy:status`, `/agy:result`, and `/agy:cancel` read
|
|
|
364
372
|
│ └── transfer.md
|
|
365
373
|
├── hooks/
|
|
366
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
|
|
367
380
|
├── schemas/
|
|
368
381
|
│ └── review-output.schema.json adversarial review output shape
|
|
369
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
|
|
370
392
|
│ ├── agy-setup.mjs /agy:setup readiness report
|
|
393
|
+
│ ├── bump-version.mjs version metadata check and bump
|
|
371
394
|
│ ├── npx-install.mjs npx agy-plugin-cc installer
|
|
395
|
+
│ ├── prepack-check.mjs refuses to npm pack with .bak files present
|
|
372
396
|
│ └── stop-review-gate-hook.mjs stop-review gate (dependency-free node)
|
|
373
397
|
├── skills/
|
|
374
398
|
│ ├── agy-cli-runtime/ CLI call contract
|
|
375
399
|
│ └── agy-result-handling/ output presentation rules
|
|
400
|
+
├── tests/ node --test suite, run with npm test
|
|
376
401
|
├── CHANGELOG.md
|
|
377
402
|
├── LICENSE
|
|
378
403
|
├── README.md
|
|
@@ -385,7 +410,7 @@ Job control stays thin too: `/agy:status`, `/agy:result`, and `/agy:cancel` read
|
|
|
385
410
|
|
|
386
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.
|
|
387
412
|
|
|
388
|
-
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.
|
|
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.
|
|
389
414
|
|
|
390
415
|
### Does the plugin use a separate Antigravity runtime?
|
|
391
416
|
|
|
@@ -393,7 +418,7 @@ No. Every delegation is a call to the same local `agy` binary you would use dire
|
|
|
393
418
|
|
|
394
419
|
### Will it use the same agy settings I already have?
|
|
395
420
|
|
|
396
|
-
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`](#agysetup) 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.
|
|
397
422
|
|
|
398
423
|
### Does it spend my Antigravity quota?
|
|
399
424
|
|
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
|
+
}
|