doc(tool-parity): live test of agy 1.1.9 — real toolset, schedule limits, buy-before-build notes
All checks were successful
release-tools / build-release (push) Successful in 5s

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-05 19:47:58 +02:00
parent 5131e9f823
commit e5b1d73c70

View File

@@ -1,13 +1,28 @@
# Tool parity: Gemini CLI vs Claude Code
# Tool parity: Google's agent vs Claude Code
Goal: let Gemini CLI (the agent driven via `agent-helm`) do everything
Claude Code can, by building the missing capabilities as small CLI tools
in this repo. Gemini calls them through `run_shell_command`, so every
tool follows the house rules: **text in, verifiable artifacts out**,
single static Go binary, self-explanatory output.
Goal: let Google's agent CLI do everything Claude Code can, by building
the missing capabilities as small CLI tools in this repo. The agent
calls them through its shell tool, so every tool follows the house
rules: **text in, verifiable artifacts out**, single static Go binary,
self-explanatory output.
**Which Google agent?** Legacy `gemini-cli` is auth-dead for personal
accounts (verified again 2026-08-05, see infra-Doc
`hosts/brasse-linux01.md`). The real target is **`agy` (Antigravity
CLI)** — live-tested in section 2, and its toolset differs from the
old Gemini CLI docs. Section 1's table is kept for reference since
Gemini CLI still exists in API-key mode.
A design rule that fell out of the live test: **a dedicated binary
beats ad-hoc shell because of approval prefixes.** agy (like Claude
Code) allowlists commands by prefix — `notifyr …` can be approved once
and forever, while every hand-rolled `for i in $(seq …); do curl …`
loop is a unique string that needs fresh human approval. Small stable
CLIs are therefore not just convenience: they are what makes
unattended agent operation possible at all.
Sources: Gemini CLI tools reference (<https://geminicli.com/docs/reference/tools/>),
Claude Code's toolset as of 2026-08.
Claude Code's toolset as of 2026-08, live probing of `agy` 1.1.9.
## 1. Already at parity — nothing to build
@@ -29,19 +44,65 @@ Not worth replicating (harness-internal to Claude Code, no value as a
CLI): `ToolSearch`, `EndConversation`, `ReportFindings`,
`ShareOnboardingGuide`, `DesignSync`, remote cloud execution.
## 2. Gaps → tools to build
## 2. Live test 2026-08-05: `agy` (Antigravity CLI 1.1.9)
Tested interactively in a tmux session (Google AI Pro account, model
Gemini 3.6 Flash). Its 19 built-in tools, self-enumerated:
`ask_permission`, `ask_question`, `define_subagent`, `generate_image`,
`grep_search`, `invoke_subagent`, `list_dir`, `list_permissions`,
`manage_subagents`, `manage_task`, `multi_replace_file_content`,
`read_url_content`, `replace_file_content`, `run_command`, `schedule`,
`search_web`, `send_message`, `view_file`, `write_to_file`.
What this changes vs the old Gemini CLI picture:
- **agy has real subagents** (`invoke_subagent`/`define_subagent`/
`manage_subagents` + `send_message`) and background-task management
(`manage_task`). → `fanout` demoted further; probably never needed.
- **agy has `schedule`** — one-shot timer or cron expression that wakes
the agent with a prompt (same idea as Claude's `ScheduleWakeup`).
Confirmed limits, from its schema: it cannot run commands itself,
it is **in-memory and dies with the session**, and it cannot reach
the phone. → `cronr` (persistent systemd timers) and `notifyr` are
still needed; `schedule` complements them within a session.
- **agy has `generate_image`** — a *reverse* gap: Claude Code has no
native image generation. Nothing to build; just worth knowing.
- No MCP-resource tools, no memory tool and no glob in its toolset
(grep/list_dir cover finding files).
Behavior tests run in a scratch arena:
| Test | Result |
|---|---|
| Enumerate tools | Clean list of 19 (above) |
| Edit a text file | Worked, auto-approved in trusted folder |
| Edit a Jupyter cell, keep `.ipynb` valid | **Passed** — it wrote a `python3 -c` json script rather than text-replacing. Notebook stayed valid. Cost: a per-command approval each time → `nbcell` demoted to nice-to-have (stable prefix + no ad-hoc python). |
| Wait for a file to appear | Worked via a hand-rolled `for … sleep 1` shell loop — a unique command string needing fresh approval. → exactly the `waitfor` case. |
| Asked agy which CLI tools *it* wants for the homelab | Its list: ntfy client, `tea` (Gitea CLI), `skopeo`/`crane` (registry), `ofelia`/cron daemon, `ctop`-style fleet status — near-1:1 with section 4, and it independently made the approval-prefix argument. |
**Buy before build:** agy's suggestions overlap with off-the-shelf
tools. Evaluate first: `tea` (official Gitea CLI — but it does not read
the Pi5's zst action logs and has no `wait-quiet`, which stay
`giteactl`'s reason to exist, possibly as a thin layer *on top of*
`tea`), `skopeo`/`crane` (cover most of `reghelper` — remaining value
is prune *plans* and size summaries), `ctop` (interactive TUI, not
agent-friendly output — `fleet` still wins for agents).
## 3. Gaps → tools to build
Ordered by expected value. Each becomes its own folder + binary +
README, per repo convention.
### 2.1 `cronr` — scheduled/recurring runs *(Claude: `CronCreate`/`CronList`/`CronDelete`, `/loop`)*
### 3.1 `cronr` — scheduled/recurring runs *(Claude: `CronCreate`/`CronList`/`CronDelete`, `/loop`)*
Gemini CLI has no way to schedule work. `cronr` manages **systemd user
timers** so an agent can create recurring or one-shot scheduled jobs
(including "run this prompt every morning" via `gemini -p …`).
agy's built-in `schedule` dies with the session (see section 2).
`cronr` manages **systemd user timers** so an agent can create
recurring or one-shot jobs that survive session exit and reboot
(including "run this prompt every morning" via `agy -p …`).
```
cronr add nightly-ci-check --schedule "*-*-* 07:00" --cmd 'gemini -p "check CI status and notify"'
cronr add nightly-ci-check --schedule "*-*-* 07:00" --cmd 'agy -p "check CI status and notify"'
cronr add once-reboot-check --at "2026-08-06 03:00" --cmd '…' # one-shot
cronr list # name, schedule, next run, last result
cronr logs nightly-ci-check # journalctl for the unit
@@ -51,7 +112,7 @@ cronr rm nightly-ci-check
Output prints the generated unit files so the result is verifiable.
No daemon of its own — systemd does the running.
### 2.2 `waitfor` — block until a condition holds *(Claude: `Monitor`)*
### 3.2 `waitfor` — block until a condition holds *(Claude: `Monitor`)*
Turns "poll every N seconds" into a single blocking tool call, so the
agent doesn't burn turns polling.
@@ -65,7 +126,7 @@ waitfor … --then 'notifyr send --msg "gitea is back up"'
Exit 0 = condition met, exit 3 = timeout; last output is printed either
way. `--then` runs a command on success (composes with `notifyr`).
### 2.3 `notifyr` — push notifications, send **and read** *(Claude: `PushNotification`)*
### 3.3 `notifyr` — push notifications, send **and read** *(Claude: `PushNotification`)*
The ntfy server **already runs on the Pi5** and is the house-wide
notification bus (topics like `Info`, `pi5-server-fel`, `ci-fel` — see
@@ -81,7 +142,7 @@ notifyr read --topic pi5-server-fel --since 2h # poll mode: what has alerted
agent *check what the infra has been complaining about* before/after a
change. Config (`~/.config/notifyr/config.json`): server URL + token.
### 2.4 `pagepub` — publish an HTML/Markdown report to a URL *(Claude: `Artifact`)*
### 3.4 `pagepub` — publish an HTML/Markdown report to a URL *(Claude: `Artifact`)*
Claude Code can publish reports as web pages; Gemini cannot. `pagepub`
rsyncs a file to a static-file host on the Pi5 (nginx container behind
@@ -96,7 +157,7 @@ pagepub list | rm <slug>
Requires the static host to exist first (small infra task; goes in
infra-Doc + NPM proxy host via `npmctl`).
### 2.5 `nbcell` — Jupyter notebook editing *(Claude: `NotebookEdit`)*
### 3.5 `nbcell` — Jupyter notebook editing *(Claude: `NotebookEdit`)*
`.ipynb` is JSON that's miserable to edit via `replace`. `nbcell`
exposes cells as text:
@@ -109,7 +170,7 @@ nbcell add nb.ipynb --at 4 --type code --from-file new.py
nbcell rm nb.ipynb 7
```
### 2.6 `wtreectl` — disposable git worktrees *(Claude: worktree isolation for agents)*
### 3.6 `wtreectl` — disposable git worktrees *(Claude: worktree isolation for agents)*
Claude Code can give each subagent an isolated git worktree. `wtreectl`
does the same for any agent:
@@ -123,7 +184,7 @@ wtreectl clean # removes worktrees with no changes
Lets two agent sessions work in the same repo without trampling each
other.
### 2.7 `fanout` — parallel subagent orchestration *(Claude: `Workflow`, `Agent`)* — roadmap, not agreed
### 3.7 `fanout` — parallel subagent orchestration *(Claude: `Workflow`, `Agent`)* — roadmap, not agreed
Runs N prompts as parallel headless agent processes (`gemini -p` /
`claude -p`) with a concurrency cap, collecting each result as JSON in
@@ -136,7 +197,7 @@ fanout run jobs.json --max 3 --out results/
Heavier than the other tools and overlaps with agent-helm's territory —
park until there's a concrete need.
## 3. Suggested tools from infra history
## 4. Suggested tools from infra history
Grounded in what the agent has already been doing per infra-Doc
(`maintenance-and-gaps.md`, `services/source-control-and-deploy.md`,
@@ -146,7 +207,7 @@ fleet, and they respect the read-only sudo policy
(`ssh/claude-sudo-policy.md`): everything below is read-or-notify;
mutations still go through Björn's supervised tmux flow.
### 3.1 `giteactl` — Gitea repos, Actions runs and CI logs
### 4.1 `giteactl` — Gitea repos, Actions runs and CI logs
The biggest recurring friction. Today: CI status is polled ad hoc, and
logs for private repos are only readable as zst files under
@@ -165,7 +226,7 @@ giteactl release <repo> [<tag>] # rolling-release assets + checksums
builds take the Pi5 down": `giteactl wait-quiet && git push`.
Composes with `waitfor`/`notifyr`.
### 3.2 `fleet` — one-shot health snapshot of the whole homelab
### 4.2 `fleet` — one-shot health snapshot of the whole homelab
Every service doc ends with the same hand-rolled loop over
`ssh pi5-claude sudo claude-docker ps/inspect …`. `fleet` does that
@@ -181,7 +242,7 @@ Read-only by construction (claude-docker wrapper + sudo allowlist), so
it needs no new permissions. Output is a stable text table an agent can
diff between runs.
### 3.3 `envaudit` — compose ↔ `.env` key auditor
### 4.3 `envaudit` — compose ↔ `.env` key auditor
Grounded in a real incident (`${STORAGE1}` undefined in `/srv/.env`
bad mount, rollback) and in maintenance-and-gaps' inline-secret
@@ -197,7 +258,7 @@ envaudit check /srv/dockge-staks --env /srv/.env
Pure text analysis of compose files — safe to run anywhere, catches the
two failure classes that have actually happened.
### 3.4 `reghelper` — docker-registry catalog & hygiene
### 4.4 `reghelper` — docker-registry catalog & hygiene
The LAN registry (`192.168.0.19:5000`) has no UI, no auth and no
cleanup story, and the backup plan explicitly wants it slimmed:
@@ -211,7 +272,7 @@ reghelper prune-plan --keep 2 # prints the delete+GC commands (does NOT run
`prune-plan` deliberately only *prints* the mutation commands for the
supervised tmux flow — same pattern as the sudo policy.
### 3.5 Backup status reader — once Backrest/restic exists
### 4.5 Backup status reader — once Backrest/restic exists
future-plans.md has the whole Backrest+restic design chosen but
unbuilt. When it lands, a `fleet backups` subcommand (last snapshot age
@@ -226,7 +287,7 @@ set of FastAPI tools (file/image sorting, face recognition, ST-card
export…) for the Dify platform. Different runtime (HTTP tools vs CLI
binaries), same philosophy — don't duplicate those here.
## 4. Suggested build order
## 5. Suggested build order
1. `notifyr` — smallest, everything else composes with it, ntfy already runs.
2. `giteactl` — removes the biggest daily friction (CI logs + build serialization).