diff --git a/README.extension.md b/README.extension.md index 3b126f31..e5c21945 100644 --- a/README.extension.md +++ b/README.extension.md @@ -55,6 +55,8 @@ The extension is organized into three sections: **Observe**, **Measure**, and ** | --- | --- | | **Local Agent** | macOS: `~/Library/Application Support/Code/User/workspaceStorage/`
Linux: `~/.config/Code/User/workspaceStorage/`
Windows: `%APPDATA%\Code\User\workspaceStorage\` | | **Local Agent (Insiders)** | macOS: `~/Library/Application Support/Code - Insiders/User/workspaceStorage/`
Linux: `~/.config/Code - Insiders/User/workspaceStorage/`
Windows: `%APPDATA%\Code - Insiders\User\workspaceStorage\` | +| **Local Agent (Server)** | Linux/macOS remote host: `~/.vscode-server/data/User/workspaceStorage/` | +| **Local Agent (Server Insiders)** | Linux/macOS remote host: `~/.vscode-server-insiders/data/User/workspaceStorage/` | | **Xcode Copilot Chat** | `~/.config/github-copilot/xcode/` (requires `sqlite3`) | | **Claude** | macOS/Linux: `~/.claude/projects/`
Windows: `%USERPROFILE%\.claude\projects\` | | **Codex** | macOS/Linux: `~/.codex/sessions/`
Windows: `%USERPROFILE%\.codex\sessions\` | diff --git a/docs/content/_index.md b/docs/content/_index.md index 72f29e26..34d003bc 100644 --- a/docs/content/_index.md +++ b/docs/content/_index.md @@ -13,6 +13,7 @@ AI Engineer Coach reads logs from multiple AI coding tools: | Harness | Source | |---|---| | **Local Agent / Local Agent (Insiders)** | Chat panel logs in the extension host directory (VS Code / VS Code Insiders) | +| **Local Agent (Server) / Local Agent (Server Insiders)** | Remote host chat panel logs under `~/.vscode-server/data/User/workspaceStorage/` or `~/.vscode-server-insiders/data/User/workspaceStorage/` | | **GitHub Copilot for Xcode** | Copilot Chat conversations from Apple's Xcode IDE | | **Claude** | Session files from Anthropic's CLI-based coding assistant | | **Codex** | Session history from OpenAI's terminal agent | diff --git a/docs/content/getting-started/supported-tools.md b/docs/content/getting-started/supported-tools.md index fdc71ebc..55e81c83 100644 --- a/docs/content/getting-started/supported-tools.md +++ b/docs/content/getting-started/supported-tools.md @@ -12,6 +12,8 @@ AI Engineer Coach reads local log files from the following AI coding assistants. The primary harness. AI Engineer Coach parses the chat panel logs that GitHub Copilot writes to the VS Code extension host log directory. This captures every request, response, model used, token counts, tool calls, file references, and terminal commands. +When VS Code connects through Remote-WSL, Remote-SSH, or a Dev Container, the logs live on the remote host under `~/.vscode-server/data/User/workspaceStorage/` (or `~/.vscode-server-insiders/data/User/workspaceStorage/` for Insiders) and appear in the dashboard as `Local Agent (Server)` or `Local Agent (Server Insiders)`. + **What is tracked:** - Requests and responses with timestamps - Model selection (e.g., `claude-opus-4.6`, `gpt-5.4`, `auto`) diff --git a/src/core/parser-vscode.test.ts b/src/core/parser-vscode.test.ts index 9547a903..1e1c2075 100644 --- a/src/core/parser-vscode.test.ts +++ b/src/core/parser-vscode.test.ts @@ -9,7 +9,7 @@ import * as path from 'path'; import { describe, it, expect } from 'vitest'; import { reconstructFromJsonl } from './parser-vscode-files'; import { parseCLIEventsFile } from './parser-vscode-cli'; -import { parseSessionFile, harnessFromPath, scanVsCodeDirs } from './parser-vscode'; +import { parseSessionFile, harnessFromPath, findVsCodeDirs, scanVsCodeDirs } from './parser-vscode'; function withTempFile(name: string, content: string, run: (filePath: string) => void): void { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ai-engineer-coach-')); @@ -742,4 +742,48 @@ describe('parseSessionFile — skill detection', () => { expect(session!.requests[0].skillsUsed).toContain('playwright-cli'); }); }); -}); \ No newline at end of file +}); +describe('harnessFromPath — VS Code Server', () => { + it('returns "Local Agent (Server)" for .vscode-server paths', () => { + expect(harnessFromPath('/home/alice/.vscode-server/data/User/workspaceStorage')).toBe('Local Agent (Server)'); + }); + + it('returns "Local Agent (Server Insiders)" for .vscode-server-insiders paths', () => { + expect(harnessFromPath('/home/alice/.vscode-server-insiders/data/User/workspaceStorage')).toBe('Local Agent (Server Insiders)'); + }); + + it('does not match .vscode-server-insiders as plain .vscode-server', () => { + // .vscode-server-insiders contains the string ".vscode-server" — ensure + // the more-specific check fires first. + const result = harnessFromPath('/home/alice/.vscode-server-insiders/data/User/workspaceStorage'); + expect(result).toBe('Local Agent (Server Insiders)'); + expect(result).not.toBe('Local Agent (Server)'); + }); +}); + +describe('findVsCodeDirs — VS Code Server', () => { + it('includes server workspaceStorage paths on non-Windows hosts', () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ai-engineer-coach-vscode-')); + const home = process.env.HOME; + const userProfile = process.env.USERPROFILE; + const expected = [ + path.join(root, '.config', 'Code', 'User', 'workspaceStorage'), + path.join(root, '.config', 'Code - Insiders', 'User', 'workspaceStorage'), + path.join(root, '.vscode-server', 'data', 'User', 'workspaceStorage'), + path.join(root, '.vscode-server-insiders', 'data', 'User', 'workspaceStorage'), + ]; + + for (const dir of expected) fs.mkdirSync(dir, { recursive: true }); + + process.env.HOME = root; + process.env.USERPROFILE = ''; + + try { + expect(findVsCodeDirs()).toEqual(expected); + } finally { + process.env.HOME = home; + process.env.USERPROFILE = userProfile; + fs.rmSync(root, { recursive: true, force: true }); + } + }); +}); diff --git a/src/core/parser-vscode.ts b/src/core/parser-vscode.ts index 048dacae..00d0cdf6 100644 --- a/src/core/parser-vscode.ts +++ b/src/core/parser-vscode.ts @@ -20,6 +20,10 @@ function isObj(v: unknown): v is Record { export function harnessFromPath(logsDir: string): string { if (logsDir.includes('Code - Insiders')) return 'Local Agent (Insiders)'; + // Check .vscode-server-insiders BEFORE .vscode-server — the latter is a + // substring of the former and would match incorrectly if checked first. + if (logsDir.includes('.vscode-server-insiders')) return 'Local Agent (Server Insiders)'; + if (logsDir.includes('.vscode-server')) return 'Local Agent (Server)'; if (logsDir.includes('.copilot')) return 'GitHub Copilot CLI'; return 'Local Agent'; } @@ -42,6 +46,15 @@ export function findVsCodeDirs(): string[] { if (vsPath && fs.existsSync(vsPath) && !dirs.includes(vsPath)) dirs.push(vsPath); } + // VS Code Server only runs on the remote host (Linux/macOS), not on Windows directly. + if (process.platform !== 'win32' && home) { + const serverEditions = ['.vscode-server', '.vscode-server-insiders']; + for (const serverDir of serverEditions) { + const serverPath = path.join(home, serverDir, 'data', 'User', 'workspaceStorage'); + if (fs.existsSync(serverPath) && !dirs.includes(serverPath)) dirs.push(serverPath); + } + } + // Copilot CLI paths const cliActive = path.join(home, '.copilot', 'session-state'); const cliLegacy = path.join(home, '.copilot', 'history-session-state');