@sooink/ai-session-tidy 0.1.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts","../src/commands/scan.ts","../src/utils/logger.ts","../src/utils/size.ts","../src/scanners/claude-code.ts","../src/utils/paths.ts","../src/scanners/cursor.ts","../src/scanners/index.ts","../src/commands/clean.ts","../src/core/cleaner.ts","../src/core/trash.ts","../src/commands/watch.ts","../src/utils/config.ts","../src/core/watcher.ts","../src/core/service.ts","../src/commands/list.ts","../src/commands/config.ts","../src/index.ts"],"sourcesContent":["import { Command } from 'commander';\n\nimport { scanCommand } from './commands/scan.js';\nimport { cleanCommand } from './commands/clean.js';\nimport { watchCommand } from './commands/watch.js';\nimport { listCommand } from './commands/list.js';\nimport { configCommand } from './commands/config.js';\nimport { getAppVersion } from './utils/config.js';\n\nexport const cli = new Command()\n .name('ai-session-tidy')\n .description(\n 'CLI tool that detects and cleans orphaned session data from AI coding tools'\n )\n .version(getAppVersion());\n\ncli.addCommand(scanCommand, { isDefault: true });\ncli.addCommand(cleanCommand);\ncli.addCommand(watchCommand);\ncli.addCommand(listCommand);\ncli.addCommand(configCommand);\n","import { Command } from 'commander';\nimport Table from 'cli-table3';\nimport ora from 'ora';\nimport chalk from 'chalk';\n\nimport { logger } from '../utils/logger.js';\nimport { formatSize } from '../utils/size.js';\nimport {\n createAllScanners,\n getAvailableScanners,\n runAllScanners,\n} from '../scanners/index.js';\nimport type { ScanResult } from '../scanners/types.js';\n\ninterface ScanOptions {\n verbose?: boolean;\n json?: boolean;\n}\n\nfunction formatTokens(count?: number): string {\n if (!count) return '0';\n if (count >= 1000000) return `${(count / 1000000).toFixed(1)}M`;\n if (count >= 1000) return `${(count / 1000).toFixed(0)}K`;\n return count.toString();\n}\n\nexport const scanCommand = new Command('scan')\n .description('Scan for orphaned session data from AI coding tools')\n .option('-v, --verbose', 'Enable verbose output')\n .option('--json', 'Output results as JSON')\n .action(async (options: ScanOptions) => {\n const spinner = ora('Scanning for orphaned sessions...').start();\n\n try {\n const allScanners = createAllScanners();\n const availableScanners = await getAvailableScanners(allScanners);\n\n if (availableScanners.length === 0) {\n spinner.warn('No AI coding tools detected on this system.');\n return;\n }\n\n if (options.verbose) {\n spinner.text = `Found ${availableScanners.length} tools: ${availableScanners.map((s) => s.name).join(', ')}`;\n }\n\n const results = await runAllScanners(availableScanners);\n spinner.stop();\n\n if (options.json) {\n outputJson(results);\n } else {\n outputTable(results, options.verbose);\n }\n } catch (error) {\n spinner.fail('Scan failed');\n logger.error(\n error instanceof Error ? error.message : 'Unknown error occurred'\n );\n process.exit(1);\n }\n });\n\nfunction outputJson(results: ScanResult[]): void {\n const allSessions = results.flatMap((r) => r.sessions);\n const output = {\n totalSessions: allSessions.length,\n totalSize: results.reduce((sum, r) => sum + r.totalSize, 0),\n results: results.map((r) => ({\n tool: r.toolName,\n sessionCount: r.sessions.length,\n totalSize: r.totalSize,\n scanDuration: r.scanDuration,\n sessions: r.sessions,\n })),\n };\n console.log(JSON.stringify(output, null, 2));\n}\n\nfunction outputTable(results: ScanResult[], verbose?: boolean): void {\n const allSessions = results.flatMap((r) => r.sessions);\n const folderSessions = allSessions.filter((s) => s.type === 'session' || s.type === undefined);\n const configEntries = allSessions.filter((s) => s.type === 'config');\n const sessionEnvEntries = allSessions.filter((s) => s.type === 'session-env');\n const todosEntries = allSessions.filter((s) => s.type === 'todos');\n const fileHistoryEntries = allSessions.filter((s) => s.type === 'file-history');\n const totalSize = results.reduce((sum, r) => sum + r.totalSize, 0);\n\n if (allSessions.length === 0) {\n logger.success('No orphaned sessions found.');\n return;\n }\n\n console.log();\n\n // Summary message\n const parts: string[] = [];\n if (folderSessions.length > 0) {\n parts.push(`${folderSessions.length} session folder(s)`);\n }\n if (configEntries.length > 0) {\n parts.push(`${configEntries.length} config entry(ies)`);\n }\n if (sessionEnvEntries.length > 0) {\n parts.push(`${sessionEnvEntries.length} session-env folder(s)`);\n }\n if (todosEntries.length > 0) {\n parts.push(`${todosEntries.length} todos file(s)`);\n }\n if (fileHistoryEntries.length > 0) {\n parts.push(`${fileHistoryEntries.length} file-history folder(s)`);\n }\n logger.warn(`Found ${parts.join(' + ')} (${formatSize(totalSize)})`);\n console.log();\n\n // Summary by tool\n const summaryTable = new Table({\n head: [\n chalk.cyan('Tool'),\n chalk.cyan('Sessions'),\n chalk.cyan('Config'),\n chalk.cyan('Env'),\n chalk.cyan('Todos'),\n chalk.cyan('History'),\n chalk.cyan('Size'),\n chalk.cyan('Scan Time'),\n ],\n style: { head: [] },\n });\n\n for (const result of results) {\n if (result.sessions.length > 0) {\n const folders = result.sessions.filter((s) => s.type === 'session' || s.type === undefined).length;\n const configs = result.sessions.filter((s) => s.type === 'config').length;\n const envs = result.sessions.filter((s) => s.type === 'session-env').length;\n const todos = result.sessions.filter((s) => s.type === 'todos').length;\n const histories = result.sessions.filter((s) => s.type === 'file-history').length;\n summaryTable.push([\n result.toolName,\n folders > 0 ? String(folders) : '-',\n configs > 0 ? String(configs) : '-',\n envs > 0 ? String(envs) : '-',\n todos > 0 ? String(todos) : '-',\n histories > 0 ? String(histories) : '-',\n formatSize(result.totalSize),\n `${result.scanDuration.toFixed(0)}ms`,\n ]);\n }\n }\n\n console.log(summaryTable.toString());\n\n // Detailed info (verbose)\n if (verbose && allSessions.length > 0) {\n // Session folders\n if (folderSessions.length > 0) {\n console.log();\n console.log(chalk.bold('Session Folders:'));\n console.log();\n\n for (const session of folderSessions) {\n const projectName = session.projectPath.split('/').pop() || session.projectPath;\n console.log(\n ` ${chalk.cyan(`[${session.toolName}]`)} ${chalk.white(projectName)} ${chalk.dim(`(${formatSize(session.size)})`)}`\n );\n console.log(` ${chalk.dim('→')} ${session.projectPath}`);\n console.log(` ${chalk.dim('Modified:')} ${session.lastModified.toLocaleDateString()}`);\n console.log();\n }\n }\n\n // Config entries\n if (configEntries.length > 0) {\n console.log();\n console.log(chalk.bold('Config Entries (~/.claude.json):'));\n console.log();\n\n for (const entry of configEntries) {\n const projectName = entry.projectPath.split('/').pop() || entry.projectPath;\n console.log(\n ` ${chalk.yellow('[config]')} ${chalk.white(projectName)}`\n );\n console.log(` ${chalk.dim('→')} ${entry.projectPath}`);\n if (entry.configStats?.lastCost) {\n const cost = `$${entry.configStats.lastCost.toFixed(2)}`;\n const inTokens = formatTokens(entry.configStats.lastTotalInputTokens);\n const outTokens = formatTokens(entry.configStats.lastTotalOutputTokens);\n console.log(` ${chalk.dim(`Cost: ${cost} | Tokens: ${inTokens} in / ${outTokens} out`)}`);\n }\n console.log();\n }\n }\n\n }\n\n console.log();\n console.log(\n chalk.dim('Run \"ai-session-tidy clean\" to remove orphaned sessions.')\n );\n}\n","import chalk from 'chalk';\n\nexport interface Logger {\n info(message: string): void;\n warn(message: string): void;\n error(message: string): void;\n success(message: string): void;\n debug(message: string): void;\n}\n\nexport const logger: Logger = {\n info(message: string): void {\n console.log(chalk.blue('ℹ'), message);\n },\n warn(message: string): void {\n console.log(chalk.yellow('⚠'), message);\n },\n error(message: string): void {\n console.log(chalk.red('✖'), message);\n },\n success(message: string): void {\n console.log(chalk.green('✔'), message);\n },\n debug(message: string): void {\n if (process.env['DEBUG']) {\n console.log(chalk.gray('🐛'), message);\n }\n },\n};\n","import { readdir, stat } from 'fs/promises';\nimport { join } from 'path';\n\nconst UNITS = ['B', 'KB', 'MB', 'GB', 'TB'] as const;\n\n/**\n * Format byte size to human-readable format\n */\nexport function formatSize(bytes: number): string {\n if (bytes <= 0) return '0 B';\n\n let size = bytes;\n let unitIndex = 0;\n\n while (size >= 1024 && unitIndex < UNITS.length - 1) {\n size /= 1024;\n unitIndex++;\n }\n\n if (unitIndex === 0) {\n return `${Math.floor(size)} ${UNITS[unitIndex]}`;\n }\n\n return `${size.toFixed(1)} ${UNITS[unitIndex]}`;\n}\n\n/**\n * Recursively calculate total size of a directory\n */\nexport async function getDirectorySize(dirPath: string): Promise<number> {\n try {\n const entries = await readdir(dirPath, { withFileTypes: true });\n let totalSize = 0;\n\n for (const entry of entries) {\n const fullPath = join(dirPath, entry.name);\n\n if (entry.isDirectory()) {\n totalSize += await getDirectorySize(fullPath);\n } else if (entry.isFile()) {\n const fileStat = await stat(fullPath);\n totalSize += fileStat.size;\n }\n }\n\n return totalSize;\n } catch {\n return 0;\n }\n}\n","import { readdir, readFile, stat, access } from 'fs/promises';\nimport { join } from 'path';\nimport { createReadStream } from 'fs';\nimport { createInterface } from 'readline';\n\nimport type { Scanner, ScanResult, OrphanedSession } from './types.js';\nimport {\n decodePath,\n getClaudeProjectsDir,\n getClaudeConfigPath,\n getClaudeSessionEnvDir,\n getClaudeTodosDir,\n getClaudeFileHistoryDir,\n} from '../utils/paths.js';\nimport { getDirectorySize } from '../utils/size.js';\n\ninterface ClaudeProjectData {\n lastCost?: number;\n lastTotalInputTokens?: number;\n lastTotalOutputTokens?: number;\n [key: string]: unknown;\n}\n\ninterface ClaudeConfig {\n projects?: Record<string, ClaudeProjectData>;\n [key: string]: unknown;\n}\n\ninterface SessionEntry {\n cwd?: string;\n}\n\nexport interface ClaudeCodeScannerOptions {\n projectsDir?: string;\n configPath?: string | null; // null to disable config scanning\n sessionEnvDir?: string | null; // null to disable session-env scanning\n todosDir?: string | null; // null to disable todos scanning\n fileHistoryDir?: string | null; // null to disable file-history scanning\n}\n\nexport class ClaudeCodeScanner implements Scanner {\n readonly name = 'claude-code' as const;\n private readonly projectsDir: string;\n private readonly configPath: string | null;\n private readonly sessionEnvDir: string | null;\n private readonly todosDir: string | null;\n private readonly fileHistoryDir: string | null;\n\n constructor(projectsDirOrOptions?: string | ClaudeCodeScannerOptions) {\n if (typeof projectsDirOrOptions === 'string') {\n // Backward compatibility: treat string as projectsDir, disable other scans\n this.projectsDir = projectsDirOrOptions;\n this.configPath = null;\n this.sessionEnvDir = null;\n this.todosDir = null;\n this.fileHistoryDir = null;\n } else if (projectsDirOrOptions) {\n this.projectsDir = projectsDirOrOptions.projectsDir ?? getClaudeProjectsDir();\n this.configPath = projectsDirOrOptions.configPath === undefined\n ? getClaudeConfigPath()\n : projectsDirOrOptions.configPath;\n this.sessionEnvDir = projectsDirOrOptions.sessionEnvDir === undefined\n ? getClaudeSessionEnvDir()\n : projectsDirOrOptions.sessionEnvDir;\n this.todosDir = projectsDirOrOptions.todosDir === undefined\n ? getClaudeTodosDir()\n : projectsDirOrOptions.todosDir;\n this.fileHistoryDir = projectsDirOrOptions.fileHistoryDir === undefined\n ? getClaudeFileHistoryDir()\n : projectsDirOrOptions.fileHistoryDir;\n } else {\n this.projectsDir = getClaudeProjectsDir();\n this.configPath = getClaudeConfigPath();\n this.sessionEnvDir = getClaudeSessionEnvDir();\n this.todosDir = getClaudeTodosDir();\n this.fileHistoryDir = getClaudeFileHistoryDir();\n }\n }\n\n async isAvailable(): Promise<boolean> {\n try {\n await access(this.projectsDir);\n return true;\n } catch {\n return false;\n }\n }\n\n async scan(): Promise<ScanResult> {\n const startTime = performance.now();\n const sessions: OrphanedSession[] = [];\n\n // 1. Scan session folders\n if (await this.isAvailable()) {\n const entries = await readdir(this.projectsDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n const sessionPath = join(this.projectsDir, entry.name);\n\n // Extract actual project path from JSONL file\n let projectPath = await this.extractProjectPath(sessionPath);\n\n // Fallback to decoding if path not found in JSONL\n if (!projectPath) {\n projectPath = decodePath(entry.name);\n }\n\n // Check if original project exists\n const projectExists = await this.pathExists(projectPath);\n if (projectExists) continue;\n\n // Exclude empty directories\n const size = await getDirectorySize(sessionPath);\n if (size === 0) continue;\n\n // Get last modified time\n const lastModified = await this.getLastModified(sessionPath);\n\n sessions.push({\n toolName: this.name,\n sessionPath,\n projectPath,\n size,\n lastModified,\n type: 'session',\n });\n }\n }\n\n // 2. Scan ~/.claude.json config file\n const configSessions = await this.scanConfigFile();\n sessions.push(...configSessions);\n\n // 3. Scan ~/.claude/session-env empty folders\n const sessionEnvSessions = await this.scanSessionEnvDir();\n sessions.push(...sessionEnvSessions);\n\n // 4. Collect valid session UUIDs then scan todos/file-history\n const validSessionIds = await this.collectValidSessionIds();\n\n // 5. Scan ~/.claude/todos for orphaned files\n const todosSessions = await this.scanTodosDir(validSessionIds);\n sessions.push(...todosSessions);\n\n // 6. Scan ~/.claude/file-history for orphaned folders\n const fileHistorySessions = await this.scanFileHistoryDir(validSessionIds);\n sessions.push(...fileHistorySessions);\n\n const totalSize = sessions.reduce((sum, s) => sum + s.size, 0);\n\n return {\n toolName: this.name,\n sessions,\n totalSize,\n scanDuration: performance.now() - startTime,\n };\n }\n\n /**\n * Detect orphaned projects from ~/.claude.json projects entries\n */\n private async scanConfigFile(): Promise<OrphanedSession[]> {\n if (!this.configPath) {\n return [];\n }\n\n const configPath = this.configPath;\n const orphanedConfigs: OrphanedSession[] = [];\n\n try {\n const content = await readFile(configPath, 'utf-8');\n const config: ClaudeConfig = JSON.parse(content);\n const configStat = await stat(configPath);\n\n if (!config.projects || typeof config.projects !== 'object') {\n return [];\n }\n\n for (const [projectPath, projectData] of Object.entries(config.projects)) {\n const projectExists = await this.pathExists(projectPath);\n if (!projectExists) {\n orphanedConfigs.push({\n toolName: this.name,\n sessionPath: configPath,\n projectPath,\n size: 0, // config entries have negligible size\n lastModified: configStat.mtime,\n type: 'config',\n configStats: {\n lastCost: projectData.lastCost,\n lastTotalInputTokens: projectData.lastTotalInputTokens,\n lastTotalOutputTokens: projectData.lastTotalOutputTokens,\n },\n });\n }\n }\n } catch {\n // Ignore if config file doesn't exist or read fails\n }\n\n return orphanedConfigs;\n }\n\n /**\n * Detect empty session environment folders from ~/.claude/session-env\n */\n private async scanSessionEnvDir(): Promise<OrphanedSession[]> {\n if (!this.sessionEnvDir) {\n return [];\n }\n\n const orphanedEnvs: OrphanedSession[] = [];\n\n try {\n await access(this.sessionEnvDir);\n const entries = await readdir(this.sessionEnvDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n const envPath = join(this.sessionEnvDir, entry.name);\n const files = await readdir(envPath);\n\n // Only treat empty folders as orphaned\n if (files.length === 0) {\n const envStat = await stat(envPath);\n orphanedEnvs.push({\n toolName: this.name,\n sessionPath: envPath,\n projectPath: entry.name, // UUID\n size: 0,\n lastModified: envStat.mtime,\n type: 'session-env',\n });\n }\n }\n } catch {\n // Ignore if directory doesn't exist or access fails\n }\n\n return orphanedEnvs;\n }\n\n /**\n * Collect valid session UUIDs from all projects\n */\n private async collectValidSessionIds(): Promise<Set<string>> {\n const sessionIds = new Set<string>();\n\n try {\n const projectDirs = await readdir(this.projectsDir, { withFileTypes: true });\n\n for (const projectDir of projectDirs) {\n if (!projectDir.isDirectory()) continue;\n\n const projectPath = join(this.projectsDir, projectDir.name);\n const files = await readdir(projectPath);\n\n for (const file of files) {\n if (file.endsWith('.jsonl')) {\n // Extract UUID from UUID.jsonl\n const sessionId = file.replace('.jsonl', '');\n sessionIds.add(sessionId);\n }\n }\n }\n } catch {\n // Return empty Set if directory access fails\n }\n\n return sessionIds;\n }\n\n /**\n * Detect orphaned todo files from ~/.claude/todos\n * Filename pattern: {session-uuid}-agent-{agent-uuid}.json\n */\n private async scanTodosDir(validSessionIds: Set<string>): Promise<OrphanedSession[]> {\n if (!this.todosDir) {\n return [];\n }\n\n const orphanedTodos: OrphanedSession[] = [];\n\n try {\n await access(this.todosDir);\n const entries = await readdir(this.todosDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.json')) continue;\n\n // Extract session UUID from filename (first UUID)\n const sessionId = entry.name.split('-agent-')[0];\n if (!sessionId) continue;\n\n // Orphan if not in valid session IDs\n if (!validSessionIds.has(sessionId)) {\n const todoPath = join(this.todosDir, entry.name);\n const todoStat = await stat(todoPath);\n\n orphanedTodos.push({\n toolName: this.name,\n sessionPath: todoPath,\n projectPath: sessionId, // Session UUID\n size: todoStat.size,\n lastModified: todoStat.mtime,\n type: 'todos',\n });\n }\n }\n } catch {\n // Ignore if directory doesn't exist or access fails\n }\n\n return orphanedTodos;\n }\n\n /**\n * Detect orphaned folders from ~/.claude/file-history\n * Folder name is the session UUID\n */\n private async scanFileHistoryDir(validSessionIds: Set<string>): Promise<OrphanedSession[]> {\n if (!this.fileHistoryDir) {\n return [];\n }\n\n const orphanedHistories: OrphanedSession[] = [];\n\n try {\n await access(this.fileHistoryDir);\n const entries = await readdir(this.fileHistoryDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n const sessionId = entry.name;\n\n // Orphan if not in valid session IDs\n if (!validSessionIds.has(sessionId)) {\n const historyPath = join(this.fileHistoryDir, entry.name);\n const size = await getDirectorySize(historyPath);\n const historyStat = await stat(historyPath);\n\n orphanedHistories.push({\n toolName: this.name,\n sessionPath: historyPath,\n projectPath: sessionId, // Session UUID\n size,\n lastModified: historyStat.mtime,\n type: 'file-history',\n });\n }\n }\n } catch {\n // Ignore if directory doesn't exist or access fails\n }\n\n return orphanedHistories;\n }\n\n /**\n * Extract project path (cwd) from JSONL file\n */\n private async extractProjectPath(sessionDir: string): Promise<string | null> {\n try {\n const files = await readdir(sessionDir);\n const jsonlFile = files.find((f) => f.endsWith('.jsonl'));\n\n if (!jsonlFile) return null;\n\n const jsonlPath = join(sessionDir, jsonlFile);\n\n // Read only first few lines to find cwd\n const cwd = await this.findCwdInJsonl(jsonlPath);\n return cwd;\n } catch {\n return null;\n }\n }\n\n private async findCwdInJsonl(jsonlPath: string): Promise<string | null> {\n return new Promise((resolve) => {\n const stream = createReadStream(jsonlPath, { encoding: 'utf-8' });\n const rl = createInterface({ input: stream, crlfDelay: Infinity });\n\n let found = false;\n let lineCount = 0;\n const maxLines = 10; // Check only first 10 lines\n\n rl.on('line', (line) => {\n if (found || lineCount >= maxLines) {\n rl.close();\n return;\n }\n\n lineCount++;\n\n try {\n const entry: SessionEntry = JSON.parse(line);\n if (entry.cwd) {\n found = true;\n rl.close();\n stream.destroy();\n resolve(entry.cwd);\n }\n } catch {\n // Ignore JSON parse failures\n }\n });\n\n rl.on('close', () => {\n if (!found) resolve(null);\n });\n\n rl.on('error', () => {\n resolve(null);\n });\n });\n }\n\n private async pathExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n }\n\n private async getLastModified(dirPath: string): Promise<Date> {\n try {\n const entries = await readdir(dirPath, { withFileTypes: true });\n let latestTime = 0;\n\n for (const entry of entries) {\n const fullPath = join(dirPath, entry.name);\n const fileStat = await stat(fullPath);\n const mtime = fileStat.mtimeMs;\n\n if (mtime > latestTime) {\n latestTime = mtime;\n }\n }\n\n return latestTime > 0 ? new Date(latestTime) : new Date();\n } catch {\n return new Date();\n }\n }\n}\n","import { homedir } from 'os';\nimport { join } from 'path';\n\n/**\n * Encode Unix path to Claude Code style\n * /home/user/project → -home-user-project\n */\nexport function encodePath(path: string): string {\n if (path === '') return '';\n // Only convert Unix slashes, leave Windows paths as-is\n if (!path.includes('/')) return path;\n return path.replace(/\\//g, '-');\n}\n\n/**\n * Decode Claude Code encoded path to Unix path\n * -home-user-project → /home/user/project\n */\nexport function decodePath(encoded: string): string {\n if (encoded === '') return '';\n // Treat as Unix encoding if it starts with a dash\n if (!encoded.startsWith('-')) return encoded;\n return encoded.replace(/-/g, '/');\n}\n\n/**\n * Return platform-specific application config directory\n */\nexport function getConfigDir(appName: string): string {\n switch (process.platform) {\n case 'darwin':\n return join(homedir(), 'Library/Application Support', appName);\n case 'win32':\n return join(process.env['APPDATA'] || '', appName);\n default:\n return join(homedir(), '.config', appName);\n }\n}\n\n/**\n * Claude Code projects directory path\n */\nexport function getClaudeProjectsDir(): string {\n return join(homedir(), '.claude', 'projects');\n}\n\n/**\n * Claude Code global config file path (~/.claude.json)\n */\nexport function getClaudeConfigPath(): string {\n return join(homedir(), '.claude.json');\n}\n\n/**\n * Cursor workspaceStorage directory path\n */\nexport function getCursorWorkspaceDir(): string {\n return join(getConfigDir('Cursor'), 'User', 'workspaceStorage');\n}\n\n/**\n * Claude Code session environment directory path (~/.claude/session-env)\n */\nexport function getClaudeSessionEnvDir(): string {\n return join(homedir(), '.claude', 'session-env');\n}\n\n/**\n * Claude Code todos directory path (~/.claude/todos)\n */\nexport function getClaudeTodosDir(): string {\n return join(homedir(), '.claude', 'todos');\n}\n\n/**\n * Claude Code file-history directory path (~/.claude/file-history)\n */\nexport function getClaudeFileHistoryDir(): string {\n return join(homedir(), '.claude', 'file-history');\n}\n\n/**\n * Replace home directory with ~ for display\n * /Users/user/.ai-session-tidy → ~/.ai-session-tidy\n */\nexport function tildify(path: string): string {\n const home = homedir();\n if (path.startsWith(home)) {\n return path.replace(home, '~');\n }\n return path;\n}\n","import { readdir, readFile, stat, access } from 'fs/promises';\nimport { join } from 'path';\nimport { fileURLToPath } from 'url';\n\nimport type { Scanner, ScanResult, OrphanedSession } from './types.js';\nimport { getCursorWorkspaceDir } from '../utils/paths.js';\nimport { getDirectorySize } from '../utils/size.js';\n\ninterface WorkspaceJson {\n folder?: string;\n}\n\nexport class CursorScanner implements Scanner {\n readonly name = 'cursor' as const;\n private readonly workspaceDir: string;\n\n constructor(workspaceDir?: string) {\n this.workspaceDir = workspaceDir ?? getCursorWorkspaceDir();\n }\n\n async isAvailable(): Promise<boolean> {\n try {\n await access(this.workspaceDir);\n return true;\n } catch {\n return false;\n }\n }\n\n async scan(): Promise<ScanResult> {\n const startTime = performance.now();\n const sessions: OrphanedSession[] = [];\n\n if (!(await this.isAvailable())) {\n return {\n toolName: this.name,\n sessions: [],\n totalSize: 0,\n scanDuration: performance.now() - startTime,\n };\n }\n\n const entries = await readdir(this.workspaceDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n const sessionPath = join(this.workspaceDir, entry.name);\n const workspaceJsonPath = join(sessionPath, 'workspace.json');\n\n // Read workspace.json\n const projectPath = await this.parseWorkspaceJson(workspaceJsonPath);\n if (!projectPath) continue;\n\n // Check if original project exists\n const projectExists = await this.pathExists(projectPath);\n if (projectExists) continue;\n\n // Calculate session size\n const size = await getDirectorySize(sessionPath);\n\n // Get last modified time\n const lastModified = await this.getLastModified(sessionPath);\n\n sessions.push({\n toolName: this.name,\n sessionPath,\n projectPath,\n size,\n lastModified,\n });\n }\n\n const totalSize = sessions.reduce((sum, s) => sum + s.size, 0);\n\n return {\n toolName: this.name,\n sessions,\n totalSize,\n scanDuration: performance.now() - startTime,\n };\n }\n\n private async parseWorkspaceJson(\n workspaceJsonPath: string\n ): Promise<string | null> {\n try {\n const content = await readFile(workspaceJsonPath, 'utf-8');\n const data: WorkspaceJson = JSON.parse(content);\n\n if (!data.folder) return null;\n\n // Convert file:// URL to regular path\n if (data.folder.startsWith('file://')) {\n return fileURLToPath(data.folder);\n }\n\n return data.folder;\n } catch {\n return null;\n }\n }\n\n private async pathExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n }\n\n private async getLastModified(dirPath: string): Promise<Date> {\n try {\n const entries = await readdir(dirPath, { withFileTypes: true });\n let latestTime = 0;\n\n for (const entry of entries) {\n const fullPath = join(dirPath, entry.name);\n const fileStat = await stat(fullPath);\n const mtime = fileStat.mtimeMs;\n\n if (mtime > latestTime) {\n latestTime = mtime;\n }\n }\n\n return latestTime > 0 ? new Date(latestTime) : new Date();\n } catch {\n return new Date();\n }\n }\n}\n","import type { Scanner, ScanResult } from './types.js';\nimport { ClaudeCodeScanner } from './claude-code.js';\nimport { CursorScanner } from './cursor.js';\n\nexport { ClaudeCodeScanner } from './claude-code.js';\nexport { CursorScanner } from './cursor.js';\nexport * from './types.js';\n\n/**\n * Create all scanner instances\n */\nexport function createAllScanners(): Scanner[] {\n return [new ClaudeCodeScanner(), new CursorScanner()];\n}\n\n/**\n * Filter to available scanners only\n */\nexport async function getAvailableScanners(\n scanners: Scanner[]\n): Promise<Scanner[]> {\n const results = await Promise.all(\n scanners.map(async (scanner) => ({\n scanner,\n available: await scanner.isAvailable(),\n }))\n );\n\n return results.filter((r) => r.available).map((r) => r.scanner);\n}\n\n/**\n * Run all scanners and merge results\n */\nexport async function runAllScanners(\n scanners: Scanner[]\n): Promise<ScanResult[]> {\n return Promise.all(scanners.map((scanner) => scanner.scan()));\n}\n","import { basename } from 'path';\n\nimport { Command } from 'commander';\nimport ora from 'ora';\nimport chalk from 'chalk';\nimport inquirer from 'inquirer';\n\nimport { logger } from '../utils/logger.js';\nimport { formatSize } from '../utils/size.js';\nimport { tildify } from '../utils/paths.js';\nimport {\n createAllScanners,\n getAvailableScanners,\n runAllScanners,\n} from '../scanners/index.js';\nimport { Cleaner } from '../core/cleaner.js';\nimport type { OrphanedSession } from '../scanners/types.js';\n\ninterface CleanOptions {\n force?: boolean;\n dryRun?: boolean;\n noTrash?: boolean;\n verbose?: boolean;\n interactive?: boolean;\n}\n\nfunction formatSessionChoice(session: OrphanedSession): string {\n const projectName = basename(session.projectPath);\n const isConfig = session.type === 'config';\n const toolTag = isConfig\n ? chalk.yellow('[config]')\n : chalk.cyan(`[${session.toolName}]`);\n const name = chalk.white(projectName);\n const size = isConfig ? '' : chalk.dim(`(${formatSize(session.size)})`);\n const path = chalk.dim(`→ ${session.projectPath}`);\n return `${toolTag} ${name} ${size}\\n ${path}`;\n}\n\nexport const cleanCommand = new Command('clean')\n .description('Remove orphaned session data')\n .option('-f, --force', 'Skip confirmation prompt')\n .option('-n, --dry-run', 'Show what would be deleted without deleting')\n .option('-i, --interactive', 'Select sessions to delete interactively')\n .option('--no-trash', 'Permanently delete instead of moving to trash')\n .option('-v, --verbose', 'Enable verbose output')\n .action(async (options: CleanOptions) => {\n const spinner = ora('Scanning for orphaned sessions...').start();\n\n try {\n // Scan\n const allScanners = createAllScanners();\n const availableScanners = await getAvailableScanners(allScanners);\n\n if (availableScanners.length === 0) {\n spinner.warn('No AI coding tools detected on this system.');\n return;\n }\n\n const results = await runAllScanners(availableScanners);\n const allSessions = results.flatMap((r) => r.sessions);\n const totalSize = results.reduce((sum, r) => sum + r.totalSize, 0);\n\n spinner.stop();\n\n if (allSessions.length === 0) {\n logger.success('No orphaned sessions found.');\n return;\n }\n\n // Separate session folders, config entries, and auto-cleanup targets\n const folderSessions = allSessions.filter(\n (s) => s.type === 'session' || s.type === undefined\n );\n const configEntries = allSessions.filter((s) => s.type === 'config');\n const sessionEnvEntries = allSessions.filter(\n (s) => s.type === 'session-env'\n );\n const todosEntries = allSessions.filter((s) => s.type === 'todos');\n const fileHistoryEntries = allSessions.filter(\n (s) => s.type === 'file-history'\n );\n\n // Auto-cleanup targets (session-env, todos, file-history)\n const autoCleanEntries = [\n ...sessionEnvEntries,\n ...todosEntries,\n ...fileHistoryEntries,\n ];\n\n // Interactive selection targets (excluding auto-cleanup targets)\n const selectableSessions = allSessions.filter(\n (s) =>\n s.type !== 'session-env' &&\n s.type !== 'todos' &&\n s.type !== 'file-history'\n );\n\n // Output summary\n console.log();\n const parts: string[] = [];\n if (folderSessions.length > 0) {\n parts.push(`${folderSessions.length} session folder(s)`);\n }\n if (configEntries.length > 0) {\n parts.push(`${configEntries.length} config entry(ies)`);\n }\n if (sessionEnvEntries.length > 0) {\n parts.push(`${sessionEnvEntries.length} session-env folder(s)`);\n }\n if (todosEntries.length > 0) {\n parts.push(`${todosEntries.length} todos file(s)`);\n }\n if (fileHistoryEntries.length > 0) {\n parts.push(`${fileHistoryEntries.length} file-history folder(s)`);\n }\n logger.warn(`Found ${parts.join(' + ')} (${formatSize(totalSize)})`);\n\n if (options.verbose && !options.interactive) {\n console.log();\n // Exclude auto-cleanup targets from detailed list\n for (const session of selectableSessions) {\n console.log(\n chalk.dim(` ${session.toolName}: ${session.projectPath}`)\n );\n }\n }\n\n // Interactive mode: session selection (excluding auto-cleanup targets)\n let sessionsToClean = allSessions;\n if (options.interactive) {\n if (selectableSessions.length === 0) {\n // Only auto-cleanup targets exist\n if (autoCleanEntries.length > 0) {\n sessionsToClean = autoCleanEntries;\n logger.info(\n `Only ${autoCleanEntries.length} auto-cleanup target(s) found`\n );\n } else {\n logger.info('No selectable sessions found.');\n return;\n }\n } else {\n console.log();\n const { selected } = await inquirer.prompt<{\n selected: OrphanedSession[];\n }>([\n {\n type: 'checkbox',\n name: 'selected',\n message: 'Select sessions to delete:',\n choices: selectableSessions.map((session) => ({\n name: formatSessionChoice(session),\n value: session,\n checked: false,\n })),\n pageSize: 15,\n loop: false,\n },\n ]);\n\n if (selected.length === 0) {\n logger.info('No sessions selected. Cancelled.');\n return;\n }\n\n // Include selected sessions + auto-cleanup targets\n sessionsToClean = [...selected, ...autoCleanEntries];\n const selectedSize = selected.reduce((sum, s) => sum + s.size, 0);\n console.log();\n if (selected.length > 0) {\n logger.info(\n `Selected: ${selected.length} session(s) (${formatSize(selectedSize)})`\n );\n }\n if (autoCleanEntries.length > 0) {\n const autoSize = autoCleanEntries.reduce((sum, s) => sum + s.size, 0);\n logger.info(\n `+ ${autoCleanEntries.length} auto-cleanup target(s) (${formatSize(autoSize)})`\n );\n }\n }\n }\n\n const cleanSize = sessionsToClean.reduce((sum, s) => sum + s.size, 0);\n\n // Dry-run mode\n if (options.dryRun) {\n console.log();\n logger.info(\n chalk.yellow('Dry-run mode: No files will be deleted.')\n );\n console.log();\n\n const dryRunFolders = sessionsToClean.filter(\n (s) => s.type === 'session' || s.type === undefined\n );\n const dryRunConfigs = sessionsToClean.filter((s) => s.type === 'config');\n const dryRunEnvs = sessionsToClean.filter(\n (s) => s.type === 'session-env'\n );\n const dryRunTodos = sessionsToClean.filter((s) => s.type === 'todos');\n const dryRunHistories = sessionsToClean.filter(\n (s) => s.type === 'file-history'\n );\n\n for (const session of dryRunFolders) {\n console.log(\n ` ${chalk.red('Would delete:')} ${session.sessionPath} (${formatSize(session.size)})`\n );\n }\n\n if (dryRunConfigs.length > 0) {\n console.log();\n console.log(\n ` ${chalk.yellow('Would remove from ~/.claude.json:')}`\n );\n for (const config of dryRunConfigs) {\n console.log(` - ${config.projectPath}`);\n }\n }\n\n // Auto-cleanup targets summary\n const autoCleanParts: string[] = [];\n if (dryRunEnvs.length > 0) {\n autoCleanParts.push(`${dryRunEnvs.length} session-env`);\n }\n if (dryRunTodos.length > 0) {\n autoCleanParts.push(`${dryRunTodos.length} todos`);\n }\n if (dryRunHistories.length > 0) {\n autoCleanParts.push(`${dryRunHistories.length} file-history`);\n }\n if (autoCleanParts.length > 0) {\n const autoSize =\n dryRunEnvs.reduce((sum, s) => sum + s.size, 0) +\n dryRunTodos.reduce((sum, s) => sum + s.size, 0) +\n dryRunHistories.reduce((sum, s) => sum + s.size, 0);\n console.log();\n console.log(\n ` ${chalk.dim(`Would auto-delete: ${autoCleanParts.join(' + ')} (${formatSize(autoSize)})`)}`\n );\n }\n return;\n }\n\n // Confirmation prompt (also in interactive mode)\n if (!options.force) {\n console.log();\n const action = options.noTrash ? 'permanently delete' : 'move to trash';\n const { confirmed } = await inquirer.prompt<{ confirmed: boolean }>([\n {\n type: 'confirm',\n name: 'confirmed',\n message: `${action} ${sessionsToClean.length} session(s) (${formatSize(cleanSize)})?`,\n default: false,\n },\n ]);\n\n if (!confirmed) {\n logger.info('Cancelled.');\n return;\n }\n }\n\n // Execute cleanup\n const cleanSpinner = ora('Cleaning orphaned sessions...').start();\n\n const cleaner = new Cleaner();\n const cleanResult = await cleaner.clean(sessionsToClean, {\n dryRun: false,\n useTrash: !options.noTrash,\n });\n\n cleanSpinner.stop();\n\n // Output results\n console.log();\n if (cleanResult.deletedCount > 0) {\n const action = options.noTrash ? 'Deleted' : 'Moved to trash';\n const parts: string[] = [];\n const { deletedByType } = cleanResult;\n\n if (deletedByType.session > 0) {\n parts.push(`${deletedByType.session} session`);\n }\n if (deletedByType.sessionEnv > 0) {\n parts.push(`${deletedByType.sessionEnv} session-env`);\n }\n if (deletedByType.todos > 0) {\n parts.push(`${deletedByType.todos} todos`);\n }\n if (deletedByType.fileHistory > 0) {\n parts.push(`${deletedByType.fileHistory} file-history`);\n }\n\n const summary =\n parts.length > 0\n ? parts.join(' + ')\n : `${cleanResult.deletedCount} item(s)`;\n logger.success(\n `${action}: ${summary} (${formatSize(cleanResult.totalSizeDeleted)})`\n );\n }\n\n if (cleanResult.alreadyGoneCount > 0 && options.verbose) {\n logger.info(\n `Skipped ${cleanResult.alreadyGoneCount} already-deleted item(s)`\n );\n }\n\n if (cleanResult.configEntriesRemoved > 0) {\n logger.success(\n `Removed ${cleanResult.configEntriesRemoved} config entry(ies) from ~/.claude.json`\n );\n if (cleanResult.backupPath) {\n logger.info(`Backup saved to: ${tildify(cleanResult.backupPath)}`);\n }\n }\n\n if (cleanResult.errors.length > 0) {\n logger.error(`Failed to delete ${cleanResult.errors.length} item(s)`);\n if (options.verbose) {\n for (const err of cleanResult.errors) {\n console.log(chalk.red(` ${err.sessionPath}: ${err.error.message}`));\n }\n }\n }\n } catch (error) {\n spinner.fail('Clean failed');\n logger.error(\n error instanceof Error ? error.message : 'Unknown error occurred'\n );\n process.exit(1);\n }\n });\n","import { readFile, writeFile, mkdir, copyFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { homedir } from 'os';\n\nimport type { OrphanedSession } from '../scanners/types.js';\nimport { moveToTrash, permanentDelete } from './trash.js';\n\nexport interface CleanOptions {\n dryRun: boolean;\n useTrash?: boolean;\n}\n\nexport interface CleanError {\n sessionPath: string;\n error: Error;\n}\n\nexport interface CleanCountByType {\n session: number;\n sessionEnv: number;\n todos: number;\n fileHistory: number;\n}\n\nexport interface CleanResult {\n deletedCount: number;\n deletedByType: CleanCountByType;\n skippedCount: number;\n alreadyGoneCount: number;\n configEntriesRemoved: number;\n totalSizeDeleted: number;\n errors: CleanError[];\n backupPath?: string;\n}\n\ninterface ClaudeConfig {\n projects?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\nfunction getBackupDir(): string {\n return join(homedir(), '.ai-session-tidy', 'backups');\n}\n\nexport class Cleaner {\n async clean(\n sessions: OrphanedSession[],\n options: CleanOptions\n ): Promise<CleanResult> {\n const result: CleanResult = {\n deletedCount: 0,\n deletedByType: {\n session: 0,\n sessionEnv: 0,\n todos: 0,\n fileHistory: 0,\n },\n skippedCount: 0,\n alreadyGoneCount: 0,\n configEntriesRemoved: 0,\n totalSizeDeleted: 0,\n errors: [],\n };\n\n const useTrash = options.useTrash ?? true;\n\n // Separate session folders and config entries\n const folderSessions = sessions.filter((s) => s.type !== 'config');\n const configEntries = sessions.filter((s) => s.type === 'config');\n\n // 1. Clean session folders/files\n for (const session of folderSessions) {\n if (options.dryRun) {\n result.skippedCount++;\n continue;\n }\n\n try {\n const deleted = useTrash\n ? await moveToTrash(session.sessionPath)\n : await permanentDelete(session.sessionPath);\n\n if (deleted) {\n result.deletedCount++;\n result.totalSizeDeleted += session.size;\n\n // Count by type\n switch (session.type) {\n case 'session-env':\n result.deletedByType.sessionEnv++;\n break;\n case 'todos':\n result.deletedByType.todos++;\n break;\n case 'file-history':\n result.deletedByType.fileHistory++;\n break;\n default:\n result.deletedByType.session++;\n }\n } else {\n // Already deleted path - not an error\n result.alreadyGoneCount++;\n }\n } catch (error) {\n result.errors.push({\n sessionPath: session.sessionPath,\n error: error instanceof Error ? error : new Error(String(error)),\n });\n }\n }\n\n // 2. Clean config entries\n if (configEntries.length > 0 && !options.dryRun) {\n try {\n const configResult = await this.cleanConfigEntries(configEntries);\n result.configEntriesRemoved = configResult.removed;\n result.backupPath = configResult.backupPath;\n } catch (error) {\n result.errors.push({\n sessionPath: configEntries[0].sessionPath,\n error: error instanceof Error ? error : new Error(String(error)),\n });\n }\n } else if (options.dryRun) {\n result.skippedCount += configEntries.length;\n }\n\n return result;\n }\n\n /**\n * Remove orphaned project entries from ~/.claude.json\n */\n private async cleanConfigEntries(\n entries: OrphanedSession[]\n ): Promise<{ removed: number; backupPath: string }> {\n if (entries.length === 0) {\n return { removed: 0, backupPath: '' };\n }\n\n const configPath = entries[0].sessionPath;\n const projectPathsToRemove = new Set(entries.map((e) => e.projectPath));\n\n // Create backup directory\n const backupDir = getBackupDir();\n if (!existsSync(backupDir)) {\n await mkdir(backupDir, { recursive: true });\n }\n\n // Create backup file (with timestamp)\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const backupPath = join(backupDir, `claude.json.${timestamp}.bak`);\n await copyFile(configPath, backupPath);\n\n // Read config file\n const content = await readFile(configPath, 'utf-8');\n const config: ClaudeConfig = JSON.parse(content);\n\n if (!config.projects) {\n return { removed: 0, backupPath };\n }\n\n // Remove orphaned project entries\n let removedCount = 0;\n for (const projectPath of projectPathsToRemove) {\n if (projectPath in config.projects) {\n delete config.projects[projectPath];\n removedCount++;\n }\n }\n\n // Save modified config\n if (removedCount > 0) {\n await writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');\n }\n\n return { removed: removedCount, backupPath };\n }\n}\n","import { rm, access } from 'fs/promises';\nimport trash from 'trash';\n\n/**\n * Check if path exists\n */\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Move file or directory to trash\n * @returns true if deleted, false if already gone\n */\nexport async function moveToTrash(path: string): Promise<boolean> {\n if (!(await pathExists(path))) {\n return false; // Already deleted - silent skip\n }\n\n await trash(path);\n return true;\n}\n\n/**\n * Permanently delete file or directory\n * @returns true if deleted, false if already gone\n */\nexport async function permanentDelete(path: string): Promise<boolean> {\n if (!(await pathExists(path))) {\n return false; // Already deleted - silent skip\n }\n\n await rm(path, { recursive: true, force: true });\n return true;\n}\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { existsSync } from 'fs';\nimport { homedir } from 'os';\nimport { join, resolve } from 'path';\n\nimport { logger } from '../utils/logger.js';\nimport { formatSize } from '../utils/size.js';\nimport { getWatchPaths as getConfigWatchPaths, setWatchPaths, getWatchDelay, getWatchDepth } from '../utils/config.js';\nimport {\n createAllScanners,\n getAvailableScanners,\n runAllScanners,\n} from '../scanners/index.js';\nimport { Watcher } from '../core/watcher.js';\nimport { Cleaner } from '../core/cleaner.js';\nimport { serviceManager } from '../core/service.js';\n\nconst DEFAULT_DELAY_MINUTES = 5;\nconst MAX_DELAY_MINUTES = 10;\n\ninterface RunOptions {\n delay?: string;\n path?: string[];\n noSave?: boolean;\n noTrash?: boolean;\n verbose?: boolean;\n}\n\nexport const watchCommand = new Command('watch')\n .description('Watch for project deletions and auto-clean orphaned sessions');\n\n// Run subcommand (default) - foreground execution\nconst runCommand = new Command('run')\n .description('Run watcher in foreground (default)')\n .option(\n '-p, --path <path>',\n 'Path to watch (can be used multiple times, saves to config)',\n (value: string, previous: string[]) => previous.concat([value]),\n [] as string[]\n )\n .option('--no-save', 'Do not save paths to config')\n .option(\n '-d, --delay <minutes>',\n `Delay before cleanup (default: ${DEFAULT_DELAY_MINUTES}, max: ${MAX_DELAY_MINUTES} minutes)`\n )\n .option('--no-trash', 'Permanently delete instead of moving to trash')\n .option('-v, --verbose', 'Enable verbose output')\n .action(runWatcher);\n\n// Start subcommand - install and start as OS service\nconst startCommand = new Command('start')\n .description('Start watcher as OS service (background + auto-start at login)')\n .action(async () => {\n const supported = serviceManager.isSupported();\n if (!supported) {\n logger.error('Service management is only supported on macOS.');\n logger.info('Use \"watch run\" to run the watcher in foreground.');\n return;\n }\n\n try {\n // Check current status\n const currentStatus = await serviceManager.status();\n if (currentStatus.status === 'running') {\n logger.info('Watcher service is already running.');\n return;\n }\n\n // Install and start\n logger.info('Installing watcher service...');\n await serviceManager.install();\n\n logger.info('Starting watcher service...');\n await serviceManager.start();\n\n // Verify\n const status = await serviceManager.status();\n if (status.status === 'running') {\n logger.success(`Watcher service started (PID: ${status.pid})`);\n logger.info('The watcher will automatically start at login.');\n logger.info(`Logs: ~/.ai-session-tidy/watcher.log`);\n } else {\n logger.warn('Service installed but may not be running yet.');\n logger.info('Check status with: ai-session-tidy watch status');\n }\n } catch (error) {\n logger.error(`Failed to start service: ${error instanceof Error ? error.message : String(error)}`);\n }\n });\n\n// Stop subcommand - stop and uninstall OS service\nconst stopCommand = new Command('stop')\n .description('Stop watcher service and disable auto-start')\n .action(async () => {\n const supported = serviceManager.isSupported();\n if (!supported) {\n logger.error('Service management is only supported on macOS.');\n return;\n }\n\n try {\n const currentStatus = await serviceManager.status();\n if (currentStatus.status === 'not_installed') {\n logger.info('Watcher service is not installed.');\n return;\n }\n\n logger.info('Stopping watcher service...');\n await serviceManager.stop();\n\n logger.info('Removing service configuration...');\n await serviceManager.uninstall();\n\n logger.success('Watcher service stopped and removed.');\n } catch (error) {\n logger.error(`Failed to stop service: ${error instanceof Error ? error.message : String(error)}`);\n }\n });\n\n// Status subcommand - show service status\nconst statusCommand = new Command('status')\n .description('Show watcher service status')\n .option('-l, --logs [lines]', 'Show recent logs', '20')\n .action(async (options: { logs?: string }) => {\n const supported = serviceManager.isSupported();\n if (!supported) {\n logger.error('Service management is only supported on macOS.');\n return;\n }\n\n try {\n const status = await serviceManager.status();\n\n console.log();\n console.log(chalk.bold('Watcher Service Status'));\n console.log('─'.repeat(40));\n console.log(`Label: ${status.label}`);\n console.log(`Status: ${formatStatus(status.status)}`);\n if (status.pid) {\n console.log(`PID: ${status.pid}`);\n }\n console.log(`Plist: ${status.plistPath}`);\n console.log();\n\n if (options.logs) {\n const lines = parseInt(options.logs, 10) || 20;\n const logs = await serviceManager.getLogs(lines);\n\n if (logs.stdout) {\n console.log(chalk.bold('Recent Logs:'));\n console.log('─'.repeat(40));\n console.log(logs.stdout);\n console.log();\n }\n\n if (logs.stderr) {\n console.log(chalk.bold.red('Error Logs:'));\n console.log('─'.repeat(40));\n console.log(logs.stderr);\n console.log();\n }\n }\n } catch (error) {\n logger.error(`Failed to get status: ${error instanceof Error ? error.message : String(error)}`);\n }\n });\n\nfunction formatStatus(status: string): string {\n switch (status) {\n case 'running':\n return chalk.green('running');\n case 'stopped':\n return chalk.yellow('stopped');\n case 'not_installed':\n return chalk.dim('not installed');\n default:\n return status;\n }\n}\n\n// Add subcommands\nwatchCommand.addCommand(runCommand, { isDefault: true });\nwatchCommand.addCommand(startCommand);\nwatchCommand.addCommand(stopCommand);\nwatchCommand.addCommand(statusCommand);\n\n// The main watcher logic (foreground mode)\nasync function runWatcher(options: RunOptions): Promise<void> {\n // Priority: CLI option > config > default\n const configDelay = getWatchDelay();\n let delayMinutes = options.delay\n ? parseInt(options.delay, 10)\n : (configDelay ?? DEFAULT_DELAY_MINUTES);\n\n // Enforce max delay\n if (delayMinutes > MAX_DELAY_MINUTES) {\n logger.warn(`Maximum delay is ${MAX_DELAY_MINUTES} minutes. Using ${MAX_DELAY_MINUTES}.`);\n delayMinutes = MAX_DELAY_MINUTES;\n }\n\n const delayMs = delayMinutes * 60 * 1000;\n\n // Determine watch paths\n let watchPaths: string[];\n if (options.path && options.path.length > 0) {\n // Use paths from -p option\n watchPaths = options.path.map((p) => resolve(p.replace(/^~/, homedir())));\n\n // Save to config (unless --no-save)\n if (!options.noSave) {\n setWatchPaths(watchPaths);\n logger.info(`Saved watch paths to config.`);\n }\n } else {\n // Read from config or use defaults\n const configPaths = getConfigWatchPaths();\n if (configPaths && configPaths.length > 0) {\n watchPaths = configPaths;\n logger.info(`Using saved watch paths from config.`);\n } else {\n watchPaths = getDefaultWatchPaths();\n logger.info(`Using default watch paths.`);\n }\n }\n\n // Filter to existing paths\n const validPaths = watchPaths.filter((p) => existsSync(p));\n if (validPaths.length === 0) {\n logger.error('No valid watch paths found. Use -p to specify paths.');\n return;\n }\n\n if (validPaths.length < watchPaths.length) {\n const invalidPaths = watchPaths.filter((p) => !existsSync(p));\n logger.warn(`Skipping non-existent paths: ${invalidPaths.join(', ')}`);\n }\n\n // Check scanners\n const allScanners = createAllScanners();\n const availableScanners = await getAvailableScanners(allScanners);\n\n if (availableScanners.length === 0) {\n logger.warn('No AI coding tools detected on this system.');\n return;\n }\n\n const depth = getWatchDepth() ?? 3;\n\n logger.info(\n `Watching for project deletions (${availableScanners.map((s) => s.name).join(', ')})`\n );\n logger.info(`Watch paths: ${validPaths.join(', ')}`);\n logger.info(`Cleanup delay: ${String(delayMinutes)} minute(s)`);\n logger.info(`Watch depth: ${String(depth)}`);\n if (process.stdout.isTTY) {\n logger.info(chalk.dim('Press Ctrl+C to stop watching.'));\n }\n console.log();\n\n const cleaner = new Cleaner();\n\n const watcher = new Watcher({\n watchPaths: validPaths,\n delayMs,\n depth,\n onDelete: async (events) => {\n // Log batch events\n if (events.length === 1) {\n logger.info(`Detected deletion: ${events[0].path}`);\n } else {\n logger.info(`Detected ${events.length} deletions (debounced)`);\n if (options.verbose) {\n for (const event of events) {\n logger.debug(` - ${event.path}`);\n }\n }\n }\n\n // Scan for orphaned sessions (executed only once)\n const results = await runAllScanners(availableScanners);\n const allSessions = results.flatMap((r) => r.sessions);\n\n if (allSessions.length === 0) {\n if (options.verbose) {\n logger.debug('No orphaned sessions found after deletion.');\n }\n return;\n }\n\n // Clean up\n const cleanResult = await cleaner.clean(allSessions, {\n dryRun: false,\n useTrash: !options.noTrash,\n });\n\n if (cleanResult.deletedCount > 0) {\n const action = options.noTrash ? 'Deleted' : 'Moved to trash';\n const parts: string[] = [];\n const { deletedByType } = cleanResult;\n\n if (deletedByType.session > 0) {\n parts.push(`${deletedByType.session} session`);\n }\n if (deletedByType.sessionEnv > 0) {\n parts.push(`${deletedByType.sessionEnv} session-env`);\n }\n if (deletedByType.todos > 0) {\n parts.push(`${deletedByType.todos} todos`);\n }\n if (deletedByType.fileHistory > 0) {\n parts.push(`${deletedByType.fileHistory} file-history`);\n }\n\n const summary = parts.length > 0 ? parts.join(' + ') : `${cleanResult.deletedCount} item(s)`;\n logger.success(\n `${action}: ${summary} (${formatSize(cleanResult.totalSizeDeleted)})`\n );\n }\n\n if (cleanResult.configEntriesRemoved > 0) {\n logger.success(\n `Removed ${cleanResult.configEntriesRemoved} config entry(ies) from ~/.claude.json`\n );\n }\n\n if (cleanResult.errors.length > 0) {\n logger.error(\n `Failed to clean ${cleanResult.errors.length} item(s)`\n );\n }\n },\n });\n\n watcher.start();\n\n // Handle Ctrl+C\n process.on('SIGINT', () => {\n console.log();\n logger.info('Stopping watcher...');\n watcher.stop();\n process.exit(0);\n });\n\n // Keep process alive\n await new Promise(() => {});\n}\n\nfunction getDefaultWatchPaths(): string[] {\n const home = homedir();\n return [\n join(home, 'dev'),\n join(home, 'code'),\n join(home, 'projects'),\n join(home, 'Development'),\n join(home, 'Developer'), // macOS Xcode\n join(home, 'Documents'),\n ];\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\nimport { homedir } from 'os';\nimport { dirname, join, resolve } from 'path';\nimport { fileURLToPath } from 'url';\n\nconst CONFIG_VERSION = '0.1';\n\nexport function getAppVersion(): string {\n const __dirname = dirname(fileURLToPath(import.meta.url));\n // Try multiple paths (source vs bundled)\n const paths = [\n join(__dirname, '..', '..', 'package.json'), // from src/utils/\n join(__dirname, '..', 'package.json'), // from dist/\n ];\n\n for (const packagePath of paths) {\n try {\n if (existsSync(packagePath)) {\n const content = readFileSync(packagePath, 'utf-8');\n const pkg = JSON.parse(content) as { version: string };\n return pkg.version;\n }\n } catch {\n continue;\n }\n }\n return 'unknown';\n}\n\nexport interface Config {\n version?: string; // app version that last saved this config\n configVersion?: string; // config schema version\n watchPaths?: string[];\n watchDelay?: number; // minutes\n watchDepth?: number; // folder depth (default: 3, max: 5)\n}\n\nfunction getConfigPath(): string {\n return join(homedir(), '.config', 'ai-session-tidy', 'config.json');\n}\n\nexport function loadConfig(): Config {\n const configPath = getConfigPath();\n\n if (!existsSync(configPath)) {\n return {};\n }\n\n try {\n const content = readFileSync(configPath, 'utf-8');\n return JSON.parse(content) as Config;\n } catch {\n return {};\n }\n}\n\nexport function saveConfig(config: Config): void {\n const configPath = getConfigPath();\n const configDir = dirname(configPath);\n\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true });\n }\n\n const configWithVersion: Config = {\n version: getAppVersion(),\n configVersion: CONFIG_VERSION,\n ...config,\n };\n\n writeFileSync(configPath, JSON.stringify(configWithVersion, null, 2), 'utf-8');\n}\n\nexport function getWatchPaths(): string[] | undefined {\n const config = loadConfig();\n return config.watchPaths;\n}\n\nexport function setWatchPaths(paths: string[]): void {\n const config = loadConfig();\n config.watchPaths = paths;\n saveConfig(config);\n}\n\nexport function addWatchPath(path: string): void {\n const config = loadConfig();\n const resolved = resolve(path.replace(/^~/, homedir()));\n const paths = config.watchPaths ?? [];\n if (!paths.includes(resolved)) {\n paths.push(resolved);\n config.watchPaths = paths;\n saveConfig(config);\n }\n}\n\nexport function removeWatchPath(path: string): boolean {\n const config = loadConfig();\n const resolved = resolve(path.replace(/^~/, homedir()));\n const paths = config.watchPaths ?? [];\n const index = paths.indexOf(resolved);\n if (index === -1) return false;\n paths.splice(index, 1);\n config.watchPaths = paths;\n saveConfig(config);\n return true;\n}\n\nexport function getWatchDelay(): number | undefined {\n const config = loadConfig();\n return config.watchDelay;\n}\n\nexport function setWatchDelay(minutes: number): void {\n const config = loadConfig();\n config.watchDelay = minutes;\n saveConfig(config);\n}\n\nexport function getWatchDepth(): number | undefined {\n const config = loadConfig();\n return config.watchDepth;\n}\n\nexport function setWatchDepth(depth: number): void {\n const config = loadConfig();\n config.watchDepth = Math.min(depth, 5); // max 5\n saveConfig(config);\n}\n\nexport function resetConfig(): void {\n saveConfig({});\n}\n","import { watch, FSWatcher } from 'chokidar';\nimport { access } from 'fs/promises';\n\nexport interface WatchEvent {\n path: string;\n timestamp: Date;\n}\n\nexport interface WatcherOptions {\n watchPaths: string[];\n /** Delay before cleanup after deletion detected (allows recovery) */\n delayMs: number;\n /** Debounce time to batch multiple delete events (default: 10 seconds) */\n debounceMs?: number;\n depth?: number;\n /** Callback to handle batched delete events */\n onDelete: (events: WatchEvent[]) => void;\n}\n\n/**\n * Watcher that monitors project folder deletions and invokes cleanup callback\n *\n * ## Event Processing Flow\n *\n * When a folder is deleted, the OS generates individual events for each subfolder:\n * ```\n * rm -rf /project\n * → unlinkDir: /project/frontend\n * → unlinkDir: /project/backend\n * → unlinkDir: /project\n * ```\n *\n * Running scan/cleanup for each event would be inefficient,\n * so we use a two-stage delay mechanism:\n *\n * 1. **Per-path delay (delayMs)**: Provides recovery opportunity (default 5 min)\n * - If folder is restored during delay, cleanup is cancelled\n *\n * 2. **Debounce (debounceMs)**: Batches multiple events together (default 10 sec)\n * - After 10 seconds with no new events, batch is executed\n * - Scan/cleanup runs only once\n *\n * ## Timeline Example\n *\n * ```\n * T+0s: /project/frontend deletion detected → 5min timer starts\n * T+0.1s: /project/backend deletion detected → 5min timer starts\n * T+0.2s: /project deletion detected → 5min timer starts\n * T+5m: /project/frontend timer complete → add to batch, 10sec debounce starts\n * T+5m0.1s: /project/backend timer complete → add to batch, debounce reset\n * T+5m0.2s: /project timer complete → add to batch, debounce reset\n * T+5m10.2s: debounce complete → onDelete([3 events]) → single scan execution\n * ```\n */\nexport class Watcher {\n private readonly options: WatcherOptions;\n private readonly debounceMs: number;\n private watcher: FSWatcher | null = null;\n /** Per-path delay timers */\n private pendingDeletes: Map<string, NodeJS.Timeout> = new Map();\n /** Events waiting for debounce */\n private batchedEvents: WatchEvent[] = [];\n /** Debounce timer */\n private debounceTimer: NodeJS.Timeout | null = null;\n\n constructor(options: WatcherOptions) {\n this.options = options;\n this.debounceMs = options.debounceMs ?? 10000; // default 10 seconds\n }\n\n start(): void {\n if (this.watcher) return;\n\n this.watcher = watch(this.options.watchPaths, {\n ignoreInitial: true,\n persistent: true,\n depth: this.options.depth ?? 3,\n });\n\n this.watcher.on('unlinkDir', (path) => {\n this.handleDelete(path);\n });\n\n this.watcher.on('addDir', (path) => {\n this.handleRecovery(path);\n });\n }\n\n stop(): void {\n if (!this.watcher) return;\n\n this.watcher.close();\n this.watcher = null;\n\n // Cancel all pending timers\n for (const timeout of this.pendingDeletes.values()) {\n clearTimeout(timeout);\n }\n this.pendingDeletes.clear();\n\n // Cancel debounce timer\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n this.batchedEvents = [];\n }\n\n isWatching(): boolean {\n return this.watcher !== null;\n }\n\n /**\n * Handle folder deletion event\n *\n * Stage 1: Set per-path delay timer\n * - Don't process immediately to allow recovery\n * - Add to batch if still deleted after delay\n */\n private handleDelete(path: string): void {\n // Ignore if already pending (prevent duplicate events)\n if (this.pendingDeletes.has(path)) return;\n\n const timeout = setTimeout(async () => {\n // Verify path is still deleted after delay\n const stillDeleted = !(await this.pathExists(path));\n\n if (stillDeleted) {\n // Move to stage 2: add to batch\n this.addToBatch({\n path,\n timestamp: new Date(),\n });\n }\n\n this.pendingDeletes.delete(path);\n }, this.options.delayMs);\n\n this.pendingDeletes.set(path, timeout);\n }\n\n /**\n * Add event to batch and reset debounce timer\n *\n * Stage 2: Debounce\n * - Reset timer on each new event\n * - Execute batch when no new events for debounce period\n * - This combines multiple subfolder deletions into single cleanup\n */\n private addToBatch(event: WatchEvent): void {\n this.batchedEvents.push(event);\n\n // Reset debounce timer (extend wait time on new event)\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n\n this.debounceTimer = setTimeout(() => {\n this.flushBatch();\n }, this.debounceMs);\n }\n\n /**\n * Deliver batched events to callback\n * - Scan/cleanup runs only once\n */\n private flushBatch(): void {\n if (this.batchedEvents.length === 0) return;\n\n const events = [...this.batchedEvents];\n this.batchedEvents = [];\n this.debounceTimer = null;\n\n this.options.onDelete(events);\n }\n\n private handleRecovery(path: string): void {\n const timeout = this.pendingDeletes.get(path);\n\n if (timeout) {\n clearTimeout(timeout);\n this.pendingDeletes.delete(path);\n }\n }\n\n private async pathExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n }\n}\n","import { homedir } from 'os';\nimport { join, dirname } from 'path';\nimport { readFile, writeFile, unlink, mkdir } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { execSync } from 'child_process';\n\nconst SERVICE_LABEL = 'sooink.ai-session-tidy.watcher';\nconst PLIST_FILENAME = `${SERVICE_LABEL}.plist`;\n\nexport type ServiceStatus = 'running' | 'stopped' | 'not_installed';\n\nexport interface ServiceInfo {\n status: ServiceStatus;\n pid?: number;\n label: string;\n plistPath: string;\n}\n\nfunction getPlistPath(): string {\n return join(homedir(), 'Library', 'LaunchAgents', PLIST_FILENAME);\n}\n\nfunction getNodePath(): string {\n // Get absolute path to node executable\n return process.execPath;\n}\n\nfunction getScriptPath(): string {\n // Get absolute path to the CLI script\n const scriptPath = process.argv[1];\n if (scriptPath && existsSync(scriptPath)) {\n return scriptPath;\n }\n throw new Error('Could not determine script path');\n}\n\nfunction generatePlist(options: {\n label: string;\n nodePath: string;\n scriptPath: string;\n args: string[];\n}): string {\n const allArgs = [options.nodePath, options.scriptPath, ...options.args];\n const argsXml = allArgs.map((arg) => ` <string>${arg}</string>`).join('\\n');\n\n const home = homedir();\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${options.label}</string>\n <key>EnvironmentVariables</key>\n <dict>\n <key>HOME</key>\n <string>${home}</string>\n </dict>\n <key>ProgramArguments</key>\n <array>\n${argsXml}\n </array>\n <key>RunAtLoad</key>\n <true/>\n <key>KeepAlive</key>\n <true/>\n <key>StandardOutPath</key>\n <string>${join(home, '.ai-session-tidy', 'watcher.log')}</string>\n <key>StandardErrorPath</key>\n <string>${join(home, '.ai-session-tidy', 'watcher.error.log')}</string>\n</dict>\n</plist>`;\n}\n\nexport class ServiceManager {\n private readonly plistPath: string;\n\n constructor() {\n this.plistPath = getPlistPath();\n }\n\n isSupported(): boolean {\n return process.platform === 'darwin';\n }\n\n async install(): Promise<void> {\n if (!this.isSupported()) {\n throw new Error('Service management is only supported on macOS');\n }\n\n // Ensure LaunchAgents directory exists\n const launchAgentsDir = dirname(this.plistPath);\n if (!existsSync(launchAgentsDir)) {\n await mkdir(launchAgentsDir, { recursive: true });\n }\n\n // Ensure log directory exists and clear old logs\n const logDir = join(homedir(), '.ai-session-tidy');\n if (!existsSync(logDir)) {\n await mkdir(logDir, { recursive: true });\n }\n\n // Clear old log files\n const stdoutPath = join(logDir, 'watcher.log');\n const stderrPath = join(logDir, 'watcher.error.log');\n await writeFile(stdoutPath, '', 'utf-8');\n await writeFile(stderrPath, '', 'utf-8');\n\n const plistContent = generatePlist({\n label: SERVICE_LABEL,\n nodePath: getNodePath(),\n scriptPath: getScriptPath(),\n args: ['watch', 'run'],\n });\n\n await writeFile(this.plistPath, plistContent, 'utf-8');\n }\n\n async uninstall(): Promise<void> {\n if (existsSync(this.plistPath)) {\n await unlink(this.plistPath);\n }\n }\n\n async start(): Promise<void> {\n if (!this.isSupported()) {\n throw new Error('Service management is only supported on macOS');\n }\n\n if (!existsSync(this.plistPath)) {\n throw new Error('Service not installed. Run \"watch start\" to install and start.');\n }\n\n try {\n execSync(`launchctl load \"${this.plistPath}\"`, { stdio: 'pipe' });\n } catch (error) {\n // Already loaded is not an error\n const message = error instanceof Error ? error.message : String(error);\n if (!message.includes('already loaded')) {\n throw error;\n }\n }\n }\n\n async stop(): Promise<void> {\n if (!this.isSupported()) {\n throw new Error('Service management is only supported on macOS');\n }\n\n if (!existsSync(this.plistPath)) {\n return; // Nothing to stop\n }\n\n try {\n execSync(`launchctl unload \"${this.plistPath}\"`, { stdio: 'pipe' });\n } catch {\n // Ignore errors when stopping (might not be running)\n }\n }\n\n async status(): Promise<ServiceInfo> {\n const info: ServiceInfo = {\n status: 'not_installed',\n label: SERVICE_LABEL,\n plistPath: this.plistPath,\n };\n\n if (!this.isSupported()) {\n return info;\n }\n\n if (!existsSync(this.plistPath)) {\n return info;\n }\n\n try {\n const output = execSync('launchctl list', { encoding: 'utf-8' });\n const lines = output.split('\\n');\n\n for (const line of lines) {\n if (line.includes(SERVICE_LABEL)) {\n const parts = line.split(/\\s+/);\n const pid = parseInt(parts[0] ?? '', 10);\n\n if (!isNaN(pid) && pid > 0) {\n info.status = 'running';\n info.pid = pid;\n } else {\n info.status = 'stopped';\n }\n return info;\n }\n }\n\n // plist exists but not loaded\n info.status = 'stopped';\n return info;\n } catch {\n info.status = 'stopped';\n return info;\n }\n }\n\n async getLogs(lines: number = 50): Promise<{ stdout: string; stderr: string }> {\n const logDir = join(homedir(), '.ai-session-tidy');\n const stdoutPath = join(logDir, 'watcher.log');\n const stderrPath = join(logDir, 'watcher.error.log');\n\n let stdout = '';\n let stderr = '';\n\n try {\n if (existsSync(stdoutPath)) {\n const content = await readFile(stdoutPath, 'utf-8');\n const logLines = content.split('\\n');\n stdout = logLines.slice(-lines).join('\\n');\n }\n } catch {\n // Ignore read errors\n }\n\n try {\n if (existsSync(stderrPath)) {\n const content = await readFile(stderrPath, 'utf-8');\n const logLines = content.split('\\n');\n stderr = logLines.slice(-lines).join('\\n');\n }\n } catch {\n // Ignore read errors\n }\n\n return { stdout, stderr };\n }\n}\n\nexport const serviceManager = new ServiceManager();\n","import { Command } from 'commander';\nimport Table from 'cli-table3';\nimport chalk from 'chalk';\n\nimport { logger } from '../utils/logger.js';\nimport { formatSize } from '../utils/size.js';\nimport {\n createAllScanners,\n getAvailableScanners,\n} from '../scanners/index.js';\nimport {\n getClaudeProjectsDir,\n getCursorWorkspaceDir,\n} from '../utils/paths.js';\n\ninterface ListOptions {\n verbose?: boolean;\n}\n\nexport const listCommand = new Command('list')\n .description('List detected AI coding tools and their data locations')\n .option('-v, --verbose', 'Show detailed information')\n .action(async (options: ListOptions) => {\n const allScanners = createAllScanners();\n const availableScanners = await getAvailableScanners(allScanners);\n\n console.log();\n console.log(chalk.bold('AI Coding Tools Status:'));\n console.log();\n\n const table = new Table({\n head: [\n chalk.cyan('Tool'),\n chalk.cyan('Status'),\n chalk.cyan('Data Location'),\n ],\n style: { head: [] },\n });\n\n const toolLocations: Record<string, string> = {\n 'claude-code': getClaudeProjectsDir(),\n cursor: getCursorWorkspaceDir(),\n };\n\n for (const scanner of allScanners) {\n const isAvailable = availableScanners.some((s) => s.name === scanner.name);\n const status = isAvailable\n ? chalk.green('Available')\n : chalk.dim('Not found');\n const location = toolLocations[scanner.name] || 'Unknown';\n\n table.push([scanner.name, status, isAvailable ? location : chalk.dim(location)]);\n }\n\n console.log(table.toString());\n\n // Additional info (verbose)\n if (options.verbose && availableScanners.length > 0) {\n console.log();\n console.log(chalk.bold('Tool Details:'));\n console.log();\n\n for (const scanner of availableScanners) {\n console.log(chalk.cyan(`${scanner.name}:`));\n\n switch (scanner.name) {\n case 'claude-code':\n console.log(' Session format: Encoded project path directories');\n console.log(' Path encoding: /path/to/project → -path-to-project');\n break;\n case 'cursor':\n console.log(' Session format: Hash-named directories with workspace.json');\n console.log(' Project info: Stored in workspace.json \"folder\" field');\n break;\n }\n console.log();\n }\n }\n\n // Summary\n console.log(\n chalk.dim(\n `${availableScanners.length} of ${allScanners.length} tools detected on this system.`\n )\n );\n });\n","import { Command } from 'commander';\nimport inquirer from 'inquirer';\n\nimport { logger } from '../utils/logger.js';\nimport {\n loadConfig,\n addWatchPath,\n removeWatchPath,\n getWatchPaths,\n getWatchDelay,\n setWatchDelay,\n getWatchDepth,\n setWatchDepth,\n resetConfig,\n} from '../utils/config.js';\n\nexport const configCommand = new Command('config').description(\n 'Manage configuration'\n);\n\nconst pathsCommand = new Command('paths').description('Manage watch paths');\n\npathsCommand\n .command('add <path>')\n .description('Add a watch path')\n .action((path: string) => {\n addWatchPath(path);\n logger.success(`Added: ${path}`);\n });\n\npathsCommand\n .command('remove <path>')\n .description('Remove a watch path')\n .action((path: string) => {\n const removed = removeWatchPath(path);\n if (removed) {\n logger.success(`Removed: ${path}`);\n } else {\n logger.warn(`Path not found: ${path}`);\n }\n });\n\npathsCommand\n .command('list')\n .description('List watch paths')\n .action(() => {\n const paths = getWatchPaths();\n if (!paths || paths.length === 0) {\n logger.info('No watch paths configured.');\n return;\n }\n console.log();\n for (const p of paths) {\n console.log(` ${p}`);\n }\n });\n\nconfigCommand.addCommand(pathsCommand);\n\nconst DEFAULT_DELAY_MINUTES = 5;\nconst MAX_DELAY_MINUTES = 10;\n\nconfigCommand\n .command('delay [minutes]')\n .description(`Get or set watch delay in minutes (default: ${DEFAULT_DELAY_MINUTES}, max: ${MAX_DELAY_MINUTES})`)\n .action((minutes?: string) => {\n if (minutes === undefined) {\n // Get current delay\n const delay = getWatchDelay() ?? DEFAULT_DELAY_MINUTES;\n console.log(`Watch delay: ${String(delay)} minute(s)`);\n } else {\n // Set delay\n const value = parseInt(minutes, 10);\n if (isNaN(value) || value < 1) {\n logger.error('Invalid delay value. Must be a positive number.');\n return;\n }\n if (value > MAX_DELAY_MINUTES) {\n logger.warn(`Maximum delay is ${String(MAX_DELAY_MINUTES)} minutes. Setting to ${String(MAX_DELAY_MINUTES)}.`);\n setWatchDelay(MAX_DELAY_MINUTES);\n return;\n }\n setWatchDelay(value);\n logger.success(`Watch delay set to ${String(value)} minute(s)`);\n }\n });\n\nconst DEFAULT_DEPTH = 3;\nconst MAX_DEPTH = 5;\n\nconfigCommand\n .command('depth [level]')\n .description('Get or set watch depth (default: 3, max: 5)')\n .action((level?: string) => {\n if (level === undefined) {\n const depth = getWatchDepth() ?? DEFAULT_DEPTH;\n console.log(`Watch depth: ${String(depth)}`);\n } else {\n const value = parseInt(level, 10);\n if (isNaN(value) || value < 1) {\n logger.error('Invalid depth value. Must be a positive number.');\n return;\n }\n if (value > MAX_DEPTH) {\n logger.warn(`Maximum depth is ${String(MAX_DEPTH)}. Setting to ${String(MAX_DEPTH)}.`);\n }\n setWatchDepth(value);\n const actualValue = Math.min(value, MAX_DEPTH);\n logger.success(`Watch depth set to ${String(actualValue)}`);\n }\n });\n\nconfigCommand\n .command('show')\n .description('Show all configuration')\n .action(() => {\n const config = loadConfig();\n console.log(JSON.stringify(config, null, 2));\n });\n\nconfigCommand\n .command('reset')\n .description('Reset configuration to defaults')\n .option('-f, --force', 'Skip confirmation prompt')\n .action(async (options: { force?: boolean }) => {\n if (!options.force) {\n const { confirmed } = await inquirer.prompt<{ confirmed: boolean }>([\n {\n type: 'confirm',\n name: 'confirmed',\n message: 'Reset all configuration to defaults?',\n default: false,\n },\n ]);\n\n if (!confirmed) {\n logger.info('Cancelled.');\n return;\n }\n }\n\n resetConfig();\n logger.success('Configuration reset to defaults.');\n });\n","#!/usr/bin/env node\n\nimport { cli } from './cli.js';\n\ncli.parse(process.argv);\n"],"mappings":"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","names":["Command","chalk","readdir","stat","join","join","join","readdir","join","stat","resolve","readdir","readFile","stat","access","join","access","readdir","join","readFile","stat","chalk","Command","ora","chalk","readFile","join","homedir","access","join","homedir","readFile","chalk","Command","ora","parts","Command","chalk","existsSync","homedir","join","resolve","existsSync","homedir","dirname","join","fileURLToPath","access","homedir","join","dirname","readFile","writeFile","mkdir","existsSync","Command","chalk","resolve","homedir","existsSync","join","Command","Table","chalk","Command","chalk","Table","Command","inquirer","Command","DEFAULT_DELAY_MINUTES","MAX_DELAY_MINUTES","inquirer","Command"]}
@@ -0,0 +1,29 @@
1
+ import eslint from '@eslint/js';
2
+ import tseslint from 'typescript-eslint';
3
+
4
+ export default tseslint.config(
5
+ eslint.configs.recommended,
6
+ ...tseslint.configs.strictTypeChecked,
7
+ ...tseslint.configs.stylisticTypeChecked,
8
+ {
9
+ languageOptions: {
10
+ parserOptions: {
11
+ projectService: true,
12
+ tsconfigRootDir: import.meta.dirname,
13
+ },
14
+ },
15
+ },
16
+ {
17
+ rules: {
18
+ '@typescript-eslint/no-explicit-any': 'error',
19
+ '@typescript-eslint/explicit-function-return-type': 'error',
20
+ '@typescript-eslint/no-unused-vars': [
21
+ 'error',
22
+ { argsIgnorePattern: '^_' },
23
+ ],
24
+ },
25
+ },
26
+ {
27
+ ignores: ['dist/', 'node_modules/', '*.config.ts', '*.config.js'],
28
+ }
29
+ );
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@sooink/ai-session-tidy",
3
+ "version": "0.1.1",
4
+ "description": "CLI tool that detects and cleans orphaned session data from AI coding tools",
5
+ "type": "module",
6
+ "bin": {
7
+ "ai-session-tidy": "./dist/index.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "exports": {
11
+ ".": "./dist/index.js"
12
+ },
13
+ "engines": {
14
+ "node": ">=24"
15
+ },
16
+ "packageManager": "pnpm@10.0.0",
17
+ "scripts": {
18
+ "dev": "tsup --watch",
19
+ "build": "tsup",
20
+ "test": "vitest",
21
+ "lint": "eslint src/",
22
+ "format": "prettier --write \"src/**/*.ts\""
23
+ },
24
+ "keywords": [
25
+ "cli",
26
+ "cleanup",
27
+ "claude",
28
+ "cursor",
29
+ "session",
30
+ "orphan"
31
+ ],
32
+ "author": "",
33
+ "license": "MIT",
34
+ "dependencies": {
35
+ "chalk": "^5.4.1",
36
+ "chokidar": "^4.0.3",
37
+ "cli-table3": "^0.6.5",
38
+ "commander": "^13.1.0",
39
+ "fast-glob": "^3.3.3",
40
+ "inquirer": "^12.3.2",
41
+ "ora": "^8.1.1",
42
+ "trash": "^9.0.0"
43
+ },
44
+ "devDependencies": {
45
+ "@eslint/js": "^9.18.0",
46
+ "@types/node": "^22.12.0",
47
+ "eslint": "^9.18.0",
48
+ "prettier": "^3.4.2",
49
+ "tsup": "^8.3.5",
50
+ "typescript": "^5.7.3",
51
+ "typescript-eslint": "^8.21.0",
52
+ "vitest": "^3.0.4"
53
+ }
54
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,21 @@
1
+ import { Command } from 'commander';
2
+
3
+ import { scanCommand } from './commands/scan.js';
4
+ import { cleanCommand } from './commands/clean.js';
5
+ import { watchCommand } from './commands/watch.js';
6
+ import { listCommand } from './commands/list.js';
7
+ import { configCommand } from './commands/config.js';
8
+ import { getAppVersion } from './utils/config.js';
9
+
10
+ export const cli = new Command()
11
+ .name('ai-session-tidy')
12
+ .description(
13
+ 'CLI tool that detects and cleans orphaned session data from AI coding tools'
14
+ )
15
+ .version(getAppVersion());
16
+
17
+ cli.addCommand(scanCommand, { isDefault: true });
18
+ cli.addCommand(cleanCommand);
19
+ cli.addCommand(watchCommand);
20
+ cli.addCommand(listCommand);
21
+ cli.addCommand(configCommand);