Skip to content

Commit a485ef1

Browse files
committed
docs(cli): package and document Zoo CLI
1 parent 571b4a2 commit a485ef1

8 files changed

Lines changed: 279 additions & 1 deletion

File tree

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
name: Zoo CLI Release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
dry_run:
7+
description: Build and verify without publishing
8+
type: boolean
9+
default: true
10+
11+
permissions:
12+
contents: read
13+
14+
jobs:
15+
artifact:
16+
strategy:
17+
fail-fast: false
18+
matrix:
19+
include:
20+
- runs-on: macos-latest
21+
platform: darwin-arm64
22+
- runs-on: ubuntu-latest
23+
platform: linux-x64
24+
- runs-on: ubuntu-24.04-arm
25+
platform: linux-arm64
26+
runs-on: ${{ matrix.runs-on }}
27+
steps:
28+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
29+
- uses: ./.github/actions/setup-node-pnpm
30+
- run: pnpm --dir packages/zoo-protocol test
31+
- run: pnpm --dir packages/zoo-host test
32+
- run: pnpm --dir apps/zoo test
33+
- run: pnpm bundle
34+
- run: pnpm --dir packages/zoo-host build
35+
- name: Assemble relocatable artifact
36+
env:
37+
PLATFORM: ${{ matrix.platform }}
38+
run: |
39+
root="zoo-cli-${PLATFORM}"
40+
mkdir -p "$root/bin" "$root/lib/host" "$root/lib/extension"
41+
cp -R apps/zoo/dist/. "$root/lib/"
42+
cp -R packages/zoo-host/dist/. "$root/lib/host/"
43+
cp -R src/dist/. "$root/lib/extension/"
44+
printf '{"type":"commonjs"}\n' > "$root/lib/extension/package.json"
45+
node -e 'const p=require("./apps/zoo/package.json"); console.log(JSON.stringify({name:p.name,version:p.version,private:true,type:"module",dependencies:{ink:p.dependencies.ink,react:p.dependencies.react}},null,2))' > "$root/package.json"
46+
npm install --prefix "$root" --omit=dev --ignore-scripts
47+
printf '%s\n' '#!/usr/bin/env sh' 'base=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)' 'export ZOO_HOST_PATH="$base/lib/host/child.js"' 'export ZOO_EXTENSION_PATH="$base/lib/extension"' 'exec node "$base/lib/index.js" "$@"' > "$root/bin/zoo"
48+
chmod +x "$root/bin/zoo"
49+
"$root/bin/zoo" --help
50+
"$root/bin/zoo" --version
51+
tar -czf "zoo-cli-${PLATFORM}.tar.gz" "$root"
52+
shasum -a 256 "zoo-cli-${PLATFORM}.tar.gz" > "zoo-cli-${PLATFORM}.tar.gz.sha256"
53+
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
54+
with:
55+
name: zoo-cli-${{ matrix.platform }}
56+
path: zoo-cli-${{ matrix.platform }}.tar.gz*
57+
58+
release:
59+
if: ${{ !inputs.dry_run }}
60+
needs: artifact
61+
runs-on: ubuntu-latest
62+
permissions:
63+
contents: write
64+
steps:
65+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
66+
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
67+
with:
68+
pattern: zoo-cli-*
69+
merge-multiple: true
70+
- env:
71+
GH_TOKEN: ${{ github.token }}
72+
run: gh release create "zoo-cli-v$(node -p 'require("./apps/zoo/package.json").version')" zoo-cli-*.tar.gz* --generate-notes

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
> Your AI-Powered Dev Team, Right in Your Editor
1616
17+
Zoo Code is also available in the terminal through the new [`zoo` CLI](docs/zoo-cli.md), with an interactive UI and deterministic text, JSON, and NDJSON automation.
18+
1719
## We are Zoo Code
1820

