| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 0 | 19 | 9 | 0% |
| Commands | 0 | 3 | 8 | 0% |
| Section tags | 2 | 3 | 4 | 22% |
What each file covers
Sections
0 shared · 19 only in A · 9 only in B- − Debug Harness
- − Quick start
- − Build extension first if needed (protos + esbuild):
- − Launch (skip-build if already built). Run with node, NOT bun — Playwright's
- − Electron launch times out under bun:
- − In another terminal:
- − Data Isolation
- − Browser Capture & OAuth
- − OAuth API
- − OAuth testing flow
- − Navigating Views — Use Commands, Not Clicks
- − Key commands
- − Typical Session
- − 1. Launch
- − 2. Open sidebar + dismiss overlays (ALWAYS do this first)
- − 3. Navigate to view
- − 4. Check captured OAuth URLs if testing auth
- − 5. Verify
- − Caveats
- + Miscellaneous
- + Searching the Codebase — Avoiding Build Output
- + How to skip build output
- + When you must search minified files
- + gRPC/Protobuf Communication
- + Adding New Global State Keys
- + StateManager Cache vs Direct globalState Access
- + ChatRow Cancelled/Interrupted States
- + Debug Harness: clear inherited VSCode/Electron env vars before launching
Commands
0 shared · 3 only in A · 8 only in B- − bun run protos && IS_DEV=true bun esbuild.mjs
- − node src/dev/debug-harness/server.ts --skip-build --auto-launch
- − bun run dev:mcp-oauth-test-server
- + bun src/dev/debug-harness/server.ts --auto-launch --skip-build
- + bun run X
- + bun install
- + bunx <bin>
- + bun file.ts
- + bun run compile
- + bun run build
- + bun run protos
Section tags
2 shared · 3 only in A · 4 only in B- − test
- − testing-strategy
- − api
- + setup
- + code-style
- + architecture
- + deployment
- build
- security
Line diff
cline/cline · .clinerules/debug-harness.md
@@ −1 @@
1# Debug Harness
2
3HTTP-controlled debugger for the VSCode extension at `src/dev/debug-harness/server.ts`.
4
5## Quick start
6
7```bash
8# Build extension first if needed (protos + esbuild):
9bun run protos && IS_DEV=true bun esbuild.mjs
10
11# Launch (skip-build if already built). Run with node, NOT bun — Playwright's
12# Electron launch times out under bun:
13node src/dev/debug-harness/server.ts --skip-build --auto-launch
14
15# In another terminal:
16curl localhost:19229/api -d '{"method":"status"}'
17```
18
19## Data Isolation
20
21The debugee runs with `CLINE_DIR=~/.cline2` by default, separate from your real `~/.cline`.
22This prevents the debugee's logout from logging out the debugger, and vice versa.
23Override with `--cline-dir /tmp/test-dir`. Check with `status()` → `clineDir`.
24
25## Browser Capture & OAuth
26
27The debugee runs with `CLINE_CAPTURE_BROWSER=1`, which intercepts `openExternal()` in
28`src/utils/env.ts`. URLs are captured instead of opening a real browser:
29
30- Logged to `$CLINE_DIR/data/debug-captured-urls.jsonl`
31- POSTed in real-time to `/captured-url` on the harness server
32- Queryable via `oauth.captured_urls`
33
34### OAuth API
35
36- **`oauth.captured_urls`** `{clear?}` — URLs the debugee tried to open
37- **`oauth.read_stored_token`** — Check auth token presence in secrets.json
38- **`oauth.simulate_callback`** `{path, code?, state?, provider?, token?}` — Build vscode:// callback URI
39- **`oauth.read_captured_urls_file`** — Read on-disk JSONL of captured URLs
40
41### OAuth testing flow
42
43For **Cline OAuth** (SDK local callback): The SDK starts a local HTTP server, the auth URL
44is captured. To complete: open the captured URL in a real browser (it redirects back to the
45SDK's callback server), OR extract the callback port and `curl http://127.0.0.1:PORT/callback?code=...`.
46
47For **MCP/Provider OAuth** (vscode:// URI): The redirect goes to a vscode:// URI.
48`oauth.simulate_callback` only *builds* the URI — it does not deliver it, and the ESM
49extension host can't `require()` the handler. To actually deliver the callback, call the
50debug-only hook via `ext.evaluate` (with `awaitPromise: true`):
51`globalThis.__clineHandleUri("vscode://saoudrizwan.claude-dev/...?code=...&state=...")`.
52It runs the same `SharedUriHandler.handleUri` as VSCode's real URI handler and exists only
53when `CLINE_CAPTURE_BROWSER` is set (the harness always sets it; never ships in prod).
54For end-to-end MCP OAuth, get a real `code` from the local MCP OAuth test server
55(`bun run dev:mcp-oauth-test-server`).
56
57## Navigating Views — Use Commands, Not Clicks
58
59Don't try to find/click small sidebar icons. Use VSCode commands via command palette.
60Registered in `src/registry.ts`:
61
62| Command | View |
63|---------|------|
64| `cline.accountButtonClicked` | Account / sign-in |
65| `cline.historyButtonClicked` | Task history |
66| `cline.settingsButtonClicked` | Settings |
67| `cline.mcpButtonClicked` | MCP servers |
68| `cline.plusButtonClicked` | New task (chat) |
69| `cline.worktreesButtonClicked` | Worktrees |
70
71```bash
72curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.accountButtonClicked"}}'
73```
74
75## Key commands
76
77All via `POST localhost:19229/api` with `{"method":"...", "params":{...}}`:
78
79- **`launch`** / **`shutdown`** — lifecycle
80- **`ui.screenshot`** — screenshot to `/tmp/cline-debug/`; returns `{path}` — **use `read_file` on the path to examine, do NOT `open` the file** (Preview.app covers the VSCode window)
81- **`ui.open_sidebar`** — open the Cline sidebar
82- **`ext.set_breakpoint`** `{file, line, condition?}` — breakpoint by source file (sourcemap-resolved)
83- **`ext.evaluate`** `{expression, callFrameId?}` — eval in extension host
84- **`ext.resume`** / **`ext.step_over`** / **`ext.step_into`** — stepping
85- **`ext.call_stack`** — inspect when paused
86- **`web.evaluate`** `{expression}` — eval in webview
87- **`web.post_message`** `{message}` — send postMessage to extension host via exposed vsCodeApi
88- **`wait_for_pause`** `{timeout?}` — block until breakpoint hit
89- **`ui.locator`** `{role?, testId?, text?, frame?}` — Playwright locator (auto-retries on stale sidebar frame)
90- **`ui.react_input`** `{text, selector?, clear?, submit?}` — set React textarea value via `execCommand('insertText')`; works reliably across multiple tasks
91- **`ui.send_message`** `{text, images?, files?, responseType?}` — send chat message bypassing the textarea entirely (via gRPC postMessage)
92- **`ui.command_palette`** `{command}` — run VSCode command
93
94## Typical Session
95
96```bash
97# 1. Launch
98curl localhost:19229/api -d '{"method":"launch","params":{"skipBuild":true}}'
99
100# 2. Open sidebar + dismiss overlays (ALWAYS do this first)
101curl localhost:19229/api -d '{"method":"ui.open_sidebar"}'
102curl localhost:19229/api -d '{"method":"web.evaluate","params":{"expression":"document.querySelectorAll(\".sr-only\").forEach(el => el.parentElement?.click())"}}'
103
104# 3. Navigate to view
105curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.accountButtonClicked"}}'
106
107# 4. Check captured OAuth URLs if testing auth
108curl localhost:19229/api -d '{"method":"oauth.captured_urls"}'
109
110# 5. Verify
111curl localhost:19229/api -d '{"method":"ui.screenshot"}'
112```
113
114## Caveats
115
116- **⚠️ Dismiss promotional overlays FIRST**: On fresh launches, full-screen promo overlays block the sidebar. **Dismiss immediately after `ui.open_sidebar`**, before any other interaction or screenshot. May need to run twice:
117 ```bash
118 curl localhost:19229/api -d '{"method": "ui.open_sidebar"}'
119 curl localhost:19229/api -d '{"method": "web.evaluate", "params": {"expression": "document.querySelectorAll(\".sr-only\").forEach(el => el.parentElement?.click())"}}'
120 ```
121- **Screenshots — don't open the file**: `ui.screenshot` and `ui.sidebar_screenshot` save PNGs to `/tmp/cline-debug/` and return the `{path}`. Use `read_file` on that path to examine screenshots. Running `open <path>` launches Preview.app on macOS which covers the VSCode window.
122- **Scripts count = 0 after launch**: CDP connects after extension host starts, so scripts parsed during startup aren't tracked. Breakpoints still work via sourcemap resolution.
123- **Port 9230**: Extension host inspector. If another VSCode instance uses this port, the harness will fail to connect. Kill other debug instances first.
124- **macOS only** for now (Playwright Electron launch behavior).
125- **Webview CDP**: `connect_webview` may fail depending on Electron version. `web.evaluate` still works via Playwright's `frame.evaluate()` fallback.
126- **Sourcemap paths**: esbuild outputs relative paths like `../src/extension.ts` in the sourcemap. The resolver handles this, but if a file isn't found, use `ext.source_files` to see exact paths.
127- **OAuth with fake codes**: Browser capture intercepts the URL but doesn't provide a valid auth code. For real OAuth testing, open the captured URL in a browser. For unit testing, mock the token exchange.
128
129See `src/dev/debug-harness/README.md` for full API reference.
130
cline/cline · .clinerules/general.md
@@ +1 @@
1This file is the secret sauce for working effectively in this codebase. It captures tribal knowledge—the nuanced, non-obvious patterns that make the difference between a quick fix and hours of back-and-forth & human intervention.
2
3**When to add to this file:**
4- User had to intervene, correct, or hand-hold
5- Multiple back-and-forth attempts were needed to get something working
6- You discovered something that required reading many files to understand
7- A change touched files you wouldn't have guessed
8- Something worked differently than you expected
9- User explicitly asks to "add this to CLAUDE.md"
10
11**Proactively suggest additions** when any of the above happen—don't wait to be asked.
12
13**What NOT to add:** Stuff you can figure out from reading a few files, obvious patterns, or standard practices. This file should be high-signal, not comprehensive.
14
15## Miscellaneous
16- The whole repo (including `apps/vscode`) uses **bun** for package management and task running. Emit `bun run X` / `bun install` / `bunx <bin>` / `bun file.ts`, never npm/npx. Node remains the *runtime* (VS Code's extension host and the standalone cline-core are Node), so Node-runtime tokens are legitimate and must not be "fixed" to bun — see @.clinerules/bun-and-node.md for the keep-list vs rewrite-list.
17- Avoid provider-specific string matching / hardcoded provider branches when fixing provider/config plumbing. Prefer provider metadata, shared catalog/defaults, explicit protocol/client capabilities, or centralized normalization utilities that apply by data shape rather than `providerId === "..."`. If a provider exception seems necessary, stop and explain why instead of adding ad-hoc string matching.
18- This is a VS Code extension—check `package.json` for available scripts before trying to verify builds (e.g., `bun run compile`, not `bun run build`).
19- When reading a configuration files that users may edit, use `readFileStrippingUtf8Bom`, `readFileSyncStrippingUtf8Bom`, or `stripUtf8Bom` from `@cline/shared/node`. DON'T strip byte order marks of user files handled by tools/passed to models.
20- When creating PRs, contributors should not create changelog-entry files. Maintainers handle release versioning and changelog curation during the release process.
21- When adding new feature flags, see this PR as a reference https://github.com/cline/cline/pull/7566
22- Additional instructions about making requests: @.clinerules/network.md
23
24## Searching the Codebase — Avoiding Build Output
25
26Several directories contain build output or generated code that produces
27noisy or unusable results with `search_files` / `grep`:
28
29| Directory | What it is | Why it's a problem |
30|-----------|-----------|-------------------|
31| `out/` | esbuild bundle output | Mirrors `src/` structure as minified JS — every search gets duplicate hits on single-line files |
32| `dist/` | Packaged extension | Entire extension bundled into one minified `extension.js` (~1 long line) |
33| `dist-standalone/` | Standalone build output | Same minification issue |
34| `src/generated/` | Generated protobuf code | Auto-generated from `proto/`; not the source of truth |
35| `src/shared/proto/` | Generated proto type defs | Auto-generated from `proto/`; not the source of truth |
36| `node_modules/` | Dependencies | Huge, not project source |
37
38### How to skip build output
39
40**`search_files`** — Point at `src/` (not the project root) and use `file_pattern`:
41```
42search_files(path="src/core", regex="myFunction", file_pattern="*.ts")
43```
44The `file_pattern` parameter is the most effective filter — e.g. `"*.ts"`,
45`"*.tsx"`, `"*.proto"`.
46
47**`grep` directly** — Exclude build dirs and restrict to source extensions:
48```bash
49grep -rn "myFunction" src/ --include="*.ts" --exclude-dir={out,dist,node_modules,generated}
50```
51
52### When you must search minified files
53
54Sometimes you need to verify what got bundled (e.g., checking if a change
55made it into the build). Minified files are typically one long line, so
56normal `grep` shows the entire file as context. Use these approaches:
57
58- **`grep -oP`** to extract just the match with limited surrounding context:
59 ```bash
60 grep -oP '.{0,40}myFunction.{0,40}' dist/extension.js
61 ```
62- **`read_file`** on files in `out/src/` — these have source maps and are
63 more readable than `dist/extension.js` (which is the fully bundled output).
64- **Source maps** — `out/src/*.js.map` and `dist/extension.js.map` can be
65 used to trace minified output back to original source locations.
66
67## gRPC/Protobuf Communication
68The extension and webview communicate via gRPC-like protocol over VS Code message passing.
69
70**Proto files live in `proto/`** (e.g., `proto/cline/task.proto`, `proto/cline/ui.proto`)
71- Each feature domain has its own `.proto` file
72- For simple data, use shared types in `proto/cline/common.proto` (`StringRequest`, `Empty`, `Int64Request`)
73- For complex data, define custom messages in the feature's `.proto` file
74- Naming: Services `PascalCaseService`, RPCs `camelCase`, Messages `PascalCase`
75- For streaming responses, use `stream` keyword (see `subscribeToAuthCallback` in `account.proto`)
76
77**Run `bun run protos`** after any proto changes—generates types in:
78- `src/shared/proto/` - Shared type definitions
79- `src/generated/grpc-js/` - Service implementations
80- `src/generated/nice-grpc/` - Promise-based clients
81- `src/generated/hosts/` - Generated handlers
82
83**Adding new enum values** (like a new `ClineSay` type) requires updating conversion mappings in `src/shared/proto-conversions/cline-message.ts`
84
85**Adding new RPC methods** requires:
86- Handler in `src/core/controller/<domain>/`
87- Call from webview via generated client: `UiServiceClient.scrollToSettings(StringRequest.create({ value: "browser" }))`
88
89**Example—the `explain-changes` feature touched:**
90- `proto/cline/task.proto` - Added `ExplainChangesRequest` message and `explainChanges` RPC
91- `proto/cline/ui.proto` - Added `GENERATE_EXPLANATION = 29` to `ClineSay` enum
92- `src/shared/ExtensionMessage.ts` - Added `ClineSayGenerateExplanation` type
93- `src/shared/proto-conversions/cline-message.ts` - Added mapping for new say type
94- `src/core/controller/task/explainChanges.ts` - Handler implementation
95- `webview-ui/src/components/chat/ChatRow.tsx` - UI rendering
96
97## Adding New Global State Keys
98Adding a new key to global state requires updates in multiple places. Missing any step causes silent failures.
99
100Required steps:
1011. Type definition in `src/shared/storage/state-keys.ts` - Add to `GlobalState` or `Settings` interface
1022. Add any default value or transform in `src/shared/storage/state-keys.ts` if the key needs one
1033. Read and write the value through `StateManager` (`setGlobalState()` / `getGlobalStateKey()`) after initialization
104
105Persistent state is file-backed through `StateManager`; do not add new runtime reads or writes against VS Code `ExtensionContext` storage. That storage is only a legacy migration source.
106
107Settings plumbing gotcha: if a key is user-toggleable from settings, wire both controller update paths:
108- `src/core/controller/state/updateSettings.ts` for webview `updateSetting(...)`
109- `src/core/controller/state/updateSettingsCli.ts` for CLI/ACP settings updates
110Missing one path causes a toggle to appear to change in one surface while the backend state stays unchanged.
111
112Webview toggle gotcha: settings changes must also round-trip back in state payloads.
113- Add the field to `UpdateSettingsRequest` in `proto/cline/state.proto` (for webview update requests), then run `bun run protos`
114- Include the key in `Controller.getStateToPostToWebview()` (`src/core/controller/index.ts`)
115- Ensure `ExtensionState` and webview defaults include the key (`src/shared/ExtensionMessage.ts`, `webview-ui/src/context/ExtensionStateContext.tsx`)
116If this round-trip wiring is missing, the backend value can update but the toggle in webview appears stuck or reverts.
117
118## StateManager Cache vs Direct globalState Access
119StateManager uses an in-memory cache populated during `StateManager.initialize()` from file-backed storage. For most state, use `controller.stateManager.setGlobalState()`/`getGlobalStateKey()`.
120
121Exception: host migration code may read legacy VS Code storage before file-backed storage is initialized.
122
123Example pattern:
124```typescript
125// Writing (normal pattern)
126controller.stateManager.setGlobalState("myKey", value)
127
128// Reading after initialization
129const value = controller.stateManager.getGlobalStateKey("myKey")
130```
131
132Use `context.globalState` only in VS Code migration code that copies legacy ExtensionContext values into the shared file-backed stores.
133
134## ChatRow Cancelled/Interrupted States
135When a ChatRow displays a loading/in-progress state (spinner), you must handle what happens when the task is cancelled. This is non-obvious because cancellation doesn't update the message content—you have to infer it from context.
136
137**The pattern:**
1381. A message has a `status` field (e.g., `"generating"`, `"complete"`, `"error"`) stored in `message.text` as JSON
1392. When cancelled mid-operation, the status stays `"generating"` forever—no one updates it
1403. To detect cancellation, check TWO conditions:
141 - `!isLast` — if this message is no longer the last message, something else happened after it (interrupted)
142 - `lastModifiedMessage?.ask === "resume_task" || "resume_completed_task"` — task was just cancelled and is waiting to resume
143
144**Example from `generate_explanation`:**
145```tsx
146const wasCancelled =
147 explanationInfo.status === "generating" &&
148 (!isLast ||
149 lastModifiedMessage?.ask === "resume_task" ||
150 lastModifiedMessage?.ask === "resume_completed_task")
151const isGenerating = explanationInfo.status === "generating" && !wasCancelled
152```
153
154**Why both checks?**
155- `!isLast` catches: cancelled → resumed → did other stuff → this old message is stale
156- `lastModifiedMessage?.ask === "resume_task"` catches: just cancelled, hasn't resumed yet, this message is still technically "last"
157
158**See also:** `BrowserSessionRow.tsx` uses similar pattern with `isLastApiReqInterrupted` and `isLastMessageResume`.
159
160**Backend side:** When streaming is cancelled, clean up properly (close tabs, clear comments, etc.) by checking `taskState.abort` after the streaming function returns.
161
162## Debug Harness: clear inherited VSCode/Electron env vars before launching
163
164The debug harness (`apps/vscode/src/dev/debug-harness/server.ts`) launches a child
165VSCode via Playwright's `_electron.launch({ env: { ...process.env, ... } })`. If you
166run the harness from a process that was itself spawned by VSCode (e.g. the Cline
167extension host, an integrated terminal, or an agent running inside VSCode), the
168parent's VSCode/Electron env vars leak into the child and break the launch.
169
170The fatal one is **`ELECTRON_RUN_AS_NODE=1`**: it makes the child VSCode binary run
171as plain Node, so it rejects every VSCode CLI flag. Symptom:
172
173```
174.../Visual Studio Code.app/Contents/MacOS/Code: bad option: --extensionDevelopmentPath=...
175Error: Process failed to launch! (Playwright _electron.launch)
176```
177
178This is NOT the macOS Playwright flakiness mentioned in the harness README — it's
179env inheritance. Fix: strip the inherited vars before starting the harness:
180
181```bash
182env -u ELECTRON_RUN_AS_NODE -u ELECTRON_NO_ATTACH_CONSOLE \
183 -u VSCODE_CLI -u VSCODE_CODE_CACHE_PATH -u VSCODE_CRASH_REPORTER_PROCESS_TYPE \
184 -u VSCODE_CWD -u VSCODE_ESM_ENTRYPOINT -u VSCODE_HANDLES_UNCAUGHT_ERRORS \
185 -u VSCODE_IPC_HOOK -u VSCODE_NLS_CONFIG -u VSCODE_PID -u VSCODE_L10N_BUNDLE_LOCATION \
186 bun src/dev/debug-harness/server.ts --auto-launch --skip-build
187```
188
189Check your own env with `env | grep -iE 'electron|vscode_'` first; `ELECTRON_RUN_AS_NODE=1`
190present means you must scrub before launching.
191
192Other harness notes confirmed in practice:
193- The extension host is **ESM** (`VSCODE_ESM_ENTRYPOINT`), so `ext.evaluate` has no
194 `require` and module-internal functions aren't reachable as globals. To inspect
195 internal builders (e.g. `buildBedrockProviderConfig`), set a breakpoint with
196 `ext.set_breakpoint` and read locals via `ext.evaluate` with the paused `callFrameId`
197 — don't try to `require()` the bundle.
198- `web.evaluate` wraps the expression as a single returned expression; multi-statement
199 snippets must be an IIFE `(() => { ...; return x; })()`, otherwise you get
200 `SyntaxError: Unexpected token ';'`.
201- Webview settings inputs are `vscode-text-field` web components with debounced React
202 onChange. Setting `.value` + dispatching events via `web.evaluate` is unreliable for
203 some fields; focus the inner shadow `input` then use real keystrokes (`ui.type` +
204 `ui.press Tab`, or click the dropdown option) to make the value persist.
205
206
@@ −1 +1 @@
1−# Debug Harness
1+This file is the secret sauce for working effectively in this codebase. It captures tribal knowledge—the nuanced, non-obvious patterns that make the difference between a quick fix and hours of back-and-forth & human intervention.
22
3−HTTP-controlled debugger for the VSCode extension at `src/dev/debug-harness/server.ts`.
3+**When to add to this file:**
4+- User had to intervene, correct, or hand-hold
5+- Multiple back-and-forth attempts were needed to get something working
6+- You discovered something that required reading many files to understand
7+- A change touched files you wouldn't have guessed
8+- Something worked differently than you expected
9+- User explicitly asks to "add this to CLAUDE.md"
410
5−## Quick start
11+**Proactively suggest additions** when any of the above happen—don't wait to be asked.
612
7−```bash
8−# Build extension first if needed (protos + esbuild):
9−bun run protos && IS_DEV=true bun esbuild.mjs
13+**What NOT to add:** Stuff you can figure out from reading a few files, obvious patterns, or standard practices. This file should be high-signal, not comprehensive.
1014
11−# Launch (skip-build if already built). Run with node, NOT bun — Playwright's
12−# Electron launch times out under bun:
13−node src/dev/debug-harness/server.ts --skip-build --auto-launch
15+## Miscellaneous
16+- The whole repo (including `apps/vscode`) uses **bun** for package management and task running. Emit `bun run X` / `bun install` / `bunx <bin>` / `bun file.ts`, never npm/npx. Node remains the *runtime* (VS Code's extension host and the standalone cline-core are Node), so Node-runtime tokens are legitimate and must not be "fixed" to bun — see @.clinerules/bun-and-node.md for the keep-list vs rewrite-list.
17+- Avoid provider-specific string matching / hardcoded provider branches when fixing provider/config plumbing. Prefer provider metadata, shared catalog/defaults, explicit protocol/client capabilities, or centralized normalization utilities that apply by data shape rather than `providerId === "..."`. If a provider exception seems necessary, stop and explain why instead of adding ad-hoc string matching.
18+- This is a VS Code extension—check `package.json` for available scripts before trying to verify builds (e.g., `bun run compile`, not `bun run build`).
19+- When reading a configuration files that users may edit, use `readFileStrippingUtf8Bom`, `readFileSyncStrippingUtf8Bom`, or `stripUtf8Bom` from `@cline/shared/node`. DON'T strip byte order marks of user files handled by tools/passed to models.
20+- When creating PRs, contributors should not create changelog-entry files. Maintainers handle release versioning and changelog curation during the release process.
21+- When adding new feature flags, see this PR as a reference https://github.com/cline/cline/pull/7566
22+- Additional instructions about making requests: @.clinerules/network.md
1423
15−# In another terminal:
16−curl localhost:19229/api -d '{"method":"status"}'
24+## Searching the Codebase — Avoiding Build Output
25+
26+Several directories contain build output or generated code that produces
27+noisy or unusable results with `search_files` / `grep`:
28+
29+| Directory | What it is | Why it's a problem |
30+|-----------|-----------|-------------------|
31+| `out/` | esbuild bundle output | Mirrors `src/` structure as minified JS — every search gets duplicate hits on single-line files |
32+| `dist/` | Packaged extension | Entire extension bundled into one minified `extension.js` (~1 long line) |
33+| `dist-standalone/` | Standalone build output | Same minification issue |
34+| `src/generated/` | Generated protobuf code | Auto-generated from `proto/`; not the source of truth |
35+| `src/shared/proto/` | Generated proto type defs | Auto-generated from `proto/`; not the source of truth |
36+| `node_modules/` | Dependencies | Huge, not project source |
37+
38+### How to skip build output
39+
40+**`search_files`** — Point at `src/` (not the project root) and use `file_pattern`:
1741 ```
42+search_files(path="src/core", regex="myFunction", file_pattern="*.ts")
43+```
44+The `file_pattern` parameter is the most effective filter — e.g. `"*.ts"`,
45+`"*.tsx"`, `"*.proto"`.
1846
19−## Data Isolation
47+**`grep` directly** — Exclude build dirs and restrict to source extensions:
48+```bash
49+grep -rn "myFunction" src/ --include="*.ts" --exclude-dir={out,dist,node_modules,generated}
50+```
2051
21−The debugee runs with `CLINE_DIR=~/.cline2` by default, separate from your real `~/.cline`.
22−This prevents the debugee's logout from logging out the debugger, and vice versa.
23−Override with `--cline-dir /tmp/test-dir`. Check with `status()` → `clineDir`.
52+### When you must search minified files
2453
25−## Browser Capture & OAuth
54+Sometimes you need to verify what got bundled (e.g., checking if a change
55+made it into the build). Minified files are typically one long line, so
56+normal `grep` shows the entire file as context. Use these approaches:
2657
27−The debugee runs with `CLINE_CAPTURE_BROWSER=1`, which intercepts `openExternal()` in
28−`src/utils/env.ts`. URLs are captured instead of opening a real browser:
58+- **`grep -oP`** to extract just the match with limited surrounding context:
59+ ```bash
60+ grep -oP '.{0,40}myFunction.{0,40}' dist/extension.js
61+ ```
62+- **`read_file`** on files in `out/src/` — these have source maps and are
63+ more readable than `dist/extension.js` (which is the fully bundled output).
64+- **Source maps** — `out/src/*.js.map` and `dist/extension.js.map` can be
65+ used to trace minified output back to original source locations.
2966
30−- Logged to `$CLINE_DIR/data/debug-captured-urls.jsonl`
31−- POSTed in real-time to `/captured-url` on the harness server
32−- Queryable via `oauth.captured_urls`
67+## gRPC/Protobuf Communication
68+The extension and webview communicate via gRPC-like protocol over VS Code message passing.
3369
34−### OAuth API
70+**Proto files live in `proto/`** (e.g., `proto/cline/task.proto`, `proto/cline/ui.proto`)
71+- Each feature domain has its own `.proto` file
72+- For simple data, use shared types in `proto/cline/common.proto` (`StringRequest`, `Empty`, `Int64Request`)
73+- For complex data, define custom messages in the feature's `.proto` file
74+- Naming: Services `PascalCaseService`, RPCs `camelCase`, Messages `PascalCase`
75+- For streaming responses, use `stream` keyword (see `subscribeToAuthCallback` in `account.proto`)
3576
36−- **`oauth.captured_urls`** `{clear?}` — URLs the debugee tried to open
37−- **`oauth.read_stored_token`** — Check auth token presence in secrets.json
38−- **`oauth.simulate_callback`** `{path, code?, state?, provider?, token?}` — Build vscode:// callback URI
39−- **`oauth.read_captured_urls_file`** — Read on-disk JSONL of captured URLs
77+**Run `bun run protos`** after any proto changes—generates types in:
78+- `src/shared/proto/` - Shared type definitions
79+- `src/generated/grpc-js/` - Service implementations
80+- `src/generated/nice-grpc/` - Promise-based clients
81+- `src/generated/hosts/` - Generated handlers
4082
41−### OAuth testing flow
83+**Adding new enum values** (like a new `ClineSay` type) requires updating conversion mappings in `src/shared/proto-conversions/cline-message.ts`
4284
43−For **Cline OAuth** (SDK local callback): The SDK starts a local HTTP server, the auth URL
44−is captured. To complete: open the captured URL in a real browser (it redirects back to the
45−SDK's callback server), OR extract the callback port and `curl http://127.0.0.1:PORT/callback?code=...`.
85+**Adding new RPC methods** requires:
86+- Handler in `src/core/controller/<domain>/`
87+- Call from webview via generated client: `UiServiceClient.scrollToSettings(StringRequest.create({ value: "browser" }))`
4688
47−For **MCP/Provider OAuth** (vscode:// URI): The redirect goes to a vscode:// URI.
48−`oauth.simulate_callback` only *builds* the URI — it does not deliver it, and the ESM
49−extension host can't `require()` the handler. To actually deliver the callback, call the
50−debug-only hook via `ext.evaluate` (with `awaitPromise: true`):
51−`globalThis.__clineHandleUri("vscode://saoudrizwan.claude-dev/...?code=...&state=...")`.
52−It runs the same `SharedUriHandler.handleUri` as VSCode's real URI handler and exists only
53−when `CLINE_CAPTURE_BROWSER` is set (the harness always sets it; never ships in prod).
54−For end-to-end MCP OAuth, get a real `code` from the local MCP OAuth test server
55−(`bun run dev:mcp-oauth-test-server`).
89+**Example—the `explain-changes` feature touched:**
90+- `proto/cline/task.proto` - Added `ExplainChangesRequest` message and `explainChanges` RPC
91+- `proto/cline/ui.proto` - Added `GENERATE_EXPLANATION = 29` to `ClineSay` enum
92+- `src/shared/ExtensionMessage.ts` - Added `ClineSayGenerateExplanation` type
93+- `src/shared/proto-conversions/cline-message.ts` - Added mapping for new say type
94+- `src/core/controller/task/explainChanges.ts` - Handler implementation
95+- `webview-ui/src/components/chat/ChatRow.tsx` - UI rendering
5696
57−## Navigating Views — Use Commands, Not Clicks
97+## Adding New Global State Keys
98+Adding a new key to global state requires updates in multiple places. Missing any step causes silent failures.
5899
59−Don't try to find/click small sidebar icons. Use VSCode commands via command palette.
60−Registered in `src/registry.ts`:
100+Required steps:
101+1. Type definition in `src/shared/storage/state-keys.ts` - Add to `GlobalState` or `Settings` interface
102+2. Add any default value or transform in `src/shared/storage/state-keys.ts` if the key needs one
103+3. Read and write the value through `StateManager` (`setGlobalState()` / `getGlobalStateKey()`) after initialization
61104
62−| Command | View |
63−|---------|------|
64−| `cline.accountButtonClicked` | Account / sign-in |
65−| `cline.historyButtonClicked` | Task history |
66−| `cline.settingsButtonClicked` | Settings |
67−| `cline.mcpButtonClicked` | MCP servers |
68−| `cline.plusButtonClicked` | New task (chat) |
69−| `cline.worktreesButtonClicked` | Worktrees |
105+Persistent state is file-backed through `StateManager`; do not add new runtime reads or writes against VS Code `ExtensionContext` storage. That storage is only a legacy migration source.
70106
71−```bash
72−curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.accountButtonClicked"}}'
107+Settings plumbing gotcha: if a key is user-toggleable from settings, wire both controller update paths:
108+- `src/core/controller/state/updateSettings.ts` for webview `updateSetting(...)`
109+- `src/core/controller/state/updateSettingsCli.ts` for CLI/ACP settings updates
110+Missing one path causes a toggle to appear to change in one surface while the backend state stays unchanged.
111+
112+Webview toggle gotcha: settings changes must also round-trip back in state payloads.
113+- Add the field to `UpdateSettingsRequest` in `proto/cline/state.proto` (for webview update requests), then run `bun run protos`
114+- Include the key in `Controller.getStateToPostToWebview()` (`src/core/controller/index.ts`)
115+- Ensure `ExtensionState` and webview defaults include the key (`src/shared/ExtensionMessage.ts`, `webview-ui/src/context/ExtensionStateContext.tsx`)
116+If this round-trip wiring is missing, the backend value can update but the toggle in webview appears stuck or reverts.
117+
118+## StateManager Cache vs Direct globalState Access
119+StateManager uses an in-memory cache populated during `StateManager.initialize()` from file-backed storage. For most state, use `controller.stateManager.setGlobalState()`/`getGlobalStateKey()`.
120+
121+Exception: host migration code may read legacy VS Code storage before file-backed storage is initialized.
122+
123+Example pattern:
124+```typescript
125+// Writing (normal pattern)
126+controller.stateManager.setGlobalState("myKey", value)
127+
128+// Reading after initialization
129+const value = controller.stateManager.getGlobalStateKey("myKey")
73130 ```
74131
75−## Key commands
132+Use `context.globalState` only in VS Code migration code that copies legacy ExtensionContext values into the shared file-backed stores.
76133
77−All via `POST localhost:19229/api` with `{"method":"...", "params":{...}}`:
134+## ChatRow Cancelled/Interrupted States
135+When a ChatRow displays a loading/in-progress state (spinner), you must handle what happens when the task is cancelled. This is non-obvious because cancellation doesn't update the message content—you have to infer it from context.
78136
79−- **`launch`** / **`shutdown`** — lifecycle
80−- **`ui.screenshot`** — screenshot to `/tmp/cline-debug/`; returns `{path}` — **use `read_file` on the path to examine, do NOT `open` the file** (Preview.app covers the VSCode window)
81−- **`ui.open_sidebar`** — open the Cline sidebar
82−- **`ext.set_breakpoint`** `{file, line, condition?}` — breakpoint by source file (sourcemap-resolved)
83−- **`ext.evaluate`** `{expression, callFrameId?}` — eval in extension host
84−- **`ext.resume`** / **`ext.step_over`** / **`ext.step_into`** — stepping
85−- **`ext.call_stack`** — inspect when paused
86−- **`web.evaluate`** `{expression}` — eval in webview
87−- **`web.post_message`** `{message}` — send postMessage to extension host via exposed vsCodeApi
88−- **`wait_for_pause`** `{timeout?}` — block until breakpoint hit
89−- **`ui.locator`** `{role?, testId?, text?, frame?}` — Playwright locator (auto-retries on stale sidebar frame)
90−- **`ui.react_input`** `{text, selector?, clear?, submit?}` — set React textarea value via `execCommand('insertText')`; works reliably across multiple tasks
91−- **`ui.send_message`** `{text, images?, files?, responseType?}` — send chat message bypassing the textarea entirely (via gRPC postMessage)
92−- **`ui.command_palette`** `{command}` — run VSCode command
137+**The pattern:**
138+1. A message has a `status` field (e.g., `"generating"`, `"complete"`, `"error"`) stored in `message.text` as JSON
139+2. When cancelled mid-operation, the status stays `"generating"` forever—no one updates it
140+3. To detect cancellation, check TWO conditions:
141+ - `!isLast` — if this message is no longer the last message, something else happened after it (interrupted)
142+ - `lastModifiedMessage?.ask === "resume_task" || "resume_completed_task"` — task was just cancelled and is waiting to resume
93143
94−## Typical Session
144+**Example from `generate_explanation`:**
145+```tsx
146+const wasCancelled =
147+ explanationInfo.status === "generating" &&
148+ (!isLast ||
149+ lastModifiedMessage?.ask === "resume_task" ||
150+ lastModifiedMessage?.ask === "resume_completed_task")
151+const isGenerating = explanationInfo.status === "generating" && !wasCancelled
152+```
95153
96−```bash
97−# 1. Launch
98−curl localhost:19229/api -d '{"method":"launch","params":{"skipBuild":true}}'
154+**Why both checks?**
155+- `!isLast` catches: cancelled → resumed → did other stuff → this old message is stale
156+- `lastModifiedMessage?.ask === "resume_task"` catches: just cancelled, hasn't resumed yet, this message is still technically "last"
99157
100−# 2. Open sidebar + dismiss overlays (ALWAYS do this first)
101−curl localhost:19229/api -d '{"method":"ui.open_sidebar"}'
102−curl localhost:19229/api -d '{"method":"web.evaluate","params":{"expression":"document.querySelectorAll(\".sr-only\").forEach(el => el.parentElement?.click())"}}'
158+**See also:** `BrowserSessionRow.tsx` uses similar pattern with `isLastApiReqInterrupted` and `isLastMessageResume`.
103159
104−# 3. Navigate to view
105−curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.accountButtonClicked"}}'
160+**Backend side:** When streaming is cancelled, clean up properly (close tabs, clear comments, etc.) by checking `taskState.abort` after the streaming function returns.
106161
107−# 4. Check captured OAuth URLs if testing auth
108−curl localhost:19229/api -d '{"method":"oauth.captured_urls"}'
162+## Debug Harness: clear inherited VSCode/Electron env vars before launching
109163
110−# 5. Verify
111−curl localhost:19229/api -d '{"method":"ui.screenshot"}'
164+The debug harness (`apps/vscode/src/dev/debug-harness/server.ts`) launches a child
165+VSCode via Playwright's `_electron.launch({ env: { ...process.env, ... } })`. If you
166+run the harness from a process that was itself spawned by VSCode (e.g. the Cline
167+extension host, an integrated terminal, or an agent running inside VSCode), the
168+parent's VSCode/Electron env vars leak into the child and break the launch.
169+
170+The fatal one is **`ELECTRON_RUN_AS_NODE=1`**: it makes the child VSCode binary run
171+as plain Node, so it rejects every VSCode CLI flag. Symptom:
172+
112173 ```
174+.../Visual Studio Code.app/Contents/MacOS/Code: bad option: --extensionDevelopmentPath=...
175+Error: Process failed to launch! (Playwright _electron.launch)
176+```
113177
114−## Caveats
178+This is NOT the macOS Playwright flakiness mentioned in the harness README — it's
179+env inheritance. Fix: strip the inherited vars before starting the harness:
115180
116−- **⚠️ Dismiss promotional overlays FIRST**: On fresh launches, full-screen promo overlays block the sidebar. **Dismiss immediately after `ui.open_sidebar`**, before any other interaction or screenshot. May need to run twice:
117− ```bash
118− curl localhost:19229/api -d '{"method": "ui.open_sidebar"}'
119− curl localhost:19229/api -d '{"method": "web.evaluate", "params": {"expression": "document.querySelectorAll(\".sr-only\").forEach(el => el.parentElement?.click())"}}'
120− ```
121−- **Screenshots — don't open the file**: `ui.screenshot` and `ui.sidebar_screenshot` save PNGs to `/tmp/cline-debug/` and return the `{path}`. Use `read_file` on that path to examine screenshots. Running `open <path>` launches Preview.app on macOS which covers the VSCode window.
122−- **Scripts count = 0 after launch**: CDP connects after extension host starts, so scripts parsed during startup aren't tracked. Breakpoints still work via sourcemap resolution.
123−- **Port 9230**: Extension host inspector. If another VSCode instance uses this port, the harness will fail to connect. Kill other debug instances first.
124−- **macOS only** for now (Playwright Electron launch behavior).
125−- **Webview CDP**: `connect_webview` may fail depending on Electron version. `web.evaluate` still works via Playwright's `frame.evaluate()` fallback.
126−- **Sourcemap paths**: esbuild outputs relative paths like `../src/extension.ts` in the sourcemap. The resolver handles this, but if a file isn't found, use `ext.source_files` to see exact paths.
127−- **OAuth with fake codes**: Browser capture intercepts the URL but doesn't provide a valid auth code. For real OAuth testing, open the captured URL in a browser. For unit testing, mock the token exchange.
181+```bash
182+env -u ELECTRON_RUN_AS_NODE -u ELECTRON_NO_ATTACH_CONSOLE \
183+ -u VSCODE_CLI -u VSCODE_CODE_CACHE_PATH -u VSCODE_CRASH_REPORTER_PROCESS_TYPE \
184+ -u VSCODE_CWD -u VSCODE_ESM_ENTRYPOINT -u VSCODE_HANDLES_UNCAUGHT_ERRORS \
185+ -u VSCODE_IPC_HOOK -u VSCODE_NLS_CONFIG -u VSCODE_PID -u VSCODE_L10N_BUNDLE_LOCATION \
186+ bun src/dev/debug-harness/server.ts --auto-launch --skip-build
187+```
128188
129−See `src/dev/debug-harness/README.md` for full API reference.
189+Check your own env with `env | grep -iE 'electron|vscode_'` first; `ELECTRON_RUN_AS_NODE=1`
190+present means you must scrub before launching.
191+
192+Other harness notes confirmed in practice:
193+- The extension host is **ESM** (`VSCODE_ESM_ENTRYPOINT`), so `ext.evaluate` has no
194+ `require` and module-internal functions aren't reachable as globals. To inspect
195+ internal builders (e.g. `buildBedrockProviderConfig`), set a breakpoint with
196+ `ext.set_breakpoint` and read locals via `ext.evaluate` with the paused `callFrameId`
197+ — don't try to `require()` the bundle.
198+- `web.evaluate` wraps the expression as a single returned expression; multi-statement
199+ snippets must be an IIFE `(() => { ...; return x; })()`, otherwise you get
200+ `SyntaxError: Unexpected token ';'`.
201+- Webview settings inputs are `vscode-text-field` web components with debounced React
202+ onChange. Setting `.value` + dispatching events via `web.evaluate` is unreliable for
203+ some fields; focus the inner shadow `input` then use real keystrokes (`ui.type` +
204+ `ui.press Tab`, or click the dropdown option) to make the value persist.
205+
130206
