Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.extension.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ The extension is organized into three sections: **Observe**, **Measure**, and **
| --- | --- |
| **Local Agent** | macOS: `~/Library/Application Support/Code/User/workspaceStorage/`<br>Linux: `~/.config/Code/User/workspaceStorage/`<br>Windows: `%APPDATA%\Code\User\workspaceStorage\` |
| **Local Agent (Insiders)** | macOS: `~/Library/Application Support/Code - Insiders/User/workspaceStorage/`<br>Linux: `~/.config/Code - Insiders/User/workspaceStorage/`<br>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/`<br>Windows: `%USERPROFILE%\.claude\projects\` |
| **Codex** | macOS/Linux: `~/.codex/sessions/`<br>Windows: `%USERPROFILE%\.codex\sessions\` |
Expand Down
1 change: 1 addition & 0 deletions docs/content/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
2 changes: 2 additions & 0 deletions docs/content/getting-started/supported-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`)
Expand Down
50 changes: 47 additions & 3 deletions src/core/parser-vscode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { describe, it, expect } from 'vitest';
import { describe, it, expect, vi } 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-'));
Expand Down Expand Up @@ -742,4 +742,48 @@ describe('parseSessionFile — skill detection', () => {
expect(session!.requests[0].skillsUsed).toContain('playwright-cli');
});
});
});
});
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 });
}
});
});
13 changes: 13 additions & 0 deletions src/core/parser-vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ function isObj(v: unknown): v is Record<string, unknown> {

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';
}
Expand All @@ -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');
Expand Down