1921
> Zoo Code continues development of this project after the Roo team wound down
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import fs from "node:fs"
2+
import { execFileSync } from "node:child_process"
3+
import path from "node:path"
4+
import { fileURLToPath } from "node:url"
5+
6+
import { describe, expect, it } from "vitest"
7+
8+
const packageRoot = path.resolve(fileURLToPath(new URL("../../", import.meta.url)))
9+
const repositoryRoot = path.resolve(packageRoot, "../..")
10+
11+
describe("CLI documentation", () => {
12+
it("keeps documented commands discoverable in live help", () => {
13+
const help = execFileSync(process.execPath, [path.join(packageRoot, "dist/index.js"), "--help"], {
14+
encoding: "utf8",
15+
})
16+
const docs = fs.readFileSync(path.join(repositoryRoot, "docs/zoo-cli.md"), "utf8")
17+
18+
for (const command of ["run", "resume", "sessions"]) {
19+
expect(help).toContain(command)
20+
expect(docs).toContain(`zoo ${command}`)
21+
}
22+
expect(docs).toContain("stream-json")
23+
expect(docs).toContain("--approval safe")
24+
})
25+
})

docs/zoo-cli-release-checklist.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Zoo CLI Release Review
2+
3+
This checklist records the release gates for the initial `zoo` CLI. It is evidence for maintainers, not a claim that unsupported capabilities exist.
4+
5+
## Security
6+
7+
- [x] Dedicated IPC carries protocol data; child stdout cannot contaminate machine output.
8+
- [x] Safe automation returns `needs_input`; auto approval preserves explicit denials and hard boundaries.
9+
- [x] Run overrides remain in memory and propagate through delegation without profile/settings mutation.
10+
- [x] macOS Keychain and Linux Secret Service adapters avoid plaintext files and secret argv values.
11+
- [x] Stateful bounded redaction covers events, command output, diagnostics, errors, and folded headers.
12+
- [x] Workspace/session identity is canonicalized and pinned per host.
13+
- [x] Startup, heartbeat, commands, timeout, cancellation, flush, shutdown, and kill phases are bounded.
14+
15+
## Privacy And Telemetry
16+
17+
- [x] The production extension retains its canonical telemetry preference and flush behavior.
18+
- [x] Public events exclude prompts/tool payloads beyond redacted terminal-visible activity.
19+
- [x] Debug diagnostics are opt-in, bounded, redacted, and sent only to stderr.
20+
- [x] Machine stdout contracts contain no hidden analytics or log records.
21+
- [ ] Dedicated `client=cli` telemetry tagging is required before enabling CLI-specific product analytics. Until then, no CLI-only prompt, tool, or command telemetry is introduced.
22+
23+
## Artifacts
24+
25+
- [x] Matrix is limited to macOS ARM64 and Linux x64/ARM64.
26+
- [x] Artifacts lock client, host, protocol, extension bundle, and Node 22 runtime expectations.
27+
- [x] Each artifact runs live `--help` and `--version` smoke checks.
28+
- [x] SHA-256 checksum accompanies every tarball.
29+
- [x] Unit, host, packaged-process, type, and lint gates run before assembly.
30+
- [ ] Signing and npm publication credentials remain maintainer-controlled release steps.
31+
32+
## Rollback And Support
33+
34+
Artifacts and tags are immutable release units. Rollback selects an earlier artifact; it never deletes or migrates `~/.zoo`, VS Code, or inherited `roo` data. Support requests should include `zoo --version`, platform, exit code, and redacted `--debug` stderr. Do not request prompt contents, API keys, vault exports, or unredacted event streams.

