RuleStack

Configs

Stacks

Compare

Diff

RuleStack

Configs

Stacks

Compare

Diff

Read API

RuleStack

Configs

Stacks

Compare

Diff

Read API

Diff/cline-cline-clinerules-general ↔ cline-cline-clinerules-debug-harness

Comparison

A · Cline rules · cline/clineB · Cline rules · cline/cline
What each file covers, counted
DimensionSharedOnly in AOnly in BOverlap
Sections09190%
Commands0830%
Section tags24322%

What each file covers

Sections

0 shared · 9 only in A · 19 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
  • + 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

Commands

0 shared · 8 only in A · 3 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
  • − bun run protos
  • + 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

Section tags

2 shared · 4 only in A · 3 only in B
  • − setup
  • − code-style
  • − architecture
  • − deployment
  • + test
  • + testing-strategy
  • + api
  •   build
  •   security

Line diff

+95 added−171 removed35 unchanged17.0% identical
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/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 
@@ −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+# Debug Harness
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+HTTP-controlled debugger for the VSCode extension at `src/dev/debug-harness/server.ts`.
104  
11−**Proactively suggest additions** when any of the above happen—don't wait to be asked.
5+## Quick start
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+```bash
8+# Build extension first if needed (protos + esbuild):
9+bun run protos && IS_DEV=true bun esbuild.mjs
1410  
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
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
2314  
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`:
15+# In another terminal:
16+curl localhost:19229/api -d '{"method":"status"}'
4117 ```
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"`.
4618  
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−```
19+## Data Isolation
5120  
52−### When you must search minified files
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`.
5324  
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:
25+## Browser Capture & OAuth
5726  
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.
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:
6629  
67−## gRPC/Protobuf Communication
68−The extension and webview communicate via gRPC-like protocol over VS Code message passing.
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`
6933  
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`)
34+### OAuth API
7635  
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
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
8240  
83−**Adding new enum values** (like a new `ClineSay` type) requires updating conversion mappings in `src/shared/proto-conversions/cline-message.ts`
41+### OAuth testing flow
8442  
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" }))`
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=...`.
8846  
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
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`).
9656  
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.
57+## Navigating Views — Use Commands, Not Clicks
9958  
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
59+Don't try to find/click small sidebar icons. Use VSCode commands via command palette.
60+Registered in `src/registry.ts`:
10461  
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.
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 |
10670  
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")
71+```bash
72+curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.accountButtonClicked"}}'
13073 ```
13174  
132−Use `context.globalState` only in VS Code migration code that copies legacy ExtensionContext values into the shared file-backed stores.
75+## Key commands
13376  
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.
77+All via `POST localhost:19229/api` with `{"method":"...", "params":{...}}`:
13678  
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
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
14393  
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−```
94+## Typical Session
15395  
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"
96+```bash
97+# 1. Launch
98+curl localhost:19229/api -d '{"method":"launch","params":{"skipBuild":true}}'
15799  
158−**See also:** `BrowserSessionRow.tsx` uses similar pattern with `isLastApiReqInterrupted` and `isLastMessageResume`.
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())"}}'
159103  
160−**Backend side:** When streaming is cancelled, clean up properly (close tabs, clear comments, etc.) by checking `taskState.abort` after the streaming function returns.
104+# 3. Navigate to view
105+curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.accountButtonClicked"}}'
161106  
162−## Debug Harness: clear inherited VSCode/Electron env vars before launching
107+# 4. Check captured OAuth URLs if testing auth
108+curl localhost:19229/api -d '{"method":"oauth.captured_urls"}'
163109  
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− 
110+# 5. Verify
111+curl localhost:19229/api -d '{"method":"ui.screenshot"}'
173112 ```
174−.../Visual Studio Code.app/Contents/MacOS/Code: bad option: --extensionDevelopmentPath=...
175−Error: Process failed to launch! (Playwright _electron.launch)
176−```
177113  
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:
114+## Caveats
180115  
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−```
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.
188128  
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− 
129+See `src/dev/debug-harness/README.md` for full API reference.
206130  
RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack