Two files, one repository
microsoft/vscode ships 2 formats across 38 indexed files. The question worth asking is whether the second one says anything the first does not.
| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 0 | 1 | 45 | 0% |
| Commands | 0 | 0 | 11 | 0% |
| Section tags | 1 | 0 | 11 | 8% |
What each file covers
Sections
0 shared · 1 only in A · 45 only in B- − VS Code Agents Instructions
- + Claude Code Integration
- + Official Claude Agent SDK Documentation
- + Core SDK Features
- + Overview
- + Architecture
- + Key Components
- + `node/claudeCodeAgent.ts`
- + `node/claudeCodeSdkService.ts`
- + `node/sessionParser/claudeCodeSessionService.ts`
- + `node/sessionParser/sdkSessionAdapter.ts`
- + `node/claudeSkills.ts`
- + `common/claudeTools.ts`
- + `common/toolInvocationFormatter.ts`
- + `../../chatSessions/vscode-node/chatHistoryBuilder.ts`
- + Message Flow
- + Tool Confirmation
- + Session Persistence
- + Folder and Working Directory Management
- + `ClaudeFolderInfo` (`common/claudeFolderInfo.ts`)
- + Folder Resolution by Workspace Type
- + Data Flow
- + Folder Picker UI
- + Session Discovery Across Folders
- + Key Files
- + Input State Reactive Pipeline
- + Key Types
- + Seeding: Extracting Initial Values
- + Shared vs. Per-State Observables
- + Derived Computation and Autorun
- + Lifetime Management (onDidDispose)
- + External Permission Mode Updates
- + Session-Started Signal
- + Critical Invariant: Subscribe After Both Branches
- + Session Metadata and Git Commands
- + Session Metadata Enrichment
- + Git Action Commands
- + Testing
- + Extension Registries
- + Hook Registry
- + Slash Command Registry
- + Tool Permission Handlers
- + MCP Server Registry
- + Configuration
- + Upgrading Anthropic SDK Packages
- + Dependencies
Commands
0 shared · 0 only in A · 11 only in B- + node/claudeCodeAgent.ts
- + node/claudeCodeSdkService.ts
- + node/sessionParser/claudeCodeSessionService.ts
- + node/sessionParser/README.md
- + node/sessionParser/sdkSessionAdapter.ts
- + node/claudeSkills.ts
- + node/test/
- + node/hooks/claudeHookRegistry.ts
- + node/toolPermissionHandlers/
- + node/mcpServers/index.ts
- + node/hooks/index.ts
Section tags
1 shared · 0 only in A · 11 only in B- + test
- + code-style
- + architecture
- + types
- + testing-strategy
- + git-pr
- + dependencies
- + ui
- + deployment
- + monorepo
- + docs
- agent-behaviour
Line diff
microsoft/vscode · AGENTS.md
@@ −1 @@
1# VS Code Agents Instructions
2
3This file provides instructions for AI coding agents working with the VS Code codebase.
4
5For detailed project overview, architecture, coding guidelines, and validation steps, see the [Copilot Instructions](.github/copilot-instructions.md).
6
microsoft/vscode · extensions/copilot/src/extension/chatSessions/claude/AGENTS.md
@@ +1 @@
1# Claude Code Integration
2
3This folder contains the Claude Code integration for VS Code Chat. It enables users to open a new Chat window and interact with a Claude Code instance directly within VS Code. **VS Code provides the UI, Claude Code provides the smarts.**
4
5> 📖 **New to the Claude session target?** See the **[User Guide](./CLAUDE_SESSION_USER_GUIDE.md)** for a comprehensive walkthrough of features, slash commands, permission modes, and best practices.
6
7## Official Claude Agent SDK Documentation
8
9> **Important:** For the most up-to-date information on the Claude Agent SDK, always refer to the official documentation:
10>
11> - **[Agent SDK Overview](https://platform.claude.com/docs/en/agent-sdk/overview)** - General SDK concepts, capabilities, and getting started guide
12> - **[Agent SDK Quickstart](https://platform.claude.com/docs/en/agent-sdk/quickstart)** - Step-by-step guide to building your first agent
13> - **[TypeScript SDK Reference](https://platform.claude.com/docs/en/agent-sdk/typescript)** - Complete API reference for the TypeScript SDK including all functions, types, and interfaces
14> - **[TypeScript V2 Preview](https://platform.claude.com/docs/en/agent-sdk/typescript-v2-preview)** - Preview of the simplified V2 interface with session-based send/stream patterns
15>
16> The SDK package is `@anthropic-ai/claude-agent-sdk`. The official documentation covers tools, hooks, subagents, MCP integration, permissions, sessions, and more.
17
18### Core SDK Features
19
20**Getting Started:**
21- [Overview](https://platform.claude.com/docs/en/agent-sdk/overview) - Learn about the Agent SDK architecture and core concepts
22- [Quickstart](https://platform.claude.com/docs/en/agent-sdk/quickstart) - Get up and running with your first agent in minutes
23
24**Core SDK Implementation:**
25- [TypeScript](https://platform.claude.com/docs/en/agent-sdk/typescript) - Main TypeScript SDK reference for building agents
26- [TypeScript v2 Preview](https://platform.claude.com/docs/en/agent-sdk/typescript-v2-preview) - Preview of upcoming v2 API with enhanced features
27- [Streaming vs Single Mode](https://platform.claude.com/docs/en/agent-sdk/streaming-vs-single-mode) - Choose between streaming responses or single-turn completions
28
29**User Interaction & Control:**
30- [Permissions](https://platform.claude.com/docs/en/agent-sdk/permissions) - Control what actions Claude can take with user approval flows
31- [User Input](https://platform.claude.com/docs/en/agent-sdk/user-input) - Collect clarifications and decisions from users during execution
32- [Hooks](https://platform.claude.com/docs/en/agent-sdk/hooks) - Execute custom logic at key points in the agent lifecycle
33
34**State & Session Management:**
35- [Sessions](https://platform.claude.com/docs/en/agent-sdk/sessions) - Manage conversation history and context across interactions
36- [File Checkpointing](https://platform.claude.com/docs/en/agent-sdk/file-checkpointing) - Save and restore file states for undo/redo functionality
37
38**Advanced Features:**
39- [Structured Outputs](https://platform.claude.com/docs/en/agent-sdk/structured-outputs) - Get reliable JSON responses with schema validation
40- [Modifying System Prompts](https://platform.claude.com/docs/en/agent-sdk/modifying-system-prompts) - Customize Claude's behavior and instructions
41- [MCP](https://platform.claude.com/docs/en/agent-sdk/mcp) - Connect to Model Context Protocol servers for extended capabilities
42- [Custom Tools](https://platform.claude.com/docs/en/agent-sdk/custom-tools) - Build your own tools to extend Claude's functionality
43
44**Agent Composition & UX:**
45- [Subagents](https://platform.claude.com/docs/en/agent-sdk/subagents) - Compose complex workflows by delegating to specialized agents
46- [Slash Commands](https://platform.claude.com/docs/en/agent-sdk/slash-commands) - Add custom `/commands` for quick actions
47- [Skills](https://platform.claude.com/docs/en/agent-sdk/skills) - Package reusable agent capabilities as installable modules
48- [Todo Tracking](https://platform.claude.com/docs/en/agent-sdk/todo-tracking) - Help Claude manage and display task progress
49- [Plugins](https://platform.claude.com/docs/en/agent-sdk/plugins) - Extend the SDK with community-built integrations
50
51## Overview
52
53The Claude Code integration allows VS Code's chat interface to communicate with Claude Code, Anthropic's agentic coding assistant. When a user sends a message in a VS Code Chat window using this integration, the message is routed to a Claude Code session that can:
54
55- Read and analyze code
56- Execute shell commands
57- Edit files
58- Search the workspace
59- Manage tasks and todos
60
61All interactions are displayed through VS Code's native chat UI, providing a seamless experience.
62
63## Architecture
64
65```
66┌─────────────────────────────────────────────────────────────────┐
67│ VS Code Chat UI │
68└─────────────────────────┬───────────────────────────────────────┘
69 │
70 ▼
71┌─────────────────────────────────────────────────────────────────┐
72│ ClaudeAgentManager │
73│ - Manages language model server lifecycle │
74│ - Routes requests to appropriate sessions │
75│ - Resolves prompts with file references │
76└─────────────────────────┬───────────────────────────────────────┘
77 │
78 ▼
79┌─────────────────────────────────────────────────────────────────┐
80│ ClaudeCodeSession │
81│ - Maintains a single Claude Code conversation │
82│ - Processes messages (assistant, user, result) │
83│ - Handles tool invocation and confirmation │
84│ - Queues multiple requests for sequential processing │
85└─────────────────────────┬───────────────────────────────────────┘
86 │
87 ▼
88┌─────────────────────────────────────────────────────────────────┐
89│ Claude Code SDK (@anthropic-ai) │
90│ - Communicates with Claude Code │
91│ - Manages tool hooks (pre/post tool use) │
92│ - Handles message streaming │
93└─────────────────────────────────────────────────────────────────┘
94```
95
96## Key Components
97
98### `node/claudeCodeAgent.ts`
99
100**ClaudeAgentManager**
101- Entry point for handling chat requests from VS Code
102- Starts and manages the language model server (`LanguageModelServer`)
103- Creates and caches `ClaudeCodeSession` instances by session ID
104- Resolves prompts by replacing VS Code references (files, locations) with actual paths
105
106**ClaudeCodeSession**
107- Represents a single Claude Code conversation session
108- Manages a queue of incoming requests from VS Code Chat
109- Uses an async iterable to feed prompts to Claude Code SDK
110- Processes three message types:
111 - **Assistant messages**: Text responses and tool use requests
112 - **User messages**: Tool results from executed tools
113 - **Result messages**: Session completion or error states
114- Handles tool confirmation dialogs via VS Code's chat API
115- Auto-approves safe operations (file edits in workspace)
116- Tracks external edits to show proper diffs
117
118### `node/claudeCodeSdkService.ts`
119
120**IClaudeCodeSdkService / ClaudeCodeSdkService**
121- Thin wrapper around the `@anthropic-ai/claude-agent-sdk`
122- Provides dependency injection for testability
123- Enables mocking in unit tests
124
125### `node/sessionParser/claudeCodeSessionService.ts`
126
127**IClaudeCodeSessionService / ClaudeCodeSessionService**
128- Loads and manages persisted Claude Code sessions from disk
129- Reads `.jsonl` session files from `~/.claude/projects/<workspace-slug>/`
130- Builds message chains from leaf nodes to reconstruct full conversations
131- Loads subagent sessions via SDK APIs (`listSubagents` + `getSubagentMessages`) and correlates them with their spawning tool use via `parent_tool_use_id` (stored as `ISubagentSession.parentToolUseId`)
132- Provides session caching with mtime-based invalidation
133- Used to resume previous Claude Code conversations
134- See `node/sessionParser/README.md` for detailed documentation
135
136### `node/sessionParser/sdkSessionAdapter.ts`
137
138Adapts raw SDK session data into the internal `IClaudeCodeSession` / `ISubagentSession` schemas:
139- **`buildClaudeCodeSession()`**: Assembles a full `IClaudeCodeSession` from session info, messages, and subagents
140- **`sdkSubagentMessagesToSubagentSession()`**: Converts raw SDK `SessionMessage[]` into an `ISubagentSession`
141- **`extractParentToolUseId()`**: Helper that scans a `SessionMessage[]` array until it finds a string `parent_tool_use_id`, used to correlate a subagent session with the Agent/Task tool_use block that spawned it
142
143### `node/claudeSkills.ts`
144
145**IClaudePluginService / ClaudePluginService**
146- Resolves plugin root directories for the Claude SDK's `plugins` option
147- Combines three sources of plugin locations:
148 1. **Config skill locations** — from `chat.agentSkillsLocations` setting, resolved via the shared `resolveSkillConfigLocations()` utility. These point to skills directories (e.g. `.../skills/`), so the service walks **one level up** to reach the plugin root expected by the SDK.
149 2. **Discovered skills** — from `IPromptsService.getSkills()`. Each skill has a `SKILL.md` at `<plugin-root>/skills/<skill-name>/SKILL.md`, so the service walks **three levels up** (`dirname(dirname(dirname(uri)))`) to reach the plugin root.
150 3. **Direct plugins** — from `IPromptsService.getPlugins()`, returned as-is since they already point to plugin root directories.
151- Filters out `.claude` directories (the Claude SDK loads these automatically)
152- Deduplicates results using `ResourceSet`
153- Plugin roots are passed to the SDK as `SdkPluginConfig[]` with `{ type: 'local', path }` in `ClaudeCodeSession._doStartSession()`
154
155**Shared utility:** `../../common/skillConfigLocations.ts` — `resolveSkillConfigLocations()` handles `~/` expansion, absolute paths, and relative paths joined to workspace folders. Used by both `ClaudePluginService` and `CopilotCLISkills`.
156
157### `common/claudeTools.ts`
158
159Defines Claude Code's tool interface:
160- **ClaudeToolNames**: Enum of all supported tool names (Bash, Read, Edit, Write, etc.). `Agent` is the current name (SDK v2.1.63+); `Task` is kept for backward compatibility with older sessions.
161- **Tool input interfaces**: Type definitions for each tool's input parameters
162- **claudeEditTools**: List of tools that modify files (Edit, MultiEdit, Write, NotebookEdit)
163- **getAffectedUrisForEditTool**: Extracts file URIs that will be modified by edit operations
164
165### `common/toolInvocationFormatter.ts`
166
167Formats tool invocations for display in VS Code's chat UI:
168- Creates `ChatToolInvocationPart` instances with appropriate messaging
169- Handles tool-specific formatting (Bash commands, file reads, searches, etc.)
170- Suppresses certain tools from display (TodoWrite, Edit, Write) where other UI handles them
171
172### `../../chatSessions/vscode-node/chatHistoryBuilder.ts`
173
174Converts a persisted `IClaudeCodeSession` into VS Code `ChatResponsePart[]` for replay in the chat UI:
175- Reconstructs assistant text, thinking blocks, tool invocations, and tool results into chat response parts
176- Matches subagent sessions to their spawning Agent/Task tool_use blocks using `ISubagentSession.parentToolUseId`, injecting the subagent's tool calls inline under the parent tool invocation
177
178## Message Flow
179
1801. **User sends message** in VS Code Chat
1812. **ClaudeAgentManager** receives the request and routes to existing or new session
1823. **ClaudeCodeSession** queues the request and feeds the prompt to Claude Code SDK
1834. **Claude Code SDK** returns streaming messages:
184 - Text content → rendered as markdown in chat
185 - Tool use requests → shown as progress, then confirmed via VS Code's confirmation API
186 - Tool results → formatted and displayed in chat
1875. **Result message** signals turn completion, request is resolved
188
189## Tool Confirmation
190
191Claude Code tools require user confirmation before execution:
192- **Auto-approved**: File edits (Edit, Write, MultiEdit) are auto-approved if the file is within the workspace
193- **Manual confirmation**: All other tools show a confirmation dialog via `CoreConfirmationTool`
194- **Denied tools**: User denial sends a "user declined" message back to Claude Code
195
196## Session Persistence
197
198Claude Code sessions are persisted to `~/.claude/projects/<workspace-slug>/` as `.jsonl` files. The `ClaudeCodeSessionService` can:
199- Load all sessions for the current workspace
200- Resume a previous session by ID
201- Cache sessions with mtime-based invalidation
202
203## Folder and Working Directory Management
204
205The integration deterministically resolves the working directory (`cwd`) and additional directories for each Claude session, rather than inheriting from `process.cwd()`. This is managed by the `ClaudeChatSessionContentProvider` and exposed through the `ClaudeFolderInfo` interface.
206
207### `ClaudeFolderInfo` (`common/claudeFolderInfo.ts`)
208
209```typescript
210interface ClaudeFolderInfo {
211 readonly cwd: string; // Primary working directory
212 readonly additionalDirectories: string[]; // Extra directories Claude can access
213}
214```
215
216### Folder Resolution by Workspace Type
217
218| Workspace Type | cwd | additionalDirectories | Folder Picker |
219|---|---|---|---|
220| **Single-root** (1 folder) | That folder | `[]` | Hidden |
221| **Multi-root** (2+ folders) | Selected folder (default: first) | All other workspace folders | Shown with workspace folders |
222| **Empty** (0 folders) | Selected MRU folder | `[]` | Shown with MRU entries |
223
224### Data Flow
225
2261. **`ClaudeChatSessionItemController`** resolves `ClaudeFolderInfo` via `getFolderInfoForSession(sessionId)`
2272. The folder info is passed through `ClaudeAgentManager.handleRequest()` to `ClaudeCodeSession`
2283. `ClaudeCodeSession._startSession()` uses `folderInfo.cwd` and `folderInfo.additionalDirectories` when building SDK `Options`
229
230### Folder Picker UI
231
232In multi-root and empty workspaces, a folder picker option appears in the chat session options:
233- **Multi-root**: Lists all workspace folders; selecting one makes it `cwd`, the rest become `additionalDirectories`
234- **Empty workspace**: Lists MRU folders from `IFolderRepositoryManager` (max 10 entries)
235- The folder option is **locked** for existing (non-untitled) sessions to prevent cwd changes mid-conversation
236
237### Session Discovery Across Folders
238
239`ClaudeCodeSessionService._getProjectSlugs()` generates workspace slugs for **all** workspace folders, enabling session discovery across all project directories in multi-root workspaces. For empty workspaces, it generates slugs for all folders known to `IFolderRepositoryManager` (MRU entries).
240
241### Key Files
242
243- **`common/claudeFolderInfo.ts`**: `ClaudeFolderInfo` interface
244- **`../../chatSessions/common/claudeWorkspaceFolderService.ts`**: `IClaudeWorkspaceFolderService` interface — computes git diff changes for session items
245- **`../../chatSessions/vscode-node/claudeWorkspaceFolderServiceImpl.ts`**: Implementation — diffs the session's branch against its base branch, caches results, and maps changes to `ChatSessionChangedFile[]` for display in the Sessions view
246- **`../../chatSessions/vscode-node/claudeChatSessionContentProvider.ts`**: Folder resolution, picker options, session metadata enrichment, and git command handlers
247- **`../../chatSessions/common/builtinSlashCommands.ts`**: Shared constants for built-in slash commands (`/commit`, `/sync`, `/merge`, etc.) used by both Claude and CopilotCLI sessions
248- **`../../chatSessions/vscode-node/folderRepositoryManagerImpl.ts`**: `FolderRepositoryManager` (abstract base) with `ClaudeFolderRepositoryManager` subclass — the Claude subclass does not depend on `ICopilotCLISessionService` (CopilotCLI has its own subclass `CopilotCLIFolderRepositoryManager`)
249- **`node/claudeCodeAgent.ts`**: Consumes `ClaudeFolderInfo` in `ClaudeCodeSession._startSession()`
250- **`node/sessionParser/claudeCodeSessionService.ts`**: `_getProjectSlugs()` generates slugs for all folders
251
252## Input State Reactive Pipeline
253
254The chat session input controls (permission mode picker, folder picker) are driven by a reactive observable pipeline, not by imperative setter calls. Understanding this pipeline is important when modifying input state behavior.
255
256### Overview
257
258VS Code calls `getChatSessionInputState` to get a `ChatSessionInputState` object whose `.groups` array drives the UI. Rather than computing groups once and returning them, the pipeline keeps `groups` live: shared observables push changes into each state object whenever relevant configuration changes.
259
260### Key Types
261
262```
263InputStateReactivePipeline {
264 permissionMode: ISettableObservable<PermissionMode>
265 folderUri: ISettableObservable<URI | undefined>
266 folderItems: ISettableObservable<readonly vscode.ChatSessionProviderOptionItem[]>
267 isSessionStarted: ISettableObservable<boolean>
268 store: DisposableStore // owns all autoruns for this pipeline
269}
270```
271
272### Seeding: Extracting Initial Values
273
274Before attaching any autoruns, `_createInputStateReactivePipeline` calls `_computeSeedValues(state.groups)` to extract the current groups into typed values. This must happen *before* the first autorun runs, because the first autorun pass immediately reads `allGroups` and writes to `state.groups` — if the per-state observables were left at defaults, that write would discard the carefully-constructed initial groups.
275
276`_computeSeedValues` extracts four values:
277
278| Value | Source | Fallback |
279|---|---|---|
280| `permissionMode` | Selected item id in the `permissionMode` group | `lastUsedPermissionMode` |
281| `folderUri` | Selected item id in the `folder` group | `undefined` |
282| `folderItems` | Full item list of the `folder` group | `[]` |
283| `isSessionStarted` | `locked: true` on any folder item or the selected item | `false` |
284
285The `isSessionStarted` recovery from `locked` items is important for the `previousInputState` path: the previous state's groups encode the lock signal via `locked: true` on their items. If `_computeSeedValues` did not recover this, the pipeline would start with `isSessionStarted = false` and the `folderGroup` derived would re-render all items as unlocked.
286
287### Shared vs. Per-State Observables
288
289`ClaudeChatSessionItemController` holds two **shared** observables (one instance per controller, not per session):
290
291| Observable | Source | Purpose |
292|---|---|---|
293| `_bypassPermissionsEnabled` | `IConfigurationService` event | Controls which permission mode items are available |
294| `_workspaceFolders` | `IWorkspaceService` event | Controls folder picker items and visibility |
295
296Each call to `getChatSessionInputState` creates a **per-state** pipeline with `_createInputStateReactivePipeline(state)`. The per-state observables are seeded via `_computeSeedValues`.
297
298`folderItems` is a settable per-state observable (not a pure `derived`) because of an async edge case: when the workspace has no folders, the items come from an async MRU fetch (`IFolderRepositoryManager`). An autorun watches `_workspaceFolders` and updates `folderItems` synchronously when folders exist, or kicks off the async MRU fetch when the workspace is empty.
299
300### Derived Computation and Autorun
301
302Inside `_createInputStateReactivePipeline`, `derived` observables combine shared and per-state inputs:
303
304```
305permissionModeGroup = derived(bypassEnabled, permissionMode)
306folderGroup = derived(folderItems, workspaceFolders, folderUri, isSessionStarted)
307allGroups = derived(permissionModeGroup, folderGroup)
308```
309
310An `autorun` reads `allGroups` and writes to `state.groups`. This is the only place `state.groups` is written — the pipeline is the single source of truth for the UI.
311
312### Lifetime Management (onDidDispose)
313
314Each pipeline's `store` is disposed via `state.onDidDispose`:
315
316```typescript
317pipeline.store.add(state.onDidDispose(() => pipeline.store.dispose()));
318```
319
320When VS Code discards a `ChatSessionInputState`, the `onDidDispose` event fires and deterministically cleans up all autoruns for that state. The `onDidDispose` subscription is itself registered on the pipeline store, so it is cleaned up as part of disposal.
321
322### External Permission Mode Updates
323
324When Claude executes `EnterPlanMode` or `ExitPlanMode` tools, `claudeMessageDispatch.ts` calls `IClaudeSessionStateService.setPermissionModeForSession()`, which fires `onDidChangeSessionState`. The pipeline subscribes to this event via a second autorun:
325
326```typescript
327const externalPermissionMode = observableFromEvent(
328 this,
329 Event.filter(sessionStateService.onDidChangeSessionState,
330 e => e.sessionId === sessionId && e.permissionMode !== undefined),
331 () => sessionStateService.getPermissionModeForSession(sessionId),
332);
333pipeline.store.add(autorun(reader => {
334 pipeline.permissionMode.set(externalPermissionMode.read(reader), undefined);
335}));
336```
337
338This autorun is registered on `pipeline.store`, so it is disposed along with all other pipeline autoruns when the state is disposed.
339
340### Session-Started Signal
341
342The `isSessionStarted` observable controls whether folder items carry `locked: true`. It is set to `true` when `getChatSessionInputState` is called with a `sessionResource` — i.e., whenever VS Code provides a resource for the session. This covers both existing on-disk sessions and sessions that have been started (where a resource has been assigned).
343
344For the `previousInputState` path, the lock state is recovered from the items themselves: `_computeSeedValues` checks for `locked: true` on folder items and restores `isSessionStarted` accordingly.
345
346### Critical Invariant: Subscribe After Both Branches
347
348`_setupInputState` creates `state` and `pipeline` in one of two branches:
349- **`context.previousInputState` path** — VS Code already has a state for this session and is asking for a fresh one; seed from the old groups.
350- **New-state path** — first call for this session; fetch groups from disk or defaults.
351
352**The external permission mode subscription must run after both branches.** If it only runs in the new-state path, permission mode changes from `EnterPlanMode`/`ExitPlanMode` are silently dropped for every session after the first `getChatSessionInputState` call. Guard against this regression by ensuring the subscription is placed outside the `if/else` block.
353
354## Session Metadata and Git Commands
355
356### Session Metadata Enrichment
357
358Each Claude session item carries metadata that drives the Sessions view UI (button visibility, status indicators). The `ClaudeChatSessionItemController._buildSessionMetadata()` method enriches session items with git repository state.
359
360**Workspace Trust:** Session metadata and git change detection are gated on workspace trust via `IWorkspaceService.isResourceTrusted()`. For untrusted working directories, `_buildSessionMetadata()` returns only the `workingDirectoryPath` (no git data), and `getWorkspaceChanges()` is skipped entirely. The trust check is resolved once in `_createClaudeChatSessionItem` and passed into `_buildSessionMetadata` to avoid redundant calls. When trusted, the metadata fetch and workspace changes fetch run concurrently via `Promise.all`.
361
362| Field | Type | Description |
363|-------|------|-------------|
364| `workingDirectoryPath` | `string` | Session's working directory (always present) |
365| `repositoryPath` | `string?` | Git repository root path |
366| `branchName` | `string?` | Current HEAD branch name |
367| `upstreamBranchName` | `string?` | Upstream tracking ref (e.g., `origin/main`) |
368| `hasGitHubRemote` | `boolean?` | Whether any remote points to GitHub |
369| `incomingChanges` | `number?` | Commits behind upstream |
370| `outgoingChanges` | `number?` | Commits ahead of upstream |
371| `uncommittedChanges` | `number?` | Total uncommitted changes (merge + index + working tree + untracked) |
372
373These metadata fields map to `when`-clause context keys in `package.json` (e.g., `sessions.hasGitRepository`, `sessions.hasUncommittedChanges`, `sessions.hasUpstream`) that control which action buttons appear in the Changes view.
374
375### Git Action Commands
376
377The `ClaudeChatSessionItemController` registers four git-related commands that appear as action buttons in the Sessions/Changes view:
378
379| Command | When Visible | Action |
380|---------|-------------|--------|
381| `github.copilot.claude.sessions.commit` | Has git repo + uncommitted changes | Sends `/commit` prompt to the session |
382| `github.copilot.claude.sessions.commitAndSync` | Has git repo + uncommitted changes + upstream | Sends `/commit and /sync` prompt |
383| `github.copilot.claude.sessions.sync` | Has git repo + no uncommitted changes + upstream | Sends `/sync` prompt |
384| `github.copilot.claude.sessions.initializeRepository` | No git repo | Calls `IGitService.initRepository()` on the session's workspace folder |
385
386The commit, commitAndSync, and sync commands use a shared `_registerPromptCommand()` helper that extracts the session resource and dispatches via `workbench.action.chat.openSessionWithPrompt.claude-code`. The slash command strings come from the shared `builtinSlashCommands` module (`../../common/builtinSlashCommands.ts`).
387
388## Testing
389
390Unit tests are located in `node/test/`:
391- `claudeCodeAgent.spec.ts`: Tests for agent and session logic
392- `claudeCodeSessionService.spec.ts`: Tests for session loading and persistence
393- `claudePluginService.spec.ts`: Tests for plugin location resolution
394- `mockClaudeCodeSdkService.ts`: Mock SDK service for testing
395- `fixtures/`: Sample `.jsonl` session files for testing
396
397Additional tests for the session item controller and content provider:
398- `../../chatSessions/vscode-node/test/claudeChatSessionContentProvider.spec.ts`: Tests for session metadata enrichment, git command handlers, session lifecycle, and content provider behavior
399
400## Extension Registries
401
402The Claude integration uses several registries to organize and manage extensibility points:
403
404### Hook Registry
405
406**Location:** `node/hooks/claudeHookRegistry.ts`
407
408The hook registry allows registering custom hooks that execute at key points in the agent lifecycle. Hooks are organized by `HookEvent` type from the Claude SDK.
409
410**Key Features:**
411- Register handlers using `registerClaudeHook(hookEvent, ctor)`
412- Handlers are constructed via dependency injection using `IInstantiationService`
413- Hook instances are built from the registry and passed to the Claude SDK
414- Multiple handlers can be registered for the same event
415
416**Example Hook Events:**
417- `'PreToolUse'` - Before a tool is executed
418- `'PostToolUse'` - After a tool completes
419- `'SubagentStart'` - When a subagent starts
420- `'SubagentEnd'` - When a subagent completes
421- `'SessionStart'` - When a session begins
422- `'SessionEnd'` - When a session ends
423
424**Current Hook Handlers:**
425- `loggingHooks.ts` - Logging hooks for debugging and telemetry
426- `sessionHooks.ts` - Session lifecycle management
427- `subagentHooks.ts` - Subagent lifecycle tracking
428- `toolHooks.ts` - Tool execution tracking and processing
429
430### Slash Command Registry
431
432**Location:** `vscode-node/slashCommands/claudeSlashCommandRegistry.ts`
433
434The slash command registry manages custom slash commands available in Claude chat sessions. Commands allow users to trigger specific functionality via `/commandname` syntax.
435
436**Key Features:**
437- Register handlers using `registerClaudeSlashCommand(handler)`
438- Each handler implements `IClaudeSlashCommandHandler` interface
439- Commands can optionally register with VS Code Command Palette
440- Handlers receive arguments, response stream, and cancellation token
441
442**Handler Interface:**
443```typescript
444interface IClaudeSlashCommandHandler {
445 readonly commandName: string; // Command name (without /)
446 readonly description: string; // Human-readable description
447 readonly commandId?: string; // Optional VS Code command ID
448 handle(args: string, stream: ChatResponseStream | undefined, token: CancellationToken): Promise<ChatResult | void>;
449}
450```
451
452**UI Patterns for Slash Commands:**
453
454Slash commands often need to present choices or gather input from users. When doing so, prefer the simpler one-shot APIs over the more complex builder APIs:
455
456- **Prefer:** `vscode.window.showQuickPick()` - Simple function call that returns the selected item(s)
457- **Avoid:** `vscode.window.createQuickPick()` - More complex, requires manual lifecycle management
458
459- **Prefer:** `vscode.window.showInputBox()` - Simple function call that returns the entered text
460- **Avoid:** `vscode.window.createInputBox()` - More complex, requires manual lifecycle management
461
462The `show*` APIs are sufficient for most slash command use cases and result in cleaner, more maintainable code. Only use `create*` APIs when you need advanced features like dynamic item updates, multi-step wizards, or custom event handling.
463
464**Current Slash Commands:**
465- `/hooks` - Configure Claude Agent hooks for tool execution and events (from `hooksCommand.ts`)
466- `/memory` - Open memory files (CLAUDE.md) for editing (from `memoryCommand.ts`)
467- `/agents` - Create and manage specialized Claude agents (from `agentsCommand.ts`)
468- `/terminal` - Create a terminal with Claude CLI configured to use Copilot Chat endpoints (from `terminalCommand.ts`) _Temporarily disabled pending legal review_
469
470### Tool Permission Handlers
471
472**Location:** `node/toolPermissionHandlers/` and `common/toolPermissionHandlers/`
473
474Tool permission handlers control what actions Claude can take without user confirmation. They define the approval logic for various tool operations.
475
476**Key Features:**
477- Auto-approve safe operations (e.g., file edits within workspace)
478- Request user confirmation for potentially dangerous operations
479- Handlers are organized by platform (common, node, vscode-node)
480
481**Handler Types:**
482- **Common handlers** (`common/toolPermissionHandlers/`):
483 - `bashToolHandler.ts` - Controls bash/shell command execution
484 - `exitPlanModeHandler.ts` - Manages plan mode transitions
485 - `askUserQuestionHandler.ts` - Delegates to the core `vscode_askQuestions` tool for question carousel UI
486
487- **Node handlers** (`node/toolPermissionHandlers/`):
488 - `editToolHandler.ts` - Handles file edit operations (Edit, Write, MultiEdit)
489
490**Auto-approval Rules:**
491- File edits are auto-approved if the file is within the workspace
492- All other tools show a confirmation dialog via VS Code's chat API
493- User denials send appropriate messages back to Claude
494
495### MCP Server Registry
496
497**Location:** `common/claudeMcpServerRegistry.ts`
498
499The MCP server registry allows contributing MCP (Model Context Protocol) server configurations to the Claude SDK Options. Contributors provide server configurations that are merged and passed to the SDK at session start.
500
501**Key Features:**
502- Register contributors using `registerClaudeMcpServerContributor(ctor)`
503- Contributors are constructed via dependency injection using `IInstantiationService`
504- Contributors implement `IClaudeMcpServerContributor` with an async `getMcpServers()` method
505- Server configurations are merged into a single `Record<string, McpServerConfig>` for the SDK
506
507**Contributor Interface:**
508```typescript
509interface IClaudeMcpServerContributor {
510 getMcpServers(): Promise<Record<string, McpServerConfig>>;
511}
512```
513
514**Supported Server Types:**
515- `McpStdioServerConfig` - Standard input/output process transport (`{ command, args?, env? }`)
516- `McpSSEServerConfig` - Server-Sent Events (`{ type: 'sse', url, headers? }`)
517- `McpHttpServerConfig` - HTTP transport (`{ type: 'http', url, headers? }`)
518- `McpSdkServerConfigWithInstance` - In-process SDK servers
519
520**Index Chain:**
521- `common/mcpServers/index.ts` → Platform-agnostic contributors
522- `node/mcpServers/index.ts` → Node-specific contributors (imports common first)
523- `vscode-node/mcpServers/index.ts` → VS Code-specific contributors (imports node first)
524
525**Extending the Registries:**
526
527To add new functionality:
528
5291. **New Hook Handler:**
530 - Create a class implementing `HookCallbackMatcher`
531 - Call `registerClaudeHook(hookEvent, YourHandler)` at module load time
532 - Import your handler module in `node/hooks/index.ts`
533
5342. **New Slash Command:**
535 - Create a class implementing `IClaudeSlashCommandHandler`
536 - Call `registerClaudeSlashCommand(YourHandler)` at module load time
537 - Import your command module in `vscode-node/slashCommands/index.ts`
538 - If providing a `commandId`, register the command in `package.json`:
539 ```json
540 {
541 "command": "copilot.claude.yourCommand",
542 "title": "Your Command Title",
543 "category": "Claude Agent"
544 }
545 ```
546
5473. **New Tool Permission Handler:**
548 - Create handler in appropriate directory (common/node/vscode-node)
549 - Implement tool approval logic
550 - Import your handler module in `index.ts` to trigger registration
551
5524. **New MCP Server Contributor:**
553 - Create a class implementing `IClaudeMcpServerContributor`
554 - Call `registerClaudeMcpServerContributor(YourContributor)` at module load time
555 - Import your contributor module in the appropriate `mcpServers/index.ts` (common/node/vscode-node)
556
557## Configuration
558
559The integration respects VS Code settings:
560- `github.copilot.advanced.claudeCodeDebugEnabled`: Enables debug logging from Claude Code SDK
561
562## Upgrading Anthropic SDK Packages
563
564For the complete upgrade process, use the **anthropic-sdk-upgrader** Claude Code agent. The agent provides step-by-step guidance for upgrading `@anthropic-ai/claude-agent-sdk` and `@anthropic-ai/sdk` packages, including:
565
566- Checking changelogs and summarizing changes
567- Categorizing changes by impact level
568- Fixing compilation errors in key files
569- Complete testing checklist
570- Troubleshooting common issues
571
572See `.claude/agents/anthropic-sdk-upgrader.md` for the full process.
573
574## Dependencies
575
576- `@anthropic-ai/claude-agent-sdk`: Official Claude Code SDK
577- `@anthropic-ai/sdk`: Anthropic API types
578- Internal services: `ILogService`, `IConfigurationService`, `IWorkspaceService`, `IToolsService`, etc.
579
@@ −1 +1 @@
1−# VS Code Agents Instructions
1+# Claude Code Integration
22
3−This file provides instructions for AI coding agents working with the VS Code codebase.
3+This folder contains the Claude Code integration for VS Code Chat. It enables users to open a new Chat window and interact with a Claude Code instance directly within VS Code. **VS Code provides the UI, Claude Code provides the smarts.**
44
5−For detailed project overview, architecture, coding guidelines, and validation steps, see the [Copilot Instructions](.github/copilot-instructions.md).
5+> 📖 **New to the Claude session target?** See the **[User Guide](./CLAUDE_SESSION_USER_GUIDE.md)** for a comprehensive walkthrough of features, slash commands, permission modes, and best practices.
6+
7+## Official Claude Agent SDK Documentation
8+
9+> **Important:** For the most up-to-date information on the Claude Agent SDK, always refer to the official documentation:
10+>
11+> - **[Agent SDK Overview](https://platform.claude.com/docs/en/agent-sdk/overview)** - General SDK concepts, capabilities, and getting started guide
12+> - **[Agent SDK Quickstart](https://platform.claude.com/docs/en/agent-sdk/quickstart)** - Step-by-step guide to building your first agent
13+> - **[TypeScript SDK Reference](https://platform.claude.com/docs/en/agent-sdk/typescript)** - Complete API reference for the TypeScript SDK including all functions, types, and interfaces
14+> - **[TypeScript V2 Preview](https://platform.claude.com/docs/en/agent-sdk/typescript-v2-preview)** - Preview of the simplified V2 interface with session-based send/stream patterns
15+>
16+> The SDK package is `@anthropic-ai/claude-agent-sdk`. The official documentation covers tools, hooks, subagents, MCP integration, permissions, sessions, and more.
17+
18+### Core SDK Features
19+
20+**Getting Started:**
21+- [Overview](https://platform.claude.com/docs/en/agent-sdk/overview) - Learn about the Agent SDK architecture and core concepts
22+- [Quickstart](https://platform.claude.com/docs/en/agent-sdk/quickstart) - Get up and running with your first agent in minutes
23+
24+**Core SDK Implementation:**
25+- [TypeScript](https://platform.claude.com/docs/en/agent-sdk/typescript) - Main TypeScript SDK reference for building agents
26+- [TypeScript v2 Preview](https://platform.claude.com/docs/en/agent-sdk/typescript-v2-preview) - Preview of upcoming v2 API with enhanced features
27+- [Streaming vs Single Mode](https://platform.claude.com/docs/en/agent-sdk/streaming-vs-single-mode) - Choose between streaming responses or single-turn completions
28+
29+**User Interaction & Control:**
30+- [Permissions](https://platform.claude.com/docs/en/agent-sdk/permissions) - Control what actions Claude can take with user approval flows
31+- [User Input](https://platform.claude.com/docs/en/agent-sdk/user-input) - Collect clarifications and decisions from users during execution
32+- [Hooks](https://platform.claude.com/docs/en/agent-sdk/hooks) - Execute custom logic at key points in the agent lifecycle
33+
34+**State & Session Management:**
35+- [Sessions](https://platform.claude.com/docs/en/agent-sdk/sessions) - Manage conversation history and context across interactions
36+- [File Checkpointing](https://platform.claude.com/docs/en/agent-sdk/file-checkpointing) - Save and restore file states for undo/redo functionality
37+
38+**Advanced Features:**
39+- [Structured Outputs](https://platform.claude.com/docs/en/agent-sdk/structured-outputs) - Get reliable JSON responses with schema validation
40+- [Modifying System Prompts](https://platform.claude.com/docs/en/agent-sdk/modifying-system-prompts) - Customize Claude's behavior and instructions
41+- [MCP](https://platform.claude.com/docs/en/agent-sdk/mcp) - Connect to Model Context Protocol servers for extended capabilities
42+- [Custom Tools](https://platform.claude.com/docs/en/agent-sdk/custom-tools) - Build your own tools to extend Claude's functionality
43+
44+**Agent Composition & UX:**
45+- [Subagents](https://platform.claude.com/docs/en/agent-sdk/subagents) - Compose complex workflows by delegating to specialized agents
46+- [Slash Commands](https://platform.claude.com/docs/en/agent-sdk/slash-commands) - Add custom `/commands` for quick actions
47+- [Skills](https://platform.claude.com/docs/en/agent-sdk/skills) - Package reusable agent capabilities as installable modules
48+- [Todo Tracking](https://platform.claude.com/docs/en/agent-sdk/todo-tracking) - Help Claude manage and display task progress
49+- [Plugins](https://platform.claude.com/docs/en/agent-sdk/plugins) - Extend the SDK with community-built integrations
50+
51+## Overview
52+
53+The Claude Code integration allows VS Code's chat interface to communicate with Claude Code, Anthropic's agentic coding assistant. When a user sends a message in a VS Code Chat window using this integration, the message is routed to a Claude Code session that can:
54+
55+- Read and analyze code
56+- Execute shell commands
57+- Edit files
58+- Search the workspace
59+- Manage tasks and todos
60+
61+All interactions are displayed through VS Code's native chat UI, providing a seamless experience.
62+
63+## Architecture
64+
65+```
66+┌─────────────────────────────────────────────────────────────────┐
67+│ VS Code Chat UI │
68+└─────────────────────────┬───────────────────────────────────────┘
69+ │
70+ ▼
71+┌─────────────────────────────────────────────────────────────────┐
72+│ ClaudeAgentManager │
73+│ - Manages language model server lifecycle │
74+│ - Routes requests to appropriate sessions │
75+│ - Resolves prompts with file references │
76+└─────────────────────────┬───────────────────────────────────────┘
77+ │
78+ ▼
79+┌─────────────────────────────────────────────────────────────────┐
80+│ ClaudeCodeSession │
81+│ - Maintains a single Claude Code conversation │
82+│ - Processes messages (assistant, user, result) │
83+│ - Handles tool invocation and confirmation │
84+│ - Queues multiple requests for sequential processing │
85+└─────────────────────────┬───────────────────────────────────────┘
86+ │
87+ ▼
88+┌─────────────────────────────────────────────────────────────────┐
89+│ Claude Code SDK (@anthropic-ai) │
90+│ - Communicates with Claude Code │
91+│ - Manages tool hooks (pre/post tool use) │
92+│ - Handles message streaming │
93+└─────────────────────────────────────────────────────────────────┘
94+```
95+
96+## Key Components
97+
98+### `node/claudeCodeAgent.ts`
99+
100+**ClaudeAgentManager**
101+- Entry point for handling chat requests from VS Code
102+- Starts and manages the language model server (`LanguageModelServer`)
103+- Creates and caches `ClaudeCodeSession` instances by session ID
104+- Resolves prompts by replacing VS Code references (files, locations) with actual paths
105+
106+**ClaudeCodeSession**
107+- Represents a single Claude Code conversation session
108+- Manages a queue of incoming requests from VS Code Chat
109+- Uses an async iterable to feed prompts to Claude Code SDK
110+- Processes three message types:
111+ - **Assistant messages**: Text responses and tool use requests
112+ - **User messages**: Tool results from executed tools
113+ - **Result messages**: Session completion or error states
114+- Handles tool confirmation dialogs via VS Code's chat API
115+- Auto-approves safe operations (file edits in workspace)
116+- Tracks external edits to show proper diffs
117+
118+### `node/claudeCodeSdkService.ts`
119+
120+**IClaudeCodeSdkService / ClaudeCodeSdkService**
121+- Thin wrapper around the `@anthropic-ai/claude-agent-sdk`
122+- Provides dependency injection for testability
123+- Enables mocking in unit tests
124+
125+### `node/sessionParser/claudeCodeSessionService.ts`
126+
127+**IClaudeCodeSessionService / ClaudeCodeSessionService**
128+- Loads and manages persisted Claude Code sessions from disk
129+- Reads `.jsonl` session files from `~/.claude/projects/<workspace-slug>/`
130+- Builds message chains from leaf nodes to reconstruct full conversations
131+- Loads subagent sessions via SDK APIs (`listSubagents` + `getSubagentMessages`) and correlates them with their spawning tool use via `parent_tool_use_id` (stored as `ISubagentSession.parentToolUseId`)
132+- Provides session caching with mtime-based invalidation
133+- Used to resume previous Claude Code conversations
134+- See `node/sessionParser/README.md` for detailed documentation
135+
136+### `node/sessionParser/sdkSessionAdapter.ts`
137+
138+Adapts raw SDK session data into the internal `IClaudeCodeSession` / `ISubagentSession` schemas:
139+- **`buildClaudeCodeSession()`**: Assembles a full `IClaudeCodeSession` from session info, messages, and subagents
140+- **`sdkSubagentMessagesToSubagentSession()`**: Converts raw SDK `SessionMessage[]` into an `ISubagentSession`
141+- **`extractParentToolUseId()`**: Helper that scans a `SessionMessage[]` array until it finds a string `parent_tool_use_id`, used to correlate a subagent session with the Agent/Task tool_use block that spawned it
142+
143+### `node/claudeSkills.ts`
144+
145+**IClaudePluginService / ClaudePluginService**
146+- Resolves plugin root directories for the Claude SDK's `plugins` option
147+- Combines three sources of plugin locations:
148+ 1. **Config skill locations** — from `chat.agentSkillsLocations` setting, resolved via the shared `resolveSkillConfigLocations()` utility. These point to skills directories (e.g. `.../skills/`), so the service walks **one level up** to reach the plugin root expected by the SDK.
149+ 2. **Discovered skills** — from `IPromptsService.getSkills()`. Each skill has a `SKILL.md` at `<plugin-root>/skills/<skill-name>/SKILL.md`, so the service walks **three levels up** (`dirname(dirname(dirname(uri)))`) to reach the plugin root.
150+ 3. **Direct plugins** — from `IPromptsService.getPlugins()`, returned as-is since they already point to plugin root directories.
151+- Filters out `.claude` directories (the Claude SDK loads these automatically)
152+- Deduplicates results using `ResourceSet`
153+- Plugin roots are passed to the SDK as `SdkPluginConfig[]` with `{ type: 'local', path }` in `ClaudeCodeSession._doStartSession()`
154+
155+**Shared utility:** `../../common/skillConfigLocations.ts` — `resolveSkillConfigLocations()` handles `~/` expansion, absolute paths, and relative paths joined to workspace folders. Used by both `ClaudePluginService` and `CopilotCLISkills`.
156+
157+### `common/claudeTools.ts`
158+
159+Defines Claude Code's tool interface:
160+- **ClaudeToolNames**: Enum of all supported tool names (Bash, Read, Edit, Write, etc.). `Agent` is the current name (SDK v2.1.63+); `Task` is kept for backward compatibility with older sessions.
161+- **Tool input interfaces**: Type definitions for each tool's input parameters
162+- **claudeEditTools**: List of tools that modify files (Edit, MultiEdit, Write, NotebookEdit)
163+- **getAffectedUrisForEditTool**: Extracts file URIs that will be modified by edit operations
164+
165+### `common/toolInvocationFormatter.ts`
166+
167+Formats tool invocations for display in VS Code's chat UI:
168+- Creates `ChatToolInvocationPart` instances with appropriate messaging
169+- Handles tool-specific formatting (Bash commands, file reads, searches, etc.)
170+- Suppresses certain tools from display (TodoWrite, Edit, Write) where other UI handles them
171+
172+### `../../chatSessions/vscode-node/chatHistoryBuilder.ts`
173+
174+Converts a persisted `IClaudeCodeSession` into VS Code `ChatResponsePart[]` for replay in the chat UI:
175+- Reconstructs assistant text, thinking blocks, tool invocations, and tool results into chat response parts
176+- Matches subagent sessions to their spawning Agent/Task tool_use blocks using `ISubagentSession.parentToolUseId`, injecting the subagent's tool calls inline under the parent tool invocation
177+
178+## Message Flow
179+
180+1. **User sends message** in VS Code Chat
181+2. **ClaudeAgentManager** receives the request and routes to existing or new session
182+3. **ClaudeCodeSession** queues the request and feeds the prompt to Claude Code SDK
183+4. **Claude Code SDK** returns streaming messages:
184+ - Text content → rendered as markdown in chat
185+ - Tool use requests → shown as progress, then confirmed via VS Code's confirmation API
186+ - Tool results → formatted and displayed in chat
187+5. **Result message** signals turn completion, request is resolved
188+
189+## Tool Confirmation
190+
191+Claude Code tools require user confirmation before execution:
192+- **Auto-approved**: File edits (Edit, Write, MultiEdit) are auto-approved if the file is within the workspace
193+- **Manual confirmation**: All other tools show a confirmation dialog via `CoreConfirmationTool`
194+- **Denied tools**: User denial sends a "user declined" message back to Claude Code
195+
196+## Session Persistence
197+
198+Claude Code sessions are persisted to `~/.claude/projects/<workspace-slug>/` as `.jsonl` files. The `ClaudeCodeSessionService` can:
199+- Load all sessions for the current workspace
200+- Resume a previous session by ID
201+- Cache sessions with mtime-based invalidation
202+
203+## Folder and Working Directory Management
204+
205+The integration deterministically resolves the working directory (`cwd`) and additional directories for each Claude session, rather than inheriting from `process.cwd()`. This is managed by the `ClaudeChatSessionContentProvider` and exposed through the `ClaudeFolderInfo` interface.
206+
207+### `ClaudeFolderInfo` (`common/claudeFolderInfo.ts`)
208+
209+```typescript
210+interface ClaudeFolderInfo {
211+ readonly cwd: string; // Primary working directory
212+ readonly additionalDirectories: string[]; // Extra directories Claude can access
213+}
214+```
215+
216+### Folder Resolution by Workspace Type
217+
218+| Workspace Type | cwd | additionalDirectories | Folder Picker |
219+|---|---|---|---|
220+| **Single-root** (1 folder) | That folder | `[]` | Hidden |
221+| **Multi-root** (2+ folders) | Selected folder (default: first) | All other workspace folders | Shown with workspace folders |
222+| **Empty** (0 folders) | Selected MRU folder | `[]` | Shown with MRU entries |
223+
224+### Data Flow
225+
226+1. **`ClaudeChatSessionItemController`** resolves `ClaudeFolderInfo` via `getFolderInfoForSession(sessionId)`
227+2. The folder info is passed through `ClaudeAgentManager.handleRequest()` to `ClaudeCodeSession`
228+3. `ClaudeCodeSession._startSession()` uses `folderInfo.cwd` and `folderInfo.additionalDirectories` when building SDK `Options`
229+
230+### Folder Picker UI
231+
232+In multi-root and empty workspaces, a folder picker option appears in the chat session options:
233+- **Multi-root**: Lists all workspace folders; selecting one makes it `cwd`, the rest become `additionalDirectories`
234+- **Empty workspace**: Lists MRU folders from `IFolderRepositoryManager` (max 10 entries)
235+- The folder option is **locked** for existing (non-untitled) sessions to prevent cwd changes mid-conversation
236+
237+### Session Discovery Across Folders
238+
239+`ClaudeCodeSessionService._getProjectSlugs()` generates workspace slugs for **all** workspace folders, enabling session discovery across all project directories in multi-root workspaces. For empty workspaces, it generates slugs for all folders known to `IFolderRepositoryManager` (MRU entries).
240+
241+### Key Files
242+
243+- **`common/claudeFolderInfo.ts`**: `ClaudeFolderInfo` interface
244+- **`../../chatSessions/common/claudeWorkspaceFolderService.ts`**: `IClaudeWorkspaceFolderService` interface — computes git diff changes for session items
245+- **`../../chatSessions/vscode-node/claudeWorkspaceFolderServiceImpl.ts`**: Implementation — diffs the session's branch against its base branch, caches results, and maps changes to `ChatSessionChangedFile[]` for display in the Sessions view
246+- **`../../chatSessions/vscode-node/claudeChatSessionContentProvider.ts`**: Folder resolution, picker options, session metadata enrichment, and git command handlers
247+- **`../../chatSessions/common/builtinSlashCommands.ts`**: Shared constants for built-in slash commands (`/commit`, `/sync`, `/merge`, etc.) used by both Claude and CopilotCLI sessions
248+- **`../../chatSessions/vscode-node/folderRepositoryManagerImpl.ts`**: `FolderRepositoryManager` (abstract base) with `ClaudeFolderRepositoryManager` subclass — the Claude subclass does not depend on `ICopilotCLISessionService` (CopilotCLI has its own subclass `CopilotCLIFolderRepositoryManager`)
249+- **`node/claudeCodeAgent.ts`**: Consumes `ClaudeFolderInfo` in `ClaudeCodeSession._startSession()`
250+- **`node/sessionParser/claudeCodeSessionService.ts`**: `_getProjectSlugs()` generates slugs for all folders
251+
252+## Input State Reactive Pipeline
253+
254+The chat session input controls (permission mode picker, folder picker) are driven by a reactive observable pipeline, not by imperative setter calls. Understanding this pipeline is important when modifying input state behavior.
255+
256+### Overview
257+
258+VS Code calls `getChatSessionInputState` to get a `ChatSessionInputState` object whose `.groups` array drives the UI. Rather than computing groups once and returning them, the pipeline keeps `groups` live: shared observables push changes into each state object whenever relevant configuration changes.
259+
260+### Key Types
261+
262+```
263+InputStateReactivePipeline {
264+ permissionMode: ISettableObservable<PermissionMode>
265+ folderUri: ISettableObservable<URI | undefined>
266+ folderItems: ISettableObservable<readonly vscode.ChatSessionProviderOptionItem[]>
267+ isSessionStarted: ISettableObservable<boolean>
268+ store: DisposableStore // owns all autoruns for this pipeline
269+}
270+```
271+
272+### Seeding: Extracting Initial Values
273+
274+Before attaching any autoruns, `_createInputStateReactivePipeline` calls `_computeSeedValues(state.groups)` to extract the current groups into typed values. This must happen *before* the first autorun runs, because the first autorun pass immediately reads `allGroups` and writes to `state.groups` — if the per-state observables were left at defaults, that write would discard the carefully-constructed initial groups.
275+
276+`_computeSeedValues` extracts four values:
277+
278+| Value | Source | Fallback |
279+|---|---|---|
280+| `permissionMode` | Selected item id in the `permissionMode` group | `lastUsedPermissionMode` |
281+| `folderUri` | Selected item id in the `folder` group | `undefined` |
282+| `folderItems` | Full item list of the `folder` group | `[]` |
283+| `isSessionStarted` | `locked: true` on any folder item or the selected item | `false` |
284+
285+The `isSessionStarted` recovery from `locked` items is important for the `previousInputState` path: the previous state's groups encode the lock signal via `locked: true` on their items. If `_computeSeedValues` did not recover this, the pipeline would start with `isSessionStarted = false` and the `folderGroup` derived would re-render all items as unlocked.
286+
287+### Shared vs. Per-State Observables
288+
289+`ClaudeChatSessionItemController` holds two **shared** observables (one instance per controller, not per session):
290+
291+| Observable | Source | Purpose |
292+|---|---|---|
293+| `_bypassPermissionsEnabled` | `IConfigurationService` event | Controls which permission mode items are available |
294+| `_workspaceFolders` | `IWorkspaceService` event | Controls folder picker items and visibility |
295+
296+Each call to `getChatSessionInputState` creates a **per-state** pipeline with `_createInputStateReactivePipeline(state)`. The per-state observables are seeded via `_computeSeedValues`.
297+
298+`folderItems` is a settable per-state observable (not a pure `derived`) because of an async edge case: when the workspace has no folders, the items come from an async MRU fetch (`IFolderRepositoryManager`). An autorun watches `_workspaceFolders` and updates `folderItems` synchronously when folders exist, or kicks off the async MRU fetch when the workspace is empty.
299+
300+### Derived Computation and Autorun
301+
302+Inside `_createInputStateReactivePipeline`, `derived` observables combine shared and per-state inputs:
303+
304+```
305+permissionModeGroup = derived(bypassEnabled, permissionMode)
306+folderGroup = derived(folderItems, workspaceFolders, folderUri, isSessionStarted)
307+allGroups = derived(permissionModeGroup, folderGroup)
308+```
309+
310+An `autorun` reads `allGroups` and writes to `state.groups`. This is the only place `state.groups` is written — the pipeline is the single source of truth for the UI.
311+
312+### Lifetime Management (onDidDispose)
313+
314+Each pipeline's `store` is disposed via `state.onDidDispose`:
315+
316+```typescript
317+pipeline.store.add(state.onDidDispose(() => pipeline.store.dispose()));
318+```
319+
320+When VS Code discards a `ChatSessionInputState`, the `onDidDispose` event fires and deterministically cleans up all autoruns for that state. The `onDidDispose` subscription is itself registered on the pipeline store, so it is cleaned up as part of disposal.
321+
322+### External Permission Mode Updates
323+
324+When Claude executes `EnterPlanMode` or `ExitPlanMode` tools, `claudeMessageDispatch.ts` calls `IClaudeSessionStateService.setPermissionModeForSession()`, which fires `onDidChangeSessionState`. The pipeline subscribes to this event via a second autorun:
325+
326+```typescript
327+const externalPermissionMode = observableFromEvent(
328+ this,
329+ Event.filter(sessionStateService.onDidChangeSessionState,
330+ e => e.sessionId === sessionId && e.permissionMode !== undefined),
331+ () => sessionStateService.getPermissionModeForSession(sessionId),
332+);
333+pipeline.store.add(autorun(reader => {
334+ pipeline.permissionMode.set(externalPermissionMode.read(reader), undefined);
335+}));
336+```
337+
338+This autorun is registered on `pipeline.store`, so it is disposed along with all other pipeline autoruns when the state is disposed.
339+
340+### Session-Started Signal
341+
342+The `isSessionStarted` observable controls whether folder items carry `locked: true`. It is set to `true` when `getChatSessionInputState` is called with a `sessionResource` — i.e., whenever VS Code provides a resource for the session. This covers both existing on-disk sessions and sessions that have been started (where a resource has been assigned).
343+
344+For the `previousInputState` path, the lock state is recovered from the items themselves: `_computeSeedValues` checks for `locked: true` on folder items and restores `isSessionStarted` accordingly.
345+
346+### Critical Invariant: Subscribe After Both Branches
347+
348+`_setupInputState` creates `state` and `pipeline` in one of two branches:
349+- **`context.previousInputState` path** — VS Code already has a state for this session and is asking for a fresh one; seed from the old groups.
350+- **New-state path** — first call for this session; fetch groups from disk or defaults.
351+
352+**The external permission mode subscription must run after both branches.** If it only runs in the new-state path, permission mode changes from `EnterPlanMode`/`ExitPlanMode` are silently dropped for every session after the first `getChatSessionInputState` call. Guard against this regression by ensuring the subscription is placed outside the `if/else` block.
353+
354+## Session Metadata and Git Commands
355+
356+### Session Metadata Enrichment
357+
358+Each Claude session item carries metadata that drives the Sessions view UI (button visibility, status indicators). The `ClaudeChatSessionItemController._buildSessionMetadata()` method enriches session items with git repository state.
359+
360+**Workspace Trust:** Session metadata and git change detection are gated on workspace trust via `IWorkspaceService.isResourceTrusted()`. For untrusted working directories, `_buildSessionMetadata()` returns only the `workingDirectoryPath` (no git data), and `getWorkspaceChanges()` is skipped entirely. The trust check is resolved once in `_createClaudeChatSessionItem` and passed into `_buildSessionMetadata` to avoid redundant calls. When trusted, the metadata fetch and workspace changes fetch run concurrently via `Promise.all`.
361+
362+| Field | Type | Description |
363+|-------|------|-------------|
364+| `workingDirectoryPath` | `string` | Session's working directory (always present) |
365+| `repositoryPath` | `string?` | Git repository root path |
366+| `branchName` | `string?` | Current HEAD branch name |
367+| `upstreamBranchName` | `string?` | Upstream tracking ref (e.g., `origin/main`) |
368+| `hasGitHubRemote` | `boolean?` | Whether any remote points to GitHub |
369+| `incomingChanges` | `number?` | Commits behind upstream |
370+| `outgoingChanges` | `number?` | Commits ahead of upstream |
371+| `uncommittedChanges` | `number?` | Total uncommitted changes (merge + index + working tree + untracked) |
372+
373+These metadata fields map to `when`-clause context keys in `package.json` (e.g., `sessions.hasGitRepository`, `sessions.hasUncommittedChanges`, `sessions.hasUpstream`) that control which action buttons appear in the Changes view.
374+
375+### Git Action Commands
376+
377+The `ClaudeChatSessionItemController` registers four git-related commands that appear as action buttons in the Sessions/Changes view:
378+
379+| Command | When Visible | Action |
380+|---------|-------------|--------|
381+| `github.copilot.claude.sessions.commit` | Has git repo + uncommitted changes | Sends `/commit` prompt to the session |
382+| `github.copilot.claude.sessions.commitAndSync` | Has git repo + uncommitted changes + upstream | Sends `/commit and /sync` prompt |
383+| `github.copilot.claude.sessions.sync` | Has git repo + no uncommitted changes + upstream | Sends `/sync` prompt |
384+| `github.copilot.claude.sessions.initializeRepository` | No git repo | Calls `IGitService.initRepository()` on the session's workspace folder |
385+
386+The commit, commitAndSync, and sync commands use a shared `_registerPromptCommand()` helper that extracts the session resource and dispatches via `workbench.action.chat.openSessionWithPrompt.claude-code`. The slash command strings come from the shared `builtinSlashCommands` module (`../../common/builtinSlashCommands.ts`).
387+
388+## Testing
389+
390+Unit tests are located in `node/test/`:
391+- `claudeCodeAgent.spec.ts`: Tests for agent and session logic
392+- `claudeCodeSessionService.spec.ts`: Tests for session loading and persistence
393+- `claudePluginService.spec.ts`: Tests for plugin location resolution
394+- `mockClaudeCodeSdkService.ts`: Mock SDK service for testing
395+- `fixtures/`: Sample `.jsonl` session files for testing
396+
397+Additional tests for the session item controller and content provider:
398+- `../../chatSessions/vscode-node/test/claudeChatSessionContentProvider.spec.ts`: Tests for session metadata enrichment, git command handlers, session lifecycle, and content provider behavior
399+
400+## Extension Registries
401+
402+The Claude integration uses several registries to organize and manage extensibility points:
403+
404+### Hook Registry
405+
406+**Location:** `node/hooks/claudeHookRegistry.ts`
407+
408+The hook registry allows registering custom hooks that execute at key points in the agent lifecycle. Hooks are organized by `HookEvent` type from the Claude SDK.
409+
410+**Key Features:**
411+- Register handlers using `registerClaudeHook(hookEvent, ctor)`
412+- Handlers are constructed via dependency injection using `IInstantiationService`
413+- Hook instances are built from the registry and passed to the Claude SDK
414+- Multiple handlers can be registered for the same event
415+
416+**Example Hook Events:**
417+- `'PreToolUse'` - Before a tool is executed
418+- `'PostToolUse'` - After a tool completes
419+- `'SubagentStart'` - When a subagent starts
420+- `'SubagentEnd'` - When a subagent completes
421+- `'SessionStart'` - When a session begins
422+- `'SessionEnd'` - When a session ends
423+
424+**Current Hook Handlers:**
425+- `loggingHooks.ts` - Logging hooks for debugging and telemetry
426+- `sessionHooks.ts` - Session lifecycle management
427+- `subagentHooks.ts` - Subagent lifecycle tracking
428+- `toolHooks.ts` - Tool execution tracking and processing
429+
430+### Slash Command Registry
431+
432+**Location:** `vscode-node/slashCommands/claudeSlashCommandRegistry.ts`
433+
434+The slash command registry manages custom slash commands available in Claude chat sessions. Commands allow users to trigger specific functionality via `/commandname` syntax.
435+
436+**Key Features:**
437+- Register handlers using `registerClaudeSlashCommand(handler)`
438+- Each handler implements `IClaudeSlashCommandHandler` interface
439+- Commands can optionally register with VS Code Command Palette
440+- Handlers receive arguments, response stream, and cancellation token
441+
442+**Handler Interface:**
443+```typescript
444+interface IClaudeSlashCommandHandler {
445+ readonly commandName: string; // Command name (without /)
446+ readonly description: string; // Human-readable description
447+ readonly commandId?: string; // Optional VS Code command ID
448+ handle(args: string, stream: ChatResponseStream | undefined, token: CancellationToken): Promise<ChatResult | void>;
449+}
450+```
451+
452+**UI Patterns for Slash Commands:**
453+
454+Slash commands often need to present choices or gather input from users. When doing so, prefer the simpler one-shot APIs over the more complex builder APIs:
455+
456+- **Prefer:** `vscode.window.showQuickPick()` - Simple function call that returns the selected item(s)
457+- **Avoid:** `vscode.window.createQuickPick()` - More complex, requires manual lifecycle management
458+
459+- **Prefer:** `vscode.window.showInputBox()` - Simple function call that returns the entered text
460+- **Avoid:** `vscode.window.createInputBox()` - More complex, requires manual lifecycle management
461+
462+The `show*` APIs are sufficient for most slash command use cases and result in cleaner, more maintainable code. Only use `create*` APIs when you need advanced features like dynamic item updates, multi-step wizards, or custom event handling.
463+
464+**Current Slash Commands:**
465+- `/hooks` - Configure Claude Agent hooks for tool execution and events (from `hooksCommand.ts`)
466+- `/memory` - Open memory files (CLAUDE.md) for editing (from `memoryCommand.ts`)
467+- `/agents` - Create and manage specialized Claude agents (from `agentsCommand.ts`)
468+- `/terminal` - Create a terminal with Claude CLI configured to use Copilot Chat endpoints (from `terminalCommand.ts`) _Temporarily disabled pending legal review_
469+
470+### Tool Permission Handlers
471+
472+**Location:** `node/toolPermissionHandlers/` and `common/toolPermissionHandlers/`
473+
474+Tool permission handlers control what actions Claude can take without user confirmation. They define the approval logic for various tool operations.
475+
476+**Key Features:**
477+- Auto-approve safe operations (e.g., file edits within workspace)
478+- Request user confirmation for potentially dangerous operations
479+- Handlers are organized by platform (common, node, vscode-node)
480+
481+**Handler Types:**
482+- **Common handlers** (`common/toolPermissionHandlers/`):
483+ - `bashToolHandler.ts` - Controls bash/shell command execution
484+ - `exitPlanModeHandler.ts` - Manages plan mode transitions
485+ - `askUserQuestionHandler.ts` - Delegates to the core `vscode_askQuestions` tool for question carousel UI
486+
487+- **Node handlers** (`node/toolPermissionHandlers/`):
488+ - `editToolHandler.ts` - Handles file edit operations (Edit, Write, MultiEdit)
489+
490+**Auto-approval Rules:**
491+- File edits are auto-approved if the file is within the workspace
492+- All other tools show a confirmation dialog via VS Code's chat API
493+- User denials send appropriate messages back to Claude
494+
495+### MCP Server Registry
496+
497+**Location:** `common/claudeMcpServerRegistry.ts`
498+
499+The MCP server registry allows contributing MCP (Model Context Protocol) server configurations to the Claude SDK Options. Contributors provide server configurations that are merged and passed to the SDK at session start.
500+
501+**Key Features:**
502+- Register contributors using `registerClaudeMcpServerContributor(ctor)`
503+- Contributors are constructed via dependency injection using `IInstantiationService`
504+- Contributors implement `IClaudeMcpServerContributor` with an async `getMcpServers()` method
505+- Server configurations are merged into a single `Record<string, McpServerConfig>` for the SDK
506+
507+**Contributor Interface:**
508+```typescript
509+interface IClaudeMcpServerContributor {
510+ getMcpServers(): Promise<Record<string, McpServerConfig>>;
511+}
512+```
513+
514+**Supported Server Types:**
515+- `McpStdioServerConfig` - Standard input/output process transport (`{ command, args?, env? }`)
516+- `McpSSEServerConfig` - Server-Sent Events (`{ type: 'sse', url, headers? }`)
517+- `McpHttpServerConfig` - HTTP transport (`{ type: 'http', url, headers? }`)
518+- `McpSdkServerConfigWithInstance` - In-process SDK servers
519+
520+**Index Chain:**
521+- `common/mcpServers/index.ts` → Platform-agnostic contributors
522+- `node/mcpServers/index.ts` → Node-specific contributors (imports common first)
523+- `vscode-node/mcpServers/index.ts` → VS Code-specific contributors (imports node first)
524+
525+**Extending the Registries:**
526+
527+To add new functionality:
528+
529+1. **New Hook Handler:**
530+ - Create a class implementing `HookCallbackMatcher`
531+ - Call `registerClaudeHook(hookEvent, YourHandler)` at module load time
532+ - Import your handler module in `node/hooks/index.ts`
533+
534+2. **New Slash Command:**
535+ - Create a class implementing `IClaudeSlashCommandHandler`
536+ - Call `registerClaudeSlashCommand(YourHandler)` at module load time
537+ - Import your command module in `vscode-node/slashCommands/index.ts`
538+ - If providing a `commandId`, register the command in `package.json`:
539+ ```json
540+ {
541+ "command": "copilot.claude.yourCommand",
542+ "title": "Your Command Title",
543+ "category": "Claude Agent"
544+ }
545+ ```
546+
547+3. **New Tool Permission Handler:**
548+ - Create handler in appropriate directory (common/node/vscode-node)
549+ - Implement tool approval logic
550+ - Import your handler module in `index.ts` to trigger registration
551+
552+4. **New MCP Server Contributor:**
553+ - Create a class implementing `IClaudeMcpServerContributor`
554+ - Call `registerClaudeMcpServerContributor(YourContributor)` at module load time
555+ - Import your contributor module in the appropriate `mcpServers/index.ts` (common/node/vscode-node)
556+
557+## Configuration
558+
559+The integration respects VS Code settings:
560+- `github.copilot.advanced.claudeCodeDebugEnabled`: Enables debug logging from Claude Code SDK
561+
562+## Upgrading Anthropic SDK Packages
563+
564+For the complete upgrade process, use the **anthropic-sdk-upgrader** Claude Code agent. The agent provides step-by-step guidance for upgrading `@anthropic-ai/claude-agent-sdk` and `@anthropic-ai/sdk` packages, including:
565+
566+- Checking changelogs and summarizing changes
567+- Categorizing changes by impact level
568+- Fixing compilation errors in key files
569+- Complete testing checklist
570+- Troubleshooting common issues
571+
572+See `.claude/agents/anthropic-sdk-upgrader.md` for the full process.
573+
574+## Dependencies
575+
576+- `@anthropic-ai/claude-agent-sdk`: Official Claude Code SDK
577+- `@anthropic-ai/sdk`: Anthropic API types
578+- Internal services: `ILogService`, `IConfigurationService`, `IWorkspaceService`, `IToolsService`, etc.
6579
