diff --git a/src/providers/claude.ts b/src/providers/claude.ts index 90cb3fe9..a306ffe9 100644 --- a/src/providers/claude.ts +++ b/src/providers/claude.ts @@ -64,7 +64,10 @@ export function getDesktopSessionsDir(): string { const override = process.env['CODEBURN_DESKTOP_SESSIONS_DIR'] if (override) return override if (process.platform === 'darwin') return join(homedir(), 'Library', 'Application Support', 'Claude', 'local-agent-mode-sessions') - if (process.platform === 'win32') return join(homedir(), 'AppData', 'Roaming', 'Claude', 'local-agent-mode-sessions') + if (process.platform === 'win32') { + const appData = process.env['APPDATA']?.trim() + return join(appData || join(homedir(), 'AppData', 'Roaming'), 'Claude', 'local-agent-mode-sessions') + } return join(homedir(), '.config', 'Claude', 'local-agent-mode-sessions') } diff --git a/tests/providers/claude-config-dirs.test.ts b/tests/providers/claude-config-dirs.test.ts index e51fb444..80cb414f 100644 --- a/tests/providers/claude-config-dirs.test.ts +++ b/tests/providers/claude-config-dirs.test.ts @@ -1,19 +1,31 @@ import { mkdtemp, mkdir, rm, writeFile } from 'fs/promises' import { delimiter as pathDelimiter, join } from 'path' -import { tmpdir, homedir } from 'os' +import { homedir, tmpdir } from 'os' import { describe, it, expect, beforeEach, afterEach } from 'vitest' -import { claude } from '../../src/providers/claude.js' +import { claude, getDesktopSessionsDir } from '../../src/providers/claude.js' import { parseAllSessions } from '../../src/parser.js' let tmpRoot: string const savedEnv = { CLAUDE_CONFIG_DIR: process.env['CLAUDE_CONFIG_DIR'], CLAUDE_CONFIG_DIRS: process.env['CLAUDE_CONFIG_DIRS'], + CODEBURN_DESKTOP_SESSIONS_DIR: process.env['CODEBURN_DESKTOP_SESSIONS_DIR'], + APPDATA: process.env['APPDATA'], HOME: process.env['HOME'], } +function withPlatform(platform: typeof process.platform, run: () => T): T { + const descriptor = Object.getOwnPropertyDescriptor(process, 'platform') + Object.defineProperty(process, 'platform', { value: platform }) + try { + return run() + } finally { + if (descriptor) Object.defineProperty(process, 'platform', descriptor) + } +} + beforeEach(async () => { tmpRoot = await mkdtemp(join(tmpdir(), 'codeburn-claude-multi-')) // Point HOME at a scratch dir so the default `~/.claude` fallback resolves @@ -23,6 +35,8 @@ beforeEach(async () => { await mkdir(process.env['HOME'], { recursive: true }) delete process.env['CLAUDE_CONFIG_DIR'] delete process.env['CLAUDE_CONFIG_DIRS'] + delete process.env['CODEBURN_DESKTOP_SESSIONS_DIR'] + delete process.env['APPDATA'] }) afterEach(async () => { @@ -197,6 +211,35 @@ describe('claude provider — CLAUDE_CONFIG_DIRS discovery', () => { }) }) +describe('claude provider — Desktop sessions dir', () => { + it('uses APPDATA as the Windows Claude Desktop sessions root', () => { + const appData = join(tmpRoot, 'roaming-profile') + process.env['APPDATA'] = appData + + withPlatform('win32', () => { + expect(getDesktopSessionsDir()).toBe(join(appData, 'Claude', 'local-agent-mode-sessions')) + }) + }) + + it('falls back to the legacy Windows roaming profile path when APPDATA is unset', () => { + delete process.env['APPDATA'] + + withPlatform('win32', () => { + expect(getDesktopSessionsDir()).toBe(join(homedir(), 'AppData', 'Roaming', 'Claude', 'local-agent-mode-sessions')) + }) + }) + + it('keeps CODEBURN_DESKTOP_SESSIONS_DIR ahead of Windows APPDATA discovery', () => { + const override = join(tmpRoot, 'desktop-override') + process.env['CODEBURN_DESKTOP_SESSIONS_DIR'] = override + process.env['APPDATA'] = join(tmpRoot, 'roaming-profile') + + withPlatform('win32', () => { + expect(getDesktopSessionsDir()).toBe(override) + }) + }) +}) + describe('claude provider — config.json claudeConfigDirs (menubar-driven)', () => { async function writeConfigJson(value: unknown): Promise { const dir = join(process.env['HOME']!, '.config', 'codeburn')