-
Notifications
You must be signed in to change notification settings - Fork 233
docs(cli): package and document Zoo CLI #1162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: fm/zoo-cli-interactive
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| name: Zoo CLI Release | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| dry_run: | ||
| description: Build and verify without publishing | ||
| type: boolean | ||
| default: true | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| artifact: | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| include: | ||
| - runs-on: macos-latest | ||
| platform: darwin-arm64 | ||
| - runs-on: ubuntu-latest | ||
| platform: linux-x64 | ||
| - runs-on: ubuntu-24.04-arm | ||
| platform: linux-arm64 | ||
| runs-on: ${{ matrix.runs-on }} | ||
| steps: | ||
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | ||
| - uses: ./.github/actions/setup-node-pnpm | ||
| - run: pnpm --dir packages/zoo-protocol test | ||
| - run: pnpm --dir packages/zoo-host test | ||
| - run: pnpm --dir apps/zoo test | ||
| - run: pnpm bundle | ||
| - run: pnpm --dir packages/zoo-host build | ||
| - name: Assemble relocatable artifact | ||
| env: | ||
| PLATFORM: ${{ matrix.platform }} | ||
| run: | | ||
| root="zoo-cli-${PLATFORM}" | ||
| mkdir -p "$root/bin" "$root/lib/host" "$root/lib/extension" | ||
| cp -R apps/zoo/dist/. "$root/lib/" | ||
| cp -R packages/zoo-host/dist/. "$root/lib/host/" | ||
| cp -R src/dist/. "$root/lib/extension/" | ||
| printf '{"type":"commonjs"}\n' > "$root/lib/extension/package.json" | ||
| 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" | ||
| npm install --prefix "$root" --omit=dev --ignore-scripts | ||
| 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" | ||
| chmod +x "$root/bin/zoo" | ||
| "$root/bin/zoo" --help | ||
| "$root/bin/zoo" --version | ||
| tar -czf "zoo-cli-${PLATFORM}.tar.gz" "$root" | ||
| shasum -a 256 "zoo-cli-${PLATFORM}.tar.gz" > "zoo-cli-${PLATFORM}.tar.gz.sha256" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The existing |
||
| - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | ||
| with: | ||
| name: zoo-cli-${{ matrix.platform }} | ||
| path: zoo-cli-${{ matrix.platform }}.tar.gz* | ||
|
|
||
| release: | ||
| if: ${{ !inputs.dry_run }} | ||
| needs: artifact | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: write | ||
| steps: | ||
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | ||
| - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 | ||
| with: | ||
| pattern: zoo-cli-* | ||
| merge-multiple: true | ||
| - env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| run: gh release create "zoo-cli-v$(node -p 'require("./apps/zoo/package.json").version')" zoo-cli-*.tar.gz* --generate-notes | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import fs from "node:fs" | ||
| import { execFileSync } from "node:child_process" | ||
| import path from "node:path" | ||
| import { fileURLToPath } from "node:url" | ||
|
|
||
| import { describe, expect, it } from "vitest" | ||
|
|
||
| const packageRoot = path.resolve(fileURLToPath(new URL("../../", import.meta.url))) | ||
| const repositoryRoot = path.resolve(packageRoot, "../..") | ||
|
|
||
| describe("CLI documentation", () => { | ||
| it("keeps documented commands discoverable in live help", () => { | ||
| const help = execFileSync(process.execPath, [path.join(packageRoot, "dist/index.js"), "--help"], { | ||
| encoding: "utf8", | ||
| }) | ||
| const docs = fs.readFileSync(path.join(repositoryRoot, "docs/zoo-cli.md"), "utf8") | ||
|
|
||
| for (const command of ["run", "resume", "sessions"]) { | ||
| expect(help).toContain(command) | ||
| expect(docs).toContain(`zoo ${command}`) | ||
| } | ||
| expect(docs).toContain("stream-json") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| expect(docs).toContain("--approval safe") | ||
| }) | ||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| # Zoo CLI Release Review | ||
|
|
||
| This checklist records the release gates for the initial `zoo` CLI. It is evidence for maintainers, not a claim that unsupported capabilities exist. | ||
|
|
||
| ## Security | ||
|
|
||
| - [x] Dedicated IPC carries protocol data; child stdout cannot contaminate machine output. | ||
| - [x] Safe automation returns `needs_input`; auto approval preserves explicit denials and hard boundaries. | ||
| - [x] Run overrides remain in memory and propagate through delegation without profile/settings mutation. | ||
| - [x] macOS Keychain and Linux Secret Service adapters avoid plaintext files and secret argv values. | ||
| - [x] Stateful bounded redaction covers events, command output, diagnostics, errors, and folded headers. | ||
| - [x] Workspace/session identity is canonicalized and pinned per host. | ||
| - [x] Startup, heartbeat, commands, timeout, cancellation, flush, shutdown, and kill phases are bounded. | ||
|
|
||
| ## Privacy And Telemetry | ||
|
|
||
| - [x] The production extension retains its canonical telemetry preference and flush behavior. | ||
| - [x] Public events exclude prompts/tool payloads beyond redacted terminal-visible activity. | ||
| - [x] Debug diagnostics are opt-in, bounded, redacted, and sent only to stderr. | ||
| - [x] Machine stdout contracts contain no hidden analytics or log records. | ||
| - [ ] 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. | ||
|
|
||
| ## Artifacts | ||
|
|
||
| - [x] Matrix is limited to macOS ARM64 and Linux x64/ARM64. | ||
| - [x] Artifacts lock client, host, protocol, extension bundle, and Node 22 runtime expectations. | ||
| - [x] Each artifact runs live `--help` and `--version` smoke checks. | ||
| - [x] SHA-256 checksum accompanies every tarball. | ||
| - [x] Unit, host, packaged-process, type, and lint gates run before assembly. | ||
| - [ ] Signing and npm publication credentials remain maintainer-controlled release steps. | ||
|
|
||
| ## Rollback And Support | ||
|
|
||
| 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. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| # Zoo Code CLI | ||
|
|
||
| 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. | ||
|
|
||
| ## Installation | ||
|
|
||
| Zoo CLI requires Node.js 22.23.1. Supported release artifacts are macOS ARM64, Linux x64, and Linux ARM64. | ||
|
|
||
| ```sh | ||
| npm install --global @zoo-code/cli | ||
| zoo --version | ||
| ``` | ||
|
|
||
| Platform tarballs contain `bin/zoo`; add that directory to `PATH`. Windows, macOS x64, Linux musl, and older CPU baseline packages are not currently supported. | ||
|
|
||
| ## Quick Start | ||
|
|
||
| Start the interactive terminal UI in the current workspace: | ||
|
|
||
| ```sh | ||
| zoo | ||
| zoo "explain this repository" | ||
| ``` | ||
|
|
||
| Run explicit automation: | ||
|
|
||
| ```sh | ||
| zoo run "run the focused tests" --approval safe | ||
| zoo run "summarize the project" --format json | ||
| zoo run "fix the reported bug" --format stream-json > events.ndjson | ||
| printf '%s\n' "review this workspace" | zoo run --format text | ||
| ``` | ||
|
|
||
| Resume and inspect workspace-scoped history: | ||
|
|
||
| ```sh | ||
| zoo sessions list | ||
| zoo sessions list --format json -C ./project | ||
| zoo resume | ||
| zoo resume 019abc --format json | ||
| ``` | ||
|
|
||
| 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. | ||
|
|
||
| ## Selection And Credentials | ||
|
|
||
| Run selections are invocation-local: | ||
|
|
||
| ```sh | ||
| zoo run "investigate" --provider anthropic --model claude-sonnet-4-20250514 --mode debug | ||
| zoo run "review" --profile work --reasoning-effort high | ||
| ``` | ||
|
|
||
| `--provider` conflicts with `--profile`. Explicit invalid providers, profiles, models, modes, sessions, workspaces, and durations fail instead of falling back. | ||
|
|
||
| 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. | ||
|
|
||
| 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. | ||
|
|
||
| ## Approvals And Threat Model | ||
|
|
||
| | Mode | Use | Unresolved `ask` | | ||
| | ------------- | ---------------------------- | -------------------------------------- | | ||
| | `interactive` | TTY UI | Prompt the user | | ||
| | `safe` | Default automation | Return resumable `needs_input`, exit 3 | | ||
| | `auto` | Explicit unattended autonomy | Approve eligible asks only | | ||
|
|
||
| `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. | ||
|
|
||
| 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. | ||
|
|
||
| ## Output Contracts | ||
|
|
||
| ### Text | ||
|
|
||
| `--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. | ||
|
|
||
| ### Final JSON | ||
|
|
||
| `--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. | ||
|
|
||
| ### Streaming JSON | ||
|
|
||
| `--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. | ||
|
|
||
| Breaking machine-schema changes increment the major schema version. Additive optional fields retain it. Unknown visible activity is represented generically rather than silently discarded. | ||
|
|
||
| ## Outcomes And Exit Codes | ||
|
|
||
| | Outcome | Exit | | ||
| | ---------------------------------- | ---: | | ||
| | Completed | 0 | | ||
| | Usage or configuration | 2 | | ||
| | Needs input | 3 | | ||
| | Explicit cancellation | 4 | | ||
| | Provider failure | 10 | | ||
| | Runtime, host, or protocol failure | 70 | | ||
| | Timeout | 124 | | ||
| | SIGINT | 130 | | ||
| | SIGTERM | 143 | | ||
|
|
||
| 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. | ||
|
|
||
| ## Sessions, Signals, And Ephemeral Runs | ||
|
|
||
| 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. | ||
|
|
||
| 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. | ||
|
|
||
| `--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. | ||
|
|
||
| ## Supported And Unsupported Capabilities | ||
|
|
||
| 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. | ||
|
|
||
| `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. | ||
|
|
||
| ## Coexistence With `roo` | ||
|
|
||
| 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. | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| - Run `zoo --version` to report the client/build contract. | ||
| - Use `--debug` for bounded redacted host diagnostics on stderr. | ||
| - Verify the effective `-C` workspace when a session is not found. | ||
| - A `needs_input` result is expected under safe approval; resume interactively to answer it. | ||
| - A host/protocol failure exits 70 and never contaminates JSON stdout. | ||
| - Timeout or signal cleanup is bounded; no host, shell, MCP, index, terminal, or watcher should survive. | ||
| - If vault access fails, verify Keychain or Secret Service availability, or use an invocation environment credential. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import { defineConfig } from "tsup" | ||
|
|
||
| export default defineConfig({ | ||
| entry: ["src/child.ts", "src/index.ts"], | ||
| format: ["esm"], | ||
| clean: true, | ||
| sourcemap: true, | ||
| target: "node22", | ||
| platform: "node", | ||
| noExternal: ["@roo-code/types", "@roo-code/vscode-shim", "@roo-code/zoo-protocol"], | ||
| }) |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tsup externalizes every node_modules package except those listed in
noExternal— which here is only@roo-code/zoo-protocol(apps/zoo/tsup.config.ts:11). Butapps/zoo/src/index.tsdoesimport { Command, Option } from "commander"at the top of the entry module. Since this dependency object installs onlyinkandreact,commanderwould not be resolvable inside the artifact — even the--help/--versionsmoke steps below would fail. Shouldcommanderbe added here? It is not a transitive dependency ofink/react, so a fresh install cannot inherit it.