@lore-ai/cli 0.1.6

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.
Files changed (45) hide show
  1. package/README.md +178 -0
  2. package/dist/bin/lore.js +14666 -0
  3. package/dist/bin/lore.js.map +1 -0
  4. package/dist/ui/assets/Analytics-W2ANIC2s.js +1 -0
  5. package/dist/ui/assets/ConversationDetail-Ct-hROwS.js +5 -0
  6. package/dist/ui/assets/Conversations-iK7E6GEl.js +1 -0
  7. package/dist/ui/assets/MarkdownPreview-2zDiish4.js +17 -0
  8. package/dist/ui/assets/MarkdownPreview-ZgkIHsf0.css +1 -0
  9. package/dist/ui/assets/Mcps-CCT1FQ4H.js +1 -0
  10. package/dist/ui/assets/Overview-B_jOY8il.js +1 -0
  11. package/dist/ui/assets/PhaseReview-B_DDY9YB.js +1 -0
  12. package/dist/ui/assets/RepoScans-FxiMynYO.js +2 -0
  13. package/dist/ui/assets/RepoSelector-DmPRS8kf.js +1 -0
  14. package/dist/ui/assets/ResizablePanels-Bbb4S6Ss.js +1 -0
  15. package/dist/ui/assets/Review-KjvS-DNP.js +3 -0
  16. package/dist/ui/assets/ScanConversation-BonEB7pv.js +1 -0
  17. package/dist/ui/assets/Scans-DXf2sNms.js +1 -0
  18. package/dist/ui/assets/Skills-MvVWWoB2.js +1 -0
  19. package/dist/ui/assets/ToolUsage-DA5MJNwl.js +33 -0
  20. package/dist/ui/assets/Vetting-BRGVrtOA.js +1 -0
  21. package/dist/ui/assets/index-CVSL0ryk.js +12 -0
  22. package/dist/ui/assets/index-DYKYIfPr.css +1 -0
  23. package/dist/ui/assets/markdown-CZuQZQX5.js +35 -0
  24. package/dist/ui/index.html +15 -0
  25. package/package.json +96 -0
  26. package/prompts/analyze-feedback.md +67 -0
  27. package/prompts/apply-refs-update.md +149 -0
  28. package/prompts/apply-skill-update.md +151 -0
  29. package/prompts/check-relevance.md +137 -0
  30. package/prompts/classify-conversations.md +78 -0
  31. package/prompts/cluster-repo-summaries.md +76 -0
  32. package/prompts/detect-staleness.md +42 -0
  33. package/prompts/distill-changes.md +62 -0
  34. package/prompts/distill-decisions.md +48 -0
  35. package/prompts/distill-patterns.md +39 -0
  36. package/prompts/generate-changelog.md +42 -0
  37. package/prompts/generate-references.md +192 -0
  38. package/prompts/generate-repo-skill.md +387 -0
  39. package/prompts/global-summary.md +55 -0
  40. package/prompts/orchestrate-merge.md +70 -0
  41. package/prompts/pr-description.md +49 -0
  42. package/prompts/research-repo.md +121 -0
  43. package/prompts/summarize-conversation.md +64 -0
  44. package/prompts/test-mcp.md +62 -0
  45. package/prompts/test-skill.md +72 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/config/defaults.ts","../../src/config/schema.ts","../../src/state/schema.ts","../../src/utils/git.ts","../../src/utils/paths.ts","../../src/utils/logger.ts","../../src/utils/constants.ts","../../src/utils/errors.ts","../../src/state/phase-store.ts","../../src/utils/cli.ts","../../src/utils/conversation-filter.ts","../../src/utils/lore-filter.ts","../../src/state/store.ts","../../src/config/loader.ts","../../src/data-sources/cursor-db.ts","../../src/scanners/cursor.ts","../../src/scanners/claude-code.ts","../../src/distiller/prompts.ts","../../src/distiller/mcp-config.ts","../../src/distiller/tokenizer.ts","../../src/distiller/repo-grouper.ts","../../src/distiller/schemas.ts","../../src/distiller/distill-cache.ts","../../src/distiller/engine.ts","../../src/writers/review-queue.ts","../../src/summarizer/cache.ts","../../src/utils/cursor-workspace.ts","../../src/data-sources/cursor.ts","../../src/data-sources/claude-code.ts","../../src/data-sources/registry.ts","../../src/repos/activity.ts","../../src/repos/selector.ts","../../src/utils/scan-metrics.ts","../../src/ui/api/state.ts","../../src/ui/api/repos.ts","../../src/ui/api/cache.ts","../../bin/lore.ts","../../src/commands/init.ts","../../src/commands/sync.ts","../../src/skill-generator/generator.ts","../../src/classifier/repo-catalog.ts","../../src/summarizer/schemas.ts","../../src/skill-generator/output-parser.ts","../../src/skill-generator/skill-files.ts","../../src/skill-generator/agent-pointer.ts","../../src/skill-generator/orphan-detector.ts","../../src/skill-generator/skill-utils.ts","../../src/skill-generator/skill-digest.ts","../../src/utils/worktree.ts","../../src/summarizer/summarizer.ts","../../src/summarizer/loader.ts","../../src/classifier/classifier.ts","../../src/classifier/fingerprint.ts","../../src/ui/api/db.ts","../../src/summarizer/clusterer.ts","../../src/commands/watch.ts","../../src/commands/status.ts","../../src/commands/recall.ts","../../src/commands/review.ts","../../src/writers/apply-proposal.ts","../../src/commands/config.ts","../../src/commands/repos.ts","../../src/commands/ui.ts","../../src/ui/server.ts","../../src/ui/api/conversations.ts","../../src/ui/api/tools.ts","../../src/ui/api/pending.ts","../../src/ui/api/vetting.ts","../../src/vetting/store.ts","../../src/vetting/tester.ts","../../src/vetting/feedback.ts","../../src/vetting/scoring.ts","../../src/vetting/structural-validator.ts","../../src/drift/detector.ts","../../src/ui/api/skill-generator.ts","../../src/ui/api/events.ts","../../src/ui/api/metrics.ts","../../src/vetting/correction-capture.ts","../../src/ui/api/repo-scans.ts","../../src/state/scan-checkpoint.ts","../../src/state/scan-pipeline.ts","../../src/commands/skills.ts","../../src/commands/summarize.ts","../../src/commands/debug.ts","../../src/debug/source-diagnostic.ts","../../src/debug/pipeline-diagnostic.ts","../../src/commands/pr.ts","../../src/pr/context.ts","../../src/pr/matcher.ts","../../src/pr/describe.ts","../../src/pr/github.ts"],"sourcesContent":["import type { LoreConfig } from './schema.js';\n\nexport const defaultConfig: LoreConfig = {\n sources: {\n cursor: { enabled: true },\n claudeCode: { enabled: true },\n },\n outputs: {\n changelog: 'CHANGELOG.md',\n adr: 'docs/decisions/',\n architecture: 'docs/architecture.md',\n },\n repos: {\n mode: 'auto',\n include: [],\n exclude: [],\n auto: {\n maxRepos: 15,\n activityWindowDays: 14,\n },\n },\n llm: {\n cli: 'auto',\n },\n schedule: '0 */4 * * *',\n reviewMode: 'propose',\n};\n","import { z } from 'zod';\n\nexport const sourceConfigSchema = z.object({\n cursor: z.object({\n enabled: z.boolean().default(true),\n }).default({}),\n claudeCode: z.object({\n enabled: z.boolean().default(true),\n }).default({}),\n});\n\nexport const outputConfigSchema = z.object({\n changelog: z.string().default('CHANGELOG.md'),\n adr: z.string().default('docs/decisions/'),\n architecture: z.string().default('docs/architecture.md'),\n});\n\nexport const repoAutoConfigSchema = z.object({\n /** Maximum number of repos to process in auto mode */\n maxRepos: z.number().int().min(1).default(15),\n /** Only consider repos with activity within this many days */\n activityWindowDays: z.number().int().min(1).default(14),\n});\n\nexport const repoConfigSchema = z.object({\n /**\n * Selection mode:\n * - 'auto': Score repos by activity, take top N (default)\n * - 'whitelist': Only process repos in the include list\n * - 'all': Process every discovered repo (original behavior)\n */\n mode: z.enum(['auto', 'whitelist', 'all']).default('auto'),\n /** Always process these repos (repo names, org/repo, or path substrings) */\n include: z.array(z.string()).default([]),\n /** Never process these repos (repo names, org/repo, or path substrings) */\n exclude: z.array(z.string()).default([]),\n /** Settings for auto mode */\n auto: repoAutoConfigSchema.default({}),\n});\n\nexport const llmConfigSchema = z.object({\n /**\n * CLI agent preference:\n * - 'auto': try claude first, then cursor-agent (default)\n * - 'claude': only use Claude Code CLI\n * - 'cursor': only use Cursor Agent CLI\n */\n cli: z.enum(['auto', 'claude', 'cursor']).default('auto'),\n});\n\nexport const loreConfigSchema = z.object({\n sources: sourceConfigSchema.default({}),\n outputs: outputConfigSchema.default({}),\n repos: repoConfigSchema.default({}),\n llm: llmConfigSchema.default({}),\n schedule: z.string().default('0 */4 * * *'),\n reviewMode: z.enum(['propose', 'auto']).default('propose'),\n});\n\nexport type LoreConfig = z.infer<typeof loreConfigSchema>;\nexport type SourceConfig = z.infer<typeof sourceConfigSchema>;\nexport type OutputConfig = z.infer<typeof outputConfigSchema>;\nexport type RepoConfig = z.infer<typeof repoConfigSchema>;\nexport type RepoAutoConfig = z.infer<typeof repoAutoConfigSchema>;\nexport type LlmConfig = z.infer<typeof llmConfigSchema>;","import { z } from 'zod';\n\nexport const repoStateEntrySchema = z.object({\n /** When the skill was last generated/updated for this repo */\n lastSkillGenAt: z.string().nullable().default(null),\n /** When conversations were last clustered for this repo */\n lastClusterAt: z.string().nullable().default(null),\n});\n\nexport const loreStateSchema = z.object({\n lastScanAt: z.string().nullable().default(null),\n lastCursorSessionId: z.string().nullable().default(null),\n lastClaudeSessionFile: z.string().nullable().default(null),\n scanCount: z.number().int().default(0),\n /** Per-repo state tracking keyed by repo name (org/repo or shortName) */\n repoStates: z.record(z.string(), repoStateEntrySchema).default({}),\n});\n\nexport type RepoStateEntry = z.infer<typeof repoStateEntrySchema>;\nexport type LoreState = z.infer<typeof loreStateSchema>;\n\nexport const defaultState: LoreState = {\n lastScanAt: null,\n lastCursorSessionId: null,\n lastClaudeSessionFile: null,\n scanCount: 0,\n repoStates: {},\n};\n","/**\n * Shared git utilities — remote origin parsing and worktree resolution.\n *\n * Consolidates three identical implementations of remote URL parsing and\n * two identical worktree resolution functions into a single source of truth.\n */\n\nimport { existsSync, readFileSync, statSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\n\n/**\n * Read the org/repo name from a git config's remote origin URL.\n *\n * @param configDir The `.git` directory (e.g. `/repo/.git`).\n * @returns The `org/repo` string or `null` if not found.\n */\nexport function parseRemoteOrigin(configDir: string): string | null {\n try {\n const configPath = join(configDir, 'config');\n if (!existsSync(configPath)) return null;\n\n const config = readFileSync(configPath, 'utf-8');\n const urlMatch = config.match(/\\[remote \"origin\"\\][^[]*url\\s*=\\s*(.+)/m);\n if (urlMatch) {\n const url = urlMatch[1].trim();\n // git@github.com:org/repo.git → org/repo\n const sshMatch = url.match(/[:\\/]([^/]+\\/[^/]+?)(?:\\.git)?$/);\n if (sshMatch) return sshMatch[1];\n // https://github.com/org/repo.git → org/repo\n const httpsMatch = url.match(/\\/([^/]+\\/[^/]+?)(?:\\.git)?$/);\n if (httpsMatch) return httpsMatch[1];\n }\n } catch { /* ignore */ }\n return null;\n}\n\n/**\n * Resolve a `.git` path to the real repo root and config directory.\n *\n * Handles two cases:\n * - Normal repo: `.git` is a directory → root = dir, configDir = dir/.git\n * - Worktree: `.git` is a file with `gitdir: /main/.git/worktrees/name`\n * → root = /main, configDir = /main/.git\n *\n * @param dir The directory containing `.git`.\n * @param gitPath The full path to the `.git` entry (file or directory).\n */\nexport function resolveGitRoot(\n dir: string,\n gitPath: string,\n): { root: string; configDir: string } {\n try {\n const st = statSync(gitPath);\n if (st.isFile()) {\n // Worktree: .git file contains \"gitdir: <path>\"\n const content = readFileSync(gitPath, 'utf-8').trim();\n const match = content.match(/^gitdir:\\s*(.+)$/m);\n if (match) {\n const gitdir = match[1].trim();\n // gitdir is typically: /path/to/main-repo/.git/worktrees/<name>\n const worktreeMatch = gitdir.match(/^(.+\\/\\.git)\\/worktrees\\/.+$/);\n if (worktreeMatch) {\n const mainGitDir = worktreeMatch[1];\n return { root: dirname(mainGitDir), configDir: mainGitDir };\n }\n }\n }\n } catch { /* fall through */ }\n\n // Normal repo (or couldn't parse worktree reference)\n return { root: dir, configDir: join(dir, '.git') };\n}\n","import { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { existsSync, readdirSync } from 'node:fs';\nimport { parseRemoteOrigin, resolveGitRoot } from './git.js';\n\n// ── Configurable base directories ────────────────────────────────\n// These allow the extension (per-repo mode) to override the default\n// global paths used by the CLI.\n\nlet _loreDirOverride: string | null = null;\nlet _promptsDirOverride: string | null = null;\n\n/**\n * Override the lore directory for the current process.\n * Call this before any pipeline functions to use per-repo `.lore/` instead of `~/.lore/`.\n */\nexport function setLoreDir(dir: string): void {\n _loreDirOverride = dir;\n}\n\n/**\n * Override the prompts directory for the current process.\n * Useful when the extension bundles prompts in a different location.\n */\nexport function setPromptsDir(dir: string): void {\n _promptsDirOverride = dir;\n}\n\n/**\n * Reset overrides back to defaults (useful for testing).\n */\nexport function resetPathOverrides(): void {\n _loreDirOverride = null;\n _promptsDirOverride = null;\n}\n\n/**\n * Resolve the .lore directory.\n * Returns the override (per-repo) if set, otherwise falls back to ~/.lore/.\n */\nexport function getLoreDir(): string {\n return _loreDirOverride ?? join(homedir(), '.lore');\n}\n\n/**\n * Assert that lore is initialized (i.e. the lore directory exists).\n * Returns the lore directory path.\n * Throws if not initialized — callers should catch and show a user-friendly message.\n */\nexport function ensureLoreInitialized(): string {\n const dir = getLoreDir();\n if (!existsSync(dir)) {\n throw new Error(`Lore is not initialized. Run 'lore init' first. (expected: ${dir})`);\n }\n return dir;\n}\n\n/**\n * Get the `.cursor/skills/<skillName>/` directory for a given repo.\n */\nexport function getSkillsDir(repoPath: string, skillName: string): string {\n return join(repoPath, '.cursor', 'skills', skillName);\n}\n\n/**\n * Get the global `~/.cursor/skills/` directory.\n */\nexport function getGlobalSkillsDir(): string {\n return join(homedir(), '.cursor', 'skills');\n}\n\n/**\n * Resolve the prompts directory (shipped with the package).\n */\nexport function getPromptsDir(): string {\n if (_promptsDirOverride) return _promptsDirOverride;\n // In the built package, prompts/ is at the package root\n return join(import.meta.dirname, '..', '..', 'prompts');\n}\n\n/**\n * Encode a project path to the Claude Code directory name format.\n * Replaces `/` and spaces with `-`, prefixes with `-`.\n * e.g. /Users/me/project -> -Users-me-project\n */\nexport function encodeClaudeProjectPath(projectPath: string): string {\n return '-' + projectPath.slice(1).replace(/[\\/ ]/g, '-');\n}\n\n/**\n * Decode a Claude Code directory name back to a project path (best-effort).\n * e.g. -Users-me-project -> /Users/me/project\n * Note: spaces are lost (become `/`), but this gives a reasonable approximation.\n */\nexport function decodeClaudeProjectPath(dirName: string): string {\n // Remove leading dash, replace dashes with /\n return '/' + dirName.slice(1).replace(/-/g, '/');\n}\n\n/**\n * Get the base Claude Code projects directory (~/.claude/projects/).\n */\nexport function getClaudeCodeBaseDir(): string {\n return join(homedir(), '.claude', 'projects');\n}\n\n/**\n * Get the Cursor workspace storage base directory (macOS).\n */\nexport function getCursorStorageDir(): string {\n return join(homedir(), 'Library', 'Application Support', 'Cursor', 'User', 'workspaceStorage');\n}\n\n/**\n * Check if any Claude Code data exists on this machine.\n */\nexport function hasClaudeCodeData(): boolean {\n return existsSync(getClaudeCodeBaseDir());\n}\n\n/**\n * Check if Cursor workspace storage exists on this machine.\n */\nexport function hasCursorData(): boolean {\n return existsSync(getCursorStorageDir());\n}\n\n// ── Known repo discovery ──────────────────────────────────────────\n\nexport interface KnownRepo {\n /** Absolute path to the repo root on disk */\n path: string;\n /** org/repo name from git remote origin (e.g. \"wix-private/premium-domains\") */\n remoteName: string | null;\n /** Short display name (directory basename) */\n shortName: string;\n /** Encoded as a Claude Code project directory name */\n encoded: string;\n}\n\n/**\n * Discover all git repos under common root directories.\n * Scans up to `maxDepth` levels deep and returns a list of KnownRepo objects.\n * Results are cached for the lifetime of the process.\n */\nlet knownReposCache: KnownRepo[] | null = null;\n\nexport function discoverKnownRepos(force = false): KnownRepo[] {\n if (knownReposCache && !force) return knownReposCache;\n\n const home = homedir();\n const roots = [\n join(home, 'Desktop'),\n join(home, 'Documents'),\n join(home, 'projects'),\n join(home, 'repos'),\n join(home, 'code'),\n join(home, 'src'),\n join(home, 'dev'),\n ].filter((r) => existsSync(r));\n\n const repos: KnownRepo[] = [];\n const seen = new Set<string>();\n\n for (const root of roots) {\n findGitReposRecursive(root, 4, repos, seen);\n }\n\n // Also add ~/.lore itself if it has a .git\n const loreDir = getLoreDir();\n if (existsSync(join(loreDir, '.git')) && !seen.has(loreDir)) {\n addRepo(loreDir, repos, seen);\n }\n\n knownReposCache = repos;\n return repos;\n}\n\nfunction findGitReposRecursive(\n dir: string,\n depth: number,\n repos: KnownRepo[],\n seen: Set<string>,\n): void {\n if (depth <= 0) return;\n\n try {\n const gitPath = join(dir, '.git');\n if (existsSync(gitPath)) {\n // Resolve worktrees to their real repo\n const resolved = resolveGitRoot(dir, gitPath);\n if (!seen.has(resolved.root)) {\n addRepo(resolved.root, repos, seen, resolved.configDir);\n }\n // Continue recursing — a git repo directory may contain other\n // independent repos (e.g. a \"Premium-group\" repo that has\n // \"premium-domains\" nested inside it as a separate clone).\n }\n\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;\n findGitReposRecursive(join(dir, entry.name), depth - 1, repos, seen);\n }\n } catch { /* permission denied, etc. */ }\n}\n\nfunction addRepo(\n repoRoot: string,\n repos: KnownRepo[],\n seen: Set<string>,\n configDir?: string,\n): void {\n seen.add(repoRoot);\n const remoteName = parseRemoteOrigin(configDir ?? join(repoRoot, '.git'));\n repos.push({\n path: repoRoot,\n remoteName,\n shortName: repoRoot.split('/').pop() ?? repoRoot,\n encoded: encodeClaudeProjectPath(repoRoot),\n });\n}\n\n// resolveToRealRepo and readRemoteOriginName moved to src/utils/git.ts\n// as resolveGitRoot and parseRemoteOrigin respectively.\n\n/**\n * Resolve a Claude Code encoded project directory name to a known repo.\n * Uses longest-prefix matching on the encoded form.\n *\n * e.g. `-Users-matanlas-Desktop-C-Premium-group-premium-cart-anonymous`\n * matches repo at `/Users/matanlas/Desktop/C/Premium-group/premium-cart-anonymous`\n * whose encoded form is `-Users-matanlas-Desktop-C-Premium-group-premium-cart-anonymous`\n */\nexport function resolveClaudeProjectToRepo(\n encodedDirName: string,\n repos?: KnownRepo[],\n): KnownRepo | null {\n const knownRepos = repos ?? discoverKnownRepos();\n let bestMatch: KnownRepo | null = null;\n let bestLen = 0;\n\n for (const repo of knownRepos) {\n // The encoded dir name must start with the repo's encoded path\n if (encodedDirName.startsWith(repo.encoded) && repo.encoded.length > bestLen) {\n // Ensure it's a clean boundary (exact match or followed by -)\n if (\n encodedDirName.length === repo.encoded.length ||\n encodedDirName[repo.encoded.length] === '-'\n ) {\n bestMatch = repo;\n bestLen = repo.encoded.length;\n }\n }\n }\n\n return bestMatch;\n}\n","import { createConsola } from 'consola';\n\nexport const logger = createConsola({\n formatOptions: {\n date: false,\n columns: 80,\n },\n});\n\nexport function setLogLevel(level: 'debug' | 'info' | 'warn' | 'error' | 'silent') {\n const levels: Record<string, number> = {\n debug: 4,\n info: 3,\n warn: 2,\n error: 1,\n silent: 0,\n };\n logger.level = levels[level] ?? 3;\n}\n","/**\n * Centralized constants — single source of truth for durations,\n * timeouts, thresholds, and limits used across the lore codebase.\n */\n\n// ── Duration helpers (ms) ────────────────────────────────────\nexport const MS_PER_SECOND = 1_000;\nexport const MS_PER_MINUTE = 60 * MS_PER_SECOND;\nexport const MS_PER_HOUR = 60 * MS_PER_MINUTE;\nexport const MS_PER_DAY = 24 * MS_PER_HOUR;\n\n// ── Common durations (ms) ────────────────────────────────────\nexport const THREE_DAYS_MS = 3 * MS_PER_DAY;\nexport const SEVEN_DAYS_MS = 7 * MS_PER_DAY;\nexport const THIRTY_DAYS_MS = 30 * MS_PER_DAY;\nexport const NINETY_DAYS_MS = 90 * MS_PER_DAY;\nexport const FOUR_HOURS_MS = 4 * MS_PER_HOUR;\n\n// ── Vetting lifecycle ────────────────────────────────────────\nexport const PROBATION_DURATION_MS = THREE_DAYS_MS;\nexport const REVIEW_INTERVAL_MS = THIRTY_DAYS_MS;\nexport const FRESHNESS_HALF_LIFE_DAYS = 30;\n\n// ── Memory pruning ──────────────────────────────────────────\nexport const DEFAULT_MAX_AGE_DAYS = 30;\nexport const DEFAULT_REPO_TOKEN_BUDGET = 50_000;\n\n// ── Git command timeouts ─────────────────────────────────────\nexport const GIT_FAST_TIMEOUT_MS = 5_000; // ls-files, rev-list, log -1\nexport const GIT_MEDIUM_TIMEOUT_MS = 10_000; // diff, worktree remove\nexport const GIT_SLOW_TIMEOUT_MS = 15_000; // worktree add, log, version check\nexport const GIT_VERY_SLOW_TIMEOUT_MS = 30_000; // worktree list\n\n// ── CLI / LLM timeouts ──────────────────────────────────────\nexport const SUMMARIZE_TIMEOUT_MS = 2 * MS_PER_MINUTE;\nexport const CLUSTER_TIMEOUT_MS = 2 * MS_PER_MINUTE;\nexport const RELEVANCE_TIMEOUT_MS = 3 * MS_PER_MINUTE;\nexport const DELTA_TIMEOUT_MS = 5 * MS_PER_MINUTE;\nexport const CLASSIFY_TIMEOUT_MS = 5 * MS_PER_MINUTE;\nexport const SKILL_TIMEOUT_MS = 8 * MS_PER_MINUTE;\nexport const DISTILL_TIMEOUT_MS = 3 * MS_PER_MINUTE;\nexport const DISTILL_INTERACTIVE_TIMEOUT_MS = 10 * MS_PER_MINUTE;\nexport const RECALL_TIMEOUT_MS = 2 * MS_PER_MINUTE;\n\n// ── Rate-limit backoff ───────────────────────────────────────\nexport const BACKOFF_DELAYS_MS = [30_000, 60_000, 120_000] as const;\n\n// ── Extension / trigger ──────────────────────────────────────\nexport const SCAN_COOLDOWN_MS = 30_000;\nexport const TRIGGER_FRESHNESS_MS = 30_000;\nexport const TRIGGER_DEBOUNCE_MS = 3_000;\nexport const TRIGGER_POLL_MS = 5_000;\nexport const VERDICT_MODAL_TIMEOUT_MS = 15_000;\nexport const STALE_WORKTREE_MS = MS_PER_HOUR;\nexport const STALENESS_HOURS = 24;\n\n// ── Cache TTL ────────────────────────────────────────────────\nexport const DEFAULT_CACHE_TTL_MS = 5 * MS_PER_MINUTE;\n\n// ── Content thresholds ───────────────────────────────────────\nexport const MIN_SUBSTANTIAL_OUTPUT = 200;\nexport const MIN_USABLE_OUTPUT = 500;\nexport const MAX_TRANSCRIPT_CHARS = 80_000;\nexport const MAX_EXISTING_SKILL_CHARS = 6_000;\nexport const MAX_DELTA_SKILL_CHARS = 4_000;\nexport const MAX_REFS_PER_FILE_CHARS = 2_000;\n\n// ── Writing phase (tool-assisted) ────────────────────────────\n// No max-turns limit: stall detection + hard ceiling are better safety nets.\n// The old per-file turn limit (2 turns/file) caused the AI to exhaust turns\n// on grep+read before ever reaching the Edit tool.\nexport const WRITING_STALL_TIMEOUT_MS = 4 * MS_PER_MINUTE; // kill if no progress for 4 min\nexport const WRITING_HARD_CEILING_MS = 20 * MS_PER_MINUTE; // absolute safety net (never exceed)\n\n// ── Thinking control (per-phase env overrides) ───────────────\n/** Verdict phase: reduce Opus 4.6 adaptive reasoning to low effort. */\nexport const VERDICT_ENV_OVERRIDES = { CLAUDE_CODE_EFFORT_LEVEL: 'low' } as const;\n/** Writing phases (generator + delta): disable extended thinking entirely. */\nexport const WRITING_ENV_OVERRIDES = { MAX_THINKING_TOKENS: '0' } as const;\n\n// ── Limits ───────────────────────────────────────────────────\nexport const GIT_LOG_LIMIT = 40;\nexport const GIT_DIFF_MAX_LINES = 200;\nexport const DEFAULT_UI_PORT = 3_333;\n","/**\n * Shared error-handling utilities.\n *\n * Provides a safe way to extract a message from an `unknown` catch variable\n * without requiring `any` type assertions.\n */\n\n/**\n * Extract a human-readable message from an unknown error value.\n *\n * @example\n * ```ts\n * try { ... } catch (err: unknown) {\n * logger.error(`Failed: ${errorMessage(err)}`);\n * }\n * ```\n */\nexport function errorMessage(err: unknown): string {\n if (err instanceof Error) return err.message;\n return String(err);\n}\n","/**\n * Phase metadata store.\n *\n * Tracks which lore process phase (classify, distill, skill-gen, vet)\n * created each Claude Code / Cursor conversation. Stored separately\n * in ~/.lore/conversation-phases.json so source data is untouched.\n *\n * Conversations without a phase entry are considered \"legacy\".\n */\n\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { getLoreDir } from '../utils/paths.js';\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport type LorePhase = 'classify' | 'distill' | 'skill-gen' | 'vet' | 'summarize' | 'cluster' | 'recall';\n\nexport interface PhaseEntry {\n /** Conversation ID (e.g. \"claude-{sessionId}\" or \"cursor-{composerId}\") */\n conversationId: string;\n /** Which lore phase created this conversation */\n phase: LorePhase;\n /** When the tag was recorded */\n taggedAt: string;\n /** Optional: the lore run that produced this (for grouping) */\n runId?: string;\n}\n\ninterface PhaseStoreData {\n version: 1;\n updatedAt: string;\n entries: PhaseEntry[];\n}\n\n// ── File path ────────────────────────────────────────────────────\n\nconst PHASE_FILE = 'conversation-phases.json';\n\nfunction getPhaseStorePath(): string {\n return join(getLoreDir(), PHASE_FILE);\n}\n\n// ── Read / Write ─────────────────────────────────────────────────\n\nfunction readStore(): PhaseStoreData {\n const path = getPhaseStorePath();\n if (!existsSync(path)) {\n return { version: 1, updatedAt: new Date().toISOString(), entries: [] };\n }\n try {\n const raw = readFileSync(path, 'utf-8');\n const data = JSON.parse(raw) as PhaseStoreData;\n if (data.version !== 1) {\n return { version: 1, updatedAt: new Date().toISOString(), entries: [] };\n }\n return data;\n } catch {\n return { version: 1, updatedAt: new Date().toISOString(), entries: [] };\n }\n}\n\nfunction writeStore(data: PhaseStoreData): void {\n const loreDir = getLoreDir();\n if (!existsSync(loreDir)) mkdirSync(loreDir, { recursive: true });\n data.updatedAt = new Date().toISOString();\n writeFileSync(getPhaseStorePath(), JSON.stringify(data, null, 2) + '\\n', 'utf-8');\n}\n\n// ── Public API ───────────────────────────────────────────────────\n\n/**\n * Tag a conversation with a lore phase.\n * If the conversation already has a phase, it is overwritten.\n */\nexport function tagConversation(\n conversationId: string,\n phase: LorePhase,\n runId?: string,\n): void {\n const store = readStore();\n const existing = store.entries.findIndex((e) => e.conversationId === conversationId);\n\n const entry: PhaseEntry = {\n conversationId,\n phase,\n taggedAt: new Date().toISOString(),\n runId,\n };\n\n if (existing >= 0) {\n store.entries[existing] = entry;\n } else {\n store.entries.push(entry);\n }\n\n writeStore(store);\n}\n\n/**\n * Tag multiple conversations at once (batch operation).\n */\nexport function tagConversations(\n conversationIds: string[],\n phase: LorePhase,\n runId?: string,\n): void {\n const store = readStore();\n const existingMap = new Map(store.entries.map((e, i) => [e.conversationId, i]));\n const now = new Date().toISOString();\n\n for (const conversationId of conversationIds) {\n const entry: PhaseEntry = {\n conversationId,\n phase,\n taggedAt: now,\n runId,\n };\n\n const idx = existingMap.get(conversationId);\n if (idx !== undefined) {\n store.entries[idx] = entry;\n } else {\n store.entries.push(entry);\n existingMap.set(conversationId, store.entries.length - 1);\n }\n }\n\n writeStore(store);\n}\n\n/**\n * Get the phase for a single conversation.\n * Returns undefined if the conversation is untagged (legacy).\n */\nexport function getPhaseForConversation(conversationId: string): LorePhase | undefined {\n const store = readStore();\n return store.entries.find((e) => e.conversationId === conversationId)?.phase;\n}\n\n/**\n * Get all entries for a specific phase.\n */\nexport function getEntriesByPhase(phase: LorePhase): PhaseEntry[] {\n const store = readStore();\n return store.entries.filter((e) => e.phase === phase);\n}\n\n/**\n * Get the full phase map: conversationId → PhaseEntry.\n */\nexport function getAllPhaseEntries(): Map<string, PhaseEntry> {\n const store = readStore();\n return new Map(store.entries.map((e) => [e.conversationId, e]));\n}\n\n/**\n * Get all tagged conversation IDs (non-legacy).\n */\nexport function getTaggedConversationIds(): Set<string> {\n const store = readStore();\n return new Set(store.entries.map((e) => e.conversationId));\n}\n\n/**\n * Get all entries for a specific run ID.\n * Used by the scan detail page to find conversations belonging to a run.\n */\nexport function getEntriesByRunId(runId: string): PhaseEntry[] {\n const store = readStore();\n return store.entries.filter((e) => e.runId === runId);\n}\n\n/**\n * Get summary counts per phase.\n */\nexport function getPhaseCounts(): Record<LorePhase, number> {\n const store = readStore();\n const counts: Record<LorePhase, number> = {\n classify: 0,\n distill: 0,\n 'skill-gen': 0,\n vet: 0,\n summarize: 0,\n cluster: 0,\n recall: 0,\n };\n for (const entry of store.entries) {\n if (entry.phase in counts) {\n counts[entry.phase]++;\n }\n }\n return counts;\n}\n","/**\n * Shared CLI resolution, rate-limit detection, and fallback logic.\n *\n * All modules that invoke Claude Code CLI or Cursor Agent CLI should use\n * these utilities to get consistent fallback + rate-limit handling.\n */\n\nimport { execa } from 'execa';\nimport { readdirSync, statSync, existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { logger } from './logger.js';\nimport { getClaudeCodeBaseDir } from './paths.js';\nimport { BACKOFF_DELAYS_MS, GIT_SLOW_TIMEOUT_MS } from './constants.js';\nimport { errorMessage } from './errors.js';\nimport { tagConversations, type LorePhase } from '../state/phase-store.js';\n\n// ── Lore process marker ─────────────────────────────────────────\n\n/**\n * Prefix for all lore process markers: `[lore:<phase>]`.\n *\n * Every CLI invocation prepends `[lore:<phase>]` (e.g. `[lore:classify]`,\n * `[lore:distill]`) as the first line of the prompt. This becomes the\n * conversation title across all vendors (Claude Code, Cursor) and is\n * 100% deterministic — no heuristics needed.\n *\n * The filter checks for this prefix to identify lore-generated conversations.\n */\nexport const LORE_MARKER_PREFIX = '[lore:';\n\n/**\n * @deprecated Use `LORE_MARKER_PREFIX` instead. Kept for backward compat\n * with conversations created before the phase-specific marker was added.\n */\nexport const LORE_PROCESS_MARKER = '<!-- lore:process -->';\n\n/** Build the phase-specific marker line for a given phase. */\nexport function buildLoreMarker(phase: LorePhase): string {\n return `[lore:${phase}]`;\n}\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport interface CliTarget {\n name: string;\n cmd: string;\n}\n\nexport interface CliInvokeOptions {\n /** Arguments to pass to the CLI (e.g. ['-p', '--output-format', 'text']). */\n args: string[];\n /** Prompt text piped via stdin. */\n input: string;\n /** Working directory. */\n cwd?: string;\n /** Timeout in ms. */\n timeout?: number;\n /** AbortSignal for cancellation. */\n signal?: AbortSignal;\n /** Optional model override (appended as `--model <model>`). */\n model?: string;\n /**\n * Model tier for automatic model routing.\n * When set, overrides `model` with the appropriate model for the tier.\n * - 'fast': haiku (classification, tagging)\n * - 'balanced': sonnet (summarization, analysis)\n * - 'powerful': opus (skill generation, verdicts)\n */\n modelTier?: ModelTier;\n /**\n * If true, the `--max-turns` flag is only added for the `claude` CLI\n * (the `agent` CLI doesn't support it). Default: false.\n */\n maxTurnsClaudeOnly?: boolean;\n /**\n * Path to a system prompt file. When set, `--append-system-prompt-file <path>`\n * is appended to the CLI args so behavioural directives live in the system\n * prompt rather than the user message.\n */\n systemPromptFile?: string;\n /**\n * Optional callback to surface progress/warning messages to the user.\n * Used to make rate-limit backoff delays visible instead of silent.\n */\n onOutput?: (message: string) => void;\n /**\n * Optional environment variable overrides for the subprocess.\n * Merged with `process.env` so callers only need to specify overrides.\n * Example: `{ MAX_THINKING_TOKENS: '0' }` to disable extended thinking.\n */\n env?: Record<string, string>;\n /**\n * Lore pipeline phase that is creating this conversation.\n * When set, the conversation is prefixed with `[lore:<phase>]` so it\n * can be filtered deterministically by all vendors.\n */\n phase?: LorePhase;\n}\n\n// ── Vendor config ────────────────────────────────────────────────\n\ninterface CliVendorConfig {\n detectionKey: keyof CliDetectionResult;\n unsupportedBoolFlags: Set<string>;\n unsupportedValueFlags: Set<string>;\n defaultFlags: string[];\n supportsFallbackModel: boolean;\n systemPromptMode: 'flag' | 'inline';\n}\n\nconst CLI_VENDOR_CONFIG: Record<string, CliVendorConfig> = {\n claude: {\n detectionKey: 'claude',\n unsupportedBoolFlags: new Set(),\n unsupportedValueFlags: new Set(),\n defaultFlags: [],\n supportsFallbackModel: true,\n systemPromptMode: 'flag',\n },\n 'cursor-agent': {\n detectionKey: 'cursor',\n unsupportedBoolFlags: new Set([\n '--strict-mcp-config',\n '--verbose',\n '--dangerously-skip-permissions',\n ]),\n unsupportedValueFlags: new Set([\n '--mcp-config',\n '--fallback-model',\n '--max-turns',\n '--tools',\n '--allowedTools',\n '--append-system-prompt-file',\n ]),\n defaultFlags: ['--force'],\n supportsFallbackModel: false,\n systemPromptMode: 'inline',\n },\n};\n\nfunction getVendorConfig(name: string): CliVendorConfig {\n return CLI_VENDOR_CONFIG[name] ?? CLI_VENDOR_CONFIG['cursor-agent'];\n}\n\n// ── Constants ────────────────────────────────────────────────────\n\nconst CLI_CHAIN: CliTarget[] = [\n { name: 'claude', cmd: 'claude' },\n { name: 'cursor-agent', cmd: 'agent' },\n { name: 'claude', cmd: `${process.env.HOME}/.local/bin/claude` },\n { name: 'cursor-agent', cmd: `${process.env.HOME}/.local/bin/agent` },\n];\n\nconst BACKOFF_DELAYS = BACKOFF_DELAYS_MS;\nconst JITTER_FRACTION = 0.25;\n\n// ── Model-tier routing ──────────────────────────────────────────\n\n/**\n * Model tiers for routing different task types to appropriate models.\n * Inspired by claude-flow's 3-tier routing.\n *\n * Usage: pass `modelTier` in CliInvokeOptions to auto-select model.\n */\nexport type ModelTier = 'fast' | 'balanced' | 'powerful';\n\nconst MODEL_TIER_MAP: Record<ModelTier, string> = {\n fast: 'haiku', // Classification, tagging, simple parsing\n balanced: 'sonnet', // Summarization, analysis, clustering\n powerful: 'opus', // Skill generation, verdicts, complex research\n};\n\n/**\n * Resolve a model tier to a concrete model name.\n */\nexport function resolveModelTier(tier: ModelTier): string {\n return MODEL_TIER_MAP[tier];\n}\n\n// ── Rate-limit tracking ─────────────────────────────────────────\n\n/**\n * Cumulative rate-limit tracking for diagnostics.\n * Tracks total time spent waiting and number of rate-limit events.\n */\ninterface RateLimitStats {\n /** Total time spent in rate-limit backoff (ms). */\n totalBackoffMs: number;\n /** Number of rate-limit events. */\n eventCount: number;\n /** Timestamps of rate-limit events. */\n events: number[];\n /** Last retry-after value parsed (seconds). */\n lastRetryAfterSec: number | null;\n}\n\nconst rateLimitStats: RateLimitStats = {\n totalBackoffMs: 0,\n eventCount: 0,\n events: [],\n lastRetryAfterSec: null,\n};\n\n/**\n * Get current rate-limit statistics.\n * Useful for the UI dashboard and adaptive concurrency.\n */\nexport function getRateLimitStats(): Readonly<RateLimitStats> {\n return { ...rateLimitStats };\n}\n\n/**\n * Reset rate-limit statistics (e.g. at scan start).\n */\nexport function resetRateLimitStats(): void {\n rateLimitStats.totalBackoffMs = 0;\n rateLimitStats.eventCount = 0;\n rateLimitStats.events = [];\n rateLimitStats.lastRetryAfterSec = null;\n}\n\n/**\n * Check if we're in a rate-limit storm (3+ events in 60 seconds).\n * Used by adaptive concurrency to trigger health-check pauses.\n */\nexport function isRateLimitStorm(): boolean {\n const now = Date.now();\n const recentEvents = rateLimitStats.events.filter((t) => now - t < 60_000);\n return recentEvents.length >= 3;\n}\n\n// ── CLI preference (user-configurable agent selection) ───────────\n\n/**\n * User preference for which CLI agent to use.\n * - 'auto': try both (claude first, then cursor-agent) — default\n * - 'claude': only try Claude Code CLI entries\n * - 'cursor': only try Cursor Agent CLI entries\n */\nexport type CliPreference = 'auto' | 'claude' | 'cursor';\n\nlet cliPreference: CliPreference = 'auto';\n\n/**\n * Set the CLI agent preference. Resets cached resolution so the next\n * `resolveCli()` call respects the new preference.\n */\nexport function setCliPreference(pref: CliPreference): void {\n cliPreference = pref;\n resolvedCli = null;\n logger.debug(`[cli] Preference set to '${pref}'`);\n}\n\n/**\n * Get the current CLI agent preference.\n */\nexport function getCliPreference(): CliPreference {\n return cliPreference;\n}\n\n// ── CLI detection ────────────────────────────────────────────────\n\n/**\n * Result of detecting which CLI agents are installed.\n */\nexport interface CliDetectionResult {\n claude: boolean;\n cursor: boolean;\n}\n\n/**\n * Detect which CLI agents are installed by probing all CLI_CHAIN entries.\n * Unlike `resolveCli()`, this does NOT cache — it reports all available\n * agents for init/UI purposes.\n */\nexport async function detectInstalledClis(): Promise<CliDetectionResult> {\n const result: CliDetectionResult = { claude: false, cursor: false };\n for (const target of CLI_CHAIN) {\n try {\n await execa(target.cmd, ['--version'], { timeout: GIT_SLOW_TIMEOUT_MS });\n const config = getVendorConfig(target.name);\n result[config.detectionKey] = true;\n } catch { /* not available */ }\n }\n return result;\n}\n\n// ── CLI resolution (cached) ─────────────────────────────────────\n\nlet resolvedCli: CliTarget | null = null;\n\n/**\n * Resolve which CLI is available. Result is cached for the process lifetime.\n * Respects the current `cliPreference` to filter the chain.\n */\nexport async function resolveCli(): Promise<CliTarget> {\n if (resolvedCli) return resolvedCli;\n\n // Filter chain based on user preference\n const chain = cliPreference === 'auto'\n ? CLI_CHAIN\n : CLI_CHAIN.filter((t) =>\n cliPreference === 'claude' ? t.name === 'claude' : t.name === 'cursor-agent',\n );\n\n for (const target of chain) {\n try {\n await execa(target.cmd, ['--version'], { timeout: GIT_SLOW_TIMEOUT_MS });\n resolvedCli = target;\n logger.debug(`[cli] Resolved to ${target.name} (${target.cmd}) [preference: ${cliPreference}]`);\n return target;\n } catch (err: unknown) {\n logger.debug(`[cli] ${target.name} check failed: ${errorMessage(err)}`);\n // try next\n }\n }\n\n const chainNames = [...new Set(chain.map((t) => t.name))].join(', ');\n throw new Error(\n `No AI CLI found (preference: ${cliPreference}). Install one of: ${chainNames}`,\n );\n}\n\n/**\n * Ensure at least one CLI is available. Throws if none found.\n */\nexport async function ensureAnyCli(): Promise<CliTarget> {\n return resolveCli();\n}\n\n// ── Rate-limit detection ────────────────────────────────────────\n\n/**\n * Detect rate-limit / quota errors in CLI output or stderr.\n */\nexport function isRateLimited(output: string): boolean {\n const lower = output.toLowerCase();\n return (\n lower.includes(\"you've hit your limit\") ||\n lower.includes('hit your limit') ||\n lower.includes('rate limit') ||\n lower.includes('rate_limit') ||\n lower.includes('too many requests') ||\n lower.includes('quota exceeded') ||\n lower.includes('overloaded') ||\n lower.includes('usage limit reached') // Issue #8959: model-specific limits use different wording\n );\n}\n\n// ── Simple CLI invocation (single attempt) ──────────────────────\n\n/**\n * Invoke a CLI binary once with the given options.\n * Returns the raw stdout text.\n * If the process fails with a rate-limit error, it returns the combined\n * output instead of throwing (so the caller can inspect & retry).\n *\n * Accepts a `CliTarget` (not a raw cmd string) so name-based feature\n * checks work correctly even when the resolved binary is an absolute\n * path (e.g. `~/.local/bin/claude`).\n */\nexport async function invokeCliOnce(\n target: CliTarget,\n opts: CliInvokeOptions,\n): Promise<string> {\n const { cmd, name } = target;\n const vendor = getVendorConfig(name);\n let args = [...opts.args];\n // Prepend phase-specific marker so the conversation title is deterministic\n // across all vendors: `[lore:classify]`, `[lore:distill]`, etc.\n const marker = opts.phase ? buildLoreMarker(opts.phase) : LORE_PROCESS_MARKER;\n let input = `${marker}\\n${opts.input}`;\n\n if (opts.model) {\n args.push('--model', opts.model);\n }\n\n if (opts.systemPromptFile) {\n if (vendor.systemPromptMode === 'flag') {\n args.push('--append-system-prompt-file', opts.systemPromptFile);\n } else {\n try {\n const systemContent = readFileSync(opts.systemPromptFile, 'utf-8');\n input = `${systemContent}\\n\\n${input}`;\n } catch { /* skip */ }\n }\n }\n\n // Strip unsupported flags for this vendor\n if (vendor.unsupportedBoolFlags.size > 0 || vendor.unsupportedValueFlags.size > 0) {\n const filtered: string[] = [];\n for (let i = 0; i < args.length; i++) {\n if (vendor.unsupportedBoolFlags.has(args[i])) continue;\n if (vendor.unsupportedValueFlags.has(args[i])) {\n if (i + 1 < args.length && !args[i + 1].startsWith('--')) i++;\n continue;\n }\n filtered.push(args[i]);\n }\n for (const flag of vendor.defaultFlags) {\n if (!filtered.includes(flag)) filtered.push(flag);\n }\n args = filtered;\n }\n\n const child = execa(cmd, args, {\n input,\n cwd: opts.cwd,\n timeout: opts.timeout,\n cancelSignal: opts.signal,\n ...(opts.env && { env: { ...process.env, ...opts.env } }),\n });\n\n let rawOutput = '';\n if (child.stdout) {\n child.stdout.on('data', (chunk: Buffer) => {\n rawOutput += chunk.toString();\n });\n }\n\n try {\n await child;\n } catch (err: unknown) {\n const stderr = (err as { stderr?: string }).stderr ?? '';\n const combined = rawOutput + stderr;\n if (isRateLimited(combined)) {\n return combined; // caller will detect via isRateLimited()\n }\n // Enhance the error message with stderr for better diagnostics\n if (stderr && err instanceof Error) {\n const stderrSnippet = stderr.trim().slice(0, 200);\n err.message = `${err.message}\\n stderr: ${stderrSnippet}`;\n }\n throw err;\n }\n\n return rawOutput;\n}\n\n// ── Full fallback invocation ────────────────────────────────────\n\n/**\n * Invoke with native model fallback and exponential backoff retry with jitter:\n *\n * 1. Resolve model tier to concrete model name (if modelTier is set).\n * 2. Primary CLI with `--fallback-model sonnet` — the CLI handles model\n * downgrade automatically when the default model is overloaded.\n * 3. If still rate-limited: exponential backoff with jitter (±25%).\n * 4. Parse retry-after from CLI output if available.\n * 5. Track cumulative rate-limit time for diagnostics.\n * 6. If all retries exhausted: log error and return raw output so the\n * caller can decide how to handle it (most already handle empty results).\n *\n * Returns the raw text output.\n */\nexport async function invokeWithFallback(\n opts: CliInvokeOptions,\n label: string,\n): Promise<string> {\n const primary = await resolveCli();\n\n // Resolve model tier to concrete model name\n const resolvedOpts = { ...opts };\n if (opts.modelTier && !opts.model) {\n resolvedOpts.model = resolveModelTier(opts.modelTier);\n }\n\n const primaryVendor = getVendorConfig(primary.name);\n const fallbackModel = resolvedOpts.model === 'sonnet' ? 'haiku' : 'sonnet';\n const enhancedArgs = primaryVendor.supportsFallbackModel\n ? [...resolvedOpts.args, '--fallback-model', fallbackModel]\n : [...resolvedOpts.args];\n\n const enhancedOpts = { ...resolvedOpts, args: enhancedArgs };\n\n // Attempt 1: primary CLI with native fallback model\n let rawOutput = await invokeCliOnce(primary, enhancedOpts);\n\n if (!isRateLimited(rawOutput)) {\n return rawOutput;\n }\n\n // Track rate-limit event\n rateLimitStats.eventCount++;\n rateLimitStats.events.push(Date.now());\n\n // Attempts 2-4: exponential backoff retries with jitter\n for (let i = 0; i < BACKOFF_DELAYS.length; i++) {\n // Parse retry-after from the output if available\n const retryAfterSec = parseRetryAfter(rawOutput);\n if (retryAfterSec !== null) {\n rateLimitStats.lastRetryAfterSec = retryAfterSec;\n }\n\n // Calculate delay: use retry-after if available, otherwise exponential backoff with jitter\n let delay: number;\n if (retryAfterSec !== null && retryAfterSec > 0) {\n delay = retryAfterSec * 1000;\n } else {\n const baseDelay = BACKOFF_DELAYS[i];\n delay = addJitter(baseDelay, JITTER_FRACTION);\n }\n\n const delaySec = Math.round(delay / 1000);\n const retryInfo = retryAfterSec !== null ? ` (retry-after: ${retryAfterSec}s)` : '';\n const backoffMsg = `[${label}] Rate-limited (retry ${i + 1}/${BACKOFF_DELAYS.length}), waiting ${delaySec}s${retryInfo}...`;\n logger.warn(backoffMsg);\n // Surface to user so they see why generation is paused\n opts.onOutput?.(`[rate-limit] ${backoffMsg}`);\n\n // Track backoff time\n rateLimitStats.totalBackoffMs += delay;\n\n await sleep(delay, opts.signal);\n\n rawOutput = await invokeCliOnce(primary, enhancedOpts);\n\n if (!isRateLimited(rawOutput)) {\n return rawOutput;\n }\n\n // Track additional rate-limit events\n rateLimitStats.eventCount++;\n rateLimitStats.events.push(Date.now());\n }\n\n // All attempts exhausted\n logger.error(\n `[${label}] All retry attempts exhausted (rate-limited after ${BACKOFF_DELAYS.length} backoff retries, total backoff: ${Math.round(rateLimitStats.totalBackoffMs / 1000)}s)`,\n );\n return rawOutput;\n}\n\n/** Reset cached CLI resolution (useful for testing or reconfiguration). */\nexport function resetResolvedCli(): void {\n resolvedCli = null;\n}\n\n// ── Helpers ─────────────────────────────────────────────────────\n\n/**\n * Sleep for the given duration, aborting early if the signal fires.\n */\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(new Error('Aborted'));\n return;\n }\n const timer = setTimeout(resolve, ms);\n const onAbort = () => {\n clearTimeout(timer);\n reject(new Error('Aborted'));\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n\n/**\n * Add jitter to a delay value.\n * Returns delay ± (fraction * delay), uniformly distributed.\n * Prevents thundering herd when multiple processes retry simultaneously.\n */\nfunction addJitter(delayMs: number, fraction: number): number {\n const jitterRange = delayMs * fraction;\n const jitter = (Math.random() * 2 - 1) * jitterRange; // -jitterRange to +jitterRange\n return Math.max(1000, Math.round(delayMs + jitter)); // Minimum 1 second\n}\n\n/**\n * Parse a retry-after value from CLI output.\n * Looks for patterns like:\n * - \"retry-after: 30\"\n * - \"Retrying in 9 seconds\"\n * - \"wait 30 seconds\"\n * - \"retry after 60s\"\n */\nfunction parseRetryAfter(output: string): number | null {\n const lower = output.toLowerCase();\n\n // Pattern: \"retry-after: N\" (HTTP header style)\n const headerMatch = lower.match(/retry-after:\\s*(\\d+)/);\n if (headerMatch) {\n return parseInt(headerMatch[1], 10);\n }\n\n // Pattern: \"retrying in N seconds\"\n const retryingMatch = lower.match(/retrying\\s+in\\s+(\\d+)\\s*s/);\n if (retryingMatch) {\n return parseInt(retryingMatch[1], 10);\n }\n\n // Pattern: \"wait N seconds\" or \"wait Ns\"\n const waitMatch = lower.match(/wait\\s+(\\d+)\\s*s/);\n if (waitMatch) {\n return parseInt(waitMatch[1], 10);\n }\n\n // Pattern: \"retry after Ns\" or \"retry after N seconds\"\n const retryAfterMatch = lower.match(/retry\\s+after\\s+(\\d+)\\s*s/);\n if (retryAfterMatch) {\n return parseInt(retryAfterMatch[1], 10);\n }\n\n return null;\n}\n\n// ── Phase-aware invocation ──────────────────────────────────────\n\n/**\n * Snapshot all Claude Code session file paths with their mtimes.\n * Used to detect which sessions were created during a phase invocation.\n */\nexport function snapshotClaudeSessions(): Map<string, number> {\n const baseDir = getClaudeCodeBaseDir();\n const snapshot = new Map<string, number>();\n\n if (!existsSync(baseDir)) return snapshot;\n\n try {\n const projectDirs = readdirSync(baseDir, { withFileTypes: true }).filter((d) => d.isDirectory());\n for (const projDir of projectDirs) {\n const projPath = join(baseDir, projDir.name);\n try {\n const files = readdirSync(projPath).filter((f) => f.endsWith('.jsonl'));\n for (const file of files) {\n const filePath = join(projPath, file);\n try {\n const st = statSync(filePath);\n snapshot.set(filePath, st.mtimeMs);\n } catch { /* skip */ }\n }\n } catch { /* skip */ }\n }\n } catch { /* base dir not readable */ }\n\n return snapshot;\n}\n\n/**\n * Detect new Claude Code sessions created since a snapshot.\n * Returns conversation IDs in the format \"claude-{sessionId}\".\n */\nexport function detectNewSessions(beforeSnapshot: Map<string, number>): string[] {\n const afterSnapshot = snapshotClaudeSessions();\n const newSessionIds: string[] = [];\n\n for (const [filePath, mtime] of afterSnapshot) {\n const beforeMtime = beforeSnapshot.get(filePath);\n // New file (didn't exist before) or modified file (mtime changed)\n if (beforeMtime === undefined || mtime > beforeMtime) {\n // Extract sessionId from path: .../projects/{projDir}/{sessionId}.jsonl\n const fileName = filePath.split('/').pop() ?? '';\n const sessionId = fileName.replace('.jsonl', '');\n if (sessionId) {\n newSessionIds.push(`claude-${sessionId}`);\n }\n }\n }\n\n return newSessionIds;\n}\n\n/**\n * Invoke with fallback and automatically tag any new Claude Code sessions\n * with the specified lore phase.\n */\nexport async function invokeWithFallbackAndTag(\n opts: CliInvokeOptions,\n label: string,\n phase: LorePhase,\n runId?: string,\n): Promise<string> {\n const before = snapshotClaudeSessions();\n // Ensure the phase is threaded into opts so invokeCliOnce\n // can build the correct `[lore:<phase>]` marker.\n const result = await invokeWithFallback({ ...opts, phase: opts.phase ?? phase }, label);\n\n // Detect and tag new sessions created during this invocation\n try {\n const newIds = detectNewSessions(before);\n if (newIds.length > 0) {\n tagConversations(newIds, phase, runId);\n logger.debug(`[phase-tag] Tagged ${newIds.length} session(s) as '${phase}' for ${label}${runId ? ` (run: ${runId})` : ''}`);\n }\n } catch (err) {\n logger.debug(`[phase-tag] Failed to tag sessions for ${label}: ${err}`);\n }\n\n return result;\n}\n","/**\n * Generic conversation ignore filter.\n *\n * Provides a data-driven mechanism for filtering conversations based on\n * configurable patterns. Consumers build a `ConversationIgnoreMap` with\n * their specific rules, then call `shouldIgnoreConversation()` to check\n * individual conversations against those rules.\n *\n * This decouples the filtering logic from any specific use-case (e.g. lore\n * self-conversations) and makes patterns composable and configurable.\n */\n\n// ── Types ────────────────────────────────────────────────────────\n\n/**\n * A single content-based ignore pattern.\n * Tested against conversation title and/or first user message.\n */\nexport interface IgnorePattern {\n /** Human-readable label for logging and diagnostics */\n label: string;\n /** Returns true if the given text matches this ignore pattern */\n test: (text: string) => boolean;\n}\n\n/**\n * A map of rules that determine which conversations to ignore.\n *\n * Combine explicit ID sets with content-based patterns for layered\n * detection. Build one via `buildIgnoreMap()` or construct manually,\n * then pass to `shouldIgnoreConversation()`.\n */\nexport interface ConversationIgnoreMap {\n /** Explicit conversation IDs to always ignore */\n ids: Set<string>;\n /** Content patterns tested against title and/or first user message */\n patterns: IgnorePattern[];\n}\n\n// ── Public API ───────────────────────────────────────────────────\n\n/**\n * Check whether a conversation should be ignored based on the provided rules.\n *\n * Evaluation order:\n * 1. Explicit ID match (highest confidence, O(1) lookup)\n * 2. Content patterns against title\n * 3. Content patterns against first user message\n *\n * Returns true if the conversation matches any rule and should be excluded.\n */\nexport function shouldIgnoreConversation(\n id: string,\n map: ConversationIgnoreMap,\n title?: string | null,\n firstUserMessage?: string | null,\n): boolean {\n // Layer 1: explicit ID\n if (map.ids.has(id)) return true;\n\n // Layer 2+3: content patterns against title and first message\n for (const pattern of map.patterns) {\n if (title && pattern.test(title)) return true;\n if (firstUserMessage && pattern.test(firstUserMessage)) return true;\n }\n\n return false;\n}\n\n/**\n * Find the first matching ignore rule for diagnostics/logging.\n * Returns the pattern label or `'id'` if matched by explicit ID,\n * or `null` if the conversation is not ignored.\n */\nexport function matchedIgnoreRule(\n id: string,\n map: ConversationIgnoreMap,\n title?: string | null,\n firstUserMessage?: string | null,\n): string | null {\n if (map.ids.has(id)) return 'id';\n\n for (const pattern of map.patterns) {\n if (title && pattern.test(title)) return pattern.label;\n if (firstUserMessage && pattern.test(firstUserMessage)) return pattern.label;\n }\n\n return null;\n}\n\n/**\n * Convenience builder: create a `ConversationIgnoreMap` from explicit IDs\n * and string-prefix patterns.\n */\nexport function buildIgnoreMap(\n ids: Set<string>,\n prefixPatterns: Array<{ label: string; prefix: string }>,\n): ConversationIgnoreMap {\n return {\n ids,\n patterns: prefixPatterns.map(({ label, prefix }) => ({\n label,\n test: (text: string) => text.trimStart().startsWith(prefix),\n })),\n };\n}\n","/**\n * Lore-specific conversation ignore rules.\n *\n * Builds a `ConversationIgnoreMap` with patterns that identify conversations\n * created by lore's own pipeline (classify, distill, skill-gen, vet,\n * summarize, cluster, recall).\n *\n * User conversations in the lore repo are intentionally **not** matched —\n * filtering is by conversation origin, not repo name.\n */\n\nimport { LORE_MARKER_PREFIX, LORE_PROCESS_MARKER } from './cli.js';\nimport { getTaggedConversationIds } from '../state/phase-store.js';\nimport type { ConversationIgnoreMap } from './conversation-filter.js';\n\n// ── Lore prompt tag detection (legacy) ───────────────────────────\n\n/**\n * Lore prompt tag prefixes for **legacy** conversations created before\n * the `[lore:<phase>]` marker was introduced.\n *\n * - `<role>` — recall path (no splitPrompt)\n * - `<context>` — dominant user-portion tag after splitPrompt\n * - `<research_findings>` — user-portion tag from the research prompt\n *\n * Tags we intentionally do NOT match:\n * `<tools>`, `<mechanical_hints>`, `<local-command-caveat>`,\n * `<command-message>`, `<ide_opened_file>`\n * — these overlap with Claude Code / Cursor IDE injected tags.\n */\nconst LEGACY_PROMPT_PREFIXES = ['<role>', '<context>', '<research_findings>'] as const;\n\nfunction startsWithLegacyLoreTag(text: string): boolean {\n const trimmed = text.trimStart();\n return LEGACY_PROMPT_PREFIXES.some((prefix) => trimmed.startsWith(prefix));\n}\n\n// ── Public API ───────────────────────────────────────────────────\n\n/**\n * Build the lore-specific conversation ignore map.\n *\n * Four detection layers (in evaluation order):\n * 1. Phase-tagged IDs from `~/.lore/conversation-phases.json`\n * — highest confidence, O(1) lookup\n * 2. `[lore:<phase>]` prefix (e.g. `[lore:classify]`, `[lore:distill]`)\n * — deterministic marker for all new conversations, becomes the\n * conversation title across all vendors. 100% reliable.\n * 3. `<!-- lore:process -->` legacy marker\n * — backward compat for conversations created between the marker\n * introduction and the phase-specific prefix migration.\n * 4. Legacy prompt-tag prefixes (`<role>`, `<context>`, `<research_findings>`)\n * — catches oldest historical conversations from before any marker.\n */\nexport function buildLoreIgnoreMap(): ConversationIgnoreMap {\n return {\n ids: getTaggedConversationIds(),\n patterns: [\n {\n label: 'lore-phase-marker',\n test: (text: string) => text.trimStart().startsWith(LORE_MARKER_PREFIX),\n },\n {\n label: 'lore-process-marker-legacy',\n test: (text: string) => text.trimStart().startsWith(LORE_PROCESS_MARKER),\n },\n {\n label: 'lore-prompt-tag-legacy',\n test: startsWithLegacyLoreTag,\n },\n ],\n };\n}\n\n// ── Legacy helpers (kept for diagnostics / backward compat) ──────\n\n/**\n * Patterns that identify lore-related repos/paths/content.\n *\n * @deprecated Use `buildLoreIgnoreMap()` + `shouldIgnoreConversation()` for\n * conversation filtering. These patterns are only useful for logging/UI labels.\n */\nconst LORE_PATTERNS = [\n /\\blore\\b/i,\n /\\blore-cli\\b/i,\n];\n\n/**\n * Returns true if the given string matches a lore-related pattern.\n *\n * @deprecated Use `shouldIgnoreConversation()` with `buildLoreIgnoreMap()`.\n */\nexport function isLoreRelated(value: string | null | undefined): boolean {\n if (!value) return false;\n return LORE_PATTERNS.some((pattern) => pattern.test(value));\n}\n\n/**\n * Returns true if any of the provided values match a lore-related pattern.\n *\n * @deprecated Use `shouldIgnoreConversation()` with `buildLoreIgnoreMap()`.\n */\nexport function isLoreRelatedAny(...values: Array<string | null | undefined>): boolean {\n return values.some((v) => isLoreRelated(v));\n}\n","import { readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type LoreState, loreStateSchema, defaultState } from './schema.js';\n\nexport class StateStore {\n private statePath: string;\n\n constructor(loreDir: string) {\n this.statePath = join(loreDir, 'state.json');\n }\n\n read(): LoreState {\n if (!existsSync(this.statePath)) {\n return { ...defaultState };\n }\n try {\n const raw = readFileSync(this.statePath, 'utf-8');\n const parsed = JSON.parse(raw);\n return loreStateSchema.parse(parsed);\n } catch {\n return { ...defaultState };\n }\n }\n\n write(state: LoreState): void {\n const validated = loreStateSchema.parse(state);\n writeFileSync(this.statePath, JSON.stringify(validated, null, 2) + '\\n', 'utf-8');\n }\n\n update(partial: Partial<LoreState>): LoreState {\n const current = this.read();\n const updated = { ...current, ...partial };\n this.write(updated);\n return updated;\n }\n}\n","import { readFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { loreConfigSchema, type LoreConfig } from './schema.js';\nimport { defaultConfig } from './defaults.js';\nimport { logger } from '../utils/logger.js';\n\n/**\n * Load and validate the Lore configuration from .lore/config.json.\n * Falls back to defaults if the file is missing or invalid.\n */\nexport function loadConfig(loreDir: string): LoreConfig {\n const configPath = resolve(loreDir, 'config.json');\n\n if (!existsSync(configPath)) {\n logger.debug('Config file not found, using defaults');\n return { ...defaultConfig };\n }\n\n try {\n const raw = readFileSync(configPath, 'utf-8');\n const parsed = JSON.parse(raw);\n return loreConfigSchema.parse(parsed);\n } catch (err) {\n logger.warn('Failed to parse config, using defaults:', err);\n return { ...defaultConfig };\n }\n}\n","/**\n * Shared Cursor SQLite database access.\n *\n * Provides openCursorDb() which tries better-sqlite3 (sync, fast, for CLI)\n * and falls back to @vscode/sqlite3 (async, Electron-compatible, for extension).\n *\n * This module replaces the duplicate openDb() implementations that previously\n * lived in src/data-sources/cursor.ts and src/ui/api/db.ts.\n */\n\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { logger } from '../utils/logger.js';\nimport type { SqlDatabase, SqlValue } from './types.js';\n\n// ── Path helper ──────────────────────────────────────────────────\n\nexport function getDbPath(): string {\n return join(\n homedir(),\n 'Library',\n 'Application Support',\n 'Cursor',\n 'User',\n 'globalStorage',\n 'state.vscdb',\n );\n}\n\n// ── Database opener with fallback ────────────────────────────────\n\n/**\n * Open the Cursor global SQLite database.\n *\n * Strategy:\n * 1. Try better-sqlite3 (synchronous, fast) — works in Node.js CLI\n * 2. Fall back to @vscode/sqlite3 (async, N-API) — works in Electron/VS Code\n * 3. Return null if both fail\n */\nexport async function openCursorDb(diagnostics?: string[]): Promise<SqlDatabase | null> {\n const diag = (msg: string) => {\n diagnostics?.push(msg);\n logger.debug(msg);\n };\n\n const dbPath = getDbPath();\n if (!existsSync(dbPath)) {\n diag(`[cursor-db] DB path does not exist: ${dbPath}`);\n return null;\n }\n\n diag(`[cursor-db] DB path exists: ${dbPath}`);\n\n // Strategy 1: Try better-sqlite3 (CLI path — fast, synchronous)\n try {\n diag('[cursor-db] Trying better-sqlite3...');\n const mod = await import('better-sqlite3');\n const Database = mod.default;\n const nativeDb = new Database(dbPath, { readonly: true, fileMustExist: true });\n\n diag('[cursor-db] ✓ Opened with better-sqlite3');\n\n const wrapped: SqlDatabase = {\n async exec(\n sql: string,\n params?: SqlValue[],\n ): Promise<Array<{ columns: string[]; values: unknown[][] }>> {\n try {\n const stmt = nativeDb.prepare(sql);\n const columns = stmt.columns().map((col: { name: string }) => col.name);\n const rows = params ? stmt.raw().all(...params) : stmt.raw().all();\n if (rows.length === 0 && columns.length === 0) return [];\n return [{ columns, values: rows as unknown[][] }];\n } catch {\n return [];\n }\n },\n close() {\n try {\n nativeDb.close();\n } catch {\n /* already closed */\n }\n },\n };\n\n return wrapped;\n } catch (err) {\n diag(\n `[cursor-db] ✗ better-sqlite3 failed: ${err instanceof Error ? err.message : err}`,\n );\n }\n\n // Strategy 2: Fall back to @vscode/sqlite3 (Extension path — async, Electron-compatible)\n try {\n diag('[cursor-db] Trying @vscode/sqlite3 fallback...');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mod: any = await import('@vscode/sqlite3');\n const sqlite3 = mod.default ?? mod;\n\n return new Promise<SqlDatabase | null>((resolve) => {\n const db = new sqlite3.Database(\n dbPath,\n sqlite3.OPEN_READONLY,\n (err: Error | null) => {\n if (err) {\n diag(`[cursor-db] ✗ @vscode/sqlite3 failed to open: ${err.message}`);\n resolve(null);\n return;\n }\n\n diag('[cursor-db] ✓ Opened with @vscode/sqlite3');\n\n const wrapped: SqlDatabase = {\n async exec(\n sql: string,\n params?: SqlValue[],\n ): Promise<Array<{ columns: string[]; values: unknown[][] }>> {\n return new Promise((res) => {\n db.all(\n sql,\n params ?? [],\n (err2: Error | null, rows: Record<string, unknown>[]) => {\n if (err2 || !rows || rows.length === 0) {\n res([]);\n return;\n }\n const columns = Object.keys(rows[0]);\n const values = rows.map((row) => columns.map((col) => row[col]));\n res([{ columns, values }]);\n },\n );\n });\n },\n close() {\n try {\n db.close();\n } catch {\n /* already closed */\n }\n },\n };\n\n resolve(wrapped);\n },\n );\n });\n } catch (err) {\n diag(\n `[cursor-db] ✗ @vscode/sqlite3 import failed: ${err instanceof Error ? err.message : err}`,\n );\n }\n\n diag('[cursor-db] ✗ All backends failed');\n return null;\n}\n\n// ── Query helper ─────────────────────────────────────────────────\n\n/**\n * Execute a SQL query and return typed row objects.\n */\nexport async function queryRows<T extends Record<string, unknown>>(\n db: SqlDatabase,\n sql: string,\n params?: SqlValue[],\n): Promise<T[]> {\n const result = await db.exec(sql, params);\n if (result.length === 0) return [];\n\n const { columns, values } = result[0];\n return values.map((row) => {\n const obj: Record<string, unknown> = {};\n columns.forEach((col, i) => {\n obj[col] = row[i];\n });\n return obj as T;\n });\n}\n","import { logger } from '../utils/logger.js';\nimport { openCursorDb } from '../data-sources/cursor-db.js';\nimport type { SqlDatabase } from '../data-sources/types.js';\nimport type { Scanner, ScanResult, ScanItem } from './types.js';\n\n/**\n * Cursor stores conversations in a global SQLite database:\n * ~/Library/Application Support/Cursor/User/globalStorage/state.vscdb\n *\n * Table: cursorDiskKV\n * - composerData:{composerId} -- session metadata + ordered list of bubbleIds\n * - bubbleId:{composerId}:{messageId} -- individual messages\n * - type 1 = user message (text in $.text and $.richText)\n * - type 2 = assistant message (text in $.text)\n */\n\ninterface ComposerData {\n _v: number;\n composerId: string;\n createdAt?: number;\n lastUpdatedAt?: number;\n unifiedMode?: string; // \"agent\" | \"chat\" | \"edit\"\n name?: string;\n text?: string;\n fullConversationHeadersOnly: Array<{\n bubbleId: string;\n type: number; // 1 = user, 2 = assistant\n }>;\n}\n\ninterface BubbleData {\n _v: number;\n type: number; // 1 = user, 2 = assistant\n bubbleId: string;\n text: string;\n richText?: string;\n isAgentic?: boolean;\n toolResults?: unknown[];\n attachedFileCodeChunksMetadataOnly?: Array<{\n relativeWorkspacePath: string;\n }>;\n}\n\nexport class CursorScanner implements Scanner {\n name = 'cursor' as const;\n\n async scan(since: string | null, until?: string | null): Promise<ScanResult> {\n const items: ScanItem[] = [];\n\n const db = await openCursorDb();\n if (!db) {\n logger.debug('Cursor global database not available');\n return { source: 'cursor', timestamp: new Date(), items: [] };\n }\n\n try {\n\n // 1. Read all composer sessions from cursorDiskKV\n const composerResult = await db.exec(\n \"SELECT key, value FROM cursorDiskKV WHERE key LIKE 'composerData:%'\",\n );\n\n if (composerResult.length === 0 || composerResult[0].values.length === 0) {\n logger.debug('No Cursor composer data found');\n db.close();\n return { source: 'cursor', timestamp: new Date(), items: [] };\n }\n\n const sinceMs = since ? new Date(since).getTime() : 0;\n const untilMs = until ? new Date(until).getTime() : Infinity;\n\n let totalParsed = 0;\n let skippedTask = 0;\n let skippedTime = 0;\n let skippedNoHeaders = 0;\n let skippedNoTurns = 0;\n\n logger.debug(`Cursor scanner: since=${since ?? 'null'} (${sinceMs}ms), total composerData entries: ${composerResult[0].values.length}`);\n\n for (const row of composerResult[0].values) {\n const rawValue = row[1];\n if (typeof rawValue !== 'string') continue;\n\n let composer: ComposerData;\n try {\n composer = JSON.parse(rawValue);\n } catch {\n continue;\n }\n totalParsed++;\n\n // Skip task sub-agents (they're part of larger sessions)\n if (composer.composerId.startsWith('task-')) { skippedTask++; continue; }\n\n // Filter by time window — use lastUpdatedAt (when a message was last\n // added) rather than createdAt so that continuing an existing\n // conversation after the last scan is still picked up.\n let activityAt = composer.lastUpdatedAt ?? composer.createdAt ?? 0;\n\n // Cursor may store timestamps in seconds or ms — normalise to ms\n if (activityAt > 0 && activityAt < 1e12) {\n activityAt = activityAt * 1000;\n }\n\n if (sinceMs > 0 && activityAt <= sinceMs) { skippedTime++; continue; }\n if (untilMs < Infinity && activityAt > untilMs) { skippedTime++; continue; }\n\n let createdAt = composer.createdAt ?? 0;\n // Normalise seconds → milliseconds\n if (createdAt > 0 && createdAt < 1e12) {\n createdAt = createdAt * 1000;\n }\n\n // Skip sessions with no messages\n const headers = composer.fullConversationHeadersOnly;\n if (!headers || headers.length === 0) { skippedNoHeaders++; continue; }\n\n // 2. Read bubble messages for this composer\n const turns = await this.readBubbles(db, composer.composerId, headers);\n if (turns.length === 0) { skippedNoTurns++; continue; }\n\n const title = this.extractTitle(turns, composer);\n const content = this.formatConversation(turns, composer);\n\n items.push({\n type: 'conversation',\n id: `cursor-${composer.composerId}`,\n title,\n content,\n metadata: {\n source: 'cursor',\n composerId: composer.composerId,\n mode: composer.unifiedMode ?? 'unknown',\n createdAt: createdAt ? new Date(createdAt).toISOString() : null,\n messageCount: turns.length,\n },\n });\n }\n\n logger.debug(\n `Cursor scanner: parsed=${totalParsed}, skippedTask=${skippedTask}, ` +\n `skippedTime=${skippedTime}, skippedNoHeaders=${skippedNoHeaders}, ` +\n `skippedNoTurns=${skippedNoTurns}, found=${items.length}`,\n );\n\n db.close();\n logger.debug(`Cursor scanner: found ${items.length} conversations`);\n } catch (err) {\n logger.error('Cursor scanner error:', err);\n }\n\n return { source: 'cursor', timestamp: new Date(), items };\n }\n\n private async readBubbles(\n db: SqlDatabase,\n composerId: string,\n headers: Array<{ bubbleId: string; type: number }>,\n ): Promise<Array<{ role: 'user' | 'assistant'; text: string; files: string[] }>> {\n const turns: Array<{ role: 'user' | 'assistant'; text: string; files: string[] }> = [];\n\n for (const header of headers) {\n const key = `bubbleId:${composerId}:${header.bubbleId}`;\n\n try {\n const result = await db.exec('SELECT value FROM cursorDiskKV WHERE key = ?', [key]);\n if (result.length === 0 || result[0].values.length === 0) continue;\n\n const rawValue = result[0].values[0][0];\n if (typeof rawValue !== 'string') continue;\n\n const bubble: BubbleData = JSON.parse(rawValue);\n const role: 'user' | 'assistant' = bubble.type === 1 ? 'user' : 'assistant';\n const text = bubble.text ?? '';\n\n // Extract file references from attached files\n const files: string[] = [];\n if (bubble.attachedFileCodeChunksMetadataOnly) {\n for (const chunk of bubble.attachedFileCodeChunksMetadataOnly) {\n if (chunk.relativeWorkspacePath) {\n files.push(chunk.relativeWorkspacePath);\n }\n }\n }\n\n if (text.trim()) {\n turns.push({ role, text: text.trim(), files });\n }\n } catch {\n // Skip unreadable bubbles\n }\n }\n\n return turns;\n }\n\n private extractTitle(\n turns: Array<{ role: string; text: string }>,\n composer: ComposerData,\n ): string {\n // Use the composer name if available\n if (composer.name) return composer.name;\n\n // Otherwise use the first user message\n const firstUser = turns.find((t) => t.role === 'user');\n if (firstUser?.text) {\n const firstLine = firstUser.text.split('\\n')[0];\n return firstLine.length > 80 ? firstLine.slice(0, 77) + '...' : firstLine;\n }\n\n return `Cursor session ${composer.composerId.slice(0, 8)}`;\n }\n\n private formatConversation(\n turns: Array<{ role: string; text: string; files: string[] }>,\n composer: ComposerData,\n ): string {\n const parts: string[] = [\n `Cursor Session: ${composer.composerId}`,\n `Mode: ${composer.unifiedMode ?? 'unknown'}`,\n `Created: ${composer.createdAt ? new Date(composer.createdAt).toISOString() : 'unknown'}`,\n `Messages: ${turns.length}`,\n '',\n '--- Conversation ---',\n '',\n ];\n\n for (const turn of turns) {\n parts.push(`[${turn.role}]: ${turn.text}`);\n if (turn.files.length > 0) {\n parts.push(` [files: ${turn.files.join(', ')}]`);\n }\n parts.push('');\n }\n\n return parts.join('\\n');\n }\n}\n","import { existsSync, readdirSync, statSync } from 'node:fs';\nimport { createReadStream } from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport { join } from 'node:path';\nimport { getClaudeCodeBaseDir, decodeClaudeProjectPath } from '../utils/paths.js';\nimport { logger } from '../utils/logger.js';\nimport { shouldIgnoreConversation } from '../utils/conversation-filter.js';\nimport { buildLoreIgnoreMap } from '../utils/lore-filter.js';\nimport type {\n Scanner,\n ScanResult,\n ScanItem,\n ConversationSession,\n ConversationTurn,\n ToolUseEntry,\n} from './types.js';\n\ninterface ClaudeMessage {\n type: string;\n uuid: string;\n parentUuid: string | null;\n sessionId: string;\n timestamp: string;\n cwd?: string;\n gitBranch?: string;\n version?: string;\n message?: {\n role: string;\n content: string | ContentBlock[];\n model?: string;\n };\n toolUseResult?: string;\n}\n\ninterface ContentBlock {\n type: string;\n text?: string;\n id?: string;\n name?: string;\n input?: Record<string, unknown>;\n content?: string;\n is_error?: boolean;\n tool_use_id?: string;\n}\n\nexport class ClaudeCodeScanner implements Scanner {\n name = 'claude-code' as const;\n private lastSessions: ConversationSession[] = [];\n\n /**\n * Get the full ConversationSession objects from the last scan.\n * Used by the alignment engine.\n */\n getLastSessions(): ConversationSession[] {\n return this.lastSessions;\n }\n\n async scan(since: string | null, until?: string | null): Promise<ScanResult> {\n const items: ScanItem[] = [];\n this.lastSessions = [];\n\n const baseDir = getClaudeCodeBaseDir();\n if (!existsSync(baseDir)) {\n logger.debug(`Claude Code projects directory not found: ${baseDir}`);\n return { source: 'claude-code', timestamp: new Date(), items: [] };\n }\n\n try {\n // Scan ALL project directories under ~/.claude/projects/\n const projectDirs = readdirSync(baseDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n\n logger.debug(`Found ${projectDirs.length} Claude Code project directories`);\n\n // Build ignore map to skip lore process conversations at scan time.\n // We no longer skip entire project directories by name — only individual\n // sessions that were created by lore processes (phase tag or prompt marker).\n const ignoreMap = buildLoreIgnoreMap();\n\n for (const projectDirName of projectDirs) {\n const projectDir = join(baseDir, projectDirName);\n const decodedProject = decodeClaudeProjectPath(projectDirName);\n\n const files = readdirSync(projectDir).filter((f) => f.endsWith('.jsonl'));\n\n for (const file of files) {\n const filePath = join(projectDir, file);\n\n // Filter by time window\n const stat = statSync(filePath);\n if (since) {\n const sinceDate = new Date(since);\n if (stat.mtime <= sinceDate) {\n continue;\n }\n }\n if (until) {\n const untilDate = new Date(until);\n if (stat.mtime > untilDate) {\n continue;\n }\n }\n\n const session = await this.parseSession(filePath, file.replace('.jsonl', ''));\n if (session && session.turns.length > 0) {\n const convId = `claude-${session.sessionId}`;\n\n // Skip lore process conversations by origin, not repo name\n const firstUserMsg = session.turns.find((t) => t.role === 'user')?.content;\n if (shouldIgnoreConversation(convId, ignoreMap, undefined, firstUserMsg)) continue;\n\n // Tag session with decoded project path\n session.cwd = session.cwd ?? decodedProject;\n this.lastSessions.push(session);\n\n items.push({\n type: 'conversation',\n id: convId,\n title: this.generateTitle(session),\n content: this.formatSessionContent(session, decodedProject),\n metadata: {\n source: 'claude-code',\n sessionId: session.sessionId,\n branch: session.branch,\n project: decodedProject,\n startedAt: session.startedAt,\n endedAt: session.endedAt,\n filesModified: session.filesModified,\n turnCount: session.turns.length,\n },\n });\n }\n }\n }\n\n logger.debug(`Claude Code scanner: found ${items.length} sessions across ${projectDirs.length} projects`);\n } catch (err) {\n logger.error('Claude Code scanner error:', err);\n }\n\n return { source: 'claude-code', timestamp: new Date(), items };\n }\n\n private async parseSession(filePath: string, sessionId: string): Promise<ConversationSession | null> {\n const turns: ConversationTurn[] = [];\n const filesModified = new Set<string>();\n let branch: string | null = null;\n let cwd: string | null = null;\n let startedAt = '';\n let endedAt = '';\n\n try {\n const rl = createInterface({\n input: createReadStream(filePath, { encoding: 'utf-8' }),\n crlfDelay: Infinity,\n });\n\n for await (const line of rl) {\n if (!line.trim()) continue;\n\n let msg: ClaudeMessage;\n try {\n msg = JSON.parse(line);\n } catch {\n continue;\n }\n\n // Skip non-message types\n if (msg.type === 'file-history-snapshot' || msg.type === 'architecture') {\n continue;\n }\n\n // Track metadata\n if (msg.gitBranch && !branch) {\n branch = msg.gitBranch;\n }\n if (msg.cwd && !cwd) {\n cwd = msg.cwd;\n }\n if (msg.timestamp) {\n if (!startedAt) startedAt = msg.timestamp;\n endedAt = msg.timestamp;\n }\n\n // Parse message content\n if (msg.message) {\n const role = msg.message.role as 'user' | 'assistant';\n const toolUse: ToolUseEntry[] = [];\n\n let text = '';\n if (typeof msg.message.content === 'string') {\n text = msg.message.content;\n } else if (Array.isArray(msg.message.content)) {\n for (const block of msg.message.content) {\n if (block.type === 'text' && block.text) {\n text += block.text + '\\n';\n } else if (block.type === 'tool_use' && block.name) {\n const filesAffected = this.extractFilesFromToolUse(block.name, block.input ?? {});\n for (const f of filesAffected) {\n filesModified.add(f);\n }\n toolUse.push({\n name: block.name,\n input: block.input ?? {},\n filesAffected,\n });\n } else if (block.type === 'tool_result') {\n continue;\n }\n }\n }\n\n if (text.trim() || toolUse.length > 0) {\n turns.push({\n role,\n content: text.trim(),\n timestamp: msg.timestamp,\n toolUse: toolUse.length > 0 ? toolUse : undefined,\n });\n }\n }\n }\n } catch (err) {\n logger.debug(`Failed to parse session file ${filePath}:`, err);\n return null;\n }\n\n if (turns.length === 0) return null;\n\n return {\n sessionId,\n source: 'claude-code',\n startedAt,\n endedAt,\n branch,\n cwd,\n turns,\n filesModified: Array.from(filesModified),\n };\n }\n\n /**\n * Extract file paths from tool_use inputs based on known tool names.\n */\n private extractFilesFromToolUse(\n toolName: string,\n input: Record<string, unknown>,\n ): string[] {\n const files: string[] = [];\n\n switch (toolName) {\n case 'Write':\n case 'Read':\n case 'StrReplace':\n case 'Delete':\n if (typeof input.path === 'string') files.push(input.path);\n break;\n case 'EditNotebook':\n if (typeof input.target_notebook === 'string') files.push(input.target_notebook);\n break;\n case 'Shell':\n case 'Bash':\n if (typeof input.command === 'string') {\n const matches = input.command.match(/(?:cat|vim|nano|code|touch|mkdir|rm|mv|cp)\\s+([^\\s|;]+)/g);\n if (matches) {\n for (const match of matches) {\n const parts = match.split(/\\s+/);\n if (parts.length > 1) {\n files.push(parts[parts.length - 1]);\n }\n }\n }\n }\n break;\n }\n\n return files;\n }\n\n private generateTitle(session: ConversationSession): string {\n const firstUserTurn = session.turns.find((t) => t.role === 'user');\n if (firstUserTurn?.content) {\n const firstLine = firstUserTurn.content.split('\\n')[0];\n return firstLine.length > 80 ? firstLine.slice(0, 77) + '...' : firstLine;\n }\n return `Claude Code session ${session.sessionId.slice(0, 8)}`;\n }\n\n private formatSessionContent(session: ConversationSession, project: string): string {\n const parts: string[] = [\n `Project: ${project}`,\n `Session: ${session.sessionId}`,\n `Branch: ${session.branch ?? 'unknown'}`,\n `Time: ${session.startedAt} - ${session.endedAt}`,\n `Files modified: ${session.filesModified.join(', ') || 'none'}`,\n '',\n '--- Conversation ---',\n '',\n ];\n\n for (const turn of session.turns) {\n parts.push(`[${turn.role}]: ${turn.content}`);\n if (turn.toolUse) {\n for (const tool of turn.toolUse) {\n parts.push(` [tool: ${tool.name}] files: ${tool.filesAffected.join(', ')}`);\n }\n }\n parts.push('');\n }\n\n return parts.join('\\n');\n }\n}\n","import { readFileSync, existsSync, writeFileSync, mkdirSync, unlinkSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { tmpdir } from 'node:os';\nimport { randomUUID } from 'node:crypto';\nimport { getPromptsDir } from '../utils/paths.js';\n\n// In-memory template cache — prompts don't change at runtime.\nconst templateCache = new Map<string, string>();\n\n/**\n * Load a prompt template from the prompts/ directory.\n * Falls back to built-in templates if the file doesn't exist.\n * Results are cached in memory to avoid repeated disk reads.\n */\nexport function loadPrompt(name: string): string {\n const cached = templateCache.get(name);\n if (cached !== undefined) return cached;\n\n const promptsDir = getPromptsDir();\n const filePath = join(promptsDir, `${name}.md`);\n\n let content: string;\n if (existsSync(filePath)) {\n content = readFileSync(filePath, 'utf-8');\n } else {\n content = getBuiltinPrompt(name);\n }\n\n templateCache.set(name, content);\n return content;\n}\n\n// ── Prompt splitting: system vs user ────────────────────────────\n\n/**\n * XML tags whose content is behavioral / instructional.\n * These are extracted into the system prompt via --append-system-prompt-file.\n */\nconst SYSTEM_TAGS = new Set([\n 'role',\n 'task',\n 'instructions',\n 'output_format',\n 'rules',\n 'progress_reporting',\n]);\n\nexport interface PromptSplit {\n /** Behavioral directives (<role>, <task>, <instructions>, <output_format>, <rules>) */\n systemPrompt: string;\n /** Data-bearing content (<context>, <repos>, <conversations>, etc.) */\n userPrompt: string;\n}\n\n/**\n * Split a fully-substituted prompt into system and user portions.\n *\n * System tags become part of the system prompt appended via\n * --append-system-prompt-file. All other XML-tagged sections\n * (data-bearing tags like <context>, <repos>, etc.) remain as\n * user input piped via stdin.\n *\n * If the prompt has no recognisable XML tags, the entire content\n * is returned as userPrompt (safe fallback).\n */\nexport function splitPrompt(fullPrompt: string): PromptSplit {\n const systemParts: string[] = [];\n const userParts: string[] = [];\n\n // Match top-level XML-style blocks: <tag>...</tag> (multi-line)\n const tagPattern = /<(\\w+)>([\\s\\S]*?)<\\/\\1>/g;\n\n let lastIdx = 0;\n let match: RegExpExecArray | null;\n\n while ((match = tagPattern.exec(fullPrompt)) !== null) {\n const tagName = match[1];\n const fullMatch = match[0];\n const beforeText = fullPrompt.slice(lastIdx, match.index).trim();\n\n // Inter-tag text (whitespace, separators) goes to system by default\n if (beforeText) {\n systemParts.push(beforeText);\n }\n\n if (SYSTEM_TAGS.has(tagName)) {\n systemParts.push(fullMatch);\n } else {\n userParts.push(fullMatch);\n }\n\n lastIdx = match.index + fullMatch.length;\n }\n\n // Any trailing text after the last tag\n const trailing = fullPrompt.slice(lastIdx).trim();\n if (trailing) {\n systemParts.push(trailing);\n }\n\n // If no tags were found, treat everything as user input (safe fallback)\n if (systemParts.length === 0 && userParts.length === 0) {\n return { systemPrompt: '', userPrompt: fullPrompt };\n }\n\n return {\n systemPrompt: systemParts.join('\\n\\n'),\n userPrompt: userParts.join('\\n\\n'),\n };\n}\n\n// ── System prompt temp file ─────────────────────────────────────\n\n/**\n * Write a system prompt to a temporary file for --append-system-prompt-file.\n * Returns the file path and a cleanup function (follows mcp-config pattern).\n */\nexport function createSystemPromptFile(content: string): { path: string; cleanup: () => void } {\n const tmpDir = join(tmpdir(), 'lore-prompts');\n mkdirSync(tmpDir, { recursive: true });\n\n const filePath = join(tmpDir, `system-${randomUUID()}.txt`);\n writeFileSync(filePath, content, 'utf-8');\n\n const cleanup = () => {\n try {\n if (existsSync(filePath)) {\n unlinkSync(filePath);\n }\n } catch {\n // Cleanup is best-effort\n }\n };\n\n return { path: filePath, cleanup };\n}\n\n// ── Built-in prompts ────────────────────────────────────────────\n\nfunction getBuiltinPrompt(name: string): string {\n const prompts: Record<string, string> = {\n 'distill-changes': DISTILL_CHANGES_PROMPT,\n 'distill-decisions': DISTILL_DECISIONS_PROMPT,\n 'distill-patterns': DISTILL_PATTERNS_PROMPT,\n 'detect-staleness': DETECT_STALENESS_PROMPT,\n 'generate-changelog': GENERATE_CHANGELOG_PROMPT,\n };\n\n return prompts[name] ?? `<task>Analyze the following data and provide insights.</task>\\n\\n`;\n}\n\nconst DISTILL_CHANGES_PROMPT = `<role>\nYou are a technical documentation assistant that analyzes development activity and extracts structured information about what changed and why.\n</role>\n\n<task>\nAnalyze the development activity provided below and produce a comprehensive structured summary of changes, decisions, patterns, stale documentation, and changelog entries.\n</task>\n\n<context>\n{context}\n</context>\n\n<instructions>\n- For matched pairs (commit + conversation), extract both WHAT changed and WHY. The conversation is the richest source of intent.\n- For unmatched commits, describe WHAT changed based on the diff and commit message.\n- For unmatched conversations, extract decisions, research findings, and architectural discussions even if no code was committed.\n- Prioritize substance over volume.\n</instructions>\n\n<output_format>\n{\n \"changes\": [{ \"summary\": \"...\", \"details\": \"...\", \"files\": [...], \"type\": \"feature|bugfix|refactor|docs|chore\" }],\n \"decisions\": [{ \"title\": \"...\", \"context\": \"...\", \"decision\": \"...\", \"alternatives\": [...] }],\n \"patterns\": [{ \"name\": \"...\", \"description\": \"...\", \"examples\": [...] }],\n \"staleDocs\": [{ \"file\": \"...\", \"reason\": \"...\", \"suggestedUpdate\": \"...\" }],\n \"changelog\": \"keep-a-changelog formatted entries\"\n}\n</output_format>\n\n<rules>\n- Respond ONLY with valid JSON matching the schema above\n- Do not wrap the response in markdown code fences\n- If no items exist for a category, use an empty array\n</rules>`;\n\nconst DISTILL_DECISIONS_PROMPT = `<role>\nYou are a technical documentation assistant specializing in Architectural Decision Records (ADRs).\n</role>\n\n<task>\nAnalyze the development conversations below and extract architectural decisions that were made.\n</task>\n\n<context>\n{context}\n</context>\n\n<instructions>\nFocus on technology choices, architecture patterns, data model decisions, API design, performance tradeoffs, and security considerations.\nFor each decision, capture: title, context, decision, consequences, and alternatives.\n</instructions>\n\n<output_format>\n[{ \"title\": \"...\", \"context\": \"...\", \"decision\": \"...\", \"consequences\": \"...\", \"alternatives\": [...] }]\n</output_format>\n\n<rules>\n- Respond ONLY with a valid JSON array\n- Do not wrap the response in markdown code fences\n- If no decisions are found, return an empty array: []\n</rules>`;\n\nconst DISTILL_PATTERNS_PROMPT = `<role>\nYou are a technical documentation assistant that identifies recurring engineering patterns and conventions.\n</role>\n\n<task>\nAnalyze the codebase activity below and identify patterns and conventions being established.\n</task>\n\n<context>\n{context}\n</context>\n\n<instructions>\nLook for: architectural patterns, coding conventions, dependency usage patterns, testing patterns, error handling approaches, and state management patterns.\nOnly report patterns clearly present in the evidence.\n</instructions>\n\n<output_format>\n[{ \"name\": \"...\", \"description\": \"...\", \"examples\": [...] }]\n</output_format>\n\n<rules>\n- Respond ONLY with a valid JSON array\n- Do not wrap the response in markdown code fences\n- If no patterns are found, return an empty array: []\n</rules>`;\n\nconst DETECT_STALENESS_PROMPT = `<role>\nYou are a documentation freshness checker.\n</role>\n\n<task>\nCompare the existing documentation against recent development activity to find stale, outdated, or missing documentation.\n</task>\n\n<existing_docs>\n{docs}\n</existing_docs>\n\n<activity_data>\n{activity}\n</activity_data>\n\n<instructions>\nCheck for: documentation referencing changed code/APIs, missing docs for new features, outdated architecture docs, stale dependency references, and incorrect configuration examples.\n</instructions>\n\n<output_format>\n[{ \"file\": \"...\", \"reason\": \"...\", \"suggestedUpdate\": \"...\" }]\n</output_format>\n\n<rules>\n- Respond ONLY with a valid JSON array\n- Do not wrap the response in markdown code fences\n- If no staleness is detected, return an empty array: []\n</rules>`;\n\nconst GENERATE_CHANGELOG_PROMPT = `<role>\nYou are a changelog writer following the Keep a Changelog format.\n</role>\n\n<task>\nGenerate clean, user-facing changelog entries from the structured changes below.\n</task>\n\n<context>\n{context}\n</context>\n\n<instructions>\n- Use only categories that have entries: Added, Changed, Fixed, Removed\n- Each entry starts with \"- \" and is a single concise line\n- Describe user-visible impact, not implementation details\n- Use present tense (\"Add X\" not \"Added X\")\n</instructions>\n\n<output_format>\n### Added\n- entry\n\n### Changed\n- entry\n\n### Fixed\n- entry\n</output_format>\n\n<rules>\n- Respond with ONLY the formatted changelog text\n- No JSON wrapping, no markdown code fences, no preamble\n- Omit empty categories\n</rules>`;\n","import { writeFileSync, mkdirSync, unlinkSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { tmpdir } from 'node:os';\nimport { randomUUID } from 'node:crypto';\n\nexport interface McpServerConfig {\n command: string;\n args: string[];\n env?: Record<string, string>;\n}\n\nexport interface McpConfig {\n mcpServers: Record<string, McpServerConfig>;\n}\n\n/**\n * Generate a temporary MCP configuration file for Claude Code CLI.\n * Returns the path to the generated file and a cleanup function.\n */\nexport function createMcpConfigFile(): { path: string; cleanup: () => void } {\n const config: McpConfig = {\n mcpServers: {\n octocode: {\n command: 'npx',\n args: ['octocode-mcp@latest'],\n },\n },\n };\n\n const tmpDir = join(tmpdir(), 'lore-mcp');\n mkdirSync(tmpDir, { recursive: true });\n\n const configPath = join(tmpDir, `mcp-${randomUUID()}.json`);\n writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');\n\n const cleanup = () => {\n try {\n if (existsSync(configPath)) {\n unlinkSync(configPath);\n }\n } catch {\n // Cleanup is best-effort\n }\n };\n\n return { path: configPath, cleanup };\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { logger } from '../utils/logger.js';\n\n/**\n * Token counting and context window management for distillation.\n *\n * - Uses @anthropic-ai/tokenizer for accurate Claude token counts\n * - Detects the active model from Claude Code settings for dynamic limits\n * - Falls back gracefully at every layer\n */\n\n// ── Model context window sizes ─────────────────────────────────────\n// Source: https://docs.anthropic.com/en/docs/build-with-claude/context-windows\n//\n// All current Claude models have a 200k default context window.\n// The 1M context beta header (\"context-1m-2025-08-07\") is available\n// for some models but Lore uses the CLI, so 200k is the safe default.\n\n/** Default context window size for all Claude models. */\nexport const DEFAULT_MODEL_LIMIT = 200_000;\n\nexport const MODEL_LIMITS: Record<string, number> = {\n // Claude 4.x family\n 'claude-opus-4-6': 200_000,\n 'claude-opus-4-5': 200_000,\n 'claude-sonnet-4-5': 200_000,\n 'claude-sonnet-4': 200_000,\n // Claude 3.x family\n 'claude-3-5-sonnet': 200_000,\n 'claude-3-5-haiku': 200_000,\n 'claude-3-opus': 200_000,\n 'claude-3-sonnet': 200_000,\n 'claude-3-haiku': 200_000,\n // Aliases (what users pass to --model)\n 'opus': 200_000,\n 'sonnet': 200_000,\n 'haiku': 200_000,\n // Default fallback\n 'default': 200_000,\n};\n\n/** Tokens reserved for the response output (subtracted from model limit). */\nexport const RESPONSE_RESERVE = 8_192;\n\n// ── Anthropic tokenizer ────────────────────────────────────────────\n// Uses the official @anthropic-ai/tokenizer for Claude-accurate counts,\n// with a js-tiktoken fallback, and a char-estimation last resort.\n\nlet countTokensFn: ((text: string) => number) | null = null;\nlet tokenizerSource = 'unknown';\n\nasync function initTokenizer(): Promise<(text: string) => number> {\n if (countTokensFn) return countTokensFn;\n\n // Attempt 1: @anthropic-ai/tokenizer (official, Claude-accurate)\n try {\n const { countTokens: anthropicCount } = await import('@anthropic-ai/tokenizer');\n countTokensFn = anthropicCount;\n tokenizerSource = '@anthropic-ai/tokenizer';\n logger.debug('Tokenizer: using @anthropic-ai/tokenizer (Claude-accurate)');\n return countTokensFn;\n } catch {\n logger.debug('@anthropic-ai/tokenizer not available, trying js-tiktoken');\n }\n\n // Attempt 2: js-tiktoken (WASM, OpenAI encoding, approximate for Claude)\n try {\n const { encodingForModel } = await import('js-tiktoken');\n const encoder = encodingForModel('gpt-4o');\n countTokensFn = (text: string) => encoder.encode(text).length;\n tokenizerSource = 'js-tiktoken (approximate)';\n logger.debug('Tokenizer: using js-tiktoken (approximate for Claude)');\n return countTokensFn;\n } catch {\n logger.debug('js-tiktoken not available, falling back to char estimation');\n }\n\n // Attempt 3: character estimation (~4 chars per token)\n countTokensFn = (text: string) => Math.ceil(text.length / 4);\n tokenizerSource = 'char-estimation (~4 chars/token)';\n logger.debug('Tokenizer: using character estimation (~4 chars/token)');\n return countTokensFn;\n}\n\n// ── Model detection ────────────────────────────────────────────────\n// Detect the active model from Claude Code's configuration files.\n\nlet detectedModel: string | null = null;\nlet modelDetectionDone = false;\n\n/**\n * Detect which model Claude Code is configured to use.\n *\n * Checks (in order):\n * 1. ~/.claude/settings.json -> model field\n * 2. Environment variable ANTHROPIC_MODEL\n * 3. Defaults to 'default'\n */\nfunction detectModel(): string {\n if (modelDetectionDone) return detectedModel ?? 'default';\n modelDetectionDone = true;\n\n // 1. Check Claude Code settings\n const settingsPaths = [\n join(homedir(), '.claude', 'settings.json'),\n join(homedir(), '.claude', 'settings.local.json'),\n ];\n\n for (const settingsPath of settingsPaths) {\n if (existsSync(settingsPath)) {\n try {\n const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));\n if (settings.model && typeof settings.model === 'string') {\n detectedModel = settings.model;\n logger.debug(`Model detected from ${settingsPath}: ${detectedModel}`);\n return settings.model;\n }\n } catch {\n // ignore parse errors\n }\n }\n }\n\n // 2. Check environment variable\n const envModel = process.env.ANTHROPIC_MODEL ?? process.env.CLAUDE_MODEL;\n if (envModel) {\n detectedModel = envModel;\n logger.debug(`Model detected from environment: ${detectedModel}`);\n return detectedModel;\n }\n\n // 3. Fallback\n detectedModel = 'default';\n logger.debug('No model detected, using default (200k context)');\n return detectedModel;\n}\n\n/**\n * Update the detected model at runtime (e.g., parsed from CLI stderr output).\n * This overrides the settings-based detection.\n */\nexport function setDetectedModel(model: string): void {\n detectedModel = model;\n modelDetectionDone = true;\n const limit = getModelLimit(model);\n logger.debug(`Model updated to \"${model}\" (context: ${limit.toLocaleString()} tokens)`);\n}\n\n// ── Public API ─────────────────────────────────────────────────────\n\n/**\n * Count the number of tokens in a text string.\n */\nexport async function countTokens(text: string): Promise<number> {\n const fn = await initTokenizer();\n return fn(text);\n}\n\n/**\n * Get which tokenizer is being used (for display in UI).\n */\nexport function getTokenizerSource(): string {\n return tokenizerSource;\n}\n\n/**\n * Get the context window limit for a given model.\n * If no model is specified, uses the auto-detected model.\n */\nexport function getModelLimit(model?: string): number {\n const m = model ?? detectModel();\n\n // Exact match\n if (MODEL_LIMITS[m]) return MODEL_LIMITS[m];\n\n // Fuzzy match: check if any key is a substring of the model string\n // This handles versioned model IDs like \"claude-sonnet-4-5-20250929\"\n for (const [key, limit] of Object.entries(MODEL_LIMITS)) {\n if (key !== 'default' && m.includes(key)) return limit;\n }\n\n // Check if the model name contains common family indicators\n if (m.includes('opus')) return MODEL_LIMITS['opus'];\n if (m.includes('sonnet')) return MODEL_LIMITS['sonnet'];\n if (m.includes('haiku')) return MODEL_LIMITS['haiku'];\n\n logger.warn(`Unknown model \"${m}\", using default context window (200k)`);\n return MODEL_LIMITS['default'];\n}\n\n/**\n * Get the maximum number of tokens available for input\n * (context window minus response reserve).\n */\nexport function getMaxInputTokens(model?: string): number {\n return getModelLimit(model) - RESPONSE_RESERVE;\n}\n\n/**\n * Get a display-friendly description of the current limits.\n */\nexport function getLimitsDescription(model?: string): string {\n const m = model ?? detectModel();\n const limit = getModelLimit(m);\n const maxInput = getMaxInputTokens(m);\n return `Model: ${m}, Context: ${limit.toLocaleString()}, Max input: ${maxInput.toLocaleString()} (${RESPONSE_RESERVE.toLocaleString()} reserved for response), Tokenizer: ${tokenizerSource}`;\n}\n\n/**\n * Truncate text to fit within a token limit, cutting at section boundaries.\n *\n * Tries to cut at paragraph/section boundaries (double newlines)\n * rather than mid-sentence.\n */\nexport async function fitToWindow(\n text: string,\n maxTokens: number,\n): Promise<{ text: string; truncated: boolean; tokenCount: number }> {\n const tokenCount = await countTokens(text);\n\n if (tokenCount <= maxTokens) {\n return { text, truncated: false, tokenCount };\n }\n\n logger.debug(`Text has ${tokenCount} tokens, truncating to ${maxTokens}`);\n\n // Split into sections (paragraphs or section breaks)\n const sections = text.split(/\\n\\n+/);\n const result: string[] = [];\n let currentTokens = 0;\n\n for (const section of sections) {\n const sectionTokens = await countTokens(section);\n if (currentTokens + sectionTokens > maxTokens) {\n result.push('\\n\\n[... content truncated to fit context window ...]');\n break;\n }\n result.push(section);\n currentTokens += sectionTokens;\n }\n\n const truncatedText = result.join('\\n\\n');\n return {\n text: truncatedText,\n truncated: true,\n tokenCount: currentTokens,\n };\n}\n","import { logger } from '../utils/logger.js';\nimport type { ConversationSession, ScanItem } from '../scanners/types.js';\n\n/**\n * Groups conversations by repository path, then consolidates\n * tiny/empty groups so we don't spawn an absurd number of agents.\n */\n\nexport interface RepoGroup {\n repoPath: string;\n /** Human-readable short name for the repo */\n repoName: string;\n sessions: ConversationSession[];\n scanItems: ScanItem[];\n}\n\n// ── Path helpers ──────────────────────────────────────────────────\n\n/**\n * Normalize a path to a consistent repo identifier.\n * Strips trailing slashes, resolves home dir variations.\n */\nfunction normalizePath(p: string): string {\n let normalized = p.replace(/\\/+$/, '');\n normalized = normalized.replace(/^~/, '/Users');\n return normalized;\n}\n\n/**\n * Detect if a path looks like a decoded Docker-style random name\n * (e.g., \"/beautiful/hodgkin\", \"/distracted/jennings\").\n * These come from Claude Code sessions that aren't tied to a real project.\n */\nfunction isRandomSessionName(path: string): boolean {\n const parts = path.split('/').filter(Boolean);\n // Docker random names are always 2 segments: adjective-noun\n // Real paths have at least 3 segments (e.g., /Users/name/project)\n if (parts.length !== 2) return false;\n // Docker adjective-noun pairs are all lowercase alpha\n return parts.every((p) => /^[a-z]+$/.test(p));\n}\n\n/**\n * Extract a human-readable repo name from a full path.\n * E.g., \"/Users/matanlas/Desktop/projects/my-app\" -> \"my-app\"\n */\nfunction extractRepoName(repoPath: string): string {\n if (repoPath === 'unknown' || repoPath === '_misc') return 'Miscellaneous';\n if (repoPath === 'cursor-global') return 'Cursor (global)';\n const parts = repoPath.split('/').filter(Boolean);\n return parts[parts.length - 1] ?? repoPath;\n}\n\n/**\n * Try to find the best matching existing group for a path.\n * Handles cases where session.cwd and meta.project resolve to slightly\n * different paths for the same repo (e.g., \"/a/b/c\" vs \"/a/b/c/\").\n */\nfunction findMatchingGroup(\n groups: Map<string, RepoGroup>,\n path: string,\n): RepoGroup | null {\n const normalized = normalizePath(path);\n if (groups.has(normalized)) return groups.get(normalized)!;\n\n // Check if any existing key is a prefix or suffix match\n for (const [key, group] of groups) {\n if (key.endsWith(normalized) || normalized.endsWith(key)) {\n return group;\n }\n }\n\n return null;\n}\n\n// ── Main grouping ─────────────────────────────────────────────────\n\n/**\n * Group conversations by their workspace/repository path, then\n * consolidate small/empty groups into a single bucket.\n */\nexport function groupByRepo(\n sessions: ConversationSession[],\n scanItems: ScanItem[],\n): Map<string, RepoGroup> {\n const groups = new Map<string, RepoGroup>();\n\n function getOrCreate(path: string): RepoGroup {\n const key = normalizePath(path);\n\n // Redirect random Docker-style names to a \"misc\" bucket\n if (isRandomSessionName(key)) {\n const miscKey = '_misc';\n let misc = groups.get(miscKey);\n if (!misc) {\n misc = {\n repoPath: miscKey,\n repoName: 'Miscellaneous',\n sessions: [],\n scanItems: [],\n };\n groups.set(miscKey, misc);\n }\n return misc;\n }\n\n let group = groups.get(key);\n if (!group) {\n group = {\n repoPath: key,\n repoName: extractRepoName(key),\n sessions: [],\n scanItems: [],\n };\n groups.set(key, group);\n }\n return group;\n }\n\n // Group sessions by cwd\n for (const session of sessions) {\n const path = session.cwd ?? 'unknown';\n const group = getOrCreate(path);\n group.sessions.push(session);\n }\n\n // Group scan items by project metadata -- try to match existing session groups\n for (const item of scanItems) {\n const meta = item.metadata as Record<string, unknown>;\n let path = 'unknown';\n\n if (typeof meta.project === 'string') {\n path = meta.project;\n } else if (typeof meta.composerId === 'string') {\n path = 'cursor-global';\n }\n\n // Try to find an existing group that matches before creating a new one\n const existing = findMatchingGroup(groups, path);\n if (existing) {\n existing.scanItems.push(item);\n } else {\n const group = getOrCreate(path);\n group.scanItems.push(item);\n }\n }\n\n // ── Consolidation: merge empty and tiny groups ──────────────\n const consolidated = consolidateGroups(groups);\n\n logger.debug(\n `Repo grouper: ${consolidated.size} groups (from initial ${groups.size}) ` +\n `for ${sessions.length} sessions and ${scanItems.length} items`,\n );\n return consolidated;\n}\n\n/**\n * Consolidate groups:\n * 1. Remove groups with 0 sessions AND 0 scan items\n * 2. Merge scan-item-only groups into their matching session group\n * 3. Merge groups with <= 1 session into a \"misc\" bucket\n */\nfunction consolidateGroups(groups: Map<string, RepoGroup>): Map<string, RepoGroup> {\n const result = new Map<string, RepoGroup>();\n // Track the misc bucket via a direct variable that TS can narrow\n // (using a closure function to lazily create it would defeat CFA)\n let miscRef: RepoGroup | undefined;\n\n function getOrCreateMisc(): RepoGroup {\n if (!miscRef) {\n miscRef = {\n repoPath: '_misc',\n repoName: 'Miscellaneous',\n sessions: [],\n scanItems: [],\n };\n }\n return miscRef;\n }\n\n for (const [key, group] of groups) {\n // Skip completely empty groups\n if (group.sessions.length === 0 && group.scanItems.length === 0) {\n continue;\n }\n\n // Groups with 0 sessions but some scan items: try to merge into a\n // session group with overlapping path, otherwise dump into misc\n if (group.sessions.length === 0) {\n // Look for a session group whose path contains this group's name\n let merged = false;\n for (const [otherKey, otherGroup] of groups) {\n if (otherGroup.sessions.length > 0 && otherKey.includes(extractRepoName(key))) {\n otherGroup.scanItems.push(...group.scanItems);\n merged = true;\n break;\n }\n }\n if (!merged) {\n miscRef = getOrCreateMisc();\n miscRef.scanItems.push(...group.scanItems);\n }\n continue;\n }\n\n // Groups with only 1 session and the key is \"unknown\" or \"_misc\" => misc\n if (key === 'unknown') {\n miscRef = getOrCreateMisc();\n miscRef.sessions.push(...group.sessions);\n miscRef.scanItems.push(...group.scanItems);\n continue;\n }\n\n // Keep the group as-is\n result.set(key, group);\n }\n\n // Add the misc bucket if it has anything\n if (miscRef && (miscRef.sessions.length > 0 || miscRef.scanItems.length > 0)) {\n // If an existing _misc was already in result, merge into it\n const existingMisc = result.get('_misc');\n if (existingMisc) {\n existingMisc.sessions.push(...miscRef.sessions);\n existingMisc.scanItems.push(...miscRef.scanItems);\n } else {\n result.set('_misc', miscRef);\n }\n }\n\n return result;\n}\n\n/**\n * Get a summary of repo groups for display.\n */\nexport function summarizeGroups(groups: Map<string, RepoGroup>): Array<{\n repoPath: string;\n repoName: string;\n sessionCount: number;\n itemCount: number;\n}> {\n return Array.from(groups.values()).map((g) => ({\n repoPath: g.repoPath,\n repoName: g.repoName,\n sessionCount: g.sessions.length,\n itemCount: g.scanItems.length,\n }));\n}\n","import { z } from 'zod';\n\n// ── Distillation output schemas ───────────────────────────────────\n// Zod schemas for all structured output from Claude CLI calls.\n// Used to validate and safely parse LLM responses.\n\n// --- Individual item schemas ---\n\nexport const distillChangeSchema = z.object({\n summary: z.string().describe('Brief one-line description of the change'),\n details: z.string().describe('Detailed explanation including the WHY'),\n files: z.array(z.string()).default([]).describe('List of affected files'),\n type: z\n .enum(['feature', 'bugfix', 'refactor', 'docs', 'chore'])\n .optional()\n .describe('Category of change'),\n /** Tagged during merge -- not produced by LLM */\n repo: z.string().optional(),\n});\n\nexport const distillDecisionSchema = z.object({\n title: z.string().describe('Decision title'),\n context: z.string().describe('Problem or situation that prompted this decision'),\n decision: z.string().describe('What was decided'),\n alternatives: z\n .array(z.string())\n .default([])\n .describe('Alternatives considered and why they were rejected'),\n consequences: z\n .string()\n .optional()\n .describe('Implications of the decision'),\n /** Tagged during merge -- not produced by LLM */\n repo: z.string().optional(),\n});\n\nexport const distillPatternSchema = z.object({\n name: z.string().describe('Pattern name'),\n description: z.string().describe('What the pattern is and why it is being adopted'),\n examples: z\n .array(z.string())\n .default([])\n .describe('Specific files or code examples'),\n /** Tagged during merge -- not produced by LLM */\n repo: z.string().optional(),\n});\n\nexport const staleDocSchema = z.object({\n file: z.string().describe('Path to the stale document'),\n reason: z.string().describe('Why this documentation appears stale'),\n suggestedUpdate: z.string().describe('What should be updated or added'),\n});\n\n// --- Composite output schemas ---\n\nexport const distillOutputSchema = z.object({\n changes: z.array(distillChangeSchema).default([]),\n decisions: z.array(distillDecisionSchema).default([]),\n patterns: z.array(distillPatternSchema).default([]),\n staleDocs: z.array(staleDocSchema).default([]),\n changelog: z.string().default(''),\n});\n\n// Standalone arrays (used by dedicated prompt endpoints)\nexport const decisionsArraySchema = z.array(distillDecisionSchema);\nexport const patternsArraySchema = z.array(distillPatternSchema);\nexport const staleDocsArraySchema = z.array(staleDocSchema);\n\n// --- Global summary schema (cross-repo) ---\n\nexport const crossRepoDecisionSchema = z.object({\n title: z.string(),\n repos: z.array(z.string()).default([]),\n description: z.string(),\n impact: z.string(),\n});\n\nexport const sharedPatternSchema = z.object({\n name: z.string(),\n description: z.string(),\n repos: z.array(z.string()).default([]),\n});\n\nexport const repoDependencySchema = z.object({\n from: z.string(),\n to: z.string(),\n description: z.string(),\n});\n\nexport const globalSummarySchema = z.object({\n crossRepoDecisions: z.array(crossRepoDecisionSchema).default([]),\n sharedPatterns: z.array(sharedPatternSchema).default([]),\n dependencies: z.array(repoDependencySchema).default([]),\n conventions: z.array(z.string()).default([]),\n summary: z.string().default(''),\n});\n\n// --- Inferred TypeScript types ---\n\nexport type DistillChange = z.infer<typeof distillChangeSchema>;\nexport type DistillDecision = z.infer<typeof distillDecisionSchema>;\nexport type DistillPattern = z.infer<typeof distillPatternSchema>;\nexport type StaleDoc = z.infer<typeof staleDocSchema>;\nexport type DistillOutput = z.infer<typeof distillOutputSchema>;\nexport type CrossRepoDecision = z.infer<typeof crossRepoDecisionSchema>;\nexport type SharedPattern = z.infer<typeof sharedPatternSchema>;\nexport type RepoDependency = z.infer<typeof repoDependencySchema>;\nexport type GlobalSummary = z.infer<typeof globalSummarySchema>;\n\n// ── Parsing helpers ───────────────────────────────────────────────\n\n/**\n * Extract JSON from a raw LLM response.\n * Handles markdown code fences, leading/trailing text, etc.\n */\nfunction extractJson(raw: string): string {\n let str = raw.trim();\n\n // Strip markdown code fences if present\n const fencedMatch = str.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)\\n?\\s*```/);\n if (fencedMatch) {\n str = fencedMatch[1].trim();\n }\n\n // If the string doesn't start with { or [, try to find JSON within it\n if (!str.startsWith('{') && !str.startsWith('[')) {\n const jsonStart = str.search(/[{\\[]/);\n if (jsonStart >= 0) {\n str = str.slice(jsonStart);\n }\n }\n\n return str;\n}\n\n/**\n * Safely parse a raw LLM response into a DistillOutput.\n * Returns a valid DistillOutput even if parsing partially fails,\n * with detailed error info attached.\n */\nexport function parseDistillOutput(raw: string): {\n data: DistillOutput;\n success: boolean;\n errors: string[];\n} {\n const errors: string[] = [];\n\n try {\n const jsonStr = extractJson(raw);\n const parsed = JSON.parse(jsonStr);\n const result = distillOutputSchema.safeParse(parsed);\n\n if (result.success) {\n return { data: result.data, success: true, errors: [] };\n }\n\n // Zod validation failed -- collect errors and return defaults with salvaged fields\n for (const issue of result.error.issues) {\n errors.push(`${issue.path.join('.')}: ${issue.message}`);\n }\n\n // Try to salvage what we can\n const salvaged: DistillOutput = {\n changes: safeParseArray<DistillChange>(parsed.changes, distillChangeSchema),\n decisions: safeParseArray<DistillDecision>(parsed.decisions, distillDecisionSchema),\n patterns: safeParseArray<DistillPattern>(parsed.patterns, distillPatternSchema),\n staleDocs: safeParseArray<StaleDoc>(parsed.staleDocs, staleDocSchema),\n changelog: typeof parsed.changelog === 'string' ? parsed.changelog : '',\n };\n\n return { data: salvaged, success: false, errors };\n } catch (err) {\n errors.push(`JSON parse failed: ${err instanceof Error ? err.message : String(err)}`);\n return {\n data: { changes: [], decisions: [], patterns: [], staleDocs: [], changelog: '' },\n success: false,\n errors,\n };\n }\n}\n\n/**\n * Safely parse a raw LLM response as an array of stale docs.\n */\nexport function parseStaleDocs(raw: string): {\n data: StaleDoc[];\n success: boolean;\n errors: string[];\n} {\n return safeParseArrayResponse(raw, staleDocsArraySchema);\n}\n\n/**\n * Safely parse a raw LLM response as a GlobalSummary.\n */\nexport function parseGlobalSummary(raw: string): {\n data: GlobalSummary;\n success: boolean;\n errors: string[];\n} {\n const errors: string[] = [];\n\n try {\n const jsonStr = extractJson(raw);\n const parsed = JSON.parse(jsonStr);\n const result = globalSummarySchema.safeParse(parsed);\n\n if (result.success) {\n return { data: result.data, success: true, errors: [] };\n }\n\n for (const issue of result.error.issues) {\n errors.push(`${issue.path.join('.')}: ${issue.message}`);\n }\n\n return {\n data: {\n crossRepoDecisions: [],\n sharedPatterns: [],\n dependencies: [],\n conventions: [],\n summary: typeof parsed.summary === 'string' ? parsed.summary : '',\n },\n success: false,\n errors,\n };\n } catch (err) {\n errors.push(`JSON parse failed: ${err instanceof Error ? err.message : String(err)}`);\n return {\n data: { crossRepoDecisions: [], sharedPatterns: [], dependencies: [], conventions: [], summary: '' },\n success: false,\n errors,\n };\n }\n}\n\n// ── Internal helpers ──────────────────────────────────────────────\n\n/**\n * Parse an array, keeping only items that pass Zod validation.\n * Invalid items are silently dropped (lenient).\n */\nfunction safeParseArray<T>(input: unknown, schema: z.ZodType<T, z.ZodTypeDef, unknown>): T[] {\n if (!Array.isArray(input)) return [];\n const result: T[] = [];\n for (const item of input) {\n const parsed = schema.safeParse(item);\n if (parsed.success) {\n result.push(parsed.data);\n }\n }\n return result;\n}\n\n/**\n * Parse a raw LLM response as an array validated by a Zod array schema.\n */\nfunction safeParseArrayResponse<T>(\n raw: string,\n schema: z.ZodType<T[]>,\n): { data: T[]; success: boolean; errors: string[] } {\n const errors: string[] = [];\n\n try {\n const jsonStr = extractJson(raw);\n const parsed = JSON.parse(jsonStr);\n const result = schema.safeParse(parsed);\n\n if (result.success) {\n return { data: result.data, success: true, errors: [] };\n }\n\n for (const issue of result.error.issues) {\n errors.push(`${issue.path.join('.')}: ${issue.message}`);\n }\n\n return { data: [], success: false, errors };\n } catch (err) {\n errors.push(`JSON parse failed: ${err instanceof Error ? err.message : String(err)}`);\n return { data: [], success: false, errors };\n }\n}\n","import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { getLoreDir } from '../utils/paths.js';\nimport { logger } from '../utils/logger.js';\n\nconst CACHE_FILENAME = 'distilled-sessions.json';\n\ninterface DistillCacheData {\n /** Session IDs that have been successfully distilled */\n sessionIds: string[];\n /** ISO timestamp of last cache update */\n updatedAt: string;\n}\n\n/**\n * Manages a cache of session IDs that have already been distilled.\n * Prevents re-processing conversations that produced no new results.\n *\n * Stored at ~/.lore/distilled-sessions.json\n */\nexport class DistillCache {\n private cachePath: string;\n private cached: Set<string>;\n\n constructor(loreDir?: string) {\n const dir = loreDir ?? getLoreDir();\n this.cachePath = join(dir, CACHE_FILENAME);\n this.cached = new Set();\n }\n\n /** Load cached session IDs from disk. */\n load(): void {\n if (!existsSync(this.cachePath)) {\n this.cached = new Set();\n return;\n }\n\n try {\n const raw = readFileSync(this.cachePath, 'utf-8');\n const data: DistillCacheData = JSON.parse(raw);\n this.cached = new Set(data.sessionIds ?? []);\n logger.debug(`Distill cache: loaded ${this.cached.size} cached session IDs`);\n } catch (err) {\n logger.warn(`Failed to read distill cache, starting fresh: ${err}`);\n this.cached = new Set();\n }\n }\n\n /** Check if a session ID has already been distilled. */\n has(sessionId: string): boolean {\n return this.cached.has(sessionId);\n }\n\n /** Get the number of cached session IDs. */\n get size(): number {\n return this.cached.size;\n }\n\n /**\n * Add session IDs to the cache after successful distillation.\n * Automatically persists to disk.\n */\n addAll(sessionIds: string[]): void {\n for (const id of sessionIds) {\n this.cached.add(id);\n }\n this.save();\n }\n\n /** Persist current cache to disk. */\n private save(): void {\n try {\n const dir = dirname(this.cachePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const data: DistillCacheData = {\n sessionIds: Array.from(this.cached),\n updatedAt: new Date().toISOString(),\n };\n writeFileSync(this.cachePath, JSON.stringify(data, null, 2) + '\\n', 'utf-8');\n logger.debug(`Distill cache: saved ${this.cached.size} session IDs`);\n } catch (err) {\n logger.warn(`Failed to write distill cache: ${err}`);\n }\n }\n}\n","import { existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { logger } from '../utils/logger.js';\nimport { ensureAnyCli, invokeWithFallback, invokeWithFallbackAndTag, snapshotClaudeSessions, detectNewSessions } from '../utils/cli.js';\nimport { tagConversations } from '../state/phase-store.js';\nimport { loadPrompt, splitPrompt, createSystemPromptFile } from './prompts.js';\nimport { createMcpConfigFile } from './mcp-config.js';\nimport { countTokens, getMaxInputTokens, fitToWindow, setDetectedModel, getLimitsDescription, getTokenizerSource } from './tokenizer.js';\nimport { groupByRepo, type RepoGroup } from './repo-grouper.js';\n// chunk-splitter is no longer used by distillMultiAgent (clean-context mode\n// processes each conversation in its own CLI call instead of chunk-based splitting)\nimport {\n parseDistillOutput as zodParseDistillOutput,\n parseStaleDocs,\n parseGlobalSummary,\n type DistillChange,\n type DistillDecision,\n type DistillPattern,\n type StaleDoc,\n type DistillOutput,\n} from './schemas.js';\nimport { getLoreDir } from '../utils/paths.js';\nimport { DISTILL_TIMEOUT_MS, DISTILL_INTERACTIVE_TIMEOUT_MS } from '../utils/constants.js';\nimport { DistillCache } from './distill-cache.js';\nimport type { AlignmentResult, ScanItem, ConversationSession } from '../scanners/types.js';\n\n// Re-export types from schemas so consumers don't need to change imports\nexport type { DistillChange, DistillDecision, DistillPattern, StaleDoc, DistillOutput };\n\nexport type DistillMode = 'headless' | 'interactive';\n\n// ── Token-proportional content budgets ──────────────────────────\n// Per-item limits as a fraction of getMaxInputTokens().\n// Scales automatically with model context window upgrades.\n// The outer fitToWindow() in invokeClaudeCode is the final safety net.\nconst CONTENT_BUDGETS = {\n matchedCommit: 0.02, // 2% per matched commit\n matchedConversation: 0.04, // 4% per matched conversation\n unmatchedCommit: 0.015, // 1.5% per unmatched commit\n unmatchedConversation: 0.04, // 4% per unmatched conversation\n doc: 0.02, // 2% per doc\n} as const;\n\n// Conservative chars-per-token for code-heavy content\nconst CHARS_PER_TOKEN_ESTIMATE = 3.5;\n\n/** Compute a character budget from a token-proportional key. */\nfunction getCharBudget(budgetKey: keyof typeof CONTENT_BUDGETS): number {\n const maxTokens = getMaxInputTokens();\n return Math.floor(maxTokens * CONTENT_BUDGETS[budgetKey] * CHARS_PER_TOKEN_ESTIMATE);\n}\n\n// ── Multi-agent optimization constants ──────────────────────────\n\n/**\n * If total tokens across ALL repos fit below this fraction of the context\n * window, skip multi-agent orchestration and use a single distill() call.\n */\nconst SINGLE_AGENT_THRESHOLD = 0.5;\n\n/**\n * Minimum conversations for a repo to get full interactive distillation.\n * Repos below this AND below MIN_SOLO_TOKENS use lightweight headless mode.\n */\nconst MIN_SOLO_CONVERSATIONS = 3;\n\n/**\n * Minimum tokens for a repo to get full interactive distillation.\n */\nconst MIN_SOLO_TOKENS = 5_000;\n\n/**\n * Repos with 1 conversation and below this token count are skipped from\n * distillation entirely — the summarizer pipeline handles them better.\n */\nconst SKIP_THRESHOLD_TOKENS = 1_500;\n\nexport interface DistillOptions {\n /** Callback invoked with each line of Claude CLI output as it streams. */\n onOutput?: (line: string) => void;\n /** AbortSignal to cancel long-running CLI invocations. */\n signal?: AbortSignal;\n /** Mode: headless (fast, -p flag) or interactive (deep, with tools). Default: headless */\n mode?: DistillMode;\n /** Max turns for interactive mode. Default: 10 */\n maxTurns?: number;\n /** Override the phase tag for CLI invocations (default: 'distill'). */\n phaseTag?: import('../state/phase-store.js').LorePhase;\n /** Run ID for linking Claude conversations back to a specific scan run. */\n runId?: string;\n}\n\n/** Summary of how agents were allocated per repo (for UI display). */\nexport interface RepoAgentPlan {\n repo: string;\n repoPath: string;\n conversations: number;\n tokens: number;\n windowPercent: number;\n agents: number;\n chunks: Array<{ label: string; sessions: number; tokens: number }>;\n}\n\nexport class DistillationEngine {\n constructor(private workDir: string) {}\n\n async distill(\n alignment: AlignmentResult,\n existingDocs: ScanItem[],\n options: DistillOptions = {},\n ): Promise<DistillOutput> {\n // Build context from alignment results\n const context = this.buildContext(alignment);\n const docsContext = this.buildDocsContext(existingDocs);\n\n options.onOutput?.('[lore] Starting main distillation...');\n\n // Run the main distillation\n const mainPrompt = loadPrompt('distill-changes')\n .replace('{context}', context);\n\n const mainResult = await this.invokeClaudeCode(mainPrompt, context, options);\n\n const parsed = zodParseDistillOutput(mainResult);\n let output = parsed.data;\n if (!parsed.success) {\n logger.warn(`Distillation output validation issues: ${parsed.errors.join('; ')}`);\n options.onOutput?.(`[lore] Warning: output validation issues (${parsed.errors.length}), salvaged valid fields`);\n for (const err of parsed.errors.slice(0, 3)) {\n options.onOutput?.(`[lore] - ${err}`);\n }\n }\n\n // If we have existing docs, check for staleness\n if (existingDocs.length > 0) {\n try {\n options.onOutput?.('[lore] Checking documentation freshness...');\n const stalenessPrompt = loadPrompt('detect-staleness')\n .replace('{docs}', docsContext)\n .replace('{activity}', context);\n\n const stalenessResult = await this.invokeClaudeCode(stalenessPrompt, docsContext, options);\n const staleResult = parseStaleDocs(stalenessResult);\n if (staleResult.success) {\n output.staleDocs = staleResult.data;\n } else {\n logger.debug(`Staleness validation issues: ${staleResult.errors.join('; ')}`);\n }\n } catch {\n logger.debug('Staleness detection failed or returned no results');\n }\n }\n\n // Generate changelog separately for cleaner formatting\n if (output.changes.length > 0) {\n try {\n options.onOutput?.('[lore] Generating changelog...');\n const changelogPrompt = loadPrompt('generate-changelog')\n .replace('{context}', JSON.stringify(output.changes, null, 2));\n\n const changelogResult = await this.invokeClaudeCode(changelogPrompt, '', options);\n output.changelog = changelogResult.trim();\n } catch {\n logger.debug('Changelog generation failed, using auto-generated format');\n output.changelog = this.autoGenerateChangelog(output.changes);\n }\n }\n\n options.onOutput?.(`[lore] Distillation complete: ${output.changes.length} changes, ${output.decisions.length} decisions, ${output.patterns.length} patterns`);\n return output;\n }\n\n private async invokeClaudeCode(\n prompt: string,\n contextData: string,\n options: DistillOptions = {},\n ): Promise<string> {\n const mode = options.mode ?? 'headless';\n\n // Ensure CLI is available (supports claude -> agent fallback)\n await ensureAnyCli();\n\n const { cleanup } = createMcpConfigFile();\n\n // Split prompt into system (behavioural) and user (data) portions\n const { systemPrompt, userPrompt } = splitPrompt(prompt);\n const { path: systemPromptPath, cleanup: cleanupSystemPrompt } = createSystemPromptFile(systemPrompt);\n\n try {\n let fullInput = contextData\n ? `${userPrompt}\\n\\n---\\n\\nCONTEXT DATA:\\n${contextData}`\n : userPrompt;\n\n // Token counting and truncation\n const maxTokens = getMaxInputTokens();\n const inputTokens = await countTokens(fullInput);\n options.onOutput?.(`[tokenizer] ${getLimitsDescription()}`);\n options.onOutput?.(`[tokenizer] Context: ${inputTokens.toLocaleString()} tokens / ${maxTokens.toLocaleString()} limit`);\n\n if (inputTokens > maxTokens) {\n options.onOutput?.(`[tokenizer] Truncating context to fit window...`);\n const fitted = await fitToWindow(fullInput, maxTokens);\n fullInput = fitted.text;\n options.onOutput?.(`[tokenizer] Truncated to ${fitted.tokenCount.toLocaleString()} tokens`);\n }\n\n if (mode === 'interactive') {\n return this.invokeClaudeCodeInteractive(fullInput, options, systemPromptPath);\n }\n\n // Headless mode: use shared fallback chain for rate-limit resilience\n // --tools \"\" → no built-in tools (pure text generation)\n // --strict-mcp-config → ignore user's global MCP servers\n const phase = options.phaseTag ?? 'distill';\n const stdout = await invokeWithFallbackAndTag(\n {\n args: ['-p', '--output-format', 'text', '--max-turns', '3', '--tools', '', '--strict-mcp-config'],\n input: fullInput,\n cwd: this.workDir,\n timeout: DISTILL_TIMEOUT_MS,\n signal: options.signal,\n systemPromptFile: systemPromptPath,\n },\n 'distill:headless',\n phase,\n options.runId,\n );\n\n return stdout;\n } finally {\n cleanupSystemPrompt();\n cleanup();\n }\n }\n\n /**\n * Interactive (non-headless) mode: runs Claude Code with full tool access.\n * Drops the -p flag so Claude can iterate with tools (file reading, grep, etc.).\n * Uses --max-turns to limit iteration depth.\n */\n private async invokeClaudeCodeInteractive(\n fullInput: string,\n options: DistillOptions = {},\n systemPromptPath?: string,\n ): Promise<string> {\n const maxTurns = options.maxTurns ?? 10;\n options.onOutput?.(`[lore] Interactive mode: max ${maxTurns} turns, full tool access`);\n\n // Use shared fallback chain for rate-limit resilience\n const phase = options.phaseTag ?? 'distill';\n const stdout = await invokeWithFallbackAndTag(\n {\n args: [\n '-p',\n '--output-format', 'text',\n '--max-turns', String(maxTurns),\n '--allowedTools', 'Read,Grep,Glob,Shell,SemanticSearch,LS',\n '--strict-mcp-config', // ignore user's global MCP servers\n ],\n input: fullInput,\n cwd: this.workDir,\n timeout: DISTILL_INTERACTIVE_TIMEOUT_MS,\n signal: options.signal,\n systemPromptFile: systemPromptPath,\n },\n 'distill:interactive',\n phase,\n options.runId,\n );\n\n return stdout;\n }\n\n private buildContext(alignment: AlignmentResult): string {\n const parts: string[] = [];\n const commitBudget = getCharBudget('matchedCommit');\n const convBudget = getCharBudget('matchedConversation');\n const unmatchedCommitBudget = getCharBudget('unmatchedCommit');\n const unmatchedConvBudget = getCharBudget('unmatchedConversation');\n\n if (alignment.matched.length > 0) {\n parts.push('=== MATCHED (Commit + Conversation) ===');\n for (const pair of alignment.matched) {\n parts.push(`\\n--- Commit: ${pair.commit.title} ---`);\n parts.push(pair.commit.content.slice(0, commitBudget));\n parts.push(`\\n--- Conversation ---`);\n const conversationContent = pair.conversation.turns\n .map((t) => `[${t.role}]: ${t.content}`)\n .join('\\n');\n parts.push(conversationContent.slice(0, convBudget));\n parts.push('');\n }\n }\n\n if (alignment.unmatchedCommits.length > 0) {\n parts.push('=== UNMATCHED COMMITS ===');\n for (const commit of alignment.unmatchedCommits) {\n parts.push(`\\n--- ${commit.title} ---`);\n parts.push(commit.content.slice(0, unmatchedCommitBudget));\n parts.push('');\n }\n }\n\n if (alignment.unmatchedConversations.length > 0) {\n parts.push('=== UNMATCHED CONVERSATIONS (no commit produced) ===');\n for (const session of alignment.unmatchedConversations) {\n parts.push(`\\n--- Session: ${session.sessionId.slice(0, 8)} (${session.branch ?? 'no branch'}) ---`);\n const conversationContent = session.turns\n .map((t) => `[${t.role}]: ${t.content}`)\n .join('\\n');\n parts.push(conversationContent.slice(0, unmatchedConvBudget));\n parts.push('');\n }\n }\n\n return parts.join('\\n');\n }\n\n private buildDocsContext(docs: ScanItem[]): string {\n const docBudget = getCharBudget('doc');\n return docs\n .map((doc) => `--- ${doc.title} ---\\n${doc.content.slice(0, docBudget)}`)\n .join('\\n\\n');\n }\n\n private parseDistillOutput(raw: string): DistillOutput {\n const result = zodParseDistillOutput(raw);\n if (!result.success) {\n logger.debug(`Distill output validation issues: ${result.errors.join('; ')}`);\n }\n return result.data;\n }\n\n /**\n * Smart multi-agent distillation with context-aware splitting.\n *\n * Flow:\n * 1. Group conversations by repo\n * 2. Measure total tokens — if small enough, fall back to single-agent\n * 3. Skip trivial repos, classify remaining as lightweight vs full\n * 4. Process each conversation with a clean CLI call (separate context per conversation)\n * 5. Deterministic merge of per-conversation results\n */\n async distillMultiAgent(\n sessions: ConversationSession[],\n scanItems: ScanItem[],\n options: DistillOptions & { concurrency?: number } = {},\n ): Promise<{ output: DistillOutput; agentPlans: RepoAgentPlan[] }> {\n const concurrency = options.concurrency ?? 3;\n\n // ── Distill cache: skip already-processed sessions ──\n const distillCache = new DistillCache();\n distillCache.load();\n\n const beforeCount = sessions.length;\n const newSessions = sessions.filter((s) => !distillCache.has(s.sessionId));\n const cachedCount = beforeCount - newSessions.length;\n\n if (cachedCount > 0) {\n options.onOutput?.(\n `[cache] ${cachedCount} session(s) already distilled, ${newSessions.length} new session(s) to process`,\n );\n }\n\n if (newSessions.length === 0) {\n options.onOutput?.('[cache] All sessions already distilled — nothing to do');\n const emptyOutput: DistillOutput = {\n changes: [],\n decisions: [],\n patterns: [],\n staleDocs: [],\n changelog: '',\n };\n return { output: emptyOutput, agentPlans: [] };\n }\n\n const groups = groupByRepo(newSessions, scanItems);\n\n if (groups.size <= 1) {\n options.onOutput?.('[multi-agent] Only 1 repo group, falling back to single-agent distillation');\n const alignment: AlignmentResult = {\n matched: [],\n unmatchedCommits: [],\n unmatchedConversations: newSessions,\n };\n const output = await this.distill(alignment, [], options);\n distillCache.addAll(newSessions.map((s) => s.sessionId));\n return { output, agentPlans: [] };\n }\n\n // Clean up temp files from previous runs\n this.cleanupTempFiles();\n\n // Filter out groups with no sessions (scan-items-only groups don't need agents)\n const activeGroups = new Map<string, RepoGroup>();\n for (const [key, group] of groups) {\n if (group.sessions.length > 0) {\n activeGroups.set(key, group);\n }\n }\n\n // If filtering reduced us to 1 or 0 groups, fall back to single-agent\n if (activeGroups.size <= 1) {\n options.onOutput?.(`[multi-agent] ${activeGroups.size} active repo group(s) after filtering, using single-agent`);\n const alignment: AlignmentResult = {\n matched: [],\n unmatchedCommits: [],\n unmatchedConversations: newSessions,\n };\n const output = await this.distill(alignment, [], options);\n distillCache.addAll(newSessions.map((s) => s.sessionId));\n return { output, agentPlans: [] };\n }\n\n options.onOutput?.(`[multi-agent] ${activeGroups.size} active repo groups (${groups.size - activeGroups.size} empty groups filtered)`);\n\n // Helper to build context from sessions\n const buildContextForSessions = (s: ConversationSession[]): string => {\n const alignment: AlignmentResult = {\n matched: [],\n unmatchedCommits: [],\n unmatchedConversations: s,\n };\n return this.buildContext(alignment);\n };\n\n // ── Layer 1: Measure total tokens and check single-agent threshold ──\n const maxInput = getMaxInputTokens();\n const singleAgentLimit = Math.floor(maxInput * SINGLE_AGENT_THRESHOLD);\n\n const repoEntries = Array.from(activeGroups.entries());\n const agentPlans: RepoAgentPlan[] = [];\n const repoTokens = new Map<string, number>();\n\n // Measure tokens for each repo\n let grandTotalTokens = 0;\n for (const [repoPath, group] of repoEntries) {\n const context = buildContextForSessions(group.sessions);\n const tokens = await countTokens(context);\n repoTokens.set(repoPath, tokens);\n grandTotalTokens += tokens;\n\n const windowPercent = Math.round((tokens / maxInput) * 100);\n agentPlans.push({\n repo: group.repoName,\n repoPath,\n conversations: group.sessions.length,\n tokens,\n windowPercent,\n agents: 1,\n chunks: [{ label: group.repoName, sessions: group.sessions.length, tokens }],\n });\n }\n\n const grandWindowPercent = Math.round((grandTotalTokens / maxInput) * 100);\n options.onOutput?.(\n `[plan] ${repoEntries.length} repos, ~${grandTotalTokens.toLocaleString()} total tokens (${grandWindowPercent}% of ${maxInput.toLocaleString()} window)`,\n );\n\n // If everything fits in one call, skip multi-agent entirely\n if (grandTotalTokens < singleAgentLimit) {\n options.onOutput?.(\n `[plan] Total tokens (${grandTotalTokens.toLocaleString()}) below single-agent threshold (${singleAgentLimit.toLocaleString()}). Using 1 call instead of ${repoEntries.length + 2}.`,\n );\n const alignment: AlignmentResult = {\n matched: [],\n unmatchedCommits: [],\n unmatchedConversations: newSessions,\n };\n const output = await this.distill(alignment, [], options);\n distillCache.addAll(newSessions.map((s) => s.sessionId));\n return { output, agentPlans };\n }\n\n // ── Layer 3: Skip trivial repos ──\n const substantialEntries: Array<[string, RepoGroup]> = [];\n let skippedTrivial = 0;\n\n for (const [repoPath, group] of repoEntries) {\n const tokens = repoTokens.get(repoPath) ?? 0;\n if (group.sessions.length <= 1 && tokens < SKIP_THRESHOLD_TOKENS) {\n skippedTrivial++;\n continue;\n }\n substantialEntries.push([repoPath, group]);\n }\n\n if (skippedTrivial > 0) {\n options.onOutput?.(\n `[plan] Skipped ${skippedTrivial} trivial repo(s) (1 conversation, <${SKIP_THRESHOLD_TOKENS} tokens) — handled by summarizer`,\n );\n }\n\n // If skipping trivial repos leaves nothing, fall back to single-agent on what's left\n if (substantialEntries.length === 0) {\n options.onOutput?.('[plan] No substantial repos after filtering. Falling back to single-agent.');\n const alignment: AlignmentResult = {\n matched: [],\n unmatchedCommits: [],\n unmatchedConversations: newSessions,\n };\n const output = await this.distill(alignment, [], options);\n distillCache.addAll(newSessions.map((s) => s.sessionId));\n return { output, agentPlans };\n }\n\n // ── Layer 2: Tiered per-conversation processing with clean context ──\n // Classify repos as lightweight vs full\n const lightweight: Array<[string, RepoGroup]> = [];\n const full: Array<[string, RepoGroup]> = [];\n\n for (const [repoPath, group] of substantialEntries) {\n const tokens = repoTokens.get(repoPath) ?? 0;\n if (group.sessions.length >= MIN_SOLO_CONVERSATIONS || tokens >= MIN_SOLO_TOKENS) {\n full.push([repoPath, group]);\n } else {\n lightweight.push([repoPath, group]);\n }\n }\n\n options.onOutput?.(\n `[plan] ${full.length} full repo(s), ${lightweight.length} lightweight repo(s), ${skippedTrivial} skipped`,\n );\n\n // Log per-repo plan\n for (const plan of agentPlans) {\n const tokens = repoTokens.get(plan.repoPath) ?? 0;\n const tier =\n tokens < SKIP_THRESHOLD_TOKENS && plan.conversations <= 1\n ? 'skip'\n : plan.conversations >= MIN_SOLO_CONVERSATIONS || tokens >= MIN_SOLO_TOKENS\n ? 'full'\n : 'lightweight';\n options.onOutput?.(\n `[plan] ${plan.repo}: ${plan.conversations} conversations, ~${plan.tokens.toLocaleString()} tokens (${plan.windowPercent}% window) → ${tier}`,\n );\n }\n\n // ── Process each conversation with clean context (separate CLI call) ──\n const allPartialOutputs: Array<{ repo: string; chunkLabel: string; output: DistillOutput }> = [];\n\n // Build a flat work queue: one entry per conversation (clean context)\n const workQueue: Array<{\n repoName: string;\n session: ConversationSession;\n mode: DistillMode;\n }> = [];\n\n for (const [, group] of lightweight) {\n for (const session of group.sessions) {\n workQueue.push({ repoName: group.repoName, session, mode: 'headless' });\n }\n }\n for (const [, group] of full) {\n for (const session of group.sessions) {\n workQueue.push({ repoName: group.repoName, session, mode: options.mode ?? 'headless' });\n }\n }\n\n const totalCalls = workQueue.length;\n options.onOutput?.(\n `[distill] Processing ${totalCalls} conversation(s) with clean context (concurrency: ${concurrency})...`,\n );\n\n // Process in batches of `concurrency`\n for (let i = 0; i < workQueue.length; i += concurrency) {\n const batch = workQueue.slice(i, i + concurrency);\n const batchPromises = batch.map(async ({ repoName, session, mode }, batchIdx) => {\n const callIdx = i + batchIdx + 1;\n const agentLabel = `[${repoName}]`;\n const tierLabel = mode === 'headless' ? 'lightweight' : 'full';\n\n options.onOutput?.(\n `${agentLabel} [${callIdx}/${totalCalls}] ${tierLabel} — clean context`,\n );\n\n // Build context for this single conversation (clean — no cross-conversation bleed)\n const context = buildContextForSessions([session]);\n const prompt = loadPrompt('distill-changes').replace('{context}', context);\n\n try {\n const result = await this.invokeClaudeCode(prompt, context, {\n ...options,\n mode,\n maxTurns: mode === 'headless' ? 3 : options.maxTurns,\n onOutput: (line) => options.onOutput?.(`${agentLabel} ${line}`),\n });\n const output = this.parseDistillOutput(result);\n options.onOutput?.(\n `${agentLabel} [${callIdx}/${totalCalls}] Done: ${output.changes.length} changes, ${output.decisions.length} decisions`,\n );\n\n return { repo: repoName, chunkLabel: session.sessionId, output };\n } catch (err) {\n options.onOutput?.(\n `${agentLabel} [${callIdx}/${totalCalls}] Error: ${err instanceof Error ? err.message : String(err)}`,\n );\n const emptyOutput: DistillOutput = {\n changes: [],\n decisions: [],\n patterns: [],\n staleDocs: [],\n changelog: '',\n };\n return { repo: repoName, chunkLabel: session.sessionId, output: emptyOutput };\n }\n });\n\n const batchResults = await Promise.all(batchPromises);\n allPartialOutputs.push(...batchResults);\n }\n\n // ── Deterministic merge (no AI orchestrator needed) ──\n options.onOutput?.(`[merge] Merging ${allPartialOutputs.length} results deterministically...`);\n const output = this.deterministicMerge(\n allPartialOutputs.map((r) => ({ output: r.output, repo: r.repo })),\n );\n\n // Auto-generate changelog if we have changes but no merged changelog\n if (output.changes.length > 0 && !output.changelog) {\n output.changelog = this.autoGenerateChangelog(output.changes);\n }\n\n options.onOutput?.(\n `[merge] Final: ${output.changes.length} changes, ${output.decisions.length} decisions, ${output.patterns.length} patterns`,\n );\n\n // ── Persist distill cache: mark all processed sessions as done ──\n const processedIds = newSessions.map((s) => s.sessionId);\n distillCache.addAll(processedIds);\n options.onOutput?.(`[cache] Cached ${processedIds.length} newly distilled session(s)`);\n\n return { output, agentPlans };\n }\n\n // ── Temp file management ──────────────────────────────────────\n\n private getTempDir(): string {\n return join(getLoreDir(), 'tmp');\n }\n\n /** Remove temp files from previous runs. */\n private cleanupTempFiles(): void {\n const tmpDir = this.getTempDir();\n if (!existsSync(tmpDir)) return;\n try {\n const files = readdirSync(tmpDir).filter((f) => f.startsWith('distill-'));\n for (const file of files) {\n rmSync(join(tmpDir, file), { force: true });\n }\n logger.debug(`Cleaned up ${files.length} temp files`);\n } catch {\n // ignore\n }\n }\n\n /** Write a sub-agent's result to a temp file for debugging. */\n private writeTempFile(repoName: string, chunkId: number, output: DistillOutput): void {\n try {\n const tmpDir = this.getTempDir();\n mkdirSync(tmpDir, { recursive: true });\n const safeName = repoName.replace(/[^a-zA-Z0-9_-]/g, '_');\n const fileName = `distill-${safeName}-chunk-${chunkId}.json`;\n writeFileSync(\n join(tmpDir, fileName),\n JSON.stringify(output, null, 2),\n 'utf-8',\n );\n } catch {\n logger.debug(`Failed to write temp file for ${repoName} chunk ${chunkId}`);\n }\n }\n\n // ── Context builders ──────────────────────────────────────────\n\n /**\n * Build a condensed context from all repo groups for the global summary agent.\n */\n private buildGlobalContext(groups: RepoGroup[]): string {\n const parts: string[] = [];\n for (const group of groups) {\n parts.push(`=== REPO: ${group.repoName} (${group.repoPath}) ===`);\n parts.push(`Sessions: ${group.sessions.length}`);\n\n // Include first user message from each session for context\n for (const session of group.sessions.slice(0, 5)) {\n const firstUser = session.turns.find((t) => t.role === 'user');\n if (firstUser?.content) {\n parts.push(` - ${firstUser.content.slice(0, 200)}`);\n }\n }\n if (group.sessions.length > 5) {\n parts.push(` ... and ${group.sessions.length - 5} more sessions`);\n }\n parts.push('');\n }\n return parts.join('\\n');\n }\n\n // ── Merge helpers ─────────────────────────────────────────────\n\n /**\n * Compute normalized Levenshtein similarity between two strings (0-1).\n * 1.0 = identical, 0.0 = completely different.\n */\n private static levenshteinSimilarity(a: string, b: string): number {\n if (a === b) return 1;\n const la = a.length;\n const lb = b.length;\n if (la === 0 || lb === 0) return 0;\n\n const matrix: number[][] = Array.from({ length: la + 1 }, (_, i) =>\n Array.from({ length: lb + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0)),\n );\n\n for (let i = 1; i <= la; i++) {\n for (let j = 1; j <= lb; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n matrix[i][j] = Math.min(\n matrix[i - 1][j] + 1,\n matrix[i][j - 1] + 1,\n matrix[i - 1][j - 1] + cost,\n );\n }\n }\n\n return 1 - matrix[la][lb] / Math.max(la, lb);\n }\n\n /**\n * Check if a new title/name is a fuzzy duplicate of any seen title.\n * Returns true only for very high similarity (>0.85).\n * For borderline cases (0.7-0.85), returns false but logs for debugging.\n */\n private isFuzzyDuplicate(newKey: string, seenKeys: Set<string>): boolean {\n for (const existing of seenKeys) {\n const sim = DistillationEngine.levenshteinSimilarity(newKey, existing);\n if (sim > 0.85) return true;\n if (sim > 0.70) {\n logger.debug(`[merge] Borderline duplicate (${(sim * 100).toFixed(0)}%): \"${newKey}\" vs \"${existing}\"`);\n }\n }\n return false;\n }\n\n /**\n * Deterministic merge with repo tagging.\n * Each item is tagged with its source repo for traceability.\n * Deduplicates decisions and patterns by exact title match first,\n * then by fuzzy Levenshtein similarity (>0.85 threshold).\n */\n private deterministicMerge(\n entries: Array<{ output: DistillOutput; repo?: string }>,\n ): DistillOutput {\n const changes: DistillChange[] = [];\n const decisions: DistillDecision[] = [];\n const patterns: DistillPattern[] = [];\n const staleDocs: StaleDoc[] = [];\n const changelogParts: string[] = [];\n\n const seenDecisions = new Set<string>();\n const seenPatterns = new Set<string>();\n // Track changelog entries by file path + change type for dedup\n const seenChangelogEntries = new Set<string>();\n\n for (const { output, repo } of entries) {\n for (const c of output.changes) {\n changes.push({ ...c, repo: c.repo ?? repo });\n }\n\n for (const d of output.decisions) {\n const key = d.title.toLowerCase();\n if (!seenDecisions.has(key) && !this.isFuzzyDuplicate(key, seenDecisions)) {\n seenDecisions.add(key);\n decisions.push({ ...d, repo: d.repo ?? repo });\n }\n }\n\n for (const p of output.patterns) {\n const key = p.name.toLowerCase();\n if (!seenPatterns.has(key) && !this.isFuzzyDuplicate(key, seenPatterns)) {\n seenPatterns.add(key);\n patterns.push({ ...p, repo: p.repo ?? repo });\n }\n }\n\n staleDocs.push(...output.staleDocs);\n if (output.changelog) {\n // Deduplicate changelog entries by content similarity\n const lines = output.changelog.split('\\n').filter((l) => l.trim().startsWith('-'));\n const uniqueLines: string[] = [];\n for (const line of lines) {\n const normalized = line.trim().toLowerCase();\n if (!seenChangelogEntries.has(normalized)) {\n seenChangelogEntries.add(normalized);\n uniqueLines.push(line);\n }\n }\n // Reconstruct changelog preserving headers\n const nonEntryLines = output.changelog.split('\\n').filter((l) => !l.trim().startsWith('-'));\n const dedupedChangelog = [...nonEntryLines.filter((l) => l.trim()), ...uniqueLines].join('\\n');\n if (dedupedChangelog.trim()) changelogParts.push(dedupedChangelog);\n }\n }\n\n return {\n changes,\n decisions,\n patterns,\n staleDocs,\n changelog: changelogParts.join('\\n\\n'),\n };\n }\n\n private autoGenerateChangelog(changes: DistillChange[]): string {\n const grouped: Record<string, string[]> = {\n Added: [],\n Changed: [],\n Fixed: [],\n Removed: [],\n };\n\n for (const change of changes) {\n const category =\n change.type === 'feature'\n ? 'Added'\n : change.type === 'bugfix'\n ? 'Fixed'\n : change.type === 'refactor'\n ? 'Changed'\n : 'Changed';\n\n grouped[category].push(`- ${change.summary}`);\n }\n\n const sections: string[] = [];\n for (const [category, entries] of Object.entries(grouped)) {\n if (entries.length > 0) {\n sections.push(`### ${category}\\n${entries.join('\\n')}`);\n }\n }\n\n return sections.join('\\n\\n');\n }\n}\n","import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport { logger } from '../utils/logger.js';\n\ninterface PendingItem {\n id: string;\n type: 'changelog' | 'adr' | 'architecture';\n content: string;\n createdAt: string;\n filename: string;\n /** Repository name this item belongs to (e.g. \"premium-domains\") */\n repo?: string;\n /** Human-readable title extracted from content */\n title?: string;\n}\n\nexport class ReviewQueueWriter {\n private pendingDir: string;\n\n constructor(loreDir: string) {\n this.pendingDir = join(loreDir, 'pending');\n mkdirSync(this.pendingDir, { recursive: true });\n }\n\n /**\n * Add a new item to the review queue.\n */\n enqueue(\n type: PendingItem['type'],\n content: string,\n opts?: { repo?: string; title?: string },\n ): string {\n const id = randomUUID();\n const filename = `${type}-${id}.json`;\n\n const item: PendingItem = {\n id,\n type,\n content,\n createdAt: new Date().toISOString(),\n filename,\n repo: opts?.repo,\n title: opts?.title,\n };\n\n const filePath = join(this.pendingDir, filename);\n writeFileSync(filePath, JSON.stringify(item, null, 2), 'utf-8');\n\n logger.debug(`Queued review item: ${filename}`);\n return id;\n }\n\n /**\n * List all pending review items.\n */\n list(): PendingItem[] {\n if (!existsSync(this.pendingDir)) return [];\n\n return readdirSync(this.pendingDir)\n .filter((f) => f.endsWith('.json'))\n .map((f) => {\n try {\n const raw = readFileSync(join(this.pendingDir, f), 'utf-8');\n return JSON.parse(raw) as PendingItem;\n } catch {\n return null;\n }\n })\n .filter((item): item is PendingItem => item !== null)\n .sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n }\n\n /**\n * Remove a pending item by ID.\n */\n remove(id: string): boolean {\n const items = this.list();\n const item = items.find((i) => i.id === id);\n if (!item) return false;\n\n try {\n unlinkSync(join(this.pendingDir, item.filename));\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Get the count of pending items.\n */\n count(): number {\n if (!existsSync(this.pendingDir)) return 0;\n return readdirSync(this.pendingDir).filter((f) => f.endsWith('.json')).length;\n }\n}\n","/**\n * Summary cache management.\n *\n * Manages the ~/.lore/summaries/ directory:\n * index.json — master index of summarized conversations\n * conversations/{id}.json — per-conversation summary files\n * repos/{slug}.md — per-repo clustered knowledge (Phase 2)\n */\n\nimport {\n existsSync,\n readFileSync,\n writeFileSync,\n mkdirSync,\n readdirSync,\n} from 'node:fs';\nimport { join } from 'node:path';\nimport { getLoreDir } from '../utils/paths.js';\nimport type { SummaryIndex, StoredConversationSummary } from './types.js';\n\n// ── Paths ────────────────────────────────────────────────────────\n\nfunction getSummariesDir(): string {\n return join(getLoreDir(), 'summaries');\n}\n\nfunction getConversationsDir(): string {\n return join(getSummariesDir(), 'conversations');\n}\n\nfunction getReposDir(): string {\n return join(getSummariesDir(), 'repos');\n}\n\nfunction getIndexPath(): string {\n return join(getSummariesDir(), 'index.json');\n}\n\n// ── Index management ─────────────────────────────────────────────\n\n/**\n * Read the summary index. Returns an empty index if it doesn't exist.\n */\nexport function readSummaryIndex(): SummaryIndex {\n const indexPath = getIndexPath();\n if (!existsSync(indexPath)) {\n return { version: 1, updatedAt: '', conversations: {} };\n }\n\n try {\n const raw = readFileSync(indexPath, 'utf-8');\n const parsed = JSON.parse(raw) as SummaryIndex;\n if (parsed.version !== 1) {\n return { version: 1, updatedAt: '', conversations: {} };\n }\n return parsed;\n } catch {\n return { version: 1, updatedAt: '', conversations: {} };\n }\n}\n\n/**\n * Write the summary index to disk.\n */\nexport function writeSummaryIndex(index: SummaryIndex): void {\n const dir = getSummariesDir();\n mkdirSync(dir, { recursive: true });\n\n index.updatedAt = new Date().toISOString();\n writeFileSync(getIndexPath(), JSON.stringify(index, null, 2) + '\\n', 'utf-8');\n}\n\n// ── Per-conversation summaries ───────────────────────────────────\n\n/**\n * Save a per-conversation summary to disk and update the index.\n */\nexport function saveConversationSummary(\n summary: StoredConversationSummary,\n index: SummaryIndex,\n): void {\n const dir = getConversationsDir();\n mkdirSync(dir, { recursive: true });\n\n // Write the summary file\n const filePath = join(dir, `${sanitizeId(summary.id)}.json`);\n writeFileSync(filePath, JSON.stringify(summary, null, 2) + '\\n', 'utf-8');\n\n // Update index\n index.conversations[summary.id] = {\n repo: summary.repoName,\n summarizedAt: summary.summarizedAt,\n conversationDate: summary.conversationDate,\n source: summary.source,\n title: summary.title,\n messageCount: summary.messageCount,\n };\n}\n\n/**\n * Load a specific conversation summary from disk.\n * Applies backward-compatible defaults for fields added after initial release.\n */\nexport function loadConversationSummary(id: string): StoredConversationSummary | null {\n const filePath = join(getConversationsDir(), `${sanitizeId(id)}.json`);\n if (!existsSync(filePath)) return null;\n\n try {\n const raw = JSON.parse(readFileSync(filePath, 'utf-8')) as StoredConversationSummary;\n // Backfill fields added for stale detection / sidebar visibility\n if (!raw.title) {\n raw.title = raw.ai?.summary?.slice(0, 80) ?? raw.id;\n }\n if (raw.messageCount == null) {\n raw.messageCount = 0;\n }\n return raw;\n } catch {\n return null;\n }\n}\n\n/**\n * Load all conversation summaries for a specific repo.\n */\nexport function loadSummariesForRepo(repoName: string): StoredConversationSummary[] {\n const index = readSummaryIndex();\n const results: StoredConversationSummary[] = [];\n\n for (const [id, entry] of Object.entries(index.conversations)) {\n if (entry.repo !== repoName) continue;\n\n const summary = loadConversationSummary(id);\n if (summary) results.push(summary);\n }\n\n return results;\n}\n\n/**\n * Get all unique repo names that have summaries.\n */\nexport function getReposWithSummaries(): string[] {\n const index = readSummaryIndex();\n const repos = new Set<string>();\n\n for (const entry of Object.values(index.conversations)) {\n if (entry.repo) repos.add(entry.repo);\n }\n\n return [...repos];\n}\n\n// ── Per-repo clustered knowledge (Phase 2) ───────────────────────\n\n/**\n * Save a per-repo clustered knowledge markdown file.\n */\nexport function saveRepoCluster(repoSlug: string, markdown: string): void {\n const dir = getReposDir();\n mkdirSync(dir, { recursive: true });\n\n const filePath = join(dir, `${repoSlug}.md`);\n writeFileSync(filePath, markdown, 'utf-8');\n}\n\n/**\n * Load a per-repo clustered knowledge markdown file.\n * Returns null if it doesn't exist.\n */\nexport function loadRepoCluster(repoSlug: string): string | null {\n const filePath = join(getReposDir(), `${repoSlug}.md`);\n if (!existsSync(filePath)) return null;\n\n try {\n return readFileSync(filePath, 'utf-8');\n } catch {\n return null;\n }\n}\n\n/**\n * List all repo slugs that have clustered knowledge.\n */\nexport function listRepoClusterSlugs(): string[] {\n const dir = getReposDir();\n if (!existsSync(dir)) return [];\n\n try {\n return readdirSync(dir)\n .filter((f) => f.endsWith('.md'))\n .map((f) => f.replace(/\\.md$/, ''));\n } catch {\n return [];\n }\n}\n\n// ── Recovery ─────────────────────────────────────────────────────\n\n/**\n * Reconcile the index with conversation files on disk.\n *\n * If the process was interrupted, individual `.json` files may exist\n * without a matching entry in the index. This scans the conversations\n * directory and backfills the index, then persists it.\n *\n * Returns the number of recovered entries.\n */\nexport function reconcileIndex(index: SummaryIndex): number {\n const dir = getConversationsDir();\n if (!existsSync(dir)) return 0;\n\n let recovered = 0;\n\n try {\n const files = readdirSync(dir).filter((f) => f.endsWith('.json'));\n\n for (const file of files) {\n const filePath = join(dir, file);\n\n try {\n const raw = readFileSync(filePath, 'utf-8');\n const summary = JSON.parse(raw) as StoredConversationSummary;\n\n // Skip if already in index\n if (index.conversations[summary.id]) continue;\n\n // Backfill the index entry\n index.conversations[summary.id] = {\n repo: summary.repoName,\n summarizedAt: summary.summarizedAt,\n conversationDate: summary.conversationDate,\n source: summary.source,\n title: summary.title,\n messageCount: summary.messageCount,\n };\n recovered++;\n } catch {\n // Skip corrupt files\n }\n }\n } catch {\n // Directory unreadable — nothing to recover\n }\n\n if (recovered > 0) {\n writeSummaryIndex(index);\n }\n\n return recovered;\n}\n\n// ── Memory pruning ───────────────────────────────────────────────\n\nimport {\n MS_PER_DAY,\n DEFAULT_MAX_AGE_DAYS,\n DEFAULT_REPO_TOKEN_BUDGET,\n} from '../utils/constants.js';\n\n/** Rough token estimate: ~4 chars per token. */\nfunction estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n\nexport interface PruneOptions {\n /** Max age in days before summaries are compressed (default: 30). */\n maxAgeDays?: number;\n /** Max token budget per repo (default: 50,000). */\n repoTokenBudget?: number;\n /** Dry run — report what would be pruned without changing anything. */\n dryRun?: boolean;\n}\n\nexport interface PruneResult {\n /** Number of summaries compressed (old summaries replaced with compact versions). */\n compressed: number;\n /** Number of summaries evicted (over budget, removed oldest first). */\n evicted: number;\n /** Token savings from compression. */\n tokensSaved: number;\n /** Per-repo breakdown. */\n repoBreakdown: Record<string, { total: number; budget: number; compressed: number; evicted: number }>;\n}\n\n/**\n * Prune the summary cache to control memory growth.\n *\n * Two strategies:\n * 1. **Age-based compression**: Summaries older than `maxAgeDays` have their\n * verbose fields (importantNotes, patterns, decisions) stripped, keeping\n * only the core summary string — reducing per-entry tokens ~5x.\n * 2. **Token-budget eviction**: For each repo, if total tokens exceed `repoTokenBudget`,\n * the oldest summaries are evicted until within budget.\n *\n * Returns a PruneResult with counts of compressed/evicted entries.\n */\nexport function pruneSummaryCache(options: PruneOptions = {}): PruneResult {\n const maxAgeDays = options.maxAgeDays ?? DEFAULT_MAX_AGE_DAYS;\n const repoTokenBudget = options.repoTokenBudget ?? DEFAULT_REPO_TOKEN_BUDGET;\n const dryRun = options.dryRun ?? false;\n\n const index = readSummaryIndex();\n const now = Date.now();\n const maxAgeMs = maxAgeDays * MS_PER_DAY;\n\n let compressed = 0;\n let evicted = 0;\n let tokensSaved = 0;\n const repoBreakdown: PruneResult['repoBreakdown'] = {};\n\n // Phase 1: Age-based compression\n for (const [id, meta] of Object.entries(index.conversations)) {\n const summaryDate = meta.conversationDate\n ? new Date(meta.conversationDate).getTime()\n : meta.summarizedAt\n ? new Date(meta.summarizedAt).getTime()\n : now;\n\n const age = now - summaryDate;\n if (age <= maxAgeMs) continue;\n\n const summary = loadConversationSummary(id);\n if (!summary) continue;\n\n const beforeTokens = estimateTokens(JSON.stringify(summary.ai));\n\n // Compress: keep only core summary, strip verbose arrays\n const compressedAi = {\n ...summary.ai,\n decisions: [],\n patterns: [],\n importantNotes: summary.ai.importantNotes.length > 0\n ? [summary.ai.importantNotes[0]] // Keep only the first note\n : [],\n filesModified: summary.ai.filesModified.slice(0, 5), // Keep top 5\n toolsUsed: [],\n mcpsUsed: [],\n docsReferenced: [],\n skillsReferenced: [],\n };\n\n const afterTokens = estimateTokens(JSON.stringify(compressedAi));\n const saved = beforeTokens - afterTokens;\n\n if (saved > 0) {\n if (!dryRun) {\n summary.ai = compressedAi;\n saveConversationSummary(summary, index);\n }\n compressed++;\n tokensSaved += saved;\n }\n }\n\n // Phase 2: Per-repo token budget enforcement\n const repoSummaries: Record<string, Array<{ id: string; tokens: number; date: number }>> = {};\n\n for (const [id, meta] of Object.entries(index.conversations)) {\n const repo = meta.repo ?? '__unclassified__';\n if (!repoSummaries[repo]) repoSummaries[repo] = [];\n\n const summary = loadConversationSummary(id);\n const tokens = summary ? estimateTokens(JSON.stringify(summary.ai)) : 0;\n const date = meta.conversationDate\n ? new Date(meta.conversationDate).getTime()\n : meta.summarizedAt\n ? new Date(meta.summarizedAt).getTime()\n : 0;\n\n repoSummaries[repo].push({ id, tokens, date });\n }\n\n for (const [repo, items] of Object.entries(repoSummaries)) {\n // Sort by date, newest first\n items.sort((a, b) => b.date - a.date);\n\n let totalTokens = items.reduce((sum, item) => sum + item.tokens, 0);\n let repoEvicted = 0;\n\n // Evict oldest until within budget\n while (totalTokens > repoTokenBudget && items.length > 1) {\n const oldest = items.pop();\n if (!oldest) break;\n\n totalTokens -= oldest.tokens;\n tokensSaved += oldest.tokens;\n\n if (!dryRun) {\n // Remove from index (keep the file for recovery, but remove index entry)\n delete index.conversations[oldest.id];\n }\n\n repoEvicted++;\n evicted++;\n }\n\n repoBreakdown[repo] = {\n total: items.length,\n budget: repoTokenBudget,\n compressed: 0, // Compression happens in Phase 1, not per-repo\n evicted: repoEvicted,\n };\n }\n\n if (!dryRun) {\n writeSummaryIndex(index);\n }\n\n return { compressed, evicted, tokensSaved, repoBreakdown };\n}\n\n/**\n * Get memory usage stats for the summary cache.\n * Useful for the UI dashboard and diagnostics.\n */\nexport function getSummaryCacheStats(): {\n totalConversations: number;\n totalTokensEstimate: number;\n perRepo: Record<string, { count: number; tokens: number; oldestDate: string | null; newestDate: string | null }>;\n} {\n const index = readSummaryIndex();\n const perRepo: Record<string, { count: number; tokens: number; oldestDate: string | null; newestDate: string | null }> = {};\n let totalTokens = 0;\n\n for (const [id, meta] of Object.entries(index.conversations)) {\n const repo = meta.repo ?? '__unclassified__';\n if (!perRepo[repo]) {\n perRepo[repo] = { count: 0, tokens: 0, oldestDate: null, newestDate: null };\n }\n\n const summary = loadConversationSummary(id);\n const tokens = summary ? estimateTokens(JSON.stringify(summary.ai)) : 0;\n\n perRepo[repo].count++;\n perRepo[repo].tokens += tokens;\n totalTokens += tokens;\n\n const date = meta.conversationDate;\n if (date) {\n if (!perRepo[repo].oldestDate || date < perRepo[repo].oldestDate!) {\n perRepo[repo].oldestDate = date;\n }\n if (!perRepo[repo].newestDate || date > perRepo[repo].newestDate!) {\n perRepo[repo].newestDate = date;\n }\n }\n }\n\n return {\n totalConversations: Object.keys(index.conversations).length,\n totalTokensEstimate: totalTokens,\n perRepo,\n };\n}\n\n// ── Helpers ──────────────────────────────────────────────────────\n\n/**\n * Sanitize a conversation ID for use as a filename.\n * e.g. \"cursor-abc123\" → \"cursor-abc123\"\n * e.g. \"claude-some/weird:id\" → \"claude-some-weird-id\"\n */\nfunction sanitizeId(id: string): string {\n return id.replace(/[^a-zA-Z0-9_-]/g, '-');\n}\n\n/**\n * Convert a repo name to a filesystem-safe slug.\n * e.g. \"wix-private/premium-domains\" → \"premium-domains\"\n */\nexport function repoNameToSlug(name: string): string {\n const base = name.includes('/') ? name.split('/').pop()! : name;\n return base\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '');\n}\n","/**\n * Shared Cursor workspace extraction utilities.\n *\n * Extracts the workspace (git repo root) from Cursor's SQLite composer data\n * and bubble tool-call arguments. Used by both the conversation loader\n * (for repo matching during scan) and the UI API (for the dashboard).\n *\n * Two extraction layers:\n * 1. Composer-level: parses file:// URIs from composer JSON fields\n * 2. Bubble-level: parses absolute paths from tool call rawArgs\n *\n * Both walk up the directory tree to find a .git root.\n */\n\nimport { existsSync, statSync, readFileSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { homedir } from 'node:os';\nimport { parseRemoteOrigin, resolveGitRoot as sharedResolveGitRoot } from './git.js';\n\n// ── Git root cache with TTL ──────────────────────────────────────\n// Module-level cache so repeated lookups for the same directory are O(1).\nconst GIT_ROOT_CACHE_TTL = 5 * 60 * 1000; // 5 minutes\nconst gitRootCache = new Map<string, { value: string | null; expiresAt: number }>();\n\nfunction getCachedGitRoot(dir: string): string | null | undefined {\n const entry = gitRootCache.get(dir);\n if (entry && entry.expiresAt > Date.now()) return entry.value;\n if (entry) gitRootCache.delete(dir); // expired\n return undefined; // cache miss\n}\n\nfunction setCachedGitRoot(dir: string, value: string | null): void {\n gitRootCache.set(dir, { value, expiresAt: Date.now() + GIT_ROOT_CACHE_TTL });\n}\n\n/**\n * Walk up from `filePath` looking for a `.git` entry (directory or file).\n * Returns the repo root path (with [org/repo] suffix), or null if none found.\n *\n * Uses module-level cache with TTL to persist across calls.\n *\n * Handles git worktrees: when `.git` is a **file** containing\n * `gitdir: /path/to/main/.git/worktrees/name`, we follow the reference\n * back to the real repo so worktrees don't appear as separate repos.\n */\nexport function findGitRoot(filePath: string): string | null {\n let dir: string;\n try {\n dir = statSync(filePath).isDirectory() ? filePath : dirname(filePath);\n } catch {\n dir = dirname(filePath);\n }\n\n const home = homedir();\n\n while (dir && dir !== '/' && dir.length >= home.length) {\n const cached = getCachedGitRoot(dir);\n if (cached !== undefined) {\n return cached;\n }\n\n const gitPath = join(dir, '.git');\n if (existsSync(gitPath)) {\n // Resolve the *real* repo root (follows worktree references)\n const resolved = sharedResolveGitRoot(dir, gitPath);\n const repoName = parseRemoteOrigin(resolved.configDir);\n const result = repoName ? `${resolved.root} [${repoName}]` : resolved.root;\n setCachedGitRoot(dir, result);\n return result;\n }\n\n dir = dirname(dir);\n }\n\n setCachedGitRoot(dirname(filePath), null);\n return null;\n}\n\n/**\n * Extract the workspace root from Cursor composer data by parsing file:// URIs\n * from various composer fields and walking up to find a `.git` root.\n *\n * Checks these fields (in order):\n * - allAttachedFileCodeChunksUris\n * - originalFileStates (keys are file URIs)\n * - newlyCreatedFiles\n * - context.mentions.fileSelections / folderSelections / selections\n * - context.fileSelections / context.folderSelections\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function extractWorkspaceFromComposer(composer: any): string | null {\n const uris: string[] = [];\n\n // allAttachedFileCodeChunksUris\n if (Array.isArray(composer.allAttachedFileCodeChunksUris)) {\n uris.push(...composer.allAttachedFileCodeChunksUris);\n }\n\n // originalFileStates – keys are file URIs\n if (composer.originalFileStates && typeof composer.originalFileStates === 'object') {\n uris.push(...Object.keys(composer.originalFileStates));\n }\n\n // newlyCreatedFiles\n if (Array.isArray(composer.newlyCreatedFiles)) {\n uris.push(...composer.newlyCreatedFiles);\n }\n\n // context.mentions.*\n const mentions = composer.context?.mentions;\n if (mentions) {\n if (mentions.fileSelections && typeof mentions.fileSelections === 'object') {\n uris.push(...Object.keys(mentions.fileSelections));\n }\n if (mentions.folderSelections && typeof mentions.folderSelections === 'object') {\n uris.push(...Object.keys(mentions.folderSelections));\n }\n if (mentions.selections && typeof mentions.selections === 'object') {\n for (const selKey of Object.keys(mentions.selections)) {\n try {\n const sel = JSON.parse(selKey);\n if (sel.uri) uris.push(sel.uri);\n } catch { /* skip */ }\n }\n }\n }\n\n // context.fileSelections / context.folderSelections (top-level, may be dict or array)\n const ctxFs = composer.context?.fileSelections;\n if (ctxFs) {\n if (typeof ctxFs === 'object' && !Array.isArray(ctxFs)) {\n uris.push(...Object.keys(ctxFs));\n } else if (Array.isArray(ctxFs)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n uris.push(...ctxFs.filter((u: any) => typeof u === 'string'));\n }\n }\n const ctxFol = composer.context?.folderSelections;\n if (ctxFol && typeof ctxFol === 'object' && !Array.isArray(ctxFol)) {\n uris.push(...Object.keys(ctxFol));\n }\n\n // ── Convert file:// URIs to local paths ────────────────────────\n const paths: string[] = [];\n for (const uri of uris) {\n if (typeof uri === 'string' && uri.startsWith('file:///')) {\n paths.push(decodeURIComponent(uri.replace('file://', '')));\n }\n }\n if (paths.length === 0) return null;\n\n // ── Derive workspace root via .git discovery ───────────────────\n // Only assign a project if the files belong to an actual git repo.\n for (const filePath of paths) {\n const gitRoot = findGitRoot(filePath);\n if (gitRoot) return gitRoot;\n }\n\n return null;\n}\n\n// extractWorkspaceFromBubbles has been moved to src/data-sources/cursor.ts\n// It now lives inside the CursorDataSource class (extractWorkspaceFromBubblesSync)\n// and no longer requires a queryFn parameter.\n\n/**\n * Parse a findGitRoot result string (e.g. \"/path/to/repo [org/repo]\")\n * into separate repoPath and repoName.\n */\nexport function parseGitRootResult(gitRootResult: string): {\n repoPath: string;\n repoName: string | null;\n} {\n const match = gitRootResult.match(/^(.+?)\\s+\\[(.+?)\\]$/);\n if (match) {\n return { repoPath: match[1], repoName: match[2] };\n }\n return { repoPath: gitRootResult, repoName: null };\n}\n","import { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { logger } from '../utils/logger.js';\nimport {\n extractWorkspaceFromComposer,\n findGitRoot,\n parseGitRootResult,\n} from '../utils/cursor-workspace.js';\nimport { openCursorDb, queryRows, getDbPath } from './cursor-db.js';\nimport type { KnownRepo } from '../utils/paths.js';\nimport type { FullConversation, ToolUsageEntry } from '../summarizer/types.js';\nimport type {\n CursorDataSourceExt,\n ConversationSummary,\n ConversationDetail,\n ToolUsage,\n SkillUsage,\n SqlDatabase,\n SqlValue,\n} from './types.js';\n\n// ── Internal Cursor data shapes ──────────────────────────────────\n\ninterface ComposerData {\n composerId: string;\n createdAt?: number;\n lastUpdatedAt?: number;\n unifiedMode?: string;\n name?: string;\n fullConversationHeadersOnly: Array<{\n bubbleId: string;\n type: number;\n }>;\n}\n\ninterface DetailBubbleData {\n type: number;\n text?: string;\n attachedFileCodeChunksMetadataOnly?: Array<{\n relativeWorkspacePath: string;\n }>;\n toolFormerData?: {\n name: string;\n status?: string;\n };\n}\n\ninterface LoaderBubbleData {\n type: number;\n text?: string;\n attachedFileCodeChunksMetadataOnly?: Array<{\n relativeWorkspacePath: string;\n }>;\n toolFormerData?: Array<{\n toolName?: string;\n }>;\n}\n\ninterface ToolCallArgs {\n target_file?: string;\n targetFile?: string;\n path?: string;\n file_path?: string;\n filePath?: string;\n directory?: string;\n dir_path?: string;\n command?: string;\n}\n\ninterface TurnData {\n role: 'user' | 'assistant';\n text: string;\n files: string[];\n tools: ToolUsageEntry[];\n}\n\n// ── SQL helpers ──────────────────────────────────────────────────\n\n/** Safely parse a JSON string returned by json_extract; returns undefined on null/failure. */\nfunction safeJsonParse(raw: string | null | undefined): unknown {\n if (raw == null) return undefined;\n try { return JSON.parse(raw); } catch { return undefined; }\n}\n\nasync function withDb<T>(fn: (db: SqlDatabase) => Promise<T> | T): Promise<T | null> {\n const db = await openCursorDb();\n if (!db) return null;\n try {\n const result = await fn(db);\n db.close();\n return result;\n } catch (err) {\n try { db.close(); } catch { /* already closed */ }\n throw err;\n }\n}\n\n// ── MCP tool name parser ─────────────────────────────────────────\n\nexport function parseMcpToolName(name: string): { server: string; tool: string } | null {\n if (!name.startsWith('mcp')) return null;\n\n if (name.startsWith('mcp_')) {\n const rest = name.slice(4);\n const underscoreIdx = rest.indexOf('_');\n if (underscoreIdx === -1) return null;\n return { server: rest.slice(0, underscoreIdx), tool: rest.slice(underscoreIdx + 1) };\n }\n\n const userIdx = name.indexOf('-user-');\n if (userIdx !== -1) {\n const serverPart = name.slice(4, userIdx);\n const afterUser = name.slice(userIdx + 6);\n const dashIdx = afterUser.indexOf('-');\n if (dashIdx !== -1) {\n return { server: serverPart, tool: afterUser.slice(dashIdx + 1) };\n }\n }\n\n return null;\n}\n\n// ── Timestamp normalizer ─────────────────────────────────────────\n\nfunction normalizeTimestamp(raw: string | number | null | undefined): string | null {\n if (!raw) return null;\n const str = String(raw);\n const asNum = Number(str);\n if (!isNaN(asNum) && str.length > 10) {\n return new Date(asNum).toISOString();\n }\n if (str.includes('T') || str.includes('-')) {\n return str;\n }\n return null;\n}\n\n// ── Workspace extraction from bubble tool args ───────────────────\n\nasync function extractWorkspaceFromBubbles(\n db: SqlDatabase,\n composerId: string,\n): Promise<string | null> {\n try {\n const rows = await queryRows<{ raw_args: string }>(\n db,\n `SELECT json_extract(value, '$.toolFormerData.rawArgs') as raw_args\n FROM cursorDiskKV\n WHERE key >= ? AND key < ?\n AND json_extract(value, '$.toolFormerData.rawArgs') IS NOT NULL\n LIMIT 10`,\n [`bubbleId:${composerId}:`, `bubbleId:${composerId};`],\n );\n return resolveWorkspaceFromRawArgs(rows.map((r) => r.raw_args));\n } catch { /* ignore */ }\n return null;\n}\n\nasync function batchExtractWorkspacesFromBubbles(\n db: SqlDatabase,\n composerIds: string[],\n): Promise<Map<string, string>> {\n const result = new Map<string, string>();\n if (composerIds.length === 0) return result;\n\n // Process in batches — targeted range queries per composerId avoid the\n // full-table scan that the old `key LIKE 'bubbleId:%'` approach required.\n const BATCH_SIZE = 20;\n for (let i = 0; i < composerIds.length; i += BATCH_SIZE) {\n const batch = composerIds.slice(i, i + BATCH_SIZE);\n\n // Build targeted range conditions: bubbleId:{composerId}: … bubbleId:{composerId};\n const conditions = batch.map(() => '(key >= ? AND key < ?)').join(' OR ');\n const params: SqlValue[] = [];\n for (const id of batch) {\n params.push(`bubbleId:${id}:`, `bubbleId:${id};`);\n }\n\n try {\n const rows = await queryRows<{ key: string; raw_args: string }>(\n db,\n `SELECT key, json_extract(value, '$.toolFormerData.rawArgs') as raw_args\n FROM cursorDiskKV\n WHERE (${conditions})\n AND json_extract(value, '$.toolFormerData.rawArgs') IS NOT NULL`,\n params,\n );\n\n for (const row of rows) {\n const parts = (row.key as string).split(':');\n if (parts.length < 3) continue;\n const composerId = parts[1];\n\n if (result.has(composerId)) continue;\n\n const workspace = resolveWorkspaceFromRawArgs([row.raw_args]);\n if (workspace) {\n result.set(composerId, workspace);\n }\n }\n } catch { /* ignore */ }\n\n // Early exit if all resolved\n if (result.size === composerIds.length) break;\n }\n\n return result;\n}\n\nfunction resolveWorkspaceFromRawArgs(rawArgsList: (string | null | undefined)[]): string | null {\n for (const rawArgs of rawArgsList) {\n if (!rawArgs) continue;\n try {\n const args: ToolCallArgs = JSON.parse(rawArgs);\n const filePath = args.target_file ?? args.targetFile ?? args.path\n ?? args.file_path ?? args.filePath ?? args.directory ?? args.dir_path;\n if (typeof filePath === 'string' && filePath.startsWith('/')) {\n const gitRoot = findGitRoot(filePath);\n if (gitRoot) return gitRoot;\n }\n if (typeof args.command === 'string') {\n const absPathMatch = args.command.match(/(?:^|\\s)(\\/[^\\s]+)/);\n if (absPathMatch) {\n const gitRoot = findGitRoot(absPathMatch[1]);\n if (gitRoot) return gitRoot;\n }\n }\n } catch { /* skip */ }\n }\n return null;\n}\n\n// ── Bubble readers ───────────────────────────────────────────────\n\nasync function readBubblesForDetail(\n db: SqlDatabase,\n composerId: string,\n headers: Array<{ bubbleId: string }>,\n): Promise<ConversationDetail['messages']> {\n const messages: ConversationDetail['messages'] = [];\n if (headers.length === 0) return messages;\n\n const bubbleKeys = headers.map((h) => `bubbleId:${composerId}:${h.bubbleId}`);\n const placeholders = bubbleKeys.map(() => '?').join(',');\n const allBubbleRows = await queryRows<{ key: string; value: string }>(\n db,\n `SELECT key, value FROM cursorDiskKV WHERE key IN (${placeholders})`,\n bubbleKeys,\n );\n\n const bubbleMap = new Map<string, string>();\n for (const row of allBubbleRows) {\n bubbleMap.set(row.key, row.value);\n }\n\n for (const header of headers) {\n const key = `bubbleId:${composerId}:${header.bubbleId}`;\n const bubbleValue = bubbleMap.get(key);\n if (!bubbleValue) continue;\n\n try {\n const bubble: DetailBubbleData = JSON.parse(bubbleValue);\n const role: 'user' | 'assistant' = bubble.type === 1 ? 'user' : 'assistant';\n const text = bubble.text ?? '';\n\n const files: string[] = [];\n if (bubble.attachedFileCodeChunksMetadataOnly) {\n for (const chunk of bubble.attachedFileCodeChunksMetadataOnly) {\n if (chunk.relativeWorkspacePath) files.push(chunk.relativeWorkspacePath);\n }\n }\n\n const toolCalls: Array<{ name: string; status?: string }> = [];\n if (bubble.toolFormerData?.name) {\n toolCalls.push({\n name: bubble.toolFormerData.name,\n status: bubble.toolFormerData.status,\n });\n }\n\n if (text.trim() || toolCalls.length > 0) {\n messages.push({ role, text: text.trim(), files, toolCalls });\n }\n } catch (err) {\n logger.debug(`Failed to parse bubble ${key}: ${err}`);\n }\n }\n\n return messages;\n}\n\nasync function readBubblesForLoader(\n db: SqlDatabase,\n composerId: string,\n headers: Array<{ bubbleId: string; type: number }>,\n): Promise<TurnData[]> {\n const turns: TurnData[] = [];\n\n for (const header of headers) {\n const key = `bubbleId:${composerId}:${header.bubbleId}`;\n\n try {\n const result = await db.exec('SELECT value FROM cursorDiskKV WHERE key = ?', [key]);\n if (result.length === 0 || result[0].values.length === 0) continue;\n\n const rawValue = result[0].values[0][0];\n if (typeof rawValue !== 'string') continue;\n\n const bubble: LoaderBubbleData = JSON.parse(rawValue);\n const role: 'user' | 'assistant' = bubble.type === 1 ? 'user' : 'assistant';\n const text = bubble.text ?? '';\n\n const files: string[] = [];\n if (bubble.attachedFileCodeChunksMetadataOnly) {\n for (const chunk of bubble.attachedFileCodeChunksMetadataOnly) {\n if (chunk.relativeWorkspacePath) files.push(chunk.relativeWorkspacePath);\n }\n }\n\n const tools: ToolUsageEntry[] = [];\n if (bubble.toolFormerData) {\n for (const t of bubble.toolFormerData) {\n if (t.toolName) {\n tools.push({ name: t.toolName, filesAffected: [] });\n }\n }\n }\n\n if (text.trim()) {\n turns.push({ role, text: text.trim(), files, tools });\n }\n } catch {\n // skip unreadable bubbles\n }\n }\n\n return turns;\n}\n\n// ── Title extraction ─────────────────────────────────────────────\n\nfunction extractTitleFromComposer(composer: ComposerData): string {\n return `Cursor session ${(composer.composerId ?? '').slice(0, 8)}...`;\n}\n\nfunction extractTitleFromTurns(turns: TurnData[], composerName?: string): string {\n if (composerName) return composerName;\n const firstUser = turns.find((t) => t.role === 'user');\n if (firstUser?.text) {\n const firstLine = firstUser.text.split('\\n')[0];\n return firstLine.length > 80 ? firstLine.slice(0, 77) + '...' : firstLine;\n }\n return 'Untitled conversation';\n}\n\n// ── Repo resolution helper ───────────────────────────────────────\n\nfunction resolveRepoFromClassification(assigned: string | null): {\n repoName: string | null;\n repoPath: string | null;\n} {\n if (!assigned) return { repoName: null, repoPath: null };\n const match = assigned.match(/^(.+?)\\s+\\[(.+?)\\]$/);\n if (match) {\n return { repoPath: match[1], repoName: match[2] };\n }\n return { repoPath: assigned, repoName: null };\n}\n\n// ── CursorDataSource implementation ──────────────────────────────\n\nexport class CursorDataSource implements CursorDataSourceExt {\n readonly name = 'cursor' as const;\n\n async listConversations(): Promise<ConversationSummary[]> {\n const t0 = Date.now();\n const db = await openCursorDb();\n if (!db) return [];\n\n try {\n const t1 = Date.now();\n // Use json_extract to avoid transferring large fullConversationHeadersOnly arrays;\n // json_array_length gives us the count without deserializing the array.\n const rows = await queryRows<{\n key: string;\n composerId: string;\n name: string | null;\n mode: string | null;\n createdAt: number | null;\n headerCount: number | null;\n attachedUris: string | null;\n fileStates: string | null;\n newFiles: string | null;\n context: string | null;\n }>(\n db,\n `SELECT\n key,\n json_extract(value, '$.composerId') AS composerId,\n json_extract(value, '$.name') AS name,\n json_extract(value, '$.unifiedMode') AS mode,\n json_extract(value, '$.createdAt') AS createdAt,\n json_array_length(value, '$.fullConversationHeadersOnly') AS headerCount,\n json_extract(value, '$.allAttachedFileCodeChunksUris') AS attachedUris,\n json_extract(value, '$.originalFileStates') AS fileStates,\n json_extract(value, '$.newlyCreatedFiles') AS newFiles,\n json_extract(value, '$.context') AS context\n FROM cursorDiskKV\n WHERE key >= 'composerData:' AND key < 'composerData;'`,\n );\n const t2 = Date.now();\n\n const results: ConversationSummary[] = [];\n\n for (const row of rows) {\n try {\n const composerId = row.composerId as string | undefined;\n if (!composerId || composerId.startsWith('task-')) continue;\n const headerCount = (row.headerCount as number | null) ?? 0;\n if (headerCount === 0) continue;\n\n // Reconstruct a minimal composer shape for extractWorkspaceFromComposer\n // (only the fields it inspects — avoids parsing the huge headers array).\n const minimalComposer = {\n allAttachedFileCodeChunksUris: safeJsonParse(row.attachedUris as string | null),\n originalFileStates: safeJsonParse(row.fileStates as string | null),\n newlyCreatedFiles: safeJsonParse(row.newFiles as string | null),\n context: safeJsonParse(row.context as string | null),\n };\n\n results.push({\n id: `cursor-${composerId}`,\n source: 'cursor',\n title: (row.name as string) || `Cursor session ${composerId.slice(0, 8)}...`,\n mode: (row.mode as string) ?? 'unknown',\n createdAt: row.createdAt ? new Date(row.createdAt as number).toISOString() : null,\n messageCount: headerCount,\n project: extractWorkspaceFromComposer(minimalComposer) ?? undefined,\n });\n } catch (err) {\n logger.debug(`Failed to parse Cursor conversation ${row.key}: ${err}`);\n }\n }\n const t3 = Date.now();\n\n const unmatched = results.filter((r) => !r.project);\n if (unmatched.length > 0) {\n const composerIds = unmatched.map((c) => c.id.replace('cursor-', ''));\n const resolved = await batchExtractWorkspacesFromBubbles(db, composerIds);\n for (const conv of unmatched) {\n const composerId = conv.id.replace('cursor-', '');\n const project = resolved.get(composerId);\n if (project) conv.project = project;\n }\n }\n const t4 = Date.now();\n\n db.close();\n\n logger.info(\n `[cursor] listConversations: ${results.length} convos from ${rows.length} rows | ` +\n `openDb=${t1 - t0}ms, query=${t2 - t1}ms, parse+workspace=${t3 - t2}ms, ` +\n `bubbleFallback=${t4 - t3}ms (${unmatched.length} unmatched), total=${t4 - t0}ms`,\n );\n\n return results;\n } catch {\n db.close();\n return [];\n }\n }\n\n async getConversationDetail(id: string): Promise<ConversationDetail | null> {\n const composerId = id.replace('cursor-', '');\n const db = await openCursorDb();\n if (!db) return null;\n\n try {\n const rows = await queryRows<{ value: string }>(\n db,\n 'SELECT value FROM cursorDiskKV WHERE key = ?',\n [`composerData:${composerId}`],\n );\n\n if (rows.length === 0) {\n db.close();\n return null;\n }\n\n const composer: ComposerData = JSON.parse(rows[0].value);\n const headers = composer.fullConversationHeadersOnly ?? [];\n const messages = await readBubblesForDetail(db, composerId, headers);\n\n db.close();\n\n return {\n id,\n source: 'cursor',\n title: composer.name || `Cursor session ${composerId.slice(0, 8)}...`,\n mode: composer.unifiedMode ?? 'unknown',\n createdAt: composer.createdAt ? new Date(composer.createdAt).toISOString() : null,\n messageCount: messages.length,\n project: extractWorkspaceFromComposer(composer) ?? undefined,\n messages,\n };\n } catch {\n db.close();\n return null;\n }\n }\n\n async getToolUsage(): Promise<ToolUsage[]> {\n const result = await withDb(async (db) => {\n const rows = await queryRows<{ tool_name: string; cnt: string; last_created: string }>(\n db,\n `SELECT \n json_extract(value, '$.toolFormerData.name') as tool_name,\n COUNT(*) as cnt,\n MAX(json_extract(value, '$.createdAt')) as last_created\n FROM cursorDiskKV \n WHERE key >= 'bubbleId:' AND key < 'bubbleId;' \n AND json_extract(value, '$.toolFormerData.name') IS NOT NULL\n GROUP BY tool_name\n ORDER BY cnt DESC`,\n );\n\n const mcpCount = rows.filter((r) => parseMcpToolName(r.tool_name)).length;\n logger.debug(`[cursor] getToolUsage: ${rows.length} distinct tools, ${mcpCount} MCP tools`);\n\n return rows.map((row) => {\n const parsed = parseMcpToolName(row.tool_name);\n return {\n name: row.tool_name,\n count: parseInt(String(row.cnt), 10),\n lastUsed: normalizeTimestamp(row.last_created),\n isMcp: !!parsed,\n mcpServer: parsed?.server,\n };\n });\n });\n\n if (!result) logger.warn('[cursor] getToolUsage: DB unavailable');\n\n return result ?? [];\n }\n\n async getSkillUsage(): Promise<SkillUsage[]> {\n const skillMap = new Map<string, SkillUsage>();\n\n const skillDirs = [\n join(homedir(), '.cursor', 'skills'),\n join(homedir(), '.cursor', 'skills-cursor'),\n join(homedir(), '.claude', 'skills'),\n ];\n\n for (const dir of skillDirs) {\n if (!existsSync(dir)) continue;\n try {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const skillMdPath = join(dir, entry.name, 'SKILL.md');\n if (!existsSync(skillMdPath)) continue;\n if (!skillMap.has(entry.name)) {\n skillMap.set(entry.name, { name: entry.name, path: skillMdPath, count: 0, lastUsed: null });\n }\n }\n } catch { /* ignore */ }\n }\n\n const db = await openCursorDb();\n if (!db) logger.warn('[cursor] getSkillUsage: DB unavailable');\n if (db) {\n try {\n const rows = await queryRows<{ raw_args: string; created: string }>(\n db,\n `SELECT \n json_extract(value, '$.toolFormerData.rawArgs') as raw_args,\n json_extract(value, '$.createdAt') as created\n FROM cursorDiskKV \n WHERE key >= 'bubbleId:' AND key < 'bubbleId;' \n AND json_extract(value, '$.toolFormerData.name') IN ('read_file', 'read_file_v2')\n AND json_extract(value, '$.toolFormerData.rawArgs') LIKE '%SKILL.md%'`,\n );\n\n logger.debug(`[cursor] getSkillUsage: ${rows.length} SKILL.md hits`);\n db.close();\n\n for (const row of rows) {\n if (!row.raw_args) continue;\n\n let filePath = '';\n try {\n const args: ToolCallArgs = JSON.parse(row.raw_args);\n filePath = args.target_file ?? args.targetFile ?? args.path ?? '';\n } catch {\n if (!row.raw_args.match(/SKILL\\.md/)) continue;\n filePath = row.raw_args;\n }\n\n if (!filePath.includes('SKILL.md')) continue;\n\n const parts = filePath.split('/');\n const skillIdx = parts.findIndex((p) => p === 'SKILL.md');\n const skillName = skillIdx > 0 ? parts[skillIdx - 1] : 'unknown';\n const created = normalizeTimestamp(row.created);\n\n const existing = skillMap.get(skillName);\n if (existing) {\n existing.count++;\n if (created && (!existing.lastUsed || created > existing.lastUsed)) {\n existing.lastUsed = created;\n }\n } else {\n skillMap.set(skillName, { name: skillName, path: filePath, count: 1, lastUsed: created });\n }\n }\n } catch {\n db.close();\n }\n }\n\n const skills = Array.from(skillMap.values()).sort((a, b) => b.count - a.count);\n logger.debug(`[cursor] getSkillUsage: ${skills.length} total, ${skills.filter((s) => s.count > 0).length} with usage`);\n return skills;\n }\n\n async extractWorkspaceFromBubbles(composerId: string): Promise<string | null> {\n const result = await withDb((db) => extractWorkspaceFromBubbles(db, composerId));\n return result ?? null;\n }\n\n async loadFullConversations(\n classCache: Map<string, { assignedRepo: string | null }>,\n knownRepos: KnownRepo[],\n ): Promise<FullConversation[]> {\n const dbPath = getDbPath();\n if (!existsSync(dbPath)) return [];\n\n const diagnostics: string[] = [];\n const db = await openCursorDb(diagnostics);\n if (!db) {\n // Throw so the loader's try/catch can surface this via onOutput\n throw new Error(\n `Cursor DB not available (path: ${dbPath}, exists: ${existsSync(dbPath)})\\n` +\n diagnostics.map((d) => ` ${d}`).join('\\n'),\n );\n }\n\n const results: FullConversation[] = [];\n\n try {\n const composerResult = await db.exec(\n \"SELECT key, value FROM cursorDiskKV WHERE key >= 'composerData:' AND key < 'composerData;'\",\n );\n\n if (composerResult.length === 0 || composerResult[0].values.length === 0) {\n db.close();\n return [];\n }\n\n for (const row of composerResult[0].values) {\n const rawValue = row[1];\n if (typeof rawValue !== 'string') continue;\n\n let composer: ComposerData;\n try {\n composer = JSON.parse(rawValue);\n } catch {\n continue;\n }\n\n if (composer.composerId.startsWith('task-')) continue;\n const headers = composer.fullConversationHeadersOnly;\n if (!headers || headers.length === 0) continue;\n\n const turns = await readBubblesForLoader(db, composer.composerId, headers);\n if (turns.length === 0) continue;\n\n const id = `cursor-${composer.composerId}`;\n const title = extractTitleFromTurns(turns, composer.name);\n\n const transcriptParts: string[] = [];\n const allFiles: string[] = [];\n const toolUsage: ToolUsageEntry[] = [];\n\n for (const turn of turns) {\n transcriptParts.push(`[${turn.role}]: ${turn.text}`);\n allFiles.push(...turn.files);\n toolUsage.push(...turn.tools);\n }\n\n const classification = classCache.get(id);\n let { repoName, repoPath } = resolveRepoFromClassification(classification?.assignedRepo ?? null);\n\n if (!repoName && !repoPath) {\n const fullComposer: ComposerData = JSON.parse(rawValue);\n let workspace = extractWorkspaceFromComposer(fullComposer);\n\n if (!workspace) {\n workspace = await extractWorkspaceFromBubbles(db, composer.composerId);\n }\n\n if (workspace) {\n const parsed = parseGitRootResult(workspace);\n repoPath = parsed.repoPath;\n repoName = parsed.repoName;\n\n if (!repoName && repoPath) {\n const match = knownRepos.find((r) => r.path === repoPath);\n if (match) repoName = match.remoteName ?? match.shortName;\n }\n }\n }\n\n const firstUserTurn = turns.find((t) => t.role === 'user');\n results.push({\n id,\n source: 'cursor',\n title,\n createdAt: composer.createdAt ? new Date(composer.createdAt).toISOString() : null,\n repoName,\n repoPath,\n messageCount: turns.length,\n firstUserMessage: firstUserTurn?.text ?? null,\n transcript: transcriptParts.join('\\n\\n'),\n toolUsage,\n filesMentioned: [...new Set(allFiles)],\n sourceRef: {\n type: 'cursor',\n dbPath,\n composerId: composer.composerId,\n },\n });\n }\n\n db.close();\n } catch (err) {\n logger.error('Failed to load Cursor conversations:', err);\n }\n\n return results;\n }\n}\n","/**\n * Claude Code data source — reads JSONL session files.\n *\n * Centralizes ALL Claude Code JSONL parsing into one file.\n * Session files live at: ~/.claude/projects/{encoded-path}/{sessionId}.jsonl\n */\n\nimport { readdir, readFile, stat, writeFile as fsWriteFile } from 'node:fs/promises';\nimport { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { createReadStream } from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport {\n getClaudeCodeBaseDir,\n decodeClaudeProjectPath,\n resolveClaudeProjectToRepo,\n discoverKnownRepos,\n type KnownRepo,\n} from '../utils/paths.js';\nimport { findGitRoot } from '../utils/cursor-workspace.js';\nimport { logger } from '../utils/logger.js';\nimport { parseMcpToolName } from './cursor.js';\nimport type { FullConversation, ToolUsageEntry } from '../summarizer/types.js';\nimport type {\n ConversationDataSource,\n ConversationSummary,\n ConversationDetail,\n ToolUsage,\n} from './types.js';\nimport { DEFAULT_CACHE_TTL_MS } from '../utils/constants.js';\n\n// ── Internal JSONL types ─────────────────────────────────────────\n\ninterface ClaudeMessage {\n type: 'user' | 'assistant' | 'summary' | 'system' | string;\n sessionId: string;\n timestamp: string;\n cwd?: string;\n gitBranch?: string;\n message?: {\n role: 'user' | 'assistant';\n content: string | ContentBlock[];\n };\n summary?: string;\n}\n\ninterface ContentBlock {\n type: string;\n text?: string;\n name?: string;\n input?: Record<string, unknown>;\n}\n\n/**\n * Lightweight entry shape used when parsing JSONL for the dashboard\n * (listConversations, getConversationDetail, getToolUsage).\n * Shares structure with ClaudeMessage but all fields are optional\n * because each JSONL line may be a subset.\n */\ninterface ClaudeJsonlEntry {\n type?: string;\n timestamp?: string;\n summary?: string;\n message?: {\n role?: string;\n content?: string | ContentBlock[];\n };\n}\n\n// ── Known repos cache with TTL ───────────────────────────────────\n\nlet knownReposCache: { repos: KnownRepo[]; expiresAt: number } | null = null;\nconst KNOWN_REPOS_CACHE_TTL = DEFAULT_CACHE_TTL_MS;\n\nfunction getCachedKnownRepos(): KnownRepo[] {\n if (knownReposCache && knownReposCache.expiresAt > Date.now()) {\n return knownReposCache.repos;\n }\n const repos = discoverKnownRepos();\n knownReposCache = { repos, expiresAt: Date.now() + KNOWN_REPOS_CACHE_TTL };\n return repos;\n}\n\n// ── Project resolution helper ────────────────────────────────────\n\nfunction resolveProject(\n projDirName: string,\n knownRepos?: KnownRepo[],\n): string | undefined {\n const repos = knownRepos ?? getCachedKnownRepos();\n const prefixMatch = resolveClaudeProjectToRepo(projDirName, repos);\n if (prefixMatch) {\n const label = prefixMatch.remoteName ?? prefixMatch.shortName;\n return `${prefixMatch.path} [${label}]`;\n }\n const projectPath = decodeClaudeProjectPath(projDirName);\n return findGitRoot(projectPath) ?? undefined;\n}\n\n// ── Repo resolution helper ───────────────────────────────────────\n\nfunction resolveRepoFromClassification(assigned: string | null): {\n repoName: string | null;\n repoPath: string | null;\n} {\n if (!assigned) return { repoName: null, repoPath: null };\n const match = assigned.match(/^(.+?)\\s+\\[(.+?)\\]$/);\n if (match) {\n return { repoPath: match[1], repoName: match[2] };\n }\n return { repoPath: assigned, repoName: null };\n}\n\n// ── File extraction from tool_use ────────────────────────────────\n\nfunction extractFilesFromTool(\n toolName: string,\n input: Record<string, unknown>,\n): string[] {\n const files: string[] = [];\n switch (toolName) {\n case 'Write':\n case 'Read':\n case 'StrReplace':\n case 'Delete':\n if (typeof input.path === 'string') files.push(input.path);\n break;\n case 'EditNotebook':\n if (typeof input.target_notebook === 'string') files.push(input.target_notebook);\n break;\n }\n return files;\n}\n\n// ── Metadata index for listConversations ─────────────────────────\n// Avoids re-reading JSONL files that haven't changed since last parse.\n// Uses file mtime to detect changes. Persisted to disk so it survives restarts.\n\ninterface MetadataIndexEntry {\n mtimeMs: number;\n summary: ConversationSummary;\n}\n\n/** Serialized shape of the on-disk index file. */\ninterface SerializedIndex {\n version: 1;\n entries: Record<string, { mtimeMs: number; summary: ConversationSummary }>;\n}\n\n/** In-memory index keyed by absolute file path. */\nlet metadataIndex: Map<string, MetadataIndexEntry> | null = null;\n\nfunction getIndexPath(): string {\n return join(getClaudeCodeBaseDir(), '.lore-metadata-index.json');\n}\n\nfunction loadIndexFromDisk(): Map<string, MetadataIndexEntry> {\n const indexPath = getIndexPath();\n try {\n if (!existsSync(indexPath)) return new Map();\n const raw = readFileSync(indexPath, 'utf-8');\n const parsed: SerializedIndex = JSON.parse(raw);\n if (parsed.version !== 1) return new Map();\n return new Map(Object.entries(parsed.entries));\n } catch {\n return new Map();\n }\n}\n\nfunction saveIndexToDisk(index: Map<string, MetadataIndexEntry>): void {\n const indexPath = getIndexPath();\n const serialized: SerializedIndex = {\n version: 1,\n entries: Object.fromEntries(index),\n };\n // Fire and forget — non-blocking, failure is non-fatal\n fsWriteFile(indexPath, JSON.stringify(serialized), 'utf-8').catch(() => {\n /* ignore */\n });\n}\n\n/**\n * Parse a single JSONL file to extract a ConversationSummary.\n * Extracted as a standalone function so it can be called selectively\n * (only for new / changed files).\n */\nfunction parseJsonlForSummary(\n filePath: string,\n sessionId: string,\n resolvedProject: string | undefined,\n): ConversationSummary | null {\n try {\n const content = readFileSync(filePath, 'utf-8');\n const lines = content.split('\\n').filter((l) => l.trim());\n\n let title = '';\n let createdAt: string | null = null;\n let messageCount = 0;\n\n for (const line of lines) {\n try {\n const entry: ClaudeJsonlEntry = JSON.parse(line);\n if (entry.type === 'summary' && entry.summary) {\n title = entry.summary;\n }\n if (entry.type === 'user' || entry.type === 'assistant') {\n messageCount++;\n }\n if (!createdAt && entry.timestamp) {\n createdAt = entry.timestamp;\n }\n } catch {\n continue;\n }\n }\n\n if (messageCount === 0) return null;\n\n // Extract title from first user message if no summary\n if (!title) {\n for (const line of lines) {\n try {\n const entry: ClaudeJsonlEntry = JSON.parse(line);\n if (entry.type === 'user') {\n const msg = entry.message?.content;\n if (typeof msg === 'string') {\n title = msg.split('\\n')[0].slice(0, 80);\n } else if (Array.isArray(msg)) {\n const textBlock = msg.find((b): b is ContentBlock => b.type === 'text');\n if (textBlock?.text) {\n title = textBlock.text.split('\\n')[0].slice(0, 80);\n }\n }\n break;\n }\n } catch {\n continue;\n }\n }\n }\n\n return {\n id: `claude-${sessionId}`,\n source: 'claude-code',\n title: title || `Session ${sessionId.slice(0, 8)}...`,\n mode: 'claude-code',\n createdAt,\n messageCount,\n project: resolvedProject,\n };\n } catch {\n return null;\n }\n}\n\n// ── ClaudeCodeDataSource implementation ──────────────────────────\n\nexport class ClaudeCodeDataSource implements ConversationDataSource {\n readonly name = 'claude-code' as const;\n\n // ── listConversations ────────────────────────────────────────\n // Uses a file-mtime metadata index to skip re-reading unchanged JSONL files.\n // On first call: parses all files, builds index, persists to disk.\n // On subsequent calls: stat() each file, only re-parse changed / new files.\n\n async listConversations(): Promise<ConversationSummary[]> {\n const baseDir = getClaudeCodeBaseDir();\n if (!existsSync(baseDir)) return [];\n\n // Load index (from memory or disk)\n if (!metadataIndex) {\n metadataIndex = loadIndexFromDisk();\n }\n\n const results: ConversationSummary[] = [];\n const knownRepos = getCachedKnownRepos();\n const seenPaths = new Set<string>();\n let indexDirty = false;\n\n try {\n const dirEntries = await readdir(baseDir, { withFileTypes: true });\n const projectDirs = dirEntries.filter((d) => d.isDirectory());\n\n for (const projDir of projectDirs) {\n const projFullPath = join(baseDir, projDir.name);\n const resolvedProject = resolveProject(projDir.name, knownRepos);\n\n let files: string[];\n try {\n files = (await readdir(projFullPath)).filter((f) => f.endsWith('.jsonl'));\n } catch {\n continue;\n }\n\n for (const file of files) {\n const sessionId = file.replace('.jsonl', '');\n const filePath = join(projFullPath, file);\n seenPaths.add(filePath);\n\n try {\n const fileStat = await stat(filePath);\n const cached = metadataIndex.get(filePath);\n\n if (cached && cached.mtimeMs === fileStat.mtimeMs) {\n // File unchanged — use cached summary (update project in case repos changed)\n cached.summary.project = resolvedProject;\n results.push(cached.summary);\n continue;\n }\n\n // File is new or changed — parse it\n const summary = parseJsonlForSummary(filePath, sessionId, resolvedProject);\n if (summary) {\n results.push(summary);\n metadataIndex.set(filePath, { mtimeMs: fileStat.mtimeMs, summary });\n indexDirty = true;\n } else {\n // Empty / unparseable — remove from index if it was there\n if (cached) {\n metadataIndex.delete(filePath);\n indexDirty = true;\n }\n }\n } catch (err) {\n logger.debug(`Failed to read Claude conversation ${filePath}: ${err}`);\n }\n }\n }\n } catch (err) {\n logger.debug(`Failed to read Claude Code base dir: ${err}`);\n }\n\n // Prune deleted files from the index\n for (const indexedPath of metadataIndex.keys()) {\n if (!seenPaths.has(indexedPath)) {\n metadataIndex.delete(indexedPath);\n indexDirty = true;\n }\n }\n\n // Persist index to disk if anything changed\n if (indexDirty) {\n saveIndexToDisk(metadataIndex);\n }\n\n return results;\n }\n\n // ── getConversationDetail ────────────────────────────────────\n\n async getConversationDetail(id: string): Promise<ConversationDetail | null> {\n const sessionId = id.replace('claude-', '');\n const baseDir = getClaudeCodeBaseDir();\n if (!existsSync(baseDir)) return null;\n\n try {\n const dirEntries = await readdir(baseDir, { withFileTypes: true });\n const projectDirs = dirEntries.filter((d) => d.isDirectory());\n\n for (const projDir of projectDirs) {\n const filePath = join(baseDir, projDir.name, `${sessionId}.jsonl`);\n if (!existsSync(filePath)) continue;\n\n const resolvedProject = resolveProject(projDir.name);\n\n const content = await readFile(filePath, 'utf-8');\n const lines = content.split('\\n').filter((l) => l.trim());\n\n const messages: ConversationDetail['messages'] = [];\n let title = '';\n let createdAt: string | null = null;\n\n for (const line of lines) {\n try {\n const entry: ClaudeJsonlEntry = JSON.parse(line);\n\n if (entry.type === 'summary' && entry.summary) {\n title = entry.summary;\n }\n\n if (!createdAt && entry.timestamp) {\n createdAt = entry.timestamp;\n }\n\n if (entry.type === 'user') {\n const msg = entry.message?.content;\n let text = '';\n if (typeof msg === 'string') {\n text = msg;\n } else if (Array.isArray(msg)) {\n text = msg\n .filter((b): b is ContentBlock => b.type === 'text')\n .map((b) => b.text ?? '')\n .join('\\n');\n }\n\n if (text.trim()) {\n messages.push({ role: 'user', text: text.trim(), files: [], toolCalls: [] });\n }\n }\n\n if (entry.type === 'assistant') {\n const contentBlocks = entry.message?.content ?? [];\n let text = '';\n const toolCalls: Array<{ name: string; status?: string }> = [];\n\n if (typeof contentBlocks === 'string') {\n text = contentBlocks;\n } else if (Array.isArray(contentBlocks)) {\n for (const block of contentBlocks) {\n if (block.type === 'text') {\n text += (text ? '\\n' : '') + (block.text ?? '');\n }\n if (block.type === 'tool_use' && block.name) {\n toolCalls.push({ name: block.name });\n }\n }\n }\n\n if (text.trim() || toolCalls.length > 0) {\n messages.push({ role: 'assistant', text: text.trim(), files: [], toolCalls });\n }\n }\n } catch {\n continue;\n }\n }\n\n if (!title && messages.length > 0) {\n const firstUser = messages.find((m) => m.role === 'user');\n title = firstUser?.text?.split('\\n')[0]?.slice(0, 80) ?? `Session ${sessionId.slice(0, 8)}`;\n }\n\n return {\n id,\n source: 'claude-code',\n title,\n mode: 'claude-code',\n createdAt,\n messageCount: messages.length,\n project: resolvedProject,\n messages,\n };\n }\n } catch {\n // Error reading\n }\n\n return null;\n }\n\n // ── getToolUsage ─────────────────────────────────────────────\n\n async getToolUsage(): Promise<ToolUsage[]> {\n const baseDir = getClaudeCodeBaseDir();\n if (!existsSync(baseDir)) return [];\n\n const toolCounts = new Map<string, { count: number; lastUsed: string | null }>();\n\n try {\n const projectDirs = readdirSync(baseDir, { withFileTypes: true }).filter((d) => d.isDirectory());\n\n for (const projDir of projectDirs) {\n const projFullPath = join(baseDir, projDir.name);\n const jsonlFiles = readdirSync(projFullPath).filter((f) => f.endsWith('.jsonl'));\n\n for (const file of jsonlFiles) {\n const filePath = join(projFullPath, file);\n try {\n const content = readFileSync(filePath, 'utf-8');\n for (const line of content.split('\\n')) {\n if (!line.trim()) continue;\n try {\n const entry: ClaudeJsonlEntry = JSON.parse(line);\n if (entry.type === 'assistant') {\n const blocks = entry.message?.content;\n if (!Array.isArray(blocks)) continue;\n for (const block of blocks) {\n if (block.type === 'tool_use' && block.name) {\n const existing = toolCounts.get(block.name);\n const timestamp = entry.timestamp ?? null;\n if (existing) {\n existing.count++;\n if (timestamp && (!existing.lastUsed || timestamp > existing.lastUsed)) {\n existing.lastUsed = timestamp;\n }\n } else {\n toolCounts.set(block.name, { count: 1, lastUsed: timestamp });\n }\n }\n }\n }\n } catch {\n continue;\n }\n }\n } catch {\n continue;\n }\n }\n }\n } catch {\n // ignore\n }\n\n return Array.from(toolCounts.entries()).map(([name, data]) => {\n const parsed = parseMcpToolName(name);\n return {\n name,\n count: data.count,\n lastUsed: data.lastUsed,\n isMcp: !!parsed,\n mcpServer: parsed?.server,\n };\n });\n }\n\n // ── loadFullConversations (for summarization pipeline) ───────\n\n async loadFullConversations(\n classCache: Map<string, { assignedRepo: string | null }>,\n knownRepos: KnownRepo[],\n ): Promise<FullConversation[]> {\n const baseDir = getClaudeCodeBaseDir();\n if (!existsSync(baseDir)) return [];\n\n const results: FullConversation[] = [];\n\n try {\n const projectDirs = readdirSync(baseDir, { withFileTypes: true })\n .filter((d) => d.isDirectory());\n\n for (const projDir of projectDirs) {\n const projFullPath = join(baseDir, projDir.name);\n\n // Resolve project to a repo\n const prefixMatch = resolveClaudeProjectToRepo(projDir.name, knownRepos);\n let projectRepoName: string | null = null;\n let projectRepoPath: string | null = null;\n if (prefixMatch) {\n projectRepoName = prefixMatch.remoteName ?? prefixMatch.shortName;\n projectRepoPath = prefixMatch.path;\n }\n\n const jsonlFiles = readdirSync(projFullPath).filter((f) => f.endsWith('.jsonl'));\n\n for (const file of jsonlFiles) {\n const sessionId = file.replace('.jsonl', '');\n const filePath = join(projFullPath, file);\n\n try {\n const convo = await this.parseFullSession(filePath, sessionId, projDir.name);\n if (!convo || convo.messageCount === 0) continue;\n\n // Use classification cache first, then fall back to project-level repo\n const id = `claude-${sessionId}`;\n const classification = classCache.get(id);\n const { repoName, repoPath } = resolveRepoFromClassification(\n classification?.assignedRepo ?? null,\n );\n\n convo.id = id;\n convo.repoName = repoName ?? projectRepoName;\n convo.repoPath = repoPath ?? projectRepoPath;\n\n results.push(convo);\n } catch {\n // Skip unreadable sessions\n }\n }\n }\n } catch (err) {\n logger.error('Failed to load Claude Code conversations:', err);\n }\n\n return results;\n }\n\n // ── Private: parse a full JSONL session for the loader ───────\n\n private async parseFullSession(\n filePath: string,\n sessionId: string,\n projectDirName: string,\n ): Promise<FullConversation | null> {\n const transcriptParts: string[] = [];\n const allFiles = new Set<string>();\n const toolUsage: ToolUsageEntry[] = [];\n let title = '';\n let createdAt: string | null = null;\n let messageCount = 0;\n let firstUserMessage: string | null = null;\n\n try {\n const rl = createInterface({\n input: createReadStream(filePath, { encoding: 'utf-8' }),\n crlfDelay: Infinity,\n });\n\n for await (const line of rl) {\n if (!line.trim()) continue;\n\n let msg: ClaudeMessage;\n try {\n msg = JSON.parse(line);\n } catch {\n continue;\n }\n\n if (!createdAt && msg.timestamp) {\n createdAt = msg.timestamp;\n }\n\n if (msg.type === 'summary' && msg.summary) {\n title = msg.summary;\n }\n\n if (msg.message && (msg.type === 'user' || msg.type === 'assistant')) {\n messageCount++;\n const role: 'user' | 'assistant' = msg.type === 'user' ? 'user' : 'assistant';\n let text = '';\n\n if (typeof msg.message.content === 'string') {\n text = msg.message.content;\n } else if (Array.isArray(msg.message.content)) {\n for (const block of msg.message.content) {\n if (block.type === 'text' && block.text) {\n text += block.text + '\\n';\n } else if (block.type === 'tool_use' && block.name) {\n const filesAffected = extractFilesFromTool(block.name, block.input ?? {});\n for (const f of filesAffected) allFiles.add(f);\n toolUsage.push({ name: block.name, filesAffected });\n }\n }\n }\n\n if (text.trim()) {\n transcriptParts.push(`[${role}]: ${text.trim()}`);\n if (role === 'user') {\n if (!title) {\n const firstLine = text.trim().split('\\n')[0];\n title = firstLine.length > 80 ? firstLine.slice(0, 77) + '...' : firstLine;\n }\n if (firstUserMessage === null) {\n firstUserMessage = text.trim();\n }\n }\n }\n }\n }\n } catch {\n return null;\n }\n\n if (messageCount === 0) return null;\n\n return {\n id: `claude-${sessionId}`,\n source: 'claude-code',\n title: title || `Claude Code session ${sessionId.slice(0, 8)}`,\n createdAt,\n repoName: null,\n repoPath: null,\n messageCount,\n firstUserMessage,\n transcript: transcriptParts.join('\\n\\n'),\n toolUsage,\n filesMentioned: [...allFiles],\n sourceRef: {\n type: 'claude-code',\n jsonlPath: filePath,\n sessionId,\n projectDir: projectDirName,\n },\n };\n }\n}\n","/**\n * Data source registry.\n *\n * Provides access to all registered ConversationDataSource implementations.\n * Currently: Cursor (SQLite) and Claude Code (JSONL).\n */\n\nimport { CursorDataSource } from './cursor.js';\nimport { ClaudeCodeDataSource } from './claude-code.js';\nimport type { ConversationDataSource, DataSourceName, CursorDataSourceExt } from './types.js';\n\n// Singleton instances\nconst cursorSource = new CursorDataSource();\nconst claudeCodeSource = new ClaudeCodeDataSource();\n\nconst sources: ConversationDataSource[] = [cursorSource, claudeCodeSource];\n\n/**\n * Get all registered data sources.\n */\nexport function getAllSources(): ConversationDataSource[] {\n return sources;\n}\n\n/**\n * Get a specific data source by name.\n */\nexport function getSource(name: DataSourceName): ConversationDataSource {\n const source = sources.find((s) => s.name === name);\n if (!source) {\n throw new Error(`Unknown data source: ${name}`);\n }\n return source;\n}\n\n/**\n * Get the Cursor data source with its extended interface (skill usage, etc.).\n */\nexport function getCursorSource(): CursorDataSourceExt {\n return cursorSource;\n}\n\n// Re-export types for convenience\nexport type { ConversationDataSource, DataSourceName, CursorDataSourceExt } from './types.js';\nexport type {\n ConversationSummary,\n ConversationDetail,\n ToolUsage,\n SkillUsage,\n} from './types.js';\n","/**\n * Repo activity scoring.\n *\n * Computes a cheap, zero-token activity score for each known repo\n * using only filesystem and git signals. Used by the repo selector\n * to determine which repos are worth processing.\n *\n * Uses async parallel git commands for speed — all repos are scored\n * concurrently (with a concurrency limit to avoid fd exhaustion).\n * A single combined shell command per repo fetches both recency and\n * commit count, halving the subprocess overhead.\n */\n\nimport { execFile } from 'node:child_process';\nimport { existsSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport type { KnownRepo } from '../utils/paths.js';\nimport { readSummaryIndex } from '../summarizer/cache.js';\nimport { logger } from '../utils/logger.js';\nimport { GIT_FAST_TIMEOUT_MS, MS_PER_DAY } from '../utils/constants.js';\n\nconst execFileAsync = promisify(execFile);\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport interface ActivitySignals {\n /** Days since last commit (lower is more active) */\n daysSinceLastCommit: number | null;\n /** Number of commits within the activity window */\n commitCount: number;\n /** Number of conversation summaries classified to this repo */\n conversationCount: number;\n /** Days since .git/index was last modified (proxy for local work) */\n daysSinceIndexModified: number | null;\n}\n\nexport interface ScoredRepo {\n repo: KnownRepo;\n signals: ActivitySignals;\n /** Composite score (higher = more active, worth processing) */\n score: number;\n}\n\n// ── Scoring weights ──────────────────────────────────────────────\n\nconst WEIGHTS = {\n /** Points for recency — decays linearly, max at 0 days ago */\n gitRecency: 40,\n /** Points per commit in the window (capped) */\n gitFrequency: 3,\n /** Max points from commit frequency */\n gitFrequencyMax: 20,\n /** Points per conversation */\n conversationCount: 5,\n /** Max points from conversations */\n conversationMax: 20,\n /** Points for .git/index freshness (local working tree) */\n indexFreshness: 20,\n} as const;\n\n// ── Concurrency control ──────────────────────────────────────────\n\nconst MAX_CONCURRENT_GIT = 10;\n\n/**\n * Run async tasks with a concurrency limit to avoid fd exhaustion.\n */\nasync function parallelMap<T, R>(\n items: T[],\n fn: (item: T) => Promise<R>,\n concurrency: number,\n): Promise<R[]> {\n const results: R[] = new Array(items.length);\n let idx = 0;\n\n async function worker() {\n while (idx < items.length) {\n const i = idx++;\n results[i] = await fn(items[i]);\n }\n }\n\n const workers = Array.from(\n { length: Math.min(concurrency, items.length) },\n () => worker(),\n );\n await Promise.all(workers);\n return results;\n}\n\n// ── Public API ───────────────────────────────────────────────────\n\n/**\n * Score all repos by activity (async, parallel).\n * Returns a ScoredRepo[] sorted by score descending (most active first).\n *\n * Runs a single combined git command per repo, with up to MAX_CONCURRENT_GIT\n * repos scored in parallel.\n */\nexport async function scoreRepos(\n repos: KnownRepo[],\n activityWindowDays: number = 14,\n): Promise<ScoredRepo[]> {\n // Pre-load the summary index once (avoids re-reading per repo)\n const conversationCounts = countConversationsPerRepo();\n\n const scored = await parallelMap(\n repos,\n async (repo) => {\n const signals = await collectSignals(repo, activityWindowDays, conversationCounts);\n const score = computeScore(signals, activityWindowDays);\n return { repo, signals, score };\n },\n MAX_CONCURRENT_GIT,\n );\n\n // Sort descending by score\n scored.sort((a, b) => b.score - a.score);\n return scored;\n}\n\n/**\n * Score a single repo (async).\n */\nexport async function scoreRepo(\n repo: KnownRepo,\n activityWindowDays: number = 14,\n): Promise<ScoredRepo> {\n const conversationCounts = countConversationsPerRepo();\n const signals = await collectSignals(repo, activityWindowDays, conversationCounts);\n const score = computeScore(signals, activityWindowDays);\n return { repo, signals, score };\n}\n\n// ── Signal collection ────────────────────────────────────────────\n\nasync function collectSignals(\n repo: KnownRepo,\n activityWindowDays: number,\n conversationCounts: Map<string, number>,\n): Promise<ActivitySignals> {\n const gitSignals = await getGitSignals(repo.path, activityWindowDays);\n return {\n ...gitSignals,\n conversationCount: getConversationCount(repo, conversationCounts),\n daysSinceIndexModified: getDaysSinceIndexModified(repo.path),\n };\n}\n\n/**\n * Fetch both git recency and commit count in a single subprocess.\n *\n * Runs: `git log -1 --format=%ct; git rev-list --count --since=\"...\" HEAD`\n * via `sh -c` to avoid two separate process spawns per repo.\n */\nasync function getGitSignals(\n repoPath: string,\n windowDays: number,\n): Promise<{ daysSinceLastCommit: number | null; commitCount: number }> {\n const since = new Date(Date.now() - windowDays * MS_PER_DAY).toISOString();\n\n try {\n const { stdout } = await execFileAsync(\n '/bin/sh',\n ['-c', `git log -1 --format=%ct 2>/dev/null; echo \"---\"; git rev-list --count --since=\"${since}\" HEAD 2>/dev/null`],\n { cwd: repoPath, timeout: GIT_FAST_TIMEOUT_MS },\n );\n\n const parts = stdout.split('---');\n const recencyLine = (parts[0] ?? '').trim();\n const countLine = (parts[1] ?? '').trim();\n\n let daysSinceLastCommit: number | null = null;\n if (recencyLine) {\n const commitTimestamp = parseInt(recencyLine, 10) * 1000;\n if (!isNaN(commitTimestamp) && commitTimestamp > 0) {\n const daysAgo = (Date.now() - commitTimestamp) / MS_PER_DAY;\n daysSinceLastCommit = Math.max(0, Math.round(daysAgo * 10) / 10);\n }\n }\n\n const commitCount = parseInt(countLine, 10) || 0;\n\n return { daysSinceLastCommit, commitCount };\n } catch {\n return { daysSinceLastCommit: null, commitCount: 0 };\n }\n}\n\n/**\n * Number of conversation summaries classified to this repo.\n * Matches by both remoteName and shortName.\n */\nfunction getConversationCount(\n repo: KnownRepo,\n counts: Map<string, number>,\n): number {\n let total = 0;\n\n // Match by remote name (org/repo)\n if (repo.remoteName) {\n total += counts.get(repo.remoteName) ?? 0;\n }\n\n // Match by short name (directory basename)\n if (repo.shortName && repo.shortName !== repo.remoteName) {\n total += counts.get(repo.shortName) ?? 0;\n }\n\n return total;\n}\n\n/**\n * Days since .git/index was last modified.\n * This is a proxy for local working tree changes.\n */\nfunction getDaysSinceIndexModified(repoPath: string): number | null {\n try {\n const indexPath = join(repoPath, '.git', 'index');\n if (!existsSync(indexPath)) return null;\n\n const st = statSync(indexPath);\n const daysAgo = (Date.now() - st.mtimeMs) / MS_PER_DAY;\n return Math.max(0, Math.round(daysAgo * 10) / 10);\n } catch {\n return null;\n }\n}\n\n// ── Conversation counting ────────────────────────────────────────\n\n/**\n * Build a map of repo name -> conversation count from the summary index.\n * Done once per scoring run.\n */\nfunction countConversationsPerRepo(): Map<string, number> {\n const counts = new Map<string, number>();\n\n try {\n const index = readSummaryIndex();\n for (const entry of Object.values(index.conversations)) {\n if (entry.repo) {\n counts.set(entry.repo, (counts.get(entry.repo) ?? 0) + 1);\n }\n }\n } catch (err) {\n logger.debug('Failed to read summary index for conversation counting:', err);\n }\n\n return counts;\n}\n\n// ── Composite score ──────────────────────────────────────────────\n\n/**\n * Compute a composite activity score from signals.\n * Score range: 0–100 (higher = more active).\n */\nfunction computeScore(signals: ActivitySignals, windowDays: number): number {\n let score = 0;\n\n // Git recency: linear decay over the activity window\n // 0 days ago = full points, windowDays+ ago = 0 points\n if (signals.daysSinceLastCommit !== null) {\n const recencyRatio = Math.max(0, 1 - signals.daysSinceLastCommit / windowDays);\n score += recencyRatio * WEIGHTS.gitRecency;\n }\n\n // Git frequency: points per commit, capped\n score += Math.min(\n signals.commitCount * WEIGHTS.gitFrequency,\n WEIGHTS.gitFrequencyMax,\n );\n\n // Conversation count: points per conversation, capped\n score += Math.min(\n signals.conversationCount * WEIGHTS.conversationCount,\n WEIGHTS.conversationMax,\n );\n\n // .git/index freshness: linear decay over the activity window\n if (signals.daysSinceIndexModified !== null) {\n const indexRatio = Math.max(0, 1 - signals.daysSinceIndexModified / windowDays);\n score += indexRatio * WEIGHTS.indexFreshness;\n }\n\n return Math.round(score * 10) / 10; // 1 decimal place\n}\n","/**\n * Repo selector.\n *\n * Combines config-driven filtering with activity-based scoring to\n * determine which repos should be processed by the expensive pipeline\n * stages (clustering, skill generation).\n */\n\nimport { discoverKnownRepos, type KnownRepo } from '../utils/paths.js';\nimport { scoreRepos, type ScoredRepo } from './activity.js';\nimport type { RepoConfig } from '../config/schema.js';\nimport { logger } from '../utils/logger.js';\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport interface RepoSelectionResult {\n /** Repos selected for processing */\n selected: ScoredRepo[];\n /** All scored repos (including non-selected) for display */\n all: ScoredRepo[];\n /** Selection mode used */\n mode: 'auto' | 'whitelist' | 'all';\n /** Why each non-selected repo was excluded */\n excluded: Array<{ repo: KnownRepo; reason: string }>;\n}\n\n// ── Public API ───────────────────────────────────────────────────\n\n/**\n * Select repos for processing based on config and activity signals.\n *\n * This is the main entry point used by sync, cluster, and skill-gen\n * to decide which repos to operate on.\n */\nexport async function selectReposForProcessing(\n repoConfig: RepoConfig,\n): Promise<RepoSelectionResult> {\n const allRepos = discoverKnownRepos();\n const windowDays = repoConfig.auto.activityWindowDays;\n const scored = await scoreRepos(allRepos, windowDays);\n\n const excluded: Array<{ repo: KnownRepo; reason: string }> = [];\n\n // Step 1: Apply exclude filter (always applied, all modes)\n const afterExclude = scored.filter((sr) => {\n if (matchesFilter(sr.repo, repoConfig.exclude)) {\n excluded.push({ repo: sr.repo, reason: 'Excluded by config' });\n return false;\n }\n return true;\n });\n\n // Step 2: Select based on mode\n let selected: ScoredRepo[];\n\n switch (repoConfig.mode) {\n case 'all': {\n selected = afterExclude;\n break;\n }\n\n case 'whitelist': {\n selected = afterExclude.filter((sr) => {\n if (matchesFilter(sr.repo, repoConfig.include)) {\n return true;\n }\n excluded.push({ repo: sr.repo, reason: 'Not in whitelist' });\n return false;\n });\n break;\n }\n\n case 'auto':\n default: {\n // Partition into forced-include and candidates\n const forced: ScoredRepo[] = [];\n const candidates: ScoredRepo[] = [];\n\n for (const sr of afterExclude) {\n if (matchesFilter(sr.repo, repoConfig.include)) {\n forced.push(sr);\n } else {\n candidates.push(sr);\n }\n }\n\n // From candidates, take top N by score (that have any activity)\n const maxRepos = repoConfig.auto.maxRepos;\n const slotsRemaining = Math.max(0, maxRepos - forced.length);\n\n // candidates are already sorted by score (descending) from scoreRepos()\n const autoSelected = candidates\n .filter((sr) => sr.score > 0)\n .slice(0, slotsRemaining);\n\n // Track excluded candidates\n const autoSelectedSet = new Set(autoSelected.map((sr) => sr.repo.path));\n for (const sr of candidates) {\n if (!autoSelectedSet.has(sr.repo.path)) {\n const reason = sr.score === 0\n ? 'No activity detected'\n : `Below top ${maxRepos} by activity score`;\n excluded.push({ repo: sr.repo, reason });\n }\n }\n\n selected = [...forced, ...autoSelected];\n break;\n }\n }\n\n logger.debug(\n `Repo selection (${repoConfig.mode}): ${selected.length} selected out of ${allRepos.length} discovered`,\n );\n\n return {\n selected,\n all: scored,\n mode: repoConfig.mode,\n excluded,\n };\n}\n\n// ── Helpers ──────────────────────────────────────────────────────\n\n/**\n * Check if a repo matches any of the filter patterns.\n * Matches against: remoteName, shortName, and path (case-insensitive substring).\n */\nfunction matchesFilter(repo: KnownRepo, patterns: string[]): boolean {\n if (patterns.length === 0) return false;\n\n for (const pattern of patterns) {\n const p = pattern.toLowerCase();\n\n if (repo.remoteName && repo.remoteName.toLowerCase().includes(p)) return true;\n if (repo.shortName && repo.shortName.toLowerCase().includes(p)) return true;\n if (repo.path.toLowerCase().includes(p)) return true;\n }\n\n return false;\n}\n\n/**\n * Extract the list of selected repo names (for passing to downstream filters).\n */\nexport function getSelectedRepoNames(result: RepoSelectionResult): string[] {\n return result.selected.map((sr) => sr.repo.remoteName ?? sr.repo.shortName);\n}\n\n/**\n * Extract the list of selected repo paths (for passing to downstream filters).\n */\nexport function getSelectedRepoPaths(result: RepoSelectionResult): string[] {\n return result.selected.map((sr) => sr.repo.path);\n}\n","/**\n * Scan lifecycle metrics.\n *\n * Tracks performance data across pipeline phases for diagnostics,\n * analytics dashboard, and adaptive optimization.\n *\n * Inspired by claude-flow's pre/post hooks for telemetry.\n *\n * Lifecycle hooks:\n * onScanStart() — Record scan start, repo, expected work\n * onPhaseComplete() — Record duration, tokens, rate-limits per phase\n * onScanEnd() — Aggregate and persist to ~/.lore/metrics.json\n *\n * Storage: ~/.lore/metrics.json (append-only log, capped at 100 entries)\n */\n\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { getRateLimitStats } from './cli.js';\nimport { logger } from './logger.js';\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport type ScanPhase =\n | 'scan'\n | 'classify'\n | 'summarize'\n | 'cluster'\n | 'relevance'\n | 'skill-gen'\n | 'vetting'\n | 'corrections'\n | 'lineage'\n | 'pruning';\n\nexport interface PhaseMetric {\n phase: ScanPhase;\n durationMs: number;\n /** Rough token estimate for this phase. */\n tokensEstimate: number;\n /** Number of items processed (conversations, repos, etc.). */\n itemsProcessed: number;\n /** Number of rate-limit events during this phase. */\n rateLimitEvents: number;\n /** Success/failure. */\n success: boolean;\n /** Optional error message. */\n error?: string;\n}\n\nexport interface ScanMetricEntry {\n /** Unique scan ID (timestamp-based). */\n id: string;\n /** When the scan started. */\n startedAt: string;\n /** When the scan ended. */\n endedAt: string;\n /** Total duration (ms). */\n totalDurationMs: number;\n /** Which repos were processed. */\n repos: string[];\n /** Per-phase metrics. */\n phases: PhaseMetric[];\n /** Total rate-limit backoff time (ms). */\n totalRateLimitMs: number;\n /** Total rate-limit events. */\n totalRateLimitEvents: number;\n /** Total items processed across all phases. */\n totalItemsProcessed: number;\n /** Whether the scan completed successfully. */\n success: boolean;\n /** Trigger source (cli, extension, watch). */\n trigger: 'cli' | 'extension' | 'watch' | 'api';\n}\n\nexport interface MetricsStore {\n version: 1;\n scans: ScanMetricEntry[];\n}\n\n// ── Active scan tracker ──────────────────────────────────────────\n\nlet activeScan: {\n id: string;\n startedAt: number;\n repos: string[];\n phases: PhaseMetric[];\n trigger: ScanMetricEntry['trigger'];\n rateLimitEventsAtStart: number;\n} | null = null;\n\n// ── Public API ───────────────────────────────────────────────────\n\n/**\n * Mark the start of a scan. Creates a new metric entry.\n */\nexport function onScanStart(\n repos: string[],\n trigger: ScanMetricEntry['trigger'] = 'cli',\n): string {\n const id = `scan-${Date.now()}`;\n activeScan = {\n id,\n startedAt: Date.now(),\n repos,\n phases: [],\n trigger,\n rateLimitEventsAtStart: getRateLimitStats().eventCount,\n };\n logger.debug(`[metrics] Scan started: ${id} (${repos.length} repos, trigger: ${trigger})`);\n return id;\n}\n\n/**\n * Record the completion of a pipeline phase.\n */\nexport function onPhaseComplete(\n phase: ScanPhase,\n metrics: {\n durationMs: number;\n tokensEstimate?: number;\n itemsProcessed?: number;\n rateLimitEvents?: number;\n success?: boolean;\n error?: string;\n },\n): void {\n if (!activeScan) {\n logger.debug(`[metrics] onPhaseComplete called without active scan (phase: ${phase})`);\n return;\n }\n\n activeScan.phases.push({\n phase,\n durationMs: metrics.durationMs,\n tokensEstimate: metrics.tokensEstimate ?? 0,\n itemsProcessed: metrics.itemsProcessed ?? 0,\n rateLimitEvents: metrics.rateLimitEvents ?? 0,\n success: metrics.success ?? true,\n error: metrics.error,\n });\n\n logger.debug(\n `[metrics] Phase complete: ${phase} (${Math.round(metrics.durationMs / 1000)}s, ${metrics.itemsProcessed ?? 0} items)`,\n );\n}\n\n/**\n * Mark the end of a scan. Aggregates and persists metrics.\n */\nexport function onScanEnd(success: boolean = true): ScanMetricEntry | null {\n if (!activeScan) {\n logger.debug('[metrics] onScanEnd called without active scan');\n return null;\n }\n\n const now = Date.now();\n const rlStats = getRateLimitStats();\n const rlEventsDuringScan = rlStats.eventCount - activeScan.rateLimitEventsAtStart;\n\n const entry: ScanMetricEntry = {\n id: activeScan.id,\n startedAt: new Date(activeScan.startedAt).toISOString(),\n endedAt: new Date(now).toISOString(),\n totalDurationMs: now - activeScan.startedAt,\n repos: activeScan.repos,\n phases: activeScan.phases,\n totalRateLimitMs: rlStats.totalBackoffMs,\n totalRateLimitEvents: rlEventsDuringScan,\n totalItemsProcessed: activeScan.phases.reduce((sum, p) => sum + p.itemsProcessed, 0),\n success,\n trigger: activeScan.trigger,\n };\n\n // Persist\n const store = readMetricsStore();\n store.scans.push(entry);\n\n // Cap at 100 entries (keep most recent)\n if (store.scans.length > 100) {\n store.scans = store.scans.slice(-100);\n }\n\n writeMetricsStore(store);\n\n logger.debug(\n `[metrics] Scan complete: ${entry.id} (${Math.round(entry.totalDurationMs / 1000)}s, ` +\n `${entry.totalItemsProcessed} items, ${entry.totalRateLimitEvents} rate-limits)`,\n );\n\n activeScan = null;\n return entry;\n}\n\n/**\n * Get the active scan ID (if any).\n */\nexport function getActiveScanId(): string | null {\n return activeScan?.id ?? null;\n}\n\n// ── Store ────────────────────────────────────────────────────────\n// Metrics are always stored globally at ~/.lore/metrics.json regardless of\n// any per-repo setLoreDir() override, so the dashboard and sidebar both\n// read from the same unified location.\n\nfunction getGlobalLoreDir(): string {\n return join(homedir(), '.lore');\n}\n\nfunction getMetricsPath(): string {\n return join(getGlobalLoreDir(), 'metrics.json');\n}\n\nexport function readMetricsStore(): MetricsStore {\n const path = getMetricsPath();\n if (!existsSync(path)) {\n return { version: 1, scans: [] };\n }\n try {\n return JSON.parse(readFileSync(path, 'utf-8')) as MetricsStore;\n } catch {\n return { version: 1, scans: [] };\n }\n}\n\nfunction writeMetricsStore(store: MetricsStore): void {\n mkdirSync(getGlobalLoreDir(), { recursive: true });\n writeFileSync(getMetricsPath(), JSON.stringify(store, null, 2), 'utf-8');\n}\n\n// ── Analytics helpers (for dashboard) ────────────────────────────\n\n/**\n * Get aggregate metrics for the dashboard.\n */\nexport function getMetricsAggregate(): {\n totalScans: number;\n averageDurationMs: number;\n averageItemsPerScan: number;\n totalRateLimitEvents: number;\n scansByDay: Record<string, number>;\n scansByTrigger: Record<string, number>;\n phaseAverages: Record<string, { avgDurationMs: number; avgItems: number }>;\n recentScans: ScanMetricEntry[];\n} {\n const store = readMetricsStore();\n const scans = store.scans;\n\n if (scans.length === 0) {\n return {\n totalScans: 0,\n averageDurationMs: 0,\n averageItemsPerScan: 0,\n totalRateLimitEvents: 0,\n scansByDay: {},\n scansByTrigger: {},\n phaseAverages: {},\n recentScans: [],\n };\n }\n\n const totalDuration = scans.reduce((sum, s) => sum + s.totalDurationMs, 0);\n const totalItems = scans.reduce((sum, s) => sum + s.totalItemsProcessed, 0);\n const totalRL = scans.reduce((sum, s) => sum + s.totalRateLimitEvents, 0);\n\n // Scans by day\n const scansByDay: Record<string, number> = {};\n for (const scan of scans) {\n const day = scan.startedAt.slice(0, 10); // YYYY-MM-DD\n scansByDay[day] = (scansByDay[day] ?? 0) + 1;\n }\n\n // Scans by trigger\n const scansByTrigger: Record<string, number> = {};\n for (const scan of scans) {\n scansByTrigger[scan.trigger] = (scansByTrigger[scan.trigger] ?? 0) + 1;\n }\n\n // Phase averages\n const phaseData: Record<string, { totalDuration: number; totalItems: number; count: number }> = {};\n for (const scan of scans) {\n for (const phase of scan.phases) {\n if (!phaseData[phase.phase]) {\n phaseData[phase.phase] = { totalDuration: 0, totalItems: 0, count: 0 };\n }\n phaseData[phase.phase].totalDuration += phase.durationMs;\n phaseData[phase.phase].totalItems += phase.itemsProcessed;\n phaseData[phase.phase].count++;\n }\n }\n\n const phaseAverages: Record<string, { avgDurationMs: number; avgItems: number }> = {};\n for (const [phase, data] of Object.entries(phaseData)) {\n phaseAverages[phase] = {\n avgDurationMs: Math.round(data.totalDuration / data.count),\n avgItems: Math.round(data.totalItems / data.count),\n };\n }\n\n return {\n totalScans: scans.length,\n averageDurationMs: Math.round(totalDuration / scans.length),\n averageItemsPerScan: Math.round(totalItems / scans.length),\n totalRateLimitEvents: totalRL,\n scansByDay,\n scansByTrigger,\n phaseAverages,\n recentScans: scans.slice(-10),\n };\n}\n","import { Hono } from 'hono';\nimport { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, unlinkSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { getLoreDir } from '../../utils/paths.js';\nimport { SEVEN_DAYS_MS, THIRTY_DAYS_MS, NINETY_DAYS_MS } from '../../utils/constants.js';\nimport { shouldIgnoreConversation } from '../../utils/conversation-filter.js';\nimport { buildLoreIgnoreMap } from '../../utils/lore-filter.js';\nimport { StateStore } from '../../state/store.js';\nimport { loadConfig } from '../../config/loader.js';\nimport { loreConfigSchema } from '../../config/schema.js';\nimport { CursorScanner } from '../../scanners/cursor.js';\nimport { ClaudeCodeScanner } from '../../scanners/claude-code.js';\nimport { DistillationEngine, type RepoAgentPlan } from '../../distiller/engine.js';\nimport { ReviewQueueWriter } from '../../writers/review-queue.js';\nimport type { ScanResult, ConversationSession, AlignmentResult } from '../../scanners/types.js';\nimport { invalidateCache } from './cache.js';\nimport { onScanStart, onPhaseComplete, onScanEnd } from '../../utils/scan-metrics.js';\n\nexport const stateApi = new Hono();\n\n// In-memory sync state visible to the UI\nlet activeSyncStatus: {\n running: boolean;\n phase: string;\n startedAt: string | null;\n error: string | null;\n progress: { current: number; total: number; percent: number } | null;\n repoSummary: RepoAgentPlan[] | null;\n} = { running: false, phase: '', startedAt: null, error: null, progress: null, repoSummary: null };\n\n// In-memory output buffer for streaming Claude CLI output to the UI\nlet syncOutputLines: string[] = [];\nlet syncAbortController: AbortController | null = null;\n\n// ── Get current state ─────────────────────────────────────────────\nstateApi.get('/', (c) => {\n const loreDir = getLoreDir();\n const store = new StateStore(loreDir);\n const state = store.read();\n return c.json(state);\n});\n\n// ── Get schedule info ─────────────────────────────────────────────\nstateApi.get('/schedule', (c) => {\n const loreDir = getLoreDir();\n const config = loadConfig(loreDir);\n const store = new StateStore(loreDir);\n const state = store.read();\n\n return c.json({\n schedule: config.schedule,\n reviewMode: config.reviewMode,\n lastScanAt: state.lastScanAt,\n scanCount: state.scanCount,\n sources: config.sources,\n syncStatus: activeSyncStatus,\n runHistory: getRunHistory(),\n });\n});\n\n// ── Update schedule / config ──────────────────────────────────────\nstateApi.post('/schedule', async (c) => {\n const loreDir = getLoreDir();\n const configPath = resolve(loreDir, 'config.json');\n\n const body = await c.req.json<{\n schedule?: string;\n reviewMode?: string;\n sources?: Record<string, { enabled: boolean }>;\n }>();\n\n // Read current config\n let raw: Record<string, unknown> = {};\n if (existsSync(configPath)) {\n try {\n raw = JSON.parse(readFileSync(configPath, 'utf-8'));\n } catch {\n // use empty\n }\n }\n\n // Apply updates\n if (body.schedule !== undefined) raw.schedule = body.schedule;\n if (body.reviewMode !== undefined) raw.reviewMode = body.reviewMode;\n if (body.sources !== undefined) raw.sources = body.sources;\n\n // Validate\n try {\n loreConfigSchema.parse(raw);\n } catch (err) {\n return c.json({ error: `Invalid config: ${err}` }, 400);\n }\n\n writeFileSync(configPath, JSON.stringify(raw, null, 2) + '\\n', 'utf-8');\n return c.json({ success: true });\n});\n\n// ── Get sync status ───────────────────────────────────────────────\nstateApi.get('/sync-status', (c) => {\n return c.json(activeSyncStatus);\n});\n\n// ── Trigger a sync run ────────────────────────────────────────────\nstateApi.post('/sync', async (c) => {\n if (activeSyncStatus.running) {\n return c.json({ error: 'A sync is already running' }, 409);\n }\n\n const body = await c.req.json<{\n dryRun?: boolean;\n prompt?: string;\n syncWindow?: { from?: string; to?: string; preset?: string };\n mode?: 'headless' | 'interactive';\n }>().catch(() => ({ dryRun: false, prompt: undefined, syncWindow: undefined, mode: undefined as 'headless' | 'interactive' | undefined }));\n\n // Run sync in background (don't block the HTTP response)\n runSync(body.dryRun ?? false, body.prompt, body.syncWindow, body.mode).catch(() => {\n // error is tracked in activeSyncStatus\n });\n\n return c.json({ started: true, dryRun: body.dryRun ?? false });\n});\n\n// ── Reset scan history (clear lastScanAt so next sync rescans all) ─\nstateApi.post('/reset-scan', (c) => {\n const loreDir = getLoreDir();\n const store = new StateStore(loreDir);\n store.update({ lastScanAt: null, scanCount: 0 });\n return c.json({ success: true });\n});\n\n// ── Get sync output (polling) ─────────────────────────────────────\nstateApi.get('/sync-output', (c) => {\n // Support ?since=N to only return lines after offset N\n const since = parseInt(c.req.query('since') ?? '0', 10);\n const lines = syncOutputLines.slice(since);\n return c.json({ lines, total: syncOutputLines.length });\n});\n\n// ── Stop a running sync ──────────────────────────────────────────\nstateApi.post('/sync-stop', (c) => {\n if (!activeSyncStatus.running || !syncAbortController) {\n return c.json({ error: 'No sync is currently running' }, 409);\n }\n syncAbortController.abort();\n syncAbortController = null;\n syncOutputLines.push('[lore] Sync cancelled by user.');\n activeSyncStatus = {\n running: false,\n phase: 'Cancelled',\n startedAt: null,\n repoSummary: null,\n error: null,\n progress: null,\n };\n return c.json({ stopped: true });\n});\n\n// ── Scheduled scan (OS crontab) ───────────────────────────────────\n\nconst CRON_TAG = '# LORE_SCHEDULED_SCAN';\n\ninterface ScheduledScanMeta {\n mode: 'once' | 'periodic';\n /** ISO date for one-off scans */\n scheduledAt?: string;\n /** Interval key for periodic scans (e.g. \"1h\", \"6h\", \"1d\", \"7d\") */\n interval?: string;\n /** Human-readable description */\n intervalLabel?: string;\n /** Preferred run time for periodic scans (HH:MM, 24h format) */\n runAt?: string;\n instructions?: string;\n}\n\nfunction getScheduledScanPath(): string {\n return resolve(getLoreDir(), 'scheduled-scan.json');\n}\n\nfunction readCrontab(): string[] {\n try {\n return execSync('crontab -l 2>/dev/null', { encoding: 'utf-8' }).split('\\n');\n } catch {\n return [];\n }\n}\n\nfunction writeCrontab(lines: string[]): void {\n const content = lines.filter((l) => l.trim() !== '').join('\\n') + '\\n';\n execSync('crontab -', { input: content, encoding: 'utf-8' });\n}\n\nfunction resolveLoreBin(): string {\n try {\n return execSync('which lore', { encoding: 'utf-8' }).trim();\n } catch {\n return 'lore';\n }\n}\n\n/** Convert an interval key like \"1h\", \"6h\", \"1d\", \"7d\" to a cron expression.\n * For daily+ intervals, `runAt` (HH:MM) controls the time of day. Defaults to 09:00. */\nfunction intervalToCron(interval: string, runAt?: string): string {\n const [h, m] = runAt ? runAt.split(':').map(Number) : [9, 0];\n const minute = m ?? 0;\n const hour = h ?? 9;\n\n switch (interval) {\n case '1h': return `${minute} * * * *`; // every hour at :MM\n case '3h': return `${minute} */3 * * *`; // every 3 hours\n case '6h': return `${minute} */6 * * *`; // every 6 hours\n case '12h': return `${minute} */12 * * *`; // every 12 hours\n case '1d': return `${minute} ${hour} * * *`; // daily\n case '3d': return `${minute} ${hour} */3 * *`; // every 3 days\n case '7d': return `${minute} ${hour} * * 1`; // weekly on Monday\n case '30d': return `${minute} ${hour} 1 * *`; // monthly on the 1st\n default: return `${minute} */6 * * *`; // fallback: every 6 hours\n }\n}\n\nfunction installCronOnce(scheduledAt: string): void {\n removeCronEntry();\n\n const target = new Date(scheduledAt);\n const minute = target.getMinutes();\n const hour = target.getHours();\n const dayOfMonth = target.getDate();\n const month = target.getMonth() + 1;\n const loreBin = resolveLoreBin();\n\n const cronExpr = `${minute} ${hour} ${dayOfMonth} ${month} *`;\n const cronLine = `${cronExpr} ${loreBin} sync ${CRON_TAG}`;\n\n const existing = readCrontab();\n existing.push(cronLine);\n writeCrontab(existing);\n}\n\nfunction installCronPeriodic(interval: string, runAt?: string): void {\n removeCronEntry();\n\n const loreBin = resolveLoreBin();\n const cronExpr = intervalToCron(interval, runAt);\n const cronLine = `${cronExpr} ${loreBin} sync ${CRON_TAG}`;\n\n const existing = readCrontab();\n existing.push(cronLine);\n writeCrontab(existing);\n}\n\nfunction removeCronEntry(): void {\n const lines = readCrontab();\n const filtered = lines.filter((l) => !l.includes(CRON_TAG));\n if (filtered.length !== lines.length) {\n writeCrontab(filtered);\n }\n}\n\nfunction readScheduledScanMeta(): ScheduledScanMeta | null {\n const p = getScheduledScanPath();\n if (!existsSync(p)) return null;\n try {\n const raw = JSON.parse(readFileSync(p, 'utf-8'));\n if (raw && (raw.scheduledAt || raw.interval)) return raw;\n return null;\n } catch {\n return null;\n }\n}\n\nfunction writeScheduledScanMeta(data: ScheduledScanMeta): void {\n const p = getScheduledScanPath();\n mkdirSync(resolve(getLoreDir()), { recursive: true });\n writeFileSync(p, JSON.stringify(data, null, 2), 'utf-8');\n}\n\nfunction clearScheduledScanMeta(): void {\n const p = getScheduledScanPath();\n if (existsSync(p)) {\n try { unlinkSync(p); } catch { /* ignore */ }\n }\n}\n\nstateApi.get('/schedule-scan', (c) => {\n const meta = readScheduledScanMeta();\n if (!meta) return c.json(null);\n\n // Verify the cron entry still exists\n const lines = readCrontab();\n const hasCron = lines.some((l) => l.includes(CRON_TAG));\n if (!hasCron) {\n clearScheduledScanMeta();\n return c.json(null);\n }\n\n return c.json({\n mode: meta.mode ?? 'once',\n scheduledAt: meta.scheduledAt ?? null,\n interval: meta.interval ?? null,\n intervalLabel: meta.intervalLabel ?? null,\n runAt: meta.runAt ?? null,\n instructions: meta.instructions ?? null,\n });\n});\n\nstateApi.post('/schedule-scan', async (c) => {\n const body = await c.req.json<{\n mode: 'once' | 'periodic';\n scheduledAt?: string;\n interval?: string;\n intervalLabel?: string;\n runAt?: string;\n instructions?: string;\n }>().catch(() => null);\n\n if (!body?.mode) {\n return c.json({ error: 'mode is required (\"once\" or \"periodic\")' }, 400);\n }\n\n try {\n if (body.mode === 'once') {\n if (!body.scheduledAt) {\n return c.json({ error: 'scheduledAt is required for one-off schedule' }, 400);\n }\n const targetTime = new Date(body.scheduledAt);\n if (isNaN(targetTime.getTime())) {\n return c.json({ error: 'Invalid date format' }, 400);\n }\n if (targetTime.getTime() <= Date.now()) {\n return c.json({ error: 'Scheduled time must be in the future' }, 400);\n }\n installCronOnce(body.scheduledAt);\n writeScheduledScanMeta({\n mode: 'once',\n scheduledAt: body.scheduledAt,\n instructions: body.instructions?.trim() || undefined,\n });\n } else {\n if (!body.interval) {\n return c.json({ error: 'interval is required for periodic schedule' }, 400);\n }\n installCronPeriodic(body.interval, body.runAt);\n writeScheduledScanMeta({\n mode: 'periodic',\n interval: body.interval,\n intervalLabel: body.intervalLabel,\n runAt: body.runAt,\n instructions: body.instructions?.trim() || undefined,\n });\n }\n\n return c.json({ success: true });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n return c.json({ error: `Failed to install cron entry: ${msg}` }, 500);\n }\n});\n\nstateApi.delete('/schedule-scan', (c) => {\n try {\n removeCronEntry();\n clearScheduledScanMeta();\n return c.json({ success: true });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n return c.json({ error: `Failed to remove cron entry: ${msg}` }, 500);\n }\n});\n\n// ── Get run history ───────────────────────────────────────────────\nstateApi.get('/run-history', (c) => {\n return c.json({ runs: getRunHistory() });\n});\n\n// ── Get single run detail ─────────────────────────────────────────\nstateApi.get('/run-history/:id', (c) => {\n const id = c.req.param('id');\n const history = getRunHistory();\n const entry = history.find((r) => r.id === id);\n if (!entry) {\n return c.json({ error: 'Run not found' }, 404);\n }\n return c.json(entry);\n});\n\n// ── Background sync logic ─────────────────────────────────────────\n\nfunction appendOutput(line: string): void {\n const timestamp = new Date().toISOString().slice(11, 19); // HH:MM:SS\n syncOutputLines.push(`[${timestamp}] ${line}`);\n}\n\nfunction resolveSyncWindow(\n stateLastScan: string | null,\n syncWindow?: { from?: string; to?: string; preset?: string },\n): { since: string | null; until: string | null } {\n if (!syncWindow) {\n // When no explicit window is provided and there's no previous scan,\n // default to last 30 days to avoid pulling in ancient history\n if (!stateLastScan) {\n const defaultSince = new Date(Date.now() - THIRTY_DAYS_MS);\n return { since: defaultSince.toISOString(), until: null };\n }\n return { since: stateLastScan, until: null };\n }\n\n // Handle presets\n if (syncWindow.preset) {\n const now = new Date();\n let from: Date;\n switch (syncWindow.preset) {\n case 'last7d':\n from = new Date(now.getTime() - SEVEN_DAYS_MS);\n break;\n case 'last30d':\n from = new Date(now.getTime() - THIRTY_DAYS_MS);\n break;\n case 'last90d':\n from = new Date(now.getTime() - NINETY_DAYS_MS);\n break;\n case 'all':\n return { since: null, until: null };\n default:\n return { since: stateLastScan, until: null };\n }\n return { since: from.toISOString(), until: null };\n }\n\n return {\n since: syncWindow.from ?? stateLastScan,\n until: syncWindow.to ?? null,\n };\n}\n\nasync function runSync(dryRun: boolean, prompt?: string, syncWindow?: { from?: string; to?: string; preset?: string }, mode?: 'headless' | 'interactive'): Promise<void> {\n const loreDir = getLoreDir();\n const runStartTime = Date.now();\n const runId = `run-${runStartTime}`;\n\n // Reset output buffer and create abort controller\n syncOutputLines = [];\n syncAbortController = new AbortController();\n\n // Collectors for enriched run history\n const phases: ScanPhaseResult[] = [];\n const errors: string[] = [];\n let repoResults: ScanRepoResult[] = [];\n\n activeSyncStatus = {\n running: true,\n phase: 'Initializing...',\n startedAt: new Date().toISOString(),\n repoSummary: null,\n error: null,\n progress: null,\n };\n\n appendOutput('Sync started');\n onScanStart([], 'api');\n\n try {\n const config = loadConfig(loreDir);\n const store = new StateStore(loreDir);\n const state = store.read();\n const { since, until } = resolveSyncWindow(state.lastScanAt, syncWindow);\n\n if (syncWindow?.preset) {\n appendOutput(`Sync window: ${syncWindow.preset}`);\n } else if (syncWindow?.from || syncWindow?.to) {\n appendOutput(`Sync window: ${syncWindow.from ?? 'beginning'} to ${syncWindow.to ?? 'now'}`);\n }\n\n // ── Phase: Scan ──────────────────────────────────────────────\n const scanPhaseStart = Date.now();\n const scanResults: ScanResult[] = [];\n const conversations: ConversationSession[] = [];\n\n const enabledSources = [\n config.sources.cursor.enabled ? 'cursor' : null,\n config.sources.claudeCode.enabled ? 'claude-code' : null,\n ].filter(Boolean).length;\n let scannedSources = 0;\n\n if (config.sources.cursor.enabled) {\n activeSyncStatus.phase = 'Scanning Cursor conversations...';\n activeSyncStatus.progress = { current: scannedSources, total: enabledSources + 1, percent: Math.round((scannedSources / (enabledSources + 1)) * 100) };\n appendOutput('Scanning Cursor conversations...');\n const scanner = new CursorScanner();\n const result = await scanner.scan(since, until);\n scanResults.push(result);\n scannedSources++;\n appendOutput(`Cursor: found ${result.items.length} items`);\n }\n\n if (config.sources.claudeCode.enabled) {\n activeSyncStatus.phase = 'Scanning Claude Code sessions...';\n activeSyncStatus.progress = { current: scannedSources, total: enabledSources + 1, percent: Math.round((scannedSources / (enabledSources + 1)) * 100) };\n appendOutput('Scanning Claude Code sessions...');\n const scanner = new ClaudeCodeScanner();\n const result = await scanner.scan(since, until);\n scanResults.push(result);\n conversations.push(...scanner.getLastSessions());\n scannedSources++;\n appendOutput(`Claude Code: found ${result.items.length} items, ${conversations.length} sessions`);\n }\n\n const totalItems = scanResults.reduce((sum, r) => sum + r.items.length, 0);\n const cursorItems = scanResults.find((r) => r.source === 'cursor')?.items.length ?? 0;\n const claudeItems = scanResults.find((r) => r.source === 'claude-code')?.items.length ?? 0;\n\n const scanPhaseDuration = Date.now() - scanPhaseStart;\n phases.push({\n name: 'scan',\n status: 'completed',\n duration: scanPhaseDuration,\n detail: `${cursorItems} Cursor, ${claudeItems} Claude Code`,\n });\n onPhaseComplete('scan', { durationMs: scanPhaseDuration, itemsProcessed: totalItems, success: true });\n\n if (totalItems === 0) {\n appendOutput('No new items found since last scan.');\n onScanEnd(true);\n logRun({ id: runId, status: 'completed', summary: 'No new items found', itemsScanned: totalItems, dryRun, duration: Date.now() - runStartTime, errors, phases, repos: [], output: [...syncOutputLines] });\n activeSyncStatus = { running: false, phase: 'Done (nothing new)', startedAt: null, error: null, progress: null, repoSummary: null };\n syncAbortController = null;\n return;\n }\n\n appendOutput(`Total: ${totalItems} items to process`);\n\n if (dryRun) {\n appendOutput(`Dry run complete: ${cursorItems} Cursor, ${claudeItems} Claude Code`);\n onScanEnd(true);\n logRun({ id: runId, status: 'completed', summary: `Dry run: ${totalItems} items (${cursorItems} Cursor, ${claudeItems} Claude)`, itemsScanned: totalItems, dryRun: true, duration: Date.now() - runStartTime, errors, phases, repos: [], output: [...syncOutputLines] });\n activeSyncStatus = { running: false, phase: `Done (dry run: ${totalItems} items)`, startedAt: null, error: null, progress: null, repoSummary: null };\n syncAbortController = null;\n return;\n }\n\n // ── Phase: Distill ───────────────────────────────────────────\n const distillStart = Date.now();\n activeSyncStatus.phase = `Distilling ${totalItems} items via Claude...`;\n activeSyncStatus.progress = { current: scannedSources, total: enabledSources + 1, percent: Math.round((scannedSources / (enabledSources + 1)) * 100) };\n appendOutput(`Invoking Claude Code CLI for distillation (${totalItems} items)...`);\n\n const distiller = new DistillationEngine(loreDir);\n const distillOpts = {\n onOutput: (line: string) => {\n appendOutput(line);\n // Capture errors from distillation output\n if (line.toLowerCase().includes('error') || line.toLowerCase().includes('failed')) {\n errors.push(`[distill] ${line}`);\n }\n },\n signal: syncAbortController?.signal,\n mode: (mode ?? 'headless') as 'headless' | 'interactive',\n runId,\n };\n\n // If a custom prompt is provided, inject it as an extra conversation\n let allConversations = [...conversations];\n if (prompt) {\n appendOutput(`User prompt injected: \"${prompt.slice(0, 80)}${prompt.length > 80 ? '...' : ''}\"`);\n allConversations = [\n {\n sessionId: 'ui-prompt',\n source: 'cursor',\n startedAt: new Date().toISOString(),\n endedAt: new Date().toISOString(),\n branch: null,\n cwd: null,\n turns: [\n {\n role: 'user',\n content: `[USER DISTILLATION GUIDANCE]: ${prompt}`,\n timestamp: new Date().toISOString(),\n },\n ],\n filesModified: [],\n },\n ...allConversations,\n ];\n }\n\n if (mode) {\n appendOutput(`Distillation mode: ${mode}`);\n }\n\n // Filter out lore **process** conversations/items before distillation.\n // Uses origin-based detection (phase tags + prompt markers) instead of\n // repo-name matching so user conversations in the lore repo pass through.\n const ignoreMap = buildLoreIgnoreMap();\n const allItemsRaw = scanResults.flatMap((r) => r.items);\n const allItems = allItemsRaw.filter((item) => {\n const meta = item.metadata as Record<string, unknown>;\n const itemId = (meta.id as string) ?? item.id ?? '';\n const title = (meta.title as string) ?? '';\n return !shouldIgnoreConversation(itemId, ignoreMap, title);\n });\n const filteredConversations = allConversations.filter((c) => {\n const convId = `claude-${c.sessionId}`;\n const firstUserMsg = c.turns.find((t) => t.role === 'user')?.content;\n return !shouldIgnoreConversation(convId, ignoreMap, undefined, firstUserMsg);\n });\n const loreExcluded = (allItemsRaw.length - allItems.length) + (allConversations.length - filteredConversations.length);\n if (loreExcluded > 0) {\n appendOutput(`Filtered out ${loreExcluded} lore process item(s)`);\n }\n\n // Use multi-agent distillation (handles splitting, repo grouping, orchestration)\n let distillOutput;\n let agentPlans: RepoAgentPlan[] = [];\n try {\n const distillResult = await distiller.distillMultiAgent(\n filteredConversations,\n allItems,\n distillOpts,\n );\n distillOutput = distillResult.output;\n agentPlans = distillResult.agentPlans;\n const distillDuration = Date.now() - distillStart;\n phases.push({\n name: 'distill',\n status: 'completed',\n duration: distillDuration,\n detail: `${distillOutput.decisions.length} decisions, ${distillOutput.patterns.length} patterns`,\n });\n onPhaseComplete('summarize', { durationMs: distillDuration, itemsProcessed: distillOutput.decisions.length + distillOutput.patterns.length, success: true });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n errors.push(`[distill] ${msg}`);\n const distillDuration = Date.now() - distillStart;\n phases.push({ name: 'distill', status: 'failed', duration: distillDuration, detail: msg });\n onPhaseComplete('summarize', { durationMs: distillDuration, success: false, error: msg });\n throw err;\n }\n\n // Store repo summary for UI display\n if (agentPlans.length > 0) {\n activeSyncStatus.repoSummary = agentPlans;\n }\n\n // ── Phase: Write to review queue ─────────────────────────────\n const writeStart = Date.now();\n activeSyncStatus.phase = 'Writing results to review queue...';\n appendOutput('Writing results to review queue...');\n\n const writer = new ReviewQueueWriter(loreDir);\n let writtenCount = 0;\n\n if (distillOutput.changelog.length > 0) {\n const firstLine = distillOutput.changelog.split('\\n').find((l) => l.trim()) ?? 'Changelog';\n writer.enqueue('changelog', distillOutput.changelog, {\n title: firstLine.replace(/^#+\\s*/, '').slice(0, 80),\n });\n writtenCount++;\n appendOutput('Queued: changelog');\n }\n\n // Enqueue each decision with its per-item repo tag\n for (const decision of distillOutput.decisions) {\n writer.enqueue('adr', JSON.stringify(decision, null, 2), {\n repo: decision.repo,\n title: decision.title,\n });\n writtenCount++;\n appendOutput(`Queued: ADR — ${decision.title}${decision.repo ? ` (${decision.repo})` : ''}`);\n }\n\n // Group architecture items by repo for clearer review\n if (distillOutput.changes.length > 0 || distillOutput.patterns.length > 0) {\n // Collect all unique repos from changes and patterns\n const repoSet = new Set<string | undefined>();\n for (const c of distillOutput.changes) repoSet.add(c.repo);\n for (const p of distillOutput.patterns) repoSet.add(p.repo);\n\n // If all items share the same repo (or no repo), enqueue as one\n const repos = Array.from(repoSet);\n if (repos.length <= 1) {\n const changeCount = distillOutput.changes.length;\n const patternCount = distillOutput.patterns.length;\n writer.enqueue('architecture', JSON.stringify({\n changes: distillOutput.changes,\n patterns: distillOutput.patterns,\n }, null, 2), {\n repo: repos[0],\n title: `${changeCount} change${changeCount !== 1 ? 's' : ''}, ${patternCount} pattern${patternCount !== 1 ? 's' : ''}`,\n });\n writtenCount++;\n appendOutput('Queued: architecture update');\n } else {\n // Enqueue per-repo architecture items for clear attribution\n for (const repo of repos) {\n const repoChanges = distillOutput.changes.filter((c) => c.repo === repo);\n const repoPatterns = distillOutput.patterns.filter((p) => p.repo === repo);\n if (repoChanges.length === 0 && repoPatterns.length === 0) continue;\n const cc = repoChanges.length;\n const pc = repoPatterns.length;\n writer.enqueue('architecture', JSON.stringify({\n changes: repoChanges,\n patterns: repoPatterns,\n }, null, 2), {\n repo,\n title: `${cc} change${cc !== 1 ? 's' : ''}, ${pc} pattern${pc !== 1 ? 's' : ''}`,\n });\n writtenCount++;\n appendOutput(`Queued: architecture update${repo ? ` (${repo})` : ''}`);\n }\n }\n }\n\n const writeDuration = Date.now() - writeStart;\n phases.push({\n name: 'write',\n status: 'completed',\n duration: writeDuration,\n detail: `${writtenCount} proposals queued`,\n });\n onPhaseComplete('cluster', { durationMs: writeDuration, itemsProcessed: writtenCount, success: true });\n\n // 4. Update state\n const now = new Date().toISOString();\n store.update({\n lastScanAt: now,\n scanCount: state.scanCount + 1,\n });\n\n activeSyncStatus.progress = { current: enabledSources + 1, total: enabledSources + 1, percent: 100 };\n appendOutput(`Sync complete: ${totalItems} items scanned, ${writtenCount} proposals queued`);\n\n // Extract tool usage from output lines\n const toolsUsedInRun = extractToolsFromOutput(syncOutputLines);\n\n // Build repo results from agent plans\n repoResults = agentPlans.map((plan): ScanRepoResult => ({\n repo: plan.repo,\n repoPath: plan.repoPath ?? '',\n skillPath: null,\n skillStatus: 'skipped',\n reason: 'distilled',\n toolsUsed: toolsUsedInRun,\n changedFiles: [],\n }));\n\n logRun({\n id: runId,\n status: 'completed',\n summary: `Scanned ${totalItems} items, queued ${writtenCount} proposals`,\n itemsScanned: totalItems,\n dryRun: false,\n prompt: prompt || undefined,\n duration: Date.now() - runStartTime,\n errors,\n phases,\n repos: repoResults,\n output: [...syncOutputLines],\n });\n\n // Invalidate conversation and state caches (targeted, not all caches)\n invalidateCache('conversations');\n invalidateCache('tools');\n\n onScanEnd(true);\n\n activeSyncStatus = {\n running: false,\n phase: `Done: ${totalItems} items scanned, ${writtenCount} proposals queued`,\n startedAt: null,\n error: null,\n progress: null,\n repoSummary: activeSyncStatus.repoSummary,\n };\n } catch (err) {\n onScanEnd(false);\n\n const isCancelled = err instanceof Error && err.name === 'AbortError';\n if (isCancelled) {\n appendOutput('Sync was cancelled.');\n logRun({ id: runId, status: 'failed', summary: 'Cancelled by user', itemsScanned: 0, dryRun, duration: Date.now() - runStartTime, errors: [...errors, 'Cancelled by user'], phases, repos: repoResults, output: [...syncOutputLines] });\n activeSyncStatus = { running: false, phase: 'Cancelled', startedAt: null, error: null, progress: null, repoSummary: null };\n } else {\n const errorMsg = err instanceof Error ? err.message : String(err);\n appendOutput(`ERROR: ${errorMsg}`);\n errors.push(errorMsg);\n logRun({ id: runId, status: 'failed', summary: errorMsg, itemsScanned: 0, dryRun, prompt: prompt || undefined, duration: Date.now() - runStartTime, errors, phases, repos: repoResults, output: [...syncOutputLines] });\n activeSyncStatus = {\n running: false,\n phase: '',\n startedAt: null,\n error: errorMsg,\n progress: null,\n repoSummary: null,\n };\n }\n } finally {\n syncAbortController = null;\n }\n}\n\n/**\n * Extract tool names mentioned in output lines.\n */\nfunction extractToolsFromOutput(lines: string[]): string[] {\n const tools = new Set<string>();\n for (const line of lines) {\n const match = line.match(/Tool:\\s*(\\w+)/);\n if (match) tools.add(match[1]);\n // Also catch [tool:X] patterns\n const match2 = line.match(/\\[tool:\\s*(\\w+)\\]/i);\n if (match2) tools.add(match2[1]);\n }\n return [...tools];\n}\n\n// ── Run history persistence ───────────────────────────────────────\n\nexport interface ScanPhaseResult {\n name: string;\n status: 'completed' | 'failed' | 'skipped';\n duration: number;\n detail: string;\n}\n\nexport interface ScanRepoResult {\n repo: string;\n repoPath: string;\n skillPath: string | null;\n skillStatus: 'generated' | 'updated' | 'skipped' | 'failed';\n reason: string;\n toolsUsed: string[];\n changedFiles: string[];\n}\n\nexport interface RunHistoryEntry {\n id: string;\n timestamp: string;\n status: 'completed' | 'failed';\n summary: string;\n itemsScanned: number;\n dryRun: boolean;\n prompt?: string;\n duration: number;\n errors: string[];\n phases: ScanPhaseResult[];\n repos: ScanRepoResult[];\n /** Terminal output lines captured during this run (for scan detail page). */\n output: string[];\n}\n\nfunction getRunHistoryPath(): string {\n return resolve(getLoreDir(), 'run-history.json');\n}\n\nexport function getRunHistory(): RunHistoryEntry[] {\n const path = getRunHistoryPath();\n if (!existsSync(path)) return [];\n try {\n return JSON.parse(readFileSync(path, 'utf-8'));\n } catch {\n return [];\n }\n}\n\nfunction logRun(entry: Omit<RunHistoryEntry, 'timestamp'> & { id: string }): void {\n const path = getRunHistoryPath();\n const history = getRunHistory();\n\n history.unshift({\n timestamp: new Date().toISOString(),\n ...entry,\n });\n\n // Keep last 50 runs\n const trimmed = history.slice(0, 50);\n mkdirSync(resolve(getLoreDir()), { recursive: true });\n writeFileSync(path, JSON.stringify(trimmed, null, 2), 'utf-8');\n}\n","/**\n * Repos API.\n *\n * Exposes repo listing with activity scores, config management\n * for the repo selector UI component, and per-repo scan history.\n */\n\nimport { Hono } from 'hono';\nimport { readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { discoverKnownRepos } from '../../utils/paths.js';\nimport { scoreRepos } from '../../repos/activity.js';\nimport { selectReposForProcessing } from '../../repos/selector.js';\nimport { loadConfig } from '../../config/loader.js';\nimport { loreConfigSchema } from '../../config/schema.js';\nimport type { z } from 'zod';\nimport { getLoreDir } from '../../utils/paths.js';\nimport { readSummaryIndex } from '../../summarizer/cache.js';\nimport { StateStore } from '../../state/store.js';\nimport { getRunHistory } from './state.js';\n\nexport const reposApi = new Hono();\n\n// ── Response cache (avoids re-running git commands on every request) ──\n\nconst CACHE_TTL_MS = 60_000; // 60 seconds\n\nlet reposCache: { data: unknown; timestamp: number } | null = null;\n\nfunction invalidateReposCache() {\n reposCache = null;\n}\n\nasync function buildReposResponse() {\n const loreDir = getLoreDir();\n const config = loadConfig(loreDir);\n\n // Score all repos (async, parallel git)\n const allRepos = discoverKnownRepos();\n const windowDays = config.repos.auto.activityWindowDays;\n const scored = await scoreRepos(allRepos, windowDays);\n\n // Get selection result to know which are selected\n const selection = await selectReposForProcessing(config.repos);\n const selectedPaths = new Set(selection.selected.map((sr) => sr.repo.path));\n\n // Build conversation counts from summary index\n const conversationCounts = new Map<string, number>();\n try {\n const index = readSummaryIndex();\n for (const entry of Object.values(index.conversations)) {\n if (entry.repo) {\n conversationCounts.set(entry.repo, (conversationCounts.get(entry.repo) ?? 0) + 1);\n }\n }\n } catch { /* ignore */ }\n\n // Build include/exclude match helpers\n const includeSet = new Set(config.repos.include.map((s) => s.toLowerCase()));\n const excludeSet = new Set(config.repos.exclude.map((s) => s.toLowerCase()));\n\n // Build list then deduplicate by repo name (keep highest-scored entry)\n const repoMap = new Map<string, {\n name: string;\n path: string;\n score: number;\n conversationCount: number;\n commitCount: number;\n selected: boolean;\n included: boolean;\n excluded: boolean;\n }>();\n\n for (const sr of scored) {\n const name = sr.repo.remoteName ?? sr.repo.shortName;\n const hasConversations = (conversationCounts.get(name) ?? 0) > 0\n || (sr.repo.remoteName && (conversationCounts.get(sr.repo.remoteName) ?? 0) > 0)\n || (conversationCounts.get(sr.repo.shortName) ?? 0) > 0;\n const nameLower = name.toLowerCase();\n const isIncluded = includeSet.has(nameLower)\n || (sr.repo.remoteName && includeSet.has(sr.repo.remoteName.toLowerCase()))\n || includeSet.has(sr.repo.shortName.toLowerCase());\n\n if (!hasConversations && !isIncluded) continue;\n\n const shortLower = sr.repo.shortName.toLowerCase();\n const remoteLower = sr.repo.remoteName?.toLowerCase() ?? '';\n\n const entry = {\n name,\n path: sr.repo.path,\n score: sr.score,\n conversationCount: sr.signals.conversationCount,\n commitCount: sr.signals.commitCount,\n selected: selectedPaths.has(sr.repo.path),\n included: includeSet.has(nameLower) || includeSet.has(shortLower) || includeSet.has(remoteLower),\n excluded: excludeSet.has(nameLower) || excludeSet.has(shortLower) || excludeSet.has(remoteLower),\n };\n\n // Deduplicate: keep the entry with the higher score\n const existing = repoMap.get(nameLower);\n if (!existing || entry.score > existing.score) {\n repoMap.set(nameLower, entry);\n }\n }\n\n const repos = Array.from(repoMap.values());\n\n return {\n repos,\n config: {\n mode: config.repos.mode,\n include: config.repos.include,\n exclude: config.repos.exclude,\n maxRepos: config.repos.auto.maxRepos,\n activityWindowDays: config.repos.auto.activityWindowDays,\n },\n };\n}\n\n// ── GET /api/repos — list repos with activity scores ──────────────\n\nreposApi.get('/', async (c) => {\n const now = Date.now();\n\n if (reposCache && (now - reposCache.timestamp) < CACHE_TTL_MS) {\n return c.json(reposCache.data);\n }\n\n const data = await buildReposResponse();\n reposCache = { data, timestamp: now };\n return c.json(data);\n});\n\n// ── GET /api/repos/scan-history — per-repo scan history ────────────\n\nreposApi.get('/scan-history', (c) => {\n const loreDir = getLoreDir();\n\n // 1. Read run history and bucket by repo\n const runs = getRunHistory();\n const repoRunMap = new Map<\n string,\n {\n repoPath: string;\n totalScans: number;\n lastScanAt: string | null;\n lastScanStatus: 'completed' | 'failed' | null;\n lastScanDuration: number | null;\n lastScanItemsScanned: number;\n lastScanErrors: string[];\n lastSkillStatus: 'generated' | 'updated' | 'skipped' | 'failed' | null;\n recentScans: Array<{\n runId: string;\n timestamp: string;\n status: 'completed' | 'failed';\n skillStatus: string;\n reason: string;\n }>;\n }\n >();\n\n for (const run of runs) {\n for (const repo of run.repos ?? []) {\n const key = repo.repo.toLowerCase();\n let entry = repoRunMap.get(key);\n if (!entry) {\n entry = {\n repoPath: repo.repoPath,\n totalScans: 0,\n lastScanAt: null,\n lastScanStatus: null,\n lastScanDuration: null,\n lastScanItemsScanned: 0,\n lastScanErrors: [],\n lastSkillStatus: null,\n recentScans: [],\n };\n repoRunMap.set(key, entry);\n }\n\n entry.totalScans++;\n\n // First hit is the latest run (runs are sorted newest-first)\n if (!entry.lastScanAt) {\n entry.lastScanAt = run.timestamp;\n entry.lastScanStatus = run.status;\n entry.lastScanDuration = run.duration;\n entry.lastScanItemsScanned = run.itemsScanned;\n entry.lastScanErrors = run.errors ?? [];\n entry.lastSkillStatus = repo.skillStatus;\n }\n\n // Keep last 10 runs per repo\n if (entry.recentScans.length < 10) {\n entry.recentScans.push({\n runId: run.id,\n timestamp: run.timestamp,\n status: run.status,\n skillStatus: repo.skillStatus,\n reason: repo.reason,\n });\n }\n }\n }\n\n // 2. Read summary index — count summaries per repo\n const summaryCounts = new Map<string, { count: number; lastAt: string | null }>();\n try {\n const index = readSummaryIndex();\n for (const entry of Object.values(index.conversations)) {\n if (!entry.repo) continue;\n const key = entry.repo.toLowerCase();\n const existing = summaryCounts.get(key);\n if (!existing) {\n summaryCounts.set(key, { count: 1, lastAt: entry.summarizedAt });\n } else {\n existing.count++;\n if (entry.summarizedAt && (!existing.lastAt || entry.summarizedAt > existing.lastAt)) {\n existing.lastAt = entry.summarizedAt;\n }\n }\n }\n } catch { /* ignore */ }\n\n // 3. Read repo states from state.json\n const store = new StateStore(loreDir);\n const state = store.read();\n const repoStates = state.repoStates ?? {};\n\n // 4. Merge all sources into a unified response\n // Collect all known repo keys across all three sources\n const allRepoKeys = new Set<string>();\n for (const key of repoRunMap.keys()) allRepoKeys.add(key);\n for (const key of summaryCounts.keys()) allRepoKeys.add(key);\n for (const key of Object.keys(repoStates)) allRepoKeys.add(key.toLowerCase());\n\n const repos: Array<{\n repo: string;\n repoPath: string;\n totalScans: number;\n lastScanAt: string | null;\n lastScanStatus: 'completed' | 'failed' | null;\n lastScanDuration: number | null;\n lastScanItemsScanned: number;\n lastScanErrors: string[];\n lastSkillStatus: 'generated' | 'updated' | 'skipped' | 'failed' | null;\n summariesCount: number;\n lastSummaryAt: string | null;\n lastSkillGenAt: string | null;\n lastClusterAt: string | null;\n recentScans: Array<{\n runId: string;\n timestamp: string;\n status: 'completed' | 'failed';\n skillStatus: string;\n reason: string;\n }>;\n }> = [];\n\n for (const key of allRepoKeys) {\n const runData = repoRunMap.get(key);\n const summaryData = summaryCounts.get(key);\n\n // Find repo state (case-insensitive lookup)\n let repoState: { lastSkillGenAt: string | null; lastClusterAt: string | null } | undefined;\n for (const [k, v] of Object.entries(repoStates)) {\n if (k.toLowerCase() === key) {\n repoState = v;\n break;\n }\n }\n\n // Determine display name: prefer the casing from run history\n let displayName = key;\n if (runData) {\n // Find original casing from the first run that has this repo\n for (const run of runs) {\n const match = (run.repos ?? []).find((r) => r.repo.toLowerCase() === key);\n if (match) {\n displayName = match.repo;\n break;\n }\n }\n } else {\n // Use casing from repoStates or summaryCounts\n for (const k of Object.keys(repoStates)) {\n if (k.toLowerCase() === key) { displayName = k; break; }\n }\n }\n\n repos.push({\n repo: displayName,\n repoPath: runData?.repoPath ?? '',\n totalScans: runData?.totalScans ?? 0,\n lastScanAt: runData?.lastScanAt ?? null,\n lastScanStatus: runData?.lastScanStatus ?? null,\n lastScanDuration: runData?.lastScanDuration ?? null,\n lastScanItemsScanned: runData?.lastScanItemsScanned ?? 0,\n lastScanErrors: runData?.lastScanErrors ?? [],\n lastSkillStatus: runData?.lastSkillStatus ?? null,\n summariesCount: summaryData?.count ?? 0,\n lastSummaryAt: summaryData?.lastAt ?? null,\n lastSkillGenAt: repoState?.lastSkillGenAt ?? null,\n lastClusterAt: repoState?.lastClusterAt ?? null,\n recentScans: runData?.recentScans ?? [],\n });\n }\n\n // Sort: most recently scanned first, then by summaries count\n repos.sort((a, b) => {\n if (a.lastScanAt && b.lastScanAt) return b.lastScanAt.localeCompare(a.lastScanAt);\n if (a.lastScanAt) return -1;\n if (b.lastScanAt) return 1;\n return b.summariesCount - a.summariesCount;\n });\n\n return c.json({ repos });\n});\n\n// ── POST /api/repos/config — update repo selection config ─────────\n\nreposApi.post('/config', async (c) => {\n const body = await c.req.json().catch(() => ({}));\n const loreDir = getLoreDir();\n const configPath = resolve(loreDir, 'config.json');\n\n // Load current raw config\n let raw: z.input<typeof loreConfigSchema> = {};\n if (existsSync(configPath)) {\n try {\n raw = JSON.parse(readFileSync(configPath, 'utf-8'));\n } catch { /* start fresh */ }\n }\n\n // Merge repos config\n if (!raw.repos) raw.repos = {};\n\n if (body.mode !== undefined) raw.repos.mode = body.mode;\n if (body.include !== undefined) raw.repos.include = body.include;\n if (body.exclude !== undefined) raw.repos.exclude = body.exclude;\n if (body.maxRepos !== undefined) {\n if (!raw.repos.auto) raw.repos.auto = {};\n raw.repos.auto.maxRepos = body.maxRepos;\n }\n if (body.activityWindowDays !== undefined) {\n if (!raw.repos.auto) raw.repos.auto = {};\n raw.repos.auto.activityWindowDays = body.activityWindowDays;\n }\n\n // Validate\n try {\n loreConfigSchema.parse(raw);\n } catch (err) {\n return c.json({ error: `Invalid config: ${err}` }, 400);\n }\n\n writeFileSync(configPath, JSON.stringify(raw, null, 2) + '\\n', 'utf-8');\n\n // Bust the repos cache so the next GET reflects new config\n invalidateReposCache();\n\n return c.json({ success: true });\n});\n\n// ── POST /api/repos/invalidate — bust the cache manually ──────────\n\nreposApi.post('/invalidate', (c) => {\n invalidateReposCache();\n return c.json({ success: true });\n});\n\n// ── Cache warm-up ─────────────────────────────────────────────────\n\n/**\n * Pre-warm the repos cache at server startup so the first request is instant.\n * Called from the central warmCache() function.\n */\nexport async function warmReposCache(): Promise<void> {\n const data = await buildReposResponse();\n reposCache = { data, timestamp: Date.now() };\n}\n","/**\n * Bounded LRU cache with stale-while-revalidate for expensive backend operations.\n *\n * Wraps functions like getCursorConversations, getClaudeCodeToolUsage, etc.\n * Uses `lru-cache` to bound memory and provide instant responses even after TTL\n * expiry (serves stale data while refreshing in the background).\n */\n\nimport { LRUCache } from 'lru-cache';\nimport { DEFAULT_CACHE_TTL_MS } from '../../utils/constants.js';\nimport { logger } from '../../utils/logger.js';\n\nconst cache = new LRUCache<string, object>({\n max: 20, // plenty for current 5 keys + room to grow\n ttl: DEFAULT_CACHE_TTL_MS, // 5 min, same as before\n allowStale: true, // serve stale data immediately on TTL expiry\n updateAgeOnGet: true, // refresh TTL on access\n});\n\n/**\n * In-flight fetcher promises — prevents duplicate concurrent fetches for the\n * same key (e.g. two API requests hitting an expired key simultaneously).\n */\nconst inFlight = new Map<string, Promise<unknown>>();\n\n/**\n * Get a value from the cache, or compute it via `fetcher` if missing/expired.\n *\n * When the cached entry is stale (past TTL but still in the LRU):\n * - Returns the stale value **immediately** (no waiting).\n * - Triggers a background refresh so the next call gets fresh data.\n *\n * When the cache has no entry at all:\n * - Calls `fetcher()` and waits for it.\n */\nexport async function getCached<T extends object>(\n key: string,\n fetcher: () => T | Promise<T>,\n ttl: number = DEFAULT_CACHE_TTL_MS,\n): Promise<T> {\n const staleValue = cache.get(key);\n\n if (staleValue !== undefined) {\n // If the entry is still within TTL, lru-cache returns it normally.\n // If it's past TTL but `allowStale` kept it, we get it here too.\n // Check if a background refresh is needed (entry is stale).\n const remaining = cache.getRemainingTTL(key);\n if (remaining <= 0) {\n // Stale — trigger background refresh (don't await)\n refreshInBackground(key, fetcher, ttl);\n }\n return staleValue as T;\n }\n\n // Cache miss — must wait for the fetcher\n return fetchAndCache(key, fetcher, ttl);\n}\n\n/**\n * Refresh a cache key in the background. De-duplicated via `inFlight`.\n */\nfunction refreshInBackground<T extends object>(\n key: string,\n fetcher: () => T | Promise<T>,\n ttl: number,\n): void {\n if (inFlight.has(key)) return; // already refreshing\n\n const promise = Promise.resolve()\n .then(() => fetcher())\n .then((data) => {\n cache.set(key, data, { ttl });\n return data;\n })\n .catch((err) => {\n logger.warn(`[cache] Background refresh failed for \"${key}\":`, err);\n })\n .finally(() => {\n inFlight.delete(key);\n });\n\n inFlight.set(key, promise);\n}\n\n/**\n * Fetch, cache, and return. De-duplicated so concurrent callers share one fetch.\n */\nasync function fetchAndCache<T extends object>(\n key: string,\n fetcher: () => T | Promise<T>,\n ttl: number,\n): Promise<T> {\n const existing = inFlight.get(key);\n if (existing) return existing as Promise<T>;\n\n const promise = Promise.resolve()\n .then(() => fetcher())\n .then((data) => {\n cache.set(key, data, { ttl });\n return data;\n })\n .finally(() => {\n inFlight.delete(key);\n });\n\n inFlight.set(key, promise);\n return promise as Promise<T>;\n}\n\n/**\n * Invalidate cache entries.\n * - No args: clear everything.\n * - With prefix: clear entries whose key starts with `prefix`.\n */\nexport function invalidateCache(prefix?: string): void {\n if (!prefix) {\n cache.clear();\n return;\n }\n for (const key of cache.keys()) {\n if (key.startsWith(prefix)) {\n cache.delete(key);\n }\n }\n}\n\n// ── Cache keys ──────────────────────────────────────────────────────\n\nexport const CACHE_KEYS = {\n cursorConversations: 'conversations:cursor',\n claudeConversations: 'conversations:claude',\n cursorToolUsage: 'tools:cursor',\n claudeToolUsage: 'tools:claude',\n cursorSkillUsage: 'tools:skills',\n} as const;\n\n/**\n * Pre-warm the cache by executing all expensive queries eagerly.\n * Called once at server startup so the first API requests are instant.\n */\nexport async function warmCache(): Promise<void> {\n // Lazy-import to avoid circular dependency at module level\n const { getSource, getCursorSource } = await import('../../data-sources/registry.js');\n const { warmReposCache } = await import('./repos.js');\n\n const cursor = getSource('cursor');\n const claude = getSource('claude-code');\n const cursorExt = getCursorSource();\n\n const start = Date.now();\n\n // Fire all in parallel for max speed — includes repos (async git scoring)\n await Promise.allSettled([\n getCached(CACHE_KEYS.cursorConversations, () => cursor.listConversations()),\n getCached(CACHE_KEYS.claudeConversations, () => claude.listConversations()),\n getCached(CACHE_KEYS.cursorToolUsage, () => cursor.getToolUsage()),\n getCached(CACHE_KEYS.claudeToolUsage, () => claude.getToolUsage()),\n getCached(CACHE_KEYS.cursorSkillUsage, () => cursorExt.getSkillUsage()),\n warmReposCache(),\n ]);\n\n const elapsed = Date.now() - start;\n logger.info(`[lore] Cache warmed in ${elapsed}ms`);\n}\n","import { Command } from 'commander';\nimport { initCommand } from '../src/commands/init.js';\nimport { syncCommand } from '../src/commands/sync.js';\nimport { watchCommand } from '../src/commands/watch.js';\nimport { statusCommand } from '../src/commands/status.js';\nimport { recallCommand } from '../src/commands/recall.js';\nimport { reviewCommand } from '../src/commands/review.js';\nimport { configCommand } from '../src/commands/config.js';\nimport { reposCommand } from '../src/commands/repos.js';\nimport { uiCommand } from '../src/commands/ui.js';\nimport { skillsCommand } from '../src/commands/skills.js';\nimport { summarizeCommand } from '../src/commands/summarize.js';\nimport { debugCommand } from '../src/commands/debug.js';\nimport { prCommand } from '../src/commands/pr.js';\nimport { setLogLevel } from '../src/utils/logger.js';\nimport { DEFAULT_UI_PORT } from '../src/utils/constants.js';\n\nconst program = new Command();\n\nprogram\n .name('lore')\n .description('Automated project knowledge that writes itself. Scans all AI coding conversations globally.')\n .version('0.1.0')\n .option('-v, --verbose', 'Enable verbose output')\n .hook('preAction', (thisCommand) => {\n if (thisCommand.opts().verbose) {\n setLogLevel('debug');\n }\n });\n\nprogram\n .command('init')\n .description('Set up Lore globally (~/.lore/)')\n .action(async () => {\n await initCommand();\n });\n\nprogram\n .command('sync')\n .description('Run a one-off scan of all AI conversations')\n .option('--dry-run', 'Show what would be done without making changes')\n .option('--cli <preference>', 'CLI agent to use: auto, claude, or cursor')\n .action(async (opts: { dryRun?: boolean; cli?: string }) => {\n await syncCommand(opts);\n });\n\nprogram\n .command('watch')\n .description('Start periodic scanning')\n .action(async () => {\n await watchCommand();\n });\n\nprogram\n .command('status')\n .description('Show what has been captured and what is pending')\n .action(async () => {\n await statusCommand();\n });\n\nprogram\n .command('recall <topic>')\n .description('Query the accumulated knowledge (progressive disclosure)')\n .option('-d, --detail <id>', 'Load full content for a specific entry ID')\n .option('--full', 'Legacy mode: query all knowledge at once (uses more tokens)')\n .action(async (topic: string, opts: { detail?: string; full?: boolean }) => {\n await recallCommand(topic, opts);\n });\n\nprogram\n .command('review')\n .description('Show proposed documentation updates awaiting approval')\n .action(async () => {\n await reviewCommand();\n });\n\nprogram\n .command('config')\n .description('View or edit configuration')\n .argument('[action]', 'Action: get, set, show')\n .argument('[key]', 'Config key (dot-notation)')\n .argument('[value]', 'Value to set')\n .action(async (action: string | undefined, key: string | undefined, value: string | undefined) => {\n await configCommand(action, key, value);\n });\n\nprogram\n .command('repos')\n .description('Interactively select which repos to process (or list/add/remove)')\n .argument('[action]', 'Action: select (default), list, active, add, remove')\n .argument('[name]', 'Repo name (for add/remove)')\n .action(async (action: string | undefined, name: string | undefined) => {\n await reposCommand(action, name);\n });\n\nprogram\n .command('ui')\n .description('Launch the Lore web dashboard')\n .option('-p, --port <port>', 'Port to run the dashboard on', String(DEFAULT_UI_PORT))\n .action(async (opts: { port: string }) => {\n await uiCommand({ port: parseInt(opts.port, 10) });\n });\n\nprogram\n .command('summarize')\n .description('Summarize all AI conversations and cluster by repo (Phase 1 + 2)')\n .option('-r, --repo <name>', 'Only process conversations for repos matching this name')\n .option('-f, --force', 'Re-summarize all conversations, ignoring cache')\n .action(async (opts: { repo?: string; force?: boolean }) => {\n await summarizeCommand(opts);\n });\n\nprogram\n .command('skills')\n .description('Generate per-repo SKILL.md guides from conversations and git history')\n .option('-r, --repo <name>', 'Only generate for repos matching this name')\n .option('-f, --force', 'Regenerate even if skill is up-to-date')\n .option('--dry-run', 'Show what would be generated without writing')\n .option('--skip-summarize', 'Skip the conversation summarization step')\n .option('--commit <sha>', 'Research against a specific commit SHA (defaults to HEAD)')\n .action(async (opts: { repo?: string; force?: boolean; dryRun?: boolean; skipSummarize?: boolean; commit?: string }) => {\n await skillsCommand(opts);\n });\n\nprogram\n .command('debug')\n .description('Run pipeline diagnostics to troubleshoot scan issues')\n .argument('[subcommand]', 'Sub-command: sources, scan (default), conversations')\n .option('--json', 'Output structured JSON for agent consumption')\n .option('-r, --repo <name>', 'Only diagnose conversations matching this repo')\n .option('-l, --limit <count>', 'Max conversations to show (for conversations sub-command)')\n .option('-s, --source <source>', 'Filter by data source: cursor or claude-code')\n .action(async (subcommand: string | undefined, opts: { json?: boolean; repo?: string; limit?: string; source?: string }) => {\n await debugCommand(subcommand, {\n json: opts.json,\n repo: opts.repo,\n limit: opts.limit ? parseInt(opts.limit, 10) : undefined,\n source: opts.source as 'cursor' | 'claude-code' | undefined,\n });\n });\n\nprogram\n .command('pr')\n .description('Generate a PR description from git history and cached conversation summaries')\n .option('-b, --base <branch>', 'Base branch (default: auto-detect from PR or main/master)')\n .option('-t, --tier <tier>', 'Quality tier: template (a), fast (b), detailed (c)', 'fast')\n .option('--no-copy', 'Do not copy to clipboard')\n .option('--inject', 'Also update the open PR on GitHub via gh CLI')\n .action(async (opts: { base?: string; tier?: string; copy?: boolean; inject?: boolean }) => {\n await prCommand(opts);\n });\n\nprogram.parse();\n","import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport pc from 'picocolors';\nimport * as clack from '@clack/prompts';\nimport { defaultConfig } from '../config/defaults.js';\nimport { loreConfigSchema, type LoreConfig } from '../config/schema.js';\nimport { defaultState } from '../state/schema.js';\nimport { getLoreDir, hasClaudeCodeData, hasCursorData } from '../utils/paths.js';\nimport { detectInstalledClis, type CliPreference } from '../utils/cli.js';\nimport { logger } from '../utils/logger.js';\n\nexport async function initCommand(): Promise<void> {\n const loreDir = getLoreDir();\n\n clack.intro(pc.bgCyan(pc.black(' lore init ')));\n\n // Check if already initialized\n if (existsSync(loreDir)) {\n const shouldOverwrite = await clack.confirm({\n message: 'Lore is already initialized (~/.lore/). Reinitialize?',\n initialValue: false,\n });\n if (clack.isCancel(shouldOverwrite) || !shouldOverwrite) {\n clack.outro('Cancelled.');\n return;\n }\n }\n\n // Detect available sources\n const detectedSources = detectSources();\n\n const spinner = clack.spinner();\n spinner.start('Setting up Lore...');\n\n // Detect installed CLI agents\n spinner.message('Detecting CLI agents...');\n const cliAgents = await detectInstalledClis();\n\n // Auto-select preference: if only one is installed, lock to it\n const detectedCli: CliPreference =\n cliAgents.claude && cliAgents.cursor ? 'auto' :\n cliAgents.claude ? 'claude' :\n cliAgents.cursor ? 'cursor' : 'auto';\n\n // Build config from detected sources and CLI preference\n const config: LoreConfig = {\n ...defaultConfig,\n sources: {\n cursor: { enabled: detectedSources.cursor },\n claudeCode: { enabled: detectedSources.claudeCode },\n },\n llm: {\n cli: detectedCli,\n },\n };\n\n // Validate config\n const validatedConfig = loreConfigSchema.parse(config);\n\n // Create ~/.lore/ directory structure\n mkdirSync(loreDir, { recursive: true });\n mkdirSync(resolve(loreDir, 'pending'), { recursive: true });\n mkdirSync(resolve(loreDir, 'output'), { recursive: true });\n\n // Write config and state\n writeFileSync(\n resolve(loreDir, 'config.json'),\n JSON.stringify(validatedConfig, null, 2) + '\\n',\n 'utf-8',\n );\n writeFileSync(\n resolve(loreDir, 'state.json'),\n JSON.stringify(defaultState, null, 2) + '\\n',\n 'utf-8',\n );\n\n spinner.stop('Lore initialized.');\n\n // Warn if no CLI agent found\n if (!cliAgents.claude && !cliAgents.cursor) {\n clack.log.warn(\n 'No AI CLI agent found. Install one of:\\n' +\n ' - Claude Code CLI: https://docs.anthropic.com/en/docs/claude-code\\n' +\n ' - Cursor Agent CLI: available via Cursor IDE',\n );\n }\n\n // Summary\n const cliLabel = detectedCli === 'auto' ? 'auto (both available)' : detectedCli;\n clack.note(\n [\n `${pc.bold('Detected sources:')}`,\n ` Cursor: ${detectedSources.cursor ? pc.green('yes') : pc.dim('no')}`,\n ` Claude Code: ${detectedSources.claudeCode ? pc.green('yes') : pc.dim('no')}`,\n '',\n `${pc.bold('CLI agents:')}`,\n ` Claude Code: ${cliAgents.claude ? pc.green('installed') : pc.dim('not found')}`,\n ` Cursor Agent: ${cliAgents.cursor ? pc.green('installed') : pc.dim('not found')}`,\n ` Preference: ${pc.cyan(cliLabel)}`,\n '',\n `${pc.bold('Location:')} ${pc.dim('~/.lore/')}`,\n `${pc.bold('Config:')} ${pc.dim('~/.lore/config.json')}`,\n `${pc.bold('State:')} ${pc.dim('~/.lore/state.json')}`,\n ].join('\\n'),\n 'Setup Summary',\n );\n\n clack.outro(`Run ${pc.cyan('lore sync')} to perform your first scan.`);\n}\n\ninterface DetectedSources {\n cursor: boolean;\n claudeCode: boolean;\n}\n\nfunction detectSources(): DetectedSources {\n const result: DetectedSources = {\n cursor: false,\n claudeCode: false,\n };\n\n result.cursor = hasCursorData();\n result.claudeCode = hasClaudeCodeData();\n\n logger.debug('Detected sources:', result);\n\n return result;\n}\n","import { existsSync } from 'node:fs';\nimport pc from 'picocolors';\nimport * as clack from '@clack/prompts';\nimport { Listr } from 'listr2';\nimport { getLoreDir } from '../utils/paths.js';\nimport { THIRTY_DAYS_MS } from '../utils/constants.js';\nimport { shouldIgnoreConversation } from '../utils/conversation-filter.js';\nimport { buildLoreIgnoreMap } from '../utils/lore-filter.js';\nimport { StateStore } from '../state/store.js';\nimport { loadConfig } from '../config/loader.js';\nimport { setCliPreference, type CliPreference } from '../utils/cli.js';\nimport { CursorScanner } from '../scanners/cursor.js';\nimport { ClaudeCodeScanner } from '../scanners/claude-code.js';\nimport { DistillationEngine } from '../distiller/engine.js';\nimport { ReviewQueueWriter } from '../writers/review-queue.js';\nimport { generateAllRepoSkills } from '../skill-generator/generator.js';\nimport { summarizeAllConversations } from '../summarizer/summarizer.js';\nimport { clusterAllRepos } from '../summarizer/clusterer.js';\nimport { selectReposForProcessing, getSelectedRepoNames } from '../repos/selector.js';\nimport { logger } from '../utils/logger.js';\nimport type { ScanResult, ConversationSession, AlignmentResult } from '../scanners/types.js';\n\ninterface SyncOptions {\n dryRun?: boolean;\n cli?: string;\n}\n\nexport async function syncCommand(options: SyncOptions): Promise<void> {\n const loreDir = getLoreDir();\n\n if (!existsSync(loreDir)) {\n clack.intro(pc.bgCyan(pc.black(' lore sync ')));\n clack.log.warn(`Lore is not initialized. Run ${pc.cyan('lore init')} first.`);\n clack.outro('');\n return;\n }\n\n clack.intro(pc.bgCyan(pc.black(' lore sync ')));\n\n // Load config and state\n const config = loadConfig(loreDir);\n\n // Wire CLI agent preference: flag overrides config\n const cliPref = (options.cli ?? config.llm.cli) as CliPreference;\n if (cliPref !== 'auto') {\n clack.log.info(`CLI agent preference: ${pc.cyan(cliPref)}`);\n }\n setCliPreference(cliPref);\n const store = new StateStore(loreDir);\n const state = store.read();\n // Default to last 30 days when no previous scan exists,\n // to avoid pulling in ancient history on first sync\n const since = state.lastScanAt\n ?? new Date(Date.now() - THIRTY_DAYS_MS).toISOString();\n\n // Collect scan results\n const scanResults: ScanResult[] = [];\n const conversations: ConversationSession[] = [];\n\n const scanTasks = new Listr(\n [\n {\n title: 'Scanning Cursor conversations',\n enabled: () => config.sources.cursor.enabled,\n task: async () => {\n const scanner = new CursorScanner();\n const result = await scanner.scan(since);\n scanResults.push(result);\n logger.debug(`Cursor: found ${result.items.length} items`);\n },\n },\n {\n title: 'Scanning Claude Code sessions',\n enabled: () => config.sources.claudeCode.enabled,\n task: async () => {\n const scanner = new ClaudeCodeScanner();\n const result = await scanner.scan(since);\n scanResults.push(result);\n conversations.push(...scanner.getLastSessions());\n logger.debug(`Claude Code: found ${result.items.length} items, ${conversations.length} sessions`);\n },\n },\n ],\n { concurrent: true, rendererOptions: { collapseSubtasks: false } },\n );\n\n await scanTasks.run();\n\n const totalItems = scanResults.reduce((sum, r) => sum + r.items.length, 0);\n if (totalItems === 0) {\n clack.outro('Nothing new to process since last scan.');\n return;\n }\n\n clack.log.info(`Found ${pc.bold(String(totalItems))} items across ${scanResults.length} sources.`);\n\n if (options.dryRun) {\n clack.note(\n [\n `Total items: ${totalItems}`,\n `Cursor items: ${scanResults.find((r) => r.source === 'cursor')?.items.length ?? 0}`,\n `Claude Code items: ${scanResults.find((r) => r.source === 'claude-code')?.items.length ?? 0}`,\n '',\n 'Dry run -- no changes written.',\n ].join('\\n'),\n 'Dry Run Summary',\n );\n clack.outro('Done (dry run).');\n return;\n }\n\n // Filter out lore **process** conversations before distillation.\n // Uses origin-based detection (phase tags + prompt markers) instead of\n // repo-name matching so user conversations in the lore repo pass through.\n const ignoreMap = buildLoreIgnoreMap();\n const filteredConversations = conversations.filter((c) => {\n const convId = `claude-${c.sessionId}`;\n const firstUserMsg = c.turns.find((t) => t.role === 'user')?.content;\n return !shouldIgnoreConversation(convId, ignoreMap, undefined, firstUserMsg);\n });\n const loreExcluded = conversations.length - filteredConversations.length;\n if (loreExcluded > 0) {\n clack.log.info(`Filtered out ${loreExcluded} lore process conversation(s).`);\n }\n\n // Distill -- pass all scan items (no git alignment needed for global mode)\n const spinner = clack.spinner();\n spinner.start('Distilling knowledge via Claude...');\n\n const allItems = scanResults.flatMap((r) => r.items);\n const alignment: AlignmentResult = {\n matched: [],\n unmatchedCommits: [],\n unmatchedConversations: filteredConversations,\n };\n\n const distiller = new DistillationEngine(loreDir);\n const distillOutput = await distiller.distill(alignment, []);\n spinner.stop('Distillation complete.');\n\n // Write outputs to review queue (global mode always proposes)\n const writeTasks = new Listr(\n [\n {\n title: 'Queuing changelog',\n enabled: () => distillOutput.changelog.length > 0,\n task: async () => {\n const writer = new ReviewQueueWriter(loreDir);\n const firstLine = distillOutput.changelog.split('\\n').find((l) => l.trim()) ?? 'Changelog';\n writer.enqueue('changelog', distillOutput.changelog, {\n title: firstLine.replace(/^#+\\s*/, '').slice(0, 80),\n });\n },\n },\n {\n title: 'Queuing decision records',\n enabled: () => distillOutput.decisions.length > 0,\n task: async () => {\n const writer = new ReviewQueueWriter(loreDir);\n for (const decision of distillOutput.decisions) {\n writer.enqueue('adr', JSON.stringify(decision, null, 2), {\n title: decision.title,\n });\n }\n },\n },\n {\n title: 'Queuing architecture updates',\n enabled: () => distillOutput.changes.length > 0 || distillOutput.patterns.length > 0,\n task: async () => {\n const writer = new ReviewQueueWriter(loreDir);\n const cc = distillOutput.changes.length;\n const pc = distillOutput.patterns.length;\n writer.enqueue('architecture', JSON.stringify({\n changes: distillOutput.changes,\n patterns: distillOutput.patterns,\n }, null, 2), {\n title: `${cc} change${cc !== 1 ? 's' : ''}, ${pc} pattern${pc !== 1 ? 's' : ''}`,\n });\n },\n },\n ],\n { concurrent: false },\n );\n\n await writeTasks.run();\n\n // ── Repo selection ──────────────────────────────────────────────\n // Select which repos to process for the expensive stages (cluster + skill-gen).\n // Scanning and summarizing still run globally (they're per-conversation and cached).\n const repoSelection = await selectReposForProcessing(config.repos);\n const selectedNames = getSelectedRepoNames(repoSelection);\n const selectedRepos = repoSelection.selected.map((sr) => sr.repo);\n\n clack.log.info(\n `Repo selection (${pc.bold(repoSelection.mode)}): ${pc.bold(String(repoSelection.selected.length))} of ${repoSelection.all.length} repos selected for clustering + skill generation.`,\n );\n\n // Summarize conversations (Phase 1 + 2)\n const sumSpinner = clack.spinner();\n sumSpinner.start('Summarizing new conversations...');\n try {\n const { result: sumResult } = await summarizeAllConversations({\n onOutput: (line) => {\n const shortLine = line.length > 80 ? line.slice(0, 77) + '...' : line;\n sumSpinner.message(shortLine);\n },\n });\n sumSpinner.stop(\n `Summarized ${sumResult.summarized} conversations (${sumResult.skipped} cached).`,\n );\n\n const clusterSpinner = clack.spinner();\n clusterSpinner.start('Clustering conversations by repo...');\n const reposClustered = await clusterAllRepos({\n selectedRepoNames: selectedNames,\n onOutput: (line) => {\n const shortLine = line.length > 80 ? line.slice(0, 77) + '...' : line;\n clusterSpinner.message(shortLine);\n },\n });\n clusterSpinner.stop(`Clustered ${reposClustered} repos.`);\n } catch (err) {\n sumSpinner.stop('Summarization failed (non-fatal).');\n logger.debug('Summarization error:', err);\n }\n\n // Generate per-repo skills (Phase 3) — only for selected repos, with worktree isolation\n const skillSpinner = clack.spinner();\n skillSpinner.start('Generating per-repo skills...');\n try {\n const skillResults = await generateAllRepoSkills({\n selectedRepos,\n useWorktree: true,\n onOutput: (line) => {\n const shortLine = line.length > 80 ? line.slice(0, 77) + '...' : line;\n skillSpinner.message(shortLine);\n },\n });\n const genCount = skillResults.filter(\n (r) => r.status === 'generated' || r.status === 'updated',\n ).length;\n skillSpinner.stop(`Skills: ${genCount} generated/updated, ${skillResults.length - genCount} skipped.`);\n } catch (err) {\n skillSpinner.stop('Skill generation failed (non-fatal).');\n logger.debug('Skill generation error:', err);\n }\n\n // Update state\n const now = new Date().toISOString();\n store.update({\n lastScanAt: now,\n scanCount: state.scanCount + 1,\n });\n\n clack.outro(`Sync complete. Run ${pc.cyan('lore review')} to inspect proposed changes.`);\n}\n","/**\n * Per-repo skill generator.\n *\n * For each discovered repo, generates a SKILL.md inside the repo's\n * .cursor/skills/{name}-guide/ directory. Uses Octocode + Claude Code CLI\n * to actively research the repo and combine findings with Lore conversation data.\n */\n\nimport { execa } from 'execa';\nimport {\n existsSync,\n readFileSync,\n readdirSync,\n writeFileSync,\n mkdirSync,\n statSync,\n} from 'node:fs';\nimport { join, basename } from 'node:path';\nimport { buildRepoCatalog, type RepoCatalogEntry } from '../classifier/repo-catalog.js';\nimport { loadPrompt, splitPrompt, createSystemPromptFile } from '../distiller/prompts.js';\nimport { createMcpConfigFile } from '../distiller/mcp-config.js';\nimport { countTokens, getMaxInputTokens } from '../distiller/tokenizer.js';\nimport { logger } from '../utils/logger.js';\nimport { ensureAnyCli, invokeWithFallback, invokeWithFallbackAndTag, isRateLimited, snapshotClaudeSessions, detectNewSessions } from '../utils/cli.js';\nimport { tagConversations } from '../state/phase-store.js';\nimport { loadRepoCluster, repoNameToSlug } from '../summarizer/cache.js';\nimport { extractJson } from '../summarizer/schemas.js';\nimport { parseMultiFileOutput, cleanOutput, extractResultFromEvents, emitStreamEvent } from './output-parser.js';\nimport { writeSkillFiles, listSkillFiles, hasDeadLinks, patchSkillWithReferenceLinks, copySkillFiles } from './skill-files.js';\nimport { injectAgentPointers } from './agent-pointer.js';\nimport { findDeadLinksDetailed, removeDeadLinks, findOrphanedFiles, migrateDeprecatedFiles } from './orphan-detector.js';\nimport { sanitizeName, extractLastUpdated, buildTechSpecificGuidance } from './skill-utils.js';\nimport { collectInstalledSkillDigests, collectUsedSkillDigests, mergeSkillDigests, formatSkillDigests, collectInstalledMcpDigests, collectUsedMcpDigests, mergeMcpDigests, formatMcpDigests } from './skill-digest.js';\nimport {\n createScanWorktree,\n cleanupScanWorktree,\n cleanupStaleWorktrees,\n resolveHeadSha,\n mergeSkillsFromWorktree,\n} from '../utils/worktree.js';\nimport { errorMessage } from '../utils/errors.js';\nimport type {\n RepoSkillOptions,\n SkillGenerationResult,\n RelevanceVerdict,\n} from './types.js';\nimport { tmpdir } from 'node:os';\nimport { randomUUID } from 'node:crypto';\nimport {\n SKILL_TIMEOUT_MS,\n DELTA_TIMEOUT_MS,\n RELEVANCE_TIMEOUT_MS,\n GIT_LOG_LIMIT,\n GIT_MEDIUM_TIMEOUT_MS,\n GIT_SLOW_TIMEOUT_MS,\n MIN_USABLE_OUTPUT,\n MAX_EXISTING_SKILL_CHARS,\n MAX_DELTA_SKILL_CHARS,\n MAX_REFS_PER_FILE_CHARS,\n GIT_DIFF_MAX_LINES,\n DEFAULT_UI_PORT,\n WRITING_STALL_TIMEOUT_MS,\n WRITING_HARD_CEILING_MS,\n VERDICT_ENV_OVERRIDES,\n WRITING_ENV_OVERRIDES,\n} from '../utils/constants.js';\n\n// ── Constants ─────────────────────────────────────────────────────\n\n// Research doc sections in priority order (highest first).\n// When truncating to fit context window, we cut from the bottom.\nconst RESEARCH_SECTION_PRIORITY = [\n '## Project Identity',\n '## Architecture',\n '## Key Entry Points',\n '## Established Patterns',\n '## Directory Structure',\n '## Dependencies & Tooling',\n '## Configuration',\n '## Testing Patterns',\n '## Code Examples',\n '## Notable Files',\n];\n\n/**\n * Truncate a research document by removing low-priority sections first.\n * Preserves the critical sections (Architecture, Key Entry Points, Established Patterns).\n * Falls back to simple character truncation if section parsing fails.\n */\nasync function truncateResearchByPriority(\n doc: string,\n maxTokens: number,\n): Promise<{ text: string; truncated: boolean }> {\n const currentTokens = await countTokens(doc);\n if (currentTokens <= maxTokens) {\n return { text: doc, truncated: false };\n }\n\n // Split document into sections by ## headers\n const sectionRegex = /^(## .+)$/gm;\n const sections: Array<{ header: string; content: string; priority: number }> = [];\n let lastIdx = 0;\n let preamble = '';\n let match: RegExpExecArray | null;\n\n const matches: Array<{ header: string; index: number }> = [];\n while ((match = sectionRegex.exec(doc)) !== null) {\n matches.push({ header: match[1], index: match.index });\n }\n\n if (matches.length === 0) {\n // No sections found — fall back to character truncation\n const charLimit = Math.floor(maxTokens * 3.5);\n return { text: doc.slice(0, charLimit) + '\\n\\n[... truncated to fit context window ...]', truncated: true };\n }\n\n preamble = doc.slice(0, matches[0].index);\n\n for (let i = 0; i < matches.length; i++) {\n const start = matches[i].index;\n const end = i + 1 < matches.length ? matches[i + 1].index : doc.length;\n const priorityIdx = RESEARCH_SECTION_PRIORITY.indexOf(matches[i].header);\n sections.push({\n header: matches[i].header,\n content: doc.slice(start, end),\n priority: priorityIdx >= 0 ? priorityIdx : RESEARCH_SECTION_PRIORITY.length,\n });\n }\n\n // Sort by priority (highest = lowest index) and accumulate until budget is reached\n const sorted = [...sections].sort((a, b) => a.priority - b.priority);\n const kept: typeof sections = [];\n let runningTokens = await countTokens(preamble);\n\n for (const section of sorted) {\n const sectionTokens = await countTokens(section.content);\n if (runningTokens + sectionTokens <= maxTokens) {\n kept.push(section);\n runningTokens += sectionTokens;\n }\n }\n\n // Reassemble in original order\n kept.sort((a, b) => {\n const aIdx = sections.indexOf(a);\n const bIdx = sections.indexOf(b);\n return aIdx - bIdx;\n });\n\n const result = preamble + kept.map((s) => s.content).join('');\n return { text: result.trim(), truncated: true };\n}\n\n// ── Public API ────────────────────────────────────────────────────\n\n/**\n * Generate skills for all repos in the catalog.\n * Returns a result per repo.\n */\nexport async function generateAllRepoSkills(\n options: RepoSkillOptions = {},\n): Promise<SkillGenerationResult[]> {\n // Use pre-filtered repos from selector when available\n const catalog = buildRepoCatalog(options.selectedRepos);\n const results: SkillGenerationResult[] = [];\n\n // Filter repos if requested (CLI --repo flag, applied on top of selector)\n let repos = catalog;\n if (options.repoFilter) {\n const filter = options.repoFilter.toLowerCase();\n repos = catalog.filter(\n (r) =>\n r.name.toLowerCase().includes(filter) ||\n r.path.toLowerCase().includes(filter),\n );\n }\n\n // Filter out repos without a real path (global buckets)\n repos = repos.filter((r) => existsSync(r.path));\n\n // Deduplicate: same remote name may appear from multiple clone locations.\n // Keep only the clone with the most recent git activity.\n repos = await deduplicateRepos(repos, options);\n\n options.onOutput?.(`[skill-gen] ${repos.length} repos to process`);\n\n // Process repos with rate-limit-aware concurrency (2 parallel by default).\n // Falls back to sequential if rate limited.\n const SKILL_GEN_CONCURRENCY = 2;\n let useSequential = false;\n\n for (let i = 0; i < repos.length; i += useSequential ? 1 : SKILL_GEN_CONCURRENCY) {\n if (options.signal?.aborted) {\n for (let j = i; j < repos.length; j++) {\n results.push({\n repo: repos[j].name,\n repoPath: repos[j].path,\n skillPath: null,\n status: 'skipped',\n reason: 'Cancelled by user',\n });\n }\n break;\n }\n\n const batch = useSequential\n ? [repos[i]]\n : repos.slice(i, i + SKILL_GEN_CONCURRENCY);\n\n const batchPromises = batch.map(async (repo, batchIdx) => {\n const idx = i + batchIdx;\n options.onOutput?.(`[skill-gen] [${idx + 1}/${repos.length}] ${repo.name}`);\n const result = await generateRepoSkill(repo, options);\n options.onOutput?.(\n `[skill-gen] [${idx + 1}/${repos.length}] ${repo.name} → ${result.status}: ${result.reason}`,\n );\n return result;\n });\n\n const batchResults = await Promise.all(batchPromises);\n results.push(...batchResults);\n\n // If any result failed due to rate limiting, switch to sequential\n if (!useSequential && batchResults.some((r) => r.status === 'failed' && r.reason?.includes('rate'))) {\n options.onOutput?.(`[skill-gen] Rate limit detected, switching to sequential processing`);\n useSequential = true;\n }\n }\n\n // Summary\n const generated = results.filter((r) => r.status === 'generated' || r.status === 'updated').length;\n const skipped = results.filter((r) => r.status === 'skipped').length;\n const failed = results.filter((r) => r.status === 'failed').length;\n options.onOutput?.(\n `[skill-gen] Done: ${generated} generated/updated, ${skipped} skipped, ${failed} failed`,\n );\n\n return results;\n}\n\n/**\n * Generate a skill for a single repo.\n */\nexport async function generateRepoSkill(\n repo: RepoCatalogEntry,\n options: RepoSkillOptions = {},\n): Promise<SkillGenerationResult> {\n // ── Path strategy ───────────────────────────────────────────────\n // mainSkillDir: always based on repo.path — used for reading existing\n // skill and as the final destination after merge.\n // writeSkillDir: where generated files are written DURING generation.\n // Points to worktree when available (observable), or\n // falls back to mainSkillDir.\n const mainSkillDir = getSkillDir(repo);\n const mainSkillPath = join(mainSkillDir, 'SKILL.md');\n\n // ── Worktree isolation ──────────────────────────────────────────\n // Priority: explicit researchPath > per-repo useWorktree > repo.path\n let readPath = options.researchPath ?? repo.path;\n let ownedWorktree = false; // true when we created the worktree and must clean it up\n\n if (!options.researchPath && options.useWorktree) {\n try {\n await cleanupStaleWorktrees(repo.path);\n const sha = options.worktreeCommit ?? await resolveHeadSha(repo.path);\n readPath = await createScanWorktree(repo.path, sha);\n ownedWorktree = true;\n options.onOutput?.(`[skill-gen] Created worktree for ${repo.name} at ${readPath} (${sha.slice(0, 8)})`);\n } catch (wtErr: any) {\n options.onOutput?.(`[skill-gen] Worktree creation failed for ${repo.name}, using live repo: ${wtErr.message ?? wtErr}`);\n readPath = repo.path;\n }\n }\n\n // Determine where to write generated files:\n // writePath (explicit) > worktree readPath (if we own it or caller set researchPath) > repo.path\n const writeBasePath = options.writePath ?? (readPath !== repo.path ? readPath : repo.path);\n const writeSkillDir = join(writeBasePath, '.cursor', 'skills', `${sanitizeName(repo.name)}-guide`);\n const writeSkillPath = join(writeSkillDir, 'SKILL.md');\n\n // Seed writeSkillDir with existing files so the agent can read-modify-write in place.\n // When using a worktree, the agent writes to writeSkillDir (worktree) but existing\n // files live in mainSkillDir (main repo). Copy them over so reads work.\n if (writeSkillDir !== mainSkillDir && existsSync(mainSkillDir)) {\n mkdirSync(writeSkillDir, { recursive: true });\n const seeded = copySkillFiles(mainSkillDir, writeSkillDir);\n if (seeded > 0) {\n options.onOutput?.(`[skill-gen] Seeded worktree skill dir with ${seeded} existing file(s)`);\n }\n }\n\n try {\n const pipelineStart = Date.now();\n\n // Check for existing skill in the MAIN repo and extract lastUpdated\n let existingContent = '';\n let lastUpdated: string | null = null;\n\n if (existsSync(mainSkillPath)) {\n existingContent = readFileSync(mainSkillPath, 'utf-8');\n lastUpdated = extractLastUpdated(existingContent);\n }\n\n // Check if supporting files referenced in SKILL.md are missing (skill integrity)\n const supportingFilesMissing = existingContent && hasDeadLinks(existingContent, mainSkillDir);\n\n // Load clustered conversation knowledge from ~/.lore/summaries/repos/\n const repoSlug = repoNameToSlug(repo.name);\n const clusteredKnowledge = loadRepoCluster(repoSlug);\n\n // Get git log since lastUpdated\n const gitLog = await getGitLog(readPath, lastUpdated && !options.force ? lastUpdated : null);\n\n // ── Early exit: no new data at all ──────────────────────────────\n if (!options.force && existingContent && !clusteredKnowledge && !supportingFilesMissing && gitLog.length === 0) {\n // Even on early exit, ensure agent pointer files exist (one-time backfill\n // for repos whose SKILL.md was generated before this feature was added).\n const skillRelPath = `.cursor/skills/${sanitizeName(repo.name)}-guide/SKILL.md`;\n const pointerResult = injectAgentPointers(repo.path, skillRelPath);\n if (pointerResult.agents) options.onOutput?.(`[skill-gen] Injected Lore pointer into AGENTS.md`);\n if (pointerResult.claude) options.onOutput?.(`[skill-gen] Injected Lore pointer into CLAUDE.md`);\n\n return {\n repo: repo.name,\n repoPath: repo.path,\n skillPath: mainSkillPath,\n status: 'skipped',\n reason: 'Up-to-date (no clustered knowledge or commits)',\n };\n }\n\n if (supportingFilesMissing) {\n options.onOutput?.(`[skill-gen] Supporting files missing in skill dir, regenerating...`);\n }\n\n // ── Phase 2.5: Relevance check (planning agent) ────────────────\n // When an existing skill exists and --force is not set, ask the AI\n // whether the delta is significant enough to warrant regeneration.\n // Returns a verdict with an optional work plan for selective generation.\n let verdict: RelevanceVerdict | null = null;\n\n if (!options.force && existingContent && !supportingFilesMissing) {\n const existingFiles = listSkillFiles(mainSkillDir);\n const diffStat = lastUpdated\n ? await getGitDiffStat(readPath, lastUpdated)\n : 'No previous update timestamp — cannot compute diff.';\n\n const verdictStart = Date.now();\n verdict = await checkRelevance(\n existingContent,\n existingFiles,\n clusteredKnowledge,\n gitLog,\n diffStat,\n readPath,\n lastUpdated,\n repo.name,\n options,\n );\n const verdictElapsed = ((Date.now() - verdictStart) / 1000).toFixed(1);\n options.onOutput?.(`[skill-gen] Relevance check complete for ${repo.name} (${verdictElapsed}s)`);\n\n // Notify caller of the verdict (for UI notifications)\n if (options.onVerdict) {\n try {\n options.onOutput?.(`[skill-gen] Invoking verdict callback for ${repo.name}...`);\n const override = await options.onVerdict(verdict, repo.name);\n options.onOutput?.(`[skill-gen] Verdict callback returned: ${override}`);\n\n // For negative verdicts, the caller can override (e.g. user clicked \"Override\")\n if (!verdict.shouldUpdate) {\n if (override) {\n options.onOutput?.(`[skill-gen] Verdict overridden by user — proceeding with full update for ${repo.name}`);\n verdict.shouldUpdate = true;\n verdict.reason = `${verdict.reason} (overridden by user)`;\n // Clear work plan so we do a full regeneration on override\n verdict.workPlan = undefined;\n }\n }\n } catch (verdictErr: any) {\n options.onOutput?.(`[skill-gen] Verdict callback error: ${verdictErr.message ?? verdictErr}`);\n // Non-fatal: continue with the verdict as-is\n }\n } else {\n options.onOutput?.(`[skill-gen] No onVerdict callback set`);\n }\n\n if (!verdict.shouldUpdate) {\n // Backfill agent pointers even when skipping generation\n const skillRelPath = `.cursor/skills/${sanitizeName(repo.name)}-guide/SKILL.md`;\n const pointerResult = injectAgentPointers(repo.path, skillRelPath);\n if (pointerResult.agents) options.onOutput?.(`[skill-gen] Injected Lore pointer into AGENTS.md`);\n if (pointerResult.claude) options.onOutput?.(`[skill-gen] Injected Lore pointer into CLAUDE.md`);\n\n return {\n repo: repo.name,\n repoPath: repo.path,\n skillPath: mainSkillPath,\n status: 'skipped',\n reason: `Relevance check: ${verdict.reason}`,\n };\n }\n\n // Verdict-only mode: return after the relevance check without generating\n if (options.verdictOnly) {\n options.onOutput?.(`[skill-gen] Verdict-only mode — stopping after relevance check for ${repo.name}`);\n\n // Backfill agent pointers even in verdict-only mode\n const skillRelPath = `.cursor/skills/${sanitizeName(repo.name)}-guide/SKILL.md`;\n const pointerResult = injectAgentPointers(repo.path, skillRelPath);\n if (pointerResult.agents) options.onOutput?.(`[skill-gen] Injected Lore pointer into AGENTS.md`);\n if (pointerResult.claude) options.onOutput?.(`[skill-gen] Injected Lore pointer into CLAUDE.md`);\n\n return {\n repo: repo.name,\n repoPath: repo.path,\n skillPath: mainSkillPath,\n status: 'skipped',\n reason: `Verdict-only: ${verdict.reason} (shouldUpdate=${verdict.shouldUpdate}, confidence=${verdict.confidence})`,\n };\n }\n }\n\n // Verdict-only with no existing skill: nothing to check\n if (options.verdictOnly) {\n options.onOutput?.(`[skill-gen] Verdict-only mode — no existing skill for ${repo.name}, skipping`);\n return {\n repo: repo.name,\n repoPath: repo.path,\n skillPath: mainSkillPath,\n status: 'skipped',\n reason: 'Verdict-only: no existing skill to check relevance against',\n };\n }\n\n // Determine which files to update (from work plan or all)\n const workPlan = verdict?.workPlan;\n const filesToUpdate = workPlan?.filesToUpdate;\n const researchFocusHints = workPlan?.researchFocus;\n const deltaAnalysis = verdict?.deltaAnalysis;\n\n // Build prompt context\n const clusteredStr = clusteredKnowledge || 'No conversation summaries available yet. Generate from repo research only.';\n const gitLogStr = gitLog.length > 0 ? gitLog.join('\\n') : 'No recent git activity.';\n const skillName = `${sanitizeName(repo.name)}-guide`;\n const timestamp = new Date().toISOString();\n\n // ── Skill awareness (installed + conversation-used) ──────────────\n // Collect digests of other installed skills AND skills actively used\n // in conversations for this repo. Merged result prioritizes used skills\n // so the LLM can make better deduplication decisions.\n const installedDigests = collectInstalledSkillDigests(skillName, repo.path);\n const usedDigests = collectUsedSkillDigests(repo.name, skillName);\n const mergedDigests = mergeSkillDigests(installedDigests, usedDigests);\n const installedSkillsStr = formatSkillDigests(mergedDigests)\n || 'No other skills installed or used.';\n if (mergedDigests.length > 0) {\n const usedCount = mergedDigests.filter((d) => d.usageCount > 0).length;\n const installedOnly = mergedDigests.length - usedCount;\n options.onOutput?.(`[skill-gen] Skill awareness: ${usedCount} used in conversations, ${installedOnly} installed-only — ${mergedDigests.map((d) => `${d.name}${d.usageCount > 0 ? `(${d.usageCount})` : ''}`).join(', ')}`);\n }\n\n // ── MCP awareness (installed + conversation-used) ────────────────\n // Collect public MCP servers from config files AND MCPs used in\n // conversations for this repo. Injected into prompts so the generated\n // SKILL.md documents MCP integration patterns.\n const installedMcps = collectInstalledMcpDigests();\n const usedMcps = collectUsedMcpDigests(repo.name);\n const mergedMcps = mergeMcpDigests(installedMcps, usedMcps);\n const installedMcpsStr = formatMcpDigests(mergedMcps)\n || 'No public MCP servers configured or used.';\n if (mergedMcps.length > 0) {\n const usedMcpCount = mergedMcps.filter((d) => d.usageCount > 0).length;\n const installedMcpOnly = mergedMcps.length - usedMcpCount;\n options.onOutput?.(`[skill-gen] MCP awareness: ${usedMcpCount} used in conversations, ${installedMcpOnly} installed-only — ${mergedMcps.map((d) => `${d.alias}${d.isPublic ? '(public)' : ''}${d.usageCount > 0 ? `(${d.usageCount})` : ''}`).join(', ')}`);\n }\n\n // ── DELTA UPDATE PATH ─────────────────────────────────────────────\n // When the verdict produced a delta analysis, skip Phase 1 research\n // entirely and apply changes directly to the existing files.\n if (existingContent && deltaAnalysis && workPlan) {\n options.onOutput?.(`[skill-gen] Delta update path: skipping Phase 1 research (verdict provided ${deltaAnalysis.length} char analysis)`);\n\n // ── Determine which core files to generate (same logic as full path) ──\n const coreFileNames = ['SKILL.md', 'established-patterns.md', 'guidelines.md'];\n const shouldGenCoreFile = (name: string): boolean => {\n if (!filesToUpdate) return true;\n return filesToUpdate.some((f) => f.file === name);\n };\n const getChangeHints = (name: string): string[] => {\n if (!filesToUpdate) return [];\n return filesToUpdate.find((f) => f.file === name)?.changeHints ?? [];\n };\n\n const hasRefUpdates = !filesToUpdate || filesToUpdate.some(\n (f) => f.file.startsWith('references/'),\n );\n\n const coreFilesToGen = coreFileNames.filter(shouldGenCoreFile);\n if (filesToUpdate) {\n const skippedCore = coreFileNames.filter((n) => !shouldGenCoreFile(n));\n if (skippedCore.length > 0) {\n options.onOutput?.(`[skill-gen] Selective delta update: updating ${coreFilesToGen.join(', ')}; skipping ${skippedCore.join(', ')}`);\n }\n }\n\n // ── Phase 2a (delta): Apply changes to core files ──\n // Claude reads existing files and writes updates directly to writeSkillDir via Bash\n const runDeltaPhase2a = async (): Promise<void> => {\n if (coreFilesToGen.length === 0) {\n options.onOutput?.(`[skill-gen] Phase 2a (delta): Skipped (no core files in work plan)`);\n return;\n }\n\n // Progress file for CLI heartbeat (Claude writes via echo, heartbeat reads)\n const phase2aProgressFile = join(tmpdir(), `lore-progress-${randomUUID()}.json`);\n\n // Build change hints section\n const changeHintsStr = coreFilesToGen.map((name) => {\n const hints = getChangeHints(name);\n if (hints.length === 0) return `- ${name}: apply changes from delta analysis`;\n return `- ${name}: ${hints.join('; ')}`;\n }).join('\\n');\n\n const updateTemplate = loadPrompt('apply-skill-update');\n const updatePrompt = updateTemplate\n .replace(/\\{\\{REPO_NAME\\}\\}/g, repo.name)\n .replace('{{DELTA_ANALYSIS}}', deltaAnalysis)\n .replace('{{CHANGE_HINTS}}', changeHintsStr)\n .replace('{{GIT_LOG}}', gitLogStr)\n .replace('{{CLUSTERED_KNOWLEDGE}}', clusteredStr)\n .replace('{{INSTALLED_SKILLS_DIGEST}}', installedSkillsStr)\n .replace('{{INSTALLED_MCPS_DIGEST}}', installedMcpsStr)\n .replace(/\\{\\{SKILL_NAME\\}\\}/g, skillName)\n .replace(/\\{\\{TIMESTAMP\\}\\}/g, timestamp)\n .replace(/\\{\\{SKILL_DIR\\}\\}/g, writeSkillDir)\n .replace(/\\{\\{UI_PORT\\}\\}/g, String(DEFAULT_UI_PORT))\n .replace(/\\{\\{TOTAL_FILES\\}\\}/g, String(coreFilesToGen.length))\n .replace(/\\{\\{PROGRESS_FILE\\}\\}/g, phase2aProgressFile);\n\n const phase2aStart = Date.now();\n options.onOutput?.(`[skill-gen] Phase 2a (delta): Updating ${coreFilesToGen.join(', ')} for ${repo.name} [model: sonnet]...`);\n // Agent writes files directly to writeSkillDir via Bash\n await invokeDeltaGenerator(updatePrompt, repo.name, options, phase2aProgressFile);\n const phase2aElapsed = ((Date.now() - phase2aStart) / 1000).toFixed(1);\n options.onOutput?.(`[skill-gen] Phase 2a (delta) complete for ${repo.name} (${phase2aElapsed}s)`);\n };\n\n // ── Phase 2b (delta): Apply changes to reference files ──\n // Claude reads existing files and writes updates directly to writeSkillDir via Bash\n const runDeltaPhase2b = async (): Promise<void> => {\n if (!hasRefUpdates) {\n options.onOutput?.(`[skill-gen] Phase 2b (delta): Skipped (no reference files in work plan)`);\n return;\n }\n\n const refFilesToUpdate = (filesToUpdate ?? []).filter(\n (f) => f.file.startsWith('references/'),\n );\n if (refFilesToUpdate.length === 0) {\n options.onOutput?.(`[skill-gen] Phase 2b (delta): Skipped (no specific ref files to update)`);\n return;\n }\n\n // Progress file for CLI heartbeat (Claude writes via echo, heartbeat reads)\n const phase2bProgressFile = join(tmpdir(), `lore-progress-${randomUUID()}.json`);\n\n const refChangeHintsStr = refFilesToUpdate.map((f) => {\n if (f.changeHints.length === 0) return `- ${f.file}: apply changes from delta analysis`;\n return `- ${f.file}: ${f.changeHints.join('; ')}`;\n }).join('\\n');\n\n const refsTemplate = loadPrompt('apply-refs-update');\n const refsPrompt = refsTemplate\n .replace(/\\{\\{REPO_NAME\\}\\}/g, repo.name)\n .replace('{{DELTA_ANALYSIS}}', deltaAnalysis)\n .replace('{{CHANGE_HINTS}}', refChangeHintsStr)\n .replace('{{INSTALLED_SKILLS_DIGEST}}', installedSkillsStr)\n .replace('{{INSTALLED_MCPS_DIGEST}}', installedMcpsStr)\n .replace(/\\{\\{SKILL_DIR\\}\\}/g, writeSkillDir)\n .replace(/\\{\\{UI_PORT\\}\\}/g, String(DEFAULT_UI_PORT))\n .replace(/\\{\\{TOTAL_FILES\\}\\}/g, String(refFilesToUpdate.length))\n .replace(/\\{\\{PROGRESS_FILE\\}\\}/g, phase2bProgressFile);\n\n const phase2bStart = Date.now();\n options.onOutput?.(`[skill-gen] Phase 2b (delta): Updating reference docs for ${repo.name} [model: sonnet]...`);\n // Agent writes files directly to writeSkillDir via Bash\n await invokeDeltaGenerator(refsPrompt, repo.name, options, phase2bProgressFile);\n const phase2bElapsed = ((Date.now() - phase2bStart) / 1000).toFixed(1);\n options.onOutput?.(`[skill-gen] Phase 2b (delta) complete for ${repo.name} (${phase2bElapsed}s)`);\n };\n\n // ── Execute delta Phase 2a + 2b in parallel ──\n options.onOutput?.(`[skill-gen] Running delta Phase 2a + 2b in parallel`);\n await Promise.all([\n runDeltaPhase2a().catch((coreErr: any) => {\n options.onOutput?.(`[skill-gen] Phase 2a (delta core) failed for ${repo.name}: ${coreErr.message ?? coreErr}. Reference docs may still succeed.`);\n logger.warn(`Delta core update failed for ${repo.name}:`, coreErr);\n }),\n runDeltaPhase2b().catch((refErr: any) => {\n options.onOutput?.(`[skill-gen] Phase 2b (delta refs) failed for ${repo.name}: ${refErr.message ?? refErr}. Core skill is intact.`);\n logger.warn(`Delta reference update failed for ${repo.name}:`, refErr);\n }),\n ]);\n\n // ── Post-process files written to disk by the agent ──\n // cleanOutput on SKILL.md (ensure frontmatter)\n if (existsSync(writeSkillPath)) {\n const rawSkill = readFileSync(writeSkillPath, 'utf-8');\n const cleaned = cleanOutput(rawSkill, skillName, repo.name, timestamp, 0);\n writeFileSync(writeSkillPath, cleaned, 'utf-8');\n }\n // Patch reference links into SKILL.md\n const refFilesOnDisk = listSkillFiles(writeSkillDir).filter(\n (f) => f.startsWith('references/'),\n );\n if (refFilesOnDisk.length > 0) {\n patchSkillWithReferenceLinks(writeSkillDir, writeSkillPath, refFilesOnDisk);\n }\n\n const totalElapsed = ((Date.now() - pipelineStart) / 1000).toFixed(1);\n options.onOutput?.(`[skill-gen] Delta pipeline complete for ${repo.name} (${totalElapsed}s total)`);\n\n // ── One-time agent pointer injection ──\n const skillRelPath = `.cursor/skills/${skillName}/SKILL.md`;\n const pointerResult = injectAgentPointers(repo.path, skillRelPath);\n if (pointerResult.agents) options.onOutput?.(`[skill-gen] Injected Lore pointer into AGENTS.md`);\n if (pointerResult.claude) options.onOutput?.(`[skill-gen] Injected Lore pointer into CLAUDE.md`);\n\n // ── Post-gen cleanup: migrate deprecated files, dead links, orphan detection ──\n const migrations = migrateDeprecatedFiles(mainSkillDir);\n for (const action of migrations) {\n options.onOutput?.(`[skill-gen] Migration: ${action}`);\n }\n\n if (existsSync(mainSkillPath)) {\n const skillContent = readFileSync(mainSkillPath, 'utf-8');\n const deadLinks = findDeadLinksDetailed(skillContent, mainSkillDir);\n if (deadLinks.length > 0) {\n const cleaned = removeDeadLinks(skillContent, deadLinks);\n writeFileSync(mainSkillPath, cleaned, 'utf-8');\n options.onOutput?.(`[skill-gen] Removed ${deadLinks.length} dead link(s) from SKILL.md`);\n }\n }\n const orphans = findOrphanedFiles(mainSkillDir);\n if (orphans.length > 0) {\n options.onOrphansFound?.(orphans, mainSkillDir);\n options.onOutput?.(`[skill-gen] Found ${orphans.length} orphaned reference file(s): ${orphans.join(', ')}`);\n }\n\n return {\n repo: repo.name,\n repoPath: repo.path,\n skillPath: mainSkillPath,\n status: 'updated',\n reason: `Delta update: ${gitLog.length} commits, ${deltaAnalysis.length} char analysis, ${coreFilesToGen.length} core files${hasRefUpdates ? ' + refs' : ''} (${totalElapsed}s)`,\n };\n }\n\n // ── FULL RESEARCH PATH (new project, no delta analysis, --force) ──\n\n // ── Phase 1: Research the repo with Octocode tools ──\n const researchTemplate = loadPrompt('research-repo');\n const techGuidance = buildTechSpecificGuidance(repo.techSignals);\n\n // Load existing skill and guidance files to inject into research prompt\n // Use readPath (worktree) so we read guidance from the frozen snapshot\n const repoGuidance = loadRepoGuidance(readPath);\n const existingSkillForResearch = existingContent\n ? existingContent.slice(0, MAX_EXISTING_SKILL_CHARS) // cap to avoid prompt bloat\n : 'None — this is a fresh repo with no existing skill.';\n\n // Build gap-focused instructions, enhanced with work plan hints\n let gapInstructions = '';\n if (existingContent) {\n const lines = [\n '**IMPORTANT — Gap-focused research mode**: An existing SKILL.md is provided in <existing_skill> above.',\n 'It already documents this repo\\'s architecture, patterns, and key files.',\n 'Focus ONLY on:',\n '1. Validating that existing content is still accurate against current code',\n '2. Discovering new patterns, files, or conventions not yet documented',\n '3. Checking for structural changes (new directories, renamed entry points, added dependencies) since the last update',\n ];\n // Inject research focus from work plan\n if (researchFocusHints && researchFocusHints.length > 0) {\n lines.push('');\n lines.push('**Priority investigation areas** (from relevance analysis):');\n for (const hint of researchFocusHints) {\n lines.push(`- ${hint}`);\n }\n lines.push('');\n lines.push('Focus your tool calls on these specific areas first.');\n }\n lines.push('Use at most 5 tool calls, targeting gaps rather than re-confirming known information.');\n gapInstructions = lines.join('\\n');\n }\n\n if (existingContent) {\n options.onOutput?.(`[skill-gen] Existing skill found (${existingContent.length} chars), using gap-focused research`);\n }\n if (researchFocusHints && researchFocusHints.length > 0) {\n options.onOutput?.(`[skill-gen] Research focus areas: ${researchFocusHints.join(', ')}`);\n }\n if (repoGuidance) {\n options.onOutput?.(`[skill-gen] Repo guidance files loaded (${repoGuidance.length} chars)`);\n }\n if (options.researchPath) {\n options.onOutput?.(`[skill-gen] Using worktree for research: ${readPath}`);\n }\n\n const researchPrompt = researchTemplate\n .replace(/\\{\\{REPO_NAME\\}\\}/g, repo.name)\n .replace(/\\{\\{REPO_PATH\\}\\}/g, readPath)\n .replace(/\\{\\{TECH_SIGNALS\\}\\}/g, repo.techSignals.join(', ') || 'unknown')\n .replace('{{TECH_SPECIFIC_GUIDANCE}}', techGuidance)\n .replace('{{EXISTING_SKILL}}', existingSkillForResearch)\n .replace('{{REPO_GUIDANCE}}', repoGuidance || 'No repo guidance files found.')\n .replace('{{INSTALLED_SKILLS_DIGEST}}', installedSkillsStr)\n .replace('{{INSTALLED_MCPS_DIGEST}}', installedMcpsStr)\n .replace('{{GAP_FOCUSED_INSTRUCTIONS}}', gapInstructions);\n\n // Adaptive max-turns: floor 15, ceiling 25, based on tech signal count.\n // Research typically needs 15-20 tool calls + headroom for writing the doc.\n // When an existing skill provides baseline context, cap at 12 to save tokens.\n // When a work plan provides focus areas, cap even lower at 8.\n const baseMaxTurns = Math.max(15, Math.min(25, repo.techSignals.length + 10));\n const adaptiveMaxTurns = researchFocusHints\n ? Math.min(baseMaxTurns, 8) // Very focused research with work plan\n : existingContent\n ? Math.min(baseMaxTurns, 12) // Gap-focused without specific hints\n : baseMaxTurns; // Full research from scratch\n\n const phase1Start = Date.now();\n options.onOutput?.(`[skill-gen] Phase 1: Researching ${repo.name} with Octocode [model: opus] (max ${adaptiveMaxTurns} turns)...`);\n let researchDoc = await invokeResearcher(researchPrompt, readPath, repo.name, options, adaptiveMaxTurns);\n const phase1Elapsed = ((Date.now() - phase1Start) / 1000).toFixed(1);\n options.onOutput?.(`[skill-gen] Phase 1 complete for ${repo.name} (${researchDoc.length} chars, ${phase1Elapsed}s)`);\n\n // ── Quality validation: warn or retry if research doc is too short ──\n if (researchDoc.length < MIN_USABLE_OUTPUT) {\n const retryStart = Date.now();\n options.onOutput?.(`[skill-gen] Warning: Research doc for ${repo.name} is very short (${researchDoc.length} chars). Retrying Phase 1...`);\n researchDoc = await invokeResearcher(researchPrompt, readPath, repo.name, options, adaptiveMaxTurns);\n const retryElapsed = ((Date.now() - retryStart) / 1000).toFixed(1);\n options.onOutput?.(`[skill-gen] Phase 1 retry complete for ${repo.name} (${researchDoc.length} chars, ${retryElapsed}s)`);\n if (researchDoc.length < MIN_USABLE_OUTPUT) {\n options.onOutput?.(`[skill-gen] Warning: Research doc still short after retry. Proceeding with available data.`);\n }\n }\n\n // Validate expected sections (warnings only, never reject)\n const expectedSections = ['## Architecture', '## Key Entry Points', '## Established Patterns'];\n const missingSections = expectedSections.filter((s) => !researchDoc.includes(s));\n if (missingSections.length > 0) {\n options.onOutput?.(`[skill-gen] Warning: Research doc missing sections: ${missingSections.join(', ')}`);\n }\n\n // ── Token validation: truncate research doc to fit Phase 2 context ──\n const maxInput = getMaxInputTokens();\n const researchBudget = Math.floor(maxInput * 0.70);\n const truncResult = await truncateResearchByPriority(researchDoc, researchBudget);\n const finalResearchDoc = truncResult.text;\n if (truncResult.truncated) {\n options.onOutput?.(`[skill-gen] Research doc truncated to fit context (budget: ${researchBudget.toLocaleString()} tokens)`);\n }\n\n // ── Determine which core files to generate ─────────────────────\n const coreFileNames = ['SKILL.md', 'established-patterns.md', 'guidelines.md'];\n const shouldGenCoreFile = (name: string): boolean => {\n if (!filesToUpdate) return true; // No work plan → generate all\n return filesToUpdate.some((f) => f.file === name);\n };\n const getChangeHints = (name: string): string[] => {\n if (!filesToUpdate) return [];\n return filesToUpdate.find((f) => f.file === name)?.changeHints ?? [];\n };\n\n // Check if any reference files need updating\n const hasRefUpdates = !filesToUpdate || filesToUpdate.some(\n (f) => f.file.startsWith('references/'),\n );\n\n const coreFilesToGen = coreFileNames.filter(shouldGenCoreFile);\n if (filesToUpdate) {\n const skippedCore = coreFileNames.filter((n) => !shouldGenCoreFile(n));\n if (skippedCore.length > 0) {\n options.onOutput?.(`[skill-gen] Selective generation: updating ${coreFilesToGen.join(', ')}; skipping ${skippedCore.join(', ')}`);\n }\n }\n\n // ── Phase 2a + 2b: Generate core skill files & reference docs ───\n // Adaptive parallelization: when an existing skill is available (update),\n // run both phases concurrently. For new projects (no existing skill),\n // run sequentially so Phase 2b can use the freshly generated SKILL.md.\n\n // Phase 2a closure: agent writes core files directly to writeSkillDir via Bash\n const runPhase2a = async (): Promise<void> => {\n if (coreFilesToGen.length === 0) {\n options.onOutput?.(`[skill-gen] Phase 2a: Skipped (no core files in work plan)`);\n return;\n }\n\n // Progress file for CLI heartbeat (Claude writes via echo, heartbeat reads)\n const phase2aProgressFile = join(tmpdir(), `lore-progress-${randomUUID()}.json`);\n\n const genTemplate = loadPrompt('generate-repo-skill');\n\n // Build change hints section for the generation prompt\n let changeHintsSection = '';\n if (filesToUpdate && filesToUpdate.length > 0) {\n const hints = coreFilesToGen.map((name) => {\n const fileHints = getChangeHints(name);\n if (fileHints.length === 0) return `- ${name}: regenerate fully`;\n return `- ${name}: ${fileHints.join('; ')}`;\n });\n changeHintsSection = `\\n\\nSELECTIVE UPDATE — Only generate these files:\\n${hints.join('\\n')}\\nDo NOT generate files not listed above.\\n`;\n }\n\n const genPrompt = genTemplate\n .replace(/\\{\\{REPO_NAME\\}\\}/g, repo.name)\n .replace('{{RESEARCH_DOC}}', finalResearchDoc + changeHintsSection)\n .replace('{{CLUSTERED_KNOWLEDGE}}', clusteredStr)\n .replace('{{GIT_LOG}}', gitLogStr)\n .replace('{{INSTALLED_SKILLS_DIGEST}}', installedSkillsStr)\n .replace('{{INSTALLED_MCPS_DIGEST}}', installedMcpsStr)\n .replace(/\\{\\{SKILL_NAME\\}\\}/g, skillName)\n .replace(/\\{\\{TIMESTAMP\\}\\}/g, timestamp)\n .replace(/\\{\\{CONVERSATION_COUNT\\}\\}/g, '0')\n .replace(/\\{\\{SKILL_DIR\\}\\}/g, writeSkillDir)\n .replace(/\\{\\{UI_PORT\\}\\}/g, String(DEFAULT_UI_PORT))\n .replace(/\\{\\{TOTAL_FILES\\}\\}/g, String(coreFilesToGen.length))\n .replace(/\\{\\{PROGRESS_FILE\\}\\}/g, phase2aProgressFile);\n\n const phase2aStart = Date.now();\n options.onOutput?.(`[skill-gen] Phase 2a: Generating ${coreFilesToGen.join(', ')} for ${repo.name} [model: sonnet]...`);\n // Agent writes files directly to writeSkillDir via Bash\n await invokeGenerator(genPrompt, repo.name, options, phase2aProgressFile);\n const phase2aElapsed = ((Date.now() - phase2aStart) / 1000).toFixed(1);\n options.onOutput?.(`[skill-gen] Phase 2a complete for ${repo.name} (${phase2aElapsed}s)`);\n };\n\n // Phase 2b closure: agent writes reference docs directly to writeSkillDir via Bash\n const runPhase2b = async (): Promise<void> => {\n if (!hasRefUpdates) {\n options.onOutput?.(`[skill-gen] Phase 2b: Skipped (no reference files in work plan)`);\n return;\n }\n\n const refsTemplate = loadPrompt('generate-references');\n\n // Build selective ref hints when a work plan targets specific reference files\n let refSelectiveSection = '';\n if (filesToUpdate) {\n const refFilesToUpdate = filesToUpdate.filter(\n (f) => f.file.startsWith('references/'),\n );\n const refFilesToSkip = (workPlan?.filesToSkip ?? []).filter(\n (f) => f.startsWith('references/'),\n );\n if (refFilesToUpdate.length > 0) {\n const hints = refFilesToUpdate.map((f) => {\n if (f.changeHints.length === 0) return `- ${f.file}: regenerate fully`;\n return `- ${f.file}: ${f.changeHints.join('; ')}`;\n });\n refSelectiveSection = `\\n\\nSELECTIVE UPDATE — Only generate these files:\\n${hints.join('\\n')}`;\n if (refFilesToSkip.length > 0) {\n refSelectiveSection += `\\n\\nDo NOT regenerate these files (they are up-to-date):\\n${refFilesToSkip.map((f) => `- ${f}`).join('\\n')}`;\n }\n refSelectiveSection += `\\nDo NOT generate files not listed in the update list above.\\n`;\n }\n }\n\n // Estimate total ref files for progress reporting\n const estRefFiles = filesToUpdate\n ? filesToUpdate.filter((f) => f.file.startsWith('references/')).length\n : 5; // default estimate for full generation\n\n // Progress file for CLI heartbeat (Claude writes via echo, heartbeat reads)\n const phase2bProgressFile = join(tmpdir(), `lore-progress-${randomUUID()}.json`);\n\n const refsPrompt = refsTemplate\n .replace(/\\{\\{REPO_NAME\\}\\}/g, repo.name)\n .replace('{{RESEARCH_DOC}}', finalResearchDoc + refSelectiveSection)\n .replace('{{INSTALLED_SKILLS_DIGEST}}', installedSkillsStr)\n .replace('{{INSTALLED_MCPS_DIGEST}}', installedMcpsStr)\n .replace(/\\{\\{SKILL_DIR\\}\\}/g, writeSkillDir)\n .replace(/\\{\\{UI_PORT\\}\\}/g, String(DEFAULT_UI_PORT))\n .replace(/\\{\\{TOTAL_FILES\\}\\}/g, String(estRefFiles))\n .replace(/\\{\\{PROGRESS_FILE\\}\\}/g, phase2bProgressFile);\n\n const phase2bStart = Date.now();\n options.onOutput?.(`[skill-gen] Phase 2b: Generating reference docs for ${repo.name} [model: sonnet]...`);\n // Agent writes files directly to writeSkillDir via Bash\n await invokeGenerator(refsPrompt, repo.name, options, phase2bProgressFile);\n const phase2bElapsed = ((Date.now() - phase2bStart) / 1000).toFixed(1);\n options.onOutput?.(`[skill-gen] Phase 2b complete for ${repo.name} (${phase2bElapsed}s)`);\n };\n\n // ── Execute Phase 2a + 2b (adaptive: parallel for updates, sequential for new projects)\n // Agent writes files directly to writeSkillDir via Bash in both phases.\n\n if (existingContent) {\n // UPDATE PATH: existing skill available — run in parallel.\n // Phase 2b reads the pre-existing SKILL.md from writeSkillDir (seeded from mainSkillDir).\n options.onOutput?.(`[skill-gen] Running Phase 2a + 2b in parallel (existing skill available)`);\n await Promise.all([\n runPhase2a().catch((coreErr: any) => {\n options.onOutput?.(`[skill-gen] Phase 2a (core) failed for ${repo.name}: ${coreErr.message ?? coreErr}. Reference docs may still succeed.`);\n logger.warn(`Core generation failed for ${repo.name}:`, coreErr);\n }),\n runPhase2b().catch((refErr: any) => {\n options.onOutput?.(`[skill-gen] Phase 2b (references) failed for ${repo.name}: ${refErr.message ?? refErr}. Core skill is intact.`);\n logger.warn(`Reference generation failed for ${repo.name}:`, refErr);\n }),\n ]);\n } else {\n // NEW PROJECT PATH: no existing skill — run sequentially.\n // Phase 2a writes SKILL.md to writeSkillDir; Phase 2b reads it from there.\n options.onOutput?.(`[skill-gen] Running Phase 2a then 2b sequentially (new project)`);\n await runPhase2a();\n\n // Phase 2b reads the freshly written SKILL.md from writeSkillDir via Bash\n try {\n await runPhase2b();\n } catch (refErr: any) {\n options.onOutput?.(`[skill-gen] Phase 2b (references) failed for ${repo.name}: ${refErr.message ?? refErr}. Core skill is intact.`);\n logger.warn(`Reference generation failed for ${repo.name}:`, refErr);\n }\n }\n\n // ── Post-process files written to disk by the agent ──────────────\n // cleanOutput on SKILL.md (ensure frontmatter)\n if (existsSync(writeSkillPath)) {\n const rawSkill = readFileSync(writeSkillPath, 'utf-8');\n const cleaned = cleanOutput(rawSkill, skillName, repo.name, timestamp, 0);\n writeFileSync(writeSkillPath, cleaned, 'utf-8');\n }\n // Patch reference links into SKILL.md\n const refFilesOnDisk = listSkillFiles(writeSkillDir).filter(\n (f) => f.startsWith('references/'),\n );\n if (refFilesOnDisk.length > 0) {\n patchSkillWithReferenceLinks(writeSkillDir, writeSkillPath, refFilesOnDisk);\n }\n\n const totalElapsed = ((Date.now() - pipelineStart) / 1000).toFixed(1);\n options.onOutput?.(`[skill-gen] Pipeline complete for ${repo.name} (${totalElapsed}s total)`);\n\n // ── One-time agent pointer injection ──\n const skillRelPath = `.cursor/skills/${skillName}/SKILL.md`;\n const pointerResult = injectAgentPointers(repo.path, skillRelPath);\n if (pointerResult.agents) options.onOutput?.(`[skill-gen] Injected Lore pointer into AGENTS.md`);\n if (pointerResult.claude) options.onOutput?.(`[skill-gen] Injected Lore pointer into CLAUDE.md`);\n\n // ── Post-gen cleanup: migrate deprecated files, dead links, orphan detection ──\n const migrations = migrateDeprecatedFiles(mainSkillDir);\n for (const action of migrations) {\n options.onOutput?.(`[skill-gen] Migration: ${action}`);\n }\n\n if (existsSync(mainSkillPath)) {\n const skillContent = readFileSync(mainSkillPath, 'utf-8');\n const deadLinks = findDeadLinksDetailed(skillContent, mainSkillDir);\n if (deadLinks.length > 0) {\n const cleaned = removeDeadLinks(skillContent, deadLinks);\n writeFileSync(mainSkillPath, cleaned, 'utf-8');\n options.onOutput?.(`[skill-gen] Removed ${deadLinks.length} dead link(s) from SKILL.md`);\n }\n }\n const orphans = findOrphanedFiles(mainSkillDir);\n if (orphans.length > 0) {\n options.onOrphansFound?.(orphans, mainSkillDir);\n options.onOutput?.(`[skill-gen] Found ${orphans.length} orphaned reference file(s): ${orphans.join(', ')}`);\n }\n\n return {\n repo: repo.name,\n repoPath: repo.path,\n skillPath: mainSkillPath,\n status: existingContent ? 'updated' : 'generated',\n reason: `${gitLog.length} commits, clustered knowledge: ${clusteredKnowledge ? 'yes' : 'none'}${verdict ? `, work plan: ${coreFilesToGen.length} core + ${hasRefUpdates ? 'refs' : 'no refs'}` : ''} (${totalElapsed}s)`,\n };\n } catch (err: unknown) {\n const msg = errorMessage(err);\n logger.error(`Skill generation failed for ${repo.name}:`, err);\n return {\n repo: repo.name,\n repoPath: repo.path,\n skillPath: null,\n status: 'failed',\n reason: msg,\n };\n } finally {\n // For generator-managed worktrees (CLI path): merge skills back to\n // main repo before cleanup so the files land in the real .cursor/skills/.\n if (ownedWorktree) {\n try {\n const merged = mergeSkillsFromWorktree(readPath, repo.path);\n if (merged > 0) {\n options.onOutput?.(`[skill-gen] Merged ${merged} file(s) from worktree to ${repo.path}`);\n }\n } catch (mergeErr: any) {\n options.onOutput?.(`[skill-gen] Warning: merge from worktree failed for ${repo.name}: ${mergeErr.message ?? mergeErr}`);\n }\n try {\n await cleanupScanWorktree(repo.path);\n options.onOutput?.(`[skill-gen] Worktree cleaned up for ${repo.name}`);\n } catch (cleanupErr: any) {\n logger.debug(`[worktree] Cleanup failed for ${repo.name}: ${cleanupErr.message ?? cleanupErr}`);\n }\n }\n }\n}\n\n// ── Claude CLI invocation ────────────────────────────────────────\n\n/**\n * Phase 1: Research the repo using Octocode MCP tools + Bash.\n * Returns a structured research document.\n */\nasync function invokeResearcher(\n prompt: string,\n cwd: string,\n repoName: string,\n options: RepoSkillOptions,\n maxTurns: number = 15,\n): Promise<string> {\n const cli = await ensureAnyCli();\n const { path: mcpConfigPath, cleanup } = createMcpConfigFile();\n\n // Split prompt into system (behavioural) and user (data) portions\n const { systemPrompt, userPrompt } = splitPrompt(prompt);\n const { path: systemPromptPath, cleanup: cleanupSystemPrompt } = createSystemPromptFile(systemPrompt);\n\n // Snapshot sessions before invocation for phase tagging\n const beforeSnapshot = snapshotClaudeSessions();\n\n // For non-claude CLIs, merge system prompt back into user input\n // since --append-system-prompt-file is not supported.\n const isClaude = cli.name === 'claude';\n const researchInput = isClaude\n ? userPrompt\n : `${systemPrompt}\\n\\n${userPrompt}`;\n\n try {\n // Claude gets --tools \"Bash\" to force Octocode MCP usage, --max-turns for\n // iteration control, stream-json for real-time progress, and various\n // Claude-specific flags. Cursor Agent doesn't support --tools or --max-turns;\n // it uses --force for permission bypass and loads MCP from .cursor/mcp.json.\n const baseArgs = [\n '-p',\n '--output-format', isClaude ? 'stream-json' : 'text',\n ...(isClaude ? [\n '--max-turns', String(maxTurns),\n '--tools', 'Bash',\n '--mcp-config', mcpConfigPath,\n '--verbose',\n '--strict-mcp-config',\n '--dangerously-skip-permissions',\n '--append-system-prompt-file', systemPromptPath,\n '--fallback-model', 'sonnet',\n ] : [\n '--force', // cursor-agent equivalent for permission bypass\n '--approve-mcps', // auto-approve MCPs in headless mode (needed for Octocode)\n ]),\n ];\n\n const child = execa(\n cli.cmd,\n baseArgs,\n {\n cwd,\n input: researchInput,\n timeout: SKILL_TIMEOUT_MS,\n cancelSignal: options.signal,\n },\n );\n\n const result = isClaude\n ? await streamAndCollect(child, `research:${repoName}`, options)\n : await collectPlainOutput(child);\n\n // Check for rate-limit in the research output.\n // Note: extractResultFromEvents now skips short rate-limit messages and\n // prefers assistant text blocks, so `result` may already contain useful\n // partial research even when rate-limited. Only retry if the result is\n // genuinely unusable (short AND rate-limited).\n if (isRateLimited(result) && result.length < MIN_USABLE_OUTPUT) {\n options.onOutput?.(`[skill-gen] Rate-limited during research for ${repoName} [model: opus] (${result.length} chars salvaged), retrying via fallback...`);\n cleanup(); // release first MCP config\n\n const { path: mcpConfigPath2, cleanup: cleanup2 } = createMcpConfigFile();\n try {\n const fallbackResult = await invokeWithFallbackAndTag(\n {\n args: [\n '-p',\n '--output-format', 'text',\n '--max-turns', String(maxTurns),\n '--tools', 'Bash',\n ...(isClaude ? [\n '--mcp-config', mcpConfigPath2,\n '--verbose',\n '--strict-mcp-config',\n '--dangerously-skip-permissions',\n ] : [\n '--force',\n '--approve-mcps',\n ]),\n ],\n input: userPrompt,\n cwd,\n timeout: SKILL_TIMEOUT_MS,\n signal: options.signal,\n systemPromptFile: systemPromptPath,\n onOutput: options.onOutput,\n },\n `research:${repoName}`,\n 'skill-gen',\n options.runId,\n );\n // Keep the longer of the two results\n if (fallbackResult.length > result.length) {\n return fallbackResult;\n }\n options.onOutput?.(`[skill-gen] Fallback result shorter (${fallbackResult.length} chars), keeping original (${result.length} chars)`);\n return result;\n } finally {\n cleanup2();\n }\n }\n\n // Tag new sessions from the direct execa invocation\n try {\n const newIds = detectNewSessions(beforeSnapshot);\n if (newIds.length > 0) {\n tagConversations(newIds, 'skill-gen', options.runId);\n }\n } catch { /* ignore tagging errors */ }\n\n return result;\n } finally {\n cleanupSystemPrompt();\n cleanup();\n }\n}\n\n/**\n * Phase 2: Generate the SKILL.md from pre-gathered research.\n *\n * Tool-assisted: Claude gets Bash access to read existing skill files on demand\n * and report progress via curl. Uses stream-json for real-time visibility.\n * Sonnet model for generation — structured writing doesn't need Opus.\n * Falls back to text mode + invokeWithFallbackAndTag on rate-limit or truncation.\n */\nasync function invokeGenerator(\n prompt: string,\n repoName: string,\n options: RepoSkillOptions,\n progressFile?: string,\n): Promise<string> {\n const cli = await ensureAnyCli();\n\n // Split prompt into system (behavioural) and user (data) portions\n const { systemPrompt, userPrompt } = splitPrompt(prompt);\n const { path: systemPromptPath, cleanup } = createSystemPromptFile(systemPrompt);\n\n // Snapshot sessions before invocation for phase tagging\n const beforeSnapshot = snapshotClaudeSessions();\n\n const isClaude = cli.name === 'claude';\n // For non-claude CLIs, merge system prompt back into user input\n const genInput = isClaude\n ? userPrompt\n : `${systemPrompt}\\n\\n${userPrompt}`;\n\n // Progress file for CLI heartbeat reads\n const ownedProgressFile = !progressFile;\n progressFile ??= join(tmpdir(), `lore-progress-${randomUUID()}.json`);\n\n try {\n // Tool-assisted generation:\n // --tools \"Bash,Read,Edit\" → Read+Edit for surgical section updates, Bash for progress reporting\n // --strict-mcp-config → ignores user's global MCP servers (Obsidian, etc.)\n // --model sonnet → structured writing doesn't need Opus; avoids tighter Opus rate limits\n // --output-format stream-json → real-time progress (heartbeats, Writing:... previews)\n // --dangerously-skip-permissions → required for Bash/Edit tools in pipe mode\n // No --max-turns: stall detection (WRITING_STALL_TIMEOUT_MS) and hard ceiling\n // (WRITING_HARD_CEILING_MS) provide better safety nets than a turn limit,\n // which was causing the AI to run out of turns before completing edits.\n // Cursor Agent doesn't support --tools or --max-turns.\n const baseArgs = [\n '-p',\n '--output-format', isClaude ? 'stream-json' : 'text',\n ...(isClaude ? [\n '--tools', 'Bash,Read,Edit',\n '--strict-mcp-config',\n '--verbose',\n '--model', 'sonnet',\n '--fallback-model', 'haiku',\n '--dangerously-skip-permissions',\n '--append-system-prompt-file', systemPromptPath,\n ] : [\n '--force', // cursor-agent equivalent for permission bypass\n '--approve-mcps', // auto-approve MCPs in headless mode\n ]),\n ];\n\n const startTime = Date.now();\n let lastActivityTime = Date.now(); // tracks last sign of life from the agent\n\n const child = execa(\n cli.cmd,\n baseArgs,\n {\n input: genInput,\n timeout: WRITING_HARD_CEILING_MS, // absolute safety net only\n cancelSignal: options.signal,\n env: { ...process.env, ...WRITING_ENV_OVERRIDES },\n },\n );\n\n // Activity-based timeout: monitor the progress file and kill only if the\n // agent goes silent. As long as progress is being reported, let it run.\n const heartbeat = setInterval(() => {\n const elapsed = Math.round((Date.now() - startTime) / 1000);\n let progressStr = '';\n\n // Check if the agent reported progress\n try {\n const stat = statSync(progressFile!);\n if (stat.mtimeMs > lastActivityTime) {\n lastActivityTime = stat.mtimeMs;\n }\n const p = JSON.parse(readFileSync(progressFile!, 'utf-8'));\n const pct = Math.round((p.completed / p.total) * 100);\n progressStr = ` [${p.completed}/${p.total} files — ${p.file}] (${pct}%)`;\n } catch { /* no progress file yet */ }\n\n // Stall detection: kill the process if no progress for too long\n const stallMs = Date.now() - lastActivityTime;\n if (stallMs > WRITING_STALL_TIMEOUT_MS) {\n const stallSec = Math.round(stallMs / 1000);\n options.onOutput?.(`[skill-gen] [gen:${repoName}] No progress for ${stallSec}s — aborting stalled agent (${elapsed}s total)`);\n child.kill();\n return;\n }\n\n options.onOutput?.(`[skill-gen] [gen:${repoName}] Writing...${progressStr} (${elapsed}s elapsed)`);\n }, 15_000);\n\n try {\n const activityCb = () => { lastActivityTime = Date.now(); };\n const result = isClaude\n ? await streamAndCollect(child, `gen:${repoName}`, options, activityCb)\n : await collectPlainOutput(child, activityCb);\n\n clearInterval(heartbeat);\n\n // Guard against the CLI returning an error message instead of content.\n if (result.includes('Reached max turns') && result.length < MIN_USABLE_OUTPUT) {\n throw new Error(`Skill generation did not produce valid output (CLI reported: ${result.trim()})`);\n }\n\n // Rate-limit recovery: if the streamed result is short and rate-limited,\n // retry via the text-mode fallback chain (same pattern as invokeResearcher).\n if (isRateLimited(result) && result.length < MIN_USABLE_OUTPUT) {\n options.onOutput?.(`[skill-gen] Rate-limited during generation for ${repoName} [model: sonnet] (${result.length} chars salvaged), retrying via text-mode fallback...`);\n const fallbackResult = await invokeWithFallbackAndTag(\n {\n args: [\n '-p',\n '--output-format', 'text',\n '--tools', 'Bash,Read,Edit',\n ...(isClaude ? [\n '--strict-mcp-config',\n '--dangerously-skip-permissions',\n ] : [\n '--force',\n '--approve-mcps',\n ]),\n ],\n input: userPrompt,\n timeout: WRITING_HARD_CEILING_MS,\n signal: options.signal,\n model: 'sonnet',\n systemPromptFile: systemPromptPath,\n onOutput: options.onOutput,\n env: { ...WRITING_ENV_OVERRIDES },\n },\n `gen:${repoName}`,\n 'skill-gen',\n options.runId,\n );\n if (fallbackResult.length > result.length) {\n return fallbackResult;\n }\n options.onOutput?.(`[skill-gen] Fallback result shorter (${fallbackResult.length} chars), keeping original (${result.length} chars)`);\n return result;\n }\n\n // Stream truncation guard (Issue #2904): if stream-json truncated the output,\n // the result will be suspiciously short. Fall back to text mode.\n if (isClaude && result.length < MIN_USABLE_OUTPUT && result.length > 0) {\n options.onOutput?.(`[skill-gen] Stream result suspiciously short (${result.length} chars) for ${repoName}, retrying with text mode...`);\n const textResult = await invokeWithFallbackAndTag(\n {\n args: [\n '-p',\n '--output-format', 'text',\n '--tools', 'Bash,Read,Edit',\n '--strict-mcp-config',\n '--dangerously-skip-permissions',\n ],\n input: userPrompt,\n timeout: WRITING_HARD_CEILING_MS,\n signal: options.signal,\n model: 'sonnet',\n systemPromptFile: systemPromptPath,\n onOutput: options.onOutput,\n },\n `gen:${repoName}`,\n 'skill-gen',\n options.runId,\n );\n if (textResult.length > result.length) {\n return textResult;\n }\n }\n\n // Tag new sessions from the direct execa invocation\n try {\n const newIds = detectNewSessions(beforeSnapshot);\n if (newIds.length > 0) {\n tagConversations(newIds, 'skill-gen', options.runId);\n }\n } catch { /* ignore tagging errors */ }\n\n return result;\n } catch (err) {\n clearInterval(heartbeat);\n throw err;\n }\n } finally {\n cleanup();\n // Best-effort cleanup of progress file (only if we created it)\n if (ownedProgressFile) {\n try { if (existsSync(progressFile)) { const { unlinkSync } = await import('node:fs'); unlinkSync(progressFile); } } catch { /* ignore */ }\n }\n }\n}\n\n// DELTA_TIMEOUT_MS imported from constants.ts\n\n/**\n * Delta update generator: applies targeted changes to existing skill files.\n *\n * Tool-assisted: Claude gets Bash access to read existing files on demand\n * and report progress via curl. This replaces the old context-dump approach\n * where all existing files were injected into the prompt.\n *\n * Uses text output format, sonnet model, and adaptive max-turns based on\n * the number of files to update.\n */\nasync function invokeDeltaGenerator(\n prompt: string,\n repoName: string,\n options: RepoSkillOptions,\n progressFile?: string,\n): Promise<string> {\n const cli = await ensureAnyCli();\n\n const { systemPrompt, userPrompt } = splitPrompt(prompt);\n const { path: systemPromptPath, cleanup } = createSystemPromptFile(systemPrompt);\n\n const isClaude = cli.name === 'claude';\n const genInput = isClaude\n ? userPrompt\n : `${systemPrompt}\\n\\n${userPrompt}`;\n\n // Progress file for CLI heartbeat reads\n const ownedProgressFile = !progressFile;\n progressFile ??= join(tmpdir(), `lore-progress-${randomUUID()}.json`);\n\n try {\n // No --max-turns: stall detection (WRITING_STALL_TIMEOUT_MS) and hard ceiling\n // (WRITING_HARD_CEILING_MS) provide better safety nets than a turn limit,\n // which was causing the AI to run out of turns before completing edits.\n // Cursor Agent doesn't support --tools or --max-turns.\n const baseArgs = [\n '-p',\n '--output-format', isClaude ? 'stream-json' : 'text',\n ...(isClaude ? [\n '--tools', 'Bash,Read,Edit',\n '--strict-mcp-config',\n '--verbose',\n '--model', 'sonnet',\n '--fallback-model', 'haiku',\n '--dangerously-skip-permissions',\n '--append-system-prompt-file', systemPromptPath,\n ] : [\n '--force', // cursor-agent equivalent for permission bypass\n '--approve-mcps', // auto-approve MCPs in headless mode\n ]),\n ];\n\n const startTime = Date.now();\n let lastActivityTime = Date.now(); // tracks last sign of life from the agent\n\n const child = execa(\n cli.cmd,\n baseArgs,\n {\n input: genInput,\n timeout: WRITING_HARD_CEILING_MS, // absolute safety net only\n cancelSignal: options.signal,\n env: { ...process.env, ...WRITING_ENV_OVERRIDES },\n },\n );\n\n // Activity-based timeout: monitor the progress file and kill only if the\n // agent goes silent. As long as progress is being reported, let it run.\n // stream-json stdout events (on every turn) keep lastActivityTime fresh\n // via activityCb passed to streamAndCollect.\n const heartbeat = setInterval(() => {\n const elapsed = Math.round((Date.now() - startTime) / 1000);\n let progressStr = '';\n\n // Check if the agent reported progress\n try {\n const stat = statSync(progressFile!);\n if (stat.mtimeMs > lastActivityTime) {\n lastActivityTime = stat.mtimeMs;\n }\n const p = JSON.parse(readFileSync(progressFile!, 'utf-8'));\n const pct = Math.round((p.completed / p.total) * 100);\n progressStr = ` [${p.completed}/${p.total} files — ${p.file}] (${pct}%)`;\n } catch { /* no progress file yet */ }\n\n // Stall detection: kill the process if no progress for too long\n const stallMs = Date.now() - lastActivityTime;\n if (stallMs > WRITING_STALL_TIMEOUT_MS) {\n const stallSec = Math.round(stallMs / 1000);\n options.onOutput?.(`[skill-gen] [delta:${repoName}] No progress for ${stallSec}s — aborting stalled agent (${elapsed}s total)`);\n child.kill();\n return;\n }\n\n options.onOutput?.(`[skill-gen] [delta:${repoName}] Writing...${progressStr} (${elapsed}s elapsed)`);\n }, 15_000);\n\n try {\n const activityCb = () => { lastActivityTime = Date.now(); };\n const result = isClaude\n ? await streamAndCollect(child, `delta:${repoName}`, options, activityCb, true)\n : await collectPlainOutput(child, activityCb);\n clearInterval(heartbeat);\n\n const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);\n options.onOutput?.(`[skill-gen] [delta:${repoName}] Done in ${elapsed}s (${result.length} chars)`);\n\n // Rate-limit recovery: fall back to invokeWithFallbackAndTag\n if (isRateLimited(result) && result.length < MIN_USABLE_OUTPUT) {\n options.onOutput?.(`[skill-gen] Rate-limited during delta generation for ${repoName}, retrying via fallback...`);\n const fallbackResult = await invokeWithFallbackAndTag(\n {\n args: [\n '-p',\n '--output-format', 'text',\n '--tools', 'Bash,Read,Edit',\n ...(isClaude ? [\n '--strict-mcp-config',\n '--dangerously-skip-permissions',\n ] : [\n '--force',\n '--approve-mcps',\n ]),\n ],\n input: userPrompt,\n timeout: WRITING_HARD_CEILING_MS,\n signal: options.signal,\n model: 'sonnet',\n systemPromptFile: systemPromptPath,\n onOutput: options.onOutput,\n env: { ...WRITING_ENV_OVERRIDES },\n },\n `delta:${repoName}`,\n 'skill-gen',\n options.runId,\n );\n if (fallbackResult.length > result.length) {\n return fallbackResult;\n }\n }\n\n return result;\n } catch (err) {\n clearInterval(heartbeat);\n throw err;\n }\n } finally {\n cleanup();\n // Best-effort cleanup of progress file (only if we created it)\n if (ownedProgressFile) {\n try { if (existsSync(progressFile)) { const { unlinkSync } = await import('node:fs'); unlinkSync(progressFile); } } catch { /* ignore */ }\n }\n }\n}\n\n// CLI resolution is handled by shared `ensureAnyCli()` from utils/cli.ts\n\n/**\n * Stream stdout/stderr from a Claude CLI child process, collect events,\n * and return the final text result.\n */\nasync function streamAndCollect(\n child: ReturnType<typeof execa>,\n label: string,\n options: RepoSkillOptions,\n onActivity?: () => void,\n suppressHeartbeat?: boolean,\n): Promise<string> {\n const events: any[] = [];\n let lastActivityAt = Date.now();\n\n // Buffer partial lines across chunks — JSON events may span chunk boundaries.\n let stdoutBuffer = '';\n if (child.stdout) {\n child.stdout.on('data', (chunk: Buffer) => {\n lastActivityAt = Date.now();\n onActivity?.();\n stdoutBuffer += chunk.toString();\n\n // Process all complete lines (terminated by \\n)\n const lines = stdoutBuffer.split('\\n');\n // Keep the last element — it's either empty (line ended with \\n) or a partial line\n stdoutBuffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (line.trim().length === 0) continue;\n try {\n const event = JSON.parse(line);\n events.push(event);\n emitStreamEvent(event, label, options);\n } catch {\n options.onOutput?.(`[skill-gen] [${label}] ${line}`);\n }\n }\n });\n\n // Process any remaining buffer when the stream ends\n child.stdout.on('end', () => {\n if (stdoutBuffer.trim().length > 0) {\n try {\n const event = JSON.parse(stdoutBuffer);\n events.push(event);\n emitStreamEvent(event, label, options);\n } catch {\n options.onOutput?.(`[skill-gen] [${label}] ${stdoutBuffer}`);\n }\n stdoutBuffer = '';\n }\n });\n }\n\n if (child.stderr) {\n child.stderr.on('data', (chunk: Buffer) => {\n lastActivityAt = Date.now();\n onActivity?.();\n const text = chunk.toString();\n for (const line of text.split('\\n')) {\n if (line.length > 0) options.onOutput?.(`[skill-gen:stderr] [${label}] ${line}`);\n }\n });\n }\n\n // Optional internal heartbeat — suppressed when the caller already has its\n // own heartbeat (e.g. invokeDeltaGenerator's stall-detection heartbeat).\n const startTime = Date.now();\n const heartbeat = suppressHeartbeat ? null : setInterval(() => {\n const elapsed = Math.round((Date.now() - startTime) / 1000);\n const sinceActivity = Math.round((Date.now() - lastActivityAt) / 1000);\n if (sinceActivity > 10) {\n options.onOutput?.(`[skill-gen] [${label}] Still working... (${elapsed}s elapsed, ${sinceActivity}s since last activity)`);\n }\n }, 15_000);\n\n try {\n await child;\n } finally {\n if (heartbeat) clearInterval(heartbeat);\n }\n\n options.onOutput?.(`[skill-gen] [${label}] Stream complete: ${events.length} events collected`);\n return extractResultFromEvents(events);\n}\n\n/**\n * Collect plain text output from a CLI child process (non-stream-json mode).\n * Used when the CLI doesn't support --output-format stream-json.\n */\nasync function collectPlainOutput(\n child: ReturnType<typeof execa>,\n onActivity?: () => void,\n): Promise<string> {\n let output = '';\n if (child.stdout) {\n child.stdout.on('data', (chunk: Buffer) => {\n onActivity?.();\n output += chunk.toString();\n });\n }\n await child;\n return output;\n}\n\n// ── Deduplication ────────────────────────────────────────────────\n\n/**\n * Deduplicate repos that share the same name (git remote origin).\n * When the same repo is cloned in multiple locations, keep only the\n * clone with the most recent git commit.\n */\nasync function deduplicateRepos(\n repos: RepoCatalogEntry[],\n options: RepoSkillOptions,\n): Promise<RepoCatalogEntry[]> {\n const byName = new Map<string, RepoCatalogEntry[]>();\n for (const repo of repos) {\n const group = byName.get(repo.name) ?? [];\n group.push(repo);\n byName.set(repo.name, group);\n }\n\n const result: RepoCatalogEntry[] = [];\n for (const [name, group] of byName) {\n if (group.length === 1) {\n result.push(group[0]);\n continue;\n }\n // Multiple clones — pick the most recently used one\n const winner = pickLatestClone(group);\n result.push(winner);\n const dropped = group.filter((r) => r !== winner);\n options.onOutput?.(\n `[skill-gen] Dedup: ${name} has ${group.length} clones, using ${winner.path} (skipping ${dropped.map((r) => r.path).join(', ')})`,\n );\n }\n return result;\n}\n\n/**\n * Given multiple clones of the same repo, return the primary one.\n *\n * The strongest signal is the number of local branches: a developer's\n * primary working clone accumulates many branches over time, while\n * one-off clones created for a specific fix have just 1-2.\n *\n * Scoring (highest wins):\n * +N number of local git branches (dominant signal)\n * +0.5 tie-breaker: most recent .git/index mtime (normalized 0-1)\n */\nfunction pickLatestClone(repos: RepoCatalogEntry[]): RepoCatalogEntry {\n let best = repos[0];\n let bestScore = -1;\n\n for (const repo of repos) {\n let score = 0;\n\n // Signal 1: number of local branches — primary clones have many\n try {\n const headsDir = join(repo.path, '.git', 'refs', 'heads');\n const branches = readdirSync(headsDir, { recursive: true });\n score += branches.length;\n } catch {\n // Fallback: try packed-refs\n try {\n const packed = readFileSync(join(repo.path, '.git', 'packed-refs'), 'utf-8');\n const branchCount = (packed.match(/refs\\/heads\\//g) ?? []).length;\n score += branchCount;\n } catch { /* no branches info */ }\n }\n\n // Signal 2: .git/index mtime as tie-breaker (normalized to 0-1)\n try {\n const indexPath = join(repo.path, '.git', 'index');\n const st = statSync(indexPath);\n score += st.mtimeMs / Date.now();\n } catch { /* ignore */ }\n\n if (score > bestScore) {\n bestScore = score;\n best = repo;\n }\n }\n return best;\n}\n\n// ── Helpers ──────────────────────────────────────────────────────\n\n/**\n * Build additive tech-specific research guidance from detected signals.\n * Framed as \"additional areas to explore\", never restrictive.\n * The general research steps in the prompt remain unchanged.\n */\n/**\n * Load existing guidance / convention files from a repo.\n * These provide pre-existing context so the researcher can focus on gaps.\n * Each file is truncated to ~2000 chars to avoid prompt bloat.\n */\nfunction loadRepoGuidance(repoPath: string): string {\n const MAX_PER_FILE = MAX_REFS_PER_FILE_CHARS;\n const candidates: Array<{ label: string; path: string }> = [\n { label: 'CONTRIBUTING.md', path: join(repoPath, 'CONTRIBUTING.md') },\n { label: 'AGENTS.md', path: join(repoPath, 'AGENTS.md') },\n { label: '.cursorrules', path: join(repoPath, '.cursorrules') },\n ];\n\n // Scan .cursor/rules/ for .md and .mdc files\n const rulesDir = join(repoPath, '.cursor', 'rules');\n if (existsSync(rulesDir)) {\n try {\n const entries = readdirSync(rulesDir);\n for (const entry of entries) {\n if (entry.endsWith('.md') || entry.endsWith('.mdc')) {\n candidates.push({\n label: `.cursor/rules/${entry}`,\n path: join(rulesDir, entry),\n });\n }\n }\n } catch { /* ignore read errors */ }\n }\n\n const parts: string[] = [];\n for (const { label, path: filePath } of candidates) {\n if (!existsSync(filePath)) continue;\n try {\n let content = readFileSync(filePath, 'utf-8').trim();\n if (content.length === 0) continue;\n if (content.length > MAX_PER_FILE) {\n content = content.slice(0, MAX_PER_FILE) + '\\n[... truncated ...]';\n }\n parts.push(`### ${label}\\n${content}`);\n } catch { /* ignore read errors */ }\n }\n\n return parts.length > 0 ? parts.join('\\n\\n') : '';\n}\n\nfunction getSkillDir(repo: RepoCatalogEntry): string {\n const name = sanitizeName(repo.name);\n return join(repo.path, '.cursor', 'skills', `${name}-guide`);\n}\n\nasync function getGitLog(repoPath: string, since: string | null): Promise<string[]> {\n try {\n const args = [\n 'log',\n `--max-count=${GIT_LOG_LIMIT}`,\n '--format=%H|%an|%ad|%s',\n '--date=short',\n '--no-merges',\n ];\n if (since) {\n args.push(`--since=${since}`);\n }\n\n const { stdout } = await execa('git', args, {\n cwd: repoPath,\n timeout: GIT_MEDIUM_TIMEOUT_MS,\n });\n\n return stdout\n .split('\\n')\n .filter((line) => line.trim().length > 0)\n .slice(0, GIT_LOG_LIMIT);\n } catch {\n return [];\n }\n}\n\n/**\n * Get a `git diff --stat` summary of changes since a given date.\n * Returns the file list with +/- counts, capped at 200 lines.\n */\nasync function getGitDiffStat(repoPath: string, since: string): Promise<string> {\n try {\n // Find the oldest commit since `since`\n const { stdout: oldest } = await execa(\n 'git',\n ['log', `--since=${since}`, '--format=%H', '--reverse'],\n { cwd: repoPath, timeout: GIT_MEDIUM_TIMEOUT_MS },\n );\n const firstSha = oldest.trim().split('\\n')[0];\n if (!firstSha) return 'No commits found since last update.';\n\n // Get the stat of all changes from that commit to HEAD\n const { stdout: stat } = await execa(\n 'git',\n ['diff', '--stat', `${firstSha}^..HEAD`],\n { cwd: repoPath, timeout: GIT_SLOW_TIMEOUT_MS },\n );\n\n const lines = stat.split('\\n');\n if (lines.length > GIT_DIFF_MAX_LINES) {\n return lines.slice(0, GIT_DIFF_MAX_LINES).join('\\n') + `\\n... (${lines.length - GIT_DIFF_MAX_LINES} more lines)`;\n }\n return stat || 'No file changes detected.';\n } catch {\n return 'Unable to compute diff stat.';\n }\n}\n\n/**\n * Phase 2.5: Check whether the new delta (conversations + commits) is\n * significant enough to warrant regenerating the skill files.\n *\n * Returns a structured verdict with an optional work plan specifying\n * exactly which files to update.\n *\n * Fail-open: defaults to shouldUpdate=true on any error.\n */\nasync function checkRelevance(\n existingSkill: string,\n existingFiles: string[],\n clusteredKnowledge: string | null,\n gitLog: string[],\n diffStat: string,\n readPath: string,\n lastUpdated: string | null,\n repoName: string,\n options: RepoSkillOptions,\n): Promise<RelevanceVerdict> {\n const fullUpdate: RelevanceVerdict = {\n shouldUpdate: true,\n confidence: 'low',\n reason: 'Relevance check failed or skipped — defaulting to full update',\n };\n\n try {\n const template = loadPrompt('check-relevance');\n const prompt = template\n .replace(/\\{\\{REPO_NAME\\}\\}/g, repoName)\n .replace('{{REPO_PATH}}', readPath)\n .replace('{{LAST_UPDATED}}', lastUpdated ?? 'unknown')\n .replace('{{EXISTING_SKILL}}', existingSkill.slice(0, MAX_DELTA_SKILL_CHARS))\n .replace('{{EXISTING_FILES}}', existingFiles.length > 0 ? existingFiles.join('\\n') : 'No existing skill files.')\n .replace('{{CLUSTERED_KNOWLEDGE}}', clusteredKnowledge || 'No new conversation knowledge.')\n .replace('{{GIT_LOG}}', gitLog.length > 0 ? gitLog.join('\\n') : 'No new commits.')\n .replace('{{GIT_DIFF_STAT}}', diffStat);\n\n const { systemPrompt, userPrompt } = splitPrompt(prompt);\n const { path: systemPromptPath, cleanup } = createSystemPromptFile(systemPrompt);\n\n try {\n options.onOutput?.(`[skill-gen] Phase 2.5: Checking relevance for ${repoName} [model: opus]...`);\n\n const relevanceStart = Date.now();\n const heartbeat = setInterval(() => {\n const elapsed = Math.round((Date.now() - relevanceStart) / 1000);\n options.onOutput?.(`[skill-gen] Relevance check still running... (${elapsed}s elapsed, timeout: ${Math.round(RELEVANCE_TIMEOUT_MS / 1000)}s)`);\n }, 15_000);\n\n let result: string;\n try {\n result = await invokeWithFallbackAndTag(\n {\n args: [\n '-p',\n '--output-format', 'text',\n '--max-turns', '8',\n '--allowedTools', 'Bash(git diff*),Bash(git show*),Bash(git log*),Bash(cat *),Bash(ls *),Bash(head *),Bash(tail *)',\n '--strict-mcp-config',\n ],\n input: userPrompt,\n cwd: readPath,\n timeout: RELEVANCE_TIMEOUT_MS,\n signal: options.signal,\n systemPromptFile: systemPromptPath,\n env: { ...VERDICT_ENV_OVERRIDES },\n },\n `relevance:${repoName}`,\n 'skill-gen',\n options.runId,\n );\n } finally {\n clearInterval(heartbeat);\n }\n\n // Parse JSON verdict from output — use robust extraction that handles\n // preamble text, markdown fences, and trailing commentary (common with\n // Cursor Agent which may include tool call output around the JSON).\n const jsonStr = extractJson(result);\n options.onOutput?.(`[skill-gen] Extracted JSON (${jsonStr.length} chars from ${result.length} chars raw output)`);\n\n const verdict = JSON.parse(jsonStr) as RelevanceVerdict;\n\n // Validate required fields\n if (typeof verdict.shouldUpdate !== 'boolean') {\n throw new Error('Missing shouldUpdate field');\n }\n\n // Low confidence → force update\n if (verdict.confidence === 'low' && !verdict.shouldUpdate) {\n options.onOutput?.(`[skill-gen] Relevance: low confidence skip → overriding to update`);\n verdict.shouldUpdate = true;\n verdict.reason = `${verdict.reason} (overridden: low confidence)`;\n }\n\n options.onOutput?.(`[skill-gen] Relevance verdict: ${verdict.shouldUpdate ? 'UPDATE' : 'SKIP'} (${verdict.confidence}) — ${verdict.reason}`);\n\n // Log delta analysis size for observability\n if (verdict.deltaAnalysis) {\n options.onOutput?.(`[skill-gen] Delta analysis: ${verdict.deltaAnalysis.length} chars`);\n }\n\n // Log the work plan details so the user can see what will be modified\n if (verdict.shouldUpdate && verdict.workPlan) {\n const { filesToUpdate, filesToSkip, researchFocus } = verdict.workPlan;\n if (filesToUpdate && filesToUpdate.length > 0) {\n options.onOutput?.(`[skill-gen] ── Work Plan ──`);\n for (const f of filesToUpdate) {\n const hints = f.changeHints.length > 0\n ? ` → ${f.changeHints.join('; ')}`\n : '';\n options.onOutput?.(`[skill-gen] ✎ ${f.file}${hints}`);\n }\n }\n if (filesToSkip && filesToSkip.length > 0) {\n options.onOutput?.(`[skill-gen] ⊘ Unchanged: ${filesToSkip.join(', ')}`);\n }\n if (researchFocus && researchFocus.length > 0) {\n options.onOutput?.(`[skill-gen] 🔍 Research focus: ${researchFocus.join('; ')}`);\n }\n options.onOutput?.(`[skill-gen] ───────────────`);\n }\n\n return verdict;\n } finally {\n cleanup();\n }\n } catch (err: unknown) {\n options.onOutput?.(`[skill-gen] Relevance check failed (${errorMessage(err)}), defaulting to full update`);\n logger.debug(`[relevance] Check failed for ${repoName}:`, err);\n return fullUpdate;\n }\n}\n","/**\n * Build a compact repo catalog for AI classification.\n *\n * Reads package.json descriptions and README first lines from each known repo\n * to produce a minimal reference list the AI can use for matching.\n */\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { discoverKnownRepos, type KnownRepo } from '../utils/paths.js';\n\nexport interface RepoCatalogEntry {\n /** Index number for the AI to reference */\n index: number;\n /** org/repo or short name */\n name: string;\n /** Absolute path on disk */\n path: string;\n /** Description from package.json or README */\n description: string;\n /** Key technology signals (from package.json dependencies) */\n techSignals: string[];\n}\n\n/**\n * Build the repo catalog from all discovered repos.\n * Each entry is compact (~50-100 tokens) to fit many repos in one AI call.\n *\n * @param filteredRepos Optional pre-filtered list of repos to use instead\n * of discovering all repos. When provided, the catalog\n * is built only from these repos.\n */\nexport function buildRepoCatalog(filteredRepos?: KnownRepo[]): RepoCatalogEntry[] {\n const repos = filteredRepos ?? discoverKnownRepos();\n const catalog: RepoCatalogEntry[] = [];\n\n for (let i = 0; i < repos.length; i++) {\n const repo = repos[i];\n const description = getRepoDescription(repo);\n const techSignals = getTechSignals(repo);\n\n catalog.push({\n index: i + 1,\n name: repo.remoteName ?? repo.shortName,\n path: repo.path,\n description,\n techSignals,\n });\n }\n\n return catalog;\n}\n\n/**\n * Format the repo catalog as a compact string for the AI prompt.\n */\nexport function formatCatalogForPrompt(catalog: RepoCatalogEntry[]): string {\n return catalog\n .map((entry) => {\n const parts = [`${entry.index}. ${entry.name} (${entry.path})`];\n if (entry.description) parts.push(` ${entry.description}`);\n if (entry.techSignals.length > 0) parts.push(` tech: ${entry.techSignals.join(', ')}`);\n return parts.join('\\n');\n })\n .join('\\n');\n}\n\nfunction getRepoDescription(repo: KnownRepo): string {\n // Try package.json first\n const pkgPath = join(repo.path, 'package.json');\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n if (pkg.description) return pkg.description.slice(0, 120);\n } catch { /* skip */ }\n }\n\n // Try README first line\n for (const readme of ['README.md', 'readme.md', 'README']) {\n const readmePath = join(repo.path, readme);\n if (existsSync(readmePath)) {\n try {\n const content = readFileSync(readmePath, 'utf-8');\n const lines = content.split('\\n').filter((l) => l.trim() && !l.startsWith('#'));\n if (lines.length > 0) return lines[0].trim().slice(0, 120);\n } catch { /* skip */ }\n }\n }\n\n return '';\n}\n\nfunction getTechSignals(repo: KnownRepo): string[] {\n const signals: string[] = [];\n const pkgPath = join(repo.path, 'package.json');\n\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n const deps = {\n ...pkg.dependencies,\n ...pkg.devDependencies,\n };\n // Extract key framework signals\n const signalMap: Record<string, string> = {\n react: 'React',\n vue: 'Vue',\n angular: 'Angular',\n next: 'Next.js',\n express: 'Express',\n hono: 'Hono',\n '@wix/design-system': 'Wix-DS',\n '@wix/sdk': 'Wix-SDK',\n jest: 'Jest',\n vitest: 'Vitest',\n typescript: 'TypeScript',\n };\n for (const [dep, label] of Object.entries(signalMap)) {\n if (deps[dep]) signals.push(label);\n }\n } catch { /* skip */ }\n }\n\n // Check for other common files\n if (existsSync(join(repo.path, 'Cargo.toml'))) signals.push('Rust');\n if (existsSync(join(repo.path, 'go.mod'))) signals.push('Go');\n if (existsSync(join(repo.path, 'pom.xml'))) signals.push('Java/Maven');\n if (existsSync(join(repo.path, 'build.gradle'))) signals.push('Java/Gradle');\n if (existsSync(join(repo.path, 'requirements.txt'))) signals.push('Python');\n\n return signals.slice(0, 5);\n}\n","/**\n * Zod schemas for all AI-generated outputs in the summarizer pipeline.\n *\n * Phase 1: conversationSummarySchema — validates per-conversation summaries.\n * Phase 2: clusterOutputSchema — validates per-repo clustered knowledge.\n *\n * Follows the same patterns as src/distiller/schemas.ts:\n * - .describe() on every field\n * - .default([]) on optional arrays\n * - Inferred TypeScript types via z.infer<>\n * - safeParse + salvage helpers\n */\n\nimport { z } from 'zod';\n\n// ── Sub-schemas ──────────────────────────────────────────────────\n\nexport const decisionEntrySchema = z.object({\n decision: z.string().describe('What was decided'),\n context: z.string().describe('Why this decision was made'),\n date: z.string().optional().describe('Approximate date from the conversation'),\n});\n\nexport const patternEntrySchema = z.object({\n pattern: z.string().describe('Pattern name or short description'),\n example: z.string().optional().describe('Code example or file reference'),\n});\n\n// ── Phase 1: Per-conversation summary (AI output) ───────────────\n\nexport const conversationSummarySchema = z.object({\n summary: z.string().describe('2-3 sentence overview of the conversation'),\n decisions: z.array(decisionEntrySchema).default([]).describe('Architectural or coding decisions made'),\n patterns: z.array(patternEntrySchema).default([]).describe('Coding patterns established or discussed'),\n toolsUsed: z.array(z.string()).default([]).describe('Tool names invoked (e.g. Bash, Read, StrReplace)'),\n mcpsUsed: z.array(z.string()).default([]).describe('MCP servers referenced (e.g. octocode, cursor-ide-browser)'),\n docsReferenced: z.array(z.string()).default([]).describe('Documentation files referenced (e.g. README.md, docs/api.md)'),\n skillsReferenced: z.array(z.string()).default([]).describe('.cursor/skills or SKILL.md files referenced'),\n filesModified: z.array(z.string()).default([]).describe('Files created, edited, or deleted'),\n importantNotes: z.array(z.string()).default([]).describe('Gotchas, warnings, constraints, or discoveries'),\n outcome: z\n .enum(['completed', 'partial', 'abandoned', 'unknown'])\n .default('unknown')\n .describe('Whether the task in the conversation was completed'),\n});\n\n// ── Phase 2: Per-repo cluster output (AI output) ────────────────\n\nexport const conflictEntrySchema = z.object({\n topic: z.string().describe('What the conflict is about'),\n approachA: z.string().describe('First approach from an earlier conversation'),\n approachB: z.string().describe('Second approach from a later conversation'),\n resolution: z.string().describe('Which approach was chosen and why'),\n resolvedBy: z\n .enum(['newer-wins', 'older-wins', 'merged', 'ai-judgment'])\n .describe('How the conflict was resolved'),\n});\n\nexport const themeEntrySchema = z.object({\n name: z.string().describe('Theme name (e.g. \"Auth\", \"UI Components\", \"Testing\")'),\n insights: z.array(z.string()).default([]).describe('Key insights from conversations on this theme'),\n patterns: z.array(patternEntrySchema).default([]).describe('Patterns related to this theme'),\n decisions: z.array(decisionEntrySchema).default([]).describe('Decisions related to this theme'),\n});\n\nexport const clusterOutputSchema = z.object({\n themes: z.array(themeEntrySchema).default([]).describe('Grouped insights by topic'),\n activeDecisions: z.array(decisionEntrySchema).default([]).describe('Current architectural decisions (conflict-resolved)'),\n deprecatedDecisions: z.array(decisionEntrySchema).default([]).describe('Superseded approaches with context'),\n patterns: z.array(patternEntrySchema).default([]).describe('Established patterns from conversations'),\n conflicts: z.array(conflictEntrySchema).default([]).describe('Detected and resolved conflicts'),\n knownIssues: z.array(z.string()).default([]).describe('Known issues, gotchas, and constraints'),\n toolingPatterns: z.array(z.string()).default([]).describe('Common tool and MCP usage patterns'),\n});\n\n// ── Inferred TypeScript types ────────────────────────────────────\n\nexport type ConversationSummaryAI = z.infer<typeof conversationSummarySchema>;\nexport type DecisionEntry = z.infer<typeof decisionEntrySchema>;\nexport type PatternEntry = z.infer<typeof patternEntrySchema>;\nexport type ConflictEntry = z.infer<typeof conflictEntrySchema>;\nexport type ThemeEntry = z.infer<typeof themeEntrySchema>;\nexport type ClusterOutput = z.infer<typeof clusterOutputSchema>;\n\n// ── Parsing helpers ──────────────────────────────────────────────\n\n/**\n * Extract JSON from a raw LLM response.\n *\n * Handles markdown code fences, leading/trailing text, and tool-call output\n * interleaved with the response (common with Cursor Agent which includes\n * git/bash output around the JSON verdict).\n *\n * Strategy:\n * 1. Balanced-brace extraction on the raw string (handles tool output\n * before/after the JSON, multiple fenced blocks, etc.).\n * 2. If that fails, strip markdown code fences and try again.\n * 3. Fallback: slice from first `{` or `[` (original behavior).\n */\nexport function extractJson(raw: string): string {\n const str = raw.trim();\n\n // Strategy 1: Balanced-brace extraction on the raw string.\n // Handles Cursor Agent output with tool traces, preamble, and trailing text.\n const extracted = extractBalancedJson(str);\n if (extracted) return extracted;\n\n // Strategy 2: Strip markdown code fences and try balanced extraction again.\n // Handles clean ```json { ... } ``` responses.\n const fencedMatch = str.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)\\n?\\s*```/);\n if (fencedMatch) {\n const fenced = fencedMatch[1].trim();\n const fenceExtracted = extractBalancedJson(fenced);\n if (fenceExtracted) return fenceExtracted;\n // If the fenced content starts with { or [, return it directly\n if (fenced.startsWith('{') || fenced.startsWith('[')) return fenced;\n }\n\n // Strategy 3: Fallback — slice from first { or [ (original behavior)\n let fallback = str;\n if (!fallback.startsWith('{') && !fallback.startsWith('[')) {\n const jsonStart = fallback.search(/[{\\[]/);\n if (jsonStart >= 0) {\n fallback = fallback.slice(jsonStart);\n }\n }\n\n return fallback;\n}\n\n/**\n * Find a balanced JSON object or array within a string.\n *\n * Scans for `{` or `[` characters, tracks depth while respecting JSON\n * string literals (skipping escaped characters), and tries JSON.parse\n * on each balanced candidate.\n *\n * When a valid JSON candidate is found, the scanner skips past it so\n * that nested objects inside a valid parent are not returned separately.\n * Returns the **last** top-level valid JSON found (LLM verdicts are\n * typically at the end, after tool output).\n *\n * Returns null if no valid balanced JSON is found.\n */\nfunction extractBalancedJson(str: string): string | null {\n let lastValid: string | null = null;\n\n for (let i = 0; i < str.length; i++) {\n const ch = str[i];\n if (ch !== '{' && ch !== '[') continue;\n\n const close = ch === '{' ? '}' : ']';\n let depth = 1;\n let j = i + 1;\n let inString = false;\n\n while (j < str.length && depth > 0) {\n const c = str[j];\n\n if (inString) {\n if (c === '\\\\') {\n j++; // skip escaped character\n } else if (c === '\"') {\n inString = false;\n }\n } else {\n if (c === '\"') {\n inString = true;\n } else if (c === ch) {\n depth++;\n } else if (c === close) {\n depth--;\n }\n }\n j++;\n }\n\n if (depth === 0) {\n const candidate = str.slice(i, j);\n try {\n JSON.parse(candidate);\n lastValid = candidate;\n // Skip past this object so we don't scan its children as\n // separate candidates. Only top-level objects are considered.\n i = j - 1;\n } catch {\n // Not valid JSON despite balanced braces — continue scanning inside\n }\n }\n }\n\n return lastValid;\n}\n\n/**\n * Parse an array, keeping only items that pass Zod validation.\n * Invalid items are silently dropped (lenient).\n */\nfunction safeParseArray<T>(input: unknown, schema: z.ZodType<T, z.ZodTypeDef, unknown>): T[] {\n if (!Array.isArray(input)) return [];\n const result: T[] = [];\n for (const item of input) {\n const parsed = schema.safeParse(item);\n if (parsed.success) {\n result.push(parsed.data);\n }\n }\n return result;\n}\n\n/**\n * Safely parse a raw LLM response into a ConversationSummaryAI.\n * Returns a valid object even if parsing partially fails, with salvage logic.\n */\nexport function parseConversationSummary(raw: string): {\n data: ConversationSummaryAI;\n success: boolean;\n errors: string[];\n} {\n const errors: string[] = [];\n\n try {\n const jsonStr = extractJson(raw);\n const parsed = JSON.parse(jsonStr);\n const result = conversationSummarySchema.safeParse(parsed);\n\n if (result.success) {\n return { data: result.data, success: true, errors: [] };\n }\n\n // Collect Zod errors\n for (const issue of result.error.issues) {\n errors.push(`${issue.path.join('.')}: ${issue.message}`);\n }\n\n // Salvage what we can\n const salvaged: ConversationSummaryAI = {\n summary: typeof parsed.summary === 'string' ? parsed.summary : '',\n decisions: safeParseArray(parsed.decisions, decisionEntrySchema),\n patterns: safeParseArray(parsed.patterns, patternEntrySchema),\n toolsUsed: Array.isArray(parsed.toolsUsed) ? parsed.toolsUsed.filter((t: unknown) => typeof t === 'string') : [],\n mcpsUsed: Array.isArray(parsed.mcpsUsed) ? parsed.mcpsUsed.filter((t: unknown) => typeof t === 'string') : [],\n docsReferenced: Array.isArray(parsed.docsReferenced) ? parsed.docsReferenced.filter((t: unknown) => typeof t === 'string') : [],\n skillsReferenced: Array.isArray(parsed.skillsReferenced) ? parsed.skillsReferenced.filter((t: unknown) => typeof t === 'string') : [],\n filesModified: Array.isArray(parsed.filesModified) ? parsed.filesModified.filter((t: unknown) => typeof t === 'string') : [],\n importantNotes: Array.isArray(parsed.importantNotes) ? parsed.importantNotes.filter((t: unknown) => typeof t === 'string') : [],\n outcome: ['completed', 'partial', 'abandoned', 'unknown'].includes(parsed.outcome) ? parsed.outcome : 'unknown',\n };\n\n return { data: salvaged, success: false, errors };\n } catch (err) {\n errors.push(`JSON parse failed: ${err instanceof Error ? err.message : String(err)}`);\n return {\n data: {\n summary: '',\n decisions: [],\n patterns: [],\n toolsUsed: [],\n mcpsUsed: [],\n docsReferenced: [],\n skillsReferenced: [],\n filesModified: [],\n importantNotes: [],\n outcome: 'unknown',\n },\n success: false,\n errors,\n };\n }\n}\n\n/**\n * Safely parse a raw LLM response into a ClusterOutput.\n * Returns a valid object even if parsing partially fails, with salvage logic.\n */\nexport function parseClusterOutput(raw: string): {\n data: ClusterOutput;\n success: boolean;\n errors: string[];\n} {\n const errors: string[] = [];\n\n try {\n const jsonStr = extractJson(raw);\n const parsed = JSON.parse(jsonStr);\n const result = clusterOutputSchema.safeParse(parsed);\n\n if (result.success) {\n return { data: result.data, success: true, errors: [] };\n }\n\n for (const issue of result.error.issues) {\n errors.push(`${issue.path.join('.')}: ${issue.message}`);\n }\n\n // Salvage (explicit type params to resolve Zod input/output ambiguity with .default())\n const salvaged: ClusterOutput = {\n themes: safeParseArray<ThemeEntry>(parsed.themes, themeEntrySchema),\n activeDecisions: safeParseArray<DecisionEntry>(parsed.activeDecisions, decisionEntrySchema),\n deprecatedDecisions: safeParseArray<DecisionEntry>(parsed.deprecatedDecisions, decisionEntrySchema),\n patterns: safeParseArray<PatternEntry>(parsed.patterns, patternEntrySchema),\n conflicts: safeParseArray<ConflictEntry>(parsed.conflicts, conflictEntrySchema),\n knownIssues: Array.isArray(parsed.knownIssues) ? parsed.knownIssues.filter((t: unknown) => typeof t === 'string') : [],\n toolingPatterns: Array.isArray(parsed.toolingPatterns) ? parsed.toolingPatterns.filter((t: unknown) => typeof t === 'string') : [],\n };\n\n return { data: salvaged, success: false, errors };\n } catch (err) {\n errors.push(`JSON parse failed: ${err instanceof Error ? err.message : String(err)}`);\n return {\n data: {\n themes: [],\n activeDecisions: [],\n deprecatedDecisions: [],\n patterns: [],\n conflicts: [],\n knownIssues: [],\n toolingPatterns: [],\n },\n success: false,\n errors,\n };\n }\n}\n","/**\n * Functions for parsing and cleaning Claude CLI output.\n *\n * Handles multi-file output parsing, frontmatter injection,\n * result extraction from stream-json events, and stream event logging.\n */\n\nimport { isRateLimited } from '../utils/cli.js';\nimport { logger } from '../utils/logger.js';\nimport { MIN_SUBSTANTIAL_OUTPUT } from '../utils/constants.js';\nimport type { RepoSkillOptions } from './types.js';\n\n// ── Stream event types ───────────────────────────────────────────\n\ninterface McpServer {\n name: string;\n status: string;\n}\n\ninterface ContentBlock {\n type: string;\n text?: string;\n name?: string;\n input?: unknown;\n content?: string | ContentBlock[];\n}\n\ninterface StreamSystemEvent {\n type: 'system';\n mcp_servers?: McpServer[];\n tools?: string[];\n}\n\ninterface StreamAssistantEvent {\n type: 'assistant';\n message?: { content: ContentBlock[] };\n}\n\ninterface StreamUserEvent {\n type: 'user';\n message?: { content: ContentBlock[] };\n}\n\ninterface StreamResultEvent {\n type: 'result';\n result?: string | { content?: string | ContentBlock[]; message?: string };\n content?: string;\n message?: string;\n duration_ms?: number;\n total_cost_usd?: number;\n}\n\nexport type StreamEvent =\n | StreamSystemEvent\n | StreamAssistantEvent\n | StreamUserEvent\n | StreamResultEvent\n | { type: string };\n\n// ── Multi-file output parsing ────────────────────────────────────\n\n/**\n * Parse output containing multiple files delimited by `--- FILE: X ---` markers.\n * Returns a Map of filename → content.\n * Falls back to treating the entire output as SKILL.md when no markers found.\n */\nexport function parseMultiFileOutput(raw: string): Map<string, string> {\n const files = new Map<string, string>();\n const markerPattern = /^---\\s*FILE:\\s*(.+?)\\s*---\\s*$/;\n\n const lines = raw.split('\\n');\n let currentFile: string | null = null;\n let currentContent: string[] = [];\n\n for (const line of lines) {\n const match = line.match(markerPattern);\n if (match) {\n // Flush previous file\n if (currentFile && currentContent.length > 0) {\n files.set(currentFile, currentContent.join('\\n').trim());\n }\n currentFile = match[1].trim();\n currentContent = [];\n } else if (currentFile) {\n currentContent.push(line);\n }\n // Lines before the first marker are discarded (preamble)\n }\n\n // Flush last file\n if (currentFile && currentContent.length > 0) {\n files.set(currentFile, currentContent.join('\\n').trim());\n }\n\n // No markers found — treat entire output as SKILL.md (backwards compat)\n if (files.size === 0) {\n files.set('SKILL.md', raw);\n }\n\n return files;\n}\n\n// ── Output cleaning ──────────────────────────────────────────────\n\n/**\n * Clean raw LLM output: strip code fences, add frontmatter if missing,\n * replace timestamp placeholders.\n */\nexport function cleanOutput(\n raw: string,\n skillName: string,\n repoName: string,\n timestamp: string,\n convCount: number,\n): string {\n let content = raw.trim();\n\n // Strip markdown code fences that Claude sometimes wraps output in\n content = content.replace(/^```(?:markdown|yaml|md)?\\n?/g, '').replace(/\\n?```$/g, '').trim();\n\n // Check if it has frontmatter\n if (content.startsWith('---')) {\n // Replace the timestamp placeholder if present\n content = content.replace(/\\{\\{TIMESTAMP\\}\\}/g, timestamp);\n return content + '\\n';\n }\n\n // Add frontmatter if missing\n const frontmatter = [\n '---',\n `name: ${skillName}`,\n `description: Development guide for ${repoName}. Repo overview, architecture patterns, coding conventions, and critical references extracted from AI conversations and git history. Use when working in this repo for context on patterns, conventions, and architecture.`,\n `lastUpdated: \"${timestamp}\"`,\n 'generatedBy: lore',\n `conversationCount: ${convCount}`,\n '---',\n '',\n ].join('\\n');\n\n return frontmatter + content + '\\n';\n}\n\n// ── Stream event extraction ──────────────────────────────────────\n\n/**\n * Extract the final text result from a stream of Claude CLI events.\n *\n * Tries multiple strategies in order:\n * 1. `result` event with string > 200 chars\n * 2. Last assistant message text blocks\n * 3. All assistant text blocks concatenated\n * 4. Short result event (last resort)\n * 5. Tool result content\n */\nexport function extractResultFromEvents(events: StreamEvent[]): string {\n // ── Diagnostics: log event types and text sizes for debugging ──\n const diagnostics: string[] = [];\n for (const ev of events) {\n if (ev.type === 'result') {\n const re = ev as StreamResultEvent;\n const resultType = typeof re.result;\n const resultLen = typeof re.result === 'string' ? re.result.length : JSON.stringify(re.result ?? '').length;\n diagnostics.push(`result(${resultType},${resultLen}chars)`);\n } else if (ev.type === 'assistant') {\n const ae = ev as StreamAssistantEvent;\n const blocks = ae.message?.content ?? [];\n const textBlocks = blocks.filter((b) => b.type === 'text');\n const toolBlocks = blocks.filter((b) => b.type === 'tool_use');\n const textLen = textBlocks.reduce((sum, b) => sum + (b.text?.length ?? 0), 0);\n diagnostics.push(`assistant(${textBlocks.length}text/${toolBlocks.length}tool,${textLen}chars)`);\n } else {\n diagnostics.push(ev.type ?? 'unknown');\n }\n }\n logger.debug(`[extractResult] Event breakdown: ${diagnostics.join(' | ')}`);\n\n // Strategy 1: 'result' event with a string result (clean exit)\n // Skip short rate-limit/error messages — prefer assistant text blocks instead.\n for (let i = events.length - 1; i >= 0; i--) {\n const ev = events[i];\n if (ev.type === 'result') {\n const re = ev as StreamResultEvent;\n if (typeof re.result === 'string' && re.result.trim().length > 0) {\n if (re.result.trim().length > MIN_SUBSTANTIAL_OUTPUT && !isRateLimited(re.result)) {\n return re.result;\n }\n logger.debug(`[extractResult] Skipping short/rate-limited result event (${re.result.length} chars): \"${re.result.slice(0, 80)}...\"`);\n }\n // Some CLI versions nest the result in result.content or result.message\n const resultObj = typeof re.result === 'object' && re.result ? re.result : undefined;\n const nested = resultObj?.content ?? resultObj?.message ?? re.content ?? re.message;\n if (typeof nested === 'string' && nested.trim().length > MIN_SUBSTANTIAL_OUTPUT && !isRateLimited(nested)) {\n return nested;\n }\n // Check for array of content blocks in the result\n if (Array.isArray(nested)) {\n const text = nested\n .filter((b: ContentBlock) => b.type === 'text' && b.text?.trim())\n .map((b: ContentBlock) => b.text)\n .join('\\n');\n if (text.length > MIN_SUBSTANTIAL_OUTPUT) return text;\n }\n }\n }\n\n // Strategy 2: concatenate text from the LAST assistant message\n const assistantEvents = events.filter(\n (ev): ev is StreamAssistantEvent =>\n ev.type === 'assistant' && Array.isArray((ev as StreamAssistantEvent).message?.content),\n );\n\n // Try the last assistant message first (most likely to have the research doc)\n if (assistantEvents.length > 0) {\n const lastAssistant = assistantEvents[assistantEvents.length - 1];\n const lastTextParts: string[] = [];\n for (const block of lastAssistant.message!.content) {\n if (block.type === 'text' && block.text?.trim()) {\n lastTextParts.push(block.text);\n }\n }\n const lastText = lastTextParts.join('\\n');\n if (lastText.length > 100) {\n return lastText;\n }\n }\n\n // Fall back to ALL assistant text blocks (concatenated)\n const allTextParts: string[] = [];\n for (const ae of assistantEvents) {\n for (const block of ae.message!.content) {\n if (block.type === 'text' && block.text?.trim()) {\n allTextParts.push(block.text);\n }\n }\n }\n if (allTextParts.length > 0) return allTextParts.join('\\n');\n\n // Strategy 3: accept the result event even if short (last resort before tool_results)\n for (let i = events.length - 1; i >= 0; i--) {\n const ev = events[i];\n if (ev.type === 'result') {\n const re = ev as StreamResultEvent;\n if (typeof re.result === 'string' && re.result.trim().length > 0) {\n return re.result;\n }\n }\n }\n\n // Strategy 4: look for tool_result content (tool outputs may contain useful text)\n const toolResults: string[] = [];\n for (const ev of events) {\n if (ev.type === 'user') {\n const ue = ev as StreamUserEvent;\n if (Array.isArray(ue.message?.content)) {\n for (const block of ue.message!.content) {\n if (block.type === 'tool_result' && typeof block.content === 'string' && block.content.trim().length > 50) {\n toolResults.push(block.content);\n }\n }\n }\n }\n }\n if (toolResults.length > 0) {\n logger.warn(`No assistant text found; falling back to ${toolResults.length} tool result(s)`);\n return toolResults.join('\\n\\n');\n }\n\n // Log diagnostics for debugging\n const typeCounts: Record<string, number> = {};\n for (const ev of events) {\n typeCounts[ev.type ?? 'unknown'] = (typeCounts[ev.type ?? 'unknown'] ?? 0) + 1;\n }\n const summary = Object.entries(typeCounts).map(([k, v]) => `${k}:${v}`).join(', ');\n throw new Error(`No result found in Claude stream output (${events.length} events: ${summary})`);\n}\n\n// ── Stream event logging ─────────────────────────────────────────\n\n/**\n * Parse a stream-json event and emit a human-readable line via onOutput.\n */\nexport function emitStreamEvent(event: StreamEvent, repoName: string, options: RepoSkillOptions): void {\n if (!options.onOutput) return;\n const prefix = `[skill-gen] [${repoName}]`;\n\n switch (event.type) {\n case 'system': {\n const se = event as StreamSystemEvent;\n if (se.mcp_servers && se.mcp_servers.length > 0) {\n const statuses = se.mcp_servers.map((s) => `${s.name}:${s.status}`).join(', ');\n options.onOutput(`${prefix} MCP: ${statuses}`);\n }\n if (se.tools && se.tools.length > 0) {\n options.onOutput(`${prefix} Tools: ${se.tools.join(', ')}`);\n }\n break;\n }\n case 'assistant': {\n const ae = event as StreamAssistantEvent;\n const content = ae.message?.content;\n if (Array.isArray(content)) {\n for (const block of content) {\n if (block.type === 'text') {\n const text = (block.text ?? '').trim();\n if (text.length > 0) {\n const preview = text.length > 120 ? text.slice(0, 117) + '...' : text;\n options.onOutput(`${prefix} Writing: ${preview}`);\n }\n } else if (block.type === 'tool_use') {\n const name = block.name ?? 'tool';\n const inputPreview = typeof block.input === 'object'\n ? JSON.stringify(block.input).slice(0, 100)\n : String(block.input ?? '').slice(0, 100);\n options.onOutput(`${prefix} Tool: ${name}(${inputPreview})`);\n }\n }\n }\n break;\n }\n case 'result': {\n const re = event as StreamResultEvent;\n const duration = re.duration_ms ? `${Math.round(re.duration_ms / 1000)}s` : '';\n const cost = re.total_cost_usd ? `$${re.total_cost_usd.toFixed(4)}` : '';\n options.onOutput(`${prefix} Done${duration ? ` in ${duration}` : ''}${cost ? ` (${cost})` : ''}`);\n break;\n }\n // Silently skip other event types (user, etc.)\n }\n}\n","/**\n * Filesystem operations for skill files.\n *\n * Handles reading, writing, listing skill files and checking\n * referential integrity (dead links) in skill documents.\n */\n\nimport {\n copyFileSync,\n existsSync,\n readFileSync,\n readdirSync,\n writeFileSync,\n mkdirSync,\n unlinkSync,\n} from 'node:fs';\nimport { join } from 'node:path';\n\n// ── List skill files ─────────────────────────────────────────────\n\n/**\n * Recursively list all .md files in a skill directory.\n * Returns relative paths like \"SKILL.md\", \"references/arch.md\".\n */\nexport function listSkillFiles(skillDir: string): string[] {\n if (!existsSync(skillDir)) return [];\n const files: string[] = [];\n function walk(dir: string, prefix: string): void {\n try {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n walk(join(dir, entry.name), `${prefix}${entry.name}/`);\n } else if (entry.name.endsWith('.md')) {\n files.push(`${prefix}${entry.name}`);\n }\n }\n } catch { /* ignore */ }\n }\n walk(skillDir, '');\n return files;\n}\n\n// ── Write skill files ────────────────────────────────────────────\n\n/**\n * Write a map of filename → content to the skill directory.\n * Creates subdirectories as needed. Returns the count of files written.\n */\nexport function writeSkillFiles(skillDir: string, files: Map<string, string>): number {\n let count = 0;\n for (const [filename, content] of files) {\n const filePath = join(skillDir, filename);\n // Create subdirectories if needed (e.g. references/01-foo.md)\n const fileDir = filePath.substring(0, filePath.lastIndexOf('/'));\n if (fileDir !== skillDir) {\n mkdirSync(fileDir, { recursive: true });\n }\n writeFileSync(filePath, content.trim() + '\\n', 'utf-8');\n count++;\n }\n return count;\n}\n\n// ── Copy skill files ─────────────────────────────────────────────\n\n/**\n * Recursively copy all .md files from srcDir to destDir.\n * Creates subdirectories as needed. Used to seed the worktree's skill\n * directory with existing files so the agent can read-modify-write.\n */\nexport function copySkillFiles(srcDir: string, destDir: string): number {\n if (!existsSync(srcDir)) return 0;\n let count = 0;\n function walk(dir: string, prefix: string): void {\n try {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n walk(join(dir, entry.name), `${prefix}${entry.name}/`);\n } else if (entry.name.endsWith('.md')) {\n const srcPath = join(dir, entry.name);\n const destPath = join(destDir, `${prefix}${entry.name}`);\n const destFileDir = destPath.substring(0, destPath.lastIndexOf('/'));\n mkdirSync(destFileDir, { recursive: true });\n copyFileSync(srcPath, destPath);\n count++;\n }\n }\n } catch { /* ignore */ }\n }\n walk(srcDir, '');\n return count;\n}\n\n// ── Delete skill files ───────────────────────────────────────────\n\n/**\n * Delete specific skill files from the skill directory.\n * Accepts relative paths like \"references/03-old-auth.md\".\n * Returns the count of files actually deleted.\n */\nexport function deleteSkillFiles(skillDir: string, files: string[]): number {\n let count = 0;\n for (const file of files) {\n const filePath = join(skillDir, file);\n try {\n if (existsSync(filePath)) {\n unlinkSync(filePath);\n count++;\n }\n } catch { /* ignore — file may have been deleted externally */ }\n }\n return count;\n}\n\n// ── Dead link detection ──────────────────────────────────────────\n\n/**\n * Check if a skill document contains markdown links to .md files that\n * don't exist on disk. URL links (http/https) are ignored.\n */\nexport function hasDeadLinks(skillContent: string, skillDir: string): boolean {\n // Match markdown links like [text](file.md) — only relative paths (no http)\n const linkRegex = /\\[.+?\\]\\(([^)]+\\.md)\\)/g;\n let match: RegExpExecArray | null;\n while ((match = linkRegex.exec(skillContent)) !== null) {\n const target = match[1];\n // Skip URLs\n if (target.startsWith('http://') || target.startsWith('https://')) continue;\n // Check if the referenced file exists\n if (!existsSync(join(skillDir, target))) {\n return true;\n }\n }\n return false;\n}\n\n// ── Reference link patching ──────────────────────────────────────\n\n/**\n * Patch the main SKILL.md with a link to references/INDEX.md when\n * that file is in the generated output.\n *\n * Accepts either a Map<string, string> (legacy — keys are filenames)\n * or a string[] of filenames (disk-based flow).\n *\n * NOTE: docs-reference.md and references/README.md are deprecated\n * and no longer patched. See orphan-detector.ts for migration.\n */\nexport function patchSkillWithReferenceLinks(\n skillDir: string,\n skillPath: string,\n refFiles: Map<string, string> | string[],\n): void {\n if (!existsSync(skillPath)) return;\n\n const fileNames = Array.isArray(refFiles)\n ? new Set(refFiles)\n : new Set(refFiles.keys());\n\n const hasRefsIndex = fileNames.has('references/INDEX.md');\n if (!hasRefsIndex) return;\n\n let skill = readFileSync(skillPath, 'utf-8');\n\n // Add to Quick Reference table (insert after Architecture/guidelines row)\n if (!skill.includes('references/INDEX.md')) {\n skill = skill.replace(\n /(\\| Architecture\\/guidelines \\|[^\\n]+\\n)/,\n '$1| Deep-dive references | See [references/INDEX.md](references/INDEX.md) |\\n',\n );\n }\n\n // Add to Additional Resources section\n if (!skill.includes('[Reference Docs]')) {\n skill = skill.replace(\n /(- \\[Guidelines\\][^\\n]+\\n)/,\n '$1- [Reference Docs](references/INDEX.md) — deep-dive documentation index\\n',\n );\n }\n\n writeFileSync(skillPath, skill, 'utf-8');\n}\n","/**\n * One-time injection of Lore pointers into AGENTS.md and CLAUDE.md.\n *\n * After a SKILL.md is generated for a repo, this module injects a small\n * fenced block pointing the AI assistant at the skill guide. Uses HTML\n * comment markers (`<!-- lore:begin -->` / `<!-- lore:end -->`) so the\n * block is non-destructive and idempotent.\n *\n * This runs ONCE per repo — the marker's presence is the \"already done\" flag.\n */\n\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { hasCursorData, hasClaudeCodeData } from '../utils/paths.js';\n\n// ── Constants ────────────────────────────────────────────────────────\n\nexport const LORE_BEGIN = '<!-- lore:begin -->';\nexport const LORE_END = '<!-- lore:end -->';\n\n// ── Detection ────────────────────────────────────────────────────────\n\n/**\n * Check if a file already contains the Lore injection marker.\n */\nexport function hasLoreInjection(filePath: string): boolean {\n if (!existsSync(filePath)) return false;\n const content = readFileSync(filePath, 'utf-8');\n return content.includes(LORE_BEGIN);\n}\n\n// ── Block builders ───────────────────────────────────────────────────\n\n/**\n * Build the AGENTS.md Lore block (Cursor — path reference).\n * Cursor's Agent Skills system resolves the SKILL.md automatically.\n */\nexport function buildAgentsMdBlock(skillRelPath: string): string {\n return [\n LORE_BEGIN,\n '# Project Knowledge (Lore)',\n '',\n 'This project uses [Lore](https://github.com/matan1542/lore) to maintain a living project guide.',\n 'The guide is automatically updated from AI conversations and git activity.',\n '',\n `**Project guide**: \\`${skillRelPath}\\``,\n '',\n 'The guide covers architecture, patterns, conventions, and key references for this codebase.',\n '',\n '> Auto-generated by Lore. Edit freely outside this block.',\n LORE_END,\n ].join('\\n');\n}\n\n/**\n * Build the CLAUDE.md Lore block (Claude Code — native @ import).\n * Claude Code auto-loads the referenced file into context.\n */\nexport function buildClaudeMdBlock(skillRelPath: string): string {\n return [\n LORE_BEGIN,\n '# Project Knowledge (Lore)',\n '',\n 'This project uses [Lore](https://github.com/matan1542/lore) to maintain a living project guide.',\n 'The guide is automatically updated from AI conversations and git activity.',\n '',\n `@${skillRelPath}`,\n '',\n '> Auto-generated by Lore. Edit freely outside this block.',\n LORE_END,\n ].join('\\n');\n}\n\n// ── Injection ────────────────────────────────────────────────────────\n\n/**\n * Inject a Lore block into a file. Creates the file if it doesn't exist,\n * or appends to the end if it does.\n */\nexport function injectBlock(filePath: string, block: string): void {\n if (existsSync(filePath)) {\n const existing = readFileSync(filePath, 'utf-8');\n writeFileSync(filePath, existing.trimEnd() + '\\n\\n' + block + '\\n', 'utf-8');\n } else {\n writeFileSync(filePath, block + '\\n', 'utf-8');\n }\n}\n\n// ── Main entry point ─────────────────────────────────────────────────\n\nexport interface AgentPointerResult {\n agents: boolean;\n claude: boolean;\n}\n\n/**\n * Inject Lore pointers into AGENTS.md and/or CLAUDE.md at the repo root.\n *\n * - Checks `hasCursorData()` / `hasClaudeCodeData()` to decide which files to create.\n * - Skips injection if the marker is already present (one-time only).\n * - Returns which files were created/updated.\n */\nexport function injectAgentPointers(\n repoPath: string,\n skillRelPath: string,\n): AgentPointerResult {\n const result: AgentPointerResult = { agents: false, claude: false };\n\n // ── AGENTS.md (Cursor) ──\n if (hasCursorData()) {\n const agentsPath = join(repoPath, 'AGENTS.md');\n if (!hasLoreInjection(agentsPath)) {\n injectBlock(agentsPath, buildAgentsMdBlock(skillRelPath));\n result.agents = true;\n }\n }\n\n // ── CLAUDE.md (Claude Code) ──\n if (hasClaudeCodeData()) {\n const claudePath = join(repoPath, 'CLAUDE.md');\n if (!hasLoreInjection(claudePath)) {\n injectBlock(claudePath, buildClaudeMdBlock(skillRelPath));\n result.claude = true;\n }\n }\n\n return result;\n}\n","/**\n * Orphaned reference file detection and dead link cleanup.\n *\n * After skill generation, this module:\n * 1. Finds dead links in skill files (links to non-existent .md files)\n * — these are auto-removed.\n * 2. Finds orphaned files (.md files not linked from any other skill file)\n * — these require manual approval to delete.\n */\n\nimport { existsSync, readFileSync, renameSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { listSkillFiles } from './skill-files.js';\n\n// ── Constants ────────────────────────────────────────────────────────\n\n/** Core files that are never considered orphaned. */\nconst CORE_FILES = new Set([\n 'SKILL.md',\n 'established-patterns.md',\n 'guidelines.md',\n 'references/INDEX.md',\n]);\n\n/**\n * Filenames deprecated per Anthropic skill standard (matched at any depth).\n * README.md is prohibited inside skill folders — agents ignore it.\n */\nconst DEPRECATED_BASENAMES = new Set(['README.md']);\n\n/**\n * Root-level files deprecated because they duplicate SKILL.md routing\n * and follow the same human-oriented index anti-pattern as README.md.\n */\nconst DEPRECATED_PATHS = new Set(['docs-reference.md']);\n\n/** Check if a file is deprecated and should always be flagged. */\nexport function isDeprecatedFile(file: string): boolean {\n if (DEPRECATED_PATHS.has(file)) return true;\n const basename = file.split('/').pop() ?? '';\n return DEPRECATED_BASENAMES.has(basename);\n}\n\n/** Regex matching markdown links like [text](file.md) — relative paths only. */\nconst LINK_REGEX = /\\[([^\\]]+)\\]\\(([^)]+\\.md)\\)/g;\n\n// ── Dead link detection ──────────────────────────────────────────────\n\n/**\n * Find markdown links in content that point to non-existent .md files.\n * Returns the dead link target paths (relative to skillDir).\n * URL links (http/https) are ignored.\n */\nexport function findDeadLinksDetailed(content: string, skillDir: string): string[] {\n const dead: string[] = [];\n let match: RegExpExecArray | null;\n const regex = new RegExp(LINK_REGEX.source, LINK_REGEX.flags);\n while ((match = regex.exec(content)) !== null) {\n const target = match[2];\n if (target.startsWith('http://') || target.startsWith('https://')) continue;\n if (!existsSync(join(skillDir, target))) {\n dead.push(target);\n }\n }\n return [...new Set(dead)]; // deduplicate\n}\n\n/**\n * Remove dead markdown links from content.\n * Replaces `[text](dead-link.md)` with just `text` (keeps the label, removes the link).\n * Only removes links whose targets are in the deadLinks list.\n */\nexport function removeDeadLinks(content: string, deadLinks: string[]): string {\n const deadSet = new Set(deadLinks);\n return content.replace(\n new RegExp(LINK_REGEX.source, LINK_REGEX.flags),\n (fullMatch, text, target) => {\n if (deadSet.has(target)) return text;\n return fullMatch;\n },\n );\n}\n\n// ── Orphan detection ─────────────────────────────────────────────────\n\n/**\n * Find .md files in skillDir that are NOT referenced by any other .md file.\n *\n * A file is \"referenced\" if any other .md file in the skill directory contains\n * a markdown link `[...](relative/path.md)` pointing to it.\n *\n * Core files (SKILL.md, established-patterns.md, guidelines.md) are never\n * considered orphaned.\n *\n * Returns relative paths like \"references/03-old-auth.md\".\n */\nexport function findOrphanedFiles(skillDir: string): string[] {\n const allFiles = listSkillFiles(skillDir);\n if (allFiles.length === 0) return [];\n\n // Collect all link targets across all files (skip deprecated files —\n // their outgoing links don't shield other files from being orphaned)\n const referenced = new Set<string>();\n for (const file of allFiles) {\n if (isDeprecatedFile(file)) continue;\n const filePath = join(skillDir, file);\n try {\n const content = readFileSync(filePath, 'utf-8');\n let match: RegExpExecArray | null;\n const regex = new RegExp(LINK_REGEX.source, LINK_REGEX.flags);\n while ((match = regex.exec(content)) !== null) {\n const target = match[2];\n if (target.startsWith('http://') || target.startsWith('https://')) continue;\n // Resolve relative links from the file's directory\n // e.g. if file is \"references/01-foo.md\" and links to \"../guidelines.md\"\n // that resolves to \"guidelines.md\"\n if (target.startsWith('../') || target.startsWith('./')) {\n const fileDir = file.includes('/') ? file.substring(0, file.lastIndexOf('/')) : '';\n const parts = [...fileDir.split('/').filter(Boolean), ...target.split('/')];\n const resolved: string[] = [];\n for (const p of parts) {\n if (p === '..') resolved.pop();\n else if (p !== '.') resolved.push(p);\n }\n referenced.add(resolved.join('/'));\n } else {\n // Link target is relative to the file's directory\n const fileDir = file.includes('/') ? file.substring(0, file.lastIndexOf('/')) + '/' : '';\n referenced.add(fileDir + target);\n }\n }\n } catch { /* ignore unreadable files */ }\n }\n\n // Find files that are not referenced and not core\n return allFiles.filter((file) => {\n if (CORE_FILES.has(file)) return false;\n if (isDeprecatedFile(file)) return true; // always flag deprecated files\n return !referenced.has(file);\n });\n}\n\n// ── Deprecated file migration ────────────────────────────────────────\n\n/**\n * Auto-migrate deprecated files in a skill directory:\n * - Rename references/README.md → references/INDEX.md (if INDEX.md doesn't exist)\n * - Remove docs-reference.md links from SKILL.md\n *\n * Returns a list of migration actions taken (for logging).\n */\nexport function migrateDeprecatedFiles(skillDir: string): string[] {\n const actions: string[] = [];\n\n // ── README.md → INDEX.md rename ──\n const readmePath = join(skillDir, 'references', 'README.md');\n const indexPath = join(skillDir, 'references', 'INDEX.md');\n if (existsSync(readmePath) && !existsSync(indexPath)) {\n try {\n renameSync(readmePath, indexPath);\n actions.push('Renamed references/README.md → references/INDEX.md');\n } catch { /* ignore — file may be locked */ }\n }\n\n // ── Remove docs-reference.md links from SKILL.md ──\n const docsRefPath = join(skillDir, 'docs-reference.md');\n const skillPath = join(skillDir, 'SKILL.md');\n if (existsSync(docsRefPath) && existsSync(skillPath)) {\n try {\n const skill = readFileSync(skillPath, 'utf-8');\n // Remove table rows and list items that link to docs-reference.md\n const cleaned = skill\n .split('\\n')\n .filter((line) => !line.includes('docs-reference.md') && !line.includes('[Documentation Reference]'))\n .join('\\n');\n if (cleaned !== skill) {\n writeFileSync(skillPath, cleaned, 'utf-8');\n actions.push('Removed docs-reference.md links from SKILL.md');\n }\n } catch { /* ignore */ }\n }\n\n return actions;\n}\n","/**\n * Pure utility functions for skill generation.\n *\n * No side effects, no filesystem, no network — pure input → output.\n */\n\nimport { repoNameToSlug } from '../summarizer/cache.js';\n\n// ── Name sanitization ────────────────────────────────────────────\n\n/**\n * Convert a repo name to a safe directory-friendly slug.\n * Delegates to the shared `repoNameToSlug` to avoid duplication.\n */\nexport const sanitizeName = repoNameToSlug;\n\n// ── Frontmatter extraction ───────────────────────────────────────\n\n/**\n * Extract the lastUpdated date from SKILL.md frontmatter.\n * Returns the date string or null if not found.\n */\nexport function extractLastUpdated(content: string): string | null {\n const match = content.match(/^lastUpdated:\\s*[\"']?(.+?)[\"']?\\s*$/m);\n return match ? match[1] : null;\n}\n\n// ── Tech-specific guidance ───────────────────────────────────────\n\n/**\n * Generate technology-specific research guidance hints based on detected\n * tech signals (e.g. \"react\", \"python\", \"typescript\").\n * Returns empty string when no relevant signals are found.\n */\nexport function buildTechSpecificGuidance(techSignals: string[]): string {\n const signals = techSignals.map((s) => s.toLowerCase());\n const hints: string[] = [];\n\n if (signals.some((s) => s.includes('react') || s.includes('next'))) {\n hints.push('For React/Next.js: also look for hook patterns, state management approach (Context/Redux/Zustand), component conventions, and routing patterns.');\n }\n if (signals.some((s) => s.includes('python') || s.includes('django') || s.includes('flask') || s.includes('fastapi'))) {\n hints.push('For Python: also look for module structure, dependency injection patterns, ORM usage, and test fixtures.');\n }\n if (signals.some((s) => s.includes('typescript') || s.includes('node'))) {\n hints.push('For TypeScript/Node: also look for type patterns (branded types, discriminated unions), error handling conventions, and build configuration.');\n }\n if (signals.some((s) => s.includes('go') || s.includes('golang'))) {\n hints.push('For Go: also look for interface patterns, error wrapping conventions, and package organization.');\n }\n if (signals.some((s) => s.includes('rust') || s.includes('cargo'))) {\n hints.push('For Rust: also look for trait patterns, error types, and module hierarchy.');\n }\n\n if (hints.length === 0) return '';\n return 'In addition to the general research steps above:\\n' + hints.join('\\n');\n}\n","/**\n * Skill & MCP Digest — Installed & used awareness for deduplication.\n *\n * **Skills**: Scans all installed skill directories (~/.cursor/skills/,\n * ~/.claude/skills/, and workspace-local .cursor/skills/) and conversation\n * summaries to build lightweight digests of each relevant skill. These digests\n * are injected into generation prompts so the LLM avoids re-documenting\n * patterns already covered by other skills.\n *\n * **MCPs**: Scans MCP config files (~/.cursor/mcp.json, ~/.claude/mcp_servers.json)\n * and conversation summaries to identify public MCP servers the user has installed.\n * Public MCPs (run via npx/uvx) are identified by their package name from args[0]\n * (e.g. \"octocode-mcp@latest\" → \"octocode-mcp\"). These are injected into prompts\n * so the generated SKILL.md documents MCP integration patterns.\n *\n * Two sources of signal for both:\n * 1. **Installed**: Present on disk / in config files\n * 2. **Used**: Referenced during AI conversations for a specific repo\n *\n * The merged result prioritizes actually-used entries over merely-installed ones.\n */\n\nimport { existsSync, readFileSync, readdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { logger } from '../utils/logger.js';\nimport { loadSummariesForRepo } from '../summarizer/cache.js';\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport interface SkillDigest {\n /** Skill directory name (e.g. \"mermaid-tools\") */\n name: string;\n /** From frontmatter `description`, truncated to 200 chars */\n description: string;\n /** Optional explicit capability declarations from frontmatter `provides` */\n provides: string[];\n /** Section topics extracted from markdown/XML headers */\n coveredTopics: string[];\n /** Number of conversations where this skill was referenced (0 = installed but unused) */\n usageCount: number;\n}\n\nexport interface McpDigest {\n /** Local alias from config key (e.g. \"octocode\") */\n alias: string;\n /** Canonical package identifier from args[0], stripped of version (e.g. \"octocode-mcp\"). Null for private MCPs. */\n packageName: string | null;\n /** Whether this is a public npm/pypi package (npx/uvx command) */\n isPublic: boolean;\n /** Number of conversations where this MCP was used (0 = installed but unused) */\n usageCount: number;\n}\n\n// ── Constants ────────────────────────────────────────────────────\n\nconst MAX_DESCRIPTION_LENGTH = 200;\n\n/** Commands that indicate a public/registry-based MCP server */\nconst PUBLIC_MCP_COMMANDS = new Set(['npx', 'uvx']);\n\n// ── Public API ───────────────────────────────────────────────────\n\n/**\n * Collect digests of all installed skills, excluding the skill being generated.\n *\n * Scans:\n * - `~/.cursor/skills/` (global Cursor skills)\n * - `~/.claude/skills/` (global Claude skills)\n * - `<repoPath>/.cursor/skills/` (workspace-local, when repoPath provided)\n *\n * @param excludeName - Skill directory name to exclude (the one being generated)\n * @param repoPath - Optional repo path to also scan workspace-local skills\n * @returns Array of SkillDigest objects\n */\nexport function collectInstalledSkillDigests(\n excludeName: string,\n repoPath?: string,\n): SkillDigest[] {\n const skillDirs = [\n join(homedir(), '.cursor', 'skills'),\n join(homedir(), '.claude', 'skills'),\n ];\n\n // Add workspace-local skills if repo path provided\n if (repoPath) {\n skillDirs.push(join(repoPath, '.cursor', 'skills'));\n }\n\n const digests: SkillDigest[] = [];\n const seen = new Set<string>(); // deduplicate by name\n\n for (const dir of skillDirs) {\n if (!existsSync(dir)) continue;\n try {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (entry.name === excludeName) continue;\n if (seen.has(entry.name)) continue;\n seen.add(entry.name);\n\n const skillMdPath = join(dir, entry.name, 'SKILL.md');\n if (!existsSync(skillMdPath)) continue;\n\n try {\n const content = readFileSync(skillMdPath, 'utf-8');\n const digest = parseSkillDigest(entry.name, content);\n if (digest) {\n digests.push(digest);\n }\n } catch {\n // Skip unreadable skill files\n logger.debug(`[skill-digest] Could not read ${skillMdPath}`);\n }\n }\n } catch {\n // Skip inaccessible directories\n }\n }\n\n return digests;\n}\n\n/**\n * Get names of other installed skills (lightweight version for conflict checking).\n * This replaces the private function that was in tester.ts.\n */\nexport function getOtherSkillNames(excludeName: string): string[] {\n const skillDirs = [\n join(homedir(), '.cursor', 'skills'),\n join(homedir(), '.claude', 'skills'),\n ];\n\n const names: string[] = [];\n for (const dir of skillDirs) {\n if (!existsSync(dir)) continue;\n try {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && entry.name !== excludeName) {\n names.push(entry.name);\n }\n }\n } catch {\n // ignore\n }\n }\n return names;\n}\n\n/**\n * Collect digests of skills actually used during AI conversations for a repo.\n *\n * Reads all conversation summaries for the given repo and aggregates the\n * `skillsReferenced` field to identify which skills the user actively relies on.\n * For each referenced skill that exists on disk, builds a full SkillDigest\n * with the conversation usage count.\n *\n * @param repoName - Repo name as stored in conversation summaries (e.g. \"matan1542/lore\")\n * @param excludeName - Skill directory name to exclude (the one being generated)\n * @returns Array of SkillDigest objects with usageCount > 0\n */\nexport function collectUsedSkillDigests(\n repoName: string,\n excludeName: string,\n): SkillDigest[] {\n let summaries;\n try {\n summaries = loadSummariesForRepo(repoName);\n } catch {\n logger.debug(`[skill-digest] Could not load summaries for repo: ${repoName}`);\n return [];\n }\n\n if (summaries.length === 0) return [];\n\n // Aggregate skill references and count occurrences\n const usageCounts = new Map<string, number>();\n for (const summary of summaries) {\n const refs = summary.ai.skillsReferenced;\n if (!refs || refs.length === 0) continue;\n\n for (const ref of refs) {\n // Normalize: \".cursor/skills/mermaid-tools/SKILL.md\" → \"mermaid-tools\"\n // \".cursor/skills/octocode-plan\" → \"octocode-plan\"\n // \"SKILL.md\" → skip (too generic)\n const skillName = normalizeSkillRef(ref);\n if (!skillName || skillName === excludeName) continue;\n\n usageCounts.set(skillName, (usageCounts.get(skillName) ?? 0) + 1);\n }\n }\n\n if (usageCounts.size === 0) return [];\n\n // For each referenced skill, try to find and parse its SKILL.md\n const digests: SkillDigest[] = [];\n const searchDirs = [\n join(homedir(), '.cursor', 'skills'),\n join(homedir(), '.claude', 'skills'),\n ];\n\n for (const [skillName, count] of usageCounts) {\n const digest = findAndParseSkill(skillName, searchDirs);\n if (digest) {\n digest.usageCount = count;\n digests.push(digest);\n } else {\n // Skill was used in conversations but no longer installed — still report it\n digests.push({\n name: skillName,\n description: '(skill was used in conversations but is no longer installed)',\n provides: [],\n coveredTopics: [],\n usageCount: count,\n });\n }\n }\n\n return digests;\n}\n\n/**\n * Merge installed skill digests with conversation-used skill digests.\n *\n * Deduplicates by name. When a skill appears in both sets, the installed\n * digest is used (richer metadata) but the usage count from conversations\n * is applied. Result is sorted by usage count (most-used first).\n *\n * @param installed - Digests from directory scanning (usageCount = 0)\n * @param used - Digests from conversation summaries (usageCount > 0)\n * @returns Merged and deduplicated array\n */\nexport function mergeSkillDigests(\n installed: SkillDigest[],\n used: SkillDigest[],\n): SkillDigest[] {\n const byName = new Map<string, SkillDigest>();\n\n // Start with installed digests (richer metadata: description, topics, provides)\n for (const d of installed) {\n byName.set(d.name, { ...d });\n }\n\n // Merge used digests: add usage counts, add new skills not in installed set\n for (const d of used) {\n const existing = byName.get(d.name);\n if (existing) {\n // Skill is both installed and used — keep installed metadata, add usage count\n existing.usageCount += d.usageCount;\n } else {\n // Skill was used but isn't installed (anymore) — add as-is\n byName.set(d.name, { ...d });\n }\n }\n\n return [...byName.values()];\n}\n\n/**\n * Format an array of SkillDigest into a string suitable for prompt injection.\n *\n * Sorts by usage count (most-used first) and annotates each skill with its\n * usage signal so the LLM can prioritize deduplication of actively-used skills.\n *\n * Returns an empty string if no digests are available (caller should substitute\n * a \"No other skills installed.\" message or omit the section).\n */\nexport function formatSkillDigests(digests: SkillDigest[]): string {\n if (digests.length === 0) return '';\n\n // Sort: most-used first, then alphabetical for ties\n const sorted = [...digests].sort((a, b) => {\n if (b.usageCount !== a.usageCount) return b.usageCount - a.usageCount;\n return a.name.localeCompare(b.name);\n });\n\n const lines: string[] = [];\n for (const d of sorted) {\n const usageLabel = d.usageCount > 0\n ? `(used in ${d.usageCount} conversation${d.usageCount === 1 ? '' : 's'})`\n : '(installed, not recently used)';\n lines.push(`- **${d.name}** ${usageLabel}: ${d.description}`);\n if (d.provides.length > 0) {\n lines.push(` Provides: ${d.provides.join(', ')}`);\n }\n if (d.coveredTopics.length > 0) {\n lines.push(` Covers: ${d.coveredTopics.join(', ')}`);\n }\n }\n return lines.join('\\n');\n}\n\n// ── MCP Digest Public API ────────────────────────────────────────\n\n/**\n * Extract the package name from an MCP args[0] value by stripping the\n * version/tag suffix.\n *\n * Examples:\n * - \"octocode-mcp@latest\" → \"octocode-mcp\"\n * - \"@scope/my-mcp@1.2.3\" → \"@scope/my-mcp\"\n * - \"some-mcp\" (no version) → \"some-mcp\"\n */\nexport function extractPackageName(rawArg: string): string {\n const trimmed = rawArg.trim();\n if (!trimmed) return trimmed;\n\n // Scoped packages: @scope/name@version\n if (trimmed.startsWith('@')) {\n const slashIdx = trimmed.indexOf('/');\n if (slashIdx === -1) return trimmed;\n // Find the @ that starts the version (after the slash)\n const afterSlash = trimmed.slice(slashIdx + 1);\n const atIdx = afterSlash.indexOf('@');\n if (atIdx === -1) return trimmed;\n return trimmed.slice(0, slashIdx + 1 + atIdx);\n }\n\n // Non-scoped: name@version\n const atIdx = trimmed.indexOf('@');\n if (atIdx === -1) return trimmed;\n return trimmed.slice(0, atIdx);\n}\n\n/**\n * Collect digests of all installed public MCP servers.\n *\n * Reads:\n * - `~/.cursor/mcp.json` (Cursor format: `{ mcpServers: { alias: { command, args } } }`)\n * - `~/.claude/mcp_servers.json` (Claude format: `{ alias: { command, args } }`)\n *\n * Only includes public MCPs (command is `npx` or `uvx`). Private/local MCPs\n * (using `node`, absolute paths, etc.) are skipped entirely.\n *\n * @returns Array of McpDigest objects with usageCount = 0\n */\nexport function collectInstalledMcpDigests(): McpDigest[] {\n const digests: McpDigest[] = [];\n const seen = new Set<string>(); // deduplicate by alias\n\n // Read Cursor MCP config: { mcpServers: { alias: { command, args } } }\n const cursorConfigPath = join(homedir(), '.cursor', 'mcp.json');\n if (existsSync(cursorConfigPath)) {\n try {\n const config = JSON.parse(readFileSync(cursorConfigPath, 'utf-8'));\n const servers = config.mcpServers ?? {};\n for (const [alias, serverConfig] of Object.entries(servers)) {\n if (seen.has(alias)) continue;\n const digest = parseMcpServerEntry(alias, serverConfig as Record<string, unknown>);\n if (digest) {\n seen.add(alias);\n digests.push(digest);\n }\n }\n } catch {\n logger.debug('[mcp-digest] Could not read Cursor MCP config');\n }\n }\n\n // Read Claude Code MCP config: { alias: { command, args } }\n const claudeConfigPath = join(homedir(), '.claude', 'mcp_servers.json');\n if (existsSync(claudeConfigPath)) {\n try {\n const config = JSON.parse(readFileSync(claudeConfigPath, 'utf-8'));\n for (const [alias, serverConfig] of Object.entries(config)) {\n if (seen.has(alias)) continue;\n const digest = parseMcpServerEntry(alias, serverConfig as Record<string, unknown>);\n if (digest) {\n seen.add(alias);\n digests.push(digest);\n }\n }\n } catch {\n logger.debug('[mcp-digest] Could not read Claude Code MCP config');\n }\n }\n\n return digests;\n}\n\n/**\n * Collect digests of MCPs used during AI conversations for a repo.\n *\n * Reads all conversation summaries for the given repo and aggregates the\n * `mcpsUsed` field. Returns McpDigest entries with usageCount > 0 for each\n * MCP alias that was referenced.\n *\n * @param repoName - Repo name as stored in conversation summaries\n * @returns Array of McpDigest objects with usageCount > 0\n */\nexport function collectUsedMcpDigests(repoName: string): McpDigest[] {\n let summaries;\n try {\n summaries = loadSummariesForRepo(repoName);\n } catch {\n logger.debug(`[mcp-digest] Could not load summaries for repo: ${repoName}`);\n return [];\n }\n\n if (summaries.length === 0) return [];\n\n // Aggregate MCP references and count occurrences\n const usageCounts = new Map<string, number>();\n for (const summary of summaries) {\n const refs = summary.ai.mcpsUsed;\n if (!refs || refs.length === 0) continue;\n\n for (const ref of refs) {\n const name = ref.trim();\n if (!name) continue;\n usageCounts.set(name, (usageCounts.get(name) ?? 0) + 1);\n }\n }\n\n if (usageCounts.size === 0) return [];\n\n const digests: McpDigest[] = [];\n for (const [alias, count] of usageCounts) {\n digests.push({\n alias,\n packageName: null, // Will be enriched during merge if installed\n isPublic: false, // Will be enriched during merge if installed\n usageCount: count,\n });\n }\n\n return digests;\n}\n\n/**\n * Merge installed MCP digests with conversation-used MCP digests.\n *\n * Deduplicates by alias. When an MCP appears in both sets, the installed\n * digest is used (richer metadata — packageName, isPublic) and the usage\n * count from conversations is applied.\n *\n * @param installed - Digests from config scanning (usageCount = 0)\n * @param used - Digests from conversation summaries (usageCount > 0)\n * @returns Merged and deduplicated array\n */\nexport function mergeMcpDigests(\n installed: McpDigest[],\n used: McpDigest[],\n): McpDigest[] {\n const byAlias = new Map<string, McpDigest>();\n\n // Start with installed digests (richer metadata: packageName, isPublic)\n for (const d of installed) {\n byAlias.set(d.alias, { ...d });\n }\n\n // Merge used digests: add usage counts, add new MCPs not in installed set\n for (const d of used) {\n const existing = byAlias.get(d.alias);\n if (existing) {\n existing.usageCount += d.usageCount;\n } else {\n // MCP was used but isn't installed (anymore) — add as-is\n byAlias.set(d.alias, { ...d });\n }\n }\n\n return [...byAlias.values()];\n}\n\n/**\n * Format an array of McpDigest into a string suitable for prompt injection.\n *\n * Sorts by usage count (most-used first). Only includes public MCPs (has\n * packageName). Annotates each MCP with its usage signal and package identity.\n *\n * Returns an empty string if no public digests are available.\n */\nexport function formatMcpDigests(digests: McpDigest[]): string {\n // Only include public MCPs with a known package name\n const publicDigests = digests.filter((d) => d.isPublic && d.packageName);\n if (publicDigests.length === 0) return '';\n\n // Sort: most-used first, then alphabetical for ties\n const sorted = [...publicDigests].sort((a, b) => {\n if (b.usageCount !== a.usageCount) return b.usageCount - a.usageCount;\n return (a.packageName ?? '').localeCompare(b.packageName ?? '');\n });\n\n const lines: string[] = [];\n for (const d of sorted) {\n const usageLabel = d.usageCount > 0\n ? `used in ${d.usageCount} conversation${d.usageCount === 1 ? '' : 's'}`\n : 'installed, not recently used';\n lines.push(`- **${d.packageName}** (${usageLabel}): npm package, alias \"${d.alias}\"`);\n }\n return lines.join('\\n');\n}\n\n// ── Internal helpers ─────────────────────────────────────────────\n\n/**\n * Parse an MCP server config entry into an McpDigest.\n * Returns null if the MCP is not public (command is not npx/uvx).\n */\nfunction parseMcpServerEntry(\n alias: string,\n config: Record<string, unknown>,\n): McpDigest | null {\n const command = typeof config.command === 'string' ? config.command : '';\n if (!PUBLIC_MCP_COMMANDS.has(command)) return null;\n\n const args = Array.isArray(config.args) ? config.args : [];\n const firstArg = typeof args[0] === 'string' ? args[0] : '';\n const packageName = firstArg ? extractPackageName(firstArg) : null;\n\n if (!packageName) return null;\n\n return {\n alias,\n packageName,\n isPublic: true,\n usageCount: 0,\n };\n}\n\n\n/**\n * Parse a SKILL.md file into a SkillDigest.\n * Returns null if the file lacks usable frontmatter.\n */\nfunction parseSkillDigest(dirName: string, content: string): SkillDigest | null {\n const frontmatter = extractFrontmatter(content);\n const name = frontmatter.name || dirName;\n const description = (frontmatter.description || '').slice(0, MAX_DESCRIPTION_LENGTH);\n\n // Extract `provides` from frontmatter (optional, YAML list)\n const provides = Array.isArray(frontmatter.provides)\n ? frontmatter.provides.map(String)\n : [];\n\n // Extract section topics from markdown headers and XML tags\n const coveredTopics = extractTopics(content);\n\n // Skip if we got nothing useful\n if (!description && coveredTopics.length === 0) return null;\n\n return { name, description, provides, coveredTopics, usageCount: 0 };\n}\n\n/**\n * Extract YAML frontmatter from a SKILL.md file.\n * Returns an object with string/array values.\n */\nfunction extractFrontmatter(content: string): Record<string, any> {\n const match = content.match(/^---\\s*\\n([\\s\\S]*?)\\n---/);\n if (!match) return {};\n\n const yaml = match[1];\n const result: Record<string, any> = {};\n\n // Simple line-by-line YAML parser (no dependency needed for frontmatter)\n let currentKey: string | null = null;\n let currentList: string[] | null = null;\n\n for (const line of yaml.split('\\n')) {\n // List item under current key\n const listMatch = line.match(/^\\s+-\\s+(.+)/);\n if (listMatch && currentKey && currentList) {\n currentList.push(listMatch[1].trim().replace(/^[\"']|[\"']$/g, ''));\n continue;\n }\n\n // New key-value pair\n const kvMatch = line.match(/^(\\w[\\w-]*)\\s*:\\s*(.*)/);\n if (kvMatch) {\n // Flush previous list\n if (currentKey && currentList) {\n result[currentKey] = currentList;\n }\n\n currentKey = kvMatch[1];\n const value = kvMatch[2].trim().replace(/^[\"']|[\"']$/g, '');\n\n if (value === '' || value === '|' || value === '>') {\n // Could be a list or multiline value\n currentList = [];\n } else {\n result[currentKey] = value;\n currentKey = null;\n currentList = null;\n }\n }\n }\n\n // Flush final list\n if (currentKey && currentList) {\n result[currentKey] = currentList;\n }\n\n return result;\n}\n\n/**\n * Normalize a skill reference string from conversation data to a skill directory name.\n *\n * Examples:\n * - \".cursor/skills/mermaid-tools/SKILL.md\" → \"mermaid-tools\"\n * - \".cursor/skills/octocode-plan\" → \"octocode-plan\"\n * - \"/Users/me/.cursor/skills/foo-bar/SKILL.md\" → \"foo-bar\"\n * - \".claude/skills/my-skill\" → \"my-skill\"\n * - \"SKILL.md\" → null (too generic)\n */\nfunction normalizeSkillRef(ref: string): string | null {\n // Match .cursor/skills/<name> or .claude/skills/<name> patterns\n const match = ref.match(/\\.(?:cursor|claude)\\/skills\\/([^/\\s]+)/);\n if (match) return match[1];\n\n // If it's just \"SKILL.md\" or doesn't match, skip\n return null;\n}\n\n/**\n * Search known skill directories for a skill by name and parse its SKILL.md.\n * Returns a SkillDigest with usageCount=0, or null if not found.\n */\nfunction findAndParseSkill(\n skillName: string,\n searchDirs: string[],\n): SkillDigest | null {\n for (const dir of searchDirs) {\n const skillMdPath = join(dir, skillName, 'SKILL.md');\n if (!existsSync(skillMdPath)) continue;\n\n try {\n const content = readFileSync(skillMdPath, 'utf-8');\n return parseSkillDigest(skillName, content);\n } catch {\n logger.debug(`[skill-digest] Could not read ${skillMdPath}`);\n }\n }\n return null;\n}\n\n/**\n * Extract topic names from markdown headers (## Title) and XML section tags (<tag>).\n * Returns a deduplicated list of human-readable topic names.\n */\nfunction extractTopics(content: string): string[] {\n const topics = new Set<string>();\n\n // Markdown ## headers → \"Architecture Overview\", \"Coding Patterns\", etc.\n const headerRegex = /^#{1,3}\\s+(.+)/gm;\n let match: RegExpExecArray | null;\n while ((match = headerRegex.exec(content)) !== null) {\n const topic = match[1].trim();\n // Skip generic/meta headers\n if (!topic.match(/^(#|---|\\[|Table of Contents|References|Additional Resources)$/i)) {\n topics.add(topic);\n }\n }\n\n // XML section tags → \"architecture\", \"patterns\", \"conventions\", etc.\n const xmlTagRegex = /^<(\\w[\\w_-]*)>\\s*$/gm;\n while ((match = xmlTagRegex.exec(content)) !== null) {\n const tag = match[1];\n // Convert tag to readable form: quick_reference → \"quick reference\"\n const readable = tag.replace(/[_-]/g, ' ');\n topics.add(readable);\n }\n\n return [...topics].slice(0, 15); // Cap to avoid prompt bloat\n}\n","/**\n * Git worktree utilities for scan isolation.\n *\n * Creates a lightweight checkout of a specific commit so that background\n * scans read from a consistent snapshot while the developer continues\n * working in the main working tree.\n *\n * Workflow: generate skill files INTO the worktree (so the user can observe\n * them in real-time), then merge them back to the main repo when done.\n */\n\nimport { execa } from 'execa';\nimport {\n existsSync,\n statSync,\n rmSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n writeFileSync,\n cpSync,\n} from 'node:fs';\nimport { join, relative } from 'node:path';\nimport { logger } from './logger.js';\nimport {\n GIT_MEDIUM_TIMEOUT_MS,\n GIT_SLOW_TIMEOUT_MS,\n GIT_VERY_SLOW_TIMEOUT_MS,\n STALE_WORKTREE_MS,\n} from './constants.js';\nimport { errorMessage } from './errors.js';\n\n/** Directory name inside .lore/ for the temporary worktree. */\nconst WORKTREE_DIR_NAME = 'worktree';\n\n/** Maximum age (ms) before a stale worktree is auto-cleaned. */\nconst STALE_THRESHOLD_MS = STALE_WORKTREE_MS;\n\n/**\n * Create a temporary git worktree at `<repoPath>/.lore/worktree/`\n * checked out at the given commit SHA.\n *\n * Uses `--detach` so no branch is created — the worktree is a headless\n * checkout of the exact commit.\n *\n * @returns The absolute path to the worktree directory.\n * @throws If git worktree creation fails.\n */\nexport async function createScanWorktree(\n repoPath: string,\n sha: string,\n): Promise<string> {\n const worktreePath = join(repoPath, '.lore', WORKTREE_DIR_NAME);\n\n // Clean up any leftover worktree from a previous run\n await cleanupScanWorktree(repoPath);\n\n // Ensure .lore/ directory exists\n mkdirSync(join(repoPath, '.lore'), { recursive: true });\n\n logger.debug(`[worktree] Creating worktree at ${worktreePath} for ${sha}`);\n\n await execa('git', ['worktree', 'add', '--detach', worktreePath, sha], {\n cwd: repoPath,\n timeout: GIT_VERY_SLOW_TIMEOUT_MS,\n });\n\n logger.debug(`[worktree] Worktree created successfully at ${worktreePath}`);\n return worktreePath;\n}\n\n/**\n * Remove the scan worktree at `<repoPath>/.lore/worktree/`.\n *\n * Runs `git worktree remove --force` and then deletes the directory\n * as a safety net in case git leaves files behind.\n *\n * Silently succeeds if no worktree exists.\n */\nexport async function cleanupScanWorktree(repoPath: string): Promise<void> {\n const worktreePath = join(repoPath, '.lore', WORKTREE_DIR_NAME);\n\n if (!existsSync(worktreePath)) return;\n\n logger.debug(`[worktree] Cleaning up worktree at ${worktreePath}`);\n\n try {\n await execa('git', ['worktree', 'remove', '--force', worktreePath], {\n cwd: repoPath,\n timeout: GIT_SLOW_TIMEOUT_MS,\n });\n } catch (err: unknown) {\n logger.debug(`[worktree] git worktree remove failed: ${errorMessage(err)}`);\n }\n\n // Safety net: remove the directory if git didn't fully clean up\n if (existsSync(worktreePath)) {\n try {\n rmSync(worktreePath, { recursive: true, force: true });\n } catch (err: unknown) {\n logger.debug(`[worktree] rmSync fallback failed: ${errorMessage(err)}`);\n }\n }\n\n // Prune stale worktree references from .git/worktrees/\n try {\n await execa('git', ['worktree', 'prune'], {\n cwd: repoPath,\n timeout: GIT_MEDIUM_TIMEOUT_MS,\n });\n } catch { /* non-critical */ }\n}\n\n/**\n * Remove stale worktrees older than 1 hour.\n *\n * This is a safety net for crashes — if a scan dies without cleanup,\n * the next scan invocation will clean up the orphan.\n */\nexport async function cleanupStaleWorktrees(repoPath: string): Promise<void> {\n const worktreePath = join(repoPath, '.lore', WORKTREE_DIR_NAME);\n\n if (!existsSync(worktreePath)) return;\n\n try {\n const st = statSync(worktreePath);\n const ageMs = Date.now() - st.mtimeMs;\n\n if (ageMs > STALE_THRESHOLD_MS) {\n logger.debug(\n `[worktree] Found stale worktree (${Math.round(ageMs / 60_000)}min old), cleaning up...`,\n );\n await cleanupScanWorktree(repoPath);\n }\n } catch (err: unknown) {\n logger.debug(`[worktree] Stale check failed: ${errorMessage(err)}`);\n }\n}\n\n/**\n * Resolve the HEAD SHA for a given repo.\n *\n * Used as a fallback when the pre-push hook didn't write a SHA\n * to the .scan-trigger file.\n */\nexport async function resolveHeadSha(repoPath: string): Promise<string> {\n const { stdout } = await execa('git', ['rev-parse', 'HEAD'], {\n cwd: repoPath,\n timeout: GIT_MEDIUM_TIMEOUT_MS,\n });\n return stdout.trim();\n}\n\n/**\n * Merge generated skill files from the worktree back to the main repo.\n *\n * Copies all files under `<worktreePath>/.cursor/skills/` to\n * `<mainRepoPath>/.cursor/skills/`, creating directories as needed.\n *\n * @returns The number of files merged.\n */\nexport function mergeSkillsFromWorktree(\n worktreePath: string,\n mainRepoPath: string,\n): number {\n const worktreeSkillsDir = join(worktreePath, '.cursor', 'skills');\n const mainSkillsDir = join(mainRepoPath, '.cursor', 'skills');\n\n if (!existsSync(worktreeSkillsDir)) {\n logger.debug('[worktree] No .cursor/skills/ in worktree, nothing to merge');\n return 0;\n }\n\n let fileCount = 0;\n\n function copyRecursive(srcDir: string, destDir: string): void {\n mkdirSync(destDir, { recursive: true });\n const entries = readdirSync(srcDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = join(srcDir, entry.name);\n const destPath = join(destDir, entry.name);\n\n if (entry.isDirectory()) {\n copyRecursive(srcPath, destPath);\n } else {\n writeFileSync(destPath, readFileSync(srcPath));\n fileCount++;\n }\n }\n }\n\n copyRecursive(worktreeSkillsDir, mainSkillsDir);\n logger.debug(`[worktree] Merged ${fileCount} skill file(s) from worktree to main repo`);\n return fileCount;\n}\n","/**\n * Per-conversation summarizer.\n *\n * Phase 1 of the pipeline: loads full conversations, invokes Claude CLI\n * to summarize each one, validates output with Zod, and persists results.\n */\n\nimport { loadPrompt, splitPrompt, createSystemPromptFile } from '../distiller/prompts.js';\nimport { logger } from '../utils/logger.js';\nimport {\n ensureAnyCli,\n invokeWithFallback as sharedInvokeWithFallback,\n} from '../utils/cli.js';\nimport { shouldIgnoreConversation } from '../utils/conversation-filter.js';\nimport { buildLoreIgnoreMap } from '../utils/lore-filter.js';\nimport { loadAllConversations } from './loader.js';\nimport {\n readSummaryIndex,\n writeSummaryIndex,\n saveConversationSummary,\n reconcileIndex,\n} from './cache.js';\nimport { parseConversationSummary } from './schemas.js';\nimport type {\n FullConversation,\n StoredConversationSummary,\n SummaryIndex,\n SummarizeOptions,\n SummarizeResult,\n ConversationInfo,\n} from './types.js';\nimport {\n MAX_TRANSCRIPT_CHARS,\n SUMMARIZE_TIMEOUT_MS,\n} from '../utils/constants.js';\nimport { errorMessage } from '../utils/errors.js';\n\n// ── Constants ────────────────────────────────────────────────────\n\n/** Minimum messages for a conversation to be worth summarizing */\nconst MIN_MESSAGES = 4;\n\n/** How many conversations to summarize in parallel */\nconst CONCURRENCY = 5;\n\n/** Flush index to disk every N completions (survives interrupts) */\nconst INDEX_FLUSH_INTERVAL = 5;\n\n// CLI resolution, rate-limit detection, and fallback logic are shared via ../utils/cli.ts\n\n// Lore self-conversation filtering is handled by shared ../utils/lore-filter.ts\n\n// ── Public API ───────────────────────────────────────────────────\n\n/**\n * Summarize all unsummarized conversations.\n * Filters out trivial conversations (< MIN_MESSAGES) and processes\n * in parallel batches for speed.\n *\n * Returns counts of summarized, skipped, and failed.\n */\nexport async function summarizeAllConversations(\n options: SummarizeOptions = {},\n): Promise<{ result: SummarizeResult; index: SummaryIndex }> {\n await ensureAnyCli();\n\n const index = readSummaryIndex();\n\n // Recover any orphaned summaries from a previous interrupted run\n const recovered = reconcileIndex(index);\n if (recovered > 0) {\n options.onOutput?.(`[summarize] Recovered ${recovered} summaries from previous interrupted run`);\n }\n\n const conversations = await loadAllConversations({ onOutput: options.onOutput });\n\n options.onOutput?.(`[summarize] Loaded ${conversations.length} conversations after loader filters`);\n\n // Filter by repo if requested\n let convos = conversations;\n if (options.repoFilter) {\n const filter = options.repoFilter.toLowerCase();\n const beforeRepo = convos.length;\n convos = convos.filter(\n (c) =>\n c.repoName?.toLowerCase().includes(filter) ||\n c.repoPath?.toLowerCase().includes(filter),\n );\n options.onOutput?.(`[summarize] Repo filter \"${options.repoFilter}\": ${beforeRepo} -> ${convos.length} (-${beforeRepo - convos.length})`);\n }\n\n // Apply repo selection (from the repo selector) — only summarize\n // conversations belonging to selected repos\n if (options.selectedRepoNames && options.selectedRepoNames.length > 0) {\n const beforeSel = convos.length;\n const selectedSet = new Set(options.selectedRepoNames.map((n) => n.toLowerCase()));\n convos = convos.filter((c) => {\n if (!c.repoName) return false; // skip unclassified conversations\n const lower = c.repoName.toLowerCase();\n // Match full name or just the short name part (after /)\n return selectedSet.has(lower) || selectedSet.has(lower.split('/').pop() ?? lower);\n });\n options.onOutput?.(`[summarize] Selected repos [${options.selectedRepoNames.join(', ')}]: ${beforeSel} -> ${convos.length} (-${beforeSel - convos.length})`);\n }\n\n // Determine which conversations need summarization\n const toSummarize: FullConversation[] = [];\n const conversationInfos: ConversationInfo[] = [];\n let skipped = 0;\n let staleCount = 0;\n let trivial = 0;\n let selfExcluded = 0;\n\n // Second-pass filter for lore process conversations (defense-in-depth;\n // the loader already filters, but summarizeAllConversations may be called\n // with externally-provided conversations in future).\n const ignoreMap = buildLoreIgnoreMap();\n\n for (const convo of convos) {\n // Skip conversations created by lore's own pipeline processes\n if (shouldIgnoreConversation(convo.id, ignoreMap, convo.title, convo.firstUserMessage)) {\n selfExcluded++;\n continue;\n }\n\n const cached = !options.force && index.conversations[convo.id];\n\n if (cached) {\n // Check if conversation has grown since last summarization (stale detection)\n const storedCount = cached.messageCount ?? 0;\n if (storedCount > 0 && convo.messageCount > storedCount) {\n staleCount++;\n toSummarize.push(convo);\n conversationInfos.push({\n id: convo.id,\n title: convo.title,\n source: convo.source,\n messageCount: convo.messageCount,\n status: 'stale',\n });\n continue;\n }\n skipped++;\n conversationInfos.push({\n id: convo.id,\n title: convo.title,\n source: convo.source,\n messageCount: convo.messageCount,\n status: 'cached',\n });\n continue;\n }\n\n // Skip trivial conversations (too few messages to be meaningful)\n if (convo.messageCount < MIN_MESSAGES) {\n trivial++;\n conversationInfos.push({\n id: convo.id,\n title: convo.title,\n source: convo.source,\n messageCount: convo.messageCount,\n status: 'trivial',\n });\n continue;\n }\n\n toSummarize.push(convo);\n conversationInfos.push({\n id: convo.id,\n title: convo.title,\n source: convo.source,\n messageCount: convo.messageCount,\n status: 'new',\n });\n }\n\n // Log conversation titles for visibility\n if (conversationInfos.length > 0) {\n const statusMarkers: Record<ConversationInfo['status'], string> = {\n new: '+',\n stale: '*',\n cached: '~',\n trivial: '-',\n };\n options.onOutput?.(`[summarize] Conversations (${conversationInfos.length}):`);\n for (const info of conversationInfos) {\n const marker = statusMarkers[info.status];\n options.onOutput?.(\n `[summarize] ${marker} ${info.title.slice(0, 70)} (${info.source}, ${info.messageCount} msgs)`,\n );\n }\n }\n\n const filterParts: string[] = [\n `${toSummarize.length} to summarize`,\n ];\n if (staleCount > 0) filterParts.push(`${staleCount} stale`);\n filterParts.push(\n `${skipped} cached`,\n `${trivial} trivial (< ${MIN_MESSAGES} msgs)`,\n );\n if (selfExcluded > 0) filterParts.push(`${selfExcluded} lore-meta excluded`);\n\n options.onOutput?.(`[summarize] ${filterParts.join(', ')}`);\n\n // Fire conversation discovery callback immediately so the sidebar\n // can show conversations before AI summarization starts.\n options.onConversations?.(conversationInfos);\n\n // Discovery-only mode: return after classification without AI summarization.\n // Used by the \"Discover Conversations\" dev mode phase.\n if (options.discoveryOnly) {\n const result: SummarizeResult = {\n summarized: 0, skipped, stale: staleCount, failed: 0,\n reposClustered: 0, conversations: conversationInfos,\n };\n options.onOutput?.(`[summarize] Discovery only — skipping AI summarization.`);\n return { result, index };\n }\n\n if (toSummarize.length === 0) {\n const result: SummarizeResult = { summarized: 0, skipped, stale: 0, failed: 0, reposClustered: 0, conversations: conversationInfos };\n options.onOutput?.(`[summarize] Nothing to summarize.`);\n return { result, index };\n }\n\n let summarized = 0;\n let failed = 0;\n let completed = 0;\n let lastFlush = 0;\n const startTime = Date.now();\n\n // Process in parallel batches using a concurrency limiter\n const queue = [...toSummarize];\n const activeSet = new Set<Promise<void>>();\n\n /** Flush the index every INDEX_FLUSH_INTERVAL completions to survive interrupts */\n const maybeFlushIndex = (): void => {\n if (completed - lastFlush >= INDEX_FLUSH_INTERVAL) {\n writeSummaryIndex(index);\n lastFlush = completed;\n }\n };\n\n const processOne = async (convo: FullConversation, idx: number): Promise<void> => {\n if (options.signal?.aborted) return;\n\n const elapsed = ((Date.now() - startTime) / 1000).toFixed(0);\n const rate = completed > 0 ? (Date.now() - startTime) / completed : 0;\n const eta = completed > 0 ? Math.round((rate * (toSummarize.length - completed)) / 1000) : 0;\n const etaStr = completed > 2 ? ` ~${eta}s remaining` : '';\n\n options.onOutput?.(\n `[summarize] [${idx + 1}/${toSummarize.length}] ${convo.title.slice(0, 50)}... (${elapsed}s${etaStr})`,\n );\n\n try {\n const summary = await summarizeConversation(convo, options);\n saveConversationSummary(summary, index);\n summarized++;\n } catch (err: unknown) {\n const msg = errorMessage(err);\n logger.error(`Failed to summarize ${convo.id}: ${msg}`);\n options.onOutput?.(`[summarize] [${idx + 1}/${toSummarize.length}] Failed: ${msg.slice(0, 200)}`);\n failed++;\n } finally {\n completed++;\n maybeFlushIndex();\n }\n };\n\n // Concurrency-limited execution\n for (let i = 0; i < queue.length; i++) {\n if (options.signal?.aborted) break;\n\n const promise = processOne(queue[i], i).then(() => {\n activeSet.delete(promise);\n });\n activeSet.add(promise);\n\n // When we hit the concurrency limit, wait for one to finish\n if (activeSet.size >= CONCURRENCY) {\n await Promise.race(activeSet);\n }\n }\n\n // Wait for remaining in-flight\n await Promise.all(activeSet);\n\n // Final flush — always persist at the end\n writeSummaryIndex(index);\n\n const totalTime = ((Date.now() - startTime) / 1000).toFixed(1);\n const result: SummarizeResult = {\n summarized,\n skipped,\n stale: staleCount,\n failed,\n reposClustered: 0, // Filled by the caller after Phase 2\n conversations: conversationInfos,\n };\n\n const doneParts = [`${summarized} summarized`];\n if (staleCount > 0) doneParts.push(`${staleCount} stale`);\n doneParts.push(`${skipped} cached`, `${trivial} trivial`, `${failed} failed`);\n options.onOutput?.(\n `[summarize] Done in ${totalTime}s: ${doneParts.join(', ')}`,\n );\n\n return { result, index };\n}\n\n// ── Single conversation summarization ────────────────────────────\n\nasync function summarizeConversation(\n convo: FullConversation,\n options: SummarizeOptions,\n): Promise<StoredConversationSummary> {\n // Build prompt from template\n const template = loadPrompt('summarize-conversation');\n\n // Mechanical pre-extraction: extract tool names, file paths, MCP refs\n const hints = buildMechanicalHints(convo);\n\n // Truncate transcript if too long\n let transcript = convo.transcript;\n if (transcript.length > MAX_TRANSCRIPT_CHARS) {\n transcript =\n transcript.slice(0, MAX_TRANSCRIPT_CHARS / 2) +\n '\\n\\n... [transcript truncated for length] ...\\n\\n' +\n transcript.slice(-MAX_TRANSCRIPT_CHARS / 2);\n }\n\n const prompt = template\n .replace('{{MECHANICAL_HINTS}}', hints)\n .replace('{{SOURCE}}', convo.source)\n .replace('{{TITLE}}', convo.title)\n .replace('{{DATE}}', convo.createdAt ?? 'unknown')\n .replace('{{MESSAGE_COUNT}}', String(convo.messageCount))\n .replace('{{TRANSCRIPT}}', transcript);\n\n // Invoke with fallback chain: claude -> claude (sonnet) -> cursor agent\n let rawOutput = await summarizeWithFallback(prompt, options, convo.id);\n\n // Parse and validate with Zod\n const { data, success, errors } = parseConversationSummary(rawOutput);\n\n if (!success) {\n logger.warn(`Partial parse for ${convo.id}: ${errors.join(', ')}`);\n }\n\n return {\n id: convo.id,\n title: convo.title,\n messageCount: convo.messageCount,\n repoName: convo.repoName,\n repoPath: convo.repoPath,\n source: convo.source,\n conversationDate: convo.createdAt,\n summarizedAt: new Date().toISOString(),\n sourceRef: convo.sourceRef,\n ai: data,\n };\n}\n\n// ── Mechanical pre-extraction ────────────────────────────────────\n\n/**\n * Extract tool names, file paths, and MCP references from the transcript\n * using regex. These are passed to Claude as hints to reduce hallucination.\n */\nfunction buildMechanicalHints(convo: FullConversation): string {\n const parts: string[] = [];\n\n // Tool usage from structured data\n if (convo.toolUsage.length > 0) {\n const toolNames = [...new Set(convo.toolUsage.map((t) => t.name))];\n parts.push(`Tools detected: ${toolNames.join(', ')}`);\n }\n\n // Also scan transcript for tool patterns\n const toolPattern = /\\[tool:\\s*(\\w+)\\]|Tool:\\s*(\\w+)\\(|tool_use.*?\"name\":\\s*\"(\\w+)\"/g;\n const transcriptTools = new Set<string>();\n let match;\n while ((match = toolPattern.exec(convo.transcript)) !== null) {\n transcriptTools.add(match[1] || match[2] || match[3]);\n }\n if (transcriptTools.size > 0) {\n parts.push(`Tools from transcript: ${[...transcriptTools].join(', ')}`);\n }\n\n // MCP references\n const mcpPattern = /mcp[_:](\\w+)|MCP.*?(\\w+).*?server/gi;\n const mcps = new Set<string>();\n while ((match = mcpPattern.exec(convo.transcript)) !== null) {\n const name = (match[1] || match[2] || '').toLowerCase();\n if (name && name.length > 1) mcps.add(name);\n }\n if (mcps.size > 0) {\n parts.push(`MCP servers: ${[...mcps].join(', ')}`);\n }\n\n // File mentions\n if (convo.filesMentioned.length > 0) {\n const files = convo.filesMentioned.slice(0, 20);\n parts.push(`Files mentioned: ${files.join(', ')}`);\n }\n\n // Skill references\n const skillPattern = /\\.cursor\\/skills\\/[^\\s,)]+|SKILL\\.md/g;\n const skills = new Set<string>();\n while ((match = skillPattern.exec(convo.transcript)) !== null) {\n skills.add(match[0]);\n }\n if (skills.size > 0) {\n parts.push(`Skills referenced: ${[...skills].join(', ')}`);\n }\n\n // Doc references\n const docPattern = /(?:README|CONTRIBUTING|CHANGELOG|LICENSE|AGENTS|CLAUDE)\\.md/g;\n const docs = new Set<string>();\n while ((match = docPattern.exec(convo.transcript)) !== null) {\n docs.add(match[0]);\n }\n if (docs.size > 0) {\n parts.push(`Docs referenced: ${[...docs].join(', ')}`);\n }\n\n return parts.length > 0 ? parts.join('\\n') : 'None detected.';\n}\n\n// ── CLI invocation (delegates to shared utils/cli.ts) ────────────\n\n/**\n * Invoke CLI with full fallback chain for a single conversation summarization.\n * Wraps the shared `invokeWithFallback` with summarizer-specific args.\n * Splits prompt into system (behavioural directives) and user (data) portions\n * for better instruction adherence via --append-system-prompt-file.\n */\nasync function summarizeWithFallback(\n prompt: string,\n options: SummarizeOptions,\n convoId: string,\n): Promise<string> {\n const { systemPrompt, userPrompt } = splitPrompt(prompt);\n const { path: systemPromptPath, cleanup } = createSystemPromptFile(systemPrompt);\n\n try {\n return await sharedInvokeWithFallback(\n {\n args: ['-p', '--output-format', 'text', '--max-turns', '1', '--tools', '', '--strict-mcp-config'],\n input: userPrompt,\n timeout: SUMMARIZE_TIMEOUT_MS,\n signal: options.signal,\n systemPromptFile: systemPromptPath,\n phase: 'summarize',\n },\n `summarize:${convoId}`,\n );\n } finally {\n cleanup();\n }\n}\n\n// isLoreConversation removed — replaced by shouldIgnoreConversation()\n// with buildLoreIgnoreMap(), which filters by conversation origin\n// (phase tags + prompt markers) rather than by repo name/path.\n","/**\n * Full conversation loader.\n *\n * Loads complete conversation content from all registered data sources\n * (Cursor SQLite DB, Claude Code JSONL files). Returns FullConversation\n * objects ready for summarization.\n */\n\nimport { discoverKnownRepos } from '../utils/paths.js';\nimport { readClassificationCache } from '../classifier/classifier.js';\nimport { logger } from '../utils/logger.js';\nimport { shouldIgnoreConversation } from '../utils/conversation-filter.js';\nimport { buildLoreIgnoreMap } from '../utils/lore-filter.js';\nimport { getAllSources } from '../data-sources/registry.js';\nimport type { FullConversation } from './types.js';\n\n// ── Public API ───────────────────────────────────────────────────\n\n/** Options for loadAllConversations. */\nexport interface LoadOptions {\n /** Optional callback to surface progress/diagnostic messages. */\n onOutput?: (line: string) => void;\n}\n\n/**\n * Load all conversations from Cursor and Claude Code.\n * Returns FullConversation objects with complete transcripts.\n */\nexport async function loadAllConversations(options?: LoadOptions): Promise<FullConversation[]> {\n const classCache = readClassificationCache();\n const knownRepos = discoverKnownRepos();\n const sources = getAllSources();\n\n options?.onOutput?.(`[loader] Sources: ${sources.map((s) => s.name).join(', ')}`);\n\n // Load from all sources in parallel\n const results = await Promise.all(\n sources.map(async (s) => {\n try {\n const convos = await s.loadFullConversations(classCache, knownRepos);\n return convos;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.error(`[loader] ${s.name} source failed: ${msg}`);\n options?.onOutput?.(`[loader] ⚠ ${s.name} source FAILED: ${msg}`);\n return [];\n }\n }),\n );\n\n // Build source breakdown\n const sourceCounts = sources.map((s, i) => {\n const count = results[i].length;\n return `${count} ${s.name}`;\n });\n\n const conversations = results.flat();\n options?.onOutput?.(`[loader] Raw loaded: ${sourceCounts.join(', ')} (${conversations.length} total)`);\n\n // Filter out lore **process** conversations at the earliest point —\n // prevents pipeline-generated conversations from reaching summarization,\n // clustering, or skill generation. User conversations in the lore repo\n // are intentionally allowed through (origin-based filter, not repo-name).\n const ignoreMap = buildLoreIgnoreMap();\n const before = conversations.length;\n const filtered = conversations.filter(\n (c) => !shouldIgnoreConversation(c.id, ignoreMap, c.title, c.firstUserMessage),\n );\n const removed = before - filtered.length;\n if (removed > 0) {\n logger.debug(`Filtered out ${removed} lore process conversation(s)`);\n options?.onOutput?.(`[loader] Lore process filter: ${before} -> ${filtered.length} (-${removed})`);\n }\n\n logger.debug(`Loaded ${filtered.length} full conversations (${sourceCounts.join(', ')}, ${removed} lore-process-excluded)`);\n return filtered;\n}\n","/**\n * AI-based conversation classifier.\n *\n * Sends lightweight conversation fingerprints + a repo catalog to Claude\n * for batch classification. Results are cached to avoid re-processing.\n */\n\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { getLoreDir } from '../utils/paths.js';\nimport { ensureAnyCli, invokeWithFallbackAndTag } from '../utils/cli.js';\nimport { CLASSIFY_TIMEOUT_MS } from '../utils/constants.js';\nimport { errorMessage } from '../utils/errors.js';\nimport { shouldIgnoreConversation } from '../utils/conversation-filter.js';\nimport { buildLoreIgnoreMap } from '../utils/lore-filter.js';\nimport { loadPrompt, splitPrompt, createSystemPromptFile } from '../distiller/prompts.js';\nimport { countTokens, getMaxInputTokens } from '../distiller/tokenizer.js';\nimport { buildRepoCatalog, formatCatalogForPrompt, type RepoCatalogEntry } from './repo-catalog.js';\nimport {\n extractCursorFingerprints,\n extractClaudeCodeFingerprints,\n type ConversationFingerprint,\n} from './fingerprint.js';\nimport { logger } from '../utils/logger.js';\n\n// ── Classification result types ──────────────────────────────────\n\nexport interface ConversationClassification {\n conversationId: string;\n /** Resolved project string in the format \"path [org/repo]\" or null */\n assignedRepo: string | null;\n confidence: number;\n method: 'git-walkup' | 'path-prefix' | 'ai-fingerprint';\n reason?: string;\n classifiedAt: string;\n}\n\ninterface ClassificationCache {\n version: 1;\n updatedAt: string;\n classifications: ConversationClassification[];\n}\n\ninterface AIClassifyResult {\n id: string;\n repo_index: number | null;\n repo_name: string | null;\n confidence: number;\n reason: string;\n}\n\n// ── Cache management ─────────────────────────────────────────────\n\nconst CACHE_FILE = 'classifications.json';\n\nfunction getCachePath(): string {\n return join(getLoreDir(), CACHE_FILE);\n}\n\nexport function readClassificationCache(): Map<string, ConversationClassification> {\n const cachePath = getCachePath();\n const map = new Map<string, ConversationClassification>();\n\n if (!existsSync(cachePath)) return map;\n\n try {\n const raw = readFileSync(cachePath, 'utf-8');\n const cache: ClassificationCache = JSON.parse(raw);\n if (cache.version !== 1) return map;\n for (const c of cache.classifications) {\n map.set(c.conversationId, c);\n }\n } catch { /* corrupt cache, start fresh */ }\n\n return map;\n}\n\nfunction writeClassificationCache(classifications: ConversationClassification[]): void {\n const loreDir = getLoreDir();\n if (!existsSync(loreDir)) mkdirSync(loreDir, { recursive: true });\n\n const cache: ClassificationCache = {\n version: 1,\n updatedAt: new Date().toISOString(),\n classifications,\n };\n\n writeFileSync(getCachePath(), JSON.stringify(cache, null, 2) + '\\n', 'utf-8');\n}\n\nexport function clearClassificationCache(): void {\n const cachePath = getCachePath();\n if (existsSync(cachePath)) {\n writeFileSync(cachePath, JSON.stringify({ version: 1, updatedAt: new Date().toISOString(), classifications: [] }, null, 2) + '\\n', 'utf-8');\n }\n}\n\n// ── Main classify function ───────────────────────────────────────\n\nexport interface ClassifyOptions {\n /** Callback for streaming output */\n onOutput?: (line: string) => void;\n /** AbortSignal for cancellation */\n signal?: AbortSignal;\n /** Confidence threshold below which assignments are discarded (default: 0.6) */\n confidenceThreshold?: number;\n /** Port the Lore server is listening on (for progress reporting, default: 3333) */\n port?: number;\n}\n\n/** Number of parallel Claude agents for classification */\nconst CONCURRENCY = 3;\n\n/**\n * Run tasks with a concurrency pool.\n */\nasync function runWithConcurrency<T>(\n tasks: (() => Promise<T>)[],\n limit: number,\n): Promise<T[]> {\n const results: T[] = [];\n let idx = 0;\n const workers = Array.from({ length: Math.min(limit, tasks.length) }, async () => {\n while (idx < tasks.length) {\n const i = idx++;\n results[i] = await tasks[i]();\n }\n });\n await Promise.all(workers);\n return results;\n}\n\n/**\n * Classify unmatched conversations into repos using AI fingerprinting.\n *\n * Only processes conversations not already in the cache.\n * Returns the full map of all classifications (cached + new).\n */\nexport async function classifyConversations(\n unmatchedCursorIds: string[],\n unmatchedClaudeIds: string[],\n options: ClassifyOptions = {},\n): Promise<Map<string, ConversationClassification>> {\n const threshold = options.confidenceThreshold ?? 0.6;\n const port = options.port ?? 3333;\n const cache = readClassificationCache();\n\n // Filter out already-classified conversations\n const newCursorIds = unmatchedCursorIds.filter((id) => !cache.has(id));\n const newClaudeIds = unmatchedClaudeIds.filter((id) => !cache.has(id));\n\n const totalNew = newCursorIds.length + newClaudeIds.length;\n if (totalNew === 0) {\n options.onOutput?.(`[classifier] All ${cache.size} conversations already classified (cached)`);\n return cache;\n }\n\n options.onOutput?.(`[classifier] ${totalNew} new conversations to classify (${cache.size} cached)`);\n\n // Extract fingerprints\n options.onOutput?.(`[classifier] Extracting fingerprints...`);\n const cursorFingerprints = await extractCursorFingerprints(newCursorIds);\n const claudeFingerprints = extractClaudeCodeFingerprints(newClaudeIds);\n const rawFingerprints = [...cursorFingerprints, ...claudeFingerprints];\n\n // Filter out lore **process** conversations before sending to Claude (token savings).\n // Uses origin-based detection (phase tags + prompt markers) instead of repo-name matching.\n const ignoreMap = buildLoreIgnoreMap();\n const allFingerprints = rawFingerprints.filter(\n (fp) => !shouldIgnoreConversation(fp.id, ignoreMap, fp.title, fp.firstMessage),\n );\n const loreExcluded = rawFingerprints.length - allFingerprints.length;\n if (loreExcluded > 0) {\n options.onOutput?.(`[classifier] Excluded ${loreExcluded} lore process conversation(s)`);\n }\n\n if (allFingerprints.length === 0) {\n options.onOutput?.(`[classifier] No fingerprints extracted`);\n return cache;\n }\n\n // Build repo catalog\n const catalog = buildRepoCatalog();\n options.onOutput?.(`[classifier] ${catalog.length} repos in catalog, ${allFingerprints.length} fingerprints`);\n\n // Dynamic batch sizing: estimate optimal batch size from context window.\n // Guardrail: minimum batch size of 10 to preserve cross-conversation context.\n const maxInput = getMaxInputTokens();\n const catalogStr = formatCatalogForPrompt(catalog);\n const catalogTokens = await countTokens(catalogStr);\n const templateTokens = await countTokens(loadPrompt('classify-conversations'));\n const overhead = templateTokens + catalogTokens + 2000; // 2k buffer for placeholders/formatting\n const availableForFingerprints = maxInput - overhead;\n\n // Estimate average fingerprint size from a sample\n const sampleSize = Math.min(5, allFingerprints.length);\n const sampleStr = formatFingerprints(allFingerprints.slice(0, sampleSize));\n const sampleTokens = await countTokens(sampleStr);\n const avgFpTokens = Math.max(1, Math.ceil(sampleTokens / sampleSize));\n\n const optimalBatchSize = Math.floor(availableForFingerprints / avgFpTokens);\n const BATCH_SIZE = Math.max(10, Math.min(optimalBatchSize, 30)); // Clamp 10-30\n\n options.onOutput?.(`[classifier] Dynamic batch size: ${BATCH_SIZE} (avg fp: ~${avgFpTokens} tokens, available: ${availableForFingerprints.toLocaleString()} tokens)`);\n\n const batches: ConversationFingerprint[][] = [];\n for (let i = 0; i < allFingerprints.length; i += BATCH_SIZE) {\n batches.push(allFingerprints.slice(i, i + BATCH_SIZE));\n }\n\n options.onOutput?.(`[classifier] ${batches.length} batches, running ${Math.min(CONCURRENCY, batches.length)} parallel agents`);\n\n const newClassifications: ConversationClassification[] = [];\n\n // Build a task per batch\n const batchTasks = batches.map((batch, batchIdx) => async () => {\n options.onOutput?.(\n `[classifier] [batch ${batchIdx}] Starting ${batch.length} convs (${batchIdx + 1}/${batches.length})`,\n );\n\n try {\n const results = await invokeClassifierWithRetry(batch, catalog, {\n ...options,\n port,\n batchId: batchIdx,\n batchTotal: batch.length,\n });\n\n for (const result of results) {\n const matchedRepo = result.repo_index\n ? catalog.find((c) => c.index === result.repo_index)\n : null;\n\n const classification: ConversationClassification = {\n conversationId: result.id,\n assignedRepo:\n result.confidence >= threshold && matchedRepo\n ? `${matchedRepo.path} [${matchedRepo.name}]`\n : null,\n confidence: result.confidence,\n method: 'ai-fingerprint',\n reason: result.reason,\n classifiedAt: new Date().toISOString(),\n };\n\n newClassifications.push(classification);\n cache.set(result.id, classification);\n }\n\n // Also mark conversations that had fingerprints but weren't returned by AI\n for (const fp of batch) {\n if (!cache.has(fp.id)) {\n const unmatched: ConversationClassification = {\n conversationId: fp.id,\n assignedRepo: null,\n confidence: 0,\n method: 'ai-fingerprint',\n reason: 'No classification returned by AI',\n classifiedAt: new Date().toISOString(),\n };\n newClassifications.push(unmatched);\n cache.set(fp.id, unmatched);\n }\n }\n\n options.onOutput?.(\n `[classifier] Batch ${batchIdx + 1}/${batches.length} complete — ${results.length} results`,\n );\n } catch (err) {\n options.onOutput?.(`[classifier] Batch ${batchIdx + 1} failed: ${err}`);\n logger.error('Classification batch failed:', err);\n // Mark all in this batch as unclassified so they retry next time\n }\n\n // Save progress after each batch (in case later ones fail)\n if (newClassifications.length > 0) {\n writeClassificationCache([...cache.values()]);\n }\n });\n\n // Run batches with concurrency pool\n await runWithConcurrency(batchTasks, CONCURRENCY);\n\n // Persist updated cache (final save)\n if (newClassifications.length > 0) {\n const allClassifications = [...cache.values()];\n writeClassificationCache(allClassifications);\n options.onOutput?.(\n `[classifier] ${newClassifications.filter((c) => c.assignedRepo).length} assigned, ` +\n `${newClassifications.filter((c) => !c.assignedRepo).length} unassigned, ` +\n `cache updated (${cache.size} total)`,\n );\n }\n\n return cache;\n}\n\n// ── Internal options (extends ClassifyOptions with per-batch context) ──\n\ninterface InvokeOptions extends ClassifyOptions {\n /** Batch index (0-based) for progress reporting */\n batchId?: number;\n /** Number of conversations in this batch */\n batchTotal?: number;\n}\n\n// ── Retry wrapper: on timeout, split batch in half and retry each half ──\n\nconst MAX_RETRIES = 2;\n\nasync function invokeClassifierWithRetry(\n fingerprints: ConversationFingerprint[],\n catalog: RepoCatalogEntry[],\n options: InvokeOptions,\n attempt = 1,\n): Promise<AIClassifyResult[]> {\n try {\n return await invokeClassifier(fingerprints, catalog, options);\n } catch (err: unknown) {\n const isTimeout = errorMessage(err).includes('timed out') || (err as { timedOut?: boolean }).timedOut;\n\n if (isTimeout && fingerprints.length > 1 && attempt <= MAX_RETRIES) {\n // Split in half and retry each sub-batch\n const mid = Math.ceil(fingerprints.length / 2);\n const left = fingerprints.slice(0, mid);\n const right = fingerprints.slice(mid);\n\n options.onOutput?.(\n `[classifier] Timeout on ${fingerprints.length} items — splitting into ${left.length} + ${right.length} (retry ${attempt}/${MAX_RETRIES})`,\n );\n\n const leftResults = await invokeClassifierWithRetry(left, catalog, { ...options, batchTotal: left.length }, attempt + 1);\n const rightResults = await invokeClassifierWithRetry(right, catalog, { ...options, batchTotal: right.length }, attempt + 1);\n return [...leftResults, ...rightResults];\n }\n\n // Not a timeout or can't split further — rethrow\n throw err;\n }\n}\n\n// ── Claude CLI invocation ────────────────────────────────────────\n\n// CLASSIFY_TIMEOUT_MS imported from constants.ts\n\nasync function invokeClassifier(\n fingerprints: ConversationFingerprint[],\n catalog: RepoCatalogEntry[],\n options: InvokeOptions,\n): Promise<AIClassifyResult[]> {\n const batchId = options.batchId ?? 0;\n const batchTotal = options.batchTotal ?? fingerprints.length;\n const port = options.port ?? 3333;\n\n // Build prompt with placeholder substitution\n const template = loadPrompt('classify-conversations');\n let catalogStr = formatCatalogForPrompt(catalog);\n let fingerprintsStr = formatFingerprints(fingerprints);\n\n // ── Token validation: ensure prompt fits context window ──\n // Guardrail: NEVER truncate catalog randomly. If over budget, compress\n // fingerprints first (shorter file lists). Only truncate catalog as last resort\n // using activity score ordering (most relevant repos kept).\n const maxInput = getMaxInputTokens();\n const templateTokens = await countTokens(template);\n const catalogTokens = await countTokens(catalogStr);\n const fpTokens = await countTokens(fingerprintsStr);\n const totalTokens = templateTokens + catalogTokens + fpTokens;\n\n if (totalTokens > maxInput) {\n options.onOutput?.(\n `[classifier] [batch ${batchId}] Prompt exceeds context (${totalTokens.toLocaleString()} > ${maxInput.toLocaleString()}). Compressing fingerprints...`,\n );\n // Strategy 1: Compress fingerprints by removing file mentions (cheapest info)\n const compressedFps = fingerprints.map((fp) => ({\n ...fp,\n fileMentions: fp.fileMentions.slice(0, 5), // keep only top 5 file mentions\n firstMessage: fp.firstMessage ? fp.firstMessage.slice(0, 200) : fp.firstMessage,\n }));\n fingerprintsStr = formatFingerprints(compressedFps);\n\n const newTotal = templateTokens + catalogTokens + await countTokens(fingerprintsStr);\n if (newTotal > maxInput) {\n // Strategy 2: Truncate catalog keeping first N repos (catalog is already sorted by relevance)\n const maxCatalogTokens = maxInput - templateTokens - await countTokens(fingerprintsStr) - 1000;\n if (maxCatalogTokens > 0) {\n const catalogLines = catalogStr.split('\\n');\n const truncatedLines: string[] = [];\n let runningTokens = 0;\n for (const line of catalogLines) {\n const lineTokens = await countTokens(line);\n if (runningTokens + lineTokens > maxCatalogTokens) break;\n truncatedLines.push(line);\n runningTokens += lineTokens;\n }\n catalogStr = truncatedLines.join('\\n');\n options.onOutput?.(\n `[classifier] [batch ${batchId}] Catalog truncated to fit (${truncatedLines.length}/${catalogLines.length} lines)`,\n );\n }\n }\n }\n\n const fullPrompt = template\n .replace('{{REPO_CATALOG}}', catalogStr)\n .replace('{{CONVERSATION_FINGERPRINTS}}', fingerprintsStr)\n .replace(/\\{\\{BATCH_ID\\}\\}/g, String(batchId))\n .replace(/\\{\\{BATCH_TOTAL\\}\\}/g, String(batchTotal))\n .replace(/\\{\\{PORT\\}\\}/g, String(port));\n\n // Split prompt into system (behavioural) and user (data) portions\n const { systemPrompt, userPrompt } = splitPrompt(fullPrompt);\n const { path: systemPromptPath, cleanup: cleanupSystemPrompt } = createSystemPromptFile(systemPrompt);\n\n options.onOutput?.(`[classifier] [batch ${batchId}] Invoking CLI for ${fingerprints.length} conversations (tools enabled)...`);\n\n // Ensure CLI is available (supports claude -> agent fallback)\n await ensureAnyCli();\n\n try {\n // Invoke with full fallback chain for rate-limit resilience\n const stdout = await invokeWithFallbackAndTag(\n {\n args: [\n '-p',\n '--output-format', 'text',\n '--max-turns', '10',\n '--allowedTools', 'Bash',\n '--strict-mcp-config', // ignore user's global MCP servers\n ],\n input: userPrompt,\n timeout: CLASSIFY_TIMEOUT_MS,\n signal: options.signal,\n systemPromptFile: systemPromptPath,\n },\n `classifier:batch-${batchId}`,\n 'classify',\n );\n\n // Extract JSON array from response — Claude may include preamble text,\n // markdown fences, or trailing commentary around the actual JSON.\n const jsonArr = extractJsonArray(stdout);\n if (!jsonArr) {\n logger.error(`[batch ${batchId}] Failed to extract JSON array from classifier response:`, stdout.slice(0, 500));\n throw new Error(\n `[batch ${batchId}] Failed to parse classifier response: no valid JSON array found in output`,\n );\n }\n return jsonArr as AIClassifyResult[];\n } finally {\n cleanupSystemPrompt();\n }\n}\n\n/**\n * Robustly extract a JSON array from Claude's response.\n * Handles: preamble text, markdown fences, trailing text, and truncated output.\n */\nfunction extractJsonArray(raw: string): unknown[] | null {\n // Strip markdown code fences\n let text = raw.replace(/```json\\n?/g, '').replace(/```\\n?/g, '').trim();\n\n // Try direct parse first (best case)\n try {\n const parsed = JSON.parse(text);\n if (Array.isArray(parsed)) return parsed;\n } catch { /* continue to extraction strategies */ }\n\n // Find the first '[' and try to find the matching ']'\n const start = text.indexOf('[');\n if (start === -1) return null;\n\n // Try from the last ']' backwards\n for (let end = text.lastIndexOf(']'); end > start; end = text.lastIndexOf(']', end - 1)) {\n const candidate = text.slice(start, end + 1);\n try {\n const parsed = JSON.parse(candidate);\n if (Array.isArray(parsed)) return parsed;\n } catch { /* try next ']' */ }\n }\n\n // JSON may be truncated — try to repair by closing open structures\n const truncated = text.slice(start);\n // Close any unclosed braces/brackets and strip trailing partial entries\n const repaired = repairTruncatedJson(truncated);\n if (repaired) {\n try {\n const parsed = JSON.parse(repaired);\n if (Array.isArray(parsed)) return parsed;\n } catch { /* give up */ }\n }\n\n return null;\n}\n\n/**\n * Attempt to repair a truncated JSON array by removing the last partial entry\n * and closing brackets.\n */\nfunction repairTruncatedJson(text: string): string | null {\n // Find the last complete object (ends with '}')\n const lastCompleteObj = text.lastIndexOf('}');\n if (lastCompleteObj === -1) return null;\n\n // Take everything up to and including that '}'\n let repaired = text.slice(0, lastCompleteObj + 1);\n\n // Remove any trailing comma\n repaired = repaired.replace(/,\\s*$/, '');\n\n // Close the array\n repaired += ']';\n\n return repaired;\n}\n\nfunction formatFingerprints(fingerprints: ConversationFingerprint[]): string {\n return fingerprints\n .map((fp) => {\n const parts: string[] = [`<conv id=\"${fp.id}\">`];\n if (fp.title) parts.push(` <title>${fp.title}</title>`);\n if (fp.firstMessage) parts.push(` <first_message>${fp.firstMessage}</first_message>`);\n if (fp.fileMentions.length > 0) {\n parts.push(` <file_mentions>${fp.fileMentions.join(', ')}</file_mentions>`);\n }\n if (fp.rawProjectPath) parts.push(` <raw_path>${fp.rawProjectPath}</raw_path>`);\n parts.push(`</conv>`);\n return parts.join('\\n');\n })\n .join('\\n');\n}\n","/**\n * Fingerprint extraction for conversation classification.\n *\n * Extracts a lightweight ~300-token \"fingerprint\" from each conversation\n * that is sufficient for AI-based repo assignment without reading the full content.\n */\n\nimport { readFileSync, readdirSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { openCursorDb, queryRows } from '../ui/api/db.js';\nimport { getClaudeCodeBaseDir, decodeClaudeProjectPath } from '../utils/paths.js';\n\nexport interface ConversationFingerprint {\n id: string;\n source: 'cursor' | 'claude-code';\n title: string;\n /** First user message, truncated to ~800 chars (~200 tokens) */\n firstMessage: string;\n /** File paths mentioned in the first 3 messages (extracted via regex) */\n fileMentions: string[];\n /** The raw project path (for Claude Code) or undefined (for unmatched Cursor) */\n rawProjectPath?: string;\n}\n\n// ── Regex to extract file paths from conversation text ──────────\n\nconst FILE_PATH_RE = /(?:^|\\s|['\"`])((?:\\/[\\w.@-]+){2,}(?:\\.\\w{1,10})?|(?:[\\w@-]+\\/){1,}[\\w.-]+\\.\\w{1,10})/gm;\n\nfunction extractFilePaths(text: string): string[] {\n const matches = new Set<string>();\n let m: RegExpExecArray | null;\n FILE_PATH_RE.lastIndex = 0;\n while ((m = FILE_PATH_RE.exec(text)) !== null) {\n const path = m[1].trim();\n // Filter out common false positives\n if (\n !path.startsWith('http') &&\n !path.startsWith('file:') &&\n !path.includes('node_modules') &&\n path.length > 5\n ) {\n matches.add(path);\n }\n }\n return [...matches].slice(0, 20);\n}\n\n// ── Cursor fingerprint extraction ──────────────────────────────\n\nexport async function extractCursorFingerprints(\n unmatchedIds: string[],\n): Promise<ConversationFingerprint[]> {\n if (unmatchedIds.length === 0) return [];\n\n const db = await openCursorDb();\n if (!db) return [];\n\n const fingerprints: ConversationFingerprint[] = [];\n\n try {\n for (const fullId of unmatchedIds) {\n const composerId = fullId.replace('cursor-', '');\n\n const rows = await queryRows<{ value: string }>(\n db,\n 'SELECT value FROM cursorDiskKV WHERE key = ?',\n [`composerData:${composerId}`],\n );\n if (rows.length === 0) continue;\n\n try {\n const composer = JSON.parse(rows[0].value);\n const headers = composer.fullConversationHeadersOnly ?? [];\n if (headers.length === 0) continue;\n\n const title = composer.name || '';\n let firstMessage = '';\n const allText: string[] = [];\n\n // Read first 3 bubbles to extract file paths and first user message\n const bubblesToRead = headers.slice(0, 6); // up to 6 headers = ~3 user + 3 assistant\n let userMsgCount = 0;\n\n for (const header of bubblesToRead) {\n if (userMsgCount >= 3) break;\n const key = `bubbleId:${composerId}:${header.bubbleId}`;\n const bubbleRows = await queryRows<{ value: string }>(\n db,\n 'SELECT value FROM cursorDiskKV WHERE key = ?',\n [key],\n );\n if (bubbleRows.length === 0) continue;\n\n try {\n const bubble = JSON.parse(bubbleRows[0].value);\n const text = bubble.text ?? '';\n if (text.trim()) {\n allText.push(text);\n if (bubble.type === 1) {\n userMsgCount++;\n if (!firstMessage) {\n firstMessage = text.slice(0, 800);\n }\n }\n }\n } catch { /* skip */ }\n }\n\n const fileMentions = extractFilePaths(allText.join('\\n'));\n\n fingerprints.push({\n id: fullId,\n source: 'cursor',\n title,\n firstMessage,\n fileMentions,\n });\n } catch { /* skip */ }\n }\n\n db.close();\n } catch {\n db.close();\n }\n\n return fingerprints;\n}\n\n// ── Claude Code fingerprint extraction ─────────────────────────\n\nexport function extractClaudeCodeFingerprints(\n unmatchedIds: string[],\n): ConversationFingerprint[] {\n if (unmatchedIds.length === 0) return [];\n\n const baseDir = getClaudeCodeBaseDir();\n if (!existsSync(baseDir)) return [];\n\n const fingerprints: ConversationFingerprint[] = [];\n\n // Build a map of sessionId → { projDir, dirName }\n const sessionMap = new Map<string, { fullPath: string; dirName: string }>();\n try {\n const projectDirs = readdirSync(baseDir, { withFileTypes: true }).filter((d) =>\n d.isDirectory(),\n );\n for (const projDir of projectDirs) {\n const projFullPath = join(baseDir, projDir.name);\n const jsonlFiles = readdirSync(projFullPath).filter((f) => f.endsWith('.jsonl'));\n for (const file of jsonlFiles) {\n const sessionId = file.replace('.jsonl', '');\n sessionMap.set(`claude-${sessionId}`, {\n fullPath: join(projFullPath, file),\n dirName: projDir.name,\n });\n }\n }\n } catch { /* ignore */ }\n\n for (const id of unmatchedIds) {\n const info = sessionMap.get(id);\n if (!info) continue;\n\n try {\n const content = readFileSync(info.fullPath, 'utf-8');\n const lines = content.split('\\n').filter((l) => l.trim());\n\n let title = '';\n let firstMessage = '';\n const allText: string[] = [];\n let userMsgCount = 0;\n\n for (const line of lines) {\n if (userMsgCount >= 3 && title) break;\n\n try {\n const entry = JSON.parse(line);\n\n if (entry.type === 'summary' && entry.summary) {\n title = entry.summary;\n }\n\n if (entry.type === 'user' && userMsgCount < 3) {\n userMsgCount++;\n const msg = entry.message?.content;\n let text = '';\n if (typeof msg === 'string') {\n text = msg;\n } else if (Array.isArray(msg)) {\n text = msg\n .filter((b: any) => b.type === 'text')\n .map((b: any) => b.text)\n .join('\\n');\n }\n if (text.trim()) {\n allText.push(text);\n if (!firstMessage) {\n firstMessage = text.slice(0, 800);\n }\n }\n }\n\n if (entry.type === 'assistant') {\n const blocks = entry.message?.content;\n if (Array.isArray(blocks)) {\n for (const block of blocks) {\n if (block.type === 'text') allText.push(block.text);\n }\n }\n }\n } catch { /* skip */ }\n }\n\n const rawProjectPath = decodeClaudeProjectPath(info.dirName);\n const fileMentions = extractFilePaths(allText.join('\\n'));\n\n fingerprints.push({\n id,\n source: 'claude-code',\n title: title || '',\n firstMessage,\n fileMentions,\n rawProjectPath,\n });\n } catch { /* skip */ }\n }\n\n return fingerprints;\n}\n","/**\n * Re-exports from the shared Cursor DB module.\n *\n * Previously this file contained a duplicate openCursorDb() implementation.\n * Now it delegates to the single source of truth in data-sources/cursor-db.ts,\n * which supports both better-sqlite3 (CLI) and @vscode/sqlite3 (extension).\n */\n\nexport { openCursorDb, queryRows } from '../../data-sources/cursor-db.js';\n","/**\n * Per-repo clustering and conflict resolution.\n *\n * Phase 2 of the pipeline: collects all conversation summaries for each repo,\n * invokes Claude to cluster themes and resolve conflicts, then saves a\n * per-repo markdown knowledge document.\n */\n\nimport { loadPrompt } from '../distiller/prompts.js';\nimport { logger } from '../utils/logger.js';\nimport { ensureAnyCli, invokeWithFallback } from '../utils/cli.js';\nimport {\n getReposWithSummaries,\n loadSummariesForRepo,\n saveRepoCluster,\n repoNameToSlug,\n} from './cache.js';\nimport { parseClusterOutput, type ClusterOutput } from './schemas.js';\nimport type { StoredConversationSummary, SummarizeOptions } from './types.js';\nimport { CLUSTER_TIMEOUT_MS } from '../utils/constants.js';\nimport { errorMessage } from '../utils/errors.js';\n\n// ── Constants ────────────────────────────────────────────────────\n\n// ── Public API ───────────────────────────────────────────────────\n\n/**\n * Cluster conversation summaries for all repos that have summaries.\n * Returns the number of repos clustered.\n *\n * @param options.selectedRepoNames When provided, only cluster repos whose\n * name matches one of these entries. Used by the repo selector to limit\n * expensive clustering to active repos.\n */\nexport async function clusterAllRepos(\n options: SummarizeOptions = {},\n): Promise<number> {\n const repos = getReposWithSummaries();\n\n // Filter repos if requested\n let filteredRepos = repos;\n if (options.repoFilter) {\n const filter = options.repoFilter.toLowerCase();\n filteredRepos = repos.filter((r) => r.toLowerCase().includes(filter));\n }\n\n // Apply repo selection (from the repo selector)\n if (options.selectedRepoNames && options.selectedRepoNames.length > 0) {\n const selectedSet = new Set(options.selectedRepoNames.map((n) => n.toLowerCase()));\n filteredRepos = filteredRepos.filter((r) => {\n const lower = r.toLowerCase();\n // Match full name or just the short name part (after /)\n return selectedSet.has(lower) || selectedSet.has(lower.split('/').pop() ?? lower);\n });\n }\n\n options.onOutput?.(\n `[cluster] ${filteredRepos.length} repos to cluster`,\n );\n\n let clustered = 0;\n\n for (let i = 0; i < filteredRepos.length; i++) {\n if (options.signal?.aborted) break;\n\n const repoName = filteredRepos[i];\n options.onOutput?.(\n `[cluster] [${i + 1}/${filteredRepos.length}] ${repoName}`,\n );\n\n try {\n await clusterRepo(repoName, options);\n clustered++;\n } catch (err: unknown) {\n const msg = errorMessage(err);\n logger.error(`Failed to cluster ${repoName}: ${msg}`);\n options.onOutput?.(`[cluster] Failed: ${msg}`);\n }\n }\n\n options.onOutput?.(\n `[cluster] Done: ${clustered}/${filteredRepos.length} repos clustered`,\n );\n\n return clustered;\n}\n\n// ── Single repo clustering ───────────────────────────────────────\n\nasync function clusterRepo(\n repoName: string,\n options: SummarizeOptions,\n): Promise<void> {\n const summaries = loadSummariesForRepo(repoName);\n\n if (summaries.length === 0) {\n options.onOutput?.(`[cluster] ${repoName}: no summaries, skipping`);\n return;\n }\n\n // If only 1 conversation, still cluster to produce the standardized format\n options.onOutput?.(\n `[cluster] ${repoName}: clustering ${summaries.length} conversation(s)...`,\n );\n\n // Log conversation titles for visibility\n for (const s of summaries) {\n const title = s.title ?? s.id;\n options.onOutput?.(`[cluster] - ${title.slice(0, 70)}`);\n }\n\n // Build the prompt\n const template = loadPrompt('cluster-repo-summaries');\n const summariesXml = formatSummariesForPrompt(summaries);\n const dateRange = getDateRange(summaries);\n\n const prompt = template\n .replace(/\\{\\{REPO_NAME\\}\\}/g, repoName)\n .replace('{{CONVERSATION_COUNT}}', String(summaries.length))\n .replace('{{DATE_RANGE}}', dateRange)\n .replace('{{SUMMARIES}}', summariesXml);\n\n // Ensure CLI is available\n await ensureAnyCli();\n\n // Invoke with full fallback chain — text mode, no tools, single turn\n const rawOutput = await invokeWithFallback(\n {\n args: ['-p', '--output-format', 'text', '--max-turns', '1', '--tools', '', '--strict-mcp-config'],\n input: prompt,\n timeout: CLUSTER_TIMEOUT_MS,\n signal: options.signal,\n phase: 'cluster',\n },\n `cluster:${repoName}`,\n );\n\n // Parse with Zod\n const { data, success, errors } = parseClusterOutput(rawOutput);\n\n if (!success) {\n logger.warn(`Partial cluster parse for ${repoName}: ${errors.join(', ')}`);\n }\n\n // Convert structured output to markdown and save\n const markdown = clusterToMarkdown(repoName, data, summaries.length);\n const slug = repoNameToSlug(repoName);\n saveRepoCluster(slug, markdown);\n\n options.onOutput?.(\n `[cluster] ${repoName}: saved (${data.themes.length} themes, ${data.conflicts.length} conflicts)`,\n );\n}\n\n// ── Formatting helpers ───────────────────────────────────────────\n\nfunction formatSummariesForPrompt(summaries: StoredConversationSummary[]): string {\n return summaries\n .sort((a, b) => {\n const dateA = a.conversationDate ?? '';\n const dateB = b.conversationDate ?? '';\n return dateA.localeCompare(dateB);\n })\n .map((s, i) => {\n const parts: string[] = [\n `<conversation index=\"${i + 1}\" id=\"${s.id}\" source=\"${s.source}\" date=\"${s.conversationDate ?? 'unknown'}\">`,\n ` <summary>${s.ai.summary}</summary>`,\n ];\n\n if (s.ai.decisions.length > 0) {\n parts.push(' <decisions>');\n for (const d of s.ai.decisions) {\n parts.push(` - ${d.decision} (context: ${d.context})`);\n }\n parts.push(' </decisions>');\n }\n\n if (s.ai.patterns.length > 0) {\n parts.push(' <patterns>');\n for (const p of s.ai.patterns) {\n parts.push(` - ${p.pattern}${p.example ? ` (e.g. ${p.example})` : ''}`);\n }\n parts.push(' </patterns>');\n }\n\n if (s.ai.toolsUsed.length > 0) {\n parts.push(` <tools>${s.ai.toolsUsed.join(', ')}</tools>`);\n }\n\n if (s.ai.mcpsUsed.length > 0) {\n parts.push(` <mcps>${s.ai.mcpsUsed.join(', ')}</mcps>`);\n }\n\n if (s.ai.skillsReferenced.length > 0) {\n parts.push(` <skills>${s.ai.skillsReferenced.join(', ')}</skills>`);\n }\n\n if (s.ai.filesModified.length > 0) {\n parts.push(` <files>${s.ai.filesModified.slice(0, 15).join(', ')}</files>`);\n }\n\n if (s.ai.importantNotes.length > 0) {\n parts.push(' <notes>');\n for (const n of s.ai.importantNotes) {\n parts.push(` - ${n}`);\n }\n parts.push(' </notes>');\n }\n\n parts.push(` <outcome>${s.ai.outcome}</outcome>`);\n parts.push('</conversation>');\n\n return parts.join('\\n');\n })\n .join('\\n\\n');\n}\n\nfunction getDateRange(summaries: StoredConversationSummary[]): string {\n const dates = summaries\n .map((s) => s.conversationDate)\n .filter((d): d is string => d !== null)\n .sort();\n\n if (dates.length === 0) return 'unknown';\n if (dates.length === 1) return dates[0];\n return `${dates[0]} to ${dates[dates.length - 1]}`;\n}\n\n// ── Markdown generation ──────────────────────────────────────────\n\nfunction clusterToMarkdown(\n repoName: string,\n data: ClusterOutput,\n conversationCount: number,\n): string {\n const lines: string[] = [\n `# Clustered Knowledge: ${repoName}`,\n '',\n `> Generated from ${conversationCount} conversation(s) on ${new Date().toISOString().split('T')[0]}`,\n '',\n ];\n\n // Themes\n if (data.themes.length > 0) {\n lines.push('## Themes', '');\n for (const theme of data.themes) {\n lines.push(`### ${theme.name}`, '');\n if (theme.insights.length > 0) {\n for (const insight of theme.insights) {\n lines.push(`- ${insight}`);\n }\n lines.push('');\n }\n if (theme.patterns.length > 0) {\n lines.push('**Patterns:**', '');\n for (const p of theme.patterns) {\n lines.push(`- ${p.pattern}${p.example ? ` — e.g. \\`${p.example}\\`` : ''}`);\n }\n lines.push('');\n }\n if (theme.decisions.length > 0) {\n lines.push('**Decisions:**', '');\n for (const d of theme.decisions) {\n lines.push(`- **${d.decision}** — ${d.context}${d.date ? ` (${d.date})` : ''}`);\n }\n lines.push('');\n }\n }\n }\n\n // Active Decisions\n if (data.activeDecisions.length > 0) {\n lines.push('## Active Decisions', '');\n for (const d of data.activeDecisions) {\n lines.push(`- **${d.decision}** — ${d.context}${d.date ? ` (${d.date})` : ''}`);\n }\n lines.push('');\n }\n\n // Deprecated Decisions\n if (data.deprecatedDecisions.length > 0) {\n lines.push('## Deprecated Decisions', '');\n for (const d of data.deprecatedDecisions) {\n lines.push(`- ~~${d.decision}~~ — ${d.context}${d.date ? ` (${d.date})` : ''}`);\n }\n lines.push('');\n }\n\n // Patterns & Conventions\n if (data.patterns.length > 0) {\n lines.push('## Patterns & Conventions', '');\n for (const p of data.patterns) {\n lines.push(`- ${p.pattern}${p.example ? ` — e.g. \\`${p.example}\\`` : ''}`);\n }\n lines.push('');\n }\n\n // Conflicts\n if (data.conflicts.length > 0) {\n lines.push('## Resolved Conflicts', '');\n for (const c of data.conflicts) {\n lines.push(`### ${c.topic}`, '');\n lines.push(`- **Approach A**: ${c.approachA}`);\n lines.push(`- **Approach B**: ${c.approachB}`);\n lines.push(`- **Resolution**: ${c.resolution} *(${c.resolvedBy})*`);\n lines.push('');\n }\n }\n\n // Known Issues & Gotchas\n if (data.knownIssues.length > 0) {\n lines.push('## Known Issues & Gotchas', '');\n for (const issue of data.knownIssues) {\n lines.push(`- ${issue}`);\n }\n lines.push('');\n }\n\n // Tool & MCP Usage\n if (data.toolingPatterns.length > 0) {\n lines.push('## Tool & MCP Usage', '');\n for (const t of data.toolingPatterns) {\n lines.push(`- ${t}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n","import { existsSync } from 'node:fs';\nimport pc from 'picocolors';\nimport * as clack from '@clack/prompts';\nimport cron from 'node-cron';\nimport { getLoreDir } from '../utils/paths.js';\nimport { loadConfig } from '../config/loader.js';\nimport { syncCommand } from './sync.js';\nimport { logger } from '../utils/logger.js';\n\nexport async function watchCommand(): Promise<void> {\n const loreDir = getLoreDir();\n\n if (!existsSync(loreDir)) {\n clack.intro(pc.bgCyan(pc.black(' lore watch ')));\n clack.log.warn(`Lore is not initialized. Run ${pc.cyan('lore init')} first.`);\n clack.outro('');\n return;\n }\n\n const config = loadConfig(loreDir);\n const schedule = config.schedule;\n\n clack.intro(pc.bgCyan(pc.black(' lore watch ')));\n\n if (!cron.validate(schedule)) {\n clack.log.error(`Invalid cron schedule: ${pc.red(schedule)}`);\n clack.outro('Fix the schedule in ~/.lore/config.json');\n return;\n }\n\n clack.log.info(`Watching with schedule: ${pc.cyan(schedule)}`);\n clack.log.info('Press Ctrl+C to stop.');\n\n // Run an initial sync\n logger.info('Running initial sync...');\n try {\n await syncCommand({});\n } catch (err) {\n logger.error('Initial sync failed:', err);\n }\n\n // Schedule periodic syncs\n cron.schedule(schedule, async () => {\n logger.info(`Scheduled sync triggered at ${new Date().toISOString()}`);\n try {\n await syncCommand({});\n } catch (err) {\n logger.error('Scheduled sync failed:', err);\n }\n });\n\n // Keep the process alive\n process.on('SIGINT', () => {\n clack.outro('Watch stopped.');\n process.exit(0);\n });\n}\n","import { existsSync, readdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport pc from 'picocolors';\nimport * as clack from '@clack/prompts';\nimport { getLoreDir } from '../utils/paths.js';\nimport { StateStore } from '../state/store.js';\nimport { loadConfig } from '../config/loader.js';\n\nexport async function statusCommand(): Promise<void> {\n const loreDir = getLoreDir();\n\n if (!existsSync(loreDir)) {\n clack.intro(pc.bgCyan(pc.black(' lore status ')));\n clack.log.warn(`Lore is not initialized. Run ${pc.cyan('lore init')} first.`);\n clack.outro('');\n return;\n }\n\n const config = loadConfig(loreDir);\n const store = new StateStore(loreDir);\n const state = store.read();\n\n // Count pending review items\n const pendingDir = resolve(loreDir, 'pending');\n let pendingCount = 0;\n if (existsSync(pendingDir)) {\n pendingCount = readdirSync(pendingDir).filter((f) => f.endsWith('.json')).length;\n }\n\n clack.intro(pc.bgCyan(pc.black(' lore status ')));\n\n const lastScan = state.lastScanAt\n ? new Date(state.lastScanAt).toLocaleString()\n : pc.dim('never');\n\n const enabledSources = Object.entries(config.sources)\n .filter(([, v]) => v.enabled)\n .map(([k]) => k);\n\n clack.note(\n [\n `${pc.bold('Location:')} ~/.lore/`,\n `${pc.bold('Last scan:')} ${lastScan}`,\n `${pc.bold('Total scans:')} ${state.scanCount}`,\n `${pc.bold('Pending reviews:')} ${pendingCount > 0 ? pc.yellow(String(pendingCount)) : pc.green('0')}`,\n `${pc.bold('Enabled sources:')} ${enabledSources.join(', ')}`,\n `${pc.bold('Review mode:')} ${config.reviewMode}`,\n `${pc.bold('Schedule:')} ${config.schedule}`,\n ].join('\\n'),\n 'Lore Status',\n );\n\n if (pendingCount > 0) {\n clack.log.info(`Run ${pc.cyan('lore review')} to inspect pending proposals.`);\n }\n\n clack.outro('');\n}\n","import { existsSync, readFileSync, readdirSync } from 'node:fs';\nimport { resolve, basename } from 'node:path';\nimport pc from 'picocolors';\nimport * as clack from '@clack/prompts';\nimport { globby } from 'globby';\nimport { getLoreDir } from '../utils/paths.js';\nimport { logger } from '../utils/logger.js';\nimport { invokeWithFallback } from '../utils/cli.js';\nimport { RECALL_TIMEOUT_MS } from '../utils/constants.js';\nimport { readSummaryIndex, loadConversationSummary, loadRepoCluster, listRepoClusterSlugs } from '../summarizer/cache.js';\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport interface RecallIndexEntry {\n id: string;\n type: 'conversation' | 'repo-cluster' | 'doc';\n title: string;\n repo: string | null;\n date: string | null;\n /** Approximate token cost to load full content */\n tokenEstimate: number;\n preview: string;\n}\n\n// ── Public API ───────────────────────────────────────────────────\n\n/**\n * Progressive disclosure recall command.\n *\n * Three modes:\n * `recall <topic>` — Layer 1: compact index of matching knowledge\n * `recall <topic> --detail <id>` — Layer 3: full content for a specific item\n * `recall <topic> --full` — Legacy: full context query (dumps everything)\n */\nexport async function recallCommand(\n topic: string,\n options: { detail?: string; full?: boolean } = {},\n): Promise<void> {\n const loreDir = getLoreDir();\n\n if (!existsSync(loreDir)) {\n clack.intro(pc.bgCyan(pc.black(' lore recall ')));\n clack.log.warn(`Lore is not initialized. Run ${pc.cyan('lore init')} first.`);\n clack.outro('');\n return;\n }\n\n clack.intro(pc.bgCyan(pc.black(' lore recall ')));\n\n // Layer 3: full detail for a specific item\n if (options.detail) {\n await recallDetail(topic, options.detail);\n clack.outro('');\n return;\n }\n\n // Legacy --full mode: dumps all context (original behavior)\n if (options.full) {\n await recallFull(topic, loreDir);\n clack.outro('');\n return;\n }\n\n // Layer 1: progressive disclosure — build compact index\n const spinner = clack.spinner();\n spinner.start(`Indexing knowledge for: ${pc.cyan(topic)}`);\n\n const index = buildRecallIndex(loreDir);\n\n if (index.length === 0) {\n spinner.stop('No knowledge found.');\n clack.log.warn(`No documentation generated yet. Run ${pc.cyan('lore sync')} first.`);\n clack.outro('');\n return;\n }\n\n // Use LLM to rank and filter the index by relevance to topic\n const indexContext = formatIndexForLLM(index);\n const rankPrompt = [\n '<role>You are a project knowledge retrieval assistant.</role>',\n '<task>Given a knowledge index and a user query, return ONLY the relevant entries ranked by relevance.</task>',\n '<context>',\n '<knowledge_index>',\n indexContext,\n '</knowledge_index>',\n `<query>${topic}</query>`,\n '</context>',\n '<output_format>',\n 'For each relevant entry, output a line:',\n ' [ID] TITLE — RELEVANCE_REASON (~TOKEN_COST tokens)',\n '',\n 'Then provide a 2-3 sentence summary answering the query based on the previews.',\n 'If the user should drill deeper into specific entries, suggest: \"Use --detail ID for full content.\"',\n '</output_format>',\n '<rules>',\n '- Only include entries relevant to the query',\n '- Order by relevance (most relevant first)',\n '- Be concise — this is an index, not a full answer',\n '- If nothing is relevant, say so clearly',\n '</rules>',\n ].join('\\n');\n\n try {\n const stdout = await invokeWithFallback(\n {\n args: ['-p', '--output-format', 'text', '--max-turns', '1', '--tools', '', '--strict-mcp-config'],\n input: rankPrompt,\n timeout: RECALL_TIMEOUT_MS,\n phase: 'recall',\n },\n 'recall:index',\n );\n\n spinner.stop(`Found ${index.length} knowledge entries.`);\n\n clack.note(stdout.trim(), `Knowledge index for: ${topic}`);\n clack.log.info(\n pc.dim(`Tip: Use ${pc.cyan(`lore recall \"${topic}\" --detail <ID>`)} for full content, or ${pc.cyan('--full')} to query all knowledge at once.`),\n );\n } catch (err) {\n spinner.stop('Index query failed.');\n logger.debug('CLI error:', err);\n // Fallback: show the raw index\n clack.log.warn('AI ranking failed. Showing raw index:');\n for (const entry of index.slice(0, 20)) {\n clack.log.info(\n ` ${pc.cyan(entry.id)} ${entry.title} ${pc.dim(`(~${entry.tokenEstimate} tokens)`)}`,\n );\n }\n }\n\n clack.outro('');\n}\n\n// ── Layer 1: Build compact index ─────────────────────────────────\n\nfunction buildRecallIndex(loreDir: string): RecallIndexEntry[] {\n const entries: RecallIndexEntry[] = [];\n\n // Index conversation summaries\n const summaryIndex = readSummaryIndex();\n for (const [id, meta] of Object.entries(summaryIndex.conversations)) {\n const summary = loadConversationSummary(id);\n const preview = summary?.ai?.summary ?? '';\n const tokenEstimate = estimateTokens(JSON.stringify(summary?.ai ?? ''));\n\n entries.push({\n id: `conv:${id}`,\n type: 'conversation',\n title: preview.slice(0, 80) || `Conversation ${id}`,\n repo: meta.repo,\n date: meta.conversationDate,\n tokenEstimate,\n preview: preview.slice(0, 150),\n });\n }\n\n // Index repo clusters\n for (const slug of listRepoClusterSlugs()) {\n const content = loadRepoCluster(slug);\n if (!content) continue;\n\n const tokenEstimate = estimateTokens(content);\n const firstLine = content.split('\\n').find((l) => l.trim().length > 0) ?? slug;\n\n entries.push({\n id: `repo:${slug}`,\n type: 'repo-cluster',\n title: `Repo knowledge: ${slug}`,\n repo: slug,\n date: null,\n tokenEstimate,\n preview: firstLine.slice(0, 150),\n });\n }\n\n // Index output docs\n const outputDir = resolve(loreDir, 'output');\n if (existsSync(outputDir)) {\n try {\n const docPaths = readdirRecursive(outputDir).filter(\n (f) => f.endsWith('.md') || f.endsWith('.json'),\n );\n\n for (const docPath of docPaths) {\n const content = readFileSync(docPath, 'utf-8');\n const tokenEstimate = estimateTokens(content);\n const name = basename(docPath);\n\n entries.push({\n id: `doc:${name}`,\n type: 'doc',\n title: name,\n repo: null,\n date: null,\n tokenEstimate,\n preview: content.slice(0, 150).replace(/\\n/g, ' '),\n });\n }\n } catch { /* output dir not readable */ }\n }\n\n return entries;\n}\n\nfunction formatIndexForLLM(entries: RecallIndexEntry[]): string {\n const lines: string[] = [];\n lines.push(`Total entries: ${entries.length}`);\n lines.push('');\n\n // Group by type\n const grouped: Record<string, RecallIndexEntry[]> = {};\n for (const entry of entries) {\n const key = entry.type;\n if (!grouped[key]) grouped[key] = [];\n grouped[key].push(entry);\n }\n\n for (const [type, items] of Object.entries(grouped)) {\n lines.push(`--- ${type} (${items.length}) ---`);\n for (const item of items) {\n lines.push(\n `[${item.id}] ${item.title}${item.repo ? ` (${item.repo})` : ''}${item.date ? ` @ ${item.date}` : ''} ~${item.tokenEstimate}tok`,\n );\n if (item.preview) lines.push(` ${item.preview}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\n// ── Layer 3: Full detail for a specific item ─────────────────────\n\nasync function recallDetail(topic: string, detailId: string): Promise<void> {\n const spinner = clack.spinner();\n spinner.start(`Loading detail for: ${pc.cyan(detailId)}`);\n\n const loreDir = getLoreDir();\n let content: string | null = null;\n let label = detailId;\n\n if (detailId.startsWith('conv:')) {\n const id = detailId.slice(5);\n const summary = loadConversationSummary(id);\n if (summary) {\n content = JSON.stringify(summary.ai, null, 2);\n label = `Conversation: ${summary.ai?.summary?.slice(0, 60) ?? id}`;\n }\n } else if (detailId.startsWith('repo:')) {\n const slug = detailId.slice(5);\n content = loadRepoCluster(slug);\n label = `Repo cluster: ${slug}`;\n } else if (detailId.startsWith('doc:')) {\n const name = detailId.slice(4);\n const outputDir = resolve(loreDir, 'output');\n const docPaths = readdirRecursive(outputDir).filter((f) => basename(f) === name);\n if (docPaths.length > 0) {\n content = readFileSync(docPaths[0], 'utf-8');\n label = `Document: ${name}`;\n }\n }\n\n if (!content) {\n spinner.stop('Not found.');\n clack.log.warn(`Entry \"${detailId}\" not found. Run ${pc.cyan('lore recall <topic>')} to see available entries.`);\n return;\n }\n\n // Use LLM to answer the topic question using this specific item's full content\n const prompt = [\n '<role>You are a project knowledge assistant.</role>',\n '<task>Answer the question using ONLY the provided knowledge entry.</task>',\n '<context>',\n `<entry id=\"${detailId}\">`,\n content,\n '</entry>',\n `<question>${topic}</question>`,\n '</context>',\n '<rules>',\n '- Answer based only on the provided content',\n '- Be specific and cite relevant details',\n '- If the content does not answer the question, say so',\n '</rules>',\n ].join('\\n');\n\n try {\n const stdout = await invokeWithFallback(\n {\n args: ['-p', '--output-format', 'text', '--max-turns', '1', '--tools', '', '--strict-mcp-config'],\n input: prompt,\n timeout: RECALL_TIMEOUT_MS,\n phase: 'recall',\n },\n 'recall:detail',\n );\n\n spinner.stop(`Loaded: ${label}`);\n clack.note(stdout.trim(), label);\n } catch (err) {\n spinner.stop('Query failed.');\n logger.debug('CLI error:', err);\n // Fallback: show raw content\n clack.note(content.slice(0, 2000), label);\n }\n}\n\n// ── Legacy full mode ─────────────────────────────────────────────\n\nasync function recallFull(topic: string, loreDir: string): Promise<void> {\n const spinner = clack.spinner();\n spinner.start(`Searching the lore for: ${pc.cyan(topic)}`);\n\n const outputDir = resolve(loreDir, 'output');\n let context = '';\n\n if (existsSync(outputDir)) {\n const docPaths = await globby(['**/*.md', '**/*.json'], {\n cwd: outputDir,\n absolute: true,\n });\n\n for (const docPath of docPaths) {\n if (existsSync(docPath)) {\n const content = readFileSync(docPath, 'utf-8');\n context += `\\n--- ${docPath} ---\\n${content}\\n`;\n }\n }\n }\n\n // Also include repo clusters for richer context\n for (const slug of listRepoClusterSlugs()) {\n const content = loadRepoCluster(slug);\n if (content) {\n context += `\\n--- repo:${slug} ---\\n${content}\\n`;\n }\n }\n\n if (!context.trim()) {\n spinner.stop('No documentation found.');\n clack.log.warn(`No documentation generated yet. Run ${pc.cyan('lore sync')} first.`);\n return;\n }\n\n const prompt = [\n '<role>You are a project knowledge assistant.</role>',\n '<task>Answer the question based on the following project documentation.</task>',\n '<context>',\n context,\n '</context>',\n `<question>${topic}</question>`,\n '<rules>',\n '- Answer based only on the documentation provided',\n '- Be concise and specific',\n '- If the answer is not in the documentation, say so',\n '</rules>',\n ].join('\\n');\n\n try {\n const stdout = await invokeWithFallback(\n {\n args: ['-p', '--output-format', 'text', '--max-turns', '1', '--tools', '', '--strict-mcp-config'],\n input: prompt,\n timeout: RECALL_TIMEOUT_MS,\n phase: 'recall',\n },\n 'recall',\n );\n\n spinner.stop('Found relevant knowledge.');\n clack.note(stdout.trim(), `About: ${topic}`);\n } catch (err) {\n spinner.stop('Query failed.');\n logger.debug('CLI error:', err);\n clack.log.error(\n 'Failed to query AI CLI. Make sure the Claude Code CLI or Cursor Agent is installed and authenticated.',\n );\n }\n}\n\n// ── Helpers ──────────────────────────────────────────────────────\n\n/** Rough token estimate: ~4 chars per token. */\nfunction estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n\n/** Recursively read all files in a directory. */\nfunction readdirRecursive(dir: string): string[] {\n const results: string[] = [];\n\n try {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n const full = resolve(dir, entry.name);\n if (entry.isDirectory()) {\n results.push(...readdirRecursive(full));\n } else {\n results.push(full);\n }\n }\n } catch { /* skip */ }\n\n return results;\n}\n","import { resolve } from 'node:path';\nimport { existsSync, readdirSync, readFileSync, unlinkSync, mkdirSync } from 'node:fs';\nimport pc from 'picocolors';\nimport * as clack from '@clack/prompts';\nimport { getLoreDir } from '../utils/paths.js';\nimport { applyProposal, type PendingItem } from '../writers/apply-proposal.js';\n\nexport async function reviewCommand(): Promise<void> {\n const loreDir = getLoreDir();\n\n if (!existsSync(loreDir)) {\n clack.intro(pc.bgCyan(pc.black(' lore review ')));\n clack.log.warn(`Lore is not initialized. Run ${pc.cyan('lore init')} first.`);\n clack.outro('');\n return;\n }\n\n clack.intro(pc.bgCyan(pc.black(' lore review ')));\n\n const pendingDir = resolve(loreDir, 'pending');\n if (!existsSync(pendingDir)) {\n clack.log.info('No pending proposals.');\n clack.outro('');\n return;\n }\n\n const files = readdirSync(pendingDir).filter((f) => f.endsWith('.json'));\n if (files.length === 0) {\n clack.log.info('No pending proposals.');\n clack.outro('');\n return;\n }\n\n clack.log.info(`Found ${pc.bold(String(files.length))} pending proposal(s).`);\n\n const outputDir = resolve(loreDir, 'output');\n mkdirSync(outputDir, { recursive: true });\n\n for (const file of files) {\n const filePath = resolve(pendingDir, file);\n const raw = readFileSync(filePath, 'utf-8');\n let item: PendingItem;\n try {\n item = JSON.parse(raw);\n } catch {\n clack.log.warn(`Skipping corrupted file: ${file}`);\n continue;\n }\n\n clack.log.step(`${pc.bold(item.type)} proposal (${item.createdAt})`);\n\n // Show preview\n const preview = item.content.length > 500\n ? item.content.slice(0, 500) + '\\n...(truncated)'\n : item.content;\n\n clack.note(preview, `Proposal: ${item.type}`);\n\n const action = await clack.select({\n message: 'What would you like to do?',\n options: [\n { value: 'accept', label: 'Accept -- save to ~/.lore/output/' },\n { value: 'skip', label: 'Skip -- review later' },\n { value: 'reject', label: 'Reject -- discard this change' },\n ],\n });\n\n if (clack.isCancel(action)) {\n clack.outro('Review cancelled.');\n return;\n }\n\n if (action === 'accept') {\n applyProposal(item, outputDir);\n unlinkSync(filePath);\n clack.log.success('Accepted and saved to ~/.lore/output/.');\n } else if (action === 'reject') {\n unlinkSync(filePath);\n clack.log.info('Rejected and removed from queue.');\n } else {\n clack.log.info('Skipped.');\n }\n }\n\n clack.outro('Review complete.');\n}\n\n// applyProposal moved to src/writers/apply-proposal.ts\n","/**\n * Shared logic for applying approved pending proposals to the output directory.\n *\n * Used by both the CLI `review` command and the UI `/pending/:id/accept` endpoint.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport interface PendingItem {\n id: string;\n type: 'changelog' | 'adr' | 'architecture';\n content: string;\n createdAt: string;\n filename: string;\n}\n\n// ── Apply ────────────────────────────────────────────────────────\n\n/**\n * Write a pending item's content to the appropriate output file.\n *\n * - `changelog` → appended to `outputDir/changelog.md`\n * - `adr` → written to `outputDir/decisions/NNNN-<title>.md`\n * - `architecture` → appended to `outputDir/architecture.md`\n */\nexport function applyProposal(item: PendingItem, outputDir: string): void {\n switch (item.type) {\n case 'changelog': {\n const filePath = resolve(outputDir, 'changelog.md');\n const existing = existsSync(filePath) ? readFileSync(filePath, 'utf-8') : '# Changelog\\n\\n';\n writeFileSync(filePath, existing + '\\n' + item.content + '\\n', 'utf-8');\n break;\n }\n case 'adr': {\n const adrDir = resolve(outputDir, 'decisions');\n mkdirSync(adrDir, { recursive: true });\n const files = readdirSync(adrDir).filter((f) => f.endsWith('.md'));\n const nextNum = String(files.length + 1).padStart(4, '0');\n let title = 'decision';\n try {\n const parsed = JSON.parse(item.content);\n if (parsed.title) {\n title = parsed.title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '')\n .slice(0, 50);\n }\n } catch {\n // use default title\n }\n writeFileSync(resolve(adrDir, `${nextNum}-${title}.md`), item.content, 'utf-8');\n break;\n }\n case 'architecture': {\n const filePath = resolve(outputDir, 'architecture.md');\n const existing = existsSync(filePath) ? readFileSync(filePath, 'utf-8') : '# Architecture\\n\\n';\n writeFileSync(filePath, existing + '\\n' + item.content + '\\n', 'utf-8');\n break;\n }\n }\n}\n","import { resolve } from 'node:path';\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport pc from 'picocolors';\nimport * as clack from '@clack/prompts';\nimport { getLoreDir } from '../utils/paths.js';\nimport { loreConfigSchema } from '../config/schema.js';\n\nexport async function configCommand(\n action: string | undefined,\n key: string | undefined,\n value: string | undefined,\n): Promise<void> {\n const loreDir = getLoreDir();\n const configPath = resolve(loreDir, 'config.json');\n\n if (!existsSync(configPath)) {\n clack.intro(pc.bgCyan(pc.black(' lore config ')));\n clack.log.warn(`Lore is not initialized. Run ${pc.cyan('lore init')} first.`);\n clack.outro('');\n return;\n }\n\n clack.intro(pc.bgCyan(pc.black(' lore config ')));\n\n const raw = JSON.parse(readFileSync(configPath, 'utf-8'));\n\n if (!action || action === 'show') {\n clack.note(JSON.stringify(raw, null, 2), 'Current Configuration (~/.lore/config.json)');\n clack.outro('');\n return;\n }\n\n if (action === 'get') {\n if (!key) {\n clack.log.error('Usage: lore config get <key>');\n clack.outro('');\n return;\n }\n const val = getNestedValue(raw, key);\n if (val === undefined) {\n clack.log.warn(`Key \"${key}\" not found.`);\n } else {\n clack.log.info(`${pc.bold(key)} = ${pc.cyan(JSON.stringify(val))}`);\n }\n clack.outro('');\n return;\n }\n\n if (action === 'set') {\n if (!key || value === undefined) {\n clack.log.error('Usage: lore config set <key> <value>');\n clack.outro('');\n return;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(value);\n } catch {\n parsed = value;\n }\n\n setNestedValue(raw, key, parsed);\n\n try {\n loreConfigSchema.parse(raw);\n } catch (err) {\n clack.log.error(`Invalid config value: ${err}`);\n clack.outro('');\n return;\n }\n\n writeFileSync(configPath, JSON.stringify(raw, null, 2) + '\\n', 'utf-8');\n clack.log.success(`Set ${pc.bold(key)} = ${pc.cyan(JSON.stringify(parsed))}`);\n clack.outro('');\n return;\n }\n\n clack.log.error(`Unknown action: ${action}. Use \"get\", \"set\", or \"show\".`);\n clack.outro('');\n}\n\nfunction getNestedValue(obj: Record<string, unknown>, path: string): unknown {\n const keys = path.split('.');\n let current: unknown = obj;\n for (const k of keys) {\n if (current == null || typeof current !== 'object') return undefined;\n current = (current as Record<string, unknown>)[k];\n }\n return current;\n}\n\nfunction setNestedValue(obj: Record<string, unknown>, path: string, value: unknown): void {\n const keys = path.split('.');\n let current: Record<string, unknown> = obj;\n for (let i = 0; i < keys.length - 1; i++) {\n const k = keys[i];\n if (!(k in current) || typeof current[k] !== 'object' || current[k] === null) {\n current[k] = {};\n }\n current = current[k] as Record<string, unknown>;\n }\n current[keys[keys.length - 1]] = value;\n}\n","/**\n * CLI command: lore repos\n *\n * View, manage, and preview repo selection for the Lore pipeline.\n * Default action (no args) opens an interactive multi-select widget.\n */\n\nimport { resolve } from 'node:path';\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport pc from 'picocolors';\nimport * as clack from '@clack/prompts';\nimport { getLoreDir } from '../utils/paths.js';\nimport { loadConfig } from '../config/loader.js';\nimport { loreConfigSchema } from '../config/schema.js';\nimport type { z } from 'zod';\nimport { selectReposForProcessing } from '../repos/selector.js';\nimport { scoreRepos } from '../repos/activity.js';\nimport { discoverKnownRepos } from '../utils/paths.js';\n\n// ── Public API ───────────────────────────────────────────────────\n\nexport async function reposCommand(\n action: string | undefined,\n name: string | undefined,\n): Promise<void> {\n const loreDir = getLoreDir();\n\n if (!existsSync(loreDir)) {\n clack.intro(pc.bgCyan(pc.black(' lore repos ')));\n clack.log.warn(`Lore is not initialized. Run ${pc.cyan('lore init')} first.`);\n clack.outro('');\n return;\n }\n\n clack.intro(pc.bgCyan(pc.black(' lore repos ')));\n\n const config = loadConfig(loreDir);\n\n // Default: interactive multi-select\n if (!action || action === 'select') {\n await interactiveSelect();\n return;\n }\n\n if (action === 'list') {\n await listRepos(config);\n return;\n }\n\n if (action === 'active') {\n await showActiveRepos(config);\n return;\n }\n\n if (action === 'add') {\n if (!name) {\n clack.log.error('Usage: lore repos add <name>');\n clack.outro('');\n return;\n }\n await addRepo(name);\n return;\n }\n\n if (action === 'remove') {\n if (!name) {\n clack.log.error('Usage: lore repos remove <name>');\n clack.outro('');\n return;\n }\n await removeRepo(name);\n return;\n }\n\n clack.log.error(`Unknown action: ${action}. Use \"select\", \"list\", \"active\", \"add\", or \"remove\".`);\n clack.outro('');\n}\n\n// ── Sub-commands ─────────────────────────────────────────────────\n\n/**\n * Interactive multi-select widget.\n * Discovers all repos, shows them sorted by activity, and lets\n * the user toggle which ones to include. Saves to config on confirm.\n */\nasync function interactiveSelect(): Promise<void> {\n const configPath = resolve(getLoreDir(), 'config.json');\n const raw = loadRawConfig(configPath);\n\n // Current include/exclude from config\n const currentInclude: string[] = raw.repos?.include ?? [];\n const currentExclude: string[] = raw.repos?.exclude ?? [];\n\n // Discover and score all repos\n const allRepos = discoverKnownRepos();\n const scored = await scoreRepos(allRepos);\n\n if (scored.length === 0) {\n clack.log.warn('No git repos found. Check that your projects are in ~/Desktop, ~/projects, ~/code, etc.');\n clack.outro('');\n return;\n }\n\n // Build multiselect options — pre-select repos that are currently included\n // or would be auto-selected (have activity + not excluded)\n const config = loadConfig(getLoreDir());\n const selectionResult = await selectReposForProcessing(config.repos);\n const autoSelectedPaths = new Set(selectionResult.selected.map((sr) => sr.repo.path));\n\n const options = scored.map((sr) => {\n const name = sr.repo.remoteName ?? sr.repo.shortName;\n const scoreStr = sr.score > 0 ? `score: ${sr.score}` : 'inactive';\n\n const hints: string[] = [scoreStr];\n if (sr.signals.commitCount > 0) hints.push(`${sr.signals.commitCount} commits`);\n if (sr.signals.conversationCount > 0) hints.push(`${sr.signals.conversationCount} convos`);\n\n // Check if explicitly included or excluded\n const isExplicitlyIncluded = currentInclude.some((p) =>\n name.toLowerCase().includes(p.toLowerCase()) ||\n sr.repo.path.toLowerCase().includes(p.toLowerCase()),\n );\n const isExplicitlyExcluded = currentExclude.some((p) =>\n name.toLowerCase().includes(p.toLowerCase()) ||\n sr.repo.path.toLowerCase().includes(p.toLowerCase()),\n );\n\n return {\n value: sr.repo.path,\n label: name,\n hint: hints.join(', ') + (isExplicitlyIncluded ? ' [included]' : '') + (isExplicitlyExcluded ? ' [excluded]' : ''),\n // Pre-select: explicitly included OR auto-selected (and not excluded)\n initialValue: isExplicitlyIncluded || (autoSelectedPaths.has(sr.repo.path) && !isExplicitlyExcluded),\n };\n });\n\n // Build initial values array for multiselect\n const initialValues = options\n .filter((o) => o.initialValue)\n .map((o) => o.value);\n\n const selected = await clack.multiselect({\n message: 'Select repos to include in pipeline (space to toggle, enter to confirm)',\n options: options.map((o) => ({\n value: o.value,\n label: o.label,\n hint: o.hint,\n })),\n initialValues,\n required: false,\n });\n\n if (clack.isCancel(selected)) {\n clack.outro('Cancelled — no changes made.');\n return;\n }\n\n const selectedPaths = new Set(selected as string[]);\n\n // Derive the new include/exclude lists from the user's selections.\n // Strategy:\n // - Switch mode to 'whitelist' so the user's picks are respected exactly\n // - Include = selected repo names\n // - Exclude = cleared (whitelist mode doesn't need it)\n const newInclude: string[] = [];\n for (const sr of scored) {\n if (selectedPaths.has(sr.repo.path)) {\n newInclude.push(sr.repo.remoteName ?? sr.repo.shortName);\n }\n }\n\n // Update config\n if (!raw.repos) raw.repos = {};\n raw.repos.mode = 'whitelist';\n raw.repos.include = newInclude;\n raw.repos.exclude = [];\n\n try {\n loreConfigSchema.parse(raw);\n } catch (err) {\n clack.log.error(`Invalid config: ${err}`);\n clack.outro('');\n return;\n }\n\n writeFileSync(configPath, JSON.stringify(raw, null, 2) + '\\n', 'utf-8');\n\n clack.log.success(\n `Saved ${pc.bold(String(newInclude.length))} repos to whitelist. Mode set to ${pc.bold('whitelist')}.`,\n );\n\n // Show what was saved\n const lines = newInclude.map((name) => ` ${pc.green('+')} ${name}`);\n clack.note(lines.join('\\n'), 'Selected Repos');\n\n clack.outro(\n `Run ${pc.cyan('lore repos list')} to review, or ${pc.cyan('lore config set repos.mode auto')} to switch back to auto-detection.`,\n );\n}\n\n/**\n * List all discovered repos with activity scores.\n * Shows which would be selected under current config.\n */\nasync function listRepos(config: ReturnType<typeof loadConfig>): Promise<void> {\n const result = await selectReposForProcessing(config.repos);\n const selectedPaths = new Set(result.selected.map((sr) => sr.repo.path));\n\n clack.log.info(\n `Mode: ${pc.bold(result.mode)} | Discovered: ${result.all.length} | Selected: ${result.selected.length}`,\n );\n\n const lines: string[] = [];\n\n for (const sr of result.all) {\n const isSelected = selectedPaths.has(sr.repo.path);\n const marker = isSelected ? pc.green('✓') : pc.dim('·');\n const name = sr.repo.remoteName ?? sr.repo.shortName;\n const score = sr.score > 0 ? pc.yellow(`${sr.score}`) : pc.dim('0');\n\n const signals: string[] = [];\n if (sr.signals.daysSinceLastCommit !== null) {\n signals.push(`last commit: ${sr.signals.daysSinceLastCommit}d ago`);\n }\n if (sr.signals.commitCount > 0) {\n signals.push(`${sr.signals.commitCount} commits`);\n }\n if (sr.signals.conversationCount > 0) {\n signals.push(`${sr.signals.conversationCount} conversations`);\n }\n\n const signalStr = signals.length > 0 ? pc.dim(` (${signals.join(', ')})`) : '';\n lines.push(` ${marker} ${isSelected ? pc.bold(name) : pc.dim(name)} score: ${score}${signalStr}`);\n }\n\n clack.note(lines.join('\\n'), 'All Repos (sorted by activity)');\n\n // Show include/exclude lists if set\n if (config.repos.include.length > 0) {\n clack.log.info(`Include list: ${config.repos.include.map((r) => pc.green(r)).join(', ')}`);\n }\n if (config.repos.exclude.length > 0) {\n clack.log.info(`Exclude list: ${config.repos.exclude.map((r) => pc.red(r)).join(', ')}`);\n }\n\n clack.outro(`Use ${pc.cyan('lore repos select')} to interactively pick repos, or ${pc.cyan('lore repos add/remove <name>')} to tweak.`);\n}\n\n/**\n * Preview which repos would be selected for processing (dry-run).\n */\nasync function showActiveRepos(config: ReturnType<typeof loadConfig>): Promise<void> {\n const result = await selectReposForProcessing(config.repos);\n\n if (result.selected.length === 0) {\n clack.log.warn('No repos selected for processing. Check your config or add repos to the include list.');\n clack.outro('');\n return;\n }\n\n const lines: string[] = [];\n\n for (let i = 0; i < result.selected.length; i++) {\n const sr = result.selected[i];\n const name = sr.repo.remoteName ?? sr.repo.shortName;\n const score = pc.yellow(`${sr.score}`);\n lines.push(` ${pc.bold(`${i + 1}.`)} ${pc.green(name)} score: ${score} ${pc.dim(sr.repo.path)}`);\n }\n\n clack.note(lines.join('\\n'), `Selected Repos (${result.selected.length})`);\n\n if (result.excluded.length > 0) {\n const topExcluded = result.excluded.slice(0, 5);\n const excludedLines = topExcluded.map((e) => {\n const name = e.repo.remoteName ?? e.repo.shortName;\n return ` ${pc.dim('·')} ${pc.dim(name)} — ${pc.dim(e.reason)}`;\n });\n if (result.excluded.length > 5) {\n excludedLines.push(pc.dim(` ... and ${result.excluded.length - 5} more`));\n }\n clack.note(excludedLines.join('\\n'), 'Excluded');\n }\n\n clack.outro(\n `These ${result.selected.length} repos will be clustered + skill-generated on next sync.`,\n );\n}\n\n/**\n * Add a repo to the include list.\n */\nasync function addRepo(name: string): Promise<void> {\n const configPath = resolve(getLoreDir(), 'config.json');\n const raw = loadRawConfig(configPath);\n\n // Ensure repos.include exists\n if (!raw.repos) raw.repos = {};\n if (!Array.isArray(raw.repos.include)) raw.repos.include = [];\n\n // Check if already included\n if (raw.repos.include.includes(name)) {\n clack.log.warn(`\"${name}\" is already in the include list.`);\n clack.outro('');\n return;\n }\n\n raw.repos.include.push(name);\n\n // Remove from exclude if present\n if (Array.isArray(raw.repos.exclude)) {\n raw.repos.exclude = raw.repos.exclude.filter((r: string) => r !== name);\n }\n\n // Validate and save\n try {\n loreConfigSchema.parse(raw);\n } catch (err) {\n clack.log.error(`Invalid config: ${err}`);\n clack.outro('');\n return;\n }\n\n writeFileSync(configPath, JSON.stringify(raw, null, 2) + '\\n', 'utf-8');\n clack.log.success(`Added ${pc.green(name)} to include list.`);\n clack.outro('');\n}\n\n/**\n * Add a repo to the exclude list (and remove from include if present).\n */\nasync function removeRepo(name: string): Promise<void> {\n const configPath = resolve(getLoreDir(), 'config.json');\n const raw = loadRawConfig(configPath);\n\n // Ensure repos.exclude exists\n if (!raw.repos) raw.repos = {};\n if (!Array.isArray(raw.repos.exclude)) raw.repos.exclude = [];\n\n // Check if already excluded\n if (raw.repos.exclude.includes(name)) {\n clack.log.warn(`\"${name}\" is already in the exclude list.`);\n clack.outro('');\n return;\n }\n\n raw.repos.exclude.push(name);\n\n // Remove from include if present\n if (Array.isArray(raw.repos.include)) {\n raw.repos.include = raw.repos.include.filter((r: string) => r !== name);\n }\n\n // Validate and save\n try {\n loreConfigSchema.parse(raw);\n } catch (err) {\n clack.log.error(`Invalid config: ${err}`);\n clack.outro('');\n return;\n }\n\n writeFileSync(configPath, JSON.stringify(raw, null, 2) + '\\n', 'utf-8');\n clack.log.success(`Added ${pc.red(name)} to exclude list.`);\n clack.outro('');\n}\n\n// ── Helpers ──────────────────────────────────────────────────────\n\ntype RawLoreConfig = z.input<typeof loreConfigSchema>;\n\nfunction loadRawConfig(configPath: string): RawLoreConfig {\n if (!existsSync(configPath)) {\n return {};\n }\n try {\n return JSON.parse(readFileSync(configPath, 'utf-8'));\n } catch {\n return {};\n }\n}\n","import pc from 'picocolors';\nimport * as clack from '@clack/prompts';\nimport { existsSync } from 'node:fs';\nimport { getLoreDir } from '../utils/paths.js';\nimport { startServer } from '../ui/server.js';\n\ninterface UiOptions {\n port?: number;\n}\n\nexport async function uiCommand(options: UiOptions = {}): Promise<void> {\n const loreDir = getLoreDir();\n\n if (!existsSync(loreDir)) {\n clack.intro(pc.bgCyan(pc.black(' lore ui ')));\n clack.log.warn(`Lore is not initialized. Run ${pc.cyan('lore init')} first.`);\n clack.outro('');\n return;\n }\n\n const port = options.port ?? 3333;\n\n clack.intro(pc.bgCyan(pc.black(' lore ui ')));\n clack.log.info(`Starting Lore dashboard on ${pc.cyan(`http://localhost:${port}`)}`);\n\n startServer(port);\n\n // Open the browser\n const { exec } = await import('node:child_process');\n const url = `http://localhost:${port}`;\n const platform = process.platform;\n if (platform === 'darwin') {\n exec(`open ${url}`);\n } else if (platform === 'linux') {\n exec(`xdg-open ${url}`);\n } else if (platform === 'win32') {\n exec(`start ${url}`);\n }\n\n clack.log.success(`Dashboard running at ${pc.cyan(url)}`);\n clack.log.info('Press Ctrl+C to stop.');\n\n // Keep process alive\n await new Promise(() => {});\n}\n","import { Hono } from 'hono';\nimport { serve } from '@hono/node-server';\nimport { join } from 'node:path';\nimport { logger } from '../utils/logger.js';\nimport { existsSync, readFileSync, statSync } from 'node:fs';\nimport { conversationsApi } from './api/conversations.js';\nimport { toolsApi } from './api/tools.js';\nimport { pendingApi } from './api/pending.js';\nimport { stateApi } from './api/state.js';\nimport { vettingApi } from './api/vetting.js';\nimport { skillGeneratorApi } from './api/skill-generator.js';\nimport { reposApi } from './api/repos.js';\nimport { eventsApi } from './api/events.js';\nimport { metricsApi } from './api/metrics.js';\nimport { repoScansApi } from './api/repo-scans.js';\nimport { warmCache } from './api/cache.js';\n\nexport function createApp(): Hono {\n const app = new Hono();\n\n // CORS for dev mode\n app.use('*', async (c, next) => {\n c.header('Access-Control-Allow-Origin', '*');\n c.header('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');\n c.header('Access-Control-Allow-Headers', 'Content-Type');\n if (c.req.method === 'OPTIONS') return new Response(null, { status: 204 });\n await next();\n });\n\n // API routes\n app.route('/api/conversations', conversationsApi);\n app.route('/api/tools', toolsApi);\n app.route('/api/pending', pendingApi);\n app.route('/api/state', stateApi);\n app.route('/api/vetting', vettingApi);\n app.route('/api/skills', skillGeneratorApi);\n app.route('/api/repos', reposApi);\n app.route('/api/events', eventsApi);\n app.route('/api/metrics', metricsApi);\n app.route('/api/repo-scans', repoScansApi);\n\n return app;\n}\n\nexport function startServer(port: number = 3333): { close: () => void } {\n const app = createApp();\n const uiDir = join(import.meta.dirname, '..', '..', 'dist', 'ui');\n\n // Serve static files manually\n app.get('/*', async (c) => {\n const path = c.req.path;\n\n // Skip API routes\n if (path.startsWith('/api/')) return c.notFound();\n\n // Try to serve static file\n const filePath = join(uiDir, path === '/' ? 'index.html' : path);\n\n if (existsSync(filePath) && statSync(filePath).isFile()) {\n const content = readFileSync(filePath);\n const ext = filePath.split('.').pop() ?? '';\n const mimeTypes: Record<string, string> = {\n html: 'text/html',\n js: 'application/javascript',\n css: 'text/css',\n json: 'application/json',\n png: 'image/png',\n jpg: 'image/jpeg',\n svg: 'image/svg+xml',\n ico: 'image/x-icon',\n woff: 'font/woff',\n woff2: 'font/woff2',\n };\n return c.body(content, 200, {\n 'Content-Type': mimeTypes[ext] ?? 'application/octet-stream',\n });\n }\n\n // SPA fallback: serve index.html\n const indexPath = join(uiDir, 'index.html');\n if (existsSync(indexPath)) {\n const html = readFileSync(indexPath, 'utf-8');\n return c.html(html);\n }\n\n return c.text('Lore UI not built. Run: yarn build:ui', 404);\n });\n\n const server = serve({ fetch: app.fetch, port });\n\n // Defer cache warm-up so the server is ready to accept requests immediately.\n // With stale-while-revalidate in the LRU cache, even if a request arrives\n // before warming completes, subsequent requests will be instant.\n setTimeout(() => {\n warmCache().catch((err) => {\n logger.warn('[lore] Cache warm-up failed (non-fatal):', err);\n });\n }, 2_000);\n\n return {\n close: () => {\n if (server && typeof (server as any).close === 'function') {\n (server as any).close();\n }\n },\n };\n}\n","import { Hono } from 'hono';\nimport {\n getAllSources,\n getSource,\n type ConversationSummary,\n type ConversationDetail,\n} from '../../data-sources/registry.js';\nimport {\n readClassificationCache,\n classifyConversations,\n clearClassificationCache,\n} from '../../classifier/classifier.js';\nimport { getCached, invalidateCache, CACHE_KEYS } from './cache.js';\nimport {\n getAllPhaseEntries,\n getEntriesByRunId,\n getTaggedConversationIds,\n type LorePhase,\n} from '../../state/phase-store.js';\nimport { shouldIgnoreConversation } from '../../utils/conversation-filter.js';\nimport { buildLoreIgnoreMap } from '../../utils/lore-filter.js';\n\nexport const conversationsApi = new Hono();\n\n// Re-export types so existing imports from other files continue to work\nexport type { ConversationSummary, ConversationDetail };\n\n// ── Shared helpers ────────────────────────────────────────────────\n\n/**\n * Load all conversations from all sources (cached).\n */\nasync function getAllConversationsCached(sourceFilter?: string): Promise<ConversationSummary[]> {\n const all: ConversationSummary[] = [];\n if (!sourceFilter || sourceFilter === 'cursor') {\n all.push(...(await getCached(CACHE_KEYS.cursorConversations, () => getSource('cursor').listConversations())));\n }\n if (!sourceFilter || sourceFilter === 'claude-code') {\n all.push(...(await getCached(CACHE_KEYS.claudeConversations, () => getSource('claude-code').listConversations())));\n }\n return all;\n}\n\n// ── Lore process conversation filter ──────────────────────────────\n// Conversations created BY lore's automated processes (classifier, distiller,\n// skill-gen, vetter) should be hidden from the main conversations view.\n// Normal user conversations about the lore repo itself are kept.\n\nfunction excludeLoreConversations(convs: ConversationSummary[]): ConversationSummary[] {\n const ignoreMap = buildLoreIgnoreMap();\n return convs.filter((c) => !shouldIgnoreConversation(c.id, ignoreMap, c.title));\n}\n\n// ── Shared classification cache helper (eliminates 3x duplication) ──\nfunction applyClassificationCache(convs: ConversationSummary[]): void {\n const classificationCache = readClassificationCache();\n for (const conv of convs) {\n if (!conv.project && classificationCache.has(conv.id)) {\n const classification = classificationCache.get(conv.id)!;\n if (classification.assignedRepo) {\n conv.project = classification.assignedRepo;\n conv.matchMethod = 'ai';\n conv.aiReason = classification.reason;\n conv.aiConfidence = classification.confidence;\n }\n }\n }\n}\n\n// ── Classification streaming state (same pattern as sync output) ──\n\nlet classifyOutputLines: string[] = [];\nlet classifyRunning = false;\nlet classifyPhase = '';\nlet classifyError: string | null = null;\nlet classifyProgress: { current: number; total: number; percent: number } | null = null;\nlet classifyAbortController: AbortController | null = null;\n\n// Per-batch progress state (updated by Claude tool calls to /classify/progress)\nconst batchProgress = new Map<number, { processed: number; total: number }>();\nlet classifyTotalConvs = 0;\nlet completedBatches = 0;\nlet totalBatches = 0;\n\nfunction appendClassifyOutput(line: string): void {\n const timestamp = new Date().toISOString().slice(11, 19);\n classifyOutputLines.push(`[${timestamp}] ${line}`);\n}\n\nfunction recalcProgress(): void {\n let totalProcessed = 0;\n for (const bp of batchProgress.values()) totalProcessed += bp.processed;\n const total = classifyTotalConvs || 1;\n classifyProgress = {\n current: Math.min(totalProcessed, total),\n total,\n percent: Math.round((Math.min(totalProcessed, total) / total) * 100),\n };\n}\n\n// ── Progress endpoint: called by Claude via curl during classification ──\nconversationsApi.post('/classify/progress', async (c) => {\n try {\n const body = await c.req.json();\n const batchId = typeof body.batchId === 'number' ? body.batchId : 0;\n const processed = typeof body.processed === 'number' ? body.processed : 0;\n const batchTotal = typeof body.total === 'number' ? body.total : 0;\n\n batchProgress.set(batchId, { processed, total: batchTotal });\n recalcProgress();\n appendClassifyOutput(`[classifier] [batch ${batchId}] ${processed}/${batchTotal} conversations classified`);\n\n const activeBatches = [...batchProgress.entries()].filter(([, v]) => v.processed < v.total).length;\n if (activeBatches > 0) {\n classifyPhase = `${activeBatches} agent${activeBatches > 1 ? 's' : ''} running, ${completedBatches}/${totalBatches} batches done`;\n }\n\n return c.json({ ok: true });\n } catch {\n return c.json({ ok: false }, 400);\n }\n});\n\n// ── Trigger AI classification of unmatched conversations ──────────\nconversationsApi.post('/classify', async (c) => {\n if (classifyRunning) {\n return c.json({ error: 'Classification already running' }, 409);\n }\n\n classifyOutputLines = [];\n classifyRunning = true;\n classifyPhase = 'Collecting conversations...';\n classifyError = null;\n classifyProgress = null;\n classifyAbortController = new AbortController();\n batchProgress.clear();\n classifyTotalConvs = 0;\n completedBatches = 0;\n totalBatches = 0;\n\n runClassification().catch(() => { /* handled inside */ });\n\n return c.json({ started: true });\n});\n\nasync function runClassification(): Promise<void> {\n try {\n appendClassifyOutput('[classifier] Starting AI classification...');\n\n const all = await getAllConversationsCached();\n\n const unmatchedCursor = all.filter((conv) => !conv.project && conv.source === 'cursor').map((c) => c.id);\n const unmatchedClaude = all.filter((conv) => !conv.project && conv.source === 'claude-code').map((c) => c.id);\n\n const totalUnmatched = unmatchedCursor.length + unmatchedClaude.length;\n classifyTotalConvs = totalUnmatched;\n appendClassifyOutput(`[classifier] ${all.length} total conversations, ${totalUnmatched} unmatched`);\n classifyProgress = { current: 0, total: totalUnmatched, percent: 0 };\n\n if (totalUnmatched === 0) {\n appendClassifyOutput('[classifier] No unmatched conversations to classify');\n classifyPhase = 'Done — nothing to classify';\n classifyRunning = false;\n return;\n }\n\n classifyPhase = 'Running AI classification (3 parallel agents)...';\n\n const cache = await classifyConversations(unmatchedCursor, unmatchedClaude, {\n port: 3333,\n onOutput: (line) => {\n appendClassifyOutput(line);\n const batchDoneMatch = line.match(/Batch (\\d+)\\/(\\d+) complete/);\n if (batchDoneMatch) {\n completedBatches++;\n totalBatches = parseInt(batchDoneMatch[2], 10);\n const activeBatches = [...batchProgress.entries()].filter(([, v]) => v.processed < v.total).length;\n classifyPhase = `${completedBatches}/${totalBatches} batches done${activeBatches > 0 ? `, ${activeBatches} running` : ''}`;\n const batchId = parseInt(batchDoneMatch[1], 10) - 1;\n const existing = batchProgress.get(batchId);\n if (existing) {\n batchProgress.set(batchId, { processed: existing.total, total: existing.total });\n }\n recalcProgress();\n }\n const startMatch = line.match(/\\[batch (\\d+)\\] Starting/);\n if (startMatch) {\n const totalMatch = line.match(/\\/(\\d+)\\)/);\n if (totalMatch) totalBatches = parseInt(totalMatch[1], 10);\n }\n },\n signal: classifyAbortController?.signal,\n });\n\n const assigned = [...cache.values()].filter((v) => v.assignedRepo).length;\n const total = cache.size;\n\n appendClassifyOutput(`[classifier] Done! ${assigned} assigned, ${total - assigned} unassigned (${total} total)`);\n classifyPhase = `Done — ${assigned} assigned, ${total - assigned} unassigned`;\n classifyProgress = { current: totalUnmatched, total: totalUnmatched, percent: 100 };\n invalidateCache('conversations');\n } catch (err) {\n const msg = err instanceof Error ? err.message : 'Classification failed';\n appendClassifyOutput(`[classifier] ERROR: ${msg}`);\n classifyError = msg;\n classifyPhase = 'Failed';\n } finally {\n classifyRunning = false;\n classifyAbortController = null;\n }\n}\n\n// ── Get classification status ─────────────────────────────────────\nconversationsApi.get('/classify/status', (c) => {\n return c.json({\n running: classifyRunning,\n phase: classifyPhase,\n error: classifyError,\n progress: classifyProgress,\n });\n});\n\n// ── Get classification output lines (polling) ─────────────────────\nconversationsApi.get('/classify/output', (c) => {\n const since = parseInt(c.req.query('since') ?? '0', 10);\n const lines = classifyOutputLines.slice(since);\n return c.json({ lines, total: classifyOutputLines.length });\n});\n\n// ── Stop a running classification ─────────────────────────────────\nconversationsApi.post('/classify/stop', (c) => {\n if (!classifyRunning || !classifyAbortController) {\n return c.json({ error: 'No classification is currently running' }, 409);\n }\n classifyAbortController.abort();\n classifyAbortController = null;\n appendClassifyOutput('[classifier] Classification cancelled by user.');\n classifyRunning = false;\n classifyPhase = 'Cancelled';\n classifyError = null;\n return c.json({ stopped: true });\n});\n\n// ── Clear classification cache ────────────────────────────────────\nconversationsApi.post('/classify/reset', async (c) => {\n clearClassificationCache();\n invalidateCache('conversations');\n return c.json({ message: 'Classification cache cleared' });\n});\n\n// ── Repo breakdown: conversations grouped by repo ─────────────────\nconversationsApi.get('/repos', async (c) => {\n let all = await getAllConversationsCached();\n\n applyClassificationCache(all);\n all = excludeLoreConversations(all);\n\n const repoMap = new Map<string, {\n repo: string;\n repoPath: string;\n conversations: ConversationSummary[];\n sources: { cursor: number; claudeCode: number };\n }>();\n\n for (const conv of all) {\n const rawProject = conv.project ?? (conv.source === 'cursor' ? 'Cursor (global)' : 'Claude Code (global)');\n\n const gitMatch = rawProject.match(/^(.+?)\\s+\\[(.+)\\]$/);\n const repoPath = gitMatch ? gitMatch[1] : rawProject;\n const repoName = gitMatch\n ? gitMatch[2]\n : repoPath.split('/').filter(Boolean).pop() ?? rawProject;\n\n const groupKey = repoName.toLowerCase();\n let group = repoMap.get(groupKey);\n if (!group) {\n group = { repo: repoName, repoPath, conversations: [], sources: { cursor: 0, claudeCode: 0 } };\n repoMap.set(groupKey, group);\n }\n group.conversations.push(conv);\n if (conv.source === 'cursor') group.sources.cursor++;\n else group.sources.claudeCode++;\n }\n\n const repos = Array.from(repoMap.values())\n .map((g) => ({\n repo: g.repo,\n repoPath: g.repoPath,\n conversationCount: g.conversations.length,\n sources: g.sources,\n latestActivity: g.conversations\n .map((c) => c.createdAt)\n .filter(Boolean)\n .sort()\n .reverse()[0] ?? null,\n conversations: g.conversations\n .sort((a, b) => {\n const aT = a.createdAt ? new Date(a.createdAt).getTime() : 0;\n const bT = b.createdAt ? new Date(b.createdAt).getTime() : 0;\n return bT - aT;\n }),\n }))\n .sort((a, b) => b.conversationCount - a.conversationCount);\n\n return c.json({ repos, totalConversations: all.length, totalRepos: repos.length });\n});\n\n// ── Phase review: conversations grouped by lore phase ─────────────\nconversationsApi.get('/phases', async (c) => {\n let all = await getAllConversationsCached();\n\n applyClassificationCache(all);\n\n const phaseEntries = getAllPhaseEntries();\n const taggedIds = getTaggedConversationIds();\n\n all.sort((a, b) => {\n const aTime = a.createdAt ? new Date(a.createdAt).getTime() : 0;\n const bTime = b.createdAt ? new Date(b.createdAt).getTime() : 0;\n return bTime - aTime;\n });\n\n const phases: Record<string, ConversationSummary[]> = {\n classify: [],\n distill: [],\n 'skill-gen': [],\n vet: [],\n };\n const legacy: ConversationSummary[] = [];\n\n const ignoreMap = buildLoreIgnoreMap();\n for (const conv of all) {\n const entry = phaseEntries.get(conv.id);\n if (entry && entry.phase in phases) {\n phases[entry.phase].push(conv);\n } else if (shouldIgnoreConversation(conv.id, ignoreMap, conv.title)) {\n legacy.push(conv);\n }\n }\n\n const counts = {\n classify: phases.classify.length,\n distill: phases.distill.length,\n 'skill-gen': phases['skill-gen'].length,\n vet: phases.vet.length,\n legacy: legacy.length,\n };\n\n return c.json({ phases, legacy, counts, total: all.length });\n});\n\n// ── Conversations by run ID (for scan detail page) ───────────────\nconversationsApi.get('/by-run/:runId', async (c) => {\n const runId = c.req.param('runId');\n const entries = getEntriesByRunId(runId);\n\n if (entries.length === 0) {\n return c.json({ phases: {} });\n }\n\n const all = await getAllConversationsCached();\n const convMap = new Map(all.map((conv) => [conv.id, conv]));\n\n const phases: Record<string, ConversationSummary[]> = {};\n for (const entry of entries) {\n const conv = convMap.get(entry.conversationId);\n if (!conv) continue;\n if (!phases[entry.phase]) phases[entry.phase] = [];\n phases[entry.phase].push(conv);\n }\n\n return c.json({ phases });\n});\n\n// ── List all conversations (paginated) ────────────────────────────\nconversationsApi.get('/', async (c) => {\n const page = parseInt(c.req.query('page') ?? '1', 10);\n const limit = Math.min(parseInt(c.req.query('limit') ?? '50', 10), 200);\n const source = c.req.query('source'); // 'cursor' | 'claude-code' | undefined\n const search = c.req.query('search')?.toLowerCase();\n const mode = c.req.query('mode'); // 'agent' | 'chat' | etc\n const phase = c.req.query('phase') as LorePhase | undefined;\n const legacyFilter = c.req.query('legacy'); // 'true' | 'false' | undefined\n\n let all = await getAllConversationsCached(source);\n\n applyClassificationCache(all);\n\n if (!phase) {\n all = excludeLoreConversations(all);\n }\n\n if (phase || legacyFilter !== undefined) {\n const phaseEntries = getAllPhaseEntries();\n const taggedIds = getTaggedConversationIds();\n\n if (phase) {\n all = all.filter((conv) => phaseEntries.get(conv.id)?.phase === phase);\n } else if (legacyFilter === 'true') {\n all = all.filter((conv) => !taggedIds.has(conv.id));\n } else if (legacyFilter === 'false') {\n all = all.filter((conv) => taggedIds.has(conv.id));\n }\n }\n\n if (search) {\n all = all.filter(\n (c) =>\n c.title.toLowerCase().includes(search) ||\n c.id.toLowerCase().includes(search) ||\n (c.project ?? '').toLowerCase().includes(search),\n );\n }\n if (mode) {\n all = all.filter((c) => c.mode === mode);\n }\n\n all.sort((a, b) => {\n const aTime = a.createdAt ? new Date(a.createdAt).getTime() : 0;\n const bTime = b.createdAt ? new Date(b.createdAt).getTime() : 0;\n return bTime - aTime;\n });\n\n const total = all.length;\n const offset = (page - 1) * limit;\n const items = all.slice(offset, offset + limit);\n\n return c.json({ items, total, page, limit, totalPages: Math.ceil(total / limit) });\n});\n\n// ── Get a single conversation with full messages ──────────────────\nconversationsApi.get('/:id', async (c) => {\n const id = c.req.param('id');\n\n // Determine source from ID prefix\n for (const source of getAllSources()) {\n if (id.startsWith(`${source.name === 'claude-code' ? 'claude' : source.name}-`)) {\n const detail = await source.getConversationDetail(id);\n if (detail) return c.json(detail);\n }\n }\n\n return c.json({ error: 'Not found' }, 404);\n});\n","import { Hono } from 'hono';\nimport { SEVEN_DAYS_MS } from '../../utils/constants.js';\nimport {\n getAllSources,\n getCursorSource,\n type ToolUsage,\n type SkillUsage,\n} from '../../data-sources/registry.js';\nimport { getCached, CACHE_KEYS } from './cache.js';\n\nexport const toolsApi = new Hono();\n\ninterface McpServer {\n name: string;\n tools: string[];\n totalInvocations: number;\n lastUsed: string | null;\n status: 'active' | 'unused';\n}\n\n// ── Tool usage stats ──────────────────────────────────────────────\ntoolsApi.get('/usage', async (c) => {\n const cursorTools = await getCached(CACHE_KEYS.cursorToolUsage, () => getAllSources()[0].getToolUsage());\n const claudeTools = await getCached(CACHE_KEYS.claudeToolUsage, () => getAllSources()[1].getToolUsage());\n\n // Merge usage from both sources\n const merged = new Map<string, ToolUsage>();\n\n for (const tool of [...cursorTools, ...claudeTools]) {\n const existing = merged.get(tool.name);\n if (existing) {\n existing.count += tool.count;\n if (tool.lastUsed && (!existing.lastUsed || tool.lastUsed > existing.lastUsed)) {\n existing.lastUsed = tool.lastUsed;\n }\n } else {\n merged.set(tool.name, { ...tool });\n }\n }\n\n const tools = Array.from(merged.values()).sort((a, b) => b.count - a.count);\n return c.json({ tools, total: tools.reduce((sum, t) => sum + t.count, 0) });\n});\n\n// ── MCP servers ───────────────────────────────────────────────────\ntoolsApi.get('/mcps', async (c) => {\n const cursorTools = await getCached(CACHE_KEYS.cursorToolUsage, () => getAllSources()[0].getToolUsage());\n const claudeTools = await getCached(CACHE_KEYS.claudeToolUsage, () => getAllSources()[1].getToolUsage());\n const allTools = [...cursorTools, ...claudeTools];\n\n const serverMap = new Map<string, McpServer>();\n const now = Date.now();\n const sevenDaysMs = SEVEN_DAYS_MS;\n\n for (const tool of allTools) {\n if (!tool.isMcp || !tool.mcpServer) continue;\n\n const existing = serverMap.get(tool.mcpServer);\n if (existing) {\n if (!existing.tools.includes(tool.name)) {\n existing.tools.push(tool.name);\n }\n existing.totalInvocations += tool.count;\n if (tool.lastUsed && (!existing.lastUsed || tool.lastUsed > existing.lastUsed)) {\n existing.lastUsed = tool.lastUsed;\n }\n } else {\n serverMap.set(tool.mcpServer, {\n name: tool.mcpServer,\n tools: [tool.name],\n totalInvocations: tool.count,\n lastUsed: tool.lastUsed,\n status: 'unused',\n });\n }\n }\n\n // Set status\n for (const server of serverMap.values()) {\n if (server.lastUsed) {\n const lastUsedMs = new Date(server.lastUsed).getTime();\n server.status = now - lastUsedMs < sevenDaysMs ? 'active' : 'unused';\n }\n }\n\n const servers = Array.from(serverMap.values()).sort((a, b) => b.totalInvocations - a.totalInvocations);\n return c.json({ servers });\n});\n\n// ── Skills ────────────────────────────────────────────────────────\ntoolsApi.get('/skills', async (c) => {\n const skills = await getCached(CACHE_KEYS.cursorSkillUsage, () => getCursorSource().getSkillUsage());\n return c.json({ skills });\n});\n\n// Re-export types for backward compatibility\nexport type { ToolUsage, SkillUsage };\n","import { Hono } from 'hono';\nimport { existsSync, readdirSync, readFileSync, unlinkSync, mkdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { getLoreDir } from '../../utils/paths.js';\nimport { applyProposal, type PendingItem } from '../../writers/apply-proposal.js';\n\nexport const pendingApi = new Hono();\n\n// ── List pending items ────────────────────────────────────────────\npendingApi.get('/', (c) => {\n const loreDir = getLoreDir();\n const pendingDir = resolve(loreDir, 'pending');\n\n if (!existsSync(pendingDir)) return c.json({ items: [] });\n\n const items = readdirSync(pendingDir)\n .filter((f) => f.endsWith('.json'))\n .map((f) => {\n try {\n const raw = readFileSync(resolve(pendingDir, f), 'utf-8');\n return JSON.parse(raw) as PendingItem;\n } catch {\n return null;\n }\n })\n .filter((item): item is PendingItem => item !== null)\n .sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n\n return c.json({ items });\n});\n\n// ── Accept a pending item ─────────────────────────────────────────\npendingApi.post('/:id/accept', async (c) => {\n const id = c.req.param('id');\n const loreDir = getLoreDir();\n const pendingDir = resolve(loreDir, 'pending');\n const outputDir = resolve(loreDir, 'output');\n\n const item = findPendingItem(pendingDir, id);\n if (!item) return c.json({ error: 'Not found' }, 404);\n\n mkdirSync(outputDir, { recursive: true });\n applyProposal(item, outputDir);\n\n // Remove from pending\n try {\n unlinkSync(resolve(pendingDir, item.filename));\n } catch {\n // ignore\n }\n\n return c.json({ success: true, id });\n});\n\n// ── Reject a pending item ─────────────────────────────────────────\npendingApi.post('/:id/reject', (c) => {\n const id = c.req.param('id');\n const loreDir = getLoreDir();\n const pendingDir = resolve(loreDir, 'pending');\n\n const item = findPendingItem(pendingDir, id);\n if (!item) return c.json({ error: 'Not found' }, 404);\n\n try {\n unlinkSync(resolve(pendingDir, item.filename));\n } catch {\n return c.json({ error: 'Failed to remove' }, 500);\n }\n\n return c.json({ success: true, id });\n});\n\n// ── List output items ─────────────────────────────────────────────\npendingApi.get('/output', (c) => {\n const loreDir = getLoreDir();\n const outputDir = resolve(loreDir, 'output');\n\n if (!existsSync(outputDir)) return c.json({ items: [] });\n\n const items: Array<{ name: string; type: string; content: string }> = [];\n\n // Read changelog\n const changelogPath = resolve(outputDir, 'changelog.md');\n if (existsSync(changelogPath)) {\n items.push({\n name: 'changelog.md',\n type: 'changelog',\n content: readFileSync(changelogPath, 'utf-8'),\n });\n }\n\n // Read architecture\n const archPath = resolve(outputDir, 'architecture.md');\n if (existsSync(archPath)) {\n items.push({\n name: 'architecture.md',\n type: 'architecture',\n content: readFileSync(archPath, 'utf-8'),\n });\n }\n\n // Read ADRs\n const adrDir = resolve(outputDir, 'decisions');\n if (existsSync(adrDir)) {\n for (const file of readdirSync(adrDir).filter((f) => f.endsWith('.md'))) {\n items.push({\n name: `decisions/${file}`,\n type: 'adr',\n content: readFileSync(resolve(adrDir, file), 'utf-8'),\n });\n }\n }\n\n return c.json({ items });\n});\n\n// ── Helpers ───────────────────────────────────────────────────────\n\nfunction findPendingItem(pendingDir: string, id: string): PendingItem | null {\n if (!existsSync(pendingDir)) return null;\n\n for (const f of readdirSync(pendingDir).filter((f) => f.endsWith('.json'))) {\n try {\n const raw = readFileSync(resolve(pendingDir, f), 'utf-8');\n const item = JSON.parse(raw) as PendingItem;\n if (item.id === id) return item;\n } catch {\n continue;\n }\n }\n return null;\n}\n\n// applyProposal moved to src/writers/apply-proposal.ts\n","import { Hono } from 'hono';\nimport { PROBATION_DURATION_MS } from '../../utils/constants.js';\nimport {\n readVettingStore,\n writeVettingStore,\n findEntry,\n updateStatus,\n getEntriesByStatus,\n getEntriesNeedingReview,\n} from '../../vetting/store.js';\nimport { runActiveTests } from '../../vetting/tester.js';\nimport { analyzeFeedback, analyzeAllFeedback } from '../../vetting/feedback.js';\nimport { runScoringLifecycle, getScoringSummary } from '../../vetting/scoring.js';\nimport { analyzeDrift, type SkillDriftReport } from '../../drift/detector.js';\nimport type { RewriteStatus } from '../../vetting/types.js';\n\nexport const vettingApi = new Hono();\n\n// ── Get all vetting entries ──────────────────────────────────────\nvettingApi.get('/', (c) => {\n const store = readVettingStore();\n const grouped = getEntriesByStatus(store);\n const needsReview = getEntriesNeedingReview(store);\n\n // Enrich entries with drift/freshness data\n let overallFreshness = 100;\n try {\n const driftOverview = analyzeDrift();\n overallFreshness = driftOverview.overallScore;\n\n // Match drift reports to vetting entries by name\n for (const entry of store.entries) {\n if (entry.type !== 'skill') continue;\n const driftReport = matchDrift(entry.name, entry.path, driftOverview.skills);\n if (driftReport) {\n (entry as unknown as Record<string, unknown>).drift = {\n freshnessScore: driftReport.freshnessScore,\n freshnessStatus: driftReport.status,\n daysSinceUpdate: driftReport.daysSinceUpdate,\n lastUpdated: driftReport.lastUpdated,\n postUpdateConversations: driftReport.postUpdateConversations,\n totalConversations: driftReport.totalConversations,\n signals: driftReport.signals,\n };\n }\n }\n } catch {\n // Drift analysis is best-effort — don't fail the vetting response\n }\n\n return c.json({\n entries: store.entries,\n probation: grouped.probation,\n approved: grouped.approved,\n rejected: grouped.rejected,\n needsReview: needsReview.map((e) => e.id),\n total: store.entries.length,\n overallFreshness,\n });\n});\n\n/**\n * Match a vetting entry to its drift report by name/path.\n */\nfunction matchDrift(\n entryName: string,\n entryPath: string,\n driftSkills: SkillDriftReport[],\n): SkillDriftReport | null {\n const nameLower = entryName.toLowerCase();\n const pathLower = entryPath.toLowerCase();\n\n for (const report of driftSkills) {\n const reportNameLower = report.name.toLowerCase();\n const reportPathLower = report.skillPath.toLowerCase();\n\n // Exact path match\n if (pathLower && reportPathLower.includes(pathLower)) return report;\n if (pathLower && pathLower.includes(reportPathLower)) return report;\n\n // Name match\n if (nameLower === reportNameLower) return report;\n\n // Partial name match (e.g. \"premium-fed-sdk\" matches \"premium-fed-sdk-guide\")\n if (reportNameLower.includes(nameLower) || nameLower.includes(reportNameLower)) return report;\n }\n\n return null;\n}\n\n// ── Get single vetting entry ─────────────────────────────────────\nvettingApi.get('/:id', (c) => {\n const id = c.req.param('id');\n const store = readVettingStore();\n const entry = findEntry(store, id);\n\n if (!entry) {\n return c.json({ error: 'Entry not found' }, 404);\n }\n\n return c.json(entry);\n});\n\n// ── Approve a vetting entry ──────────────────────────────────────\nvettingApi.post('/:id/approve', (c) => {\n const id = c.req.param('id');\n const store = readVettingStore();\n updateStatus(store, id, 'approved');\n return c.json({ success: true });\n});\n\n// ── Reject a vetting entry ───────────────────────────────────────\nvettingApi.post('/:id/reject', (c) => {\n const id = c.req.param('id');\n const store = readVettingStore();\n updateStatus(store, id, 'rejected');\n return c.json({ success: true });\n});\n\n// ── Re-enter probation (after user edits) ────────────────────────\nvettingApi.post('/:id/re-probate', (c) => {\n const id = c.req.param('id');\n const store = readVettingStore();\n const entry = findEntry(store, id);\n if (!entry) return c.json({ error: 'Entry not found' }, 404);\n\n entry.status = 'probation';\n entry.probationEnds = new Date(Date.now() + PROBATION_DURATION_MS).toISOString();\n entry.feedback = [];\n entry.rewrites = [];\n entry.activeScore = { testsRun: 0, testsPassed: 0, lastTestAt: null };\n writeVettingStore(store);\n\n return c.json({ success: true });\n});\n\n// ── Trigger active tests ─────────────────────────────────────────\nvettingApi.post('/test', async (c) => {\n try {\n const results = await runActiveTests();\n return c.json({ results });\n } catch (err) {\n return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);\n }\n});\n\n// ── Analyze feedback for a single entry ──────────────────────────\nvettingApi.post('/:id/analyze', async (c) => {\n const id = c.req.param('id');\n try {\n const result = await analyzeFeedback(id);\n if (!result) return c.json({ error: 'Entry not found' }, 404);\n return c.json(result);\n } catch (err) {\n return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);\n }\n});\n\n// ── Analyze all feedback ─────────────────────────────────────────\nvettingApi.post('/analyze-all', async (c) => {\n try {\n const results = await analyzeAllFeedback();\n return c.json({ results });\n } catch (err) {\n return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);\n }\n});\n\n// ── Run scoring lifecycle ─────────────────────────────────────────\nvettingApi.post('/score', (c) => {\n const result = runScoringLifecycle();\n return c.json(result);\n});\n\n// ── Get scoring summary for an entry ─────────────────────────────\nvettingApi.get('/:id/score', (c) => {\n const id = c.req.param('id');\n const summary = getScoringSummary(id);\n if (!summary) return c.json({ error: 'Entry not found' }, 404);\n return c.json(summary);\n});\n\n// ── Update rewrite proposal status ───────────────────────────────\nvettingApi.post('/:id/rewrites/:rewriteId', async (c) => {\n const { id, rewriteId } = c.req.param();\n const body = await c.req.json<{ status: RewriteStatus }>();\n\n const store = readVettingStore();\n const entry = findEntry(store, id);\n if (!entry) return c.json({ error: 'Entry not found' }, 404);\n\n const rewrite = entry.rewrites.find((r) => r.id === rewriteId);\n if (!rewrite) return c.json({ error: 'Rewrite not found' }, 404);\n\n rewrite.status = body.status;\n if (body.status === 'applied') {\n rewrite.appliedAt = new Date().toISOString();\n }\n writeVettingStore(store);\n\n return c.json({ success: true });\n});\n","import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { getLoreDir } from '../utils/paths.js';\nimport { logger } from '../utils/logger.js';\nimport { PROBATION_DURATION_MS, REVIEW_INTERVAL_MS } from '../utils/constants.js';\nimport type {\n VettingStore,\n VettingEntry,\n VettingItemType,\n VettingStatus,\n FeedbackEntry,\n RewriteProposal,\n} from './types.js';\n\nfunction getStorePath(): string {\n return resolve(getLoreDir(), 'vetting.json');\n}\n\n/**\n * Read the vetting store from disk.\n */\nexport function readVettingStore(): VettingStore {\n const path = getStorePath();\n if (!existsSync(path)) {\n return { version: 1, entries: [] };\n }\n try {\n const raw = readFileSync(path, 'utf-8');\n return JSON.parse(raw) as VettingStore;\n } catch {\n logger.warn('Failed to read vetting store, starting fresh');\n return { version: 1, entries: [] };\n }\n}\n\n/**\n * Write the vetting store to disk.\n */\nexport function writeVettingStore(store: VettingStore): void {\n const path = getStorePath();\n mkdirSync(resolve(getLoreDir()), { recursive: true });\n writeFileSync(path, JSON.stringify(store, null, 2), 'utf-8');\n}\n\n/**\n * Find a vetting entry by ID.\n */\nexport function findEntry(store: VettingStore, id: string): VettingEntry | undefined {\n return store.entries.find((e) => e.id === id);\n}\n\n/**\n * Find a vetting entry by name and type.\n */\nexport function findEntryByName(\n store: VettingStore,\n name: string,\n type: VettingItemType,\n): VettingEntry | undefined {\n return store.entries.find((e) => e.name === name && e.type === type);\n}\n\n/**\n * Register a new skill or MCP for vetting (enters probation).\n */\nexport function registerForVetting(\n store: VettingStore,\n item: { type: VettingItemType; name: string; path: string },\n): VettingEntry {\n const now = new Date();\n const probationEnd = new Date(now.getTime() + PROBATION_DURATION_MS);\n\n const entry: VettingEntry = {\n id: `vet-${item.type}-${item.name}-${Date.now()}`,\n type: item.type,\n name: item.name,\n path: item.path,\n detectedAt: now.toISOString(),\n probationEnds: probationEnd.toISOString(),\n nextReviewAt: probationEnd.toISOString(),\n status: 'probation',\n passiveScore: {\n usageCount: 0,\n successRate: 1,\n conversationsUsedIn: 0,\n },\n activeScore: {\n testsRun: 0,\n testsPassed: 0,\n lastTestAt: null,\n },\n finalScore: 0,\n feedback: [],\n rewrites: [],\n };\n\n store.entries.push(entry);\n writeVettingStore(store);\n return entry;\n}\n\n/**\n * Record passive usage observation for a skill/MCP.\n */\nexport function recordPassiveUsage(\n store: VettingStore,\n entryId: string,\n success: boolean,\n conversationId?: string,\n): void {\n const entry = findEntry(store, entryId);\n if (!entry) return;\n\n entry.passiveScore.usageCount++;\n entry.passiveScore.conversationsUsedIn++;\n\n // Update running success rate\n const total = entry.passiveScore.usageCount;\n const currentSuccesses = Math.round(entry.passiveScore.successRate * (total - 1));\n entry.passiveScore.successRate = (currentSuccesses + (success ? 1 : 0)) / total;\n\n if (!success) {\n entry.feedback.push({\n timestamp: new Date().toISOString(),\n source: 'passive',\n category: 'failure',\n description: 'Usage failure detected during passive monitoring',\n evidence: conversationId ?? 'unknown',\n });\n }\n\n writeVettingStore(store);\n}\n\n/**\n * Record active test result for a skill/MCP.\n */\nexport function recordActiveTest(\n store: VettingStore,\n entryId: string,\n passed: boolean,\n testOutput: string,\n): void {\n const entry = findEntry(store, entryId);\n if (!entry) return;\n\n entry.activeScore.testsRun++;\n if (passed) entry.activeScore.testsPassed++;\n entry.activeScore.lastTestAt = new Date().toISOString();\n\n if (!passed) {\n entry.feedback.push({\n timestamp: new Date().toISOString(),\n source: 'active',\n category: 'quality',\n description: `Active test failed: ${testOutput.slice(0, 200)}`,\n evidence: testOutput,\n });\n }\n\n writeVettingStore(store);\n}\n\n/**\n * Add a feedback entry to a vetting entry.\n */\nexport function addFeedback(\n store: VettingStore,\n entryId: string,\n feedback: FeedbackEntry,\n): void {\n const entry = findEntry(store, entryId);\n if (!entry) return;\n\n entry.feedback.push(feedback);\n writeVettingStore(store);\n}\n\n/**\n * Add a rewrite proposal to a vetting entry.\n */\nexport function addRewriteProposal(\n store: VettingStore,\n entryId: string,\n proposal: RewriteProposal,\n): void {\n const entry = findEntry(store, entryId);\n if (!entry) return;\n\n entry.rewrites.push(proposal);\n writeVettingStore(store);\n}\n\n/**\n * Update the status of a vetting entry.\n */\nexport function updateStatus(\n store: VettingStore,\n entryId: string,\n status: VettingStatus,\n): void {\n const entry = findEntry(store, entryId);\n if (!entry) return;\n\n entry.status = status;\n\n if (status === 'approved') {\n const now = new Date();\n entry.nextReviewAt = new Date(now.getTime() + REVIEW_INTERVAL_MS).toISOString();\n }\n\n writeVettingStore(store);\n}\n\n/**\n * Get entries that need review (probation ended or periodic review due).\n */\nexport function getEntriesNeedingReview(store: VettingStore): VettingEntry[] {\n const now = new Date();\n return store.entries.filter((entry) => {\n if (entry.status === 'rejected') return false;\n if (entry.status === 'probation' && new Date(entry.probationEnds) <= now) return true;\n if (entry.status === 'approved' && new Date(entry.nextReviewAt) <= now) return true;\n return false;\n });\n}\n\n/**\n * Get all entries grouped by status.\n */\nexport function getEntriesByStatus(store: VettingStore): {\n probation: VettingEntry[];\n approved: VettingEntry[];\n rejected: VettingEntry[];\n} {\n return {\n probation: store.entries.filter((e) => e.status === 'probation'),\n approved: store.entries.filter((e) => e.status === 'approved'),\n rejected: store.entries.filter((e) => e.status === 'rejected'),\n };\n}\n","import { readFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { logger } from '../utils/logger.js';\nimport { DistillationEngine } from '../distiller/engine.js';\nimport { loadPrompt } from '../distiller/prompts.js';\nimport { getLoreDir } from '../utils/paths.js';\nimport { getOtherSkillNames } from '../skill-generator/skill-digest.js';\nimport {\n readVettingStore,\n recordActiveTest,\n getEntriesNeedingReview,\n} from './store.js';\nimport type { VettingEntry } from './types.js';\n\nexport interface TestResult {\n entryId: string;\n entryName: string;\n passed: boolean;\n output: string;\n scores?: {\n clarity?: number;\n triggerAccuracy?: number;\n outputQuality?: number;\n instructionCompleteness?: number;\n total?: number;\n };\n}\n\n/**\n * Run active tests for all entries currently in probation or needing review.\n */\nexport async function runActiveTests(\n options: {\n onOutput?: (line: string) => void;\n signal?: AbortSignal;\n } = {},\n): Promise<TestResult[]> {\n const store = readVettingStore();\n const toTest = [\n ...store.entries.filter((e) => e.status === 'probation'),\n ...getEntriesNeedingReview(store),\n ];\n\n // Deduplicate\n const seen = new Set<string>();\n const unique = toTest.filter((e) => {\n if (seen.has(e.id)) return false;\n seen.add(e.id);\n return true;\n });\n\n if (unique.length === 0) {\n options.onOutput?.('[vetting] No entries to test');\n return [];\n }\n\n options.onOutput?.(`[vetting] Testing ${unique.length} entries...`);\n\n const results: TestResult[] = [];\n const engine = new DistillationEngine(getLoreDir());\n\n for (const entry of unique) {\n if (options.signal?.aborted) break;\n\n options.onOutput?.(`[vetting] Testing: ${entry.name} (${entry.type})`);\n\n try {\n const result = entry.type === 'skill'\n ? await testSkill(engine, entry, options)\n : await testMcp(engine, entry, options);\n\n results.push(result);\n recordActiveTest(store, entry.id, result.passed, result.output);\n options.onOutput?.(`[vetting] ${entry.name}: ${result.passed ? 'PASSED' : 'FAILED'}`);\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n options.onOutput?.(`[vetting] ${entry.name}: ERROR - ${errorMsg}`);\n results.push({\n entryId: entry.id,\n entryName: entry.name,\n passed: false,\n output: errorMsg,\n });\n recordActiveTest(store, entry.id, false, errorMsg);\n }\n }\n\n return results;\n}\n\n/**\n * Test a single skill by invoking Claude with the skill-vetter prompt.\n */\nasync function testSkill(\n engine: DistillationEngine,\n entry: VettingEntry,\n options: { onOutput?: (line: string) => void; signal?: AbortSignal },\n): Promise<TestResult> {\n // Read the skill content\n let skillContent = '';\n if (existsSync(entry.path)) {\n skillContent = readFileSync(entry.path, 'utf-8');\n } else {\n return {\n entryId: entry.id,\n entryName: entry.name,\n passed: false,\n output: `Skill file not found: ${entry.path}`,\n };\n }\n\n // Get other skills for conflict checking\n const otherSkills = getOtherSkillNames(entry.name);\n\n // Load the test prompt\n let testPrompt: string;\n try {\n testPrompt = loadPrompt('test-skill');\n } catch {\n testPrompt = getDefaultSkillTestPrompt();\n }\n\n testPrompt = testPrompt\n .replace('{skill_content}', skillContent)\n .replace('{other_skills}', otherSkills.join(', '))\n .replace('{user_rules}', '');\n\n // Invoke Claude for evaluation\n const alignment = {\n matched: [],\n unmatchedCommits: [],\n unmatchedConversations: [{\n sessionId: `test-${entry.id}`,\n source: 'cursor' as const,\n startedAt: new Date().toISOString(),\n endedAt: new Date().toISOString(),\n branch: null,\n cwd: null,\n turns: [{\n role: 'user' as const,\n content: testPrompt,\n timestamp: new Date().toISOString(),\n }],\n filesModified: [],\n }],\n };\n\n const output = await engine.distill(alignment, [], {\n onOutput: options.onOutput,\n signal: options.signal,\n mode: 'headless',\n phaseTag: 'vet',\n });\n\n // Try to parse structured scores from the output\n let scores: TestResult['scores'];\n let passed = true;\n\n try {\n const raw = output.changelog || JSON.stringify(output);\n const parsed = JSON.parse(raw);\n if (parsed.scores) {\n scores = parsed.scores;\n passed = (parsed.scores.total ?? 0) >= 60;\n }\n } catch {\n // If we can't parse structured output, consider it passed if we got any output\n passed = !!(output.changes.length || output.decisions.length || output.patterns.length || output.changelog);\n }\n\n return {\n entryId: entry.id,\n entryName: entry.name,\n passed,\n output: JSON.stringify(output, null, 2).slice(0, 2000),\n scores,\n };\n}\n\n/**\n * Test a single MCP by invoking Claude with the MCP test prompt.\n */\nasync function testMcp(\n engine: DistillationEngine,\n entry: VettingEntry,\n options: { onOutput?: (line: string) => void; signal?: AbortSignal },\n): Promise<TestResult> {\n // Load MCP config to get tool definitions\n const mcpConfig = getMcpConfig(entry.name);\n\n let testPrompt: string;\n try {\n testPrompt = loadPrompt('test-mcp');\n } catch {\n testPrompt = getDefaultMcpTestPrompt();\n }\n\n testPrompt = testPrompt\n .replace('{mcp_config}', JSON.stringify(mcpConfig, null, 2))\n .replace('{usage_data}', JSON.stringify(entry.passiveScore, null, 2));\n\n const alignment = {\n matched: [],\n unmatchedCommits: [],\n unmatchedConversations: [{\n sessionId: `test-${entry.id}`,\n source: 'cursor' as const,\n startedAt: new Date().toISOString(),\n endedAt: new Date().toISOString(),\n branch: null,\n cwd: null,\n turns: [{\n role: 'user' as const,\n content: testPrompt,\n timestamp: new Date().toISOString(),\n }],\n filesModified: [],\n }],\n };\n\n const output = await engine.distill(alignment, [], {\n onOutput: options.onOutput,\n signal: options.signal,\n mode: 'headless',\n phaseTag: 'vet',\n });\n\n return {\n entryId: entry.id,\n entryName: entry.name,\n passed: !!(output.changes.length || output.decisions.length || output.changelog),\n output: JSON.stringify(output, null, 2).slice(0, 2000),\n };\n}\n\n/**\n * Get MCP configuration for a server by name.\n */\nfunction getMcpConfig(serverName: string): Record<string, unknown> {\n // Check Claude Code MCP config\n const claudeConfigPath = join(homedir(), '.claude', 'mcp_servers.json');\n if (existsSync(claudeConfigPath)) {\n try {\n const config = JSON.parse(readFileSync(claudeConfigPath, 'utf-8'));\n if (config[serverName]) return config[serverName];\n } catch {\n // ignore\n }\n }\n\n // Check Cursor MCP settings\n const cursorConfigPath = join(homedir(), '.cursor', 'mcp.json');\n if (existsSync(cursorConfigPath)) {\n try {\n const config = JSON.parse(readFileSync(cursorConfigPath, 'utf-8'));\n if (config.mcpServers?.[serverName]) return config.mcpServers[serverName];\n } catch {\n // ignore\n }\n }\n\n return { name: serverName, tools: [] };\n}\n\nfunction getDefaultSkillTestPrompt(): string {\n return `Evaluate this skill by analyzing its SKILL.md structure, triggers, and instructions.\nScore each dimension (clarity, triggerAccuracy, outputQuality, instructionCompleteness) from 0-25.\nReturn JSON with scores and test results.`;\n}\n\nfunction getDefaultMcpTestPrompt(): string {\n return `Evaluate this MCP server configuration by analyzing its tools and reliability.\nScore reliability, tool coverage, and documentation from 0-33.\nReturn JSON with scores and test results.`;\n}\n","import { readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { logger } from '../utils/logger.js';\nimport { DistillationEngine } from '../distiller/engine.js';\nimport { loadPrompt } from '../distiller/prompts.js';\nimport { getLoreDir } from '../utils/paths.js';\nimport {\n readVettingStore,\n writeVettingStore,\n findEntry,\n addRewriteProposal,\n} from './store.js';\nimport type { VettingEntry, FeedbackEntry, RewriteProposal } from './types.js';\n\nexport interface FeedbackAnalysisResult {\n entryId: string;\n overallHealth: 'healthy' | 'needs-attention' | 'critical';\n strengths: string[];\n weaknesses: Array<{\n area: string;\n description: string;\n suggestedFix: string;\n severity: 'minor' | 'structural';\n }>;\n conflicts: Array<{\n conflictsWith: string;\n description: string;\n resolution: string;\n }>;\n proposedRewrites: RewriteProposal[];\n autoApplied: number;\n pendingApproval: number;\n}\n\n/**\n * Analyze feedback for a single vetting entry and propose improvements.\n *\n * - Groups feedback by category\n * - Invokes Claude with the analyze-feedback prompt\n * - For minor changes: auto-applies them\n * - For structural changes: creates RewriteProposal for user approval\n */\nexport async function analyzeFeedback(\n entryId: string,\n options: {\n onOutput?: (line: string) => void;\n signal?: AbortSignal;\n } = {},\n): Promise<FeedbackAnalysisResult | null> {\n const store = readVettingStore();\n const entry = findEntry(store, entryId);\n if (!entry) {\n logger.warn(`Feedback analysis: entry ${entryId} not found`);\n return null;\n }\n\n if (entry.feedback.length === 0) {\n options.onOutput?.(`[feedback] No feedback for ${entry.name}, skipping analysis`);\n return {\n entryId,\n overallHealth: 'healthy',\n strengths: [],\n weaknesses: [],\n conflicts: [],\n proposedRewrites: [],\n autoApplied: 0,\n pendingApproval: 0,\n };\n }\n\n options.onOutput?.(`[feedback] Analyzing ${entry.feedback.length} feedback entries for ${entry.name}...`);\n\n // Read the current SKILL.md content (for skills)\n let skillContent = '';\n if (entry.type === 'skill' && existsSync(entry.path)) {\n skillContent = readFileSync(entry.path, 'utf-8');\n }\n\n // Build the analysis prompt\n let analyzePrompt: string;\n try {\n analyzePrompt = loadPrompt('analyze-feedback');\n } catch {\n analyzePrompt = getDefaultAnalyzePrompt();\n }\n\n analyzePrompt = analyzePrompt\n .replace('{skill_content}', skillContent)\n .replace('{feedback_entries}', JSON.stringify(entry.feedback, null, 2))\n .replace('{previous_scores}', JSON.stringify({\n passive: entry.passiveScore,\n active: entry.activeScore,\n final: entry.finalScore,\n }, null, 2));\n\n // Invoke Claude for analysis\n const engine = new DistillationEngine(getLoreDir());\n const alignment = {\n matched: [],\n unmatchedCommits: [],\n unmatchedConversations: [{\n sessionId: `feedback-${entry.id}`,\n source: 'cursor' as const,\n startedAt: new Date().toISOString(),\n endedAt: new Date().toISOString(),\n branch: null,\n cwd: null,\n turns: [{\n role: 'user' as const,\n content: analyzePrompt,\n timestamp: new Date().toISOString(),\n }],\n filesModified: [],\n }],\n };\n\n let analysisOutput;\n try {\n analysisOutput = await engine.distill(alignment, [], {\n onOutput: options.onOutput,\n signal: options.signal,\n mode: 'headless',\n phaseTag: 'vet',\n });\n } catch (err) {\n logger.error(`Feedback analysis failed for ${entry.name}:`, err);\n return null;\n }\n\n // Parse the analysis result\n let analysis: {\n overallHealth?: string;\n strengths?: string[];\n weaknesses?: Array<{\n area: string;\n description: string;\n suggestedFix: string;\n severity: string;\n }>;\n conflicts?: Array<{\n conflictsWith: string;\n description: string;\n resolution: string;\n }>;\n rewrites?: Array<{\n reason: string;\n diff: string;\n severity: string;\n }>;\n } = {};\n\n try {\n const raw = analysisOutput.changelog || JSON.stringify(analysisOutput);\n analysis = JSON.parse(raw);\n } catch {\n logger.debug('Could not parse structured feedback analysis');\n }\n\n // Process rewrites\n const proposedRewrites: RewriteProposal[] = [];\n let autoApplied = 0;\n let pendingApproval = 0;\n\n if (analysis.rewrites) {\n for (const rewrite of analysis.rewrites) {\n const severity = rewrite.severity === 'minor' ? 'minor' : 'structural';\n const proposal: RewriteProposal = {\n id: `rw-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n timestamp: new Date().toISOString(),\n reason: rewrite.reason,\n diff: rewrite.diff,\n severity,\n status: severity === 'minor' ? 'applied' : 'pending',\n appliedAt: severity === 'minor' ? new Date().toISOString() : undefined,\n };\n\n proposedRewrites.push(proposal);\n\n if (severity === 'minor' && entry.type === 'skill' && existsSync(entry.path)) {\n // Auto-apply minor changes\n try {\n applyDiff(entry.path, rewrite.diff);\n autoApplied++;\n options.onOutput?.(`[feedback] Auto-applied minor fix: ${rewrite.reason}`);\n } catch (err) {\n // If diff application fails, queue as pending instead\n proposal.status = 'pending';\n proposal.appliedAt = undefined;\n pendingApproval++;\n options.onOutput?.(`[feedback] Could not auto-apply: ${rewrite.reason} (queued for review)`);\n }\n } else if (severity === 'structural') {\n pendingApproval++;\n options.onOutput?.(`[feedback] Structural change queued: ${rewrite.reason}`);\n }\n\n addRewriteProposal(store, entryId, proposal);\n }\n }\n\n return {\n entryId,\n overallHealth: (analysis.overallHealth as 'healthy' | 'needs-attention' | 'critical') ?? 'healthy',\n strengths: analysis.strengths ?? [],\n weaknesses: (analysis.weaknesses ?? []).map((w) => ({\n area: w.area,\n description: w.description,\n suggestedFix: w.suggestedFix,\n severity: w.severity === 'minor' ? 'minor' as const : 'structural' as const,\n })),\n conflicts: analysis.conflicts ?? [],\n proposedRewrites,\n autoApplied,\n pendingApproval,\n };\n}\n\n/**\n * Run feedback analysis for all entries that have accumulated feedback.\n */\nexport async function analyzeAllFeedback(\n options: {\n onOutput?: (line: string) => void;\n signal?: AbortSignal;\n } = {},\n): Promise<FeedbackAnalysisResult[]> {\n const store = readVettingStore();\n const withFeedback = store.entries.filter((e) => e.feedback.length > 0);\n\n if (withFeedback.length === 0) {\n options.onOutput?.('[feedback] No entries have feedback to analyze');\n return [];\n }\n\n options.onOutput?.(`[feedback] Analyzing feedback for ${withFeedback.length} entries...`);\n\n const results: FeedbackAnalysisResult[] = [];\n for (const entry of withFeedback) {\n if (options.signal?.aborted) break;\n const result = await analyzeFeedback(entry.id, options);\n if (result) results.push(result);\n }\n\n return results;\n}\n\n/**\n * Apply a unified diff to a file.\n * Simple implementation: replaces old lines with new lines.\n */\nfunction applyDiff(filePath: string, diff: string): void {\n const content = readFileSync(filePath, 'utf-8');\n const lines = diff.split('\\n');\n\n // Extract removed and added lines from the diff\n const removals: string[] = [];\n const additions: string[] = [];\n\n for (const line of lines) {\n if (line.startsWith('-') && !line.startsWith('---')) {\n removals.push(line.slice(1));\n } else if (line.startsWith('+') && !line.startsWith('+++')) {\n additions.push(line.slice(1));\n }\n }\n\n if (removals.length === 0) {\n // Pure addition: can't determine where to insert, skip\n throw new Error('Cannot apply: no removal context to match');\n }\n\n // Find the removal block in the content\n const removalBlock = removals.join('\\n');\n const additionBlock = additions.join('\\n');\n\n if (!content.includes(removalBlock)) {\n throw new Error('Cannot apply: removal block not found in file');\n }\n\n const newContent = content.replace(removalBlock, additionBlock);\n writeFileSync(filePath, newContent, 'utf-8');\n}\n\nfunction getDefaultAnalyzePrompt(): string {\n return `Analyze the accumulated feedback for this skill/MCP and propose improvements.\nGroup feedback by category, identify patterns, and suggest specific edits.\nReturn JSON with overallHealth, strengths, weaknesses, conflicts, and rewrites.`;\n}\n","import { logger } from '../utils/logger.js';\nimport {\n MS_PER_DAY,\n PROBATION_DURATION_MS,\n REVIEW_INTERVAL_MS,\n FRESHNESS_HALF_LIFE_DAYS,\n} from '../utils/constants.js';\nimport {\n readVettingStore,\n writeVettingStore,\n findEntry,\n updateStatus,\n getEntriesNeedingReview,\n} from './store.js';\nimport { validateSkillStructure } from './structural-validator.js';\nimport type { VettingEntry, VettingStatus } from './types.js';\n\n/**\n * Default scoring configuration.\n * Can be overridden in ~/.lore/config.json under `vetting`.\n *\n * Multi-dimensional scoring (enhanced):\n * - passive: Usage signals from conversation monitoring\n * - active: Automated test results\n * - structural: SKILL.md format and content quality\n * - freshness: How recently the skill was generated/updated\n */\nexport interface ScoringConfig {\n passiveWeight: number; // Default: 0.25\n activeWeight: number; // Default: 0.25\n structuralWeight: number; // Default: 0.30\n freshnessWeight: number; // Default: 0.20\n approveThreshold: number; // Default: 60\n rejectThreshold: number; // Default: 50 (below this = rejected)\n}\n\nconst DEFAULT_CONFIG: ScoringConfig = {\n passiveWeight: 0.25,\n activeWeight: 0.25,\n structuralWeight: 0.30,\n freshnessWeight: 0.20,\n approveThreshold: 60,\n rejectThreshold: 50,\n};\n\n/**\n * Multi-dimensional score breakdown for a vetting entry.\n */\nexport interface ScoreBreakdown {\n passive: number;\n active: number;\n structural: number;\n freshness: number;\n total: number;\n}\n\n/**\n * Calculate the final score for a vetting entry.\n *\n * Enhanced 4-dimension formula:\n * passiveNormalized = min(1, usageCount / 10) * 0.5 + successRate * 0.5\n * activeNormalized = testsPassed / max(1, testsRun)\n * structuralNormalized = validateSkillStructure().total / 100\n * freshnessNormalized = decay curve based on days since last update\n *\n * finalScore = round(\n * passiveWeight * passiveNormalized * 100 +\n * activeWeight * activeNormalized * 100 +\n * structuralWeight * structuralNormalized * 100 +\n * freshnessWeight * freshnessNormalized * 100\n * )\n */\nexport function calculateScore(\n entry: VettingEntry,\n _previousScore?: number,\n config: ScoringConfig = DEFAULT_CONFIG,\n): number {\n const breakdown = calculateScoreBreakdown(entry, config);\n return breakdown.total;\n}\n\n/**\n * Calculate a detailed score breakdown for a vetting entry.\n * Returns individual dimension scores and the weighted total.\n */\nexport function calculateScoreBreakdown(\n entry: VettingEntry,\n config: ScoringConfig = DEFAULT_CONFIG,\n): ScoreBreakdown {\n // Passive component\n const usageNorm = Math.min(1, entry.passiveScore.usageCount / 10);\n const passiveNormalized = usageNorm * 0.5 + entry.passiveScore.successRate * 0.5;\n\n // Active component\n const activeNormalized = entry.activeScore.testsRun > 0\n ? entry.activeScore.testsPassed / entry.activeScore.testsRun\n : 0.5; // Default to 0.5 if no tests run yet\n\n // Structural component (new)\n let structuralNormalized = 0.5; // Default for non-skill entries or if path missing\n if (entry.type === 'skill' && entry.path) {\n try {\n const structuralResult = validateSkillStructure(entry.path);\n structuralNormalized = structuralResult.total / 100;\n } catch (err) {\n logger.debug(`Structural validation failed for ${entry.name}: ${err}`);\n }\n }\n\n // Freshness component (new): decay curve based on age\n const freshnessNormalized = calculateFreshness(entry);\n\n const passive = Math.round(passiveNormalized * 100);\n const active = Math.round(activeNormalized * 100);\n const structural = Math.round(structuralNormalized * 100);\n const freshness = Math.round(freshnessNormalized * 100);\n\n const total = Math.round(\n config.passiveWeight * passive +\n config.activeWeight * active +\n config.structuralWeight * structural +\n config.freshnessWeight * freshness,\n );\n\n return {\n passive,\n active,\n structural,\n freshness,\n total: Math.max(0, Math.min(100, total)),\n };\n}\n\n/**\n * Calculate freshness score (0-1) based on how recently the skill was detected/updated.\n *\n * Uses an exponential decay curve:\n * - 0 days old → 1.0\n * - 7 days old → ~0.85\n * - 30 days old → ~0.50\n * - 90 days old → ~0.15\n * - 180 days old → ~0.02\n */\nfunction calculateFreshness(entry: VettingEntry): number {\n const now = Date.now();\n const detectedAt = new Date(entry.detectedAt).getTime();\n const ageDays = (now - detectedAt) / MS_PER_DAY;\n\n // Exponential decay with half-life of FRESHNESS_HALF_LIFE_DAYS\n return Math.exp(-0.693 * ageDays / FRESHNESS_HALF_LIFE_DAYS);\n}\n\n/**\n * Run the scoring lifecycle for all entries that need review.\n *\n * - Entries in probation past their end date: score and promote/reject\n * - Approved entries past their review date: re-score and flag if degraded\n *\n * Returns a summary of actions taken.\n */\nexport function runScoringLifecycle(\n config: ScoringConfig = DEFAULT_CONFIG,\n): {\n reviewed: number;\n approved: number;\n rejected: number;\n flagged: number;\n extended: number;\n} {\n const store = readVettingStore();\n const needsReview = getEntriesNeedingReview(store);\n\n let reviewed = 0;\n let approved = 0;\n let rejected = 0;\n let flagged = 0;\n let extended = 0;\n\n for (const entry of needsReview) {\n reviewed++;\n const previousScore = entry.finalScore;\n const newScore = calculateScore(entry, previousScore, config);\n entry.finalScore = newScore;\n\n logger.debug(`Scoring: ${entry.name} = ${newScore} (was ${previousScore})`);\n\n if (entry.status === 'probation') {\n if (newScore >= config.approveThreshold) {\n // Approved!\n updateStatus(store, entry.id, 'approved');\n approved++;\n logger.info(`Vetting: \"${entry.name}\" approved with score ${newScore}`);\n } else if (newScore >= config.rejectThreshold) {\n // Borderline: extend probation by 3 more days\n entry.probationEnds = new Date(Date.now() + PROBATION_DURATION_MS).toISOString();\n entry.nextReviewAt = entry.probationEnds;\n extended++;\n logger.info(`Vetting: \"${entry.name}\" borderline (${newScore}), extending probation`);\n } else {\n // Rejected\n updateStatus(store, entry.id, 'rejected');\n rejected++;\n logger.info(`Vetting: \"${entry.name}\" rejected with score ${newScore}`);\n }\n } else if (entry.status === 'approved') {\n // Periodic review\n if (newScore < config.rejectThreshold) {\n // Score dropped below threshold: flag for review (re-enter probation)\n entry.status = 'probation';\n entry.probationEnds = new Date(Date.now() + PROBATION_DURATION_MS).toISOString();\n entry.nextReviewAt = entry.probationEnds;\n flagged++;\n logger.info(`Vetting: \"${entry.name}\" flagged for review (score dropped to ${newScore})`);\n } else {\n // Schedule next review\n entry.nextReviewAt = new Date(Date.now() + REVIEW_INTERVAL_MS).toISOString();\n logger.debug(`Vetting: \"${entry.name}\" re-approved, next review: ${entry.nextReviewAt}`);\n }\n }\n }\n\n writeVettingStore(store);\n\n return { reviewed, approved, rejected, flagged, extended };\n}\n\n/**\n * Get a scoring summary for a single entry (for UI display).\n * Enhanced with multi-dimensional breakdown.\n */\nexport function getScoringSummary(entryId: string): {\n passiveComponent: number;\n activeComponent: number;\n structuralComponent: number;\n freshnessComponent: number;\n finalScore: number;\n status: VettingStatus;\n daysInProbation: number | null;\n daysUntilReview: number | null;\n /** Structural validation issues (if skill type). */\n structuralIssues: string[];\n} | null {\n const store = readVettingStore();\n const entry = findEntry(store, entryId);\n if (!entry) return null;\n\n const breakdown = calculateScoreBreakdown(entry);\n\n // Get structural issues if it's a skill\n let structuralIssues: string[] = [];\n if (entry.type === 'skill' && entry.path) {\n try {\n const result = validateSkillStructure(entry.path);\n structuralIssues = result.issues;\n } catch { /* ignore */ }\n }\n\n const now = Date.now();\n let daysInProbation: number | null = null;\n let daysUntilReview: number | null = null;\n\n if (entry.status === 'probation') {\n daysInProbation = Math.round((now - new Date(entry.detectedAt).getTime()) / MS_PER_DAY);\n const remaining = new Date(entry.probationEnds).getTime() - now;\n daysUntilReview = Math.max(0, Math.round(remaining / MS_PER_DAY));\n } else if (entry.status === 'approved') {\n const remaining = new Date(entry.nextReviewAt).getTime() - now;\n daysUntilReview = Math.max(0, Math.round(remaining / MS_PER_DAY));\n }\n\n return {\n passiveComponent: breakdown.passive,\n activeComponent: breakdown.active,\n structuralComponent: breakdown.structural,\n freshnessComponent: breakdown.freshness,\n finalScore: entry.finalScore,\n status: entry.status,\n daysInProbation,\n daysUntilReview,\n structuralIssues,\n };\n}\n","/**\n * Structural validation for SKILL.md files.\n *\n * Validates skills against industry-standard quality criteria\n * inspired by everything-claude-code (43k stars):\n * - Required sections present\n * - Max line count (500 lines)\n * - Section completeness scoring\n * - Scope validation (single domain)\n *\n * Used by the vetting pipeline during probation scoring.\n */\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport { logger } from '../utils/logger.js';\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport interface StructuralScore {\n /** Overall score 0-100. */\n total: number;\n /** Whether the skill has valid frontmatter (name, description). */\n hasFrontmatter: boolean;\n /** Line count of the SKILL.md. */\n lineCount: number;\n /** Whether the skill exceeds the max line limit. */\n exceedsMaxLines: boolean;\n /** Sections found in the skill. */\n sectionsFound: string[];\n /** Required sections that are missing. */\n sectionsMissing: string[];\n /** Score breakdown per dimension (0-25 each). */\n dimensions: {\n /** Frontmatter + metadata completeness. */\n metadata: number;\n /** Required sections coverage. */\n structure: number;\n /** Content quality signals (examples, code blocks, lists). */\n content: number;\n /** Scope focus (penalize if too many unrelated topics). */\n scope: number;\n };\n /** Human-readable issues found. */\n issues: string[];\n}\n\n// ── Constants ────────────────────────────────────────────────────\n\n/** Maximum recommended line count for a skill. */\nconst MAX_LINES = 500;\n\n/**\n * Required sections for a well-structured SKILL.md.\n * Based on everything-claude-code's quality criteria.\n */\nconst REQUIRED_SECTIONS = [\n 'quick_reference',\n 'architecture',\n 'entry_points',\n 'workflow',\n 'patterns',\n 'conventions',\n 'validation',\n] as const;\n\n/**\n * Alternative section names that satisfy the same requirement.\n * Maps from required section to acceptable alternatives.\n */\nconst SECTION_ALIASES: Record<string, string[]> = {\n quick_reference: ['quick reference', 'quick-reference', 'overview', 'summary', 'getting started'],\n architecture: ['architecture overview', 'system design', 'design', 'how it works'],\n entry_points: ['entry points', 'key files', 'file map', 'project structure', 'structure'],\n workflow: ['development workflow', 'dev workflow', 'how to', 'usage', 'getting started'],\n patterns: ['coding patterns', 'code patterns', 'established patterns', 'conventions and patterns'],\n conventions: ['coding conventions', 'code style', 'style guide', 'rules'],\n validation: ['validation checkpoints', 'checklist', 'quality checks', 'review checklist'],\n};\n\n// ── Public API ───────────────────────────────────────────────────\n\n/**\n * Validate a SKILL.md file structurally.\n * Returns a StructuralScore with detailed breakdown.\n */\nexport function validateSkillStructure(skillPath: string): StructuralScore {\n const issues: string[] = [];\n\n if (!existsSync(skillPath)) {\n return {\n total: 0,\n hasFrontmatter: false,\n lineCount: 0,\n exceedsMaxLines: false,\n sectionsFound: [],\n sectionsMissing: [...REQUIRED_SECTIONS],\n dimensions: { metadata: 0, structure: 0, content: 0, scope: 0 },\n issues: [`Skill file not found: ${skillPath}`],\n };\n }\n\n let content: string;\n try {\n content = readFileSync(skillPath, 'utf-8');\n } catch (err) {\n return {\n total: 0,\n hasFrontmatter: false,\n lineCount: 0,\n exceedsMaxLines: false,\n sectionsFound: [],\n sectionsMissing: [...REQUIRED_SECTIONS],\n dimensions: { metadata: 0, structure: 0, content: 0, scope: 0 },\n issues: [`Cannot read skill file: ${err}`],\n };\n }\n\n const lines = content.split('\\n');\n const lineCount = lines.length;\n\n // 1. Metadata dimension (0-25)\n const metadataScore = scoreMetadata(content, issues);\n\n // 2. Structure dimension (0-25)\n const { score: structureScore, found, missing } = scoreStructure(content, issues);\n\n // 3. Content dimension (0-25)\n const contentScore = scoreContent(content, issues);\n\n // 4. Scope dimension (0-25)\n const scopeScore = scoreScope(content, lineCount, issues);\n\n // Line count check\n const exceedsMaxLines = lineCount > MAX_LINES;\n if (exceedsMaxLines) {\n issues.push(`Skill exceeds ${MAX_LINES} line limit (${lineCount} lines)`);\n }\n\n const total = Math.round(metadataScore + structureScore + contentScore + scopeScore);\n\n return {\n total: Math.max(0, Math.min(100, total)),\n hasFrontmatter: metadataScore > 10,\n lineCount,\n exceedsMaxLines,\n sectionsFound: found,\n sectionsMissing: missing,\n dimensions: {\n metadata: Math.round(metadataScore),\n structure: Math.round(structureScore),\n content: Math.round(contentScore),\n scope: Math.round(scopeScore),\n },\n issues,\n };\n}\n\n/**\n * Quick check: does a skill pass minimum structural quality?\n * Returns true if the skill scores >= 40 (out of 100).\n */\nexport function meetsMinimumQuality(skillPath: string): boolean {\n const result = validateSkillStructure(skillPath);\n return result.total >= 40;\n}\n\n// ── Scoring dimensions ───────────────────────────────────────────\n\nfunction scoreMetadata(content: string, issues: string[]): number {\n let score = 0;\n\n // Check for YAML frontmatter\n const hasFrontmatter = content.startsWith('---');\n if (hasFrontmatter) {\n score += 5;\n\n const fmEnd = content.indexOf('---', 3);\n if (fmEnd > 0) {\n const fm = content.slice(3, fmEnd);\n\n // Check for name field\n if (/^name:\\s*.+/m.test(fm)) {\n score += 5;\n } else {\n issues.push('Missing \"name\" in frontmatter');\n }\n\n // Check for description field\n if (/^description:\\s*.+/m.test(fm)) {\n score += 5;\n } else {\n issues.push('Missing \"description\" in frontmatter');\n }\n\n // Check for lastUpdated\n if (/^lastUpdated:\\s*.+/m.test(fm)) {\n score += 3;\n }\n\n // Check for generatedBy\n if (/^generatedBy:\\s*.+/m.test(fm)) {\n score += 2;\n }\n\n // Check for triggers\n if (/triggers/i.test(fm)) {\n score += 5;\n }\n }\n } else {\n issues.push('Missing YAML frontmatter (---name, description---)');\n }\n\n return Math.min(25, score);\n}\n\nfunction scoreStructure(\n content: string,\n issues: string[],\n): { score: number; found: string[]; missing: string[] } {\n const lower = content.toLowerCase();\n const found: string[] = [];\n const missing: string[] = [];\n\n for (const section of REQUIRED_SECTIONS) {\n const aliases = [section, ...(SECTION_ALIASES[section] ?? [])];\n const isPresent = aliases.some((alias) => {\n // Match XML tags like <quick_reference> or markdown headers like ## Quick Reference\n const xmlTag = `<${alias.replace(/\\s+/g, '_')}>`;\n const mdHeader = new RegExp(`^#{1,3}\\\\s*${alias.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}`, 'mi');\n return lower.includes(xmlTag) || mdHeader.test(content);\n });\n\n if (isPresent) {\n found.push(section);\n } else {\n missing.push(section);\n }\n }\n\n if (missing.length > 0) {\n issues.push(`Missing sections: ${missing.join(', ')}`);\n }\n\n // Score: 25 * (found / total)\n const score = (found.length / REQUIRED_SECTIONS.length) * 25;\n return { score, found, missing };\n}\n\nfunction scoreContent(content: string, issues: string[]): number {\n let score = 0;\n\n // Code blocks (examples)\n const codeBlocks = (content.match(/```[\\s\\S]*?```/g) || []).length;\n if (codeBlocks > 0) {\n score += Math.min(8, codeBlocks * 2);\n } else {\n issues.push('No code examples found (use ``` code blocks)');\n }\n\n // Tables\n const tables = (content.match(/\\|.*\\|.*\\|/g) || []).length;\n if (tables > 0) {\n score += Math.min(5, tables);\n }\n\n // Bullet lists\n const bullets = (content.match(/^[\\s]*[-*]\\s/gm) || []).length;\n if (bullets > 0) {\n score += Math.min(5, Math.ceil(bullets / 3));\n }\n\n // Inline code references (backtick-wrapped terms)\n const inlineCode = (content.match(/`[^`]+`/g) || []).length;\n if (inlineCode > 0) {\n score += Math.min(4, Math.ceil(inlineCode / 5));\n }\n\n // File paths mentioned\n const filePaths = (content.match(/`[^`]*\\.[a-z]{1,5}`/g) || []).length;\n if (filePaths > 0) {\n score += Math.min(3, Math.ceil(filePaths / 3));\n } else {\n issues.push('No file path references found');\n }\n\n return Math.min(25, score);\n}\n\nfunction scoreScope(content: string, lineCount: number, issues: string[]): number {\n let score = 25; // Start at max, deduct for issues\n\n // Penalize for excessive length\n if (lineCount > MAX_LINES) {\n const overagePct = (lineCount - MAX_LINES) / MAX_LINES;\n score -= Math.min(10, Math.round(overagePct * 20));\n }\n\n // Check for too many top-level headers (suggests multi-domain)\n const topHeaders = (content.match(/^#\\s+.+/gm) || []).length;\n if (topHeaders > 3) {\n score -= 5;\n issues.push(`Too many top-level headers (${topHeaders}) — consider splitting into multiple skills`);\n }\n\n // Very short skills are likely incomplete\n if (lineCount < 20) {\n score -= 15;\n issues.push('Skill is very short (< 20 lines) — likely incomplete');\n } else if (lineCount < 50) {\n score -= 5;\n issues.push('Skill is short (< 50 lines) — may lack depth');\n }\n\n // Check for empty/stub content\n const nonEmptyLines = content.split('\\n').filter((l) => l.trim().length > 0).length;\n const contentRatio = nonEmptyLines / Math.max(1, lineCount);\n if (contentRatio < 0.3) {\n score -= 10;\n issues.push('Low content density (too many empty lines)');\n }\n\n return Math.max(0, Math.min(25, score));\n}\n","/**\n * Doc Drift Detection & Freshness Scoring.\n *\n * Compares generated skills against recent conversation activity to detect\n * when documentation has drifted from reality. Produces a freshness score\n * per skill (0–100) and a list of drift signals.\n *\n * Data sources:\n * - Generated skills: {repo}/.cursor/skills/{name}-guide/SKILL.md\n * - Conversation summaries: ~/.lore/summaries/conversations/{id}.json\n * - Summary index: ~/.lore/summaries/index.json\n */\n\nimport { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport {\n readSummaryIndex,\n loadConversationSummary,\n getReposWithSummaries,\n} from '../summarizer/cache.js';\nimport { buildRepoCatalog } from '../classifier/repo-catalog.js';\nimport type { StoredConversationSummary } from '../summarizer/types.js';\n\n// ── Types ─────────────────────────────────────────────────────────\n\nexport interface DriftSignal {\n /** What kind of drift was detected */\n type: 'new-pattern' | 'new-decision' | 'file-activity' | 'stale-age' | 'skill-referenced';\n /** Human-readable description */\n description: string;\n /** Severity: how much this signal affects the score */\n severity: 'low' | 'medium' | 'high';\n /** Source conversation ID (if applicable) */\n conversationId?: string;\n /** When this drift was detected */\n date?: string;\n}\n\nexport interface SkillDriftReport {\n /** Skill name (from frontmatter) */\n name: string;\n /** Repo name (e.g. \"wix-private/premium-domains\") */\n repo: string;\n /** Absolute path to the SKILL.md */\n skillPath: string;\n /** Freshness score 0–100 (100 = perfectly fresh) */\n freshnessScore: number;\n /** When the skill was last updated */\n lastUpdated: string | null;\n /** How many days since last update */\n daysSinceUpdate: number;\n /** Conversations that happened after the skill was last updated */\n postUpdateConversations: number;\n /** Total conversations for this repo */\n totalConversations: number;\n /** Detected drift signals */\n signals: DriftSignal[];\n /** Overall status */\n status: 'fresh' | 'aging' | 'stale' | 'critical';\n}\n\nexport interface DriftOverview {\n /** Overall health score (average of all skill scores) */\n overallScore: number;\n /** Total skills analyzed */\n totalSkills: number;\n /** Breakdown by status */\n statusCounts: { fresh: number; aging: number; stale: number; critical: number };\n /** Per-skill reports, sorted by freshness (worst first) */\n skills: SkillDriftReport[];\n /** When this analysis was performed */\n analyzedAt: string;\n}\n\n// ── Score constants ───────────────────────────────────────────────\n\nconst SCORE_BASE = 100;\nconst DECAY_PER_DAY = 1.5; // -1.5 per day since last update\nconst DECAY_CAP = 40; // max decay from age alone\nconst ACTIVITY_PENALTY = 4; // -4 per post-update conversation touching related files\nconst ACTIVITY_CAP = 30; // max penalty from activity\nconst PATTERN_PENALTY = 6; // -6 per new pattern not in skill\nconst DECISION_PENALTY = 6; // -6 per new decision not in skill\nconst PATTERN_DECISION_CAP = 25; // max penalty from patterns/decisions\nconst SKILL_REF_PENALTY = 3; // -3 per conversation that referenced (and may have overridden) the skill\n\n// ── Public API ────────────────────────────────────────────────────\n\n/**\n * Analyze all generated skills and produce drift reports.\n */\nexport function analyzeDrift(): DriftOverview {\n const skills = findGeneratedSkills();\n const summaryIndex = readSummaryIndex();\n\n const reports: SkillDriftReport[] = [];\n\n for (const skill of skills) {\n const report = analyzeSkill(skill, summaryIndex);\n reports.push(report);\n }\n\n // Sort worst first\n reports.sort((a, b) => a.freshnessScore - b.freshnessScore);\n\n const totalSkills = reports.length;\n const overallScore = totalSkills > 0\n ? Math.round(reports.reduce((sum, r) => sum + r.freshnessScore, 0) / totalSkills)\n : 100;\n\n const statusCounts = { fresh: 0, aging: 0, stale: 0, critical: 0 };\n for (const r of reports) {\n statusCounts[r.status]++;\n }\n\n return {\n overallScore,\n totalSkills,\n statusCounts,\n skills: reports,\n analyzedAt: new Date().toISOString(),\n };\n}\n\n// ── Skill discovery ───────────────────────────────────────────────\n\ninterface FoundSkill {\n name: string;\n repo: string;\n repoPath: string;\n skillPath: string;\n lastUpdated: string | null;\n content: string;\n}\n\n/**\n * Find all Lore-generated skills across known repos.\n */\nfunction findGeneratedSkills(): FoundSkill[] {\n const catalog = buildRepoCatalog();\n const skills: FoundSkill[] = [];\n\n for (const repo of catalog) {\n if (!existsSync(repo.path)) continue;\n\n const skillsDir = join(repo.path, '.cursor', 'skills');\n if (!existsSync(skillsDir)) continue;\n\n try {\n const entries = readdirSync(skillsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const skillPath = join(skillsDir, entry.name, 'SKILL.md');\n if (!existsSync(skillPath)) continue;\n\n try {\n const content = readFileSync(skillPath, 'utf-8');\n\n // Only analyze Lore-generated skills\n if (!content.includes('generatedBy: lore') && !content.includes('generatedBy: \"lore\"')) {\n continue;\n }\n\n const name = extractFrontmatterField(content, 'name') ?? entry.name;\n const lastUpdated = extractFrontmatterField(content, 'lastUpdated');\n\n skills.push({\n name,\n repo: repo.name,\n repoPath: repo.path,\n skillPath,\n lastUpdated: lastUpdated ? lastUpdated.replace(/^[\"']|[\"']$/g, '') : null,\n content,\n });\n } catch {\n // skip unreadable files\n }\n }\n } catch {\n // skip unreadable dirs\n }\n }\n\n // Also check global skill dirs for Lore-generated skills\n const globalDirs = [\n join(homedir(), '.cursor', 'skills'),\n join(homedir(), '.cursor', 'skills-cursor'),\n join(homedir(), '.claude', 'skills'),\n ];\n\n for (const dir of globalDirs) {\n if (!existsSync(dir)) continue;\n try {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const skillPath = join(dir, entry.name, 'SKILL.md');\n if (!existsSync(skillPath)) continue;\n\n // Skip if already found (from repo-local check)\n if (skills.some((s) => s.skillPath === skillPath)) continue;\n\n try {\n const content = readFileSync(skillPath, 'utf-8');\n if (!content.includes('generatedBy: lore') && !content.includes('generatedBy: \"lore\"')) {\n continue;\n }\n\n const name = extractFrontmatterField(content, 'name') ?? entry.name;\n const lastUpdated = extractFrontmatterField(content, 'lastUpdated');\n\n // Try to match to a repo from the content\n const repoName = inferRepoFromSkill(content, entry.name, catalog);\n\n skills.push({\n name,\n repo: repoName ?? entry.name,\n repoPath: '',\n skillPath,\n lastUpdated: lastUpdated ? lastUpdated.replace(/^[\"']|[\"']$/g, '') : null,\n content,\n });\n } catch {\n // skip\n }\n }\n } catch {\n // skip\n }\n }\n\n return skills;\n}\n\n// ── Per-skill analysis ────────────────────────────────────────────\n\nfunction analyzeSkill(\n skill: FoundSkill,\n summaryIndex: ReturnType<typeof readSummaryIndex>,\n): SkillDriftReport {\n const now = Date.now();\n const signals: DriftSignal[] = [];\n\n // Calculate age\n let daysSinceUpdate = 0;\n if (skill.lastUpdated) {\n const updatedMs = new Date(skill.lastUpdated).getTime();\n daysSinceUpdate = Math.max(0, Math.floor((now - updatedMs) / (1000 * 60 * 60 * 24)));\n } else {\n // No lastUpdated — try file mtime\n try {\n const stat = statSync(skill.skillPath);\n daysSinceUpdate = Math.max(0, Math.floor((now - stat.mtimeMs) / (1000 * 60 * 60 * 24)));\n } catch {\n daysSinceUpdate = 30; // assume stale\n }\n }\n\n // Find conversations for this repo\n const repoConversations: Array<{\n id: string;\n date: string | null;\n repo: string | null;\n }> = [];\n\n for (const [id, entry] of Object.entries(summaryIndex.conversations)) {\n if (matchesRepo(entry.repo, skill.repo, skill.name)) {\n repoConversations.push({ id, date: entry.conversationDate, repo: entry.repo });\n }\n }\n\n const totalConversations = repoConversations.length;\n\n // Find post-update conversations\n const updateMs = skill.lastUpdated ? new Date(skill.lastUpdated).getTime() : 0;\n const postUpdateConvs = repoConversations.filter((c) => {\n if (!c.date) return false;\n return new Date(c.date).getTime() > updateMs;\n });\n\n // Load post-update conversation summaries for deeper analysis\n const newPatterns: string[] = [];\n const newDecisions: string[] = [];\n let fileActivityCount = 0;\n let skillRefCount = 0;\n\n for (const conv of postUpdateConvs) {\n const summary = loadConversationSummary(conv.id);\n if (!summary?.ai) continue;\n\n // Check for new patterns\n for (const pattern of summary.ai.patterns) {\n if (pattern.pattern && !skillMentions(skill.content, pattern.pattern)) {\n newPatterns.push(pattern.pattern);\n }\n }\n\n // Check for new decisions\n for (const decision of summary.ai.decisions) {\n if (decision.decision && !skillMentions(skill.content, decision.decision)) {\n newDecisions.push(decision.decision);\n }\n }\n\n // Check for file modifications in the repo\n if (summary.ai.filesModified.length > 0) {\n fileActivityCount++;\n }\n\n // Check if the skill itself was referenced\n if (summary.ai.skillsReferenced.length > 0) {\n skillRefCount++;\n }\n }\n\n // ── Build drift signals ─────────────────────────────────────────\n\n if (daysSinceUpdate > 7) {\n signals.push({\n type: 'stale-age',\n description: `Skill hasn't been updated in ${daysSinceUpdate} days`,\n severity: daysSinceUpdate > 30 ? 'high' : daysSinceUpdate > 14 ? 'medium' : 'low',\n });\n }\n\n if (fileActivityCount > 0) {\n signals.push({\n type: 'file-activity',\n description: `${fileActivityCount} conversation${fileActivityCount > 1 ? 's' : ''} modified files since last skill update`,\n severity: fileActivityCount > 5 ? 'high' : fileActivityCount > 2 ? 'medium' : 'low',\n });\n }\n\n // Deduplicate and limit patterns/decisions\n const uniquePatterns = [...new Set(newPatterns)].slice(0, 5);\n const uniqueDecisions = [...new Set(newDecisions)].slice(0, 5);\n\n for (const pattern of uniquePatterns) {\n signals.push({\n type: 'new-pattern',\n description: `New pattern not in skill: \"${truncate(pattern, 80)}\"`,\n severity: 'medium',\n });\n }\n\n for (const decision of uniqueDecisions) {\n signals.push({\n type: 'new-decision',\n description: `New decision not in skill: \"${truncate(decision, 80)}\"`,\n severity: 'medium',\n });\n }\n\n if (skillRefCount > 0) {\n signals.push({\n type: 'skill-referenced',\n description: `Skill was referenced in ${skillRefCount} conversation${skillRefCount > 1 ? 's' : ''} — may need updates based on usage`,\n severity: 'low',\n });\n }\n\n // ── Calculate freshness score ───────────────────────────────────\n\n let score = SCORE_BASE;\n\n // Age decay\n score -= Math.min(daysSinceUpdate * DECAY_PER_DAY, DECAY_CAP);\n\n // File activity penalty\n score -= Math.min(fileActivityCount * ACTIVITY_PENALTY, ACTIVITY_CAP);\n\n // Pattern/decision drift penalty\n const patternDecisionPenalty = (uniquePatterns.length * PATTERN_PENALTY)\n + (uniqueDecisions.length * DECISION_PENALTY);\n score -= Math.min(patternDecisionPenalty, PATTERN_DECISION_CAP);\n\n // Skill reference penalty (light)\n score -= Math.min(skillRefCount * SKILL_REF_PENALTY, 10);\n\n // Clamp\n score = Math.max(0, Math.min(100, Math.round(score)));\n\n // Determine status\n let status: SkillDriftReport['status'];\n if (score >= 75) status = 'fresh';\n else if (score >= 50) status = 'aging';\n else if (score >= 25) status = 'stale';\n else status = 'critical';\n\n return {\n name: skill.name,\n repo: skill.repo,\n skillPath: skill.skillPath,\n freshnessScore: score,\n lastUpdated: skill.lastUpdated,\n daysSinceUpdate,\n postUpdateConversations: postUpdateConvs.length,\n totalConversations,\n signals,\n status,\n };\n}\n\n// ── Helpers ───────────────────────────────────────────────────────\n\nfunction extractFrontmatterField(content: string, field: string): string | null {\n const match = content.match(new RegExp(`^${field}:\\\\s*(.+?)\\\\s*$`, 'm'));\n return match ? match[1].trim() : null;\n}\n\n/**\n * Check if a repo name from a conversation matches a skill's repo.\n * Supports partial matching (e.g. \"premium-domains\" matches \"wix-private/premium-domains\").\n */\nfunction matchesRepo(\n conversationRepo: string | null,\n skillRepo: string,\n skillName: string,\n): boolean {\n if (!conversationRepo) return false;\n\n const convLower = conversationRepo.toLowerCase();\n const skillLower = skillRepo.toLowerCase();\n const nameLower = skillName.toLowerCase();\n\n // Exact match\n if (convLower === skillLower) return true;\n\n // Partial match (repo name without org prefix)\n const convBase = convLower.includes('/') ? convLower.split('/').pop()! : convLower;\n const skillBase = skillLower.includes('/') ? skillLower.split('/').pop()! : skillLower;\n\n if (convBase === skillBase) return true;\n\n // Match against skill name (sanitized repo name)\n if (convBase.includes(nameLower) || nameLower.includes(convBase)) return true;\n\n return false;\n}\n\n/**\n * Check if the skill content mentions a concept (fuzzy substring match).\n * Used to detect whether new patterns/decisions are already covered.\n */\nfunction skillMentions(skillContent: string, concept: string): boolean {\n const lower = skillContent.toLowerCase();\n // Extract key words (3+ chars) from the concept and check if most appear\n const words = concept\n .toLowerCase()\n .split(/\\W+/)\n .filter((w) => w.length >= 3);\n\n if (words.length === 0) return true; // empty concept = assume covered\n\n const threshold = Math.ceil(words.length * 0.6); // 60% of words must appear\n let matches = 0;\n for (const word of words) {\n if (lower.includes(word)) matches++;\n }\n\n return matches >= threshold;\n}\n\nfunction truncate(str: string, maxLen: number): string {\n if (str.length <= maxLen) return str;\n return str.slice(0, maxLen - 3) + '...';\n}\n\n/**\n * Try to infer a repo name from skill content or name.\n */\nfunction inferRepoFromSkill(\n _content: string,\n dirName: string,\n catalog: Array<{ name: string; path: string }>,\n): string | null {\n // Try matching the directory name against catalog entries\n const clean = dirName.replace(/-guide$/, '').toLowerCase();\n for (const entry of catalog) {\n const entryBase = entry.name.includes('/')\n ? entry.name.split('/').pop()!.toLowerCase()\n : entry.name.toLowerCase();\n if (entryBase === clean || entryBase.includes(clean) || clean.includes(entryBase)) {\n return entry.name;\n }\n }\n return null;\n}\n","import { Hono } from 'hono';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join, basename } from 'node:path';\nimport {\n generateAllRepoSkills,\n} from '../../skill-generator/generator.js';\nimport { buildRepoCatalog } from '../../classifier/repo-catalog.js';\nimport { summarizeAllConversations } from '../../summarizer/summarizer.js';\nimport { clusterAllRepos } from '../../summarizer/clusterer.js';\nimport { selectReposForProcessing, getSelectedRepoNames } from '../../repos/selector.js';\nimport { loadConfig } from '../../config/loader.js';\nimport { getLoreDir, type KnownRepo } from '../../utils/paths.js';\nimport type { SkillGenerationResult } from '../../skill-generator/types.js';\nimport { errorMessage } from '../../utils/errors.js';\nimport { z } from 'zod';\n\n// ── Request body schemas ─────────────────────────────────────────\n\nconst generateBodySchema = z.object({\n repo: z.string().optional(),\n force: z.boolean().optional(),\n selectedRepoNames: z.array(z.string()).optional(),\n}).passthrough();\n\nexport const skillGeneratorApi = new Hono();\n\n// ── Streaming state (same pattern as classify) ────────────────────\n\nlet genOutputLines: string[] = [];\nlet genRunning = false;\nlet genPhase = '';\nlet genError: string | null = null;\nlet genProgress: { current: number; total: number; percent: number } | null = null;\nlet genAbortController: AbortController | null = null;\nlet genResults: SkillGenerationResult[] = [];\n\nfunction appendGenOutput(line: string): void {\n const timestamp = new Date().toISOString().slice(11, 19);\n genOutputLines.push(`[${timestamp}] ${line}`);\n}\n\n// ── Trigger skill generation for all repos ────────────────────────\n\nskillGeneratorApi.post('/generate', async (c) => {\n if (genRunning) {\n return c.json({ error: 'Skill generation already running' }, 409);\n }\n\n const rawBody = await c.req.json().catch(() => ({}));\n const body = generateBodySchema.parse(rawBody);\n const repoFilter = body.repo;\n const force = body.force;\n const selectedRepoNames = body.selectedRepoNames;\n\n // Reset state\n genOutputLines = [];\n genRunning = true;\n genPhase = 'Starting skill generation...';\n genError = null;\n genProgress = null;\n genResults = [];\n genAbortController = new AbortController();\n\n // Fire-and-forget\n runGeneration(repoFilter, force, selectedRepoNames).catch(() => { /* handled inside */ });\n\n return c.json({ started: true });\n});\n\nasync function runGeneration(repoFilter?: string, force?: boolean, uiSelectedRepoNames?: string[]): Promise<void> {\n try {\n // ── Repo selection ──────────────────────────────────────────────\n // Prefer repo names passed directly from the UI (avoids race\n // conditions between config persistence and generation start).\n // Falls back to config-based selection when not provided.\n const loreDir = getLoreDir();\n const config = loadConfig(loreDir);\n const repoSelection = await selectReposForProcessing(config.repos);\n\n let selectedRepoNames: string[];\n let selectedRepos: KnownRepo[];\n\n if (uiSelectedRepoNames && uiSelectedRepoNames.length > 0) {\n // UI passed explicit selection — filter the discovered repos to\n // match only those names (case-insensitive).\n const uiSet = new Set(uiSelectedRepoNames.map((n) => n.toLowerCase()));\n const matched = repoSelection.all.filter((sr) => {\n const name = sr.repo.remoteName ?? sr.repo.shortName;\n return uiSet.has(name.toLowerCase());\n });\n selectedRepos = matched.map((sr) => sr.repo);\n selectedRepoNames = matched.map((sr) => sr.repo.remoteName ?? sr.repo.shortName);\n\n appendGenOutput(\n `[selection] Repo selection (ui-explicit): ${selectedRepos.length} of ${repoSelection.all.length} repos selected`,\n );\n } else {\n // Fallback: use config-based selection\n selectedRepoNames = getSelectedRepoNames(repoSelection);\n selectedRepos = repoSelection.selected.map((sr) => sr.repo);\n\n appendGenOutput(\n `[selection] Repo selection (${repoSelection.mode}): ${selectedRepos.length} of ${repoSelection.all.length} repos selected`,\n );\n }\n\n // Phase 1+2: Summarize and cluster conversations\n appendGenOutput('[summarize] Summarizing conversations...');\n genPhase = 'Summarizing conversations...';\n\n try {\n const { result: sumResult } = await summarizeAllConversations({\n repoFilter,\n selectedRepoNames,\n force,\n onOutput: (line) => {\n appendGenOutput(line);\n if (line.includes('[summarize]')) {\n const countMatch = line.match(/\\[(\\d+)\\/(\\d+)\\]/);\n if (countMatch) {\n genPhase = `Summarizing (${countMatch[1]}/${countMatch[2]})...`;\n }\n }\n },\n signal: genAbortController?.signal,\n });\n\n appendGenOutput(`[summarize] Done: ${sumResult.summarized} summarized, ${sumResult.skipped} cached`);\n\n appendGenOutput('[cluster] Clustering by repo...');\n genPhase = 'Clustering by repo...';\n\n const reposClustered = await clusterAllRepos({\n repoFilter,\n selectedRepoNames,\n onOutput: (line) => {\n appendGenOutput(line);\n if (line.includes('[cluster]')) {\n const countMatch = line.match(/\\[(\\d+)\\/(\\d+)\\]/);\n if (countMatch) {\n genPhase = `Clustering (${countMatch[1]}/${countMatch[2]})...`;\n }\n }\n },\n signal: genAbortController?.signal,\n });\n\n appendGenOutput(`[cluster] Done: ${reposClustered} repos clustered`);\n } catch (err: unknown) {\n appendGenOutput(`[summarize] Warning: summarization failed (non-fatal): ${errorMessage(err)}`);\n }\n\n // Phase 3: Generate skills — only for selected repos\n appendGenOutput('[skill-gen] Starting per-repo skill generation...');\n genPhase = 'Generating skills...';\n\n const results = await generateAllRepoSkills({\n repoFilter,\n selectedRepos,\n force,\n useWorktree: true,\n onOutput: (line) => {\n appendGenOutput(line);\n\n // Track repo-level progress: \"[1/24]\" or \"24 repos to process\"\n const progressMatch = line.match(/\\[(\\d+)\\/(\\d+)\\]/);\n if (progressMatch) {\n const current = parseInt(progressMatch[1], 10);\n const batchTotal = parseInt(progressMatch[2], 10);\n genProgress = {\n current,\n total: batchTotal,\n percent: Math.round((current / batchTotal) * 100),\n };\n genPhase = `Generating skills (${current}/${batchTotal})...`;\n }\n // Pick up the deduplicated total from the \"N repos to process\" line\n const totalMatch = line.match(/(\\d+) repos to process/);\n if (totalMatch) {\n const total = parseInt(totalMatch[1], 10);\n genProgress = { current: 0, total, percent: 0 };\n genPhase = `Generating skills (0/${total})...`;\n }\n\n // Track completions: \"→ generated|updated|skipped|failed\"\n const statusMatch = line.match(/→ (generated|updated|skipped|failed)/);\n if (statusMatch) {\n const repoMatch = line.match(/\\] (.+?) →/);\n if (repoMatch) {\n genPhase = `${repoMatch[1]}: ${statusMatch[1]}`;\n }\n }\n\n // Track tool calls: \"Tool: Bash(...)\"\n const toolMatch = line.match(/Tool: (\\w+)\\(/);\n if (toolMatch) {\n const repoMatch = line.match(/\\[skill-gen\\] \\[(.+?)\\]/);\n const repo = repoMatch ? repoMatch[1] : '';\n genPhase = `${repo}: running ${toolMatch[1]}`;\n }\n\n // Track writing progress\n if (line.includes('Writing:')) {\n const repoMatch = line.match(/\\[skill-gen\\] \\[(.+?)\\]/);\n const repo = repoMatch ? repoMatch[1] : '';\n genPhase = `${repo}: writing SKILL.md`;\n }\n\n // Track invocation start\n if (line.includes('Invoking Claude for')) {\n const repoMatch = line.match(/Invoking Claude for (.+?)\\.\\.\\./);\n if (repoMatch) genPhase = `${repoMatch[1]}: researching...`;\n }\n\n // Track \"Still working\" heartbeats\n if (line.includes('Still working')) {\n const repoMatch = line.match(/\\[skill-gen\\] \\[(.+?)\\]/);\n const elapsed = line.match(/(\\d+)s elapsed/);\n if (repoMatch) {\n genPhase = `${repoMatch[1]}: working${elapsed ? ` (${elapsed[1]}s)` : ''}...`;\n }\n }\n\n // Track \"Done in Xs\"\n if (line.includes('] Done')) {\n const repoMatch = line.match(/\\[skill-gen\\] \\[(.+?)\\] Done/);\n if (repoMatch) genPhase = `${repoMatch[1]}: complete`;\n }\n },\n signal: genAbortController?.signal,\n });\n\n genResults = results;\n const generated = results.filter((r) => r.status === 'generated' || r.status === 'updated').length;\n const failed = results.filter((r) => r.status === 'failed').length;\n const totalProcessed = results.length;\n\n appendGenOutput(`[skill-gen] Done! ${generated} generated/updated, ${failed} failed`);\n genPhase = `Done — ${generated} generated/updated`;\n genProgress = { current: totalProcessed, total: totalProcessed, percent: 100 };\n } catch (err: unknown) {\n const msg = errorMessage(err);\n appendGenOutput(`[skill-gen] ERROR: ${msg}`);\n genError = msg;\n genPhase = 'Failed';\n } finally {\n genRunning = false;\n genAbortController = null;\n }\n}\n\n// ── Get generation status ─────────────────────────────────────────\n\nskillGeneratorApi.get('/generate/status', (c) => {\n return c.json({\n running: genRunning,\n phase: genPhase,\n error: genError,\n progress: genProgress,\n results: genResults,\n });\n});\n\n// ── Get generation output lines (polling) ─────────────────────────\n\nskillGeneratorApi.get('/generate/output', (c) => {\n const since = parseInt(c.req.query('since') ?? '0', 10);\n const lines = genOutputLines.slice(since);\n return c.json({ lines, total: genOutputLines.length });\n});\n\n// ── Stop a running generation ─────────────────────────────────────\n\nskillGeneratorApi.post('/generate/stop', (c) => {\n if (!genRunning || !genAbortController) {\n return c.json({ error: 'No generation is currently running' }, 409);\n }\n genAbortController.abort();\n genAbortController = null;\n appendGenOutput('[skill-gen] Generation cancelled by user.');\n genRunning = false;\n genPhase = 'Cancelled';\n genError = null;\n return c.json({ stopped: true });\n});\n\n// ── Check skill status for a specific repo ────────────────────────\n\nskillGeneratorApi.get('/repo-status', (c) => {\n const repoPath = c.req.query('repoPath');\n if (!repoPath) {\n return c.json({ error: 'repoPath query param required' }, 400);\n }\n\n // Look for a SKILL.md in the repo's .cursor/skills/ directory\n const skillsDir = join(repoPath, '.cursor', 'skills');\n if (!existsSync(skillsDir)) {\n return c.json({ exists: false, path: null, lastUpdated: null });\n }\n\n // Find any *-guide directory\n try {\n const { readdirSync } = require('node:fs');\n const entries = readdirSync(skillsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && entry.name.endsWith('-guide')) {\n const skillPath = join(skillsDir, entry.name, 'SKILL.md');\n if (existsSync(skillPath)) {\n const content = readFileSync(skillPath, 'utf-8');\n const lastMatch = content.match(/^lastUpdated:\\s*[\"']?(.+?)[\"']?\\s*$/m);\n const convMatch = content.match(/^conversationCount:\\s*(\\d+)\\s*$/m);\n return c.json({\n exists: true,\n path: skillPath,\n lastUpdated: lastMatch ? lastMatch[1] : null,\n conversationCount: convMatch ? parseInt(convMatch[1], 10) : null,\n });\n }\n }\n }\n } catch { /* ignore */ }\n\n return c.json({ exists: false, path: null, lastUpdated: null });\n});\n","/**\n * Server-Sent Events (SSE) API for real-time scan progress.\n *\n * Provides live streaming of scan phases, generation output,\n * and rate-limit events to the web dashboard.\n *\n * Endpoint: GET /api/events/stream\n *\n * Event types:\n * - scan:start — Scan started with repos and trigger info\n * - scan:phase — Phase started/completed with metrics\n * - scan:progress — Progress update within a phase\n * - scan:rateLimit — Rate-limit event\n * - scan:end — Scan completed\n * - skill:output — Streaming skill generation output\n * - heartbeat — Keep-alive pulse every 15s\n */\n\nimport { Hono } from 'hono';\nimport { streamSSE } from 'hono/streaming';\nimport { writeFileSync } from 'node:fs';\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport interface SSEEvent {\n type: string;\n data: Record<string, unknown>;\n timestamp: string;\n}\n\ntype SSEClient = {\n id: string;\n send: (event: SSEEvent) => void;\n};\n\n// ── Client registry ──────────────────────────────────────────────\n\nconst clients = new Map<string, SSEClient>();\n\n/**\n * Broadcast an event to all connected SSE clients.\n */\nexport function broadcastEvent(type: string, data: Record<string, unknown>): void {\n const event: SSEEvent = {\n type,\n data,\n timestamp: new Date().toISOString(),\n };\n\n for (const client of clients.values()) {\n try {\n client.send(event);\n } catch {\n // Client disconnected, will be cleaned up\n clients.delete(client.id);\n }\n }\n}\n\n/**\n * Get the number of connected SSE clients.\n */\nexport function getConnectedClients(): number {\n return clients.size;\n}\n\n// ── Convenience broadcasters ─────────────────────────────────────\n\nexport function broadcastScanStart(repos: string[], trigger: string): void {\n broadcastEvent('scan:start', { repos, trigger });\n}\n\nexport function broadcastPhaseUpdate(\n phase: string,\n status: 'started' | 'completed',\n metrics?: { durationMs?: number; itemsProcessed?: number },\n): void {\n broadcastEvent('scan:phase', { phase, status, ...metrics });\n}\n\nexport function broadcastProgress(\n phase: string,\n completed: number,\n total: number,\n label?: string,\n): void {\n broadcastEvent('scan:progress', { phase, completed, total, label });\n}\n\nexport function broadcastRateLimit(\n retryNumber: number,\n waitSeconds: number,\n): void {\n broadcastEvent('scan:rateLimit', { retryNumber, waitSeconds });\n}\n\nexport function broadcastScanEnd(\n success: boolean,\n durationMs: number,\n itemsProcessed: number,\n): void {\n broadcastEvent('scan:end', { success, durationMs, itemsProcessed });\n}\n\nexport function broadcastSkillOutput(\n repoName: string,\n chunk: string,\n): void {\n broadcastEvent('skill:output', { repoName, chunk });\n}\n\nexport function broadcastFileProgress(\n repoName: string,\n file: string,\n completed: number,\n total: number,\n): void {\n broadcastEvent('skill:fileProgress', { repoName, file, completed, total });\n}\n\n// ── Hono route ───────────────────────────────────────────────────\n\nexport const eventsApi = new Hono();\n\neventsApi.get('/stream', (c) => {\n return streamSSE(c, async (stream) => {\n const clientId = `sse-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n\n // Register client\n const client: SSEClient = {\n id: clientId,\n send: (event) => {\n stream.writeSSE({\n event: event.type,\n data: JSON.stringify(event.data),\n id: event.timestamp,\n });\n },\n };\n clients.set(clientId, client);\n\n // Send initial connection event\n await stream.writeSSE({\n event: 'connected',\n data: JSON.stringify({ clientId, connectedClients: clients.size }),\n });\n\n // Heartbeat every 15 seconds\n const heartbeat = setInterval(() => {\n try {\n stream.writeSSE({\n event: 'heartbeat',\n data: JSON.stringify({ time: new Date().toISOString(), clients: clients.size }),\n });\n } catch {\n clearInterval(heartbeat);\n clients.delete(clientId);\n }\n }, 15_000);\n\n // Keep connection alive until client disconnects.\n // Use max 32-bit signed int (~24.8 days) to avoid Node.js TimeoutOverflowWarning\n // (Number.MAX_SAFE_INTEGER overflows the 32-bit timer and gets clamped to 1ms).\n try {\n await stream.sleep(2_147_483_647);\n } catch {\n // Client disconnected\n } finally {\n clearInterval(heartbeat);\n clients.delete(clientId);\n }\n });\n});\n\n/**\n * POST /api/events/progress — Receive progress updates from Claude via curl.\n *\n * Called by the writing phase agent to report file-by-file completion.\n * Broadcasts the update via SSE and optionally writes to a temp file\n * so the CLI heartbeat can display progress when no web UI is connected.\n */\neventsApi.post('/progress', async (c) => {\n try {\n const body = await c.req.json<{\n file?: string;\n completed?: number;\n total?: number;\n phase?: string;\n progressFile?: string;\n }>();\n\n const phase = body.phase ?? 'skill-gen';\n const completed = body.completed ?? 0;\n const total = body.total ?? 0;\n const file = body.file ?? 'unknown';\n\n broadcastProgress(phase, completed, total, file);\n\n // Also write to temp file for CLI heartbeat reads\n if (body.progressFile) {\n try {\n writeFileSync(body.progressFile, JSON.stringify({ file, completed, total, phase }), 'utf-8');\n } catch { /* best-effort */ }\n }\n\n return c.json({ ok: true });\n } catch {\n return c.json({ ok: false, error: 'Invalid JSON' }, 400);\n }\n});\n\n/**\n * GET /api/events/status — Get SSE connection status.\n */\neventsApi.get('/status', (c) => {\n return c.json({\n connectedClients: clients.size,\n clientIds: [...clients.keys()],\n });\n});\n","/**\n * Metrics API for the analytics dashboard.\n *\n * Provides endpoints for scan history, performance metrics,\n * rate-limit frequency, and skill quality trending.\n *\n * Endpoints:\n * GET /api/metrics/overview — Aggregate scan metrics\n * GET /api/metrics/scans — Recent scan history\n * GET /api/metrics/phases — Per-phase performance averages\n * GET /api/metrics/ratelimits — Rate-limit frequency over time\n * GET /api/metrics/quality — Skill quality scores trending\n * GET /api/metrics/memory — Memory/cache usage stats\n */\n\nimport { Hono } from 'hono';\nimport { readMetricsStore, getMetricsAggregate } from '../../utils/scan-metrics.js';\nimport { getSummaryCacheStats } from '../../summarizer/cache.js';\nimport { readVettingStore } from '../../vetting/store.js';\nimport { calculateScoreBreakdown } from '../../vetting/scoring.js';\nimport { readCorrectionsStore } from '../../vetting/correction-capture.js';\n\nexport const metricsApi = new Hono();\n\n/**\n * GET /api/metrics/overview — Aggregate scan metrics.\n */\nmetricsApi.get('/overview', (c) => {\n try {\n const aggregate = getMetricsAggregate();\n return c.json(aggregate);\n } catch (err) {\n return c.json({ error: String(err) }, 500);\n }\n});\n\n/**\n * GET /api/metrics/scans — Recent scan history with full details.\n */\nmetricsApi.get('/scans', (c) => {\n try {\n const store = readMetricsStore();\n const limit = parseInt(c.req.query('limit') ?? '20', 10);\n const scans = store.scans.slice(-limit).reverse(); // Most recent first\n return c.json({ scans, total: store.scans.length });\n } catch (err) {\n return c.json({ error: String(err) }, 500);\n }\n});\n\n/**\n * GET /api/metrics/phases — Per-phase performance averages.\n */\nmetricsApi.get('/phases', (c) => {\n try {\n const aggregate = getMetricsAggregate();\n return c.json({ phases: aggregate.phaseAverages });\n } catch (err) {\n return c.json({ error: String(err) }, 500);\n }\n});\n\n/**\n * GET /api/metrics/ratelimits — Rate-limit frequency over time.\n */\nmetricsApi.get('/ratelimits', (c) => {\n try {\n const store = readMetricsStore();\n const rlData = store.scans.map((scan) => ({\n date: scan.startedAt,\n events: scan.totalRateLimitEvents,\n backoffMs: scan.totalRateLimitMs,\n scanId: scan.id,\n }));\n const totalEvents = rlData.reduce((sum, d) => sum + d.events, 0);\n const totalBackoffMs = rlData.reduce((sum, d) => sum + d.backoffMs, 0);\n\n return c.json({\n history: rlData,\n totalEvents,\n totalBackoffMs,\n averageEventsPerScan: store.scans.length > 0\n ? Math.round(totalEvents / store.scans.length * 10) / 10\n : 0,\n });\n } catch (err) {\n return c.json({ error: String(err) }, 500);\n }\n});\n\n/**\n * GET /api/metrics/quality — Skill quality scores trending.\n */\nmetricsApi.get('/quality', (c) => {\n try {\n const vettingStore = readVettingStore();\n const corrections = readCorrectionsStore();\n\n const skills = vettingStore.entries\n .filter((e) => e.type === 'skill')\n .map((entry) => {\n const breakdown = calculateScoreBreakdown(entry);\n return {\n name: entry.name,\n status: entry.status,\n detectedAt: entry.detectedAt,\n finalScore: entry.finalScore,\n breakdown,\n feedbackCount: entry.feedback.length,\n rewriteCount: entry.rewrites.length,\n passiveUsage: entry.passiveScore.usageCount,\n successRate: entry.passiveScore.successRate,\n };\n });\n\n // Correction summary\n const recentCorrections = corrections.corrections\n .slice(-20)\n .reverse();\n\n const highConfidenceCorrections = corrections.corrections\n .filter((c_) => c_.confidence >= 0.80).length;\n\n return c.json({\n skills,\n corrections: {\n total: corrections.corrections.length,\n highConfidence: highConfidenceCorrections,\n recent: recentCorrections,\n lastScanAt: corrections.lastScanAt,\n },\n summary: {\n totalSkills: skills.length,\n approved: skills.filter((s) => s.status === 'approved').length,\n probation: skills.filter((s) => s.status === 'probation').length,\n rejected: skills.filter((s) => s.status === 'rejected').length,\n averageScore: skills.length > 0\n ? Math.round(skills.reduce((sum, s) => sum + s.finalScore, 0) / skills.length)\n : 0,\n },\n });\n } catch (err) {\n return c.json({ error: String(err) }, 500);\n }\n});\n\n/**\n * GET /api/metrics/memory — Memory/cache usage stats.\n */\nmetricsApi.get('/memory', (c) => {\n try {\n const cacheStats = getSummaryCacheStats();\n return c.json(cacheStats);\n } catch (err) {\n return c.json({ error: String(err) }, 500);\n }\n});\n","/**\n * Correction capture for skill quality improvement.\n *\n * Scans conversation transcripts for correction patterns that indicate\n * existing skills contain inaccurate or outdated information.\n *\n * Inspired by claude-reflect's regex-based correction detection:\n * - \"no, use X\" / \"don't use Y\" / \"actually...\" / \"that's wrong\"\n * - Confidence scoring per pattern (0.60-0.95)\n * - Creates correction proposals in the review queue\n *\n * Correction proposals are stored in the vetting pipeline as feedback\n * entries and also written to ~/.lore/corrections.json for the UI.\n */\n\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { getLoreDir } from '../utils/paths.js';\nimport { logger } from '../utils/logger.js';\nimport {\n readVettingStore,\n addFeedback,\n findEntryByName,\n} from './store.js';\nimport type { FeedbackEntry } from './types.js';\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport interface CorrectionPattern {\n /** Regex pattern to match against transcript text. */\n pattern: RegExp;\n /** Confidence that this pattern indicates a genuine correction (0-1). */\n confidence: number;\n /** Category label for the correction type. */\n category: 'explicit-correction' | 'preference' | 'deprecation' | 'mistake';\n}\n\nexport interface DetectedCorrection {\n /** The matched text. */\n matchedText: string;\n /** Surrounding context (50 chars before/after). */\n context: string;\n /** Conversation ID where the correction was found. */\n conversationId: string;\n /** Skill name this correction targets (if identifiable). */\n targetSkill: string | null;\n /** Confidence score (0-1). */\n confidence: number;\n /** Category of correction. */\n category: CorrectionPattern['category'];\n /** Timestamp of detection. */\n detectedAt: string;\n}\n\nexport interface CorrectionStore {\n version: 1;\n corrections: DetectedCorrection[];\n lastScanAt: string | null;\n}\n\n// ── Correction patterns ──────────────────────────────────────────\n\n/**\n * Patterns that indicate a user is correcting agent behavior.\n * Ordered roughly by confidence (highest first).\n */\nconst CORRECTION_PATTERNS: CorrectionPattern[] = [\n // Explicit corrections (highest confidence)\n {\n pattern: /\\bremember:\\s*.+/gi,\n confidence: 0.95,\n category: 'explicit-correction',\n },\n {\n pattern: /\\bthat'?s (?:wrong|incorrect|not right|outdated)\\b/gi,\n confidence: 0.90,\n category: 'explicit-correction',\n },\n {\n pattern: /\\bno,?\\s+(?:use|do|it should be|the correct|instead)\\b/gi,\n confidence: 0.85,\n category: 'explicit-correction',\n },\n {\n pattern: /\\bactually[,.]?\\s+(?:we|you|it|the|this)\\b/gi,\n confidence: 0.80,\n category: 'explicit-correction',\n },\n {\n pattern: /\\bdon'?t\\s+(?:use|do|add|include|import)\\s+\\w+/gi,\n confidence: 0.80,\n category: 'preference',\n },\n {\n pattern: /\\bstop\\s+(?:using|doing|adding)\\s+\\w+/gi,\n confidence: 0.85,\n category: 'preference',\n },\n // Preference patterns (medium confidence)\n {\n pattern: /\\b(?:always|never)\\s+(?:use|do|import|include)\\s+\\w+/gi,\n confidence: 0.75,\n category: 'preference',\n },\n {\n pattern: /\\bprefer\\s+\\w+\\s+(?:over|instead of)\\s+\\w+/gi,\n confidence: 0.75,\n category: 'preference',\n },\n {\n pattern: /\\bwe\\s+(?:don'?t|no longer|stopped)\\s+(?:use|using)\\b/gi,\n confidence: 0.70,\n category: 'deprecation',\n },\n // Deprecation patterns\n {\n pattern: /\\b(?:deprecated|obsolete|replaced by|migrated to)\\b/gi,\n confidence: 0.70,\n category: 'deprecation',\n },\n // Mistake corrections (lower confidence - more false positives)\n {\n pattern: /\\bthat'?s not (?:how|what|the way)\\b/gi,\n confidence: 0.65,\n category: 'mistake',\n },\n {\n pattern: /\\bwrong (?:file|path|import|pattern|approach)\\b/gi,\n confidence: 0.65,\n category: 'mistake',\n },\n];\n\n/**\n * Skill-reference patterns to identify which skill a correction targets.\n */\nconst SKILL_REFERENCE_PATTERNS = [\n /\\.cursor\\/skills\\/([^\\s/]+)/g,\n /SKILL\\.md/g,\n /skill[:\\s]+[\"']?([a-zA-Z0-9_-]+)[\"']?/gi,\n /the\\s+(\\w+[-_]\\w+)\\s+skill/gi,\n];\n\n// ── Public API ───────────────────────────────────────────────────\n\n/**\n * Scan conversation transcripts for correction patterns.\n *\n * @param conversations - Array of {id, transcript, repoName} objects\n * @param knownSkillNames - Names of known skills (for targeted matching)\n * @returns Array of detected corrections\n */\nexport function scanForCorrections(\n conversations: Array<{ id: string; transcript: string; repoName?: string | null }>,\n knownSkillNames: string[] = [],\n): DetectedCorrection[] {\n const corrections: DetectedCorrection[] = [];\n\n for (const convo of conversations) {\n // Only scan user messages (skip assistant responses)\n const userMessages = extractUserMessages(convo.transcript);\n\n for (const message of userMessages) {\n for (const pattern of CORRECTION_PATTERNS) {\n const matches = message.matchAll(pattern.pattern);\n\n for (const match of matches) {\n const matchedText = match[0];\n const startIdx = Math.max(0, (match.index ?? 0) - 50);\n const endIdx = Math.min(message.length, (match.index ?? 0) + matchedText.length + 50);\n const context = message.slice(startIdx, endIdx);\n\n // Try to identify which skill is being corrected\n const targetSkill = identifyTargetSkill(context, message, knownSkillNames);\n\n corrections.push({\n matchedText,\n context,\n conversationId: convo.id,\n targetSkill,\n confidence: pattern.confidence,\n category: pattern.category,\n detectedAt: new Date().toISOString(),\n });\n }\n }\n }\n }\n\n // Deduplicate: same correction text + same conversation = one entry\n const seen = new Set<string>();\n return corrections.filter((c) => {\n const key = `${c.conversationId}:${c.matchedText.toLowerCase().trim()}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\n/**\n * Process detected corrections:\n * 1. Store in corrections.json\n * 2. Add as feedback to matching vetting entries\n * 3. Return summary for the sync pipeline\n */\nexport function processCorrections(\n corrections: DetectedCorrection[],\n): { stored: number; feedbackAdded: number; highConfidence: number } {\n if (corrections.length === 0) {\n return { stored: 0, feedbackAdded: 0, highConfidence: 0 };\n }\n\n // Save to corrections store\n const store = readCorrectionsStore();\n store.corrections.push(...corrections);\n store.lastScanAt = new Date().toISOString();\n\n // Keep only last 500 corrections\n if (store.corrections.length > 500) {\n store.corrections = store.corrections.slice(-500);\n }\n\n writeCorrectionsStore(store);\n\n // Add feedback to vetting entries for targeted corrections\n const vettingStore = readVettingStore();\n let feedbackAdded = 0;\n\n for (const correction of corrections) {\n if (!correction.targetSkill) continue;\n\n const entry = findEntryByName(vettingStore, correction.targetSkill, 'skill');\n if (!entry) continue;\n\n const feedback: FeedbackEntry = {\n timestamp: correction.detectedAt,\n source: 'passive',\n category: 'quality',\n description: `Correction detected (${correction.category}): \"${correction.matchedText}\"`,\n evidence: correction.context,\n };\n\n addFeedback(vettingStore, entry.id, feedback);\n feedbackAdded++;\n }\n\n const highConfidence = corrections.filter((c) => c.confidence >= 0.80).length;\n\n logger.debug(`[corrections] ${corrections.length} detected, ${feedbackAdded} linked to skills, ${highConfidence} high-confidence`);\n\n return { stored: corrections.length, feedbackAdded, highConfidence };\n}\n\n// ── Corrections store ────────────────────────────────────────────\n\nfunction getCorrectionsPath(): string {\n return join(getLoreDir(), 'corrections.json');\n}\n\nexport function readCorrectionsStore(): CorrectionStore {\n const path = getCorrectionsPath();\n if (!existsSync(path)) {\n return { version: 1, corrections: [], lastScanAt: null };\n }\n try {\n return JSON.parse(readFileSync(path, 'utf-8')) as CorrectionStore;\n } catch {\n return { version: 1, corrections: [], lastScanAt: null };\n }\n}\n\nfunction writeCorrectionsStore(store: CorrectionStore): void {\n mkdirSync(getLoreDir(), { recursive: true });\n writeFileSync(getCorrectionsPath(), JSON.stringify(store, null, 2), 'utf-8');\n}\n\n// ── Helpers ──────────────────────────────────────────────────────\n\n/**\n * Extract user messages from a transcript.\n * Looks for patterns like \"Human:\", \"User:\", or role markers.\n */\nfunction extractUserMessages(transcript: string): string[] {\n const messages: string[] = [];\n\n // Try to split by role markers\n const parts = transcript.split(/(?:^|\\n)(?:Human|User|human|user):\\s*/);\n if (parts.length > 1) {\n for (let i = 1; i < parts.length; i++) {\n // Take text up to the next assistant marker\n const assistantIdx = parts[i].search(/(?:^|\\n)(?:Assistant|assistant|Claude|claude):\\s*/);\n const userText = assistantIdx >= 0 ? parts[i].slice(0, assistantIdx) : parts[i];\n if (userText.trim()) messages.push(userText.trim());\n }\n }\n\n // If no role markers found, scan the entire transcript\n if (messages.length === 0) {\n messages.push(transcript);\n }\n\n return messages;\n}\n\n/**\n * Try to identify which skill a correction targets.\n */\nfunction identifyTargetSkill(\n _context: string,\n fullMessage: string,\n knownSkillNames: string[],\n): string | null {\n // Check for explicit skill references in the full message\n for (const pattern of SKILL_REFERENCE_PATTERNS) {\n const matches = fullMessage.matchAll(new RegExp(pattern));\n for (const match of matches) {\n if (match[1]) return match[1];\n }\n }\n\n // Check if any known skill name appears in the message\n for (const name of knownSkillNames) {\n if (fullMessage.toLowerCase().includes(name.toLowerCase())) {\n return name;\n }\n }\n\n return null;\n}\n","/**\n * Repo Scan Dashboard API — dedicated endpoints for single-repo\n * skill generation with checkpoint persistence and SSE events.\n *\n * Mounted at /api/repo-scans in server.ts.\n */\n\nimport { Hono } from 'hono';\nimport { existsSync, readFileSync, readdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { z } from 'zod';\nimport {\n readScanCheckpoint,\n writeScanCheckpoint,\n updateScanCheckpoint,\n clearScanCheckpoint,\n type ScanCheckpoint,\n type PhaseName,\n} from '../../state/scan-checkpoint.js';\nimport {\n PIPELINE,\n getPhaseExecutionPlan,\n CLEANUP_STRATEGY,\n type PhaseOption,\n} from '../../state/scan-pipeline.js';\nimport { summarizeAllConversations } from '../../summarizer/summarizer.js';\nimport { loadConversationSummary } from '../../summarizer/cache.js';\nimport { clusterAllRepos } from '../../summarizer/clusterer.js';\nimport { generateAllRepoSkills } from '../../skill-generator/generator.js';\nimport { deleteSkillFiles } from '../../skill-generator/skill-files.js';\nimport { findOrphanedFiles } from '../../skill-generator/orphan-detector.js';\nimport { selectReposForProcessing, getSelectedRepoNames } from '../../repos/selector.js';\nimport { loadConfig } from '../../config/loader.js';\nimport { getLoreDir, type KnownRepo } from '../../utils/paths.js';\nimport { errorMessage } from '../../utils/errors.js';\nimport {\n broadcastEvent,\n broadcastPhaseUpdate,\n broadcastSkillOutput,\n} from './events.js';\n\n// ── Types ─────────────────────────────────────────────────────────\n\nexport interface PhaseStatus {\n name: string;\n label: string;\n status: 'pending' | 'active' | 'completed' | 'skipped' | 'failed';\n startedAt?: string;\n completedAt?: string;\n durationMs?: number;\n summary?: string;\n outputLines?: string[];\n}\n\nexport interface RepoScanStatus {\n running: boolean;\n repoName: string | null;\n error: string | null;\n phases: PhaseStatus[];\n markdownContent: string;\n isResumed: boolean;\n /** Conversations discovered during the summarize phase */\n conversations: ScanConversationEntry[];\n /** Orphaned files awaiting user review (null = none pending) */\n pendingOrphans: { files: string[]; skillDir: string } | null;\n}\n\n// ── In-memory state ───────────────────────────────────────────────\n\nlet scanRunning = false;\nlet scanRepoName: string | null = null;\nlet scanError: string | null = null;\nlet scanAbortController: AbortController | null = null;\nlet scanPhases: PhaseStatus[] = [];\nlet scanMarkdownContent = '';\nlet scanOutputLines: string[] = [];\nlet scanIsResumed = false;\n\n/** Conversation summaries collected during the summarize phase */\ninterface ScanConversationEntry {\n id: string;\n title: string;\n source: 'cursor' | 'claude-code' | string;\n messageCount: number;\n status: 'new' | 'stale' | 'cached' | 'trivial';\n /** AI-generated summary text (loaded from disk after summarization) */\n summary?: string;\n /** Tools used in the conversation */\n toolsUsed?: string[];\n /** Files modified in the conversation */\n filesModified?: string[];\n /** Outcome of the conversation task */\n outcome?: string;\n}\n\nlet scanConversations: ScanConversationEntry[] = [];\n\n/** Orphaned files awaiting user review instead of auto-deletion */\nlet pendingOrphans: { files: string[]; skillDir: string } | null = null;\n\nfunction appendOutput(line: string): void {\n const ts = new Date().toISOString().slice(11, 19);\n scanOutputLines.push(`[${ts}] ${line}`);\n}\n\nfunction setPhaseStatus(name: string, updates: Partial<PhaseStatus>): void {\n const idx = scanPhases.findIndex((p) => p.name === name);\n if (idx >= 0) {\n scanPhases[idx] = { ...scanPhases[idx], ...updates };\n }\n}\n\nfunction initPhases(completedPhases: PhaseName[] = []): void {\n const completedSet = new Set(completedPhases);\n scanPhases = PIPELINE.map((p) => ({\n name: p.name,\n label: p.label,\n status: completedSet.has(p.name) ? 'skipped' as const : 'pending' as const,\n summary: completedSet.has(p.name) ? 'Completed in previous run' : undefined,\n }));\n}\n\n// ── Request schemas ───────────────────────────────────────────────\n\nconst startBodySchema = z.object({\n repoName: z.string(),\n force: z.boolean().optional(),\n phase: z.enum(['all', 'discover', 'summarize', 'cluster', 'verdict', 'generate']).optional(),\n});\n\n// ── Hono app ──────────────────────────────────────────────────────\n\nexport const repoScansApi = new Hono();\n\n// ── Checkpoint endpoints ──────────────────────────────────────────\n\nrepoScansApi.get('/checkpoint', (c) => {\n const loreDir = getLoreDir();\n const checkpoint = readScanCheckpoint(loreDir);\n return c.json({ checkpoint });\n});\n\nrepoScansApi.delete('/checkpoint', (c) => {\n const loreDir = getLoreDir();\n clearScanCheckpoint(loreDir);\n return c.json({ cleared: true });\n});\n\n// ── Scan control ──────────────────────────────────────────────────\n\nrepoScansApi.post('/start', async (c) => {\n if (scanRunning) {\n return c.json({ error: 'A repo scan is already running' }, 409);\n }\n\n const rawBody = await c.req.json().catch(() => ({}));\n const body = startBodySchema.parse(rawBody);\n\n // Reset state\n scanOutputLines = [];\n scanConversations = [];\n pendingOrphans = null;\n scanRunning = true;\n scanRepoName = body.repoName;\n scanError = null;\n scanMarkdownContent = '';\n scanAbortController = new AbortController();\n scanIsResumed = false;\n initPhases();\n\n // Write initial checkpoint\n const loreDir = getLoreDir();\n writeScanCheckpoint(loreDir, {\n version: 1,\n startedAt: new Date().toISOString(),\n sha: 'HEAD',\n repoName: body.repoName,\n loreDir,\n phase: body.phase ?? 'all',\n force: body.force ?? false,\n completedPhases: [],\n savedVerdict: null,\n });\n\n // Fire-and-forget\n runRepoScan(body.repoName, body.force, body.phase as PhaseOption | undefined).catch(() => {});\n\n broadcastEvent('skill:scanStart', { repo: body.repoName });\n\n return c.json({ started: true });\n});\n\nrepoScansApi.post('/resume', async (c) => {\n if (scanRunning) {\n return c.json({ error: 'A repo scan is already running' }, 409);\n }\n\n const loreDir = getLoreDir();\n const checkpoint = readScanCheckpoint(loreDir);\n if (!checkpoint) {\n return c.json({ error: 'No checkpoint found to resume from' }, 404);\n }\n\n // Reset state\n scanOutputLines = [];\n scanConversations = [];\n pendingOrphans = null;\n scanRunning = true;\n scanRepoName = checkpoint.repoName;\n scanError = null;\n scanMarkdownContent = '';\n scanAbortController = new AbortController();\n scanIsResumed = true;\n initPhases(checkpoint.completedPhases);\n\n // Fire-and-forget\n runRepoScan(\n checkpoint.repoName,\n checkpoint.force,\n checkpoint.phase as PhaseOption | undefined,\n checkpoint.completedPhases,\n checkpoint.savedVerdict,\n ).catch(() => {});\n\n broadcastEvent('skill:scanStart', { repo: checkpoint.repoName, resumed: true });\n\n return c.json({ started: true, resumed: true, checkpoint });\n});\n\nrepoScansApi.post('/stop', (c) => {\n if (!scanRunning || !scanAbortController) {\n return c.json({ error: 'No repo scan is currently running' }, 409);\n }\n\n scanAbortController.abort();\n scanAbortController = null;\n scanRunning = false;\n appendOutput('[repo-scan] Scan cancelled by user.');\n\n // user-cancel strategy: clear checkpoint + worktree\n const loreDir = getLoreDir();\n const strategy = CLEANUP_STRATEGY['user-cancel'];\n if (strategy.clearCheckpoint) {\n clearScanCheckpoint(loreDir);\n }\n\n // Mark any active phase as failed\n scanPhases = scanPhases.map((p) =>\n p.status === 'active' ? { ...p, status: 'failed' as const, summary: 'Cancelled' } : p,\n );\n\n broadcastEvent('skill:scanEnd', {\n repo: scanRepoName,\n success: false,\n reason: 'user-cancel',\n });\n\n scanError = 'Cancelled by user';\n return c.json({ stopped: true });\n});\n\n// ── Orphan resolution ─────────────────────────────────────────────\n\nconst resolveOrphansSchema = z.object({\n approve: z.array(z.string()),\n reject: z.array(z.string()),\n});\n\nrepoScansApi.post('/orphans/resolve', async (c) => {\n if (!pendingOrphans) {\n return c.json({ error: 'No pending orphans to resolve' }, 404);\n }\n\n const rawBody = await c.req.json().catch(() => ({}));\n const body = resolveOrphansSchema.parse(rawBody);\n\n // Delete approved files (user confirmed they are orphaned)\n let deleted = 0;\n if (body.approve.length > 0) {\n deleted = deleteSkillFiles(pendingOrphans.skillDir, body.approve);\n appendOutput(`[orphans] Deleted ${deleted} orphaned file(s): ${body.approve.join(', ')}`);\n }\n\n // Rejected files are kept (user wants to keep them)\n if (body.reject.length > 0) {\n appendOutput(`[orphans] Kept ${body.reject.length} file(s): ${body.reject.join(', ')}`);\n }\n\n // Clear pending state\n pendingOrphans = null;\n\n broadcastEvent('skill:orphansResolved', {\n deleted: body.approve.length,\n kept: body.reject.length,\n repoName: scanRepoName,\n });\n\n return c.json({ resolved: true, deleted, kept: body.reject.length });\n});\n\n// ── Status & output ───────────────────────────────────────────────\n\nrepoScansApi.get('/status', (c) => {\n const status: RepoScanStatus = {\n running: scanRunning,\n repoName: scanRepoName,\n error: scanError,\n phases: scanPhases,\n markdownContent: scanMarkdownContent,\n isResumed: scanIsResumed,\n conversations: scanConversations,\n pendingOrphans,\n };\n return c.json(status);\n});\n\nrepoScansApi.get('/output', (c) => {\n const since = parseInt(c.req.query('since') ?? '0', 10);\n const lines = scanOutputLines.slice(since);\n return c.json({ lines, total: scanOutputLines.length });\n});\n\n// ── Phase materials ───────────────────────────────────────────────\n\nrepoScansApi.get('/phase/:name/materials', (c) => {\n const name = c.req.param('name');\n const phase = scanPhases.find((p) => p.name === name);\n\n if (!phase) {\n return c.json({ error: `Phase \"${name}\" not found` }, 404);\n }\n\n // Return phase-specific output lines\n return c.json({\n phase: phase.name,\n status: phase.status,\n summary: phase.summary,\n outputLines: phase.outputLines ?? [],\n markdownContent: name === 'generate' ? scanMarkdownContent : null,\n });\n});\n\n// ── Skill content (read current SKILL.md) ─────────────────────────\n\nrepoScansApi.get('/skill-content', (c) => {\n const repoPath = c.req.query('repoPath');\n if (!repoPath) {\n return c.json({ error: 'repoPath query param required' }, 400);\n }\n\n const skillsDir = join(repoPath, '.cursor', 'skills');\n if (!existsSync(skillsDir)) {\n return c.json({ content: null });\n }\n\n try {\n const entries = readdirSync(skillsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && entry.name.endsWith('-guide')) {\n const skillPath = join(skillsDir, entry.name, 'SKILL.md');\n if (existsSync(skillPath)) {\n const content = readFileSync(skillPath, 'utf-8');\n return c.json({ content, path: skillPath });\n }\n }\n }\n } catch { /* ignore */ }\n\n return c.json({ content: null });\n});\n\n// ── Pipeline runner ───────────────────────────────────────────────\n\nasync function runRepoScan(\n repoName: string,\n force?: boolean,\n phase?: PhaseOption,\n resumeCompletedPhases?: PhaseName[],\n savedVerdict?: unknown,\n): Promise<void> {\n const loreDir = getLoreDir();\n const completedPhases = new Set<PhaseName>(resumeCompletedPhases ?? []);\n\n try {\n // ── Repo selection ────────────────────────────────────────────\n const config = loadConfig(loreDir);\n const repoSelection = await selectReposForProcessing(config.repos);\n const uiSet = new Set([repoName.toLowerCase()]);\n const matched = repoSelection.all.filter((sr) => {\n const name = sr.repo.remoteName ?? sr.repo.shortName;\n return uiSet.has(name.toLowerCase());\n });\n const selectedRepos: KnownRepo[] = matched.map((sr) => sr.repo);\n const selectedRepoNames: string[] = matched.map((sr) => sr.repo.remoteName ?? sr.repo.shortName);\n\n if (selectedRepos.length === 0) {\n throw new Error(`Repo \"${repoName}\" not found in discovered repos`);\n }\n\n appendOutput(`[repo-scan] Starting scan for ${repoName}`);\n\n // ── Build execution plan ──────────────────────────────────────\n const plan = getPhaseExecutionPlan(PIPELINE, phase, completedPhases);\n\n for (const { phase: phaseConfig, action } of plan) {\n if (scanAbortController?.signal.aborted) break;\n\n if (action === 'skip') {\n setPhaseStatus(phaseConfig.name, {\n status: 'skipped',\n summary: 'Completed in previous run',\n });\n appendOutput(`[repo-scan] Skipping ${phaseConfig.label} (completed in previous run)`);\n broadcastEvent('skill:phaseSkipped', {\n repo: repoName,\n phase: phaseConfig.name,\n reason: 'completed in previous run',\n });\n continue;\n }\n\n // ── Run phase ──────────────────────────────────────────────\n const phaseStartTime = Date.now();\n setPhaseStatus(phaseConfig.name, {\n status: 'active',\n startedAt: new Date().toISOString(),\n });\n\n broadcastPhaseUpdate(phaseConfig.name, 'started', {});\n broadcastEvent('skill:phaseStart', {\n repo: repoName,\n phase: phaseConfig.name,\n phaseNum: phaseConfig.displayNum,\n total: PIPELINE.length,\n });\n\n try {\n const phaseLines: string[] = [];\n\n if (phaseConfig.name === 'summarize') {\n appendOutput(`[summarize] Summarizing conversations for ${repoName}...`);\n\n const { result: sumResult } = await summarizeAllConversations({\n repoFilter: repoName,\n selectedRepoNames,\n force,\n onOutput: (line) => {\n appendOutput(line);\n phaseLines.push(line);\n broadcastSkillOutput(repoName, line);\n\n const countMatch = line.match(/\\[(\\d+)\\/(\\d+)\\]/);\n if (countMatch) {\n setPhaseStatus('summarize', {\n summary: `Summarizing (${countMatch[1]}/${countMatch[2]})...`,\n });\n }\n },\n signal: scanAbortController?.signal,\n });\n\n const summary = `${sumResult.summarized} summarized, ${sumResult.skipped} cached`;\n setPhaseStatus('summarize', { summary, outputLines: phaseLines });\n appendOutput(`[summarize] Done: ${summary}`);\n\n // Store conversation details for the dedicated summary card view\n // Enrich with AI-generated summary text from disk\n scanConversations = sumResult.conversations.map((conv) => {\n const entry: ScanConversationEntry = {\n id: conv.id,\n title: conv.title,\n source: conv.source,\n messageCount: conv.messageCount,\n status: conv.status,\n };\n\n // Load the full stored summary to get AI-generated fields\n try {\n const stored = loadConversationSummary(conv.id);\n if (stored?.ai) {\n entry.summary = stored.ai.summary;\n entry.toolsUsed = stored.ai.toolsUsed;\n entry.filesModified = stored.ai.filesModified;\n entry.outcome = stored.ai.outcome;\n }\n } catch { /* ignore - summary may not exist for trivial convos */ }\n\n return entry;\n });\n\n broadcastEvent('skill:conversationSummary', {\n repo: repoName,\n summarized: sumResult.summarized,\n cached: sumResult.skipped,\n conversations: scanConversations,\n });\n }\n\n if (phaseConfig.name === 'cluster') {\n appendOutput(`[cluster] Clustering conversations for ${repoName}...`);\n\n const phaseClusterLines: string[] = [];\n const reposClustered = await clusterAllRepos({\n repoFilter: repoName,\n selectedRepoNames,\n onOutput: (line) => {\n appendOutput(line);\n phaseClusterLines.push(line);\n broadcastSkillOutput(repoName, line);\n\n const countMatch = line.match(/\\[(\\d+)\\/(\\d+)\\]/);\n if (countMatch) {\n setPhaseStatus('cluster', {\n summary: `Clustering (${countMatch[1]}/${countMatch[2]})...`,\n });\n }\n },\n signal: scanAbortController?.signal,\n });\n\n const clusterSummary = `${reposClustered} repos clustered`;\n setPhaseStatus('cluster', { summary: clusterSummary, outputLines: phaseClusterLines });\n appendOutput(`[cluster] Done: ${clusterSummary}`);\n }\n\n if (phaseConfig.name === 'generate') {\n appendOutput(`[generate] Generating skill for ${repoName}...`);\n\n const phaseGenLines: string[] = [];\n const results = await generateAllRepoSkills({\n repoFilter: repoName,\n selectedRepos,\n force,\n useWorktree: true,\n onOutput: (line) => {\n appendOutput(line);\n phaseGenLines.push(line);\n broadcastSkillOutput(repoName, line);\n\n // Track writing progress for live markdown\n if (line.includes('Writing:') || line.includes('SKILL.md')) {\n setPhaseStatus('generate', { summary: 'Writing SKILL.md...' });\n }\n if (line.includes('Invoking Claude for')) {\n setPhaseStatus('generate', { summary: 'Researching...' });\n }\n if (line.includes('Still working')) {\n const elapsed = line.match(/(\\d+)s elapsed/);\n setPhaseStatus('generate', {\n summary: `Working${elapsed ? ` (${elapsed[1]}s)` : ''}...`,\n });\n }\n },\n onOrphansFound: (orphans, skillDir) => {\n // Store orphans for manual review instead of auto-deleting\n pendingOrphans = { files: orphans, skillDir };\n appendOutput(`[skill-gen] Found ${orphans.length} orphaned file(s): ${orphans.join(', ')}`);\n broadcastEvent('skill:orphansFound', { files: orphans, repoName });\n },\n signal: scanAbortController?.signal,\n });\n\n const generated = results.filter((r) => r.status === 'generated' || r.status === 'updated').length;\n const failed = results.filter((r) => r.status === 'failed').length;\n const genSummary = `${generated} generated/updated${failed ? `, ${failed} failed` : ''}`;\n setPhaseStatus('generate', { summary: genSummary, outputLines: phaseGenLines });\n appendOutput(`[generate] Done: ${genSummary}`);\n\n // Read the generated SKILL.md content for live preview\n // AND run post-merge orphan sweep (worktree merge only copies files,\n // it never deletes files the user previously removed — so orphaned\n // reference files can reappear after merge)\n for (const repo of selectedRepos) {\n const repoPath = repo.path;\n const skillsDir = join(repoPath, '.cursor', 'skills');\n if (existsSync(skillsDir)) {\n try {\n const entries = readdirSync(skillsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && entry.name.endsWith('-guide')) {\n const skillDir = join(skillsDir, entry.name);\n const skillPath = join(skillDir, 'SKILL.md');\n if (existsSync(skillPath)) {\n // Post-merge orphan sweep: detect & store for review\n const postMergeOrphans = findOrphanedFiles(skillDir);\n if (postMergeOrphans.length > 0) {\n // Merge with any orphans already found during generation\n const existingFiles = new Set(pendingOrphans?.files ?? []);\n for (const f of postMergeOrphans) existingFiles.add(f);\n pendingOrphans = { files: [...existingFiles], skillDir };\n appendOutput(`[generate] Post-merge: found ${postMergeOrphans.length} orphaned file(s): ${postMergeOrphans.join(', ')}`);\n phaseGenLines.push(`Post-merge: found ${postMergeOrphans.length} orphaned file(s) awaiting review`);\n broadcastEvent('skill:orphansFound', { files: [...existingFiles], repoName });\n }\n\n scanMarkdownContent = readFileSync(skillPath, 'utf-8');\n broadcastEvent('skill:markdownChunk', {\n repo: repoName,\n chunk: scanMarkdownContent,\n totalChars: scanMarkdownContent.length,\n });\n }\n }\n }\n } catch { /* ignore */ }\n }\n }\n }\n\n // ── Phase completed ────────────────────────────────────────\n const durationMs = Date.now() - phaseStartTime;\n setPhaseStatus(phaseConfig.name, {\n status: 'completed',\n completedAt: new Date().toISOString(),\n durationMs,\n });\n\n completedPhases.add(phaseConfig.name);\n updateScanCheckpoint(loreDir, {\n completedPhases: Array.from(completedPhases),\n });\n\n broadcastPhaseUpdate(phaseConfig.name, 'completed', { durationMs });\n broadcastEvent('skill:phaseComplete', {\n repo: repoName,\n phase: phaseConfig.name,\n durationMs,\n outputSummary: scanPhases.find((p) => p.name === phaseConfig.name)?.summary ?? '',\n });\n } catch (err: unknown) {\n const msg = errorMessage(err);\n setPhaseStatus(phaseConfig.name, {\n status: 'failed',\n summary: msg,\n });\n appendOutput(`[${phaseConfig.name}] ERROR: ${msg}`);\n // Non-fatal for summarize, fatal for others\n if (phaseConfig.name !== 'summarize') throw err;\n }\n }\n\n // ── All phases complete ────────────────────────────────────────\n clearScanCheckpoint(loreDir);\n broadcastEvent('skill:scanEnd', {\n repo: repoName,\n success: true,\n });\n appendOutput(`[repo-scan] Scan complete for ${repoName}`);\n } catch (err: unknown) {\n const msg = errorMessage(err);\n scanError = msg;\n appendOutput(`[repo-scan] ERROR: ${msg}`);\n broadcastEvent('skill:scanEnd', {\n repo: repoName,\n success: false,\n error: msg,\n });\n } finally {\n scanRunning = false;\n scanAbortController = null;\n }\n}\n","/**\n * Scan session checkpoint for resume-on-reopen resilience.\n *\n * Persists pipeline progress to `.lore/scan-session.json` so that\n * an interrupted scan (IDE close, crash) can be resumed from the\n * last completed phase instead of restarting from scratch.\n *\n * Follows the JSON File Store pattern (pattern #19):\n * - Zod-validated schema with `.default()` for graceful degradation\n * - Returns null on missing/corrupt files (fail-open)\n * - Stored under the per-repo `.lore/` directory\n */\n\nimport { readFileSync, writeFileSync, existsSync, unlinkSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { z } from 'zod';\n\n// ── Schema ───────────────────────────────────────────────────────\n\nexport const phaseNameSchema = z.enum(['summarize', 'cluster', 'generate']);\nexport type PhaseName = z.infer<typeof phaseNameSchema>;\n\nexport const scanCheckpointSchema = z.object({\n version: z.literal(1),\n /** ISO timestamp when the scan started. */\n startedAt: z.string(),\n /** Git SHA the scan is running against. */\n sha: z.string(),\n /** Repo name (org/repo or shortName). */\n repoName: z.string(),\n /** Absolute path to the lore directory. */\n loreDir: z.string(),\n /** Which phase scope was requested (e.g. 'all', 'cluster'). */\n phase: z.enum(['all', 'discover', 'summarize', 'cluster', 'verdict', 'generate']),\n /** Whether --force was used. */\n force: z.boolean().default(false),\n /** Phases that completed successfully before interruption. */\n completedPhases: z.array(phaseNameSchema).default([]),\n /** Saved relevance verdict (persisted so generate can reuse it on resume). */\n savedVerdict: z.any().nullable().default(null),\n});\n\nexport type ScanCheckpoint = z.infer<typeof scanCheckpointSchema>;\n\n// ── File path ────────────────────────────────────────────────────\n\nconst CHECKPOINT_FILENAME = 'scan-session.json';\n\nfunction getCheckpointPath(loreDir: string): string {\n return join(loreDir, CHECKPOINT_FILENAME);\n}\n\n// ── CRUD ─────────────────────────────────────────────────────────\n\n/**\n * Write a full scan checkpoint to disk.\n * Creates the `.lore/` directory if it doesn't exist.\n */\nexport function writeScanCheckpoint(loreDir: string, checkpoint: ScanCheckpoint): void {\n mkdirSync(loreDir, { recursive: true });\n const validated = scanCheckpointSchema.parse(checkpoint);\n writeFileSync(\n getCheckpointPath(loreDir),\n JSON.stringify(validated, null, 2) + '\\n',\n 'utf-8',\n );\n}\n\n/**\n * Read the scan checkpoint from disk.\n * Returns null if the file is missing, corrupt, or fails validation.\n */\nexport function readScanCheckpoint(loreDir: string): ScanCheckpoint | null {\n const filePath = getCheckpointPath(loreDir);\n if (!existsSync(filePath)) return null;\n\n try {\n const raw = readFileSync(filePath, 'utf-8');\n const parsed = JSON.parse(raw);\n return scanCheckpointSchema.parse(parsed);\n } catch {\n return null;\n }\n}\n\n/**\n * Merge partial updates into the existing checkpoint (read-merge-write).\n * Returns the updated checkpoint, or null if no checkpoint exists.\n */\nexport function updateScanCheckpoint(\n loreDir: string,\n partial: Partial<Pick<ScanCheckpoint, 'completedPhases' | 'savedVerdict'>>,\n): ScanCheckpoint | null {\n const current = readScanCheckpoint(loreDir);\n if (!current) return null;\n\n const updated: ScanCheckpoint = { ...current, ...partial };\n writeScanCheckpoint(loreDir, updated);\n return updated;\n}\n\n/**\n * Delete the scan checkpoint file.\n * No-ops gracefully if the file doesn't exist.\n */\nexport function clearScanCheckpoint(loreDir: string): void {\n const filePath = getCheckpointPath(loreDir);\n try {\n if (existsSync(filePath)) {\n unlinkSync(filePath);\n }\n } catch {\n // Non-critical — file may already be gone\n }\n}\n","/**\n * Declarative pipeline phase registry and cleanup strategy map.\n *\n * Pure logic — no VS Code dependency. Imported by both the extension\n * (scan-runner.ts) and unit tests.\n */\n\nimport type { PhaseName } from './scan-checkpoint.js';\n\n// ── Pipeline phase registry ──────────────────────────────────────\n\n/** Phase selection option from ScanOptions.phase */\nexport type PhaseOption = 'discover' | 'summarize' | 'cluster' | 'verdict' | 'generate' | 'all';\n\nexport interface PipelinePhaseConfig {\n name: PhaseName;\n /** Which top-level phase options include this phase. */\n activeIn: Set<PhaseOption>;\n /** Phase display number for the PTY header. */\n displayNum: number;\n /** Human-readable label. */\n label: string;\n}\n\nexport const PIPELINE: PipelinePhaseConfig[] = [\n {\n name: 'summarize',\n activeIn: new Set<PhaseOption>(['all', 'summarize', 'cluster']),\n displayNum: 1,\n label: 'Summarizing conversations',\n },\n {\n name: 'cluster',\n activeIn: new Set<PhaseOption>(['all', 'cluster']),\n displayNum: 2,\n label: 'Clustering by repo',\n },\n {\n name: 'generate',\n activeIn: new Set<PhaseOption>(['all', 'generate']),\n displayNum: 3,\n label: 'Generating skill',\n },\n];\n\n/**\n * Pure function: compute which phases to run vs skip.\n * Exported for unit testing without VS Code dependency.\n */\nexport function getPhaseExecutionPlan(\n pipeline: PipelinePhaseConfig[],\n selectedPhase: PhaseOption | undefined,\n completedPhases: Set<PhaseName>,\n): Array<{ phase: PipelinePhaseConfig; action: 'run' | 'skip' }> {\n return pipeline\n .filter((p) => p.activeIn.has(selectedPhase ?? 'all'))\n .map((phase) => ({\n phase,\n action: completedPhases.has(phase.name) ? 'skip' as const : 'run' as const,\n }));\n}\n\n// ── Cleanup strategy map ─────────────────────────────────────────\n\nexport type StopReason = 'user-cancel' | 'ide-close';\n\ninterface CleanupStrategy {\n clearCheckpoint: boolean;\n cleanupWorktree: boolean;\n}\n\nexport const CLEANUP_STRATEGY: Record<StopReason, CleanupStrategy> = {\n 'user-cancel': { clearCheckpoint: true, cleanupWorktree: true },\n 'ide-close': { clearCheckpoint: false, cleanupWorktree: false },\n};\n","import { existsSync } from 'node:fs';\nimport pc from 'picocolors';\nimport * as clack from '@clack/prompts';\nimport { getLoreDir } from '../utils/paths.js';\nimport { loadConfig } from '../config/loader.js';\nimport { generateAllRepoSkills } from '../skill-generator/generator.js';\nimport { summarizeAllConversations } from '../summarizer/summarizer.js';\nimport { clusterAllRepos } from '../summarizer/clusterer.js';\nimport { listRepoClusterSlugs } from '../summarizer/cache.js';\nimport { selectReposForProcessing, getSelectedRepoNames } from '../repos/selector.js';\nimport {\n createScanWorktree,\n cleanupScanWorktree,\n cleanupStaleWorktrees,\n resolveHeadSha,\n} from '../utils/worktree.js';\n\ninterface SkillsOptions {\n repo?: string;\n force?: boolean;\n dryRun?: boolean;\n skipSummarize?: boolean;\n /** Specific commit SHA to research against (defaults to HEAD). */\n commit?: string;\n}\n\nexport async function skillsCommand(options: SkillsOptions): Promise<void> {\n const loreDir = getLoreDir();\n\n if (!existsSync(loreDir)) {\n clack.intro(pc.bgMagenta(pc.black(' lore skills ')));\n clack.log.warn(`Lore is not initialized. Run ${pc.cyan('lore init')} first.`);\n clack.outro('');\n return;\n }\n\n clack.intro(pc.bgMagenta(pc.black(' lore skills ')));\n\n if (options.dryRun) {\n clack.log.info('Dry run — showing what would be generated.');\n }\n\n // Repo selection: when --repo flag is used, skip selector (explicit override).\n // Otherwise, use the config-driven selector to limit expensive processing.\n const config = loadConfig(loreDir);\n let selectedNames: string[] | undefined;\n let selectedRepoObjects: import('../utils/paths.js').KnownRepo[] | undefined;\n\n if (!options.repo) {\n const repoSelection = await selectReposForProcessing(config.repos);\n selectedNames = getSelectedRepoNames(repoSelection);\n selectedRepoObjects = repoSelection.selected.map((sr) => sr.repo);\n\n clack.log.info(\n `Repo selection (${pc.bold(repoSelection.mode)}): ${pc.bold(String(repoSelection.selected.length))} of ${repoSelection.all.length} repos selected.`,\n );\n }\n\n // Auto-run summarization unless explicitly skipped.\n // --force only affects skill generation, not re-summarization/clustering.\n // Only re-cluster when no clusters exist yet.\n if (!options.skipSummarize) {\n const existingClusters = listRepoClusterSlugs();\n if (existingClusters.length === 0) {\n const sumSpinner = clack.spinner();\n sumSpinner.start('Summarizing conversations (Phase 1)...');\n\n const { result } = await summarizeAllConversations({\n repoFilter: options.repo,\n onOutput: (line) => {\n const shortLine = line.length > 80 ? line.slice(0, 77) + '...' : line;\n sumSpinner.message(shortLine);\n },\n });\n\n sumSpinner.stop(\n `Summarized ${result.summarized} conversations (${result.skipped} cached).`,\n );\n\n const clusterSpinner = clack.spinner();\n clusterSpinner.start('Clustering by repo (Phase 2)...');\n\n await clusterAllRepos({\n repoFilter: options.repo,\n selectedRepoNames: selectedNames,\n onOutput: (line) => {\n const shortLine = line.length > 80 ? line.slice(0, 77) + '...' : line;\n clusterSpinner.message(shortLine);\n },\n });\n\n clusterSpinner.stop('Clustering complete.');\n } else {\n clack.log.info(\n pc.dim(`Using ${existingClusters.length} existing repo clusters. Pass without --skip-summarize to re-summarize.`),\n );\n }\n }\n\n // Phase 3: Generate skills (with worktree isolation)\n const spinner = clack.spinner();\n spinner.start('Generating repo skills via Claude (Phase 3)...');\n\n const results = await generateAllRepoSkills({\n repoFilter: options.repo,\n selectedRepos: selectedRepoObjects,\n force: options.force,\n useWorktree: true,\n worktreeCommit: options.commit,\n onOutput: (line) => {\n const shortLine = line.length > 80 ? line.slice(0, 77) + '...' : line;\n spinner.message(shortLine);\n },\n });\n\n spinner.stop('Skill generation complete.');\n\n // Show results\n const generated = results.filter((r) => r.status === 'generated');\n const updated = results.filter((r) => r.status === 'updated');\n const skipped = results.filter((r) => r.status === 'skipped');\n const failed = results.filter((r) => r.status === 'failed');\n\n const lines: string[] = [];\n if (generated.length > 0) {\n lines.push(pc.green(`Generated: ${generated.length}`));\n for (const r of generated) {\n lines.push(` ${pc.dim('+')} ${r.repo} → ${r.skillPath}`);\n }\n }\n if (updated.length > 0) {\n lines.push(pc.blue(`Updated: ${updated.length}`));\n for (const r of updated) {\n lines.push(` ${pc.dim('~')} ${r.repo} → ${r.skillPath}`);\n }\n }\n if (skipped.length > 0) {\n lines.push(pc.dim(`Skipped: ${skipped.length}`));\n }\n if (failed.length > 0) {\n lines.push(pc.red(`Failed: ${failed.length}`));\n for (const r of failed) {\n lines.push(` ${pc.dim('!')} ${r.repo}: ${r.reason}`);\n }\n }\n\n if (lines.length > 0) {\n clack.note(lines.join('\\n'), 'Results');\n }\n\n clack.outro(\n `${generated.length + updated.length} skills written. They are now available in each repo's .cursor/skills/ directory.`,\n );\n}\n","import { existsSync } from 'node:fs';\nimport pc from 'picocolors';\nimport * as clack from '@clack/prompts';\nimport { getLoreDir } from '../utils/paths.js';\nimport { loadConfig } from '../config/loader.js';\nimport { summarizeAllConversations } from '../summarizer/summarizer.js';\nimport { clusterAllRepos } from '../summarizer/clusterer.js';\nimport { selectReposForProcessing, getSelectedRepoNames } from '../repos/selector.js';\n\ninterface SummarizeCommandOptions {\n repo?: string;\n force?: boolean;\n}\n\nexport async function summarizeCommand(options: SummarizeCommandOptions): Promise<void> {\n const loreDir = getLoreDir();\n\n if (!existsSync(loreDir)) {\n clack.intro(pc.bgYellow(pc.black(' lore summarize ')));\n clack.log.warn(`Lore is not initialized. Run ${pc.cyan('lore init')} first.`);\n clack.outro('');\n return;\n }\n\n clack.intro(pc.bgYellow(pc.black(' lore summarize ')));\n\n // Repo selection for clustering: when --repo is explicit, skip selector\n const config = loadConfig(loreDir);\n let selectedNames: string[] | undefined;\n\n if (!options.repo) {\n const repoSelection = await selectReposForProcessing(config.repos);\n selectedNames = getSelectedRepoNames(repoSelection);\n\n clack.log.info(\n `Repo selection (${pc.bold(repoSelection.mode)}): ${pc.bold(String(repoSelection.selected.length))} of ${repoSelection.all.length} repos will be clustered.`,\n );\n }\n\n // Phase 1: Per-conversation summarization\n const summarySpinner = clack.spinner();\n summarySpinner.start('Phase 1: Summarizing conversations via Claude...');\n\n const { result, index } = await summarizeAllConversations({\n repoFilter: options.repo,\n force: options.force,\n onOutput: (line) => {\n const shortLine = line.length > 80 ? line.slice(0, 77) + '...' : line;\n summarySpinner.message(shortLine);\n },\n });\n\n summarySpinner.stop(\n `Phase 1 complete: ${result.summarized} summarized, ${result.skipped} skipped, ${result.failed} failed`,\n );\n\n // Phase 2: Per-repo clustering (only for selected repos)\n const clusterSpinner = clack.spinner();\n clusterSpinner.start('Phase 2: Clustering conversations by repo...');\n\n const reposClustered = await clusterAllRepos({\n repoFilter: options.repo,\n selectedRepoNames: selectedNames,\n onOutput: (line) => {\n const shortLine = line.length > 80 ? line.slice(0, 77) + '...' : line;\n clusterSpinner.message(shortLine);\n },\n });\n\n clusterSpinner.stop(`Phase 2 complete: ${reposClustered} repos clustered`);\n\n // Summary\n const totalConvos = Object.keys(index.conversations).length;\n clack.note(\n [\n `Total conversations indexed: ${pc.bold(String(totalConvos))}`,\n `Newly summarized: ${pc.green(String(result.summarized))}`,\n `Already up-to-date: ${pc.dim(String(result.skipped))}`,\n `Failed: ${result.failed > 0 ? pc.red(String(result.failed)) : pc.dim('0')}`,\n `Repos clustered: ${pc.blue(String(reposClustered))}`,\n '',\n `Summaries saved to ${pc.cyan('~/.lore/summaries/')}`,\n ].join('\\n'),\n 'Results',\n );\n\n clack.outro(\n `Run ${pc.cyan('lore skills')} to generate per-repo SKILL.md files from these summaries.`,\n );\n}\n","/**\n * `lore debug` command — pipeline diagnostics for troubleshooting.\n *\n * Sub-commands:\n * - `lore debug sources` — data source health checks\n * - `lore debug scan` — full pipeline trace with per-filter breakdown\n * - `lore debug conversations` — per-conversation filter audit\n *\n * All sub-commands support `--json` for structured agent-consumable output.\n * All operations are read-only — no AI calls, no state mutations.\n */\n\nimport pc from 'picocolors';\nimport * as clack from '@clack/prompts';\nimport { diagnoseSourceHealth } from '../debug/source-diagnostic.js';\nimport {\n diagnosePipeline,\n diagnoseConversations,\n type PipelineDiagnosticResult,\n type DetectedIssue,\n} from '../debug/pipeline-diagnostic.js';\nimport type { SourceHealthResult } from '../debug/source-diagnostic.js';\n\n// ── Options ──────────────────────────────────────────────────────\n\nexport interface DebugCommandOptions {\n json?: boolean;\n repo?: string;\n limit?: number;\n source?: 'cursor' | 'claude-code';\n}\n\n// ── Public API ───────────────────────────────────────────────────\n\nexport async function debugCommand(\n subcommand: string | undefined,\n options: DebugCommandOptions = {},\n): Promise<void> {\n const sub = subcommand ?? 'scan';\n\n switch (sub) {\n case 'sources':\n await debugSources(options);\n break;\n case 'scan':\n await debugScan(options);\n break;\n case 'conversations':\n await debugConversations(options);\n break;\n default:\n if (!options.json) {\n clack.intro(pc.bgRed(pc.white(' lore debug ')));\n clack.log.error(`Unknown sub-command: ${pc.bold(sub)}`);\n clack.log.info(`Available: ${pc.cyan('sources')}, ${pc.cyan('scan')}, ${pc.cyan('conversations')}`);\n clack.outro('');\n } else {\n writeJson({ error: `Unknown sub-command: ${sub}` });\n }\n }\n}\n\n// ── Sub-command: sources ─────────────────────────────────────────\n\nasync function debugSources(options: DebugCommandOptions): Promise<void> {\n const result = await diagnoseSourceHealth();\n\n if (options.json) {\n writeJson(result);\n return;\n }\n\n clack.intro(pc.bgYellow(pc.black(' lore debug sources ')));\n\n // Cursor\n const c = result.cursor;\n const cursorStatus = c.dbExists\n ? pc.green('OK')\n : pc.red('NOT FOUND');\n\n clack.log.info(`${pc.bold('Cursor')} ${c.enabled ? '' : pc.dim('(disabled in config)')}`);\n clack.log.message([\n ` DB path: ${pc.dim(c.dbPath)}`,\n ` Status: ${cursorStatus}`,\n c.dbSizeBytes !== null\n ? ` DB size: ${formatBytes(c.dbSizeBytes)}`\n : null,\n c.dbLastModified\n ? ` Last modified: ${c.dbLastModified}`\n : null,\n c.composerRowCount !== null\n ? ` Composer rows: ${pc.bold(String(c.composerRowCount))}`\n : null,\n c.error\n ? ` ${pc.red('Error:')} ${c.error}`\n : null,\n ].filter(Boolean).join('\\n'));\n\n // Claude Code\n const cc = result.claudeCode;\n const ccStatus = cc.baseDirExists\n ? pc.green('OK')\n : pc.red('NOT FOUND');\n\n clack.log.info(`${pc.bold('Claude Code')} ${cc.enabled ? '' : pc.dim('(disabled in config)')}`);\n clack.log.message([\n ` Base dir: ${pc.dim(cc.baseDir)}`,\n ` Status: ${ccStatus}`,\n cc.baseDirExists\n ? ` Project dirs: ${pc.bold(String(cc.projectDirCount))}`\n : null,\n cc.baseDirExists\n ? ` Session files: ${pc.bold(String(cc.totalSessionFiles))}`\n : null,\n cc.error\n ? ` ${pc.red('Error:')} ${cc.error}`\n : null,\n ].filter(Boolean).join('\\n'));\n\n clack.outro('Source check complete.');\n}\n\n// ── Sub-command: scan ────────────────────────────────────────────\n\nasync function debugScan(options: DebugCommandOptions): Promise<void> {\n if (!options.json) {\n clack.intro(pc.bgYellow(pc.black(' lore debug scan ')));\n\n const spinner = clack.spinner();\n spinner.start('Loading conversations from all sources...');\n\n const result = await diagnosePipeline({ repoFilter: options.repo });\n spinner.stop('Conversations loaded.');\n\n renderScanResult(result);\n return;\n }\n\n // JSON mode — no clack\n const result = await diagnosePipeline({ repoFilter: options.repo });\n writeJson(result);\n}\n\nfunction renderScanResult(result: PipelineDiagnosticResult): void {\n // ── Raw load counts ────────────────────────────────────────────\n const loadParts = Object.entries(result.rawLoaded)\n .map(([source, count]) => `${pc.bold(String(count))} ${source}`)\n .join(', ');\n\n clack.log.info(`${pc.bold('Raw loaded:')} ${loadParts} (${pc.bold(String(result.totalRawLoaded))} total)`);\n\n // ── Waterfall ──────────────────────────────────────────────────\n if (result.steps.length > 0) {\n clack.log.info(pc.bold('Pipeline waterfall:'));\n\n for (const step of result.steps) {\n const arrow = step.removedCount > 0\n ? pc.red(`-${step.removedCount}`)\n : pc.green('+0');\n\n clack.log.message(\n ` ${step.name}: ${pc.bold(String(step.inputCount))} -> ${pc.bold(String(step.outputCount))} (${arrow})`,\n );\n\n // Show removed conversations (up to 5)\n const toShow = step.removed.slice(0, 5);\n for (const r of toShow) {\n const titleStr = r.title.length > 50 ? r.title.slice(0, 47) + '...' : r.title;\n clack.log.message(\n ` ${pc.red('x')} ${pc.dim(r.id)} \"${titleStr}\"`,\n );\n clack.log.message(\n ` ${pc.dim(r.reason)}`,\n );\n }\n if (step.removed.length > 5) {\n clack.log.message(` ${pc.dim(`... and ${step.removed.length - 5} more`)}`);\n }\n }\n }\n\n // ── Lore filter pattern breakdown ──────────────────────────────\n if (Object.keys(result.filterPatternBreakdown).length > 0) {\n clack.log.info(pc.bold('Lore filter pattern breakdown:'));\n for (const [pattern, count] of Object.entries(result.filterPatternBreakdown)) {\n clack.log.message(` ${pc.dim(pattern)}: ${pc.bold(String(count))}`);\n }\n }\n\n // ── Repo assignment stats ──────────────────────────────────────\n clack.log.info(pc.bold('Repo assignment:'));\n clack.log.message([\n ` Assigned: ${pc.bold(String(result.repoStats.assigned))}`,\n ` Unassigned: ${pc.bold(String(result.repoStats.unassigned))}`,\n ` Unique repos: ${result.repoStats.uniqueRepos.length > 0\n ? result.repoStats.uniqueRepos.map((r) => pc.cyan(r)).join(', ')\n : pc.dim('(none)')}`,\n ].join('\\n'));\n\n if (result.selectedRepos) {\n clack.log.message(` Selected repos: ${result.selectedRepos.map((r) => pc.cyan(r)).join(', ')}`);\n }\n\n // ── Surviving conversations ────────────────────────────────────\n const newCount = result.surviving.filter((s) => s.cacheStatus === 'new').length;\n const staleCount = result.surviving.filter((s) => s.cacheStatus === 'stale').length;\n const cachedCount = result.surviving.filter((s) => s.cacheStatus === 'cached').length;\n const trivialCount = result.surviving.filter((s) => s.cacheStatus === 'trivial').length;\n\n clack.log.info(pc.bold('Summarization readiness:'));\n clack.log.message([\n ` New (will summarize): ${pc.green(String(newCount))}`,\n ` Stale (will re-summarize): ${pc.yellow(String(staleCount))}`,\n ` Cached (up to date): ${pc.dim(String(cachedCount))}`,\n ` Trivial (< 4 msgs): ${pc.dim(String(trivialCount))}`,\n ].join('\\n'));\n\n // Show new/stale conversations\n const actionable = result.surviving.filter((s) => s.cacheStatus === 'new' || s.cacheStatus === 'stale');\n if (actionable.length > 0) {\n clack.log.message('');\n for (const s of actionable.slice(0, 10)) {\n const marker = s.cacheStatus === 'new' ? pc.green('+') : pc.yellow('*');\n const titleStr = s.title.length > 55 ? s.title.slice(0, 52) + '...' : s.title;\n const repo = s.repoName ?? s.repoPath ?? pc.dim('no repo');\n clack.log.message(\n ` ${marker} ${titleStr} ${pc.dim(`(${s.source}, ${s.messageCount} msgs, ${repo})`)}`,\n );\n }\n if (actionable.length > 10) {\n clack.log.message(` ${pc.dim(`... and ${actionable.length - 10} more`)}`);\n }\n }\n\n // ── Issues ─────────────────────────────────────────────────────\n if (result.issues.length > 0) {\n clack.log.info('');\n clack.log.info(pc.bold('Detected issues:'));\n\n for (const issue of result.issues) {\n const icon = severityIcon(issue);\n const color = severityColor(issue);\n clack.log.message(` ${icon} ${color(issue.message)}`);\n clack.log.message(` ${pc.dim(issue.hint)}`);\n }\n } else {\n clack.log.success('No issues detected.');\n }\n\n clack.outro('Debug scan complete.');\n}\n\n// ── Sub-command: conversations ───────────────────────────────────\n\nasync function debugConversations(options: DebugCommandOptions): Promise<void> {\n if (!options.json) {\n clack.intro(pc.bgYellow(pc.black(' lore debug conversations ')));\n const spinner = clack.spinner();\n spinner.start('Loading all conversations...');\n\n const conversations = await diagnoseConversations({\n limit: options.limit,\n source: options.source,\n });\n spinner.stop(`Loaded ${conversations.length} conversations.`);\n\n if (conversations.length === 0) {\n clack.log.warn('No conversations found.');\n clack.outro('');\n return;\n }\n\n for (const c of conversations) {\n const titleStr = c.title.length > 55 ? c.title.slice(0, 52) + '...' : c.title;\n const repo = c.repoName ?? c.repoPath ?? pc.dim('no repo');\n const statusColor = cacheStatusColor(c.cacheStatus);\n\n clack.log.message('');\n clack.log.message(\n `${pc.bold(titleStr)} ${pc.dim(`(${c.source}, ${c.messageCount} msgs)`)}`,\n );\n clack.log.message(` ID: ${pc.dim(c.id)}`);\n clack.log.message(` Repo: ${repo}`);\n clack.log.message(` Date: ${c.createdAt ?? pc.dim('unknown')}`);\n clack.log.message(` Status: ${statusColor(c.cacheStatus)}`);\n\n // Show filter verdicts\n const excludedFilters = c.filterVerdicts.filter((v) => v.excluded);\n if (excludedFilters.length > 0) {\n clack.log.message(` ${pc.red('Excluded by:')}`);\n for (const v of excludedFilters) {\n clack.log.message(` ${pc.red('x')} ${v.filter}: ${pc.dim(v.reason)}`);\n }\n } else {\n const passedFilters = c.filterVerdicts.map((v) => pc.green(v.filter)).join(', ');\n clack.log.message(` ${pc.green('Passes all filters:')} ${passedFilters}`);\n }\n }\n\n clack.outro(`${conversations.length} conversations audited.`);\n return;\n }\n\n // JSON mode\n const conversations = await diagnoseConversations({\n limit: options.limit,\n source: options.source,\n });\n writeJson(conversations);\n}\n\n// ── Formatting helpers ───────────────────────────────────────────\n\nfunction writeJson(data: unknown): void {\n process.stdout.write(JSON.stringify(data, null, 2) + '\\n');\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nfunction severityIcon(issue: DetectedIssue): string {\n switch (issue.severity) {\n case 'error': return pc.red('!!');\n case 'warning': return pc.yellow('!');\n case 'info': return pc.blue('i');\n }\n}\n\nfunction severityColor(issue: DetectedIssue): (s: string) => string {\n switch (issue.severity) {\n case 'error': return pc.red;\n case 'warning': return pc.yellow;\n case 'info': return pc.blue;\n }\n}\n\nfunction cacheStatusColor(status: string): (s: string) => string {\n switch (status) {\n case 'new': return pc.green;\n case 'stale': return pc.yellow;\n case 'cached': return pc.dim;\n case 'trivial': return pc.dim;\n case 'filtered': return pc.red;\n default: return pc.white;\n }\n}\n","/**\n * Data source health diagnostic.\n *\n * Checks accessibility and basic stats for each conversation data source\n * (Cursor SQLite DB, Claude Code JSONL directories). Used by `lore debug sources`\n * to quickly verify that data sources are reachable and populated.\n *\n * Read-only — no mutations, no AI calls.\n */\n\nimport { existsSync, statSync, readdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { getClaudeCodeBaseDir } from '../utils/paths.js';\nimport { loadConfig } from '../config/loader.js';\nimport { getLoreDir } from '../utils/paths.js';\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport interface SourceHealthResult {\n cursor: CursorHealth;\n claudeCode: ClaudeCodeHealth;\n}\n\nexport interface CursorHealth {\n enabled: boolean;\n dbPath: string;\n dbExists: boolean;\n dbSizeBytes: number | null;\n dbLastModified: string | null;\n composerRowCount: number | null;\n error: string | null;\n}\n\nexport interface ClaudeCodeHealth {\n enabled: boolean;\n baseDir: string;\n baseDirExists: boolean;\n projectDirCount: number;\n totalSessionFiles: number;\n error: string | null;\n}\n\n// ── Cursor DB path ───────────────────────────────────────────────\n\nfunction getCursorDbPath(): string {\n return join(\n homedir(),\n 'Library',\n 'Application Support',\n 'Cursor',\n 'User',\n 'globalStorage',\n 'state.vscdb',\n );\n}\n\n// ── Public API ───────────────────────────────────────────────────\n\nexport async function diagnoseSourceHealth(): Promise<SourceHealthResult> {\n const loreDir = getLoreDir();\n const config = existsSync(loreDir) ? loadConfig(loreDir) : null;\n\n const [cursor, claudeCode] = await Promise.all([\n diagnoseCursor(config),\n diagnoseClaudeCode(config),\n ]);\n\n return { cursor, claudeCode };\n}\n\n// ── Cursor diagnostic ────────────────────────────────────────────\n\nasync function diagnoseCursor(\n config: ReturnType<typeof loadConfig> | null,\n): Promise<CursorHealth> {\n const dbPath = getCursorDbPath();\n const enabled = config?.sources?.cursor?.enabled ?? true;\n\n const result: CursorHealth = {\n enabled,\n dbPath,\n dbExists: false,\n dbSizeBytes: null,\n dbLastModified: null,\n composerRowCount: null,\n error: null,\n };\n\n if (!existsSync(dbPath)) {\n result.error = 'Database file not found';\n return result;\n }\n\n result.dbExists = true;\n\n try {\n const stats = statSync(dbPath);\n result.dbSizeBytes = stats.size;\n result.dbLastModified = stats.mtime.toISOString();\n } catch (err) {\n result.error = `Cannot stat DB: ${err instanceof Error ? err.message : String(err)}`;\n return result;\n }\n\n // Try to open and count composer rows\n try {\n const mod = await import('better-sqlite3');\n const Database = mod.default;\n const db = new Database(dbPath, { readonly: true, fileMustExist: true });\n\n try {\n const stmt = db.prepare(\n \"SELECT COUNT(*) as cnt FROM cursorDiskKV WHERE key LIKE 'composerData:%'\",\n );\n const row = stmt.get() as { cnt: number } | undefined;\n result.composerRowCount = row?.cnt ?? 0;\n } catch (err) {\n result.error = `Query failed: ${err instanceof Error ? err.message : String(err)}`;\n } finally {\n try { db.close(); } catch { /* already closed */ }\n }\n } catch (err) {\n result.error = `Cannot open DB: ${err instanceof Error ? err.message : String(err)}`;\n }\n\n return result;\n}\n\n// ── Claude Code diagnostic ───────────────────────────────────────\n\nasync function diagnoseClaudeCode(\n config: ReturnType<typeof loadConfig> | null,\n): Promise<ClaudeCodeHealth> {\n const baseDir = getClaudeCodeBaseDir();\n const enabled = config?.sources?.claudeCode?.enabled ?? true;\n\n const result: ClaudeCodeHealth = {\n enabled,\n baseDir,\n baseDirExists: false,\n projectDirCount: 0,\n totalSessionFiles: 0,\n error: null,\n };\n\n if (!existsSync(baseDir)) {\n result.error = 'Projects directory not found';\n return result;\n }\n\n result.baseDirExists = true;\n\n try {\n const entries = readdirSync(baseDir, { withFileTypes: true });\n const projectDirs = entries.filter((e) => e.isDirectory());\n result.projectDirCount = projectDirs.length;\n\n let totalJsonl = 0;\n for (const dir of projectDirs) {\n try {\n const files = readdirSync(join(baseDir, dir.name));\n totalJsonl += files.filter((f) => f.endsWith('.jsonl')).length;\n } catch { /* skip unreadable dirs */ }\n }\n result.totalSessionFiles = totalJsonl;\n } catch (err) {\n result.error = `Cannot read directory: ${err instanceof Error ? err.message : String(err)}`;\n }\n\n return result;\n}\n","/**\n * Pipeline diagnostic — instrumented scan pipeline.\n *\n * Runs the full conversation load → filter → classify pipeline in read-only\n * mode, collecting per-filter removal reasons and auto-detecting common issues.\n *\n * Designed for both human-readable output (via `lore debug scan`) and\n * structured JSON output for agent consumption (`--json` flag).\n *\n * Read-only — no AI invocations, no state mutations, no file writes.\n */\n\nimport { existsSync } from 'node:fs';\nimport { discoverKnownRepos, getLoreDir } from '../utils/paths.js';\nimport { readClassificationCache } from '../classifier/classifier.js';\nimport { shouldIgnoreConversation, matchedIgnoreRule } from '../utils/conversation-filter.js';\nimport { buildLoreIgnoreMap } from '../utils/lore-filter.js';\nimport { getAllSources } from '../data-sources/registry.js';\nimport { readSummaryIndex, reconcileIndex } from '../summarizer/cache.js';\nimport { loadConfig } from '../config/loader.js';\nimport {\n selectReposForProcessing,\n getSelectedRepoNames,\n} from '../repos/selector.js';\nimport type { FullConversation, SummaryIndex } from '../summarizer/types.js';\n\n// ── Constants (mirror summarizer) ────────────────────────────────\n\nconst MIN_MESSAGES = 4;\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport type FilterName =\n | 'lore-process'\n | 'repo-filter'\n | 'repo-selection'\n | 'min-messages';\n\nexport interface FilteredConversation {\n id: string;\n title: string;\n source: 'cursor' | 'claude-code';\n messageCount: number;\n repoName: string | null;\n repoPath: string | null;\n createdAt: string | null;\n filter: FilterName;\n reason: string;\n /** First 100 chars of the first user message (aids debugging false positives) */\n firstMessageSnippet: string | null;\n}\n\nexport interface SurvivingConversation {\n id: string;\n title: string;\n source: 'cursor' | 'claude-code';\n messageCount: number;\n repoName: string | null;\n repoPath: string | null;\n createdAt: string | null;\n cacheStatus: 'new' | 'stale' | 'cached' | 'trivial';\n /** First 100 chars of the first user message (aids debugging) */\n firstMessageSnippet: string | null;\n}\n\nexport interface PipelineStep {\n name: string;\n inputCount: number;\n outputCount: number;\n removedCount: number;\n removed: FilteredConversation[];\n}\n\nexport interface DetectedIssue {\n severity: 'error' | 'warning' | 'info';\n message: string;\n hint: string;\n}\n\nexport interface PipelineDiagnosticResult {\n /** Per-source raw load counts */\n rawLoaded: Record<string, number>;\n /** Total conversations loaded before any filtering */\n totalRawLoaded: number;\n /** Step-by-step pipeline trace */\n steps: PipelineStep[];\n /** Conversations that survived all filters */\n surviving: SurvivingConversation[];\n /** Auto-detected issues */\n issues: DetectedIssue[];\n /** Repo assignment stats */\n repoStats: {\n assigned: number;\n unassigned: number;\n uniqueRepos: string[];\n };\n /** Selected repos from config */\n selectedRepos: string[] | null;\n /** Breakdown of which lore-process filter pattern caught how many conversations */\n filterPatternBreakdown: Record<string, number>;\n}\n\n// ── Options ──────────────────────────────────────────────────────\n\nexport interface PipelineDiagnosticOptions {\n /** Filter to a specific repo (mirrors --repo flag) */\n repoFilter?: string;\n}\n\n// ── Public API ───────────────────────────────────────────────────\n\nexport async function diagnosePipeline(\n options: PipelineDiagnosticOptions = {},\n): Promise<PipelineDiagnosticResult> {\n const steps: PipelineStep[] = [];\n const issues: DetectedIssue[] = [];\n\n // ── Load raw conversations from all sources ────────────────────\n const classCache = readClassificationCache();\n const knownRepos = discoverKnownRepos();\n const sources = getAllSources();\n\n const sourceResults = await Promise.all(\n sources.map((s) => s.loadFullConversations(classCache, knownRepos)),\n );\n\n const rawLoaded: Record<string, number> = {};\n for (let i = 0; i < sources.length; i++) {\n rawLoaded[sources[i].name] = sourceResults[i].length;\n }\n\n let conversations: FullConversation[] = sourceResults.flat();\n const totalRawLoaded = conversations.length;\n\n if (totalRawLoaded === 0) {\n issues.push({\n severity: 'error',\n message: 'No conversations loaded from any data source',\n hint: 'Run `lore debug sources` to check if data sources are accessible and contain data.',\n });\n }\n\n // ── Filter 1: Lore process conversation filter (unified) ───────\n // Uses origin-based detection: phase store tags + prompt markers.\n // User conversations in the lore repo are intentionally allowed through.\n const ignoreMap = buildLoreIgnoreMap();\n const loreProcessRemoved: FilteredConversation[] = [];\n const afterLoreProcess = conversations.filter((c) => {\n const rule = matchedIgnoreRule(c.id, ignoreMap, c.title, c.firstUserMessage);\n if (rule) {\n const reason = rule === 'id'\n ? `ID ${c.id} is in the phase store (created by a lore process)`\n : `Matched ignore pattern: ${rule}`;\n loreProcessRemoved.push(makeFiltered(c, 'lore-process', reason));\n return false;\n }\n return true;\n });\n steps.push(makeStep('Lore process filter (phase-tag + prompt-signature)', conversations.length, afterLoreProcess.length, loreProcessRemoved));\n conversations = afterLoreProcess;\n\n // ── Repo assignment stats ──────────────────────────────────────\n const assigned = conversations.filter((c) => c.repoName || c.repoPath);\n const unassigned = conversations.filter((c) => !c.repoName && !c.repoPath);\n const uniqueRepos = [\n ...new Set(\n conversations\n .map((c) => c.repoName ?? c.repoPath)\n .filter((v): v is string => !!v),\n ),\n ];\n\n const repoStats = {\n assigned: assigned.length,\n unassigned: unassigned.length,\n uniqueRepos,\n };\n\n if (unassigned.length > 0) {\n const pct = Math.round((unassigned.length / (conversations.length || 1)) * 100);\n issues.push({\n severity: pct > 50 ? 'warning' : 'info',\n message: `${unassigned.length} conversation(s) (${pct}%) have no repo assignment`,\n hint: 'Workspace extraction may be failing for these conversations. They cannot match any repo filter. Check if the Cursor conversations have file URIs in their composer data.',\n });\n }\n\n // ── Filter 5: Repo selection (config-driven) ───────────────────\n let selectedRepos: string[] | null = null;\n const loreDir = getLoreDir();\n if (!options.repoFilter && existsSync(loreDir)) {\n try {\n const config = loadConfig(loreDir);\n const repoSelection = await selectReposForProcessing(config.repos);\n selectedRepos = getSelectedRepoNames(repoSelection);\n } catch { /* config not available, skip */ }\n }\n\n if (options.repoFilter) {\n const filter = options.repoFilter.toLowerCase();\n const repoFilterRemoved: FilteredConversation[] = [];\n const afterRepoFilter = conversations.filter((c) => {\n const matches =\n c.repoName?.toLowerCase().includes(filter) ||\n c.repoPath?.toLowerCase().includes(filter);\n if (!matches) {\n repoFilterRemoved.push(makeFiltered(c, 'repo-filter',\n `repoName=\"${c.repoName ?? 'null'}\", repoPath=\"${c.repoPath ?? 'null'}\" does not match filter \"${options.repoFilter}\"`,\n ));\n return false;\n }\n return true;\n });\n steps.push(makeStep(`Repo filter: \"${options.repoFilter}\"`, conversations.length, afterRepoFilter.length, repoFilterRemoved));\n conversations = afterRepoFilter;\n\n if (repoFilterRemoved.length > 0 && afterRepoFilter.length === 0) {\n issues.push({\n severity: 'error',\n message: `No conversations match repo filter \"${options.repoFilter}\"`,\n hint: `Available repos: ${uniqueRepos.length > 0 ? uniqueRepos.join(', ') : '(none detected)'}. ` +\n `${unassigned.length} conversations have no repo assignment and cannot match any filter.`,\n });\n }\n }\n\n if (selectedRepos && selectedRepos.length > 0 && !options.repoFilter) {\n const selectedSet = new Set(selectedRepos.map((n) => n.toLowerCase()));\n const repoSelRemoved: FilteredConversation[] = [];\n const afterRepoSel = conversations.filter((c) => {\n if (!c.repoName) {\n repoSelRemoved.push(makeFiltered(c, 'repo-selection',\n `No repoName assigned — cannot match selected repos: ${selectedRepos!.join(', ')}`,\n ));\n return false;\n }\n const lower = c.repoName.toLowerCase();\n const matches = selectedSet.has(lower) || selectedSet.has(lower.split('/').pop() ?? lower);\n if (!matches) {\n repoSelRemoved.push(makeFiltered(c, 'repo-selection',\n `repoName=\"${c.repoName}\" not in selected repos: ${selectedRepos!.join(', ')}`,\n ));\n return false;\n }\n return true;\n });\n steps.push(makeStep(`Repo selection (${selectedRepos.length} selected)`, conversations.length, afterRepoSel.length, repoSelRemoved));\n conversations = afterRepoSel;\n }\n\n // ── Cache / summarization readiness ────────────────────────────\n const index = readSummaryIndex();\n reconcileIndex(index);\n\n const surviving: SurvivingConversation[] = [];\n const trivialRemoved: FilteredConversation[] = [];\n\n for (const c of conversations) {\n const cached = index.conversations[c.id];\n if (cached) {\n const storedCount = cached.messageCount ?? 0;\n if (storedCount > 0 && c.messageCount > storedCount) {\n surviving.push(makeSurviving(c, 'stale'));\n } else {\n surviving.push(makeSurviving(c, 'cached'));\n }\n } else if (c.messageCount < MIN_MESSAGES) {\n trivialRemoved.push(makeFiltered(c, 'min-messages',\n `${c.messageCount} messages < minimum ${MIN_MESSAGES}`,\n ));\n surviving.push(makeSurviving(c, 'trivial'));\n } else {\n surviving.push(makeSurviving(c, 'new'));\n }\n }\n\n if (trivialRemoved.length > 0) {\n steps.push(makeStep(`Min messages (< ${MIN_MESSAGES})`, conversations.length, conversations.length - trivialRemoved.length, trivialRemoved));\n }\n\n // ── Issue: trivial conversations ───────────────────────────────\n if (trivialRemoved.length > 0 && trivialRemoved.length === conversations.length) {\n issues.push({\n severity: 'warning',\n message: `All ${trivialRemoved.length} remaining conversation(s) are trivial (< ${MIN_MESSAGES} messages)`,\n hint: 'Conversations need at least 4 messages to be considered for summarization. Try having longer conversations or check if message parsing is working correctly.',\n });\n }\n\n // ── Summary stats ──────────────────────────────────────────────\n const newCount = surviving.filter((s) => s.cacheStatus === 'new').length;\n const staleCount = surviving.filter((s) => s.cacheStatus === 'stale').length;\n const cachedCount = surviving.filter((s) => s.cacheStatus === 'cached').length;\n const trivialCount = surviving.filter((s) => s.cacheStatus === 'trivial').length;\n\n if (newCount === 0 && staleCount === 0 && cachedCount === 0 && trivialCount === 0 && totalRawLoaded > 0) {\n issues.push({\n severity: 'error',\n message: 'All conversations were filtered out before reaching summarization',\n hint: 'Check the step-by-step waterfall above to see which filter is removing them.',\n });\n }\n\n // ── Filter pattern breakdown ─────────────────────────────────────\n const filterPatternBreakdown: Record<string, number> = {};\n for (const removed of loreProcessRemoved) {\n const key = removed.reason.startsWith('ID ')\n ? 'phase-store-id'\n : removed.reason.replace('Matched ignore pattern: ', '');\n filterPatternBreakdown[key] = (filterPatternBreakdown[key] ?? 0) + 1;\n }\n\n return {\n rawLoaded,\n totalRawLoaded,\n steps,\n surviving,\n issues,\n repoStats,\n selectedRepos,\n filterPatternBreakdown,\n };\n}\n\n// ── Per-conversation diagnostic (for `debug conversations`) ──────\n\nexport interface ConversationDiagnostic {\n id: string;\n title: string;\n source: 'cursor' | 'claude-code';\n messageCount: number;\n repoName: string | null;\n repoPath: string | null;\n createdAt: string | null;\n /** Which filters would exclude this conversation */\n filterVerdicts: Array<{\n filter: FilterName;\n excluded: boolean;\n reason: string;\n }>;\n /** Final cache status if it passes all filters */\n cacheStatus: 'new' | 'stale' | 'cached' | 'trivial' | 'filtered';\n}\n\nexport interface ConversationDiagnosticOptions {\n /** Max conversations to return */\n limit?: number;\n /** Filter by data source */\n source?: 'cursor' | 'claude-code';\n}\n\nexport async function diagnoseConversations(\n options: ConversationDiagnosticOptions = {},\n): Promise<ConversationDiagnostic[]> {\n const classCache = readClassificationCache();\n const knownRepos = discoverKnownRepos();\n const sources = getAllSources();\n\n const sourceResults = await Promise.all(\n sources.map((s) => s.loadFullConversations(classCache, knownRepos)),\n );\n\n let conversations = sourceResults.flat();\n\n // Apply source filter\n if (options.source) {\n conversations = conversations.filter((c) => c.source === options.source);\n }\n\n // Apply limit\n if (options.limit && options.limit > 0) {\n conversations = conversations.slice(0, options.limit);\n }\n\n const ignoreMap = buildLoreIgnoreMap();\n const index = readSummaryIndex();\n reconcileIndex(index);\n\n return conversations.map((c) => {\n const verdicts: ConversationDiagnostic['filterVerdicts'] = [];\n let excluded = false;\n\n // Check lore process filter (unified: phase-tag + prompt-signature)\n const rule = matchedIgnoreRule(c.id, ignoreMap, c.title, c.firstUserMessage);\n verdicts.push({\n filter: 'lore-process',\n excluded: !!rule,\n reason: rule\n ? (rule === 'id' ? `ID ${c.id} found in phase store` : `Matched ignore pattern: ${rule}`)\n : 'Not a lore process conversation',\n });\n if (rule) excluded = true;\n\n const isTrivial = c.messageCount < MIN_MESSAGES;\n verdicts.push({\n filter: 'min-messages',\n excluded: isTrivial,\n reason: isTrivial ? `${c.messageCount} messages < ${MIN_MESSAGES} minimum` : `${c.messageCount} messages >= ${MIN_MESSAGES} minimum`,\n });\n\n // Determine cache status\n let cacheStatus: ConversationDiagnostic['cacheStatus'] = 'filtered';\n if (!excluded) {\n const cached = index.conversations[c.id];\n if (cached) {\n const storedCount = cached.messageCount ?? 0;\n cacheStatus = (storedCount > 0 && c.messageCount > storedCount) ? 'stale' : 'cached';\n } else {\n cacheStatus = isTrivial ? 'trivial' : 'new';\n }\n }\n\n return {\n id: c.id,\n title: c.title,\n source: c.source,\n messageCount: c.messageCount,\n repoName: c.repoName,\n repoPath: c.repoPath,\n createdAt: c.createdAt,\n filterVerdicts: verdicts,\n cacheStatus,\n };\n });\n}\n\n// ── Helpers ──────────────────────────────────────────────────────\n\nfunction snippetFromFirstMsg(msg: string | null): string | null {\n if (!msg) return null;\n return msg.length > 100 ? msg.slice(0, 100) + '...' : msg;\n}\n\nfunction makeFiltered(c: FullConversation, filter: FilterName, reason: string): FilteredConversation {\n return {\n id: c.id,\n title: c.title,\n source: c.source,\n messageCount: c.messageCount,\n repoName: c.repoName,\n repoPath: c.repoPath,\n createdAt: c.createdAt,\n filter,\n reason,\n firstMessageSnippet: snippetFromFirstMsg(c.firstUserMessage),\n };\n}\n\nfunction makeSurviving(c: FullConversation, cacheStatus: SurvivingConversation['cacheStatus']): SurvivingConversation {\n return {\n id: c.id,\n title: c.title,\n source: c.source,\n messageCount: c.messageCount,\n repoName: c.repoName,\n repoPath: c.repoPath,\n createdAt: c.createdAt,\n cacheStatus,\n firstMessageSnippet: snippetFromFirstMsg(c.firstUserMessage),\n };\n}\n\nfunction makeStep(\n name: string,\n inputCount: number,\n outputCount: number,\n removed: FilteredConversation[],\n): PipelineStep {\n return {\n name,\n inputCount,\n outputCount,\n removedCount: removed.length,\n removed,\n };\n}\n\n// buildLoreRepoReason removed — repo-name-based filtering replaced\n// by process-conversation detection (phase tags + prompt markers).\n","/**\n * `lore pr` command — generate a PR description from git + cached Lore data.\n *\n * Usage:\n * lore pr — generate and copy to clipboard\n * lore pr --inject — also update the open PR on GitHub\n * lore pr --tier template — use template only (no LLM)\n * lore pr --tier detailed — use Sonnet with full diffs\n * lore pr --base develop — use 'develop' as the base branch\n */\n\nimport pc from 'picocolors';\nimport * as clack from '@clack/prompts';\nimport { gatherPrContext, detectDefaultBranch } from '../pr/context.js';\nimport { generatePrDescription, type PrDescriptionTier } from '../pr/describe.js';\nimport { injectPrDescription, detectOpenPr, isGhAvailable } from '../pr/github.js';\nimport { logger } from '../utils/logger.js';\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport interface PrCommandOptions {\n base?: string;\n tier?: string;\n copy?: boolean;\n inject?: boolean;\n}\n\n// ── Command ──────────────────────────────────────────────────────\n\nexport async function prCommand(options: PrCommandOptions = {}): Promise<void> {\n clack.intro(pc.bgCyan(pc.black(' lore pr ')));\n\n const repoPath = process.cwd();\n\n // 1. Resolve tier\n const tier = parseTier(options.tier);\n clack.log.info(`Tier: ${tierLabel(tier)}`);\n\n // 2. Resolve base branch\n const spin = clack.spinner();\n spin.start('Detecting base branch...');\n\n let base: string;\n if (options.base) {\n base = options.base;\n } else {\n // Try to get base from open PR first\n const pr = await detectOpenPr(repoPath);\n if (pr) {\n base = pr.baseRefName;\n spin.stop(`Base branch: ${pc.cyan(base)} (from PR #${pr.number})`);\n } else {\n base = await detectDefaultBranch(repoPath);\n spin.stop(`Base branch: ${pc.cyan(base)} (auto-detected)`);\n }\n }\n\n // 3. Gather context\n spin.start('Gathering PR context...');\n const includeDiff = tier === 'detailed';\n const context = await gatherPrContext(repoPath, base, includeDiff);\n spin.stop(\n `${context.commits.length} commits, ${context.changedFiles.length} files, ${context.matchedSummaries.length} matched conversations`,\n );\n\n if (context.commits.length === 0 && context.changedFiles.length === 0) {\n clack.log.warn(`No changes found between ${pc.cyan(base)} and HEAD.`);\n clack.log.info(`Make sure you have commits ahead of ${pc.cyan(base)}.`);\n clack.outro('');\n return;\n }\n\n // 4. Generate description\n spin.start(`Generating description (${tierLabel(tier)})...`);\n const description = await generatePrDescription(context, tier);\n spin.stop('Description generated');\n\n // 5. Display the description\n clack.log.message(pc.dim('─'.repeat(60)));\n console.log(description);\n clack.log.message(pc.dim('─'.repeat(60)));\n\n // 6. Copy to clipboard (if requested or by default)\n if (options.copy !== false) {\n try {\n const { execSync } = await import('node:child_process');\n execSync('pbcopy', { input: description, timeout: 5000 });\n clack.log.success('Copied to clipboard!');\n } catch {\n // pbcopy is macOS-specific; on other platforms, just show the output\n clack.log.info('Could not copy to clipboard (pbcopy not available).');\n }\n }\n\n // 7. Inject into PR (if requested)\n if (options.inject) {\n spin.start('Updating PR on GitHub...');\n const result = await injectPrDescription(repoPath, async () => description);\n switch (result) {\n case 'updated':\n spin.stop('PR description updated on GitHub!');\n break;\n case 'no-pr':\n spin.stop('No open PR found for this branch.');\n break;\n case 'no-gh':\n spin.stop('gh CLI not available or not authenticated.');\n break;\n case 'error':\n spin.stop('Failed to update PR.');\n break;\n }\n }\n\n clack.outro(pc.green('Done'));\n}\n\n// ── Helpers ──────────────────────────────────────────────────────\n\nfunction parseTier(input?: string): PrDescriptionTier {\n switch (input?.toLowerCase()) {\n case 'a':\n case 'template':\n return 'template';\n case 'b':\n case 'fast':\n return 'fast';\n case 'c':\n case 'detailed':\n return 'detailed';\n default:\n return 'fast';\n }\n}\n\nfunction tierLabel(tier: PrDescriptionTier): string {\n switch (tier) {\n case 'template':\n return `${pc.yellow('template')} (no LLM)`;\n case 'fast':\n return `${pc.cyan('fast')} (Haiku)`;\n case 'detailed':\n return `${pc.magenta('detailed')} (Sonnet + diffs)`;\n }\n}\n","/**\n * Gather all context needed to generate a PR description.\n *\n * Combines git log, diff stat, matched conversation summaries,\n * and optionally full diffs into a single PrContext object.\n */\n\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { join } from 'node:path';\nimport { parseRemoteOrigin, resolveGitRoot } from '../utils/git.js';\nimport { loadRepoCluster, repoNameToSlug } from '../summarizer/cache.js';\nimport { matchConversationsToPr, type MatchedSummary } from './matcher.js';\nimport { GIT_MEDIUM_TIMEOUT_MS } from '../utils/constants.js';\n\nconst execFileAsync = promisify(execFile);\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport interface PrCommit {\n sha: string;\n subject: string;\n}\n\nexport interface PrContext {\n /** Current branch name */\n branch: string;\n /** Base branch the PR targets */\n base: string;\n /** Commits in the PR range (base..HEAD) */\n commits: PrCommit[];\n /** Files changed in the PR */\n changedFiles: string[];\n /** Conversation summaries matched by file overlap */\n matchedSummaries: MatchedSummary[];\n /** Per-repo clustered knowledge (if available) */\n clusteredKnowledge: string | null;\n /** Full diff text (only populated for Tier C) */\n diff: string | null;\n /** Repo name (e.g. \"org/repo\") */\n repoName: string | null;\n}\n\n// ── Public API ───────────────────────────────────────────────────\n\n/**\n * Gather all PR context from git and cached Lore data.\n *\n * @param repoPath Absolute path to the repo root\n * @param base Base branch (e.g. \"main\")\n * @param includeDiff Whether to include full diff text (for Tier C)\n */\nexport async function gatherPrContext(\n repoPath: string,\n base: string,\n includeDiff = false,\n): Promise<PrContext> {\n // Run independent git commands in parallel\n const [branch, commits, changedFiles, diff, repoName] = await Promise.all([\n getCurrentBranch(repoPath),\n getCommitLog(repoPath, base),\n getChangedFiles(repoPath, base),\n includeDiff ? getDiff(repoPath, base) : Promise.resolve(null),\n getRepoName(repoPath),\n ]);\n\n // Match conversations from cache\n const matchedSummaries = repoName\n ? matchConversationsToPr(repoName, changedFiles, repoPath)\n : [];\n\n // Load clustered knowledge\n let clusteredKnowledge: string | null = null;\n if (repoName) {\n const slug = repoNameToSlug(repoName);\n clusteredKnowledge = loadRepoCluster(slug);\n }\n\n return {\n branch,\n base,\n commits,\n changedFiles,\n matchedSummaries,\n clusteredKnowledge,\n diff,\n repoName,\n };\n}\n\n/**\n * Auto-detect the default branch for the repo (main or master).\n */\nexport async function detectDefaultBranch(repoPath: string): Promise<string> {\n // Try common default branch names\n for (const candidate of ['main', 'master']) {\n try {\n await execFileAsync('git', ['rev-parse', '--verify', candidate], {\n cwd: repoPath,\n timeout: GIT_MEDIUM_TIMEOUT_MS,\n });\n return candidate;\n } catch {\n // Branch doesn't exist, try next\n }\n }\n\n // Fallback: try to get the default from remote\n try {\n const { stdout } = await execFileAsync(\n 'git',\n ['symbolic-ref', 'refs/remotes/origin/HEAD', '--short'],\n { cwd: repoPath, timeout: GIT_MEDIUM_TIMEOUT_MS },\n );\n const ref = stdout.trim(); // e.g. \"origin/main\"\n return ref.replace('origin/', '');\n } catch {\n return 'main'; // ultimate fallback\n }\n}\n\n// ── Git helpers ──────────────────────────────────────────────────\n\nasync function getCurrentBranch(repoPath: string): Promise<string> {\n try {\n const { stdout } = await execFileAsync(\n 'git',\n ['branch', '--show-current'],\n { cwd: repoPath, timeout: GIT_MEDIUM_TIMEOUT_MS },\n );\n return stdout.trim();\n } catch {\n return 'unknown';\n }\n}\n\nasync function getCommitLog(repoPath: string, base: string): Promise<PrCommit[]> {\n try {\n const { stdout } = await execFileAsync(\n 'git',\n ['log', `${base}..HEAD`, '--format=%h %s', '--no-merges'],\n { cwd: repoPath, timeout: GIT_MEDIUM_TIMEOUT_MS },\n );\n\n return stdout\n .trim()\n .split('\\n')\n .filter(Boolean)\n .map((line) => {\n const spaceIdx = line.indexOf(' ');\n return {\n sha: line.slice(0, spaceIdx),\n subject: line.slice(spaceIdx + 1),\n };\n });\n } catch {\n return [];\n }\n}\n\nasync function getChangedFiles(repoPath: string, base: string): Promise<string[]> {\n try {\n const { stdout } = await execFileAsync(\n 'git',\n ['diff', `${base}..HEAD`, '--name-only'],\n { cwd: repoPath, timeout: GIT_MEDIUM_TIMEOUT_MS },\n );\n\n return stdout\n .trim()\n .split('\\n')\n .filter(Boolean);\n } catch {\n return [];\n }\n}\n\nasync function getDiff(repoPath: string, base: string): Promise<string> {\n try {\n const { stdout } = await execFileAsync(\n 'git',\n ['diff', `${base}..HEAD`, '--stat-width=120'],\n { cwd: repoPath, timeout: GIT_MEDIUM_TIMEOUT_MS },\n );\n\n // Also get the actual diff, but truncate it\n const { stdout: fullDiff } = await execFileAsync(\n 'git',\n ['diff', `${base}..HEAD`],\n { cwd: repoPath, timeout: GIT_MEDIUM_TIMEOUT_MS, maxBuffer: 5 * 1024 * 1024 },\n );\n\n // Truncate to ~50K chars to keep within token budgets\n const MAX_DIFF_CHARS = 50_000;\n if (fullDiff.length > MAX_DIFF_CHARS) {\n return fullDiff.slice(0, MAX_DIFF_CHARS) + '\\n\\n... [diff truncated] ...';\n }\n return fullDiff;\n } catch {\n return '';\n }\n}\n\nasync function getRepoName(repoPath: string): Promise<string | null> {\n const gitPath = join(repoPath, '.git');\n const { configDir } = resolveGitRoot(repoPath, gitPath);\n return parseRemoteOrigin(configDir);\n}\n","/**\n * Match cached conversation summaries to a PR's changed file set.\n *\n * Uses the `ai.filesModified` field from StoredConversationSummary\n * to find conversations that touched files overlapping with the PR.\n */\n\nimport { loadSummariesForRepo } from '../summarizer/cache.js';\nimport type { StoredConversationSummary } from '../summarizer/types.js';\nimport { normalize, relative, isAbsolute } from 'node:path';\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport interface MatchedSummary {\n summary: StoredConversationSummary;\n /** Files from the PR that this conversation also touched */\n overlappingFiles: string[];\n /** Ratio of overlapping files to total PR files (0–1) */\n relevanceScore: number;\n}\n\n// ── Public API ───────────────────────────────────────────────────\n\n/**\n * Match conversation summaries to a PR by comparing `ai.filesModified`\n * with the PR's changed files.\n *\n * @param repoName The repo name (e.g. \"matan1542/lore\")\n * @param changedFiles Files changed in the PR (relative paths)\n * @param repoPath Absolute path to the repo root (for normalizing summary paths)\n * @returns Matched summaries sorted by relevance score (highest first)\n */\nexport function matchConversationsToPr(\n repoName: string,\n changedFiles: string[],\n repoPath?: string,\n): MatchedSummary[] {\n const summaries = loadSummariesForRepo(repoName);\n if (summaries.length === 0 || changedFiles.length === 0) return [];\n\n // Normalize PR files for comparison\n const prFileSet = new Set(changedFiles.map((f) => normalizeFilePath(f)));\n\n const matches: MatchedSummary[] = [];\n\n for (const summary of summaries) {\n const filesModified = summary.ai?.filesModified ?? [];\n if (filesModified.length === 0) continue;\n\n // Find overlapping files\n const overlapping: string[] = [];\n for (const file of filesModified) {\n const normalized = normalizeFilePath(file, repoPath);\n if (prFileSet.has(normalized)) {\n overlapping.push(normalized);\n }\n }\n\n if (overlapping.length > 0) {\n matches.push({\n summary,\n overlappingFiles: overlapping,\n relevanceScore: overlapping.length / changedFiles.length,\n });\n }\n }\n\n // Sort by relevance (most overlapping files first)\n matches.sort((a, b) => b.relevanceScore - a.relevanceScore);\n\n return matches;\n}\n\n// ── Helpers ──────────────────────────────────────────────────────\n\n/**\n * Normalize a file path for consistent comparison.\n * Strips repo root prefix, leading slashes, and normalizes separators.\n */\nfunction normalizeFilePath(filePath: string, repoPath?: string): string {\n let normalized = filePath;\n\n // If the path is absolute and we have a repo root, make it relative\n if (repoPath && isAbsolute(normalized)) {\n normalized = relative(repoPath, normalized);\n }\n\n // Normalize path separators and remove leading ./\n normalized = normalize(normalized)\n .replace(/^\\.\\//, '')\n .replace(/^\\//, '');\n\n return normalized;\n}\n","/**\n * Generate PR descriptions from gathered context.\n *\n * Three tiers:\n * A (template) — pure template fill, no LLM, < 1 second\n * B (fast) — Haiku LLM call, 3-8 seconds\n * C (detailed) — Sonnet LLM call with full diffs, 10-25 seconds\n */\n\nimport type { PrContext } from './context.js';\nimport type { MatchedSummary } from './matcher.js';\nimport { invokeWithFallback } from '../utils/cli.js';\nimport { loadPrompt, splitPrompt, createSystemPromptFile } from '../distiller/prompts.js';\nimport { WRITING_ENV_OVERRIDES, VERDICT_ENV_OVERRIDES, MS_PER_MINUTE } from '../utils/constants.js';\nimport { logger } from '../utils/logger.js';\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport type PrDescriptionTier = 'template' | 'fast' | 'detailed';\n\n// ── Timeouts ─────────────────────────────────────────────────────\n\nconst PR_DESCRIBE_TIMEOUT_MS = 1 * MS_PER_MINUTE;\n\n// ── Public API ───────────────────────────────────────────────────\n\n/**\n * Generate a PR description from the gathered context.\n *\n * @param context The gathered PR context\n * @param tier Quality tier: 'template' (A), 'fast' (B), or 'detailed' (C)\n * @returns The generated Markdown description\n */\nexport async function generatePrDescription(\n context: PrContext,\n tier: PrDescriptionTier = 'fast',\n): Promise<string> {\n switch (tier) {\n case 'template':\n return templateDescription(context);\n case 'fast':\n return llmDescription(context, 'haiku');\n case 'detailed':\n return llmDescription(context, 'sonnet');\n default:\n return templateDescription(context);\n }\n}\n\n// ── Tier A: Template ─────────────────────────────────────────────\n\n/**\n * Generate a PR description using a pure Markdown template.\n * No LLM call — fills in structured data directly.\n */\nfunction templateDescription(context: PrContext): string {\n const parts: string[] = [];\n\n // Summary section\n parts.push('## Summary\\n');\n if (context.matchedSummaries.length > 0) {\n // Use the top conversation summaries as the PR summary\n const topSummaries = context.matchedSummaries\n .slice(0, 3)\n .map((m) => m.summary.ai.summary)\n .filter(Boolean);\n if (topSummaries.length > 0) {\n parts.push(topSummaries.join(' '));\n } else {\n parts.push(buildFallbackSummary(context));\n }\n } else {\n parts.push(buildFallbackSummary(context));\n }\n\n // Changes section\n parts.push('\\n\\n## Changes\\n');\n if (context.commits.length > 0) {\n for (const commit of context.commits.slice(0, 20)) {\n parts.push(`- ${commit.subject}`);\n }\n if (context.commits.length > 20) {\n parts.push(`- ... and ${context.commits.length - 20} more commits`);\n }\n } else {\n parts.push('- No commits found in range');\n }\n\n // Decisions section (from matched summaries)\n const decisions = collectDecisions(context.matchedSummaries);\n if (decisions.length > 0) {\n parts.push('\\n\\n## Decisions\\n');\n for (const decision of decisions.slice(0, 5)) {\n parts.push(`- **${decision.decision}**: ${decision.context}`);\n }\n }\n\n // Files section\n parts.push('\\n\\n## Files Changed\\n');\n parts.push(`${context.changedFiles.length} files changed`);\n if (context.changedFiles.length <= 15) {\n parts.push('');\n for (const file of context.changedFiles) {\n parts.push(`- \\`${file}\\``);\n }\n }\n\n return parts.join('\\n');\n}\n\n// ── Tier B/C: LLM ───────────────────────────────────────────────\n\n/**\n * Generate a PR description using an LLM call.\n * Falls back to template if the LLM call fails.\n */\nasync function llmDescription(\n context: PrContext,\n model: 'haiku' | 'sonnet',\n): Promise<string> {\n const promptContext = buildPromptContext(context);\n const promptTemplate = loadPrompt('pr-description');\n const fullPrompt = promptTemplate.replace('{context}', promptContext);\n\n const { systemPrompt, userPrompt } = splitPrompt(fullPrompt);\n\n let systemPromptFile: { path: string; cleanup: () => void } | undefined;\n const args = ['-p', '--output-format', 'text', '--max-turns', '1', '--tools', ''];\n\n if (systemPrompt) {\n systemPromptFile = createSystemPromptFile(systemPrompt);\n args.push('--append-system-prompt-file', systemPromptFile.path);\n }\n\n const env = model === 'haiku' ? WRITING_ENV_OVERRIDES : VERDICT_ENV_OVERRIDES;\n\n try {\n const result = await invokeWithFallback(\n {\n args,\n input: userPrompt || promptContext,\n timeout: PR_DESCRIBE_TIMEOUT_MS,\n model,\n env: { ...env },\n },\n 'pr-description',\n );\n\n const cleaned = cleanLlmOutput(result);\n if (cleaned.length < 50) {\n logger.warn('[pr] LLM output too short, falling back to template');\n return templateDescription(context);\n }\n\n return cleaned;\n } catch (err) {\n logger.warn(`[pr] LLM call failed, falling back to template: ${err}`);\n return templateDescription(context);\n } finally {\n systemPromptFile?.cleanup();\n }\n}\n\n// ── Context building ─────────────────────────────────────────────\n\n/**\n * Build the context string injected into the prompt template.\n */\nfunction buildPromptContext(context: PrContext): string {\n const parts: string[] = [];\n\n parts.push(`Branch: ${context.branch} → ${context.base}`);\n parts.push(`Commits: ${context.commits.length}`);\n parts.push(`Files changed: ${context.changedFiles.length}`);\n parts.push('');\n\n // Commit list\n parts.push('### Commits');\n for (const commit of context.commits.slice(0, 30)) {\n parts.push(`- ${commit.sha} ${commit.subject}`);\n }\n parts.push('');\n\n // Changed files\n parts.push('### Files Changed');\n for (const file of context.changedFiles) {\n parts.push(`- ${file}`);\n }\n parts.push('');\n\n // Matched conversation summaries\n if (context.matchedSummaries.length > 0) {\n parts.push('### Related Conversation Summaries');\n for (const match of context.matchedSummaries.slice(0, 5)) {\n const s = match.summary;\n parts.push(`\\n#### ${s.title}`);\n parts.push(`Summary: ${s.ai.summary}`);\n\n if (s.ai.decisions.length > 0) {\n parts.push('Decisions:');\n for (const d of s.ai.decisions) {\n parts.push(` - ${d.decision}: ${d.context}`);\n }\n }\n\n if (s.ai.patterns.length > 0) {\n parts.push('Patterns:');\n for (const p of s.ai.patterns) {\n parts.push(` - ${p.pattern}`);\n }\n }\n\n parts.push(`Outcome: ${s.ai.outcome}`);\n parts.push(`Overlapping files: ${match.overlappingFiles.join(', ')}`);\n }\n parts.push('');\n }\n\n // Full diff (Tier C only)\n if (context.diff) {\n parts.push('### Diff');\n parts.push(context.diff);\n }\n\n return parts.join('\\n');\n}\n\n// ── Helpers ──────────────────────────────────────────────────────\n\nfunction buildFallbackSummary(context: PrContext): string {\n const fileCount = context.changedFiles.length;\n const commitCount = context.commits.length;\n return `This PR contains ${commitCount} commit${commitCount !== 1 ? 's' : ''} across ${fileCount} file${fileCount !== 1 ? 's' : ''} on branch \\`${context.branch}\\`.`;\n}\n\ninterface DecisionInfo {\n decision: string;\n context: string;\n}\n\nfunction collectDecisions(matches: MatchedSummary[]): DecisionInfo[] {\n const seen = new Set<string>();\n const decisions: DecisionInfo[] = [];\n\n for (const match of matches) {\n for (const d of match.summary.ai.decisions) {\n const key = d.decision.toLowerCase();\n if (seen.has(key)) continue;\n seen.add(key);\n decisions.push({ decision: d.decision, context: d.context });\n }\n }\n\n return decisions;\n}\n\n/**\n * Clean up LLM output: strip leading/trailing whitespace,\n * remove markdown code fences if the LLM wrapped the output.\n */\nfunction cleanLlmOutput(raw: string): string {\n let cleaned = raw.trim();\n\n // Remove wrapping code fences\n if (cleaned.startsWith('```markdown')) {\n cleaned = cleaned.slice('```markdown'.length);\n } else if (cleaned.startsWith('```md')) {\n cleaned = cleaned.slice('```md'.length);\n } else if (cleaned.startsWith('```')) {\n cleaned = cleaned.slice(3);\n }\n if (cleaned.endsWith('```')) {\n cleaned = cleaned.slice(0, -3);\n }\n\n return cleaned.trim();\n}\n","/**\n * GitHub interaction via the `gh` CLI.\n *\n * Detects open PRs for the current branch and updates their body.\n * All operations are best-effort: if `gh` is missing or not authenticated,\n * we degrade gracefully.\n */\n\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { logger } from '../utils/logger.js';\nimport { GIT_MEDIUM_TIMEOUT_MS } from '../utils/constants.js';\n\nconst execFileAsync = promisify(execFile);\n\n// ── Lore PR markers ─────────────────────────────────────────────\n\nexport const LORE_PR_BEGIN = '<!-- lore:pr:begin -->';\nexport const LORE_PR_END = '<!-- lore:pr:end -->';\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport interface OpenPr {\n number: number;\n title: string;\n body: string;\n baseRefName: string;\n headRefName: string;\n}\n\nexport type PrUpdateResult = 'updated' | 'no-pr' | 'no-gh' | 'error';\n\n// ── Public API ───────────────────────────────────────────────────\n\n/**\n * Check if `gh` CLI is installed and authenticated.\n */\nexport async function isGhAvailable(): Promise<boolean> {\n try {\n await execFileAsync('gh', ['auth', 'status'], {\n timeout: GIT_MEDIUM_TIMEOUT_MS,\n });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Detect an open PR for the current branch.\n *\n * @param repoPath Working directory (must be inside a git repo)\n * @returns The open PR info, or null if no PR exists\n */\nexport async function detectOpenPr(repoPath: string): Promise<OpenPr | null> {\n try {\n const { stdout } = await execFileAsync(\n 'gh',\n [\n 'pr', 'view',\n '--json', 'number,title,body,baseRefName,headRefName',\n ],\n { cwd: repoPath, timeout: GIT_MEDIUM_TIMEOUT_MS },\n );\n\n const pr = JSON.parse(stdout.trim()) as OpenPr;\n return pr;\n } catch {\n // No PR found, gh not available, or not authenticated\n return null;\n }\n}\n\n/**\n * Update a PR's body with the generated description.\n *\n * Uses the Lore marker strategy:\n * - If markers exist, replace only the Lore section\n * - If no markers exist, append the Lore section\n *\n * @param repoPath Working directory\n * @param prNumber PR number to update\n * @param generatedBody The Lore-generated description content\n * @param existingBody The current PR body\n */\nexport async function updatePrBody(\n repoPath: string,\n prNumber: number,\n generatedBody: string,\n existingBody: string,\n): Promise<void> {\n const wrappedContent = `${LORE_PR_BEGIN}\\n${generatedBody}\\n${LORE_PR_END}`;\n let newBody: string;\n\n if (existingBody.includes(LORE_PR_BEGIN) && existingBody.includes(LORE_PR_END)) {\n // Replace existing Lore section\n const beforeMarker = existingBody.slice(0, existingBody.indexOf(LORE_PR_BEGIN));\n const afterMarker = existingBody.slice(\n existingBody.indexOf(LORE_PR_END) + LORE_PR_END.length,\n );\n newBody = `${beforeMarker}${wrappedContent}${afterMarker}`;\n } else if (existingBody.trim()) {\n // Append to existing content\n newBody = `${existingBody.trim()}\\n\\n${wrappedContent}`;\n } else {\n // Empty body — just use the generated content\n newBody = wrappedContent;\n }\n\n await execFileAsync(\n 'gh',\n ['pr', 'edit', String(prNumber), '--body', newBody],\n { cwd: repoPath, timeout: GIT_MEDIUM_TIMEOUT_MS },\n );\n}\n\n/**\n * Full flow: detect PR, generate description, and update.\n * Returns the result status.\n */\nexport async function injectPrDescription(\n repoPath: string,\n generateFn: () => Promise<string>,\n): Promise<PrUpdateResult> {\n // 1. Check gh availability\n const ghOk = await isGhAvailable();\n if (!ghOk) {\n logger.debug('[pr] gh CLI not available or not authenticated');\n return 'no-gh';\n }\n\n // 2. Detect open PR\n const pr = await detectOpenPr(repoPath);\n if (!pr) {\n logger.debug('[pr] No open PR for current branch');\n return 'no-pr';\n }\n\n // 3. Generate description\n try {\n const description = await generateFn();\n\n // 4. Update PR body\n await updatePrBody(repoPath, pr.number, description, pr.body);\n logger.info(`[pr] Updated PR #${pr.number} description`);\n return 'updated';\n } catch (err) {\n logger.error(`[pr] Failed to update PR: ${err}`);\n return 'error';\n }\n}\n"],"mappings":"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","names":["z","init_schema","join","existsSync","readFileSync","existsSync","join","readdirSync","statSync","existsSync","readFileSync","join","resolve","readFileSync","writeFileSync","existsSync","join","init_schema","readFileSync","existsSync","resolve","existsSync","join","homedir","resolve","existsSync","readdirSync","statSync","join","stat","readFileSync","existsSync","writeFileSync","mkdirSync","join","writeFileSync","mkdirSync","unlinkSync","existsSync","join","tmpdir","randomUUID","existsSync","readFileSync","join","homedir","z","existsSync","readFileSync","writeFileSync","mkdirSync","join","dirname","existsSync","mkdirSync","readdirSync","writeFileSync","join","output","tierLabel","_","existsSync","mkdirSync","readdirSync","readFileSync","writeFileSync","unlinkSync","join","randomUUID","existsSync","readFileSync","writeFileSync","mkdirSync","readdirSync","join","existsSync","statSync","join","dirname","homedir","existsSync","readdirSync","join","homedir","init_cursor","existsSync","readdirSync","readFileSync","join","createReadStream","createInterface","knownReposCache","resolveRepoFromClassification","getIndexPath","init_claude_code","init_cursor","init_cursor","init_claude_code","existsSync","statSync","join","readFileSync","writeFileSync","existsSync","mkdirSync","join","homedir","readFileSync","writeFileSync","existsSync","mkdirSync","unlinkSync","resolve","pc","init_cache","Hono","readFileSync","writeFileSync","existsSync","resolve","getSource","getCursorSource","warmReposCache","init_cache","init_schema","existsSync","mkdirSync","writeFileSync","spinner","existsSync","pc","clack","execa","existsSync","readFileSync","readdirSync","writeFileSync","mkdirSync","statSync","join","readFileSync","existsSync","join","z","extractJson","safeParseArray","existsSync","readFileSync","readdirSync","writeFileSync","mkdirSync","unlinkSync","join","existsSync","readdirSync","join","mkdirSync","unlinkSync","readFileSync","writeFileSync","existsSync","readFileSync","writeFileSync","join","existsSync","readFileSync","writeFileSync","join","basename","existsSync","join","readFileSync","writeFileSync","existsSync","readFileSync","readdirSync","join","homedir","atIdx","execa","existsSync","statSync","rmSync","mkdirSync","readdirSync","readFileSync","writeFileSync","join","tmpdir","randomUUID","existsSync","join","mkdirSync","readFileSync","skillRelPath","pointerResult","coreFileNames","shouldGenCoreFile","getChangeHints","hasRefUpdates","coreFilesToGen","writeFileSync","refFilesOnDisk","totalElapsed","migrations","orphans","execa","stat","statSync","unlinkSync","readdirSync","extractJson","readFileSync","writeFileSync","existsSync","mkdirSync","join","readFileSync","readdirSync","existsSync","join","existsSync","readdirSync","join","readFileSync","join","existsSync","readFileSync","cache","mkdirSync","writeFileSync","sources","CONCURRENCY","result","existsSync","pc","spinner","existsSync","pc","clack","existsSync","pc","existsSync","readdirSync","resolve","pc","clack","existsSync","readFileSync","readdirSync","resolve","basename","pc","clack","spinner","estimateTokens","resolve","existsSync","readdirSync","readFileSync","unlinkSync","mkdirSync","pc","clack","existsSync","readFileSync","writeFileSync","mkdirSync","readdirSync","resolve","existsSync","pc","resolve","readdirSync","mkdirSync","readFileSync","unlinkSync","resolve","existsSync","readFileSync","writeFileSync","pc","clack","resolve","existsSync","readFileSync","writeFileSync","pc","clack","addRepo","pc","clack","existsSync","Hono","join","existsSync","readFileSync","statSync","Hono","init_cache","Hono","cache","c","init_cache","Hono","Hono","existsSync","readdirSync","readFileSync","unlinkSync","mkdirSync","resolve","Hono","resolve","existsSync","readdirSync","readFileSync","mkdirSync","unlinkSync","f","Hono","readFileSync","writeFileSync","existsSync","mkdirSync","resolve","readFileSync","existsSync","join","homedir","existsSync","readFileSync","join","homedir","readFileSync","writeFileSync","existsSync","existsSync","readFileSync","writeFileSync","readFileSync","existsSync","existsSync","readFileSync","readdirSync","statSync","join","homedir","existsSync","join","readdirSync","readFileSync","homedir","stat","statSync","Hono","Hono","existsSync","readFileSync","join","z","Hono","join","existsSync","readdirSync","readFileSync","Hono","writeFileSync","Hono","writeFileSync","Hono","readFileSync","writeFileSync","existsSync","mkdirSync","join","join","existsSync","readFileSync","Hono","Hono","existsSync","readFileSync","readdirSync","join","z","readFileSync","writeFileSync","existsSync","unlinkSync","mkdirSync","join","z","appendOutput","z","Hono","join","existsSync","readdirSync","readFileSync","init_cache","Hono","join","existsSync","statSync","readFileSync","existsSync","pc","existsSync","pc","clack","existsSync","pc","spinner","existsSync","pc","clack","existsSync","pc","pc","clack","existsSync","statSync","readdirSync","join","homedir","existsSync","MIN_MESSAGES","sources","existsSync","pc","spinner","result","conversations","pc","clack","execFile","promisify","join","relative","execFileAsync","promisify","execFile","join","execFile","promisify","execFileAsync","pc","execSync"]}