docs/zoo-cli.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# Zoo Code CLI
2+
3+
The `zoo` executable runs the production Zoo Code extension in a private supervised host. It does not contain a second agent loop. Existing `.roo`, `.roomodes`, `.rooignore`, `AGENTS.md`, rules, skills, custom tools, and MCP configuration keep their extension semantics.
4+
5+
## Installation
6+
7+
Zoo CLI requires Node.js 22.23.1. Supported release artifacts are macOS ARM64, Linux x64, and Linux ARM64.
8+
9+
```sh
10+
npm install --global @zoo-code/cli
11+
zoo --version
12+
```
13+
14+
Platform tarballs contain `bin/zoo`; add that directory to `PATH`. Windows, macOS x64, Linux musl, and older CPU baseline packages are not currently supported.
15+
16+
## Quick Start
17+
18+
Start the interactive terminal UI in the current workspace:
19+
20+
```sh
21+
zoo
22+
zoo "explain this repository"
23+
```
24+
25+
Run explicit automation:
26+
27+
```sh
28+
zoo run "run the focused tests" --approval safe
29+
zoo run "summarize the project" --format json
30+
zoo run "fix the reported bug" --format stream-json > events.ndjson
31+
printf '%s\n' "review this workspace" | zoo run --format text
32+
```
33+
34+
Resume and inspect workspace-scoped history:
35+
36+
```sh
37+
zoo sessions list
38+
zoo sessions list --format json -C ./project
39+
zoo resume
40+
zoo resume 019abc --format json
41+
```
42+
43+
Run `zoo --help`, `zoo run --help`, or `zoo resume --help` for live option reference. A positional prompt and piped prompt cannot be combined. Root `zoo` always requires TTY stdin and stdout; redirection never changes approval policy.
44+
45+
## Selection And Credentials
46+
47+
Run selections are invocation-local:
48+
49+
```sh
50+
zoo run "investigate" --provider anthropic --model claude-sonnet-4-20250514 --mode debug
51+
zoo run "review" --profile work --reasoning-effort high
52+
```
53+
54+
`--provider` conflicts with `--profile`. Explicit invalid providers, profiles, models, modes, sessions, workspaces, and durations fail instead of falling back.
55+
56+
Automation can read provider credentials from the provider's documented environment variable. Persisted credentials are accessed only through the operating-system vault adapter: macOS Keychain or Linux Secret Service. Secrets are never written to shim JSON, accepted as command-line flags, or included in events and diagnostics. Unsupported OAuth flows must be completed through a supported environment or vault setup.
57+
58+
Precedence is invocation override, invocation environment credential, selected vault profile, canonical project configuration, CLI state, then product default. CLI state is under `~/.zoo`; VS Code and inherited Roo CLI storage are not imported implicitly.
59+
60+
## Approvals And Threat Model
61+
62+
| Mode | Use | Unresolved `ask` |
63+
| ------------- | ---------------------------- | -------------------------------------- |
64+
| `interactive` | TTY UI | Prompt the user |
65+
| `safe` | Default automation | Return resumable `needs_input`, exit 3 |
66+
| `auto` | Explicit unattended autonomy | Approve eligible asks only |
67+
68+
`auto` is powerful and should only run in a workspace and account you trust. It never overrides explicit command denials, protected-file policy, outside-workspace restrictions, organization policy, destructive-command boundaries, mode restrictions, or MCP restrictions. Follow-up questions are not answered with invented text.
69+
70+
Tool arguments, terminal output, MCP payloads, errors, debug diagnostics, and final content pass through bounded redaction before rendering. Project files cannot expand access beyond canonical trust boundaries.
71+
72+
## Output Contracts
73+
74+
### Text
75+
76+
`--format text` is append-only and suitable for logs. It shows initialization, assistant/reasoning activity, tools, approvals, terminal and MCP activity, delegation, warnings, and the final result. `--quiet` emits only the final content or failure.
77+
78+
### Final JSON
79+
80+
`--format json` writes exactly one compact `zoo-run-result` object to stdout. Diagnostics go to stderr. Important fields are `schemaVersion`, `success`, `outcome`, root/current task IDs, workspace, resumability, content or stable error, usage/cost, elapsed time, and changed files.
81+
82+
### Streaming JSON
83+
84+
`--format stream-json` writes newline-delimited `zoo-stream` v1 records. The first record is `system.init`; each record has a monotonic `seq`, timestamp, and host identity. Deltas reconstruct ordered output. Exactly one authoritative-root `task.result` is terminal. stdout contains no ANSI or human diagnostics. `--quiet` is intentionally incompatible.
85+
86+
Breaking machine-schema changes increment the major schema version. Additive optional fields retain it. Unknown visible activity is represented generically rather than silently discarded.
87+
88+
## Outcomes And Exit Codes
89+
90+
| Outcome | Exit |
91+
| ---------------------------------- | ---: |
92+
| Completed | 0 |
93+
| Usage or configuration | 2 |
94+
| Needs input | 3 |
95+
| Explicit cancellation | 4 |
96+
| Provider failure | 10 |
97+
| Runtime, host, or protocol failure | 70 |
98+
| Timeout | 124 |
99+
| SIGINT | 130 |
100+
| SIGTERM | 143 |
101+
102+
Stable errors include invalid selection/workspace/session, missing credentials, permission denial, provider failure, host startup/crash, incompatible protocol, sequence gap, cancellation failure, cleanup timeout, task timeout, and closed output.
103+
104+
## Sessions, Signals, And Ephemeral Runs
105+
106+
Sessions are scoped to the canonical real path of `-C/--cwd`. `zoo resume` selects the latest root for that workspace; an ID must belong to the same workspace. Delegated histories retain root/current identity.
107+
108+
The first Ctrl+C requests canonical cancellation and waits for interrupted history to settle. A second Ctrl+C escalates cleanup. SIGTERM follows bounded graceful cancellation. `--timeout 10m` is a parent-owned whole-invocation deadline covering startup, history, acceptance, execution, cancellation, flush, and shutdown. Broken stdout triggers cancellation without a stack trace.
109+
110+
`--ephemeral` creates isolated temporary storage and removes it after success, error, signal, or timeout. Its session cannot be resumed after exit. It does not weaken project rules or approvals.
111+
112+
## Supported And Unsupported Capabilities
113+
114+
The CLI preserves canonical modes, rules, `.rooignore`, instructions, tools, MCP startup, histories, delegation, cancellation, terminal execution, and accepted root completion. Editor tabs, selections, decorations, diff UI, terminal panels, browser automation, and checkpoints are unavailable. The CLI does not expose config/profile mutation, auth management, MCP management, session mutation/import/export, cloud/daemon/remote control, worktrees, schedules, or a public long-lived stdin protocol.
115+
116+
`modes list` and `models list` are also withheld in this release: the current canonical queries activate mutable extension services, so they do not yet meet the side-effect-free metadata requirement.
117+
118+
## Coexistence With `roo`
119+
120+
The inherited `roo` executable remains intact during migration. `zoo` uses `~/.zoo`; it does not read or mutate inherited CLI state. Project `.roo*` files remain canonical and are shared by design. No history or plaintext-secret migration occurs automatically.
121+
122+
## Troubleshooting
123+
124+
- Run `zoo --version` to report the client/build contract.
125+
- Use `--debug` for bounded redacted host diagnostics on stderr.
126+
- Verify the effective `-C` workspace when a session is not found.
127+
- A `needs_input` result is expected under safe approval; resume interactively to answer it.
128+
- A host/protocol failure exits 70 and never contaminates JSON stdout.
129+
- Timeout or signal cleanup is bounded; no host, shell, MCP, index, terminal, or watcher should survive.
130+
- If vault access fails, verify Keychain or Secret Service availability, or use an invocation environment credential.

packages/zoo-host/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"lint": "eslint src --ext=ts --max-warnings=0",
1010
"check-types": "tsc --noEmit",
1111
"test": "vitest run",
12-
"build": "tsc",
12+
"build": "tsup",
1313
"clean": "rimraf dist .turbo"
1414
},
1515
"dependencies": {
@@ -21,6 +21,7 @@
2121
"@roo-code/config-eslint": "workspace:^",
2222
"@roo-code/config-typescript": "workspace:^",
2323
"@types/node": "22.20.1",
24+
"tsup": "8.5.1",
2425
"vitest": "4.1.9"
2526
}
2627
}

packages/zoo-host/tsup.config.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { defineConfig } from "tsup"
2+
3+
export default defineConfig({
4+
entry: ["src/child.ts", "src/index.ts"],
5+
format: ["esm"],
6+
clean: true,
7+
sourcemap: true,
8+
target: "node22",
9+
platform: "node",
10+
noExternal: ["@roo-code/types", "@roo-code/vscode-shim", "@roo-code/zoo-protocol"],
11+
})

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)