| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 0 | 9 | 4 | 0% |
| Commands | 3 | 5 | 11 | 16% |
| Section tags | 1 | 5 | 3 | 11% |
What each file covers
Sections
0 shared · 9 only in A · 4 only in B- − 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
- + Bun (tooling) and Node (runtime)
- + Use bun for tooling
- + Node is the runtime — do NOT rewrite these to bun
- + Tests: bun vs the VS Code host
Commands
3 shared · 5 only in A · 11 only in B- − bun src/dev/debug-harness/server.ts --auto-launch --skip-build
- − bun run X
- − bun run compile
- − bun run build
- − bun run protos
- + npm install
- + npm ci
- + bun run <script>
- + npm run <script>
- + npx <bin>
- + bun esbuild.mjs
- + bun run --parallel ...
- + bun.lock
- + node
- + node:fs
- + bun:test
- bun install
- bunx <bin>
- bun file.ts
Section tags
1 shared · 5 only in A · 3 only in B- − build
- − code-style
- − architecture
- − security
- − deployment
- + test
- + monorepo
- + do-not
- setup
Line diff
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
cline/cline · .clinerules/bun-and-node.md
@@ +1 @@
1# Bun (tooling) and Node (runtime)
2
3This repo uses **bun** for package management and task running, and **Node** as
4the execution runtime. Both are correct at the same time; the distinction is the
5source of most confusion, so keep it straight before editing scripts, configs,
6docs, or comments.
7
8## Use bun for tooling
9
10- `bun install` (never `npm install` / `npm ci`)
11- `bun run <script>` (never `npm run <script>`)
12- `bunx <bin>` (never `npx <bin>`)
13- `bun <file>.ts` to run a TS entrypoint directly (no `ts-node` / `tsx`)
14- `bun esbuild.mjs` to drive the build (esbuild/vite are still the bundlers)
15- `bun run --parallel ...` for parallel tasks
16
17The root `bun.lock` is the single lockfile for the whole workspace, including
18`apps/vscode`, `webview-ui`, and `testing-platform`. There are no per-package npm
19lockfiles.
20
21## Node is the runtime — do NOT rewrite these to bun
22
23The build product runs on Node: the VS Code extension host loads
24`dist/extension.js` as CommonJS under Node, and the standalone `cline-core` is a
25Node process. The following are Node runtime/ABI references and are correct as-is:
26
27| Reference | Why it is Node |
28|-----------|----------------|
29| esbuild `platform: "node"` / `target: "node..."` | The bundle targets the Node runtime (extension host, standalone core). |
30| `TARGET_NODE_VERSION` (`scripts/package-standalone.mjs`) | Pins the Node ABI of the bundled standalone runtime (matches the JetBrains-packaged Node). |
31| `prebuild-install --target=<node version>` | Downloads native `.node` binaries for that Node ABI. |
32| `NODE_PATH=... node cline-core.js` | The standalone core is launched by Node, not bun. |
33| `node:` import specifiers (e.g. `node:fs`) | Node builtin module scheme; unrelated to tooling. |
34| `process.versions.node`, `engines.node`, `@types/node` | Runtime version probe / declared runtime / its types. |
35| `ELECTRON_RUN_AS_NODE` | VS Code/Electron runs the extension host as Node. |
36
37When a file legitimately uses both bun and node (e.g. `package-standalone.mjs`
38does `bun install` but `prebuild-install --target=<node>`), the `node` token is
39the runtime/ABI target, not tooling. If unsure, leave it.
40
41## Tests: bun vs the VS Code host
42
43A test file's runner is decided by its import:
44
45- **`import ... from "bun:test"`** → runs under `bun test` (the node-side unit
46 suites + the SDK/model-catalog suites). `scripts/run-bun-unit-tests.ts`
47 discovers these by the `bun:test` import and runs one isolated bun process per
48 file. `build-tests.js` excludes them from the integration compile so the
49 `bun:test` builtin never reaches Node.
50- **`import ... from "mocha"`** → runs under `@vscode/test-cli` in a real VS Code
51 extension host (Node). These exercise the live `vscode` API and cannot run
52 under bun.
53
54So a file imports `bun:test` XOR `mocha`. Don't add `bun:test` to a test that
55needs the real extension host.
56
@@ −1 +1 @@
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.
1+# Bun (tooling) and Node (runtime)
22
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"
3+This repo uses **bun** for package management and task running, and **Node** as
4+the execution runtime. Both are correct at the same time; the distinction is the
5+source of most confusion, so keep it straight before editing scripts, configs,
6+docs, or comments.
107
11−**Proactively suggest additions** when any of the above happen—don't wait to be asked.
8+## Use bun for tooling
129
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.
10+- `bun install` (never `npm install` / `npm ci`)
11+- `bun run <script>` (never `npm run <script>`)
12+- `bunx <bin>` (never `npx <bin>`)
13+- `bun <file>.ts` to run a TS entrypoint directly (no `ts-node` / `tsx`)
14+- `bun esbuild.mjs` to drive the build (esbuild/vite are still the bundlers)
15+- `bun run --parallel ...` for parallel tasks
1416
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
17+The root `bun.lock` is the single lockfile for the whole workspace, including
18+`apps/vscode`, `webview-ui`, and `testing-platform`. There are no per-package npm
19+lockfiles.
2320
24−## Searching the Codebase — Avoiding Build Output
21+## Node is the runtime — do NOT rewrite these to bun
2522
26−Several directories contain build output or generated code that produces
27−noisy or unusable results with `search_files` / `grep`:
23+The build product runs on Node: the VS Code extension host loads
24+`dist/extension.js` as CommonJS under Node, and the standalone `cline-core` is a
25+Node process. The following are Node runtime/ABI references and are correct as-is:
2826
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 |
27+| Reference | Why it is Node |
28+|-----------|----------------|
29+| esbuild `platform: "node"` / `target: "node..."` | The bundle targets the Node runtime (extension host, standalone core). |
30+| `TARGET_NODE_VERSION` (`scripts/package-standalone.mjs`) | Pins the Node ABI of the bundled standalone runtime (matches the JetBrains-packaged Node). |
31+| `prebuild-install --target=<node version>` | Downloads native `.node` binaries for that Node ABI. |
32+| `NODE_PATH=... node cline-core.js` | The standalone core is launched by Node, not bun. |
33+| `node:` import specifiers (e.g. `node:fs`) | Node builtin module scheme; unrelated to tooling. |
34+| `process.versions.node`, `engines.node`, `@types/node` | Runtime version probe / declared runtime / its types. |
35+| `ELECTRON_RUN_AS_NODE` | VS Code/Electron runs the extension host as Node. |
3736
38−### How to skip build output
37+When a file legitimately uses both bun and node (e.g. `package-standalone.mjs`
38+does `bun install` but `prebuild-install --target=<node>`), the `node` token is
39+the runtime/ABI target, not tooling. If unsure, leave it.
3940
40−**`search_files`** — Point at `src/` (not the project root) and use `file_pattern`:
41−```
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"`.
41+## Tests: bun vs the VS Code host
4642
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−```
43+A test file's runner is decided by its import:
5144
52−### When you must search minified files
45+- **`import ... from "bun:test"`** → runs under `bun test` (the node-side unit
46+ suites + the SDK/model-catalog suites). `scripts/run-bun-unit-tests.ts`
47+ discovers these by the `bun:test` import and runs one isolated bun process per
48+ file. `build-tests.js` excludes them from the integration compile so the
49+ `bun:test` builtin never reaches Node.
50+- **`import ... from "mocha"`** → runs under `@vscode/test-cli` in a real VS Code
51+ extension host (Node). These exercise the live `vscode` API and cannot run
52+ under bun.
5353
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:
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
68−The 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
98−Adding a new key to global state requires updates in multiple places. Missing any step causes silent failures.
99−
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
104−
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.
106−
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")
130−```
131−
132−Use `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
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.
136−
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
143−
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−```
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−
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−
173−```
174−.../Visual Studio Code.app/Contents/MacOS/Code: bad option: --extensionDevelopmentPath=...
175−Error: Process failed to launch! (Playwright _electron.launch)
176−```
177−
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:
180−
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−```
188−
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−
54+So a file imports `bun:test` XOR `mocha`. Don't add `bun:test` to a test that
55+needs the real extension host.
20656
