| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 0 | 9 | 8 | 0% |
| Commands | 1 | 7 | 1 | 11% |
| Section tags | 3 | 3 | 2 | 38% |
What each file covers
Sections
0 shared · 9 only in A · 8 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
- + Cline Protobuf Development Guide
- + Overview
- + Key Concepts & Best Practices
- + 4-Step Development Workflow
- + 1. Define the RPC in a `.proto` File
- + 2. Compile Definitions
- + 3. Implement the Backend Handler
- + 4. Call the RPC from the Webview
Commands
1 shared · 7 only in A · 1 only in B- − 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
- + task.proto
- bun run protos
Section tags
3 shared · 3 only in A · 2 only in B- − setup
- − security
- − deployment
- + api
- + agent-behaviour
- build
- code-style
- architecture
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/protobuf-development.md
@@ +1 @@
1# Cline Protobuf Development Guide
2
3This guide outlines how to add new gRPC endpoints for communication between the webview (frontend) and the extension host (backend).
4
5## Overview
6
7Cline uses [Protobuf](https://protobuf.dev/) to define a strongly-typed API, ensuring efficient and type-safe communication. All definitions are in the `/proto` directory. The compiler and plugins are included as project dependencies, so no manual installation is needed.
8
9## Key Concepts & Best Practices
10
11- **File Structure**: Each feature domain should have its own `.proto` file (e.g., `account.proto`, `task.proto`).
12- **Message Design**:
13 - For simple, single-value data, use the shared types in `proto/common.proto` (e.g., `StringRequest`, `Empty`, `Int64Request`). This promotes consistency.
14 - For complex data structures, define custom messages within the feature's `.proto` file (see `task.proto` for examples like `NewTaskRequest`).
15- **Naming Conventions**:
16 - Services: `PascalCaseService` (e.g., `AccountService`).
17 - RPCs: `camelCase` (e.g., `accountEmailIdentified`).
18 - Messages: `PascalCase` (e.g., `StringRequest`).
19- **Streaming**: For server-to-client streaming, use the `stream` keyword on the response type. See `subscribeToAuthCallback` in `account.proto` for an example.
20
21---
22
23## 4-Step Development Workflow
24
25Here’s how to add a new RPC, using `scrollToSettings` as an example.
26
27### 1. Define the RPC in a `.proto` File
28
29Add your service method to the appropriate file in the `proto/` directory.
30
31**File: `proto/ui.proto`**
32```proto
33service UiService {
34 // ... other RPCs
35 // Scrolls to a specific settings section in the settings view
36 rpc scrollToSettings(StringRequest) returns (KeyValuePair);
37}
38```
39Here, we use the common `StringRequest` and `KeyValuePair` types.
40
41### 2. Compile Definitions
42
43After editing a `.proto` file, regenerate the TypeScript code. From the project root, run:
44```bash
45bun run protos
46```
47This command compiles all `.proto` files and outputs the generated code to `src/generated/` and `src/shared/`. Do not edit these generated files manually.
48
49### 3. Implement the Backend Handler
50
51Create the RPC implementation in the backend. Handlers are located in `src/core/controller/[service-name]/`.
52
53**File: `src/core/controller/ui/scrollToSettings.ts`**
54```typescript
55import { Controller } from ".."
56import { StringRequest, KeyValuePair } from "../../../shared/proto/common"
57
58/**
59 * Executes a scroll to settings action
60 * @param controller The controller instance
61 * @param request The request containing the ID of the settings section to scroll to
62 * @returns KeyValuePair with action and value fields for the UI to process
63 */
64export async function scrollToSettings(controller: Controller, request: StringRequest): Promise<KeyValuePair> {
65 return KeyValuePair.create({
66 key: "scrollToSettings",
67 value: request.value || "",
68 })
69}
70```
71
72### 4. Call the RPC from the Webview
73
74Call the new RPC from a React component in `webview-ui/`. The generated client makes this simple.
75
76**File: `webview-ui/src/components/browser/BrowserSettingsMenu.tsx`** (Example)
77```tsx
78import { UiServiceClient } from "../../../services/grpc"
79import { StringRequest } from "../../../../shared/proto/common"
80
81// ... inside a React component
82const handleMenuClick = async () => {
83 try {
84 await UiServiceClient.scrollToSettings(StringRequest.create({ value: "browser" }))
85 } catch (error) {
86 console.error("Error scrolling to browser settings:", error)
87 }
88}
89```
90
@@ −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+# Cline Protobuf Development Guide
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 guide outlines how to add new gRPC endpoints for communication between the webview (frontend) and the extension host (backend).
104
11−**Proactively suggest additions** when any of the above happen—don't wait to be asked.
5+## Overview
126
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.
7+Cline uses [Protobuf](https://protobuf.dev/) to define a strongly-typed API, ensuring efficient and type-safe communication. All definitions are in the `/proto` directory. The compiler and plugins are included as project dependencies, so no manual installation is needed.
148
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
9+## Key Concepts & Best Practices
2310
24−## Searching the Codebase — Avoiding Build Output
11+- **File Structure**: Each feature domain should have its own `.proto` file (e.g., `account.proto`, `task.proto`).
12+- **Message Design**:
13+ - For simple, single-value data, use the shared types in `proto/common.proto` (e.g., `StringRequest`, `Empty`, `Int64Request`). This promotes consistency.
14+ - For complex data structures, define custom messages within the feature's `.proto` file (see `task.proto` for examples like `NewTaskRequest`).
15+- **Naming Conventions**:
16+ - Services: `PascalCaseService` (e.g., `AccountService`).
17+ - RPCs: `camelCase` (e.g., `accountEmailIdentified`).
18+ - Messages: `PascalCase` (e.g., `StringRequest`).
19+- **Streaming**: For server-to-client streaming, use the `stream` keyword on the response type. See `subscribeToAuthCallback` in `account.proto` for an example.
2520
26−Several directories contain build output or generated code that produces
27−noisy or unusable results with `search_files` / `grep`:
21+---
2822
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 |
23+## 4-Step Development Workflow
3724
38−### How to skip build output
25+Here’s how to add a new RPC, using `scrollToSettings` as an example.
3926
40−**`search_files`** — Point at `src/` (not the project root) and use `file_pattern`:
27+### 1. Define the RPC in a `.proto` File
28+
29+Add your service method to the appropriate file in the `proto/` directory.
30+
31+**File: `proto/ui.proto`**
32+```proto
33+service UiService {
34+ // ... other RPCs
35+ // Scrolls to a specific settings section in the settings view
36+ rpc scrollToSettings(StringRequest) returns (KeyValuePair);
37+}
4138 ```
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"`.
39+Here, we use the common `StringRequest` and `KeyValuePair` types.
4640
47−**`grep` directly** — Exclude build dirs and restrict to source extensions:
41+### 2. Compile Definitions
42+
43+After editing a `.proto` file, regenerate the TypeScript code. From the project root, run:
4844 ```bash
49−grep -rn "myFunction" src/ --include="*.ts" --exclude-dir={out,dist,node_modules,generated}
45+bun run protos
5046 ```
47+This command compiles all `.proto` files and outputs the generated code to `src/generated/` and `src/shared/`. Do not edit these generated files manually.
5148
52−### When you must search minified files
49+### 3. Implement the Backend Handler
5350
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:
51+Create the RPC implementation in the backend. Handlers are located in `src/core/controller/[service-name]/`.
5752
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:
53+**File: `src/core/controller/ui/scrollToSettings.ts`**
12454 ```typescript
125−// Writing (normal pattern)
126−controller.stateManager.setGlobalState("myKey", value)
55+import { Controller } from ".."
56+import { StringRequest, KeyValuePair } from "../../../shared/proto/common"
12757
128−// Reading after initialization
129−const value = controller.stateManager.getGlobalStateKey("myKey")
58+/**
59+ * Executes a scroll to settings action
60+ * @param controller The controller instance
61+ * @param request The request containing the ID of the settings section to scroll to
62+ * @returns KeyValuePair with action and value fields for the UI to process
63+ */
64+export async function scrollToSettings(controller: Controller, request: StringRequest): Promise<KeyValuePair> {
65+ return KeyValuePair.create({
66+ key: "scrollToSettings",
67+ value: request.value || "",
68+ })
69+}
13070 ```
13171
132−Use `context.globalState` only in VS Code migration code that copies legacy ExtensionContext values into the shared file-backed stores.
72+### 4. Call the RPC from the Webview
13373
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.
74+Call the new RPC from a React component in `webview-ui/`. The generated client makes this simple.
13675
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`:**
76+**File: `webview-ui/src/components/browser/BrowserSettingsMenu.tsx`** (Example)
14577 ```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−```
78+import { UiServiceClient } from "../../../services/grpc"
79+import { StringRequest } from "../../../../shared/proto/common"
15380
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−
81+// ... inside a React component
82+const handleMenuClick = async () => {
83+ try {
84+ await UiServiceClient.scrollToSettings(StringRequest.create({ value: "browser" }))
85+ } catch (error) {
86+ console.error("Error scrolling to browser settings:", error)
87+ }
88+}
17389 ```
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−
20690
