@claude-sessions/core 0.4.7-beta.0 → 0.4.7-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1609,7 +1609,7 @@ var loadSessionTreeDataInternal = (projectName, sessionId, summariesByTargetSess
|
|
|
1609
1609
|
};
|
|
1610
1610
|
});
|
|
1611
1611
|
var loadSessionTreeData = (projectName, sessionId) => loadSessionTreeDataInternal(projectName, sessionId, void 0);
|
|
1612
|
-
var DEFAULT_SORT = { field: "
|
|
1612
|
+
var DEFAULT_SORT = { field: "updated", order: "desc" };
|
|
1613
1613
|
var buildPhase1 = (projectPath, allJsonlFiles) => Effect6.gen(function* () {
|
|
1614
1614
|
const globalUuidMap = /* @__PURE__ */ new Map();
|
|
1615
1615
|
const allSummaries = [];
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/paths.ts","../src/logger.ts","../src/utils.ts","../src/projects.ts","../src/tree-constants.ts","../src/agents.ts","../src/todos.ts","../src/session/projects.ts","../src/session/crud.ts","../src/session/validation.ts","../src/session/tree.ts","../src/session/cache.ts","../src/session/analysis.ts","../src/session/cleanup.ts","../src/session/search.ts","../src/session/files.ts","../src/session/index-file.ts"],"sourcesContent":["/**\n * Path utilities for Claude Code session management\n *\n * Architecture:\n * - Pure Functions: extractCwdFromContent, isSessionFile, toRelativePath (no I/O)\n * - I/O Functions: tryGetCwdFromFile, getRealPathFromSession (with optional DI for testing)\n */\nimport * as fs from 'node:fs'\nimport * as fsp from 'node:fs/promises'\nimport * as os from 'node:os'\nimport * as path from 'node:path'\nimport { createLogger } from './logger.js'\nimport { tryParseJsonLine } from './utils.js'\n\nconst log = createLogger('paths')\n\n// ============================================\n// Types (for dependency injection)\n// ============================================\n\nexport interface Logger {\n debug: (msg: string) => void\n warn: (msg: string) => void\n}\n\nexport interface FileSystem {\n readFileSync: (path: string, encoding: 'utf-8') => string\n readdirSync: (path: string) => string[]\n}\n\nexport interface AsyncFileSystem {\n readFile: (path: string, encoding: 'utf-8') => Promise<string>\n}\n\n// ============================================\n// Directory Paths\n// ============================================\n\n/** Get Claude sessions directory (~/.claude/projects)\n * Can be overridden with CLAUDE_SESSIONS_DIR environment variable for testing\n */\nexport const getSessionsDir = (): string =>\n process.env.CLAUDE_SESSIONS_DIR || path.join(os.homedir(), '.claude', 'projects')\n\n/** Get Claude todos directory (~/.claude/todos) */\nexport const getTodosDir = (): string => path.join(os.homedir(), '.claude', 'todos')\n\n// ============================================\n// Internal Path Utilities\n// ============================================\n\n/** Windows path detection patterns */\nconst WINDOWS_PATTERNS = {\n /** Matches Windows absolute path: C:\\ or C:/ */\n absolutePath: /^([A-Za-z]):[/\\\\]/,\n /** Matches Windows folder name format: C-- */\n folderName: /^([A-Za-z])--/,\n} as const\n\ntype WindowsPathResult = { isWindows: true; drive: string; rest: string } | { isWindows: false }\n\n/** Parse Windows folder name format, extracting drive letter and rest */\nconst parseWindowsFolderName = (name: string): WindowsPathResult => {\n const match = name.match(WINDOWS_PATTERNS.folderName)\n return match ? { isWindows: true, drive: match[1], rest: name.slice(3) } : { isWindows: false }\n}\n\n/** Check if path looks like Windows format */\nconst isWindowsPath = (p: string): boolean => WINDOWS_PATTERNS.absolutePath.test(p)\n\n// ============================================\n// Pure Functions (No I/O)\n// ============================================\n\n/** Extract cwd from file content - pure function for easy testing */\nexport const extractCwdFromContent = (content: string, filePath?: string): string | null => {\n const lines = content.split('\\n').filter((l) => l.trim())\n\n for (let i = 0; i < lines.length; i++) {\n const parsed = tryParseJsonLine<{ cwd?: string }>(lines[i], i + 1, filePath)\n if (parsed?.cwd) {\n return parsed.cwd\n }\n }\n\n return null\n}\n\n/** Check if filename is a session file */\nexport const isSessionFile = (filename: string): boolean =>\n filename.endsWith('.jsonl') && !filename.startsWith('agent-')\n\n/** Expand ~ path to absolute path with OS-native separators */\nexport const expandHomePath = (tildePath: string, homeDir: string): string => {\n if (!tildePath.startsWith('~')) return tildePath\n const relativePart = tildePath.slice(1)\n // path.join uses OS-native separator\n return path.join(homeDir, ...relativePart.split('/').filter(Boolean))\n}\n\n/** Convert path to relative form if under home directory */\nexport const toRelativePath = (absolutePath: string, homeDir: string): string => {\n const normalizedPath = absolutePath.replace(/\\\\/g, '/')\n const normalizedHome = homeDir.replace(/\\\\/g, '/')\n\n // Windows: case-insensitive comparison (C: vs c:)\n const isWin = isWindowsPath(homeDir)\n const pathLower = normalizedPath.toLowerCase()\n const homeLower = normalizedHome.toLowerCase()\n\n // Check for exact match or path with separator after home dir\n if (isWin ? pathLower === homeLower : normalizedPath === normalizedHome) {\n return '~'\n }\n\n const startsWithHome = isWin\n ? pathLower.startsWith(homeLower + '/')\n : normalizedPath.startsWith(normalizedHome + '/')\n\n if (startsWithHome) {\n // Always use forward slash for consistency with .claude.json\n const relativePart = absolutePath.slice(homeDir.length)\n return '~' + relativePart.replace(/\\\\/g, '/')\n }\n // Always normalize to forward slash\n return absolutePath.replace(/\\\\/g, '/')\n}\n\n// ============================================\n// Path Conversion (Pure)\n// ============================================\n\n/**\n * Convert project folder name to display path\n * Unix: -home-user-projects -> /home/user/projects\n * Windows: C--Users-david -> C:\\Users\\david\n * Handle dot-prefixed folders: --claude -> /.claude\n */\nexport const folderNameToDisplayPath = (folderName: string): string => {\n const parsed = parseWindowsFolderName(folderName)\n if (parsed.isWindows) {\n return parsed.drive + ':\\\\' + parsed.rest.replace(/--/g, '\\\\.').replace(/-/g, '\\\\')\n }\n\n // Unix path\n return folderName.replace(/^-/, '/').replace(/--/g, '/.').replace(/-/g, '/')\n}\n\n/**\n * Convert absolute path to project folder name\n * All non-alphanumeric characters are converted to '-'\n * Matches official Claude Code: A.replace(/[^a-zA-Z0-9]/g, '-')\n */\nexport const pathToFolderName = (absolutePath: string): string =>\n absolutePath.replace(/[^a-zA-Z0-9]/g, '-')\n\n// ============================================\n// I/O Functions (with optional DI for testing)\n// ============================================\n\n/**\n * Try to extract cwd from a single session file\n * @param filePath - Path to session file\n * @param fileSystem - Optional FileSystem for testing\n * @param logger - Optional Logger for testing\n */\nexport const tryGetCwdFromFile = (\n filePath: string,\n fileSystem: FileSystem = fs,\n logger: Logger = log\n): string | null => {\n const basename = path.basename(filePath)\n\n try {\n const content = fileSystem.readFileSync(filePath, 'utf-8')\n const cwd = extractCwdFromContent(content)\n\n if (cwd === null) {\n return null\n }\n\n return cwd\n } catch (e) {\n logger.warn(`tryGetCwdFromFile: ${basename} -> read error: ${e}`)\n return null\n }\n}\n\n/**\n * Extract real cwd path from session files in a project\n * @param folderName - Project folder name\n * @param sessionsDir - Optional sessions directory for testing\n * @param fileSystem - Optional FileSystem for testing\n * @param logger - Optional Logger for testing\n */\nexport const getRealPathFromSession = (\n folderName: string,\n sessionsDir: string = getSessionsDir(),\n fileSystem: FileSystem = fs,\n logger: Logger = log\n): string | null => {\n const projectDir = path.join(sessionsDir, folderName)\n\n try {\n const files = fileSystem.readdirSync(projectDir).filter(isSessionFile)\n\n const cwdList: string[] = []\n for (const f of files) {\n const cwd = tryGetCwdFromFile(path.join(projectDir, f), fileSystem, logger)\n if (cwd !== null) {\n cwdList.push(cwd)\n }\n }\n\n // Find cwd that matches folder name\n const matched = cwdList.find((cwd) => pathToFolderName(cwd) === folderName)\n if (matched) {\n return matched\n }\n\n // Log for debugging\n if (cwdList.length > 0) {\n logger.warn(\n `getRealPathFromSession: ${folderName} -> no match, cwds found: ${cwdList.join(', ')}`\n )\n }\n return null\n } catch {\n return null\n }\n}\n\n// ============================================\n// Public API\n// ============================================\n\n/** Get Claude config file path (~/.claude.json) */\nconst getClaudeConfigPath = (): string => path.join(os.homedir(), '.claude.json')\n\n/**\n * Resolve folder name to actual path using ~/.claude.json projects list.\n * Since pathToFolderName converts space/dot/separator all to dash,\n * we can't reverse it reliably. Instead, we look up known project paths\n * from Claude's config and find which one matches when converted.\n *\n * @param folderName - The folder name to resolve (e.g., \"C--Users-david-New-folder\")\n * @param fileSystem - Optional AsyncFileSystem for testing\n * @returns The actual absolute path or null if not found\n */\nexport const resolvePathFromClaudeConfig = async (\n folderName: string,\n fileSystem: AsyncFileSystem = fsp\n): Promise<string | null> => {\n try {\n const configPath = getClaudeConfigPath()\n const content = await fileSystem.readFile(configPath, 'utf-8')\n const config = JSON.parse(content) as { projects?: Record<string, unknown> }\n\n if (!config.projects) return null\n\n // Check each registered project path\n for (const projectPath of Object.keys(config.projects)) {\n if (pathToFolderName(projectPath) === folderName) {\n return projectPath\n }\n }\n\n return null\n } catch {\n return null\n }\n}\n\n/**\n * Convert folder name to relative or absolute path for display\n * If path is under home directory, show relative (~/...)\n *\n * Priority order (fastest first):\n * 1. Claude config lookup (~/.claude.json) - single async file read, cached by OS\n * 2. Session cwd extraction (sync reads session files - expensive but accurate)\n * 3. Pattern-based conversion (pure function, zero I/O - fallback, may be inaccurate for dots/spaces)\n */\nexport const folderNameToPath = async (folderName: string): Promise<string> => {\n const homeDir = os.homedir()\n\n // First try Claude config lookup (fast: single file read, handles spaces/dots correctly)\n const configPath = await resolvePathFromClaudeConfig(folderName)\n if (configPath) {\n return toRelativePath(configPath, homeDir)\n }\n\n // Second, try session cwd extraction (accurate but reads session files)\n const realPath = getRealPathFromSession(folderName)\n if (realPath) {\n return toRelativePath(realPath, homeDir)\n }\n\n // Fallback to pattern-based conversion (may be incorrect for dots/spaces in path)\n const absolutePath = folderNameToDisplayPath(folderName)\n return toRelativePath(absolutePath, homeDir)\n}\n\n/**\n * Find project folder name that matches the given workspace path\n * Searches through all projects and checks if any session's cwd matches\n *\n * @param workspacePath - Absolute path of current workspace\n * @param projectNames - List of project folder names to search\n * @param sessionsDir - Optional sessions directory for testing\n * @param fileSystem - Optional FileSystem for testing\n * @param logger - Optional Logger for testing\n * @returns Matching project folder name or null\n */\nexport const findProjectByWorkspacePath = (\n workspacePath: string,\n projectNames: string[],\n sessionsDir: string = getSessionsDir(),\n fileSystem: FileSystem = fs,\n logger: Logger = log\n): string | null => {\n // First, check if direct conversion matches any project\n const directMatch = pathToFolderName(workspacePath)\n if (projectNames.includes(directMatch)) {\n return directMatch\n }\n\n // Search through projects to find one with matching cwd\n for (const projectName of projectNames) {\n const projectDir = path.join(sessionsDir, projectName)\n\n try {\n const files = fileSystem.readdirSync(projectDir).filter(isSessionFile)\n\n for (const f of files) {\n const cwd = tryGetCwdFromFile(path.join(projectDir, f), fileSystem, logger)\n if (cwd === workspacePath) {\n logger.debug(\n `findProjectByWorkspacePath: ${workspacePath} -> found in ${projectName} (moved session)`\n )\n return projectName\n }\n }\n } catch {\n // Skip inaccessible projects\n }\n }\n\n logger.debug(`findProjectByWorkspacePath: ${workspacePath} -> no matching project found`)\n return null\n}\n","/**\n * Simple logger abstraction for Claude Sessions\n * Consumers can provide their own logger implementation\n */\n\nexport interface Logger {\n debug: (message: string, ...args: unknown[]) => void\n info: (message: string, ...args: unknown[]) => void\n warn: (message: string, ...args: unknown[]) => void\n error: (message: string, ...args: unknown[]) => void\n}\n\n// Default console logger\nconst consoleLogger: Logger = {\n debug: (msg, ...args) => console.debug(`[DEBUG] ${msg}`, ...args),\n info: (msg, ...args) => console.info(`[INFO] ${msg}`, ...args),\n warn: (msg, ...args) => console.warn(`[WARN] ${msg}`, ...args),\n error: (msg, ...args) => console.error(`[ERROR] ${msg}`, ...args),\n}\n\n// Global logger instance\nlet currentLogger: Logger = consoleLogger\n\n/**\n * Set custom logger implementation\n * @example\n * // VSCode extension\n * setLogger({\n * debug: (msg) => outputChannel.appendLine(`[DEBUG] ${msg}`),\n * info: (msg) => outputChannel.appendLine(`[INFO] ${msg}`),\n * warn: (msg) => outputChannel.appendLine(`[WARN] ${msg}`),\n * error: (msg) => outputChannel.appendLine(`[ERROR] ${msg}`),\n * })\n */\nexport const setLogger = (logger: Logger): void => {\n currentLogger = logger\n}\n\n/**\n * Get current logger instance\n */\nexport const getLogger = (): Logger => currentLogger\n\n/**\n * Create a namespaced logger\n * @example\n * const log = createLogger('paths')\n * log.debug('Converting folder name') // [DEBUG] [paths] Converting folder name\n */\nexport const createLogger = (namespace: string): Logger => ({\n debug: (msg, ...args) => currentLogger.debug(`[${namespace}] ${msg}`, ...args),\n info: (msg, ...args) => currentLogger.info(`[${namespace}] ${msg}`, ...args),\n warn: (msg, ...args) => currentLogger.warn(`[${namespace}] ${msg}`, ...args),\n error: (msg, ...args) => currentLogger.error(`[${namespace}] ${msg}`, ...args),\n})\n","/**\n * Utility functions for message processing\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport type {\n Message,\n MessagePayload,\n TextContent,\n AskUserQuestionResult,\n SummaryInfo,\n SessionTodos,\n} from './types.js'\nimport { createLogger } from './logger.js'\n\nconst logger = createLogger('utils')\n\n// IDE tag pattern for removal\nconst IDE_TAG_PATTERN = /<ide_[^>]*>[\\s\\S]*?<\\/ide_[^>]*>/g\n\n// Extract text content from message payload\nexport const extractTextContent = (\n message: MessagePayload | undefined,\n options: { stripIdeTags?: boolean } = {}\n): string => {\n if (!message) return ''\n\n const content = message.content\n if (!content) return ''\n\n let result: string\n\n // If content is string, return directly\n if (typeof content === 'string') {\n result = content\n } else if (Array.isArray(content)) {\n // If content is array, extract text items\n result = content\n .filter((item): item is TextContent => typeof item === 'object' && item?.type === 'text')\n .map((item) => {\n if (item.text == null) {\n logger.warn('TextContent item has undefined or null text property')\n return ''\n }\n return item.text\n })\n .join('')\n } else {\n result = ''\n }\n\n // Optionally strip IDE tags\n if (options.stripIdeTags) {\n result = result.replace(IDE_TAG_PATTERN, '').trim()\n }\n\n return result\n}\n\n/**\n * Parse command message content (e.g., slash commands like /commit)\n * Returns the command name and message extracted from XML tags\n */\nexport const parseCommandMessage = (\n content?: string\n): { name: string; message: string; args: string } => {\n const name = content?.match(/<command-name>([^<]+)<\\/command-name>/)?.[1] ?? ''\n const message = content?.match(/<command-message>([^<]+)<\\/command-message>/)?.[1] ?? ''\n const args = content?.match(/<command-args>([^<]+)<\\/command-args>/)?.[1]?.trim() ?? ''\n return { name, message, args }\n}\n\n// Extract title from message or text (strips IDE tags, uses first line)\nexport const extractTitle = (input: MessagePayload | string | undefined): string => {\n if (!input) return 'Untitled'\n\n // Extract text content with IDE tags stripped\n let text: string\n if (typeof input === 'string') {\n text = input.replace(IDE_TAG_PATTERN, '').trim()\n } else {\n text = extractTextContent(input, { stripIdeTags: true })\n }\n\n if (!text) return 'Untitled'\n\n // Extract first paragraph before parsing commands\n // This prevents embedded JSON/code from being parsed as commands\n let firstParagraph = text.trim()\n if (firstParagraph.includes('\\n\\n')) {\n firstParagraph = firstParagraph.split('\\n\\n')[0]\n }\n\n // Check for slash command format only in first paragraph\n const { name, args } = parseCommandMessage(firstParagraph)\n if (name) return args ? `${name} ${args}` : name\n\n return firstParagraph || 'Untitled'\n}\n\n// Check if message contains \"Invalid API key\"\nexport const isInvalidApiKeyMessage = (msg: Message): boolean => {\n const text = extractTextContent(msg.message)\n return text.includes('Invalid API key')\n}\n\n// Error patterns to exclude from tree view\nconst ERROR_SESSION_PATTERNS = [\n 'API Error',\n 'authentication_error',\n 'Invalid API key',\n 'OAuth token has expired',\n 'Please run /login',\n]\n\n// Check if session title/summary indicates an error session\nexport const isErrorSessionTitle = (title: string | undefined): boolean => {\n if (!title) return false\n return ERROR_SESSION_PATTERNS.some((pattern) => title.includes(pattern))\n}\n\n// Check if a message is a continuation summary (from compact)\nexport const isContinuationSummary = (msg: Message): boolean => {\n // isCompactSummary flag is set by Claude Code for continuation summaries\n if (msg.isCompactSummary === true) return true\n\n // Fallback: check message content\n if (msg.type !== 'user') return false\n const text = extractTextContent(msg.message as MessagePayload | undefined)\n return text.startsWith('This session is being continued from')\n}\n\n/**\n * Get display title with fallback logic\n * Priority: customTitle > currentSummary (truncated) > title > fallback\n * Also handles slash command format in title\n */\nexport const getDisplayTitle = (\n customTitle: string | undefined,\n currentSummary: string | undefined,\n title: string | undefined,\n maxLength = 60,\n fallback = 'Untitled'\n): string => {\n if (customTitle) return customTitle\n if (currentSummary) {\n return currentSummary.length > maxLength\n ? currentSummary.slice(0, maxLength - 3) + '...'\n : currentSummary\n }\n if (title && title !== 'Untitled') {\n // Extract first paragraph to avoid parsing embedded JSON/code\n const firstParagraph = title.includes('\\n\\n') ? title.split('\\n\\n')[0] : title\n // Check if first paragraph contains command-name tag (slash command)\n if (firstParagraph.includes('<command-name>')) {\n const { name, args } = parseCommandMessage(firstParagraph)\n if (name) return args ? `${name} ${args}` : name\n }\n return firstParagraph\n }\n return fallback\n}\n\n// Helper to replace message content with extracted text\nconst replaceMessageContent = (msg: Message, text: string): Message => ({\n ...msg,\n message: {\n ...msg.message,\n content: [{ type: 'text', text } satisfies TextContent],\n },\n toolUseResult: undefined,\n})\n\n// Clean up first message content when splitting session\n// Handles: 1) tool rejection with reason, 2) AskUserQuestion responses\nexport const cleanupSplitFirstMessage = (msg: Message): Message => {\n const toolUseResult = msg.toolUseResult\n if (!toolUseResult) return msg\n\n // Case 1: AskUserQuestion response (toolUseResult is object with questions/answers)\n if (typeof toolUseResult === 'object' && 'answers' in toolUseResult) {\n const answers = (toolUseResult as AskUserQuestionResult).answers\n const qaText = Object.entries(answers)\n .map(([q, a]) => `Q: ${q}\\nA: ${a}`)\n .join('\\n\\n')\n return replaceMessageContent(msg, qaText)\n }\n\n // Case 2: Tool rejection with reason (toolUseResult is string)\n if (typeof toolUseResult === 'string') {\n const rejectionMarker = 'The user provided the following reason for the rejection:'\n const rejectionIndex = toolUseResult.indexOf(rejectionMarker)\n if (rejectionIndex === -1) return msg\n\n const text = toolUseResult.slice(rejectionIndex + rejectionMarker.length).trim()\n if (!text) return msg\n return replaceMessageContent(msg, text)\n }\n\n return msg\n}\n\n/**\n * Mask home directory path with ~\n * Only masks the specified homeDir, not other users' paths\n */\nexport const maskHomePath = (text: string, homeDir: string): string => {\n if (!homeDir) return text\n\n // Escape special regex characters in homeDir\n const escapedHome = homeDir.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n const regex = new RegExp(`${escapedHome}(?=[/\\\\\\\\]|$)`, 'g')\n\n return text.replace(regex, '~')\n}\n\n/**\n * Get sort timestamp for session (Unix timestamp ms)\n * Priority: summaries[0].timestamp > createdAt > 0\n */\nexport const getSessionSortTimestamp = (session: {\n summaries?: SummaryInfo[]\n createdAt?: string\n}): number => {\n const timestampStr = session.summaries?.[0]?.timestamp ?? session.createdAt\n return timestampStr ? new Date(timestampStr).getTime() : 0\n}\n\n/**\n * Try to parse a single JSON line, returning null on failure with optional warning log\n * Use this when you want to skip invalid lines instead of throwing\n */\nexport const tryParseJsonLine = <T = Record<string, unknown>>(\n line: string,\n lineNumber: number,\n filePath?: string\n): T | null => {\n try {\n return JSON.parse(line) as T\n } catch {\n if (filePath) {\n logger.warn(`Skipping invalid JSON at line ${lineNumber} in ${filePath}`)\n }\n return null\n }\n}\n\n/**\n * Parse JSONL lines with optional strict mode\n * @param strict - When true, throws on malformed lines. When false (default), skips with a warning.\n */\nexport const parseJsonlLines = <T = Record<string, unknown>>(\n lines: string[],\n filePath: string,\n { strict = false }: { strict?: boolean } = {}\n): T[] => {\n const results: T[] = []\n for (let idx = 0; idx < lines.length; idx++) {\n try {\n results.push(JSON.parse(lines[idx]) as T)\n } catch (e) {\n const err = e as Error\n if (strict) {\n throw new Error(`Failed to parse line ${idx + 1} in ${filePath}: ${err.message}`, {\n cause: e,\n })\n }\n logger.warn(`Skipping malformed line ${idx + 1} in ${filePath}: ${err.message}`)\n }\n }\n return results\n}\n\n/**\n * Read and parse JSONL file (Effect wrapper)\n * Combines file reading and JSONL parsing with proper error messages\n */\nexport const readJsonlFile = <T = Record<string, unknown>>(\n filePath: string,\n options?: { strict?: boolean }\n) =>\n Effect.gen(function* () {\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n return parseJsonlLines<T>(lines, filePath, options)\n })\n\n// ============================================================================\n// UI Shared Utilities (VSCode Extension & Web UI)\n// ============================================================================\n\n/**\n * Format timestamp as relative time (e.g., \"2h ago\", \"3d ago\")\n * Used by both VSCode Extension tree view and Web UI\n *\n * @param timestamp - Unix timestamp (ms) or ISO date string\n * @returns Relative time string\n */\nexport const formatRelativeTime = (timestamp: number | string): string => {\n const date = typeof timestamp === 'string' ? new Date(timestamp) : new Date(timestamp)\n const now = new Date()\n const diff = now.getTime() - date.getTime()\n\n const minutes = Math.floor(diff / 60000)\n const hours = Math.floor(diff / 3600000)\n const days = Math.floor(diff / 86400000)\n\n if (minutes < 1) return 'just now'\n if (minutes < 60) return `${minutes}m ago`\n if (hours < 24) return `${hours}h ago`\n if (days < 7) return `${days}d ago`\n\n return date.toLocaleDateString()\n}\n\n/**\n * Calculate total todo count from session todos\n * Includes both session-level and agent-level todos\n */\nexport const getTotalTodoCount = (todos: SessionTodos): number => {\n return todos.sessionTodos.length + todos.agentTodos.reduce((sum, a) => sum + a.todos.length, 0)\n}\n\n/**\n * Check if session has sub-items (summaries, agents, or todos)\n * Used to determine if tree item should be expandable\n */\nexport const sessionHasSubItems = (data: {\n summaries: { length: number }\n agents: { length: number }\n todos: SessionTodos\n}): boolean => {\n const todoCount = getTotalTodoCount(data.todos)\n return data.summaries.length > 0 || data.agents.length > 0 || todoCount > 0\n}\n\n/**\n * Get session tooltip text based on what's displayed as title\n * Shows complementary information to the displayed title + session ID\n *\n * Logic:\n * - If customTitle is displayed → show currentSummary\n * - If currentSummary is displayed → show original title\n * - If title is displayed → show currentSummary\n * - Always append session ID at the end\n */\nexport const getSessionTooltip = (session: {\n id: string\n title?: string\n customTitle?: string\n currentSummary?: string\n createdAt?: string\n updatedAt?: string\n}): string => {\n const { id, title, customTitle, currentSummary, createdAt, updatedAt } = session\n let text: string\n // If customTitle is displayed, show currentSummary in tooltip\n if (customTitle && currentSummary) {\n text = currentSummary\n }\n // If currentSummary is displayed as title, show original title in tooltip\n else if (currentSummary && title && title !== 'Untitled') {\n text = title\n }\n // If title is displayed, show currentSummary if available\n else if (currentSummary) {\n text = currentSummary\n } else {\n text = title ?? 'No title'\n }\n\n // Append metadata\n const lines: string[] = [`ID: ${id}`]\n if (createdAt) {\n lines.push(`Created: ${new Date(createdAt).toLocaleString()}`)\n }\n if (updatedAt) {\n lines.push(`Updated: ${new Date(updatedAt).toLocaleString()}`)\n }\n\n text += '\\n\\n' + lines.join('\\n')\n\n return text\n}\n\n/**\n * Check if session can be moved to target project\n * Returns false if source and target are the same project\n */\nexport const canMoveSession = (sourceProject: string, targetProject: string): boolean => {\n return sourceProject !== targetProject\n}\n","/**\n * Project-level utilities\n */\nimport type { Project } from './types.js'\nimport { pathToFolderName } from './paths.js'\n\n/**\n * Sort projects with priority:\n * 1. Current project (if specified)\n * 2. Current user's home directory subpaths\n * 3. Others (alphabetically by displayName)\n */\nexport const sortProjects = (\n projects: Project[],\n options: {\n currentProjectName?: string | null\n homeDir?: string\n filterEmpty?: boolean\n } = {}\n): Project[] => {\n const { currentProjectName, homeDir, filterEmpty = true } = options\n\n const filtered = filterEmpty ? projects.filter((p) => p.sessionCount > 0) : projects\n\n // Convert homeDir to folder name format for comparison with project.name\n // e.g., \"/Users/david\" -> \"-Users-david\"\n const homeDirPrefix = homeDir ? pathToFolderName(homeDir) : null\n\n return filtered.sort((a, b) => {\n // Current project always first\n if (currentProjectName) {\n if (a.name === currentProjectName) return -1\n if (b.name === currentProjectName) return 1\n }\n\n // Then prioritize current user's home directory paths\n // Compare using project.name (folder name format) with homeDirPrefix\n if (homeDirPrefix) {\n const aIsUserHome = a.name.startsWith(homeDirPrefix)\n const bIsUserHome = b.name.startsWith(homeDirPrefix)\n if (aIsUserHome && !bIsUserHome) return -1\n if (!aIsUserHome && bIsUserHome) return 1\n }\n\n // Finally sort by display name\n return a.displayName.localeCompare(b.displayName)\n })\n}\n","/**\n * Shared constants and utilities for tree view rendering\n * Used by both VSCode Extension and Web UI\n */\n\n// ============================================================================\n// Tree Item Types\n// ============================================================================\n\n/**\n * Tree item type discriminator\n * Used for identifying node types in both VSCode TreeView and Web UI\n */\nexport type TreeItemType =\n | 'project'\n | 'session'\n | 'summaries-group'\n | 'todos-group'\n | 'agents-group'\n | 'summary'\n | 'todo'\n | 'agent'\n\n// ============================================================================\n// Icon Mappings (Framework-agnostic)\n// ============================================================================\n\n/**\n * Icon definition with both VSCode codicon and emoji variants\n */\ninterface IconDef {\n /** VSCode codicon name (without 'codicon-' prefix) */\n codicon: string\n /** Emoji for web/terminal display */\n emoji: string\n}\n\n/**\n * Todo status icon with optional color\n */\ninterface TodoIconDef extends IconDef {\n /** Optional color for VSCode ThemeColor */\n color?: 'green' | 'yellow'\n}\n\n/**\n * Icon mappings for all tree item types\n * - Use `.codicon` for VSCode Extension (ThemeIcon)\n * - Use `.emoji` for Web UI and terminal\n */\nexport const TREE_ICONS = {\n project: { codicon: 'folder', emoji: '📁' },\n session: { codicon: 'comment-discussion', emoji: '💬' },\n 'summaries-group': { codicon: 'history', emoji: '📝' },\n 'todos-group': { codicon: 'checklist', emoji: '📋' },\n 'agents-group': { codicon: 'hubot', emoji: '🤖' },\n summary: { codicon: 'note', emoji: '📝' },\n agent: { codicon: 'hubot', emoji: '🤖' },\n todo: {\n completed: { codicon: 'pass-filled', emoji: '✅', color: 'green' } as TodoIconDef,\n in_progress: { codicon: 'sync~spin', emoji: '🔄', color: 'yellow' } as TodoIconDef,\n pending: { codicon: 'circle-outline', emoji: '⭕' } as TodoIconDef,\n },\n} as const\n\n/**\n * Get todo icon based on status\n */\nexport const getTodoIcon = (status: 'pending' | 'in_progress' | 'completed'): TodoIconDef => {\n return TREE_ICONS.todo[status]\n}\n\n// ============================================================================\n// Tree Node ID Generation\n// ============================================================================\n\n/**\n * Generate unique tree node ID for drag & drop and state management\n * Format: `{type}:{projectName}:{sessionId}[:agentId][:itemIndex]`\n *\n * @example\n * generateTreeNodeId('session', 'my-project', 'abc123')\n * // => 'session:my-project:abc123'\n *\n * generateTreeNodeId('todo', 'my-project', 'abc123', 'agent-1', 0)\n * // => 'todo:my-project:abc123:agent-1:0'\n */\nexport const generateTreeNodeId = (\n type: TreeItemType,\n projectName: string,\n sessionId: string,\n agentId?: string,\n itemIndex?: number\n): string => {\n let id = `${type}:${projectName}:${sessionId}`\n if (agentId) id += `:${agentId}`\n if (itemIndex !== undefined) id += `:${itemIndex}`\n return id\n}\n\n/**\n * Parse tree node ID back to components\n */\nexport const parseTreeNodeId = (\n id: string\n): {\n type: TreeItemType\n projectName: string\n sessionId: string\n agentId?: string\n itemIndex?: number\n} | null => {\n const parts = id.split(':')\n if (parts.length < 3) return null\n\n const result: ReturnType<typeof parseTreeNodeId> = {\n type: parts[0] as TreeItemType,\n projectName: parts[1],\n sessionId: parts[2],\n }\n\n if (parts.length >= 4 && parts[3]) {\n result.agentId = parts[3]\n }\n\n if (parts.length >= 5 && parts[4]) {\n const idx = parseInt(parts[4], 10)\n if (!isNaN(idx)) {\n result.itemIndex = idx\n }\n }\n\n return result\n}\n","/**\n * Agent file management utilities\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir } from './paths.js'\nimport { tryParseJsonLine } from './utils.js'\nimport type { Message } from './types.js'\n\n// Find agent files linked to a session\nexport const findLinkedAgents = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const agentFiles = files.filter((f) => f.startsWith('agent-') && f.endsWith('.jsonl'))\n\n const linkedAgents: string[] = []\n\n for (const agentFile of agentFiles) {\n const filePath = path.join(projectPath, agentFile)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const firstLine = content.split('\\n')[0]\n\n if (firstLine) {\n try {\n const parsed = JSON.parse(firstLine) as { sessionId?: string }\n if (parsed.sessionId === sessionId) {\n linkedAgents.push(agentFile.replace('.jsonl', ''))\n }\n } catch {\n // Skip invalid JSON\n }\n }\n }\n\n return linkedAgents\n })\n\n// Internal type for orphan agents with file path and line count\ntype OrphanAgentWithPath = {\n agentId: string\n sessionId: string\n filePath: string\n lineCount: number\n}\n\n// Internal function to find orphan agents with file paths (used by delete)\nconst findOrphanAgentsWithPaths = (projectName: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n\n // Collect session IDs from .jsonl files (excluding agent files)\n const sessionIds = new Set(\n files\n .filter((f) => !f.startsWith('agent-') && f.endsWith('.jsonl'))\n .map((f) => f.replace('.jsonl', ''))\n )\n\n const orphanAgents: OrphanAgentWithPath[] = []\n\n // Helper to check if an agent file is orphan\n const checkAgentFile = async (\n filePath: string\n ): Promise<{ agentId: string; sessionId: string; lineCount: number } | null> => {\n try {\n const content = await fs.readFile(filePath, 'utf-8')\n const lines = content.split('\\n').filter((l) => l.trim())\n const firstLine = lines[0]\n if (!firstLine) return null\n\n const parsed = JSON.parse(firstLine) as { sessionId?: string }\n if (parsed.sessionId && !sessionIds.has(parsed.sessionId)) {\n const fileName = path.basename(filePath)\n return {\n agentId: fileName.replace('.jsonl', ''),\n sessionId: parsed.sessionId,\n lineCount: lines.length,\n }\n }\n } catch {\n // Skip invalid files\n }\n return null\n }\n\n // 1. Check agent files in project root\n const rootAgentFiles = files.filter((f) => f.startsWith('agent-') && f.endsWith('.jsonl'))\n for (const agentFile of rootAgentFiles) {\n const filePath = path.join(projectPath, agentFile)\n const orphan = yield* Effect.tryPromise(() => checkAgentFile(filePath))\n if (orphan) {\n orphanAgents.push({ ...orphan, filePath })\n }\n }\n\n // 2. Check subagents folders inside session directories\n for (const entry of files) {\n // Check if it's a directory (potential session folder)\n const entryPath = path.join(projectPath, entry)\n const stat = yield* Effect.tryPromise(() => fs.stat(entryPath).catch(() => null))\n\n if (stat?.isDirectory() && !entry.startsWith('.')) {\n // Check for subagents folder\n const subagentsPath = path.join(entryPath, 'subagents')\n const subagentsExists = yield* Effect.tryPromise(() =>\n fs\n .stat(subagentsPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (subagentsExists) {\n const subagentFiles = yield* Effect.tryPromise(() =>\n fs.readdir(subagentsPath).catch(() => [])\n )\n\n for (const subagentFile of subagentFiles) {\n if (subagentFile.startsWith('agent-') && subagentFile.endsWith('.jsonl')) {\n const filePath = path.join(subagentsPath, subagentFile)\n const orphan = yield* Effect.tryPromise(() => checkAgentFile(filePath))\n if (orphan) {\n orphanAgents.push({ ...orphan, filePath })\n }\n }\n }\n }\n }\n }\n\n return orphanAgents\n })\n\n// Find orphan agent files (agents whose parent session no longer exists)\n// Checks both:\n// 1. agent-*.jsonl files in project root\n// 2. agent-*.jsonl files in <session-id>/subagents/ folders\nexport const findOrphanAgents = (projectName: string) =>\n Effect.gen(function* () {\n const orphans = yield* findOrphanAgentsWithPaths(projectName)\n // Return without filePath for API compatibility\n return orphans.map(({ agentId, sessionId }) => ({ agentId, sessionId }))\n })\n\n// Delete orphan agent files\n// - Files with ≤2 lines (warmup only): permanently delete\n// - Files with >2 lines: move to .bak for recovery\n// - Clean up empty subagents folders and empty session folders\nexport const deleteOrphanAgents = (projectName: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const orphans = yield* findOrphanAgentsWithPaths(projectName)\n\n const deletedAgents: string[] = []\n const backedUpAgents: string[] = []\n const cleanedFolders: string[] = []\n let backupDirCreated = false\n\n // Track folders that might become empty\n const foldersToCheck = new Set<string>()\n\n for (const orphan of orphans) {\n // Track parent folders for cleanup\n const parentDir = path.dirname(orphan.filePath)\n if (parentDir.endsWith('/subagents') || parentDir.endsWith('\\\\subagents')) {\n foldersToCheck.add(parentDir)\n }\n\n if (orphan.lineCount <= 2) {\n // Warmup-only files: permanently delete\n yield* Effect.tryPromise(() => fs.unlink(orphan.filePath))\n deletedAgents.push(orphan.agentId)\n } else {\n // Files with actual data: move to .bak\n if (!backupDirCreated) {\n const backupDir = path.join(projectPath, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(backupDir, { recursive: true }))\n backupDirCreated = true\n }\n const backupDir = path.join(projectPath, '.bak')\n const agentBackupPath = path.join(backupDir, `${orphan.agentId}.jsonl`)\n yield* Effect.tryPromise(() => fs.rename(orphan.filePath, agentBackupPath))\n backedUpAgents.push(orphan.agentId)\n }\n }\n\n // Clean up empty subagents folders and their parent session folders\n for (const subagentsDir of foldersToCheck) {\n const isEmpty = yield* Effect.tryPromise(async () => {\n const files = await fs.readdir(subagentsDir)\n return files.length === 0\n })\n\n if (isEmpty) {\n // Remove empty subagents folder\n yield* Effect.tryPromise(() => fs.rmdir(subagentsDir))\n cleanedFolders.push(subagentsDir)\n\n // Check if parent session folder is also empty\n const sessionDir = path.dirname(subagentsDir)\n const sessionDirEmpty = yield* Effect.tryPromise(async () => {\n const files = await fs.readdir(sessionDir)\n return files.length === 0\n })\n\n if (sessionDirEmpty) {\n yield* Effect.tryPromise(() => fs.rmdir(sessionDir))\n cleanedFolders.push(sessionDir)\n }\n }\n }\n\n return {\n success: true,\n deletedAgents,\n backedUpAgents,\n cleanedFolders,\n deletedCount: deletedAgents.length,\n backedUpCount: backedUpAgents.length,\n cleanedFolderCount: cleanedFolders.length,\n count: deletedAgents.length + backedUpAgents.length,\n }\n })\n\n// Load agent messages from agent session file\nexport const loadAgentMessages = (\n projectName: string,\n _sessionId: string, // Reserved for future validation\n agentId: string\n) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n // Agent files are stored as agent-<agentId>.jsonl in project directory\n const agentFilePath = path.join(projectPath, `${agentId}.jsonl`)\n\n const content = yield* Effect.tryPromise(() => fs.readFile(agentFilePath, 'utf-8'))\n\n const lines = content.split('\\n').filter((line) => line.trim())\n const messages: Message[] = []\n\n for (let i = 0; i < lines.length; i++) {\n const parsed = tryParseJsonLine<Message>(lines[i], i + 1, agentFilePath)\n if (!parsed) continue\n // Skip header line (contains sessionId)\n if ('sessionId' in parsed && !('type' in parsed)) {\n continue\n }\n messages.push(parsed)\n }\n\n return messages\n })\n","/**\n * Todo file management utilities\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir, getTodosDir } from './paths.js'\nimport type { TodoItem, SessionTodos } from './types.js'\n\n// Find linked todo files for a session and its agents\n// Scans todos directory for files matching session pattern\nexport const findLinkedTodos = (sessionId: string, agentIds: string[] = []) =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n\n // Check if todos directory exists\n const exists = yield* Effect.tryPromise(() =>\n fs\n .access(todosDir)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!exists) {\n return {\n sessionId,\n sessionTodos: [],\n agentTodos: [],\n hasTodos: false,\n }\n }\n\n // Read session's own todo file\n const sessionTodoPath = path.join(todosDir, `${sessionId}.json`)\n let sessionTodos: TodoItem[] = []\n\n const sessionTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(sessionTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (sessionTodoExists) {\n const content = yield* Effect.tryPromise(() => fs.readFile(sessionTodoPath, 'utf-8'))\n try {\n sessionTodos = JSON.parse(content) as TodoItem[]\n } catch {\n // Invalid JSON, treat as empty\n }\n }\n\n // Scan todos directory for agent todo files matching this session\n const allFiles = yield* Effect.tryPromise(() => fs.readdir(todosDir))\n const agentTodoPattern = new RegExp(`^${sessionId}-agent-([a-f0-9-]+)\\\\.json$`)\n\n // Collect agent IDs from both provided list and directory scan\n const discoveredAgentIds = new Set<string>(agentIds)\n for (const file of allFiles) {\n const match = file.match(agentTodoPattern)\n if (match) {\n discoveredAgentIds.add(`agent-${match[1]}`)\n }\n }\n\n // Read agent todo files\n const agentTodos: { agentId: string; todos: TodoItem[] }[] = []\n\n for (const agentId of discoveredAgentIds) {\n // Agent todo files are named: {sessionId}-agent-{shortAgentId}.json\n const shortAgentId = agentId.replace('agent-', '')\n const agentTodoPath = path.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`)\n\n const agentTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(agentTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (agentTodoExists) {\n const content = yield* Effect.tryPromise(() => fs.readFile(agentTodoPath, 'utf-8'))\n try {\n const todos = JSON.parse(content) as TodoItem[]\n if (todos.length > 0) {\n agentTodos.push({ agentId, todos })\n }\n } catch {\n // Invalid JSON, skip\n }\n }\n }\n\n const hasTodos = sessionTodos.length > 0 || agentTodos.some((at) => at.todos.length > 0)\n\n return {\n sessionId,\n sessionTodos,\n agentTodos,\n hasTodos,\n } as SessionTodos\n })\n\n// Check if session has any todos (quick check)\n// Scans todos directory for files matching session pattern\nexport const sessionHasTodos = (sessionId: string, agentIds: string[] = []) =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n\n // Check if todos directory exists\n const exists = yield* Effect.tryPromise(() =>\n fs\n .access(todosDir)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!exists) return false\n\n // Check session's own todo file\n const sessionTodoPath = path.join(todosDir, `${sessionId}.json`)\n const sessionTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(sessionTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (sessionTodoExists) {\n const content = yield* Effect.tryPromise(() => fs.readFile(sessionTodoPath, 'utf-8'))\n try {\n const todos = JSON.parse(content) as TodoItem[]\n if (todos.length > 0) return true\n } catch {\n // Invalid JSON, continue\n }\n }\n\n // Scan todos directory for agent todo files matching this session\n const allFiles = yield* Effect.tryPromise(() => fs.readdir(todosDir))\n const agentTodoPattern = new RegExp(`^${sessionId}-agent-([a-f0-9-]+)\\\\.json$`)\n\n // Collect agent IDs from both provided list and directory scan\n const discoveredAgentIds = new Set<string>(agentIds)\n for (const file of allFiles) {\n const match = file.match(agentTodoPattern)\n if (match) {\n discoveredAgentIds.add(`agent-${match[1]}`)\n }\n }\n\n // Check agent todo files\n for (const agentId of discoveredAgentIds) {\n const shortAgentId = agentId.replace('agent-', '')\n const agentTodoPath = path.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`)\n\n const agentTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(agentTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (agentTodoExists) {\n const content = yield* Effect.tryPromise(() => fs.readFile(agentTodoPath, 'utf-8'))\n try {\n const todos = JSON.parse(content) as TodoItem[]\n if (todos.length > 0) return true\n } catch {\n // Invalid JSON, continue\n }\n }\n }\n\n return false\n })\n\n// Delete linked todo files for a session (move to .bak)\nexport const deleteLinkedTodos = (sessionId: string, agentIds: string[]) =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n\n // Check if todos directory exists\n const exists = yield* Effect.tryPromise(() =>\n fs\n .access(todosDir)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!exists) return { deletedCount: 0 }\n\n // Create backup directory\n const backupDir = path.join(todosDir, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(backupDir, { recursive: true }))\n\n let deletedCount = 0\n\n // Delete session's own todo file\n const sessionTodoPath = path.join(todosDir, `${sessionId}.json`)\n const sessionTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(sessionTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (sessionTodoExists) {\n const backupPath = path.join(backupDir, `${sessionId}.json`)\n yield* Effect.tryPromise(() => fs.rename(sessionTodoPath, backupPath))\n deletedCount++\n }\n\n // Delete agent todo files\n for (const agentId of agentIds) {\n const shortAgentId = agentId.replace('agent-', '')\n const agentTodoPath = path.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`)\n\n const agentTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(agentTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (agentTodoExists) {\n const backupPath = path.join(backupDir, `${sessionId}-agent-${shortAgentId}.json`)\n yield* Effect.tryPromise(() => fs.rename(agentTodoPath, backupPath))\n deletedCount++\n }\n }\n\n return { deletedCount }\n })\n\n// Find all orphan todo files (session no longer exists)\nexport const findOrphanTodos = () =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n const sessionsDir = getSessionsDir()\n\n // Check if directories exist\n const [todosExists, sessionsExists] = yield* Effect.all([\n Effect.tryPromise(() =>\n fs\n .access(todosDir)\n .then(() => true)\n .catch(() => false)\n ),\n Effect.tryPromise(() =>\n fs\n .access(sessionsDir)\n .then(() => true)\n .catch(() => false)\n ),\n ])\n\n if (!todosExists || !sessionsExists) return []\n\n // Get all todo files\n const todoFiles = yield* Effect.tryPromise(() => fs.readdir(todosDir))\n const jsonFiles = todoFiles.filter((f) => f.endsWith('.json'))\n\n // Build set of all valid session IDs across all projects\n const validSessionIds = new Set<string>()\n const projectEntries = yield* Effect.tryPromise(() =>\n fs.readdir(sessionsDir, { withFileTypes: true })\n )\n\n for (const entry of projectEntries) {\n if (!entry.isDirectory() || entry.name.startsWith('.')) continue\n const projectPath = path.join(sessionsDir, entry.name)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n for (const f of files) {\n if (f.endsWith('.jsonl') && !f.startsWith('agent-')) {\n validSessionIds.add(f.replace('.jsonl', ''))\n }\n }\n }\n\n // Find orphan todo files\n const orphans: string[] = []\n for (const todoFile of jsonFiles) {\n // Parse session ID from todo filename\n // Format: {sessionId}.json or {sessionId}-agent-{agentId}.json\n const match = todoFile.match(/^([a-f0-9-]+)(?:-agent-[a-f0-9]+)?\\.json$/)\n if (match) {\n const sessionId = match[1]\n if (!validSessionIds.has(sessionId)) {\n orphans.push(todoFile)\n }\n }\n }\n\n return orphans\n })\n\n// Delete orphan todo files\nexport const deleteOrphanTodos = () =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n const orphans = yield* findOrphanTodos()\n\n if (orphans.length === 0) return { success: true, deletedCount: 0 }\n\n // Create backup directory\n const backupDir = path.join(todosDir, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(backupDir, { recursive: true }))\n\n let deletedCount = 0\n\n for (const orphan of orphans) {\n const filePath = path.join(todosDir, orphan)\n const backupPath = path.join(backupDir, orphan)\n yield* Effect.tryPromise(() => fs.rename(filePath, backupPath))\n deletedCount++\n }\n\n return { success: true, deletedCount }\n })\n","/**\n * Project listing operations\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir, folderNameToPath } from '../paths.js'\nimport type { Project } from '../types.js'\n\n// List all project directories\nexport const listProjects = Effect.gen(function* () {\n const sessionsDir = getSessionsDir()\n\n const exists = yield* Effect.tryPromise(() =>\n fs\n .access(sessionsDir)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!exists) {\n return [] as Project[]\n }\n\n const entries = yield* Effect.tryPromise(() => fs.readdir(sessionsDir, { withFileTypes: true }))\n\n const projects = yield* Effect.all(\n entries\n .filter((e) => e.isDirectory() && !e.name.startsWith('.'))\n .map((entry) =>\n Effect.gen(function* () {\n const projectPath = path.join(sessionsDir, entry.name)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n // Exclude agent- files (subagent logs)\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n const displayName = yield* Effect.tryPromise(() => folderNameToPath(entry.name))\n\n return {\n name: entry.name,\n displayName,\n path: projectPath,\n sessionCount: sessionFiles.length,\n } satisfies Project\n })\n ),\n { concurrency: 10 }\n )\n\n return projects\n})\n","/**\n * Session CRUD operations\n */\nimport { Effect, pipe, Array as A, Option as O } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport * as crypto from 'node:crypto'\nimport { getSessionsDir } from '../paths.js'\nimport {\n extractTitle,\n isContinuationSummary,\n cleanupSplitFirstMessage,\n parseJsonlLines,\n readJsonlFile,\n} from '../utils.js'\nimport { validateChain, autoRepairChain } from './validation.js'\nimport { findLinkedAgents } from '../agents.js'\nimport { deleteLinkedTodos } from '../todos.js'\nimport type {\n Message,\n SessionMeta,\n DeleteSessionResult,\n RenameSessionResult,\n SplitSessionResult,\n MoveSessionResult,\n} from '../types.js'\n\n// Update summary message in session\nexport const updateSessionSummary = (projectName: string, sessionId: string, newSummary: string) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const messages = yield* readJsonlFile<Record<string, unknown>>(filePath, { strict: true })\n\n // Find existing summary message\n const summaryIdx = messages.findIndex((m) => m.type === 'summary')\n\n if (summaryIdx >= 0) {\n // Update existing summary\n messages[summaryIdx] = { ...messages[summaryIdx], summary: newSummary }\n } else {\n // Add new summary at the beginning\n const firstUserMsg = messages.find((m) => m.type === 'user')\n const summaryMsg = {\n type: 'summary',\n summary: newSummary,\n leafUuid: (firstUserMsg as Message | undefined)?.uuid ?? null,\n }\n messages.unshift(summaryMsg)\n }\n\n const newContent = messages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n return { success: true }\n })\n\n// List sessions in a project\nexport const listSessions = (projectName: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n // Exclude agent- files (subagent logs)\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n\n const sessions = yield* Effect.all(\n sessionFiles.map((file) =>\n Effect.gen(function* () {\n const filePath = path.join(projectPath, file)\n const messages = yield* readJsonlFile<Message>(filePath)\n\n const sessionId = file.replace('.jsonl', '')\n\n // Filter only user/assistant messages for counting\n const userAssistantMessages = messages.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n )\n\n // Check if session has summary (for preserved sessions without user/assistant messages)\n const hasSummary = messages.some((m) => m.type === 'summary')\n\n const firstMessage = userAssistantMessages[0]\n const lastMessage = userAssistantMessages[userAssistantMessages.length - 1]\n\n // Extract title from first user message\n const title = pipe(\n messages,\n A.findFirst((m) => m.type === 'user'),\n O.map((m) => extractTitle(m.message)),\n O.getOrElse(() => (hasSummary ? '[Summary Only]' : `Session ${sessionId.slice(0, 8)}`))\n )\n\n // Extract currentSummary from first summary message\n const currentSummary = pipe(\n messages,\n A.findFirst((m) => m.type === 'summary'),\n O.map((m) => m.summary as string),\n O.getOrUndefined\n )\n\n // Extract customTitle from custom-title message\n const customTitle = pipe(\n messages,\n A.findFirst((m) => m.type === 'custom-title'),\n O.map((m) => (m as { customTitle?: string }).customTitle),\n O.flatMap(O.fromNullable),\n O.getOrUndefined\n )\n\n return {\n id: sessionId,\n projectName,\n title,\n customTitle,\n currentSummary,\n // If session has summary but no user/assistant messages, count as 1\n messageCount:\n userAssistantMessages.length > 0 ? userAssistantMessages.length : hasSummary ? 1 : 0,\n createdAt: firstMessage?.timestamp,\n updatedAt: lastMessage?.timestamp,\n } satisfies SessionMeta\n })\n ),\n { concurrency: 10 }\n )\n\n // Sort by newest first\n return sessions.sort((a, b) => {\n const dateA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0\n const dateB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0\n return dateB - dateA\n })\n })\n\n// Read session messages\nexport const readSession = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n return yield* readJsonlFile<Message>(filePath)\n })\n\nimport { deleteMessageWithChainRepair } from './validation.js'\n\n// Delete a message from session and repair parentUuid chain\n// Optional targetType parameter to specify which type to delete when uuid/messageId collision exists\nexport const deleteMessage = (\n projectName: string,\n sessionId: string,\n messageUuid: string,\n targetType?: 'file-history-snapshot' | 'summary'\n) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const messages = yield* readJsonlFile<Record<string, unknown>>(filePath, { strict: true })\n\n // Use the pure function for chain repair\n const result = deleteMessageWithChainRepair(messages, messageUuid, targetType)\n\n if (!result.deleted) {\n return { success: false, error: 'Message not found' }\n }\n\n const newContent = messages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n return { success: true, deletedMessage: result.deleted }\n })\n\n// Restore a deleted message at a specific index\nexport const restoreMessage = (\n projectName: string,\n sessionId: string,\n message: Record<string, unknown>,\n index: number\n) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const messages = yield* readJsonlFile<Record<string, unknown>>(filePath, { strict: true })\n\n const msgUuid = message.uuid ?? message.messageId\n if (!msgUuid) {\n return { success: false, error: 'Message has no uuid or messageId' }\n }\n\n // Find the message that currently has parentUuid pointing to restored message's parent\n // and update it to point to the restored message instead\n const restoredParentUuid = message.parentUuid as string | undefined\n for (const msg of messages) {\n if (msg.parentUuid === restoredParentUuid) {\n // This message was previously pointing to the deleted message's parent\n // Now it should point to the restored message\n msg.parentUuid = msgUuid\n break // Only one message should be affected\n }\n }\n\n // Insert message at the specified index (or at end if index is out of bounds)\n const insertIndex = Math.min(index, messages.length)\n messages.splice(insertIndex, 0, message)\n\n const newContent = messages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n return { success: true }\n })\n\n// Delete a session and its linked agent/todo files\nexport const deleteSession = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const sessionsDir = getSessionsDir()\n const projectPath = path.join(sessionsDir, projectName)\n const filePath = path.join(projectPath, `${sessionId}.jsonl`)\n\n // Find linked agents first (before any deletion)\n const linkedAgents = yield* findLinkedAgents(projectName, sessionId)\n\n // Check file size - if empty (0 bytes), just delete without backup\n const stat = yield* Effect.tryPromise(() => fs.stat(filePath))\n if (stat.size === 0) {\n yield* Effect.tryPromise(() => fs.unlink(filePath))\n // Still delete linked agents and todos for empty sessions\n const agentBackupDir = path.join(projectPath, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(agentBackupDir, { recursive: true }))\n for (const agentId of linkedAgents) {\n const agentPath = path.join(projectPath, `${agentId}.jsonl`)\n const agentBackupPath = path.join(agentBackupDir, `${agentId}.jsonl`)\n yield* Effect.tryPromise(() => fs.rename(agentPath, agentBackupPath).catch(() => {}))\n }\n yield* deleteLinkedTodos(sessionId, linkedAgents)\n return { success: true, deletedAgents: linkedAgents.length } satisfies DeleteSessionResult\n }\n\n // Create backup directory\n const backupDir = path.join(sessionsDir, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(backupDir, { recursive: true }))\n\n // Delete linked agent files (move to .bak in project folder)\n const agentBackupDir = path.join(projectPath, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(agentBackupDir, { recursive: true }))\n for (const agentId of linkedAgents) {\n const agentPath = path.join(projectPath, `${agentId}.jsonl`)\n const agentBackupPath = path.join(agentBackupDir, `${agentId}.jsonl`)\n yield* Effect.tryPromise(() => fs.rename(agentPath, agentBackupPath).catch(() => {}))\n }\n\n // Delete linked todo files\n const todosResult = yield* deleteLinkedTodos(sessionId, linkedAgents)\n\n // Move session to backup (format: project_name_session_id.jsonl)\n const backupPath = path.join(backupDir, `${projectName}_${sessionId}.jsonl`)\n yield* Effect.tryPromise(() => fs.rename(filePath, backupPath))\n\n return {\n success: true,\n backupPath,\n deletedAgents: linkedAgents.length,\n deletedTodos: todosResult.deletedCount,\n } satisfies DeleteSessionResult\n })\n\n// Rename session by updating custom-title and first summary\n// custom-title is stored in this session file\n// summary is stored in OTHER session files (where leafUuid points to this session's messages)\nexport const renameSession = (projectName: string, sessionId: string, newTitle: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const filePath = path.join(projectPath, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n\n if (lines.length === 0) {\n return { success: false, error: 'Empty session' } satisfies RenameSessionResult\n }\n\n const messages = parseJsonlLines<Record<string, unknown>>(lines, filePath, { strict: true })\n\n // Remove existing custom-title (may be at wrong position) and append to end\n const customTitleIdx = messages.findIndex((m) => m.type === 'custom-title')\n if (customTitleIdx >= 0) {\n messages.splice(customTitleIdx, 1)\n }\n messages.push({\n type: 'custom-title',\n customTitle: newTitle,\n sessionId,\n })\n\n const newContent = messages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n return { success: true } satisfies RenameSessionResult\n })\n\n// Move session from one project to another\nexport const moveSession = (\n sourceProject: string,\n sessionId: string,\n targetProject: string\n): Effect.Effect<MoveSessionResult, Error> =>\n Effect.gen(function* () {\n const sessionsDir = getSessionsDir()\n const sourcePath = path.join(sessionsDir, sourceProject)\n const targetPath = path.join(sessionsDir, targetProject)\n\n const sourceFile = path.join(sourcePath, `${sessionId}.jsonl`)\n const targetFile = path.join(targetPath, `${sessionId}.jsonl`)\n\n // Check source file exists\n const sourceExists = yield* Effect.tryPromise(() =>\n fs\n .access(sourceFile)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!sourceExists) {\n return { success: false, error: 'Source session not found' }\n }\n\n // Check target file does not exist\n const targetExists = yield* Effect.tryPromise(() =>\n fs\n .access(targetFile)\n .then(() => true)\n .catch(() => false)\n )\n\n if (targetExists) {\n return { success: false, error: 'Session already exists in target project' }\n }\n\n // Create target directory if needed\n yield* Effect.tryPromise(() => fs.mkdir(targetPath, { recursive: true }))\n\n // Find linked agents before moving\n const linkedAgents = yield* findLinkedAgents(sourceProject, sessionId)\n\n // Move session file\n yield* Effect.tryPromise(() => fs.rename(sourceFile, targetFile))\n\n // Move linked agent files\n for (const agentId of linkedAgents) {\n const sourceAgentFile = path.join(sourcePath, `${agentId}.jsonl`)\n const targetAgentFile = path.join(targetPath, `${agentId}.jsonl`)\n\n const agentExists = yield* Effect.tryPromise(() =>\n fs\n .access(sourceAgentFile)\n .then(() => true)\n .catch(() => false)\n )\n\n if (agentExists) {\n yield* Effect.tryPromise(() => fs.rename(sourceAgentFile, targetAgentFile))\n }\n }\n\n return { success: true }\n })\n\n// Split session at a specific message\n// Original session keeps the ID and contains messages FROM splitAtMessageUuid onwards (newer messages)\n// New session gets a new ID and contains messages BEFORE splitAtMessageUuid (older messages)\nexport const splitSession = (projectName: string, sessionId: string, splitAtMessageUuid: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const filePath = path.join(projectPath, `${sessionId}.jsonl`)\n\n // Parse all messages preserving their full structure\n const allMessages = yield* readJsonlFile<Message>(filePath, { strict: true })\n\n // Find the split point\n const splitIndex = allMessages.findIndex((m) => m.uuid === splitAtMessageUuid)\n if (splitIndex === -1) {\n return { success: false, error: 'Message not found' } satisfies SplitSessionResult\n }\n\n if (splitIndex === 0) {\n return { success: false, error: 'Cannot split at first message' } satisfies SplitSessionResult\n }\n\n // Generate new session ID for the OLD messages (before split point)\n const newSessionId = crypto.randomUUID()\n\n // Check if the split message is a continuation summary\n const splitMessage = allMessages[splitIndex]\n const shouldDuplicate = isContinuationSummary(splitMessage)\n\n // Split messages:\n // - keptMessages: from splitIndex onwards (stays in original session with original ID) - NEW messages\n // - movedMessages: before splitIndex (goes to new session with new ID) - OLD messages\n let keptMessages = allMessages.slice(splitIndex)\n let movedMessages: Message[]\n\n if (shouldDuplicate) {\n // Create a copy of the continuation message with new UUID for the new (old messages) session\n const duplicatedMessage: Message = {\n ...splitMessage,\n uuid: crypto.randomUUID(),\n sessionId: newSessionId,\n }\n movedMessages = [...allMessages.slice(0, splitIndex), duplicatedMessage]\n } else {\n movedMessages = allMessages.slice(0, splitIndex)\n }\n\n // Update kept messages: fix first message's parentUuid\n keptMessages = keptMessages.map((msg, index) => {\n let updated: Message = { ...msg }\n if (index === 0) {\n // First message of kept session should have no parent\n updated.parentUuid = null\n // Clean up first message content if it's a tool_result rejection\n updated = cleanupSplitFirstMessage(updated)\n }\n return updated\n })\n\n // Update moved messages with new sessionId\n const updatedMovedMessages: Message[] = movedMessages.map((msg) => ({\n ...msg,\n sessionId: newSessionId,\n }))\n\n // Write kept messages (newer) to original file (keeps original ID)\n const keptContent = keptMessages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, keptContent, 'utf-8'))\n\n // Write moved messages (older) to new session file\n const newFilePath = path.join(projectPath, `${newSessionId}.jsonl`)\n const newContent = updatedMovedMessages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(newFilePath, newContent, 'utf-8'))\n\n // Update linked agent files that reference the old sessionId\n // Agents related to OLD messages should be moved to new session\n const agentFiles = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const agentJsonlFiles = agentFiles.filter((f) => f.startsWith('agent-') && f.endsWith('.jsonl'))\n\n for (const agentFile of agentJsonlFiles) {\n const agentPath = path.join(projectPath, agentFile)\n const agentContent = yield* Effect.tryPromise(() => fs.readFile(agentPath, 'utf-8'))\n const agentLines = agentContent.trim().split('\\n').filter(Boolean)\n\n if (agentLines.length === 0) continue\n\n const agentMessages = parseJsonlLines<Record<string, unknown>>(agentLines, agentPath, {\n strict: true,\n })\n const firstAgentMsg = agentMessages[0] as { sessionId?: string }\n\n // If this agent belongs to the original session, check if it should be moved\n if (firstAgentMsg.sessionId === sessionId) {\n // Check if any message in MOVED (old) messages is related to this agent\n const agentId = agentFile.replace('agent-', '').replace('.jsonl', '')\n const isRelatedToMoved = movedMessages.some(\n (msg) => (msg as { agentId?: string }).agentId === agentId\n )\n\n if (isRelatedToMoved) {\n // Update all messages in this agent file to reference new sessionId\n const updatedAgentMessages = agentMessages.map((msg) =>\n JSON.stringify({ ...msg, sessionId: newSessionId })\n )\n const updatedAgentContent = updatedAgentMessages.join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(agentPath, updatedAgentContent, 'utf-8'))\n }\n }\n }\n\n return {\n success: true,\n newSessionId,\n newSessionPath: newFilePath,\n movedMessageCount: movedMessages.length,\n duplicatedSummary: shouldDuplicate,\n } satisfies SplitSessionResult\n })\n\n// Repair broken parentUuid chain in a session\nexport const repairChain = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const messages = yield* readJsonlFile<Message>(filePath, { strict: true })\n\n // Validate before repair\n const beforeResult = validateChain(messages)\n\n // Repair chain\n const repairCount = autoRepairChain(messages)\n\n if (repairCount > 0) {\n const newContent = messages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n }\n\n return {\n success: true,\n repairCount,\n errorsBefore: beforeResult.errors.length,\n errorsAfter: validateChain(messages).errors.length,\n }\n })\n","/**\n * Session message chain validation utilities\n *\n * Validates:\n * 1. parentUuid chain integrity (skip file-history-snapshot)\n * 2. tool_use_id / tool_result matching\n */\n\nexport interface ChainError {\n type: 'broken_chain' | 'orphan_parent'\n uuid: string\n line: number\n parentUuid: string | null\n expectedParent?: string\n}\n\nexport interface ToolUseResultError {\n type: 'orphan_tool_result'\n uuid: string\n line: number\n toolUseId: string\n}\n\nexport interface ProgressError {\n type: 'unwanted_progress'\n line: number\n hookEvent?: string\n hookName?: string\n messageType?: string\n}\n\nexport interface ValidationResult {\n valid: boolean\n errors: (ChainError | ToolUseResultError | ProgressError)[]\n}\n\ninterface MessageContent {\n type?: string\n id?: string\n tool_use_id?: string\n}\n\ninterface MessagePayload {\n role?: string\n content?: unknown // Can be string | MessageContent[] | etc.\n}\n\nexport interface GenericMessage {\n type?: string\n uuid?: string\n parentUuid?: string | null\n logicalParentUuid?: string | null\n message?: MessagePayload\n messageId?: string\n}\n\n/**\n * Validate parentUuid chain for messages\n *\n * Rules:\n * - Skip file-history-snapshot type (has no uuid, uses messageId instead)\n * - Skip messages without uuid\n * - First message can have null parentUuid\n * - Subsequent messages must have valid parentUuid pointing to existing uuid\n */\nexport function validateChain(\n messages: readonly GenericMessage[]\n): ValidationResult & { errors: ChainError[] } {\n const errors: ChainError[] = []\n\n // Collect all uuids for validation\n const uuids = new Set<string>()\n for (const msg of messages) {\n if (msg.uuid) {\n uuids.add(msg.uuid)\n }\n }\n\n // Track first message with uuid\n let foundFirstMessage = false\n\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]\n\n // Skip file-history-snapshot and summary (no uuid chain participation)\n if (msg.type === 'file-history-snapshot' || msg.type === 'summary') {\n continue\n }\n\n // Skip messages without uuid\n if (!msg.uuid) {\n continue\n }\n\n // First message can have null parentUuid or orphan_parent (compacted session)\n // Only undefined is an error for first message\n if (!foundFirstMessage) {\n foundFirstMessage = true\n // undefined is an error\n if (msg.parentUuid === undefined) {\n errors.push({\n type: 'broken_chain',\n uuid: msg.uuid,\n line: i + 1,\n parentUuid: null,\n })\n continue\n }\n // null or orphan_parent is OK for first message\n continue\n }\n\n // Check for broken chain (null/undefined parentUuid for non-first message)\n // Skip if logicalParentUuid exists (e.g., compact_boundary messages)\n if (msg.parentUuid === null || msg.parentUuid === undefined) {\n if (!msg.logicalParentUuid) {\n errors.push({\n type: 'broken_chain',\n uuid: msg.uuid,\n line: i + 1,\n parentUuid: null,\n })\n }\n continue\n }\n\n // Check for orphan parent (parentUuid pointing to non-existent uuid)\n if (!uuids.has(msg.parentUuid)) {\n errors.push({\n type: 'orphan_parent',\n uuid: msg.uuid,\n line: i + 1,\n parentUuid: msg.parentUuid,\n })\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n }\n}\n\n/**\n * Validate for unwanted progress messages (hook outputs)\n *\n * Only 'Stop' hookEvent is treated as an error (should be removed)\n */\nexport function validateProgressMessages(\n messages: readonly GenericMessage[]\n): ValidationResult & { errors: ProgressError[] } {\n const errors: ProgressError[] = []\n\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]\n if (msg.type === 'progress') {\n const progressMsg = msg as GenericMessage & {\n hookEvent?: string\n hookName?: string\n data?: { hookEvent?: string; hookName?: string }\n }\n // Support both flat (hookEvent) and nested (data.hookEvent) formats\n const hookEvent = progressMsg.hookEvent ?? progressMsg.data?.hookEvent\n const hookName = progressMsg.hookName ?? progressMsg.data?.hookName\n\n // 'Stop' hookEvent is an error\n if (hookEvent === 'Stop') {\n errors.push({\n type: 'unwanted_progress',\n line: i + 1,\n hookEvent,\n hookName,\n })\n }\n } else if (msg.type === 'saved_hook_context') {\n errors.push({\n type: 'unwanted_progress',\n line: i + 1,\n messageType: 'saved_hook_context',\n })\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n }\n}\n\n/**\n * Validate tool_use_id / tool_result matching\n *\n * Rules:\n * - All tool_result blocks must have a corresponding tool_use block in the session\n * - tool_use blocks are collected from all messages (not just previous)\n */\nexport function validateToolUseResult(\n messages: readonly GenericMessage[]\n): ValidationResult & { errors: ToolUseResultError[] } {\n const errors: ToolUseResultError[] = []\n\n // Collect all tool_use IDs\n const toolUseIds = new Set<string>()\n for (const msg of messages) {\n const content = msg.message?.content\n if (Array.isArray(content)) {\n for (const item of content as MessageContent[]) {\n if (item.type === 'tool_use' && item.id) {\n toolUseIds.add(item.id)\n }\n }\n }\n }\n\n // Check all tool_result blocks have matching tool_use\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]\n const content = msg.message?.content\n if (!Array.isArray(content)) {\n continue\n }\n\n for (const item of content as MessageContent[]) {\n if (item.type === 'tool_result' && item.tool_use_id) {\n if (!toolUseIds.has(item.tool_use_id)) {\n errors.push({\n type: 'orphan_tool_result',\n uuid: msg.uuid || '',\n line: i + 1,\n toolUseId: item.tool_use_id,\n })\n }\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n }\n}\n\n/**\n * Auto-repair chain errors by linking messages to their previous message\n *\n * Fixes:\n * - broken_chain: Sets parentUuid to the previous message's uuid\n * - orphan_parent: Sets parentUuid to the previous message's uuid\n *\n * @param messages - Array of messages (will be mutated)\n * @returns Number of repairs made\n */\nexport function autoRepairChain<T extends GenericMessage>(messages: T[]): number {\n let repairCount = 0\n\n // Build uuid set for validation\n const uuids = new Set<string>()\n for (const msg of messages) {\n if (msg.uuid) {\n uuids.add(msg.uuid)\n }\n }\n\n // Track last valid uuid and state\n let lastUuid: string | null = null\n let isFirstWithUuid = true\n\n for (const msg of messages) {\n // Skip file-history-snapshot and summary (no uuid chain participation)\n if (msg.type === 'file-history-snapshot' || msg.type === 'summary') {\n continue\n }\n\n // Skip messages without uuid\n if (!msg.uuid) {\n continue\n }\n\n // First message with uuid: only fix undefined -> null\n // null and orphan_parent are valid for first message (compacted session)\n if (isFirstWithUuid) {\n isFirstWithUuid = false\n if (msg.parentUuid === undefined) {\n msg.parentUuid = null\n repairCount++\n }\n lastUuid = msg.uuid\n continue\n }\n\n // Check for broken_chain (null/undefined parentUuid)\n if (msg.parentUuid === null || msg.parentUuid === undefined) {\n if (lastUuid) {\n msg.parentUuid = lastUuid\n repairCount++\n }\n }\n // Check for orphan_parent (parentUuid points to non-existent uuid)\n else if (!uuids.has(msg.parentUuid)) {\n if (lastUuid) {\n msg.parentUuid = lastUuid\n repairCount++\n }\n }\n\n lastUuid = msg.uuid\n }\n\n return repairCount\n}\n\n/**\n * Repair parentUuid chain after removing messages\n *\n * When a message is removed, any message that had the removed message as its\n * parentUuid needs to be updated to point to the removed message's parentUuid.\n * Handles chained removals (e.g., a -> p1 -> p2 -> b, removing p1 and p2 should result in a -> b).\n *\n * @param messages - Array of messages (will be mutated)\n * @param removedMessages - Messages that were removed (need uuid and parentUuid)\n */\nexport function repairParentUuidChain<T extends GenericMessage>(\n messages: T[],\n removedMessages: T[]\n): void {\n // Build a map of removed uuid -> parentUuid for chain resolution\n const removedMap = new Map<string, string | null | undefined>()\n for (const removed of removedMessages) {\n if (removed.uuid && removed.type !== 'file-history-snapshot') {\n removedMap.set(removed.uuid, removed.parentUuid)\n }\n }\n\n // Resolve final parent by following the chain through removed messages\n const resolveParent = (parentUuid: string | null | undefined): string | null | undefined => {\n let current = parentUuid\n while (current && removedMap.has(current)) {\n current = removedMap.get(current)\n }\n return current\n }\n\n // Update all messages pointing to removed messages\n for (const msg of messages) {\n if (msg.parentUuid && removedMap.has(msg.parentUuid)) {\n msg.parentUuid = resolveParent(msg.parentUuid)\n }\n }\n}\n\n/**\n * Delete a message and repair the parentUuid chain\n *\n * This is a pure function for client-side use (without file I/O)\n * Server-side deleteMessage in crud.ts uses similar logic with file operations\n *\n * @param messages - Array of messages (will be mutated)\n * @param targetId - uuid, messageId, or leafUuid of message to delete\n * @param targetType - Optional: 'file-history-snapshot' or 'summary' to disambiguate collisions\n * @returns Object with deleted message and messages to also delete (orphan tool_results)\n */\n// Helper: find target message index by type or priority\nfunction findTargetIndex<T extends GenericMessage>(\n messages: T[],\n targetId: string,\n targetType?: 'file-history-snapshot' | 'summary'\n): number {\n if (targetType === 'file-history-snapshot') {\n return messages.findIndex((m) => m.type === 'file-history-snapshot' && m.messageId === targetId)\n }\n if (targetType === 'summary') {\n return messages.findIndex(\n (m) => (m as GenericMessage & { leafUuid?: string }).leafUuid === targetId\n )\n }\n // Priority: uuid > leafUuid > messageId\n let idx = messages.findIndex((m) => m.uuid === targetId)\n if (idx === -1) {\n idx = messages.findIndex(\n (m) => (m as GenericMessage & { leafUuid?: string }).leafUuid === targetId\n )\n }\n if (idx === -1) {\n idx = messages.findIndex((m) => m.type === 'file-history-snapshot' && m.messageId === targetId)\n }\n return idx\n}\n\n// Helper: check if user message contains tool_result matching any toolUseId\nfunction hasMatchingToolResult(msg: GenericMessage, toolUseIds: string[]): boolean {\n if (msg.type !== 'user') return false\n const content = msg.message?.content\n if (!Array.isArray(content)) return false\n return (content as MessageContent[]).some(\n (item) =>\n item.type === 'tool_result' && item.tool_use_id && toolUseIds.includes(item.tool_use_id)\n )\n}\n\nexport function deleteMessageWithChainRepair<T extends GenericMessage>(\n messages: T[],\n targetId: string,\n targetType?: 'file-history-snapshot' | 'summary'\n): { deleted: T | null; alsoDeleted: T[] } {\n const targetIndex = findTargetIndex(messages, targetId, targetType)\n\n if (targetIndex === -1) {\n return { deleted: null, alsoDeleted: [] }\n }\n\n const deletedMsg = messages[targetIndex]\n\n // Collect tool_use IDs from deleted message\n const toolUseIds: string[] = []\n if (deletedMsg.type === 'assistant') {\n const content = deletedMsg.message?.content\n if (Array.isArray(content)) {\n for (const item of content as MessageContent[]) {\n if (item.type === 'tool_use' && item.id) {\n toolUseIds.push(item.id)\n }\n }\n }\n }\n\n // Find orphan tool_result messages\n const toolResultIndices: number[] = []\n if (toolUseIds.length > 0) {\n for (let i = 0; i < messages.length; i++) {\n if (hasMatchingToolResult(messages[i], toolUseIds)) {\n toolResultIndices.push(i)\n }\n }\n }\n\n // All indices to delete (sorted descending for safe splice)\n const indicesToDelete = [targetIndex, ...toolResultIndices].sort((a, b) => b - a)\n\n // Collect messages to delete before removing\n const messagesToDelete = indicesToDelete.map((i) => messages[i])\n const alsoDeleted = toolResultIndices.map((i) => messages[i])\n\n // Repair parentUuid chain using the extracted utility\n repairParentUuidChain(messages, messagesToDelete)\n\n // Remove messages in reverse order\n for (const idx of indicesToDelete) {\n messages.splice(idx, 1)\n }\n\n return { deleted: deletedMsg, alsoDeleted }\n}\n","/**\n * Session tree data operations for VSCode extension\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir, folderNameToPath } from '../paths.js'\nimport {\n extractTextContent,\n extractTitle,\n getSessionSortTimestamp,\n isErrorSessionTitle,\n parseJsonlLines,\n tryParseJsonLine,\n} from '../utils.js'\nimport { findLinkedAgents } from '../agents.js'\nimport { findLinkedTodos } from '../todos.js'\nimport { loadTreeCache, writeTreeCache, validateCache, type TreeCache } from './cache.js'\nimport { createLogger } from '../logger.js'\nimport type {\n Message,\n SessionTreeData,\n SummaryInfo,\n SessionSortOptions,\n AgentInfo,\n ProjectTreeData,\n JsonlRecord,\n} from '../types.js'\n\nconst log = createLogger('tree')\n\n/**\n * Sort sessions based on sort options\n * Exported for testing purposes\n */\nexport const sortSessions = <T extends SessionTreeData>(\n sessions: T[],\n sort: SessionSortOptions\n): T[] => {\n return sessions.sort((a, b) => {\n let comparison = 0\n\n switch (sort.field) {\n case 'summary': {\n // By pre-calculated sort timestamp (oldest summary timestamp, matches official extension)\n comparison = a.sortTimestamp - b.sortTimestamp\n break\n }\n case 'modified': {\n // By file modification time\n comparison = (a.fileMtime ?? 0) - (b.fileMtime ?? 0)\n break\n }\n case 'created': {\n // By session creation time\n const createdA = a.createdAt ? new Date(a.createdAt).getTime() : 0\n const createdB = b.createdAt ? new Date(b.createdAt).getTime() : 0\n comparison = createdA - createdB\n break\n }\n case 'updated': {\n // By last message timestamp\n const updatedA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0\n const updatedB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0\n comparison = updatedA - updatedB\n break\n }\n case 'messageCount': {\n // By number of messages\n comparison = a.messageCount - b.messageCount\n break\n }\n case 'title': {\n // Alphabetical by display title (customTitle > currentSummary > title)\n const titleA = a.customTitle ?? a.currentSummary ?? a.title\n const titleB = b.customTitle ?? b.currentSummary ?? b.title\n comparison = titleA.localeCompare(titleB)\n break\n }\n }\n\n // Apply sort order (desc = newest/largest first)\n return sort.order === 'desc' ? -comparison : comparison\n })\n}\n\n// Internal helper for loading session tree data\nconst loadSessionTreeDataInternal = (\n projectName: string,\n sessionId: string,\n summariesByTargetSession?: Map<string, SummaryInfo[]>,\n fileMtime?: number\n) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const filePath = path.join(projectPath, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n const messages = parseJsonlLines<JsonlRecord>(lines, filePath)\n\n // Get summaries that TARGET this session (by leafUuid pointing to messages in this session)\n let summaries: SummaryInfo[]\n if (summariesByTargetSession) {\n // Project-wide loading: use pre-computed summaries targeting this session\n // Sort by timestamp ascending (oldest first), then by sourceFile descending (larger filename first)\n // Official extension shows the OLDEST summary as currentSummary, and prefers larger filenames when timestamps match\n summaries = [...(summariesByTargetSession.get(sessionId) ?? [])].sort((a, b) => {\n // 1. timestamp ascending (oldest first - official extension shows oldest as currentSummary)\n const timestampCmp = (a.timestamp ?? '').localeCompare(b.timestamp ?? '')\n if (timestampCmp !== 0) return timestampCmp\n // 2. sourceFile descending (larger filename first, e.g., b878041c > 355e3718)\n return (b.sourceFile ?? '').localeCompare(a.sourceFile ?? '')\n })\n } else {\n // Single session loading: need to search the entire project for summaries targeting this session\n summaries = []\n // Build uuid set for this session's messages\n const sessionUuids = new Set<string>()\n for (const msg of messages) {\n if (msg.uuid && typeof msg.uuid === 'string') {\n sessionUuids.add(msg.uuid)\n }\n }\n // Search all session files in the project for summaries with leafUuid pointing to this session\n const projectFiles = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const allJsonlFiles = projectFiles.filter((f) => f.endsWith('.jsonl'))\n for (const file of allJsonlFiles) {\n try {\n const otherFilePath = path.join(projectPath, file)\n const otherContent = yield* Effect.tryPromise(() => fs.readFile(otherFilePath, 'utf-8'))\n const otherLines = otherContent.trim().split('\\n').filter(Boolean)\n for (let i = 0; i < otherLines.length; i++) {\n const msg = tryParseJsonLine<JsonlRecord>(otherLines[i], i + 1, otherFilePath)\n if (!msg) continue\n if (\n msg.type === 'summary' &&\n typeof msg.summary === 'string' &&\n typeof msg.leafUuid === 'string' &&\n sessionUuids.has(msg.leafUuid)\n ) {\n // This summary's leafUuid points to a message in THIS session\n const targetMsg = messages.find((m) => m.uuid === msg.leafUuid)\n summaries.push({\n summary: msg.summary as string,\n leafUuid: msg.leafUuid,\n timestamp:\n (targetMsg?.timestamp as string) ?? (msg.timestamp as string | undefined),\n sourceFile: file,\n })\n }\n }\n } catch {\n // Skip unreadable files\n }\n }\n }\n // Sort by timestamp ascending (oldest first), then sourceFile descending\n // Official extension shows the OLDEST summary as currentSummary\n summaries.sort((a, b) => {\n const timestampCmp = (a.timestamp ?? '').localeCompare(b.timestamp ?? '')\n if (timestampCmp !== 0) return timestampCmp\n return (b.sourceFile ?? '').localeCompare(a.sourceFile ?? '')\n })\n\n // Find last compact_boundary\n let lastCompactBoundaryUuid: string | undefined\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]\n if (msg.type === 'system' && msg.subtype === 'compact_boundary') {\n lastCompactBoundaryUuid = msg.uuid as string\n break\n }\n }\n\n // Get first user message\n const firstUserMsg = messages.find((m) => m.type === 'user') as Message | undefined\n\n // customTitle is stored as separate custom-title type line\n const customTitleMsg = messages.find((m) => m.type === 'custom-title') as\n | { type: 'custom-title'; customTitle?: string }\n | undefined\n const customTitle = customTitleMsg?.customTitle\n\n // Get title from first user message\n const title = firstUserMsg\n ? extractTitle(firstUserMsg.message)\n : summaries.length > 0\n ? '[Summary Only]'\n : `Session ${sessionId.slice(0, 8)}`\n\n // Count user/assistant messages\n const userAssistantMessages = messages.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n )\n const firstMessage = userAssistantMessages[0]\n const lastMessage = userAssistantMessages[userAssistantMessages.length - 1]\n\n // Find linked agents\n const linkedAgentIds = yield* findLinkedAgents(projectName, sessionId)\n\n // Load agent info (message counts)\n const agents: AgentInfo[] = []\n for (const agentId of linkedAgentIds) {\n const agentPath = path.join(projectPath, `${agentId}.jsonl`)\n try {\n const agentContent = yield* Effect.tryPromise(() => fs.readFile(agentPath, 'utf-8'))\n const agentLines = agentContent.trim().split('\\n').filter(Boolean)\n const agentMsgs = agentLines.map((l) => JSON.parse(l) as JsonlRecord)\n const agentUserAssistant = agentMsgs.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n )\n\n // Try to extract agent name from first message\n let agentName: string | undefined\n const firstAgentMsg = agentMsgs.find((m) => m.type === 'user')\n if (firstAgentMsg) {\n const text = extractTextContent(firstAgentMsg.message as Message['message'])\n if (text) {\n agentName = extractTitle(text)\n }\n }\n\n agents.push({\n id: agentId,\n name: agentName,\n messageCount: agentUserAssistant.length,\n })\n } catch {\n // Agent file might not exist or be readable\n agents.push({\n id: agentId,\n messageCount: 0,\n })\n }\n }\n\n // Load todos\n const todos = yield* findLinkedTodos(sessionId, linkedAgentIds)\n\n // Pre-calculate sort timestamp for 'summary' field\n const createdAt = (firstMessage?.timestamp as string) ?? undefined\n const sortTimestamp = getSessionSortTimestamp({ summaries, createdAt })\n\n return {\n id: sessionId,\n projectName,\n title,\n customTitle,\n currentSummary: summaries[0]?.summary,\n messageCount:\n userAssistantMessages.length > 0\n ? userAssistantMessages.length\n : summaries.length > 0\n ? 1\n : 0,\n createdAt,\n updatedAt: (lastMessage?.timestamp as string) ?? undefined,\n fileMtime,\n sortTimestamp,\n summaries,\n agents,\n todos,\n lastCompactBoundaryUuid,\n } satisfies SessionTreeData\n })\n\n// Public wrapper for single session (without global uuid map, leafUuid lookup is limited)\nexport const loadSessionTreeData = (projectName: string, sessionId: string) =>\n loadSessionTreeDataInternal(projectName, sessionId, undefined)\n\n/** Default sort: by oldest summary timestamp (matches official extension) */\nconst DEFAULT_SORT: SessionSortOptions = { field: 'summary', order: 'desc' }\n\n// ============================================================================\n// Phase 1 helpers (UUID map + summaries)\n// ============================================================================\n\ninterface Phase1Result {\n globalUuidMap: Map<string, { sessionId: string; timestamp?: string }>\n allSummaries: Array<{\n summary: string\n leafUuid?: string\n timestamp?: string\n sourceFile: string\n }>\n}\n\n/** Build global UUID map and collect all summaries from all JSONL files in a project */\nconst buildPhase1 = (projectPath: string, allJsonlFiles: string[]) =>\n Effect.gen(function* () {\n const globalUuidMap = new Map<string, { sessionId: string; timestamp?: string }>()\n const allSummaries: Phase1Result['allSummaries'] = []\n\n yield* Effect.all(\n allJsonlFiles.map((file) =>\n Effect.gen(function* () {\n const filePath = path.join(projectPath, file)\n const fileSessionId = file.replace('.jsonl', '')\n try {\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n for (let i = 0; i < lines.length; i++) {\n const msg = tryParseJsonLine<JsonlRecord>(lines[i], i + 1, filePath)\n if (!msg) continue\n if (msg.uuid && typeof msg.uuid === 'string') {\n globalUuidMap.set(msg.uuid, {\n sessionId: fileSessionId,\n timestamp: msg.timestamp as string | undefined,\n })\n }\n // Also check messageId for file-history-snapshot type\n if (msg.messageId && typeof msg.messageId === 'string') {\n globalUuidMap.set(msg.messageId, {\n sessionId: fileSessionId,\n timestamp: (msg.snapshot as Record<string, unknown> | undefined)?.timestamp as\n | string\n | undefined,\n })\n }\n // Collect summaries with source file for sorting\n if (msg.type === 'summary' && typeof msg.summary === 'string') {\n allSummaries.push({\n summary: msg.summary as string,\n leafUuid: msg.leafUuid as string | undefined,\n timestamp: msg.timestamp as string | undefined,\n sourceFile: file,\n })\n }\n }\n } catch {\n // Skip unreadable files\n }\n })\n ),\n { concurrency: 20 }\n )\n\n return { globalUuidMap, allSummaries } satisfies Phase1Result\n })\n\n/** Build summariesByTargetSession map from Phase 1 output */\nconst buildSummariesByTargetSession = (\n globalUuidMap: Map<string, { sessionId: string; timestamp?: string }>,\n allSummaries: Phase1Result['allSummaries']\n): Map<string, SummaryInfo[]> => {\n const summariesByTargetSession = new Map<string, SummaryInfo[]>()\n for (const summaryData of allSummaries) {\n if (summaryData.leafUuid) {\n const targetInfo = globalUuidMap.get(summaryData.leafUuid)\n if (targetInfo) {\n const targetSessionId = targetInfo.sessionId\n if (!summariesByTargetSession.has(targetSessionId)) {\n summariesByTargetSession.set(targetSessionId, [])\n }\n summariesByTargetSession.get(targetSessionId)!.push({\n summary: summaryData.summary,\n leafUuid: summaryData.leafUuid,\n timestamp: summaryData.timestamp ?? targetInfo.timestamp,\n sourceFile: summaryData.sourceFile,\n })\n }\n }\n }\n return summariesByTargetSession\n}\n\n/** Sort summaries consistently (oldest first by timestamp, then sourceFile desc) */\nconst sortSummaries = (summaries: SummaryInfo[]): SummaryInfo[] => {\n return [...summaries].sort((a, b) => {\n const timestampCmp = (a.timestamp ?? '').localeCompare(b.timestamp ?? '')\n if (timestampCmp !== 0) return timestampCmp\n return (b.sourceFile ?? '').localeCompare(a.sourceFile ?? '')\n })\n}\n\n/** Apply sort, filter error sessions, and wrap as ProjectTreeData */\nconst buildProjectTreeResult = (\n project: { name: string; displayName: string; path: string },\n sessions: SessionTreeData[],\n sort: SessionSortOptions\n): ProjectTreeData => {\n const sortedSessions = sortSessions(sessions, sort)\n\n const filteredSessions = sortedSessions.filter((s) => {\n if (isErrorSessionTitle(s.title)) return false\n if (isErrorSessionTitle(s.customTitle)) return false\n if (isErrorSessionTitle(s.currentSummary)) return false\n return true\n })\n\n return {\n name: project.name,\n displayName: project.displayName,\n path: project.path,\n sessionCount: filteredSessions.length,\n sessions: filteredSessions,\n }\n}\n\n// ============================================================================\n// Cache helpers\n// ============================================================================\n\n/** Serialize Phase 1 + Phase 2 results into a TreeCache */\nconst buildTreeCache = (\n globalUuidMap: Map<string, { sessionId: string; timestamp?: string }>,\n allSummaries: Phase1Result['allSummaries'],\n sessions: SessionTreeData[],\n fileMtimes: Map<string, number>\n): TreeCache => {\n const uuidMapRecord: Record<string, { sessionId: string; timestamp?: string }> = {}\n for (const [key, val] of globalUuidMap) {\n uuidMapRecord[key] = val\n }\n\n const sessionsRecord: Record<string, { fileMtime: number; data: SessionTreeData }> = {}\n for (const s of sessions) {\n sessionsRecord[s.id] = {\n fileMtime: fileMtimes.get(s.id) ?? 0,\n data: s,\n }\n }\n\n return {\n version: 1,\n globalUuidMap: uuidMapRecord,\n allSummaries,\n sessions: sessionsRecord,\n }\n}\n\n/** Update an unchanged session's summaries from new Phase 1 data */\nconst updateSessionSummaries = (\n cached: SessionTreeData,\n summariesByTargetSession: Map<string, SummaryInfo[]>\n): SessionTreeData => {\n const newSummaries = sortSummaries(summariesByTargetSession.get(cached.id) ?? [])\n\n // Check if summaries actually changed\n const oldJson = JSON.stringify(cached.summaries)\n const newJson = JSON.stringify(newSummaries)\n if (oldJson === newJson) return cached\n\n const newSortTimestamp = getSessionSortTimestamp({\n summaries: newSummaries,\n createdAt: cached.createdAt,\n })\n\n return {\n ...cached,\n summaries: newSummaries,\n currentSummary: newSummaries[0]?.summary,\n sortTimestamp: newSortTimestamp,\n }\n}\n\n// ============================================================================\n// Main entry point\n// ============================================================================\n\n// Load all sessions tree data for a project (with caching)\nexport const loadProjectTreeData = (projectName: string, sortOptions?: SessionSortOptions) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n\n // Check project exists (fast: single stat instead of listing ALL projects)\n const exists = yield* Effect.tryPromise(() =>\n fs\n .access(projectPath)\n .then(() => true)\n .catch(() => false)\n )\n if (!exists) return null\n\n // Resolve display name for this single project (avoids processing all projects)\n const displayName = yield* Effect.tryPromise(() => folderNameToPath(projectName))\n const project = { name: projectName, displayName, path: projectPath }\n\n const sort = sortOptions ?? DEFAULT_SORT\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n const sessionFileIds = sessionFiles.map((f) => f.replace('.jsonl', ''))\n\n // Step 1: Collect file modification times (cheap ~50ms for 57 files)\n const fileMtimes = new Map<string, number>()\n yield* Effect.all(\n sessionFiles.map((file) =>\n Effect.gen(function* () {\n const filePath = path.join(projectPath, file)\n try {\n const stat = yield* Effect.tryPromise(() => fs.stat(filePath))\n fileMtimes.set(file.replace('.jsonl', ''), stat.mtimeMs)\n } catch {\n // Ignore stat errors\n }\n })\n ),\n { concurrency: 20 }\n )\n\n // Step 2: Try cache\n const cache = yield* Effect.tryPromise(() => loadTreeCache(projectName))\n\n if (cache) {\n const validation = validateCache(cache, sessionFileIds, fileMtimes)\n\n if (validation.isFullHit) {\n // Fast path: all files unchanged, return cached data directly\n log.debug(`cache hit for ${projectName} (${sessionFileIds.length} sessions)`)\n const sessions = sessionFileIds\n .map((id) => cache.sessions[id]?.data)\n .filter((s): s is SessionTreeData => s != null)\n return buildProjectTreeResult(project, sessions, sort)\n }\n\n const changedCount =\n validation.changedSessionIds.length +\n validation.newSessionIds.length +\n validation.deletedSessionIds.length\n log.debug(\n `cache partial miss for ${projectName}: ` +\n `${validation.changedSessionIds.length} changed, ` +\n `${validation.newSessionIds.length} new, ` +\n `${validation.deletedSessionIds.length} deleted, ` +\n `${validation.unchangedSessionIds.length} unchanged`\n )\n\n // Incremental path: rebuild Phase 1, then selectively reload Phase 2\n if (changedCount <= sessionFileIds.length / 2) {\n const result = yield* loadProjectTreeDataIncremental(\n projectName,\n projectPath,\n files,\n sessionFiles,\n fileMtimes,\n cache,\n validation,\n project,\n sort\n )\n return result\n }\n // If more than half changed, just do a full load (faster than incremental)\n }\n\n // Full load (no cache or too many changes)\n log.debug(`full load for ${projectName} (${sessionFileIds.length} sessions)`)\n return yield* loadProjectTreeDataFull(\n projectName,\n projectPath,\n files,\n sessionFiles,\n fileMtimes,\n project,\n sort\n )\n })\n\n/** Full load: Phase 1 + Phase 2 on all files, then write cache */\nconst loadProjectTreeDataFull = (\n projectName: string,\n projectPath: string,\n files: string[],\n sessionFiles: string[],\n fileMtimes: Map<string, number>,\n project: { name: string; displayName: string; path: string },\n sort: SessionSortOptions\n) =>\n Effect.gen(function* () {\n const allJsonlFiles = files.filter((f) => f.endsWith('.jsonl'))\n\n // Phase 1: Build global UUID map + collect all summaries\n const { globalUuidMap, allSummaries } = yield* buildPhase1(projectPath, allJsonlFiles)\n\n // Phase 1.5: Build summariesByTargetSession\n const summariesByTargetSession = buildSummariesByTargetSession(globalUuidMap, allSummaries)\n\n // Phase 2: Load all session tree data\n const sessions = yield* Effect.all(\n sessionFiles.map((file) => {\n const sessionId = file.replace('.jsonl', '')\n const mtime = fileMtimes.get(sessionId)\n return loadSessionTreeDataInternal(projectName, sessionId, summariesByTargetSession, mtime)\n }),\n { concurrency: 10 }\n )\n\n // Write cache in background (don't block return)\n const cacheData = buildTreeCache(globalUuidMap, allSummaries, sessions, fileMtimes)\n writeTreeCache(projectName, cacheData).catch((err) => {\n log.debug(`cache write failed for ${projectName}: ${err}`)\n })\n\n return buildProjectTreeResult(project, sessions, sort)\n })\n\n/** Incremental load: Phase 1 on all files, Phase 2 only for changed sessions */\nconst loadProjectTreeDataIncremental = (\n projectName: string,\n projectPath: string,\n files: string[],\n sessionFiles: string[],\n fileMtimes: Map<string, number>,\n cache: TreeCache,\n validation: ReturnType<typeof validateCache>,\n project: { name: string; displayName: string; path: string },\n sort: SessionSortOptions\n) =>\n Effect.gen(function* () {\n const allJsonlFiles = files.filter((f) => f.endsWith('.jsonl'))\n\n // Phase 1: Rebuild UUID map + summaries fully (fast: ~2-3s, just parsing)\n const { globalUuidMap, allSummaries } = yield* buildPhase1(projectPath, allJsonlFiles)\n const summariesByTargetSession = buildSummariesByTargetSession(globalUuidMap, allSummaries)\n\n // Phase 2: Only load changed + new sessions\n const sessionsToLoad = [...validation.changedSessionIds, ...validation.newSessionIds]\n const loadedSessions = yield* Effect.all(\n sessionsToLoad.map((sessionId) => {\n const mtime = fileMtimes.get(sessionId)\n return loadSessionTreeDataInternal(projectName, sessionId, summariesByTargetSession, mtime)\n }),\n { concurrency: 10 }\n )\n\n // Build loaded map for quick lookup\n const loadedMap = new Map<string, SessionTreeData>()\n for (const s of loadedSessions) {\n loadedMap.set(s.id, s)\n }\n\n // Merge: use loaded for changed/new, update summaries for unchanged, skip deleted\n const allSessions: SessionTreeData[] = []\n for (const file of sessionFiles) {\n const sessionId = file.replace('.jsonl', '')\n const loaded = loadedMap.get(sessionId)\n if (loaded) {\n allSessions.push(loaded)\n } else if (cache.sessions[sessionId]) {\n // Unchanged session: update summaries from new Phase 1 data\n const updated = updateSessionSummaries(\n cache.sessions[sessionId].data,\n summariesByTargetSession\n )\n // Update fileMtime from current stat\n allSessions.push({ ...updated, fileMtime: fileMtimes.get(sessionId) })\n }\n // deleted sessions are not in sessionFiles, so they're naturally excluded\n }\n\n // Write updated cache\n const cacheData = buildTreeCache(globalUuidMap, allSummaries, allSessions, fileMtimes)\n writeTreeCache(projectName, cacheData).catch((err) => {\n log.debug(`cache write failed for ${projectName}: ${err}`)\n })\n\n return buildProjectTreeResult(project, allSessions, sort)\n })\n","/**\n * Tree data cache for fast project loading\n *\n * Stores the full output of loadProjectTreeData alongside sessions at\n * ~/.claude/projects/{project}/.tree-cache.json\n * Invalidated by comparing file mtimes.\n */\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir } from '../paths.js'\nimport type { SessionTreeData } from '../types.js'\nimport { createLogger } from '../logger.js'\n\nconst log = createLogger('cache')\n\nconst CACHE_VERSION = 1\nconst CACHE_FILENAME = '.tree-cache.json'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface CachedSessionData {\n fileMtime: number\n data: SessionTreeData\n}\n\nexport interface TreeCache {\n version: number\n /** Phase 1 output: global UUID map entries */\n globalUuidMap: Record<string, { sessionId: string; timestamp?: string }>\n /** Phase 1 output: all summary records */\n allSummaries: Array<{\n summary: string\n leafUuid?: string\n timestamp?: string\n sourceFile: string\n }>\n /** Phase 2 output: per-session tree data keyed by sessionId */\n sessions: Record<string, CachedSessionData>\n}\n\nexport interface CacheValidation {\n isFullHit: boolean\n changedSessionIds: string[]\n unchangedSessionIds: string[]\n deletedSessionIds: string[]\n /** New session files not in cache */\n newSessionIds: string[]\n}\n\n// ============================================================================\n// Path helper\n// ============================================================================\n\nexport const getCachePath = (projectName: string): string =>\n path.join(getSessionsDir(), projectName, CACHE_FILENAME)\n\n// ============================================================================\n// Read / Write / Validate\n// ============================================================================\n\nexport const loadTreeCache = async (projectName: string): Promise<TreeCache | null> => {\n const cachePath = getCachePath(projectName)\n try {\n const raw = await fs.readFile(cachePath, 'utf-8')\n const parsed = JSON.parse(raw) as TreeCache\n if (parsed.version !== CACHE_VERSION) {\n log.debug(`cache version mismatch (${parsed.version} !== ${CACHE_VERSION}), ignoring`)\n return null\n }\n return parsed\n } catch {\n return null\n }\n}\n\nexport const writeTreeCache = async (projectName: string, cache: TreeCache): Promise<void> => {\n const cachePath = getCachePath(projectName)\n const tmpPath = cachePath + '.tmp'\n try {\n await fs.writeFile(tmpPath, JSON.stringify(cache), 'utf-8')\n await fs.rename(tmpPath, cachePath)\n } catch (e) {\n log.debug(`failed to write cache: ${e}`)\n // Clean up temp file on failure\n try {\n await fs.unlink(tmpPath)\n } catch {\n // ignore\n }\n }\n}\n\n/**\n * Compare current file mtimes against cache to determine what changed.\n * @param cache - Previously saved cache\n * @param sessionFileIds - Current session IDs (from file listing, without .jsonl)\n * @param currentMtimes - Map of sessionId → current mtime (ms)\n */\nexport const validateCache = (\n cache: TreeCache,\n sessionFileIds: string[],\n currentMtimes: Map<string, number>\n): CacheValidation => {\n const cachedIds = new Set(Object.keys(cache.sessions))\n const currentIds = new Set(sessionFileIds)\n\n const changedSessionIds: string[] = []\n const unchangedSessionIds: string[] = []\n const newSessionIds: string[] = []\n const deletedSessionIds: string[] = []\n\n // Check each current file against cache\n for (const id of currentIds) {\n if (!cachedIds.has(id)) {\n newSessionIds.push(id)\n } else {\n const cachedMtime = cache.sessions[id].fileMtime\n const currentMtime = currentMtimes.get(id) ?? 0\n // 1ms tolerance: filesystem mtimes have varying precision across platforms\n if (Math.abs(cachedMtime - currentMtime) <= 1) {\n unchangedSessionIds.push(id)\n } else {\n changedSessionIds.push(id)\n }\n }\n }\n\n // Files that were in cache but no longer exist\n for (const id of cachedIds) {\n if (!currentIds.has(id)) {\n deletedSessionIds.push(id)\n }\n }\n\n const isFullHit =\n changedSessionIds.length === 0 && newSessionIds.length === 0 && deletedSessionIds.length === 0\n\n return { isFullHit, changedSessionIds, unchangedSessionIds, deletedSessionIds, newSessionIds }\n}\n","/**\n * Session analysis, compression, and knowledge extraction\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir } from '../paths.js'\nimport type {\n CompressSessionOptions,\n CompressSessionResult,\n ContentItem,\n ConversationLine,\n Message,\n ProjectKnowledge,\n SessionAnalysis,\n SummarizeSessionOptions,\n SummarizeSessionResult,\n} from '../types.js'\nimport { extractTextContent, parseJsonlLines } from '../utils.js'\nimport { readSession } from './crud.js'\nimport { repairParentUuidChain } from './validation.js'\n\n// Helper: Check if item is a tool_use content item\nconst isToolUse = (\n item: unknown\n): item is { type: 'tool_use'; name?: string; id?: string; input?: { file_path?: string } } =>\n item !== null &&\n typeof item === 'object' &&\n 'type' in item &&\n (item as ContentItem).type === 'tool_use'\n\n// Helper: Check if item is a tool_result with error\nconst isToolResultError = (\n item: unknown\n): item is { type: 'tool_result'; tool_use_id?: string; is_error: true } =>\n item !== null &&\n typeof item === 'object' &&\n 'type' in item &&\n (item as ContentItem).type === 'tool_result' &&\n 'is_error' in item &&\n (item as { is_error?: boolean }).is_error === true\n\n// Helper: Find tool_use by ID across all messages\nconst findToolUseById = (messages: Message[], toolUseId: string): string | null => {\n for (const msg of messages) {\n const content = msg.message?.content\n if (!Array.isArray(content)) continue\n for (const item of content) {\n if (isToolUse(item) && item.id === toolUseId) {\n return item.name ?? 'unknown'\n }\n }\n }\n return null\n}\n\n// Analyze session for optimization insights\nexport const analyzeSession = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const messages = yield* readSession(projectName, sessionId)\n\n // Initialize counters\n let userMessages = 0\n let assistantMessages = 0\n let summaryCount = 0\n let snapshotCount = 0\n const toolUsageMap = new Map<string, { count: number; errorCount: number }>()\n const filesChanged = new Set<string>()\n const patterns: { type: string; description: string; count: number }[] = []\n const milestones: { timestamp?: string; description: string; messageUuid?: string }[] = []\n\n // Track timestamps for duration\n let firstTimestamp: string | undefined\n let lastTimestamp: string | undefined\n\n for (const msg of messages) {\n // Track timestamps\n if (msg.timestamp) {\n if (!firstTimestamp) firstTimestamp = msg.timestamp\n lastTimestamp = msg.timestamp\n }\n\n // Count message types\n if (msg.type === 'user') {\n userMessages++\n // Check for milestone indicators in user messages\n const content = typeof msg.content === 'string' ? msg.content : ''\n if (content.toLowerCase().includes('commit') || content.toLowerCase().includes('완료')) {\n milestones.push({\n timestamp: msg.timestamp,\n description: `User checkpoint: ${content.slice(0, 50)}...`,\n messageUuid: msg.uuid,\n })\n }\n } else if (msg.type === 'assistant') {\n assistantMessages++\n\n // Track tool usage\n const content = msg.message?.content\n if (!Array.isArray(content)) continue\n for (const item of content) {\n if (!isToolUse(item)) continue\n const toolName = item.name ?? 'unknown'\n const existing = toolUsageMap.get(toolName) ?? { count: 0, errorCount: 0 }\n existing.count++\n toolUsageMap.set(toolName, existing)\n\n // Track file changes\n if ((toolName === 'Write' || toolName === 'Edit') && item.input?.file_path) {\n filesChanged.add(item.input.file_path)\n }\n }\n } else if (msg.type === 'summary') {\n summaryCount++\n // Extract milestone from summary\n if (msg.summary) {\n milestones.push({\n timestamp: msg.timestamp,\n description: `Summary: ${msg.summary.slice(0, 100)}...`,\n messageUuid: msg.uuid,\n })\n }\n } else if (msg.type === 'file-history-snapshot') {\n snapshotCount++\n // Track files from snapshots\n const snapshot = msg as unknown as {\n snapshot?: { trackedFileBackups?: Record<string, unknown> }\n }\n if (snapshot.snapshot?.trackedFileBackups) {\n for (const filePath of Object.keys(snapshot.snapshot.trackedFileBackups)) {\n filesChanged.add(filePath)\n }\n }\n }\n }\n\n // Track tool errors from tool_result messages\n for (const msg of messages) {\n if (msg.type !== 'user' || !Array.isArray(msg.content)) continue\n for (const item of msg.content) {\n if (!isToolResultError(item) || !item.tool_use_id) continue\n const toolName = findToolUseById(messages, item.tool_use_id)\n if (!toolName) continue\n const existing = toolUsageMap.get(toolName)\n if (existing) existing.errorCount++\n }\n }\n\n // Calculate duration\n let durationMinutes = 0\n if (firstTimestamp && lastTimestamp) {\n const first = new Date(firstTimestamp).getTime()\n const last = new Date(lastTimestamp).getTime()\n durationMinutes = Math.round((last - first) / 1000 / 60)\n }\n\n // Detect patterns\n const toolUsageArray = Array.from(toolUsageMap.entries()).map(([name, stats]) => ({\n name,\n count: stats.count,\n errorCount: stats.errorCount,\n }))\n\n // Pattern: High error rate\n for (const tool of toolUsageArray) {\n if (tool.count >= 3 && tool.errorCount / tool.count > 0.3) {\n patterns.push({\n type: 'high_error_rate',\n description: `${tool.name} had ${tool.errorCount}/${tool.count} errors (${Math.round((tool.errorCount / tool.count) * 100)}%)`,\n count: tool.errorCount,\n })\n }\n }\n\n // Pattern: Many snapshots (potential for compression)\n if (snapshotCount > 10) {\n patterns.push({\n type: 'many_snapshots',\n description: `${snapshotCount} file-history-snapshots could be compressed`,\n count: snapshotCount,\n })\n }\n\n return {\n sessionId,\n projectName,\n durationMinutes,\n stats: {\n totalMessages: messages.length,\n userMessages,\n assistantMessages,\n summaryCount,\n snapshotCount,\n },\n toolUsage: toolUsageArray.sort((a, b) => b.count - a.count),\n filesChanged: Array.from(filesChanged),\n patterns,\n milestones,\n } satisfies SessionAnalysis\n })\n\n// Compress session by removing snapshots and truncating tool outputs\nexport const compressSession = (\n projectName: string,\n sessionId: string,\n options: CompressSessionOptions = {}\n) =>\n Effect.gen(function* () {\n const { keepSnapshots = 'first_last', maxToolOutputLength = 5000 } = options\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n\n // Read original file\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const originalSize = Buffer.byteLength(content, 'utf-8')\n const lines = content.trim().split('\\n').filter(Boolean)\n const messages = parseJsonlLines<Record<string, unknown>>(lines, filePath, { strict: true })\n\n let removedCustomTitles = 0\n let removedProgress = 0\n let removedSnapshots = 0\n let truncatedOutputs = 0\n\n // Find snapshot indices and custom-title indices\n const customTitleIndices: number[] = []\n const snapshotIndices: number[] = []\n messages.forEach((msg, idx) => {\n if (msg.type === 'custom-title') {\n customTitleIndices.push(idx)\n }\n if (msg.type === 'file-history-snapshot') {\n snapshotIndices.push(idx)\n }\n })\n\n // Collect messages to remove\n const messagesToRemove: Record<string, unknown>[] = []\n\n // Filter messages based on keepSnapshots option, remove progress and duplicate custom-titles\n const filteredMessages = messages.filter((msg, idx) => {\n // Always remove progress messages (hook progress, etc.)\n if (msg.type === 'progress') {\n removedProgress++\n messagesToRemove.push(msg)\n return false\n }\n\n // Keep only the last custom-title record\n if (msg.type === 'custom-title') {\n if (\n customTitleIndices.length > 1 &&\n idx !== customTitleIndices[customTitleIndices.length - 1]\n ) {\n removedCustomTitles++\n messagesToRemove.push(msg)\n return false\n }\n }\n\n if (msg.type === 'file-history-snapshot') {\n if (keepSnapshots === 'none') {\n removedSnapshots++\n messagesToRemove.push(msg)\n return false\n }\n if (keepSnapshots === 'first_last') {\n const isFirst = idx === snapshotIndices[0]\n const isLast = idx === snapshotIndices[snapshotIndices.length - 1]\n if (!isFirst && !isLast) {\n removedSnapshots++\n messagesToRemove.push(msg)\n return false\n }\n }\n }\n return true\n })\n\n // Repair parentUuid chain after removing messages\n repairParentUuidChain(filteredMessages, messagesToRemove)\n\n // Truncate long tool outputs\n for (const msg of filteredMessages) {\n if (msg.type === 'user' && Array.isArray(msg.content)) {\n for (const item of msg.content as Array<Record<string, unknown>>) {\n if (item.type === 'tool_result' && typeof item.content === 'string') {\n if (maxToolOutputLength > 0 && item.content.length > maxToolOutputLength) {\n item.content = item.content.slice(0, maxToolOutputLength) + '\\n... [truncated]'\n truncatedOutputs++\n }\n }\n }\n }\n }\n\n // Write compressed file\n const newContent = filteredMessages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n const compressedSize = Buffer.byteLength(newContent, 'utf-8')\n\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n return {\n success: true,\n originalSize,\n compressedSize,\n removedCustomTitles,\n removedProgress,\n removedSnapshots,\n truncatedOutputs,\n } satisfies CompressSessionResult\n })\n\n// Extract knowledge from multiple sessions in a project\nexport const extractProjectKnowledge = (projectName: string, sessionIds?: string[]) =>\n Effect.gen(function* () {\n const sessionsDir = getSessionsDir()\n const projectDir = path.join(sessionsDir, projectName)\n\n // Get all session files if not specified\n let targetSessionIds = sessionIds\n if (!targetSessionIds) {\n const files = yield* Effect.tryPromise(() => fs.readdir(projectDir))\n targetSessionIds = files\n .filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n .map((f) => f.replace('.jsonl', ''))\n }\n\n // Aggregate data across sessions\n const fileModifyCount = new Map<string, { count: number; lastModified?: string }>()\n const toolSequences: string[][] = []\n const decisions: { context: string; decision: string; sessionId: string }[] = []\n\n for (const sessionId of targetSessionIds) {\n try {\n const messages = yield* readSession(projectName, sessionId)\n\n // Track file modifications\n for (const msg of messages) {\n if (msg.type === 'file-history-snapshot') {\n const snapshot = msg as unknown as {\n snapshot?: { trackedFileBackups?: Record<string, unknown>; timestamp?: string }\n }\n if (snapshot.snapshot?.trackedFileBackups) {\n for (const filePath of Object.keys(snapshot.snapshot.trackedFileBackups)) {\n const existing = fileModifyCount.get(filePath) ?? { count: 0 }\n existing.count++\n existing.lastModified = snapshot.snapshot.timestamp\n fileModifyCount.set(filePath, existing)\n }\n }\n }\n\n // Track tool sequences\n if (\n msg.type === 'assistant' &&\n msg.message?.content &&\n Array.isArray(msg.message.content)\n ) {\n const tools: string[] = []\n for (const item of msg.message.content) {\n if (item && typeof item === 'object' && 'type' in item && item.type === 'tool_use') {\n const toolUse = item as { name?: string }\n if (toolUse.name) tools.push(toolUse.name)\n }\n }\n if (tools.length > 1) {\n toolSequences.push(tools)\n }\n }\n\n // Extract decisions from summaries\n if (msg.type === 'summary' && msg.summary) {\n decisions.push({\n context: 'Session summary',\n decision: msg.summary.slice(0, 200),\n sessionId,\n })\n }\n }\n } catch {\n // Skip sessions that fail to read\n continue\n }\n }\n\n // Build hot files list\n const hotFiles = Array.from(fileModifyCount.entries())\n .map(([filePath, data]) => ({\n path: filePath,\n modifyCount: data.count,\n lastModified: data.lastModified,\n }))\n .sort((a, b) => b.modifyCount - a.modifyCount)\n .slice(0, 20)\n\n // Build workflow patterns\n const workflowMap = new Map<string, number>()\n for (const seq of toolSequences) {\n const key = seq.join(' -> ')\n workflowMap.set(key, (workflowMap.get(key) ?? 0) + 1)\n }\n const workflows = Array.from(workflowMap.entries())\n .filter(([, count]) => count >= 2)\n .map(([sequence, count]) => ({\n sequence: sequence.split(' -> '),\n count,\n }))\n .sort((a, b) => b.count - a.count)\n .slice(0, 10)\n\n return {\n projectName,\n patterns: [],\n hotFiles,\n workflows,\n decisions: decisions.slice(0, 20),\n } satisfies ProjectKnowledge\n })\n\n// Helper to truncate text and replace newlines\nfunction truncateText(text: string, maxLen: number): string {\n const cleaned = text.replace(/\\n/g, ' ')\n if (cleaned.length > maxLen) {\n return cleaned.slice(0, maxLen) + '...'\n }\n return cleaned\n}\n\n// Summarize session into user/assistant conversation format\nexport const summarizeSession = (\n projectName: string,\n sessionId: string,\n options: SummarizeSessionOptions = {}\n) =>\n Effect.gen(function* () {\n const { limit = 50, maxLength = 100 } = options\n const messages = yield* readSession(projectName, sessionId)\n\n const lines: ConversationLine[] = []\n let count = 0\n\n for (const msg of messages) {\n if (count >= limit) break\n\n if (msg.type === 'user' || msg.type === 'human') {\n // Extract timestamp for user messages using locale-based formatting\n let timeStr: string | undefined\n if (msg.timestamp) {\n try {\n const dt = new Date(msg.timestamp)\n timeStr = dt.toLocaleString('ko-KR', {\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n })\n } catch {\n // Skip timestamp on error\n }\n }\n\n const text = extractTextContent(msg.message)\n if (text) {\n const truncated = truncateText(text, maxLength)\n lines.push({ role: 'user', content: truncated, timestamp: timeStr })\n count++\n }\n } else if (msg.type === 'assistant') {\n const text = extractTextContent(msg.message)\n if (text) {\n const truncated = truncateText(text, maxLength)\n lines.push({ role: 'assistant', content: truncated })\n count++\n }\n }\n }\n\n // Build formatted string\n const formatted = lines\n .map((line) => {\n if (line.role === 'user') {\n return line.timestamp\n ? `user [${line.timestamp}]: ${line.content}`\n : `user: ${line.content}`\n }\n return `assistant: ${line.content}`\n })\n .join('\\n')\n\n return {\n sessionId,\n projectName,\n lines,\n formatted,\n } satisfies SummarizeSessionResult\n })\n","/**\n * Session cleanup and maintenance operations\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir } from '../paths.js'\nimport { isInvalidApiKeyMessage, parseJsonlLines } from '../utils.js'\nimport { findLinkedAgents, findOrphanAgents, deleteOrphanAgents } from '../agents.js'\nimport { sessionHasTodos, findOrphanTodos, deleteOrphanTodos } from '../todos.js'\nimport { listProjects } from './projects.js'\nimport { listSessions, deleteSession } from './crud.js'\nimport type { Message, CleanupPreview, ClearSessionsResult } from '../types.js'\n\n// Remove invalid API key messages from a session, returns remaining message count\nconst cleanInvalidMessages = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n\n if (lines.length === 0) return { removedCount: 0, remainingCount: 0 }\n\n const messages = parseJsonlLines<Message>(lines, filePath)\n const invalidIndices: number[] = []\n\n // Find all invalid API key messages\n messages.forEach((msg, idx) => {\n if (isInvalidApiKeyMessage(msg)) {\n invalidIndices.push(idx)\n }\n })\n\n if (invalidIndices.length === 0) {\n const userAssistantCount = messages.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n ).length\n const hasSummary = messages.some((m) => m.type === 'summary')\n // Count summary-only sessions as having 1 message\n const remainingCount = userAssistantCount > 0 ? userAssistantCount : hasSummary ? 1 : 0\n return { removedCount: 0, remainingCount }\n }\n\n // Remove invalid messages and fix parentUuid chain\n const filtered: Message[] = []\n let lastValidUuid: string | null = null\n\n for (let i = 0; i < messages.length; i++) {\n if (invalidIndices.includes(i)) {\n continue // Skip invalid message\n }\n\n const msg = messages[i]\n // Update parentUuid to point to last valid message\n if (msg.parentUuid && invalidIndices.some((idx) => messages[idx]?.uuid === msg.parentUuid)) {\n msg.parentUuid = lastValidUuid\n }\n filtered.push(msg)\n lastValidUuid = msg.uuid\n }\n\n const newContent =\n filtered.length > 0 ? filtered.map((m) => JSON.stringify(m)).join('\\n') + '\\n' : ''\n\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n const remainingUserAssistant = filtered.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n ).length\n const hasSummary = filtered.some((m) => m.type === 'summary')\n // Count summary-only sessions as having 1 message\n const remainingCount = remainingUserAssistant > 0 ? remainingUserAssistant : hasSummary ? 1 : 0\n return { removedCount: invalidIndices.length, remainingCount }\n })\n\n// Preview cleanup - find empty and invalid sessions\nexport const previewCleanup = (projectName?: string) =>\n Effect.gen(function* () {\n const projects = yield* listProjects\n const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects\n\n // Get orphan todos count (global, not per-project)\n const orphanTodos = yield* findOrphanTodos()\n const orphanTodoCount = orphanTodos.length\n\n const results = yield* Effect.all(\n targetProjects.map((project) =>\n Effect.gen(function* () {\n const sessions = yield* listSessions(project.name)\n const emptySessions = sessions.filter((s) => s.messageCount === 0)\n const invalidSessions = sessions.filter(\n (s) => s.title?.includes('Invalid API key') || s.title?.includes('API key')\n )\n\n // Count empty sessions that have todos\n let emptyWithTodosCount = 0\n for (const session of emptySessions) {\n const linkedAgents = yield* findLinkedAgents(project.name, session.id)\n const hasTodos = yield* sessionHasTodos(session.id, linkedAgents)\n if (hasTodos) {\n emptyWithTodosCount++\n }\n }\n\n // Count orphan agents\n const orphanAgents = yield* findOrphanAgents(project.name)\n\n return {\n project: project.name,\n emptySessions,\n invalidSessions,\n emptyWithTodosCount,\n orphanAgentCount: orphanAgents.length,\n orphanTodoCount: 0, // Will set for first project only\n } satisfies CleanupPreview\n })\n ),\n { concurrency: 5 }\n )\n\n // Add orphanTodoCount only to the first result to avoid double counting\n if (results.length > 0) {\n results[0] = { ...results[0], orphanTodoCount }\n }\n\n return results\n })\n\n// Clear sessions\nexport const clearSessions = (options: {\n projectName?: string\n clearEmpty?: boolean\n clearInvalid?: boolean\n skipWithTodos?: boolean\n clearOrphanAgents?: boolean\n clearOrphanTodos?: boolean\n}) =>\n Effect.gen(function* () {\n const {\n projectName,\n clearEmpty = true,\n clearInvalid = true,\n skipWithTodos = true,\n clearOrphanAgents = true,\n clearOrphanTodos = false,\n } = options\n const projects = yield* listProjects\n const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects\n\n let deletedSessionCount = 0\n let removedMessageCount = 0\n let deletedOrphanAgentCount = 0\n let deletedOrphanTodoCount = 0\n const sessionsToDelete: { project: string; sessionId: string }[] = []\n\n // Step 1: Clean invalid API key messages from all sessions (if clearInvalid)\n if (clearInvalid) {\n for (const project of targetProjects) {\n const projectPath = path.join(getSessionsDir(), project.name)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n\n for (const file of sessionFiles) {\n const sessionId = file.replace('.jsonl', '')\n const result = yield* cleanInvalidMessages(project.name, sessionId)\n removedMessageCount += result.removedCount\n\n // Mark for deletion if now empty\n if (result.remainingCount === 0) {\n sessionsToDelete.push({ project: project.name, sessionId })\n }\n }\n }\n }\n\n // Step 2: Also find originally empty sessions (if clearEmpty is true)\n if (clearEmpty) {\n for (const project of targetProjects) {\n const sessions = yield* listSessions(project.name)\n for (const session of sessions) {\n if (session.messageCount === 0) {\n const alreadyMarked = sessionsToDelete.some(\n (s) => s.project === project.name && s.sessionId === session.id\n )\n if (!alreadyMarked) {\n // Skip sessions with todos if skipWithTodos is true\n if (skipWithTodos) {\n const linkedAgents = yield* findLinkedAgents(project.name, session.id)\n const hasTodos = yield* sessionHasTodos(session.id, linkedAgents)\n if (hasTodos) continue\n }\n sessionsToDelete.push({ project: project.name, sessionId: session.id })\n }\n }\n }\n }\n }\n\n // Step 3: Delete all empty sessions (this also deletes linked agents and todos)\n for (const { project, sessionId } of sessionsToDelete) {\n yield* deleteSession(project, sessionId)\n deletedSessionCount++\n }\n\n // Step 4: Delete orphan agents if requested\n if (clearOrphanAgents) {\n for (const project of targetProjects) {\n const result = yield* deleteOrphanAgents(project.name)\n deletedOrphanAgentCount += result.count\n }\n }\n\n // Step 5: Delete orphan todos if requested (global, not per-project)\n if (clearOrphanTodos) {\n const result = yield* deleteOrphanTodos()\n deletedOrphanTodoCount = result.deletedCount\n }\n\n return {\n success: true,\n deletedCount: deletedSessionCount,\n removedMessageCount,\n deletedOrphanAgentCount,\n deletedOrphanTodoCount,\n } satisfies ClearSessionsResult\n })\n","/**\n * Session search operations\n */\nimport { Effect, pipe } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir } from '../paths.js'\nimport { extractTextContent, extractTitle, tryParseJsonLine } from '../utils.js'\nimport { listProjects } from './projects.js'\nimport { listSessions } from './crud.js'\nimport type { Message, SearchResult, Project } from '../types.js'\n\n// Pure function: extract snippet around match\nconst extractSnippet = (text: string, matchIndex: number, queryLength: number): string => {\n const start = Math.max(0, matchIndex - 50)\n const end = Math.min(text.length, matchIndex + queryLength + 50)\n return (start > 0 ? '...' : '') + text.slice(start, end).trim() + (end < text.length ? '...' : '')\n}\n\n// Pure function: find first matching message in session content\nconst findContentMatch = (\n lines: string[],\n queryLower: string,\n filePath: string\n): { msg: Message; snippet: string } | null => {\n for (let i = 0; i < lines.length; i++) {\n const msg = tryParseJsonLine<Message>(lines[i], i + 1, filePath)\n if (!msg) continue\n if (msg.type !== 'user' && msg.type !== 'assistant') continue\n\n const text = extractTextContent(msg.message)\n const textLower = text.toLowerCase()\n const matchIndex = textLower.indexOf(queryLower)\n\n if (matchIndex !== -1) {\n return {\n msg,\n snippet: extractSnippet(text, matchIndex, queryLower.length),\n }\n }\n }\n return null\n}\n\n// Search single session for content match\nconst searchSessionContent = (\n projectName: string,\n sessionId: string,\n filePath: string,\n queryLower: string\n) =>\n pipe(\n Effect.tryPromise(() => fs.readFile(filePath, 'utf-8')),\n Effect.map((content) => {\n const lines = content.trim().split('\\n').filter(Boolean)\n const match = findContentMatch(lines, queryLower, filePath)\n\n if (!match) return null\n\n return {\n sessionId,\n projectName,\n title: extractTitle(match.msg.message) || `Session ${sessionId.slice(0, 8)}`,\n matchType: 'content' as const,\n snippet: match.snippet,\n messageUuid: match.msg.uuid,\n timestamp: match.msg.timestamp,\n } satisfies SearchResult\n }),\n Effect.catchAll(() => Effect.succeed(null))\n )\n\n// Search all sessions in a project for content matches\nconst searchProjectContent = (project: Project, queryLower: string, alreadyFoundIds: Set<string>) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), project.name)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n\n // Filter out already found sessions and create search effects\n const searchEffects = sessionFiles\n .map((file) => ({\n sessionId: file.replace('.jsonl', ''),\n filePath: path.join(projectPath, file),\n }))\n .filter(({ sessionId }) => !alreadyFoundIds.has(`${project.name}:${sessionId}`))\n .map(({ sessionId, filePath }) =>\n searchSessionContent(project.name, sessionId, filePath, queryLower)\n )\n\n const results = yield* Effect.all(searchEffects, { concurrency: 10 })\n return results.filter((r): r is NonNullable<typeof r> => r !== null)\n })\n\n// Search sessions - two-phase: title search (fast) then content search (slow)\nexport const searchSessions = (\n query: string,\n options: { projectName?: string; searchContent?: boolean } = {}\n) =>\n Effect.gen(function* () {\n const { projectName, searchContent = false } = options\n const queryLower = query.toLowerCase()\n\n const projects = yield* listProjects\n const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects\n\n // Phase 1: Title search (fast) - using Effect.all for parallel execution\n const titleSearchEffects = targetProjects.map((project) =>\n pipe(\n listSessions(project.name),\n Effect.map((sessions) =>\n sessions\n .filter((session) => (session.title ?? '').toLowerCase().includes(queryLower))\n .map(\n (session) =>\n ({\n sessionId: session.id,\n projectName: project.name,\n title: session.title ?? 'Untitled',\n matchType: 'title' as const,\n timestamp: session.updatedAt,\n }) satisfies SearchResult\n )\n )\n )\n )\n\n const titleResultsNested = yield* Effect.all(titleSearchEffects, { concurrency: 10 })\n const titleResults = titleResultsNested.flat()\n\n // Phase 2: Content search (slow, optional)\n let contentResults: SearchResult[] = []\n if (searchContent) {\n const alreadyFoundIds = new Set(titleResults.map((r) => `${r.projectName}:${r.sessionId}`))\n\n const contentSearchEffects = targetProjects.map((project) =>\n searchProjectContent(project, queryLower, alreadyFoundIds)\n )\n\n const contentResultsNested = yield* Effect.all(contentSearchEffects, { concurrency: 5 })\n contentResults = contentResultsNested.flat()\n }\n\n // Combine and sort by timestamp (newest first)\n const allResults = [...titleResults, ...contentResults]\n return allResults.sort((a, b) => {\n const dateA = a.timestamp ? new Date(a.timestamp).getTime() : 0\n const dateB = b.timestamp ? new Date(b.timestamp).getTime() : 0\n return dateB - dateA\n })\n })\n","/**\n * Session file tracking operations\n */\nimport { Effect } from 'effect'\nimport { readSession } from './crud.js'\nimport type { FileChange, SessionFilesSummary } from '../types.js'\n\n// Get files changed in a session (from file-history-snapshot and tool_use)\nexport const getSessionFiles = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const messages = yield* readSession(projectName, sessionId)\n const fileChanges: FileChange[] = []\n const seenFiles = new Set<string>()\n\n for (const msg of messages) {\n // Check file-history-snapshot type\n if (msg.type === 'file-history-snapshot') {\n const snapshot = msg as unknown as {\n type: string\n messageId?: string\n snapshot?: {\n trackedFileBackups?: Record<string, unknown>\n timestamp?: string\n }\n }\n const backups = snapshot.snapshot?.trackedFileBackups\n if (backups && typeof backups === 'object') {\n for (const filePath of Object.keys(backups)) {\n if (!seenFiles.has(filePath)) {\n seenFiles.add(filePath)\n fileChanges.push({\n path: filePath,\n action: 'modified',\n timestamp: snapshot.snapshot?.timestamp,\n messageUuid: snapshot.messageId ?? msg.uuid,\n })\n }\n }\n }\n }\n\n // Check tool_use for Write/Edit operations\n if (msg.type === 'assistant' && msg.message?.content) {\n const content = msg.message.content\n if (Array.isArray(content)) {\n for (const item of content) {\n if (item && typeof item === 'object' && 'type' in item && item.type === 'tool_use') {\n const toolUse = item as { name?: string; input?: { file_path?: string } }\n if (\n (toolUse.name === 'Write' || toolUse.name === 'Edit') &&\n toolUse.input?.file_path\n ) {\n const filePath = toolUse.input.file_path\n if (!seenFiles.has(filePath)) {\n seenFiles.add(filePath)\n fileChanges.push({\n path: filePath,\n action: toolUse.name === 'Write' ? 'created' : 'modified',\n timestamp: msg.timestamp,\n messageUuid: msg.uuid,\n })\n }\n }\n }\n }\n }\n }\n }\n\n return {\n sessionId,\n projectName,\n files: fileChanges,\n totalChanges: fileChanges.length,\n } satisfies SessionFilesSummary\n })\n","/**\n * Sessions index file operations\n *\n * The sessions-index.json file is maintained by the official Claude Code extension.\n * It provides quick access to session metadata without parsing JSONL files.\n *\n * File location: ~/.claude/projects/{project-folder}/sessions-index.json\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir } from '../paths.js'\nimport type { SessionsIndex, SessionIndexEntry } from '../types.js'\n\n/**\n * Load sessions-index.json for a project\n * Returns null if the file doesn't exist\n */\nexport const loadSessionsIndex = (projectName: string) =>\n Effect.gen(function* () {\n const indexPath = path.join(getSessionsDir(), projectName, 'sessions-index.json')\n\n try {\n const content = yield* Effect.tryPromise(() => fs.readFile(indexPath, 'utf-8'))\n const index = JSON.parse(content) as SessionsIndex\n return index\n } catch {\n return null\n }\n })\n\n/**\n * Get display title from index entry\n * Priority: customTitle > summary > firstPrompt (cleaned)\n */\nexport const getIndexEntryDisplayTitle = (entry: SessionIndexEntry): string => {\n if (entry.customTitle) return entry.customTitle\n if (entry.summary) return entry.summary\n\n // Clean up firstPrompt\n let prompt = entry.firstPrompt\n if (prompt === 'No prompt') return 'Untitled'\n if (prompt.startsWith('[Request interrupted')) return 'Untitled'\n\n // Remove IDE tags\n prompt = prompt.replace(/<ide_[^>]*>[^<]*<\\/ide_[^>]*>/g, '').trim()\n if (!prompt) return 'Untitled'\n\n // Truncate\n if (prompt.length > 60) {\n return prompt.slice(0, 57) + '...'\n }\n\n return prompt\n}\n\n/**\n * Sort index entries by modified time (newest first)\n */\nexport const sortIndexEntriesByModified = (entries: SessionIndexEntry[]): SessionIndexEntry[] => {\n return [...entries].sort((a, b) => {\n const modA = new Date(a.modified).getTime()\n const modB = new Date(b.modified).getTime()\n return modB - modA\n })\n}\n\n/**\n * Check if sessions-index.json exists for a project\n */\nexport const hasSessionsIndex = (projectName: string) =>\n Effect.gen(function* () {\n const indexPath = path.join(getSessionsDir(), projectName, 'sessions-index.json')\n try {\n yield* Effect.tryPromise(() => fs.access(indexPath))\n return true\n } catch {\n return false\n }\n })\n"],"mappings":"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","names":["fs","logger","fs","logger","basename","Effect","fs","path","Effect","stat","backupDir","Effect","fs","path","Effect","Effect","fs","path","Effect","Effect","fs","path","Effect","stat","agentBackupDir","Effect","fs","path","fs","path","log","log","Effect","stat","Effect","fs","path","Effect","Effect","fs","path","Effect","hasSummary","remainingCount","Effect","pipe","fs","path","pipe","Effect","Effect","Effect","Effect","fs","path","Effect"]}
|
|
1
|
+
{"version":3,"sources":["../src/paths.ts","../src/logger.ts","../src/utils.ts","../src/projects.ts","../src/tree-constants.ts","../src/agents.ts","../src/todos.ts","../src/session/projects.ts","../src/session/crud.ts","../src/session/validation.ts","../src/session/tree.ts","../src/session/cache.ts","../src/session/analysis.ts","../src/session/cleanup.ts","../src/session/search.ts","../src/session/files.ts","../src/session/index-file.ts"],"sourcesContent":["/**\n * Path utilities for Claude Code session management\n *\n * Architecture:\n * - Pure Functions: extractCwdFromContent, isSessionFile, toRelativePath (no I/O)\n * - I/O Functions: tryGetCwdFromFile, getRealPathFromSession (with optional DI for testing)\n */\nimport * as fs from 'node:fs'\nimport * as fsp from 'node:fs/promises'\nimport * as os from 'node:os'\nimport * as path from 'node:path'\nimport { createLogger } from './logger.js'\nimport { tryParseJsonLine } from './utils.js'\n\nconst log = createLogger('paths')\n\n// ============================================\n// Types (for dependency injection)\n// ============================================\n\nexport interface Logger {\n debug: (msg: string) => void\n warn: (msg: string) => void\n}\n\nexport interface FileSystem {\n readFileSync: (path: string, encoding: 'utf-8') => string\n readdirSync: (path: string) => string[]\n}\n\nexport interface AsyncFileSystem {\n readFile: (path: string, encoding: 'utf-8') => Promise<string>\n}\n\n// ============================================\n// Directory Paths\n// ============================================\n\n/** Get Claude sessions directory (~/.claude/projects)\n * Can be overridden with CLAUDE_SESSIONS_DIR environment variable for testing\n */\nexport const getSessionsDir = (): string =>\n process.env.CLAUDE_SESSIONS_DIR || path.join(os.homedir(), '.claude', 'projects')\n\n/** Get Claude todos directory (~/.claude/todos) */\nexport const getTodosDir = (): string => path.join(os.homedir(), '.claude', 'todos')\n\n// ============================================\n// Internal Path Utilities\n// ============================================\n\n/** Windows path detection patterns */\nconst WINDOWS_PATTERNS = {\n /** Matches Windows absolute path: C:\\ or C:/ */\n absolutePath: /^([A-Za-z]):[/\\\\]/,\n /** Matches Windows folder name format: C-- */\n folderName: /^([A-Za-z])--/,\n} as const\n\ntype WindowsPathResult = { isWindows: true; drive: string; rest: string } | { isWindows: false }\n\n/** Parse Windows folder name format, extracting drive letter and rest */\nconst parseWindowsFolderName = (name: string): WindowsPathResult => {\n const match = name.match(WINDOWS_PATTERNS.folderName)\n return match ? { isWindows: true, drive: match[1], rest: name.slice(3) } : { isWindows: false }\n}\n\n/** Check if path looks like Windows format */\nconst isWindowsPath = (p: string): boolean => WINDOWS_PATTERNS.absolutePath.test(p)\n\n// ============================================\n// Pure Functions (No I/O)\n// ============================================\n\n/** Extract cwd from file content - pure function for easy testing */\nexport const extractCwdFromContent = (content: string, filePath?: string): string | null => {\n const lines = content.split('\\n').filter((l) => l.trim())\n\n for (let i = 0; i < lines.length; i++) {\n const parsed = tryParseJsonLine<{ cwd?: string }>(lines[i], i + 1, filePath)\n if (parsed?.cwd) {\n return parsed.cwd\n }\n }\n\n return null\n}\n\n/** Check if filename is a session file */\nexport const isSessionFile = (filename: string): boolean =>\n filename.endsWith('.jsonl') && !filename.startsWith('agent-')\n\n/** Expand ~ path to absolute path with OS-native separators */\nexport const expandHomePath = (tildePath: string, homeDir: string): string => {\n if (!tildePath.startsWith('~')) return tildePath\n const relativePart = tildePath.slice(1)\n // path.join uses OS-native separator\n return path.join(homeDir, ...relativePart.split('/').filter(Boolean))\n}\n\n/** Convert path to relative form if under home directory */\nexport const toRelativePath = (absolutePath: string, homeDir: string): string => {\n const normalizedPath = absolutePath.replace(/\\\\/g, '/')\n const normalizedHome = homeDir.replace(/\\\\/g, '/')\n\n // Windows: case-insensitive comparison (C: vs c:)\n const isWin = isWindowsPath(homeDir)\n const pathLower = normalizedPath.toLowerCase()\n const homeLower = normalizedHome.toLowerCase()\n\n // Check for exact match or path with separator after home dir\n if (isWin ? pathLower === homeLower : normalizedPath === normalizedHome) {\n return '~'\n }\n\n const startsWithHome = isWin\n ? pathLower.startsWith(homeLower + '/')\n : normalizedPath.startsWith(normalizedHome + '/')\n\n if (startsWithHome) {\n // Always use forward slash for consistency with .claude.json\n const relativePart = absolutePath.slice(homeDir.length)\n return '~' + relativePart.replace(/\\\\/g, '/')\n }\n // Always normalize to forward slash\n return absolutePath.replace(/\\\\/g, '/')\n}\n\n// ============================================\n// Path Conversion (Pure)\n// ============================================\n\n/**\n * Convert project folder name to display path\n * Unix: -home-user-projects -> /home/user/projects\n * Windows: C--Users-david -> C:\\Users\\david\n * Handle dot-prefixed folders: --claude -> /.claude\n */\nexport const folderNameToDisplayPath = (folderName: string): string => {\n const parsed = parseWindowsFolderName(folderName)\n if (parsed.isWindows) {\n return parsed.drive + ':\\\\' + parsed.rest.replace(/--/g, '\\\\.').replace(/-/g, '\\\\')\n }\n\n // Unix path\n return folderName.replace(/^-/, '/').replace(/--/g, '/.').replace(/-/g, '/')\n}\n\n/**\n * Convert absolute path to project folder name\n * All non-alphanumeric characters are converted to '-'\n * Matches official Claude Code: A.replace(/[^a-zA-Z0-9]/g, '-')\n */\nexport const pathToFolderName = (absolutePath: string): string =>\n absolutePath.replace(/[^a-zA-Z0-9]/g, '-')\n\n// ============================================\n// I/O Functions (with optional DI for testing)\n// ============================================\n\n/**\n * Try to extract cwd from a single session file\n * @param filePath - Path to session file\n * @param fileSystem - Optional FileSystem for testing\n * @param logger - Optional Logger for testing\n */\nexport const tryGetCwdFromFile = (\n filePath: string,\n fileSystem: FileSystem = fs,\n logger: Logger = log\n): string | null => {\n const basename = path.basename(filePath)\n\n try {\n const content = fileSystem.readFileSync(filePath, 'utf-8')\n const cwd = extractCwdFromContent(content)\n\n if (cwd === null) {\n return null\n }\n\n return cwd\n } catch (e) {\n logger.warn(`tryGetCwdFromFile: ${basename} -> read error: ${e}`)\n return null\n }\n}\n\n/**\n * Extract real cwd path from session files in a project\n * @param folderName - Project folder name\n * @param sessionsDir - Optional sessions directory for testing\n * @param fileSystem - Optional FileSystem for testing\n * @param logger - Optional Logger for testing\n */\nexport const getRealPathFromSession = (\n folderName: string,\n sessionsDir: string = getSessionsDir(),\n fileSystem: FileSystem = fs,\n logger: Logger = log\n): string | null => {\n const projectDir = path.join(sessionsDir, folderName)\n\n try {\n const files = fileSystem.readdirSync(projectDir).filter(isSessionFile)\n\n const cwdList: string[] = []\n for (const f of files) {\n const cwd = tryGetCwdFromFile(path.join(projectDir, f), fileSystem, logger)\n if (cwd !== null) {\n cwdList.push(cwd)\n }\n }\n\n // Find cwd that matches folder name\n const matched = cwdList.find((cwd) => pathToFolderName(cwd) === folderName)\n if (matched) {\n return matched\n }\n\n // Log for debugging\n if (cwdList.length > 0) {\n logger.warn(\n `getRealPathFromSession: ${folderName} -> no match, cwds found: ${cwdList.join(', ')}`\n )\n }\n return null\n } catch {\n return null\n }\n}\n\n// ============================================\n// Public API\n// ============================================\n\n/** Get Claude config file path (~/.claude.json) */\nconst getClaudeConfigPath = (): string => path.join(os.homedir(), '.claude.json')\n\n/**\n * Resolve folder name to actual path using ~/.claude.json projects list.\n * Since pathToFolderName converts space/dot/separator all to dash,\n * we can't reverse it reliably. Instead, we look up known project paths\n * from Claude's config and find which one matches when converted.\n *\n * @param folderName - The folder name to resolve (e.g., \"C--Users-david-New-folder\")\n * @param fileSystem - Optional AsyncFileSystem for testing\n * @returns The actual absolute path or null if not found\n */\nexport const resolvePathFromClaudeConfig = async (\n folderName: string,\n fileSystem: AsyncFileSystem = fsp\n): Promise<string | null> => {\n try {\n const configPath = getClaudeConfigPath()\n const content = await fileSystem.readFile(configPath, 'utf-8')\n const config = JSON.parse(content) as { projects?: Record<string, unknown> }\n\n if (!config.projects) return null\n\n // Check each registered project path\n for (const projectPath of Object.keys(config.projects)) {\n if (pathToFolderName(projectPath) === folderName) {\n return projectPath\n }\n }\n\n return null\n } catch {\n return null\n }\n}\n\n/**\n * Convert folder name to relative or absolute path for display\n * If path is under home directory, show relative (~/...)\n *\n * Priority order (fastest first):\n * 1. Claude config lookup (~/.claude.json) - single async file read, cached by OS\n * 2. Session cwd extraction (sync reads session files - expensive but accurate)\n * 3. Pattern-based conversion (pure function, zero I/O - fallback, may be inaccurate for dots/spaces)\n */\nexport const folderNameToPath = async (folderName: string): Promise<string> => {\n const homeDir = os.homedir()\n\n // First try Claude config lookup (fast: single file read, handles spaces/dots correctly)\n const configPath = await resolvePathFromClaudeConfig(folderName)\n if (configPath) {\n return toRelativePath(configPath, homeDir)\n }\n\n // Second, try session cwd extraction (accurate but reads session files)\n const realPath = getRealPathFromSession(folderName)\n if (realPath) {\n return toRelativePath(realPath, homeDir)\n }\n\n // Fallback to pattern-based conversion (may be incorrect for dots/spaces in path)\n const absolutePath = folderNameToDisplayPath(folderName)\n return toRelativePath(absolutePath, homeDir)\n}\n\n/**\n * Find project folder name that matches the given workspace path\n * Searches through all projects and checks if any session's cwd matches\n *\n * @param workspacePath - Absolute path of current workspace\n * @param projectNames - List of project folder names to search\n * @param sessionsDir - Optional sessions directory for testing\n * @param fileSystem - Optional FileSystem for testing\n * @param logger - Optional Logger for testing\n * @returns Matching project folder name or null\n */\nexport const findProjectByWorkspacePath = (\n workspacePath: string,\n projectNames: string[],\n sessionsDir: string = getSessionsDir(),\n fileSystem: FileSystem = fs,\n logger: Logger = log\n): string | null => {\n // First, check if direct conversion matches any project\n const directMatch = pathToFolderName(workspacePath)\n if (projectNames.includes(directMatch)) {\n return directMatch\n }\n\n // Search through projects to find one with matching cwd\n for (const projectName of projectNames) {\n const projectDir = path.join(sessionsDir, projectName)\n\n try {\n const files = fileSystem.readdirSync(projectDir).filter(isSessionFile)\n\n for (const f of files) {\n const cwd = tryGetCwdFromFile(path.join(projectDir, f), fileSystem, logger)\n if (cwd === workspacePath) {\n logger.debug(\n `findProjectByWorkspacePath: ${workspacePath} -> found in ${projectName} (moved session)`\n )\n return projectName\n }\n }\n } catch {\n // Skip inaccessible projects\n }\n }\n\n logger.debug(`findProjectByWorkspacePath: ${workspacePath} -> no matching project found`)\n return null\n}\n","/**\n * Simple logger abstraction for Claude Sessions\n * Consumers can provide their own logger implementation\n */\n\nexport interface Logger {\n debug: (message: string, ...args: unknown[]) => void\n info: (message: string, ...args: unknown[]) => void\n warn: (message: string, ...args: unknown[]) => void\n error: (message: string, ...args: unknown[]) => void\n}\n\n// Default console logger\nconst consoleLogger: Logger = {\n debug: (msg, ...args) => console.debug(`[DEBUG] ${msg}`, ...args),\n info: (msg, ...args) => console.info(`[INFO] ${msg}`, ...args),\n warn: (msg, ...args) => console.warn(`[WARN] ${msg}`, ...args),\n error: (msg, ...args) => console.error(`[ERROR] ${msg}`, ...args),\n}\n\n// Global logger instance\nlet currentLogger: Logger = consoleLogger\n\n/**\n * Set custom logger implementation\n * @example\n * // VSCode extension\n * setLogger({\n * debug: (msg) => outputChannel.appendLine(`[DEBUG] ${msg}`),\n * info: (msg) => outputChannel.appendLine(`[INFO] ${msg}`),\n * warn: (msg) => outputChannel.appendLine(`[WARN] ${msg}`),\n * error: (msg) => outputChannel.appendLine(`[ERROR] ${msg}`),\n * })\n */\nexport const setLogger = (logger: Logger): void => {\n currentLogger = logger\n}\n\n/**\n * Get current logger instance\n */\nexport const getLogger = (): Logger => currentLogger\n\n/**\n * Create a namespaced logger\n * @example\n * const log = createLogger('paths')\n * log.debug('Converting folder name') // [DEBUG] [paths] Converting folder name\n */\nexport const createLogger = (namespace: string): Logger => ({\n debug: (msg, ...args) => currentLogger.debug(`[${namespace}] ${msg}`, ...args),\n info: (msg, ...args) => currentLogger.info(`[${namespace}] ${msg}`, ...args),\n warn: (msg, ...args) => currentLogger.warn(`[${namespace}] ${msg}`, ...args),\n error: (msg, ...args) => currentLogger.error(`[${namespace}] ${msg}`, ...args),\n})\n","/**\n * Utility functions for message processing\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport type {\n Message,\n MessagePayload,\n TextContent,\n AskUserQuestionResult,\n SummaryInfo,\n SessionTodos,\n} from './types.js'\nimport { createLogger } from './logger.js'\n\nconst logger = createLogger('utils')\n\n// IDE tag pattern for removal\nconst IDE_TAG_PATTERN = /<ide_[^>]*>[\\s\\S]*?<\\/ide_[^>]*>/g\n\n// Extract text content from message payload\nexport const extractTextContent = (\n message: MessagePayload | undefined,\n options: { stripIdeTags?: boolean } = {}\n): string => {\n if (!message) return ''\n\n const content = message.content\n if (!content) return ''\n\n let result: string\n\n // If content is string, return directly\n if (typeof content === 'string') {\n result = content\n } else if (Array.isArray(content)) {\n // If content is array, extract text items\n result = content\n .filter((item): item is TextContent => typeof item === 'object' && item?.type === 'text')\n .map((item) => {\n if (item.text == null) {\n logger.warn('TextContent item has undefined or null text property')\n return ''\n }\n return item.text\n })\n .join('')\n } else {\n result = ''\n }\n\n // Optionally strip IDE tags\n if (options.stripIdeTags) {\n result = result.replace(IDE_TAG_PATTERN, '').trim()\n }\n\n return result\n}\n\n/**\n * Parse command message content (e.g., slash commands like /commit)\n * Returns the command name and message extracted from XML tags\n */\nexport const parseCommandMessage = (\n content?: string\n): { name: string; message: string; args: string } => {\n const name = content?.match(/<command-name>([^<]+)<\\/command-name>/)?.[1] ?? ''\n const message = content?.match(/<command-message>([^<]+)<\\/command-message>/)?.[1] ?? ''\n const args = content?.match(/<command-args>([^<]+)<\\/command-args>/)?.[1]?.trim() ?? ''\n return { name, message, args }\n}\n\n// Extract title from message or text (strips IDE tags, uses first line)\nexport const extractTitle = (input: MessagePayload | string | undefined): string => {\n if (!input) return 'Untitled'\n\n // Extract text content with IDE tags stripped\n let text: string\n if (typeof input === 'string') {\n text = input.replace(IDE_TAG_PATTERN, '').trim()\n } else {\n text = extractTextContent(input, { stripIdeTags: true })\n }\n\n if (!text) return 'Untitled'\n\n // Extract first paragraph before parsing commands\n // This prevents embedded JSON/code from being parsed as commands\n let firstParagraph = text.trim()\n if (firstParagraph.includes('\\n\\n')) {\n firstParagraph = firstParagraph.split('\\n\\n')[0]\n }\n\n // Check for slash command format only in first paragraph\n const { name, args } = parseCommandMessage(firstParagraph)\n if (name) return args ? `${name} ${args}` : name\n\n return firstParagraph || 'Untitled'\n}\n\n// Check if message contains \"Invalid API key\"\nexport const isInvalidApiKeyMessage = (msg: Message): boolean => {\n const text = extractTextContent(msg.message)\n return text.includes('Invalid API key')\n}\n\n// Error patterns to exclude from tree view\nconst ERROR_SESSION_PATTERNS = [\n 'API Error',\n 'authentication_error',\n 'Invalid API key',\n 'OAuth token has expired',\n 'Please run /login',\n]\n\n// Check if session title/summary indicates an error session\nexport const isErrorSessionTitle = (title: string | undefined): boolean => {\n if (!title) return false\n return ERROR_SESSION_PATTERNS.some((pattern) => title.includes(pattern))\n}\n\n// Check if a message is a continuation summary (from compact)\nexport const isContinuationSummary = (msg: Message): boolean => {\n // isCompactSummary flag is set by Claude Code for continuation summaries\n if (msg.isCompactSummary === true) return true\n\n // Fallback: check message content\n if (msg.type !== 'user') return false\n const text = extractTextContent(msg.message as MessagePayload | undefined)\n return text.startsWith('This session is being continued from')\n}\n\n/**\n * Get display title with fallback logic\n * Priority: customTitle > currentSummary (truncated) > title > fallback\n * Also handles slash command format in title\n */\nexport const getDisplayTitle = (\n customTitle: string | undefined,\n currentSummary: string | undefined,\n title: string | undefined,\n maxLength = 60,\n fallback = 'Untitled'\n): string => {\n if (customTitle) return customTitle\n if (currentSummary) {\n return currentSummary.length > maxLength\n ? currentSummary.slice(0, maxLength - 3) + '...'\n : currentSummary\n }\n if (title && title !== 'Untitled') {\n // Extract first paragraph to avoid parsing embedded JSON/code\n const firstParagraph = title.includes('\\n\\n') ? title.split('\\n\\n')[0] : title\n // Check if first paragraph contains command-name tag (slash command)\n if (firstParagraph.includes('<command-name>')) {\n const { name, args } = parseCommandMessage(firstParagraph)\n if (name) return args ? `${name} ${args}` : name\n }\n return firstParagraph\n }\n return fallback\n}\n\n// Helper to replace message content with extracted text\nconst replaceMessageContent = (msg: Message, text: string): Message => ({\n ...msg,\n message: {\n ...msg.message,\n content: [{ type: 'text', text } satisfies TextContent],\n },\n toolUseResult: undefined,\n})\n\n// Clean up first message content when splitting session\n// Handles: 1) tool rejection with reason, 2) AskUserQuestion responses\nexport const cleanupSplitFirstMessage = (msg: Message): Message => {\n const toolUseResult = msg.toolUseResult\n if (!toolUseResult) return msg\n\n // Case 1: AskUserQuestion response (toolUseResult is object with questions/answers)\n if (typeof toolUseResult === 'object' && 'answers' in toolUseResult) {\n const answers = (toolUseResult as AskUserQuestionResult).answers\n const qaText = Object.entries(answers)\n .map(([q, a]) => `Q: ${q}\\nA: ${a}`)\n .join('\\n\\n')\n return replaceMessageContent(msg, qaText)\n }\n\n // Case 2: Tool rejection with reason (toolUseResult is string)\n if (typeof toolUseResult === 'string') {\n const rejectionMarker = 'The user provided the following reason for the rejection:'\n const rejectionIndex = toolUseResult.indexOf(rejectionMarker)\n if (rejectionIndex === -1) return msg\n\n const text = toolUseResult.slice(rejectionIndex + rejectionMarker.length).trim()\n if (!text) return msg\n return replaceMessageContent(msg, text)\n }\n\n return msg\n}\n\n/**\n * Mask home directory path with ~\n * Only masks the specified homeDir, not other users' paths\n */\nexport const maskHomePath = (text: string, homeDir: string): string => {\n if (!homeDir) return text\n\n // Escape special regex characters in homeDir\n const escapedHome = homeDir.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n const regex = new RegExp(`${escapedHome}(?=[/\\\\\\\\]|$)`, 'g')\n\n return text.replace(regex, '~')\n}\n\n/**\n * Get sort timestamp for session (Unix timestamp ms)\n * Priority: summaries[0].timestamp > createdAt > 0\n */\nexport const getSessionSortTimestamp = (session: {\n summaries?: SummaryInfo[]\n createdAt?: string\n}): number => {\n const timestampStr = session.summaries?.[0]?.timestamp ?? session.createdAt\n return timestampStr ? new Date(timestampStr).getTime() : 0\n}\n\n/**\n * Try to parse a single JSON line, returning null on failure with optional warning log\n * Use this when you want to skip invalid lines instead of throwing\n */\nexport const tryParseJsonLine = <T = Record<string, unknown>>(\n line: string,\n lineNumber: number,\n filePath?: string\n): T | null => {\n try {\n return JSON.parse(line) as T\n } catch {\n if (filePath) {\n logger.warn(`Skipping invalid JSON at line ${lineNumber} in ${filePath}`)\n }\n return null\n }\n}\n\n/**\n * Parse JSONL lines with optional strict mode\n * @param strict - When true, throws on malformed lines. When false (default), skips with a warning.\n */\nexport const parseJsonlLines = <T = Record<string, unknown>>(\n lines: string[],\n filePath: string,\n { strict = false }: { strict?: boolean } = {}\n): T[] => {\n const results: T[] = []\n for (let idx = 0; idx < lines.length; idx++) {\n try {\n results.push(JSON.parse(lines[idx]) as T)\n } catch (e) {\n const err = e as Error\n if (strict) {\n throw new Error(`Failed to parse line ${idx + 1} in ${filePath}: ${err.message}`, {\n cause: e,\n })\n }\n logger.warn(`Skipping malformed line ${idx + 1} in ${filePath}: ${err.message}`)\n }\n }\n return results\n}\n\n/**\n * Read and parse JSONL file (Effect wrapper)\n * Combines file reading and JSONL parsing with proper error messages\n */\nexport const readJsonlFile = <T = Record<string, unknown>>(\n filePath: string,\n options?: { strict?: boolean }\n) =>\n Effect.gen(function* () {\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n return parseJsonlLines<T>(lines, filePath, options)\n })\n\n// ============================================================================\n// UI Shared Utilities (VSCode Extension & Web UI)\n// ============================================================================\n\n/**\n * Format timestamp as relative time (e.g., \"2h ago\", \"3d ago\")\n * Used by both VSCode Extension tree view and Web UI\n *\n * @param timestamp - Unix timestamp (ms) or ISO date string\n * @returns Relative time string\n */\nexport const formatRelativeTime = (timestamp: number | string): string => {\n const date = typeof timestamp === 'string' ? new Date(timestamp) : new Date(timestamp)\n const now = new Date()\n const diff = now.getTime() - date.getTime()\n\n const minutes = Math.floor(diff / 60000)\n const hours = Math.floor(diff / 3600000)\n const days = Math.floor(diff / 86400000)\n\n if (minutes < 1) return 'just now'\n if (minutes < 60) return `${minutes}m ago`\n if (hours < 24) return `${hours}h ago`\n if (days < 7) return `${days}d ago`\n\n return date.toLocaleDateString()\n}\n\n/**\n * Calculate total todo count from session todos\n * Includes both session-level and agent-level todos\n */\nexport const getTotalTodoCount = (todos: SessionTodos): number => {\n return todos.sessionTodos.length + todos.agentTodos.reduce((sum, a) => sum + a.todos.length, 0)\n}\n\n/**\n * Check if session has sub-items (summaries, agents, or todos)\n * Used to determine if tree item should be expandable\n */\nexport const sessionHasSubItems = (data: {\n summaries: { length: number }\n agents: { length: number }\n todos: SessionTodos\n}): boolean => {\n const todoCount = getTotalTodoCount(data.todos)\n return data.summaries.length > 0 || data.agents.length > 0 || todoCount > 0\n}\n\n/**\n * Get session tooltip text based on what's displayed as title\n * Shows complementary information to the displayed title + session ID\n *\n * Logic:\n * - If customTitle is displayed → show currentSummary\n * - If currentSummary is displayed → show original title\n * - If title is displayed → show currentSummary\n * - Always append session ID at the end\n */\nexport const getSessionTooltip = (session: {\n id: string\n title?: string\n customTitle?: string\n currentSummary?: string\n createdAt?: string\n updatedAt?: string\n}): string => {\n const { id, title, customTitle, currentSummary, createdAt, updatedAt } = session\n let text: string\n // If customTitle is displayed, show currentSummary in tooltip\n if (customTitle && currentSummary) {\n text = currentSummary\n }\n // If currentSummary is displayed as title, show original title in tooltip\n else if (currentSummary && title && title !== 'Untitled') {\n text = title\n }\n // If title is displayed, show currentSummary if available\n else if (currentSummary) {\n text = currentSummary\n } else {\n text = title ?? 'No title'\n }\n\n // Append metadata\n const lines: string[] = [`ID: ${id}`]\n if (createdAt) {\n lines.push(`Created: ${new Date(createdAt).toLocaleString()}`)\n }\n if (updatedAt) {\n lines.push(`Updated: ${new Date(updatedAt).toLocaleString()}`)\n }\n\n text += '\\n\\n' + lines.join('\\n')\n\n return text\n}\n\n/**\n * Check if session can be moved to target project\n * Returns false if source and target are the same project\n */\nexport const canMoveSession = (sourceProject: string, targetProject: string): boolean => {\n return sourceProject !== targetProject\n}\n","/**\n * Project-level utilities\n */\nimport type { Project } from './types.js'\nimport { pathToFolderName } from './paths.js'\n\n/**\n * Sort projects with priority:\n * 1. Current project (if specified)\n * 2. Current user's home directory subpaths\n * 3. Others (alphabetically by displayName)\n */\nexport const sortProjects = (\n projects: Project[],\n options: {\n currentProjectName?: string | null\n homeDir?: string\n filterEmpty?: boolean\n } = {}\n): Project[] => {\n const { currentProjectName, homeDir, filterEmpty = true } = options\n\n const filtered = filterEmpty ? projects.filter((p) => p.sessionCount > 0) : projects\n\n // Convert homeDir to folder name format for comparison with project.name\n // e.g., \"/Users/david\" -> \"-Users-david\"\n const homeDirPrefix = homeDir ? pathToFolderName(homeDir) : null\n\n return filtered.sort((a, b) => {\n // Current project always first\n if (currentProjectName) {\n if (a.name === currentProjectName) return -1\n if (b.name === currentProjectName) return 1\n }\n\n // Then prioritize current user's home directory paths\n // Compare using project.name (folder name format) with homeDirPrefix\n if (homeDirPrefix) {\n const aIsUserHome = a.name.startsWith(homeDirPrefix)\n const bIsUserHome = b.name.startsWith(homeDirPrefix)\n if (aIsUserHome && !bIsUserHome) return -1\n if (!aIsUserHome && bIsUserHome) return 1\n }\n\n // Finally sort by display name\n return a.displayName.localeCompare(b.displayName)\n })\n}\n","/**\n * Shared constants and utilities for tree view rendering\n * Used by both VSCode Extension and Web UI\n */\n\n// ============================================================================\n// Tree Item Types\n// ============================================================================\n\n/**\n * Tree item type discriminator\n * Used for identifying node types in both VSCode TreeView and Web UI\n */\nexport type TreeItemType =\n | 'project'\n | 'session'\n | 'summaries-group'\n | 'todos-group'\n | 'agents-group'\n | 'summary'\n | 'todo'\n | 'agent'\n\n// ============================================================================\n// Icon Mappings (Framework-agnostic)\n// ============================================================================\n\n/**\n * Icon definition with both VSCode codicon and emoji variants\n */\ninterface IconDef {\n /** VSCode codicon name (without 'codicon-' prefix) */\n codicon: string\n /** Emoji for web/terminal display */\n emoji: string\n}\n\n/**\n * Todo status icon with optional color\n */\ninterface TodoIconDef extends IconDef {\n /** Optional color for VSCode ThemeColor */\n color?: 'green' | 'yellow'\n}\n\n/**\n * Icon mappings for all tree item types\n * - Use `.codicon` for VSCode Extension (ThemeIcon)\n * - Use `.emoji` for Web UI and terminal\n */\nexport const TREE_ICONS = {\n project: { codicon: 'folder', emoji: '📁' },\n session: { codicon: 'comment-discussion', emoji: '💬' },\n 'summaries-group': { codicon: 'history', emoji: '📝' },\n 'todos-group': { codicon: 'checklist', emoji: '📋' },\n 'agents-group': { codicon: 'hubot', emoji: '🤖' },\n summary: { codicon: 'note', emoji: '📝' },\n agent: { codicon: 'hubot', emoji: '🤖' },\n todo: {\n completed: { codicon: 'pass-filled', emoji: '✅', color: 'green' } as TodoIconDef,\n in_progress: { codicon: 'sync~spin', emoji: '🔄', color: 'yellow' } as TodoIconDef,\n pending: { codicon: 'circle-outline', emoji: '⭕' } as TodoIconDef,\n },\n} as const\n\n/**\n * Get todo icon based on status\n */\nexport const getTodoIcon = (status: 'pending' | 'in_progress' | 'completed'): TodoIconDef => {\n return TREE_ICONS.todo[status]\n}\n\n// ============================================================================\n// Tree Node ID Generation\n// ============================================================================\n\n/**\n * Generate unique tree node ID for drag & drop and state management\n * Format: `{type}:{projectName}:{sessionId}[:agentId][:itemIndex]`\n *\n * @example\n * generateTreeNodeId('session', 'my-project', 'abc123')\n * // => 'session:my-project:abc123'\n *\n * generateTreeNodeId('todo', 'my-project', 'abc123', 'agent-1', 0)\n * // => 'todo:my-project:abc123:agent-1:0'\n */\nexport const generateTreeNodeId = (\n type: TreeItemType,\n projectName: string,\n sessionId: string,\n agentId?: string,\n itemIndex?: number\n): string => {\n let id = `${type}:${projectName}:${sessionId}`\n if (agentId) id += `:${agentId}`\n if (itemIndex !== undefined) id += `:${itemIndex}`\n return id\n}\n\n/**\n * Parse tree node ID back to components\n */\nexport const parseTreeNodeId = (\n id: string\n): {\n type: TreeItemType\n projectName: string\n sessionId: string\n agentId?: string\n itemIndex?: number\n} | null => {\n const parts = id.split(':')\n if (parts.length < 3) return null\n\n const result: ReturnType<typeof parseTreeNodeId> = {\n type: parts[0] as TreeItemType,\n projectName: parts[1],\n sessionId: parts[2],\n }\n\n if (parts.length >= 4 && parts[3]) {\n result.agentId = parts[3]\n }\n\n if (parts.length >= 5 && parts[4]) {\n const idx = parseInt(parts[4], 10)\n if (!isNaN(idx)) {\n result.itemIndex = idx\n }\n }\n\n return result\n}\n","/**\n * Agent file management utilities\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir } from './paths.js'\nimport { tryParseJsonLine } from './utils.js'\nimport type { Message } from './types.js'\n\n// Find agent files linked to a session\nexport const findLinkedAgents = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const agentFiles = files.filter((f) => f.startsWith('agent-') && f.endsWith('.jsonl'))\n\n const linkedAgents: string[] = []\n\n for (const agentFile of agentFiles) {\n const filePath = path.join(projectPath, agentFile)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const firstLine = content.split('\\n')[0]\n\n if (firstLine) {\n try {\n const parsed = JSON.parse(firstLine) as { sessionId?: string }\n if (parsed.sessionId === sessionId) {\n linkedAgents.push(agentFile.replace('.jsonl', ''))\n }\n } catch {\n // Skip invalid JSON\n }\n }\n }\n\n return linkedAgents\n })\n\n// Internal type for orphan agents with file path and line count\ntype OrphanAgentWithPath = {\n agentId: string\n sessionId: string\n filePath: string\n lineCount: number\n}\n\n// Internal function to find orphan agents with file paths (used by delete)\nconst findOrphanAgentsWithPaths = (projectName: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n\n // Collect session IDs from .jsonl files (excluding agent files)\n const sessionIds = new Set(\n files\n .filter((f) => !f.startsWith('agent-') && f.endsWith('.jsonl'))\n .map((f) => f.replace('.jsonl', ''))\n )\n\n const orphanAgents: OrphanAgentWithPath[] = []\n\n // Helper to check if an agent file is orphan\n const checkAgentFile = async (\n filePath: string\n ): Promise<{ agentId: string; sessionId: string; lineCount: number } | null> => {\n try {\n const content = await fs.readFile(filePath, 'utf-8')\n const lines = content.split('\\n').filter((l) => l.trim())\n const firstLine = lines[0]\n if (!firstLine) return null\n\n const parsed = JSON.parse(firstLine) as { sessionId?: string }\n if (parsed.sessionId && !sessionIds.has(parsed.sessionId)) {\n const fileName = path.basename(filePath)\n return {\n agentId: fileName.replace('.jsonl', ''),\n sessionId: parsed.sessionId,\n lineCount: lines.length,\n }\n }\n } catch {\n // Skip invalid files\n }\n return null\n }\n\n // 1. Check agent files in project root\n const rootAgentFiles = files.filter((f) => f.startsWith('agent-') && f.endsWith('.jsonl'))\n for (const agentFile of rootAgentFiles) {\n const filePath = path.join(projectPath, agentFile)\n const orphan = yield* Effect.tryPromise(() => checkAgentFile(filePath))\n if (orphan) {\n orphanAgents.push({ ...orphan, filePath })\n }\n }\n\n // 2. Check subagents folders inside session directories\n for (const entry of files) {\n // Check if it's a directory (potential session folder)\n const entryPath = path.join(projectPath, entry)\n const stat = yield* Effect.tryPromise(() => fs.stat(entryPath).catch(() => null))\n\n if (stat?.isDirectory() && !entry.startsWith('.')) {\n // Check for subagents folder\n const subagentsPath = path.join(entryPath, 'subagents')\n const subagentsExists = yield* Effect.tryPromise(() =>\n fs\n .stat(subagentsPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (subagentsExists) {\n const subagentFiles = yield* Effect.tryPromise(() =>\n fs.readdir(subagentsPath).catch(() => [])\n )\n\n for (const subagentFile of subagentFiles) {\n if (subagentFile.startsWith('agent-') && subagentFile.endsWith('.jsonl')) {\n const filePath = path.join(subagentsPath, subagentFile)\n const orphan = yield* Effect.tryPromise(() => checkAgentFile(filePath))\n if (orphan) {\n orphanAgents.push({ ...orphan, filePath })\n }\n }\n }\n }\n }\n }\n\n return orphanAgents\n })\n\n// Find orphan agent files (agents whose parent session no longer exists)\n// Checks both:\n// 1. agent-*.jsonl files in project root\n// 2. agent-*.jsonl files in <session-id>/subagents/ folders\nexport const findOrphanAgents = (projectName: string) =>\n Effect.gen(function* () {\n const orphans = yield* findOrphanAgentsWithPaths(projectName)\n // Return without filePath for API compatibility\n return orphans.map(({ agentId, sessionId }) => ({ agentId, sessionId }))\n })\n\n// Delete orphan agent files\n// - Files with ≤2 lines (warmup only): permanently delete\n// - Files with >2 lines: move to .bak for recovery\n// - Clean up empty subagents folders and empty session folders\nexport const deleteOrphanAgents = (projectName: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const orphans = yield* findOrphanAgentsWithPaths(projectName)\n\n const deletedAgents: string[] = []\n const backedUpAgents: string[] = []\n const cleanedFolders: string[] = []\n let backupDirCreated = false\n\n // Track folders that might become empty\n const foldersToCheck = new Set<string>()\n\n for (const orphan of orphans) {\n // Track parent folders for cleanup\n const parentDir = path.dirname(orphan.filePath)\n if (parentDir.endsWith('/subagents') || parentDir.endsWith('\\\\subagents')) {\n foldersToCheck.add(parentDir)\n }\n\n if (orphan.lineCount <= 2) {\n // Warmup-only files: permanently delete\n yield* Effect.tryPromise(() => fs.unlink(orphan.filePath))\n deletedAgents.push(orphan.agentId)\n } else {\n // Files with actual data: move to .bak\n if (!backupDirCreated) {\n const backupDir = path.join(projectPath, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(backupDir, { recursive: true }))\n backupDirCreated = true\n }\n const backupDir = path.join(projectPath, '.bak')\n const agentBackupPath = path.join(backupDir, `${orphan.agentId}.jsonl`)\n yield* Effect.tryPromise(() => fs.rename(orphan.filePath, agentBackupPath))\n backedUpAgents.push(orphan.agentId)\n }\n }\n\n // Clean up empty subagents folders and their parent session folders\n for (const subagentsDir of foldersToCheck) {\n const isEmpty = yield* Effect.tryPromise(async () => {\n const files = await fs.readdir(subagentsDir)\n return files.length === 0\n })\n\n if (isEmpty) {\n // Remove empty subagents folder\n yield* Effect.tryPromise(() => fs.rmdir(subagentsDir))\n cleanedFolders.push(subagentsDir)\n\n // Check if parent session folder is also empty\n const sessionDir = path.dirname(subagentsDir)\n const sessionDirEmpty = yield* Effect.tryPromise(async () => {\n const files = await fs.readdir(sessionDir)\n return files.length === 0\n })\n\n if (sessionDirEmpty) {\n yield* Effect.tryPromise(() => fs.rmdir(sessionDir))\n cleanedFolders.push(sessionDir)\n }\n }\n }\n\n return {\n success: true,\n deletedAgents,\n backedUpAgents,\n cleanedFolders,\n deletedCount: deletedAgents.length,\n backedUpCount: backedUpAgents.length,\n cleanedFolderCount: cleanedFolders.length,\n count: deletedAgents.length + backedUpAgents.length,\n }\n })\n\n// Load agent messages from agent session file\nexport const loadAgentMessages = (\n projectName: string,\n _sessionId: string, // Reserved for future validation\n agentId: string\n) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n // Agent files are stored as agent-<agentId>.jsonl in project directory\n const agentFilePath = path.join(projectPath, `${agentId}.jsonl`)\n\n const content = yield* Effect.tryPromise(() => fs.readFile(agentFilePath, 'utf-8'))\n\n const lines = content.split('\\n').filter((line) => line.trim())\n const messages: Message[] = []\n\n for (let i = 0; i < lines.length; i++) {\n const parsed = tryParseJsonLine<Message>(lines[i], i + 1, agentFilePath)\n if (!parsed) continue\n // Skip header line (contains sessionId)\n if ('sessionId' in parsed && !('type' in parsed)) {\n continue\n }\n messages.push(parsed)\n }\n\n return messages\n })\n","/**\n * Todo file management utilities\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir, getTodosDir } from './paths.js'\nimport type { TodoItem, SessionTodos } from './types.js'\n\n// Find linked todo files for a session and its agents\n// Scans todos directory for files matching session pattern\nexport const findLinkedTodos = (sessionId: string, agentIds: string[] = []) =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n\n // Check if todos directory exists\n const exists = yield* Effect.tryPromise(() =>\n fs\n .access(todosDir)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!exists) {\n return {\n sessionId,\n sessionTodos: [],\n agentTodos: [],\n hasTodos: false,\n }\n }\n\n // Read session's own todo file\n const sessionTodoPath = path.join(todosDir, `${sessionId}.json`)\n let sessionTodos: TodoItem[] = []\n\n const sessionTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(sessionTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (sessionTodoExists) {\n const content = yield* Effect.tryPromise(() => fs.readFile(sessionTodoPath, 'utf-8'))\n try {\n sessionTodos = JSON.parse(content) as TodoItem[]\n } catch {\n // Invalid JSON, treat as empty\n }\n }\n\n // Scan todos directory for agent todo files matching this session\n const allFiles = yield* Effect.tryPromise(() => fs.readdir(todosDir))\n const agentTodoPattern = new RegExp(`^${sessionId}-agent-([a-f0-9-]+)\\\\.json$`)\n\n // Collect agent IDs from both provided list and directory scan\n const discoveredAgentIds = new Set<string>(agentIds)\n for (const file of allFiles) {\n const match = file.match(agentTodoPattern)\n if (match) {\n discoveredAgentIds.add(`agent-${match[1]}`)\n }\n }\n\n // Read agent todo files\n const agentTodos: { agentId: string; todos: TodoItem[] }[] = []\n\n for (const agentId of discoveredAgentIds) {\n // Agent todo files are named: {sessionId}-agent-{shortAgentId}.json\n const shortAgentId = agentId.replace('agent-', '')\n const agentTodoPath = path.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`)\n\n const agentTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(agentTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (agentTodoExists) {\n const content = yield* Effect.tryPromise(() => fs.readFile(agentTodoPath, 'utf-8'))\n try {\n const todos = JSON.parse(content) as TodoItem[]\n if (todos.length > 0) {\n agentTodos.push({ agentId, todos })\n }\n } catch {\n // Invalid JSON, skip\n }\n }\n }\n\n const hasTodos = sessionTodos.length > 0 || agentTodos.some((at) => at.todos.length > 0)\n\n return {\n sessionId,\n sessionTodos,\n agentTodos,\n hasTodos,\n } as SessionTodos\n })\n\n// Check if session has any todos (quick check)\n// Scans todos directory for files matching session pattern\nexport const sessionHasTodos = (sessionId: string, agentIds: string[] = []) =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n\n // Check if todos directory exists\n const exists = yield* Effect.tryPromise(() =>\n fs\n .access(todosDir)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!exists) return false\n\n // Check session's own todo file\n const sessionTodoPath = path.join(todosDir, `${sessionId}.json`)\n const sessionTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(sessionTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (sessionTodoExists) {\n const content = yield* Effect.tryPromise(() => fs.readFile(sessionTodoPath, 'utf-8'))\n try {\n const todos = JSON.parse(content) as TodoItem[]\n if (todos.length > 0) return true\n } catch {\n // Invalid JSON, continue\n }\n }\n\n // Scan todos directory for agent todo files matching this session\n const allFiles = yield* Effect.tryPromise(() => fs.readdir(todosDir))\n const agentTodoPattern = new RegExp(`^${sessionId}-agent-([a-f0-9-]+)\\\\.json$`)\n\n // Collect agent IDs from both provided list and directory scan\n const discoveredAgentIds = new Set<string>(agentIds)\n for (const file of allFiles) {\n const match = file.match(agentTodoPattern)\n if (match) {\n discoveredAgentIds.add(`agent-${match[1]}`)\n }\n }\n\n // Check agent todo files\n for (const agentId of discoveredAgentIds) {\n const shortAgentId = agentId.replace('agent-', '')\n const agentTodoPath = path.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`)\n\n const agentTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(agentTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (agentTodoExists) {\n const content = yield* Effect.tryPromise(() => fs.readFile(agentTodoPath, 'utf-8'))\n try {\n const todos = JSON.parse(content) as TodoItem[]\n if (todos.length > 0) return true\n } catch {\n // Invalid JSON, continue\n }\n }\n }\n\n return false\n })\n\n// Delete linked todo files for a session (move to .bak)\nexport const deleteLinkedTodos = (sessionId: string, agentIds: string[]) =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n\n // Check if todos directory exists\n const exists = yield* Effect.tryPromise(() =>\n fs\n .access(todosDir)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!exists) return { deletedCount: 0 }\n\n // Create backup directory\n const backupDir = path.join(todosDir, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(backupDir, { recursive: true }))\n\n let deletedCount = 0\n\n // Delete session's own todo file\n const sessionTodoPath = path.join(todosDir, `${sessionId}.json`)\n const sessionTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(sessionTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (sessionTodoExists) {\n const backupPath = path.join(backupDir, `${sessionId}.json`)\n yield* Effect.tryPromise(() => fs.rename(sessionTodoPath, backupPath))\n deletedCount++\n }\n\n // Delete agent todo files\n for (const agentId of agentIds) {\n const shortAgentId = agentId.replace('agent-', '')\n const agentTodoPath = path.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`)\n\n const agentTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(agentTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (agentTodoExists) {\n const backupPath = path.join(backupDir, `${sessionId}-agent-${shortAgentId}.json`)\n yield* Effect.tryPromise(() => fs.rename(agentTodoPath, backupPath))\n deletedCount++\n }\n }\n\n return { deletedCount }\n })\n\n// Find all orphan todo files (session no longer exists)\nexport const findOrphanTodos = () =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n const sessionsDir = getSessionsDir()\n\n // Check if directories exist\n const [todosExists, sessionsExists] = yield* Effect.all([\n Effect.tryPromise(() =>\n fs\n .access(todosDir)\n .then(() => true)\n .catch(() => false)\n ),\n Effect.tryPromise(() =>\n fs\n .access(sessionsDir)\n .then(() => true)\n .catch(() => false)\n ),\n ])\n\n if (!todosExists || !sessionsExists) return []\n\n // Get all todo files\n const todoFiles = yield* Effect.tryPromise(() => fs.readdir(todosDir))\n const jsonFiles = todoFiles.filter((f) => f.endsWith('.json'))\n\n // Build set of all valid session IDs across all projects\n const validSessionIds = new Set<string>()\n const projectEntries = yield* Effect.tryPromise(() =>\n fs.readdir(sessionsDir, { withFileTypes: true })\n )\n\n for (const entry of projectEntries) {\n if (!entry.isDirectory() || entry.name.startsWith('.')) continue\n const projectPath = path.join(sessionsDir, entry.name)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n for (const f of files) {\n if (f.endsWith('.jsonl') && !f.startsWith('agent-')) {\n validSessionIds.add(f.replace('.jsonl', ''))\n }\n }\n }\n\n // Find orphan todo files\n const orphans: string[] = []\n for (const todoFile of jsonFiles) {\n // Parse session ID from todo filename\n // Format: {sessionId}.json or {sessionId}-agent-{agentId}.json\n const match = todoFile.match(/^([a-f0-9-]+)(?:-agent-[a-f0-9]+)?\\.json$/)\n if (match) {\n const sessionId = match[1]\n if (!validSessionIds.has(sessionId)) {\n orphans.push(todoFile)\n }\n }\n }\n\n return orphans\n })\n\n// Delete orphan todo files\nexport const deleteOrphanTodos = () =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n const orphans = yield* findOrphanTodos()\n\n if (orphans.length === 0) return { success: true, deletedCount: 0 }\n\n // Create backup directory\n const backupDir = path.join(todosDir, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(backupDir, { recursive: true }))\n\n let deletedCount = 0\n\n for (const orphan of orphans) {\n const filePath = path.join(todosDir, orphan)\n const backupPath = path.join(backupDir, orphan)\n yield* Effect.tryPromise(() => fs.rename(filePath, backupPath))\n deletedCount++\n }\n\n return { success: true, deletedCount }\n })\n","/**\n * Project listing operations\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir, folderNameToPath } from '../paths.js'\nimport type { Project } from '../types.js'\n\n// List all project directories\nexport const listProjects = Effect.gen(function* () {\n const sessionsDir = getSessionsDir()\n\n const exists = yield* Effect.tryPromise(() =>\n fs\n .access(sessionsDir)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!exists) {\n return [] as Project[]\n }\n\n const entries = yield* Effect.tryPromise(() => fs.readdir(sessionsDir, { withFileTypes: true }))\n\n const projects = yield* Effect.all(\n entries\n .filter((e) => e.isDirectory() && !e.name.startsWith('.'))\n .map((entry) =>\n Effect.gen(function* () {\n const projectPath = path.join(sessionsDir, entry.name)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n // Exclude agent- files (subagent logs)\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n const displayName = yield* Effect.tryPromise(() => folderNameToPath(entry.name))\n\n return {\n name: entry.name,\n displayName,\n path: projectPath,\n sessionCount: sessionFiles.length,\n } satisfies Project\n })\n ),\n { concurrency: 10 }\n )\n\n return projects\n})\n","/**\n * Session CRUD operations\n */\nimport { Effect, pipe, Array as A, Option as O } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport * as crypto from 'node:crypto'\nimport { getSessionsDir } from '../paths.js'\nimport {\n extractTitle,\n isContinuationSummary,\n cleanupSplitFirstMessage,\n parseJsonlLines,\n readJsonlFile,\n} from '../utils.js'\nimport { validateChain, autoRepairChain } from './validation.js'\nimport { findLinkedAgents } from '../agents.js'\nimport { deleteLinkedTodos } from '../todos.js'\nimport type {\n Message,\n SessionMeta,\n DeleteSessionResult,\n RenameSessionResult,\n SplitSessionResult,\n MoveSessionResult,\n} from '../types.js'\n\n// Update summary message in session\nexport const updateSessionSummary = (projectName: string, sessionId: string, newSummary: string) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const messages = yield* readJsonlFile<Record<string, unknown>>(filePath, { strict: true })\n\n // Find existing summary message\n const summaryIdx = messages.findIndex((m) => m.type === 'summary')\n\n if (summaryIdx >= 0) {\n // Update existing summary\n messages[summaryIdx] = { ...messages[summaryIdx], summary: newSummary }\n } else {\n // Add new summary at the beginning\n const firstUserMsg = messages.find((m) => m.type === 'user')\n const summaryMsg = {\n type: 'summary',\n summary: newSummary,\n leafUuid: (firstUserMsg as Message | undefined)?.uuid ?? null,\n }\n messages.unshift(summaryMsg)\n }\n\n const newContent = messages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n return { success: true }\n })\n\n// List sessions in a project\nexport const listSessions = (projectName: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n // Exclude agent- files (subagent logs)\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n\n const sessions = yield* Effect.all(\n sessionFiles.map((file) =>\n Effect.gen(function* () {\n const filePath = path.join(projectPath, file)\n const messages = yield* readJsonlFile<Message>(filePath)\n\n const sessionId = file.replace('.jsonl', '')\n\n // Filter only user/assistant messages for counting\n const userAssistantMessages = messages.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n )\n\n // Check if session has summary (for preserved sessions without user/assistant messages)\n const hasSummary = messages.some((m) => m.type === 'summary')\n\n const firstMessage = userAssistantMessages[0]\n const lastMessage = userAssistantMessages[userAssistantMessages.length - 1]\n\n // Extract title from first user message\n const title = pipe(\n messages,\n A.findFirst((m) => m.type === 'user'),\n O.map((m) => extractTitle(m.message)),\n O.getOrElse(() => (hasSummary ? '[Summary Only]' : `Session ${sessionId.slice(0, 8)}`))\n )\n\n // Extract currentSummary from first summary message\n const currentSummary = pipe(\n messages,\n A.findFirst((m) => m.type === 'summary'),\n O.map((m) => m.summary as string),\n O.getOrUndefined\n )\n\n // Extract customTitle from custom-title message\n const customTitle = pipe(\n messages,\n A.findFirst((m) => m.type === 'custom-title'),\n O.map((m) => (m as { customTitle?: string }).customTitle),\n O.flatMap(O.fromNullable),\n O.getOrUndefined\n )\n\n return {\n id: sessionId,\n projectName,\n title,\n customTitle,\n currentSummary,\n // If session has summary but no user/assistant messages, count as 1\n messageCount:\n userAssistantMessages.length > 0 ? userAssistantMessages.length : hasSummary ? 1 : 0,\n createdAt: firstMessage?.timestamp,\n updatedAt: lastMessage?.timestamp,\n } satisfies SessionMeta\n })\n ),\n { concurrency: 10 }\n )\n\n // Sort by newest first\n return sessions.sort((a, b) => {\n const dateA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0\n const dateB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0\n return dateB - dateA\n })\n })\n\n// Read session messages\nexport const readSession = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n return yield* readJsonlFile<Message>(filePath)\n })\n\nimport { deleteMessageWithChainRepair } from './validation.js'\n\n// Delete a message from session and repair parentUuid chain\n// Optional targetType parameter to specify which type to delete when uuid/messageId collision exists\nexport const deleteMessage = (\n projectName: string,\n sessionId: string,\n messageUuid: string,\n targetType?: 'file-history-snapshot' | 'summary'\n) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const messages = yield* readJsonlFile<Record<string, unknown>>(filePath, { strict: true })\n\n // Use the pure function for chain repair\n const result = deleteMessageWithChainRepair(messages, messageUuid, targetType)\n\n if (!result.deleted) {\n return { success: false, error: 'Message not found' }\n }\n\n const newContent = messages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n return { success: true, deletedMessage: result.deleted }\n })\n\n// Restore a deleted message at a specific index\nexport const restoreMessage = (\n projectName: string,\n sessionId: string,\n message: Record<string, unknown>,\n index: number\n) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const messages = yield* readJsonlFile<Record<string, unknown>>(filePath, { strict: true })\n\n const msgUuid = message.uuid ?? message.messageId\n if (!msgUuid) {\n return { success: false, error: 'Message has no uuid or messageId' }\n }\n\n // Find the message that currently has parentUuid pointing to restored message's parent\n // and update it to point to the restored message instead\n const restoredParentUuid = message.parentUuid as string | undefined\n for (const msg of messages) {\n if (msg.parentUuid === restoredParentUuid) {\n // This message was previously pointing to the deleted message's parent\n // Now it should point to the restored message\n msg.parentUuid = msgUuid\n break // Only one message should be affected\n }\n }\n\n // Insert message at the specified index (or at end if index is out of bounds)\n const insertIndex = Math.min(index, messages.length)\n messages.splice(insertIndex, 0, message)\n\n const newContent = messages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n return { success: true }\n })\n\n// Delete a session and its linked agent/todo files\nexport const deleteSession = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const sessionsDir = getSessionsDir()\n const projectPath = path.join(sessionsDir, projectName)\n const filePath = path.join(projectPath, `${sessionId}.jsonl`)\n\n // Find linked agents first (before any deletion)\n const linkedAgents = yield* findLinkedAgents(projectName, sessionId)\n\n // Check file size - if empty (0 bytes), just delete without backup\n const stat = yield* Effect.tryPromise(() => fs.stat(filePath))\n if (stat.size === 0) {\n yield* Effect.tryPromise(() => fs.unlink(filePath))\n // Still delete linked agents and todos for empty sessions\n const agentBackupDir = path.join(projectPath, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(agentBackupDir, { recursive: true }))\n for (const agentId of linkedAgents) {\n const agentPath = path.join(projectPath, `${agentId}.jsonl`)\n const agentBackupPath = path.join(agentBackupDir, `${agentId}.jsonl`)\n yield* Effect.tryPromise(() => fs.rename(agentPath, agentBackupPath).catch(() => {}))\n }\n yield* deleteLinkedTodos(sessionId, linkedAgents)\n return { success: true, deletedAgents: linkedAgents.length } satisfies DeleteSessionResult\n }\n\n // Create backup directory\n const backupDir = path.join(sessionsDir, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(backupDir, { recursive: true }))\n\n // Delete linked agent files (move to .bak in project folder)\n const agentBackupDir = path.join(projectPath, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(agentBackupDir, { recursive: true }))\n for (const agentId of linkedAgents) {\n const agentPath = path.join(projectPath, `${agentId}.jsonl`)\n const agentBackupPath = path.join(agentBackupDir, `${agentId}.jsonl`)\n yield* Effect.tryPromise(() => fs.rename(agentPath, agentBackupPath).catch(() => {}))\n }\n\n // Delete linked todo files\n const todosResult = yield* deleteLinkedTodos(sessionId, linkedAgents)\n\n // Move session to backup (format: project_name_session_id.jsonl)\n const backupPath = path.join(backupDir, `${projectName}_${sessionId}.jsonl`)\n yield* Effect.tryPromise(() => fs.rename(filePath, backupPath))\n\n return {\n success: true,\n backupPath,\n deletedAgents: linkedAgents.length,\n deletedTodos: todosResult.deletedCount,\n } satisfies DeleteSessionResult\n })\n\n// Rename session by updating custom-title and first summary\n// custom-title is stored in this session file\n// summary is stored in OTHER session files (where leafUuid points to this session's messages)\nexport const renameSession = (projectName: string, sessionId: string, newTitle: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const filePath = path.join(projectPath, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n\n if (lines.length === 0) {\n return { success: false, error: 'Empty session' } satisfies RenameSessionResult\n }\n\n const messages = parseJsonlLines<Record<string, unknown>>(lines, filePath, { strict: true })\n\n // Remove existing custom-title (may be at wrong position) and append to end\n const customTitleIdx = messages.findIndex((m) => m.type === 'custom-title')\n if (customTitleIdx >= 0) {\n messages.splice(customTitleIdx, 1)\n }\n messages.push({\n type: 'custom-title',\n customTitle: newTitle,\n sessionId,\n })\n\n const newContent = messages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n return { success: true } satisfies RenameSessionResult\n })\n\n// Move session from one project to another\nexport const moveSession = (\n sourceProject: string,\n sessionId: string,\n targetProject: string\n): Effect.Effect<MoveSessionResult, Error> =>\n Effect.gen(function* () {\n const sessionsDir = getSessionsDir()\n const sourcePath = path.join(sessionsDir, sourceProject)\n const targetPath = path.join(sessionsDir, targetProject)\n\n const sourceFile = path.join(sourcePath, `${sessionId}.jsonl`)\n const targetFile = path.join(targetPath, `${sessionId}.jsonl`)\n\n // Check source file exists\n const sourceExists = yield* Effect.tryPromise(() =>\n fs\n .access(sourceFile)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!sourceExists) {\n return { success: false, error: 'Source session not found' }\n }\n\n // Check target file does not exist\n const targetExists = yield* Effect.tryPromise(() =>\n fs\n .access(targetFile)\n .then(() => true)\n .catch(() => false)\n )\n\n if (targetExists) {\n return { success: false, error: 'Session already exists in target project' }\n }\n\n // Create target directory if needed\n yield* Effect.tryPromise(() => fs.mkdir(targetPath, { recursive: true }))\n\n // Find linked agents before moving\n const linkedAgents = yield* findLinkedAgents(sourceProject, sessionId)\n\n // Move session file\n yield* Effect.tryPromise(() => fs.rename(sourceFile, targetFile))\n\n // Move linked agent files\n for (const agentId of linkedAgents) {\n const sourceAgentFile = path.join(sourcePath, `${agentId}.jsonl`)\n const targetAgentFile = path.join(targetPath, `${agentId}.jsonl`)\n\n const agentExists = yield* Effect.tryPromise(() =>\n fs\n .access(sourceAgentFile)\n .then(() => true)\n .catch(() => false)\n )\n\n if (agentExists) {\n yield* Effect.tryPromise(() => fs.rename(sourceAgentFile, targetAgentFile))\n }\n }\n\n return { success: true }\n })\n\n// Split session at a specific message\n// Original session keeps the ID and contains messages FROM splitAtMessageUuid onwards (newer messages)\n// New session gets a new ID and contains messages BEFORE splitAtMessageUuid (older messages)\nexport const splitSession = (projectName: string, sessionId: string, splitAtMessageUuid: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const filePath = path.join(projectPath, `${sessionId}.jsonl`)\n\n // Parse all messages preserving their full structure\n const allMessages = yield* readJsonlFile<Message>(filePath, { strict: true })\n\n // Find the split point\n const splitIndex = allMessages.findIndex((m) => m.uuid === splitAtMessageUuid)\n if (splitIndex === -1) {\n return { success: false, error: 'Message not found' } satisfies SplitSessionResult\n }\n\n if (splitIndex === 0) {\n return { success: false, error: 'Cannot split at first message' } satisfies SplitSessionResult\n }\n\n // Generate new session ID for the OLD messages (before split point)\n const newSessionId = crypto.randomUUID()\n\n // Check if the split message is a continuation summary\n const splitMessage = allMessages[splitIndex]\n const shouldDuplicate = isContinuationSummary(splitMessage)\n\n // Split messages:\n // - keptMessages: from splitIndex onwards (stays in original session with original ID) - NEW messages\n // - movedMessages: before splitIndex (goes to new session with new ID) - OLD messages\n let keptMessages = allMessages.slice(splitIndex)\n let movedMessages: Message[]\n\n if (shouldDuplicate) {\n // Create a copy of the continuation message with new UUID for the new (old messages) session\n const duplicatedMessage: Message = {\n ...splitMessage,\n uuid: crypto.randomUUID(),\n sessionId: newSessionId,\n }\n movedMessages = [...allMessages.slice(0, splitIndex), duplicatedMessage]\n } else {\n movedMessages = allMessages.slice(0, splitIndex)\n }\n\n // Update kept messages: fix first message's parentUuid\n keptMessages = keptMessages.map((msg, index) => {\n let updated: Message = { ...msg }\n if (index === 0) {\n // First message of kept session should have no parent\n updated.parentUuid = null\n // Clean up first message content if it's a tool_result rejection\n updated = cleanupSplitFirstMessage(updated)\n }\n return updated\n })\n\n // Update moved messages with new sessionId\n const updatedMovedMessages: Message[] = movedMessages.map((msg) => ({\n ...msg,\n sessionId: newSessionId,\n }))\n\n // Write kept messages (newer) to original file (keeps original ID)\n const keptContent = keptMessages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, keptContent, 'utf-8'))\n\n // Write moved messages (older) to new session file\n const newFilePath = path.join(projectPath, `${newSessionId}.jsonl`)\n const newContent = updatedMovedMessages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(newFilePath, newContent, 'utf-8'))\n\n // Update linked agent files that reference the old sessionId\n // Agents related to OLD messages should be moved to new session\n const agentFiles = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const agentJsonlFiles = agentFiles.filter((f) => f.startsWith('agent-') && f.endsWith('.jsonl'))\n\n for (const agentFile of agentJsonlFiles) {\n const agentPath = path.join(projectPath, agentFile)\n const agentContent = yield* Effect.tryPromise(() => fs.readFile(agentPath, 'utf-8'))\n const agentLines = agentContent.trim().split('\\n').filter(Boolean)\n\n if (agentLines.length === 0) continue\n\n const agentMessages = parseJsonlLines<Record<string, unknown>>(agentLines, agentPath, {\n strict: true,\n })\n const firstAgentMsg = agentMessages[0] as { sessionId?: string }\n\n // If this agent belongs to the original session, check if it should be moved\n if (firstAgentMsg.sessionId === sessionId) {\n // Check if any message in MOVED (old) messages is related to this agent\n const agentId = agentFile.replace('agent-', '').replace('.jsonl', '')\n const isRelatedToMoved = movedMessages.some(\n (msg) => (msg as { agentId?: string }).agentId === agentId\n )\n\n if (isRelatedToMoved) {\n // Update all messages in this agent file to reference new sessionId\n const updatedAgentMessages = agentMessages.map((msg) =>\n JSON.stringify({ ...msg, sessionId: newSessionId })\n )\n const updatedAgentContent = updatedAgentMessages.join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(agentPath, updatedAgentContent, 'utf-8'))\n }\n }\n }\n\n return {\n success: true,\n newSessionId,\n newSessionPath: newFilePath,\n movedMessageCount: movedMessages.length,\n duplicatedSummary: shouldDuplicate,\n } satisfies SplitSessionResult\n })\n\n// Repair broken parentUuid chain in a session\nexport const repairChain = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const messages = yield* readJsonlFile<Message>(filePath, { strict: true })\n\n // Validate before repair\n const beforeResult = validateChain(messages)\n\n // Repair chain\n const repairCount = autoRepairChain(messages)\n\n if (repairCount > 0) {\n const newContent = messages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n }\n\n return {\n success: true,\n repairCount,\n errorsBefore: beforeResult.errors.length,\n errorsAfter: validateChain(messages).errors.length,\n }\n })\n","/**\n * Session message chain validation utilities\n *\n * Validates:\n * 1. parentUuid chain integrity (skip file-history-snapshot)\n * 2. tool_use_id / tool_result matching\n */\n\nexport interface ChainError {\n type: 'broken_chain' | 'orphan_parent'\n uuid: string\n line: number\n parentUuid: string | null\n expectedParent?: string\n}\n\nexport interface ToolUseResultError {\n type: 'orphan_tool_result'\n uuid: string\n line: number\n toolUseId: string\n}\n\nexport interface ProgressError {\n type: 'unwanted_progress'\n line: number\n hookEvent?: string\n hookName?: string\n messageType?: string\n}\n\nexport interface ValidationResult {\n valid: boolean\n errors: (ChainError | ToolUseResultError | ProgressError)[]\n}\n\ninterface MessageContent {\n type?: string\n id?: string\n tool_use_id?: string\n}\n\ninterface MessagePayload {\n role?: string\n content?: unknown // Can be string | MessageContent[] | etc.\n}\n\nexport interface GenericMessage {\n type?: string\n uuid?: string\n parentUuid?: string | null\n logicalParentUuid?: string | null\n message?: MessagePayload\n messageId?: string\n}\n\n/**\n * Validate parentUuid chain for messages\n *\n * Rules:\n * - Skip file-history-snapshot type (has no uuid, uses messageId instead)\n * - Skip messages without uuid\n * - First message can have null parentUuid\n * - Subsequent messages must have valid parentUuid pointing to existing uuid\n */\nexport function validateChain(\n messages: readonly GenericMessage[]\n): ValidationResult & { errors: ChainError[] } {\n const errors: ChainError[] = []\n\n // Collect all uuids for validation\n const uuids = new Set<string>()\n for (const msg of messages) {\n if (msg.uuid) {\n uuids.add(msg.uuid)\n }\n }\n\n // Track first message with uuid\n let foundFirstMessage = false\n\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]\n\n // Skip file-history-snapshot and summary (no uuid chain participation)\n if (msg.type === 'file-history-snapshot' || msg.type === 'summary') {\n continue\n }\n\n // Skip messages without uuid\n if (!msg.uuid) {\n continue\n }\n\n // First message can have null parentUuid or orphan_parent (compacted session)\n // Only undefined is an error for first message\n if (!foundFirstMessage) {\n foundFirstMessage = true\n // undefined is an error\n if (msg.parentUuid === undefined) {\n errors.push({\n type: 'broken_chain',\n uuid: msg.uuid,\n line: i + 1,\n parentUuid: null,\n })\n continue\n }\n // null or orphan_parent is OK for first message\n continue\n }\n\n // Check for broken chain (null/undefined parentUuid for non-first message)\n // Skip if logicalParentUuid exists (e.g., compact_boundary messages)\n if (msg.parentUuid === null || msg.parentUuid === undefined) {\n if (!msg.logicalParentUuid) {\n errors.push({\n type: 'broken_chain',\n uuid: msg.uuid,\n line: i + 1,\n parentUuid: null,\n })\n }\n continue\n }\n\n // Check for orphan parent (parentUuid pointing to non-existent uuid)\n if (!uuids.has(msg.parentUuid)) {\n errors.push({\n type: 'orphan_parent',\n uuid: msg.uuid,\n line: i + 1,\n parentUuid: msg.parentUuid,\n })\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n }\n}\n\n/**\n * Validate for unwanted progress messages (hook outputs)\n *\n * Only 'Stop' hookEvent is treated as an error (should be removed)\n */\nexport function validateProgressMessages(\n messages: readonly GenericMessage[]\n): ValidationResult & { errors: ProgressError[] } {\n const errors: ProgressError[] = []\n\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]\n if (msg.type === 'progress') {\n const progressMsg = msg as GenericMessage & {\n hookEvent?: string\n hookName?: string\n data?: { hookEvent?: string; hookName?: string }\n }\n // Support both flat (hookEvent) and nested (data.hookEvent) formats\n const hookEvent = progressMsg.hookEvent ?? progressMsg.data?.hookEvent\n const hookName = progressMsg.hookName ?? progressMsg.data?.hookName\n\n // 'Stop' hookEvent is an error\n if (hookEvent === 'Stop') {\n errors.push({\n type: 'unwanted_progress',\n line: i + 1,\n hookEvent,\n hookName,\n })\n }\n } else if (msg.type === 'saved_hook_context') {\n errors.push({\n type: 'unwanted_progress',\n line: i + 1,\n messageType: 'saved_hook_context',\n })\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n }\n}\n\n/**\n * Validate tool_use_id / tool_result matching\n *\n * Rules:\n * - All tool_result blocks must have a corresponding tool_use block in the session\n * - tool_use blocks are collected from all messages (not just previous)\n */\nexport function validateToolUseResult(\n messages: readonly GenericMessage[]\n): ValidationResult & { errors: ToolUseResultError[] } {\n const errors: ToolUseResultError[] = []\n\n // Collect all tool_use IDs\n const toolUseIds = new Set<string>()\n for (const msg of messages) {\n const content = msg.message?.content\n if (Array.isArray(content)) {\n for (const item of content as MessageContent[]) {\n if (item.type === 'tool_use' && item.id) {\n toolUseIds.add(item.id)\n }\n }\n }\n }\n\n // Check all tool_result blocks have matching tool_use\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]\n const content = msg.message?.content\n if (!Array.isArray(content)) {\n continue\n }\n\n for (const item of content as MessageContent[]) {\n if (item.type === 'tool_result' && item.tool_use_id) {\n if (!toolUseIds.has(item.tool_use_id)) {\n errors.push({\n type: 'orphan_tool_result',\n uuid: msg.uuid || '',\n line: i + 1,\n toolUseId: item.tool_use_id,\n })\n }\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n }\n}\n\n/**\n * Auto-repair chain errors by linking messages to their previous message\n *\n * Fixes:\n * - broken_chain: Sets parentUuid to the previous message's uuid\n * - orphan_parent: Sets parentUuid to the previous message's uuid\n *\n * @param messages - Array of messages (will be mutated)\n * @returns Number of repairs made\n */\nexport function autoRepairChain<T extends GenericMessage>(messages: T[]): number {\n let repairCount = 0\n\n // Build uuid set for validation\n const uuids = new Set<string>()\n for (const msg of messages) {\n if (msg.uuid) {\n uuids.add(msg.uuid)\n }\n }\n\n // Track last valid uuid and state\n let lastUuid: string | null = null\n let isFirstWithUuid = true\n\n for (const msg of messages) {\n // Skip file-history-snapshot and summary (no uuid chain participation)\n if (msg.type === 'file-history-snapshot' || msg.type === 'summary') {\n continue\n }\n\n // Skip messages without uuid\n if (!msg.uuid) {\n continue\n }\n\n // First message with uuid: only fix undefined -> null\n // null and orphan_parent are valid for first message (compacted session)\n if (isFirstWithUuid) {\n isFirstWithUuid = false\n if (msg.parentUuid === undefined) {\n msg.parentUuid = null\n repairCount++\n }\n lastUuid = msg.uuid\n continue\n }\n\n // Check for broken_chain (null/undefined parentUuid)\n if (msg.parentUuid === null || msg.parentUuid === undefined) {\n if (lastUuid) {\n msg.parentUuid = lastUuid\n repairCount++\n }\n }\n // Check for orphan_parent (parentUuid points to non-existent uuid)\n else if (!uuids.has(msg.parentUuid)) {\n if (lastUuid) {\n msg.parentUuid = lastUuid\n repairCount++\n }\n }\n\n lastUuid = msg.uuid\n }\n\n return repairCount\n}\n\n/**\n * Repair parentUuid chain after removing messages\n *\n * When a message is removed, any message that had the removed message as its\n * parentUuid needs to be updated to point to the removed message's parentUuid.\n * Handles chained removals (e.g., a -> p1 -> p2 -> b, removing p1 and p2 should result in a -> b).\n *\n * @param messages - Array of messages (will be mutated)\n * @param removedMessages - Messages that were removed (need uuid and parentUuid)\n */\nexport function repairParentUuidChain<T extends GenericMessage>(\n messages: T[],\n removedMessages: T[]\n): void {\n // Build a map of removed uuid -> parentUuid for chain resolution\n const removedMap = new Map<string, string | null | undefined>()\n for (const removed of removedMessages) {\n if (removed.uuid && removed.type !== 'file-history-snapshot') {\n removedMap.set(removed.uuid, removed.parentUuid)\n }\n }\n\n // Resolve final parent by following the chain through removed messages\n const resolveParent = (parentUuid: string | null | undefined): string | null | undefined => {\n let current = parentUuid\n while (current && removedMap.has(current)) {\n current = removedMap.get(current)\n }\n return current\n }\n\n // Update all messages pointing to removed messages\n for (const msg of messages) {\n if (msg.parentUuid && removedMap.has(msg.parentUuid)) {\n msg.parentUuid = resolveParent(msg.parentUuid)\n }\n }\n}\n\n/**\n * Delete a message and repair the parentUuid chain\n *\n * This is a pure function for client-side use (without file I/O)\n * Server-side deleteMessage in crud.ts uses similar logic with file operations\n *\n * @param messages - Array of messages (will be mutated)\n * @param targetId - uuid, messageId, or leafUuid of message to delete\n * @param targetType - Optional: 'file-history-snapshot' or 'summary' to disambiguate collisions\n * @returns Object with deleted message and messages to also delete (orphan tool_results)\n */\n// Helper: find target message index by type or priority\nfunction findTargetIndex<T extends GenericMessage>(\n messages: T[],\n targetId: string,\n targetType?: 'file-history-snapshot' | 'summary'\n): number {\n if (targetType === 'file-history-snapshot') {\n return messages.findIndex((m) => m.type === 'file-history-snapshot' && m.messageId === targetId)\n }\n if (targetType === 'summary') {\n return messages.findIndex(\n (m) => (m as GenericMessage & { leafUuid?: string }).leafUuid === targetId\n )\n }\n // Priority: uuid > leafUuid > messageId\n let idx = messages.findIndex((m) => m.uuid === targetId)\n if (idx === -1) {\n idx = messages.findIndex(\n (m) => (m as GenericMessage & { leafUuid?: string }).leafUuid === targetId\n )\n }\n if (idx === -1) {\n idx = messages.findIndex((m) => m.type === 'file-history-snapshot' && m.messageId === targetId)\n }\n return idx\n}\n\n// Helper: check if user message contains tool_result matching any toolUseId\nfunction hasMatchingToolResult(msg: GenericMessage, toolUseIds: string[]): boolean {\n if (msg.type !== 'user') return false\n const content = msg.message?.content\n if (!Array.isArray(content)) return false\n return (content as MessageContent[]).some(\n (item) =>\n item.type === 'tool_result' && item.tool_use_id && toolUseIds.includes(item.tool_use_id)\n )\n}\n\nexport function deleteMessageWithChainRepair<T extends GenericMessage>(\n messages: T[],\n targetId: string,\n targetType?: 'file-history-snapshot' | 'summary'\n): { deleted: T | null; alsoDeleted: T[] } {\n const targetIndex = findTargetIndex(messages, targetId, targetType)\n\n if (targetIndex === -1) {\n return { deleted: null, alsoDeleted: [] }\n }\n\n const deletedMsg = messages[targetIndex]\n\n // Collect tool_use IDs from deleted message\n const toolUseIds: string[] = []\n if (deletedMsg.type === 'assistant') {\n const content = deletedMsg.message?.content\n if (Array.isArray(content)) {\n for (const item of content as MessageContent[]) {\n if (item.type === 'tool_use' && item.id) {\n toolUseIds.push(item.id)\n }\n }\n }\n }\n\n // Find orphan tool_result messages\n const toolResultIndices: number[] = []\n if (toolUseIds.length > 0) {\n for (let i = 0; i < messages.length; i++) {\n if (hasMatchingToolResult(messages[i], toolUseIds)) {\n toolResultIndices.push(i)\n }\n }\n }\n\n // All indices to delete (sorted descending for safe splice)\n const indicesToDelete = [targetIndex, ...toolResultIndices].sort((a, b) => b - a)\n\n // Collect messages to delete before removing\n const messagesToDelete = indicesToDelete.map((i) => messages[i])\n const alsoDeleted = toolResultIndices.map((i) => messages[i])\n\n // Repair parentUuid chain using the extracted utility\n repairParentUuidChain(messages, messagesToDelete)\n\n // Remove messages in reverse order\n for (const idx of indicesToDelete) {\n messages.splice(idx, 1)\n }\n\n return { deleted: deletedMsg, alsoDeleted }\n}\n","/**\n * Session tree data operations for VSCode extension\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir, folderNameToPath } from '../paths.js'\nimport {\n extractTextContent,\n extractTitle,\n getSessionSortTimestamp,\n isErrorSessionTitle,\n parseJsonlLines,\n tryParseJsonLine,\n} from '../utils.js'\nimport { findLinkedAgents } from '../agents.js'\nimport { findLinkedTodos } from '../todos.js'\nimport { loadTreeCache, writeTreeCache, validateCache, type TreeCache } from './cache.js'\nimport { createLogger } from '../logger.js'\nimport type {\n Message,\n SessionTreeData,\n SummaryInfo,\n SessionSortOptions,\n AgentInfo,\n ProjectTreeData,\n JsonlRecord,\n} from '../types.js'\n\nconst log = createLogger('tree')\n\n/**\n * Sort sessions based on sort options\n * Exported for testing purposes\n */\nexport const sortSessions = <T extends SessionTreeData>(\n sessions: T[],\n sort: SessionSortOptions\n): T[] => {\n return sessions.sort((a, b) => {\n let comparison = 0\n\n switch (sort.field) {\n case 'summary': {\n // By pre-calculated sort timestamp (oldest summary timestamp, matches official extension)\n comparison = a.sortTimestamp - b.sortTimestamp\n break\n }\n case 'modified': {\n // By file modification time\n comparison = (a.fileMtime ?? 0) - (b.fileMtime ?? 0)\n break\n }\n case 'created': {\n // By session creation time\n const createdA = a.createdAt ? new Date(a.createdAt).getTime() : 0\n const createdB = b.createdAt ? new Date(b.createdAt).getTime() : 0\n comparison = createdA - createdB\n break\n }\n case 'updated': {\n // By last message timestamp\n const updatedA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0\n const updatedB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0\n comparison = updatedA - updatedB\n break\n }\n case 'messageCount': {\n // By number of messages\n comparison = a.messageCount - b.messageCount\n break\n }\n case 'title': {\n // Alphabetical by display title (customTitle > currentSummary > title)\n const titleA = a.customTitle ?? a.currentSummary ?? a.title\n const titleB = b.customTitle ?? b.currentSummary ?? b.title\n comparison = titleA.localeCompare(titleB)\n break\n }\n }\n\n // Apply sort order (desc = newest/largest first)\n return sort.order === 'desc' ? -comparison : comparison\n })\n}\n\n// Internal helper for loading session tree data\nconst loadSessionTreeDataInternal = (\n projectName: string,\n sessionId: string,\n summariesByTargetSession?: Map<string, SummaryInfo[]>,\n fileMtime?: number\n) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const filePath = path.join(projectPath, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n const messages = parseJsonlLines<JsonlRecord>(lines, filePath)\n\n // Get summaries that TARGET this session (by leafUuid pointing to messages in this session)\n let summaries: SummaryInfo[]\n if (summariesByTargetSession) {\n // Project-wide loading: use pre-computed summaries targeting this session\n // Sort by timestamp ascending (oldest first), then by sourceFile descending (larger filename first)\n // Official extension shows the OLDEST summary as currentSummary, and prefers larger filenames when timestamps match\n summaries = [...(summariesByTargetSession.get(sessionId) ?? [])].sort((a, b) => {\n // 1. timestamp ascending (oldest first - official extension shows oldest as currentSummary)\n const timestampCmp = (a.timestamp ?? '').localeCompare(b.timestamp ?? '')\n if (timestampCmp !== 0) return timestampCmp\n // 2. sourceFile descending (larger filename first, e.g., b878041c > 355e3718)\n return (b.sourceFile ?? '').localeCompare(a.sourceFile ?? '')\n })\n } else {\n // Single session loading: need to search the entire project for summaries targeting this session\n summaries = []\n // Build uuid set for this session's messages\n const sessionUuids = new Set<string>()\n for (const msg of messages) {\n if (msg.uuid && typeof msg.uuid === 'string') {\n sessionUuids.add(msg.uuid)\n }\n }\n // Search all session files in the project for summaries with leafUuid pointing to this session\n const projectFiles = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const allJsonlFiles = projectFiles.filter((f) => f.endsWith('.jsonl'))\n for (const file of allJsonlFiles) {\n try {\n const otherFilePath = path.join(projectPath, file)\n const otherContent = yield* Effect.tryPromise(() => fs.readFile(otherFilePath, 'utf-8'))\n const otherLines = otherContent.trim().split('\\n').filter(Boolean)\n for (let i = 0; i < otherLines.length; i++) {\n const msg = tryParseJsonLine<JsonlRecord>(otherLines[i], i + 1, otherFilePath)\n if (!msg) continue\n if (\n msg.type === 'summary' &&\n typeof msg.summary === 'string' &&\n typeof msg.leafUuid === 'string' &&\n sessionUuids.has(msg.leafUuid)\n ) {\n // This summary's leafUuid points to a message in THIS session\n const targetMsg = messages.find((m) => m.uuid === msg.leafUuid)\n summaries.push({\n summary: msg.summary as string,\n leafUuid: msg.leafUuid,\n timestamp:\n (targetMsg?.timestamp as string) ?? (msg.timestamp as string | undefined),\n sourceFile: file,\n })\n }\n }\n } catch {\n // Skip unreadable files\n }\n }\n }\n // Sort by timestamp ascending (oldest first), then sourceFile descending\n // Official extension shows the OLDEST summary as currentSummary\n summaries.sort((a, b) => {\n const timestampCmp = (a.timestamp ?? '').localeCompare(b.timestamp ?? '')\n if (timestampCmp !== 0) return timestampCmp\n return (b.sourceFile ?? '').localeCompare(a.sourceFile ?? '')\n })\n\n // Find last compact_boundary\n let lastCompactBoundaryUuid: string | undefined\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]\n if (msg.type === 'system' && msg.subtype === 'compact_boundary') {\n lastCompactBoundaryUuid = msg.uuid as string\n break\n }\n }\n\n // Get first user message\n const firstUserMsg = messages.find((m) => m.type === 'user') as Message | undefined\n\n // customTitle is stored as separate custom-title type line\n const customTitleMsg = messages.find((m) => m.type === 'custom-title') as\n | { type: 'custom-title'; customTitle?: string }\n | undefined\n const customTitle = customTitleMsg?.customTitle\n\n // Get title from first user message\n const title = firstUserMsg\n ? extractTitle(firstUserMsg.message)\n : summaries.length > 0\n ? '[Summary Only]'\n : `Session ${sessionId.slice(0, 8)}`\n\n // Count user/assistant messages\n const userAssistantMessages = messages.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n )\n const firstMessage = userAssistantMessages[0]\n const lastMessage = userAssistantMessages[userAssistantMessages.length - 1]\n\n // Find linked agents\n const linkedAgentIds = yield* findLinkedAgents(projectName, sessionId)\n\n // Load agent info (message counts)\n const agents: AgentInfo[] = []\n for (const agentId of linkedAgentIds) {\n const agentPath = path.join(projectPath, `${agentId}.jsonl`)\n try {\n const agentContent = yield* Effect.tryPromise(() => fs.readFile(agentPath, 'utf-8'))\n const agentLines = agentContent.trim().split('\\n').filter(Boolean)\n const agentMsgs = agentLines.map((l) => JSON.parse(l) as JsonlRecord)\n const agentUserAssistant = agentMsgs.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n )\n\n // Try to extract agent name from first message\n let agentName: string | undefined\n const firstAgentMsg = agentMsgs.find((m) => m.type === 'user')\n if (firstAgentMsg) {\n const text = extractTextContent(firstAgentMsg.message as Message['message'])\n if (text) {\n agentName = extractTitle(text)\n }\n }\n\n agents.push({\n id: agentId,\n name: agentName,\n messageCount: agentUserAssistant.length,\n })\n } catch {\n // Agent file might not exist or be readable\n agents.push({\n id: agentId,\n messageCount: 0,\n })\n }\n }\n\n // Load todos\n const todos = yield* findLinkedTodos(sessionId, linkedAgentIds)\n\n // Pre-calculate sort timestamp for 'summary' field\n const createdAt = (firstMessage?.timestamp as string) ?? undefined\n const sortTimestamp = getSessionSortTimestamp({ summaries, createdAt })\n\n return {\n id: sessionId,\n projectName,\n title,\n customTitle,\n currentSummary: summaries[0]?.summary,\n messageCount:\n userAssistantMessages.length > 0\n ? userAssistantMessages.length\n : summaries.length > 0\n ? 1\n : 0,\n createdAt,\n updatedAt: (lastMessage?.timestamp as string) ?? undefined,\n fileMtime,\n sortTimestamp,\n summaries,\n agents,\n todos,\n lastCompactBoundaryUuid,\n } satisfies SessionTreeData\n })\n\n// Public wrapper for single session (without global uuid map, leafUuid lookup is limited)\nexport const loadSessionTreeData = (projectName: string, sessionId: string) =>\n loadSessionTreeDataInternal(projectName, sessionId, undefined)\n\n/** Default sort: by latest message timestamp (matches official extension) */\nconst DEFAULT_SORT: SessionSortOptions = { field: 'updated', order: 'desc' }\n\n// ============================================================================\n// Phase 1 helpers (UUID map + summaries)\n// ============================================================================\n\ninterface Phase1Result {\n globalUuidMap: Map<string, { sessionId: string; timestamp?: string }>\n allSummaries: Array<{\n summary: string\n leafUuid?: string\n timestamp?: string\n sourceFile: string\n }>\n}\n\n/** Build global UUID map and collect all summaries from all JSONL files in a project */\nconst buildPhase1 = (projectPath: string, allJsonlFiles: string[]) =>\n Effect.gen(function* () {\n const globalUuidMap = new Map<string, { sessionId: string; timestamp?: string }>()\n const allSummaries: Phase1Result['allSummaries'] = []\n\n yield* Effect.all(\n allJsonlFiles.map((file) =>\n Effect.gen(function* () {\n const filePath = path.join(projectPath, file)\n const fileSessionId = file.replace('.jsonl', '')\n try {\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n for (let i = 0; i < lines.length; i++) {\n const msg = tryParseJsonLine<JsonlRecord>(lines[i], i + 1, filePath)\n if (!msg) continue\n if (msg.uuid && typeof msg.uuid === 'string') {\n globalUuidMap.set(msg.uuid, {\n sessionId: fileSessionId,\n timestamp: msg.timestamp as string | undefined,\n })\n }\n // Also check messageId for file-history-snapshot type\n if (msg.messageId && typeof msg.messageId === 'string') {\n globalUuidMap.set(msg.messageId, {\n sessionId: fileSessionId,\n timestamp: (msg.snapshot as Record<string, unknown> | undefined)?.timestamp as\n | string\n | undefined,\n })\n }\n // Collect summaries with source file for sorting\n if (msg.type === 'summary' && typeof msg.summary === 'string') {\n allSummaries.push({\n summary: msg.summary as string,\n leafUuid: msg.leafUuid as string | undefined,\n timestamp: msg.timestamp as string | undefined,\n sourceFile: file,\n })\n }\n }\n } catch {\n // Skip unreadable files\n }\n })\n ),\n { concurrency: 20 }\n )\n\n return { globalUuidMap, allSummaries } satisfies Phase1Result\n })\n\n/** Build summariesByTargetSession map from Phase 1 output */\nconst buildSummariesByTargetSession = (\n globalUuidMap: Map<string, { sessionId: string; timestamp?: string }>,\n allSummaries: Phase1Result['allSummaries']\n): Map<string, SummaryInfo[]> => {\n const summariesByTargetSession = new Map<string, SummaryInfo[]>()\n for (const summaryData of allSummaries) {\n if (summaryData.leafUuid) {\n const targetInfo = globalUuidMap.get(summaryData.leafUuid)\n if (targetInfo) {\n const targetSessionId = targetInfo.sessionId\n if (!summariesByTargetSession.has(targetSessionId)) {\n summariesByTargetSession.set(targetSessionId, [])\n }\n summariesByTargetSession.get(targetSessionId)!.push({\n summary: summaryData.summary,\n leafUuid: summaryData.leafUuid,\n timestamp: summaryData.timestamp ?? targetInfo.timestamp,\n sourceFile: summaryData.sourceFile,\n })\n }\n }\n }\n return summariesByTargetSession\n}\n\n/** Sort summaries consistently (oldest first by timestamp, then sourceFile desc) */\nconst sortSummaries = (summaries: SummaryInfo[]): SummaryInfo[] => {\n return [...summaries].sort((a, b) => {\n const timestampCmp = (a.timestamp ?? '').localeCompare(b.timestamp ?? '')\n if (timestampCmp !== 0) return timestampCmp\n return (b.sourceFile ?? '').localeCompare(a.sourceFile ?? '')\n })\n}\n\n/** Apply sort, filter error sessions, and wrap as ProjectTreeData */\nconst buildProjectTreeResult = (\n project: { name: string; displayName: string; path: string },\n sessions: SessionTreeData[],\n sort: SessionSortOptions\n): ProjectTreeData => {\n const sortedSessions = sortSessions(sessions, sort)\n\n const filteredSessions = sortedSessions.filter((s) => {\n if (isErrorSessionTitle(s.title)) return false\n if (isErrorSessionTitle(s.customTitle)) return false\n if (isErrorSessionTitle(s.currentSummary)) return false\n return true\n })\n\n return {\n name: project.name,\n displayName: project.displayName,\n path: project.path,\n sessionCount: filteredSessions.length,\n sessions: filteredSessions,\n }\n}\n\n// ============================================================================\n// Cache helpers\n// ============================================================================\n\n/** Serialize Phase 1 + Phase 2 results into a TreeCache */\nconst buildTreeCache = (\n globalUuidMap: Map<string, { sessionId: string; timestamp?: string }>,\n allSummaries: Phase1Result['allSummaries'],\n sessions: SessionTreeData[],\n fileMtimes: Map<string, number>\n): TreeCache => {\n const uuidMapRecord: Record<string, { sessionId: string; timestamp?: string }> = {}\n for (const [key, val] of globalUuidMap) {\n uuidMapRecord[key] = val\n }\n\n const sessionsRecord: Record<string, { fileMtime: number; data: SessionTreeData }> = {}\n for (const s of sessions) {\n sessionsRecord[s.id] = {\n fileMtime: fileMtimes.get(s.id) ?? 0,\n data: s,\n }\n }\n\n return {\n version: 1,\n globalUuidMap: uuidMapRecord,\n allSummaries,\n sessions: sessionsRecord,\n }\n}\n\n/** Update an unchanged session's summaries from new Phase 1 data */\nconst updateSessionSummaries = (\n cached: SessionTreeData,\n summariesByTargetSession: Map<string, SummaryInfo[]>\n): SessionTreeData => {\n const newSummaries = sortSummaries(summariesByTargetSession.get(cached.id) ?? [])\n\n // Check if summaries actually changed\n const oldJson = JSON.stringify(cached.summaries)\n const newJson = JSON.stringify(newSummaries)\n if (oldJson === newJson) return cached\n\n const newSortTimestamp = getSessionSortTimestamp({\n summaries: newSummaries,\n createdAt: cached.createdAt,\n })\n\n return {\n ...cached,\n summaries: newSummaries,\n currentSummary: newSummaries[0]?.summary,\n sortTimestamp: newSortTimestamp,\n }\n}\n\n// ============================================================================\n// Main entry point\n// ============================================================================\n\n// Load all sessions tree data for a project (with caching)\nexport const loadProjectTreeData = (projectName: string, sortOptions?: SessionSortOptions) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n\n // Check project exists (fast: single stat instead of listing ALL projects)\n const exists = yield* Effect.tryPromise(() =>\n fs\n .access(projectPath)\n .then(() => true)\n .catch(() => false)\n )\n if (!exists) return null\n\n // Resolve display name for this single project (avoids processing all projects)\n const displayName = yield* Effect.tryPromise(() => folderNameToPath(projectName))\n const project = { name: projectName, displayName, path: projectPath }\n\n const sort = sortOptions ?? DEFAULT_SORT\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n const sessionFileIds = sessionFiles.map((f) => f.replace('.jsonl', ''))\n\n // Step 1: Collect file modification times (cheap ~50ms for 57 files)\n const fileMtimes = new Map<string, number>()\n yield* Effect.all(\n sessionFiles.map((file) =>\n Effect.gen(function* () {\n const filePath = path.join(projectPath, file)\n try {\n const stat = yield* Effect.tryPromise(() => fs.stat(filePath))\n fileMtimes.set(file.replace('.jsonl', ''), stat.mtimeMs)\n } catch {\n // Ignore stat errors\n }\n })\n ),\n { concurrency: 20 }\n )\n\n // Step 2: Try cache\n const cache = yield* Effect.tryPromise(() => loadTreeCache(projectName))\n\n if (cache) {\n const validation = validateCache(cache, sessionFileIds, fileMtimes)\n\n if (validation.isFullHit) {\n // Fast path: all files unchanged, return cached data directly\n log.debug(`cache hit for ${projectName} (${sessionFileIds.length} sessions)`)\n const sessions = sessionFileIds\n .map((id) => cache.sessions[id]?.data)\n .filter((s): s is SessionTreeData => s != null)\n return buildProjectTreeResult(project, sessions, sort)\n }\n\n const changedCount =\n validation.changedSessionIds.length +\n validation.newSessionIds.length +\n validation.deletedSessionIds.length\n log.debug(\n `cache partial miss for ${projectName}: ` +\n `${validation.changedSessionIds.length} changed, ` +\n `${validation.newSessionIds.length} new, ` +\n `${validation.deletedSessionIds.length} deleted, ` +\n `${validation.unchangedSessionIds.length} unchanged`\n )\n\n // Incremental path: rebuild Phase 1, then selectively reload Phase 2\n if (changedCount <= sessionFileIds.length / 2) {\n const result = yield* loadProjectTreeDataIncremental(\n projectName,\n projectPath,\n files,\n sessionFiles,\n fileMtimes,\n cache,\n validation,\n project,\n sort\n )\n return result\n }\n // If more than half changed, just do a full load (faster than incremental)\n }\n\n // Full load (no cache or too many changes)\n log.debug(`full load for ${projectName} (${sessionFileIds.length} sessions)`)\n return yield* loadProjectTreeDataFull(\n projectName,\n projectPath,\n files,\n sessionFiles,\n fileMtimes,\n project,\n sort\n )\n })\n\n/** Full load: Phase 1 + Phase 2 on all files, then write cache */\nconst loadProjectTreeDataFull = (\n projectName: string,\n projectPath: string,\n files: string[],\n sessionFiles: string[],\n fileMtimes: Map<string, number>,\n project: { name: string; displayName: string; path: string },\n sort: SessionSortOptions\n) =>\n Effect.gen(function* () {\n const allJsonlFiles = files.filter((f) => f.endsWith('.jsonl'))\n\n // Phase 1: Build global UUID map + collect all summaries\n const { globalUuidMap, allSummaries } = yield* buildPhase1(projectPath, allJsonlFiles)\n\n // Phase 1.5: Build summariesByTargetSession\n const summariesByTargetSession = buildSummariesByTargetSession(globalUuidMap, allSummaries)\n\n // Phase 2: Load all session tree data\n const sessions = yield* Effect.all(\n sessionFiles.map((file) => {\n const sessionId = file.replace('.jsonl', '')\n const mtime = fileMtimes.get(sessionId)\n return loadSessionTreeDataInternal(projectName, sessionId, summariesByTargetSession, mtime)\n }),\n { concurrency: 10 }\n )\n\n // Write cache in background (don't block return)\n const cacheData = buildTreeCache(globalUuidMap, allSummaries, sessions, fileMtimes)\n writeTreeCache(projectName, cacheData).catch((err) => {\n log.debug(`cache write failed for ${projectName}: ${err}`)\n })\n\n return buildProjectTreeResult(project, sessions, sort)\n })\n\n/** Incremental load: Phase 1 on all files, Phase 2 only for changed sessions */\nconst loadProjectTreeDataIncremental = (\n projectName: string,\n projectPath: string,\n files: string[],\n sessionFiles: string[],\n fileMtimes: Map<string, number>,\n cache: TreeCache,\n validation: ReturnType<typeof validateCache>,\n project: { name: string; displayName: string; path: string },\n sort: SessionSortOptions\n) =>\n Effect.gen(function* () {\n const allJsonlFiles = files.filter((f) => f.endsWith('.jsonl'))\n\n // Phase 1: Rebuild UUID map + summaries fully (fast: ~2-3s, just parsing)\n const { globalUuidMap, allSummaries } = yield* buildPhase1(projectPath, allJsonlFiles)\n const summariesByTargetSession = buildSummariesByTargetSession(globalUuidMap, allSummaries)\n\n // Phase 2: Only load changed + new sessions\n const sessionsToLoad = [...validation.changedSessionIds, ...validation.newSessionIds]\n const loadedSessions = yield* Effect.all(\n sessionsToLoad.map((sessionId) => {\n const mtime = fileMtimes.get(sessionId)\n return loadSessionTreeDataInternal(projectName, sessionId, summariesByTargetSession, mtime)\n }),\n { concurrency: 10 }\n )\n\n // Build loaded map for quick lookup\n const loadedMap = new Map<string, SessionTreeData>()\n for (const s of loadedSessions) {\n loadedMap.set(s.id, s)\n }\n\n // Merge: use loaded for changed/new, update summaries for unchanged, skip deleted\n const allSessions: SessionTreeData[] = []\n for (const file of sessionFiles) {\n const sessionId = file.replace('.jsonl', '')\n const loaded = loadedMap.get(sessionId)\n if (loaded) {\n allSessions.push(loaded)\n } else if (cache.sessions[sessionId]) {\n // Unchanged session: update summaries from new Phase 1 data\n const updated = updateSessionSummaries(\n cache.sessions[sessionId].data,\n summariesByTargetSession\n )\n // Update fileMtime from current stat\n allSessions.push({ ...updated, fileMtime: fileMtimes.get(sessionId) })\n }\n // deleted sessions are not in sessionFiles, so they're naturally excluded\n }\n\n // Write updated cache\n const cacheData = buildTreeCache(globalUuidMap, allSummaries, allSessions, fileMtimes)\n writeTreeCache(projectName, cacheData).catch((err) => {\n log.debug(`cache write failed for ${projectName}: ${err}`)\n })\n\n return buildProjectTreeResult(project, allSessions, sort)\n })\n","/**\n * Tree data cache for fast project loading\n *\n * Stores the full output of loadProjectTreeData alongside sessions at\n * ~/.claude/projects/{project}/.tree-cache.json\n * Invalidated by comparing file mtimes.\n */\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir } from '../paths.js'\nimport type { SessionTreeData } from '../types.js'\nimport { createLogger } from '../logger.js'\n\nconst log = createLogger('cache')\n\nconst CACHE_VERSION = 1\nconst CACHE_FILENAME = '.tree-cache.json'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface CachedSessionData {\n fileMtime: number\n data: SessionTreeData\n}\n\nexport interface TreeCache {\n version: number\n /** Phase 1 output: global UUID map entries */\n globalUuidMap: Record<string, { sessionId: string; timestamp?: string }>\n /** Phase 1 output: all summary records */\n allSummaries: Array<{\n summary: string\n leafUuid?: string\n timestamp?: string\n sourceFile: string\n }>\n /** Phase 2 output: per-session tree data keyed by sessionId */\n sessions: Record<string, CachedSessionData>\n}\n\nexport interface CacheValidation {\n isFullHit: boolean\n changedSessionIds: string[]\n unchangedSessionIds: string[]\n deletedSessionIds: string[]\n /** New session files not in cache */\n newSessionIds: string[]\n}\n\n// ============================================================================\n// Path helper\n// ============================================================================\n\nexport const getCachePath = (projectName: string): string =>\n path.join(getSessionsDir(), projectName, CACHE_FILENAME)\n\n// ============================================================================\n// Read / Write / Validate\n// ============================================================================\n\nexport const loadTreeCache = async (projectName: string): Promise<TreeCache | null> => {\n const cachePath = getCachePath(projectName)\n try {\n const raw = await fs.readFile(cachePath, 'utf-8')\n const parsed = JSON.parse(raw) as TreeCache\n if (parsed.version !== CACHE_VERSION) {\n log.debug(`cache version mismatch (${parsed.version} !== ${CACHE_VERSION}), ignoring`)\n return null\n }\n return parsed\n } catch {\n return null\n }\n}\n\nexport const writeTreeCache = async (projectName: string, cache: TreeCache): Promise<void> => {\n const cachePath = getCachePath(projectName)\n const tmpPath = cachePath + '.tmp'\n try {\n await fs.writeFile(tmpPath, JSON.stringify(cache), 'utf-8')\n await fs.rename(tmpPath, cachePath)\n } catch (e) {\n log.debug(`failed to write cache: ${e}`)\n // Clean up temp file on failure\n try {\n await fs.unlink(tmpPath)\n } catch {\n // ignore\n }\n }\n}\n\n/**\n * Compare current file mtimes against cache to determine what changed.\n * @param cache - Previously saved cache\n * @param sessionFileIds - Current session IDs (from file listing, without .jsonl)\n * @param currentMtimes - Map of sessionId → current mtime (ms)\n */\nexport const validateCache = (\n cache: TreeCache,\n sessionFileIds: string[],\n currentMtimes: Map<string, number>\n): CacheValidation => {\n const cachedIds = new Set(Object.keys(cache.sessions))\n const currentIds = new Set(sessionFileIds)\n\n const changedSessionIds: string[] = []\n const unchangedSessionIds: string[] = []\n const newSessionIds: string[] = []\n const deletedSessionIds: string[] = []\n\n // Check each current file against cache\n for (const id of currentIds) {\n if (!cachedIds.has(id)) {\n newSessionIds.push(id)\n } else {\n const cachedMtime = cache.sessions[id].fileMtime\n const currentMtime = currentMtimes.get(id) ?? 0\n // 1ms tolerance: filesystem mtimes have varying precision across platforms\n if (Math.abs(cachedMtime - currentMtime) <= 1) {\n unchangedSessionIds.push(id)\n } else {\n changedSessionIds.push(id)\n }\n }\n }\n\n // Files that were in cache but no longer exist\n for (const id of cachedIds) {\n if (!currentIds.has(id)) {\n deletedSessionIds.push(id)\n }\n }\n\n const isFullHit =\n changedSessionIds.length === 0 && newSessionIds.length === 0 && deletedSessionIds.length === 0\n\n return { isFullHit, changedSessionIds, unchangedSessionIds, deletedSessionIds, newSessionIds }\n}\n","/**\n * Session analysis, compression, and knowledge extraction\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir } from '../paths.js'\nimport type {\n CompressSessionOptions,\n CompressSessionResult,\n ContentItem,\n ConversationLine,\n Message,\n ProjectKnowledge,\n SessionAnalysis,\n SummarizeSessionOptions,\n SummarizeSessionResult,\n} from '../types.js'\nimport { extractTextContent, parseJsonlLines } from '../utils.js'\nimport { readSession } from './crud.js'\nimport { repairParentUuidChain } from './validation.js'\n\n// Helper: Check if item is a tool_use content item\nconst isToolUse = (\n item: unknown\n): item is { type: 'tool_use'; name?: string; id?: string; input?: { file_path?: string } } =>\n item !== null &&\n typeof item === 'object' &&\n 'type' in item &&\n (item as ContentItem).type === 'tool_use'\n\n// Helper: Check if item is a tool_result with error\nconst isToolResultError = (\n item: unknown\n): item is { type: 'tool_result'; tool_use_id?: string; is_error: true } =>\n item !== null &&\n typeof item === 'object' &&\n 'type' in item &&\n (item as ContentItem).type === 'tool_result' &&\n 'is_error' in item &&\n (item as { is_error?: boolean }).is_error === true\n\n// Helper: Find tool_use by ID across all messages\nconst findToolUseById = (messages: Message[], toolUseId: string): string | null => {\n for (const msg of messages) {\n const content = msg.message?.content\n if (!Array.isArray(content)) continue\n for (const item of content) {\n if (isToolUse(item) && item.id === toolUseId) {\n return item.name ?? 'unknown'\n }\n }\n }\n return null\n}\n\n// Analyze session for optimization insights\nexport const analyzeSession = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const messages = yield* readSession(projectName, sessionId)\n\n // Initialize counters\n let userMessages = 0\n let assistantMessages = 0\n let summaryCount = 0\n let snapshotCount = 0\n const toolUsageMap = new Map<string, { count: number; errorCount: number }>()\n const filesChanged = new Set<string>()\n const patterns: { type: string; description: string; count: number }[] = []\n const milestones: { timestamp?: string; description: string; messageUuid?: string }[] = []\n\n // Track timestamps for duration\n let firstTimestamp: string | undefined\n let lastTimestamp: string | undefined\n\n for (const msg of messages) {\n // Track timestamps\n if (msg.timestamp) {\n if (!firstTimestamp) firstTimestamp = msg.timestamp\n lastTimestamp = msg.timestamp\n }\n\n // Count message types\n if (msg.type === 'user') {\n userMessages++\n // Check for milestone indicators in user messages\n const content = typeof msg.content === 'string' ? msg.content : ''\n if (content.toLowerCase().includes('commit') || content.toLowerCase().includes('완료')) {\n milestones.push({\n timestamp: msg.timestamp,\n description: `User checkpoint: ${content.slice(0, 50)}...`,\n messageUuid: msg.uuid,\n })\n }\n } else if (msg.type === 'assistant') {\n assistantMessages++\n\n // Track tool usage\n const content = msg.message?.content\n if (!Array.isArray(content)) continue\n for (const item of content) {\n if (!isToolUse(item)) continue\n const toolName = item.name ?? 'unknown'\n const existing = toolUsageMap.get(toolName) ?? { count: 0, errorCount: 0 }\n existing.count++\n toolUsageMap.set(toolName, existing)\n\n // Track file changes\n if ((toolName === 'Write' || toolName === 'Edit') && item.input?.file_path) {\n filesChanged.add(item.input.file_path)\n }\n }\n } else if (msg.type === 'summary') {\n summaryCount++\n // Extract milestone from summary\n if (msg.summary) {\n milestones.push({\n timestamp: msg.timestamp,\n description: `Summary: ${msg.summary.slice(0, 100)}...`,\n messageUuid: msg.uuid,\n })\n }\n } else if (msg.type === 'file-history-snapshot') {\n snapshotCount++\n // Track files from snapshots\n const snapshot = msg as unknown as {\n snapshot?: { trackedFileBackups?: Record<string, unknown> }\n }\n if (snapshot.snapshot?.trackedFileBackups) {\n for (const filePath of Object.keys(snapshot.snapshot.trackedFileBackups)) {\n filesChanged.add(filePath)\n }\n }\n }\n }\n\n // Track tool errors from tool_result messages\n for (const msg of messages) {\n if (msg.type !== 'user' || !Array.isArray(msg.content)) continue\n for (const item of msg.content) {\n if (!isToolResultError(item) || !item.tool_use_id) continue\n const toolName = findToolUseById(messages, item.tool_use_id)\n if (!toolName) continue\n const existing = toolUsageMap.get(toolName)\n if (existing) existing.errorCount++\n }\n }\n\n // Calculate duration\n let durationMinutes = 0\n if (firstTimestamp && lastTimestamp) {\n const first = new Date(firstTimestamp).getTime()\n const last = new Date(lastTimestamp).getTime()\n durationMinutes = Math.round((last - first) / 1000 / 60)\n }\n\n // Detect patterns\n const toolUsageArray = Array.from(toolUsageMap.entries()).map(([name, stats]) => ({\n name,\n count: stats.count,\n errorCount: stats.errorCount,\n }))\n\n // Pattern: High error rate\n for (const tool of toolUsageArray) {\n if (tool.count >= 3 && tool.errorCount / tool.count > 0.3) {\n patterns.push({\n type: 'high_error_rate',\n description: `${tool.name} had ${tool.errorCount}/${tool.count} errors (${Math.round((tool.errorCount / tool.count) * 100)}%)`,\n count: tool.errorCount,\n })\n }\n }\n\n // Pattern: Many snapshots (potential for compression)\n if (snapshotCount > 10) {\n patterns.push({\n type: 'many_snapshots',\n description: `${snapshotCount} file-history-snapshots could be compressed`,\n count: snapshotCount,\n })\n }\n\n return {\n sessionId,\n projectName,\n durationMinutes,\n stats: {\n totalMessages: messages.length,\n userMessages,\n assistantMessages,\n summaryCount,\n snapshotCount,\n },\n toolUsage: toolUsageArray.sort((a, b) => b.count - a.count),\n filesChanged: Array.from(filesChanged),\n patterns,\n milestones,\n } satisfies SessionAnalysis\n })\n\n// Compress session by removing snapshots and truncating tool outputs\nexport const compressSession = (\n projectName: string,\n sessionId: string,\n options: CompressSessionOptions = {}\n) =>\n Effect.gen(function* () {\n const { keepSnapshots = 'first_last', maxToolOutputLength = 5000 } = options\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n\n // Read original file\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const originalSize = Buffer.byteLength(content, 'utf-8')\n const lines = content.trim().split('\\n').filter(Boolean)\n const messages = parseJsonlLines<Record<string, unknown>>(lines, filePath, { strict: true })\n\n let removedCustomTitles = 0\n let removedProgress = 0\n let removedSnapshots = 0\n let truncatedOutputs = 0\n\n // Find snapshot indices and custom-title indices\n const customTitleIndices: number[] = []\n const snapshotIndices: number[] = []\n messages.forEach((msg, idx) => {\n if (msg.type === 'custom-title') {\n customTitleIndices.push(idx)\n }\n if (msg.type === 'file-history-snapshot') {\n snapshotIndices.push(idx)\n }\n })\n\n // Collect messages to remove\n const messagesToRemove: Record<string, unknown>[] = []\n\n // Filter messages based on keepSnapshots option, remove progress and duplicate custom-titles\n const filteredMessages = messages.filter((msg, idx) => {\n // Always remove progress messages (hook progress, etc.)\n if (msg.type === 'progress') {\n removedProgress++\n messagesToRemove.push(msg)\n return false\n }\n\n // Keep only the last custom-title record\n if (msg.type === 'custom-title') {\n if (\n customTitleIndices.length > 1 &&\n idx !== customTitleIndices[customTitleIndices.length - 1]\n ) {\n removedCustomTitles++\n messagesToRemove.push(msg)\n return false\n }\n }\n\n if (msg.type === 'file-history-snapshot') {\n if (keepSnapshots === 'none') {\n removedSnapshots++\n messagesToRemove.push(msg)\n return false\n }\n if (keepSnapshots === 'first_last') {\n const isFirst = idx === snapshotIndices[0]\n const isLast = idx === snapshotIndices[snapshotIndices.length - 1]\n if (!isFirst && !isLast) {\n removedSnapshots++\n messagesToRemove.push(msg)\n return false\n }\n }\n }\n return true\n })\n\n // Repair parentUuid chain after removing messages\n repairParentUuidChain(filteredMessages, messagesToRemove)\n\n // Truncate long tool outputs\n for (const msg of filteredMessages) {\n if (msg.type === 'user' && Array.isArray(msg.content)) {\n for (const item of msg.content as Array<Record<string, unknown>>) {\n if (item.type === 'tool_result' && typeof item.content === 'string') {\n if (maxToolOutputLength > 0 && item.content.length > maxToolOutputLength) {\n item.content = item.content.slice(0, maxToolOutputLength) + '\\n... [truncated]'\n truncatedOutputs++\n }\n }\n }\n }\n }\n\n // Write compressed file\n const newContent = filteredMessages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n const compressedSize = Buffer.byteLength(newContent, 'utf-8')\n\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n return {\n success: true,\n originalSize,\n compressedSize,\n removedCustomTitles,\n removedProgress,\n removedSnapshots,\n truncatedOutputs,\n } satisfies CompressSessionResult\n })\n\n// Extract knowledge from multiple sessions in a project\nexport const extractProjectKnowledge = (projectName: string, sessionIds?: string[]) =>\n Effect.gen(function* () {\n const sessionsDir = getSessionsDir()\n const projectDir = path.join(sessionsDir, projectName)\n\n // Get all session files if not specified\n let targetSessionIds = sessionIds\n if (!targetSessionIds) {\n const files = yield* Effect.tryPromise(() => fs.readdir(projectDir))\n targetSessionIds = files\n .filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n .map((f) => f.replace('.jsonl', ''))\n }\n\n // Aggregate data across sessions\n const fileModifyCount = new Map<string, { count: number; lastModified?: string }>()\n const toolSequences: string[][] = []\n const decisions: { context: string; decision: string; sessionId: string }[] = []\n\n for (const sessionId of targetSessionIds) {\n try {\n const messages = yield* readSession(projectName, sessionId)\n\n // Track file modifications\n for (const msg of messages) {\n if (msg.type === 'file-history-snapshot') {\n const snapshot = msg as unknown as {\n snapshot?: { trackedFileBackups?: Record<string, unknown>; timestamp?: string }\n }\n if (snapshot.snapshot?.trackedFileBackups) {\n for (const filePath of Object.keys(snapshot.snapshot.trackedFileBackups)) {\n const existing = fileModifyCount.get(filePath) ?? { count: 0 }\n existing.count++\n existing.lastModified = snapshot.snapshot.timestamp\n fileModifyCount.set(filePath, existing)\n }\n }\n }\n\n // Track tool sequences\n if (\n msg.type === 'assistant' &&\n msg.message?.content &&\n Array.isArray(msg.message.content)\n ) {\n const tools: string[] = []\n for (const item of msg.message.content) {\n if (item && typeof item === 'object' && 'type' in item && item.type === 'tool_use') {\n const toolUse = item as { name?: string }\n if (toolUse.name) tools.push(toolUse.name)\n }\n }\n if (tools.length > 1) {\n toolSequences.push(tools)\n }\n }\n\n // Extract decisions from summaries\n if (msg.type === 'summary' && msg.summary) {\n decisions.push({\n context: 'Session summary',\n decision: msg.summary.slice(0, 200),\n sessionId,\n })\n }\n }\n } catch {\n // Skip sessions that fail to read\n continue\n }\n }\n\n // Build hot files list\n const hotFiles = Array.from(fileModifyCount.entries())\n .map(([filePath, data]) => ({\n path: filePath,\n modifyCount: data.count,\n lastModified: data.lastModified,\n }))\n .sort((a, b) => b.modifyCount - a.modifyCount)\n .slice(0, 20)\n\n // Build workflow patterns\n const workflowMap = new Map<string, number>()\n for (const seq of toolSequences) {\n const key = seq.join(' -> ')\n workflowMap.set(key, (workflowMap.get(key) ?? 0) + 1)\n }\n const workflows = Array.from(workflowMap.entries())\n .filter(([, count]) => count >= 2)\n .map(([sequence, count]) => ({\n sequence: sequence.split(' -> '),\n count,\n }))\n .sort((a, b) => b.count - a.count)\n .slice(0, 10)\n\n return {\n projectName,\n patterns: [],\n hotFiles,\n workflows,\n decisions: decisions.slice(0, 20),\n } satisfies ProjectKnowledge\n })\n\n// Helper to truncate text and replace newlines\nfunction truncateText(text: string, maxLen: number): string {\n const cleaned = text.replace(/\\n/g, ' ')\n if (cleaned.length > maxLen) {\n return cleaned.slice(0, maxLen) + '...'\n }\n return cleaned\n}\n\n// Summarize session into user/assistant conversation format\nexport const summarizeSession = (\n projectName: string,\n sessionId: string,\n options: SummarizeSessionOptions = {}\n) =>\n Effect.gen(function* () {\n const { limit = 50, maxLength = 100 } = options\n const messages = yield* readSession(projectName, sessionId)\n\n const lines: ConversationLine[] = []\n let count = 0\n\n for (const msg of messages) {\n if (count >= limit) break\n\n if (msg.type === 'user' || msg.type === 'human') {\n // Extract timestamp for user messages using locale-based formatting\n let timeStr: string | undefined\n if (msg.timestamp) {\n try {\n const dt = new Date(msg.timestamp)\n timeStr = dt.toLocaleString('ko-KR', {\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n })\n } catch {\n // Skip timestamp on error\n }\n }\n\n const text = extractTextContent(msg.message)\n if (text) {\n const truncated = truncateText(text, maxLength)\n lines.push({ role: 'user', content: truncated, timestamp: timeStr })\n count++\n }\n } else if (msg.type === 'assistant') {\n const text = extractTextContent(msg.message)\n if (text) {\n const truncated = truncateText(text, maxLength)\n lines.push({ role: 'assistant', content: truncated })\n count++\n }\n }\n }\n\n // Build formatted string\n const formatted = lines\n .map((line) => {\n if (line.role === 'user') {\n return line.timestamp\n ? `user [${line.timestamp}]: ${line.content}`\n : `user: ${line.content}`\n }\n return `assistant: ${line.content}`\n })\n .join('\\n')\n\n return {\n sessionId,\n projectName,\n lines,\n formatted,\n } satisfies SummarizeSessionResult\n })\n","/**\n * Session cleanup and maintenance operations\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir } from '../paths.js'\nimport { isInvalidApiKeyMessage, parseJsonlLines } from '../utils.js'\nimport { findLinkedAgents, findOrphanAgents, deleteOrphanAgents } from '../agents.js'\nimport { sessionHasTodos, findOrphanTodos, deleteOrphanTodos } from '../todos.js'\nimport { listProjects } from './projects.js'\nimport { listSessions, deleteSession } from './crud.js'\nimport type { Message, CleanupPreview, ClearSessionsResult } from '../types.js'\n\n// Remove invalid API key messages from a session, returns remaining message count\nconst cleanInvalidMessages = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n\n if (lines.length === 0) return { removedCount: 0, remainingCount: 0 }\n\n const messages = parseJsonlLines<Message>(lines, filePath)\n const invalidIndices: number[] = []\n\n // Find all invalid API key messages\n messages.forEach((msg, idx) => {\n if (isInvalidApiKeyMessage(msg)) {\n invalidIndices.push(idx)\n }\n })\n\n if (invalidIndices.length === 0) {\n const userAssistantCount = messages.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n ).length\n const hasSummary = messages.some((m) => m.type === 'summary')\n // Count summary-only sessions as having 1 message\n const remainingCount = userAssistantCount > 0 ? userAssistantCount : hasSummary ? 1 : 0\n return { removedCount: 0, remainingCount }\n }\n\n // Remove invalid messages and fix parentUuid chain\n const filtered: Message[] = []\n let lastValidUuid: string | null = null\n\n for (let i = 0; i < messages.length; i++) {\n if (invalidIndices.includes(i)) {\n continue // Skip invalid message\n }\n\n const msg = messages[i]\n // Update parentUuid to point to last valid message\n if (msg.parentUuid && invalidIndices.some((idx) => messages[idx]?.uuid === msg.parentUuid)) {\n msg.parentUuid = lastValidUuid\n }\n filtered.push(msg)\n lastValidUuid = msg.uuid\n }\n\n const newContent =\n filtered.length > 0 ? filtered.map((m) => JSON.stringify(m)).join('\\n') + '\\n' : ''\n\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n const remainingUserAssistant = filtered.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n ).length\n const hasSummary = filtered.some((m) => m.type === 'summary')\n // Count summary-only sessions as having 1 message\n const remainingCount = remainingUserAssistant > 0 ? remainingUserAssistant : hasSummary ? 1 : 0\n return { removedCount: invalidIndices.length, remainingCount }\n })\n\n// Preview cleanup - find empty and invalid sessions\nexport const previewCleanup = (projectName?: string) =>\n Effect.gen(function* () {\n const projects = yield* listProjects\n const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects\n\n // Get orphan todos count (global, not per-project)\n const orphanTodos = yield* findOrphanTodos()\n const orphanTodoCount = orphanTodos.length\n\n const results = yield* Effect.all(\n targetProjects.map((project) =>\n Effect.gen(function* () {\n const sessions = yield* listSessions(project.name)\n const emptySessions = sessions.filter((s) => s.messageCount === 0)\n const invalidSessions = sessions.filter(\n (s) => s.title?.includes('Invalid API key') || s.title?.includes('API key')\n )\n\n // Count empty sessions that have todos\n let emptyWithTodosCount = 0\n for (const session of emptySessions) {\n const linkedAgents = yield* findLinkedAgents(project.name, session.id)\n const hasTodos = yield* sessionHasTodos(session.id, linkedAgents)\n if (hasTodos) {\n emptyWithTodosCount++\n }\n }\n\n // Count orphan agents\n const orphanAgents = yield* findOrphanAgents(project.name)\n\n return {\n project: project.name,\n emptySessions,\n invalidSessions,\n emptyWithTodosCount,\n orphanAgentCount: orphanAgents.length,\n orphanTodoCount: 0, // Will set for first project only\n } satisfies CleanupPreview\n })\n ),\n { concurrency: 5 }\n )\n\n // Add orphanTodoCount only to the first result to avoid double counting\n if (results.length > 0) {\n results[0] = { ...results[0], orphanTodoCount }\n }\n\n return results\n })\n\n// Clear sessions\nexport const clearSessions = (options: {\n projectName?: string\n clearEmpty?: boolean\n clearInvalid?: boolean\n skipWithTodos?: boolean\n clearOrphanAgents?: boolean\n clearOrphanTodos?: boolean\n}) =>\n Effect.gen(function* () {\n const {\n projectName,\n clearEmpty = true,\n clearInvalid = true,\n skipWithTodos = true,\n clearOrphanAgents = true,\n clearOrphanTodos = false,\n } = options\n const projects = yield* listProjects\n const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects\n\n let deletedSessionCount = 0\n let removedMessageCount = 0\n let deletedOrphanAgentCount = 0\n let deletedOrphanTodoCount = 0\n const sessionsToDelete: { project: string; sessionId: string }[] = []\n\n // Step 1: Clean invalid API key messages from all sessions (if clearInvalid)\n if (clearInvalid) {\n for (const project of targetProjects) {\n const projectPath = path.join(getSessionsDir(), project.name)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n\n for (const file of sessionFiles) {\n const sessionId = file.replace('.jsonl', '')\n const result = yield* cleanInvalidMessages(project.name, sessionId)\n removedMessageCount += result.removedCount\n\n // Mark for deletion if now empty\n if (result.remainingCount === 0) {\n sessionsToDelete.push({ project: project.name, sessionId })\n }\n }\n }\n }\n\n // Step 2: Also find originally empty sessions (if clearEmpty is true)\n if (clearEmpty) {\n for (const project of targetProjects) {\n const sessions = yield* listSessions(project.name)\n for (const session of sessions) {\n if (session.messageCount === 0) {\n const alreadyMarked = sessionsToDelete.some(\n (s) => s.project === project.name && s.sessionId === session.id\n )\n if (!alreadyMarked) {\n // Skip sessions with todos if skipWithTodos is true\n if (skipWithTodos) {\n const linkedAgents = yield* findLinkedAgents(project.name, session.id)\n const hasTodos = yield* sessionHasTodos(session.id, linkedAgents)\n if (hasTodos) continue\n }\n sessionsToDelete.push({ project: project.name, sessionId: session.id })\n }\n }\n }\n }\n }\n\n // Step 3: Delete all empty sessions (this also deletes linked agents and todos)\n for (const { project, sessionId } of sessionsToDelete) {\n yield* deleteSession(project, sessionId)\n deletedSessionCount++\n }\n\n // Step 4: Delete orphan agents if requested\n if (clearOrphanAgents) {\n for (const project of targetProjects) {\n const result = yield* deleteOrphanAgents(project.name)\n deletedOrphanAgentCount += result.count\n }\n }\n\n // Step 5: Delete orphan todos if requested (global, not per-project)\n if (clearOrphanTodos) {\n const result = yield* deleteOrphanTodos()\n deletedOrphanTodoCount = result.deletedCount\n }\n\n return {\n success: true,\n deletedCount: deletedSessionCount,\n removedMessageCount,\n deletedOrphanAgentCount,\n deletedOrphanTodoCount,\n } satisfies ClearSessionsResult\n })\n","/**\n * Session search operations\n */\nimport { Effect, pipe } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir } from '../paths.js'\nimport { extractTextContent, extractTitle, tryParseJsonLine } from '../utils.js'\nimport { listProjects } from './projects.js'\nimport { listSessions } from './crud.js'\nimport type { Message, SearchResult, Project } from '../types.js'\n\n// Pure function: extract snippet around match\nconst extractSnippet = (text: string, matchIndex: number, queryLength: number): string => {\n const start = Math.max(0, matchIndex - 50)\n const end = Math.min(text.length, matchIndex + queryLength + 50)\n return (start > 0 ? '...' : '') + text.slice(start, end).trim() + (end < text.length ? '...' : '')\n}\n\n// Pure function: find first matching message in session content\nconst findContentMatch = (\n lines: string[],\n queryLower: string,\n filePath: string\n): { msg: Message; snippet: string } | null => {\n for (let i = 0; i < lines.length; i++) {\n const msg = tryParseJsonLine<Message>(lines[i], i + 1, filePath)\n if (!msg) continue\n if (msg.type !== 'user' && msg.type !== 'assistant') continue\n\n const text = extractTextContent(msg.message)\n const textLower = text.toLowerCase()\n const matchIndex = textLower.indexOf(queryLower)\n\n if (matchIndex !== -1) {\n return {\n msg,\n snippet: extractSnippet(text, matchIndex, queryLower.length),\n }\n }\n }\n return null\n}\n\n// Search single session for content match\nconst searchSessionContent = (\n projectName: string,\n sessionId: string,\n filePath: string,\n queryLower: string\n) =>\n pipe(\n Effect.tryPromise(() => fs.readFile(filePath, 'utf-8')),\n Effect.map((content) => {\n const lines = content.trim().split('\\n').filter(Boolean)\n const match = findContentMatch(lines, queryLower, filePath)\n\n if (!match) return null\n\n return {\n sessionId,\n projectName,\n title: extractTitle(match.msg.message) || `Session ${sessionId.slice(0, 8)}`,\n matchType: 'content' as const,\n snippet: match.snippet,\n messageUuid: match.msg.uuid,\n timestamp: match.msg.timestamp,\n } satisfies SearchResult\n }),\n Effect.catchAll(() => Effect.succeed(null))\n )\n\n// Search all sessions in a project for content matches\nconst searchProjectContent = (project: Project, queryLower: string, alreadyFoundIds: Set<string>) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), project.name)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n\n // Filter out already found sessions and create search effects\n const searchEffects = sessionFiles\n .map((file) => ({\n sessionId: file.replace('.jsonl', ''),\n filePath: path.join(projectPath, file),\n }))\n .filter(({ sessionId }) => !alreadyFoundIds.has(`${project.name}:${sessionId}`))\n .map(({ sessionId, filePath }) =>\n searchSessionContent(project.name, sessionId, filePath, queryLower)\n )\n\n const results = yield* Effect.all(searchEffects, { concurrency: 10 })\n return results.filter((r): r is NonNullable<typeof r> => r !== null)\n })\n\n// Search sessions - two-phase: title search (fast) then content search (slow)\nexport const searchSessions = (\n query: string,\n options: { projectName?: string; searchContent?: boolean } = {}\n) =>\n Effect.gen(function* () {\n const { projectName, searchContent = false } = options\n const queryLower = query.toLowerCase()\n\n const projects = yield* listProjects\n const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects\n\n // Phase 1: Title search (fast) - using Effect.all for parallel execution\n const titleSearchEffects = targetProjects.map((project) =>\n pipe(\n listSessions(project.name),\n Effect.map((sessions) =>\n sessions\n .filter((session) => (session.title ?? '').toLowerCase().includes(queryLower))\n .map(\n (session) =>\n ({\n sessionId: session.id,\n projectName: project.name,\n title: session.title ?? 'Untitled',\n matchType: 'title' as const,\n timestamp: session.updatedAt,\n }) satisfies SearchResult\n )\n )\n )\n )\n\n const titleResultsNested = yield* Effect.all(titleSearchEffects, { concurrency: 10 })\n const titleResults = titleResultsNested.flat()\n\n // Phase 2: Content search (slow, optional)\n let contentResults: SearchResult[] = []\n if (searchContent) {\n const alreadyFoundIds = new Set(titleResults.map((r) => `${r.projectName}:${r.sessionId}`))\n\n const contentSearchEffects = targetProjects.map((project) =>\n searchProjectContent(project, queryLower, alreadyFoundIds)\n )\n\n const contentResultsNested = yield* Effect.all(contentSearchEffects, { concurrency: 5 })\n contentResults = contentResultsNested.flat()\n }\n\n // Combine and sort by timestamp (newest first)\n const allResults = [...titleResults, ...contentResults]\n return allResults.sort((a, b) => {\n const dateA = a.timestamp ? new Date(a.timestamp).getTime() : 0\n const dateB = b.timestamp ? new Date(b.timestamp).getTime() : 0\n return dateB - dateA\n })\n })\n","/**\n * Session file tracking operations\n */\nimport { Effect } from 'effect'\nimport { readSession } from './crud.js'\nimport type { FileChange, SessionFilesSummary } from '../types.js'\n\n// Get files changed in a session (from file-history-snapshot and tool_use)\nexport const getSessionFiles = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const messages = yield* readSession(projectName, sessionId)\n const fileChanges: FileChange[] = []\n const seenFiles = new Set<string>()\n\n for (const msg of messages) {\n // Check file-history-snapshot type\n if (msg.type === 'file-history-snapshot') {\n const snapshot = msg as unknown as {\n type: string\n messageId?: string\n snapshot?: {\n trackedFileBackups?: Record<string, unknown>\n timestamp?: string\n }\n }\n const backups = snapshot.snapshot?.trackedFileBackups\n if (backups && typeof backups === 'object') {\n for (const filePath of Object.keys(backups)) {\n if (!seenFiles.has(filePath)) {\n seenFiles.add(filePath)\n fileChanges.push({\n path: filePath,\n action: 'modified',\n timestamp: snapshot.snapshot?.timestamp,\n messageUuid: snapshot.messageId ?? msg.uuid,\n })\n }\n }\n }\n }\n\n // Check tool_use for Write/Edit operations\n if (msg.type === 'assistant' && msg.message?.content) {\n const content = msg.message.content\n if (Array.isArray(content)) {\n for (const item of content) {\n if (item && typeof item === 'object' && 'type' in item && item.type === 'tool_use') {\n const toolUse = item as { name?: string; input?: { file_path?: string } }\n if (\n (toolUse.name === 'Write' || toolUse.name === 'Edit') &&\n toolUse.input?.file_path\n ) {\n const filePath = toolUse.input.file_path\n if (!seenFiles.has(filePath)) {\n seenFiles.add(filePath)\n fileChanges.push({\n path: filePath,\n action: toolUse.name === 'Write' ? 'created' : 'modified',\n timestamp: msg.timestamp,\n messageUuid: msg.uuid,\n })\n }\n }\n }\n }\n }\n }\n }\n\n return {\n sessionId,\n projectName,\n files: fileChanges,\n totalChanges: fileChanges.length,\n } satisfies SessionFilesSummary\n })\n","/**\n * Sessions index file operations\n *\n * The sessions-index.json file is maintained by the official Claude Code extension.\n * It provides quick access to session metadata without parsing JSONL files.\n *\n * File location: ~/.claude/projects/{project-folder}/sessions-index.json\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir } from '../paths.js'\nimport type { SessionsIndex, SessionIndexEntry } from '../types.js'\n\n/**\n * Load sessions-index.json for a project\n * Returns null if the file doesn't exist\n */\nexport const loadSessionsIndex = (projectName: string) =>\n Effect.gen(function* () {\n const indexPath = path.join(getSessionsDir(), projectName, 'sessions-index.json')\n\n try {\n const content = yield* Effect.tryPromise(() => fs.readFile(indexPath, 'utf-8'))\n const index = JSON.parse(content) as SessionsIndex\n return index\n } catch {\n return null\n }\n })\n\n/**\n * Get display title from index entry\n * Priority: customTitle > summary > firstPrompt (cleaned)\n */\nexport const getIndexEntryDisplayTitle = (entry: SessionIndexEntry): string => {\n if (entry.customTitle) return entry.customTitle\n if (entry.summary) return entry.summary\n\n // Clean up firstPrompt\n let prompt = entry.firstPrompt\n if (prompt === 'No prompt') return 'Untitled'\n if (prompt.startsWith('[Request interrupted')) return 'Untitled'\n\n // Remove IDE tags\n prompt = prompt.replace(/<ide_[^>]*>[^<]*<\\/ide_[^>]*>/g, '').trim()\n if (!prompt) return 'Untitled'\n\n // Truncate\n if (prompt.length > 60) {\n return prompt.slice(0, 57) + '...'\n }\n\n return prompt\n}\n\n/**\n * Sort index entries by modified time (newest first)\n */\nexport const sortIndexEntriesByModified = (entries: SessionIndexEntry[]): SessionIndexEntry[] => {\n return [...entries].sort((a, b) => {\n const modA = new Date(a.modified).getTime()\n const modB = new Date(b.modified).getTime()\n return modB - modA\n })\n}\n\n/**\n * Check if sessions-index.json exists for a project\n */\nexport const hasSessionsIndex = (projectName: string) =>\n Effect.gen(function* () {\n const indexPath = path.join(getSessionsDir(), projectName, 'sessions-index.json')\n try {\n yield* Effect.tryPromise(() => fs.access(indexPath))\n return true\n } catch {\n return false\n }\n })\n"],"mappings":"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","names":["fs","logger","fs","logger","basename","Effect","fs","path","Effect","stat","backupDir","Effect","fs","path","Effect","Effect","fs","path","Effect","Effect","fs","path","Effect","stat","agentBackupDir","Effect","fs","path","fs","path","log","log","Effect","stat","Effect","fs","path","Effect","Effect","fs","path","Effect","hasSummary","remainingCount","Effect","pipe","fs","path","pipe","Effect","Effect","Effect","Effect","fs","path","Effect"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-sessions/core",
|
|
3
|
-
"version": "0.4.7-beta.
|
|
3
|
+
"version": "0.4.7-beta.2",
|
|
4
4
|
"description": "Core library for Claude Code session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,13 +24,13 @@
|
|
|
24
24
|
"dist"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"effect": "^3.
|
|
27
|
+
"effect": "^3.21.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@types/node": "^25.
|
|
30
|
+
"@types/node": "^25.5.0",
|
|
31
31
|
"tsup": "^8.3.0",
|
|
32
32
|
"typescript": "^5.7.0",
|
|
33
|
-
"vitest": "^4.0
|
|
33
|
+
"vitest": "^4.1.0"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "tsup",
|