import { readdirSync, statSync } from 'fs' import { homedir } from 'os' import { join } from 'path' import { parseSessionFile } from './parser.js' import { openSessionsDb, type SessionsDb } from './db.js' import type { SessionMeta } from '../../types/session.js' export const PROJECTS_ROOT = join(homedir(), '.claude', 'projects') export const DEFAULT_DB_PATH = join(homedir(), '.claude-work', 'sessions.db') /** * Percorre a raiz de projectos Claude (profundidade 2) e devolve todos os .jsonl. * Estrutura: ~/.claude/projects//.jsonl */ export function findAllJsonl(root: string = PROJECTS_ROOT): string[] { const result: string[] = [] let entries: string[] try { entries = readdirSync(root) } catch { return result } for (const entry of entries) { const projectDir = join(root, entry) let st try { st = statSync(projectDir) } catch { continue } if (!st.isDirectory()) continue let files: string[] try { files = readdirSync(projectDir) } catch { continue } for (const f of files) { if (f.endsWith('.jsonl')) result.push(join(projectDir, f)) } } return result } /** * Indexa um único ficheiro (parse + upsert). Uso individual — útil para o watcher (Task 8). */ export async function indexFile(db: SessionsDb, path: string): Promise { const { meta } = await parseSessionFile(path) db.upsertSession(meta) } export interface IndexAllOptions { dbPath?: string onProgress?: (done: number, total: number) => void } /** * Full scan: percorre todos os JSONL e faz upsert em lote (batch 50 via transacção). */ export async function indexAll( options: IndexAllOptions = {}, ): Promise<{ indexed: number; failed: number }> { const db = openSessionsDb(options.dbPath ?? DEFAULT_DB_PATH) const files = findAllJsonl() const BATCH = 50 let indexed = 0 let failed = 0 let batch: SessionMeta[] = [] try { for (let i = 0; i < files.length; i++) { try { const { meta } = await parseSessionFile(files[i]) batch.push(meta) if (batch.length >= BATCH) { db.upsertMany(batch) indexed += batch.length batch = [] } } catch (err) { failed++ console.error(`[indexer] erro em ${files[i]}:`, err) } if (options.onProgress) { options.onProgress(indexed + failed + batch.length, files.length) } } if (batch.length > 0) { db.upsertMany(batch) indexed += batch.length } } finally { db.close() } return { indexed, failed } }