|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +Zoo Code is a community fork of Roo Code — a VS Code extension that provides an AI-powered coding assistant in the editor. It supports multiple AI providers (Anthropic, OpenAI, Gemini, OpenRouter, Ollama, etc.) through a React webview UI backed by a TypeScript extension host. |
| 8 | + |
| 9 | +## Monorepo Structure |
| 10 | + |
| 11 | +``` |
| 12 | +src/ # VS Code extension (main workspace package, legacy location — intended to be apps/vscode) |
| 13 | + core/ # Extension core: webview provider, task system, tools, config, prompts, checkpoints |
| 14 | + api/ # API abstraction layer for AI providers |
| 15 | + services/ # MCP, code-index, marketplace, ripgrep, tree-sitter, glob, skills, telemetry |
| 16 | + integrations/ # VS Code integrations: editor, terminal, diagnostics, workspace, theme |
| 17 | + shared/ # Shared types and utilities (modes, tools, costs, context-mentions, etc.) |
| 18 | + utils/ # Utility functions (safeWriteJson, shell allowlist, etc.) |
| 19 | + extension.ts # Extension entry point |
| 20 | + activate/ # Activation helpers: commands, code actions, URI handlers |
| 21 | +webview-ui/ # React frontend (Vite + Tailwind CSS + shadcn/ui) |
| 22 | +apps/ |
| 23 | + cli/ # CLI tool |
| 24 | + vscode-e2e/ # End-to-end tests using @vscode/test-electron |
| 25 | + vscode-nightly/ # Nightly build configuration |
| 26 | +packages/ |
| 27 | + build/ # esbuild-based build helper |
| 28 | + cloud/ # Cloud service client (auth, licensing) |
| 29 | + config-eslint/ # Shared ESLint config |
| 30 | + config-typescript/ # Shared TypeScript config |
| 31 | + core/ # Shared core types/interfaces (@roo-code/core) |
| 32 | + ipc/ # IPC protocol between extension and webview |
| 33 | + telemetry/ # Telemetry service (PostHog-based) |
| 34 | + types/ # Shared type definitions (@roo-code/types) |
| 35 | + vscode-shim/ # VS Code API shim for non-extension contexts |
| 36 | +``` |
| 37 | + |
| 38 | +## Environment Requirements |
| 39 | + |
| 40 | +- **Node.js**: `20.20.2` (see `.nvmrc`). Other versions may work but are not tested. |
| 41 | +- **pnpm**: `10.8.1` (see root `package.json` → `packageManager`). The repo enforces pnpm via `only-allow`. |
| 42 | +- **VS Code**: `^1.84.0` for extension development (see `src/package.json` → `engines.vscode`). |
| 43 | + |
| 44 | +## Essential Commands |
| 45 | + |
| 46 | +```bash |
| 47 | +# Install dependencies (uses frozen-lockfile for CI/reproducible installs) |
| 48 | +pnpm install |
| 49 | + |
| 50 | +# Install:vsix and install:vsix:nightly explicitly use --frozen-lockfile |
| 51 | + |
| 52 | +# Build all packages |
| 53 | +pnpm build |
| 54 | + |
| 55 | +# Bundle only (esbuild for extension + vite for webview; faster than full build) |
| 56 | +pnpm bundle |
| 57 | + |
| 58 | +# Type checking |
| 59 | +pnpm check-types |
| 60 | + |
| 61 | +# Linting |
| 62 | +pnpm lint |
| 63 | + |
| 64 | +# Run all tests (depends on @roo-code/types#build — types must compile first) |
| 65 | +pnpm test |
| 66 | + |
| 67 | +# Run tests from specific workspace (critical — must run from workspace dir, not root) |
| 68 | +cd src && npx vitest run path/to/test.spec.ts |
| 69 | +cd webview-ui && npx vitest run src/path/to/test.spec.ts |
| 70 | + |
| 71 | +# Format code (also runs automatically on commit via lint-staged) |
| 72 | +pnpm format |
| 73 | + |
| 74 | +# Build VSIX package |
| 75 | +pnpm vsix |
| 76 | + |
| 77 | +# Install VSIX directly into VS Code (builds, uninstalls old version, installs new, prompts restart) |
| 78 | +pnpm install:vsix [-y] [--editor=<code|cursor|code-insiders>] |
| 79 | + |
| 80 | +# Clean build artifacts |
| 81 | +pnpm clean |
| 82 | +``` |
| 83 | + |
| 84 | +### Build Details |
| 85 | + |
| 86 | +The extension bundle is handled by [src/esbuild.mjs](src/esbuild.mjs) (not a CLI invocation): |
| 87 | + |
| 88 | +- **Two entry points**: `extension.ts` → `dist/extension.js` (CJS) and `workers/countTokens.ts` → `dist/workers/` |
| 89 | +- **Three externals**: `vscode`, `esbuild`, `global-agent` — global-agent must stay external because it dynamically patches Node.js http/https; undici is bundled because the VSIX is packaged with `--no-dependencies` |
| 90 | +- **Four plugins**: `copyFiles` (README, CHANGELOG, LICENSE, .env, material-icons, audio, marketplace assets), `copyWasms`, `copyLocales`, `esbuild-problem-matcher` |
| 91 | +- **Windows EBUSY**: Rebuilds run sequentially (`extensionCtx.rebuild()` then `workerCtx.rebuild()`) — NOT `Promise.all` — to avoid concurrent `onEnd` hooks copying the same asset directories. `distDir` cleanup retries up to 5 times with linear backoff for `ENOTEMPTY`/`EBUSY`/`EPERM` |
| 92 | +- **Build-time defines**: `process.env.PKG_RELEASE_CHANNEL` (default `"stable"`) and `process.env.POSTHOG_API_KEY` (default `""`) |
| 93 | +- **Sourcemaps**: Always generated (`sourcemap: true`) with `sourcesContent: false` |
| 94 | + |
| 95 | +### Pre-commit Hooks |
| 96 | + |
| 97 | +The repo uses **husky** + **lint-staged**. On every commit, staged files matching `*.{js,jsx,ts,tsx,json,css,md}` are auto-formatted with Prettier. This is configured in root `package.json` → `lint-staged`. |
| 98 | + |
| 99 | +## Code Style |
| 100 | + |
| 101 | +- **Prettier**: tabs (width 4), no semicolons, 120-char print width. See `.prettierrc.json`. |
| 102 | +- **ESLint**: Shared configs in `@roo-code/config-eslint` (`packages/config-eslint/`) with three presets: `base` (TS + prettier + turbo), `react` (extends base), `next-js` (extends react). Each workspace has its own `eslint.config.mjs` that imports one of these. |
| 103 | +- **pnpm-lock.yaml** is the lockfile. `package-lock.json` is gitignored. |
| 104 | + |
| 105 | +## Rebranding Context |
| 106 | + |
| 107 | +Zoo Code is a community fork of Roo Code. Many internal identifiers still reflect the transition: |
| 108 | +- Command prefix: `zoo-code.*` (new), but config keys may use `roo-cline.*` (legacy) |
| 109 | +- Extension manifest in `src/package.json` is `zoo-code`, but root `package.json` is still `roo-code` |
| 110 | +- The `src/` directory was intended to be `apps/vscode` (see `pnpm-workspace.yaml` comment) |
| 111 | +- Some packages (`@roo-code/types`, `@roo-code/core`) retain the old namespace |
| 112 | +- Do not assume consistent naming — always check actual identifiers before referencing them |
| 113 | + |
| 114 | +## Changesets |
| 115 | + |
| 116 | +This project uses [changesets](https://github.com/changesets/changesets) for versioning and changelog management: |
| 117 | +- Run `pnpm changeset:version` to bump versions based on pending changesets (also copies `CHANGELOG.md` to `src/`) |
| 118 | +- Changeset config is in `.changeset/config.json` |
| 119 | +- The core extension is in a **fixed group** (`[["zoo-code"]]`) — always versioned together |
| 120 | +- `@roo-code/cli` is **ignored** by changesets (CLI versioning is independent) |
| 121 | +- Custom changelog formatter in `.changeset/changelog-config.js` — summaries are bullet-pointed; dependency updates produce no changelog lines |
| 122 | + |
| 123 | +## CI/CD |
| 124 | + |
| 125 | +GitHub Actions workflows in `.github/workflows/`: |
| 126 | + |
| 127 | +| Workflow | Trigger | Purpose | |
| 128 | +|---|---|---| |
| 129 | +| `code-qa.yml` | PRs to main | Lint, type-check, knip dead-code check, unit tests (ubuntu + windows), translation validation | |
| 130 | +| `e2e.yml` | PRs to main | E2E tests with caching: if source hash previously passed, tests are skipped entirely | |
| 131 | +| `marketplace-publish.yml` | `v*.*.*` tag | Publish to VS Code Marketplace + Open VSX Registry + GitHub Release | |
| 132 | +| `nightly-publish.yml` | Push to main | Pre-release publish to VS Code Marketplace only (odd minor version + run_number) | |
| 133 | +| `release-validation.yml` | PRs touching release files | Validates semver, changelog entries, README sync, VSIX contents | |
| 134 | +| `cli-release.yml` | Manual trigger | Multi-platform CLI build (darwin-arm64, linux-x64, linux-arm64) | |
| 135 | + |
| 136 | +**Codecov strategy** (`code-qa.yml` unit-test job): Three separate coverage uploads on ubuntu only — non-core, core-unit, core-integration — each with distinct flags to prevent duplicate line counting when paths overlap. See `codecov.yml`. |
| 137 | + |
| 138 | +**Translation validation** (`check-translations` job): Runs `scripts/find-missing-translations.js` which compares non-English locale files against English baselines across all three i18n systems. Supports `--locale`, `--file`, `--area` filters. |
| 139 | + |
| 140 | +## Test Rules |
| 141 | + |
| 142 | +- **Tests MUST be run from the correct workspace directory, NOT from the monorepo root.** `npx vitest` at root fails with "command not found." |
| 143 | +- `vitest` globals (`vi`, `describe`, `it`, `expect`) are configured via `globals: true` in each workspace's config — no imports needed. |
| 144 | +- Both `src/` and `webview-ui/` have a `pretest` script that runs `turbo run bundle --cwd ..`. Bundle must succeed before tests execute. |
| 145 | +- `turbo.json` `test` task depends on `@roo-code/types#build`. Type generation from `packages/types/` must complete before any test can run. |
| 146 | +- Prefer the narrowest test layer: unit tests in `__tests__/` for logic, e2e in `apps/vscode-e2e/` only for real VS Code extension host behavior. |
| 147 | + |
| 148 | +**Per-workspace test environments:** |
| 149 | + |
| 150 | +| Workspace | Environment | Test file pattern | Notes | |
| 151 | +|---|---|---|---| |
| 152 | +| `src/` | node (default) | `*.test.ts` | VS Code mocked in `__mocks__/vscode.js` | |
| 153 | +| `webview-ui/` | **jsdom** | `*.spec.{ts,tsx}` | VS Code mocked in `src/__mocks__/vscode.ts`; aliases: `@` → `./src` | |
| 154 | +| `apps/cli/` | node | `*.test.{ts,tsx}` | 120s timeout for integration tests | |
| 155 | +| `packages/core/` | node | `*.test.ts` | Separate unit and integration configs via `scripts/test-config.ts` | |
| 156 | + |
| 157 | +**Coverage**: Codecov with project target `auto`, patch target 80%. Only ubuntu uploads coverage in CI — three separate uploads (non-core, core-unit, core-integration) to avoid double-counting overlapping paths. |
| 158 | + |
| 159 | +## Environment Variables |
| 160 | + |
| 161 | +**Build-time** (defined in esbuild `define`): |
| 162 | +- `PKG_RELEASE_CHANNEL` — `"stable"` or `"prerelease"` (nightly) |
| 163 | +- `POSTHOG_API_KEY` — analytics key (empty by default) |
| 164 | + |
| 165 | +**CLI** (`apps/cli/`): `ROO_AUTH_BASE_URL`, `ROO_SDK_BASE_URL`, `ROO_CODE_PROVIDER_URL` — point to production or local dev services. At runtime in release builds: `ROO_CLI_ROOT`, `ROO_EXTENSION_PATH`, `ROO_RIPGREP_PATH`. |
| 166 | + |
| 167 | +**E2E** (`apps/vscode-e2e/.env.local.sample`): Provider API keys for test runs. |
| 168 | + |
| 169 | +**CI secrets**: `VSCE_PAT`, `OVSX_PAT`, `POSTHOG_API_KEY`, `CODECOV_TOKEN`. |
| 170 | + |
| 171 | +## Key Architecture Patterns |
| 172 | + |
| 173 | +### API Provider System |
| 174 | +All AI providers live in [src/api/providers/](src/api/providers/). Each provider extends `BaseProvider` ([base-provider.ts](src/api/providers/base-provider.ts)) and implements model fetching + request handling. OpenAI-compatible providers extend `BaseOpenAiCompatibleProvider` ([base-openai-compatible-provider.ts](src/api/providers/base-openai-compatible-provider.ts)). The main API entry point is [src/api/index.ts](src/api/index.ts). |
| 175 | + |
| 176 | +### Extension Activation Flow |
| 177 | +[extension.ts](src/extension.ts) is the entry point. It: |
| 178 | +1. Loads environment variables, initializes network proxy, i18n |
| 179 | +2. Creates `ContextProxy` for workspace configuration |
| 180 | +3. Instantiates `ClineProvider` (the webview provider), `McpServerManager`, `CodeIndexManager`, `MdmService` |
| 181 | +4. Registers commands, code actions, terminal actions, URI handlers from [activate/](src/activate/) |
| 182 | + |
| 183 | +### Webview ↔ Extension Communication |
| 184 | +Uses VS Code's `postMessage` API. The `ClineProvider` in [src/core/webview/ClineProvider.ts](src/core/webview/ClineProvider.ts) manages two-way communication. The `WebviewMessage` type in [src/shared/WebviewMessage.ts](src/shared/WebviewMessage.ts) defines the message protocol. |
| 185 | + |
| 186 | +### Task System |
| 187 | +`src/core/task/` implements the main agent loop — handling user messages, tool execution, context management, and checkpoint creation. `src/core/task-persistence/` handles saving/restoring task state. |
| 188 | + |
| 189 | +### Context Management |
| 190 | +`src/core/context/` and `src/core/context-management/` manage the context window — condensing long conversations, tracking token usage, and managing context mentions. |
| 191 | + |
| 192 | +## Mandatory Patterns |
| 193 | + |
| 194 | +- **safeWriteJson** ([src/utils/safeWriteJson.ts](src/utils/safeWriteJson.ts)): All JSON file writes MUST use this. It provides atomic writes with file locking, temp files, backup, and rollback. Creates parent directories automatically — do NOT call `mkdir` before it. Test files are exempt. |
| 195 | +- **Settings View Pattern**: Settings inputs in webview-ui must bind to local `cachedState`, NOT live `useExtensionState()`. Direct wiring causes race conditions. |
| 196 | +- **Shell Detection**: [src/utils/shell.ts](src/utils/shell.ts) uses `vscode.env.shell` (VS Code 1.37+) as the primary shell resolution source, with platform-specific fallback chains (`userInfo().shell` → `COMSPEC`/`SHELL` → platform default). Both external terminal (`Terminal.ts`) and inline terminal (`ExecaTerminalProcess.ts`) use a unified shell resolution: `getExecaShellPath() || getShell()`. The external terminal path falls back to the inline path only when VS Code shell integration is unavailable (WSL, disabled, or unsupported shell) — `no_shell_integration` is emitted per-command. `Terminal.getConfiguredWslProfileArgs()` reads trusted-scope VS Code WSL profile args for supplementary distro detection (two-tier with `getShell()`). |
| 197 | +- **Tailwind over inline styles**: In webview-ui, use Tailwind CSS classes. VS Code CSS variables must be registered in [webview-ui/src/index.css](webview-ui/src/index.css) before use in Tailwind classes. |
| 198 | + |
| 199 | +### Terminal Execution |
| 200 | + |
| 201 | +There are two terminal execution paths — any terminal-related change must consider both: |
| 202 | + |
| 203 | +1. **Inline Terminal** (default, recommended): Uses `execa` via [`ExecaTerminalProcess.ts`](src/integrations/terminal/ExecaTerminalProcess.ts). Bypasses VS Code shell integration. Debug logs use `[ExecaTerminalProcess#run]` and `[ExecaTerminalProcess#abort]` prefixes. |
| 204 | +2. **VS Code Terminal**: Uses `vscode.window.createTerminal` via [`Terminal.ts`](src/integrations/terminal/Terminal.ts). Supports shell integration but can time out (default 15s), falling back to Inline Terminal with a `no_shell_integration` event. |
| 205 | + |
| 206 | +**`TerminalProcess` is a critical, stable interface** — see [src/integrations/terminal/README.md](src/integrations/terminal/README.md). Modifying it without understanding VS Code shell integration architecture can break terminal output reliability. Key design points: |
| 207 | + |
| 208 | +- 100ms throttled event output, zero-copy with index-based tracking (no substring ops) |
| 209 | +- `fullOutput` is never split on carriage returns; regex is avoided (500x slower than string parsing for large outputs) |
| 210 | +- The return type `RooTerminalProcessResultPromise` is both a `RooTerminalProcess` (event emitter) and a `Promise<void>` (completion) |
| 211 | +- Upstream VS Code bug [#237208](https://github.com/microsoft/vscode/issues/237208) may affect escape sequence handling |
| 212 | + |
| 213 | +### Internationalization (i18n) |
| 214 | + |
| 215 | +The extension is localized into 17+ languages. There are **three separate i18n systems**: |
| 216 | + |
| 217 | +1. **Extension manifest i18n**: Uses `%key%` placeholders resolved from `package.nls.*.json` files in `src/`. Controls VS Code commands, views, configuration contributions. |
| 218 | +2. **Webview UI i18n**: Uses `i18next` with resource files in `src/i18n/locales/`. Controls the React UI text. |
| 219 | +3. **Root documentation i18n**: Translated README and CONTRIBUTING docs in `locales/`. |
| 220 | + |
| 221 | +Always add new user-facing extension strings to `package.nls.json` (English base) at minimum. Webview UI strings go in `src/i18n/locales/`. These systems are independent — updating one does not affect the others. |
| 222 | + |
| 223 | +**Backend loader** ([src/i18n/setup.ts](src/i18n/setup.ts)): At runtime, uses Node.js `fs` to scan language directories and load JSON translation files by namespace (e.g., `chat.json`, `common.json`). In test environments (`NODE_ENV === "test"`), file-system loading is skipped entirely and empty resources are returned. |
| 224 | + |
| 225 | +**Translation validation**: `scripts/find-missing-translations.js` recursively diffs non-English locale files against English baselines across all three systems. Supports `--locale`, `--file`, `--area` (core/webview/package-nls/all) filters. |
| 226 | + |
| 227 | +## Debugging |
| 228 | + |
| 229 | +- **F5 in VS Code**: The pre-configured launch config in `.vscode/launch.json` starts an Extension Development Host with `NODE_ENV=development` and `VSCODE_DEBUG_MODE=true`. The default build task runs three parallel watchers (webview, bundle, tsc). |
| 230 | +- **Webview DevTools**: Command Palette → "Developer: Open Webview Developer Tools" (NOT F12). The webview runs in an isolated VSCode context — `localStorage` and filesystem access are unavailable. |
| 231 | +- **Extension host logs**: View → Output → select "Extension Host" (NOT the Debug Console). |
| 232 | +- **Shell integration**: Check the "Roo Code" output channel for shell integration status messages and timeouts. |
| 233 | +- **CLI debugging** (`apps/cli/`): `console.log` breaks the TUI display. Use file-based logging — write to `/tmp/roo-cli-debug.log` with `fs.appendFileSync()`. |
| 234 | + |
| 235 | +## Contribution Workflow |
| 236 | + |
| 237 | +Per `CONTRIBUTING.md`: **File a GitHub Issue and get it assigned before submitting a PR.** PRs without linked issues will not be reviewed. All PRs start as drafts. |
0 commit comments