pitroom 0.23.0 → 0.24.1

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/docs/backends.md CHANGED
@@ -27,7 +27,7 @@ interface Backend {
27
27
  id: string; // "opencode", used in targets: "opencode:provider/model"
28
28
  name: string;
29
29
  capabilities: {
30
- readOnly: 'permission-rules' | 'os-sandbox' | 'tool-allowlist' | 'approval-mode';
30
+ readOnly: 'permission-rules' | 'os-sandbox' | 'tool-allowlist' | 'approval-mode' | 'snapshot';
31
31
  resume: 'by-id' | 'none';
32
32
  reportsCost: boolean;
33
33
  attachFiles: boolean;
@@ -61,6 +61,7 @@ CLI's **own** mechanism, and never with flags that switch safety off
61
61
  | **Gemini CLI** (`--prompt … --output-format stream-json`) | `--approval-mode plan` (the policy engine allows read tools only) | `auto_edit` + Pitroom's `--policy` rules (shell minus history-changing git) | by session id (`--resume <id>`) → `resume: 'by-id'` | tokens only |
62
62
  | **Qwen Code** (`<prompt> --safe-mode --output-format stream-json`) | `--approval-mode plan` (edits, shell and reads outside the working directory declined) | `--approval-mode default` + `--allowed-tools Edit(./**) write_file(./**) notebook_edit(./**) run_shell_command`, shell minus history-changing git via `--exclude-tools` | `--resume <id>` | tokens only |
63
63
  | **Kilo Code** (`kilo run --format json`) | OpenCode's read profile through `KILO_CONFIG_CONTENT` | OpenCode's write profile | `--session <id>` | reported |
64
+ | **Antigravity CLI** (`agy -p … --output-format stream-json --sandbox`) | a read-only **snapshot of its own** (agy has no read-only mode for `-p`), plus `--mode plan` and Pitroom's deny rules | `--mode accept-edits`, shell in agy's terminal sandbox, the same deny rules | `--conversation <id>` | tokens only |
64
65
 
65
66
  Whatever the CLI, the core still applies the git guard, closes stdin (Codex and
66
67
  Gemini otherwise read piped stdin into the prompt), sets `PWD` to the worker's
@@ -169,6 +170,32 @@ How a worker is started, and why:
169
170
  - **`--log-level ERROR`** in capitals: Kilo rejects `error` and prints its help instead of running.
170
171
  - Never `--auto` ("auto-approve permissions that are not explicitly denied"), never `--attach`.
171
172
 
173
+ ## Antigravity CLI notes
174
+
175
+ Status: **beta**. Checked against **agy 1.3.5** on macOS (October 2026): a read run, a follow-up, an isolated edit, shell commands in the sandbox, the deny rules, an unknown model, a used-up quota and a missing sign-in were run against the real CLI, and `test/fixtures/antigravity` holds their recorded output (home paths replaced). Not yet checked on Linux (the sign-in file link) or Windows (`doctor` says to use `-i` there).
176
+
177
+ Why it is here: Google refuses a Google account sign-in to Gemini CLI (`IneligibleTierError`, "migrate to the Antigravity suite"), so for someone without an AI Studio key, `agy` is the way to use their Google account's models (Gemini, and the Claude and GPT-OSS models Antigravity offers).
178
+
179
+ How a worker is started, and why:
180
+
181
+ - **`-p <prompt> --output-format stream-json`**: one prompt, a JSON event stream, exit. `-p` takes the prompt as its value; a prompt that starts with `-` gets a leading space.
182
+ - **Read runs read a snapshot of their own** (`readOnly: 'snapshot'`). agy has no read-only mode for `-p` runs (issue 45 of google-antigravity/antigravity-cli), and `--mode plan` is reported to still write in them (issue 1181: the file is created in the same turn). So the core never lets Antigravity read the project: right before its attempt it makes the run a snapshot of its own (`snapshots/<tree>-<run id>`, made like the shared clean snapshot: from git objects, uncommitted and untracked files included, secret-looking and ignored files left out, files read-only), whatever `readIn` says. Not the shared one: directories there stay writable, so a new or replaced file would be seen by every later read run on the same state. Its follow-ups read the same snapshot. After the attempt, whatever git sees newly changed there is reported as `READ-ONLY VIOLATION` (the stream need not mention it; a follow-up is not blamed for its parent's writes), and the answer is not cached. A worker after it in the chain reads where it was meant to (the project or the shared snapshot), never Antigravity's, and a second Antigravity target gets a fresh snapshot if the first wrote in its own. As a fallback it costs nothing until it runs. These snapshots are full checkouts: pruning keeps the newest few of them apart from the shared ones, so a burst of Antigravity runs does not push those out, and a follow-up whose snapshot was pruned gets it made again from the same tree. If the snapshot cannot be made, Antigravity is skipped like a failed attempt (the next worker runs, or the run fails: as the worker before it failed, a rate limit staying one, or, when no worker ran at all, without counting against any worker's record); it never falls back to the project. `--in-place`, reviews (whose package lives in the git folder) and folders outside git read the directory itself, with a warning. `pitroom doctor --probe` runs it in an empty temporary folder. `--mode plan` is passed anyway; write and isolate use `--mode accept-edits`. Never `--dangerously-skip-permissions` (the contract tests reject it).
183
+ - **`--sandbox`** in every mode: shell commands run in agy's terminal sandbox (the workspace writable, its `.git` read-only, `.env` and `~/.ssh` unreadable, no network). The sandbox and the permission rules are for macOS and Linux; on Windows agy still uses its previous permission system, and `doctor` says to use `-i` there.
184
+ - **A home of its own per workspace.** agy reads its settings only from `~/.gemini/antigravity-cli/settings.json` (and its permissions from `~/.gemini/config`); `ANTIGRAVITY_APP_DATA_DIR` and every other variable tried leave its data folder where it is (its log says `CLI app data directory: …`). So a worker runs with `HOME` set to `<pitroom home>/antigravity-home/<id>`, one per workspace (the run's snapshot, its isolated copy, or the project for `-w`), where Pitroom writes `settings.json` (atomically: a parallel worker never reads half a file). The sign-in is linked in: on macOS agy finds it in the login keychain through `$HOME/Library/Keychains`, which is linked to yours; elsewhere it is the file `antigravity-oauth-token`, linked from `~/.gemini/antigravity-cli` (and a dangling link is repaired). Conversations stay in that home, so a follow-up, which runs in the same workspace, finds its own; homes not used for two weeks are removed. The worker's commands see that home too: your dotfiles are out of their reach, your own agy settings, plugins and MCP servers do not apply, and agy picks its default model unless `-W antigravity:<model>` names one. The settings: `enableTerminalSandbox`, `toolPermission: proceed-in-sandbox` and the grant `command(*)` (a headless run cannot answer a prompt: an ungranted command is refused, and the turn may end without an answer), `artifactReviewPolicy: always-proceed`, `allowNonWorkspaceAccess: false`, `write_file` for its own `brain` folder (plan mode writes its plan there, outside the workspace), `read_file` for the folders of your PATH under your home (the sandbox sees only the system and what is granted: without it an nvm `node` is `Operation not permitted`; a `bin` in a versioned install brings the install along, other folders only themselves, as `~/.cargo` holds credentials), and deny rules (deny beats allow): the workspace's secret-looking files and the secrets in your home (`.ssh`, `.aws`, `.gnupg`, `.netrc`, `.config/gh`, …) by absolute path, the shared forbidden commands (`src/backends/deny.ts`), `mcp(*)`, `read_url(*)` and `execute_url(*)`.
185
+ - **What the task names outside the workspace** is granted read-only: the folders and files it gives by absolute path (or `~/…`) that exist, with every secret-looking file in them denied (looked for at any depth and in every folder; a folder of more than 20,000 entries, with a link leading out of it or nowhere, or with a folder that cannot be read, is not granted, as its secrets could not all be found; a named path counts where its links lead); without a grant a headless agy refuses the read and ends the turn without an answer. Never the root or a top-level folder (`/tmp`), your home itself or a folder above it, a folder holding the workspace or the project (it would open the project around a snapshot, secrets included), a dot-folder in your home (`~/.config`, `~/.npmrc`) or `~/Library` (apps keep session tokens there), Pitroom's own state or a secret-looking file; and nothing on Windows, where agy compares paths without their drive letter, so a grant would open the same path on every drive. In a snapshot or an isolated copy, the project's own paths in the task are rewritten to the copy's (compared with links resolved), so the worker works on its copy rather than being refused at the project, which it may not read or write.
186
+ - **How agy matches the rules**, as seen in 1.3.5 (its docs say only "prefix, word by word"): `read_file` takes an absolute path or one relative to the workspace, as a folder prefix, and no pattern; a `regex:` target is not read, and a relative path breaks the sandbox (`sandbox configuration error: … non-absolute file path`) without denying anything, so secret files are listed one by one. A `command` rule is compared word by word with the start of each command in the line, after agy has split it at `;`, `&&`, `||` and `|` and taken off wrappers such as `env -i` and `timeout 60` (but not `xargs`, and not what find runs with `-exec`). A `regex:` command rule is split at spaces too, each word a regex for one word (`.*` is exactly one word), and backslashes are dropped. A line with `{}` in it is not split: a rule is matched against the whole line, start to end. The rules are built for that: each forbidden command as it is and by path, git behind its global options, a recursive `rm` wherever its option stands, `sh -c` and `eval`, find with `-exec`, `-ok` or `-delete` refused outright (a rule for what it runs only holds when it spans the whole line), xargs running a forbidden command, and, for lines with `{}`, `git <subcommand>`, `rm -r`, xargs and find anywhere in them. `test/backends.test.mjs` checks them with that matching; they remain pattern rules, **a layer on top, not a guarantee**: other spellings of a command can get past them. The git guard is what holds for git whatever the spelling (`.git` is read-only in the sandbox too), and a read run's snapshot or an isolated copy is what holds for your files: use `-i` for changes rather than `-w`, which lets a worker write (and delete) in the project itself. One settings file serves every mode, since a follow-up may run in another: so **`--web` does not apply** to Antigravity workers.
187
+ - **`--print-timeout 24h`**: agy's docs give its own limit as 5 minutes (1.3.5's help says none), and a run that hits it ends with partial output; Pitroom's timeout governs instead.
188
+ - **Model**: `-W antigravity:<model>` passes `--model` (`agy models` lists yours), and `#level` passes `--effort`. Without one, agy picks. Its quota is per Google account and can run out for hours: give it a fallback.
189
+ - **`--continue`**: `--conversation <id>` with the id of the stream's `init` event, never `-c` (the latest conversation, which is unsafe beside parallel workers).
190
+
191
+ The stream, as agy 1.3.5 writes it: every line is `{"event": "init" | "step_update" | "result", …}` with the payload under the event's name. `init.model` names the model when `--model` was given. `step_update.step_type` is `user_input`, `agent_response` (text in `text_delta` chunks), `tool` (`tool_name`, and `tool_info` with the tool's name, parameters and `output`, or `error: {type, message}`), `checkpoint` or `error_message`; `state` is `ACTIVE`, `DONE`, or `ERROR` for a failed or refused call. The answer is `result.response`; tokens are `result.usage` (`input_tokens` with `cache_read_tokens` inside it, `output_tokens`, `thinking_tokens`, `total_tokens`), or the sum of the finished steps' own usage when a run is cut short; `result.denied_actions` lists calls refused for want of a grant. Tools are agy's own names: `run_command` (`CommandLine`) is the shell, `view_file` (`AbsolutePath`) reads, `write_to_file`, `replace_file_content` and `multi_replace_file_content` (`TargetFile`) are edits. Every field is read tolerantly.
192
+
193
+ Failures, as recorded. A command refused for want of a grant ends the turn with **status `SUCCESS`, no answer and exit 0**, and a stderr line (`jetski: no output produced — a tool required the "command" permission …`); a command refused by a deny rule usually lets the agent answer that it was refused. So an Antigravity run without an answer is always a failure, never an empty answer. A used-up quota exits 3 with `result.status: ERROR` (`Individual quota reached … Resets in 2h23m`) and an `AGY_ERROR: {"short_error": "RESOURCE_EXHAUSTED (code 429): …"}` line on stderr: `rate-limited`, so the chain moves on; it can hit one model (Claude Opus) while another (Gemini Flash) still answers. An unknown model exits 1 (`is not recognized as a known model`): `model-unavailable`. A prompt the model's safety filter blocks comes back as an ordinary answer with status `SUCCESS` (`This request was blocked by Gemini's filters…`): it is reported as a failure, not as the answer. No sign-in waits 60 seconds for a browser sign-in, then exits 1 (`authentication failed or timed out`): `auth`.
194
+
195
+ Known agy problems Pitroom cannot work around: a follow-up whose conversation id agy does not know starts a new conversation (stderr: `warning: conversation "…" not found`), which the run shows as a warning, as the earlier turns are then missing; a turn can end while a `run_command` it started is still running, which is then killed (issue 1044); a subagent's output may not reach the stream (issue 1077). A model can also refuse a task by itself (reading `.env`, or a `git commit` the worker contract forbids) before agy's rules come into it.
196
+
197
+ What is still open: Linux (the token file link, the secret-service keyring) and Windows. A stronger guard would be a `PreToolUse` hook that denies (agy honours a hook's `deny` in `-p` runs, issue 1053, though not its `allow`); the worker home would be the place for its `hooks.json`.
198
+
172
199
  ## Hermes Agent: not supported yet
173
200
 
174
201
  [Hermes Agent](https://pypi.org/project/hermes-agent/) (Nous Research; looked at 0.19.0 from PyPI) was considered and left out, because it lacks the controls every other worker gets from its own CLI:
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pitroom",
3
- "version": "0.23.0",
3
+ "version": "0.24.1",
4
4
  "description": "A free pit crew for your expensive coding agent: hand reading, fixes, tests and reviews to cheap worker agents and get back the answer, the exact diff and a receipt. Fourteen skills adapt a full development workflow. Needs the pitroom command (npm i -g pitroom).",
5
5
  "contextFileName": "gemini-context.md"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pitroom",
3
- "version": "0.23.0",
3
+ "version": "0.24.1",
4
4
  "mcpName": "io.github.ATASTECH/pitroom",
5
5
  "description": "A free pit crew for your expensive coding agent: the superpowers development workflow, run by cheap workers. Delegate research, fixes, tests, reviews and whole plans from Claude Code, Codex or any agent to OpenCode, Codex, Claude Code or Gemini CLI workers, with receipts that prove the savings.",
6
6
  "type": "module",
@@ -34,6 +34,9 @@ commandPrefix = [
34
34
  "git add",
35
35
  "rm -rf",
36
36
  "rm -fr",
37
+ "rm -r",
38
+ "rm -R",
39
+ "rm --recursive",
37
40
  "sudo",
38
41
  "chown",
39
42
  "kill",
@@ -48,6 +51,7 @@ commandPrefix = [
48
51
  "gemini",
49
52
  "qwen",
50
53
  "kilo",
51
- "kilocode"
54
+ "kilocode",
55
+ "agy"
52
56
  ]
53
57
  deny_message = "Pitroom: history-changing git, destructive commands and other agents are not for workers."