@tekmidian/pai 0.10.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["getProject","upsertTag","cmdList","cmdInfo","cmdTag","upsertTag","getProject","cmdHealth","getProject","toTitleCase","cmdList","getProject","toTitleCase","HANDOVER_TODO_LOCATIONS","findProjectTodo","stripContinueSection","probeResume","findNotesDir","findClaudeNotesDir","cmdMigrate","slugify","cmdStats","tierColor","CONFIG_FILE","CLAUDE_JSON_PATH","readClaudeJson","writeClaudeJson","cmdInstall","cmdStatus","HOME","cmdStatus","HOME","REGISTRY_DB","CONFIG_FILE","BACKUPS_DIR","DOCKER_CONTAINER","PG_DATABASE","PG_USER","line","CONFIG_FILE","getTemplatesDir","getTemplatesDir","getTemplatesDir","getTemplatesDir","useSymlinks","CONFIG_FILE","CONFIG_FILE","CONFIG_FILE","cmdStatus","fmtTs","cmdList","cmdStats","makeClient","getPool","cmdQuery","HISTORY_FILE","shortenProject","fmtTs","searchHistory"],"sources":["../../src/session/promote.ts","../../src/cli/commands/project/helpers.ts","../../src/cli/commands/project/commands.ts","../../src/cli/commands/project/session-config.ts","../../src/cli/commands/project/health.ts","../../src/cli/commands/project/projects-index.ts","../../src/session/slug-generator.ts","../../src/cli/commands/session/helpers.ts","../../src/cli/commands/session/commands.ts","../../src/cli/commands/session/checkpoint.ts","../../src/cli/commands/session/handover.ts","../../src/cli/lib/session-scan.ts","../../src/cli/lib/aibroker-client.ts","../../src/cli/commands/session/recent.ts","../../src/cli/commands/session/goto.ts","../../src/cli/commands/session/pause.ts","../../src/cli/commands/session/end.ts","../../src/cli/commands/session/pause-all.ts","../../src/cli/commands/session/sessions-index.ts","../../src/cli/commands/session-cleanup/types.ts","../../src/cli/commands/session-cleanup/rename.ts","../../src/cli/commands/session-cleanup/scanner.ts","../../src/cli/commands/session-cleanup/executor.ts","../../src/cli/commands/session-cleanup/index.ts","../../src/cli/commands/registry/migrate.ts","../../src/cli/commands/registry/index.ts","../../src/cli/commands/memory/embed.ts","../../src/cli/commands/memory/index-cmd.ts","../../src/cli/commands/memory/search.ts","../../src/cli/commands/memory/stats.ts","../../src/cli/commands/memory/index.ts","../../src/cli/commands/mcp.ts","../../src/cli/commands/daemon.ts","../../src/cli/commands/backup.ts","../../src/cli/commands/restore.ts","../../src/cli/commands/setup/utils.ts","../../src/cli/commands/setup/steps/01-welcome.ts","../../src/cli/commands/setup/steps/02-storage.ts","../../src/cli/commands/setup/steps/03-embedding.ts","../../src/cli/commands/setup/steps/04-claude-md.ts","../../src/cli/commands/setup/steps/05-pai-skill.ts","../../src/cli/commands/setup/steps/06-steering-rules.ts","../../src/cli/commands/setup/steps/07-skill-stubs.ts","../../src/cli/commands/setup/steps/08-hooks.ts","../../src/cli/commands/setup/steps/09-ts-hooks.ts","../../src/cli/commands/settings-manager.ts","../../src/cli/commands/setup/steps/10-settings.ts","../../src/cli/commands/setup/steps/11-daemon.ts","../../src/cli/commands/setup/steps/12-mcp.ts","../../src/cli/commands/setup/steps/13-directories.ts","../../src/cli/commands/setup/steps/14-initial-index.ts","../../src/cli/commands/setup/steps/15-verify.ts","../../src/cli/commands/setup/index.ts","../../src/obsidian/sync/symlinks.ts","../../src/obsidian/sync/generate.ts","../../src/obsidian/sync/walk.ts","../../src/obsidian/sync/master.ts","../../src/obsidian/status.ts","../../src/cli/commands/obsidian.ts","../../src/cli/commands/zettel/utils.ts","../../src/cli/commands/zettel/explore.ts","../../src/cli/commands/zettel/health.ts","../../src/cli/commands/zettel/surprise.ts","../../src/cli/commands/zettel/suggest.ts","../../src/cli/commands/zettel/converse.ts","../../src/cli/commands/zettel/themes.ts","../../src/cli/commands/zettel/index.ts","../../src/cli/commands/observation.ts","../../src/cli/commands/update.ts","../../src/cli/commands/notify.ts","../../src/cli/commands/topic.ts","../../src/memory/kg-backfill.ts","../../src/cli/commands/kg.ts","../../src/cli/commands/db.ts","../../src/cli/commands/find.ts","../../src/cli/lib/history-search.ts","../../src/cli/commands/main-resolver.ts","../../src/cli/index.ts"],"sourcesContent":["/**\n * pai project promote --from-session <path> --to <new-project-path> [--name \"Name\"]\n *\n * Promotes a session note into a new standalone project:\n * 1. Validates inputs\n * 2. Scaffolds the new project directory\n * 3. Copies the session note and creates a MEMORY.md\n * 4. Registers the new project in the PAI registry\n * 5. Optionally backlinks from the source project's TODO.md\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport {\n existsSync,\n mkdirSync,\n copyFileSync,\n readFileSync,\n appendFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { join, basename, dirname, resolve } from \"node:path\";\nimport {\n ok,\n warn,\n err,\n dim,\n bold,\n slugify,\n encodeDir,\n resolvePath,\n scaffoldProjectDirs,\n now,\n} from \"../cli/utils.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface PromoteOptions {\n fromSession: string;\n to: string;\n name?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Derive a human-readable project name from a session note filename.\n * \"0042 - 2026-02-24 - Dark Mode Implementation.md\" → \"Dark Mode Implementation\"\n */\nfunction deriveNameFromFilename(filename: string): string {\n const base = basename(filename, \".md\");\n // Strip leading \"NNNN - YYYY-MM-DD - \" prefix if present\n const match = base.match(/^\\d+ - \\d{4}-\\d{2}-\\d{2} - (.+)$/);\n return match ? match[1] : base;\n}\n\n/**\n * Extract the first N H2 (##) section headings from markdown content.\n */\nfunction extractH2Headings(content: string, limit = 2): string[] {\n const headings: string[] = [];\n for (const m of content.matchAll(/^## .+$/gm)) {\n headings.push(m[0]);\n if (headings.length >= limit) break;\n }\n return headings;\n}\n\n/**\n * Find the source project root from the session note path.\n * Convention: session note lives at {project-root}/Notes/{filename}\n * So: dirname(dirname(sessionPath)) = project root.\n */\nfunction findSourceProjectRoot(sessionPath: string): string {\n const notesDir = dirname(resolve(sessionPath));\n return dirname(notesDir);\n}\n\n// ---------------------------------------------------------------------------\n// Main implementation\n// ---------------------------------------------------------------------------\n\nexport function cmdPromote(db: Database, opts: PromoteOptions): void {\n const sessionPath = resolvePath(opts.fromSession);\n const targetPath = resolvePath(opts.to);\n\n // ---- Validate inputs ----\n\n if (!existsSync(sessionPath)) {\n console.error(err(`Session note not found: ${sessionPath}`));\n process.exit(1);\n }\n if (!sessionPath.endsWith(\".md\")) {\n console.error(err(`--from-session must be a markdown (.md) file: ${sessionPath}`));\n process.exit(1);\n }\n if (existsSync(targetPath)) {\n console.error(err(`Target path already exists: ${targetPath}`));\n process.exit(1);\n }\n\n // ---- Derive project name and slug ----\n\n const displayName = opts.name ?? deriveNameFromFilename(sessionPath);\n const slug = slugify(displayName);\n\n if (!slug) {\n console.error(err(`Could not derive a valid slug from name: \"${displayName}\"`));\n process.exit(1);\n }\n\n // ---- Check registry for conflicts ----\n\n const encodedDir = encodeDir(targetPath);\n const existing = db\n .prepare(\"SELECT id FROM projects WHERE slug = ? OR root_path = ? OR encoded_dir = ?\")\n .get(slug, targetPath, encodedDir);\n if (existing) {\n console.error(\n err(`A project with slug \"${slug}\" or path \"${targetPath}\" is already registered.`)\n );\n process.exit(1);\n }\n\n // ---- Scaffold new project ----\n\n scaffoldProjectDirs(targetPath);\n\n // ---- Copy session note ----\n\n const today = new Date().toISOString().slice(0, 10);\n const sourceBasename = basename(sessionPath);\n const sourceProjectRoot = findSourceProjectRoot(sessionPath);\n const sourceProjectName = basename(sourceProjectRoot);\n\n const destNoteName = `0001 - ${today} - Promoted from ${sourceProjectName}.md`;\n const destNotePath = join(targetPath, \"Notes\", destNoteName);\n\n copyFileSync(sessionPath, destNotePath);\n\n // ---- Create MEMORY.md ----\n\n const sessionContent = readFileSync(sessionPath, \"utf-8\");\n const headings = extractH2Headings(sessionContent);\n\n const memoryContent = [\n `# Project Memory`,\n ``,\n `## Origin`,\n ``,\n `- **Promoted from:** ${sessionPath}`,\n `- **Source project:** ${sourceProjectRoot}`,\n `- **Date of promotion:** ${today}`,\n `- **Original session note:** ${sourceBasename}`,\n ``,\n `## Initial Context`,\n ``,\n headings.length > 0\n ? `Key topics from the source session:\\n\\n${headings.map((h) => `- ${h.replace(/^## /, \"\")}`).join(\"\\n\")}`\n : `(Extracted from session note — see ${destNoteName})`,\n ``,\n ].join(\"\\n\");\n\n writeFileSync(join(targetPath, \"Notes\", \"MEMORY.md\"), memoryContent, \"utf-8\");\n\n // ---- Register in PAI registry ----\n\n const ts = now();\n db.prepare(\n `INSERT INTO projects\n (slug, display_name, root_path, encoded_dir, type, status, created_at, updated_at)\n VALUES (?, ?, ?, ?, 'local', 'active', ?, ?)`\n ).run(slug, displayName, targetPath, encodedDir, ts, ts);\n\n // ---- Backlink in source TODO.md ----\n\n const sourceTodoPath = join(sourceProjectRoot, \"Notes\", \"TODO.md\");\n if (existsSync(sourceTodoPath)) {\n const backlink = `\\n- Promoted to project: [${slug}](${targetPath})\\n`;\n appendFileSync(sourceTodoPath, backlink, \"utf-8\");\n console.log(dim(` Backlink added to: ${sourceTodoPath}`));\n }\n\n // ---- Success output ----\n\n console.log();\n console.log(ok(`Project promoted: ${bold(slug)}`));\n console.log(dim(` Display name: ${displayName}`));\n console.log(dim(` Path: ${targetPath}`));\n console.log(dim(` Slug: ${slug}`));\n console.log(dim(` Session note: ${destNoteName}`));\n console.log(dim(` Memory: Notes/MEMORY.md created`));\n console.log();\n console.log(dim(` Next: cd ${targetPath} && pai session list ${slug}`));\n}\n","/**\n * Shared database helper functions for project sub-commands.\n * All functions accept a Database instance and are pure query wrappers.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport type { ProjectRow } from \"./types.js\";\nimport { err } from \"../../utils.js\";\n\nexport function getProject(db: Database, slug: string): ProjectRow | undefined {\n const direct = db\n .prepare(\"SELECT * FROM projects WHERE slug = ?\")\n .get(slug) as ProjectRow | undefined;\n if (direct) return direct;\n\n const alias = db\n .prepare(\n `SELECT p.* FROM projects p\n JOIN aliases a ON a.project_id = p.id\n WHERE a.alias = ?`\n )\n .get(slug) as ProjectRow | undefined;\n return alias;\n}\n\nexport function requireProject(db: Database, slug: string): ProjectRow {\n const project = getProject(db, slug);\n if (!project) {\n console.error(err(`Project not found: ${slug}`));\n process.exit(1);\n }\n return project;\n}\n\n/**\n * Resolve an identifier that may be a list index number or a slug.\n */\nexport function resolveIdentifier(db: Database, identifier: string): ProjectRow | undefined {\n const num = parseInt(identifier, 10);\n if (!isNaN(num) && num > 0 && String(num) === identifier) {\n const rows = db.prepare(\n \"SELECT * FROM projects ORDER BY status ASC, updated_at DESC\"\n ).all() as ProjectRow[];\n if (num <= rows.length) return rows[num - 1];\n }\n return getProject(db, identifier);\n}\n\nexport function getProjectTags(db: Database, projectId: number): string[] {\n const rows = db\n .prepare(\n `SELECT t.name FROM tags t\n JOIN project_tags pt ON pt.tag_id = t.id\n WHERE pt.project_id = ?\n ORDER BY t.name`\n )\n .all(projectId) as { name: string }[];\n return rows.map((r) => r.name);\n}\n\nexport function getProjectAliases(db: Database, projectId: number): string[] {\n const rows = db\n .prepare(\"SELECT alias FROM aliases WHERE project_id = ? ORDER BY alias\")\n .all(projectId) as { alias: string }[];\n return rows.map((r) => r.alias);\n}\n\nexport function getSessionCount(db: Database, projectId: number): number {\n const row = db\n .prepare(\"SELECT COUNT(*) AS cnt FROM sessions WHERE project_id = ?\")\n .get(projectId) as { cnt: number };\n return row.cnt;\n}\n\nexport function getLastSessionDate(db: Database, projectId: number): number | null {\n const row = db\n .prepare(\n `SELECT created_at FROM sessions WHERE project_id = ?\n ORDER BY created_at DESC LIMIT 1`\n )\n .get(projectId) as { created_at: number } | undefined;\n return row ? row.created_at : null;\n}\n\nexport function upsertTag(db: Database, tagName: string): number {\n db.prepare(\"INSERT OR IGNORE INTO tags (name) VALUES (?)\").run(tagName);\n const row = db.prepare(\"SELECT id FROM tags WHERE name = ?\").get(tagName) as { id: number };\n return row.id;\n}\n","/**\n * Core project CRUD commands: add, list, info, archive, unarchive, move, tag,\n * alias, edit, detect, consolidate, go, rebind — plus private helpers levenshtein,\n * containsIgnoreCase, findProjectNotesDirs, and findMovedPath.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport {\n existsSync,\n readdirSync,\n statSync,\n mkdirSync,\n renameSync,\n} from \"node:fs\";\nimport { join, basename, resolve } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport {\n ok,\n warn,\n err,\n dim,\n bold,\n header,\n slugFromPath,\n encodeDir,\n resolvePath,\n scaffoldProjectDirs,\n renderTable,\n shortenPath,\n fmtDate,\n now,\n} from \"../../utils.js\";\nimport {\n detectProject,\n formatDetection,\n formatDetectionJson,\n} from \"../detect.js\";\nimport { ensurePaiMarker } from \"../../../registry/pai-marker.js\";\nimport type { ProjectRow, SessionRow } from \"./types.js\";\nimport {\n requireProject,\n resolveIdentifier,\n getProject,\n getProjectTags,\n getProjectAliases,\n getSessionCount,\n getLastSessionDate,\n upsertTag,\n} from \"./helpers.js\";\nimport { loadScanConfig, resolveHome } from \"../registry/scan.js\";\n\n// ---------------------------------------------------------------------------\n// Private helpers\n// ---------------------------------------------------------------------------\n\nfunction levenshtein(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n const dp: number[][] = Array.from({ length: m + 1 }, (_, i) =>\n Array.from({ length: n + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0))\n );\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n dp[i][j] =\n a[i - 1] === b[j - 1]\n ? dp[i - 1][j - 1]\n : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);\n }\n }\n return dp[m][n];\n}\n\nfunction containsIgnoreCase(haystack: string, needle: string): boolean {\n return haystack.toLowerCase().includes(needle.toLowerCase());\n}\n\nfunction findProjectNotesDirs(project: ProjectRow): {\n encodedDir: string;\n fullPath: string;\n notesPath: string;\n noteCount: number;\n}[] {\n const claudeProjects = join(homedir(), \".claude\", \"projects\");\n if (!existsSync(claudeProjects)) return [];\n\n const results: {\n encodedDir: string;\n fullPath: string;\n notesPath: string;\n noteCount: number;\n }[] = [];\n const rootEncoded = encodeDir(project.root_path);\n\n try {\n for (const entry of readdirSync(claudeProjects)) {\n const full = join(claudeProjects, entry);\n try {\n if (!statSync(full).isDirectory()) continue;\n } catch {\n continue;\n }\n\n if (entry !== rootEncoded && !entry.startsWith(rootEncoded)) continue;\n\n const notesPath = join(full, \"Notes\");\n if (!existsSync(notesPath)) continue;\n\n let noteCount = 0;\n try {\n noteCount = readdirSync(notesPath).filter(\n (f) => f.endsWith(\".md\") || f.endsWith(\".txt\")\n ).length;\n } catch {\n // count stays 0\n }\n\n results.push({ encodedDir: entry, fullPath: full, notesPath, noteCount });\n }\n } catch {\n // Unreadable — ignore\n }\n\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// Moved-project detection\n// ---------------------------------------------------------------------------\n\n// Directories to skip during filesystem walk\nconst SKIP_DIRS = new Set([\n \".git\", \"node_modules\", \".next\", \".nuxt\", \"dist\", \"build\", \"coverage\",\n \".cache\", \"__pycache__\", \"vendor\", \".svn\", \".hg\", \"venv\", \".venv\",\n \"target\", // Rust/Java build dir\n \"Notes\", // PAI session notes — not project roots\n]);\n\n/**\n * Walk `dir` up to `maxDepth` levels deep, collecting all subdirectory paths\n * whose basename matches `targetBasename` (case-insensitive on macOS/case-sensitive\n * on Linux, whichever is appropriate — we use exact case matching to keep it\n * correct and fast).\n *\n * Skips hidden directories and known build/tool dirs.\n * Stops walking a branch if the deadline is exceeded (returns partial results).\n */\nfunction walkForBasename(\n dir: string,\n targetBasename: string,\n maxDepth: number,\n deadline: number,\n results: string[]\n): void {\n if (Date.now() > deadline) return;\n if (maxDepth <= 0) return;\n\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n\n for (const entry of entries) {\n if (Date.now() > deadline) return;\n if (entry.startsWith(\".\")) continue;\n if (SKIP_DIRS.has(entry)) continue;\n\n const full = join(dir, entry);\n let isDir = false;\n try {\n isDir = statSync(full).isDirectory();\n } catch {\n continue;\n }\n if (!isDir) continue;\n\n // Case-insensitive comparison for the target basename (handles macOS APFS)\n if (entry.toLowerCase() === targetBasename.toLowerCase()) {\n results.push(full);\n // Don't recurse into the match itself — we want the directory, not children\n continue;\n }\n\n walkForBasename(full, targetBasename, maxDepth - 1, deadline, results);\n }\n}\n\n/**\n * Try to find where a project has moved.\n *\n * Searches scan_dirs from config (plus a set of common fallback dirs) for a\n * directory whose basename matches the basename of the registered root_path.\n *\n * Returns:\n * { found: string } — exactly one match found\n * { ambiguous: string[] } — multiple matches found\n * { found: undefined } — no match found (or timed out)\n */\nexport function findMovedPath(registeredPath: string): { found?: string; ambiguous?: string[] } {\n const targetBasename = basename(registeredPath);\n const config = loadScanConfig();\n const deadline = Date.now() + 5_000; // 5-second max\n\n // Build the list of top-level directories to search\n const home = homedir();\n const configDirs = (config.scan_dirs ?? []).map((d) => resolveHome(d));\n\n // Walk UP the registered path to find the deepest existing ancestor.\n // This is the best search root when a project has moved within the same tree.\n // e.g. ~/Cloud/Ablage/2025/Project → ~/Cloud/Ablage/2026/Project\n // Deepest existing ancestor: ~/Cloud/Ablage (if 2025 is gone but Ablage exists)\n function deepestExistingAncestor(p: string): string | null {\n let current = p;\n while (true) {\n const parent = join(current, \"..\");\n if (parent === current) return null; // filesystem root\n if (parent === home) return null; // don't use home itself as search root\n if (existsSync(parent)) return parent;\n current = parent;\n }\n }\n\n const ancestorRoot = deepestExistingAncestor(registeredPath);\n\n const fallbackDirs = [\n join(home, \"dev\"),\n join(home, \"Desktop\"),\n join(home, \"Projects\"),\n join(home, \"Documents\"),\n // Common cloud-storage root folders (Synology Drive, iCloud, etc.)\n join(home, \"Cloud\"),\n join(home, \"Daten\", \"Cloud\"),\n join(home, \"Daten\"),\n join(home, \"Library\", \"Mobile Documents\", \"com~apple~CloudDocs\"), // iCloud Drive\n // The deepest ancestor of the registered path that still exists on disk\n ...(ancestorRoot ? [ancestorRoot] : []),\n ];\n\n // Deduplicate and filter to existing directories\n const searchRoots = [...new Set([...configDirs, ...fallbackDirs])]\n .map((d) => {\n try { return resolve(d); } catch { return null; }\n })\n .filter((d): d is string => d !== null && existsSync(d));\n\n const results: string[] = [];\n for (const root of searchRoots) {\n if (Date.now() > deadline) break;\n walkForBasename(root, targetBasename, 6, deadline, results);\n }\n\n // Deduplicate results (different roots might overlap)\n const unique = [...new Set(results)];\n\n if (unique.length === 0) return {};\n if (unique.length === 1) return { found: unique[0] };\n return { ambiguous: unique };\n}\n\n// ---------------------------------------------------------------------------\n// Command implementations\n// ---------------------------------------------------------------------------\n\nexport function cmdAdd(\n db: Database,\n rawPath: string,\n opts: { slug?: string; type?: string; displayName?: string }\n): void {\n const rootPath = resolvePath(rawPath);\n const slug = opts.slug ?? slugFromPath(rootPath);\n const encodedDir = encodeDir(rootPath);\n const displayName = opts.displayName ?? slug;\n const type = opts.type ?? \"local\";\n\n const validTypes = [\"local\", \"central\", \"obsidian-linked\", \"external\"];\n if (!validTypes.includes(type)) {\n console.error(err(`Invalid type \"${type}\". Valid: ${validTypes.join(\", \")}`));\n process.exit(1);\n }\n\n const existing = db\n .prepare(\"SELECT id FROM projects WHERE slug = ? OR root_path = ?\")\n .get(slug, rootPath);\n if (existing) {\n console.error(\n err(`Project already registered (slug: ${slug} or path: ${rootPath})`)\n );\n process.exit(1);\n }\n\n const dirName = basename(rootPath).toLowerCase();\n const similar = db\n .prepare(\n `SELECT slug, root_path FROM projects WHERE status = 'active' AND slug != ?`\n )\n .all(slug) as { slug: string; root_path: string }[];\n const matches = similar.filter(\n (s) =>\n basename(s.root_path).toLowerCase() === dirName ||\n s.slug.replace(/-\\d+$/, \"\") === slug.replace(/-\\d+$/, \"\")\n );\n if (matches.length > 0) {\n console.log(warn(`Similar project(s) already registered:`));\n for (const m of matches) {\n console.log(dim(` ${bold(m.slug)} ${shortenPath(m.root_path, 50)}`));\n }\n console.log(\n dim(\n ` Consider: pai project alias ${matches[0].slug} <name> (to link them)`\n )\n );\n console.log(\n dim(` Or: pai project archive ${slug} (if this is a duplicate)`)\n );\n console.log();\n }\n\n const ts = now();\n db.prepare(\n `INSERT INTO projects\n (slug, display_name, root_path, encoded_dir, type, status, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, 'active', ?, ?)`\n ).run(slug, displayName, rootPath, encodedDir, type, ts, ts);\n\n scaffoldProjectDirs(rootPath);\n\n try {\n ensurePaiMarker(rootPath, slug, displayName);\n } catch {\n // Non-fatal — warn but don't fail the add command.\n }\n\n console.log(ok(`Project added: ${bold(slug)}`));\n console.log(dim(` Path: ${rootPath}`));\n console.log(dim(` Encoded dir: ${encodedDir}`));\n console.log(dim(` Type: ${type}`));\n}\n\nexport function cmdList(\n db: Database,\n opts: { status?: string; tag?: string; type?: string; all?: boolean }\n): void {\n let query = `\n SELECT p.*,\n (SELECT COUNT(*) FROM sessions s WHERE s.project_id = p.id) AS session_count,\n (SELECT MAX(s.created_at) FROM sessions s WHERE s.project_id = p.id) AS last_active\n FROM projects p\n `;\n const params: unknown[] = [];\n const where: string[] = [];\n\n // Default: active only, unless --all or explicit --status given\n if (opts.status) {\n where.push(\"p.status = ?\");\n params.push(opts.status);\n } else if (!opts.all) {\n where.push(\"p.status = 'active'\");\n }\n if (opts.type) {\n where.push(\"p.type = ?\");\n params.push(opts.type);\n }\n if (opts.tag) {\n where.push(`p.id IN (\n SELECT pt.project_id FROM project_tags pt\n JOIN tags t ON t.id = pt.tag_id WHERE t.name = ?\n )`);\n params.push(opts.tag);\n }\n\n if (where.length) query += \" WHERE \" + where.join(\" AND \");\n query += \" ORDER BY p.status ASC, p.updated_at DESC\";\n\n const rows = db.prepare(query).all(...params) as (ProjectRow & {\n session_count: number;\n last_active: number | null;\n })[];\n\n if (!rows.length) {\n console.log(warn(\"No projects found.\"));\n return;\n }\n\n const tableRows = rows.map((r, i) => [\n dim(String(i + 1)),\n bold(r.slug),\n dim(shortenPath(r.root_path, 50)),\n r.status === \"active\" ? chalk.green(r.status) : chalk.yellow(r.status),\n dim(r.type),\n String(r.session_count),\n fmtDate(r.last_active),\n ]);\n\n console.log(\n renderTable(\n [\"#\", \"Slug\", \"Path\", \"Status\", \"Type\", \"Sessions\", \"Last Active\"],\n tableRows\n )\n );\n console.log();\n\n // When showing active-only, inform the user about hidden archived projects\n const totalRow = db.prepare(\"SELECT COUNT(*) AS cnt FROM projects\").get() as { cnt: number };\n const hiddenCount = totalRow.cnt - rows.length;\n if (!opts.all && !opts.status && hiddenCount > 0) {\n console.log(dim(` ${rows.length} active project(s) (${hiddenCount} archived — use --all to show)`));\n } else {\n console.log(dim(` ${rows.length} project(s)`));\n }\n}\n\nexport function cmdInfo(db: Database, identifier: string): void {\n const project =\n resolveIdentifier(db, identifier) ?? requireProject(db, identifier);\n const tags = getProjectTags(db, project.id);\n const aliases = getProjectAliases(db, project.id);\n const sessionCount = getSessionCount(db, project.id);\n const lastSession = getLastSessionDate(db, project.id);\n\n const recentSessions = db\n .prepare(\n `SELECT * FROM sessions WHERE project_id = ?\n ORDER BY created_at DESC LIMIT 5`\n )\n .all(project.id) as SessionRow[];\n\n console.log();\n console.log(header(` ${project.display_name}`));\n console.log();\n console.log(` ${bold(\"Slug:\")} ${project.slug}`);\n console.log(` ${bold(\"Path:\")} ${project.root_path}`);\n console.log(` ${bold(\"Encoded dir:\")} ${project.encoded_dir}`);\n console.log(` ${bold(\"Type:\")} ${project.type}`);\n console.log(\n ` ${bold(\"Status:\")} ${\n project.status === \"active\"\n ? chalk.green(project.status)\n : chalk.yellow(project.status)\n }`\n );\n console.log(\n ` ${bold(\"Tags:\")} ${\n tags.length ? tags.map((t) => chalk.cyan(t)).join(\", \") : dim(\"none\")\n }`\n );\n console.log(\n ` ${bold(\"Aliases:\")} ${aliases.length ? aliases.join(\", \") : dim(\"none\")}`\n );\n console.log(` ${bold(\"Sessions:\")} ${sessionCount}`);\n console.log(` ${bold(\"Last active:\")} ${fmtDate(lastSession)}`);\n console.log(` ${bold(\"Created:\")} ${fmtDate(project.created_at)}`);\n if (project.archived_at) {\n console.log(` ${bold(\"Archived:\")} ${fmtDate(project.archived_at)}`);\n }\n\n if (recentSessions.length) {\n console.log();\n console.log(` ${bold(\"Recent sessions:\")}`);\n const sessionRows = recentSessions.map((s) => [\n dim(`#${s.number}`),\n s.date,\n s.title.length > 50 ? s.title.slice(0, 47) + \"...\" : s.title,\n s.status === \"completed\"\n ? chalk.green(s.status)\n : chalk.yellow(s.status),\n ]);\n console.log(\n renderTable([\"#\", \"Date\", \"Title\", \"Status\"], sessionRows)\n .split(\"\\n\")\n .map((l) => \" \" + l)\n .join(\"\\n\")\n );\n }\n console.log();\n}\n\nexport function cmdArchive(db: Database, slug: string): void {\n const project = requireProject(db, slug);\n if (project.status === \"archived\") {\n console.log(warn(`Project ${slug} is already archived.`));\n return;\n }\n const ts = now();\n db.prepare(\n \"UPDATE projects SET status = 'archived', archived_at = ?, updated_at = ? WHERE id = ?\"\n ).run(ts, ts, project.id);\n console.log(ok(`Archived: ${bold(slug)}`));\n}\n\nexport function cmdUnarchive(db: Database, slug: string): void {\n const project = requireProject(db, slug);\n if (project.status !== \"archived\") {\n console.log(\n warn(`Project ${slug} is not archived (status: ${project.status}).`)\n );\n return;\n }\n const ts = now();\n db.prepare(\n \"UPDATE projects SET status = 'active', archived_at = NULL, updated_at = ? WHERE id = ?\"\n ).run(ts, project.id);\n console.log(ok(`Unarchived: ${bold(slug)}`));\n}\n\nexport function cmdMove(db: Database, slug: string, newPath: string): void {\n const project = requireProject(db, slug);\n const resolvedNew = resolvePath(newPath);\n const newEncoded = encodeDir(resolvedNew);\n const ts = now();\n\n db.prepare(\n \"UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?\"\n ).run(resolvedNew, newEncoded, ts, project.id);\n\n console.log(ok(`Moved: ${bold(slug)}`));\n console.log(dim(` Old path: ${project.root_path}`));\n console.log(dim(` New path: ${resolvedNew}`));\n}\n\nexport function cmdTag(\n db: Database,\n slug: string,\n tags: string[]\n): void {\n const project = requireProject(db, slug);\n const added: string[] = [];\n const skipped: string[] = [];\n\n for (const tagName of tags) {\n const tagId = upsertTag(db, tagName);\n const exists = db\n .prepare(\n \"SELECT 1 FROM project_tags WHERE project_id = ? AND tag_id = ?\"\n )\n .get(project.id, tagId);\n if (exists) {\n skipped.push(tagName);\n } else {\n db.prepare(\n \"INSERT INTO project_tags (project_id, tag_id) VALUES (?, ?)\"\n ).run(project.id, tagId);\n added.push(tagName);\n }\n }\n\n if (added.length) {\n console.log(\n ok(\n `Tagged ${bold(slug)}: ${added.map((t) => chalk.cyan(t)).join(\", \")}`\n )\n );\n }\n if (skipped.length) {\n console.log(dim(` Already present: ${skipped.join(\", \")}`));\n }\n}\n\nexport function cmdAlias(\n db: Database,\n slug: string,\n alias: string\n): void {\n requireProject(db, slug);\n\n const conflict = db\n .prepare(\"SELECT id FROM projects WHERE slug = ?\")\n .get(alias);\n if (conflict) {\n console.error(\n err(`\"${alias}\" is already a project slug — cannot use as alias.`)\n );\n process.exit(1);\n }\n\n const project = getProject(db, slug)!;\n try {\n db.prepare(\n \"INSERT INTO aliases (alias, project_id) VALUES (?, ?)\"\n ).run(alias, project.id);\n console.log(ok(`Alias added: ${bold(alias)} → ${slug}`));\n } catch {\n console.error(err(`Alias \"${alias}\" is already registered.`));\n process.exit(1);\n }\n}\n\nexport function cmdEdit(\n db: Database,\n slug: string,\n opts: { displayName?: string; type?: string }\n): void {\n const project = requireProject(db, slug);\n\n if (!opts.displayName && !opts.type) {\n console.log(warn(\"Nothing to update. Use --display-name or --type.\"));\n return;\n }\n\n const validTypes = [\"local\", \"central\", \"obsidian-linked\", \"external\"];\n if (opts.type && !validTypes.includes(opts.type)) {\n console.error(\n err(`Invalid type \"${opts.type}\". Valid: ${validTypes.join(\", \")}`)\n );\n process.exit(1);\n }\n\n const ts = now();\n if (opts.displayName) {\n db.prepare(\n \"UPDATE projects SET display_name = ?, updated_at = ? WHERE id = ?\"\n ).run(opts.displayName, ts, project.id);\n console.log(ok(`Display name updated: ${bold(opts.displayName)}`));\n }\n if (opts.type) {\n db.prepare(\n \"UPDATE projects SET type = ?, updated_at = ? WHERE id = ?\"\n ).run(opts.type, ts, project.id);\n console.log(ok(`Type updated: ${bold(opts.type)}`));\n }\n}\n\nexport function cmdDetect(\n db: Database,\n pathArg: string | undefined,\n opts: { json?: boolean }\n): void {\n const cwd = pathArg ? resolvePath(pathArg) : process.cwd();\n const detection = detectProject(db, cwd);\n\n if (!detection) {\n if (opts.json) {\n console.log(JSON.stringify({ error: \"no_match\", cwd }, null, 2));\n } else {\n console.log(warn(`No registered project found for: ${cwd}`));\n console.log(dim(\" Run 'pai project add .' to register this directory.\"));\n }\n process.exit(0);\n return;\n }\n\n if (opts.json) {\n console.log(formatDetectionJson(detection));\n return;\n }\n\n console.log();\n console.log(header(\" Project Detection Result\"));\n console.log();\n console.log(\n formatDetection(detection)\n .split(\"\\n\")\n .map((l) => \" \" + l)\n .join(\"\\n\")\n );\n console.log();\n}\n\nexport function cmdConsolidate(\n db: Database,\n identifier: string,\n opts: { yes?: boolean; dryRun?: boolean }\n): void {\n const project =\n resolveIdentifier(db, identifier) ?? requireProject(db, identifier);\n\n console.log();\n console.log(header(` Consolidate: ${project.slug}`));\n console.log(` Target: ${project.root_path}`);\n console.log();\n\n const dirs = findProjectNotesDirs(project);\n\n if (dirs.length === 0) {\n console.log(warn(\" No scattered notes directories found for this project.\"));\n return;\n }\n\n const canonicalNotes = join(project.root_path, \"Notes\");\n const toMerge = dirs.filter((d) => d.notesPath !== canonicalNotes);\n\n if (toMerge.length === 0) {\n console.log(ok(\" All notes are already in the canonical location.\"));\n console.log(dim(` ${canonicalNotes}`));\n return;\n }\n\n console.log(\n ` Found ${toMerge.length} scattered Notes directory(ies) to consolidate:`\n );\n console.log();\n\n for (const d of toMerge) {\n console.log(` ${bold(d.encodedDir)}`);\n console.log(dim(` Notes: ${d.notesPath} (${d.noteCount} file(s))`));\n }\n\n console.log();\n console.log(` Destination: ${canonicalNotes}`);\n console.log();\n\n if (opts.dryRun) {\n console.log(warn(\" Dry run — no changes made. Remove --dry-run to proceed.\"));\n return;\n }\n\n if (!opts.yes) {\n console.log(\n warn(\n \" Run with --yes to perform consolidation, or --dry-run to preview changes.\"\n )\n );\n return;\n }\n\n mkdirSync(canonicalNotes, { recursive: true });\n\n let movedCount = 0;\n for (const d of toMerge) {\n try {\n const files = readdirSync(d.notesPath);\n for (const f of files) {\n if (!f.endsWith(\".md\") && !f.endsWith(\".txt\")) continue;\n const src = join(d.notesPath, f);\n const dest = join(canonicalNotes, f);\n if (!existsSync(dest)) {\n renameSync(src, dest);\n console.log(ok(` Moved: ${f}`));\n movedCount++;\n } else {\n console.log(warn(` Skipped (exists): ${f}`));\n }\n }\n } catch (e) {\n console.error(err(` Error reading ${d.notesPath}: ${e}`));\n }\n }\n\n console.log();\n console.log(ok(` Consolidated ${movedCount} file(s) into ${canonicalNotes}`));\n}\n\n/**\n * Attempt moved-path recovery for a project whose registered root_path is\n * missing. Updates the DB if exactly one candidate is found.\n *\n * Returns the new path on success, or undefined if ambiguous/not found.\n * Prints all messages to stderr (safe for shell-wrapper stdout capture).\n */\nfunction tryRecoverMovedProject(db: Database, project: ProjectRow): string | undefined {\n process.stderr.write(\n warn(`Path not found: ${project.root_path}\\n`) +\n dim(\" Searching for moved location...\\n\")\n );\n\n const result = findMovedPath(project.root_path);\n\n if (result.found) {\n const newPath = result.found;\n const newEncoded = encodeDir(newPath);\n const ts = now();\n db.prepare(\n \"UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?\"\n ).run(newPath, newEncoded, ts, project.id);\n process.stderr.write(\n ok(`Project moved: ${shortenPath(project.root_path, 50)}\\n`) +\n dim(` → ${newPath}\\n`) +\n ok(\"Registry updated.\\n\")\n );\n return newPath;\n }\n\n if (result.ambiguous) {\n process.stderr.write(\n warn(`Multiple directories named \"${basename(project.root_path)}\" found:\\n`)\n );\n for (const candidate of result.ambiguous) {\n process.stderr.write(dim(` ${candidate}\\n`));\n }\n process.stderr.write(\n dim(`\\n Disambiguate with: pai projects rebind ${project.slug} <path>\\n`)\n );\n return undefined;\n }\n\n process.stderr.write(\n err(\n `Project \"${project.slug}\" root_path \"${project.root_path}\" does not exist on disk\\n` +\n ` and no folder named \"${basename(project.root_path)}\" was found in scan dirs.\\n`\n ) +\n dim(` Fix with: pai projects rebind ${project.slug} <new-path>\\n`)\n );\n return undefined;\n}\n\nexport function cmdGo(db: Database, query: string): void {\n const all = db\n .prepare(\n \"SELECT * FROM projects WHERE status = 'active' ORDER BY updated_at DESC\"\n )\n .all() as ProjectRow[];\n\n if (!all.length) {\n console.error(\n err(\"No active projects registered. Run: pai project add <path>\")\n );\n process.exit(1);\n }\n\n const q = query.trim().toLowerCase();\n\n // 1. Exact slug or alias match\n const exact = getProject(db, query);\n if (exact) {\n if (!existsSync(exact.root_path)) {\n const recovered = tryRecoverMovedProject(db, exact);\n if (!recovered) { process.exitCode = 1; return; }\n process.stdout.write(recovered + \"\\n\");\n return;\n }\n process.stdout.write(exact.root_path + \"\\n\");\n return;\n }\n\n // 2. Substring match against slug, display_name, or root_path basename\n const partial = all.filter(\n (p) =>\n containsIgnoreCase(p.slug, q) ||\n containsIgnoreCase(p.display_name, q) ||\n containsIgnoreCase(basename(p.root_path), q)\n );\n\n if (partial.length === 1) {\n const p = partial[0];\n if (!existsSync(p.root_path)) {\n const recovered = tryRecoverMovedProject(db, p);\n if (!recovered) { process.exitCode = 1; return; }\n process.stdout.write(recovered + \"\\n\");\n return;\n }\n process.stdout.write(p.root_path + \"\\n\");\n return;\n }\n\n if (partial.length > 1) {\n console.error(\n err(`Ambiguous: \"${query}\" matches ${partial.length} projects:\\n`)\n );\n partial.forEach((p, i) => {\n console.error(\n ` ${dim(String(i + 1).padStart(2))} ${bold(p.slug.padEnd(30))} ${dim(\n shortenPath(p.root_path, 50)\n )}`\n );\n });\n console.error();\n console.error(dim(\" Use a more specific name or the exact slug.\"));\n process.exit(1);\n }\n\n // 3. No match — Levenshtein suggestions\n const scored = all\n .map((p) => {\n const distSlug = levenshtein(q, p.slug.toLowerCase());\n const distName = levenshtein(q, p.display_name.toLowerCase());\n return { project: p, dist: Math.min(distSlug, distName) };\n })\n .sort((a, b) => a.dist - b.dist);\n\n const threshold = 4;\n const suggestions =\n scored.filter((s) => s.dist <= threshold).length > 0\n ? scored.filter((s) => s.dist <= threshold).slice(0, 3)\n : scored.slice(0, 3);\n\n console.error(err(`Project not found: \"${query}\"\\n`));\n if (suggestions.length) {\n console.error(warn(\" Did you mean?\"));\n for (const s of suggestions) {\n console.error(\n ` ${bold(s.project.slug.padEnd(30))} ${dim(\n shortenPath(s.project.root_path, 50)\n )}`\n );\n }\n console.error();\n console.error(dim(\" Run: pai project list (to see all projects)\"));\n }\n process.exit(1);\n}\n\nexport function cmdRebind(\n db: Database,\n slug: string,\n newPath: string\n): void {\n const project = requireProject(db, slug);\n const resolved = resolve(newPath.startsWith(\"~/\") ? join(homedir(), newPath.slice(2)) : newPath);\n\n if (!existsSync(resolved)) {\n console.error(err(`Path does not exist: ${resolved}`));\n process.exit(1);\n }\n\n let isDir = false;\n try {\n isDir = statSync(resolved).isDirectory();\n } catch {\n // stat failed\n }\n if (!isDir) {\n console.error(err(`Path is not a directory: ${resolved}`));\n process.exit(1);\n }\n\n const newEncoded = encodeDir(resolved);\n\n // Check if another project already owns this path\n const conflict = db\n .prepare(\"SELECT slug FROM projects WHERE encoded_dir = ? AND id != ?\")\n .get(newEncoded, project.id) as { slug: string } | undefined;\n if (conflict) {\n console.error(\n err(`Path is already registered to project: ${bold(conflict.slug)}\\n`) +\n dim(` ${resolved}\\n`) +\n dim(` Archive or move that project first, or choose a different path.`)\n );\n process.exit(1);\n }\n\n const ts = now();\n db.prepare(\n \"UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?\"\n ).run(resolved, newEncoded, ts, project.id);\n\n console.log(ok(`Rebound: ${bold(slug)}`));\n console.log(dim(` Old path: ${project.root_path}`));\n console.log(dim(` New path: ${resolved}`));\n console.log(dim(` Encoded: ${newEncoded}`));\n}\n","/**\n * Session launch configuration for projects.\n * Implements per-project and global config CRUD (pai project config),\n * and the name/unname/names commands for curated project shortlists.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport { ok, warn, err, dim, bold, header, shortenPath, fmtDate, now, renderTable } from \"../../utils.js\";\nimport type { ProjectRow, SessionConfig, ConfigOption } from \"./types.js\";\nimport { resolveIdentifier, requireProject, getProjectAliases } from \"./helpers.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nexport const CONFIG_OPTIONS: ConfigOption[] = [\n { key: 'permission', type: 'string', description: 'Permission preset: full | trusted | default', examples: ['full', 'trusted', 'default'] },\n { key: 'flags', type: 'string', description: 'Raw Claude CLI flags', examples: ['--dangerously-skip-permissions', '--allowedTools Edit,Read'] },\n { key: 'env', type: 'object', description: 'Environment variables as key=value pairs', examples: ['IS_SANDBOX=1', 'CLAUDE_MODEL=opus'] },\n { key: 'autoStart', type: 'boolean', description: 'Auto-start session (skip interactive prompt)', examples: ['true', 'false'] },\n { key: 'prompt', type: 'string', description: 'Initial prompt sent to Claude on launch', examples: ['go', 'continue', 'run tests'] },\n { key: 'model', type: 'string', description: 'Model override for the session', examples: ['opus', 'sonnet', 'haiku'] },\n];\n\nexport const PERMISSION_PRESETS: Record<string, Partial<SessionConfig>> = {\n full: { permission: 'full', flags: '--dangerously-skip-permissions', env: { IS_SANDBOX: '1' }, autoStart: true, prompt: 'go' },\n trusted: { permission: 'trusted', flags: '', env: {}, autoStart: true, prompt: 'go' },\n default: { permission: 'default', flags: '', env: {}, autoStart: false },\n};\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport function getSessionConfig(project: ProjectRow): SessionConfig {\n return project.session_config ? JSON.parse(project.session_config) : {};\n}\n\nexport function getGlobalDefaults(): SessionConfig {\n try {\n const configPath = join(homedir(), '.config', 'pai', 'config.json');\n if (existsSync(configPath)) {\n const config = JSON.parse(readFileSync(configPath, 'utf-8'));\n return config.sessionDefaults ?? {};\n }\n } catch { /* ignore */ }\n return {};\n}\n\nexport function saveGlobalDefaults(defaults: SessionConfig): void {\n const configPath = join(homedir(), '.config', 'pai', 'config.json');\n let config: Record<string, unknown> = {};\n try {\n if (existsSync(configPath)) {\n config = JSON.parse(readFileSync(configPath, 'utf-8'));\n }\n } catch { /* ignore */ }\n config.sessionDefaults = defaults;\n mkdirSync(join(homedir(), '.config', 'pai'), { recursive: true });\n writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n');\n}\n\nfunction applyPreset(config: SessionConfig, preset: string): SessionConfig {\n const presetConfig = PERMISSION_PRESETS[preset];\n if (!presetConfig) {\n return { ...config, permission: 'custom', flags: preset };\n }\n return { ...config, ...presetConfig };\n}\n\nfunction parseConfigValue(key: string, value: string): unknown {\n const option = CONFIG_OPTIONS.find(o => o.key === key);\n if (!option) return value;\n\n switch (option.type) {\n case 'boolean':\n return value === 'true' || value === '1' || value === 'yes';\n case 'object':\n if (key === 'env') {\n const [k, ...vParts] = value.split('=');\n return { [k]: vParts.join('=') || '1' };\n }\n return value;\n default:\n return value;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Commands\n// ---------------------------------------------------------------------------\n\nexport function cmdName(\n db: Database,\n identifier: string,\n shortname: string,\n opts: { permission?: string }\n): void {\n const project = resolveIdentifier(db, identifier) ?? requireProject(db, identifier);\n\n if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(shortname)) {\n console.error(err(`Invalid name \"${shortname}\". Use letters, digits, hyphens, underscores. Must start with a letter.`));\n process.exit(1);\n }\n\n const conflictProject = db\n .prepare(\"SELECT id FROM projects WHERE slug = ? AND id != ?\")\n .get(shortname, project.id) as { id: number } | undefined;\n if (conflictProject) {\n console.error(err(`\"${shortname}\" is already a project slug.`));\n process.exit(1);\n }\n\n const conflictAlias = db\n .prepare(\"SELECT project_id FROM aliases WHERE alias = ?\")\n .get(shortname) as { project_id: number } | undefined;\n if (conflictAlias && conflictAlias.project_id !== project.id) {\n console.error(err(`\"${shortname}\" is already used by another project.`));\n process.exit(1);\n }\n\n if (!conflictAlias) {\n db.prepare(\"INSERT INTO aliases (alias, project_id) VALUES (?, ?)\").run(shortname, project.id);\n }\n\n if (opts.permission) {\n const existing = getSessionConfig(project);\n const config = applyPreset(existing, opts.permission);\n db.prepare(\"UPDATE projects SET session_config = ?, updated_at = ? WHERE id = ?\")\n .run(JSON.stringify(config), now(), project.id);\n }\n\n console.log(ok(`Named: ${bold(shortname)} → ${project.slug} (${shortenPath(project.root_path, 50)})`));\n if (opts.permission) {\n console.log(dim(` Permission: ${opts.permission}`));\n }\n}\n\nexport function cmdUnname(db: Database, shortname: string): void {\n const alias = db\n .prepare(\"SELECT project_id FROM aliases WHERE alias = ?\")\n .get(shortname) as { project_id: number } | undefined;\n\n if (!alias) {\n console.error(err(`No named project found: \"${shortname}\"`));\n process.exit(1);\n }\n\n db.prepare(\"DELETE FROM aliases WHERE alias = ?\").run(shortname);\n\n const remaining = db\n .prepare(\"SELECT COUNT(*) AS cnt FROM aliases WHERE project_id = ?\")\n .get(alias.project_id) as { cnt: number };\n\n console.log(ok(`Removed name: ${bold(shortname)}`));\n if (remaining.cnt === 0) {\n console.log(dim(\" Project has no remaining names.\"));\n }\n}\n\nexport function cmdNames(db: Database, opts: { json?: boolean }): void {\n const rows = db.prepare(`\n SELECT p.*, a.alias AS name,\n (SELECT COUNT(*) FROM sessions s WHERE s.project_id = p.id) AS session_count,\n (SELECT MAX(s.created_at) FROM sessions s WHERE s.project_id = p.id) AS last_active\n FROM projects p\n JOIN aliases a ON a.project_id = p.id\n WHERE p.status = 'active'\n ORDER BY p.updated_at DESC\n `).all() as (ProjectRow & { name: string; session_count: number; last_active: number | null; session_config: string | null })[];\n\n if (opts.json) {\n const grouped = new Map<number, unknown>();\n for (const row of rows) {\n if (!grouped.has(row.id)) {\n grouped.set(row.id, {\n name: row.name,\n names: [row.name],\n slug: row.slug,\n display_name: row.display_name,\n root_path: row.root_path,\n session_count: row.session_count,\n last_active: row.last_active ? new Date(row.last_active).toISOString() : null,\n session_config: row.session_config ? JSON.parse(row.session_config) : null,\n });\n } else {\n (grouped.get(row.id) as { names: string[] }).names.push(row.name);\n }\n }\n console.log(JSON.stringify([...grouped.values()], null, 2));\n return;\n }\n\n if (!rows.length) {\n console.log(warn(\"No named projects. Use: pai project name <slug-or-number> <shortname>\"));\n return;\n }\n\n const seen = new Set<number>();\n const tableRows: string[][] = [];\n for (const row of rows) {\n if (seen.has(row.id)) continue;\n seen.add(row.id);\n\n const allNames = rows.filter(r => r.id === row.id).map(r => r.name);\n const config = row.session_config ? JSON.parse(row.session_config) as SessionConfig : null;\n const perm = config?.permission ?? dim('default');\n\n tableRows.push([\n bold(allNames.join(', ')),\n row.slug,\n dim(shortenPath(row.root_path, 40)),\n typeof perm === 'string' ? perm : dim('default'),\n String(row.session_count),\n fmtDate(row.last_active),\n ]);\n }\n\n console.log();\n console.log(header(\" Named Projects\"));\n console.log();\n console.log(renderTable([\"Name(s)\", \"Slug\", \"Path\", \"Permission\", \"Sessions\", \"Last Active\"], tableRows));\n console.log();\n console.log(dim(` ${tableRows.length} named project(s)`));\n}\n\nexport function cmdConfig(\n db: Database,\n identifier: string | undefined,\n opts: {\n set?: string[];\n unset?: string[];\n preset?: string;\n defaults?: boolean;\n options?: boolean;\n json?: boolean;\n reset?: boolean;\n }\n): void {\n // Discovery: list available options\n if (opts.options) {\n if (opts.json) {\n console.log(JSON.stringify({\n options: CONFIG_OPTIONS,\n presets: Object.entries(PERMISSION_PRESETS).map(([name, config]) => ({ name, ...config })),\n }, null, 2));\n return;\n }\n\n console.log();\n console.log(header(\" Session Config Options\"));\n console.log();\n console.log(bold(\" Available keys:\"));\n console.log();\n for (const opt of CONFIG_OPTIONS) {\n console.log(` ${bold(opt.key.padEnd(14))} ${dim(`(${opt.type})`)} ${opt.description}`);\n console.log(` ${' '.repeat(14)} ${dim('e.g.')} ${opt.examples.map(e => chalk.cyan(e)).join(', ')}`);\n }\n console.log();\n console.log(bold(\" Permission presets:\"));\n console.log();\n for (const [name, config] of Object.entries(PERMISSION_PRESETS)) {\n const parts = [];\n if (config.flags) parts.push(`flags: ${config.flags}`);\n if (config.env && Object.keys(config.env).length) parts.push(`env: ${Object.entries(config.env).map(([k, v]) => `${k}=${v}`).join(' ')}`);\n if (config.autoStart) parts.push('autoStart');\n if (config.prompt) parts.push(`prompt: \"${config.prompt}\"`);\n console.log(` ${bold(name.padEnd(10))} ${dim(parts.join(', ') || '(vanilla)')}`);\n }\n console.log();\n return;\n }\n\n // Global defaults mode\n if (opts.defaults) {\n let defaults = getGlobalDefaults();\n\n if (opts.reset) {\n saveGlobalDefaults({});\n console.log(ok(\"Global session defaults reset.\"));\n return;\n }\n\n if (opts.preset) {\n defaults = applyPreset(defaults, opts.preset);\n saveGlobalDefaults(defaults);\n console.log(ok(`Global defaults set to preset: ${bold(opts.preset)}`));\n return;\n }\n\n if (opts.set?.length) {\n for (const pair of opts.set) {\n const eqIdx = pair.indexOf('=');\n if (eqIdx === -1) { console.error(err(`Invalid format: \"${pair}\". Use key=value.`)); continue; }\n const key = pair.substring(0, eqIdx);\n const value = pair.substring(eqIdx + 1);\n if (!CONFIG_OPTIONS.find(o => o.key === key)) { console.error(err(`Unknown key: \"${key}\". Run: pai project config --options`)); continue; }\n if (key === 'env') {\n defaults.env = { ...(defaults.env ?? {}), ...(parseConfigValue('env', value) as Record<string, string>) };\n } else {\n (defaults as Record<string, unknown>)[key] = parseConfigValue(key, value);\n }\n }\n saveGlobalDefaults(defaults);\n console.log(ok(\"Global defaults updated.\"));\n }\n\n if (opts.unset?.length) {\n for (const key of opts.unset) {\n if (key.startsWith('env.')) {\n const envKey = key.substring(4);\n if (defaults.env) delete defaults.env[envKey];\n } else {\n delete (defaults as Record<string, unknown>)[key];\n }\n }\n saveGlobalDefaults(defaults);\n console.log(ok(\"Global defaults updated.\"));\n }\n\n if (opts.json) {\n console.log(JSON.stringify(defaults, null, 2));\n } else {\n console.log();\n console.log(header(\" Global Session Defaults\"));\n console.log();\n if (Object.keys(defaults).length === 0) {\n console.log(dim(\" No defaults set. New sessions use vanilla Claude.\"));\n console.log(dim(\" Set with: pai project config --defaults --preset full\"));\n } else {\n for (const [key, value] of Object.entries(defaults)) {\n const display = typeof value === 'object' ? JSON.stringify(value) : String(value);\n console.log(` ${bold(key.padEnd(14))} ${display}`);\n }\n }\n console.log();\n }\n return;\n }\n\n // Per-project config\n if (!identifier) {\n console.error(err(\"Specify a project: pai project config <name-or-slug>\"));\n console.error(dim(\" Or use --defaults for global defaults, --options for available keys.\"));\n process.exit(1);\n }\n\n const project = resolveIdentifier(db, identifier) ?? requireProject(db, identifier);\n let config = getSessionConfig(project);\n\n if (opts.reset) {\n db.prepare(\"UPDATE projects SET session_config = NULL, updated_at = ? WHERE id = ?\").run(now(), project.id);\n console.log(ok(`Config reset for ${bold(project.slug)}. Will use global defaults.`));\n return;\n }\n\n if (opts.preset) {\n config = applyPreset(config, opts.preset);\n db.prepare(\"UPDATE projects SET session_config = ?, updated_at = ? WHERE id = ?\")\n .run(JSON.stringify(config), now(), project.id);\n console.log(ok(`Applied preset ${bold(opts.preset)} to ${bold(project.slug)}`));\n }\n\n if (opts.set?.length) {\n for (const pair of opts.set) {\n const eqIdx = pair.indexOf('=');\n if (eqIdx === -1) { console.error(err(`Invalid format: \"${pair}\". Use key=value.`)); continue; }\n const key = pair.substring(0, eqIdx);\n const value = pair.substring(eqIdx + 1);\n if (!CONFIG_OPTIONS.find(o => o.key === key)) { console.error(err(`Unknown key: \"${key}\". Run: pai project config --options`)); continue; }\n if (key === 'env') {\n config.env = { ...(config.env ?? {}), ...(parseConfigValue('env', value) as Record<string, string>) };\n } else {\n (config as Record<string, unknown>)[key] = parseConfigValue(key, value);\n }\n }\n db.prepare(\"UPDATE projects SET session_config = ?, updated_at = ? WHERE id = ?\")\n .run(JSON.stringify(config), now(), project.id);\n console.log(ok(`Config updated for ${bold(project.slug)}`));\n }\n\n if (opts.unset?.length) {\n for (const key of opts.unset) {\n if (key.startsWith('env.')) {\n const envKey = key.substring(4);\n if (config.env) delete config.env[envKey];\n } else {\n delete (config as Record<string, unknown>)[key];\n }\n }\n db.prepare(\"UPDATE projects SET session_config = ?, updated_at = ? WHERE id = ?\")\n .run(JSON.stringify(config), now(), project.id);\n console.log(ok(`Config updated for ${bold(project.slug)}`));\n }\n\n const effective = { ...getGlobalDefaults(), ...config };\n const aliases = getProjectAliases(db, project.id);\n\n if (opts.json) {\n console.log(JSON.stringify({\n project: project.slug,\n names: aliases,\n root_path: project.root_path,\n config,\n global_defaults: getGlobalDefaults(),\n effective,\n }, null, 2));\n return;\n }\n\n console.log();\n console.log(header(` Config: ${project.slug}`));\n if (aliases.length) console.log(dim(` Names: ${aliases.join(', ')}`));\n console.log(dim(` Path: ${project.root_path}`));\n console.log();\n\n if (Object.keys(config).length === 0) {\n console.log(dim(\" No project-specific config. Using global defaults.\"));\n } else {\n console.log(bold(\" Project config:\"));\n for (const [key, value] of Object.entries(config)) {\n const display = typeof value === 'object' ? JSON.stringify(value) : String(value);\n console.log(` ${bold(key.padEnd(14))} ${display}`);\n }\n }\n\n const globalDefaults = getGlobalDefaults();\n if (Object.keys(globalDefaults).length > 0) {\n console.log();\n console.log(dim(\" Global defaults (overridden by project config):\"));\n for (const [key, value] of Object.entries(globalDefaults)) {\n const overridden = key in config;\n const display = typeof value === 'object' ? JSON.stringify(value) : String(value);\n console.log(` ${dim(key.padEnd(14))} ${overridden ? chalk.strikethrough(display) + ' ' + dim('(overridden)') : display}`);\n }\n }\n\n console.log();\n console.log(bold(\" Effective (what AIBroker uses):\"));\n for (const [key, value] of Object.entries(effective)) {\n const display = typeof value === 'object' ? JSON.stringify(value) : String(value);\n console.log(` ${bold(key.padEnd(14))} ${display}`);\n }\n console.log();\n}\n","/**\n * Project health check command — audits registered projects for missing paths,\n * moved directories, and orphaned note directories.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport { existsSync, readdirSync, statSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport { ok, warn, err, dim, bold, header, shortenPath, now, renderTable, encodeDir } from \"../../utils.js\";\nimport type { HealthRow, HealthCategory, ProjectHealth, ProjectRow } from \"./types.js\";\n\nfunction findOrphanedNotesDirs(project: ProjectRow): string[] {\n const claudeProjects = join(homedir(), \".claude\", \"projects\");\n if (!existsSync(claudeProjects)) return [];\n\n const expected = encodeDir(project.root_path);\n const results: string[] = [];\n\n try {\n for (const entry of readdirSync(claudeProjects)) {\n const full = join(claudeProjects, entry);\n try {\n if (!statSync(full).isDirectory()) continue;\n } catch {\n continue;\n }\n if (entry === expected || entry === project.encoded_dir) {\n const notesDir = join(full, \"Notes\");\n if (existsSync(notesDir)) {\n results.push(notesDir);\n }\n }\n }\n } catch {\n // Unreadable — ignore\n }\n return results;\n}\n\nfunction suggestMovedPath(project: ProjectRow): string | undefined {\n const name = basename(project.root_path);\n const candidates = [\n join(homedir(), \"dev\", name),\n join(homedir(), \"dev\", \"ai\", name),\n join(homedir(), \"Desktop\", name),\n join(homedir(), \"Projects\", name),\n ];\n for (const candidate of candidates) {\n if (existsSync(candidate)) return candidate;\n }\n return undefined;\n}\n\nexport function cmdHealth(\n db: Database,\n opts: { fix?: boolean; json?: boolean; status?: string }\n): void {\n const rows = db\n .prepare(\n `SELECT p.*,\n (SELECT COUNT(*) FROM sessions s WHERE s.project_id = p.id) AS session_count\n FROM projects p\n ORDER BY p.status ASC, p.updated_at DESC`\n )\n .all() as HealthRow[];\n\n const results: ProjectHealth[] = rows.map((project) => {\n const pathExists = existsSync(project.root_path);\n const orphaned = findOrphanedNotesDirs(project);\n\n let category: HealthCategory;\n let suggestedPath: string | undefined;\n\n if (pathExists) {\n category = \"active\";\n } else {\n suggestedPath = suggestMovedPath(project);\n category = suggestedPath ? \"stale\" : \"dead\";\n }\n\n return {\n project,\n category,\n suggestedPath,\n claudeNotesExists: orphaned.length > 0,\n orphanedNotesDirs: orphaned,\n };\n });\n\n const filtered = opts.status ? results.filter((r) => r.category === opts.status) : results;\n\n if (opts.json) {\n console.log(JSON.stringify(\n filtered.map((r) => ({\n slug: r.project.slug,\n root_path: r.project.root_path,\n status: r.project.status,\n health: r.category,\n session_count: r.project.session_count,\n suggested_path: r.suggestedPath ?? null,\n claude_notes_exists: r.claudeNotesExists,\n orphaned_notes_dirs: r.orphanedNotesDirs,\n })),\n null,\n 2\n ));\n return;\n }\n\n const active = filtered.filter((r) => r.category === \"active\");\n const stale = filtered.filter((r) => r.category === \"stale\");\n const dead = filtered.filter((r) => r.category === \"dead\");\n\n console.log();\n console.log(header(\" PAI Project Health Report\"));\n console.log();\n console.log(\n ` ${chalk.green(\"Active:\")} ${active.length} ${chalk.yellow(\"Stale (moved?):\")} ${stale.length} ${chalk.red(\"Dead (missing):\")} ${dead.length}`\n );\n console.log();\n\n if (active.length) {\n console.log(bold(\" Active projects (path exists):\"));\n const tableRows = active.map((r) => [\n bold(r.project.slug),\n dim(shortenPath(r.project.root_path, 50)),\n String(r.project.session_count),\n r.claudeNotesExists ? chalk.green(\"yes\") : dim(\"no\"),\n ]);\n console.log(\n renderTable([\"Slug\", \"Path\", \"Sessions\", \"Claude Notes\"], tableRows)\n .split(\"\\n\").map((l) => \" \" + l).join(\"\\n\")\n );\n console.log();\n }\n\n if (stale.length) {\n console.log(warn(\" Stale projects (path missing, possible new location found):\"));\n for (const r of stale) {\n console.log(` ${bold(r.project.slug)}`);\n console.log(dim(` Old path: ${r.project.root_path}`));\n console.log(chalk.cyan(` Found at: ${r.suggestedPath}`));\n if (r.claudeNotesExists) {\n console.log(chalk.green(` Notes: ${r.orphanedNotesDirs.join(\", \")}`));\n }\n if (opts.fix && r.suggestedPath) {\n const ts = now();\n const newEncoded = encodeDir(r.suggestedPath);\n db.prepare(\"UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?\")\n .run(r.suggestedPath, newEncoded, ts, r.project.id);\n console.log(ok(` Auto-fixed: updated path to ${r.suggestedPath}`));\n } else if (r.suggestedPath) {\n console.log(dim(` Fix: pai project move ${r.project.slug} ${r.suggestedPath}`));\n }\n }\n console.log();\n }\n\n if (dead.length) {\n console.log(err(\" Dead projects (path missing, no match found):\"));\n for (const r of dead) {\n console.log(` ${bold(r.project.slug)} ${dim(r.project.root_path)}`);\n if (r.claudeNotesExists) {\n console.log(chalk.yellow(` Notes: ${r.orphanedNotesDirs.join(\", \")}`));\n }\n if (r.project.session_count === 0 && opts.fix) {\n db.prepare(\"UPDATE projects SET status = 'archived', archived_at = ?, updated_at = ? WHERE id = ?\")\n .run(now(), now(), r.project.id);\n console.log(ok(\" Auto-fixed: archived (0 sessions, path gone)\"));\n } else {\n console.log(dim(` Fix: pai project archive ${r.project.slug} (or pai project move ...)`));\n }\n }\n console.log();\n }\n\n console.log(dim(` ${rows.length} total: ${active.length} active, ${stale.length} stale, ${dead.length} dead`));\n\n if (!opts.fix && (stale.length > 0 || dead.length > 0)) {\n console.log();\n console.log(warn(\" Run with --fix to auto-remediate where possible.\"));\n }\n}\n","/**\n * Commander registration for all `pai projects` sub-commands (plural namespace).\n *\n * This is the canonical namespace. `pai project <subcmd>` (singular) is a\n * deprecated alias that forwards here with a stderr notice.\n */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport { cmdPromote } from \"../../../session/promote.js\";\nimport {\n cmdAdd,\n cmdList,\n cmdInfo,\n cmdArchive,\n cmdUnarchive,\n cmdMove,\n cmdTag,\n cmdAlias,\n cmdEdit,\n cmdDetect,\n cmdConsolidate,\n cmdGo,\n cmdRebind,\n findMovedPath,\n} from \"./commands.js\";\nimport { existsSync } from \"node:fs\";\nimport { ok, warn, err, dim, bold, shortenPath, encodeDir, now } from \"../../utils.js\";\nimport { basename } from \"node:path\";\nimport { cmdName, cmdUnname, cmdNames, cmdConfig } from \"./session-config.js\";\nimport { cmdHealth } from \"./health.js\";\nimport { resolveIdentifier } from \"./helpers.js\";\n\nexport { cmdGo };\n\nexport function registerProjectsCommands(\n projectsCmd: Command,\n getDb: () => Database\n): void {\n // pai projects list — default when `pai projects` is invoked bare\n projectsCmd\n .command(\"list\", { isDefault: true })\n .description(\n \"List registered projects. Short form: pai projects (bare, no subcommand)\\n\" +\n \"Default: active projects only. Use --all to include archived.\"\n )\n .option(\"--all\", \"Include archived projects (default: active only)\")\n .option(\"--status <status>\", \"Filter by status: active | archived\")\n .option(\"--tag <tag>\", \"Filter by tag\")\n .option(\"--type <type>\", \"Filter by type\")\n .action((opts: { all?: boolean; status?: string; tag?: string; type?: string }) => {\n cmdList(getDb(), opts);\n });\n\n // pai projects cd <identifier>\n projectsCmd\n .command(\"cd <identifier>\")\n .description(\n \"cd to a project directory. Short form: pai cd <name>\\n\" +\n \"(The shell wrapper handles the actual cd; pure output here.)\\n\" +\n \"Auto-detects moved projects when the registered path no longer exists.\"\n )\n .action((identifier: string) => {\n const db = getDb();\n const project = resolveIdentifier(db, identifier);\n if (!project) {\n console.error(`Project not found: ${identifier}`);\n process.exit(1);\n return;\n }\n\n if (existsSync(project.root_path)) {\n process.stdout.write(project.root_path + \"\\n\");\n return;\n }\n\n // Path missing — search for moved location\n process.stderr.write(\n warn(`Path not found: ${project.root_path}\\n`) +\n dim(\" Searching for moved location...\\n\")\n );\n\n const result = findMovedPath(project.root_path);\n\n if (result.found) {\n const newPath = result.found;\n const newEncoded = encodeDir(newPath);\n const ts = now();\n db.prepare(\n \"UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?\"\n ).run(newPath, newEncoded, ts, project.id);\n process.stderr.write(\n ok(`Project moved: ${shortenPath(project.root_path, 50)}\\n`) +\n dim(` → ${newPath}\\n`) +\n ok(\"Registry updated.\\n\")\n );\n process.stdout.write(newPath + \"\\n\");\n return;\n }\n\n if (result.ambiguous) {\n process.stderr.write(\n warn(`Multiple directories named \"${basename(project.root_path)}\" found:\\n`)\n );\n for (const candidate of result.ambiguous) {\n process.stderr.write(dim(` ${candidate}\\n`));\n }\n process.stderr.write(\n dim(`\\n Disambiguate with: pai projects rebind ${project.slug} <path>\\n`)\n );\n process.exitCode = 1;\n return;\n }\n\n process.stderr.write(\n err(\n `Project \"${project.slug}\" root_path \"${project.root_path}\" does not exist on disk\\n` +\n ` and no folder named \"${basename(project.root_path)}\" was found in scan dirs.\\n`\n ) +\n dim(` Fix with: pai projects rebind ${project.slug} <new-path>\\n`)\n );\n process.exitCode = 1;\n });\n\n // pai projects add <path>\n projectsCmd\n .command(\"add <path>\")\n .description(\"Register a project directory in the PAI registry\")\n .option(\"--slug <slug>\", \"Override auto-generated slug\")\n .option(\"--type <type>\", \"Project type: local | central | obsidian-linked | external\", \"local\")\n .option(\"--display-name <name>\", \"Human-readable display name\")\n .action(\n (\n rawPath: string,\n opts: { slug?: string; type?: string; displayName?: string }\n ) => {\n cmdAdd(getDb(), rawPath, opts);\n }\n );\n\n // pai projects info <slug>\n projectsCmd\n .command(\"info <slug>\")\n .description(\"Show full details for a project\")\n .action((slug: string) => {\n cmdInfo(getDb(), slug);\n });\n\n // pai projects archive <slug>\n projectsCmd\n .command(\"archive <slug>\")\n .description(\"Archive a project\")\n .action((slug: string) => {\n cmdArchive(getDb(), slug);\n });\n\n // pai projects unarchive <slug>\n projectsCmd\n .command(\"unarchive <slug>\")\n .description(\"Restore an archived project to active status\")\n .action((slug: string) => {\n cmdUnarchive(getDb(), slug);\n });\n\n // pai projects move <slug> <new-path>\n projectsCmd\n .command(\"move <slug> <new-path>\")\n .description(\"Update the root path for a project\")\n .action((slug: string, newPath: string) => {\n cmdMove(getDb(), slug, newPath);\n });\n\n // pai projects rebind <slug> <new-path>\n projectsCmd\n .command(\"rebind <slug> <new-path>\")\n .description(\n \"Manually update the root_path for a project (for when auto-detect found multiple matches).\\n\" +\n \"Validates the new path exists and is a directory, then updates the registry.\"\n )\n .action((slug: string, newPath: string) => {\n cmdRebind(getDb(), slug, newPath);\n });\n\n // pai projects tag <slug> <tags...>\n projectsCmd\n .command(\"tag <slug> <tags...>\")\n .description(\"Add one or more tags to a project\")\n .action((slug: string, tags: string[]) => {\n cmdTag(getDb(), slug, tags);\n });\n\n // pai projects alias <slug> <alias>\n projectsCmd\n .command(\"alias <slug> <alias>\")\n .description(\"Register an alternative slug for a project\")\n .action((slug: string, alias: string) => {\n cmdAlias(getDb(), slug, alias);\n });\n\n // pai projects edit <slug>\n projectsCmd\n .command(\"edit <slug>\")\n .description(\"Edit project metadata\")\n .option(\"--display-name <name>\", \"New display name\")\n .option(\"--type <type>\", \"New type\")\n .action(\n (slug: string, opts: { displayName?: string; type?: string }) => {\n cmdEdit(getDb(), slug, opts);\n }\n );\n\n // pai projects detect [path]\n projectsCmd\n .command(\"detect [path]\")\n .description(\"Detect which registered project the given path (or CWD) belongs to\")\n .option(\"--json\", \"Output raw JSON instead of human-readable text\")\n .action((pathArg: string | undefined, opts: { json?: boolean }) => {\n cmdDetect(getDb(), pathArg, opts);\n });\n\n // pai projects health\n projectsCmd\n .command(\"health\")\n .description(\n \"Audit all registered projects: check which paths still exist, find moved/dead projects\"\n )\n .option(\"--fix\", \"Auto-remediate where possible\")\n .option(\"--json\", \"Output raw JSON report\")\n .option(\"--status <category>\", \"Filter output to: active | stale | dead\")\n .action((opts: { fix?: boolean; json?: boolean; status?: string }) => {\n cmdHealth(getDb(), opts);\n });\n\n // pai projects consolidate <identifier>\n projectsCmd\n .command(\"consolidate <identifier>\")\n .description(\n \"Consolidate scattered ~/.claude/projects/.../Notes/ directories for a project into its canonical Notes/ location\"\n )\n .option(\"--yes\", \"Perform consolidation without confirmation prompt\")\n .option(\"--dry-run\", \"Preview what would be moved without making changes\")\n .action(\n (identifier: string, opts: { yes?: boolean; dryRun?: boolean }) => {\n cmdConsolidate(getDb(), identifier, opts);\n }\n );\n\n // pai projects promote\n projectsCmd\n .command(\"promote\")\n .description(\"Promote a session note into a new standalone project\")\n .requiredOption(\"--from-session <path>\", \"Path to the session note markdown file\")\n .requiredOption(\"--to <path>\", \"Directory path for the new project (must not exist)\")\n .option(\"--name <name>\", \"Display name for the new project (derived from filename if omitted)\")\n .action((opts: { fromSession: string; to: string; name?: string }) => {\n cmdPromote(getDb(), opts);\n });\n\n // pai projects go <query>\n projectsCmd\n .command(\"go <query>\")\n .description(\n \"Print the root path for a project by slug, partial name, or fuzzy match.\\n\" +\n \"Designed for shell integration: cd $(pai projects go <query>)\"\n )\n .action((query: string) => {\n cmdGo(getDb(), query);\n });\n\n // pai projects name <identifier> <shortname>\n projectsCmd\n .command(\"name <identifier> <shortname>\")\n .description(\"Give a project a short name for quick access\")\n .option(\"--permission <level>\", \"Permission level: full | trusted | default\")\n .action(\n (\n identifier: string,\n shortname: string,\n opts: { permission?: string }\n ) => {\n cmdName(getDb(), identifier, shortname, opts);\n }\n );\n\n // pai projects unname <shortname>\n projectsCmd\n .command(\"unname <shortname>\")\n .description(\"Remove a project's short name\")\n .action((shortname: string) => {\n cmdUnname(getDb(), shortname);\n });\n\n // pai projects names\n projectsCmd\n .command(\"names\")\n .description(\"List named projects (your curated shortlist)\")\n .option(\"--json\", \"Output JSON for AIBroker consumption\")\n .action((opts: { json?: boolean }) => {\n cmdNames(getDb(), opts);\n });\n\n // pai projects config [identifier]\n projectsCmd\n .command(\"config [identifier]\")\n .description(\n \"View or modify session launch config for a project.\\n\" +\n \"Use --options to discover available keys and presets.\"\n )\n .option(\n \"--set <key=value...>\",\n \"Set config values (repeatable)\",\n (v: string, prev: string[]) => [...prev, v],\n [] as string[]\n )\n .option(\n \"--unset <key...>\",\n \"Remove config keys (repeatable, use env.KEY for env vars)\",\n (v: string, prev: string[]) => [...prev, v],\n [] as string[]\n )\n .option(\"--preset <name>\", \"Apply a permission preset: full | trusted | default\")\n .option(\"--defaults\", \"Manage global session defaults instead of a project\")\n .option(\"--options\", \"List available config keys and presets\")\n .option(\"--json\", \"Output JSON\")\n .option(\"--reset\", \"Reset config to empty (inherit global defaults)\")\n .action(\n (\n identifier: string | undefined,\n opts: {\n set?: string[];\n unset?: string[];\n preset?: string;\n defaults?: boolean;\n options?: boolean;\n json?: boolean;\n reset?: boolean;\n }\n ) => {\n cmdConfig(getDb(), identifier, opts);\n }\n );\n}\n","/**\n * Slug generator for PAI session notes.\n *\n * Extracts a 1-3 word descriptive slug from Claude Code JSONL transcripts\n * using keyword frequency analysis — no LLM required.\n *\n * JSONL format (one JSON object per line):\n * - type \"user\": { type: \"user\", message: { role: \"user\", content: string | [{ type, text }] } }\n * - type \"assistant\": { type: \"assistant\", message: { role: \"assistant\", content: [{ type, text }] } }\n */\n\nimport { readFileSync, existsSync, readdirSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { STOP_WORDS } from \"../utils/stop-words.js\";\n\n// ---------------------------------------------------------------------------\n// Public API types\n// ---------------------------------------------------------------------------\n\nexport interface SlugOptions {\n /** Maximum number of words in the generated slug (default: 3) */\n maxWords?: number;\n /** Maximum character length of the generated slug (default: 30) */\n maxLength?: number;\n}\n\n// STOP_WORDS imported from utils/stop-words.ts\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Extract plain text from a parsed JSONL message object.\n */\nfunction extractText(obj: Record<string, unknown>): string | null {\n const msg = obj.message as Record<string, unknown> | undefined;\n if (!msg) return null;\n\n const content = msg.content;\n\n // Array of content blocks (modern assistant messages)\n if (Array.isArray(content)) {\n const texts: string[] = [];\n for (const block of content) {\n const b = block as Record<string, unknown>;\n if (b.type === \"text\" && typeof b.text === \"string\") {\n texts.push(b.text);\n }\n }\n return texts.join(\" \") || null;\n }\n\n // Plain string content (user messages)\n if (typeof content === \"string\") {\n return content || null;\n }\n\n return null;\n}\n\n/**\n * Tokenize a string into lowercase words, filtering stop words and short tokens.\n */\nfunction tokenize(text: string): string[] {\n return text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \" \")\n .split(/\\s+/)\n .filter((w) => {\n if (w.length < 3) return false;\n if (/^\\d+$/.test(w)) return false;\n if (STOP_WORDS.has(w)) return false;\n return true;\n });\n}\n\n// ---------------------------------------------------------------------------\n// Public functions\n// ---------------------------------------------------------------------------\n\n/**\n * Find the latest JSONL transcript for a project's Claude encoded directory.\n *\n * @param encodedDir Claude-encoded project directory name,\n * e.g. \"-Users-alice-dev-ai-PAI\"\n * @returns Absolute path to the most recently modified .jsonl file, or null.\n */\nexport function findLatestTranscript(encodedDir: string): string | null {\n const sessionsDir = join(\n homedir(),\n \".claude\",\n \"projects\",\n encodedDir,\n \"sessions\"\n );\n\n if (!existsSync(sessionsDir)) return null;\n\n let entries: string[];\n try {\n entries = readdirSync(sessionsDir).filter((f) => f.endsWith(\".jsonl\"));\n } catch {\n return null;\n }\n\n if (!entries.length) return null;\n\n // Sort by mtime descending, return the newest\n const sorted = entries\n .map((f) => {\n const fullPath = join(sessionsDir, f);\n try {\n return { path: fullPath, mtime: statSync(fullPath).mtimeMs };\n } catch {\n return { path: fullPath, mtime: 0 };\n }\n })\n .sort((a, b) => b.mtime - a.mtime);\n\n return sorted[0].path;\n}\n\n/**\n * Read the last `count` human/assistant message pairs from a JSONL file.\n *\n * Synchronous — reads the whole file in memory. JSONL transcripts are\n * bounded by context window size so this is safe even for large sessions.\n *\n * @param jsonlPath Absolute path to a Claude Code .jsonl transcript file.\n * @param count Number of conversation pairs to read (default: 15).\n * @returns Array of plain-text message strings (up to count*2 entries).\n */\nexport function readLastMessages(\n jsonlPath: string,\n count: number = 15\n): string[] {\n if (!existsSync(jsonlPath)) return [];\n\n let raw: string;\n try {\n raw = readFileSync(jsonlPath, \"utf8\");\n } catch {\n return [];\n }\n\n const lines = raw.split(\"\\n\").filter((l) => l.trim().length > 0);\n const messages: string[] = [];\n const limit = count * 2;\n\n // Walk from the end to collect the most recent messages first\n for (let i = lines.length - 1; i >= 0 && messages.length < limit; i--) {\n let obj: Record<string, unknown>;\n try {\n obj = JSON.parse(lines[i]) as Record<string, unknown>;\n } catch {\n continue;\n }\n\n const type = obj.type as string | undefined;\n if (type !== \"assistant\" && type !== \"user\") continue;\n\n const text = extractText(obj);\n if (text && text.trim().length > 0) {\n // Prepend to maintain chronological order\n messages.unshift(text);\n }\n }\n\n return messages;\n}\n\n/**\n * Generate a descriptive 1-3 word slug from conversation message strings.\n *\n * Algorithm:\n * 1. Tokenize all messages, remove stop words and short tokens\n * 2. Count word frequency\n * 3. Pick the top `maxWords` most frequent tokens\n * 4. Join with hyphen, lowercase\n *\n * @param messages Array of plain-text conversation messages.\n * @param opts Optional slug configuration.\n * @returns A lowercase hyphen-joined slug, e.g. \"memory-engine-refactor\",\n * or \"unnamed-session\" if no meaningful words are found.\n */\nexport function generateSlug(messages: string[], opts?: SlugOptions): string {\n const maxWords = opts?.maxWords ?? 3;\n const maxLength = opts?.maxLength ?? 30;\n\n if (!messages.length) return \"unnamed-session\";\n\n // Count word frequencies across all messages\n const freq = new Map<string, number>();\n for (const msg of messages) {\n for (const token of tokenize(msg)) {\n freq.set(token, (freq.get(token) ?? 0) + 1);\n }\n }\n\n if (!freq.size) return \"unnamed-session\";\n\n // Sort by frequency descending, take top maxWords\n const topWords = Array.from(freq.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, maxWords)\n .map(([word]) => word);\n\n if (!topWords.length) return \"unnamed-session\";\n\n // Join and trim to maxLength (truncate at last hyphen boundary)\n let slug = topWords.join(\"-\");\n if (slug.length > maxLength) {\n slug = slug.slice(0, maxLength).replace(/-[^-]*$/, \"\");\n }\n\n return slug || \"unnamed-session\";\n}\n","/**\n * Shared DB helpers, formatting, and path utilities for session sub-commands.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport { err } from \"../../utils.js\";\nimport type { SessionRow, ProjectRow } from \"./types.js\";\n\nexport function getProject(db: Database, slug: string): ProjectRow | undefined {\n return db\n .prepare(\n \"SELECT id, slug, display_name, root_path, encoded_dir FROM projects WHERE slug = ?\"\n )\n .get(slug) as ProjectRow | undefined;\n}\n\nexport function statusColor(status: string): string {\n switch (status) {\n case \"completed\":\n return chalk.green(status);\n case \"compacted\":\n return chalk.blue(status);\n default:\n return chalk.yellow(status);\n }\n}\n\n/** Convert a slug to title-cased display name: \"memory-engine\" → \"Memory Engine\" */\nexport function toTitleCase(slug: string): string {\n return slug\n .replace(/-/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\n/** Notes directory for a project: ~/.claude/projects/<encoded_dir>/Notes/ */\nexport function getNotesDir(project: ProjectRow): string {\n return join(homedir(), \".claude\", \"projects\", project.encoded_dir, \"Notes\");\n}\n\n/** Format a session filename: number=27, date=\"2026-02-23\" → \"0027 - 2026-02-23 - Title.md\" */\nexport function formatFilename(\n number: number,\n date: string,\n titleSlug: string\n): string {\n const n = String(number).padStart(4, \"0\");\n return `${n} - ${date} - ${titleSlug}.md`;\n}\n\n/** Resolve a session by project + number or \"latest\". Exits on failure. */\nexport function resolveSession(\n db: Database,\n project: ProjectRow,\n numberOrLatest: string\n): SessionRow {\n let session: SessionRow | undefined;\n\n if (numberOrLatest === \"latest\") {\n session = db\n .prepare(\n \"SELECT * FROM sessions WHERE project_id = ? ORDER BY number DESC LIMIT 1\"\n )\n .get(project.id) as SessionRow | undefined;\n } else {\n const num = parseInt(numberOrLatest, 10);\n if (isNaN(num)) {\n console.error(err(`Invalid session number: ${numberOrLatest}`));\n process.exit(1);\n }\n session = db\n .prepare(\"SELECT * FROM sessions WHERE project_id = ? AND number = ?\")\n .get(project.id, num) as SessionRow | undefined;\n }\n\n if (!session) {\n console.error(\n err(`Session ${numberOrLatest} not found in project ${project.slug}`)\n );\n process.exit(1);\n }\n\n return session;\n}\n\nexport function upsertTag(db: Database, tagName: string): number {\n db.prepare(\"INSERT OR IGNORE INTO tags (name) VALUES (?)\").run(tagName);\n const row = db\n .prepare(\"SELECT id FROM tags WHERE name = ?\")\n .get(tagName) as { id: number };\n return row.id;\n}\n\nexport function getSessionTags(db: Database, sessionId: number): string[] {\n const rows = db\n .prepare(\n `SELECT t.name FROM tags t\n JOIN session_tags st ON st.tag_id = t.id\n WHERE st.session_id = ?\n ORDER BY t.name`\n )\n .all(sessionId) as { name: string }[];\n return rows.map((r) => r.name);\n}\n","/**\n * Session CRUD commands: list, info, rename, slug, tag, route, active, auto-route.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport {\n existsSync,\n readdirSync,\n renameSync,\n readFileSync,\n writeFileSync,\n statSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport {\n ok,\n warn,\n err,\n dim,\n bold,\n header,\n renderTable,\n fmtDate,\n} from \"../../utils.js\";\nimport {\n findLatestTranscript,\n readLastMessages,\n generateSlug,\n} from \"../../../session/slug-generator.js\";\nimport type { SessionRow, ProjectRow } from \"./types.js\";\nimport {\n getProject,\n statusColor,\n toTitleCase,\n getNotesDir,\n formatFilename,\n resolveSession,\n upsertTag,\n getSessionTags,\n} from \"./helpers.js\";\n\n// ---------------------------------------------------------------------------\n// list\n// ---------------------------------------------------------------------------\n\nexport function cmdList(\n db: Database,\n projectSlug: string | undefined,\n opts: { limit?: string; status?: string }\n): void {\n const limit = parseInt(opts.limit ?? \"20\", 10);\n const params: unknown[] = [];\n\n let query = `\n SELECT s.*, p.slug AS project_slug, p.display_name AS project_name\n FROM sessions s\n JOIN projects p ON p.id = s.project_id\n `;\n\n const where: string[] = [];\n if (projectSlug) {\n const project = getProject(db, projectSlug);\n if (!project) {\n console.error(err(`Project not found: ${projectSlug}`));\n process.exit(1);\n }\n where.push(\"s.project_id = ?\");\n params.push(project.id);\n }\n if (opts.status) {\n where.push(\"s.status = ?\");\n params.push(opts.status);\n }\n\n if (where.length) query += \" WHERE \" + where.join(\" AND \");\n query += \" ORDER BY s.date DESC, s.number DESC\";\n query += ` LIMIT ${limit}`;\n\n const rows = db.prepare(query).all(...params) as (SessionRow & {\n project_slug: string;\n project_name: string;\n })[];\n\n if (!rows.length) {\n console.log(warn(\"No sessions found.\"));\n return;\n }\n\n const showProject = !projectSlug;\n const headers = showProject\n ? [\"#\", \"Project\", \"Date\", \"Title\", \"Status\", \"Tokens\"]\n : [\"#\", \"Date\", \"Title\", \"Status\", \"Tokens\"];\n\n const tableRows = rows.map((r) => {\n const title = r.title.length > 45 ? r.title.slice(0, 42) + \"...\" : r.title;\n const tokens =\n r.token_count != null ? dim(r.token_count.toLocaleString()) : dim(\"—\");\n if (showProject) {\n return [\n dim(`#${r.number}`),\n r.project_slug,\n r.date,\n title,\n statusColor(r.status),\n tokens,\n ];\n }\n return [dim(`#${r.number}`), r.date, title, statusColor(r.status), tokens];\n });\n\n console.log();\n if (projectSlug) {\n const project = getProject(db, projectSlug)!;\n console.log(` ${bold(project.display_name)} sessions:`);\n console.log();\n }\n console.log(renderTable(headers, tableRows));\n console.log();\n console.log(dim(` ${rows.length} session(s) shown (limit: ${limit})`));\n}\n\n// ---------------------------------------------------------------------------\n// info\n// ---------------------------------------------------------------------------\n\nexport function cmdInfo(\n db: Database,\n projectSlug: string,\n sessionNumber: string\n): void {\n const project = getProject(db, projectSlug);\n if (!project) {\n console.error(err(`Project not found: ${projectSlug}`));\n process.exit(1);\n }\n\n const session = resolveSession(db, project, sessionNumber);\n\n console.log();\n console.log(header(` Session #${session.number}: ${session.title}`));\n console.log();\n console.log(\n ` ${bold(\"Project:\")} ${project.display_name} (${project.slug})`\n );\n console.log(` ${bold(\"Date:\")} ${session.date}`);\n console.log(` ${bold(\"Status:\")} ${statusColor(session.status)}`);\n console.log(` ${bold(\"Filename:\")} ${session.filename}`);\n console.log(` ${bold(\"Slug:\")} ${session.slug}`);\n if (session.claude_session_id) {\n console.log(\n ` ${bold(\"Claude ID:\")} ${dim(session.claude_session_id)}`\n );\n }\n if (session.token_count != null) {\n console.log(\n ` ${bold(\"Tokens:\")} ${session.token_count.toLocaleString()}`\n );\n }\n console.log(` ${bold(\"Created:\")} ${fmtDate(session.created_at)}`);\n if (session.closed_at) {\n console.log(` ${bold(\"Closed:\")} ${fmtDate(session.closed_at)}`);\n }\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// rename\n// ---------------------------------------------------------------------------\n\nexport function cmdRename(\n db: Database,\n projectSlug: string,\n numberOrLatest: string,\n newSlug: string\n): void {\n const project = getProject(db, projectSlug);\n if (!project) {\n console.error(err(`Project not found: ${projectSlug}`));\n process.exit(1);\n }\n\n const session = resolveSession(db, project, numberOrLatest);\n const notesDir = getNotesDir(project);\n\n if (!existsSync(notesDir)) {\n console.error(err(`Notes directory not found: ${notesDir}`));\n process.exit(1);\n }\n\n const titleSlug = toTitleCase(newSlug);\n const newFilename = formatFilename(session.number, session.date, titleSlug);\n const oldPath = join(notesDir, session.filename);\n const newPath = join(notesDir, newFilename);\n\n if (existsSync(oldPath)) {\n if (oldPath !== newPath) {\n try {\n renameSync(oldPath, newPath);\n } catch (e) {\n console.error(err(`Failed to rename file: ${e}`));\n process.exit(1);\n }\n }\n } else {\n console.log(\n warn(` Note: file not found at expected path: ${session.filename}`)\n );\n console.log(warn(` Skipping disk rename. Database will still be updated.`));\n }\n\n if (existsSync(newPath)) {\n try {\n const content = readFileSync(newPath, \"utf8\");\n const lines = content.split(\"\\n\");\n let h1Updated = false;\n const updated = lines.map((line) => {\n if (!h1Updated && line.startsWith(\"# \")) {\n h1Updated = true;\n return `# ${titleSlug}`;\n }\n return line;\n });\n if (!h1Updated) updated.unshift(`# ${titleSlug}`, \"\");\n writeFileSync(newPath, updated.join(\"\\n\"), \"utf8\");\n } catch (e) {\n console.error(err(`Failed to update H1 in file: ${e}`));\n }\n }\n\n const normalizedSlug = newSlug\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n\n db.prepare(\n \"UPDATE sessions SET slug = ?, title = ?, filename = ? WHERE id = ?\"\n ).run(normalizedSlug, titleSlug, newFilename, session.id);\n\n console.log();\n console.log(ok(` Session #${session.number} renamed.`));\n console.log(` ${bold(\"Old:\")} ${session.filename}`);\n console.log(` ${bold(\"New:\")} ${newFilename}`);\n console.log(` ${bold(\"Slug:\")} ${normalizedSlug}`);\n console.log(` ${bold(\"Title:\")} ${titleSlug}`);\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// slug\n// ---------------------------------------------------------------------------\n\nexport function cmdSlug(\n db: Database,\n projectSlug: string,\n numberOrLatest: string,\n opts: { apply?: boolean }\n): void {\n const project = getProject(db, projectSlug);\n if (!project) {\n console.error(err(`Project not found: ${projectSlug}`));\n process.exit(1);\n }\n\n const session = resolveSession(db, project, numberOrLatest);\n const transcriptPath = findLatestTranscript(project.encoded_dir);\n\n if (!transcriptPath) {\n console.log(warn(` No JSONL transcripts found for project ${projectSlug}`));\n console.log(\"unnamed-session\");\n return;\n }\n\n const messages = readLastMessages(transcriptPath);\n\n if (messages.length < 2) {\n console.log(\n warn(` Too few messages found (${messages.length}) in transcript`)\n );\n console.log(\"unnamed-session\");\n return;\n }\n\n const generatedSlug = generateSlug(messages);\n console.log(generatedSlug);\n\n if (opts.apply) {\n console.log();\n console.log(dim(` Applying slug to session #${session.number}...`));\n cmdRename(db, projectSlug, String(session.number), generatedSlug);\n }\n}\n\n// ---------------------------------------------------------------------------\n// tag\n// ---------------------------------------------------------------------------\n\nexport function cmdTag(\n db: Database,\n projectSlug: string,\n sessionNumber: string,\n rawTags: string[]\n): void {\n const project = getProject(db, projectSlug);\n if (!project) {\n console.error(err(`Project not found: ${projectSlug}`));\n process.exit(1);\n }\n\n const session = resolveSession(db, project, sessionNumber);\n\n if (rawTags.length === 0) {\n const current = getSessionTags(db, session.id);\n console.log();\n if (current.length === 0) {\n console.log(dim(` Session #${session.number} has no tags.`));\n } else {\n console.log(\n ` ${bold(`Session #${session.number}`)} tags: ${current\n .map((t) => chalk.cyan(t))\n .join(\", \")}`\n );\n }\n console.log();\n return;\n }\n\n const tags = rawTags\n .flatMap((t) => t.split(\",\"))\n .map((t) => t.trim().toLowerCase())\n .filter((t) => t.length > 0);\n\n if (tags.length === 0) {\n console.log(warn(\"No valid tags provided.\"));\n return;\n }\n\n const added: string[] = [];\n const skipped: string[] = [];\n\n for (const tagName of tags) {\n const tagId = upsertTag(db, tagName);\n const exists = db\n .prepare(\"SELECT 1 FROM session_tags WHERE session_id = ? AND tag_id = ?\")\n .get(session.id, tagId);\n if (exists) {\n skipped.push(tagName);\n } else {\n db.prepare(\n \"INSERT INTO session_tags (session_id, tag_id) VALUES (?, ?)\"\n ).run(session.id, tagId);\n added.push(tagName);\n }\n }\n\n console.log();\n if (added.length) {\n console.log(\n ok(\n ` Tagged session #${session.number}: ${added\n .map((t) => chalk.cyan(t))\n .join(\", \")}`\n )\n );\n }\n if (skipped.length) {\n console.log(dim(` Already present: ${skipped.join(\", \")}`));\n }\n\n const allTags = getSessionTags(db, session.id);\n console.log(\n ` ${bold(\"All tags:\")} ${allTags.map((t) => chalk.cyan(t)).join(\", \")}`\n );\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// route\n// ---------------------------------------------------------------------------\n\nexport function cmdRoute(\n db: Database,\n projectSlug: string,\n sessionNumber: string,\n targetProjectSlug: string,\n opts: { type?: string }\n): void {\n const project = getProject(db, projectSlug);\n if (!project) {\n console.error(err(`Project not found: ${projectSlug}`));\n process.exit(1);\n }\n\n const session = resolveSession(db, project, sessionNumber);\n\n const targetProject = db\n .prepare(\"SELECT id, slug, display_name FROM projects WHERE slug = ?\")\n .get(targetProjectSlug) as\n | { id: number; slug: string; display_name: string }\n | undefined;\n\n if (!targetProject) {\n console.error(err(`Target project not found: ${targetProjectSlug}`));\n process.exit(1);\n }\n\n const validTypes = [\"related\", \"follow-up\", \"reference\"];\n const linkType = opts.type ?? \"related\";\n if (!validTypes.includes(linkType)) {\n console.error(\n err(`Invalid link type \"${linkType}\". Valid: ${validTypes.join(\", \")}`)\n );\n process.exit(1);\n }\n\n try {\n db.prepare(\n `INSERT INTO links (session_id, target_project_id, link_type, created_at)\n VALUES (?, ?, ?, ?)`\n ).run(session.id, targetProject.id, linkType, Date.now());\n } catch {\n console.log(\n warn(\n ` Link already exists: session #${session.number} → ${targetProjectSlug}`\n )\n );\n return;\n }\n\n console.log();\n console.log(\n ok(\n ` Linked session #${session.number} (${project.slug}) → ${targetProject.display_name} (${targetProjectSlug})`\n )\n );\n console.log(dim(` Link type: ${linkType}`));\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// active\n// ---------------------------------------------------------------------------\n\nexport function cmdActive(\n db: Database,\n opts: { minutes?: string; json?: boolean }\n): void {\n const minutes = parseInt(opts.minutes ?? \"60\", 10);\n const cutoff = Date.now() - minutes * 60 * 1000;\n const claudeProjectsDir = join(homedir(), \".claude\", \"projects\");\n\n if (!existsSync(claudeProjectsDir)) {\n console.log(err(\"Claude projects directory not found.\"));\n return;\n }\n\n interface ActiveSession {\n slug: string;\n displayName: string;\n rootPath: string;\n encodedDir: string;\n lastModified: Date;\n jsonlFile: string;\n }\n\n const active: ActiveSession[] = [];\n const entries = readdirSync(claudeProjectsDir);\n\n for (const entry of entries) {\n const projectDir = join(claudeProjectsDir, entry);\n try {\n if (!statSync(projectDir).isDirectory()) continue;\n } catch {\n continue;\n }\n\n let latestJsonl: string | null = null;\n let latestMtime = 0;\n\n try {\n for (const file of readdirSync(projectDir)) {\n if (!file.endsWith(\".jsonl\")) continue;\n const filePath = join(projectDir, file);\n try {\n const mtime = statSync(filePath).mtimeMs;\n if (mtime > latestMtime) {\n latestMtime = mtime;\n latestJsonl = filePath;\n }\n } catch {\n continue;\n }\n }\n } catch {\n continue;\n }\n\n if (!latestJsonl || latestMtime < cutoff) continue;\n\n const project = db\n .prepare(\n \"SELECT slug, display_name, root_path FROM projects WHERE encoded_dir = ?\"\n )\n .get(entry) as\n | { slug: string; display_name: string; root_path: string }\n | undefined;\n\n active.push({\n slug: project?.slug ?? entry,\n displayName: project?.display_name ?? project?.slug ?? entry,\n rootPath: project?.root_path ?? \"\",\n encodedDir: entry,\n lastModified: new Date(latestMtime),\n jsonlFile: latestJsonl,\n });\n }\n\n active.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());\n\n const seen = new Set<string>();\n const deduped = active.filter((a) => {\n const key = a.slug.replace(/-\\d+$/, \"\");\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n\n if (opts.json) {\n console.log(\n JSON.stringify(\n deduped.map((a) => ({\n slug: a.slug,\n display_name: a.displayName,\n root_path: a.rootPath,\n last_modified: a.lastModified.toISOString(),\n })),\n null,\n 2\n )\n );\n return;\n }\n\n if (deduped.length === 0) {\n console.log(dim(`No active sessions in the last ${minutes} minutes.`));\n return;\n }\n\n console.log(\n header(`Currently Active Sessions`) +\n dim(` (modified in last ${minutes}min)`)\n );\n console.log();\n\n const rows = deduped.map((a) => {\n const time = a.lastModified.toTimeString().slice(0, 5);\n const dirName = a.rootPath\n ? a.rootPath.replace(homedir(), \"~\").split(\"/\").pop() ?? a.slug\n : a.slug;\n return [chalk.cyan(dirName), dim(a.slug), chalk.green(time)];\n });\n\n console.log(renderTable([\"Directory\", \"Project\", \"Last Active\"], rows));\n}\n\n// ---------------------------------------------------------------------------\n// auto-route\n// ---------------------------------------------------------------------------\n\nexport async function cmdAutoRoute(opts: {\n cwd?: string;\n context?: string;\n json?: boolean;\n}): Promise<void> {\n const { autoRoute, formatAutoRoute, formatAutoRouteJson } = await import(\n \"../../../session/auto-route.js\"\n );\n const { openRegistry } = await import(\"../../../registry/db.js\");\n const { createStorageBackend } = await import(\"../../../storage/factory.js\");\n const { loadConfig } = await import(\"../../../daemon/config.js\");\n\n const config = loadConfig();\n const registryDb = openRegistry();\n const federation = await createStorageBackend(config);\n\n const targetCwd = opts.cwd ?? process.cwd();\n const result = await autoRoute(\n registryDb,\n federation,\n targetCwd,\n opts.context\n );\n\n if (!result) {\n console.log();\n console.log(warn(\" No project match found for: \" + targetCwd));\n console.log();\n console.log(\n dim(\" Tried: path match, PAI.md marker walk\") +\n (opts.context ? dim(\", topic detection\") : \"\")\n );\n console.log();\n console.log(dim(\" Run 'pai project add .' to register this directory.\"));\n console.log();\n return;\n }\n\n if (opts.json) {\n console.log(formatAutoRouteJson(result));\n return;\n }\n\n console.log();\n console.log(header(\" PAI Auto-Route\"));\n console.log();\n console.log(` ${bold(\"Project:\")} ${result.display_name}`);\n console.log(` ${bold(\"Slug:\")} ${result.slug}`);\n console.log(` ${bold(\"Root path:\")} ${result.root_path}`);\n console.log(` ${bold(\"Method:\")} ${result.method}`);\n console.log(\n ` ${bold(\"Confidence:\")} ${(result.confidence * 100).toFixed(0)}%`\n );\n console.log();\n console.log(ok(\" Routed to: \") + bold(result.slug));\n console.log();\n}\n","/**\n * Session checkpoint command — appends a timestamped block to the active\n * session note. Designed for use in hooks; fast, silent, rate-limited.\n */\n\nimport { existsSync, readdirSync, statSync, readFileSync, writeFileSync, renameSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir, tmpdir } from \"node:os\";\n\nfunction findNotesDirForCwd(): string | null {\n const cwd = process.cwd();\n const claudeProjectsDir = join(homedir(), \".claude\", \"projects\");\n\n if (!existsSync(claudeProjectsDir)) return null;\n\n const expectedEncoded = cwd.replace(/[/\\s.\\-]/g, \"-\");\n let encodedDir: string | null = null;\n\n try {\n const entries = readdirSync(claudeProjectsDir);\n\n if (entries.includes(expectedEncoded)) {\n encodedDir = expectedEncoded;\n } else {\n for (const entry of entries) {\n const full = join(claudeProjectsDir, entry);\n try {\n if (!statSync(full).isDirectory()) continue;\n } catch {\n continue;\n }\n const candidate = entry.replace(/-+$/, \"\");\n const expected = expectedEncoded.replace(/-+$/, \"\");\n if (candidate === expected) {\n encodedDir = entry;\n break;\n }\n }\n }\n } catch {\n return null;\n }\n\n if (!encodedDir) return null;\n const notesDir = join(claudeProjectsDir, encodedDir, \"Notes\");\n return existsSync(notesDir) ? notesDir : null;\n}\n\nfunction findLatestNoteFile(notesDir: string): string | null {\n let entries: string[];\n try {\n entries = readdirSync(notesDir);\n } catch {\n return null;\n }\n\n const mdFiles = entries.filter((e) => e.endsWith(\".md\"));\n if (mdFiles.length === 0) return null;\n\n let latestPath: string | null = null;\n let latestMtime = 0;\n\n for (const file of mdFiles) {\n const full = join(notesDir, file);\n try {\n const { mtimeMs } = statSync(full);\n if (mtimeMs > latestMtime) {\n latestMtime = mtimeMs;\n latestPath = full;\n }\n } catch {\n // skip unreadable files\n }\n }\n\n return latestPath;\n}\n\nfunction checkpointTooRecent(notesDir: string, minGapSeconds: number): boolean {\n const safeKey = notesDir.replace(/[^a-zA-Z0-9]/g, \"-\").slice(-80);\n const tmpFile = join(tmpdir(), `pai-checkpoint-${safeKey}`);\n if (!existsSync(tmpFile)) return false;\n try {\n const { mtimeMs } = statSync(tmpFile);\n return Date.now() - mtimeMs < minGapSeconds * 1000;\n } catch {\n return false;\n }\n}\n\nfunction touchCheckpointSentinel(notesDir: string): void {\n const safeKey = notesDir.replace(/[^a-zA-Z0-9]/g, \"-\").slice(-80);\n const tmpFile = join(tmpdir(), `pai-checkpoint-${safeKey}`);\n try {\n writeFileSync(tmpFile, String(Date.now()), \"utf8\");\n } catch {\n // Non-fatal — rate limiting is best-effort\n }\n}\n\nexport function cmdCheckpoint(\n message: string,\n opts: { minGap?: string }\n): void {\n const minGapSeconds = parseInt(opts.minGap ?? \"300\", 10);\n\n const notesDir = findNotesDirForCwd();\n if (!notesDir) process.exit(0);\n\n if (checkpointTooRecent(notesDir, minGapSeconds)) process.exit(0);\n\n const notePath = findLatestNoteFile(notesDir);\n if (!notePath) process.exit(0);\n\n const timestamp = new Date().toISOString();\n const block = `\\n## Checkpoint — ${timestamp}\\n${message}\\n`;\n\n const tmpPath = `${notePath}.checkpoint.tmp`;\n try {\n const existing = readFileSync(notePath, \"utf8\");\n writeFileSync(tmpPath, existing + block, \"utf8\");\n renameSync(tmpPath, notePath);\n } catch {\n try {\n if (existsSync(tmpPath)) renameSync(tmpPath, tmpPath + \".dead\");\n } catch {\n /* ignore */\n }\n process.exit(0);\n }\n\n touchCheckpointSentinel(notesDir);\n process.exit(0);\n}\n","/**\n * Session handover command — writes a ## Continue section to the project's\n * TODO.md. Called from session-stop and pre-compact hooks.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { SessionRow, ProjectRow } from \"./types.js\";\n\nconst HANDOVER_TODO_LOCATIONS = [\n \"Notes/TODO.md\",\n \".claude/Notes/TODO.md\",\n \"tasks/todo.md\",\n \"TODO.md\",\n];\n\nfunction findProjectTodo(\n rootPath: string\n): { path: string; content: string } | null {\n for (const rel of HANDOVER_TODO_LOCATIONS) {\n const full = join(rootPath, rel);\n if (existsSync(full)) {\n try {\n return { path: full, content: readFileSync(full, \"utf8\") };\n } catch {\n // unreadable — try next\n }\n }\n }\n return null;\n}\n\nfunction stripContinueSection(content: string): string {\n const lines = content.split(\"\\n\");\n const startIdx = lines.findIndex((l) => l.trim() === \"## Continue\");\n if (startIdx === -1) return content;\n\n let endIdx = lines.length;\n for (let i = startIdx + 1; i < lines.length; i++) {\n const trimmed = lines[i].trim();\n if (\n trimmed === \"---\" ||\n (trimmed.startsWith(\"##\") && trimmed !== \"## Continue\")\n ) {\n endIdx = i;\n break;\n }\n }\n\n let trailingEnd = endIdx;\n if (trailingEnd < lines.length && lines[trailingEnd].trim() === \"---\") {\n trailingEnd += 1;\n }\n\n const before = lines.slice(0, startIdx);\n const after = lines.slice(trailingEnd);\n while (after.length > 0 && after[0].trim() === \"\") after.shift();\n\n return [...before, ...after].join(\"\\n\");\n}\n\nexport function cmdHandover(\n db: Database,\n projectSlug: string | undefined,\n numberOrLatest: string | undefined\n): void {\n // ---- 1. Resolve project ----\n let project: ProjectRow | undefined;\n\n if (projectSlug) {\n project = db\n .prepare(\n \"SELECT id, slug, display_name, root_path, encoded_dir FROM projects WHERE slug = ?\"\n )\n .get(projectSlug) as ProjectRow | undefined;\n if (!project) process.exit(0);\n } else {\n const cwd = process.cwd();\n const row = db\n .prepare(\n `SELECT id, slug, display_name, root_path, encoded_dir\n FROM projects\n WHERE ? LIKE root_path || '%'\n ORDER BY length(root_path) DESC\n LIMIT 1`\n )\n .get(cwd) as ProjectRow | undefined;\n if (!row) process.exit(0);\n project = row;\n }\n\n // ---- 2. Resolve session ----\n let session: SessionRow | undefined;\n const nol = numberOrLatest ?? \"latest\";\n\n if (nol === \"latest\") {\n session = db\n .prepare(\n \"SELECT * FROM sessions WHERE project_id = ? ORDER BY number DESC LIMIT 1\"\n )\n .get(project!.id) as SessionRow | undefined;\n } else {\n const num = parseInt(nol, 10);\n if (!isNaN(num)) {\n session = db\n .prepare(\"SELECT * FROM sessions WHERE project_id = ? AND number = ?\")\n .get(project!.id, num) as SessionRow | undefined;\n }\n }\n\n // ---- 3. Find or create TODO ----\n const todo = findProjectTodo(project!.root_path);\n let todoPath: string;\n let existingContent: string;\n\n if (todo) {\n todoPath = todo.path;\n existingContent = todo.content;\n } else {\n const notesDir = join(project!.root_path, \"Notes\");\n try {\n if (!existsSync(notesDir)) mkdirSync(notesDir, { recursive: true });\n } catch {\n process.exit(0);\n }\n todoPath = join(notesDir, \"TODO.md\");\n existingContent = \"\";\n }\n\n // ---- 4. Build ## Continue block ----\n const timestamp = new Date().toISOString();\n const cwd = process.cwd();\n\n let sessionLine: string;\n if (session) {\n const num = String(session.number).padStart(4, \"0\");\n const titlePart = session.title || session.slug || \"Session\";\n sessionLine = `${num} - ${session.date} - ${titlePart}`;\n } else {\n sessionLine = \"Unknown session\";\n }\n\n const continueBlock = [\n \"## Continue\",\n \"\",\n `> **Last session:** ${sessionLine}`,\n `> **Paused at:** ${timestamp}`,\n \">\",\n `> Working directory: ${cwd}. Check the latest session note for details.`,\n \"\",\n \"---\",\n \"\",\n ].join(\"\\n\");\n\n // ---- 5. Prepend, stripping old ## Continue ----\n const stripped = stripContinueSection(existingContent).trimStart();\n const newContent = continueBlock + stripped;\n\n // ---- 6. Write atomically ----\n const tmpPath = `${todoPath}.handover.tmp`;\n try {\n writeFileSync(tmpPath, newContent, \"utf8\");\n renameSync(tmpPath, todoPath);\n } catch {\n try {\n if (existsSync(tmpPath)) renameSync(tmpPath, `${tmpPath}.dead`);\n } catch {\n /* ignore */\n }\n process.exit(0);\n }\n\n process.exit(0);\n}\n","/**\n * Shared scanner for Claude Code sessions stored in ~/.claude/projects/ .\n *\n * Storage layout (Claude Code v2.1.143+):\n *\n * <project>/<uuid>.jsonl — top-level: metadata, system snapshots, hook events.\n * REQUIRED for claude --resume to work.\n * <project>/sessions/<uuid>.jsonl — full transcript: user/assistant/attachment lines.\n * 3000+ files, almost none resumable on their own.\n *\n * Resumability rule (empirically verified):\n * A session is resumable iff the TOP-LEVEL jsonl exists AND contains at least one\n * line of type \"system\". Sessions that only have a sessions/ counterpart cannot be\n * resumed by Claude Code regardless of how much transcript content they have.\n *\n * Stale-UUID problem (fixed in this version):\n * The clc session.json registry stores ONE uuid per named session — the uuid Claude Code\n * had when the user last named the session. If the user resumes and the session gets a\n * new uuid (or they Ctrl+C and start fresh), the registry entry still points to the OLD\n * uuid. The scanner now resolves names to the MOST RECENT top-level jsonl in the project\n * directory, not the clc-cached uuid.\n *\n * Resolution strategy:\n * 1. Walk top-level <project>/<uuid>.jsonl files (Pass 1).\n * 2. For each, attach the clc name if the uuid matches (exact hit).\n * 3. After Pass 1, for every clc registry entry:\n * a. If the cached uuid was found → already handled.\n * b. Find the encodedDir for this entry's directory.\n * c. Check ALL sessions in that encodedDir (already in our Pass-1 results).\n * d. Pick the MOST RECENT resumable session in that dir and attach the name to it.\n * If no resumable session, fall through to transcript-only pass.\n * 4. This means the displayed uuid for \"Jobs Matthias\" is always today's active session,\n * not the stale cached one.\n *\n * Used by: pai sessions, pai resume\n */\n\nimport { readdirSync, statSync, readFileSync, existsSync, realpathSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type { Database } from \"better-sqlite3\";\nimport { smartDecodeDir } from \"../utils.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Display status for a session:\n * resumable — top-level jsonl exists with ≥1 system line (claude --resume works)\n * stub — in clc registry, top-level jsonl exists but 0 system lines (Ctrl+C exit)\n * transcript-only — in clc registry but no top-level jsonl at all\n * orphan — not in clc registry, only sessions/ transcript exists\n */\nexport type SessionStatus = \"resumable\" | \"stub\" | \"transcript-only\" | \"orphan\";\n\nexport interface ScannedSession {\n /** Full UUID from the filename. */\n uuid: string;\n /** Short 8-char prefix for display. */\n shortId: string;\n /** Encoded project directory name (e.g. \"-Users-foo-myproject\"). */\n encodedDir: string;\n /** Decoded project root path (via smartDecodeDir). */\n decodedPath: string;\n\n // Top-level jsonl — the resumability source\n /** Full path to the top-level <project>/<uuid>.jsonl (may not exist). */\n topLevelPath: string;\n /** Number of \"system\" lines in the top-level jsonl. */\n topLevelSystemLines: number;\n /** Size of the top-level jsonl in bytes. */\n topLevelSize: number;\n /**\n * True iff the top-level jsonl has at least one \"system\" line.\n * This is the canonical resumability gate — matches Claude Code's --resume behaviour.\n */\n resumable: boolean;\n /** Categorised display status. */\n sessionStatus: SessionStatus;\n\n // Sessions/ transcript data (best-effort, may not exist)\n /** Full path to sessions/<uuid>.jsonl, if present. */\n sessionJsonlPath?: string;\n /** Number of \"user\" type lines in the transcript. */\n userLines: number;\n /** Last user message text (truncated to 80 chars). */\n lastUserPrompt: string;\n /** Total line count in the transcript file. */\n msgCount: number;\n /** Auto-generated title from the last ai-title line in the transcript. */\n aiTitle?: string;\n\n /** mtime in epoch ms — top-level file mtime (primary), transcript mtime (fallback). */\n mtime: number;\n\n /**\n * Human-readable friendly name (for display and /Name restoration).\n * Priority: clc session.json name → ai-title → project dir basename.\n * NEVER slugified — preserves original spaces and capitalisation.\n */\n friendlyName?: string;\n /**\n * Literal directory from the clc session registry for this UUID.\n * May contain symlinks and emoji (e.g. /Users/foo/dev/... or .../🧠 Vault/...).\n * When present, realpathSync(clcDirectory) gives the correct cwd for --resume.\n */\n clcDirectory?: string;\n /** Project root path from PAI registry (authoritative cwd for resume). */\n registryRootPath?: string;\n}\n\n// ---------------------------------------------------------------------------\n// clc session registry (~/.claude/session.json)\n// ---------------------------------------------------------------------------\n\nconst CLC_SESSIONS_FILE = join(homedir(), \".claude\", \"session.json\");\n\ninterface ClcEntry {\n name?: string;\n resume?: string;\n session?: string;\n directory?: string;\n}\n\ninterface ClcRegistry {\n sessions?: ClcEntry[];\n}\n\nexport interface ClcInfo {\n name: string;\n /** Literal directory from clc registry (may contain symlinks, emoji, spaces). */\n directory?: string;\n}\n\n/** Load clc's session registry → uuid → ClcInfo map. Verbatim, never slugified. */\nfunction buildClcInfoMap(): Map<string, ClcInfo> {\n const map = new Map<string, ClcInfo>();\n try {\n const raw = readFileSync(CLC_SESSIONS_FILE, \"utf8\");\n const data = JSON.parse(raw) as ClcRegistry;\n for (const entry of data.sessions ?? []) {\n const name = entry.name?.trim();\n const uuid = (entry.resume ?? entry.session ?? \"\").trim();\n if (name && uuid) {\n map.set(uuid, { name, directory: entry.directory?.trim() || undefined });\n }\n }\n } catch {\n // Missing or malformed — not fatal\n }\n return map;\n}\n\n// ---------------------------------------------------------------------------\n// PAI registry\n// ---------------------------------------------------------------------------\n\ninterface RegistryProject {\n root_path: string;\n encoded_dir: string;\n}\n\n/** Build encoded_dir → root_path map from PAI registry (authoritative cwd). */\nfunction buildRegistryRootPathMap(db: Database): Map<string, string> {\n try {\n const rows = db\n .prepare(\n `SELECT root_path, encoded_dir FROM projects\n WHERE encoded_dir IS NOT NULL AND encoded_dir != ''`\n )\n .all() as RegistryProject[];\n const map = new Map<string, string>();\n for (const row of rows) {\n map.set(row.encoded_dir, row.root_path);\n }\n return map;\n } catch {\n return new Map();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Top-level jsonl parser\n// ---------------------------------------------------------------------------\n\ninterface TopLevelInfo {\n systemLines: number;\n size: number;\n mtime: number;\n}\n\nfunction parseTopLevel(filePath: string): TopLevelInfo {\n let systemLines = 0;\n let size = 0;\n let mtime = 0;\n try {\n const st = statSync(filePath);\n size = st.size;\n mtime = st.mtimeMs;\n const content = readFileSync(filePath, \"utf8\");\n for (const line of content.split(\"\\n\")) {\n const t = line.trim();\n if (!t) continue;\n if (t.includes('\"type\":\"system\"') || t.includes('\"type\": \"system\"')) {\n systemLines++;\n }\n }\n } catch {\n /* ignore */\n }\n return { systemLines, size, mtime };\n}\n\n// ---------------------------------------------------------------------------\n// Sessions/ transcript parser\n// ---------------------------------------------------------------------------\n\ninterface TranscriptInfo {\n userLines: number;\n lastUserPrompt: string;\n msgCount: number;\n aiTitle?: string;\n mtime: number;\n}\n\nfunction parseTranscript(filePath: string): TranscriptInfo {\n let userLines = 0;\n let lastUserPrompt = \"\";\n let msgCount = 0;\n let aiTitle: string | undefined;\n let mtime = 0;\n\n try {\n mtime = statSync(filePath).mtimeMs;\n } catch {\n return { userLines: 0, lastUserPrompt: \"\", msgCount: 0, mtime: 0 };\n }\n\n let content: string;\n try {\n content = readFileSync(filePath, \"utf8\");\n } catch {\n return { userLines: 0, lastUserPrompt: \"\", msgCount: 0, mtime };\n }\n\n for (const line of content.split(\"\\n\")) {\n const t = line.trim();\n if (!t) continue;\n msgCount++;\n\n const hasUser = t.includes('\"type\":\"user\"') || t.includes('\"type\": \"user\"');\n const hasTitle =\n t.includes('\"type\":\"ai-title\"') || t.includes('\"type\": \"ai-title\"');\n if (!hasUser && !hasTitle) continue;\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(t) as Record<string, unknown>;\n } catch {\n continue;\n }\n\n if (parsed.type === \"ai-title\") {\n const v = parsed.title ?? parsed.name;\n if (typeof v === \"string\" && v.trim()) aiTitle = v.trim();\n continue;\n }\n\n if (parsed.type !== \"user\") continue;\n userLines++;\n\n const msg = parsed.message as Record<string, unknown> | undefined;\n if (!msg) continue;\n const c = msg.content;\n let text = \"\";\n if (typeof c === \"string\") {\n text = c;\n } else if (Array.isArray(c) && c.length > 0) {\n const first = c[0] as Record<string, unknown>;\n if (typeof first.text === \"string\") text = first.text;\n }\n if (text) lastUserPrompt = text.slice(0, 80).replace(/\\n/g, \" \");\n }\n\n return { userLines, lastUserPrompt, msgCount, aiTitle, mtime };\n}\n\n// ---------------------------------------------------------------------------\n// Main scanner\n// ---------------------------------------------------------------------------\n\nexport interface ScanOptions {\n /** Maximum results to return after sorting (default: 200). */\n limit?: number;\n /**\n * Filter mode:\n * \"named\" (default) — resumable OR in clc registry (named sessions the user cares about)\n * \"all\" — everything including unnamed orphans\n * \"resumable\" — only sessions claude --resume accepts (top-level + system lines)\n */\n filter?: \"named\" | \"all\" | \"resumable\";\n /**\n * @deprecated Use filter instead. When true → filter:\"resumable\", false → filter:\"named\".\n * Kept for backwards compatibility with callers that predate the filter field.\n */\n resumableOnly?: boolean;\n}\n\nconst CLAUDE_PROJECTS_DIR = join(homedir(), \".claude\", \"projects\");\n\n// UUID format: 8-4-4-4-12 hex digits\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Compute the effective filter mode, resolving the legacy resumableOnly flag.\n */\nfunction resolveFilter(opts: ScanOptions): \"named\" | \"all\" | \"resumable\" {\n if (opts.filter) return opts.filter;\n if (opts.resumableOnly === true) return \"resumable\";\n if (opts.resumableOnly === false) return \"all\";\n return \"named\"; // default\n}\n\n/**\n * Scan ~/.claude/projects/ for all Claude Code sessions.\n *\n * Pass 1: walk top-level <project>/<uuid>.jsonl files (the resumability source).\n * Pass 2: handle clc registry entries whose cached UUID was not found in Pass 1.\n * For each such entry, scan the entry's project dir for the FRESHEST session\n * and attach the registry name to it. This fixes the stale-UUID bug where\n * clc's session.json points to an old uuid after a fresh start.\n * Pass 3: any remaining clc entries with truly no top-level jsonl → transcript-only.\n *\n * Filter modes:\n * \"named\" — resumable + registry-known stubs + transcript-only (default)\n * \"all\" — all of the above + unnamed orphans from sessions/ subdirs\n * \"resumable\" — only sessions claude --resume accepts\n *\n * Returns results sorted by mtime descending.\n */\nexport function scanSessions(\n db: Database,\n opts: ScanOptions = {}\n): ScannedSession[] {\n const limit = opts.limit ?? 200;\n const filterMode = resolveFilter(opts);\n\n if (!existsSync(CLAUDE_PROJECTS_DIR)) return [];\n\n const clcInfoMap = buildClcInfoMap();\n const rootPathMap = buildRegistryRootPathMap(db);\n const results: ScannedSession[] = [];\n // Track which UUIDs we've already added (from the jsonl walk)\n const seenUuids = new Set<string>();\n // Track which clc names we've already attached to a session\n const attachedClcUuids = new Set<string>();\n\n let encodedDirs: string[];\n try {\n encodedDirs = readdirSync(CLAUDE_PROJECTS_DIR);\n } catch {\n return [];\n }\n\n // Build a map: encodedDir → all top-level sessions found in Pass 1\n // This is used in Pass 2 to find the freshest session for a clc registry entry.\n const sessionsByEncodedDir = new Map<string, ScannedSession[]>();\n\n // ---- Pass 1: walk top-level <project>/<uuid>.jsonl files ----\n for (const encodedDir of encodedDirs) {\n const projectDir = join(CLAUDE_PROJECTS_DIR, encodedDir);\n try {\n if (!statSync(projectDir).isDirectory()) continue;\n } catch {\n continue;\n }\n\n let files: string[];\n try {\n files = readdirSync(projectDir);\n } catch {\n continue;\n }\n\n const decodedPath =\n smartDecodeDir(encodedDir) ?? encodedDir.replace(/-/g, \"/\");\n const registryRootPath = rootPathMap.get(encodedDir);\n const projectBasename = basename(decodedPath);\n\n for (const file of files) {\n if (!file.endsWith(\".jsonl\")) continue;\n const uuid = file.slice(0, -6);\n if (!UUID_RE.test(uuid)) continue;\n\n const topLevelPath = join(projectDir, file);\n const topInfo = parseTopLevel(topLevelPath);\n const resumable = topInfo.systemLines > 0;\n const clcInfo = clcInfoMap.get(uuid);\n const inRegistry = !!clcInfo;\n\n // Determine status\n const sessionStatus: SessionStatus = resumable\n ? \"resumable\"\n : inRegistry\n ? \"stub\"\n : \"orphan\";\n\n // Apply filter — but include ALL in this pass so Pass 2 can use them\n // We'll filter again when deciding what to add to `results`.\n const passesFilter =\n filterMode === \"resumable\"\n ? resumable\n : filterMode === \"named\"\n ? resumable || inRegistry\n : true;\n\n // Join with sessions/ transcript (best-effort)\n const sessionJsonlPath = join(projectDir, \"sessions\", `${uuid}.jsonl`);\n const hasTranscript = existsSync(sessionJsonlPath);\n const transcript: TranscriptInfo = hasTranscript\n ? parseTranscript(sessionJsonlPath)\n : { userLines: 0, lastUserPrompt: \"\", msgCount: 0, mtime: 0 };\n\n // Use top-level mtime as primary\n const mtime = topInfo.mtime || transcript.mtime;\n\n // Name resolution: clc registry (verbatim) → ai-title → project basename\n const friendlyName =\n clcInfo?.name ?? transcript.aiTitle ?? projectBasename ?? undefined;\n\n const session: ScannedSession = {\n uuid,\n shortId: uuid.slice(0, 8),\n encodedDir,\n decodedPath,\n topLevelPath,\n topLevelSystemLines: topInfo.systemLines,\n topLevelSize: topInfo.size,\n resumable,\n sessionStatus,\n sessionJsonlPath: hasTranscript ? sessionJsonlPath : undefined,\n userLines: transcript.userLines,\n lastUserPrompt: transcript.lastUserPrompt,\n msgCount: transcript.msgCount,\n aiTitle: transcript.aiTitle,\n mtime,\n friendlyName,\n clcDirectory: clcInfo?.directory,\n registryRootPath,\n };\n\n // Track in per-dir map for Pass 2 lookups (all sessions, regardless of filter)\n if (!sessionsByEncodedDir.has(encodedDir)) {\n sessionsByEncodedDir.set(encodedDir, []);\n }\n sessionsByEncodedDir.get(encodedDir)!.push(session);\n\n seenUuids.add(uuid);\n if (inRegistry) attachedClcUuids.add(uuid);\n\n if (passesFilter) {\n results.push(session);\n }\n }\n }\n\n // ---- Pass 2: clc registry entries whose cached UUID wasn't found as a top-level file ----\n // For each such entry, find the project's encodedDir and look for the FRESHEST\n // resumable session there. Attach the clc name to it.\n // This fixes the stale-UUID bug: clc may store a uuid that's no longer valid,\n // but the project dir has a new active session the user is actually in.\n if (filterMode !== \"resumable\") {\n for (const [cachedUuid, clcInfo] of clcInfoMap) {\n if (attachedClcUuids.has(cachedUuid)) continue; // already named in Pass 1\n\n // Derive the encodedDir from clcInfo.directory\n let foundEncodedDir: string | undefined;\n if (clcInfo.directory) {\n const real = realpathSyncSafe(clcInfo.directory);\n if (real) {\n const encoded = encodeProjectDir(real);\n if (existsSync(join(CLAUDE_PROJECTS_DIR, encoded))) {\n foundEncodedDir = encoded;\n }\n }\n }\n\n // Fallback: check if the cached uuid exists as a sessions/ transcript somewhere\n if (!foundEncodedDir) {\n for (const encodedDir of encodedDirs) {\n if (existsSync(join(CLAUDE_PROJECTS_DIR, encodedDir, \"sessions\", `${cachedUuid}.jsonl`))) {\n foundEncodedDir = encodedDir;\n break;\n }\n }\n }\n\n if (foundEncodedDir) {\n const dirSessions = sessionsByEncodedDir.get(foundEncodedDir) ?? [];\n\n // Find the freshest RESUMABLE session in this dir that doesn't yet have a name\n const freshestResumable = dirSessions\n .filter((s) => s.resumable && !s.friendlyName)\n .sort((a, b) => b.mtime - a.mtime)[0];\n\n if (freshestResumable) {\n // Attach the clc name to this fresher session\n freshestResumable.friendlyName = clcInfo.name;\n freshestResumable.clcDirectory =\n freshestResumable.clcDirectory ?? clcInfo.directory;\n freshestResumable.sessionStatus = \"resumable\";\n attachedClcUuids.add(freshestResumable.uuid);\n\n // Add to results if not already there (it might have been filtered out as orphan)\n if (!seenUuids.has(freshestResumable.uuid) || !results.includes(freshestResumable)) {\n // It was added to sessionsByEncodedDir but might have been filtered from results\n if (!results.includes(freshestResumable)) {\n results.push(freshestResumable);\n }\n }\n continue; // Done with this clc entry\n }\n\n // No unnamed resumable session found. The project might have resumable sessions\n // that already have a name (different name). Or only stubs/orphans.\n // Fall through to transcript-only handling below.\n }\n\n // ---- Pass 3: truly no top-level jsonl → transcript-only ----\n if (!foundEncodedDir) {\n // We couldn't find the project dir at all\n const encodedDir = \"\";\n const decodedPath = clcInfo.directory ?? cachedUuid;\n const registryRootPath = undefined;\n\n const transcript: TranscriptInfo = { userLines: 0, lastUserPrompt: \"\", msgCount: 0, mtime: 0 };\n const mtime = transcript.mtime;\n\n seenUuids.add(cachedUuid);\n results.push({\n uuid: cachedUuid,\n shortId: cachedUuid.slice(0, 8),\n encodedDir,\n decodedPath,\n topLevelPath: \"\",\n topLevelSystemLines: 0,\n topLevelSize: 0,\n resumable: false,\n sessionStatus: \"transcript-only\",\n sessionJsonlPath: undefined,\n userLines: 0,\n lastUserPrompt: \"\",\n msgCount: 0,\n mtime,\n friendlyName: clcInfo.name,\n clcDirectory: clcInfo.directory,\n registryRootPath,\n });\n continue;\n }\n\n // Project dir was found but no unnamed resumable session.\n // Check sessions/ for the cached uuid transcript.\n const foundTranscriptPath = existsSync(\n join(CLAUDE_PROJECTS_DIR, foundEncodedDir, \"sessions\", `${cachedUuid}.jsonl`)\n )\n ? join(CLAUDE_PROJECTS_DIR, foundEncodedDir, \"sessions\", `${cachedUuid}.jsonl`)\n : undefined;\n\n const decodedPath =\n clcInfo.directory ??\n (smartDecodeDir(foundEncodedDir) ?? foundEncodedDir.replace(/-/g, \"/\"));\n const registryRootPath = rootPathMap.get(foundEncodedDir);\n const topLevelPath = join(\n CLAUDE_PROJECTS_DIR,\n foundEncodedDir,\n `${cachedUuid}.jsonl`\n );\n\n const transcript: TranscriptInfo = foundTranscriptPath\n ? parseTranscript(foundTranscriptPath)\n : { userLines: 0, lastUserPrompt: \"\", msgCount: 0, mtime: 0 };\n\n seenUuids.add(cachedUuid);\n results.push({\n uuid: cachedUuid,\n shortId: cachedUuid.slice(0, 8),\n encodedDir: foundEncodedDir,\n decodedPath,\n topLevelPath,\n topLevelSystemLines: 0,\n topLevelSize: 0,\n resumable: false,\n sessionStatus: \"transcript-only\",\n sessionJsonlPath: foundTranscriptPath,\n userLines: transcript.userLines,\n lastUserPrompt: transcript.lastUserPrompt,\n msgCount: transcript.msgCount,\n aiTitle: transcript.aiTitle,\n mtime: transcript.mtime,\n friendlyName: clcInfo.name,\n clcDirectory: clcInfo.directory,\n registryRootPath,\n });\n }\n }\n\n results.sort((a, b) => b.mtime - a.mtime);\n return results.slice(0, limit);\n}\n\n// ---------------------------------------------------------------------------\n// Path encoding helpers\n// ---------------------------------------------------------------------------\n\n/** Claude Code's project-dir encoding: replace / . - (space) with - */\nfunction encodeProjectDir(realPath: string): string {\n // Leading slash becomes leading -; all / . - and space → -\n return realPath.replace(/[/.\\- ]/g, \"-\");\n}\n\n/** realpathSync that returns null instead of throwing */\nfunction realpathSyncSafe(p: string): string | null {\n try {\n return realpathSync(p);\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Age formatter\n// ---------------------------------------------------------------------------\n\nexport function fmtAge(mtime: number): string {\n const diffMs = Date.now() - mtime;\n const diffMin = Math.floor(diffMs / 60_000);\n const diffHr = Math.floor(diffMs / 3_600_000);\n const diffDay = Math.floor(diffMs / 86_400_000);\n\n if (diffMin < 60) return `${diffMin}m`;\n if (diffHr < 24) return `${diffHr}h`;\n if (diffDay < 30) return `${diffDay}d`;\n return `${Math.floor(diffDay / 30)}mo`;\n}\n\n// ---------------------------------------------------------------------------\n// Resolve a name/id/prefix to a session entry\n// ---------------------------------------------------------------------------\n\nexport interface ResolvedSession {\n session: ScannedSession;\n /** The friendly name to use verbatim in the /Name command. Never slugified. */\n friendlyName?: string;\n}\n\n/**\n * Filesystem-level UUID scan: walk ALL ~/.claude/projects/<encoded-dir>/<uuid>.jsonl\n * looking for top-level files whose UUID starts with `prefix`.\n *\n * Returns a ScannedSession-like object (minimal fields) for each match, or an empty\n * array if nothing is found. This is used as a fallback when the regular catalog\n * (limited to named/recent sessions) doesn't contain the requested UUID.\n *\n * Complexity: O(number of project directories + files per dir). Typically <200 dirs\n * with a handful of top-level jsonl each — fast enough for an interactive CLI.\n */\nfunction scanFilesystemForUuidPrefix(prefix: string): ScannedSession[] {\n if (!existsSync(CLAUDE_PROJECTS_DIR)) return [];\n\n const prefixLower = prefix.toLowerCase();\n const matches: ScannedSession[] = [];\n\n let encodedDirs: string[];\n try {\n encodedDirs = readdirSync(CLAUDE_PROJECTS_DIR);\n } catch {\n return [];\n }\n\n for (const encodedDir of encodedDirs) {\n const projectDir = join(CLAUDE_PROJECTS_DIR, encodedDir);\n try {\n if (!statSync(projectDir).isDirectory()) continue;\n } catch {\n continue;\n }\n\n let files: string[];\n try {\n files = readdirSync(projectDir);\n } catch {\n continue;\n }\n\n for (const file of files) {\n if (!file.endsWith(\".jsonl\")) continue;\n const uuid = file.slice(0, -6);\n if (!UUID_RE.test(uuid)) continue;\n if (!uuid.toLowerCase().startsWith(prefixLower)) continue;\n\n const topLevelPath = join(projectDir, file);\n const topInfo = parseTopLevel(topLevelPath);\n const resumable = topInfo.systemLines > 0;\n const decodedPath = smartDecodeDir(encodedDir) ?? encodedDir.replace(/-/g, \"/\");\n\n // Check sessions/ transcript for prompt and title\n const sessionJsonlPath = join(projectDir, \"sessions\", `${uuid}.jsonl`);\n const hasTranscript = existsSync(sessionJsonlPath);\n const transcript: TranscriptInfo = hasTranscript\n ? parseTranscript(sessionJsonlPath)\n : { userLines: 0, lastUserPrompt: \"\", msgCount: 0, mtime: 0 };\n\n matches.push({\n uuid,\n shortId: uuid.slice(0, 8),\n encodedDir,\n decodedPath,\n topLevelPath,\n topLevelSystemLines: topInfo.systemLines,\n topLevelSize: topInfo.size,\n resumable,\n sessionStatus: resumable ? \"resumable\" : \"stub\",\n sessionJsonlPath: hasTranscript ? sessionJsonlPath : undefined,\n userLines: transcript.userLines,\n lastUserPrompt: transcript.lastUserPrompt,\n msgCount: transcript.msgCount,\n aiTitle: transcript.aiTitle,\n mtime: topInfo.mtime || transcript.mtime,\n friendlyName: transcript.aiTitle ?? basename(decodedPath),\n clcDirectory: undefined,\n registryRootPath: undefined,\n });\n }\n }\n\n return matches.sort((a, b) => b.mtime - a.mtime);\n}\n\n/**\n * Resolve a name-or-id-or-prefix to a single ScannedSession.\n *\n * Comparisons are case-insensitive; stored casing is preserved in output.\n *\n * Priority:\n * 1. Exact case-insensitive match on friendlyName\n * 2. Partial case-insensitive match (contains)\n * 3. UUID prefix match against the in-memory catalog\n * 4. UUID prefix match against the full filesystem (fallback for any session)\n */\nexport function resolveSessionByNameOrId(\n sessions: ScannedSession[],\n query: string\n): ResolvedSession {\n const qLower = query.toLowerCase().trim();\n\n // 1. Exact match (case-insensitive, preserve stored casing)\n const byExact = sessions.filter(\n (s) => s.friendlyName && s.friendlyName.toLowerCase() === qLower\n );\n if (byExact.length >= 1) {\n return { session: byExact[0], friendlyName: byExact[0].friendlyName };\n }\n\n // 2. Partial match\n const byPartial = sessions.filter(\n (s) => s.friendlyName && s.friendlyName.toLowerCase().includes(qLower)\n );\n if (byPartial.length === 1) {\n return { session: byPartial[0], friendlyName: byPartial[0].friendlyName };\n }\n if (byPartial.length > 1) {\n const candidates = byPartial\n .slice(0, 5)\n .map(\n (s, i) =>\n ` ${i + 1}. ${s.shortId} ${s.friendlyName ?? s.decodedPath} (${fmtAge(s.mtime)} ago)`\n )\n .join(\"\\n\");\n throw new Error(\n `Ambiguous name \"${query}\" — ${byPartial.length} matches:\\n${candidates}\\n\\nBe more specific or use a UUID prefix.`\n );\n }\n\n // 3. UUID prefix against catalog\n const byUuid = sessions.filter((s) => s.uuid.startsWith(qLower));\n if (byUuid.length === 1) {\n return { session: byUuid[0], friendlyName: byUuid[0].friendlyName };\n }\n if (byUuid.length > 1) {\n const candidates = byUuid\n .slice(0, 5)\n .map(\n (s, i) =>\n ` ${i + 1}. ${s.shortId} ${s.friendlyName ?? s.decodedPath} (${fmtAge(s.mtime)} ago)`\n )\n .join(\"\\n\");\n throw new Error(\n `UUID prefix \"${query}\" is ambiguous — ${byUuid.length} matches:\\n${candidates}\\n\\nProvide more characters.`\n );\n }\n\n // 4. Filesystem fallback — scan ALL ~/.claude/projects/*/<uuid>.jsonl files\n // This handles any session UUID, even if not in the named/recent catalog.\n // Only attempted when the query looks like a UUID prefix (hex + dashes).\n const UUID_PREFIX_RE = /^[0-9a-f-]{4,36}$/i;\n if (UUID_PREFIX_RE.test(qLower)) {\n const fsSessions = scanFilesystemForUuidPrefix(qLower);\n if (fsSessions.length === 1) {\n return { session: fsSessions[0], friendlyName: fsSessions[0].friendlyName };\n }\n if (fsSessions.length > 1) {\n const candidates = fsSessions\n .slice(0, 5)\n .map(\n (s, i) =>\n ` ${i + 1}. ${s.shortId} ${s.friendlyName ?? s.decodedPath} (${fmtAge(s.mtime)} ago)`\n )\n .join(\"\\n\");\n throw new Error(\n `UUID prefix \"${query}\" is ambiguous — ${fsSessions.length} matches:\\n${candidates}\\n\\nProvide more characters.`\n );\n }\n }\n\n throw new Error(\n `No session found matching \"${query}\".\\n\\nRun: pai sessions to list sessions.\\nRun: pai sessions --all to include transcript-only sessions.\\nRun: pai find <words> to search prompt history.`\n );\n}\n","/**\n * aibroker-client.ts — Lightweight IPC client for AIBroker daemon.\n *\n * Connects to the AIBroker Unix Domain Socket, sends a JSON-RPC request,\n * and reads a single newline-terminated JSON response. No class needed —\n * just a thin async function matching the WatcherClient protocol.\n *\n * Socket path: /tmp/aibroker.sock (default; override via AIBROKER_SOCKET env).\n */\n\nimport { connect } from \"node:net\";\nimport { randomUUID } from \"node:crypto\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Lightweight session metadata returned by the AIBroker `sessions` IPC method.\n * Does NOT include scrollback content — use `session_content` for that.\n */\nexport interface AiBrokerSessionMeta {\n index: number;\n sessionId: string;\n /** iTerm2 tab title or profile name */\n name: string;\n /** PAI session name set via /Name; null for bare shells */\n paiName: string | null;\n atPrompt: boolean;\n /** \"claude\" for Claude Code panes, \"shell\" for bare terminals */\n kind: \"claude\" | \"shell\";\n /** Whether this is the currently focused pane */\n active: boolean;\n}\n\ninterface AiBrokerSessionsResult {\n sessions: AiBrokerSessionMeta[];\n}\n\n// ---------------------------------------------------------------------------\n// Core call\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_SOCKET = process.env.AIBROKER_SOCKET ?? \"/tmp/aibroker.sock\";\n\n/**\n * Call an AIBroker IPC method and return the result.\n *\n * Resolves with the `result` field of a successful response.\n * Rejects if the socket is not available, the call times out, or the\n * daemon returns an error.\n *\n * @param method IPC method name (e.g. \"session_content\", \"send_to_session\")\n * @param params Method parameters object\n * @param timeoutMs Connection + response timeout in milliseconds (default: 8 000)\n */\nexport function callAiBroker(\n method: string,\n params: Record<string, unknown> = {},\n timeoutMs = 8_000\n): Promise<Record<string, unknown>> {\n return new Promise((resolve, reject) => {\n const socketPath = DEFAULT_SOCKET;\n let done = false;\n let buffer = \"\";\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n function finish(err: Error | null, value?: Record<string, unknown>): void {\n if (done) return;\n done = true;\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n try {\n socket.destroy();\n } catch {\n /* ignore */\n }\n if (err) reject(err);\n else resolve(value!);\n }\n\n const socket = connect(socketPath, () => {\n const request = {\n id: randomUUID(),\n sessionId: process.env.TERM_SESSION_ID ?? \"pai-cli\",\n method,\n params,\n };\n const itermId = process.env.ITERM_SESSION_ID;\n if (itermId) Object.assign(request, { itermSessionId: itermId });\n socket.write(JSON.stringify(request) + \"\\n\");\n });\n\n socket.on(\"data\", (chunk: Buffer) => {\n buffer += chunk.toString(\"utf8\");\n const nl = buffer.indexOf(\"\\n\");\n if (nl === -1) return;\n const line = buffer.slice(0, nl);\n\n let response: { ok: boolean; result?: Record<string, unknown>; error?: string };\n try {\n response = JSON.parse(line);\n } catch {\n finish(new Error(`AIBroker IPC parse error: ${line.slice(0, 120)}`));\n return;\n }\n\n if (!response.ok) {\n finish(new Error(response.error ?? \"AIBroker IPC call failed\"));\n } else {\n finish(null, response.result ?? {});\n }\n });\n\n socket.on(\"error\", (e: NodeJS.ErrnoException) => {\n if (e.code === \"ENOENT\" || e.code === \"ECONNREFUSED\") {\n finish(new Error(\"AIBroker not running (socket not found).\"));\n } else {\n finish(e);\n }\n });\n\n socket.on(\"end\", () => {\n if (!done) finish(new Error(\"AIBroker IPC connection closed before response.\"));\n });\n\n timer = setTimeout(\n () => finish(new Error(\"AIBroker IPC call timed out.\")),\n timeoutMs\n );\n });\n}\n\n// ---------------------------------------------------------------------------\n// Typed helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Fetch all live iTerm2 session metadata from AIBroker via the `sessions` method.\n * Returns an empty array if AIBroker is not running.\n *\n * This is metadata-only (no scrollback). It is faster than `session_content`\n * and the correct source for listing/routing purposes.\n */\nexport async function fetchLiveSessions(): Promise<AiBrokerSessionMeta[]> {\n try {\n const result = await callAiBroker(\"sessions\", {});\n const sessions = (result as unknown as AiBrokerSessionsResult).sessions;\n if (!Array.isArray(sessions)) return [];\n return sessions;\n } catch {\n return [];\n }\n}\n\n/**\n * Send text to a specific AIBroker session by its iTerm2 sessionId.\n */\nexport async function sendToSession(\n sessionId: string,\n text: string\n): Promise<{ ok: boolean; error?: string }> {\n try {\n await callAiBroker(\"send_to_session\", { sessionId, text });\n return { ok: true };\n } catch (e) {\n return { ok: false, error: String(e) };\n }\n}\n","/**\n * pai session recent [-n N] [--all] [--json]\n *\n * List named sessions — resumable OR in the clc registry.\n * Default: \"named\" filter (resumable + stubs + transcript-only from clc registry).\n * --all: also include unnamed orphan sessions (only in sessions/ subdir, not in registry).\n *\n * Status column (always shown):\n * resumable — top-level jsonl with system snapshot (claude --resume works) — green\n * stub — in registry, top-level exists but no system lines (Ctrl+C exit) — yellow\n * transcript-only — in registry but no top-level jsonl — yellow\n * orphan — not in registry, only transcript exists — dim (--all only)\n *\n * When AIBroker is running, a \"Live Sessions\" section is shown first,\n * listing currently-active iTerm2 panes that have an active Claude session.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport chalk from \"chalk\";\nimport { renderTable, err, dim, header } from \"../../utils.js\";\nimport { scanSessions, fmtAge, type SessionStatus } from \"../../lib/session-scan.js\";\nimport { fetchLiveSessions, type AiBrokerSessionMeta } from \"../../lib/aibroker-client.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction fmtSize(bytes: number): string {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1_048_576) return `${Math.round(bytes / 1024)}K`;\n return `${(bytes / 1_048_576).toFixed(1)}M`;\n}\n\nfunction fmtStatus(status: SessionStatus): string {\n switch (status) {\n case \"resumable\":\n return chalk.green(\"resumable\");\n case \"stub\":\n return chalk.yellow(\"stub\");\n case \"transcript-only\":\n return chalk.yellow(\"transcript-only\");\n case \"orphan\":\n return chalk.dim(\"orphan\");\n }\n}\n\n// ---------------------------------------------------------------------------\n// Live-sessions rendering (AIBroker integration)\n// ---------------------------------------------------------------------------\n\nfunction renderLiveSessions(allLiveSessions: AiBrokerSessionMeta[], allTabs: boolean): void {\n // Default: Claude sessions only (kind === \"claude\"). --all-tabs includes shells.\n const displayed = allTabs\n ? allLiveSessions\n : allLiveSessions.filter((s) => s.kind !== \"shell\");\n const skippedCount = allLiveSessions.length - displayed.length;\n\n if (displayed.length === 0) return;\n\n console.log(\"\\n\" + header(\"Live Sessions\") + \"\\n\");\n\n const liveHeaders = [\"#\", \"id\", \"name\", \"at prompt\", \"kind\"];\n const liveRows = displayed.map((s, idx) => {\n const shortId = s.sessionId.slice(0, 8);\n // Prefer paiName for Claude sessions, fall back to iTerm2 tab name\n const rawName = s.paiName ?? s.name;\n const name = rawName.length > 36 ? rawName.slice(0, 35) + \"…\" : rawName;\n const atPrompt = s.atPrompt ? chalk.green(\"yes\") : chalk.yellow(\"busy\");\n const kind = s.kind === \"claude\"\n ? chalk.cyan(s.kind)\n : chalk.dim(s.kind);\n\n return [\n chalk.dim(String(idx + 1)),\n chalk.cyan(shortId),\n name,\n atPrompt,\n kind,\n ];\n });\n\n console.log(renderTable(liveHeaders, liveRows));\n\n if (skippedCount > 0) {\n console.log(\n dim(` (${skippedCount} shell tab${skippedCount === 1 ? \"\" : \"s\"} hidden — use --all-tabs to show)`)\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport async function cmdRecent(\n db: Database,\n opts: { n?: string; all?: boolean; allTabs?: boolean; json?: boolean }\n): Promise<void> {\n const limit = parseInt(opts.n ?? \"20\", 10);\n const includeAll = opts.all === true;\n const allTabs = opts.allTabs === true;\n\n // Fetch live sessions from AIBroker (silently no-ops if not running).\n const liveSessions = await fetchLiveSessions();\n\n const sessions = scanSessions(db, {\n limit,\n filter: includeAll ? \"all\" : \"named\",\n });\n\n if (opts.json) {\n const output = {\n live: liveSessions.map((s) => ({\n index: s.index,\n sessionId: s.sessionId,\n name: s.name,\n paiName: s.paiName,\n atPrompt: s.atPrompt,\n kind: s.kind,\n active: s.active,\n })),\n paused: sessions.map((s, idx) => ({\n idx: idx + 1,\n uuid: s.uuid,\n shortId: s.shortId,\n resumable: s.resumable,\n sessionStatus: s.sessionStatus,\n age: fmtAge(s.mtime),\n mtime: s.mtime,\n name: s.friendlyName,\n lastUserPrompt: s.lastUserPrompt,\n userLines: s.userLines,\n msgCount: s.msgCount,\n topLevelSize: s.topLevelSize,\n decodedPath: s.decodedPath,\n })),\n };\n console.log(JSON.stringify(output, null, 2));\n return;\n }\n\n // ── Live section ──────────────────────────────────────────────────────────\n const hasClaudeLive = liveSessions.some((s) => s.kind === \"claude\");\n if (liveSessions.length > 0) {\n renderLiveSessions(liveSessions, allTabs);\n }\n\n // ── Paused / disk-scan section ────────────────────────────────────────────\n if (sessions.length === 0) {\n if (!hasClaudeLive) {\n // Nothing at all\n if (includeAll) {\n console.log(err(\"No sessions found in ~/.claude/projects/.\"));\n } else {\n console.log(\n err(\n \"No named sessions found.\\n\\n\" +\n \" Named sessions appear when you have entries in ~/.claude/session.json\\n\" +\n \" (set via /Name inside Claude Code) or resumable top-level jsonl files.\\n\" +\n \" Run: pai session recent --all to list all sessions including unnamed orphans.\"\n )\n );\n }\n }\n // If we had live Claude sessions, nothing more to print.\n return;\n }\n\n const title = includeAll\n ? \"Paused / All Sessions (named + orphans)\"\n : \"Paused / Resumable Sessions\";\n console.log(\"\\n\" + header(title) + \"\\n\");\n\n const headers = [\"#\", \"id\", \"age\", \"name / project\", \"last prompt\", \"msgs\", \"size\", \"status\"];\n\n const rows = sessions.map((s, idx) => {\n const name = s.friendlyName ?? s.decodedPath;\n const prompt = s.lastUserPrompt\n ? chalk.dim(\n s.lastUserPrompt.length > 40\n ? s.lastUserPrompt.slice(0, 39) + \"…\"\n : s.lastUserPrompt\n )\n : dim(\"—\");\n\n return [\n chalk.dim(String(idx + 1)),\n chalk.cyan(s.shortId),\n dim(fmtAge(s.mtime)),\n name.length > 26 ? name.slice(0, 25) + \"…\" : name,\n prompt,\n s.userLines > 0 ? String(s.userLines) : dim(\"—\"),\n dim(fmtSize(s.topLevelSize)),\n fmtStatus(s.sessionStatus),\n ];\n });\n\n console.log(renderTable(headers, rows));\n\n console.log(\n \"\\n\" +\n dim(\"Go to a session: \") +\n chalk.white(\"pai resume <name>\") +\n \"\\n\" +\n (hasClaudeLive\n ? dim(\"Pause all live: \") + chalk.white(\"pai pause all\") + \"\\n\"\n : \"\") +\n (includeAll\n ? \"\"\n : dim(\"Show all sessions: \") +\n chalk.white(\"pai sessions --all\") +\n \"\\n\")\n );\n}\n","/**\n * pai resume <name> / pai sessions goto <name> [--dry-run]\n *\n * Smart session launcher with auto-fallback:\n *\n * 1. Resolve name → (uuid?, projectDir, friendlyName)\n * 2. chdir(projectDir)\n * 3. If uuid is set:\n * a. Probe: claude --resume <uuid> --print --output-format=json \"_\"\n * (timeout 5s; exits non-zero / logs \"No conversation found\" if invalid)\n * b. If probe succeeds:\n * exec claude --resume <uuid> --name \"<friendlyName>\" \"/Name <friendlyName>\\ngo\"\n * c. Else (probe failed):\n * print clear stderr line: \"Resume failed. Starting fresh session in same dir.\"\n * exec claude --name \"<friendlyName>\" \"/Name <friendlyName>\\ngo\"\n * 4. Else (no uuid known):\n * exec claude --name \"<friendlyName>\" \"/Name <friendlyName>\\ngo\"\n *\n * Why both --name AND /Name?\n * --name <friendlyName> → sets Claude Code's internal session label\n * \"/Name <friendlyName>\\ngo\" → runs the /Name slash command via AIBroker, which updates\n * iTerm tab title, statusline, and AIBroker session registry;\n * the \\ngo on the next line triggers PAI's ## Continue resume\n *\n * The probe adds ~300ms on the happy path. On the fallback path, total latency is the same\n * as a fresh start.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport { spawnSync } from \"node:child_process\";\nimport { realpathSync } from \"node:fs\";\nimport chalk from \"chalk\";\nimport { err } from \"../../utils.js\";\nimport {\n scanSessions,\n resolveSessionByNameOrId,\n fmtAge,\n type ScannedSession,\n} from \"../../lib/session-scan.js\";\n\n// ---------------------------------------------------------------------------\n// Probe helper\n// ---------------------------------------------------------------------------\n\ninterface ProbeResult {\n ok: boolean;\n reason?: string;\n}\n\n/**\n * Run claude --resume <uuid> --print --output-format=json \"_\" in the given cwd.\n * Returns ok=true if the session is resumable (exit 0 and no \"No conversation found\"\n * in stderr). Timeout: 5 seconds.\n */\nfunction probeResume(uuid: string, cwd: string): ProbeResult {\n const result = spawnSync(\n \"claude\",\n [\"--resume\", uuid, \"--print\", \"--output-format=json\", \"_\"],\n {\n cwd,\n timeout: 5_000,\n env: process.env,\n // Capture stderr for inspection; suppress stdout (tty)\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\n }\n );\n\n if (result.error) {\n return { ok: false, reason: `spawn error: ${result.error.message}` };\n }\n\n const stderr = result.stderr?.toString(\"utf8\") ?? \"\";\n\n if (\n stderr.toLowerCase().includes(\"no conversation found\") ||\n stderr.toLowerCase().includes(\"session not found\")\n ) {\n return { ok: false, reason: \"No conversation found for this UUID\" };\n }\n\n if (result.status !== 0) {\n return {\n ok: false,\n reason: `claude exited ${result.status ?? \"signal\"}${stderr ? `: ${stderr.slice(0, 120).trim()}` : \"\"}`,\n };\n }\n\n return { ok: true };\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport function cmdGoto(\n db: Database,\n query: string,\n opts: { dryRun?: boolean }\n): void {\n // ---- 1. Resolve name → session ----\n const allSessions = scanSessions(db, { limit: 500, filter: \"named\" });\n\n let resolved;\n try {\n resolved = resolveSessionByNameOrId(allSessions, query);\n } catch (resolveErr) {\n let msg = String(resolveErr).replace(/^Error: /, \"\");\n // Append pai find suggestion for \"not found\" errors\n if (msg.includes(\"No session found matching\")) {\n msg += `\\n\\nTip: pai find \"${query}\" — search prompt history by keywords`;\n }\n console.error(err(msg));\n process.exit(1);\n }\n\n const { session: matchedSession, friendlyName } = resolved;\n\n // ---- 2. Find best resumable UUID for this project ----\n let resumableUuid: string | undefined;\n let resumableSession: ScannedSession | undefined;\n\n if (matchedSession.resumable) {\n resumableUuid = matchedSession.uuid;\n resumableSession = matchedSession;\n } else if (matchedSession.encodedDir) {\n // Find the most-recently-modified resumable session in the same project dir\n const sameProject = allSessions.filter(\n (s) => s.encodedDir === matchedSession.encodedDir && s.resumable\n );\n sameProject.sort((a, b) => b.mtime - a.mtime);\n if (sameProject.length > 0) {\n resumableSession = sameProject[0];\n resumableUuid = resumableSession.uuid;\n }\n }\n\n // ---- 3. Determine project dir (realpathSync for --resume cwd correctness) ----\n const rawDir =\n matchedSession.clcDirectory ??\n matchedSession.registryRootPath ??\n matchedSession.decodedPath;\n\n let projectDir: string;\n try {\n projectDir = realpathSync(rawDir);\n } catch {\n console.error(\n err(\n `session \"${query}\": directory does not exist or cannot be resolved.\\n` +\n ` Registry says: ${rawDir}\\n` +\n ` The directory may have moved or been deleted.`\n )\n );\n process.exit(1);\n }\n\n // ---- 4. Build argv components ----\n const name = friendlyName ?? query;\n // Initial prompt: /Name sets tab/statusline via AIBroker; \\ngo triggers ## Continue\n const promptArg = `/Name ${name}\\ngo`;\n\n // ---- 5. Dry-run mode ----\n if (opts.dryRun) {\n if (resumableUuid) {\n const argvResume = `claude --resume ${resumableUuid} --name \"${name}\" \"/Name ${name}\\\\ngo\"`;\n const argvFresh = `claude --name \"${name}\" \"/Name ${name}\\\\ngo\"`;\n console.log(\n \"\\n\" + chalk.bold(\"Dry run — would probe then exec (RESUME path):\") + \"\\n\"\n );\n console.log(` cwd: ${chalk.cyan(projectDir)}`);\n console.log(` probe: claude --resume ${resumableUuid} --print --output-format=json \"_\"`);\n console.log(` argv: ${chalk.white(argvResume)}`);\n console.log(` fallback: ${chalk.yellow(argvFresh)}`);\n if (resumableSession) {\n console.log(`\\n uuid: ${resumableSession.uuid}`);\n console.log(` age: ${fmtAge(resumableSession.mtime)}`);\n console.log(` status: ${resumableSession.sessionStatus}`);\n console.log(` sys: ${resumableSession.topLevelSystemLines} system lines`);\n }\n } else {\n const argvFresh = `claude --name \"${name}\" \"/Name ${name}\\\\ngo\"`;\n console.log(\n \"\\n\" + chalk.bold(\"Dry run — would exec (FRESH path, no resumable UUID):\") + \"\\n\"\n );\n console.log(` cwd: ${chalk.cyan(projectDir)}`);\n console.log(` argv: ${chalk.white(argvFresh)}`);\n }\n console.log();\n return;\n }\n\n // ---- 6. Live execution ----\n if (resumableUuid) {\n // Probe first\n const probe = probeResume(resumableUuid, projectDir);\n\n if (probe.ok) {\n // Happy path: session is resumable\n const result = spawnSync(\n \"claude\",\n [\"--resume\", resumableUuid, \"--name\", name, promptArg],\n {\n cwd: projectDir,\n stdio: \"inherit\",\n env: process.env,\n }\n );\n if (result.error) {\n console.error(err(`Failed to launch claude: ${result.error.message}`));\n process.exit(1);\n }\n process.exit(result.status ?? 0);\n } else {\n // Fallback path\n process.stderr.write(\n chalk.yellow(\n `\\n Resume failed for ${resumableUuid.slice(0, 8)}: ${probe.reason ?? \"unknown error\"}\\n` +\n ` Starting fresh session in same directory.\\n\\n`\n )\n );\n const result = spawnSync(\n \"claude\",\n [\"--name\", name, promptArg],\n {\n cwd: projectDir,\n stdio: \"inherit\",\n env: process.env,\n }\n );\n if (result.error) {\n console.error(err(`Failed to launch claude: ${result.error.message}`));\n process.exit(1);\n }\n process.exit(result.status ?? 0);\n }\n } else {\n // No UUID at all — fresh start\n const result = spawnSync(\n \"claude\",\n [\"--name\", name, promptArg],\n {\n cwd: projectDir,\n stdio: \"inherit\",\n env: process.env,\n }\n );\n if (result.error) {\n console.error(err(`Failed to launch claude: ${result.error.message}`));\n process.exit(1);\n }\n process.exit(result.status ?? 0);\n }\n}\n","/**\n * pai session pause [--dry-run]\n *\n * Writes a \"## Continue\" checkpoint to the project's TODO.md, then prints a\n * prominent reminder about HOW to exit Claude Code safely.\n *\n * The write logic mirrors cmdHandover exactly — it finds the project by cwd,\n * finds (or creates) TODO.md, and prepends a ## Continue block. The difference\n * is that we do NOT call process.exit() after writing, so the warning is shown.\n *\n * WHY Ctrl+C is dangerous:\n * Claude Code's stop-hook fires only on a clean exit (e.g. /exit inside the\n * Claude prompt, or claude process receiving SIGTERM). When you Ctrl+C from\n * a terminal that's running claude, the stop-hook is bypassed, which means:\n * - The top-level <project>/<uuid>.jsonl does NOT get its final system snapshot.\n * - Claude Code's --resume cannot find the session (0 system lines).\n * - The session becomes orphaned — transcript exists but is unresumable.\n *\n * If the project TODO.md gets its ## Continue checkpoint (written by this\n * command), the *content* can still be recovered. But the *session context*\n * (conversation history) is lost unless you exit cleanly via /exit.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport {\n existsSync,\n readFileSync,\n writeFileSync,\n renameSync,\n mkdirSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\nimport type { ProjectRow, SessionRow } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// TODO.md discovery (same locations as handover.ts)\n// ---------------------------------------------------------------------------\n\nconst HANDOVER_TODO_LOCATIONS = [\n \"Notes/TODO.md\",\n \".claude/Notes/TODO.md\",\n \"tasks/todo.md\",\n \"TODO.md\",\n];\n\nfunction findProjectTodo(\n rootPath: string\n): { path: string; content: string } | null {\n for (const rel of HANDOVER_TODO_LOCATIONS) {\n const full = join(rootPath, rel);\n if (existsSync(full)) {\n try {\n return { path: full, content: readFileSync(full, \"utf8\") };\n } catch {\n // unreadable — try next\n }\n }\n }\n return null;\n}\n\nfunction stripContinueSection(content: string): string {\n const lines = content.split(\"\\n\");\n const startIdx = lines.findIndex((l) => l.trim() === \"## Continue\");\n if (startIdx === -1) return content;\n\n let endIdx = lines.length;\n for (let i = startIdx + 1; i < lines.length; i++) {\n const trimmed = lines[i].trim();\n if (\n trimmed === \"---\" ||\n (trimmed.startsWith(\"##\") && trimmed !== \"## Continue\")\n ) {\n endIdx = i;\n break;\n }\n }\n\n let trailingEnd = endIdx;\n if (trailingEnd < lines.length && lines[trailingEnd].trim() === \"---\") {\n trailingEnd += 1;\n }\n\n const before = lines.slice(0, startIdx);\n const after = lines.slice(trailingEnd);\n while (after.length > 0 && after[0].trim() === \"\") after.shift();\n\n return [...before, ...after].join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport function cmdPause(\n db: Database,\n opts: { dryRun?: boolean }\n): void {\n // ---- 1. Resolve project by cwd ----\n const cwd = process.cwd();\n const project = db\n .prepare(\n `SELECT id, slug, display_name, root_path, encoded_dir\n FROM projects\n WHERE ? LIKE root_path || '%'\n ORDER BY length(root_path) DESC\n LIMIT 1`\n )\n .get(cwd) as ProjectRow | undefined;\n\n if (!project) {\n console.error(\n chalk.red(\"pai session pause: \") +\n \"Current directory is not within a registered PAI project.\\n\" +\n ` cwd: ${cwd}`\n );\n process.exit(1);\n }\n\n // ---- 2. Resolve latest session ----\n const session = db\n .prepare(\n \"SELECT * FROM sessions WHERE project_id = ? ORDER BY number DESC LIMIT 1\"\n )\n .get(project.id) as SessionRow | undefined;\n\n // ---- 3. Find or create TODO.md ----\n const todo = findProjectTodo(project.root_path);\n let todoPath: string;\n let existingContent: string;\n\n if (todo) {\n todoPath = todo.path;\n existingContent = todo.content;\n } else {\n const notesDir = join(project.root_path, \"Notes\");\n if (!opts.dryRun) {\n try {\n if (!existsSync(notesDir)) mkdirSync(notesDir, { recursive: true });\n } catch (mkdirErr) {\n console.error(\n chalk.red(\"pai session pause: \") +\n `Could not create Notes directory: ${String(mkdirErr)}`\n );\n process.exit(1);\n }\n }\n todoPath = join(notesDir, \"TODO.md\");\n existingContent = \"\";\n }\n\n // ---- 4. Build ## Continue block ----\n const timestamp = new Date().toISOString();\n\n let sessionLine: string;\n if (session) {\n const num = String(session.number).padStart(4, \"0\");\n const titlePart = session.title || session.slug || \"Session\";\n sessionLine = `${num} - ${session.date} - ${titlePart}`;\n } else {\n sessionLine = \"Unknown session\";\n }\n\n const continueBlock = [\n \"## Continue\",\n \"\",\n `> **Last session:** ${sessionLine}`,\n `> **Paused at:** ${timestamp}`,\n \">\",\n `> Working directory: ${cwd}. Check the latest session note for details.`,\n \"\",\n \"---\",\n \"\",\n ].join(\"\\n\");\n\n // ---- 5. Prepend, stripping old ## Continue ----\n const stripped = stripContinueSection(existingContent).trimStart();\n const newContent = continueBlock + stripped;\n\n // ---- 6. Write atomically (or dry-run preview) ----\n if (opts.dryRun) {\n console.log(\"\\n\" + chalk.bold(\"Dry run — would write to:\") + \" \" + chalk.cyan(todoPath));\n console.log(chalk.dim(\"─\".repeat(60)));\n // Show just the ## Continue block that would be prepended\n console.log(chalk.dim(continueBlock));\n console.log(chalk.dim(\"─\".repeat(60)));\n } else {\n const tmpPath = `${todoPath}.pause.tmp`;\n try {\n writeFileSync(tmpPath, newContent, \"utf8\");\n renameSync(tmpPath, todoPath);\n } catch (writeErr) {\n try {\n if (existsSync(tmpPath)) renameSync(tmpPath, `${tmpPath}.dead`);\n } catch {\n /* ignore */\n }\n console.error(\n chalk.red(\"pai session pause: \") +\n `Failed to write TODO.md: ${String(writeErr)}`\n );\n process.exit(1);\n }\n console.log(\n chalk.green(\" ## Continue written to: \") + chalk.cyan(todoPath)\n );\n }\n\n // ---- 7. Print the safety warning ----\n const box = [\n \"\",\n chalk.bgRed.white.bold(\n \" IMPORTANT: How to exit safely \"\n ),\n \"\",\n chalk.yellow(\n \" Inside the Claude Code session, type: \" +\n chalk.white.bold(\"/exit\") +\n chalk.yellow(\" (then press Enter)\")\n ),\n \"\",\n chalk.red.bold(\" DO NOT press Ctrl+C.\"),\n \"\",\n chalk.dim(\n \" Ctrl+C bypasses PAI's stop-hook, which means:\"\n ),\n chalk.dim(\" - No project snapshot is written to the top-level jsonl\"),\n chalk.dim(\" - The session becomes orphaned (cannot --resume next time)\"),\n chalk.dim(\n \" - pai session goto will launch a fresh session instead of resuming\"\n ),\n \"\",\n chalk.dim(\" The ## Continue checkpoint in TODO.md is already saved.\"),\n chalk.dim(\" Use /exit to preserve full session resumability.\"),\n \"\",\n ].join(\"\\n\");\n\n console.log(box);\n}\n","/**\n * pai end [--dry-run]\n *\n * Extends `pai pause` with two additional steps:\n * 1. Marks the current session note **Status: Completed** and adds a\n * Completed timestamp — mirrors what `finalizeSessionNote()` does in the\n * hooks lib but without importing the hooks-only module.\n * 2. Prints a slightly more final safe-exit reminder.\n *\n * The TODO.md ## Continue checkpoint is written first (same as `pai pause`),\n * so if the user exits without the session note step succeeding the content\n * checkpoint is still intact.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport {\n existsSync,\n readFileSync,\n writeFileSync,\n readdirSync,\n renameSync,\n mkdirSync,\n} from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { realpathSync } from \"node:fs\";\nimport chalk from \"chalk\";\nimport type { ProjectRow, SessionRow } from \"./types.js\";\nimport { cmdPause } from \"./pause.js\";\n\n// ---------------------------------------------------------------------------\n// Notes-directory resolution (mirrors paths.ts without importing hooks lib)\n// ---------------------------------------------------------------------------\n\n/** Encode a path the same way Claude Code does. */\nfunction encodePath(path: string): string {\n return path\n .replace(/\\//g, \"-\")\n .replace(/\\./g, \"-\")\n .replace(/ /g, \"-\");\n}\n\n/** PAI_DIR — mirrors pai-paths.ts resolution. */\nfunction getPaiDir(): string {\n const envDir = process.env.PAI_DIR;\n if (envDir) {\n try {\n return realpathSync(envDir);\n } catch {\n return envDir;\n }\n }\n return join(homedir(), \".claude\");\n}\n\n/**\n * Find the notes directory for a project — checks local first, then central.\n * Returns null if neither exists (dry-run safe: don't create).\n */\nfunction findNotesDir(\n rootPath: string,\n encodedDir: string\n): string | null {\n // Check local paths first\n for (const rel of [\"Notes\", \"notes\", \".claude/Notes\"]) {\n const p = join(rootPath, rel);\n if (existsSync(p)) return p;\n }\n // Central fallback: ~/.claude/projects/<encoded>/Notes\n const central = join(getPaiDir(), \"projects\", encodedDir, \"Notes\");\n if (existsSync(central)) return central;\n return null;\n}\n\n/**\n * Find the current (latest) session note inside notesDir.\n * Searches YYYY/MM subdirectory (current month, then previous month),\n * then flat notesDir as legacy fallback.\n */\nfunction findLatestNote(notesDir: string): string | null {\n const findIn = (dir: string): string | null => {\n if (!existsSync(dir)) return null;\n const files = readdirSync(dir)\n .filter((f) => /^\\d{3,4}[\\s_-].*\\.md$/.test(f))\n .sort((a, b) => {\n const na = parseInt(a.match(/^(\\d+)/)?.[1] ?? \"0\", 10);\n const nb = parseInt(b.match(/^(\\d+)/)?.[1] ?? \"0\", 10);\n return na - nb;\n });\n return files.length > 0 ? join(dir, files[files.length - 1]) : null;\n };\n\n const now = new Date();\n const year = String(now.getFullYear());\n const month = String(now.getMonth() + 1).padStart(2, \"0\");\n\n const current = findIn(join(notesDir, year, month));\n if (current) return current;\n\n const prev = new Date(now.getFullYear(), now.getMonth() - 1, 1);\n const py = String(prev.getFullYear());\n const pm = String(prev.getMonth() + 1).padStart(2, \"0\");\n const prevFound = findIn(join(notesDir, py, pm));\n if (prevFound) return prevFound;\n\n return findIn(notesDir);\n}\n\n// ---------------------------------------------------------------------------\n// Session-note finalization (mirrors finalizeSessionNote from session-notes.ts)\n// ---------------------------------------------------------------------------\n\nfunction finalizeNote(notePath: string): { finalized: boolean; path: string } {\n const content = readFileSync(notePath, \"utf-8\");\n\n if (content.includes(\"**Status:** Completed\")) {\n return { finalized: false, path: notePath }; // already done\n }\n\n let updated = content.replace(\"**Status:** In Progress\", \"**Status:** Completed\");\n\n if (!updated.includes(\"**Completed:**\")) {\n const completionTime = new Date().toISOString();\n updated = updated.replace(\n \"---\\n\\n## Work Done\",\n `**Completed:** ${completionTime}\\n\\n---\\n\\n## Work Done`\n );\n }\n\n // Write atomically\n const tmp = `${notePath}.end.tmp`;\n writeFileSync(tmp, updated, \"utf-8\");\n renameSync(tmp, notePath);\n\n return { finalized: true, path: notePath };\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport function cmdEnd(db: Database, opts: { dryRun?: boolean }): void {\n // ---- Step 1: Run pause logic (writes ## Continue, prints warning) ----\n // We call cmdPause first; it prints the ## Continue block and the initial\n // safe-exit box. We'll print the end-specific reminder afterwards.\n cmdPause(db, opts);\n\n // ---- Step 2: Locate the project ----\n const cwd = process.cwd();\n const project = db\n .prepare(\n `SELECT id, slug, display_name, root_path, encoded_dir\n FROM projects\n WHERE ? LIKE root_path || '%'\n ORDER BY length(root_path) DESC\n LIMIT 1`\n )\n .get(cwd) as ProjectRow | undefined;\n\n if (!project) {\n // cmdPause already printed an error. Nothing more to do.\n return;\n }\n\n // ---- Step 3: Find notes directory ----\n const notesDir = findNotesDir(project.root_path, project.encoded_dir ?? \"\");\n\n if (!notesDir) {\n console.log(\n chalk.dim(\" (No session note found — notes directory does not exist yet.)\")\n );\n printEndBox(opts.dryRun);\n return;\n }\n\n // ---- Step 4: Find latest session note ----\n const notePath = findLatestNote(notesDir);\n\n if (!notePath) {\n console.log(chalk.dim(\" (No session note found in notes directory.)\"));\n printEndBox(opts.dryRun);\n return;\n }\n\n // ---- Step 5: Mark as completed ----\n if (opts.dryRun) {\n console.log(\n \"\\n\" +\n chalk.bold(\"Dry run — would finalize session note:\") +\n \" \" +\n chalk.cyan(notePath)\n );\n console.log(\n chalk.dim(\n \" Would replace: **Status:** In Progress\\n\" +\n \" with: **Status:** Completed\"\n )\n );\n console.log(\n chalk.dim(\" Would add: **Completed:** <timestamp>\")\n );\n } else {\n const { finalized, path } = finalizeNote(notePath);\n if (finalized) {\n console.log(\n chalk.green(\" Session note finalized: \") + chalk.cyan(basename(path))\n );\n } else {\n console.log(\n chalk.dim(\n ` Session note already marked Completed: ${basename(notePath)}`\n )\n );\n }\n }\n\n // ---- Step 6: Print the more-final exit reminder ----\n printEndBox(opts.dryRun);\n}\n\nfunction printEndBox(dryRun?: boolean): void {\n const label = dryRun ? \" (dry-run)\" : \"\";\n const box = [\n \"\",\n chalk.bgRed.white.bold(\n ` SESSION ENDING${label}: How to exit safely `\n ),\n \"\",\n chalk.yellow(\n \" Inside the Claude Code session, type: \" +\n chalk.white.bold(\"/exit\") +\n chalk.yellow(\" (then press Enter)\")\n ),\n \"\",\n chalk.red.bold(\" DO NOT press Ctrl+C.\"),\n \"\",\n chalk.dim(\" Ctrl+C bypasses PAI's stop-hook, which means:\"),\n chalk.dim(\" - The session note is NOT written to by the stop-hook\"),\n chalk.dim(\" - The session becomes orphaned (cannot --resume next time)\"),\n chalk.dim(\" - The final session summary is never generated\"),\n \"\",\n chalk.dim(\n \" ## Continue checkpoint and session note status are already saved.\"\n ),\n chalk.dim(\" Use /exit to let PAI's stop-hook finalize the session fully.\"),\n \"\",\n ].join(\"\\n\");\n\n console.log(box);\n}\n","/**\n * pai pause all [--exit] [--dry-run]\n *\n * Pause every live Claude Code session that AIBroker knows about.\n *\n * For each live session returned by AIBroker's session_content IPC method:\n * 1. Send \"pause session\" to the iTerm2 pane via send_to_session.\n * 2. Optionally send \"\\n/exit\\n\" after a short delay (--exit flag).\n * 3. Print a summary of what was sent and to which sessions.\n *\n * If AIBroker is not running, prints a clear error and exits.\n * If no live sessions are found, reports \"nothing to pause\".\n */\n\nimport chalk from \"chalk\";\nimport {\n fetchLiveSessions,\n sendToSession,\n type AiBrokerSessionMeta,\n} from \"../../lib/aibroker-client.js\";\nimport { header, dim, ok, err, warn } from \"../../utils.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction sessionLabel(s: AiBrokerSessionMeta): string {\n const name = s.paiName ?? s.name;\n return `${chalk.cyan(s.sessionId.slice(0, 8))} ${chalk.bold(name)}`;\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport async function cmdPauseAll(opts: {\n exit?: boolean;\n dryRun?: boolean;\n wait?: number;\n}): Promise<void> {\n const waitMs = opts.wait ?? 5_000; // ms to wait after \"pause session\" before /exit\n\n // ── Fetch live sessions ────────────────────────────────────────────────────\n let liveSessions: AiBrokerSessionMeta[];\n try {\n liveSessions = await fetchLiveSessions();\n } catch (e) {\n console.error(err(\"AIBroker is not running. Cannot list live sessions.\"));\n console.error(dim(\" Start AIBroker or run `pai pause` from each session manually.\"));\n process.exitCode = 1;\n return;\n }\n\n // ── Filter: Claude sessions only (skip bare shells) ───────────────────────\n const claudeSessions = liveSessions.filter((s) => s.kind === \"claude\");\n const skipped = liveSessions.length - claudeSessions.length;\n\n if (skipped > 0) {\n process.stderr.write(\n `Skipping ${skipped} non-Claude tab${skipped === 1 ? \"\" : \"s\"} (bare shells).\\n`\n );\n }\n\n if (claudeSessions.length === 0) {\n console.log(warn(\"No live Claude sessions found via AIBroker. Nothing to pause.\"));\n return;\n }\n\n // ── Dry-run ───────────────────────────────────────────────────────────────\n if (opts.dryRun) {\n console.log(\"\\n\" + header(\"Dry Run — Would Pause These Sessions\") + \"\\n\");\n for (const s of claudeSessions) {\n console.log(\" \" + sessionLabel(s));\n console.log(dim(' → send: \"pause session\"'));\n if (opts.exit) {\n console.log(dim(` → wait ${waitMs}ms then send: \"/exit\"`));\n }\n }\n console.log();\n return;\n }\n\n // ── Live run ──────────────────────────────────────────────────────────────\n console.log(\n \"\\n\" +\n header(\"Pausing All Live Sessions\") +\n \"\\n\" +\n dim(` ${claudeSessions.length} Claude session(s) via AIBroker`) +\n \"\\n\"\n );\n\n const results: Array<{ session: AiBrokerSessionMeta; pauseOk: boolean; exitOk?: boolean; error?: string }> = [];\n\n for (const s of claudeSessions) {\n process.stdout.write(\" \" + sessionLabel(s) + \" … \");\n\n // Send \"pause session\" command\n const pauseResult = await sendToSession(s.sessionId, \"pause session\\n\");\n if (!pauseResult.ok) {\n console.log(err(\"FAILED: \" + (pauseResult.error ?? \"unknown error\")));\n results.push({ session: s, pauseOk: false, error: pauseResult.error });\n continue;\n }\n console.log(ok(\"paused\"));\n results.push({ session: s, pauseOk: true });\n }\n\n // ── Optional /exit after waiting ──────────────────────────────────────────\n if (opts.exit) {\n const pausedSessions = results.filter((r) => r.pauseOk).map((r) => r.session);\n if (pausedSessions.length > 0) {\n console.log(\n \"\\n\" +\n dim(` Waiting ${waitMs / 1000}s for sessions to save state…`)\n );\n await sleep(waitMs);\n\n console.log();\n for (const s of pausedSessions) {\n process.stdout.write(\" \" + sessionLabel(s) + \" exiting … \");\n const exitResult = await sendToSession(s.sessionId, \"/exit\\n\");\n const rec = results.find((r) => r.session.sessionId === s.sessionId)!;\n rec.exitOk = exitResult.ok;\n if (!exitResult.ok) {\n console.log(warn(\"exit failed: \" + (exitResult.error ?? \"unknown\")));\n } else {\n console.log(ok(\"exited\"));\n }\n }\n }\n }\n\n // ── Summary ───────────────────────────────────────────────────────────────\n const total = results.length;\n const succeeded = results.filter((r) => r.pauseOk).length;\n const failed = total - succeeded;\n\n console.log();\n if (failed === 0) {\n console.log(ok(`All ${total} session(s) paused successfully.`));\n } else {\n console.log(\n warn(`${succeeded}/${total} session(s) paused. `) +\n err(`${failed} failed.`)\n );\n for (const r of results.filter((r) => !r.pauseOk)) {\n const label = r.session.paiName ?? r.session.name ?? r.session.sessionId.slice(0, 8);\n console.log(err(` ${label}: ${r.error ?? \"unknown error\"}`));\n }\n }\n\n if (opts.exit) {\n const exitOk = results.filter((r) => r.exitOk).length;\n console.log(dim(` /exit sent to ${exitOk} session(s).`));\n }\n\n console.log();\n}\n","/**\n * Commander registration for all `pai sessions` sub-commands (plural namespace).\n */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport {\n cmdList,\n cmdInfo,\n cmdRename,\n cmdSlug,\n cmdTag,\n cmdRoute,\n cmdActive,\n cmdAutoRoute,\n} from \"./commands.js\";\nimport { cmdCheckpoint } from \"./checkpoint.js\";\nimport { cmdHandover } from \"./handover.js\";\nimport { cmdRecent } from \"./recent.js\";\nimport { cmdGoto } from \"./goto.js\";\nimport { cmdPause } from \"./pause.js\";\nimport { cmdEnd } from \"./end.js\";\nimport { cmdPauseAll } from \"./pause-all.js\";\n\nexport function registerSessionsCommands(\n sessionsCmd: Command,\n getDb: () => Database\n): void {\n // pai sessions list [-n N] [--all] [--json]\n // Default when `pai sessions` is invoked bare (isDefault).\n sessionsCmd\n .command(\"list\", { isDefault: true })\n .description(\n \"Resumable sessions catalog — named sessions with resume status.\\n\" +\n \"Short form: pai sessions (bare, no subcommand)\\n\" +\n \"Use --all to also show unnamed orphan sessions.\"\n )\n .option(\"-n <count>\", \"Maximum sessions to show (default: 20)\", \"20\")\n .option(\"--all\", \"Include unnamed orphan sessions (not in clc registry)\")\n .option(\"--all-tabs\", \"Show all iTerm2 tabs in Live Sessions, including bare shells\")\n .option(\"--json\", \"Output raw JSON instead of formatted table\")\n .action(async (opts: { n?: string; all?: boolean; allTabs?: boolean; json?: boolean }) => {\n await cmdRecent(getDb(), opts);\n });\n\n // pai sessions goto <name-or-id> [--dry-run]\n sessionsCmd\n .command(\"goto <name-or-id>\")\n .description(\n \"Go to a session: resume if a resumable snapshot exists, start fresh otherwise.\\n\" +\n \"Recommended short form: pai resume <name>\\n\" +\n \"Resolves by clc/registry name (case-insensitive) or UUID prefix.\"\n )\n .option(\"--dry-run\", \"Print the exact argv and cwd, then exit without launching\")\n .action(\n (nameOrId: string, opts: { dryRun?: boolean }) => {\n cmdGoto(getDb(), nameOrId, { dryRun: opts.dryRun });\n }\n );\n\n // pai sessions pause [--dry-run]\n sessionsCmd\n .command(\"pause\")\n .description(\n \"Write a ## Continue checkpoint to the project's TODO.md.\\n\" +\n \"Recommended short form: pai pause\\n\" +\n \"Use /exit inside Claude Code to preserve full session resumability.\"\n )\n .option(\"--dry-run\", \"Preview the ## Continue block without writing it\")\n .action((opts: { dryRun?: boolean }) => {\n cmdPause(getDb(), opts);\n });\n\n // pai sessions pause-all [--exit] [--wait <ms>] [--dry-run]\n sessionsCmd\n .command(\"pause-all\")\n .description(\n \"Pause every live Claude Code session via AIBroker.\\n\" +\n \"Requires AIBroker to be running. Sends 'pause session' to each live iTerm2 pane.\\n\" +\n \"Top-level short form: pai pause all\"\n )\n .option(\"--exit\", \"Also send /exit to each session after it has saved state\")\n .option(\"--wait <ms>\", \"Milliseconds to wait before /exit (default: 5000)\", \"5000\")\n .option(\"--dry-run\", \"Show what would be sent without actually sending\")\n .action(async (opts: { exit?: boolean; wait?: string; dryRun?: boolean }) => {\n await cmdPauseAll({\n exit: opts.exit,\n dryRun: opts.dryRun,\n wait: opts.wait !== undefined ? parseInt(opts.wait, 10) : undefined,\n });\n });\n\n // pai sessions end [--dry-run]\n sessionsCmd\n .command(\"end\")\n .description(\n \"Finalize a session: write ## Continue checkpoint + mark session note Completed.\\n\" +\n \"Recommended short form: pai end\\n\" +\n \"Use /exit inside Claude Code after running this command.\"\n )\n .option(\"--dry-run\", \"Preview all changes without writing them\")\n .action((opts: { dryRun?: boolean }) => {\n cmdEnd(getDb(), opts);\n });\n\n // pai sessions info <project-slug> <number>\n sessionsCmd\n .command(\"info <project-slug> <number>\")\n .description(\"Show full details for a specific session\")\n .action((projectSlug: string, number: string) => {\n cmdInfo(getDb(), projectSlug, number);\n });\n\n // pai sessions rename <project-slug> <number> <new-slug>\n sessionsCmd\n .command(\"rename <project-slug> <number> <new-slug>\")\n .description(\"Rename a session note — updates file on disk, H1 title, and registry\")\n .action((projectSlug: string, number: string, newSlug: string) => {\n cmdRename(getDb(), projectSlug, number, newSlug);\n });\n\n // pai sessions slug <project-slug> <number>\n sessionsCmd\n .command(\"slug <project-slug> <number>\")\n .description(\"Generate a descriptive slug from the session JSONL transcript\")\n .option(\"--apply\", \"Rename the session note using the generated slug\")\n .action(\n (projectSlug: string, number: string, opts: { apply?: boolean }) => {\n cmdSlug(getDb(), projectSlug, number, opts);\n }\n );\n\n // pai sessions tag <project-slug> <number> [tags...]\n sessionsCmd\n .command(\"tag <project-slug> <number> [tags...]\")\n .description(\"Set or show tags on a session.\")\n .action((projectSlug: string, number: string, tags: string[]) => {\n cmdTag(getDb(), projectSlug, number, tags);\n });\n\n // pai sessions route <project-slug> <number> <target-project>\n sessionsCmd\n .command(\"route <project-slug> <number> <target-project>\")\n .description(\"Create a cross-reference link from a session to a target project\")\n .option(\"--type <type>\", \"Link type: related | follow-up | reference\", \"related\")\n .action(\n (\n projectSlug: string,\n number: string,\n targetProject: string,\n opts: { type?: string }\n ) => {\n cmdRoute(getDb(), projectSlug, number, targetProject, opts);\n }\n );\n\n // pai sessions handover [project-slug] [session-id]\n sessionsCmd\n .command(\"handover [project-slug] [session-id]\")\n .description(\n \"Write a ## Continue section to the project's TODO.md.\\n\" +\n \"Called automatically from session-stop and pre-compact hooks.\"\n )\n .action(\n (projectSlug: string | undefined, sessionId: string | undefined) => {\n cmdHandover(getDb(), projectSlug, sessionId);\n }\n );\n\n // pai sessions checkpoint <message>\n sessionsCmd\n .command(\"checkpoint <message>\")\n .description(\n \"Append a timestamped checkpoint to the active session note.\\n\" +\n \"Rate-limited: skips silently if last checkpoint was < --min-gap seconds ago.\"\n )\n .option(\n \"--min-gap <seconds>\",\n \"Minimum seconds between checkpoints (default: 300 = 5 minutes)\",\n \"300\"\n )\n .action((message: string, opts: { minGap?: string }) => {\n cmdCheckpoint(message, opts);\n });\n\n // pai sessions active [--minutes N] [--json]\n sessionsCmd\n .command(\"active\")\n .description(\n \"Show currently active Claude Code sessions.\\n\" +\n \"Detects live sessions by checking which JSONL transcript files\\n\" +\n \"were recently modified in ~/.claude/projects/.\"\n )\n .option(\n \"--minutes <n>\",\n \"Consider sessions active if modified within N minutes (default: 60)\",\n \"60\"\n )\n .option(\"--json\", \"Output raw JSON instead of formatted display\")\n .action((opts: { minutes?: string; json?: boolean }) => {\n cmdActive(getDb(), opts);\n });\n\n // pai sessions auto-route [--cwd path] [--context \"text\"] [--json]\n sessionsCmd\n .command(\"auto-route\")\n .description(\n \"Auto-detect which project this session belongs to.\\n\" +\n \"Tries: (1) path match in registry, (2) Notes/PAI.md marker walk, (3) topic detection.\"\n )\n .option(\"--cwd <path>\", \"Working directory to detect from (default: process.cwd())\")\n .option(\"--context <text>\", \"Conversation context for topic-based fallback routing\")\n .option(\"--json\", \"Output raw JSON instead of formatted display\")\n .action(\n async (opts: { cwd?: string; context?: string; json?: boolean }) => {\n await cmdAutoRoute(opts);\n }\n );\n}\n","/**\n * Shared types and constants for the session-cleanup command.\n */\n\nexport interface ProjectRow {\n id: number;\n slug: string;\n display_name: string;\n root_path: string;\n encoded_dir: string;\n claude_notes_dir: string | null;\n}\n\nexport interface SessionRow {\n id: number;\n project_id: number;\n number: number;\n date: string;\n slug: string;\n title: string;\n filename: string;\n status: string;\n}\n\nexport type SessionClassification = \"EMPTY\" | \"UNNAMED\" | \"NAMED\" | \"LEGACY_FORMAT\";\n\nexport interface SessionCandidate {\n session: SessionRow | null; // null if file exists on disk but not in DB\n filename: string;\n filepath: string;\n sizeBytes: number;\n classification: SessionClassification;\n autoName?: string; // proposed name for UNNAMED sessions\n date: string;\n number: number;\n}\n\nexport interface NotesDirPlan {\n notesDir: string;\n toDelete: SessionCandidate[];\n toRename: SessionCandidate[];\n toMove: SessionCandidate[]; // survivors that need moving to YYYY/MM/ within this dir\n}\n\nexport interface CleanupPlan {\n project: ProjectRow;\n notesDirs: NotesDirPlan[]; // one entry per discovered Notes/ directory (up to 2)\n renumberMap: Map<number, number>; // old number → new number (global across both dirs)\n}\n\n// Template content indicators — if the file only contains these patterns, delete it.\nexport const TEMPLATE_INDICATORS = [\n \"<!-- PAI will add completed work here during session -->\",\n \"<!-- PAI will add completed work here -->\",\n \"Session completed.\",\n \"Session started and ready for your instructions\",\n];\n\n// Session filename patterns\nexport const MODERN_PATTERN = /^(\\d{4}) - (\\d{4}-\\d{2}-\\d{2}) - (.+)\\.md$/;\nexport const LEGACY_PATTERN = /^(\\d{4})_(\\d{4}-\\d{2}-\\d{2})_(.+)\\.md$/;\n","/**\n * Auto-name extraction and string helpers for session-cleanup.\n * Derives a meaningful session title from Markdown content.\n */\n\n// Meta-phrases indicating template/status text rather than real work.\nconst META_PHRASE_PATTERNS: RegExp[] = [\n /session initialized and ready for your instructions/i,\n /fresh session with no pending tasks/i,\n /starting new session.*checking for pending work/i,\n /fresh session with empty todo/i,\n /session started and ready/i,\n /^session\\b.*\\bready\\b/i,\n /^session\\b.*\\binitialized\\b/i,\n /^session\\b.*\\bno pending\\b/i,\n /^session\\b.*\\bno prior work\\b/i,\n /^no pending tasks/i,\n /^no prior work/i,\n];\n\nconst TITLE_CASE_MINOR_WORDS = new Set([\n \"a\", \"an\", \"the\", \"in\", \"on\", \"at\", \"for\", \"to\", \"of\", \"and\", \"or\",\n \"but\", \"via\", \"with\", \"from\", \"by\", \"as\", \"nor\",\n]);\n\nfunction cleanMarkdownLine(raw: string): string | null {\n let s = raw.trim();\n s = s.replace(/^[-*+]\\s+\\[[ xX]\\]\\s*/, \"\");\n s = s.replace(/^[-*+]\\s+/, \"\");\n s = s.replace(/^\\[[ xX]\\]\\s*/, \"\");\n s = s.replace(/\\*\\*\\s*([^*]+?)\\s*\\*\\*/g, \"$1\");\n s = s.replace(/\\*\\s*([^*]+?)\\s*\\*/g, \"$1\");\n s = s.replace(/`([^`]+)`/g, \"$1\");\n s = s.replace(/^\\*+\\s*/, \"\");\n s = s.replace(/^[.,;:]+/, \"\").replace(/[.,;:]+$/, \"\");\n s = s.replace(/\\s+/g, \" \").trim();\n return s.length >= 4 ? s : null;\n}\n\nfunction isMetaPhrase(text: string): boolean {\n return META_PHRASE_PATTERNS.some((re) => re.test(text));\n}\n\nfunction toTitleCase(text: string): string {\n const words = text.split(\" \");\n return words\n .map((word, i) => {\n const lower = word.toLowerCase();\n if (i !== 0 && TITLE_CASE_MINOR_WORDS.has(lower)) return lower;\n return word.charAt(0).toUpperCase() + word.slice(1);\n })\n .join(\" \");\n}\n\nexport function sanitizeName(raw: string): string {\n let s = raw.replace(/[\\/\\\\:*?\"<>|#`]/g, \"\");\n s = s.replace(/\\s+/g, \" \").trim();\n if (s.length > 60) {\n const truncated = s.slice(0, 60);\n const lastSpace = truncated.lastIndexOf(\" \");\n s = lastSpace > 20 ? truncated.slice(0, lastSpace) : truncated;\n }\n s = s.trim();\n return toTitleCase(s);\n}\n\nexport function extractAutoName(content: string): string {\n const lines = content.split(\"\\n\");\n\n const CONTENT_SECTION_HEADINGS = new Set([\n \"Work Done\", \"Summary\", \"Completed\", \"What Was Done\",\n \"Results\", \"Outcomes\", \"Changes\", \"Progress\",\n ]);\n const SKIP_SECTION_HEADINGS = new Set([\n \"Next Steps\", \"Tags\", \"TODO\", \"Blockers\", \"Notes\",\n \"Metadata\", \"Context\", \"Background\",\n ]);\n\n let pastH1 = false;\n let pastFirstHr = false;\n let currentSection: string | null = null;\n const contentSectionLines: string[] = [];\n const otherH2Headings: string[] = [];\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n if (trimmed.startsWith(\"# \")) {\n pastH1 = true;\n continue;\n }\n if (!pastH1) continue;\n\n if (!pastFirstHr) {\n if (trimmed === \"---\") pastFirstHr = true;\n continue;\n }\n\n if (trimmed.startsWith(\"## \")) {\n const headingText = trimmed.slice(3).trim();\n if (CONTENT_SECTION_HEADINGS.has(headingText)) {\n currentSection = \"content\";\n } else if (SKIP_SECTION_HEADINGS.has(headingText)) {\n currentSection = \"skip\";\n } else {\n currentSection = null;\n otherH2Headings.push(headingText);\n }\n continue;\n }\n\n if (trimmed.startsWith(\"#\")) continue;\n if (trimmed === \"-->\") continue;\n if (trimmed.startsWith(\"<!--\") && /^<!--.*-->$/.test(trimmed)) continue;\n\n if (currentSection === \"content\" && trimmed.length > 0) {\n const withoutComment = trimmed.replace(/^<!--.*?-->\\s*/, \"\");\n const effective = withoutComment.length > 0 ? withoutComment : trimmed;\n if (effective.length === 0) continue;\n contentSectionLines.push(effective);\n }\n }\n\n for (const raw of contentSectionLines) {\n const cleaned = cleanMarkdownLine(raw);\n if (!cleaned) continue;\n if (isMetaPhrase(cleaned)) continue;\n if (cleaned.startsWith(\"<!--\") || cleaned.includes(\"PAI will add\")) continue;\n if (cleaned.length < 5) continue;\n return sanitizeName(cleaned);\n }\n\n for (const heading of otherH2Headings) {\n const cleaned = cleanMarkdownLine(heading);\n if (!cleaned) continue;\n if (isMetaPhrase(cleaned)) continue;\n if (cleaned.length > 3 && cleaned.length < 80) {\n return sanitizeName(cleaned);\n }\n }\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"# \")) {\n const title = trimmed.slice(2).trim();\n if (/^Session \\d{4}/i.test(title)) continue;\n const cleaned = cleanMarkdownLine(title);\n if (cleaned && !isMetaPhrase(cleaned) && cleaned.length > 3) {\n return sanitizeName(cleaned);\n }\n }\n }\n\n return \"Unnamed Session\";\n}\n\n/** Format a 4-digit padded session number. */\nexport function padNum(n: number): string {\n return String(n).padStart(4, \"0\");\n}\n","/**\n * Analysis phase: scan Notes/ directories and build CleanupPlans.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport {\n existsSync,\n readdirSync,\n readFileSync,\n statSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type {\n ProjectRow,\n SessionRow,\n SessionCandidate,\n SessionClassification,\n NotesDirPlan,\n CleanupPlan,\n} from \"./types.js\";\nimport {\n TEMPLATE_INDICATORS,\n MODERN_PATTERN,\n LEGACY_PATTERN,\n} from \"./types.js\";\nimport { extractAutoName, padNum } from \"./rename.js\";\n\n// ---------------------------------------------------------------------------\n// DB helpers\n// ---------------------------------------------------------------------------\n\nexport function getAllProjects(db: Database): ProjectRow[] {\n return db\n .prepare(\n \"SELECT id, slug, display_name, root_path, encoded_dir, claude_notes_dir FROM projects WHERE status = 'active' ORDER BY slug\"\n )\n .all() as ProjectRow[];\n}\n\nexport function getProject(\n db: Database,\n slug: string\n): ProjectRow | undefined {\n return db\n .prepare(\n \"SELECT id, slug, display_name, root_path, encoded_dir, claude_notes_dir FROM projects WHERE slug = ?\"\n )\n .get(slug) as ProjectRow | undefined;\n}\n\nfunction getProjectSessions(\n db: Database,\n projectId: number\n): SessionRow[] {\n return db\n .prepare(\"SELECT * FROM sessions WHERE project_id = ? ORDER BY number ASC\")\n .all(projectId) as SessionRow[];\n}\n\n// ---------------------------------------------------------------------------\n// Notes directory discovery\n// ---------------------------------------------------------------------------\n\nfunction findRootNotesDir(rootPath: string): string | null {\n const canonical = join(rootPath, \"Notes\");\n if (existsSync(canonical)) return canonical;\n const alt = join(rootPath, \".claude\", \"Notes\");\n if (existsSync(alt)) return alt;\n return null;\n}\n\nfunction findClaudeNotesDir(\n project: ProjectRow,\n rootNotesDir: string | null\n): string | null {\n const candidate =\n project.claude_notes_dir ??\n join(homedir(), \".claude\", \"projects\", project.encoded_dir, \"Notes\");\n\n if (!existsSync(candidate)) return null;\n if (rootNotesDir && candidate === rootNotesDir) return null;\n return candidate;\n}\n\nexport function findAllNotesDirs(project: ProjectRow): string[] {\n const rootDir = findRootNotesDir(project.root_path);\n const claudeDir = findClaudeNotesDir(project, rootDir);\n const dirs: string[] = [];\n if (rootDir) dirs.push(rootDir);\n if (claudeDir) dirs.push(claudeDir);\n return dirs;\n}\n\n// ---------------------------------------------------------------------------\n// Content analysis\n// ---------------------------------------------------------------------------\n\nfunction isTemplateOnly(content: string): boolean {\n const hasTemplateMarker = TEMPLATE_INDICATORS.some((ind) =>\n content.includes(ind)\n );\n if (!hasTemplateMarker) return false;\n\n const lines = content.split(\"\\n\");\n let inWorkDone = false;\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed === \"## Work Done\") {\n inWorkDone = true;\n continue;\n }\n if (trimmed.startsWith(\"## \") && inWorkDone) break;\n if (!inWorkDone) continue;\n if (!trimmed) continue;\n if (trimmed.startsWith(\"<!--\") && trimmed.endsWith(\"-->\")) continue;\n if (trimmed.startsWith(\"<!--\")) continue;\n if (trimmed === \"-->\") continue;\n if (trimmed === \"Session completed.\") continue;\n if (trimmed === \"#Session\" || trimmed === \"**Tags:** #Session\") continue;\n return false;\n }\n\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Scan + analysis\n// ---------------------------------------------------------------------------\n\nexport function scanNotesDir(\n notesDir: string,\n dbByFilename: Map<string, SessionRow>\n): SessionCandidate[] {\n const candidates: SessionCandidate[] = [];\n\n let flatFiles: string[] = [];\n try {\n flatFiles = readdirSync(notesDir, { withFileTypes: true })\n .filter((d) => d.isFile() && d.name.endsWith(\".md\"))\n .map((d) => d.name);\n } catch {\n // Directory unreadable\n }\n\n const subDirFiles: { filename: string; filepath: string }[] = [];\n try {\n const topEntries = readdirSync(notesDir, { withFileTypes: true });\n for (const entry of topEntries) {\n if (!entry.isDirectory()) continue;\n if (!/^\\d{4}$/.test(entry.name)) continue;\n const yearDir = join(notesDir, entry.name);\n const monthDirs = readdirSync(yearDir, { withFileTypes: true });\n for (const mEntry of monthDirs) {\n if (!mEntry.isDirectory()) continue;\n const monthDir = join(yearDir, mEntry.name);\n const files = readdirSync(monthDir).filter((f) => f.endsWith(\".md\"));\n for (const f of files) {\n subDirFiles.push({ filename: f, filepath: join(monthDir, f) });\n }\n }\n }\n } catch {\n // Ignore errors scanning sub-dirs\n }\n\n const allFiles: { filename: string; filepath: string }[] = [\n ...flatFiles.map((f) => ({ filename: f, filepath: join(notesDir, f) })),\n ...subDirFiles,\n ];\n\n for (const { filename, filepath } of allFiles) {\n let num: number;\n let date: string;\n let namepart: string;\n let classification: SessionClassification;\n\n const modernMatch = MODERN_PATTERN.exec(filename);\n const legacyMatch = LEGACY_PATTERN.exec(filename);\n\n if (modernMatch) {\n num = parseInt(modernMatch[1], 10);\n date = modernMatch[2];\n namepart = modernMatch[3];\n classification = \"NAMED\";\n } else if (legacyMatch) {\n num = parseInt(legacyMatch[1], 10);\n date = legacyMatch[2];\n namepart = legacyMatch[3];\n classification = \"LEGACY_FORMAT\";\n } else {\n continue; // not a session file\n }\n\n let sizeBytes = 0;\n let content = \"\";\n try {\n sizeBytes = statSync(filepath).size;\n content = readFileSync(filepath, \"utf8\");\n } catch {\n continue;\n }\n\n const dbSession =\n dbByFilename.get(filename) ??\n dbByFilename.get(filepath.split(`${notesDir}/`)[1] ?? \"\") ??\n null;\n\n if (classification !== \"LEGACY_FORMAT\") {\n if (sizeBytes < 400 || isTemplateOnly(content)) {\n classification = \"EMPTY\";\n } else if (\n namepart === \"New Session\" ||\n namepart === (process.env.USER ?? \"\") ||\n namepart === \"session-started-and-ready-for-your-instructions\"\n ) {\n classification = \"UNNAMED\";\n }\n }\n\n const candidate: SessionCandidate = {\n session: dbSession,\n filename,\n filepath,\n sizeBytes,\n classification,\n date,\n number: num,\n };\n\n if (\n classification === \"UNNAMED\" ||\n classification === \"LEGACY_FORMAT\"\n ) {\n candidate.autoName = extractAutoName(content);\n }\n\n candidates.push(candidate);\n }\n\n return candidates;\n}\n\nfunction buildRenumberMap(\n survivors: SessionCandidate[]\n): Map<number, number> {\n const map = new Map<number, number>();\n const sorted = [...survivors].sort((a, b) => a.number - b.number);\n sorted.forEach((s, idx) => {\n const newNum = idx + 1;\n if (s.number !== newNum) map.set(s.number, newNum);\n });\n return map;\n}\n\nexport function analyzeProject(\n db: Database,\n project: ProjectRow\n): CleanupPlan | null {\n const notesDirPaths = findAllNotesDirs(project);\n if (notesDirPaths.length === 0) return null;\n\n const dbSessions = getProjectSessions(db, project.id);\n const dbByFilename = new Map<string, SessionRow>();\n for (const s of dbSessions) dbByFilename.set(s.filename, s);\n\n const notesDirPlans: NotesDirPlan[] = [];\n const allSurvivors: SessionCandidate[] = [];\n\n for (const notesDir of notesDirPaths) {\n const candidates = scanNotesDir(notesDir, dbByFilename);\n if (candidates.length === 0) continue;\n\n const toDelete = candidates.filter((c) => c.classification === \"EMPTY\");\n const toRename = candidates.filter(\n (c) =>\n c.classification === \"UNNAMED\" || c.classification === \"LEGACY_FORMAT\"\n );\n const survivors = candidates.filter((c) => c.classification !== \"EMPTY\");\n\n notesDirPlans.push({ notesDir, toDelete, toRename, toMove: survivors });\n allSurvivors.push(...survivors);\n }\n\n if (notesDirPlans.length === 0) return null;\n\n const renumberMap = buildRenumberMap(allSurvivors);\n\n return { project, notesDirs: notesDirPlans, renumberMap };\n}\n","/**\n * Dry-run display, Postgres vector DB path updates, and cleanup execution.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport {\n existsSync,\n readdirSync,\n readFileSync,\n writeFileSync,\n mkdirSync,\n renameSync,\n unlinkSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\nimport { ok, warn, err, dim, bold, header } from \"../../utils.js\";\nimport type { CleanupPlan, SessionCandidate } from \"./types.js\";\nimport { padNum } from \"./rename.js\";\n\n// ---------------------------------------------------------------------------\n// Postgres helpers\n// ---------------------------------------------------------------------------\n\nasync function countVectorDbPaths(oldPaths: string[]): Promise<number> {\n if (oldPaths.length === 0) return 0;\n try {\n const { loadConfig } = await import(\"../../../daemon/config.js\");\n const { PostgresBackend } = await import(\"../../../storage/postgres.js\");\n const config = loadConfig();\n if (config.storageBackend !== \"postgres\") return 0;\n const pgBackend = new PostgresBackend(config.postgres ?? {});\n const connErr = await pgBackend.testConnection();\n if (connErr) { await pgBackend.close(); return 0; }\n const pool = (pgBackend as unknown as {\n pool: { query: (sql: string, params: string[]) => Promise<{ rows: Array<{ n: string }> }> };\n }).pool;\n const placeholders = oldPaths.map((_, i) => `$${i + 1}`).join(\", \");\n const result = await pool.query(\n `SELECT COUNT(*)::text AS n FROM pai_files WHERE path IN (${placeholders})`,\n oldPaths\n );\n await pgBackend.close();\n return parseInt(result.rows[0]?.n ?? \"0\", 10);\n } catch {\n return 0;\n }\n}\n\nasync function updateVectorDbPaths(\n moves: Array<{ oldPath: string; newPath: string }>\n): Promise<number> {\n if (moves.length === 0) return 0;\n try {\n const { loadConfig } = await import(\"../../../daemon/config.js\");\n const { PostgresBackend } = await import(\"../../../storage/postgres.js\");\n const config = loadConfig();\n if (config.storageBackend !== \"postgres\") return 0;\n const pgBackend = new PostgresBackend(config.postgres ?? {});\n const connErr = await pgBackend.testConnection();\n if (connErr) {\n process.stderr.write(`[session-cleanup] Postgres unavailable (${connErr}). Skipping vector DB path update.\\n`);\n await pgBackend.close();\n return 0;\n }\n const pool = (pgBackend as unknown as {\n pool: { connect: () => Promise<{\n query: (sql: string, params: string[]) => Promise<{ rowCount: number | null }>;\n release: () => void;\n }> };\n }).pool;\n const client = await pool.connect();\n let filesUpdated = 0;\n try {\n await client.query(\"BEGIN\", []);\n for (const { oldPath, newPath } of moves) {\n const r = await client.query(\"UPDATE pai_files SET path = $1 WHERE path = $2\", [newPath, oldPath]);\n filesUpdated += r.rowCount ?? 0;\n await client.query(\"UPDATE pai_chunks SET path = $1 WHERE path = $2\", [newPath, oldPath]);\n }\n await client.query(\"COMMIT\", []);\n } catch (e) {\n await client.query(\"ROLLBACK\", []);\n throw e;\n } finally {\n client.release();\n }\n await pgBackend.close();\n return filesUpdated;\n } catch (e) {\n process.stderr.write(`[session-cleanup] Failed to update vector DB paths: ${e}\\n`);\n return -1;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Dry-run display\n// ---------------------------------------------------------------------------\n\nexport async function displayDryRun(plans: CleanupPlan[]): Promise<void> {\n let totalDelete = 0;\n let totalRename = 0;\n let totalMove = 0;\n let totalRenumber = 0;\n\n for (const plan of plans) {\n const hasWork =\n plan.notesDirs.some(\n (d) => d.toDelete.length > 0 || d.toRename.length > 0 || d.toMove.length > 0\n ) || plan.renumberMap.size > 0;\n if (!hasWork) continue;\n\n console.log();\n console.log(\n header(` Project: ${plan.project.display_name} (${plan.project.slug})`)\n );\n\n for (const dirPlan of plan.notesDirs) {\n console.log(dim(` Notes: ${dirPlan.notesDir}`));\n\n if (dirPlan.toDelete.length > 0) {\n console.log(bold(\" DELETE (empty/template-only sessions):\"));\n for (const c of dirPlan.toDelete) {\n console.log(\n ` ${chalk.red(\"DEL\")} ${dim(padNum(c.number))} - ${c.date} - ${\n c.filename.split(\" - \").slice(2).join(\" - \")\n } ${dim(`(${c.sizeBytes}b)`)}`\n );\n totalDelete++;\n }\n console.log();\n }\n\n if (dirPlan.toRename.length > 0) {\n console.log(bold(\" RENAME (unnamed or legacy-format sessions):\"));\n for (const c of dirPlan.toRename) {\n const autoName = c.autoName ?? \"Unnamed Session\";\n console.log(` ${chalk.yellow(\"REN\")} ${c.filename}`);\n console.log(` → ${padNum(c.number)} - ${c.date} - ${autoName}.md`);\n totalRename++;\n }\n console.log();\n }\n\n if (dirPlan.toMove.length > 0) {\n console.log(bold(\" MOVE TO YYYY/MM/ hierarchy:\"));\n for (const c of dirPlan.toMove) {\n const [year, month] = c.date.split(\"-\");\n console.log(` ${chalk.cyan(\"MOV\")} ${c.filename}`);\n console.log(` → ${year}/${month}/${c.filename}`);\n totalMove++;\n }\n console.log();\n }\n }\n\n if (plan.renumberMap.size > 0) {\n console.log(\n bold(\" RENUMBER (after deletions, global across all Notes/ dirs):\")\n );\n for (const [oldN, newN] of plan.renumberMap) {\n console.log(\n ` ${chalk.blue(\"NUM\")} #${padNum(oldN)} → #${padNum(newN)}`\n );\n totalRenumber++;\n }\n console.log();\n }\n }\n\n const wouldMovePaths: string[] = [];\n for (const plan of plans) {\n for (const dirPlan of plan.notesDirs) {\n for (const c of dirPlan.toMove) {\n const [year, month] = c.date.split(\"-\");\n const targetPath = join(dirPlan.notesDir, year, month, c.filename);\n if (c.filepath !== targetPath) wouldMovePaths.push(c.filepath);\n }\n }\n }\n\n const vectorDbCount = await countVectorDbPaths(wouldMovePaths);\n\n console.log();\n console.log(bold(\" Summary (dry-run):\"));\n console.log(` ${chalk.red(\"DEL\")} ${totalDelete} empty sessions to delete`);\n console.log(` ${chalk.yellow(\"REN\")} ${totalRename} unnamed sessions to rename`);\n console.log(` ${chalk.blue(\"NUM\")} ${totalRenumber} sessions to renumber`);\n console.log(` ${chalk.cyan(\"MOV\")} ${totalMove} sessions to move into YYYY/MM/ dirs`);\n if (vectorDbCount > 0) {\n console.log(\n ` ${chalk.magenta(\"VEC\")} ${vectorDbCount} file path(s) will be updated in the vector DB (embeddings preserved)`\n );\n } else if (wouldMovePaths.length > 0) {\n console.log(\n ` ${chalk.magenta(\"VEC\")} 0 file path(s) found in vector DB for moved files (no embeddings to preserve)`\n );\n }\n console.log();\n console.log(warn(\" This is a dry-run. Add --execute to apply changes.\"));\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// Execution\n// ---------------------------------------------------------------------------\n\nexport async function executeCleanup(\n db: Database,\n plans: CleanupPlan[],\n skipReindex: boolean\n): Promise<void> {\n let deleted = 0;\n let renamed = 0;\n let moved = 0;\n let renumbered = 0;\n let dbUpdated = 0;\n const vectorDbMoves: Array<{ oldPath: string; newPath: string }> = [];\n\n for (const plan of plans) {\n console.log();\n console.log(\n header(` Project: ${plan.project.display_name} (${plan.project.slug})`)\n );\n\n for (const dirPlan of plan.notesDirs) {\n const { notesDir } = dirPlan;\n if (plan.notesDirs.length > 1) console.log(dim(` Directory: ${notesDir}`));\n\n // Step 1: Delete empty sessions\n for (const c of dirPlan.toDelete) {\n try {\n unlinkSync(c.filepath);\n console.log(ok(` DEL ${c.filename}`));\n deleted++;\n } catch (e) {\n console.log(err(` FAIL to delete ${c.filename}: ${e}`));\n }\n if (c.session) {\n try {\n db.prepare(\"DELETE FROM sessions WHERE id = ?\").run(c.session.id);\n dbUpdated++;\n } catch (e) {\n console.log(err(` FAIL to remove session #${c.number} from DB: ${e}`));\n }\n }\n }\n\n // Step 2: Rename unnamed/legacy sessions\n for (const c of dirPlan.toRename) {\n const autoName = c.autoName ?? \"Unnamed Session\";\n const newFilename = `${padNum(c.number)} - ${c.date} - ${autoName}.md`;\n const newPath = join(notesDir, newFilename);\n\n if (c.filepath !== newPath) {\n try {\n renameSync(c.filepath, newPath);\n console.log(ok(` REN ${c.filename}`));\n console.log(dim(` → ${newFilename}`));\n renamed++;\n (c as { filename: string }).filename = newFilename;\n (c as { filepath: string }).filepath = newPath;\n } catch (e) {\n console.log(err(` FAIL rename ${c.filename}: ${e}`));\n continue;\n }\n }\n\n try {\n const content = readFileSync(newPath, \"utf8\");\n const lines = content.split(\"\\n\");\n let h1Updated = false;\n const updated = lines.map((line) => {\n if (!h1Updated && line.startsWith(\"# \")) {\n h1Updated = true;\n return `# ${autoName}`;\n }\n return line;\n });\n if (!h1Updated) updated.unshift(`# ${autoName}`, \"\");\n writeFileSync(newPath, updated.join(\"\\n\"), \"utf8\");\n } catch {\n // Non-fatal\n }\n\n if (c.session) {\n const normalizedSlug = autoName\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n try {\n db.prepare(\n \"UPDATE sessions SET slug = ?, title = ?, filename = ? WHERE id = ?\"\n ).run(normalizedSlug, autoName, newFilename, c.session.id);\n dbUpdated++;\n } catch (e) {\n console.log(err(` FAIL DB update for session #${c.number}: ${e}`));\n }\n }\n }\n\n // Step 3a: Renumber survivors\n if (plan.renumberMap.size > 0) {\n const toRenumber = dirPlan.toMove.filter((c) =>\n plan.renumberMap.has(c.number)\n );\n\n const tempFiles: {\n candidate: SessionCandidate;\n tempPath: string;\n newNum: number;\n }[] = [];\n for (const c of toRenumber) {\n const newNum = plan.renumberMap.get(c.number)!;\n const tempFilename = `__tmp_${padNum(c.number)}_${c.filename}`;\n const tempPath = join(notesDir, tempFilename);\n try {\n if (existsSync(c.filepath)) {\n renameSync(c.filepath, tempPath);\n tempFiles.push({ candidate: c, tempPath, newNum });\n }\n } catch (e) {\n console.log(err(` FAIL temp-rename #${c.number}: ${e}`));\n }\n }\n\n for (const { candidate: c, tempPath, newNum } of tempFiles) {\n const newFilename = c.filename.replace(/^\\d{4}/, padNum(newNum));\n const newPath = join(notesDir, newFilename);\n try {\n renameSync(tempPath, newPath);\n console.log(\n ok(` NUM #${padNum(c.number)} → #${padNum(newNum)}: ${newFilename}`)\n );\n renumbered++;\n (c as { filename: string }).filename = newFilename;\n (c as { filepath: string }).filepath = newPath;\n (c as { number: number }).number = newNum;\n } catch (e) {\n console.log(err(` FAIL final-rename #${newNum}: ${e}`));\n }\n\n if (existsSync(newPath)) {\n try {\n const content = readFileSync(newPath, \"utf8\");\n const lines = content.split(\"\\n\");\n const updated = lines.map((line) => {\n if (line.match(/^# Session \\d{4}:/)) {\n return line.replace(\n /^# Session \\d{4}:/,\n `# Session ${padNum(newNum)}:`\n );\n }\n return line;\n });\n writeFileSync(newPath, updated.join(\"\\n\"), \"utf8\");\n } catch {\n // Non-fatal\n }\n }\n }\n\n const dbRenumbers = tempFiles\n .filter(({ candidate: c }) => c.session != null)\n .map(({ candidate: c, newNum }) => ({\n session: c.session!,\n newNum,\n newFilename: c.filename,\n }));\n\n if (dbRenumbers.length > 0) {\n const renumberDb = db.transaction(() => {\n for (const { session, newNum } of dbRenumbers) {\n db.prepare(\"UPDATE sessions SET number = ? WHERE id = ?\").run(\n -newNum,\n session.id\n );\n }\n for (const { session, newNum, newFilename } of dbRenumbers) {\n db.prepare(\n \"UPDATE sessions SET number = ?, filename = ? WHERE id = ?\"\n ).run(newNum, newFilename, session.id);\n }\n });\n try {\n renumberDb();\n dbUpdated += dbRenumbers.length;\n } catch (e) {\n console.log(err(` FAIL DB renumber transaction: ${e}`));\n }\n }\n }\n\n // Step 3b: Move to YYYY/MM/\n for (const c of dirPlan.toMove) {\n const [year, month] = c.date.split(\"-\");\n const targetDir = join(notesDir, year, month);\n const targetPath = join(targetDir, c.filename);\n if (c.filepath === targetPath) continue;\n\n try {\n mkdirSync(targetDir, { recursive: true });\n } catch (e) {\n console.log(err(` FAIL mkdir ${targetDir}: ${e}`));\n continue;\n }\n\n const oldAbsPath = c.filepath;\n try {\n if (existsSync(c.filepath)) {\n renameSync(c.filepath, targetPath);\n console.log(ok(` MOV ${c.filename}`));\n console.log(dim(` → ${year}/${month}/${c.filename}`));\n moved++;\n vectorDbMoves.push({ oldPath: oldAbsPath, newPath: targetPath });\n }\n } catch (e) {\n console.log(err(` FAIL move ${c.filename}: ${e}`));\n continue;\n }\n\n const newFilenameInDb = `${year}/${month}/${c.filename}`;\n if (c.session) {\n try {\n db.prepare(\"UPDATE sessions SET filename = ? WHERE id = ?\").run(\n newFilenameInDb,\n c.session.id\n );\n dbUpdated++;\n } catch (e) {\n console.log(err(` FAIL DB update path for ${c.filename}: ${e}`));\n }\n }\n }\n }\n }\n\n // Step 5: Update Postgres vector DB paths\n let vectorDbUpdated = 0;\n if (vectorDbMoves.length > 0) {\n console.log();\n console.log(\n dim(\n ` Updating ${vectorDbMoves.length} file path(s) in vector DB to preserve embeddings...`\n )\n );\n const result = await updateVectorDbPaths(vectorDbMoves);\n if (result >= 0) {\n vectorDbUpdated = result;\n console.log(\n ok(` Updated ${vectorDbUpdated} file path(s) in Postgres (embeddings preserved)`)\n );\n } else {\n console.log(\n warn(\n \" Vector DB path update failed — embeddings may be orphaned (check logs)\"\n )\n );\n }\n }\n\n console.log();\n console.log(bold(\" Cleanup complete:\"));\n console.log(ok(` ${deleted} session(s) deleted`));\n console.log(ok(` ${renamed} session(s) renamed`));\n console.log(ok(` ${renumbered} session(s) renumbered`));\n console.log(ok(` ${moved} session(s) moved to YYYY/MM/ hierarchy`));\n console.log(ok(` ${dbUpdated} registry DB record(s) updated`));\n if (vectorDbMoves.length > 0) {\n console.log(\n ok(\n ` ${vectorDbUpdated} vector DB file path(s) updated (embeddings preserved)`\n )\n );\n }\n\n if (!skipReindex) {\n console.log();\n console.log(\n dim(\" Memory re-index: the PAI daemon will pick up changes within 5 minutes.\")\n );\n console.log(dim(\" To force immediate re-index: pai memory index --all\"));\n }\n\n console.log();\n}\n","/**\n * Commander registration for `pai session cleanup`.\n */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport { err, dim, ok, header } from \"../../utils.js\";\nimport { getAllProjects, getProject, analyzeProject } from \"./scanner.js\";\nimport { displayDryRun, executeCleanup } from \"./executor.js\";\n\nexport function registerSessionCleanupCommand(\n sessionCmd: Command,\n getDb: () => Database\n): void {\n sessionCmd\n .command(\"cleanup [project-slug]\")\n .description(\n \"Clean up session notes: delete empties, auto-name unnamed, move into YYYY/MM/ hierarchy, renumber\"\n )\n .option(\"--execute\", \"Actually perform the cleanup (default is dry-run)\")\n .option(\"--no-renumber\", \"Skip renumbering sessions after deletions\")\n .option(\"--no-reindex\", \"Skip triggering memory re-index after moves\")\n .action(\n async (\n projectSlug: string | undefined,\n opts: { execute?: boolean; renumber?: boolean; reindex?: boolean }\n ) => {\n const db = getDb();\n const dryRun = !opts.execute;\n const skipReindex = opts.reindex === false;\n\n let projects;\n if (projectSlug) {\n const p = getProject(db, projectSlug);\n if (!p) {\n console.error(err(`Project not found: ${projectSlug}`));\n process.exit(1);\n }\n projects = [p];\n } else {\n projects = getAllProjects(db);\n }\n\n console.log();\n console.log(\n header(\n dryRun\n ? \" pai session cleanup — DRY RUN (no changes will be made)\"\n : \" pai session cleanup — EXECUTING\"\n )\n );\n console.log(dim(` Analyzing ${projects.length} project(s)...`));\n\n const plans = [];\n for (const project of projects) {\n const plan = analyzeProject(db, project);\n if (plan) plans.push(plan);\n }\n\n const activePlans = plans.filter(\n (p) =>\n p.notesDirs.some(\n (d) =>\n d.toDelete.length > 0 ||\n d.toRename.length > 0 ||\n d.toMove.length > 0\n ) || p.renumberMap.size > 0\n );\n\n if (activePlans.length === 0) {\n console.log();\n console.log(ok(\" Nothing to do — all session notes are clean!\"));\n console.log();\n return;\n }\n\n if (dryRun) {\n await displayDryRun(activePlans);\n } else {\n await executeCleanup(db, activePlans, skipReindex);\n }\n }\n );\n}\n","/** Registry migrate command: import data from ~/.claude/session-registry.json. */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { ok, warn, err, dim } from \"../../utils.js\";\nimport { slugify, parseSessionFilename } from \"../../../registry/migrate.js\";\nimport { upsertProject, upsertSession } from \"./utils.js\";\nimport type { Database } from \"better-sqlite3\";\n\n// ---------------------------------------------------------------------------\n// Legacy JSON registry types\n// ---------------------------------------------------------------------------\n\ninterface LegacyProjectEntry {\n encoded_dir: string;\n original_path: string;\n notes?: string[];\n session_count?: number;\n note_count?: number;\n todo_exists?: boolean;\n last_modified?: string;\n path_exists?: boolean;\n}\n\ninterface LegacyJsonRegistry {\n version?: number;\n projects?: LegacyProjectEntry[];\n [key: string]: unknown;\n}\n\nconst SESSION_REGISTRY_PATH = join(homedir(), \".claude\", \"session-registry.json\");\n\n// ---------------------------------------------------------------------------\n// cmdMigrate\n// ---------------------------------------------------------------------------\n\nexport function cmdMigrate(db: Database): void {\n if (!existsSync(SESSION_REGISTRY_PATH)) {\n console.error(err(`session-registry.json not found: ${SESSION_REGISTRY_PATH}`));\n process.exit(1);\n }\n\n let registry: LegacyJsonRegistry;\n try {\n const raw = readFileSync(SESSION_REGISTRY_PATH, \"utf8\");\n registry = JSON.parse(raw) as LegacyJsonRegistry;\n } catch (e) {\n console.error(err(`Failed to parse session-registry.json: ${e}`));\n process.exit(1);\n }\n\n const projects = registry.projects ?? [];\n if (!projects.length) {\n console.log(warn(\"No projects found in session-registry.json.\"));\n return;\n }\n\n console.log(dim(`Migrating ${projects.length} project(s) from session-registry.json ...`));\n\n let projectsNew = 0;\n let projectsSkipped = 0;\n let sessionsNew = 0;\n const errors: string[] = [];\n\n for (const entry of projects) {\n if (!entry.original_path || !entry.encoded_dir) {\n errors.push(`Skipping entry with missing path: ${JSON.stringify(entry)}`);\n continue;\n }\n\n const rootPath = entry.original_path;\n const encodedDir = entry.encoded_dir;\n const slug = slugify(rootPath);\n\n try {\n const { isNew, id } = upsertProject(db, slug, rootPath, encodedDir);\n if (isNew) projectsNew++;\n else projectsSkipped++;\n\n const notes = entry.notes ?? [];\n for (const filename of notes) {\n const parsed = parseSessionFilename(filename);\n if (!parsed) continue;\n\n const isNewSession = upsertSession(db, id, parsed.number, parsed.date, parsed.slug, parsed.title, parsed.filename);\n if (isNewSession) sessionsNew++;\n }\n } catch (e) {\n errors.push(`Error processing ${entry.encoded_dir}: ${String(e)}`);\n }\n }\n\n console.log(ok(\"Migration complete.\"));\n console.log(dim(` Projects: ${projectsNew} new, ${projectsSkipped} already existed`));\n console.log(dim(` Sessions: ${sessionsNew} new`));\n\n if (errors.length) {\n console.log();\n console.log(warn(` ${errors.length} error(s) during migration:`));\n for (const e of errors.slice(0, 5)) {\n console.log(dim(` ${e}`));\n }\n if (errors.length > 5) {\n console.log(dim(` ... and ${errors.length - 5} more`));\n }\n }\n}\n","/** Registry command registration and simple sub-commands (stats, rebuild, lookup). */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport { ok, warn, err, dim, bold, fmtDate } from \"../../utils.js\";\nimport { homedir } from \"node:os\";\nimport { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { cmdScan, loadScanConfig, saveScanConfig, resolveHome } from \"./scan.js\";\nimport { cmdMigrate } from \"./migrate.js\";\n\n// ---------------------------------------------------------------------------\n// stats\n// ---------------------------------------------------------------------------\n\nfunction cmdStats(db: Database): void {\n const totalProjects = (db.prepare(\"SELECT COUNT(*) AS n FROM projects\").get() as { n: number }).n;\n const activeProjects = (db.prepare(\"SELECT COUNT(*) AS n FROM projects WHERE status = 'active'\").get() as { n: number }).n;\n const archivedProjects = (db.prepare(\"SELECT COUNT(*) AS n FROM projects WHERE status = 'archived'\").get() as { n: number }).n;\n const totalSessions = (db.prepare(\"SELECT COUNT(*) AS n FROM sessions\").get() as { n: number }).n;\n const totalTags = (db.prepare(\"SELECT COUNT(*) AS n FROM tags\").get() as { n: number }).n;\n\n const lastProject = db\n .prepare(\"SELECT updated_at FROM projects ORDER BY updated_at DESC LIMIT 1\")\n .get() as { updated_at: number } | undefined;\n\n const lastSession = db\n .prepare(\"SELECT created_at FROM sessions ORDER BY created_at DESC LIMIT 1\")\n .get() as { created_at: number } | undefined;\n\n console.log();\n console.log(bold(\" PAI Registry Stats\"));\n console.log();\n console.log(` ${bold(\"Projects:\")} ${totalProjects}`);\n console.log(` ${bold(\" Active:\")} ${activeProjects}`);\n console.log(` ${bold(\" Archived:\")} ${archivedProjects}`);\n console.log(` ${bold(\"Sessions:\")} ${totalSessions}`);\n console.log(` ${bold(\"Tags:\")} ${totalTags}`);\n if (lastProject) {\n console.log(` ${bold(\"Last updated:\")} ${fmtDate(lastProject.updated_at)}`);\n }\n if (lastSession) {\n console.log(` ${bold(\"Last session:\")} ${fmtDate(lastSession.created_at)}`);\n }\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// rebuild\n// ---------------------------------------------------------------------------\n\nfunction cmdRebuild(db: Database): void {\n console.log(warn(\"Rebuilding registry — all existing data will be erased.\"));\n console.log(dim(\"Clearing all tables ...\"));\n\n db.exec(`\n DELETE FROM compaction_log;\n DELETE FROM session_tags;\n DELETE FROM project_tags;\n DELETE FROM aliases;\n DELETE FROM sessions;\n DELETE FROM projects;\n DELETE FROM tags;\n DELETE FROM schema_version;\n `);\n\n console.log(dim(\"Registry cleared. Re-scanning ...\"));\n cmdScan(db);\n}\n\n// ---------------------------------------------------------------------------\n// lookup\n// ---------------------------------------------------------------------------\n\nfunction cmdLookup(db: Database, fsPath: string): void {\n const resolved = resolve(fsPath);\n\n const row = db\n .prepare(\"SELECT slug FROM projects WHERE root_path = ?\")\n .get(resolved) as { slug: string } | undefined;\n\n if (!row) {\n process.exit(1);\n }\n\n process.stdout.write(row.slug + \"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerRegistryCommands(\n registryCmd: Command,\n getDb: () => Database\n): void {\n // pai registry scan\n registryCmd\n .command(\"scan\")\n .description(\"Walk ~/.claude/projects/ and configured scan_dirs, upsert all projects\")\n .option(\"--add-dir <path>\", \"Add a directory to scan_dirs config\")\n .option(\"--remove-dir <path>\", \"Remove a directory from scan_dirs config\")\n .option(\"--show-dirs\", \"Show currently configured scan directories\")\n .option(\"--quick\", \"Minimal output mode (used by hooks and daemon)\")\n .action((opts: { addDir?: string; removeDir?: string; showDirs?: boolean; quick?: boolean }) => {\n if (opts.showDirs) {\n const config = loadScanConfig();\n if (!config.scan_dirs.length) {\n console.log(dim(\" No extra scan directories configured.\"));\n console.log(dim(\" Use --add-dir <path> to add one.\"));\n } else {\n console.log(bold(\" Configured scan directories:\"));\n for (const d of config.scan_dirs) {\n console.log(` ${d}`);\n }\n }\n return;\n }\n if (opts.addDir) {\n const config = loadScanConfig();\n const resolved = resolveHome(opts.addDir);\n if (!existsSync(resolved)) {\n console.error(err(`Directory not found: ${resolved}`));\n process.exit(1);\n }\n const display = resolved.startsWith(homedir())\n ? \"~\" + resolved.slice(homedir().length)\n : resolved;\n if (config.scan_dirs.includes(display) || config.scan_dirs.includes(resolved)) {\n console.log(warn(`Already configured: ${display}`));\n } else {\n config.scan_dirs.push(display);\n saveScanConfig(config);\n console.log(ok(`Added scan directory: ${bold(display)}`));\n }\n }\n if (opts.removeDir) {\n const config = loadScanConfig();\n const resolved = resolveHome(opts.removeDir);\n const display = resolved.startsWith(homedir())\n ? \"~\" + resolved.slice(homedir().length)\n : resolved;\n const before = config.scan_dirs.length;\n config.scan_dirs = config.scan_dirs.filter((d) => resolveHome(d) !== resolved);\n if (config.scan_dirs.length < before) {\n saveScanConfig(config);\n console.log(ok(`Removed scan directory: ${bold(display)}`));\n } else {\n console.log(warn(`Not found in config: ${display}`));\n }\n }\n if (!opts.addDir && !opts.removeDir) {\n cmdScan(getDb(), { quick: opts.quick });\n }\n });\n\n // pai registry migrate\n registryCmd\n .command(\"migrate\")\n .description(\"Import data from ~/.claude/session-registry.json\")\n .action(() => {\n cmdMigrate(getDb());\n });\n\n // pai registry stats\n registryCmd\n .command(\"stats\")\n .description(\"Show summary statistics for the registry\")\n .action(() => {\n cmdStats(getDb());\n });\n\n // pai registry rebuild\n registryCmd\n .command(\"rebuild\")\n .description(\"Erase all registry data and rebuild from the filesystem (destructive)\")\n .action(() => {\n cmdRebuild(getDb());\n });\n\n // pai registry lookup --path <path>\n registryCmd\n .command(\"lookup\")\n .description(\"Find the project slug for a filesystem path (for use in scripts)\")\n .requiredOption(\"--path <path>\", \"Filesystem path to look up\")\n .action((opts: { path: string }) => {\n cmdLookup(getDb(), opts.path);\n });\n}\n","/** Memory embed command: generate embeddings for un-embedded chunks. */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport { openFederation } from \"../../../memory/db.js\";\nimport { embedChunks } from \"../../../memory/indexer.js\";\nimport { dim, bold, ok, err } from \"../../utils.js\";\n\n// ---------------------------------------------------------------------------\n// Shared embed runner (used by both index --embed and embed sub-command)\n// ---------------------------------------------------------------------------\n\nexport async function runEmbed(\n federation: Database,\n projectId?: number,\n projectSlug?: string,\n batchSize = 50,\n): Promise<void> {\n const label = projectSlug ? `project ${projectSlug}` : \"all projects\";\n console.log(dim(`Generating embeddings for ${label} (this may take a while on first run)...`));\n\n const { chunksEmbedded } = await embedChunks(\n federation,\n projectId,\n batchSize,\n (done, total) => {\n process.stdout.write(`\\r ${done} / ${total} chunks embedded...`);\n },\n );\n\n process.stdout.write(\"\\r\");\n console.log(ok(`Done.`) + ` ${bold(String(chunksEmbedded))} chunks embedded`);\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerEmbedCommand(\n memoryCmd: Command,\n getDb: () => Database,\n): void {\n memoryCmd\n .command(\"embed [project-slug]\")\n .description(\"Generate embeddings for un-embedded chunks (Phase 2.5)\")\n .option(\"--batch-size <n>\", \"Chunks to embed per batch\", \"50\")\n .action(async (projectSlug: string | undefined, opts: { batchSize?: string }) => {\n const registryDb = getDb();\n\n let federation: Database;\n try {\n federation = openFederation();\n } catch (e) {\n console.error(err(`Failed to open federation database: ${e}`));\n process.exit(1);\n }\n\n if (projectSlug) {\n const project = registryDb\n .prepare(\"SELECT id, slug FROM projects WHERE slug = ?\")\n .get(projectSlug) as { id: number; slug: string } | undefined;\n\n if (!project) {\n console.error(err(`Project not found: ${projectSlug}`));\n process.exit(1);\n }\n\n await runEmbed(federation, project.id, project.slug, parseInt(opts.batchSize ?? \"50\", 10));\n } else {\n await runEmbed(federation, undefined, undefined, parseInt(opts.batchSize ?? \"50\", 10));\n }\n });\n}\n","/** Memory index command: index one or all projects into the memory store. */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport { openFederation } from \"../../../memory/db.js\";\nimport { indexProject, indexAll } from \"../../../memory/indexer.js\";\nimport { dim, bold, ok, err } from \"../../utils.js\";\nimport { PaiClient } from \"../../../daemon/ipc-client.js\";\nimport { loadConfig } from \"../../../daemon/config.js\";\nimport { runEmbed } from \"./embed.js\";\n\nexport function registerIndexCommand(\n memoryCmd: Command,\n getDb: () => Database,\n): void {\n memoryCmd\n .command(\"index [project-slug]\")\n .description(\"Index memory files for one project or all projects\")\n .option(\"--all\", \"Index all active projects (default when no slug given)\")\n .option(\"--embed\", \"Also generate embeddings for newly indexed chunks (Phase 2.5)\")\n .option(\"--direct\", \"Skip daemon IPC and run index directly (for debugging)\")\n .action(async (projectSlug: string | undefined, opts: { all?: boolean; embed?: boolean; direct?: boolean }) => {\n const registryDb = getDb();\n\n // If daemon is running and no --direct flag, trigger via IPC (non-blocking)\n if (!opts.direct && !projectSlug) {\n try {\n const config = loadConfig();\n const client = new PaiClient(config.socketPath);\n await client.triggerIndex();\n console.log(ok(\"Index triggered in daemon.\") + dim(\" Check daemon logs for progress.\"));\n console.log(dim(\" Run `pai daemon logs` to watch progress.\"));\n return;\n } catch {\n console.log(dim(\"Daemon not running. Running direct index...\"));\n }\n }\n\n let federation: Database;\n try {\n federation = openFederation();\n } catch (e) {\n console.error(err(`Failed to open federation database: ${e}`));\n process.exit(1);\n }\n\n if (projectSlug) {\n const project = registryDb\n .prepare(\"SELECT id, slug, display_name, root_path FROM projects WHERE slug = ? AND status = 'active'\")\n .get(projectSlug) as\n | { id: number; slug: string; display_name: string; root_path: string }\n | undefined;\n\n if (!project) {\n console.error(err(`Project not found or not active: ${projectSlug}`));\n process.exit(1);\n }\n\n console.log(dim(`Indexing ${project.display_name} (${project.slug})...`));\n const result = await indexProject(federation, project.id, project.root_path);\n\n console.log(\n ok(`Done.`) +\n ` ${bold(String(result.filesProcessed))} files indexed` +\n `, ${bold(String(result.chunksCreated))} chunks created` +\n `, ${dim(String(result.filesSkipped) + \" skipped (unchanged)\")}`,\n );\n\n if (opts.embed) {\n await runEmbed(federation, project.id, project.slug);\n }\n\n } else if (opts.all || !projectSlug) {\n console.log(dim(\"Indexing all active projects...\"));\n\n const { projects, result } = await indexAll(federation, registryDb);\n\n console.log(\n ok(`Done.`) +\n ` ${bold(String(projects))} projects` +\n `, ${bold(String(result.filesProcessed))} files indexed` +\n `, ${bold(String(result.chunksCreated))} chunks created` +\n `, ${dim(String(result.filesSkipped) + \" skipped (unchanged)\")}`,\n );\n\n if (opts.embed) {\n await runEmbed(federation);\n }\n }\n });\n}\n","/** Memory search command: BM25/semantic/hybrid search across federation.db. */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport chalk from \"chalk\";\nimport { openFederation } from \"../../../memory/db.js\";\nimport { searchMemory, populateSlugs, type SearchResult } from \"../../../memory/search.js\";\nimport { dim, bold, ok, warn, err } from \"../../utils.js\";\nimport { loadConfig } from \"../../../daemon/config.js\";\nimport { createStorageBackend } from \"../../../storage/factory.js\";\n\n// ---------------------------------------------------------------------------\n// Helper\n// ---------------------------------------------------------------------------\n\nfunction tierColor(tier: string): string {\n switch (tier) {\n case \"evergreen\": return chalk.green(tier);\n case \"daily\": return chalk.yellow(tier);\n case \"topic\": return chalk.blue(tier);\n case \"session\": return chalk.dim(tier);\n default: return tier;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerSearchCommand(\n memoryCmd: Command,\n getDb: () => Database,\n): void {\n memoryCmd\n .command(\"search <query>\")\n .description(\"Search indexed memory (BM25 keyword, semantic, or hybrid)\")\n .option(\"--project <slug>\", \"Restrict search to a specific project\")\n .option(\"--source <source>\", \"Restrict to 'memory' or 'notes'\")\n .option(\"--limit <n>\", \"Maximum results to return\")\n .option(\"--mode <mode>\", \"Search mode: keyword (default), semantic, hybrid\")\n .option(\"--no-rerank\", \"Skip cross-encoder reranking (reranking is on by default)\")\n .option(\"--recency <days>\", \"Apply recency boost: score halves every N days. 0 = off\")\n .action(\n async (\n query: string,\n opts: { project?: string; source?: string; limit?: string; mode?: string; rerank: boolean; recency?: string },\n ) => {\n const registryDb = getDb();\n\n let federation: Database;\n try {\n federation = openFederation();\n } catch (e) {\n console.error(err(`Failed to open federation database: ${e}`));\n process.exit(1);\n }\n\n const config = loadConfig();\n const searchConfig = config.search;\n\n const maxResults = parseInt(opts.limit ?? String(searchConfig.defaultLimit), 10);\n const mode = (opts.mode ?? searchConfig.mode) as \"keyword\" | \"semantic\" | \"hybrid\";\n\n if (![\"keyword\", \"semantic\", \"hybrid\"].includes(mode)) {\n console.error(err(`Invalid mode: ${mode}. Use keyword, semantic, or hybrid.`));\n process.exit(1);\n }\n\n let projectIds: number[] | undefined;\n if (opts.project) {\n const project = registryDb\n .prepare(\"SELECT id FROM projects WHERE slug = ?\")\n .get(opts.project) as { id: number } | undefined;\n\n if (!project) {\n console.error(warn(`Project not found: ${opts.project} — searching all projects`));\n } else {\n projectIds = [project.id];\n }\n }\n\n const sources = opts.source ? [opts.source] : undefined;\n const searchOpts = { projectIds, sources, maxResults };\n\n let results: SearchResult[];\n\n if (mode === \"keyword\") {\n results = searchMemory(federation, query, searchOpts);\n\n } else if (mode === \"semantic\" || mode === \"hybrid\") {\n const backend = await createStorageBackend(config);\n\n try {\n const { generateEmbedding } = await import(\"../../../memory/embeddings.js\");\n\n console.log(dim(\"Generating query embedding...\"));\n const queryEmbedding = await generateEmbedding(query, true);\n\n if (mode === \"semantic\") {\n results = await backend.searchSemantic(queryEmbedding, searchOpts);\n } else {\n // Hybrid: combine keyword (BM25) and semantic results with min-max normalization\n const [keywordResults, semanticResults] = await Promise.all([\n backend.searchKeyword(query, { ...searchOpts, maxResults: 500 }),\n backend.searchSemantic(queryEmbedding, { ...searchOpts, maxResults: 500 }),\n ]);\n\n if (keywordResults.length === 0 && semanticResults.length === 0) {\n results = [];\n } else {\n const keyFor = (r: SearchResult) =>\n `${r.projectId}:${r.path}:${r.startLine}:${r.endLine}`;\n\n function minMaxNormalize(items: SearchResult[]): Map<string, number> {\n if (items.length === 0) return new Map();\n const min = Math.min(...items.map((r) => r.score));\n const max = Math.max(...items.map((r) => r.score));\n const range = max - min;\n const m = new Map<string, number>();\n for (const r of items) {\n m.set(keyFor(r), range === 0 ? 1 : (r.score - min) / range);\n }\n return m;\n }\n\n const kwNorm = minMaxNormalize(keywordResults);\n const semNorm = minMaxNormalize(semanticResults);\n const allKeys = new Set<string>([\n ...keywordResults.map(keyFor),\n ...semanticResults.map(keyFor),\n ]);\n const metaMap = new Map<string, SearchResult>();\n for (const r of [...keywordResults, ...semanticResults]) {\n metaMap.set(keyFor(r), r);\n }\n\n const combined: SearchResult[] = [];\n for (const key of allKeys) {\n const meta = metaMap.get(key)!;\n const combinedScore = 0.5 * (kwNorm.get(key) ?? 0) + 0.5 * (semNorm.get(key) ?? 0);\n combined.push({ ...meta, score: combinedScore });\n }\n\n results = combined.sort((a, b) => b.score - a.score).slice(0, maxResults);\n }\n }\n } finally {\n await backend.close();\n }\n } else {\n results = [];\n }\n\n if (!results || results.length === 0) {\n console.log(dim(`No results found for: \"${query}\" (mode: ${mode})`));\n return;\n }\n\n // Cross-encoder reranking (on by default, skip with --no-rerank)\n if (opts.rerank !== false) {\n const { rerankResults } = await import(\"../../../memory/reranker.js\");\n console.log(dim(\"Reranking with cross-encoder...\"));\n results = await rerankResults(query, results, { topK: maxResults });\n }\n\n // Recency boost (applied after reranking)\n const recencyDays = parseInt(opts.recency ?? String(searchConfig.recencyBoostDays), 10);\n if (recencyDays > 0) {\n const { applyRecencyBoost } = await import(\"../../../memory/search.js\");\n console.log(dim(`Applying recency boost (half-life: ${recencyDays} days)...`));\n results = applyRecencyBoost(results, recencyDays);\n }\n\n const withSlugs = populateSlugs(results, registryDb);\n const rerankLabel = opts.rerank !== false ? \" +rerank\" : \"\";\n const modeLabel = mode !== \"keyword\" ? ` [${mode}${rerankLabel}]` : (opts.rerank !== false ? ` [rerank]` : \"\");\n\n console.log(\n `\\n ${bold(`Search results for: \"${query}\"`)}${modeLabel} ${dim(`(${withSlugs.length} found)`)}\\n`,\n );\n\n for (const result of withSlugs) {\n const projectLabel = result.projectSlug\n ? chalk.cyan(result.projectSlug)\n : chalk.cyan(String(result.projectId));\n\n const tierLabel = tierColor(result.tier);\n const scoreLabel = dim(`score: ${result.score.toFixed(4)}`);\n const locationLabel = dim(`${result.path}:${result.startLine}-${result.endLine}`);\n\n console.log(` ${projectLabel} ${tierLabel} ${locationLabel} ${scoreLabel}`);\n\n const snippetLines = result.snippet\n .split(\"\\n\")\n .slice(0, 3)\n .map((l) => ` ${l}`);\n console.log(snippetLines.join(\"\\n\"));\n console.log();\n }\n },\n );\n}\n","/** Memory status and settings commands for the PAI memory index. */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport chalk from \"chalk\";\nimport { openFederation } from \"../../../memory/db.js\";\nimport { renderTable, dim, bold, ok, err, warn, fmtDate } from \"../../utils.js\";\nimport { loadConfig, CONFIG_FILE, ensureConfigDir } from \"../../../daemon/config.js\";\n\nfunction tierColor(tier: string): string {\n switch (tier) {\n case \"evergreen\": return chalk.green(tier);\n case \"daily\": return chalk.yellow(tier);\n case \"topic\": return chalk.blue(tier);\n case \"session\": return chalk.dim(tier);\n default: return tier;\n }\n}\n\nexport function registerStatsCommands(\n memoryCmd: Command,\n getDb: () => Database,\n): void {\n\n // -------------------------------------------------------------------------\n // pai memory status [project-slug]\n // -------------------------------------------------------------------------\n\n memoryCmd\n .command(\"status [project-slug]\")\n .description(\"Show memory index statistics\")\n .action((projectSlug: string | undefined) => {\n const registryDb = getDb();\n\n let federation: Database;\n try {\n federation = openFederation();\n } catch (e) {\n console.error(err(`Failed to open federation database: ${e}`));\n process.exit(1);\n }\n\n if (projectSlug) {\n const project = registryDb\n .prepare(\"SELECT id, slug, display_name FROM projects WHERE slug = ?\")\n .get(projectSlug) as\n | { id: number; slug: string; display_name: string }\n | undefined;\n\n if (!project) {\n console.error(err(`Project not found: ${projectSlug}`));\n process.exit(1);\n }\n\n const fileStats = federation\n .prepare(\"SELECT COUNT(*) as files FROM memory_files WHERE project_id = ?\")\n .get(project.id) as { files: number };\n\n const chunkStats = federation\n .prepare(\"SELECT COUNT(*) as chunks FROM memory_chunks WHERE project_id = ?\")\n .get(project.id) as { chunks: number };\n\n const lastUpdate = federation\n .prepare(\"SELECT MAX(updated_at) as last_at FROM memory_chunks WHERE project_id = ?\")\n .get(project.id) as { last_at: number | null };\n\n const tierBreakdown = federation\n .prepare(\"SELECT tier, COUNT(*) as n FROM memory_chunks WHERE project_id = ? GROUP BY tier ORDER BY n DESC\")\n .all(project.id) as Array<{ tier: string; n: number }>;\n\n console.log(`\\n ${bold(project.display_name)} ${dim(`(${project.slug})`)}\\n`);\n console.log(` ${bold(\"Files indexed:\")} ${fileStats.files}`);\n console.log(` ${bold(\"Chunks:\")} ${chunkStats.chunks}`);\n console.log(` ${bold(\"Last indexed:\")} ${lastUpdate.last_at ? fmtDate(lastUpdate.last_at) : dim(\"never\")}`);\n\n if (tierBreakdown.length > 0) {\n console.log(`\\n ${bold(\"By tier:\")}`);\n for (const row of tierBreakdown) {\n console.log(` ${tierColor(row.tier).padEnd(20)} ${row.n} chunks`);\n }\n }\n console.log();\n\n } else {\n const globalFiles = federation.prepare(\"SELECT COUNT(*) as n FROM memory_files\").get() as { n: number };\n const globalChunks = federation.prepare(\"SELECT COUNT(*) as n FROM memory_chunks\").get() as { n: number };\n const lastUpdate = federation.prepare(\"SELECT MAX(updated_at) as last_at FROM memory_chunks\").get() as { last_at: number | null };\n\n const projectStats = federation\n .prepare(\n `SELECT\n mf.project_id,\n COUNT(DISTINCT mf.path) as files,\n COUNT(mc.id) as chunks,\n MAX(mc.updated_at) as last_at\n FROM memory_files mf\n LEFT JOIN memory_chunks mc ON mc.project_id = mf.project_id AND mc.path = mf.path\n GROUP BY mf.project_id\n ORDER BY chunks DESC`,\n )\n .all() as Array<{ project_id: number; files: number; chunks: number; last_at: number | null }>;\n\n const projectIds = projectStats.map((p) => p.project_id);\n const slugMap = new Map<number, string>();\n if (projectIds.length > 0) {\n const placeholders = projectIds.map(() => \"?\").join(\", \");\n const slugRows = registryDb\n .prepare(`SELECT id, slug, display_name FROM projects WHERE id IN (${placeholders})`)\n .all(...projectIds) as Array<{ id: number; slug: string; display_name: string }>;\n for (const row of slugRows) {\n slugMap.set(row.id, row.slug);\n }\n }\n\n console.log(`\\n ${bold(\"PAI Memory Index — Global Status\")}\\n`);\n console.log(` ${bold(\"Total files:\")} ${globalFiles.n} ${bold(\"Total chunks:\")} ${globalChunks.n}`);\n console.log(` ${bold(\"Last indexed:\")} ${lastUpdate.last_at ? fmtDate(lastUpdate.last_at) : dim(\"never\")}`);\n\n if (projectStats.length > 0) {\n console.log();\n const rows = projectStats.map((p) => [\n chalk.cyan(slugMap.get(p.project_id) ?? String(p.project_id)),\n String(p.files),\n String(p.chunks),\n p.last_at ? fmtDate(p.last_at) : dim(\"never\"),\n ]);\n console.log(renderTable([\"Project\", \"Files\", \"Chunks\", \"Last Indexed\"], rows));\n } else {\n console.log(dim(\"\\n No projects indexed yet. Run `pai memory index --all` to start.\"));\n }\n console.log();\n }\n });\n\n // -------------------------------------------------------------------------\n // pai memory settings [key] [value]\n // -------------------------------------------------------------------------\n\n memoryCmd\n .command(\"settings [key] [value]\")\n .description(\"View or modify search settings in ~/.config/pai/config.json\")\n .action((key: string | undefined, value: string | undefined) => {\n const config = loadConfig();\n const search = config.search;\n\n if (!key) {\n console.log(`\\n ${bold(\"PAI Memory — Search Settings\")}\\n`);\n console.log(` ${bold(\"mode:\")} ${search.mode}`);\n console.log(` ${bold(\"rerank:\")} ${search.rerank}`);\n console.log(` ${bold(\"recencyBoostDays:\")} ${search.recencyBoostDays}`);\n console.log(` ${bold(\"defaultLimit:\")} ${search.defaultLimit}`);\n console.log(` ${bold(\"snippetLength:\")} ${search.snippetLength}`);\n console.log();\n console.log(dim(` Config file: ${CONFIG_FILE}`));\n console.log(dim(` Edit directly or use: pai memory settings <key> <value>`));\n console.log();\n return;\n }\n\n if (!value) {\n const val = (search as unknown as Record<string, unknown>)[key];\n if (val === undefined) {\n console.error(err(`Unknown setting: ${key}`));\n console.log(dim(` Valid keys: mode, rerank, recencyBoostDays, defaultLimit, snippetLength`));\n process.exit(1);\n }\n console.log(String(val));\n return;\n }\n\n const validKeys = new Set([\"mode\", \"rerank\", \"recencyBoostDays\", \"defaultLimit\", \"snippetLength\"]);\n if (!validKeys.has(key)) {\n console.error(err(`Unknown setting: ${key}`));\n console.log(dim(` Valid keys: ${[...validKeys].join(\", \")}`));\n process.exit(1);\n }\n\n let fileConfig: Record<string, unknown> = {};\n if (existsSync(CONFIG_FILE)) {\n try {\n fileConfig = JSON.parse(readFileSync(CONFIG_FILE, \"utf-8\")) as Record<string, unknown>;\n } catch {\n console.error(err(`Could not parse ${CONFIG_FILE}`));\n process.exit(1);\n }\n }\n\n if (!fileConfig.search || typeof fileConfig.search !== \"object\") {\n fileConfig.search = {};\n }\n\n let parsed: string | number | boolean;\n if (key === \"mode\") {\n if (![\"keyword\", \"semantic\", \"hybrid\"].includes(value)) {\n console.error(err(`Invalid mode: ${value}. Must be keyword, semantic, or hybrid.`));\n process.exit(1);\n }\n parsed = value;\n } else if (key === \"rerank\") {\n parsed = value === \"true\" || value === \"1\" || value === \"on\";\n } else {\n parsed = parseInt(value, 10);\n if (isNaN(parsed)) {\n console.error(err(`Invalid number: ${value}`));\n process.exit(1);\n }\n }\n\n (fileConfig.search as Record<string, unknown>)[key] = parsed;\n\n try {\n ensureConfigDir();\n writeFileSync(CONFIG_FILE, JSON.stringify(fileConfig, null, 2) + \"\\n\", \"utf-8\");\n console.log(ok(`Set search.${key} = ${parsed}`));\n console.log(dim(` Restart daemon to apply: pai daemon restart`));\n } catch (e) {\n console.error(err(`Could not write config: ${e}`));\n process.exit(1);\n }\n });\n}\n","/** Barrel: registers all memory sub-commands and re-exports registerMemoryCommands. */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport { registerIndexCommand } from \"./index-cmd.js\";\nimport { registerEmbedCommand } from \"./embed.js\";\nimport { registerSearchCommand } from \"./search.js\";\nimport { registerStatsCommands } from \"./stats.js\";\n\nexport function registerMemoryCommands(\n memoryCmd: Command,\n getDb: () => Database,\n): void {\n registerIndexCommand(memoryCmd, getDb);\n registerEmbedCommand(memoryCmd, getDb);\n registerSearchCommand(memoryCmd, getDb);\n registerStatsCommands(memoryCmd, getDb);\n}\n","/**\n * pai mcp <sub-command>\n *\n * install — Register the PAI MCP server in ~/.claude.json\n * status — Show whether the PAI MCP server is registered and the binary exists\n */\n\nimport type { Command } from \"commander\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { ok, warn, err, dim, bold } from \"../utils.js\";\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nconst CLAUDE_JSON_PATH = join(homedir(), \".claude.json\");\n\n/**\n * Resolve the absolute path to the built MCP entry point.\n *\n * tsdown bundles all CLI commands into a single dist/cli/index.mjs file, so\n * import.meta.url always resolves to dist/cli/index.mjs at runtime.\n * From dist/cli/ we go up one level to dist/ and then into mcp/index.mjs.\n */\nfunction getMcpBinPath(): string {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n // dist/cli/index.mjs → dist/ → dist/mcp/index.mjs\n return join(__dirname, \"../mcp/index.mjs\");\n}\n\n// ---------------------------------------------------------------------------\n// Read / write ~/.claude.json safely\n// ---------------------------------------------------------------------------\n\nfunction readClaudeJson(): Record<string, unknown> {\n if (!existsSync(CLAUDE_JSON_PATH)) return {};\n try {\n const raw = readFileSync(CLAUDE_JSON_PATH, \"utf8\");\n return JSON.parse(raw) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nfunction writeClaudeJson(data: Record<string, unknown>): void {\n writeFileSync(CLAUDE_JSON_PATH, JSON.stringify(data, null, 2) + \"\\n\", \"utf8\");\n}\n\n// ---------------------------------------------------------------------------\n// install\n// ---------------------------------------------------------------------------\n\nfunction cmdInstall(): void {\n const mcpBin = getMcpBinPath();\n\n const config = readClaudeJson();\n\n // Ensure mcpServers key exists\n if (typeof config.mcpServers !== \"object\" || config.mcpServers === null) {\n config.mcpServers = {};\n }\n\n const servers = config.mcpServers as Record<string, unknown>;\n\n if (\"pai\" in servers) {\n console.log(warn(\"PAI MCP server is already registered in ~/.claude.json.\"));\n console.log(dim(` Entry: ${JSON.stringify(servers[\"pai\"])}`));\n console.log(dim(\" Use `pai mcp status` to verify the configuration.\"));\n return;\n }\n\n servers[\"pai\"] = {\n command: \"node\",\n args: [mcpBin],\n };\n\n try {\n writeClaudeJson(config);\n } catch (e) {\n console.error(err(`Failed to write ~/.claude.json: ${e}`));\n process.exit(1);\n }\n\n console.log(ok(\"PAI MCP server registered in ~/.claude.json.\"));\n console.log(dim(` Binary: ${mcpBin}`));\n console.log(dim(\"\"));\n console.log(dim(\" Restart Claude Code to activate the PAI MCP tools:\"));\n console.log(dim(\" memory_search, memory_get, project_info,\"));\n console.log(dim(\" project_list, session_list, registry_search\"));\n\n if (!existsSync(mcpBin)) {\n console.log();\n console.log(\n warn(` Note: MCP binary not found at ${mcpBin}`)\n );\n console.log(dim(\" Run `bun run build` to compile it first.\"));\n }\n}\n\n// ---------------------------------------------------------------------------\n// status\n// ---------------------------------------------------------------------------\n\nfunction cmdStatus(): void {\n const mcpBin = getMcpBinPath();\n const config = readClaudeJson();\n\n const servers =\n typeof config.mcpServers === \"object\" && config.mcpServers !== null\n ? (config.mcpServers as Record<string, unknown>)\n : {};\n\n const registered = \"pai\" in servers;\n const binExists = existsSync(mcpBin);\n\n console.log();\n console.log(bold(\" PAI MCP Server Status\"));\n console.log();\n\n if (registered) {\n const entry = servers[\"pai\"];\n console.log(ok(` Registered in ~/.claude.json`));\n console.log(dim(` Config: ${JSON.stringify(entry)}`));\n } else {\n console.log(warn(` NOT registered in ~/.claude.json`));\n console.log(dim(` Run: pai mcp install`));\n }\n\n console.log();\n\n if (binExists) {\n console.log(ok(` MCP binary found: ${mcpBin}`));\n } else {\n console.log(warn(` MCP binary NOT found: ${mcpBin}`));\n console.log(dim(\" Run: bun run build\"));\n }\n\n console.log();\n\n if (registered && binExists) {\n console.log(dim(\" Status: READY — restart Claude Code to use PAI tools\"));\n } else if (registered && !binExists) {\n console.log(warn(\" Status: NEEDS BUILD — run `bun run build`\"));\n } else {\n console.log(dim(\" Status: NOT INSTALLED — run `pai mcp install`\"));\n }\n\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerMcpCommands(mcpCmd: Command): void {\n mcpCmd\n .command(\"install\")\n .description(\n \"Register the PAI MCP server in ~/.claude.json (restart Claude Code to activate)\"\n )\n .action(() => {\n cmdInstall();\n });\n\n mcpCmd\n .command(\"status\")\n .description(\n \"Show whether the PAI MCP server is registered and the binary exists\"\n )\n .action(() => {\n cmdStatus();\n });\n}\n","/**\n * pai daemon <sub-command>\n *\n * serve — Start the PAI daemon in the foreground\n * status — Query daemon status via IPC\n * restart — Send SIGTERM to running daemon (launchd will restart it)\n * install — Write launchd plist + update ~/.claude.json to use the shim\n * uninstall — Remove launchd plist + revert ~/.claude.json to direct MCP\n * logs — Tail the daemon log file\n */\n\nimport type { Command } from \"commander\";\nimport {\n existsSync,\n readFileSync,\n writeFileSync,\n unlinkSync,\n mkdirSync,\n} from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { execSync, spawnSync } from \"node:child_process\";\nimport { ok, warn, err, dim, bold } from \"../utils.js\";\nimport { loadConfig } from \"../../daemon/config.js\";\nimport { PaiClient } from \"../../daemon/ipc-client.js\";\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nconst HOME = homedir();\nconst CLAUDE_JSON_PATH = join(HOME, \".claude.json\");\nconst PLIST_LABEL = \"com.pai.pai-daemon\";\nconst LAUNCH_AGENTS_DIR = join(HOME, \"Library\", \"LaunchAgents\");\nconst PLIST_PATH = join(LAUNCH_AGENTS_DIR, `${PLIST_LABEL}.plist`);\nconst DAEMON_LOG = \"/tmp/pai-daemon.log\";\n\n/**\n * Resolve the absolute path to the built daemon entry point.\n * tsdown bundles into dist/daemon/index.mjs (or similar).\n * From dist/cli/index.mjs → dist/ → dist/daemon/index.mjs\n */\nfunction getDaemonBinPath(): string {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n return join(__dirname, \"../daemon/index.mjs\");\n}\n\n/**\n * Resolve the absolute path to the built MCP shim entry point.\n * dist/cli/index.mjs → dist/ → dist/daemon-mcp/index.mjs\n */\nfunction getShimBinPath(): string {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n return join(__dirname, \"../daemon-mcp/index.mjs\");\n}\n\n// ---------------------------------------------------------------------------\n// claude.json helpers\n// ---------------------------------------------------------------------------\n\nfunction readClaudeJson(): Record<string, unknown> {\n if (!existsSync(CLAUDE_JSON_PATH)) return {};\n try {\n const raw = readFileSync(CLAUDE_JSON_PATH, \"utf8\");\n return JSON.parse(raw) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nfunction writeClaudeJson(data: Record<string, unknown>): void {\n writeFileSync(CLAUDE_JSON_PATH, JSON.stringify(data, null, 2) + \"\\n\", \"utf8\");\n}\n\n// ---------------------------------------------------------------------------\n// launchd plist generation\n// ---------------------------------------------------------------------------\n\nfunction generatePlist(daemonBin: string): string {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${PLIST_LABEL}</string>\n\n <key>ProgramArguments</key>\n <array>\n <string>/usr/local/bin/node</string>\n <string>${daemonBin}</string>\n <string>serve</string>\n </array>\n\n <key>KeepAlive</key>\n <true/>\n\n <key>ThrottleInterval</key>\n <integer>3</integer>\n\n <key>StandardOutPath</key>\n <string>${DAEMON_LOG}</string>\n\n <key>StandardErrorPath</key>\n <string>${DAEMON_LOG}</string>\n\n <key>RunAtLoad</key>\n <true/>\n\n <key>EnvironmentVariables</key>\n <dict>\n <key>PATH</key>\n <string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>\n </dict>\n</dict>\n</plist>\n`;\n}\n\n// ---------------------------------------------------------------------------\n// Command implementations\n// ---------------------------------------------------------------------------\n\nasync function cmdStatus(): Promise<void> {\n const config = loadConfig();\n const client = new PaiClient(config.socketPath);\n\n try {\n const status = await client.status();\n const s = status as Record<string, unknown>;\n\n console.log();\n console.log(bold(\" PAI Daemon Status\"));\n console.log();\n console.log(ok(` Daemon running`));\n console.log(dim(` Uptime: ${s[\"uptime\"]}s`));\n console.log(dim(` Socket: ${s[\"socketPath\"]}`));\n console.log(\n dim(\n ` Index: ${s[\"indexInProgress\"] ? \"in progress\" : \"idle\"} (interval: ${s[\"indexIntervalSecs\"]}s)`\n )\n );\n if (s[\"lastIndexTime\"]) {\n console.log(dim(` Last index: ${s[\"lastIndexTime\"]}`));\n }\n if (s[\"db\"]) {\n const db = s[\"db\"] as Record<string, unknown>;\n console.log(\n dim(\n ` DB: ${db[\"projects\"]} projects, ${db[\"files\"]} files, ${db[\"chunks\"]} chunks`\n )\n );\n }\n console.log();\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.log();\n console.log(warn(\" PAI Daemon Status\"));\n console.log();\n console.log(warn(` Daemon not running: ${msg}`));\n console.log(dim(\" Start with: pai daemon serve\"));\n console.log(dim(\" Or install as service: pai daemon install\"));\n console.log();\n process.exit(1);\n }\n}\n\nfunction cmdRestart(): void {\n // Find and signal the running daemon\n try {\n const result = spawnSync(\"pgrep\", [\"-f\", \"pai-daemon.*serve\"], {\n encoding: \"utf8\",\n });\n\n if (result.status !== 0 || !result.stdout.trim()) {\n console.log(warn(\"No running pai-daemon process found.\"));\n\n // If launchd is managing it, kick it via launchctl\n const unloadResult = spawnSync(\n \"launchctl\",\n [\"kickstart\", \"-k\", `gui/${process.getuid?.() ?? 501}/${PLIST_LABEL}`],\n { encoding: \"utf8\" }\n );\n if (unloadResult.status === 0) {\n console.log(ok(\"Sent kickstart to launchd.\"));\n } else {\n console.log(dim(\"Not managed by launchd either. Run: pai daemon serve\"));\n }\n return;\n }\n\n const pids = result.stdout\n .trim()\n .split(\"\\n\")\n .map((p) => p.trim());\n for (const pid of pids) {\n try {\n process.kill(parseInt(pid, 10), \"SIGTERM\");\n console.log(ok(`Sent SIGTERM to pid ${pid}.`));\n } catch {\n console.log(warn(`Could not signal pid ${pid}.`));\n }\n }\n console.log(dim(\"launchd will restart the daemon automatically.\"));\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(err(`restart error: ${msg}`));\n process.exit(1);\n }\n}\n\nfunction cmdInstall(): void {\n const daemonBin = getDaemonBinPath();\n const shimBin = getShimBinPath();\n\n console.log();\n console.log(bold(\" PAI Daemon Install\"));\n console.log();\n\n // 1. Write launchd plist\n if (!existsSync(LAUNCH_AGENTS_DIR)) {\n mkdirSync(LAUNCH_AGENTS_DIR, { recursive: true });\n }\n\n const plistContent = generatePlist(daemonBin);\n try {\n writeFileSync(PLIST_PATH, plistContent, \"utf8\");\n console.log(ok(` Wrote launchd plist: ${PLIST_PATH}`));\n } catch (e) {\n console.error(err(` Failed to write plist: ${e}`));\n process.exit(1);\n }\n\n // 2. Load the plist (unload first in case it was already there)\n try {\n spawnSync(\"launchctl\", [\"unload\", PLIST_PATH], { encoding: \"utf8\" });\n const loadResult = spawnSync(\"launchctl\", [\"load\", PLIST_PATH], {\n encoding: \"utf8\",\n });\n if (loadResult.status === 0) {\n console.log(ok(\" Loaded plist with launchctl.\"));\n } else {\n console.log(warn(` launchctl load: ${loadResult.stderr?.trim() ?? \"unknown error\"}`));\n }\n } catch {\n console.log(warn(\" Could not run launchctl. Load manually:\"));\n console.log(dim(` launchctl load ${PLIST_PATH}`));\n }\n\n // 3. Update ~/.claude.json to use the shim\n const config = readClaudeJson();\n\n if (typeof config.mcpServers !== \"object\" || config.mcpServers === null) {\n config.mcpServers = {};\n }\n\n const servers = config.mcpServers as Record<string, unknown>;\n\n // Check if already pointing to the shim\n const existing = servers[\"pai\"] as Record<string, unknown> | undefined;\n const existingArgs = existing?.args as string[] | undefined;\n const alreadyShim = existingArgs?.includes(shimBin);\n\n if (alreadyShim) {\n console.log(ok(\" ~/.claude.json already points to the shim.\"));\n } else {\n // Back up the existing entry if any\n if (existing) {\n servers[\"pai-legacy\"] = existing;\n console.log(dim(\" Backed up existing 'pai' entry as 'pai-legacy'.\"));\n }\n\n servers[\"pai\"] = {\n command: \"node\",\n args: [shimBin],\n };\n\n try {\n writeClaudeJson(config);\n console.log(ok(\" Updated ~/.claude.json to use daemon shim.\"));\n } catch (e) {\n console.error(err(` Failed to write ~/.claude.json: ${e}`));\n process.exit(1);\n }\n }\n\n // 4. Verify binaries exist\n console.log();\n if (!existsSync(daemonBin)) {\n console.log(warn(` Daemon binary not found: ${daemonBin}`));\n console.log(dim(\" Run: bun run build\"));\n } else {\n console.log(ok(` Daemon binary: ${daemonBin}`));\n }\n\n if (!existsSync(shimBin)) {\n console.log(warn(` Shim binary not found: ${shimBin}`));\n console.log(dim(\" Run: bun run build\"));\n } else {\n console.log(ok(` Shim binary: ${shimBin}`));\n }\n\n console.log();\n console.log(dim(\" Restart Claude Code to activate the daemon-backed PAI tools.\"));\n console.log();\n}\n\nfunction cmdUninstall(): void {\n console.log();\n console.log(bold(\" PAI Daemon Uninstall\"));\n console.log();\n\n // 1. Unload and remove plist\n if (existsSync(PLIST_PATH)) {\n try {\n spawnSync(\"launchctl\", [\"unload\", PLIST_PATH], { encoding: \"utf8\" });\n console.log(ok(\" Unloaded launchd plist.\"));\n } catch {\n console.log(warn(\" Could not unload plist via launchctl.\"));\n }\n try {\n unlinkSync(PLIST_PATH);\n console.log(ok(` Removed plist: ${PLIST_PATH}`));\n } catch (e) {\n console.log(warn(` Could not remove plist: ${e}`));\n }\n } else {\n console.log(dim(\" No launchd plist found.\"));\n }\n\n // 2. Revert ~/.claude.json to legacy direct MCP\n const config = readClaudeJson();\n const servers =\n typeof config.mcpServers === \"object\" && config.mcpServers !== null\n ? (config.mcpServers as Record<string, unknown>)\n : {};\n\n const legacy = servers[\"pai-legacy\"];\n if (legacy) {\n servers[\"pai\"] = legacy;\n delete servers[\"pai-legacy\"];\n try {\n writeClaudeJson(config);\n console.log(ok(\" Reverted ~/.claude.json to legacy direct MCP.\"));\n } catch (e) {\n console.log(warn(` Could not update ~/.claude.json: ${e}`));\n }\n } else {\n console.log(dim(\" No legacy PAI entry found in ~/.claude.json.\"));\n }\n\n console.log();\n console.log(dim(\" Restart Claude Code to deactivate daemon-backed tools.\"));\n console.log();\n}\n\nasync function cmdMigrate(connectionString?: string): Promise<void> {\n console.log();\n console.log(bold(\" PAI Daemon Migrate\"));\n console.log();\n console.log(dim(\" Running SQLite → PostgreSQL migration...\"));\n console.log();\n\n // Resolve the migration script path relative to this built file\n const { fileURLToPath } = await import(\"node:url\");\n const { dirname: pathDirname, join: pathJoin } = await import(\"node:path\");\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = pathDirname(__filename);\n\n // The migration script is at docker/migrate-sqlite.ts in the source tree\n // When built, we look for it relative to the package root\n const migrationScript = pathJoin(__dirname, \"../..\", \"docker\", \"migrate-sqlite.ts\");\n const { spawnSync: spawn } = await import(\"node:child_process\");\n\n // Use npx tsx to run the TypeScript migration script (bun doesn't support better-sqlite3)\n const spawnArgs = [\"tsx\", migrationScript];\n if (connectionString) {\n spawnArgs.push(\"--connection-string\", connectionString);\n }\n\n const result = spawn(\"npx\", spawnArgs, { stdio: \"inherit\", encoding: \"utf8\" });\n\n if (result.status !== 0) {\n console.error(err(\" Migration failed. Check output above.\"));\n process.exit(result.status ?? 1);\n }\n}\n\nfunction cmdLogs(opts: { lines?: string; follow?: boolean }): void {\n const lines = opts.lines ?? \"50\";\n\n if (!existsSync(DAEMON_LOG)) {\n console.log(warn(`No daemon log found at ${DAEMON_LOG}.`));\n console.log(dim(\"The daemon may not have run yet.\"));\n return;\n }\n\n if (opts.follow) {\n // exec stays running\n try {\n execSync(`tail -f -n ${lines} \"${DAEMON_LOG}\"`, { stdio: \"inherit\" });\n } catch {\n // User pressed Ctrl+C — that's fine\n }\n } else {\n try {\n execSync(`tail -n ${lines} \"${DAEMON_LOG}\"`, { stdio: \"inherit\" });\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(err(`Could not read log: ${msg}`));\n process.exit(1);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerDaemonCommands(daemonCmd: Command): void {\n daemonCmd\n .command(\"serve\")\n .description(\"Start the PAI daemon in the foreground\")\n .action(async () => {\n const { serve } = await import(\"../../daemon/daemon.js\");\n const { loadConfig: lc, ensureConfigDir } = await import(\n \"../../daemon/config.js\"\n );\n ensureConfigDir();\n const config = lc();\n await serve(config);\n });\n\n daemonCmd\n .command(\"migrate\")\n .description(\"Migrate federation data from SQLite to PostgreSQL\")\n .option(\n \"--connection-string <url>\",\n \"Postgres connection string (default: from config or postgresql://pai:pai@localhost:5432/pai)\"\n )\n .action(async (opts: { connectionString?: string }) => {\n await cmdMigrate(opts.connectionString);\n });\n\n daemonCmd\n .command(\"status\")\n .description(\"Query the running daemon status\")\n .action(async () => {\n await cmdStatus();\n });\n\n daemonCmd\n .command(\"restart\")\n .description(\"Send SIGTERM to the running daemon (launchd will restart it)\")\n .action(() => {\n cmdRestart();\n });\n\n daemonCmd\n .command(\"install\")\n .description(\n \"Install daemon as a launchd service and update ~/.claude.json to use the shim\"\n )\n .action(() => {\n cmdInstall();\n });\n\n daemonCmd\n .command(\"uninstall\")\n .description(\"Remove the launchd service and revert to direct MCP\")\n .action(() => {\n cmdUninstall();\n });\n\n daemonCmd\n .command(\"logs\")\n .description(`Tail the daemon log (${DAEMON_LOG})`)\n .option(\"-n, --lines <n>\", \"Number of lines to show\", \"50\")\n .option(\"-f, --follow\", \"Follow log output (like tail -f)\")\n .action((opts: { lines?: string; follow?: boolean }) => {\n cmdLogs(opts);\n });\n}\n","/**\n * pai backup — snapshot registry, config, and Postgres database.\n *\n * Creates a timestamped backup directory at:\n * ~/.pai/backups/YYYY-MM-DD-HHmmss/\n *\n * Contents:\n * registry.db — SQLite registry database\n * config.json — PAI daemon config\n * postgres-pai.sql — pg_dump of the Postgres \"pai\" database (via docker exec)\n */\n\nimport type { Command } from \"commander\";\nimport {\n existsSync,\n mkdirSync,\n copyFileSync,\n statSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { execSync } from \"node:child_process\";\nimport { ok, warn, err, dim, bold } from \"../utils.js\";\nimport { loadConfig } from \"../../daemon/config.js\";\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nconst HOME = homedir();\nconst REGISTRY_DB = join(HOME, \".pai\", \"registry.db\");\nconst CONFIG_FILE = join(HOME, \".config\", \"pai\", \"config.json\");\nconst BACKUPS_DIR = join(HOME, \".pai\", \"backups\");\nconst DOCKER_CONTAINER = \"pai-pgvector\";\nconst PG_DATABASE = \"pai\";\nconst PG_USER = \"pai\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction timestamp(): string {\n const now = new Date();\n const YYYY = now.getFullYear();\n const MM = String(now.getMonth() + 1).padStart(2, \"0\");\n const DD = String(now.getDate()).padStart(2, \"0\");\n const HH = String(now.getHours()).padStart(2, \"0\");\n const mm = String(now.getMinutes()).padStart(2, \"0\");\n const ss = String(now.getSeconds()).padStart(2, \"0\");\n return `${YYYY}-${MM}-${DD}-${HH}${mm}${ss}`;\n}\n\nfunction fmtBytes(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 fileSize(path: string): string {\n try {\n return fmtBytes(statSync(path).size);\n } catch {\n return \"unknown\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registration\n// ---------------------------------------------------------------------------\n\nexport function registerBackupCommands(program: Command): void {\n program\n .command(\"backup\")\n .description(\"Backup registry, config, and Postgres database to ~/.pai/backups/\")\n .option(\"--no-postgres\", \"Skip the Postgres pg_dump (faster, registry+config only)\")\n .action(async (opts: { postgres: boolean }) => {\n const ts = timestamp();\n const backupDir = join(BACKUPS_DIR, ts);\n\n console.log(dim(`Creating backup: ${backupDir}`));\n mkdirSync(backupDir, { recursive: true });\n\n const results: { label: string; path: string; size: string; status: string }[] = [];\n\n // ------------------------------------------------------------------\n // 1. Registry SQLite DB\n // ------------------------------------------------------------------\n\n if (existsSync(REGISTRY_DB)) {\n const dest = join(backupDir, \"registry.db\");\n try {\n copyFileSync(REGISTRY_DB, dest);\n results.push({ label: \"Registry DB\", path: dest, size: fileSize(dest), status: ok(\"ok\") });\n } catch (e) {\n results.push({ label: \"Registry DB\", path: dest, size: \"-\", status: err(`failed: ${e}`) });\n }\n } else {\n results.push({ label: \"Registry DB\", path: REGISTRY_DB, size: \"-\", status: warn(\"not found — skipped\") });\n }\n\n // ------------------------------------------------------------------\n // 2. Config file\n // ------------------------------------------------------------------\n\n if (existsSync(CONFIG_FILE)) {\n const dest = join(backupDir, \"config.json\");\n try {\n copyFileSync(CONFIG_FILE, dest);\n results.push({ label: \"Config\", path: dest, size: fileSize(dest), status: ok(\"ok\") });\n } catch (e) {\n results.push({ label: \"Config\", path: dest, size: \"-\", status: err(`failed: ${e}`) });\n }\n } else {\n results.push({ label: \"Config\", path: CONFIG_FILE, size: \"-\", status: warn(\"not found — skipped\") });\n }\n\n // ------------------------------------------------------------------\n // 3. Optional: Federation SQLite (legacy)\n // ------------------------------------------------------------------\n\n const federationDb = join(HOME, \".pai\", \"federation.db\");\n if (existsSync(federationDb)) {\n const dest = join(backupDir, \"federation.db\");\n try {\n copyFileSync(federationDb, dest);\n results.push({ label: \"Federation DB (legacy)\", path: dest, size: fileSize(dest), status: ok(\"ok\") });\n } catch (e) {\n results.push({ label: \"Federation DB (legacy)\", path: dest, size: \"-\", status: warn(`skipped: ${e}`) });\n }\n }\n\n // ------------------------------------------------------------------\n // 4. Postgres pg_dump via docker exec\n // ------------------------------------------------------------------\n\n if (opts.postgres) {\n const sqlDest = join(backupDir, \"postgres-pai.sql\");\n console.log(dim(` Running pg_dump on ${DOCKER_CONTAINER} (this may take a moment)...`));\n try {\n // Check Docker is running and container exists\n execSync(`docker inspect ${DOCKER_CONTAINER} --format='{{.State.Status}}'`, {\n stdio: \"pipe\",\n });\n\n execSync(\n `docker exec ${DOCKER_CONTAINER} pg_dump -U ${PG_USER} ${PG_DATABASE} > \"${sqlDest}\"`,\n { stdio: [\"pipe\", \"pipe\", \"pipe\"], shell: true as unknown as string }\n );\n results.push({ label: \"Postgres DB\", path: sqlDest, size: fileSize(sqlDest), status: ok(\"ok\") });\n } catch (e) {\n const msg = e instanceof Error ? e.message.split(\"\\n\")[0] : String(e);\n results.push({ label: \"Postgres DB\", path: sqlDest, size: \"-\", status: err(`failed: ${msg}`) });\n console.log(warn(` Postgres backup failed. Is Docker running with container '${DOCKER_CONTAINER}'?`));\n }\n } else {\n results.push({ label: \"Postgres DB\", path: \"-\", size: \"-\", status: dim(\"skipped (--no-postgres)\") });\n }\n\n // ------------------------------------------------------------------\n // Summary\n // ------------------------------------------------------------------\n\n console.log(`\\n${bold(\"Backup complete:\")} ${backupDir}\\n`);\n\n const labelWidth = Math.max(...results.map((r) => r.label.length)) + 2;\n for (const r of results) {\n const label = r.label.padEnd(labelWidth);\n console.log(` ${bold(label)} ${r.status} ${dim(r.size)}`);\n }\n\n console.log(`\\n ${dim(\"Path:\")} ${backupDir}`);\n console.log(` ${dim(\"To restore:\")} pai restore ${backupDir}\\n`);\n });\n}\n","/**\n * pai restore [path] — restore from a backup created by `pai backup`.\n *\n * If no path is given, lists available backups and restores the latest one\n * after prompting for confirmation.\n *\n * Restores:\n * registry.db — SQLite registry database\n * config.json — PAI daemon config\n * postgres-pai.sql — Postgres dump (piped into psql via docker exec)\n * federation.db — Legacy SQLite federation DB (if present)\n */\n\nimport type { Command } from \"commander\";\nimport {\n existsSync,\n readdirSync,\n statSync,\n copyFileSync,\n mkdirSync,\n readFileSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { execSync, spawnSync } from \"node:child_process\";\nimport { createInterface } from \"node:readline\";\nimport { ok, warn, err, dim, bold } from \"../utils.js\";\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nconst HOME = homedir();\nconst REGISTRY_DB = join(HOME, \".pai\", \"registry.db\");\nconst CONFIG_FILE = join(HOME, \".config\", \"pai\", \"config.json\");\nconst BACKUPS_DIR = join(HOME, \".pai\", \"backups\");\nconst DOCKER_CONTAINER = \"pai-pgvector\";\nconst PG_DATABASE = \"pai\";\nconst PG_USER = \"pai\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction confirm(question: string): Promise<boolean> {\n return new Promise((resolve) => {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n rl.question(`${question} [y/N] `, (answer) => {\n rl.close();\n resolve(answer.trim().toLowerCase() === \"y\");\n });\n });\n}\n\nfunction listBackups(): string[] {\n if (!existsSync(BACKUPS_DIR)) return [];\n return readdirSync(BACKUPS_DIR)\n .filter((name) => {\n const full = join(BACKUPS_DIR, name);\n return statSync(full).isDirectory() && /^\\d{4}-\\d{2}-\\d{2}-\\d{6}$/.test(name);\n })\n .sort()\n .reverse(); // newest first\n}\n\nfunction formatBackupDate(name: string): string {\n // YYYY-MM-DD-HHmmss → \"YYYY-MM-DD HH:mm:ss\"\n const m = name.match(/^(\\d{4}-\\d{2}-\\d{2})-(\\d{2})(\\d{2})(\\d{2})$/);\n if (!m) return name;\n return `${m[1]} ${m[2]}:${m[3]}:${m[4]}`;\n}\n\nfunction backupContents(backupDir: string): string[] {\n try {\n return readdirSync(backupDir);\n } catch {\n return [];\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registration\n// ---------------------------------------------------------------------------\n\nexport function registerRestoreCommands(program: Command): void {\n program\n .command(\"restore [backup-path]\")\n .description(\"Restore from a backup directory (created by pai backup)\")\n .option(\"--no-postgres\", \"Skip restoring the Postgres database\")\n .option(\"--yes\", \"Skip confirmation prompt (non-interactive)\")\n .action(async (backupPath: string | undefined, opts: { postgres: boolean; yes: boolean }) => {\n\n // ------------------------------------------------------------------\n // 1. Determine backup directory\n // ------------------------------------------------------------------\n\n let resolvedDir: string;\n\n if (backupPath) {\n resolvedDir = backupPath.startsWith(\"~\")\n ? backupPath.replace(/^~/, HOME)\n : backupPath;\n\n if (!existsSync(resolvedDir)) {\n console.error(err(`Backup directory not found: ${resolvedDir}`));\n process.exit(1);\n }\n } else {\n // No path given — pick from available backups\n const backups = listBackups();\n if (backups.length === 0) {\n console.error(err(`No backups found in ${BACKUPS_DIR}`));\n console.log(dim(\" Run 'pai backup' to create one.\"));\n process.exit(1);\n }\n\n console.log(`\\n${bold(\"Available backups\")} (newest first):\\n`);\n backups.forEach((name, i) => {\n const dir = join(BACKUPS_DIR, name);\n const contents = backupContents(dir).join(\", \");\n const marker = i === 0 ? ok(\" ← latest\") : \"\";\n console.log(` ${bold(String(i + 1).padStart(2))}. ${formatBackupDate(name)}${marker}`);\n console.log(` ${dim(dir)}`);\n console.log(` ${dim(\"Contents:\")} ${contents}`);\n });\n\n console.log();\n resolvedDir = join(BACKUPS_DIR, backups[0]);\n console.log(dim(`Using latest backup: ${resolvedDir}\\n`));\n }\n\n // ------------------------------------------------------------------\n // 2. Inventory what's in the backup\n // ------------------------------------------------------------------\n\n const hasRegistry = existsSync(join(resolvedDir, \"registry.db\"));\n const hasConfig = existsSync(join(resolvedDir, \"config.json\"));\n const hasSql = existsSync(join(resolvedDir, \"postgres-pai.sql\"));\n const hasFed = existsSync(join(resolvedDir, \"federation.db\"));\n\n console.log(`${bold(\"Backup contents:\")}`);\n console.log(` registry.db ${hasRegistry ? ok(\"present\") : warn(\"missing\")}`);\n console.log(` config.json ${hasConfig ? ok(\"present\") : warn(\"missing\")}`);\n console.log(` postgres-pai.sql ${hasSql && opts.postgres ? ok(\"present\") : hasSql ? warn(\"present (skipped via --no-postgres)\") : warn(\"missing\")}`);\n if (hasFed) {\n console.log(` federation.db ${ok(\"present\")} ${dim(\"(legacy)\")}`);\n }\n\n // ------------------------------------------------------------------\n // 3. Confirm\n // ------------------------------------------------------------------\n\n console.log(`\\n${warn(\"WARNING:\")} This will OVERWRITE your current PAI data.`);\n if (hasSql && opts.postgres) {\n console.log(warn(\" Postgres database will be dropped and recreated from the backup.\"));\n }\n\n const proceed = opts.yes || await confirm(\"Proceed with restore?\");\n if (!proceed) {\n console.log(dim(\"Restore cancelled.\"));\n process.exit(0);\n }\n\n console.log();\n\n const results: { label: string; status: string }[] = [];\n\n // ------------------------------------------------------------------\n // 4. Restore registry.db\n // ------------------------------------------------------------------\n\n if (hasRegistry) {\n try {\n mkdirSync(join(HOME, \".pai\"), { recursive: true });\n copyFileSync(join(resolvedDir, \"registry.db\"), REGISTRY_DB);\n results.push({ label: \"Registry DB\", status: ok(\"restored\") });\n } catch (e) {\n results.push({ label: \"Registry DB\", status: err(`failed: ${e}`) });\n }\n } else {\n results.push({ label: \"Registry DB\", status: warn(\"missing in backup — skipped\") });\n }\n\n // ------------------------------------------------------------------\n // 5. Restore config.json\n // ------------------------------------------------------------------\n\n if (hasConfig) {\n try {\n mkdirSync(join(HOME, \".config\", \"pai\"), { recursive: true });\n copyFileSync(join(resolvedDir, \"config.json\"), CONFIG_FILE);\n results.push({ label: \"Config\", status: ok(\"restored\") });\n } catch (e) {\n results.push({ label: \"Config\", status: err(`failed: ${e}`) });\n }\n } else {\n results.push({ label: \"Config\", status: warn(\"missing in backup — skipped\") });\n }\n\n // ------------------------------------------------------------------\n // 6. Restore federation.db (legacy)\n // ------------------------------------------------------------------\n\n if (hasFed) {\n try {\n copyFileSync(join(resolvedDir, \"federation.db\"), join(HOME, \".pai\", \"federation.db\"));\n results.push({ label: \"Federation DB (legacy)\", status: ok(\"restored\") });\n } catch (e) {\n results.push({ label: \"Federation DB (legacy)\", status: warn(`skipped: ${e}`) });\n }\n }\n\n // ------------------------------------------------------------------\n // 7. Restore Postgres via docker exec\n // ------------------------------------------------------------------\n\n if (hasSql && opts.postgres) {\n console.log(dim(\" Restoring Postgres database (this may take a while)...\"));\n try {\n // Verify container is running\n execSync(`docker inspect ${DOCKER_CONTAINER} --format='{{.State.Status}}'`, {\n stdio: \"pipe\",\n });\n\n // Drop and recreate the database, then restore\n const dropCreate = `docker exec ${DOCKER_CONTAINER} psql -U ${PG_USER} -c \"DROP DATABASE IF EXISTS ${PG_DATABASE}; CREATE DATABASE ${PG_DATABASE} OWNER ${PG_USER};\"`;\n execSync(dropCreate, { stdio: \"pipe\", shell: true as unknown as string });\n\n // Pipe the SQL dump into psql\n const sqlContent = readFileSync(join(resolvedDir, \"postgres-pai.sql\"), \"utf8\");\n const psqlResult = spawnSync(\n \"docker\",\n [\"exec\", \"-i\", DOCKER_CONTAINER, \"psql\", \"-U\", PG_USER, PG_DATABASE],\n { input: sqlContent, encoding: \"utf8\", stdio: [\"pipe\", \"pipe\", \"pipe\"] }\n );\n\n if (psqlResult.status !== 0) {\n const errMsg = psqlResult.stderr?.split(\"\\n\")[0] ?? \"unknown error\";\n results.push({ label: \"Postgres DB\", status: err(`failed: ${errMsg}`) });\n } else {\n results.push({ label: \"Postgres DB\", status: ok(\"restored\") });\n }\n } catch (e) {\n const msg = e instanceof Error ? e.message.split(\"\\n\")[0] : String(e);\n results.push({ label: \"Postgres DB\", status: err(`failed: ${msg}`) });\n console.log(warn(` Is Docker running with container '${DOCKER_CONTAINER}'?`));\n }\n } else if (!hasSql || !opts.postgres) {\n const reason = !hasSql ? \"no SQL dump in backup\" : \"--no-postgres\";\n results.push({ label: \"Postgres DB\", status: dim(`skipped (${reason})`) });\n }\n\n // ------------------------------------------------------------------\n // Summary\n // ------------------------------------------------------------------\n\n console.log(`\\n${bold(\"Restore complete:\")}\\n`);\n const labelWidth = Math.max(...results.map((r) => r.label.length)) + 2;\n for (const r of results) {\n console.log(` ${bold(r.label.padEnd(labelWidth))} ${r.status}`);\n }\n\n const hasErrors = results.some((r) => r.status.includes(\"\\u001b[31m\"));\n if (!hasErrors) {\n console.log(`\\n ${ok(\"All done.\")} You may need to restart the PAI daemon.\\n`);\n console.log(` ${dim(\"Restart:\")} pai daemon restart\\n`);\n } else {\n console.log(`\\n ${warn(\"Some items failed — check output above.\")}\\n`);\n process.exit(1);\n }\n });\n}\n","/**\n * Shared helpers for the PAI setup wizard: chalk colour shortcuts,\n * readline prompts, config read/write, and filesystem path finders.\n */\n\nimport { createInterface } from \"node:readline\";\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport { CONFIG_DIR, CONFIG_FILE } from \"../../../daemon/config.js\";\n\n// ---------------------------------------------------------------------------\n// Chalk colour helpers\n// ---------------------------------------------------------------------------\n\nexport const c = {\n bold: (s: string) => chalk.bold(s),\n dim: (s: string) => chalk.dim(s),\n green: (s: string) => chalk.green(s),\n yellow: (s: string) => chalk.yellow(s),\n cyan: (s: string) => chalk.cyan(s),\n red: (s: string) => chalk.red(s),\n blue: (s: string) => chalk.blue(s),\n ok: (s: string) => chalk.green(\" \" + s),\n warn: (s: string) => chalk.yellow(\" \" + s),\n err: (s: string) => chalk.red(\" \" + s),\n};\n\nexport function line(text = \"\"): void {\n console.log(text);\n}\n\nexport function section(title: string): void {\n line();\n console.log(chalk.bold.cyan(\" \" + title));\n console.log(chalk.dim(\" \" + \"─\".repeat(title.length)));\n}\n\n// ---------------------------------------------------------------------------\n// Readline prompt helpers\n// ---------------------------------------------------------------------------\n\nexport function createRl() {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n rl.on(\"SIGINT\", () => {\n line();\n line(c.dim(\" Setup cancelled. Run `pai setup` again to restart.\"));\n line();\n process.exit(0);\n });\n\n return rl;\n}\n\nexport type Rl = ReturnType<typeof createRl>;\n\nexport async function prompt(rl: Rl, question: string): Promise<string> {\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n resolve(answer.trim());\n });\n });\n}\n\n/** Prompt for a numbered menu selection. Returns 0-based index. */\nexport async function promptMenu(\n rl: Rl,\n options: Array<{ label: string; description?: string }>,\n defaultIdx = 0,\n): Promise<number> {\n for (let i = 0; i < options.length; i++) {\n const num = chalk.bold(` ${i + 1}.`);\n const label = i === defaultIdx ? chalk.cyan(options[i].label) : options[i].label;\n const marker = i === defaultIdx ? chalk.dim(\" (recommended)\") : \"\";\n console.log(`${num} ${label}${marker}`);\n if (options[i].description) {\n console.log(chalk.dim(` ${options[i].description}`));\n }\n }\n line();\n\n while (true) {\n const answer = await prompt(\n rl,\n chalk.bold(` Enter number [1-${options.length}] (default: ${defaultIdx + 1}): `),\n );\n\n if (answer === \"\") return defaultIdx;\n\n const n = parseInt(answer, 10);\n if (!isNaN(n) && n >= 1 && n <= options.length) {\n return n - 1;\n }\n\n console.log(c.warn(`Please enter a number between 1 and ${options.length}.`));\n }\n}\n\n/** Prompt for a yes/no answer. Returns true for yes. */\nexport async function promptYesNo(\n rl: Rl,\n question: string,\n defaultYes = true,\n): Promise<boolean> {\n const hint = defaultYes ? \"[Y/n]\" : \"[y/N]\";\n const answer = await prompt(rl, ` ${question} ${chalk.dim(hint)}: `);\n\n if (answer === \"\") return defaultYes;\n return answer.toLowerCase().startsWith(\"y\");\n}\n\n// ---------------------------------------------------------------------------\n// Config read/write helpers\n// ---------------------------------------------------------------------------\n\nexport function readConfigRaw(): Record<string, unknown> {\n if (!existsSync(CONFIG_FILE)) return {};\n try {\n return JSON.parse(readFileSync(CONFIG_FILE, \"utf-8\")) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nexport function writeConfigRaw(data: Record<string, unknown>): void {\n if (!existsSync(CONFIG_DIR)) {\n mkdirSync(CONFIG_DIR, { recursive: true });\n }\n writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2) + \"\\n\", \"utf-8\");\n}\n\nexport function mergeConfig(updates: Record<string, unknown>): void {\n const current = readConfigRaw();\n const merged = { ...current, ...updates };\n if (updates.postgres && typeof current.postgres === \"object\" && current.postgres !== null) {\n merged.postgres = { ...(current.postgres as object), ...(updates.postgres as object) };\n }\n writeConfigRaw(merged);\n}\n\n// ---------------------------------------------------------------------------\n// Docker and connection helpers\n// ---------------------------------------------------------------------------\n\nexport function hasDocker(): boolean {\n try {\n const result = spawnSync(\"docker\", [\"--version\"], { stdio: \"pipe\" });\n return result.status === 0;\n } catch {\n return false;\n }\n}\n\nexport function getDockerDir(): string {\n const candidates = [\n join(process.cwd(), \"docker\"),\n join(homedir(), \"dev\", \"ai\", \"PAI\", \"docker\"),\n join(\"/\", \"usr\", \"local\", \"lib\", \"node_modules\", \"@tekmidian\", \"pai\", \"docker\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(join(candidate, \"docker-compose.yml\"))) return candidate;\n }\n return join(process.cwd(), \"docker\");\n}\n\nexport async function testPostgresConnection(connectionString: string): Promise<boolean> {\n try {\n const pgModule = await import(\"pg\");\n const pg = pgModule.default ?? pgModule;\n const client = new pg.Client({ connectionString });\n await client.connect();\n await client.end();\n return true;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Filesystem path finders\n// ---------------------------------------------------------------------------\n\nexport function getTemplatesDir(): string {\n const candidates = [\n join(process.cwd(), \"templates\"),\n join(homedir(), \"dev\", \"ai\", \"PAI\", \"templates\"),\n join(\"/\", \"usr\", \"local\", \"lib\", \"node_modules\", \"@tekmidian\", \"pai\", \"templates\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(join(candidate, \"claude-md.template.md\"))) return candidate;\n }\n return join(process.cwd(), \"templates\");\n}\n\nexport function getHooksDir(): string {\n const candidates = [\n join(process.cwd(), \"src\", \"hooks\"),\n join(homedir(), \"dev\", \"ai\", \"PAI\", \"src\", \"hooks\"),\n join(\"/\", \"usr\", \"local\", \"lib\", \"node_modules\", \"@tekmidian\", \"pai\", \"src\", \"hooks\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(join(candidate, \"session-stop.sh\"))) return candidate;\n }\n return join(process.cwd(), \"src\", \"hooks\");\n}\n\nexport function getDistHooksDir(): string {\n const moduleDir = new URL(\".\", import.meta.url).pathname;\n const fromModule = join(moduleDir, \"..\", \"..\", \"hooks\");\n\n const candidates = [\n fromModule,\n join(process.cwd(), \"dist\", \"hooks\"),\n join(homedir(), \"dev\", \"ai\", \"PAI\", \"dist\", \"hooks\"),\n join(\"/\", \"usr\", \"local\", \"lib\", \"node_modules\", \"@tekmidian\", \"pai\", \"dist\", \"hooks\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(join(candidate, \"stop-hook.mjs\"))) return candidate;\n }\n return fromModule;\n}\n\nexport function getDistDir(): string {\n const moduleDir = new URL(\".\", import.meta.url).pathname;\n const fromModule = join(moduleDir, \"..\", \"..\", \"..\");\n\n const candidates = [\n fromModule,\n join(process.cwd(), \"dist\"),\n join(homedir(), \"dev\", \"ai\", \"PAI\", \"dist\"),\n join(\"/\", \"usr\", \"local\", \"lib\", \"node_modules\", \"@tekmidian\", \"pai\", \"dist\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(join(candidate, \"skills\"))) return candidate;\n }\n return fromModule;\n}\n\nexport function getStatuslineScript(): string | null {\n const candidates = [\n join(process.cwd(), \"statusline-command.sh\"),\n join(homedir(), \"dev\", \"ai\", \"PAI\", \"statusline-command.sh\"),\n join(\"/\", \"usr\", \"local\", \"lib\", \"node_modules\", \"@tekmidian\", \"pai\", \"statusline-command.sh\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(candidate)) return candidate;\n }\n return null;\n}\n\nexport function getTabColorScript(): string | null {\n const candidates = [\n join(process.cwd(), \"tab-color-command.sh\"),\n join(homedir(), \"dev\", \"ai\", \"PAI\", \"tab-color-command.sh\"),\n join(\"/\", \"usr\", \"local\", \"lib\", \"node_modules\", \"@tekmidian\", \"pai\", \"tab-color-command.sh\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(candidate)) return candidate;\n }\n return null;\n}\n","/** Step 1: Welcome banner displayed at the start of the setup wizard. */\n\nimport chalk from \"chalk\";\nimport { c, line } from \"../utils.js\";\n\nexport function stepWelcome(): void {\n line();\n line(chalk.bold.cyan(\" ╔════════════════════════════════════════╗\"));\n line(chalk.bold.cyan(\" ║ PAI Knowledge OS — Setup Wizard ║\"));\n line(chalk.bold.cyan(\" ╚════════════════════════════════════════╝\"));\n line();\n line(\" PAI is a personal knowledge system that indexes your files, generates\");\n line(\" semantic embeddings for intelligent search, and stores everything in a\");\n line(\" local database so you can search your knowledge base with natural language.\");\n line();\n line(c.dim(\" This wizard will guide you through the initial configuration.\"));\n line(c.dim(\" Press Ctrl+C at any time to cancel.\"));\n}\n","/** Step 2: Storage backend selection (SQLite or PostgreSQL) and Docker helper. */\n\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport { c, line, section, type Rl, prompt, promptMenu, promptYesNo, readConfigRaw, getDockerDir, hasDocker, testPostgresConnection } from \"../utils.js\";\n\nasync function startDocker(rl: Rl): Promise<boolean> {\n const dockerDir = getDockerDir();\n const composePath = join(dockerDir, \"docker-compose.yml\");\n\n if (!existsSync(composePath)) {\n console.log(c.warn(`docker-compose.yml not found at ${dockerDir}`));\n console.log(c.dim(\" You can start it manually later:\"));\n console.log(c.dim(\" docker compose up -d\"));\n return false;\n }\n\n console.log(c.dim(` Starting PostgreSQL container from ${dockerDir}...`));\n\n try {\n const result = spawnSync(\"docker\", [\"compose\", \"up\", \"-d\"], {\n cwd: dockerDir,\n stdio: \"inherit\",\n });\n\n if (result.status !== 0) {\n console.log(c.warn(\" Docker compose failed. You can start it manually:\"));\n console.log(c.dim(` cd ${dockerDir} && docker compose up -d`));\n return false;\n }\n\n console.log(c.ok(\"PostgreSQL container started.\"));\n return true;\n } catch (e) {\n console.log(c.warn(` Could not run docker compose: ${e}`));\n return false;\n }\n}\n\nexport async function stepStorage(rl: Rl): Promise<Record<string, unknown>> {\n section(\"Step 2: Storage Backend\");\n\n const existing = readConfigRaw();\n if (existing.storageBackend) {\n const backend = String(existing.storageBackend);\n if (backend === \"postgres\") {\n try {\n const result = spawnSync(\"docker\", [\"ps\", \"--filter\", \"name=pai-pgvector\", \"--format\", \"{{.Status}}\"], { stdio: \"pipe\" });\n const status = result.stdout?.toString().trim();\n if (status && status.includes(\"Up\")) {\n console.log(c.ok(`Storage backend: PostgreSQL (container running). Skipping.`));\n return existing;\n }\n console.log(c.dim(\" PostgreSQL container found but not running. Starting...\"));\n await startDocker(rl);\n await new Promise((r) => setTimeout(r, 3000));\n console.log(c.ok(\"PostgreSQL container started.\"));\n } catch {\n console.log(c.ok(`Storage backend: PostgreSQL (configured). Skipping.`));\n }\n return existing;\n } else {\n console.log(c.ok(`Storage backend: ${backend}. Skipping.`));\n return existing;\n }\n }\n\n line();\n line(\" Choose how PAI stores your indexed knowledge:\");\n line();\n\n const choice = await promptMenu(rl, [\n {\n label: \"PostgreSQL with pgvector\",\n description: \"Best for large collections, semantic search, and production use. Requires Docker or a Postgres server.\",\n },\n {\n label: \"SQLite\",\n description: \"Simple, no dependencies, zero configuration. Good for trying PAI out. Keyword search only.\",\n },\n ]);\n\n if (choice === 1) {\n line();\n console.log(c.ok(\"SQLite selected. No additional setup needed.\"));\n return { storageBackend: \"sqlite\" };\n }\n\n // PostgreSQL\n line();\n line(\" PostgreSQL requires a running Postgres server with the pgvector extension.\");\n line();\n\n if (hasDocker()) {\n console.log(c.ok(\"Docker is installed.\"));\n line();\n\n const useDocker = await promptYesNo(rl, \"Start the PAI PostgreSQL container with Docker? (recommended)\", true);\n\n if (useDocker) {\n line();\n await startDocker(rl);\n line();\n\n console.log(c.dim(\" Waiting 3 seconds for container to be ready...\"));\n await new Promise((r) => setTimeout(r, 3000));\n\n const connStr = \"postgresql://pai:pai@localhost:5432/pai\";\n console.log(c.dim(` Testing connection to ${connStr}...`));\n\n const ok2 = await testPostgresConnection(connStr);\n if (ok2) {\n console.log(c.ok(\"Connection successful!\"));\n } else {\n console.log(c.warn(\"Connection test failed. The container may still be starting.\"));\n console.log(c.dim(\" Using default connection string — you can verify with `pai daemon status`.\"));\n }\n return { storageBackend: \"postgres\", postgres: { connectionString: connStr } };\n }\n } else {\n console.log(c.dim(\" Docker not found. Using manual connection string entry.\"));\n }\n\n // Manual entry\n line();\n line(\" Enter your PostgreSQL connection details:\");\n line();\n\n const useConnStr = await promptYesNo(rl, \"Use a full connection string? (e.g. postgresql://user:pass@host:5432/dbname)\", true);\n\n if (useConnStr) {\n const connStr = await prompt(rl, chalk.bold(\" Connection string: \"));\n if (connStr) {\n console.log(c.dim(\" Testing connection...\"));\n const connected = await testPostgresConnection(connStr);\n if (connected) {\n console.log(c.ok(\"Connection successful!\"));\n } else {\n console.log(c.warn(\"Connection test failed — check credentials and try again later.\"));\n }\n return { storageBackend: \"postgres\", postgres: { connectionString: connStr } };\n }\n }\n\n const host = await prompt(rl, chalk.bold(\" Host [localhost]: \")) || \"localhost\";\n const portStr = await prompt(rl, chalk.bold(\" Port [5432]: \")) || \"5432\";\n const database = await prompt(rl, chalk.bold(\" Database [pai]: \")) || \"pai\";\n const user = await prompt(rl, chalk.bold(\" User [pai]: \")) || \"pai\";\n const password = await prompt(rl, chalk.bold(\" Password [pai]: \")) || \"pai\";\n\n const connStr = `postgresql://${user}:${password}@${host}:${portStr}/${database}`;\n console.log(c.dim(` Connection string: ${connStr}`));\n console.log(c.dim(\" Testing connection...\"));\n\n const connected = await testPostgresConnection(connStr);\n if (connected) {\n console.log(c.ok(\"Connection successful!\"));\n } else {\n console.log(c.warn(\"Connection test failed — check credentials and try again later.\"));\n }\n\n return { storageBackend: \"postgres\", postgres: { connectionString: connStr } };\n}\n","/** Step 3: Embedding model selection for semantic search. */\n\nimport { c, line, section, type Rl, promptMenu, readConfigRaw } from \"../utils.js\";\n\nexport async function stepEmbedding(rl: Rl): Promise<Record<string, unknown>> {\n section(\"Step 3: Embedding Model\");\n\n const existing = readConfigRaw();\n if (existing.embeddingModel) {\n console.log(c.ok(`Embedding model: ${existing.embeddingModel}. Skipping.`));\n return { embeddingModel: existing.embeddingModel };\n }\n\n line();\n line(\" An embedding model converts your text into vectors for semantic search.\");\n line(\" Models are downloaded from HuggingFace on first use.\");\n line();\n\n const choice = await promptMenu(rl, [\n {\n label: \"Snowflake Arctic Embed m v1.5\",\n description: \"768 dims, ~118MB download. Best retrieval quality per MB (MTEB score 55.14). Asymmetric retrieval — different handling for queries vs documents. Best for most users.\",\n },\n {\n label: \"BGE Small EN v1.5\",\n description: \"384 dims, ~32MB download. Lightweight and fast. Good for limited disk space or when faster embedding is more important than maximum quality. English only.\",\n },\n {\n label: \"Nomic Embed Text v1.5\",\n description: \"768 dims, ~100MB download. 8K token context window — excellent for long documents. Matryoshka dimensions (can truncate for speed/size tradeoffs).\",\n },\n {\n label: \"None — skip embeddings\",\n description: \"BM25/keyword search only. No model download needed. You can add embeddings later by running `pai memory embed` after selecting a model.\",\n },\n ]);\n\n const models: Record<number, string | null> = {\n 0: \"Snowflake/snowflake-arctic-embed-m-v1.5\",\n 1: \"BAAI/bge-small-en-v1.5\",\n 2: \"nomic-ai/nomic-embed-text-v1.5\",\n 3: null,\n };\n\n const selectedModel = models[choice];\n\n line();\n if (selectedModel) {\n console.log(c.ok(`Model selected: ${selectedModel}`));\n console.log(c.dim(\" The model will be downloaded on first use of `pai memory embed`.\"));\n } else {\n console.log(c.ok(\"Skipping embeddings. Keyword search will still work.\"));\n console.log(c.dim(\" Add later: update embeddingModel in ~/.config/pai/config.json\"));\n }\n\n return { embeddingModel: selectedModel ?? \"none\" };\n}\n","/** Step 4: CLAUDE.md generation from PAI template. */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\nimport { CONFIG_DIR } from \"../../../../daemon/config.js\";\nimport { c, line, section, type Rl, promptYesNo, getTemplatesDir } from \"../utils.js\";\n\nexport async function stepClaudeMd(rl: Rl): Promise<boolean> {\n section(\"Step 4: Agent Configuration (CLAUDE.md)\");\n line();\n line(\" PAI ships a CLAUDE.md template with agent orchestration patterns:\");\n line(\" swarm mode, model escalation, parallel execution, quality standards.\");\n line();\n\n const claudeDir = join(homedir(), \".claude\");\n const claudeMd = join(claudeDir, \"CLAUDE.md\");\n const agentPrefs = join(CONFIG_DIR, \"agent-prefs.md\");\n const templatesDir = getTemplatesDir();\n const templatePath = join(templatesDir, \"claude-md.template.md\");\n\n if (!existsSync(templatePath)) {\n console.log(c.warn(\"Template not found: \" + templatePath));\n console.log(c.dim(\" Skipping CLAUDE.md generation. You can copy it manually from templates/.\"));\n return false;\n }\n\n if (existsSync(claudeMd)) {\n const content = readFileSync(claudeMd, \"utf-8\");\n const isGenerated = content.includes(\"Generated by PAI Setup\");\n if (isGenerated) {\n console.log(c.dim(\" Found existing PAI-generated CLAUDE.md.\"));\n } else {\n console.log(c.yellow(\" Found existing CLAUDE.md (custom, not PAI-generated).\"));\n console.log(c.dim(\" A backup will be created before overwriting.\"));\n }\n line();\n\n const overwrite = await promptYesNo(rl, \"Update ~/.claude/CLAUDE.md with the latest PAI template?\", isGenerated);\n if (!overwrite) {\n console.log(c.dim(\" Keeping existing CLAUDE.md unchanged.\"));\n return false;\n }\n\n if (!isGenerated) {\n const backupPath = claudeMd + \".backup\";\n writeFileSync(backupPath, content, \"utf-8\");\n console.log(c.ok(`Backed up existing CLAUDE.md to ${backupPath}`));\n }\n }\n\n let template = readFileSync(templatePath, \"utf-8\");\n template = template.replace(/\\$\\{HOME\\}/g, homedir());\n\n if (!existsSync(claudeDir)) {\n mkdirSync(claudeDir, { recursive: true });\n }\n writeFileSync(claudeMd, template, \"utf-8\");\n\n line();\n console.log(c.ok(\"Generated ~/.claude/CLAUDE.md from PAI template.\"));\n\n if (!existsSync(agentPrefs)) {\n line();\n line(\" For personal settings (project mappings, notification preferences),\");\n line(\" copy the example and customize:\");\n line();\n console.log(chalk.cyan(` cp ${templatesDir}/agent-prefs.example.md ${agentPrefs}`));\n line();\n\n const createPrefs = await promptYesNo(rl, \"Copy the example agent-prefs.md now?\", true);\n if (createPrefs) {\n const examplePath = join(templatesDir, \"agent-prefs.example.md\");\n if (existsSync(examplePath)) {\n if (!existsSync(CONFIG_DIR)) {\n mkdirSync(CONFIG_DIR, { recursive: true });\n }\n writeFileSync(agentPrefs, readFileSync(examplePath, \"utf-8\"), \"utf-8\");\n console.log(c.ok(`Created ${agentPrefs}`));\n console.log(c.dim(\" Edit this file to add your personal preferences and project mappings.\"));\n } else {\n console.log(c.warn(\"Example file not found. Create agent-prefs.md manually.\"));\n }\n }\n } else {\n console.log(c.dim(\" Personal preferences: \" + agentPrefs));\n }\n\n return true;\n}\n","/** Step 5: PAI SKILL.md installation to ~/.claude/skills/PAI/. */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getTemplatesDir } from \"../utils.js\";\n\nexport async function stepPaiSkill(rl: Rl): Promise<boolean> {\n section(\"Step 5: PAI Skill Installation\");\n line();\n line(\" PAI ships a SKILL.md that tells Claude Code how to invoke PAI commands.\");\n line();\n\n const templatesDir = getTemplatesDir();\n const templatePath = join(templatesDir, \"pai-skill.template.md\");\n\n if (!existsSync(templatePath)) {\n console.log(c.warn(\"Skill template not found: \" + templatePath));\n console.log(c.dim(\" Skipping PAI skill installation.\"));\n return false;\n }\n\n const skillDir = join(homedir(), \".claude\", \"skills\", \"PAI\");\n const skillFile = join(skillDir, \"SKILL.md\");\n\n if (existsSync(skillFile)) {\n const content = readFileSync(skillFile, \"utf-8\");\n const isGenerated = content.includes(\"Generated by PAI Setup\");\n if (isGenerated) {\n console.log(c.dim(\" Found existing PAI-generated SKILL.md.\"));\n } else {\n console.log(c.yellow(\" Found existing SKILL.md (not PAI-generated).\"));\n console.log(c.dim(\" A backup will be created before overwriting.\"));\n }\n line();\n\n const overwrite = await promptYesNo(rl, \"Update ~/.claude/skills/PAI/SKILL.md with the latest PAI skill?\", isGenerated);\n if (!overwrite) {\n console.log(c.dim(\" Keeping existing SKILL.md unchanged.\"));\n return false;\n }\n\n if (!isGenerated) {\n const backupPath = skillFile + \".backup\";\n writeFileSync(backupPath, content, \"utf-8\");\n console.log(c.ok(`Backed up existing SKILL.md to ${backupPath}`));\n }\n } else {\n const install = await promptYesNo(rl, \"Install PAI skill to ~/.claude/skills/PAI/SKILL.md?\", true);\n if (!install) {\n console.log(c.dim(\" Skipping PAI skill installation.\"));\n return false;\n }\n }\n\n let template = readFileSync(templatePath, \"utf-8\");\n template = template.replace(/\\$\\{HOME\\}/g, homedir());\n\n if (!existsSync(skillDir)) {\n mkdirSync(skillDir, { recursive: true });\n }\n writeFileSync(skillFile, template, \"utf-8\");\n\n line();\n console.log(c.ok(\"Installed ~/.claude/skills/PAI/SKILL.md\"));\n return true;\n}\n","/** Step 6: AI Steering Rules installation to ~/.claude/skills/PAI/. */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getTemplatesDir } from \"../utils.js\";\n\nexport async function stepAiSteeringRules(rl: Rl): Promise<boolean> {\n section(\"Step 6: AI Steering Rules\");\n line();\n line(\" PAI ships a set of universal behavioral rules for AI assistants:\");\n line(\" surgical fixes, verification before assertion, root cause analysis,\");\n line(\" and honest failure modes. These rules load at startup as a skill.\");\n line();\n\n const templatesDir = getTemplatesDir();\n const templatePath = join(templatesDir, \"ai-steering-rules.template.md\");\n\n if (!existsSync(templatePath)) {\n console.log(c.warn(\"AI steering rules template not found: \" + templatePath));\n console.log(c.dim(\" Skipping AI steering rules installation.\"));\n return false;\n }\n\n const skillDir = join(homedir(), \".claude\", \"skills\", \"PAI\");\n const skillFile = join(skillDir, \"AI-STEERING-RULES.md\");\n\n if (existsSync(skillFile)) {\n const content = readFileSync(skillFile, \"utf-8\");\n const isGenerated = content.includes(\"Generated by PAI Setup\");\n if (isGenerated) {\n console.log(c.dim(\" Found existing PAI-generated AI-STEERING-RULES.md.\"));\n } else {\n console.log(c.yellow(\" Found existing AI-STEERING-RULES.md (not PAI-generated).\"));\n console.log(c.dim(\" A backup will be created before overwriting.\"));\n }\n line();\n\n const overwrite = await promptYesNo(rl, \"Update ~/.claude/skills/PAI/AI-STEERING-RULES.md with the latest rules?\", isGenerated);\n if (!overwrite) {\n console.log(c.dim(\" Keeping existing AI-STEERING-RULES.md unchanged.\"));\n return false;\n }\n\n if (!isGenerated) {\n const backupPath = skillFile + \".backup\";\n writeFileSync(backupPath, content, \"utf-8\");\n console.log(c.ok(`Backed up existing AI-STEERING-RULES.md to ${backupPath}`));\n }\n } else {\n const install = await promptYesNo(rl, \"Install AI steering rules to ~/.claude/skills/PAI/AI-STEERING-RULES.md?\", true);\n if (!install) {\n console.log(c.dim(\" Skipping AI steering rules installation.\"));\n return false;\n }\n }\n\n const template = readFileSync(templatePath, \"utf-8\");\n\n if (!existsSync(skillDir)) {\n mkdirSync(skillDir, { recursive: true });\n }\n writeFileSync(skillFile, template, \"utf-8\");\n\n line();\n console.log(c.ok(\"Installed ~/.claude/skills/PAI/AI-STEERING-RULES.md\"));\n return true;\n}\n","/**\n * Step 7: Install PAI MCP skill stubs to ~/.claude/skills/.\n *\n * Each PAI MCP prompt has a SKILL.md stub generated at build time\n * (dist/skills/<Name>/SKILL.md). This step symlinks (macOS/Linux) or\n * copies (Windows) them into Claude Code's skill discovery directory.\n *\n * Skills MUST live at ~/.claude/skills/<Name>/SKILL.md (top level),\n * NOT under a user/ subdirectory — Claude Code only scans one level deep.\n *\n * Symlinks keep the stubs in sync — rebuilding PAI updates them automatically.\n * Existing non-symlink directories with the same name are never overwritten.\n */\n\nimport {\n existsSync,\n readdirSync,\n symlinkSync,\n lstatSync,\n readlinkSync,\n copyFileSync,\n mkdirSync,\n unlinkSync,\n} from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getDistDir } from \"../utils.js\";\n\nexport async function stepSkillStubs(rl: Rl): Promise<boolean> {\n section(\"Step 7: MCP Skill Stubs\");\n line();\n line(\" PAI MCP prompts need SKILL.md stubs so Claude Code can discover them.\");\n line(\" Stubs are symlinked from dist/skills/ — rebuilding PAI keeps them in sync.\");\n line();\n\n const distSkills = join(getDistDir(), \"skills\");\n\n if (!existsSync(distSkills)) {\n console.log(c.warn(\"dist/skills/ not found — run `bun run build` first.\"));\n return false;\n }\n\n const skillNames = readdirSync(distSkills).filter((name) => {\n const p = join(distSkills, name);\n return existsSync(join(p, \"SKILL.md\"));\n });\n\n if (skillNames.length === 0) {\n console.log(c.warn(\"No skill stubs found in dist/skills/.\"));\n return false;\n }\n\n console.log(c.dim(` Found ${skillNames.length} skill stubs to install.`));\n line();\n\n const install = await promptYesNo(\n rl,\n `Symlink ${skillNames.length} PAI skill stubs to ~/.claude/skills/?`,\n true,\n );\n if (!install) {\n console.log(c.dim(\" Skipping skill stub installation.\"));\n return false;\n }\n\n const targetBase = join(homedir(), \".claude\", \"skills\");\n mkdirSync(targetBase, { recursive: true });\n\n // Clean up stale symlinks from old user/ location\n const oldUserBase = join(targetBase, \"user\");\n if (existsSync(oldUserBase)) {\n for (const name of skillNames) {\n const oldTarget = join(oldUserBase, name);\n if (existsSync(oldTarget) && lstatSync(oldTarget).isSymbolicLink()) {\n unlinkSync(oldTarget);\n }\n }\n }\n\n const useSymlinks = platform() !== \"win32\";\n let installed = 0;\n let skipped = 0;\n let updated = 0;\n\n for (const name of skillNames) {\n const source = resolve(join(distSkills, name));\n const target = join(targetBase, name);\n\n // If target exists and is not a symlink, never overwrite (user's own skill)\n if (existsSync(target) && !lstatSync(target).isSymbolicLink()) {\n console.log(c.dim(` SKIP ${name} (existing user skill, not a symlink)`));\n skipped++;\n continue;\n }\n\n // If target is an existing symlink, check if it already points to the right place\n if (existsSync(target) && lstatSync(target).isSymbolicLink()) {\n const currentTarget = readlinkSync(target);\n if (resolve(currentTarget) === source) {\n // Already correct\n installed++;\n continue;\n }\n // Stale symlink — remove and recreate\n unlinkSync(target);\n updated++;\n }\n\n if (useSymlinks) {\n symlinkSync(source, target);\n } else {\n // Windows: copy the SKILL.md\n mkdirSync(target, { recursive: true });\n copyFileSync(join(source, \"SKILL.md\"), join(target, \"SKILL.md\"));\n }\n installed++;\n }\n\n line();\n const method = useSymlinks ? \"symlinked\" : \"copied\";\n if (updated > 0) {\n console.log(c.ok(`${installed} stubs ${method}, ${updated} updated, ${skipped} skipped (user skills).`));\n } else {\n console.log(c.ok(`${installed} skill stubs ${method} to ~/.claude/skills/.`));\n if (skipped > 0) {\n console.log(c.dim(` ${skipped} skipped (existing user skills not managed by PAI).`));\n }\n }\n return true;\n}\n","/** Step 7: Shell lifecycle hooks installation (pre-compact, session-stop, statusline). */\n\nimport {\n existsSync, lstatSync, readlinkSync, symlinkSync, unlinkSync,\n copyFileSync, chmodSync, mkdirSync,\n} from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getHooksDir, getStatuslineScript, getTabColorScript } from \"../utils.js\";\n\nconst useSymlinks = platform() !== \"win32\";\n\nexport async function stepHooks(rl: Rl): Promise<boolean> {\n section(\"Step 7: Lifecycle Hooks\");\n line();\n line(\" PAI hooks fire on session stop and context compaction to save state,\");\n line(\" update notes, and display live statusline information.\");\n if (useSymlinks) {\n line(\" Files are symlinked so they auto-update when PAI is rebuilt.\");\n } else {\n line(\" Files are copied (Windows — re-run setup after PAI updates).\");\n }\n line();\n\n const install = await promptYesNo(rl, \"Install PAI lifecycle hooks (session stop, pre-compact, statusline)?\", true);\n if (!install) {\n console.log(c.dim(\" Skipping hook installation.\"));\n return false;\n }\n\n const hooksDir = getHooksDir();\n const statuslineSrc = getStatuslineScript();\n const tabColorSrc = getTabColorScript();\n\n const claudeDir = join(homedir(), \".claude\");\n const hooksTarget = join(claudeDir, \"Hooks\");\n\n if (!existsSync(hooksTarget)) {\n mkdirSync(hooksTarget, { recursive: true });\n }\n\n let anyInstalled = false;\n\n function installLink(src: string, dest: string, label: string): void {\n if (!existsSync(src)) {\n console.log(c.warn(` Source not found: ${src}`));\n return;\n }\n\n const absSrc = resolve(src);\n\n // Check existing target\n if (existsSync(dest) || (lstatSync(dest, { throwIfNoEntry: false })?.isSymbolicLink?.())) {\n try {\n const stat = lstatSync(dest);\n if (stat.isSymbolicLink()) {\n if (resolve(readlinkSync(dest)) === absSrc) {\n console.log(c.dim(` Current: ${label}`));\n return;\n }\n // Stale symlink — replace\n unlinkSync(dest);\n } else if (stat.isFile()) {\n // Old copy — replace with symlink\n unlinkSync(dest);\n } else {\n return; // Don't touch directories\n }\n } catch {\n // lstatSync failed — target doesn't exist, proceed\n }\n }\n\n if (useSymlinks) {\n symlinkSync(absSrc, dest);\n } else {\n copyFileSync(src, dest);\n chmodSync(dest, 0o755);\n }\n const verb = useSymlinks ? \"Linked\" : \"Installed\";\n console.log(c.ok(`${verb}: ${label}`));\n anyInstalled = true;\n }\n\n line();\n installLink(join(hooksDir, \"pre-compact.sh\"), join(hooksTarget, \"pai-pre-compact.sh\"), \"pai-pre-compact.sh\");\n installLink(join(hooksDir, \"session-stop.sh\"), join(hooksTarget, \"pai-session-stop.sh\"), \"pai-session-stop.sh\");\n\n if (statuslineSrc) {\n installLink(statuslineSrc, join(claudeDir, \"statusline-command.sh\"), \"statusline-command.sh\");\n } else {\n console.log(c.warn(\" statusline-command.sh not found — skipping statusline.\"));\n }\n\n if (tabColorSrc) {\n installLink(tabColorSrc, join(claudeDir, \"tab-color-command.sh\"), \"tab-color-command.sh\");\n } else {\n console.log(c.warn(\" tab-color-command.sh not found — skipping tab color.\"));\n }\n\n return anyInstalled;\n}\n","/** Step 7b: TypeScript (.mjs) hooks installation to ~/.claude/Hooks/. */\n\nimport {\n existsSync, readdirSync, lstatSync, readlinkSync,\n symlinkSync, unlinkSync, copyFileSync, chmodSync, mkdirSync,\n} from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getDistHooksDir } from \"../utils.js\";\n\nconst useSymlinks = platform() !== \"win32\";\n\nexport async function stepTsHooks(rl: Rl): Promise<boolean> {\n section(\"Step 7b: TypeScript Hooks Installation\");\n line();\n line(\" PAI ships compiled TypeScript hooks (.mjs) that fire on session events,\");\n line(\" tool use, and context compaction to capture context and update notes.\");\n if (useSymlinks) {\n line(\" Files are symlinked so they auto-update when PAI is rebuilt.\");\n } else {\n line(\" Files are copied (Windows — re-run setup after PAI updates).\");\n }\n line();\n\n const install = await promptYesNo(rl, \"Install PAI TypeScript hooks to ~/.claude/Hooks/?\", true);\n if (!install) {\n console.log(c.dim(\" Skipping TypeScript hooks installation.\"));\n return false;\n }\n\n const distHooksDir = getDistHooksDir();\n\n if (!existsSync(distHooksDir)) {\n console.log(c.warn(` dist/hooks/ directory not found at: ${distHooksDir}`));\n console.log(c.dim(\" Build the package first: bun run build\"));\n return false;\n }\n\n const claudeDir = join(homedir(), \".claude\");\n const hooksTarget = join(claudeDir, \"Hooks\");\n\n if (!existsSync(hooksTarget)) {\n mkdirSync(hooksTarget, { recursive: true });\n }\n\n let allFiles: string[];\n try {\n allFiles = readdirSync(distHooksDir).filter((f) => f.endsWith(\".mjs\"));\n } catch (e) {\n console.log(c.warn(` Could not read dist/hooks/: ${e}`));\n return false;\n }\n\n if (allFiles.length === 0) {\n console.log(c.warn(\" No .mjs files found in dist/hooks/. Build first: bun run build\"));\n return false;\n }\n\n line();\n let linkedCount = 0;\n let skippedCount = 0;\n let cleanedCount = 0;\n\n for (const filename of allFiles) {\n const src = resolve(join(distHooksDir, filename));\n const dest = join(hooksTarget, filename);\n\n // Check existing target\n let needsLink = true;\n if (existsSync(dest) || lstatSync(dest, { throwIfNoEntry: false })?.isSymbolicLink?.()) {\n try {\n const stat = lstatSync(dest);\n if (stat.isSymbolicLink()) {\n if (resolve(readlinkSync(dest)) === src) {\n console.log(c.dim(` Current: ${filename}`));\n skippedCount++;\n needsLink = false;\n } else {\n unlinkSync(dest); // Stale symlink\n }\n } else if (stat.isFile()) {\n unlinkSync(dest); // Old copy — replace with symlink\n }\n } catch {\n // lstatSync failed — target doesn't exist\n }\n }\n\n if (needsLink) {\n if (useSymlinks) {\n symlinkSync(src, dest);\n } else {\n copyFileSync(src, dest);\n chmodSync(dest, 0o755);\n }\n const verb = useSymlinks ? \"Linked\" : \"Installed\";\n console.log(c.ok(`${verb}: ${filename}`));\n linkedCount++;\n }\n\n // Clean up stale .ts files from pre-build era\n const staleTsPath = join(hooksTarget, filename.replace(/\\.mjs$/, \".ts\"));\n if (existsSync(staleTsPath)) {\n unlinkSync(staleTsPath);\n console.log(c.ok(`Cleaned up stale: ${filename.replace(/\\.mjs$/, \".ts\")}`));\n cleanedCount++;\n }\n }\n\n line();\n if (linkedCount > 0 || cleanedCount > 0) {\n const parts = [];\n const verb = useSymlinks ? \"linked\" : \"installed\";\n if (linkedCount > 0) parts.push(`${linkedCount} hook(s) ${verb}`);\n if (skippedCount > 0) parts.push(`${skippedCount} current`);\n if (cleanedCount > 0) parts.push(`${cleanedCount} stale .ts file(s) cleaned up`);\n console.log(c.ok(parts.join(\", \") + \".\"));\n } else {\n console.log(c.dim(` All ${skippedCount} hook(s) already current.`));\n }\n\n return linkedCount > 0 || cleanedCount > 0;\n}\n","/**\n * settings-manager — merge-not-overwrite utility for ~/.claude/settings.json\n *\n * Provides safe, idempotent writes to Claude Code's settings.json:\n * - env vars: added only if the key is absent (never overwrites)\n * - hooks: appended per hookType, deduplicated by command string\n * - statusLine: written only if the key is not already present\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nconst CLAUDE_DIR = join(homedir(), \".claude\");\nconst SETTINGS_FILE = join(CLAUDE_DIR, \"settings.json\");\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type HookType =\n | \"PreCompact\"\n | \"Stop\"\n | \"SessionStart\"\n | \"SessionEnd\"\n | \"UserPromptSubmit\"\n | \"PreToolUse\"\n | \"PostToolUse\"\n | \"SubagentStop\";\n\nexport interface HookEntry {\n hookType: HookType;\n matcher?: string;\n command: string;\n}\n\nexport interface SettingsMergeOptions {\n env?: Record<string, string>;\n hooks?: HookEntry[];\n statusLine?: { type: string; command: string };\n permissions?: {\n allow?: string[];\n deny?: string[];\n };\n flags?: Record<string, unknown>;\n}\n\nexport interface MergeResult {\n changed: boolean;\n report: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Internal shape of settings.json hooks\n// ---------------------------------------------------------------------------\n\ninterface HookCommand {\n type: string;\n command: string;\n}\n\ninterface HookRule {\n matcher?: string;\n hooks: HookCommand[];\n}\n\ntype HooksSection = Partial<Record<HookType, HookRule[]>>;\n\n// ---------------------------------------------------------------------------\n// Read / write helpers\n// ---------------------------------------------------------------------------\n\nexport function readSettingsJson(): Record<string, unknown> {\n if (!existsSync(SETTINGS_FILE)) return {};\n try {\n return JSON.parse(readFileSync(SETTINGS_FILE, \"utf-8\")) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nexport function writeSettingsJson(data: Record<string, unknown>): void {\n if (!existsSync(CLAUDE_DIR)) {\n mkdirSync(CLAUDE_DIR, { recursive: true });\n }\n writeFileSync(SETTINGS_FILE, JSON.stringify(data, null, 2) + \"\\n\", \"utf-8\");\n}\n\n// ---------------------------------------------------------------------------\n// Merge helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Merge env vars — add keys that are absent, never overwrite existing ones.\n */\nfunction mergeEnv(\n settings: Record<string, unknown>,\n incoming: Record<string, string>,\n report: string[],\n): boolean {\n let changed = false;\n\n const existing = (\n typeof settings[\"env\"] === \"object\" && settings[\"env\"] !== null\n ? settings[\"env\"]\n : {}\n ) as Record<string, string>;\n\n for (const [key, value] of Object.entries(incoming)) {\n if (Object.prototype.hasOwnProperty.call(existing, key)) {\n report.push(chalk.dim(` Skipped: env.${key} already set`));\n } else {\n existing[key] = value;\n report.push(chalk.green(` Added env: ${key}`));\n changed = true;\n }\n }\n\n settings[\"env\"] = existing;\n return changed;\n}\n\n/**\n * Strip file extension from a command basename for extension-agnostic dedup.\n * This ensures that e.g. \"context-compression-hook.ts\" and\n * \"context-compression-hook.mjs\" are treated as the same hook.\n */\nfunction commandStem(cmd: string): string {\n const base = cmd.split(\"/\").pop() ?? cmd;\n return base.replace(/\\.(mjs|ts|js|sh)$/, \"\");\n}\n\n/**\n * Collect every command string already registered for a given hookType.\n * Stores full command, basename, AND extension-stripped stem for flexible\n * matching (handles ${PAI_DIR}/Hooks/foo.sh vs /Users/.../Hooks/foo.sh,\n * and .ts → .mjs migrations).\n */\nfunction existingCommandsForHookType(rules: HookRule[]): Set<string> {\n const cmds = new Set<string>();\n for (const rule of rules) {\n for (const entry of rule.hooks) {\n cmds.add(entry.command);\n // Also add the basename so expanded paths match template paths\n const base = entry.command.split(\"/\").pop();\n if (base) cmds.add(base);\n // Also add extension-stripped stem for cross-extension dedup\n cmds.add(commandStem(entry.command));\n }\n }\n return cmds;\n}\n\n/**\n * Find and remove an existing rule whose command has the same stem\n * (extension-agnostic) as the incoming command. Returns true if a\n * replacement was made. This handles .ts → .mjs migrations cleanly.\n */\nfunction replaceStaleHook(\n existingRules: HookRule[],\n incomingStem: string,\n incomingCommand: string,\n incomingMatcher: string | undefined,\n): boolean {\n for (let i = 0; i < existingRules.length; i++) {\n const rule = existingRules[i];\n for (let j = 0; j < rule.hooks.length; j++) {\n const existingStem = commandStem(rule.hooks[j].command);\n if (existingStem === incomingStem && rule.hooks[j].command !== incomingCommand) {\n // Replace the old command with the new one\n rule.hooks[j].command = incomingCommand;\n // Update matcher if provided\n if (incomingMatcher !== undefined) {\n rule.matcher = incomingMatcher;\n }\n return true;\n }\n }\n }\n return false;\n}\n\n/**\n * Merge hooks — append entries, deduplicating by command string.\n * Extension-agnostic: a .mjs hook replaces an existing .ts hook with the\n * same stem, ensuring clean .ts → .mjs migrations without duplicates.\n */\nfunction mergeHooks(\n settings: Record<string, unknown>,\n incoming: HookEntry[],\n report: string[],\n): boolean {\n let changed = false;\n\n const hooksSection = (\n typeof settings[\"hooks\"] === \"object\" && settings[\"hooks\"] !== null\n ? settings[\"hooks\"]\n : {}\n ) as HooksSection;\n\n for (const entry of incoming) {\n const { hookType, matcher, command } = entry;\n\n const existingRules: HookRule[] = Array.isArray(hooksSection[hookType])\n ? (hooksSection[hookType] as HookRule[])\n : [];\n\n const basename = command.split(\"/\").pop() ?? command;\n const stem = commandStem(command);\n\n // Exact match — already registered, skip\n const existingCmds = existingCommandsForHookType(existingRules);\n if (existingCmds.has(command) || existingCmds.has(basename)) {\n report.push(chalk.dim(` Skipped: hook ${hookType} → ${basename} already registered`));\n continue;\n }\n\n // Stem match with different extension — replace old entry (.ts → .mjs migration)\n if (existingCmds.has(stem)) {\n if (replaceStaleHook(existingRules, stem, command, matcher)) {\n hooksSection[hookType] = existingRules;\n report.push(chalk.yellow(` Upgraded: hook ${hookType} → ${basename} (replaced stale extension)`));\n changed = true;\n continue;\n }\n }\n\n // No match at all — append new rule\n const newRule: HookRule = {\n hooks: [{ type: \"command\", command }],\n };\n if (matcher !== undefined) {\n newRule.matcher = matcher;\n }\n\n existingRules.push(newRule);\n hooksSection[hookType] = existingRules;\n\n report.push(chalk.green(` Added hook: ${hookType} → ${basename}`));\n changed = true;\n }\n\n settings[\"hooks\"] = hooksSection;\n return changed;\n}\n\n/**\n * Merge statusLine — write only if the key is not already present.\n */\nfunction mergeStatusLine(\n settings: Record<string, unknown>,\n incoming: { type: string; command: string },\n report: string[],\n): boolean {\n if (Object.prototype.hasOwnProperty.call(settings, \"statusLine\")) {\n report.push(chalk.dim(\" Skipped: statusLine already configured\"));\n return false;\n }\n\n settings[\"statusLine\"] = { ...incoming };\n report.push(chalk.green(\" Added statusLine\"));\n return true;\n}\n\n/**\n * Merge permissions — append allow/deny entries, deduplicating.\n */\nfunction mergePermissions(\n settings: Record<string, unknown>,\n incoming: { allow?: string[]; deny?: string[] },\n report: string[],\n): boolean {\n let changed = false;\n\n const perms = (\n typeof settings[\"permissions\"] === \"object\" && settings[\"permissions\"] !== null\n ? settings[\"permissions\"]\n : {}\n ) as Record<string, string[]>;\n\n for (const list of [\"allow\", \"deny\"] as const) {\n const entries = incoming[list];\n if (!entries || entries.length === 0) continue;\n\n const existing: string[] = Array.isArray(perms[list]) ? perms[list] : [];\n const existingSet = new Set(existing);\n\n for (const entry of entries) {\n if (existingSet.has(entry)) {\n report.push(chalk.dim(` Skipped: permissions.${list} \"${entry}\" already present`));\n } else {\n existing.push(entry);\n existingSet.add(entry);\n report.push(chalk.green(` Added permissions.${list}: ${entry}`));\n changed = true;\n }\n }\n\n perms[list] = existing;\n }\n\n settings[\"permissions\"] = perms;\n return changed;\n}\n\n/**\n * Merge flags — set keys only if not already present, never overwrite.\n */\nfunction mergeFlags(\n settings: Record<string, unknown>,\n incoming: Record<string, unknown>,\n report: string[],\n): boolean {\n let changed = false;\n\n for (const [key, value] of Object.entries(incoming)) {\n if (Object.prototype.hasOwnProperty.call(settings, key)) {\n report.push(chalk.dim(` Skipped: ${key} already set`));\n } else {\n settings[key] = value;\n report.push(chalk.green(` Added flag: ${key}`));\n changed = true;\n }\n }\n\n return changed;\n}\n\n// ---------------------------------------------------------------------------\n// Public orchestrator\n// ---------------------------------------------------------------------------\n\n/**\n * Merge env vars, hooks, and/or a statusLine entry into ~/.claude/settings.json.\n * Never overwrites existing values — only adds what is missing.\n *\n * Returns { changed, report } where report contains human-readable lines.\n */\nexport function mergeSettings(opts: SettingsMergeOptions): MergeResult {\n const settings = readSettingsJson();\n const report: string[] = [];\n let changed = false;\n\n if (opts.env !== undefined && Object.keys(opts.env).length > 0) {\n if (mergeEnv(settings, opts.env, report)) changed = true;\n }\n\n if (opts.hooks !== undefined && opts.hooks.length > 0) {\n if (mergeHooks(settings, opts.hooks, report)) changed = true;\n }\n\n if (opts.statusLine !== undefined) {\n if (mergeStatusLine(settings, opts.statusLine, report)) changed = true;\n }\n\n if (opts.permissions !== undefined) {\n if (mergePermissions(settings, opts.permissions, report)) changed = true;\n }\n\n if (opts.flags !== undefined && Object.keys(opts.flags).length > 0) {\n if (mergeFlags(settings, opts.flags, report)) changed = true;\n }\n\n if (changed) {\n writeSettingsJson(settings);\n }\n\n return { changed, report };\n}\n","/** Steps 8b and 8: Assistant name prompt and settings.json patch. */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\nimport { mergeSettings } from \"../../settings-manager.js\";\nimport { c, line, section, type Rl, prompt, promptYesNo } from \"../utils.js\";\n\nexport async function stepDaName(rl: Rl): Promise<string> {\n section(\"Step 8b: Assistant Name\");\n line();\n line(\" Choose a name for your AI assistant. This name appears in tab titles\");\n line(\" and session notes when hooks are active.\");\n line();\n\n const answer = await prompt(rl, chalk.bold(\" Assistant name [PAI]: \"));\n const daName = answer || \"PAI\";\n line();\n console.log(c.ok(`Assistant name set to: ${daName}`));\n return daName;\n}\n\nexport async function stepSettings(rl: Rl, daName: string): Promise<boolean> {\n section(\"Step 8: Settings Patch\");\n line();\n line(\" PAI will add env vars, all hook registrations, permissions, and flags\");\n line(\" to ~/.claude/settings.json. Existing values are never overwritten.\");\n line();\n\n const patch = await promptYesNo(rl, \"Patch ~/.claude/settings.json with PAI hooks, env vars, and settings?\", true);\n if (!patch) {\n console.log(c.dim(\" Skipping settings patch.\"));\n return false;\n }\n\n const paiDir = join(homedir(), \".claude\");\n\n const result = mergeSettings({\n env: {\n PAI_DIR: paiDir,\n CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: \"80\",\n CLAUDE_CODE_MAX_OUTPUT_TOKENS: \"64000\",\n DA: daName,\n },\n hooks: [\n { hookType: \"SessionStart\", command: \"${PAI_DIR}/Hooks/session-commands.mjs\" },\n { hookType: \"SessionStart\", command: \"${PAI_DIR}/Hooks/load-core-context.mjs\" },\n { hookType: \"SessionStart\", command: \"${PAI_DIR}/Hooks/load-project-context.mjs\" },\n { hookType: \"SessionStart\", command: \"${PAI_DIR}/Hooks/initialize-session.mjs\" },\n { hookType: \"SessionStart\", command: \"${PAI_DIR}/Hooks/capture-all-events.mjs --event-type SessionStart\" },\n { hookType: \"SessionStart\", matcher: \"compact\", command: \"${PAI_DIR}/Hooks/post-compact-inject.mjs\" },\n { hookType: \"UserPromptSubmit\", command: \"${PAI_DIR}/Hooks/cleanup-session-files.mjs\" },\n { hookType: \"UserPromptSubmit\", command: \"${PAI_DIR}/Hooks/update-tab-titles.mjs\" },\n { hookType: \"UserPromptSubmit\", command: \"${PAI_DIR}/Hooks/capture-all-events.mjs --event-type UserPromptSubmit\" },\n { hookType: \"PreToolUse\", matcher: \"Bash\", command: \"${PAI_DIR}/Hooks/security-validator.mjs\" },\n { hookType: \"PreToolUse\", matcher: \"*\", command: \"${PAI_DIR}/Hooks/capture-all-events.mjs --event-type PreToolUse\" },\n { hookType: \"PostToolUse\", matcher: \"TodoWrite\", command: \"${PAI_DIR}/Hooks/sync-todo-to-md.mjs\" },\n { hookType: \"PostToolUse\", matcher: \"*\", command: \"${PAI_DIR}/Hooks/capture-all-events.mjs --event-type PostToolUse\" },\n { hookType: \"PostToolUse\", matcher: \"*\", command: \"${PAI_DIR}/Hooks/capture-tool-output.mjs\" },\n { hookType: \"PostToolUse\", matcher: \"*\", command: \"${PAI_DIR}/Hooks/update-tab-on-action.mjs\" },\n { hookType: \"Stop\", command: \"${PAI_DIR}/Hooks/stop-hook.mjs\" },\n { hookType: \"Stop\", command: \"${PAI_DIR}/Hooks/capture-all-events.mjs --event-type Stop\" },\n { hookType: \"Stop\", command: \"${PAI_DIR}/Hooks/pai-session-stop.sh\" },\n { hookType: \"SubagentStop\", command: \"${PAI_DIR}/Hooks/subagent-stop-hook.mjs\" },\n { hookType: \"SubagentStop\", command: \"${PAI_DIR}/Hooks/capture-all-events.mjs --event-type SubagentStop\" },\n { hookType: \"SessionEnd\", command: \"${PAI_DIR}/Hooks/capture-session-summary.mjs\" },\n { hookType: \"SessionEnd\", command: \"${PAI_DIR}/Hooks/capture-all-events.mjs --event-type SessionEnd\" },\n { hookType: \"PreCompact\", command: \"${PAI_DIR}/Hooks/context-compression-hook.mjs\" },\n { hookType: \"PreCompact\", command: \"${PAI_DIR}/Hooks/capture-all-events.mjs --event-type PreCompact\" },\n { hookType: \"PreCompact\", matcher: \"\", command: \"${PAI_DIR}/Hooks/pai-pre-compact.sh\" },\n ],\n statusLine: {\n type: \"command\",\n command: \"bash ${PAI_DIR}/statusline-command.sh\",\n },\n permissions: {\n allow: [\"Bash\", \"Read\", \"Write\", \"Edit\", \"Glob\", \"Grep\", \"WebFetch\", \"WebSearch\", \"NotebookEdit\", \"TodoWrite\", \"ExitPlanMode\", \"mcp__pai\"],\n deny: [\"Bash(rm -rf /)\", \"Bash(rm -rf /*)\", \"Bash(rm -rf ~)\", \"Bash(rm -rf $HOME)\", \"Bash(sudo rm -rf /)\", \"Bash(sudo rm -rf /*)\"],\n },\n flags: {\n enableAllProjectMcpServers: true,\n },\n });\n\n line();\n for (const r of result.report) {\n console.log(r);\n }\n\n if (!result.changed) {\n console.log(c.dim(\" Settings already up-to-date. No changes made.\"));\n }\n\n return result.changed;\n}\n","/** Step 9: PAI daemon installation via launchd plist. */\n\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport { c, line, section, type Rl, promptYesNo } from \"../utils.js\";\n\nexport async function stepDaemon(rl: Rl): Promise<boolean> {\n section(\"Step 9: Daemon Install\");\n line();\n line(\" The PAI daemon indexes your projects every 5 minutes in the background.\");\n line();\n\n const plistPath = join(homedir(), \"Library\", \"LaunchAgents\", \"com.pai.pai-daemon.plist\");\n const exists = existsSync(plistPath);\n\n if (exists) {\n console.log(c.dim(\" PAI daemon plist already installed.\"));\n line();\n\n const reinstall = await promptYesNo(rl, \"Reinstall the PAI daemon launchd plist?\", false);\n if (!reinstall) {\n console.log(c.dim(\" Keeping existing daemon installation.\"));\n return false;\n }\n } else {\n const install = await promptYesNo(rl, \"Install the PAI daemon to run automatically at login?\", true);\n if (!install) {\n console.log(c.dim(\" Skipping daemon install. Run manually: pai daemon install\"));\n return false;\n }\n }\n\n line();\n const result = spawnSync(\"pai\", [\"daemon\", \"install\"], { stdio: \"inherit\" });\n\n if (result.status !== 0) {\n console.log(c.warn(\" Daemon install failed. Run manually: pai daemon install\"));\n return false;\n }\n\n console.log(c.ok(\"Daemon installed as com.pai.pai-daemon.\"));\n return true;\n}\n","/** Step 10: PAI MCP server registration in ~/.claude.json. */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport { c, line, section, type Rl, promptYesNo } from \"../utils.js\";\n\nexport async function stepMcp(rl: Rl): Promise<boolean> {\n section(\"Step 10: MCP Registration\");\n line();\n line(\" Registering the PAI MCP server lets Claude Code call PAI tools directly.\");\n line();\n\n const claudeJsonPath = join(homedir(), \".claude.json\");\n if (existsSync(claudeJsonPath)) {\n try {\n const raw = readFileSync(claudeJsonPath, \"utf-8\");\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n const mcpServers = parsed[\"mcpServers\"] as Record<string, unknown> | undefined;\n if (mcpServers && Object.prototype.hasOwnProperty.call(mcpServers, \"pai\")) {\n console.log(c.ok(\"PAI MCP server already registered in ~/.claude.json.\"));\n console.log(c.dim(\" Skipping MCP registration.\"));\n return false;\n }\n } catch {\n // continue\n }\n }\n\n const register = await promptYesNo(rl, \"Register the PAI MCP server in ~/.claude.json?\", true);\n if (!register) {\n console.log(c.dim(\" Skipping MCP registration. Run manually: pai mcp install\"));\n return false;\n }\n\n line();\n const result = spawnSync(\"pai\", [\"mcp\", \"install\"], { stdio: \"inherit\" });\n\n if (result.status !== 0) {\n console.log(c.warn(\" MCP registration failed. Run manually: pai mcp install\"));\n return false;\n }\n\n console.log(c.ok(\"PAI MCP server registered in ~/.claude.json.\"));\n return true;\n}\n","/** Step 11: Directory scanning configuration and registry scan prompt. */\n\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\nimport { line, section, type Rl, promptYesNo } from \"../utils.js\";\n\nexport async function stepDirectories(rl: Rl): Promise<void> {\n section(\"Step 11: Directories to Index\");\n line();\n line(\" PAI indexes files in your registered projects. You can register projects\");\n line(\" individually with `pai project add <path>`, or let the registry scanner\");\n line(\" discover them automatically with `pai registry scan`.\");\n line();\n\n const defaults = [\n join(homedir(), \"Projects\"),\n join(homedir(), \"Documents\"),\n join(homedir(), \"dev\"),\n ].filter(existsSync);\n\n if (defaults.length > 0) {\n line(\" These directories exist on your system:\");\n for (const d of defaults) {\n console.log(chalk.dim(` ${d}`));\n }\n line();\n }\n\n const runScan = await promptYesNo(rl, \"Run `pai registry scan` to auto-detect projects after setup?\", false);\n\n if (runScan) {\n line();\n console.log(chalk.dim(\" Registry scan will run after setup completes.\"));\n } else {\n console.log(chalk.dim(\" Add projects manually: pai project add <path>\"));\n console.log(chalk.dim(\" Or discover them later: pai registry scan\"));\n }\n\n (stepDirectories as { _runScan?: boolean })._runScan = runScan;\n}\n","/** Step 12: Initial index — optionally starts the daemon and runs registry scan. */\n\nimport { spawnSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport { c, line, section, type Rl, promptYesNo } from \"../utils.js\";\nimport { stepDirectories } from \"./13-directories.js\";\n\nexport async function stepInitialIndex(rl: Rl): Promise<void> {\n section(\"Step 12: Initial Index\");\n line();\n line(\" Indexing scans your registered projects and builds the search index.\");\n line(\" The daemon runs indexing automatically every 5 minutes once started.\");\n line();\n\n const willScan = (stepDirectories as { _runScan?: boolean })._runScan;\n\n if (willScan) {\n const startDaemon = await promptYesNo(rl, \"Start the PAI daemon now? (enables background indexing)\", true);\n\n if (startDaemon) {\n line();\n console.log(c.dim(\" Starting daemon...\"));\n\n try {\n const result = spawnSync(\"pai\", [\"daemon\", \"serve\", \"--background\"], { stdio: \"pipe\", timeout: 10000 });\n if (result.status === 0) {\n console.log(c.ok(\"Daemon started in background.\"));\n } else {\n console.log(c.warn(\"Could not start daemon. Run manually: pai daemon serve\"));\n }\n } catch {\n console.log(c.warn(\"Could not start daemon. Run manually: pai daemon serve\"));\n }\n\n line();\n console.log(c.dim(\" Running registry scan to detect projects...\"));\n\n try {\n const result = spawnSync(\"pai\", [\"registry\", \"scan\"], { stdio: \"inherit\", timeout: 30000 });\n if (result.status !== 0) {\n console.log(c.warn(\"Registry scan encountered issues. Run `pai registry scan` manually.\"));\n }\n } catch {\n console.log(c.warn(\"Could not run registry scan. Run manually: pai registry scan\"));\n }\n } else {\n console.log(chalk.dim(\" Start the daemon later: pai daemon serve\"));\n console.log(chalk.dim(\" Scan projects later: pai registry scan\"));\n }\n } else {\n console.log(chalk.dim(\" Register projects with: pai project add <path>\"));\n console.log(chalk.dim(\" Then index them with: pai memory index --all\"));\n console.log(chalk.dim(\" Or start the daemon: pai daemon serve\"));\n }\n}\n","/** Step 13: Setup summary — displays all configuration choices made during setup. */\n\nimport chalk from \"chalk\";\nimport { CONFIG_FILE } from \"../../../../daemon/config.js\";\nimport { line, section } from \"../utils.js\";\n\nexport function stepSummary(\n configUpdates: Record<string, unknown>,\n claudeMdGenerated: boolean,\n paiSkillInstalled: boolean,\n aiSteeringRulesInstalled: boolean,\n skillStubsInstalled: boolean,\n hooksInstalled: boolean,\n tsHooksInstalled: boolean,\n settingsPatched: boolean,\n daName: string,\n daemonInstalled: boolean,\n mcpRegistered: boolean,\n): void {\n section(\"Setup Complete\");\n line();\n console.log(chalk.green(\" PAI Knowledge OS is configured!\"));\n line();\n\n const backend = configUpdates.storageBackend as string;\n const model = configUpdates.embeddingModel as string;\n\n line(chalk.bold(\" Configuration saved to: \") + chalk.dim(CONFIG_FILE));\n line();\n console.log(chalk.dim(\" Storage backend: \") + chalk.cyan(backend ?? \"sqlite\"));\n console.log(chalk.dim(\" Embedding model: \") + chalk.cyan(model && model !== \"none\" ? model : \"(none — keyword search only)\"));\n console.log(chalk.dim(\" CLAUDE.md: \") + chalk.cyan(claudeMdGenerated ? \"~/.claude/CLAUDE.md (generated)\" : \"(unchanged)\"));\n console.log(chalk.dim(\" PAI skill: \") + chalk.cyan(paiSkillInstalled ? \"~/.claude/skills/PAI/SKILL.md (installed)\" : \"(unchanged)\"));\n console.log(chalk.dim(\" Steering rules: \") + chalk.cyan(aiSteeringRulesInstalled ? \"~/.claude/skills/PAI/AI-STEERING-RULES.md (installed)\" : \"(unchanged)\"));\n console.log(chalk.dim(\" Skill stubs: \") + chalk.cyan(skillStubsInstalled ? \"18 MCP prompt stubs symlinked to ~/.claude/skills/\" : \"(unchanged)\"));\n console.log(chalk.dim(\" Hooks (shell): \") + chalk.cyan(hooksInstalled ? \"pai-pre-compact.sh, pai-session-stop.sh (installed)\" : \"(unchanged)\"));\n console.log(chalk.dim(\" Hooks (TS): \") + chalk.cyan(tsHooksInstalled ? \"14 .mjs hooks installed to ~/.claude/Hooks/\" : \"(unchanged)\"));\n console.log(chalk.dim(\" Assistant name: \") + chalk.cyan(daName));\n console.log(chalk.dim(\" Settings: \") + chalk.cyan(settingsPatched ? \"env vars, hooks, permissions, flags (patched)\" : \"(unchanged)\"));\n console.log(chalk.dim(\" Daemon: \") + chalk.cyan(daemonInstalled ? \"com.pai.pai-daemon (installed)\" : \"(unchanged)\"));\n console.log(chalk.dim(\" MCP: \") + chalk.cyan(mcpRegistered ? \"registered in ~/.claude.json\" : \"(unchanged)\"));\n line();\n console.log(chalk.bold.yellow(\" → RESTART Claude Code to activate all changes.\"));\n line();\n\n line(chalk.bold(\" Next steps:\"));\n line();\n console.log(chalk.dim(\" # Register a project\"));\n console.log(chalk.cyan(\" pai project add ~/your/project\"));\n line();\n console.log(chalk.dim(\" # Index your files\"));\n console.log(chalk.cyan(\" pai memory index --all\"));\n line();\n console.log(chalk.dim(\" # Search your knowledge\"));\n console.log(chalk.cyan(\" pai memory search \\\"your query\\\"\"));\n line();\n if (model && model !== \"none\") {\n console.log(chalk.dim(\" # Generate embeddings for semantic search\"));\n console.log(chalk.cyan(\" pai memory embed\"));\n line();\n console.log(chalk.dim(\" # Semantic search\"));\n console.log(chalk.cyan(\" pai memory search --mode semantic \\\"your query\\\"\"));\n line();\n }\n console.log(chalk.dim(\" # Start the background daemon\"));\n console.log(chalk.cyan(\" pai daemon serve\"));\n line();\n console.log(chalk.dim(\" # Show all commands\"));\n console.log(chalk.cyan(\" pai --help\"));\n line();\n}\n","/**\n * PAI setup wizard — main entry point.\n * Orchestrates all setup steps in order and registers the Commander command.\n */\n\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { existsSync } from \"node:fs\";\nimport { CONFIG_FILE, loadConfig } from \"../../../daemon/config.js\";\nimport { createRl, prompt, promptYesNo, line, mergeConfig } from \"./utils.js\";\nimport {\n stepWelcome,\n stepStorage,\n stepEmbedding,\n stepClaudeMd,\n stepPaiSkill,\n stepAiSteeringRules,\n stepSkillStubs,\n stepHooks,\n stepTsHooks,\n stepDaName,\n stepSettings,\n stepDaemon,\n stepMcp,\n stepDirectories,\n stepInitialIndex,\n stepSummary,\n} from \"./steps/index.js\";\n\nasync function runSetup(): Promise<void> {\n const rl = createRl();\n\n try {\n if (existsSync(CONFIG_FILE)) {\n const current = loadConfig();\n line();\n console.log(\n chalk.yellow(\" Note: PAI is already configured.\") +\n chalk.dim(\" Proceeding will update your existing configuration.\"),\n );\n console.log(chalk.dim(` Config: ${CONFIG_FILE}`));\n console.log(chalk.dim(` Current backend: ${current.storageBackend}`));\n line();\n\n const proceed = await promptYesNo(rl, \"Continue and update configuration?\", true);\n if (!proceed) {\n rl.close();\n line(chalk.dim(\" Setup cancelled.\"));\n line();\n return;\n }\n }\n\n // Step 1: Welcome\n stepWelcome();\n line();\n await prompt(rl, chalk.dim(\" Press Enter to begin setup...\"));\n\n // Step 2: Storage\n const storageConfig = await stepStorage(rl);\n\n // Step 3: Embeddings\n const embeddingConfig = await stepEmbedding(rl);\n\n // Step 4: Agent configuration (CLAUDE.md)\n const claudeMdGenerated = await stepClaudeMd(rl);\n\n // Step 5: PAI Skill\n const paiSkillInstalled = await stepPaiSkill(rl);\n\n // Step 6: AI Steering Rules\n const aiSteeringRulesInstalled = await stepAiSteeringRules(rl);\n\n // Step 7: Skill Stubs\n const skillStubsInstalled = await stepSkillStubs(rl);\n\n // Step 8: Hooks (shell scripts)\n const hooksInstalled = await stepHooks(rl);\n\n // Step 7b: TypeScript hooks (.mjs files)\n const tsHooksInstalled = await stepTsHooks(rl);\n\n // Step 8b: DA name\n const daName = await stepDaName(rl);\n\n // Step 8: Settings.json\n const settingsPatched = await stepSettings(rl, daName);\n\n // Step 9: Daemon\n const daemonInstalled = await stepDaemon(rl);\n\n // Step 10: MCP\n const mcpRegistered = await stepMcp(rl);\n\n // Step 11: Directories (informational — no config written)\n await stepDirectories(rl);\n\n // Write config after gathering all choices\n const allUpdates = { ...storageConfig, ...embeddingConfig };\n mergeConfig(allUpdates);\n\n line();\n console.log(chalk.green(\" Configuration saved.\"));\n\n // Step 12: Initial index\n await stepInitialIndex(rl);\n\n // Step 13: Summary\n stepSummary(\n allUpdates,\n claudeMdGenerated,\n paiSkillInstalled,\n aiSteeringRulesInstalled,\n skillStubsInstalled,\n hooksInstalled,\n tsHooksInstalled,\n settingsPatched,\n daName,\n daemonInstalled,\n mcpRegistered,\n );\n\n } finally {\n rl.close();\n }\n}\n\nexport function registerSetupCommand(program: Command): void {\n program\n .command(\"setup\")\n .alias(\"install\")\n .description(\n \"Interactive setup wizard — configure storage, embeddings, agent config, and indexing\",\n )\n .action(async () => {\n await runSetup();\n });\n}\n","/** Symlink management: create, validate, migrate, and clean vault project symlinks. */\n\nimport {\n existsSync,\n mkdirSync,\n symlinkSync,\n readdirSync,\n lstatSync,\n unlinkSync,\n readlinkSync,\n writeFileSync,\n rmdirSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { Database } from \"better-sqlite3\";\nimport type { ProjectRow, SyncStats } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** Find the project-root Notes directory for a project. */\nexport function findNotesDir(rootPath: string): string | null {\n const canonical = join(rootPath, \"Notes\");\n if (existsSync(canonical)) return canonical;\n const alt = join(rootPath, \".claude\", \"Notes\");\n if (existsSync(alt)) return alt;\n return null;\n}\n\n/**\n * Find the Claude Code session notes directory from the registry-stored value.\n * Returns null if not set, missing on disk, or identical to notesDir.\n */\nexport function findClaudeNotesDir(\n claudeNotesDirFromRegistry: string | null,\n notesDir: string | null\n): string | null {\n if (!claudeNotesDirFromRegistry) return null;\n if (!existsSync(claudeNotesDirFromRegistry)) return null;\n if (notesDir && claudeNotesDirFromRegistry === notesDir) return null;\n return claudeNotesDirFromRegistry;\n}\n\n/** Check whether a path exists via lstat (does not follow symlinks). */\nfunction lstatExists(p: string): boolean {\n try {\n lstatSync(p);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Resolve slug collisions by appending -2, -3, etc. */\nexport function uniqueSlug(base: string, taken: Set<string>): string {\n if (!taken.has(base)) return base;\n let n = 2;\n while (taken.has(`${base}-${n}`)) n++;\n return `${base}-${n}`;\n}\n\n/**\n * Remove broken symlinks from a directory (one level deep).\n * Returns count of entries removed.\n */\nexport function cleanBrokenSymlinks(dir: string): number {\n let removed = 0;\n if (!existsSync(dir)) return removed;\n for (const entry of readdirSync(dir)) {\n const full = join(dir, entry);\n try {\n const stat = lstatSync(full);\n if (stat.isSymbolicLink()) {\n const target = readlinkSync(full);\n if (!existsSync(target)) {\n unlinkSync(full);\n removed++;\n }\n }\n } catch {\n // Skip unreadable entries\n }\n }\n return removed;\n}\n\n/**\n * Ensure a sub-symlink inside a project directory is correct.\n * Returns true if a new symlink was created, false otherwise.\n */\nexport function ensureSubSymlink(\n linkPath: string,\n target: string,\n errors: string[],\n label: string\n): boolean {\n if (lstatExists(linkPath)) {\n try {\n const stat = lstatSync(linkPath);\n if (stat.isSymbolicLink()) {\n const current = readlinkSync(linkPath);\n if (current === target) {\n return false; // Already correct\n }\n unlinkSync(linkPath);\n } else {\n errors.push(`${label}: path exists but is not a symlink — skipped`);\n return false;\n }\n } catch (e) {\n errors.push(`${label}: error checking existing path — ${e}`);\n return false;\n }\n }\n\n try {\n symlinkSync(target, linkPath);\n return true;\n } catch (e) {\n errors.push(`${label}: symlink creation failed — ${e}`);\n return false;\n }\n}\n\n/**\n * Migrate a legacy flat symlink at `slugPath` to a real directory.\n * If `slugPath` is already a real directory, this is a no-op.\n * If it is a symlink (legacy), it is removed so mkdirSync can create the dir.\n */\nexport function migrateToProjectDir(\n slugPath: string,\n errors: string[],\n slug: string\n): boolean {\n if (!lstatExists(slugPath)) {\n return true; // Does not exist yet — mkdirSync will create it\n }\n\n try {\n const stat = lstatSync(slugPath);\n\n if (stat.isDirectory()) {\n return true; // Already a real directory — nothing to migrate\n }\n\n if (stat.isSymbolicLink()) {\n unlinkSync(slugPath);\n return true; // Removed old symlink — now ready for mkdirSync\n }\n\n errors.push(`${slug}: path exists as non-directory, non-symlink — skipped`);\n return false;\n } catch (e) {\n errors.push(`${slug}: error during migration — ${e}`);\n return false;\n }\n}\n\n/** Remove a project directory from the vault if it is empty. */\nfunction removeProjectDirIfEmpty(slugPath: string): void {\n try {\n const entries = readdirSync(slugPath);\n if (entries.length === 0) {\n rmdirSync(slugPath);\n }\n } catch {\n // Non-fatal\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public: syncVault\n// ---------------------------------------------------------------------------\n\n/**\n * Sync all active project Notes directories into the Obsidian vault.\n *\n * For each active project with at least one Notes source, creates:\n * {vault}/{slug}/ — real directory\n * notes → {root}/Notes/\n * sessions → ~/.claude/projects/{enc}/Notes/ (if different)\n *\n * Archived projects get a stub markdown file in {vault}/_archive/.\n */\nexport function syncVault(vaultPath: string, db: Database): SyncStats {\n const stats: SyncStats = { created: 0, updated: 0, removed: 0, stubbed: 0, errors: [] };\n\n mkdirSync(vaultPath, { recursive: true });\n stats.removed += cleanBrokenSymlinks(vaultPath);\n\n const projects = db\n .prepare(\n `SELECT id, slug, display_name, root_path, encoded_dir, status, obsidian_link, claude_notes_dir\n FROM projects\n ORDER BY status ASC, slug ASC`\n )\n .all() as ProjectRow[];\n\n const takenSlugs = new Set<string>();\n\n for (const project of projects) {\n if (project.status === \"active\") {\n const notesDir = findNotesDir(project.root_path);\n const claudeNotesDir = findClaudeNotesDir(project.claude_notes_dir, notesDir);\n\n if (!notesDir && !claudeNotesDir) {\n continue;\n }\n\n const slug = uniqueSlug(project.slug, takenSlugs);\n takenSlugs.add(slug);\n const slugPath = join(vaultPath, slug);\n\n if (!migrateToProjectDir(slugPath, stats.errors, slug)) {\n continue;\n }\n\n try {\n mkdirSync(slugPath, { recursive: true });\n } catch (e) {\n stats.errors.push(`${slug}: failed to create project directory — ${e}`);\n continue;\n }\n\n if (notesDir) {\n const notesLink = join(slugPath, \"notes\");\n const created = ensureSubSymlink(notesLink, notesDir, stats.errors, `${slug}/notes`);\n if (created) stats.created++;\n else stats.updated++;\n }\n\n if (claudeNotesDir) {\n const sessionsLink = join(slugPath, \"sessions\");\n const created = ensureSubSymlink(sessionsLink, claudeNotesDir, stats.errors, `${slug}/sessions`);\n if (created) stats.created++;\n else stats.updated++;\n }\n\n try {\n db.prepare(\"UPDATE projects SET obsidian_link = ?, updated_at = ? WHERE id = ?\").run(\n slugPath,\n Date.now(),\n project.id\n );\n } catch {\n // Non-fatal\n }\n } else if (project.status === \"archived\") {\n const archiveDir = join(vaultPath, \"_archive\");\n mkdirSync(archiveDir, { recursive: true });\n const stubPath = join(archiveDir, `${project.slug}.md`);\n if (!existsSync(stubPath)) {\n const content = [\n `# ${project.display_name}`,\n \"\",\n \"> Archived project — no live notes available.\",\n \"\",\n `- **Slug:** ${project.slug}`,\n `- **Root:** ${project.root_path}`,\n \"\",\n ].join(\"\\n\");\n try {\n writeFileSync(stubPath, content, \"utf-8\");\n stats.stubbed++;\n } catch (e) {\n stats.errors.push(`${project.slug} (archive stub): ${e}`);\n }\n }\n }\n }\n\n return stats;\n}\n","/** Index and topic page generation — writes _index.md and _topics/{tag}.md. */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type { Database } from \"better-sqlite3\";\nimport { fmtDate } from \"../../cli/utils.js\";\nimport type { ProjectRow, SessionStats, TagRow } from \"./types.js\";\nimport { findNotesDir, findClaudeNotesDir } from \"./symlinks.js\";\n\n/**\n * Generate _index.md listing all projects with session counts, tags, and\n * indicators for which note sources are available (notes, sessions, or both).\n */\nexport function generateIndex(vaultPath: string, db: Database): void {\n mkdirSync(vaultPath, { recursive: true });\n\n const rows = db\n .prepare(\n `SELECT p.id, p.slug, p.display_name, p.status, p.root_path,\n p.encoded_dir, p.claude_notes_dir,\n (SELECT COUNT(*) FROM sessions s WHERE s.project_id = p.id) AS session_count,\n (SELECT MAX(s.created_at) FROM sessions s WHERE s.project_id = p.id) AS last_active\n FROM projects p\n ORDER BY p.status ASC, p.updated_at DESC`\n )\n .all() as (ProjectRow & SessionStats)[];\n\n const getTagsForProject = db.prepare(\n `SELECT t.name FROM tags t\n JOIN project_tags pt ON pt.tag_id = t.id\n WHERE pt.project_id = ?\n ORDER BY t.name`\n );\n\n const active = rows.filter((r) => r.status === \"active\");\n const archived = rows.filter((r) => r.status !== \"active\");\n\n const lines: string[] = [\n \"# PAI Project Index\",\n \"\",\n `> Auto-generated by PAI Knowledge OS — ${new Date().toISOString()}`,\n \"\",\n \"## Active Projects\",\n \"\",\n \"| Project | Sessions | Last Active | Sources | Tags |\",\n \"| ------- | -------- | ----------- | ------- | ---- |\",\n ];\n\n for (const row of active) {\n const tags = (getTagsForProject.all(row.id) as TagRow[]).map((t) => `\\`${t.name}\\``).join(\" \");\n const lastActive = fmtDate(row.last_active);\n\n const notesDir = findNotesDir(row.root_path);\n const claudeNotesDir = findClaudeNotesDir(row.claude_notes_dir, notesDir);\n const sources: string[] = [];\n if (notesDir) sources.push(\"notes\");\n if (claudeNotesDir) sources.push(\"sessions\");\n const sourcesLabel = sources.length > 0 ? sources.join(\", \") : \"—\";\n\n lines.push(\n `| [[${row.slug}/|${row.display_name}]] | ${row.session_count} | ${lastActive} | ${sourcesLabel} | ${tags || \"—\"} |`\n );\n }\n\n if (archived.length > 0) {\n lines.push(\n \"\",\n \"## Archived Projects\",\n \"\",\n \"| Project | Sessions | Tags |\",\n \"| ------- | -------- | ---- |\"\n );\n for (const row of archived) {\n const tags = (getTagsForProject.all(row.id) as TagRow[]).map((t) => `\\`${t.name}\\``).join(\" \");\n lines.push(\n `| [${row.display_name}](_archive/${row.slug}.md) | ${row.session_count} | ${tags || \"—\"} |`\n );\n }\n }\n\n lines.push(\n \"\",\n \"---\",\n `*${rows.length} projects total — ${active.length} active, ${archived.length} archived*`\n );\n\n writeFileSync(join(vaultPath, \"_index.md\"), lines.join(\"\\n\") + \"\\n\", \"utf-8\");\n}\n\n/**\n * Generate per-tag topic pages at _topics/{tag}.md.\n * Returns count of pages written.\n */\nexport function generateTopicPages(vaultPath: string, db: Database): number {\n const topicsDir = join(vaultPath, \"_topics\");\n mkdirSync(topicsDir, { recursive: true });\n\n const allTags = db\n .prepare(\"SELECT id, name FROM tags ORDER BY name\")\n .all() as { id: number; name: string }[];\n\n const getProjectsForTag = db.prepare(\n `SELECT p.id, p.slug, p.display_name, p.status,\n (SELECT COUNT(*) FROM sessions s WHERE s.project_id = p.id) AS session_count,\n (SELECT MAX(s.created_at) FROM sessions s WHERE s.project_id = p.id) AS last_active\n FROM projects p\n JOIN project_tags pt ON pt.project_id = p.id\n WHERE pt.tag_id = ?\n ORDER BY p.status ASC, p.updated_at DESC`\n );\n\n let written = 0;\n for (const tag of allTags) {\n const projects = getProjectsForTag.all(tag.id) as (ProjectRow & SessionStats)[];\n if (!projects.length) continue;\n\n const lines: string[] = [\n `# Topic: ${tag.name}`,\n \"\",\n `> Auto-generated by PAI Knowledge OS — ${new Date().toISOString()}`,\n \"\",\n \"## Projects\",\n \"\",\n \"| Project | Status | Sessions | Last Active |\",\n \"| ------- | ------ | -------- | ----------- |\",\n ];\n\n for (const p of projects) {\n const link =\n p.status === \"active\"\n ? `[[${p.slug}/|${p.display_name}]]`\n : `[${p.display_name}](../_archive/${p.slug}.md)`;\n lines.push(`| ${link} | ${p.status} | ${p.session_count} | ${fmtDate(p.last_active)} |`);\n }\n\n lines.push(\"\", \"---\", `*${projects.length} project(s) tagged \\`${tag.name}\\`*`);\n\n writeFileSync(join(topicsDir, `${tag.name}.md`), lines.join(\"\\n\") + \"\\n\", \"utf-8\");\n written++;\n }\n\n return written;\n}\n\n/**\n * Default vault path: ~/.pai/obsidian-vault\n */\nexport function defaultVaultPath(): string {\n return join(homedir(), \".pai\", \"obsidian-vault\");\n}\n","/** Directory walking and session file discovery for vault note generation. */\n\nimport {\n existsSync,\n readdirSync,\n lstatSync,\n readlinkSync,\n} from \"node:fs\";\nimport { join, relative } from \"node:path\";\nimport type { SessionFile } from \"./types.js\";\n\nexport const SESSION_FILENAME_RE = /^(\\d{4}) - (\\d{4}-\\d{2})-\\d{2} - .+\\.md$/;\n\n/**\n * Walk a directory (non-recursive root level, then one level of YYYY/MM subdirs).\n * Returns absolute paths to all .md files found, skipping master note files.\n */\nexport function walkNotesDir(dir: string): string[] {\n const results: string[] = [];\n if (!existsSync(dir)) return results;\n\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return results;\n }\n\n for (const entry of entries) {\n if (entry.startsWith(\".\")) continue;\n // Skip any master note file (pattern: _{slug}-master.md or legacy _master.md)\n if (entry === \"_master.md\" || /^_[^/]+-master\\.md$/.test(entry)) continue;\n const full = join(dir, entry);\n let stat: ReturnType<typeof lstatSync>;\n try {\n stat = lstatSync(full);\n } catch {\n continue;\n }\n if (stat.isDirectory()) {\n if (/^\\d{4}$/.test(entry)) {\n let monthEntries: string[];\n try {\n monthEntries = readdirSync(full);\n } catch {\n continue;\n }\n for (const month of monthEntries) {\n const monthPath = join(full, month);\n if (/^\\d{2}$/.test(month) && existsSync(monthPath)) {\n let monthFiles: string[];\n try {\n monthFiles = readdirSync(monthPath);\n } catch {\n continue;\n }\n for (const f of monthFiles) {\n if (f.endsWith(\".md\") && !f.startsWith(\".\")) {\n results.push(join(monthPath, f));\n }\n }\n }\n }\n }\n } else if (entry.endsWith(\".md\")) {\n results.push(full);\n }\n }\n return results;\n}\n\n/**\n * Extract YYYY/MM from a session file path.\n * Tries the path first (/YYYY/MM/ pattern), then falls back to filename date.\n */\nexport function extractYearMonth(filePath: string): string {\n const pathMatch = filePath.match(/\\/(\\d{4})\\/(\\d{2})\\//);\n if (pathMatch) return `${pathMatch[1]}/${pathMatch[2]}`;\n\n const basename = filePath.split(\"/\").pop() ?? \"\";\n const nameMatch = basename.match(/^\\d{4} - (\\d{4})-(\\d{2})-\\d{2}/);\n if (nameMatch) return `${nameMatch[1]}/${nameMatch[2]}`;\n\n return \"unknown\";\n}\n\n/**\n * Collect all session .md files from the notes and sessions symlinks\n * inside a vault project directory.\n */\nexport function collectSessionFiles(slugPath: string): SessionFile[] {\n const sessionFiles: SessionFile[] = [];\n const subLinks = [\"notes\", \"sessions\"];\n\n for (const subLink of subLinks) {\n const linkPath = join(slugPath, subLink);\n if (!existsSync(linkPath)) continue;\n\n let realDir: string;\n try {\n const stat = lstatSync(linkPath);\n if (stat.isSymbolicLink()) {\n realDir = readlinkSync(linkPath);\n } else if (stat.isDirectory()) {\n realDir = linkPath;\n } else {\n continue;\n }\n } catch {\n continue;\n }\n\n const files = walkNotesDir(realDir);\n for (const absPath of files) {\n const basename = absPath.split(\"/\").pop() ?? \"\";\n if (!SESSION_FILENAME_RE.test(basename)) continue;\n\n const relFromReal = relative(realDir, absPath);\n const vaultRelPath = `${subLink}/${relFromReal}`;\n const wikilinkTarget = vaultRelPath.replace(/\\.md$/, \"\");\n\n sessionFiles.push({\n absPath,\n vaultRelPath,\n wikilinkTarget,\n yearMonth: extractYearMonth(absPath),\n basename: basename.replace(/\\.md$/, \"\"),\n });\n }\n }\n\n return sessionFiles;\n}\n","/** Master note generation and session tag fixing for vault projects. */\n\nimport {\n existsSync,\n unlinkSync,\n writeFileSync,\n readFileSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { Database } from \"better-sqlite3\";\nimport type { ProjectRow } from \"./types.js\";\nimport { collectSessionFiles, walkNotesDir } from \"./walk.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Build the per-project master note filename. */\nexport function masterFilename(slug: string): string {\n return `_${slug}-master.md`;\n}\n\n/**\n * Remove any old/broken backlink footer from a session file.\n * Returns true if the file was modified.\n */\nfunction removeOldBacklink(filePath: string): boolean {\n let content: string;\n try {\n content = readFileSync(filePath, \"utf-8\");\n } catch {\n return false;\n }\n const oldPattern = /\\n---\\n\\[\\[\\.\\.\\/[^\\]]+\\]\\]\\n?$/;\n const newPattern = /\\n---\\n\\[\\[_[^\\]]*-master\\|[^\\]]*\\]\\]\\n?$/;\n const cleaned = content.replace(oldPattern, \"\").replace(newPattern, \"\");\n if (cleaned === content) return false;\n try {\n writeFileSync(filePath, cleaned, \"utf-8\");\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Append the master note backlink footer to a session file, idempotently.\n * Only writes if the sentinel string is not already present.\n */\nfunction appendBacklinkIfMissing(\n filePath: string,\n slug: string,\n displayName: string\n): boolean {\n let content: string;\n try {\n content = readFileSync(filePath, \"utf-8\");\n } catch {\n return false;\n }\n const masterName = masterFilename(slug).replace(/\\.md$/, \"\");\n const sentinel = `[[${masterName}|`;\n if (content.includes(sentinel)) return false;\n\n const footer = `\\n---\\n[[${masterName}|← ${displayName} Master]]\\n`;\n try {\n writeFileSync(filePath, content + footer, \"utf-8\");\n return true;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public: generateMasterNotes\n// ---------------------------------------------------------------------------\n\n/**\n * Generate _master.md files for projects that have >= threshold session files.\n *\n * For each qualifying project, writes a {vaultPath}/{slug}/_master.md containing:\n * - Project title, session count + date range\n * - Sessions grouped by YYYY/MM with Obsidian [[wikilinks]]\n *\n * Also appends a backlink footer to each session file (idempotently).\n *\n * @param vaultPath Absolute path to the PAI Obsidian vault\n * @param db Registry SQLite database\n * @param threshold Minimum session count to generate a master note (default: 5)\n * @returns Number of master notes written\n */\nexport function generateMasterNotes(\n vaultPath: string,\n db: Database,\n threshold = 5\n): number {\n if (!existsSync(vaultPath)) return 0;\n\n const projects = db\n .prepare(\n `SELECT id, slug, display_name, root_path, encoded_dir, status, obsidian_link, claude_notes_dir\n FROM projects\n WHERE status = 'active'\n ORDER BY slug ASC`\n )\n .all() as ProjectRow[];\n\n let written = 0;\n\n for (const project of projects) {\n const slugPath = join(vaultPath, project.slug);\n if (!existsSync(slugPath)) continue;\n\n // Remove legacy _master.md if present\n const legacyMaster = join(slugPath, \"_master.md\");\n if (existsSync(legacyMaster)) {\n try { unlinkSync(legacyMaster); } catch { /* Non-fatal */ }\n }\n\n const sessionFiles = collectSessionFiles(slugPath);\n if (sessionFiles.length < threshold) continue;\n\n // Sort by yearMonth then basename\n sessionFiles.sort((a, b) => {\n if (a.yearMonth !== b.yearMonth) return a.yearMonth.localeCompare(b.yearMonth);\n return a.basename.localeCompare(b.basename);\n });\n\n const byMonth = new Map<string, typeof sessionFiles>();\n for (const sf of sessionFiles) {\n if (!byMonth.has(sf.yearMonth)) byMonth.set(sf.yearMonth, []);\n byMonth.get(sf.yearMonth)!.push(sf);\n }\n\n const firstDate = sessionFiles[0]?.basename.match(/\\d{4}-\\d{2}-\\d{2}/)?.[0] ?? \"unknown\";\n const lastDate = sessionFiles[sessionFiles.length - 1]?.basename.match(/\\d{4}-\\d{2}-\\d{2}/)?.[0] ?? \"unknown\";\n\n const lines: string[] = [\n `# Project: ${project.display_name}`,\n \"\",\n `> Auto-generated by PAI Knowledge OS — ${new Date().toISOString()}`,\n \"\",\n \"## Overview\",\n \"\",\n `- **Sessions:** ${sessionFiles.length}`,\n `- **Date range:** ${firstDate} to ${lastDate}`,\n `- **Tags:** #${project.slug} #project`,\n \"\",\n \"## Sessions by Month\",\n \"\",\n ];\n\n for (const [ym, files] of byMonth) {\n lines.push(`### ${ym}`, \"\");\n for (const sf of files) {\n lines.push(`- [[${sf.wikilinkTarget}|${sf.basename}]]`);\n }\n lines.push(\"\");\n }\n\n lines.push(\"## Topics\", \"\", `#${project.slug} #project`, \"\");\n\n const masterPath = join(slugPath, masterFilename(project.slug));\n try {\n writeFileSync(masterPath, lines.join(\"\\n\"), \"utf-8\");\n written++;\n } catch {\n continue;\n }\n\n for (const sf of sessionFiles) {\n removeOldBacklink(sf.absPath);\n appendBacklinkIfMissing(sf.absPath, project.slug, project.display_name);\n }\n }\n\n return written;\n}\n\n// ---------------------------------------------------------------------------\n// Public: fixSessionTags\n// ---------------------------------------------------------------------------\n\n/**\n * Remove the generic #Session tag from session note files across all projects.\n *\n * Scans all session and notes directories for every active project and removes\n * #Session (with or without a trailing space) from any `**Tags:**` line.\n *\n * @param db Registry SQLite database\n * @returns Object with counts: { filesScanned, filesModified, errors }\n */\nexport function fixSessionTags(\n db: Database\n): { filesScanned: number; filesModified: number; errors: string[] } {\n const results = { filesScanned: 0, filesModified: 0, errors: [] as string[] };\n\n const projects = db\n .prepare(\n `SELECT id, slug, display_name, root_path, encoded_dir, status, obsidian_link, claude_notes_dir\n FROM projects\n WHERE status = 'active'\n ORDER BY slug ASC`\n )\n .all() as ProjectRow[];\n\n for (const project of projects) {\n const dirsToScan: string[] = [];\n\n // Inline findNotesDir to avoid a circular dependency with symlinks.ts\n const canonical = join(project.root_path, \"Notes\");\n const alt = join(project.root_path, \".claude\", \"Notes\");\n const notesDir = existsSync(canonical) ? canonical : existsSync(alt) ? alt : null;\n\n if (notesDir) dirsToScan.push(notesDir);\n\n if (\n project.claude_notes_dir &&\n existsSync(project.claude_notes_dir) &&\n project.claude_notes_dir !== notesDir\n ) {\n dirsToScan.push(project.claude_notes_dir);\n }\n\n for (const dir of dirsToScan) {\n const files = walkNotesDir(dir);\n for (const filePath of files) {\n results.filesScanned++;\n let content: string;\n try {\n content = readFileSync(filePath, \"utf-8\");\n } catch (e) {\n results.errors.push(`${filePath}: read error — ${e}`);\n continue;\n }\n\n if (!content.includes(\"#Session\")) continue;\n\n const updated = content.replace(\n /(\\*\\*Tags:\\*\\*[^\\n]*)#Session ?/g,\n \"$1\"\n );\n\n if (updated === content) continue;\n\n const cleaned = updated.replace(/(\\*\\*Tags:\\*\\*) *\\n/g, \"$1\\n\");\n\n try {\n writeFileSync(filePath, cleaned, \"utf-8\");\n results.filesModified++;\n } catch (e) {\n results.errors.push(`${filePath}: write error — ${e}`);\n }\n }\n }\n }\n\n return results;\n}\n","/**\n * obsidian/status.ts — Vault health checking for PAI Phase 4\n *\n * Vault structure (per project):\n *\n * {vault}/{slug}/ — real directory\n * notes → {root}/Notes/ (project root notes, optional)\n * sessions → ~/.claude/projects/{enc}/Notes/ (Claude Code session notes, optional)\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport { existsSync, readdirSync, lstatSync, readlinkSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface ProjectRow {\n id: number;\n slug: string;\n display_name: string;\n root_path: string;\n status: string;\n claude_notes_dir: string | null;\n obsidian_link: string | null;\n}\n\nexport interface SymlinkHealth {\n slug: string;\n linkPath: string;\n target: string | null;\n state: \"healthy\" | \"broken\" | \"orphaned\" | \"missing\";\n notes: string;\n}\n\nexport interface VaultHealthReport {\n vaultPath: string;\n healthy: SymlinkHealth[];\n broken: SymlinkHealth[];\n orphaned: SymlinkHealth[];\n missing: SymlinkHealth[];\n totalProjects: number;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Check the health of a sub-symlink inside a project directory.\n * Returns true if the symlink exists and its target exists on disk.\n */\nfunction subSymlinkHealthy(dir: string, name: string): boolean {\n const linkPath = join(dir, name);\n try {\n const stat = lstatSync(linkPath);\n if (!stat.isSymbolicLink()) return false;\n const target = readlinkSync(linkPath);\n return existsSync(target);\n } catch {\n return false;\n }\n}\n\n/**\n * Check if a sub-symlink exists (regardless of target validity).\n */\nfunction subSymlinkExists(dir: string, name: string): boolean {\n try {\n lstatSync(join(dir, name));\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Find a project root Notes dir (checks canonical and .claude/Notes fallback).\n */\nfunction findNotesDir(rootPath: string): string | null {\n const canonical = join(rootPath, \"Notes\");\n if (existsSync(canonical)) return canonical;\n const alt = join(rootPath, \".claude\", \"Notes\");\n if (existsSync(alt)) return alt;\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Check health of the Obsidian vault:\n *\n * Project entries are now real directories with sub-symlinks (`notes`, `sessions`).\n *\n * - healthy: project dir exists; at least one sub-symlink is present and resolves\n * - broken: project dir exists but all sub-symlinks have missing targets\n * - orphaned: directory entry in vault has no matching registry project\n * - missing: active project with at least one Notes source but no vault dir\n */\nexport function checkHealth(vaultPath: string, db: Database): VaultHealthReport {\n const report: VaultHealthReport = {\n vaultPath,\n healthy: [],\n broken: [],\n orphaned: [],\n missing: [],\n totalProjects: 0,\n };\n\n if (!existsSync(vaultPath)) {\n return report;\n }\n\n const projects = db\n .prepare(\n `SELECT id, slug, display_name, root_path, status, claude_notes_dir, obsidian_link\n FROM projects WHERE status = 'active'`\n )\n .all() as ProjectRow[];\n\n report.totalProjects = projects.length;\n\n // Build a map of slug → project for quick lookup\n const projectsBySlug = new Map<string, ProjectRow>();\n for (const p of projects) {\n projectsBySlug.set(p.slug, p);\n }\n\n // Scan vault directory\n const seenSlugs = new Set<string>();\n let entries: string[] = [];\n try {\n entries = readdirSync(vaultPath);\n } catch {\n return report;\n }\n\n for (const entry of entries) {\n // Skip internal PAI-generated dirs/files\n if (entry.startsWith(\"_\")) continue;\n\n const fullPath = join(vaultPath, entry);\n let stat;\n try {\n stat = lstatSync(fullPath);\n } catch {\n continue;\n }\n\n const project = projectsBySlug.get(entry);\n\n if (stat.isDirectory()) {\n // New-style project directory\n if (!project) {\n // Directory in vault with no matching registry project\n report.orphaned.push({\n slug: entry,\n linkPath: fullPath,\n target: null,\n state: \"orphaned\",\n notes: \"No matching active project in registry (directory)\",\n });\n continue;\n }\n\n seenSlugs.add(entry);\n\n const notesOk = subSymlinkHealthy(fullPath, \"notes\");\n const sessionsOk = subSymlinkHealthy(fullPath, \"sessions\");\n const notesExists = subSymlinkExists(fullPath, \"notes\");\n const sessionsExists = subSymlinkExists(fullPath, \"sessions\");\n\n if (notesOk || sessionsOk) {\n const parts: string[] = [];\n if (notesOk) parts.push(\"notes\");\n if (sessionsOk) parts.push(\"sessions\");\n report.healthy.push({\n slug: entry,\n linkPath: fullPath,\n target: null,\n state: \"healthy\",\n notes: `Sources: ${parts.join(\", \")}`,\n });\n } else {\n // At least one sub-link exists but none resolves\n const broken: string[] = [];\n if (notesExists) broken.push(\"notes\");\n if (sessionsExists) broken.push(\"sessions\");\n report.broken.push({\n slug: entry,\n linkPath: fullPath,\n target: null,\n state: \"broken\",\n notes: `Broken sub-symlinks: ${broken.join(\", \") || \"(empty directory)\"}`,\n });\n }\n } else if (stat.isSymbolicLink()) {\n // Legacy flat symlink — treat same as before\n let target: string | null = null;\n try {\n target = readlinkSync(fullPath);\n } catch {\n // Can't read symlink\n }\n\n const targetExists = target !== null && existsSync(target);\n\n if (!project) {\n report.orphaned.push({\n slug: entry,\n linkPath: fullPath,\n target,\n state: \"orphaned\",\n notes: \"No matching active project in registry (legacy symlink)\",\n });\n continue;\n }\n\n seenSlugs.add(entry);\n\n if (targetExists) {\n report.healthy.push({\n slug: entry,\n linkPath: fullPath,\n target,\n state: \"healthy\",\n notes: \"(legacy flat symlink — run sync to upgrade)\",\n });\n } else {\n report.broken.push({\n slug: entry,\n linkPath: fullPath,\n target,\n state: \"broken\",\n notes: `Target missing: ${target ?? \"(unknown)\"}`,\n });\n }\n }\n // Non-symlink, non-directory entries are ignored (e.g. stray files)\n }\n\n // Find active projects with Notes/ dirs that have no vault entry\n for (const project of projects) {\n if (seenSlugs.has(project.slug)) continue;\n\n const notesCanonical = join(project.root_path, \"Notes\");\n const notesAlt = join(project.root_path, \".claude\", \"Notes\");\n const hasProjectNotes = existsSync(notesCanonical) || existsSync(notesAlt);\n const hasClaudeNotes =\n project.claude_notes_dir !== null && existsSync(project.claude_notes_dir);\n\n if (hasProjectNotes || hasClaudeNotes) {\n const sources: string[] = [];\n if (hasProjectNotes) sources.push(\"notes\");\n if (hasClaudeNotes) sources.push(\"sessions\");\n report.missing.push({\n slug: project.slug,\n linkPath: join(vaultPath, project.slug),\n target: null,\n state: \"missing\",\n notes: `Has sources (${sources.join(\", \")}) but no vault entry — run 'pai obsidian sync'`,\n });\n }\n }\n\n return report;\n}\n","/**\n * pai obsidian <sub-command>\n *\n * sync — symlink project Notes/ dirs into vault, write _index.md + _topics/\n * status — health report: healthy, broken, orphaned, missing symlinks\n * open — open vault in Obsidian app (macOS)\n */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport { execSync } from \"node:child_process\";\nimport { ok, warn, err, dim, bold, header } from \"../utils.js\";\nimport {\n syncVault,\n generateIndex,\n generateTopicPages,\n generateMasterNotes,\n fixSessionTags,\n defaultVaultPath,\n} from \"../../obsidian/sync.js\";\nimport { checkHealth } from \"../../obsidian/status.js\";\nimport {\n loadConfig,\n expandHome,\n CONFIG_FILE,\n} from \"../../daemon/config.js\";\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport chalk from \"chalk\";\n\n// ---------------------------------------------------------------------------\n// Config helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Read obsidianVaultPath from ~/.config/pai/config.json.\n * Falls back to defaultVaultPath() if not set.\n */\nfunction getVaultPath(override?: string): string {\n if (override) return expandHome(override);\n\n try {\n const raw = readFileSync(CONFIG_FILE, \"utf-8\");\n const cfg = JSON.parse(raw) as Record<string, unknown>;\n if (typeof cfg.obsidianVaultPath === \"string\" && cfg.obsidianVaultPath) {\n return expandHome(cfg.obsidianVaultPath);\n }\n } catch {\n // Config missing or unreadable — use default\n }\n return defaultVaultPath();\n}\n\n/**\n * Persist obsidianVaultPath into config.json so future syncs use the same path.\n */\nfunction saveVaultPath(vaultPath: string): void {\n let cfg: Record<string, unknown> = {};\n try {\n cfg = JSON.parse(readFileSync(CONFIG_FILE, \"utf-8\")) as Record<string, unknown>;\n } catch {\n // Start fresh if file is missing/corrupt\n }\n cfg.obsidianVaultPath = vaultPath;\n mkdirSync(dirname(CONFIG_FILE), { recursive: true });\n writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2) + \"\\n\", \"utf-8\");\n}\n\n// ---------------------------------------------------------------------------\n// Command implementations\n// ---------------------------------------------------------------------------\n\nfunction cmdSync(\n db: Database,\n opts: { vault?: string; quiet?: boolean }\n): void {\n const vaultPath = getVaultPath(opts.vault);\n const q = opts.quiet ?? false;\n\n if (!q) {\n console.log();\n console.log(header(\" PAI Obsidian Sync\"));\n console.log(dim(` Vault: ${vaultPath}`));\n console.log();\n }\n\n // --- Symlinks ---\n if (!q) process.stdout.write(\" Syncing symlinks...\");\n const stats = syncVault(vaultPath, db);\n if (!q) {\n process.stdout.write(\n `\\r ${ok(\"Symlinks:\")} created ${stats.created} updated ${stats.updated} removed ${stats.removed} stubs ${stats.stubbed}\\n`\n );\n if (stats.errors.length) {\n for (const e of stats.errors) {\n console.log(warn(` Warning: ${e}`));\n }\n }\n }\n\n // --- Index ---\n if (!q) process.stdout.write(\" Generating index...\");\n generateIndex(vaultPath, db);\n if (!q) console.log(`\\r ${ok(\"Index:\")} _index.md written `);\n\n // --- Topic pages ---\n if (!q) process.stdout.write(\" Generating topic pages...\");\n const topicCount = generateTopicPages(vaultPath, db);\n if (!q) console.log(`\\r ${ok(\"Topics:\")} ${topicCount} topic page(s) written `);\n\n // --- Fix session tags (remove generic #Session) ---\n if (!q) process.stdout.write(\" Fixing session tags...\");\n const tagStats = fixSessionTags(db);\n if (!q) {\n console.log(\n `\\r ${ok(\"Tags:\")} ${tagStats.filesModified} file(s) updated (scanned ${tagStats.filesScanned}) `\n );\n if (tagStats.errors.length) {\n for (const e of tagStats.errors) {\n console.log(warn(` Warning: ${e}`));\n }\n }\n }\n\n // --- Master notes ---\n if (!q) process.stdout.write(\" Generating master notes...\");\n const masterCount = generateMasterNotes(vaultPath, db);\n if (!q) console.log(`\\r ${ok(\"Masters:\")} ${masterCount} master note(s) written `);\n\n // Persist vault path so status/open can use it\n saveVaultPath(vaultPath);\n\n if (!q) {\n console.log();\n console.log(ok(` Done. Vault ready at: ${vaultPath}`));\n console.log();\n }\n}\n\nfunction cmdStatus(db: Database, opts: { vault?: string }): void {\n const vaultPath = getVaultPath(opts.vault);\n const report = checkHealth(vaultPath, db);\n\n console.log();\n console.log(header(\" PAI Obsidian Vault Status\"));\n console.log(dim(` Vault: ${vaultPath}`));\n console.log();\n\n if (!existsSync(vaultPath)) {\n console.log(warn(\" Vault directory does not exist. Run: pai obsidian sync\"));\n console.log();\n return;\n }\n\n // Summary line\n const healthyCount = report.healthy.length;\n const brokenCount = report.broken.length;\n const orphanedCount = report.orphaned.length;\n const missingCount = report.missing.length;\n\n console.log(\n ` ${chalk.green(\"Healthy:\")} ${healthyCount} ` +\n `${chalk.red(\"Broken:\")} ${brokenCount} ` +\n `${chalk.yellow(\"Orphaned:\")} ${orphanedCount} ` +\n `${chalk.cyan(\"Missing:\")} ${missingCount}`\n );\n console.log();\n\n if (report.healthy.length && report.healthy.length <= 10) {\n console.log(bold(\" Healthy symlinks:\"));\n for (const h of report.healthy) {\n console.log(` ${chalk.green(\"✓\")} ${bold(h.slug)} ${dim(\"→ \" + (h.target ?? \"\"))}`);\n }\n console.log();\n } else if (report.healthy.length > 10) {\n console.log(bold(` ${report.healthy.length} healthy symlinks (all good)`));\n console.log();\n }\n\n if (report.broken.length) {\n console.log(err(\" Broken symlinks (target missing):\"));\n for (const b of report.broken) {\n console.log(` ${chalk.red(\"✗\")} ${bold(b.slug)} ${dim(b.notes)}`);\n }\n console.log(dim(\" Fix: pai obsidian sync\"));\n console.log();\n }\n\n if (report.orphaned.length) {\n console.log(warn(\" Orphaned symlinks (no registry project):\"));\n for (const o of report.orphaned) {\n console.log(` ${chalk.yellow(\"?\")} ${bold(o.slug)} ${dim(o.notes)}`);\n }\n console.log();\n }\n\n if (report.missing.length) {\n console.log(warn(\" Projects with Notes/ but no vault symlink:\"));\n for (const m of report.missing) {\n console.log(` ${chalk.cyan(\"→\")} ${bold(m.slug)} ${dim(m.notes)}`);\n }\n console.log();\n }\n\n if (brokenCount === 0 && orphanedCount === 0 && missingCount === 0) {\n console.log(ok(\" Vault is healthy.\"));\n console.log();\n }\n}\n\nfunction cmdOpen(opts: { vault?: string }): void {\n const vaultPath = getVaultPath(opts.vault);\n // Derive vault name from last path component (Obsidian uses folder name as vault name)\n const parts = vaultPath.split(\"/\").filter(Boolean);\n const vaultName = parts[parts.length - 1] ?? \"obsidian-vault\";\n\n if (!existsSync(vaultPath)) {\n console.error(err(`Vault not found at: ${vaultPath}`));\n console.error(dim(\" Run: pai obsidian sync\"));\n process.exit(1);\n }\n\n const url = `obsidian://open?vault=${encodeURIComponent(vaultName)}`;\n console.log(dim(` Opening: ${url}`));\n try {\n execSync(`open \"${url}\"`, { stdio: \"ignore\" });\n console.log(ok(` Opened vault \"${vaultName}\" in Obsidian.`));\n } catch (e) {\n console.error(err(` Failed to open Obsidian: ${e}`));\n console.error(dim(\" Ensure Obsidian is installed and the vault is registered in Obsidian.\"));\n process.exit(1);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerObsidianCommands(\n obsidianCmd: Command,\n getDb: () => Database\n): void {\n // pai obsidian sync\n obsidianCmd\n .command(\"sync\")\n .description(\"Sync project Notes/ dirs into vault, generate _index.md and topic pages\")\n .option(\"--vault <path>\", \"Override vault path (default: ~/.pai/obsidian-vault)\")\n .option(\"--quiet\", \"Minimal output — suitable for cron/hook use\")\n .action((opts: { vault?: string; quiet?: boolean }) => {\n cmdSync(getDb(), opts);\n });\n\n // pai obsidian status\n obsidianCmd\n .command(\"status\")\n .description(\"Show vault health: healthy, broken, orphaned, and missing symlinks\")\n .option(\"--vault <path>\", \"Override vault path\")\n .action((opts: { vault?: string }) => {\n cmdStatus(getDb(), opts);\n });\n\n // pai obsidian open\n obsidianCmd\n .command(\"open\")\n .description(\"Open the vault in Obsidian (macOS)\")\n .option(\"--vault <path>\", \"Override vault path\")\n .action((opts: { vault?: string }) => {\n cmdOpen(opts);\n });\n}\n","/** Shared utilities for zettel CLI commands. */\n\nimport type { Database } from \"better-sqlite3\";\nimport { openFederation } from \"../../../memory/db.js\";\nimport { err } from \"../../utils.js\";\n\n/** Shorten a vault path to just the last 2-3 components for display. */\nexport function shortPath(p: string, parts = 3): string {\n const segments = p.split(\"/\").filter(Boolean);\n return segments.slice(-parts).join(\"/\");\n}\n\n// Lazy-loaded federation DB singleton\nlet _fedDb: Database | null = null;\n\n/** Get (or lazily open) the PAI federation database. */\nexport function getFedDb(): Database {\n if (!_fedDb) {\n try {\n _fedDb = openFederation();\n } catch (e) {\n console.error(err(`Failed to open PAI federation DB: ${e}`));\n process.exit(1);\n }\n }\n return _fedDb;\n}\n","/** zettel explore: follow link chains from a starting note. */\n\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { ok, warn, err, dim, bold, header } from \"../../utils.js\";\nimport { getFedDb, shortPath } from \"./utils.js\";\n\nasync function cmdExplore(\n note: string,\n opts: { depth?: string; direction?: string; mode?: string }\n): Promise<void> {\n const depth = parseInt(opts.depth ?? \"3\", 10);\n const direction = (opts.direction ?? \"both\") as \"forward\" | \"backward\" | \"both\";\n const mode = (opts.mode ?? \"all\") as \"sequential\" | \"associative\" | \"all\";\n\n const { zettelExplore } = await import(\"../../../zettelkasten/index.js\");\n const db = getFedDb();\n\n const result = zettelExplore(db, { startNote: note, depth, direction, mode });\n\n console.log();\n console.log(header(\" PAI Zettel Explore\"));\n console.log(dim(` Starting note: ${note}`));\n console.log(dim(` Depth: ${depth} Direction: ${direction} Mode: ${mode}`));\n console.log();\n\n if (result.nodes.length === 0) {\n console.log(warn(\" No connected notes found. Check that the note path exists in the vault index.\"));\n console.log();\n return;\n }\n\n console.log(` ${chalk.cyan(\"●\")} ${bold(shortPath(result.root))} ${dim(\"(root)\")}`);\n\n const byDepth = new Map<number, typeof result.nodes>();\n for (const node of result.nodes) {\n const list = byDepth.get(node.depth) ?? [];\n list.push(node);\n byDepth.set(node.depth, list);\n }\n\n for (let d = 1; d <= depth; d++) {\n const nodes = byDepth.get(d) ?? [];\n if (nodes.length === 0) continue;\n\n console.log();\n console.log(dim(` ${\" \".repeat(d - 1)}Depth ${d}:`));\n for (const node of nodes) {\n const indent = \" \".repeat(d);\n const isBranching = result.branchingPoints.includes(node.path);\n const typeColor = node.linkType === \"sequential\" ? chalk.blue : chalk.magenta;\n const branchMark = isBranching ? chalk.yellow(\" ⑂ branching\") : \"\";\n const title = node.title ?? shortPath(node.path);\n const stats = dim(`in:${node.inbound} out:${node.outbound}`);\n console.log(\n ` ${indent}${typeColor(\"→\")} ${bold(title)}${branchMark} ${stats} ${dim(typeColor(node.linkType))}`\n );\n }\n }\n\n console.log();\n const edgeSummary = `${result.edges.length} edges (${result.edges.filter(e => e.type === \"sequential\").length} sequential, ${result.edges.filter(e => e.type === \"associative\").length} associative)`;\n console.log(dim(` ${edgeSummary}`));\n if (result.branchingPoints.length > 0) {\n console.log(ok(` ${result.branchingPoints.length} branching point(s) found`));\n }\n if (result.maxDepthReached) {\n console.log(warn(\" Max depth reached — use --depth to explore further\"));\n }\n console.log();\n}\n\nexport function registerExploreCommand(parent: Command): void {\n parent\n .command(\"explore <note>\")\n .description(\"Follow link chains from a starting note\")\n .option(\"--depth <n>\", \"Maximum traversal depth (1-10)\", \"3\")\n .option(\"--direction <d>\", \"Link direction: forward | backward | both\", \"both\")\n .option(\"--mode <m>\", \"Edge mode: sequential | associative | all\", \"all\")\n .action(async (note: string, opts: { depth?: string; direction?: string; mode?: string }) => {\n try {\n await cmdExplore(note, opts);\n } catch (e) {\n console.error(err(` Error: ${e}`));\n process.exit(1);\n }\n });\n}\n","/** zettel health: vault structural health audit. */\n\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { ok, warn, err, dim, bold, header } from \"../../utils.js\";\nimport { getFedDb, shortPath } from \"./utils.js\";\n\nasync function cmdHealth(opts: {\n scope?: string;\n project?: string;\n days?: string;\n include?: string;\n}): Promise<void> {\n const scope = (opts.scope ?? \"full\") as \"full\" | \"recent\" | \"project\";\n const projectPath = opts.project;\n const recentDays = parseInt(opts.days ?? \"30\", 10);\n const includeTypes = opts.include\n ? (opts.include.split(\",\").map(s => s.trim()) as Array<\"dead_links\" | \"orphans\" | \"disconnected\" | \"low_connectivity\">)\n : undefined;\n\n const { zettelHealth } = await import(\"../../../zettelkasten/index.js\");\n const db = getFedDb();\n\n const result = zettelHealth(db, { scope, projectPath, recentDays, include: includeTypes });\n\n console.log();\n console.log(header(\" PAI Zettel Health\"));\n console.log(dim(` Scope: ${scope}${scope === \"project\" ? ` Path: ${projectPath ?? \"(none)\"}` : \"\"}${scope === \"recent\" ? ` Days: ${recentDays}` : \"\"}`));\n console.log();\n\n const score = result.healthScore;\n const scoreColor = score >= 80 ? chalk.green : score >= 60 ? chalk.yellow : chalk.red;\n const barWidth = 30;\n const filled = Math.round((score / 100) * barWidth);\n const bar = scoreColor(\"█\".repeat(filled)) + dim(\"░\".repeat(barWidth - filled));\n console.log(` Health Score: ${scoreColor(bold(String(score)))}% [${bar}]`);\n console.log();\n console.log(dim(` Files: ${result.totalFiles} Links: ${result.totalLinks}`));\n console.log();\n\n if (result.deadLinks.length === 0) {\n console.log(ok(\" Dead links: none\"));\n } else {\n console.log(warn(` Dead links: ${result.deadLinks.length}`));\n const preview = result.deadLinks.slice(0, 10);\n for (const dl of preview) {\n console.log(` ${chalk.red(\"✗\")} ${dim(shortPath(dl.sourcePath))} → ${bold(dl.targetRaw)} ${dim(`(line ${dl.lineNumber})`)}`);\n }\n if (result.deadLinks.length > 10) {\n console.log(dim(` ... and ${result.deadLinks.length - 10} more`));\n }\n console.log();\n }\n\n if (result.orphans.length === 0) {\n console.log(ok(\" Orphan notes: none\"));\n } else {\n console.log(warn(` Orphan notes: ${result.orphans.length}`));\n const preview = result.orphans.slice(0, 10);\n for (const o of preview) {\n console.log(` ${chalk.yellow(\"○\")} ${dim(shortPath(o))}`);\n }\n if (result.orphans.length > 10) {\n console.log(dim(` ... and ${result.orphans.length - 10} more`));\n }\n console.log();\n }\n\n if (result.disconnectedClusters <= 1) {\n console.log(ok(\" Disconnected clusters: 1 (fully connected)\"));\n } else {\n console.log(warn(` Disconnected clusters: ${result.disconnectedClusters}`));\n }\n\n if (result.lowConnectivity.length === 0) {\n console.log(ok(\" Low-connectivity: none\"));\n } else {\n console.log(warn(` Low-connectivity: ${result.lowConnectivity.length} note(s) with ≤1 link`));\n const preview = result.lowConnectivity.slice(0, 5);\n for (const lc of preview) {\n console.log(` ${chalk.dim(\"—\")} ${dim(shortPath(lc))}`);\n }\n if (result.lowConnectivity.length > 5) {\n console.log(dim(` ... and ${result.lowConnectivity.length - 5} more`));\n }\n }\n\n console.log();\n}\n\nexport function registerHealthCommand(parent: Command): void {\n parent\n .command(\"health\")\n .description(\"Vault structural health audit: dead links, orphans, connectivity\")\n .option(\"--scope <s>\", \"Scope: full | recent | project\", \"full\")\n .option(\"--project <path>\", \"Project path prefix (requires --scope project)\")\n .option(\"--days <n>\", \"Look-back window in days (requires --scope recent)\", \"30\")\n .option(\"--include <types>\", \"Comma-separated subset: dead_links,orphans,disconnected,low_connectivity\")\n .action(async (opts: { scope?: string; project?: string; days?: string; include?: string }) => {\n try {\n await cmdHealth(opts);\n } catch (e) {\n console.error(err(` Error: ${e}`));\n process.exit(1);\n }\n });\n}\n","/** zettel surprise: find semantically similar but graph-distant notes. */\n\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { warn, err, dim, bold, header } from \"../../utils.js\";\nimport { getFedDb, shortPath } from \"./utils.js\";\n\nasync function cmdSurprise(\n note: string,\n opts: { vaultProjectId?: string; limit?: string; minSimilarity?: string; minDistance?: string }\n): Promise<void> {\n if (!opts.vaultProjectId) {\n console.error(err(\" --vault-project-id is required\"));\n process.exit(1);\n }\n\n const vaultProjectId = parseInt(opts.vaultProjectId, 10);\n const limit = parseInt(opts.limit ?? \"10\", 10);\n const minSimilarity = parseFloat(opts.minSimilarity ?? \"0.3\");\n const minGraphDistance = parseInt(opts.minDistance ?? \"3\", 10);\n\n const { zettelSurprise } = await import(\"../../../zettelkasten/index.js\");\n const db = getFedDb();\n\n console.log();\n console.log(header(\" PAI Zettel Surprise\"));\n console.log(dim(` Reference note: ${note}`));\n process.stdout.write(dim(\" Searching for surprising connections...\\n\"));\n\n const results = await zettelSurprise(db, {\n referencePath: note,\n vaultProjectId,\n limit,\n minSimilarity,\n minGraphDistance,\n });\n\n if (results.length === 0) {\n console.log(warn(\" No surprising connections found. Try lowering --min-similarity or --min-distance.\"));\n console.log();\n return;\n }\n\n console.log();\n console.log(bold(` Found ${results.length} surprising connection(s):`));\n console.log();\n\n for (let i = 0; i < results.length; i++) {\n const r = results[i];\n const title = r.title ?? shortPath(r.path);\n const surpriseBar = Math.round(r.surpriseScore * 10);\n console.log(` ${chalk.cyan(String(i + 1).padStart(2, \" \"))}. ${bold(title)}`);\n console.log(` ${dim(\"Surprise:\")} ${chalk.magenta(r.surpriseScore.toFixed(3))} ${\"■\".repeat(surpriseBar)}${\"□\".repeat(10 - surpriseBar)}`);\n console.log(` ${dim(\"Cosine:\")} ${r.cosineSimilarity.toFixed(3)} ${dim(\"Graph distance:\")} ${r.graphDistance}`);\n if (r.sharedSnippet) {\n console.log(` ${dim(\"Context:\")} ${r.sharedSnippet.slice(0, 120)}`);\n }\n console.log();\n }\n}\n\nexport function registerSurpriseCommand(parent: Command): void {\n parent\n .command(\"surprise <note>\")\n .description(\"Find semantically similar but graph-distant notes (surprising connections)\")\n .requiredOption(\"--vault-project-id <n>\", \"Project ID for the vault in the federation DB\")\n .option(\"--limit <n>\", \"Maximum results\", \"10\")\n .option(\"--min-similarity <f>\", \"Minimum cosine similarity (0–1)\", \"0.3\")\n .option(\"--min-distance <n>\", \"Minimum graph distance\", \"3\")\n .action(async (note: string, opts: { vaultProjectId?: string; limit?: string; minSimilarity?: string; minDistance?: string }) => {\n try {\n await cmdSurprise(note, opts);\n } catch (e) {\n console.error(err(` Error: ${e}`));\n process.exit(1);\n }\n });\n}\n","/** zettel suggest: suggest new wikilink connections for a note. */\n\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { warn, err, dim, bold, header } from \"../../utils.js\";\nimport { getFedDb, shortPath } from \"./utils.js\";\n\nasync function cmdSuggest(\n note: string,\n opts: { vaultProjectId?: string; limit?: string; excludeLinked?: boolean }\n): Promise<void> {\n if (!opts.vaultProjectId) {\n console.error(err(\" --vault-project-id is required\"));\n process.exit(1);\n }\n\n const vaultProjectId = parseInt(opts.vaultProjectId, 10);\n const limit = parseInt(opts.limit ?? \"5\", 10);\n const excludeLinked = opts.excludeLinked !== false;\n\n const { zettelSuggest } = await import(\"../../../zettelkasten/index.js\");\n const db = getFedDb();\n\n console.log();\n console.log(header(\" PAI Zettel Suggest\"));\n console.log(dim(` Note: ${note}`));\n process.stdout.write(dim(\" Computing suggestions...\\n\"));\n\n const suggestions = await zettelSuggest(db, {\n notePath: note,\n vaultProjectId,\n limit,\n excludeLinked,\n });\n\n if (suggestions.length === 0) {\n console.log(warn(\" No suggestions found. The note may be well-connected already.\"));\n console.log();\n return;\n }\n\n console.log();\n console.log(bold(` ${suggestions.length} suggested connection(s):`));\n console.log();\n\n for (let i = 0; i < suggestions.length; i++) {\n const s = suggestions[i];\n const title = s.title ?? shortPath(s.path);\n console.log(` ${chalk.green(String(i + 1).padStart(2, \" \"))}. ${bold(title)}`);\n console.log(` ${dim(\"Score:\")} ${chalk.green(s.score.toFixed(3))} ${dim(\"Semantic:\")} ${s.semanticScore.toFixed(2)} ${dim(\"Tag:\")} ${s.tagScore.toFixed(2)} ${dim(\"Neighbor:\")} ${s.neighborScore.toFixed(2)}`);\n console.log(` ${dim(\"Reason:\")} ${s.reason}`);\n console.log(` ${dim(\"Wikilink:\")} ${chalk.cyan(s.suggestedWikilink)}`);\n console.log();\n }\n}\n\nexport function registerSuggestCommand(parent: Command): void {\n parent\n .command(\"suggest <note>\")\n .description(\"Suggest new wikilink connections for a note\")\n .requiredOption(\"--vault-project-id <n>\", \"Project ID for the vault in the federation DB\")\n .option(\"--limit <n>\", \"Maximum suggestions\", \"5\")\n .option(\"--no-exclude-linked\", \"Include notes already linked from this one\")\n .action(async (note: string, opts: { vaultProjectId?: string; limit?: string; excludeLinked?: boolean }) => {\n try {\n await cmdSuggest(note, opts);\n } catch (e) {\n console.error(err(` Error: ${e}`));\n process.exit(1);\n }\n });\n}\n","/** zettel converse: ask the vault a question and get a synthesis prompt. */\n\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { warn, err, dim, bold, header } from \"../../utils.js\";\nimport { getFedDb, shortPath } from \"./utils.js\";\n\nasync function cmdConverse(\n question: string,\n opts: { vaultProjectId?: string; depth?: string; limit?: string }\n): Promise<void> {\n if (!opts.vaultProjectId) {\n console.error(err(\" --vault-project-id is required\"));\n process.exit(1);\n }\n\n const vaultProjectId = parseInt(opts.vaultProjectId, 10);\n const depth = parseInt(opts.depth ?? \"2\", 10);\n const limit = parseInt(opts.limit ?? \"15\", 10);\n\n const { zettelConverse } = await import(\"../../../zettelkasten/index.js\");\n const db = getFedDb();\n\n console.log();\n console.log(header(\" PAI Zettel Converse\"));\n console.log(dim(` Question: \"${question}\"`));\n process.stdout.write(dim(\" Searching vault for relevant notes...\\n\"));\n\n const result = await zettelConverse(db, { question, vaultProjectId, depth, limit });\n\n if (result.relevantNotes.length === 0) {\n console.log(warn(\" No relevant notes found. Try rephrasing your question.\"));\n console.log();\n return;\n }\n\n console.log();\n console.log(bold(` ${result.relevantNotes.length} relevant note(s) from ${result.domains.length} domain(s):`));\n console.log(dim(` Domains: ${result.domains.join(\", \")}`));\n console.log();\n\n for (const note of result.relevantNotes) {\n const title = note.title ?? shortPath(note.path);\n console.log(` ${chalk.cyan(\"◆\")} ${bold(title)} ${dim(`[${note.domain}] score: ${note.score.toFixed(3)}`)}`);\n if (note.snippet) {\n console.log(` ${dim(note.snippet.slice(0, 200))}`);\n }\n console.log();\n }\n\n if (result.connections.length > 0) {\n console.log(bold(\" Cross-domain connections:\"));\n for (const conn of result.connections.slice(0, 10)) {\n console.log(\n ` ${chalk.magenta(\"⟷\")} ${dim(conn.fromDomain)} ${chalk.dim(\"→\")} ${dim(conn.toDomain)} ` +\n `${dim(shortPath(conn.fromPath))} → ${dim(shortPath(conn.toPath))} ` +\n `${dim(`strength: ${conn.strength}`)}`\n );\n }\n console.log();\n }\n\n console.log(bold(\" Synthesis prompt (paste into your AI):\"));\n console.log();\n const promptLines = result.synthesisPrompt.split(\"\\n\");\n for (const line of promptLines) {\n console.log(` ${dim(line)}`);\n }\n console.log();\n}\n\nexport function registerConverseCommand(parent: Command): void {\n parent\n .command(\"converse <question>\")\n .description(\"Ask the vault a question and get a synthesis prompt with relevant notes\")\n .requiredOption(\"--vault-project-id <n>\", \"Project ID for the vault in the federation DB\")\n .option(\"--depth <n>\", \"Graph expansion depth around matched notes\", \"2\")\n .option(\"--limit <n>\", \"Maximum relevant notes to include\", \"15\")\n .action(async (question: string, opts: { vaultProjectId?: string; depth?: string; limit?: string }) => {\n try {\n await cmdConverse(question, opts);\n } catch (e) {\n console.error(err(` Error: ${e}`));\n process.exit(1);\n }\n });\n}\n","/** zettel themes: detect emerging theme clusters in recently edited notes. */\n\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { warn, err, dim, bold, header } from \"../../utils.js\";\nimport { getFedDb, shortPath } from \"./utils.js\";\n\nasync function cmdThemes(opts: {\n vaultProjectId?: string;\n days?: string;\n minSize?: string;\n maxThemes?: string;\n threshold?: string;\n}): Promise<void> {\n if (!opts.vaultProjectId) {\n console.error(err(\" --vault-project-id is required\"));\n process.exit(1);\n }\n\n const vaultProjectId = parseInt(opts.vaultProjectId, 10);\n const lookbackDays = parseInt(opts.days ?? \"30\", 10);\n const minClusterSize = parseInt(opts.minSize ?? \"3\", 10);\n const maxThemes = parseInt(opts.maxThemes ?? \"10\", 10);\n const similarityThreshold = parseFloat(opts.threshold ?? \"0.65\");\n\n const { zettelThemes } = await import(\"../../../zettelkasten/index.js\");\n const db = getFedDb();\n\n console.log();\n console.log(header(\" PAI Zettel Themes\"));\n console.log(dim(` Lookback: ${lookbackDays}d Min cluster: ${minClusterSize} Threshold: ${similarityThreshold}`));\n process.stdout.write(dim(\" Detecting emerging themes...\\n\"));\n\n const result = await zettelThemes(db, {\n vaultProjectId,\n lookbackDays,\n minClusterSize,\n maxThemes,\n similarityThreshold,\n });\n\n if (result.themes.length === 0) {\n console.log(warn(` No themes detected in the last ${lookbackDays} days. Try --days with a larger window.`));\n console.log();\n return;\n }\n\n const fromDate = new Date(result.timeWindow.from).toISOString().slice(0, 10);\n const toDate = new Date(result.timeWindow.to).toISOString().slice(0, 10);\n\n console.log();\n console.log(bold(` ${result.themes.length} theme(s) from ${result.totalNotesAnalyzed} notes [${fromDate} → ${toDate}]:`));\n console.log();\n\n for (let i = 0; i < result.themes.length; i++) {\n const cluster = result.themes[i];\n const diversityBar = Math.round(cluster.folderDiversity * 10);\n const indexSuggestion = cluster.suggestIndexNote ? chalk.yellow(\" ⚑ suggest index note\") : \"\";\n\n console.log(` ${chalk.cyan(String(i + 1).padStart(2, \" \"))}. ${bold(cluster.label)}${indexSuggestion}`);\n console.log(\n ` ${dim(\"Notes:\")} ${cluster.size} ` +\n `${dim(\"Diversity:\")} ${\"█\".repeat(diversityBar)}${\"░\".repeat(10 - diversityBar)} ${cluster.folderDiversity.toFixed(2)} ` +\n `${dim(\"Linked:\")} ${Math.round(cluster.linkedRatio * 100)}%`\n );\n\n const preview = cluster.notes.slice(0, 5);\n for (const note of preview) {\n const title = note.title ?? shortPath(note.path);\n console.log(` ${dim(\"•\")} ${title}`);\n }\n if (cluster.notes.length > 5) {\n console.log(dim(` ... and ${cluster.notes.length - 5} more`));\n }\n console.log();\n }\n}\n\nexport function registerThemesCommand(parent: Command): void {\n parent\n .command(\"themes\")\n .description(\"Detect emerging theme clusters in recently edited notes\")\n .requiredOption(\"--vault-project-id <n>\", \"Project ID for the vault in the federation DB\")\n .option(\"--days <n>\", \"Look-back window in days\", \"30\")\n .option(\"--min-size <n>\", \"Minimum notes per cluster\", \"3\")\n .option(\"--max-themes <n>\", \"Maximum themes to return\", \"10\")\n .option(\"--threshold <f>\", \"Similarity threshold for clustering (0–1)\", \"0.65\")\n .action(async (opts: { vaultProjectId?: string; days?: string; minSize?: string; maxThemes?: string; threshold?: string }) => {\n try {\n await cmdThemes(opts);\n } catch (e) {\n console.error(err(` Error: ${e}`));\n process.exit(1);\n }\n });\n}\n","/** Barrel: registers all zettel sub-commands and exports registerZettelCommands. */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport { registerExploreCommand } from \"./explore.js\";\nimport { registerHealthCommand } from \"./health.js\";\nimport { registerSurpriseCommand } from \"./surprise.js\";\nimport { registerSuggestCommand } from \"./suggest.js\";\nimport { registerConverseCommand } from \"./converse.js\";\nimport { registerThemesCommand } from \"./themes.js\";\n\nexport function registerZettelCommands(\n parent: Command,\n _getDb: () => Database // registry DB (unused — federation DB is opened directly)\n): void {\n registerExploreCommand(parent);\n registerHealthCommand(parent);\n registerSurpriseCommand(parent);\n registerSuggestCommand(parent);\n registerConverseCommand(parent);\n registerThemesCommand(parent);\n}\n","/**\n * pai observation <sub-command>\n *\n * list — List recent observations with filtering options\n * search — Search observations by title/narrative\n * stats — Show observation statistics\n */\n\nimport type { Command } from \"commander\";\nimport { createConnection } from \"net\";\nimport { ok, warn, err, dim, bold, header } from \"../utils.js\";\nimport chalk from \"chalk\";\n\n// ---------------------------------------------------------------------------\n// IPC helper — communicates with PAI daemon via Unix socket\n// ---------------------------------------------------------------------------\n\nfunction ipcCall(method: string, params: Record<string, unknown>): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let settled = false;\n const settle = (fn: () => void) => { if (!settled) { settled = true; fn(); } };\n\n const client = createConnection(\"/tmp/pai.sock\", () => {\n client.write(JSON.stringify({ id: 1, method, params }) + \"\\n\");\n });\n let data = \"\";\n client.on(\"data\", (chunk) => {\n data += chunk.toString();\n // Daemon sends a single JSON line then closes — try parsing eagerly\n try {\n const resp = JSON.parse(data) as { ok: boolean; result: unknown; error?: string };\n client.end();\n settle(() => resp.ok ? resolve(resp.result) : reject(new Error(resp.error ?? \"IPC call failed\")));\n } catch {\n // Incomplete — wait for more data or end\n }\n });\n client.on(\"end\", () => {\n settle(() => {\n try {\n const resp = JSON.parse(data) as { ok: boolean; result: unknown; error?: string };\n resp.ok ? resolve(resp.result) : reject(new Error(resp.error ?? \"IPC call failed\"));\n } catch (e) { reject(e); }\n });\n });\n client.on(\"error\", (e) => settle(() => reject(e)));\n setTimeout(() => { client.destroy(); settle(() => reject(new Error(\"IPC timeout\"))); }, 10000);\n });\n}\n\n// ---------------------------------------------------------------------------\n// Type colour mapping\n// ---------------------------------------------------------------------------\n\nfunction typeColor(type: string): string {\n switch (type) {\n case \"decision\": return chalk.cyan(type);\n case \"bugfix\": return chalk.red(type);\n case \"feature\": return chalk.green(type);\n case \"refactor\": return chalk.yellow(type);\n case \"discovery\": return chalk.blue(type);\n case \"change\": return chalk.magenta(type);\n default: return chalk.white(type);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Date formatting helper\n// ---------------------------------------------------------------------------\n\nfunction fmtTs(ts: string | null | undefined): string {\n if (!ts) return dim(\"—\");\n try {\n const d = new Date(ts);\n const date = d.toISOString().slice(0, 10);\n const time = d.toISOString().slice(11, 16);\n return `${date} ${time}`;\n } catch {\n return dim(\"—\");\n }\n}\n\n// ---------------------------------------------------------------------------\n// Truncate a string to maxLen visible characters\n// ---------------------------------------------------------------------------\n\nfunction trunc(s: string, maxLen: number): string {\n if (!s) return \"\";\n if (s.length <= maxLen) return s;\n return s.slice(0, maxLen - 1) + \"…\";\n}\n\n// ---------------------------------------------------------------------------\n// Observation shape (from IPC responses)\n// ---------------------------------------------------------------------------\n\ninterface Observation {\n id: number;\n type: string;\n title: string;\n project_slug?: string | null;\n session_id?: string | null;\n created_at: string;\n narrative?: string | null;\n}\n\ninterface ObservationStats {\n total: number;\n by_type: Array<{ type: string; count: number }>;\n by_project: Array<{ project_slug: string | null; count: number }>;\n most_recent: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Command: pai observation list\n// ---------------------------------------------------------------------------\n\nasync function cmdList(opts: {\n project?: string;\n type?: string;\n session?: string;\n limit?: string;\n}): Promise<void> {\n const limit = parseInt(opts.limit ?? \"20\", 10);\n\n const params: Record<string, unknown> = { limit };\n if (opts.project) params.project_slug = opts.project;\n if (opts.type) params.type = opts.type;\n if (opts.session) params.session_id = opts.session;\n\n let observations: Observation[];\n try {\n observations = (await ipcCall(\"observation_list\", params)) as Observation[];\n } catch (e) {\n console.error(err(` Failed to reach PAI daemon: ${e}`));\n console.error(dim(\" Is the daemon running? Try: pai daemon status\"));\n process.exit(1);\n }\n\n console.log();\n console.log(header(\" PAI Observations\"));\n\n const filterParts: string[] = [];\n if (opts.project) filterParts.push(`project: ${opts.project}`);\n if (opts.type) filterParts.push(`type: ${opts.type}`);\n if (opts.session) filterParts.push(`session: ${opts.session}`);\n filterParts.push(`limit: ${limit}`);\n console.log(dim(` ${filterParts.join(\" | \")}`));\n console.log();\n\n if (!observations || observations.length === 0) {\n console.log(warn(\" No observations found.\"));\n console.log();\n return;\n }\n\n // Column widths\n const ID_W = 4;\n const TYPE_W = 10;\n const TITLE_W = 42;\n const PROJ_W = 14;\n const TS_W = 16;\n\n // Header row\n console.log(\n \" \" +\n bold(\"id\".padEnd(ID_W)) + \" \" +\n bold(\"type\".padEnd(TYPE_W)) + \" \" +\n bold(\"title\".padEnd(TITLE_W)) + \" \" +\n bold(\"project\".padEnd(PROJ_W)) + \" \" +\n bold(\"created_at\")\n );\n console.log(\n dim(\n \" \" +\n \"-\".repeat(ID_W) + \" \" +\n \"-\".repeat(TYPE_W) + \" \" +\n \"-\".repeat(TITLE_W) + \" \" +\n \"-\".repeat(PROJ_W) + \" \" +\n \"-\".repeat(TS_W)\n )\n );\n\n for (const obs of observations) {\n const idStr = String(obs.id).padStart(ID_W, \" \");\n const typeStr = typeColor(obs.type ?? \"\").padEnd(TYPE_W + (typeColor(obs.type ?? \"\").length - (obs.type ?? \"\").length));\n const titleStr = trunc(obs.title ?? \"\", TITLE_W).padEnd(TITLE_W);\n const projStr = trunc(obs.project_slug ?? \"—\", PROJ_W).padEnd(PROJ_W);\n const tsStr = fmtTs(obs.created_at);\n\n console.log(` ${idStr} ${typeStr} ${titleStr} ${projStr} ${dim(tsStr)}`);\n }\n\n console.log();\n console.log(dim(` ${observations.length} observation(s)`));\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// Command: pai observation search <query>\n// ---------------------------------------------------------------------------\n\nasync function cmdSearch(\n query: string,\n opts: { project?: string; type?: string; limit?: string }\n): Promise<void> {\n const limit = parseInt(opts.limit ?? \"20\", 10);\n\n const params: Record<string, unknown> = { query, limit };\n if (opts.project) params.project_slug = opts.project;\n if (opts.type) params.type = opts.type;\n\n let allObservations: Observation[];\n try {\n // Fetch more than requested to allow client-side text filtering\n allObservations = (await ipcCall(\"observation_query\", { ...params, limit: limit * 5, query: undefined })) as Observation[];\n } catch (e) {\n console.error(err(` Failed to reach PAI daemon: ${e}`));\n console.error(dim(\" Is the daemon running? Try: pai daemon status\"));\n process.exit(1);\n }\n\n // Client-side text filter (observation_query doesn't support text search)\n const q = query.toLowerCase();\n const observations = allObservations\n .filter(o =>\n (o.title ?? \"\").toLowerCase().includes(q) ||\n (o.narrative ?? \"\").toLowerCase().includes(q) ||\n (o.tool_input_summary ?? \"\").toLowerCase().includes(q)\n )\n .slice(0, limit);\n\n console.log();\n console.log(header(\" PAI Observation Search\"));\n console.log(dim(` Query: \"${query}\"${opts.project ? ` project: ${opts.project}` : \"\"}${opts.type ? ` type: ${opts.type}` : \"\"} limit: ${limit}`));\n console.log();\n\n if (!observations || observations.length === 0) {\n console.log(warn(` No observations matching \"${query}\".`));\n console.log();\n return;\n }\n\n for (let i = 0; i < observations.length; i++) {\n const obs = observations[i];\n const idx = chalk.dim(String(i + 1).padStart(3, \" \"));\n const type = typeColor(obs.type ?? \"\");\n const title = bold(obs.title ?? \"(untitled)\");\n const proj = obs.project_slug ? dim(`[${obs.project_slug}]`) : dim(\"[—]\");\n const ts = dim(fmtTs(obs.created_at));\n const id = dim(`#${obs.id}`);\n\n console.log(` ${idx} ${type.padEnd(12)} ${title}`);\n console.log(` ${proj} ${ts} ${id}`);\n\n if (obs.narrative) {\n const snippet = trunc(obs.narrative.replace(/\\n/g, \" \"), 160);\n console.log(` ${dim(snippet)}`);\n }\n console.log();\n }\n\n console.log(dim(` ${observations.length} result(s)`));\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// Command: pai observation stats\n// ---------------------------------------------------------------------------\n\nasync function cmdStats(): Promise<void> {\n let stats: ObservationStats;\n try {\n stats = (await ipcCall(\"observation_stats\", {})) as ObservationStats;\n } catch (e) {\n console.error(err(` Failed to reach PAI daemon: ${e}`));\n console.error(dim(\" Is the daemon running? Try: pai daemon status\"));\n process.exit(1);\n }\n\n console.log();\n console.log(header(\" PAI Observation Statistics\"));\n console.log();\n\n console.log(` ${bold(\"Total observations:\")} ${chalk.cyan(String(stats.total ?? 0))}`);\n if (stats.most_recent) {\n console.log(` ${bold(\"Most recent:\")} ${dim(fmtTs(stats.most_recent))}`);\n }\n console.log();\n\n // By type\n if (stats.by_type && stats.by_type.length > 0) {\n console.log(bold(\" By type:\"));\n const maxCount = Math.max(...stats.by_type.map((r) => r.count));\n for (const row of stats.by_type) {\n const barWidth = 20;\n const filled = Math.round((row.count / maxCount) * barWidth);\n const bar = chalk.cyan(\"█\".repeat(filled)) + dim(\"░\".repeat(barWidth - filled));\n const label = typeColor(row.type).padEnd(12 + (typeColor(row.type).length - row.type.length));\n console.log(` ${label} ${bar} ${String(row.count).padStart(5)}`);\n }\n console.log();\n }\n\n // By project\n if (stats.by_project && stats.by_project.length > 0) {\n console.log(bold(\" By project:\"));\n const maxCount = Math.max(...stats.by_project.map((r) => r.count));\n const show = stats.by_project.slice(0, 15);\n for (const row of show) {\n const barWidth = 20;\n const filled = Math.round((row.count / maxCount) * barWidth);\n const bar = chalk.green(\"█\".repeat(filled)) + dim(\"░\".repeat(barWidth - filled));\n const label = (row.project_slug ?? \"—\").padEnd(20);\n console.log(` ${dim(label)} ${bar} ${String(row.count).padStart(5)}`);\n }\n if (stats.by_project.length > 15) {\n console.log(dim(` ... and ${stats.by_project.length - 15} more project(s)`));\n }\n console.log();\n }\n\n if ((!stats.by_type || stats.by_type.length === 0) && (!stats.by_project || stats.by_project.length === 0)) {\n console.log(warn(\" No observation data yet.\"));\n console.log();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerObservationCommands(parent: Command): void {\n // pai observation list\n parent\n .command(\"list\")\n .description(\"List recent observations\")\n .option(\"--project <slug>\", \"Filter by project slug\")\n .option(\"--type <type>\", \"Filter by type (decision, bugfix, feature, refactor, discovery, change)\")\n .option(\"--session <id>\", \"Filter by session ID\")\n .option(\"--limit <n>\", \"Maximum results\", \"20\")\n .action(async (opts: { project?: string; type?: string; session?: string; limit?: string }) => {\n try {\n await cmdList(opts);\n } catch (e) {\n console.error(err(` Error: ${e}`));\n process.exit(1);\n }\n });\n\n // pai observation search <query>\n parent\n .command(\"search <query>\")\n .description(\"Search observations by title or narrative text\")\n .option(\"--project <slug>\", \"Filter by project slug\")\n .option(\"--type <type>\", \"Filter by type\")\n .option(\"--limit <n>\", \"Maximum results\", \"20\")\n .action(async (query: string, opts: { project?: string; type?: string; limit?: string }) => {\n try {\n await cmdSearch(query, opts);\n } catch (e) {\n console.error(err(` Error: ${e}`));\n process.exit(1);\n }\n });\n\n // pai observation stats\n parent\n .command(\"stats\")\n .description(\"Show observation statistics: totals, by type, by project\")\n .action(async () => {\n try {\n await cmdStats();\n } catch (e) {\n console.error(err(` Error: ${e}`));\n process.exit(1);\n }\n });\n}\n","/**\n * pai update — Update PAI from the GitHub repository without losing customizations.\n *\n * Steps:\n * 1. Verify we're in a git repo with a remote (origin)\n * 2. Stash any local changes\n * 3. Pull latest from origin/main\n * 4. Pop the stash if there was one (handle conflicts gracefully)\n * 5. Rebuild: bun install && bun run build\n * 6. Restart the daemon (SIGHUP or pai daemon restart)\n * 7. Check if the CLAUDE.md template has changed and offer to refresh\n * 8. Run pai registry scan to update markers\n * 9. Report what changed\n */\n\nimport type { Command } from \"commander\";\nimport {\n existsSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { execSync, spawnSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport { ok, warn, err, dim, bold } from \"../utils.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction line(text = \"\") {\n console.log(text);\n}\n\nfunction step(msg: string) {\n console.log(chalk.bold.cyan(`\\n ${msg}`));\n}\n\nfunction info(msg: string) {\n console.log(dim(` ${msg}`));\n}\n\nfunction success(msg: string) {\n console.log(ok(` ${msg}`));\n}\n\nfunction warning(msg: string) {\n console.log(warn(` ${msg}`));\n}\n\nfunction error(msg: string) {\n console.log(err(` ${msg}`));\n}\n\n/**\n * Run a shell command, streaming output to stdout.\n * Returns true on success, false on failure.\n */\nfunction run(cmd: string, cwd?: string): boolean {\n try {\n execSync(cmd, {\n stdio: \"inherit\",\n cwd: cwd ?? process.cwd(),\n env: process.env,\n });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Run a shell command silently, returning stdout or null on failure.\n */\nfunction capture(cmd: string, cwd?: string): string | null {\n try {\n return execSync(cmd, {\n stdio: \"pipe\",\n cwd: cwd ?? process.cwd(),\n env: process.env,\n }).toString().trim();\n } catch {\n return null;\n }\n}\n\n/**\n * Get the PAI source directory: walk up from dist/cli/ or src/cli/ to the\n * package root where package.json lives.\n */\nfunction getPaiSrcDir(): string {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n // dist/cli/index.mjs → dist/ → package root\n return join(__dirname, \"../..\");\n}\n\n/**\n * Locate the templates directory relative to the installed package.\n */\nfunction getTemplatesDir(): string {\n const candidates = [\n join(getPaiSrcDir(), \"templates\"),\n join(homedir(), \"dev\", \"ai\", \"PAI\", \"templates\"),\n join(\"/\", \"usr\", \"local\", \"lib\", \"node_modules\", \"@mnott\", \"pai\", \"templates\"),\n ];\n for (const c of candidates) {\n if (existsSync(join(c, \"claude-md.template.md\"))) return c;\n }\n return join(getPaiSrcDir(), \"templates\");\n}\n\n// ---------------------------------------------------------------------------\n// Update steps\n// ---------------------------------------------------------------------------\n\n/**\n * Step 1: Verify git repo + remote.\n * Returns the PAI source directory (repo root) or null if not a git repo.\n */\nfunction stepVerifyRepo(): string | null {\n step(\"Checking repository...\");\n\n const paiDir = getPaiSrcDir();\n\n // Check git repo\n const gitDir = capture(\"git rev-parse --show-toplevel\", paiDir);\n if (!gitDir) {\n error(\"Not a git repository. Cannot update automatically.\");\n info(\"Install PAI via npm to get automatic updates: npm install -g @mnott/pai\");\n return null;\n }\n\n // Check remote\n const remote = capture(\"git remote get-url origin\", gitDir);\n if (!remote) {\n error(\"No 'origin' remote configured. Cannot pull updates.\");\n info(\"Add a remote: git remote add origin https://github.com/mnott/PAI.git\");\n return null;\n }\n\n info(`Repository: ${gitDir}`);\n info(`Remote: ${remote}`);\n return gitDir;\n}\n\n/**\n * Step 2: Stash local changes.\n * Returns true if something was stashed (so we need to pop later).\n */\nfunction stepStash(repoDir: string): boolean {\n step(\"Stashing local changes...\");\n\n // Check if there are any changes to stash\n const status = capture(\"git status --porcelain\", repoDir);\n if (!status) {\n info(\"Working tree is clean — nothing to stash.\");\n return false;\n }\n\n info(\"Uncommitted changes found:\");\n const statusLines = status.split(\"\\n\").slice(0, 5);\n for (const l of statusLines) {\n info(` ${l}`);\n }\n if (status.split(\"\\n\").length > 5) {\n info(` ... and ${status.split(\"\\n\").length - 5} more`);\n }\n\n const result = capture(\"git stash push -m 'pai update: auto-stash'\", repoDir);\n if (result === null) {\n warning(\"Could not stash changes. Proceeding anyway — merge conflicts may occur.\");\n return false;\n }\n\n if (result.includes(\"No local changes to save\")) {\n info(\"Nothing to stash.\");\n return false;\n }\n\n success(\"Changes stashed.\");\n return true;\n}\n\n/**\n * Step 3: Pull latest from origin/main.\n * Returns the git log summary of new commits or null if already up to date.\n */\nfunction stepPull(repoDir: string): string | null {\n step(\"Pulling latest from origin/main...\");\n\n // Get current HEAD before pull\n const headBefore = capture(\"git rev-parse HEAD\", repoDir);\n\n const pulled = run(\"git pull origin main\", repoDir);\n if (!pulled) {\n warning(\"git pull failed. There may be merge conflicts with your stash.\");\n return null;\n }\n\n // Get HEAD after pull\n const headAfter = capture(\"git rev-parse HEAD\", repoDir);\n\n if (headBefore === headAfter) {\n success(\"Already up to date — no new commits.\");\n return null;\n }\n\n // Show what changed\n const log = capture(\n `git log --oneline ${headBefore ?? \"\"}..${headAfter ?? \"HEAD\"}`,\n repoDir,\n );\n if (log) {\n success(\"New commits pulled:\");\n for (const l of log.split(\"\\n\")) {\n info(` ${l}`);\n }\n }\n\n return log;\n}\n\n/**\n * Step 4: Pop stash (if we stashed anything).\n */\nfunction stepPopStash(repoDir: string): void {\n step(\"Restoring local changes (git stash pop)...\");\n\n const result = spawnSync(\"git\", [\"stash\", \"pop\"], {\n cwd: repoDir,\n stdio: \"pipe\",\n encoding: \"utf8\",\n });\n\n if (result.status === 0) {\n success(\"Local changes restored.\");\n } else {\n const stderr = result.stderr ?? \"\";\n if (stderr.includes(\"CONFLICT\")) {\n warning(\"Stash pop caused merge conflicts. Resolve them manually:\");\n info(\" git status\");\n info(\" # Edit conflicting files\");\n info(\" git add <files>\");\n info(\" git stash drop\");\n } else {\n warning(`Stash pop encountered an issue: ${stderr.trim() || \"unknown error\"}`);\n info(\" Run: git stash pop\");\n info(\" Or: git stash list (to see stashed changes)\");\n }\n }\n}\n\n/**\n * Step 5: Rebuild.\n */\nfunction stepBuild(repoDir: string): boolean {\n step(\"Rebuilding PAI (bun install && bun run build)...\");\n\n info(\"Installing dependencies...\");\n const installed = run(\"bun install\", repoDir);\n if (!installed) {\n error(\"bun install failed.\");\n return false;\n }\n\n info(\"Building...\");\n const built = run(\"bun run build\", repoDir);\n if (!built) {\n error(\"bun run build failed.\");\n return false;\n }\n\n success(\"Build complete.\");\n return true;\n}\n\n/**\n * Step 6: Restart the daemon.\n * Tries SIGHUP first (graceful reload); falls back to pai daemon restart.\n */\nfunction stepRestartDaemon(repoDir: string): void {\n step(\"Restarting PAI daemon...\");\n\n // Try to find the daemon PID from the socket/pid file\n const pidFile = \"/tmp/pai-daemon.pid\";\n if (existsSync(pidFile)) {\n try {\n const pid = parseInt(readFileSync(pidFile, \"utf8\").trim(), 10);\n if (!isNaN(pid) && pid > 0) {\n const killed = capture(`kill -HUP ${pid}`);\n if (killed !== null) {\n success(`Sent SIGHUP to daemon (PID ${pid}).`);\n return;\n }\n }\n } catch {\n // fall through to pai daemon restart\n }\n }\n\n // Fall back to pai daemon restart subcommand\n const result = spawnSync(\"pai\", [\"daemon\", \"restart\"], {\n cwd: repoDir,\n stdio: \"pipe\",\n encoding: \"utf8\",\n timeout: 10_000,\n });\n\n if (result.status === 0) {\n success(\"Daemon restarted.\");\n } else {\n warning(\"Could not restart daemon automatically.\");\n info(\" Restart manually: pai daemon restart\");\n info(\" Or: pai daemon serve\");\n }\n}\n\n/**\n * Step 7: Check if CLAUDE.md template changed and offer to refresh.\n */\nfunction stepRefreshClaudeMd(repoDir: string, newCommitsLog: string | null): void {\n step(\"Checking CLAUDE.md template...\");\n\n const templatesDir = getTemplatesDir();\n const templatePath = join(templatesDir, \"claude-md.template.md\");\n const claudeMd = join(homedir(), \".claude\", \"CLAUDE.md\");\n\n if (!existsSync(templatePath)) {\n info(\"Template not found — skipping CLAUDE.md check.\");\n return;\n }\n\n if (!existsSync(claudeMd)) {\n info(\"No ~/.claude/CLAUDE.md found — skipping.\");\n return;\n }\n\n const userContent = readFileSync(claudeMd, \"utf8\");\n const isPaiGenerated = userContent.includes(\"Generated by PAI Setup\");\n\n if (!isPaiGenerated) {\n warning(\"~/.claude/CLAUDE.md appears to be custom (not PAI-generated).\");\n info(\" Skipping auto-update. Review the new template manually:\");\n info(` ${templatePath}`);\n return;\n }\n\n // Check if the template was actually modified in the new commits\n let templateChanged = false;\n if (newCommitsLog) {\n // Check git diff on the template file for newly pulled commits\n const diff = capture(\n \"git diff HEAD~1..HEAD -- templates/claude-md.template.md\",\n repoDir,\n );\n templateChanged = !!(diff && diff.trim().length > 0);\n }\n\n if (!templateChanged) {\n info(\"CLAUDE.md template unchanged in this update.\");\n return;\n }\n\n info(\"CLAUDE.md template was updated in this release.\");\n info(\"Refreshing ~/.claude/CLAUDE.md (PAI-generated — safe to overwrite)...\");\n\n let template = readFileSync(templatePath, \"utf8\");\n // Substitute ${HOME} with actual home directory\n template = template.replace(/\\$\\{HOME\\}/g, homedir());\n writeFileSync(claudeMd, template, \"utf8\");\n\n success(\"~/.claude/CLAUDE.md refreshed from updated template.\");\n}\n\n/**\n * Step 8: Run pai registry scan.\n */\nfunction stepRegistryScan(repoDir: string): void {\n step(\"Running pai registry scan...\");\n\n const result = spawnSync(\"pai\", [\"registry\", \"scan\"], {\n cwd: repoDir,\n stdio: \"inherit\",\n timeout: 60_000,\n });\n\n if (result.status === 0) {\n success(\"Registry scan complete.\");\n } else {\n warning(\"Registry scan encountered issues.\");\n info(\" Run manually: pai registry scan\");\n }\n}\n\n// ---------------------------------------------------------------------------\n// Main update action\n// ---------------------------------------------------------------------------\n\nasync function runUpdate(): Promise<void> {\n line();\n console.log(chalk.bold.cyan(\" ╔═══════════════════════════════════╗\"));\n console.log(chalk.bold.cyan(\" ║ PAI Knowledge OS — Update ║\"));\n console.log(chalk.bold.cyan(\" ╚═══════════════════════════════════╝\"));\n line();\n\n // Step 1: Verify repo\n const repoDir = stepVerifyRepo();\n if (!repoDir) {\n line();\n process.exit(1);\n }\n\n // Step 2: Stash local changes\n const didStash = stepStash(repoDir);\n\n // Step 3: Pull\n const newCommitsLog = stepPull(repoDir);\n\n // Step 4: Pop stash (only if we stashed something)\n if (didStash) {\n stepPopStash(repoDir);\n }\n\n // If no new commits and no stash needed, we can short-circuit after\n // confirming things are current. But we still offer rebuild in case the\n // user's build is stale.\n\n // Step 5: Build\n const buildOk = stepBuild(repoDir);\n if (!buildOk) {\n line();\n error(\"Update failed at build step. Check errors above.\");\n line();\n process.exit(1);\n }\n\n // Step 6: Restart daemon\n stepRestartDaemon(repoDir);\n\n // Step 7: CLAUDE.md refresh\n stepRefreshClaudeMd(repoDir, newCommitsLog);\n\n // Step 8: Registry scan\n stepRegistryScan(repoDir);\n\n // Summary\n line();\n console.log(chalk.bold.cyan(\" ─────────────────────────────────────\"));\n if (newCommitsLog) {\n success(\"PAI updated successfully!\");\n } else {\n success(\"PAI is up to date and rebuilt successfully!\");\n }\n console.log(dim(\" Version: \") + chalk.cyan(capture(\"pai --version\", repoDir) ?? \"unknown\"));\n line();\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerUpdateCommand(program: Command): void {\n program\n .command(\"update\")\n .description(\n \"Update PAI from GitHub (git pull + rebuild + daemon restart). Preserves local customizations.\",\n )\n .action(async () => {\n await runUpdate();\n });\n}\n","/**\n * pai notify <sub-command>\n *\n * status — Show current notification mode and active channels\n * get — Alias for status\n * set — Set notification mode or channel/routing config\n * test — Send a test notification through configured channels\n * send — Send a notification (event + message)\n *\n * All sub-commands communicate with the running daemon via IPC.\n */\n\nimport type { Command } from \"commander\";\nimport { ok, warn, err, dim, bold, header } from \"../utils.js\";\nimport { loadConfig } from \"../../daemon/config.js\";\nimport { PaiClient } from \"../../daemon/ipc-client.js\";\nimport type {\n NotificationConfig,\n NotificationMode,\n NotificationEvent,\n} from \"../../notifications/types.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction makeClient(): PaiClient {\n const config = loadConfig();\n return new PaiClient(config.socketPath);\n}\n\nfunction modeColor(mode: NotificationMode): string {\n switch (mode) {\n case \"off\":\n return warn(mode);\n case \"voice\":\n return bold(ok(mode));\n case \"auto\":\n return ok(mode);\n default:\n return ok(mode);\n }\n}\n\nfunction printConfig(config: NotificationConfig, activeChannels: string[]): void {\n console.log();\n console.log(header(\" PAI Notification Config\"));\n console.log();\n console.log(` ${bold(\"Mode:\")} ${modeColor(config.mode)}`);\n console.log();\n console.log(` ${bold(\"Channels:\")}`);\n for (const [ch, cfg] of Object.entries(config.channels)) {\n const c = cfg as { enabled: boolean; url?: string; voiceName?: string };\n const status = c.enabled ? ok(\"enabled\") : dim(\"disabled\");\n let extra = \"\";\n if (ch === \"ntfy\" && c.url) extra = dim(` ${c.url}`);\n if (ch === \"voice\" && c.voiceName) extra = dim(` voice: ${c.voiceName}`);\n console.log(` ${bold(ch.padEnd(12))}${status}${extra}`);\n }\n console.log();\n console.log(` ${bold(\"Active:\")} ${activeChannels.length > 0 ? activeChannels.join(\", \") : dim(\"(none)\")}`);\n console.log();\n console.log(` ${bold(\"Routing:\")} ${dim(\"(auto mode only)\")}`);\n for (const [event, channels] of Object.entries(config.routing)) {\n const ch = (channels as string[]).join(\", \") || dim(\"(none)\");\n console.log(` ${event.padEnd(12)}→ ${ch}`);\n }\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// Command implementations\n// ---------------------------------------------------------------------------\n\nasync function cmdStatus(): Promise<void> {\n const client = makeClient();\n try {\n const { config, activeChannels } = await client.getNotificationConfig();\n printConfig(config, activeChannels);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.log();\n console.log(warn(\" Cannot reach PAI daemon.\"));\n console.log(dim(` ${msg}`));\n console.log(dim(\" Start it with: pai daemon serve\"));\n console.log();\n process.exit(1);\n }\n}\n\nasync function cmdSet(opts: {\n mode?: string;\n ntfyUrl?: string;\n ntfyPriority?: string;\n whatsappRecipient?: string;\n enableChannel?: string[];\n disableChannel?: string[];\n}): Promise<void> {\n const client = makeClient();\n\n // Build the patch object\n const patch: {\n mode?: NotificationMode;\n channels?: Record<string, unknown>;\n } = {};\n\n if (opts.mode) {\n const validModes: NotificationMode[] = [\n \"auto\", \"voice\", \"whatsapp\", \"ntfy\", \"macos\", \"cli\", \"off\",\n ];\n if (!validModes.includes(opts.mode as NotificationMode)) {\n console.error(err(`Invalid mode: ${opts.mode}. Valid: ${validModes.join(\", \")}`));\n process.exit(1);\n }\n patch.mode = opts.mode as NotificationMode;\n }\n\n // Channel enable/disable\n const channels: Record<string, unknown> = {};\n\n if (opts.enableChannel) {\n for (const ch of opts.enableChannel) {\n channels[ch] = { enabled: true };\n }\n }\n\n if (opts.disableChannel) {\n for (const ch of opts.disableChannel) {\n channels[ch] = { enabled: false };\n }\n }\n\n if (opts.ntfyUrl) {\n channels[\"ntfy\"] = {\n ...(channels[\"ntfy\"] as object ?? {}),\n url: opts.ntfyUrl,\n };\n }\n\n if (opts.ntfyPriority) {\n channels[\"ntfy\"] = {\n ...(channels[\"ntfy\"] as object ?? {}),\n priority: opts.ntfyPriority,\n };\n }\n\n if (opts.whatsappRecipient) {\n channels[\"whatsapp\"] = {\n ...(channels[\"whatsapp\"] as object ?? {}),\n recipient: opts.whatsappRecipient,\n };\n }\n\n if (Object.keys(channels).length > 0) {\n patch.channels = channels;\n }\n\n if (!patch.mode && !patch.channels) {\n console.error(err(\"No changes specified. Use --mode, --enable, --disable, etc.\"));\n console.log(dim(\" Example: pai notify set --mode voice\"));\n console.log(dim(\" Example: pai notify set --mode auto --enable macos --disable ntfy\"));\n process.exit(1);\n }\n\n try {\n const { config } = await client.setNotificationConfig(\n patch as Parameters<typeof client.setNotificationConfig>[0]\n );\n console.log();\n console.log(ok(\" Notification config updated.\"));\n console.log(` ${bold(\"Mode:\")} ${modeColor(config.mode)}`);\n console.log();\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(err(` Failed: ${msg}`));\n process.exit(1);\n }\n}\n\nasync function cmdTest(opts: { event?: string }): Promise<void> {\n const client = makeClient();\n const event = (opts.event ?? \"info\") as NotificationEvent;\n\n console.log();\n console.log(dim(` Sending test ${event} notification...`));\n\n try {\n const result = await client.sendNotification({\n event,\n message: `PAI test notification (${event})`,\n title: \"PAI Test\",\n });\n\n if (result.channelsSucceeded.length === 0 && result.channelsAttempted.length > 0) {\n console.log(warn(\" All channels failed.\"));\n } else if (result.channelsAttempted.length === 0) {\n console.log(warn(\" No channels active for this event. Check mode and channel config.\"));\n } else {\n console.log(ok(` Sent to: ${result.channelsSucceeded.join(\", \")}`));\n }\n\n if (result.channelsFailed.length > 0) {\n console.log(warn(` Failed: ${result.channelsFailed.join(\", \")}`));\n }\n\n console.log(dim(` Mode: ${result.mode}`));\n console.log();\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(err(` Failed: ${msg}`));\n process.exit(1);\n }\n}\n\nasync function cmdSend(\n event: string,\n message: string,\n opts: { title?: string }\n): Promise<void> {\n const client = makeClient();\n const validEvents: NotificationEvent[] = [\n \"error\", \"progress\", \"completion\", \"info\", \"debug\",\n ];\n\n if (!validEvents.includes(event as NotificationEvent)) {\n console.error(\n err(`Invalid event: ${event}. Valid: ${validEvents.join(\", \")}`)\n );\n process.exit(1);\n }\n\n try {\n const result = await client.sendNotification({\n event: event as NotificationEvent,\n message,\n title: opts.title,\n });\n\n if (result.channelsSucceeded.length > 0) {\n console.log(ok(` Sent to: ${result.channelsSucceeded.join(\", \")}`));\n } else {\n console.log(warn(\" No channels received the notification.\"));\n }\n\n if (result.channelsFailed.length > 0) {\n console.log(warn(` Failed: ${result.channelsFailed.join(\", \")}`));\n }\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(err(` Failed: ${msg}`));\n process.exit(1);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerNotifyCommands(notifyCmd: Command): void {\n // pai notify status (default / alias for get)\n notifyCmd\n .command(\"status\")\n .description(\"Show current notification mode and active channels\")\n .action(async () => {\n await cmdStatus();\n });\n\n // pai notify get (alias for status)\n notifyCmd\n .command(\"get\")\n .description(\"Show current notification config (alias for status)\")\n .action(async () => {\n await cmdStatus();\n });\n\n // pai notify set\n notifyCmd\n .command(\"set\")\n .description(\"Update notification mode or channel configuration\")\n .option(\n \"--mode <mode>\",\n \"Notification mode: auto | voice | whatsapp | ntfy | macos | cli | off\"\n )\n .option(\"--enable <channel...>\", \"Enable one or more channels\")\n .option(\"--disable <channel...>\", \"Disable one or more channels\")\n .option(\"--ntfy-url <url>\", \"Set the ntfy.sh topic URL\")\n .option(\n \"--ntfy-priority <level>\",\n \"Set ntfy priority: min | low | default | high | urgent\"\n )\n .option(\"--whatsapp-recipient <contact>\", \"Set WhatsApp recipient\")\n .action(\n async (opts: {\n mode?: string;\n enable?: string[];\n disable?: string[];\n ntfyUrl?: string;\n ntfyPriority?: string;\n whatsappRecipient?: string;\n }) => {\n await cmdSet({\n mode: opts.mode,\n enableChannel: opts.enable,\n disableChannel: opts.disable,\n ntfyUrl: opts.ntfyUrl,\n ntfyPriority: opts.ntfyPriority,\n whatsappRecipient: opts.whatsappRecipient,\n });\n }\n );\n\n // pai notify test\n notifyCmd\n .command(\"test\")\n .description(\"Send a test notification through configured channels\")\n .option(\n \"--event <event>\",\n \"Event type to test: error | progress | completion | info | debug\",\n \"info\"\n )\n .action(async (opts: { event?: string }) => {\n await cmdTest(opts);\n });\n\n // pai notify send <event> <message>\n notifyCmd\n .command(\"send <event> <message>\")\n .description(\"Send a notification with an explicit event type and message\")\n .option(\"--title <title>\", \"Optional notification title\")\n .action(async (event: string, message: string, opts: { title?: string }) => {\n await cmdSend(event, message, opts);\n });\n}\n","/**\n * pai topic <sub-command>\n *\n * check <context> — Check whether context text has drifted to a different project\n *\n * Communicates with the running daemon via IPC.\n * The daemon uses BM25 keyword search to match context against indexed memory.\n *\n * Examples:\n * pai topic check \"working on authentication and JWT tokens\"\n * pai topic check \"fixing the React component\" --current myapp\n * pai topic check \"database schema migration\" --threshold 0.7\n */\n\nimport type { Command } from \"commander\";\nimport { ok, warn, err, dim, bold, header } from \"../utils.js\";\nimport { loadConfig } from \"../../daemon/config.js\";\nimport { PaiClient } from \"../../daemon/ipc-client.js\";\nimport type { TopicCheckResult } from \"../../topics/detector.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction makeClient(): PaiClient {\n const config = loadConfig();\n return new PaiClient(config.socketPath);\n}\n\nfunction confidenceBar(confidence: number, width = 20): string {\n const filled = Math.round(confidence * width);\n const empty = width - filled;\n return \"[\" + \"=\".repeat(filled) + \" \".repeat(empty) + \"]\";\n}\n\nfunction printTopicResult(result: TopicCheckResult): void {\n console.log();\n console.log(header(\" PAI Topic Check\"));\n console.log();\n\n console.log(` ${bold(\"Current project:\")} ${result.currentProject ?? dim(\"(none)\")}`);\n console.log(` ${bold(\"Suggested project:\")} ${result.suggestedProject ?? dim(\"(none)\")}`);\n console.log(\n ` ${bold(\"Confidence:\")} ${confidenceBar(result.confidence)} ${(result.confidence * 100).toFixed(1)}%`\n );\n console.log(` ${bold(\"Chunks scored:\")} ${result.chunkCount}`);\n\n if (result.topProjects.length > 0) {\n console.log();\n console.log(` ${bold(\"Top matches:\")}`);\n for (const p of result.topProjects) {\n const bar = confidenceBar(p.score, 15);\n const marker = p.slug === result.currentProject ? dim(\" (current)\") : \"\";\n console.log(` ${p.slug.padEnd(30)} ${bar} ${(p.score * 100).toFixed(1)}%${marker}`);\n }\n }\n\n console.log();\n\n if (result.shifted) {\n console.log(\n warn(\" TOPIC SHIFT DETECTED\") +\n dim(` — conversation appears to be about \"${result.suggestedProject}\", not \"${result.currentProject}\"`)\n );\n console.log();\n } else if (result.suggestedProject && result.suggestedProject === result.currentProject) {\n console.log(ok(\" No shift detected\") + dim(\" — context matches current project\"));\n console.log();\n } else if (!result.currentProject) {\n if (result.suggestedProject) {\n console.log(\n ok(\" Best matching project: \") + bold(result.suggestedProject) +\n dim(` (confidence: ${(result.confidence * 100).toFixed(0)}%)`)\n );\n } else {\n console.log(dim(\" No matching project found in memory index.\"));\n }\n console.log();\n } else {\n console.log(\n dim(` No shift detected (top match \"${result.suggestedProject}\" has confidence ${(result.confidence * 100).toFixed(0)}% — below threshold)`)\n );\n console.log();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Command implementations\n// ---------------------------------------------------------------------------\n\nasync function cmdCheck(\n context: string,\n opts: {\n current?: string;\n threshold?: string;\n json?: boolean;\n }\n): Promise<void> {\n const client = makeClient();\n\n const threshold = opts.threshold ? parseFloat(opts.threshold) : undefined;\n\n if (threshold !== undefined && (isNaN(threshold) || threshold < 0 || threshold > 1)) {\n console.error(err(\" --threshold must be a number between 0 and 1\"));\n process.exit(1);\n }\n\n try {\n const result = await client.topicCheck({\n context,\n currentProject: opts.current,\n threshold,\n });\n\n if (opts.json) {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n\n printTopicResult(result);\n\n // Exit with code 1 if a shift was detected (useful for scripting / hooks)\n if (result.shifted) {\n process.exit(1);\n }\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.log();\n console.log(warn(\" Cannot reach PAI daemon.\"));\n console.log(dim(` ${msg}`));\n console.log(dim(\" Start it with: pai daemon serve\"));\n console.log();\n process.exit(1);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerTopicCommands(topicCmd: Command): void {\n // pai topic check <context>\n topicCmd\n .command(\"check <context>\")\n .description(\n \"Check whether context text has drifted to a different project.\\n\" +\n 'Example: pai topic check \"working on JWT authentication\"\\n' +\n \"Exit code 1 if a shift is detected (useful for hooks).\"\n )\n .option(\n \"--current <slug>\",\n \"The project this session is currently routed to\"\n )\n .option(\n \"--threshold <n>\",\n \"Confidence threshold [0-1] to declare a shift (default: 0.6)\"\n )\n .option(\"--json\", \"Output raw JSON result\")\n .action(\n async (\n context: string,\n opts: { current?: string; threshold?: string; json?: boolean }\n ) => {\n await cmdCheck(context, opts);\n }\n );\n}\n","/**\n * kg-backfill.ts — Populate the temporal knowledge graph from existing\n * session notes.\n *\n * Walks `Notes/YYYY/MM/*.md` for each registered project, runs the same\n * extractor that the session-summary-worker uses on every NEW summary, and\n * stores triples in Postgres. Idempotent: a state file at\n * ~/.config/pai/kg-backfill-state.json records which note paths have been\n * processed so re-runs skip them.\n *\n * Even without the state file the operation is safe — extractAndStoreTriples\n * uses supersession logic so re-extracting a note never produces duplicates.\n */\n\nimport { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type { Pool } from \"pg\";\n\nimport { openRegistry } from \"../registry/db.js\";\nimport { loadConfig } from \"../daemon/config.js\";\nimport { createStorageBackend } from \"../storage/factory.js\";\nimport { extractAndStoreTriples } from \"./kg-extraction.js\";\nimport { openFederation } from \"./db.js\";\n\n// ---------------------------------------------------------------------------\n// State file\n// ---------------------------------------------------------------------------\n\nconst STATE_FILE = join(homedir(), \".config\", \"pai\", \"kg-backfill-state.json\");\n\ninterface BackfillState {\n /** Map of absolute note path -> ISO timestamp of when it was processed. */\n processed: Record<string, string>;\n}\n\nfunction loadState(): BackfillState {\n try {\n if (existsSync(STATE_FILE)) {\n const raw = JSON.parse(readFileSync(STATE_FILE, \"utf-8\"));\n if (raw && typeof raw === \"object\" && raw.processed) return raw as BackfillState;\n }\n } catch { /* ignore */ }\n return { processed: {} };\n}\n\nfunction saveState(state: BackfillState): void {\n try {\n const dir = join(homedir(), \".config\", \"pai\");\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n writeFileSync(STATE_FILE, JSON.stringify(state, null, 2), \"utf-8\");\n } catch (e) {\n process.stderr.write(`[kg-backfill] failed to save state: ${e}\\n`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Note discovery\n// ---------------------------------------------------------------------------\n\ninterface ProjectRow {\n id: number;\n slug: string;\n root_path: string;\n}\n\ninterface ProjectNote {\n project: ProjectRow;\n notePath: string;\n}\n\n/**\n * Find all session notes under <project_root>/Notes/YYYY/MM/*.md.\n * Falls back to <project_root>/Notes/*.md (flat layout) when no year/month\n * subdirectories are present.\n */\nfunction findProjectNotes(project: ProjectRow): string[] {\n const notesRoot = join(project.root_path, \"Notes\");\n if (!existsSync(notesRoot)) return [];\n\n const found: Array<{ path: string; mtime: number }> = [];\n\n function walk(dir: string, depth: number): void {\n if (depth > 4) return;\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const name of entries) {\n const full = join(dir, name);\n let st;\n try {\n st = statSync(full);\n } catch {\n continue;\n }\n if (st.isDirectory()) {\n walk(full, depth + 1);\n } else if (st.isFile() && name.endsWith(\".md\") && /^\\d{3,4}/.test(name)) {\n found.push({ path: full, mtime: st.mtimeMs });\n }\n }\n }\n\n walk(notesRoot, 0);\n // Process oldest first so newer extractions can correctly supersede older facts\n found.sort((a, b) => a.mtime - b.mtime);\n return found.map((f) => f.path);\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport interface BackfillOptions {\n /** If set, only process this project's notes. */\n projectSlug?: string;\n /** Cap the total number of notes processed across all projects. */\n limit?: number;\n /** If true, do not call the extractor and do not write any state. */\n dryRun?: boolean;\n /** Progress callback fired before each note is processed. */\n onProgress?: (current: number, total: number, note: string) => void;\n}\n\nexport interface BackfillResult {\n notes_processed: number;\n triples_extracted: number;\n triples_added: number;\n triples_superseded: number;\n errors: number;\n}\n\n/**\n * Backfill the knowledge graph from existing session notes.\n *\n * Requires the Postgres backend (KG tables live there). Throws if Postgres\n * is unavailable, since this is an explicit user action.\n */\nexport async function backfillKgFromNotes(\n options: BackfillOptions = {}\n): Promise<BackfillResult> {\n const result: BackfillResult = {\n notes_processed: 0,\n triples_extracted: 0,\n triples_added: 0,\n triples_superseded: 0,\n errors: 0,\n };\n\n const config = loadConfig();\n if (config.storageBackend !== \"postgres\") {\n throw new Error(\n \"kg backfill requires the Postgres backend. \" +\n 'Set \"storageBackend\": \"postgres\" in ~/.config/pai/config.json.'\n );\n }\n\n const backend = await createStorageBackend(config);\n if (backend.backendType !== \"postgres\") {\n throw new Error(\n \"Postgres backend unavailable — fell back to SQLite. Cannot backfill KG.\"\n );\n }\n\n const pool: Pool = (backend as unknown as { getPool(): Pool }).getPool();\n\n // Open federation SQLite for entity upserts (kg_entities table)\n const federationDb = openFederation();\n\n // -- Load projects -------------------------------------------------------\n const registry = openRegistry();\n let projects: ProjectRow[];\n try {\n if (options.projectSlug) {\n const row = registry\n .prepare(\"SELECT id, slug, root_path FROM projects WHERE slug = ?\")\n .get(options.projectSlug) as ProjectRow | undefined;\n if (!row) {\n throw new Error(`Project not found: ${options.projectSlug}`);\n }\n projects = [row];\n } else {\n projects = registry\n .prepare(\n \"SELECT id, slug, root_path FROM projects WHERE status = 'active' ORDER BY slug\"\n )\n .all() as ProjectRow[];\n }\n } finally {\n registry.close();\n }\n\n // -- Build the work list -------------------------------------------------\n const state = loadState();\n const work: ProjectNote[] = [];\n for (const project of projects) {\n const notes = findProjectNotes(project);\n for (const notePath of notes) {\n if (state.processed[notePath]) continue;\n work.push({ project, notePath });\n }\n }\n\n const total = options.limit ? Math.min(options.limit, work.length) : work.length;\n\n // -- Process notes -------------------------------------------------------\n for (let i = 0; i < total; i++) {\n const { project, notePath } = work[i];\n options.onProgress?.(i + 1, total, notePath);\n\n if (options.dryRun) {\n result.notes_processed++;\n continue;\n }\n\n let noteContent: string;\n try {\n noteContent = readFileSync(notePath, \"utf-8\");\n } catch (e) {\n process.stderr.write(`[kg-backfill] read failed ${notePath}: ${e}\\n`);\n result.errors++;\n continue;\n }\n\n try {\n const stats = await extractAndStoreTriples(pool, {\n summaryText: noteContent,\n projectSlug: project.slug,\n projectId: project.id,\n sessionId: `backfill:${notePath}`,\n gitLog: \"\",\n model: \"sonnet\",\n federationDb,\n });\n\n result.notes_processed++;\n result.triples_extracted += stats.extracted;\n result.triples_added += stats.added;\n result.triples_superseded += stats.superseded;\n\n // Mark as processed (state file is the idempotency hint; the supersession\n // logic in the extractor is the actual safety net)\n state.processed[notePath] = new Date().toISOString();\n // Save incrementally so a crash mid-run doesn't lose progress\n if ((i + 1) % 5 === 0) saveState(state);\n } catch (e) {\n process.stderr.write(`[kg-backfill] extract failed ${notePath}: ${e}\\n`);\n result.errors++;\n }\n }\n\n if (!options.dryRun) saveState(state);\n federationDb.close();\n await backend.close();\n\n return result;\n}\n","/**\n * pai kg <sub-command>\n *\n * Temporal knowledge graph management:\n * pai kg backfill [--project SLUG] [--limit N] [--dry-run]\n * pai kg query [--subject S] [--predicate P] [--object O] [--as-of DATE] [--project SLUG]\n * pai kg list [--project SLUG] [--limit N]\n * pai kg stats\n *\n * All commands require the Postgres backend (KG tables live in Postgres).\n */\n\nimport type { Command } from \"commander\";\nimport type { Pool } from \"pg\";\n\nimport { ok, warn, err, dim, bold, header } from \"../utils.js\";\nimport { loadConfig } from \"../../daemon/config.js\";\nimport { createStorageBackend } from \"../../storage/factory.js\";\nimport { kgQuery } from \"../../memory/kg.js\";\nimport { backfillKgFromNotes } from \"../../memory/kg-backfill.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nasync function getPool(): Promise<{ pool: Pool; close: () => Promise<void> }> {\n const config = loadConfig();\n if (config.storageBackend !== \"postgres\") {\n console.error(err(\" KG commands require Postgres backend.\"));\n console.error(dim(' Set \"storageBackend\": \"postgres\" in ~/.config/pai/config.json'));\n process.exit(1);\n }\n const backend = await createStorageBackend(config);\n if (backend.backendType !== \"postgres\") {\n console.error(err(\" Postgres backend unavailable — fell back to SQLite.\"));\n process.exit(1);\n }\n const pool = (backend as unknown as { getPool(): Pool }).getPool();\n return { pool, close: () => backend.close() };\n}\n\nfunction shorten(s: string, n: number): string {\n return s.length <= n ? s : s.slice(0, n - 1) + \"…\";\n}\n\n// ---------------------------------------------------------------------------\n// pai kg backfill\n// ---------------------------------------------------------------------------\n\nasync function cmdBackfill(opts: {\n project?: string;\n limit?: string;\n dryRun?: boolean;\n}): Promise<void> {\n const limit = opts.limit ? parseInt(opts.limit, 10) : undefined;\n if (limit !== undefined && (isNaN(limit) || limit < 1)) {\n console.error(err(\" --limit must be a positive integer\"));\n process.exit(1);\n }\n\n console.log();\n console.log(header(\" PAI KG Backfill\"));\n console.log();\n if (opts.project) console.log(` ${bold(\"Project:\")} ${opts.project}`);\n if (limit) console.log(` ${bold(\"Limit:\")} ${limit}`);\n if (opts.dryRun) console.log(` ${bold(\"Mode:\")} ${warn(\"dry-run\")}`);\n console.log();\n\n try {\n const result = await backfillKgFromNotes({\n projectSlug: opts.project,\n limit,\n dryRun: opts.dryRun,\n onProgress: (current, total, note) => {\n const short = shorten(note.replace(process.env.HOME ?? \"\", \"~\"), 70);\n process.stdout.write(` [${current}/${total}] ${dim(short)}\\n`);\n },\n });\n\n console.log();\n console.log(ok(\" Backfill complete\"));\n console.log(` Notes processed: ${bold(String(result.notes_processed))}`);\n console.log(` Triples extracted: ${bold(String(result.triples_extracted))}`);\n console.log(` Triples added: ${bold(String(result.triples_added))}`);\n console.log(` Triples superseded: ${bold(String(result.triples_superseded))}`);\n if (result.errors > 0) {\n console.log(` ${warn(\"Errors:\")} ${result.errors}`);\n }\n console.log();\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(err(` ${msg}`));\n process.exit(1);\n }\n}\n\n// ---------------------------------------------------------------------------\n// pai kg query\n// ---------------------------------------------------------------------------\n\nasync function cmdQuery(opts: {\n subject?: string;\n predicate?: string;\n object?: string;\n asOf?: string;\n project?: string;\n json?: boolean;\n}): Promise<void> {\n const { pool, close } = await getPool();\n try {\n let projectId: number | undefined;\n if (opts.project) {\n const r = await pool.query<{ id: number }>(\n \"SELECT id FROM projects WHERE slug = $1 LIMIT 1\",\n [opts.project]\n );\n if (r.rows.length === 0) {\n console.error(warn(` Project not found in Postgres: ${opts.project}`));\n } else {\n projectId = r.rows[0].id;\n }\n }\n\n const asOf = opts.asOf ? new Date(opts.asOf) : undefined;\n if (asOf && isNaN(asOf.getTime())) {\n console.error(err(` Invalid --as-of date: ${opts.asOf}`));\n process.exit(1);\n }\n\n const triples = await kgQuery(pool, {\n subject: opts.subject,\n predicate: opts.predicate,\n object: opts.object,\n project_id: projectId,\n as_of: asOf,\n });\n\n if (opts.json) {\n console.log(JSON.stringify(triples, null, 2));\n return;\n }\n\n console.log();\n console.log(header(` ${triples.length} triple(s)`));\n console.log();\n for (const t of triples) {\n const validity = t.valid_to\n ? dim(`(invalidated ${t.valid_to.toISOString().slice(0, 10)})`)\n : dim(`(valid since ${t.valid_from.toISOString().slice(0, 10)})`);\n console.log(\n ` ${bold(t.subject)} ${dim(\"·\")} ${t.predicate} ${dim(\"·\")} ${t.object} ${validity}`\n );\n }\n console.log();\n } finally {\n await close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// pai kg list\n// ---------------------------------------------------------------------------\n\nasync function cmdList(opts: { project?: string; limit?: string }): Promise<void> {\n const limit = opts.limit ? parseInt(opts.limit, 10) : 50;\n const { pool, close } = await getPool();\n try {\n let projectId: number | undefined;\n if (opts.project) {\n const r = await pool.query<{ id: number }>(\n \"SELECT id FROM projects WHERE slug = $1 LIMIT 1\",\n [opts.project]\n );\n if (r.rows.length > 0) projectId = r.rows[0].id;\n }\n\n const triples = await kgQuery(pool, { project_id: projectId });\n const slice = triples.slice(0, limit);\n\n console.log();\n console.log(\n header(` ${slice.length} of ${triples.length} currently-valid triple(s)`)\n );\n console.log();\n for (const t of slice) {\n console.log(\n ` ${bold(t.subject)} ${dim(\"·\")} ${t.predicate} ${dim(\"·\")} ${t.object}`\n );\n }\n if (triples.length > slice.length) {\n console.log();\n console.log(dim(` (${triples.length - slice.length} more — increase --limit)`));\n }\n console.log();\n } finally {\n await close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// pai kg stats\n// ---------------------------------------------------------------------------\n\nasync function cmdStats(): Promise<void> {\n const { pool, close } = await getPool();\n try {\n const totals = await pool.query<{\n total: string;\n valid: string;\n invalidated: string;\n subjects: string;\n predicates: string;\n }>(\n `SELECT\n COUNT(*)::text AS total,\n COUNT(*) FILTER (WHERE valid_to IS NULL)::text AS valid,\n COUNT(*) FILTER (WHERE valid_to IS NOT NULL)::text AS invalidated,\n COUNT(DISTINCT subject)::text AS subjects,\n COUNT(DISTINCT predicate)::text AS predicates\n FROM kg_triples`\n );\n\n const contradictions = await pool.query<{ count: string }>(\n `SELECT COUNT(*)::text AS count FROM (\n SELECT subject, predicate\n FROM kg_triples\n WHERE valid_to IS NULL\n GROUP BY subject, predicate\n HAVING COUNT(*) > 1\n ) c`\n );\n\n const row = totals.rows[0] ?? {};\n console.log();\n console.log(header(\" PAI KG Stats\"));\n console.log();\n console.log(` ${bold(\"Total triples:\")} ${row.total ?? \"0\"}`);\n console.log(` ${bold(\"Currently valid:\")} ${row.valid ?? \"0\"}`);\n console.log(` ${bold(\"Invalidated:\")} ${row.invalidated ?? \"0\"}`);\n console.log(` ${bold(\"Distinct subjects:\")} ${row.subjects ?? \"0\"}`);\n console.log(` ${bold(\"Distinct predicates:\")} ${row.predicates ?? \"0\"}`);\n console.log(` ${bold(\"Contradictions:\")} ${contradictions.rows[0]?.count ?? \"0\"}`);\n console.log();\n } finally {\n await close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerKgCommands(kgCmd: Command): void {\n kgCmd\n .command(\"backfill\")\n .description(\"Populate the KG from existing session notes (idempotent)\")\n .option(\"--project <slug>\", \"Restrict backfill to a single project\")\n .option(\"--limit <n>\", \"Maximum number of notes to process\")\n .option(\"--dry-run\", \"List notes that would be processed without extracting\")\n .action(async (opts: { project?: string; limit?: string; dryRun?: boolean }) => {\n await cmdBackfill(opts);\n });\n\n kgCmd\n .command(\"query\")\n .description(\"Query KG triples by subject, predicate, object, time, or project\")\n .option(\"--subject <s>\", \"Filter by subject\")\n .option(\"--predicate <p>\", \"Filter by predicate\")\n .option(\"--object <o>\", \"Filter by object\")\n .option(\"--as-of <date>\", \"Point-in-time query (YYYY-MM-DD or ISO 8601)\")\n .option(\"--project <slug>\", \"Restrict to a project slug\")\n .option(\"--json\", \"Output raw JSON\")\n .action(\n async (opts: {\n subject?: string;\n predicate?: string;\n object?: string;\n asOf?: string;\n project?: string;\n json?: boolean;\n }) => {\n await cmdQuery(opts);\n }\n );\n\n kgCmd\n .command(\"list\")\n .description(\"List currently-valid triples\")\n .option(\"--project <slug>\", \"Restrict to a project slug\")\n .option(\"--limit <n>\", \"Maximum triples to print\", \"50\")\n .action(async (opts: { project?: string; limit?: string }) => {\n await cmdList(opts);\n });\n\n kgCmd\n .command(\"stats\")\n .description(\"Show triple counts and contradiction count\")\n .action(async () => {\n await cmdStats();\n });\n}\n","/**\n * pai db <sub-command>\n *\n * Quick database inspection commands for both SQLite and Postgres backends.\n *\n * pai db query <sql> [--db sqlite|postgres] [--json]\n * pai db tables [--db sqlite|postgres]\n * pai db schema <table> [--db sqlite|postgres]\n */\n\nimport type { Command } from \"commander\";\nimport { ok, warn, err, dim, bold, header, renderTable } from \"../utils.js\";\nimport { loadConfig } from \"../../daemon/config.js\";\nimport { openFederation } from \"../../memory/db.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype DbTarget = \"sqlite\" | \"postgres\";\n\ninterface CommonOpts {\n db?: DbTarget;\n json?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// SQLite helpers (synchronous — better-sqlite3)\n// ---------------------------------------------------------------------------\n\nfunction getSqliteDb() {\n return openFederation();\n}\n\nfunction sqliteQuery(sql: string): { columns: string[]; rows: unknown[][] } {\n const db = getSqliteDb();\n try {\n const stmt = db.prepare(sql);\n const raw = stmt.all() as Record<string, unknown>[];\n if (raw.length === 0) return { columns: [], rows: [] };\n const columns = Object.keys(raw[0]);\n const rows = raw.map((r) => columns.map((c) => r[c]));\n return { columns, rows };\n } finally {\n db.close();\n }\n}\n\nfunction sqliteTables(): string[] {\n const db = getSqliteDb();\n try {\n const rows = db\n .prepare(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name\"\n )\n .all() as { name: string }[];\n return rows.map((r) => r.name);\n } finally {\n db.close();\n }\n}\n\nfunction sqliteSchema(table: string): { columns: string[]; rows: unknown[][] } {\n const db = getSqliteDb();\n try {\n // Validate table name — only allow alphanumeric / underscores\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(table)) {\n throw new Error(`Invalid table name: ${table}`);\n }\n const rows = db.prepare(`PRAGMA table_info(${table})`).all() as {\n cid: number;\n name: string;\n type: string;\n notnull: number;\n dflt_value: string | null;\n pk: number;\n }[];\n if (rows.length === 0) {\n throw new Error(`Table not found: ${table}`);\n }\n const columns = [\"cid\", \"name\", \"type\", \"notnull\", \"default\", \"pk\"];\n const data = rows.map((r) => [\n r.cid,\n r.name,\n r.type,\n r.notnull ? \"NOT NULL\" : \"\",\n r.dflt_value ?? \"\",\n r.pk ? \"PK\" : \"\",\n ]);\n return { columns, data: data } as unknown as { columns: string[]; rows: unknown[][] };\n } finally {\n db.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Postgres helpers (async — pg Pool)\n// ---------------------------------------------------------------------------\n\nasync function getPool() {\n const config = loadConfig();\n const pgConfig = config.postgres ?? {};\n\n const { Pool } = await import(\"pg\");\n\n // Build connection params from config\n const poolConfig = pgConfig.connectionString\n ? { connectionString: pgConfig.connectionString }\n : {\n host: pgConfig.host ?? \"localhost\",\n port: pgConfig.port ?? 5432,\n database: pgConfig.database ?? \"pai\",\n user: pgConfig.user ?? \"pai\",\n password: pgConfig.password ?? \"pai\",\n connectionTimeoutMillis: pgConfig.connectionTimeoutMs ?? 5000,\n };\n\n const pool = new Pool(poolConfig);\n\n // Quick connectivity test\n try {\n const client = await pool.connect();\n client.release();\n } catch (e) {\n await pool.end();\n const msg = e instanceof Error ? e.message : String(e);\n throw new Error(`Cannot connect to Postgres: ${msg}`);\n }\n\n return pool;\n}\n\nasync function postgresQuery(\n sql: string\n): Promise<{ columns: string[]; rows: unknown[][] }> {\n const pool = await getPool();\n try {\n const result = await pool.query(sql);\n const columns = result.fields.map((f) => f.name);\n const rows = result.rows.map((r: Record<string, unknown>) =>\n columns.map((c) => r[c])\n );\n return { columns, rows };\n } finally {\n await pool.end();\n }\n}\n\nasync function postgresTables(): Promise<string[]> {\n const pool = await getPool();\n try {\n const result = await pool.query<{ tablename: string }>(\n \"SELECT tablename FROM pg_tables WHERE schemaname = 'public' ORDER BY tablename\"\n );\n return result.rows.map((r) => r.tablename);\n } finally {\n await pool.end();\n }\n}\n\nasync function postgresSchema(\n table: string\n): Promise<{ columns: string[]; rows: unknown[][] }> {\n const pool = await getPool();\n try {\n const result = await pool.query<{\n column_name: string;\n data_type: string;\n is_nullable: string;\n column_default: string | null;\n }>(\n `SELECT column_name, data_type, is_nullable, column_default\n FROM information_schema.columns\n WHERE table_schema = 'public' AND table_name = $1\n ORDER BY ordinal_position`,\n [table]\n );\n if (result.rows.length === 0) {\n throw new Error(`Table not found in public schema: ${table}`);\n }\n const columns = [\"column\", \"type\", \"nullable\", \"default\"];\n const rows = result.rows.map((r) => [\n r.column_name,\n r.data_type,\n r.is_nullable === \"YES\" ? \"YES\" : \"NO\",\n r.column_default ?? \"\",\n ]);\n return { columns, rows };\n } finally {\n await pool.end();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Output helpers\n// ---------------------------------------------------------------------------\n\nfunction printResult(\n result: { columns: string[]; rows: unknown[][] },\n json: boolean\n): void {\n if (json) {\n const objects = result.rows.map((row) => {\n const obj: Record<string, unknown> = {};\n result.columns.forEach((col, i) => {\n obj[col] = row[i];\n });\n return obj;\n });\n console.log(JSON.stringify(objects, null, 2));\n return;\n }\n\n if (result.rows.length === 0) {\n console.log();\n console.log(dim(\" (no rows)\"));\n console.log();\n return;\n }\n\n const stringRows = result.rows.map((row) =>\n row.map((cell) => {\n if (cell === null || cell === undefined) return dim(\"NULL\");\n if (typeof cell === \"object\") return JSON.stringify(cell);\n return String(cell);\n })\n );\n\n console.log();\n console.log(renderTable(result.columns, stringRows));\n console.log();\n console.log(dim(` ${result.rows.length} row(s)`));\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// pai db query\n// ---------------------------------------------------------------------------\n\nasync function cmdQuery(\n sql: string,\n opts: CommonOpts\n): Promise<void> {\n const target: DbTarget = opts.db ?? \"sqlite\";\n console.log();\n console.log(\n header(` Query [${target}]`) + dim(` ${sql.slice(0, 80)}${sql.length > 80 ? \"…\" : \"\"}`)\n );\n\n try {\n let result: { columns: string[]; rows: unknown[][] };\n if (target === \"postgres\") {\n result = await postgresQuery(sql);\n } else {\n result = sqliteQuery(sql);\n }\n printResult(result, opts.json ?? false);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(err(` ${msg}`));\n process.exit(1);\n }\n}\n\n// ---------------------------------------------------------------------------\n// pai db tables\n// ---------------------------------------------------------------------------\n\nasync function cmdTables(opts: CommonOpts): Promise<void> {\n const target: DbTarget = opts.db ?? \"sqlite\";\n console.log();\n console.log(header(` Tables [${target}]`));\n console.log();\n\n try {\n let tables: string[];\n if (target === \"postgres\") {\n tables = await postgresTables();\n } else {\n tables = sqliteTables();\n }\n\n if (opts.json) {\n console.log(JSON.stringify(tables, null, 2));\n return;\n }\n\n if (tables.length === 0) {\n console.log(dim(\" (no tables found)\"));\n } else {\n for (const t of tables) {\n console.log(` ${bold(t)}`);\n }\n }\n console.log();\n console.log(dim(` ${tables.length} table(s)`));\n console.log();\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(err(` ${msg}`));\n process.exit(1);\n }\n}\n\n// ---------------------------------------------------------------------------\n// pai db schema\n// ---------------------------------------------------------------------------\n\nasync function cmdSchema(table: string, opts: CommonOpts): Promise<void> {\n const target: DbTarget = opts.db ?? \"sqlite\";\n console.log();\n console.log(header(` Schema: ${table} [${target}]`));\n\n try {\n let result: { columns: string[]; rows: unknown[][] };\n if (target === \"postgres\") {\n result = await postgresSchema(table);\n } else {\n const raw = sqliteSchema(table) as unknown as {\n columns: string[];\n data: unknown[][];\n };\n result = { columns: raw.columns, rows: raw.data };\n }\n printResult(result, opts.json ?? false);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(err(` ${msg}`));\n process.exit(1);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerDbCommands(dbCmd: Command): void {\n dbCmd\n .command(\"query <sql>\")\n .description(\"Run a SQL query against the selected database\")\n .option(\"--db <target>\", \"Database target: sqlite (default) or postgres\", \"sqlite\")\n .option(\"--json\", \"Output results as JSON array\")\n .action(async (sql: string, opts: CommonOpts) => {\n await cmdQuery(sql, opts);\n });\n\n dbCmd\n .command(\"tables\")\n .description(\"List all tables in the selected database\")\n .option(\"--db <target>\", \"Database target: sqlite (default) or postgres\", \"sqlite\")\n .option(\"--json\", \"Output as JSON array\")\n .action(async (opts: CommonOpts) => {\n await cmdTables(opts);\n });\n\n dbCmd\n .command(\"schema <table>\")\n .description(\"Show column schema for a table\")\n .option(\"--db <target>\", \"Database target: sqlite (default) or postgres\", \"sqlite\")\n .option(\"--json\", \"Output as JSON array\")\n .action(async (table: string, opts: CommonOpts) => {\n await cmdSchema(table, opts);\n });\n}\n","/**\n * pai find <query> [--n=20] [--json]\n *\n * Content-based search across ~/.claude/history.jsonl.\n * Finds user prompts matching the query (case-insensitive substring),\n * groups by sessionId, sorts by most-recent match, and shows a table\n * with short UUID, date, project path, and the matching prompt snippet.\n *\n * The sessionId from history.jsonl is the same UUID used by claude --resume,\n * so rows are directly resumable via: pai resume <id>\n *\n * Note: older history.jsonl entries (before Claude Code ~2.0) may lack a\n * sessionId field. These are grouped under a synthetic \"no-session\" bucket\n * and shown in a separate note at the bottom.\n */\n\nimport { existsSync, createReadStream } from \"node:fs\";\nimport { createInterface } from \"node:readline\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport { renderTable, header, dim, warn, err } from \"../utils.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface HistoryEntry {\n display?: string;\n timestamp?: number;\n project?: string;\n sessionId?: string;\n pastedContents?: unknown;\n}\n\ninterface SessionMatch {\n sessionId: string | null;\n /** Most-recent timestamp among matching lines */\n lastMatchTs: number;\n /** Most-recent matching prompt text */\n lastMatchDisplay: string;\n /** Project path (from the most-recent match) */\n project: string;\n /** Number of matching lines in this session */\n matchCount: number;\n}\n\n// ---------------------------------------------------------------------------\n// History file location\n// ---------------------------------------------------------------------------\n\nconst HISTORY_FILE = join(homedir(), \".claude\", \"history.jsonl\");\n\n// ---------------------------------------------------------------------------\n// Shorten project path for display\n// ---------------------------------------------------------------------------\n\nfunction shortenProject(p: string, maxLen = 42): string {\n if (p.length <= maxLen) return p;\n // Keep tail (the meaningful part) and prefix with …\n return \"…\" + p.slice(-(maxLen - 1));\n}\n\n// ---------------------------------------------------------------------------\n// Format timestamp\n// ---------------------------------------------------------------------------\n\nfunction fmtTs(ts: number): string {\n const d = new Date(ts);\n const pad = (n: number) => String(n).padStart(2, \"0\");\n return (\n `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ` +\n `${pad(d.getHours())}:${pad(d.getMinutes())}`\n );\n}\n\n// ---------------------------------------------------------------------------\n// Core search function (streaming to avoid loading 26k lines into memory)\n// ---------------------------------------------------------------------------\n\nexport async function searchHistory(\n query: string,\n maxResults: number\n): Promise<SessionMatch[]> {\n if (!existsSync(HISTORY_FILE)) return [];\n\n const queryLower = query.toLowerCase();\n // Escape regex special chars from query for safety — but we're doing\n // string .includes() not regex, so no escaping needed.\n\n const bySession = new Map<string, SessionMatch>();\n const noSession: SessionMatch = {\n sessionId: null,\n lastMatchTs: 0,\n lastMatchDisplay: \"\",\n project: \"\",\n matchCount: 0,\n };\n\n const rl = createInterface({\n input: createReadStream(HISTORY_FILE, { encoding: \"utf8\" }),\n crlfDelay: Infinity,\n });\n\n for await (const line of rl) {\n const t = line.trim();\n if (!t) continue;\n\n let entry: HistoryEntry;\n try {\n entry = JSON.parse(t) as HistoryEntry;\n } catch {\n continue;\n }\n\n const display = entry.display ?? \"\";\n if (!display.toLowerCase().includes(queryLower)) continue;\n\n const ts = entry.timestamp ?? 0;\n const project = entry.project ?? \"\";\n const sessionId = entry.sessionId ?? null;\n\n if (!sessionId) {\n // Older entry without sessionId\n if (ts > noSession.lastMatchTs) {\n noSession.lastMatchTs = ts;\n noSession.lastMatchDisplay = display;\n noSession.project = project;\n }\n noSession.matchCount++;\n continue;\n }\n\n const existing = bySession.get(sessionId);\n if (!existing) {\n bySession.set(sessionId, {\n sessionId,\n lastMatchTs: ts,\n lastMatchDisplay: display,\n project,\n matchCount: 1,\n });\n } else {\n existing.matchCount++;\n if (ts > existing.lastMatchTs) {\n existing.lastMatchTs = ts;\n existing.lastMatchDisplay = display;\n existing.project = project;\n }\n }\n }\n\n // Sort by most-recent match descending\n const sorted = [...bySession.values()].sort(\n (a, b) => b.lastMatchTs - a.lastMatchTs\n );\n\n return sorted.slice(0, maxResults);\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport async function cmdFind(\n query: string,\n opts: { n?: string; json?: boolean }\n): Promise<void> {\n const maxResults = parseInt(opts.n ?? \"20\", 10);\n\n if (!existsSync(HISTORY_FILE)) {\n console.error(err(`~/.claude/history.jsonl not found.`));\n process.exitCode = 1;\n return;\n }\n\n const matches = await searchHistory(query, maxResults);\n\n if (opts.json) {\n console.log(JSON.stringify(matches, null, 2));\n return;\n }\n\n if (matches.length === 0) {\n console.log(warn(`No sessions found matching \"${query}\" in ~/.claude/history.jsonl.`));\n console.log(dim(` Try a shorter or different search term.`));\n return;\n }\n\n console.log(\"\\n\" + header(\"Session Search Results\") + \"\\n\");\n console.log(dim(` Query: \"${query}\" (${matches.length} session(s) with matching prompts)\\n`));\n\n const headers = [\"#\", \"id\", \"when\", \"project\", \"last matching prompt\"];\n const rows = matches.map((m, idx) => {\n const shortId = (m.sessionId ?? \"no-session\").slice(0, 8);\n const when = m.lastMatchTs > 0 ? fmtTs(m.lastMatchTs) : dim(\"—\");\n const project = shortenProject(m.project || dim(\"—\"));\n // Truncate the prompt snippet — strip newlines, trim\n const snippet = m.lastMatchDisplay\n .replace(/\\n+/g, \" \")\n .trim()\n .slice(0, 48);\n const display = snippet.length < m.lastMatchDisplay.replace(/\\n+/g, \" \").trim().length\n ? `\"${snippet}…\"`\n : `\"${snippet}\"`;\n\n return [\n dim(String(idx + 1)),\n chalk.cyan(shortId),\n when,\n dim(project),\n chalk.dim(display),\n ];\n });\n\n console.log(renderTable(headers, rows));\n console.log();\n console.log(dim(\" Resume: \") + chalk.white(\"pai resume <id>\"));\n console.log();\n}\n","/**\n * history-search.ts\n *\n * Content-based search across ~/.claude/history.jsonl.\n * Streams the file (avoids loading 26k+ lines into memory), groups matching\n * lines by sessionId, and returns results sorted by most-recent match.\n *\n * Used by: pai <query> (main resolver) and pai find (compat alias)\n */\n\nimport { existsSync, createReadStream } from \"node:fs\";\nimport { createInterface } from \"node:readline\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface HistoryEntry {\n display?: string;\n timestamp?: number;\n project?: string;\n sessionId?: string;\n pastedContents?: unknown;\n}\n\nexport interface SessionMatch {\n sessionId: string | null;\n /** Most-recent timestamp among matching lines */\n lastMatchTs: number;\n /** Most-recent matching prompt text */\n lastMatchDisplay: string;\n /** Project path (from the most-recent match) */\n project: string;\n /** Number of matching lines in this session */\n matchCount: number;\n}\n\n// ---------------------------------------------------------------------------\n// History file location\n// ---------------------------------------------------------------------------\n\nexport const HISTORY_FILE = join(homedir(), \".claude\", \"history.jsonl\");\n\n// ---------------------------------------------------------------------------\n// Core search function (streaming)\n// ---------------------------------------------------------------------------\n\n/**\n * Search ~/.claude/history.jsonl for prompts matching the query (case-insensitive\n * substring). Results are grouped by sessionId and sorted by most-recent match.\n *\n * Entries without a sessionId (old Claude Code versions) are excluded from\n * results since they can't be resumed.\n */\nexport async function searchHistory(\n query: string,\n maxResults: number\n): Promise<SessionMatch[]> {\n if (!existsSync(HISTORY_FILE)) return [];\n\n const queryLower = query.toLowerCase();\n const bySession = new Map<string, SessionMatch>();\n\n const rl = createInterface({\n input: createReadStream(HISTORY_FILE, { encoding: \"utf8\" }),\n crlfDelay: Infinity,\n });\n\n for await (const line of rl) {\n const t = line.trim();\n if (!t) continue;\n\n let entry: HistoryEntry;\n try {\n entry = JSON.parse(t) as HistoryEntry;\n } catch {\n continue;\n }\n\n const display = entry.display ?? \"\";\n if (!display.toLowerCase().includes(queryLower)) continue;\n\n const ts = entry.timestamp ?? 0;\n const project = entry.project ?? \"\";\n const sessionId = entry.sessionId ?? null;\n\n // Skip entries with no sessionId — can't resume them\n if (!sessionId) continue;\n\n const existing = bySession.get(sessionId);\n if (!existing) {\n bySession.set(sessionId, {\n sessionId,\n lastMatchTs: ts,\n lastMatchDisplay: display,\n project,\n matchCount: 1,\n });\n } else {\n existing.matchCount++;\n if (ts > existing.lastMatchTs) {\n existing.lastMatchTs = ts;\n existing.lastMatchDisplay = display;\n existing.project = project;\n }\n }\n }\n\n // Sort by most-recent match descending\n const sorted = [...bySession.values()].sort(\n (a, b) => b.lastMatchTs - a.lastMatchTs\n );\n\n return sorted.slice(0, maxResults);\n}\n","/**\n * main-resolver.ts\n *\n * `pai [<query>]` — topic-first session discovery and launcher (v0.10.0)\n *\n * Decision tree:\n * 1. No arg → show interactive picker of recent sessions (top 20 by mtime)\n * 2. UUID prefix → universal filesystem scan; auto-launch the match\n * 3. Known name → catalog name match; auto-launch immediately\n * 4. Free-text query → search history.jsonl; show candidates with excerpt; user picks\n *\n * The \"-y\" / \"--auto\" flag skips the interactive prompt and picks candidate #1.\n * \"pai <query> <N>\" also picks candidate #N directly.\n *\n * Launch logic (shared with goto.ts):\n * - Find best resumable UUID for the matched project dir\n * - Probe claude --resume to verify it's still valid\n * - If valid: claude --resume <uuid> --name <name> \"/Name <name>\\ngo\"\n * - If invalid: claude --name <name> \"/Name <name>\\ngo\" (fresh in same dir)\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport { createInterface } from \"node:readline\";\nimport { existsSync } from \"node:fs\";\nimport { realpathSync } from \"node:fs\";\nimport { spawnSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport { err, dim, warn, ok, header, bold, renderTable, fmtDate, shortenPath } from \"../utils.js\";\nimport {\n scanSessions,\n resolveSessionByNameOrId,\n fmtAge,\n type ScannedSession,\n} from \"../lib/session-scan.js\";\nimport { searchHistory, HISTORY_FILE, type SessionMatch } from \"../lib/history-search.js\";\nimport { fetchLiveSessions } from \"../lib/aibroker-client.js\";\n\n// ---------------------------------------------------------------------------\n// Probe helper (same as goto.ts)\n// ---------------------------------------------------------------------------\n\ninterface ProbeResult {\n ok: boolean;\n reason?: string;\n}\n\nfunction probeResume(uuid: string, cwd: string): ProbeResult {\n const result = spawnSync(\n \"claude\",\n [\"--resume\", uuid, \"--print\", \"--output-format=json\", \"_\"],\n {\n cwd,\n timeout: 5_000,\n env: process.env,\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\n }\n );\n\n if (result.error) {\n return { ok: false, reason: `spawn error: ${result.error.message}` };\n }\n\n const stderr = result.stderr?.toString(\"utf8\") ?? \"\";\n\n if (\n stderr.toLowerCase().includes(\"no conversation found\") ||\n stderr.toLowerCase().includes(\"session not found\")\n ) {\n return { ok: false, reason: \"No conversation found for this UUID\" };\n }\n\n if (result.status !== 0) {\n return {\n ok: false,\n reason: `claude exited ${result.status ?? \"signal\"}${stderr ? `: ${stderr.slice(0, 120).trim()}` : \"\"}`,\n };\n }\n\n return { ok: true };\n}\n\n// ---------------------------------------------------------------------------\n// Launch session\n// ---------------------------------------------------------------------------\n\n/**\n * Launch Claude for the given session. Handles resume probe + fallback to fresh.\n * Never returns on success (process.exit inside spawnSync block).\n */\nfunction launchSession(\n session: ScannedSession,\n allSessions: ScannedSession[],\n dryRun: boolean\n): void {\n // Find the best resumable UUID in this project dir\n let resumableUuid: string | undefined;\n let resumableSession: ScannedSession | undefined;\n\n if (session.resumable) {\n resumableUuid = session.uuid;\n resumableSession = session;\n } else if (session.encodedDir) {\n const sameProject = allSessions.filter(\n (s) => s.encodedDir === session.encodedDir && s.resumable\n );\n sameProject.sort((a, b) => b.mtime - a.mtime);\n if (sameProject.length > 0) {\n resumableSession = sameProject[0];\n resumableUuid = resumableSession.uuid;\n }\n }\n\n // Determine project dir\n const rawDir =\n session.clcDirectory ??\n session.registryRootPath ??\n session.decodedPath;\n\n let projectDir: string;\n try {\n projectDir = realpathSync(rawDir);\n } catch {\n console.error(\n err(\n `Session directory does not exist or cannot be resolved.\\n` +\n ` Path: ${rawDir}\\n` +\n ` The directory may have moved or been deleted.`\n )\n );\n process.exit(1);\n return;\n }\n\n const name = session.friendlyName ?? session.shortId;\n const promptArg = `/Name ${name}\\ngo`;\n\n if (dryRun) {\n if (resumableUuid) {\n console.log(\"\\n\" + chalk.bold(\"Dry run — would probe then exec (RESUME path):\") + \"\\n\");\n console.log(` cwd: ${chalk.cyan(projectDir)}`);\n console.log(` probe: claude --resume ${resumableUuid} --print --output-format=json \"_\"`);\n console.log(` argv: claude --resume ${resumableUuid} --name \"${name}\" \"/Name ${name}\\\\ngo\"`);\n console.log(` fallback: claude --name \"${name}\" \"/Name ${name}\\\\ngo\"`);\n } else {\n console.log(\"\\n\" + chalk.bold(\"Dry run — would exec (FRESH path):\") + \"\\n\");\n console.log(` cwd: ${chalk.cyan(projectDir)}`);\n console.log(` argv: claude --name \"${name}\" \"/Name ${name}\\\\ngo\"`);\n }\n console.log();\n return;\n }\n\n if (resumableUuid) {\n const probe = probeResume(resumableUuid, projectDir);\n if (probe.ok) {\n const result = spawnSync(\n \"claude\",\n [\"--resume\", resumableUuid, \"--name\", name, promptArg],\n { cwd: projectDir, stdio: \"inherit\", env: process.env }\n );\n if (result.error) {\n console.error(err(`Failed to launch claude: ${result.error.message}`));\n process.exit(1);\n }\n process.exit(result.status ?? 0);\n } else {\n process.stderr.write(\n chalk.yellow(\n `\\n Resume failed for ${resumableUuid.slice(0, 8)}: ${probe.reason ?? \"unknown error\"}\\n` +\n ` Starting fresh session in same directory.\\n\\n`\n )\n );\n const result = spawnSync(\n \"claude\",\n [\"--name\", name, promptArg],\n { cwd: projectDir, stdio: \"inherit\", env: process.env }\n );\n if (result.error) {\n console.error(err(`Failed to launch claude: ${result.error.message}`));\n process.exit(1);\n }\n process.exit(result.status ?? 0);\n }\n } else {\n const result = spawnSync(\n \"claude\",\n [\"--name\", name, promptArg],\n { cwd: projectDir, stdio: \"inherit\", env: process.env }\n );\n if (result.error) {\n console.error(err(`Failed to launch claude: ${result.error.message}`));\n process.exit(1);\n }\n process.exit(result.status ?? 0);\n }\n}\n\n// ---------------------------------------------------------------------------\n// History match → ScannedSession bridge\n// ---------------------------------------------------------------------------\n\n/**\n * Given a SessionMatch from history search, find the corresponding ScannedSession\n * (for launch). Falls back to a minimal synthetic session using the decodedPath\n * from the history entry's project field.\n */\nfunction matchToSession(\n match: SessionMatch,\n allSessions: ScannedSession[]\n): ScannedSession | null {\n if (!match.sessionId) return null;\n\n // Try catalog first (for friendlyName, registryRootPath, resumable status)\n const catalogMatch = allSessions.find((s) => s.uuid === match.sessionId);\n if (catalogMatch) return catalogMatch;\n\n // Not in catalog — synthesize a minimal session using the project path from history\n // The sessionId is UUID format, use it to find the top-level jsonl directly\n if (!match.project) return null;\n\n return {\n uuid: match.sessionId,\n shortId: match.sessionId.slice(0, 8),\n encodedDir: \"\",\n decodedPath: match.project,\n topLevelPath: \"\",\n topLevelSystemLines: 0,\n topLevelSize: 0,\n resumable: false,\n sessionStatus: \"transcript-only\",\n sessionJsonlPath: undefined,\n userLines: 0,\n lastUserPrompt: match.lastMatchDisplay.slice(0, 80),\n msgCount: 0,\n mtime: match.lastMatchTs,\n friendlyName: undefined,\n clcDirectory: undefined,\n registryRootPath: match.project,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Formatters\n// ---------------------------------------------------------------------------\n\nfunction fmtTs(ts: number): string {\n const d = new Date(ts);\n const pad = (n: number) => String(n).padStart(2, \"0\");\n return (\n `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ` +\n `${pad(d.getHours())}:${pad(d.getMinutes())}`\n );\n}\n\nfunction shortenProject(p: string, maxLen = 44): string {\n if (!p || p.length <= maxLen) return p || dim(\"—\");\n return \"…\" + p.slice(-(maxLen - 1));\n}\n\n// ---------------------------------------------------------------------------\n// Interactive picker prompt\n// ---------------------------------------------------------------------------\n\nasync function askForChoice(max: number): Promise<number | null> {\n return new Promise((resolve) => {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n rl.question(\n dim(`\\n Enter # to launch (1-${max}), or press Enter to cancel: `),\n (answer) => {\n rl.close();\n const n = parseInt(answer.trim(), 10);\n if (!isNaN(n) && n >= 1 && n <= max) {\n resolve(n);\n } else {\n resolve(null);\n }\n }\n );\n });\n}\n\n// ---------------------------------------------------------------------------\n// Main command\n// ---------------------------------------------------------------------------\n\nexport interface MainResolverOpts {\n auto?: boolean; // -y / --auto: pick #1 without prompting\n dryRun?: boolean; // --dry-run: show what would happen\n n?: string; // --n <count>: max candidates for history search\n}\n\nexport async function cmdMain(\n db: Database,\n query: string | undefined,\n pickN: number | undefined,\n opts: MainResolverOpts\n): Promise<void> {\n const maxResults = parseInt(opts.n ?? \"20\", 10);\n const allSessions = scanSessions(db, { limit: 500, filter: \"named\" });\n\n // -----------------------------------------------------------------------\n // Case 1: No query → recent session picker\n // -----------------------------------------------------------------------\n if (!query) {\n // Show live sessions from AIBroker + most recent disk sessions\n let liveSessions: Awaited<ReturnType<typeof fetchLiveSessions>> = [];\n try {\n liveSessions = await fetchLiveSessions();\n } catch {\n // AIBroker not running — show only disk sessions\n }\n\n const claudeLive = liveSessions.filter((s) => s.kind !== \"shell\");\n const recentDisk = allSessions.slice(0, maxResults);\n\n if (claudeLive.length === 0 && recentDisk.length === 0) {\n console.log(warn(\"No sessions found. Start Claude Code in a project directory first.\"));\n return;\n }\n\n // Live sessions section\n if (claudeLive.length > 0) {\n console.log(\"\\n\" + header(\"Live Sessions\") + \"\\n\");\n const liveHeaders = [\"#\", \"id\", \"name\", \"at prompt\"];\n const liveRows = claudeLive.map((s, i) => [\n dim(String(i + 1)),\n chalk.cyan(s.sessionId.slice(0, 8)),\n s.paiName ?? s.name ?? dim(\"—\"),\n s.atPrompt ? chalk.green(\"yes\") : chalk.dim(\"no\"),\n ]);\n console.log(renderTable(liveHeaders, liveRows));\n }\n\n // Recent disk sessions section\n if (recentDisk.length > 0) {\n console.log(\"\\n\" + header(\"Recent Sessions\") + \"\\n\");\n const diskHeaders = [\"#\", \"id\", \"age\", \"project\", \"last prompt\"];\n const diskRows = recentDisk.map((s, i) => {\n const snippet = s.lastUserPrompt.replace(/\\n+/g, \" \").trim().slice(0, 40);\n return [\n dim(String(i + 1)),\n chalk.cyan(s.shortId),\n dim(fmtAge(s.mtime)),\n dim(shortenProject(s.friendlyName ?? s.decodedPath, 30)),\n chalk.dim(snippet ? `\"${snippet}\"` : \"—\"),\n ];\n });\n console.log(renderTable(diskHeaders, diskRows));\n }\n\n console.log();\n console.log(dim(\" Resume a session: \") + chalk.white(\"pai <topic>\") + dim(\" or \") + chalk.white(\"pai <id>\"));\n console.log();\n return;\n }\n\n // -----------------------------------------------------------------------\n // Case 2: UUID prefix (8+ hex chars) → universal filesystem scan\n // -----------------------------------------------------------------------\n const UUID_PREFIX_RE = /^[0-9a-f-]{8,36}$/i;\n if (UUID_PREFIX_RE.test(query)) {\n // Try catalog first\n const byUuid = allSessions.filter((s) => s.uuid.startsWith(query.toLowerCase()));\n if (byUuid.length === 1) {\n launchSession(byUuid[0], allSessions, opts.dryRun ?? false);\n return;\n }\n if (byUuid.length > 1) {\n console.error(err(`UUID prefix \"${query}\" is ambiguous — ${byUuid.length} catalog matches.`));\n process.exitCode = 1;\n return;\n }\n // Fall through to name match / history search — UUID might be a partial word too\n }\n\n // -----------------------------------------------------------------------\n // Case 3: Known session name → auto-launch\n // -----------------------------------------------------------------------\n {\n const qLower = query.toLowerCase();\n const byExact = allSessions.filter(\n (s) => s.friendlyName && s.friendlyName.toLowerCase() === qLower\n );\n if (byExact.length >= 1) {\n launchSession(byExact[0], allSessions, opts.dryRun ?? false);\n return;\n }\n\n const byPartial = allSessions.filter(\n (s) => s.friendlyName && s.friendlyName.toLowerCase().includes(qLower)\n );\n if (byPartial.length === 1) {\n launchSession(byPartial[0], allSessions, opts.dryRun ?? false);\n return;\n }\n if (byPartial.length > 1) {\n // Multiple named sessions match — show as candidates\n console.log(\"\\n\" + header(`Sessions matching \"${query}\"`) + \"\\n\");\n const headers = [\"#\", \"id\", \"age\", \"name\", \"project\"];\n const rows = byPartial.slice(0, maxResults).map((s, i) => [\n dim(String(i + 1)),\n chalk.cyan(s.shortId),\n dim(fmtAge(s.mtime)),\n s.friendlyName ?? dim(\"—\"),\n dim(shortenProject(s.decodedPath, 36)),\n ]);\n console.log(renderTable(headers, rows));\n console.log();\n\n if (pickN !== undefined) {\n const idx = pickN - 1;\n if (idx >= 0 && idx < byPartial.length) {\n launchSession(byPartial[idx], allSessions, opts.dryRun ?? false);\n return;\n }\n console.error(err(`Invalid choice: ${pickN}`));\n process.exitCode = 1;\n return;\n }\n\n if (opts.auto) {\n launchSession(byPartial[0], allSessions, opts.dryRun ?? false);\n return;\n }\n\n const choice = await askForChoice(Math.min(byPartial.length, maxResults));\n if (choice !== null) {\n launchSession(byPartial[choice - 1], allSessions, opts.dryRun ?? false);\n }\n return;\n }\n }\n\n // -----------------------------------------------------------------------\n // Case 4: Free-text history search\n // -----------------------------------------------------------------------\n if (!existsSync(HISTORY_FILE)) {\n console.error(err(\"~/.claude/history.jsonl not found.\"));\n console.error(dim(\" No prompt history available for search.\"));\n process.exitCode = 1;\n return;\n }\n\n process.stderr.write(dim(` Searching prompt history for \"${query}\"...\\n`));\n const matches = await searchHistory(query, maxResults);\n\n if (matches.length === 0) {\n console.log(warn(`No sessions found matching \"${query}\".`));\n console.log(dim(` Try a shorter or different search term.`));\n console.log(dim(` Or run: `) + chalk.white(\"pai\") + dim(\" (no args) to see all recent sessions.\"));\n return;\n }\n\n console.log(\"\\n\" + header(`Sessions matching \"${query}\"`) + \"\\n\");\n const headers = [\"#\", \"id\", \"when\", \"project\", \"last matching prompt\"];\n const rows = matches.map((m, idx) => {\n const shortId = (m.sessionId ?? \"—\").slice(0, 8);\n const when = m.lastMatchTs > 0 ? fmtTs(m.lastMatchTs) : dim(\"—\");\n const project = shortenProject(m.project || \"—\");\n const snippet = m.lastMatchDisplay.replace(/\\n+/g, \" \").trim().slice(0, 48);\n const fullSnippet = m.lastMatchDisplay.replace(/\\n+/g, \" \").trim();\n const display = snippet.length < fullSnippet.length ? `\"${snippet}…\"` : `\"${snippet}\"`;\n return [\n dim(String(idx + 1)),\n chalk.cyan(shortId),\n when,\n dim(project),\n chalk.dim(display),\n ];\n });\n\n console.log(renderTable(headers, rows));\n console.log();\n\n // Direct pick by inline number\n if (pickN !== undefined) {\n const idx = pickN - 1;\n if (idx >= 0 && idx < matches.length) {\n const session = matchToSession(matches[idx], allSessions);\n if (!session) {\n console.error(err(\"Could not resolve session for launch (no project path).\"));\n process.exitCode = 1;\n return;\n }\n launchSession(session, allSessions, opts.dryRun ?? false);\n return;\n }\n console.error(err(`Invalid choice: ${pickN}`));\n process.exitCode = 1;\n return;\n }\n\n // Auto-pick first\n if (opts.auto) {\n const session = matchToSession(matches[0], allSessions);\n if (!session) {\n console.error(err(\"Could not resolve session for launch (no project path).\"));\n process.exitCode = 1;\n return;\n }\n launchSession(session, allSessions, opts.dryRun ?? false);\n return;\n }\n\n // Interactive pick\n const choice = await askForChoice(matches.length);\n if (choice !== null) {\n const session = matchToSession(matches[choice - 1], allSessions);\n if (!session) {\n console.error(err(\"Could not resolve session for launch (no project path).\"));\n process.exitCode = 1;\n return;\n }\n launchSession(session, allSessions, opts.dryRun ?? false);\n }\n}\n","#!/usr/bin/env node\n/**\n * PAI Knowledge OS — CLI entry point (v0.10.0 topic-first redesign)\n *\n * Top-level surface:\n * pai → recent sessions picker\n * pai <topic> → history search + candidate picker + launch\n * pai <uuid-prefix> → direct session resume via filesystem scan\n * pai cd <name> → cd to project directory (no Claude launch)\n * pai pause [all] → save state (or mass-pause every live session)\n * pai end → finalize session\n *\n * Power-user namespaces (still accessible, hidden from main help):\n * pai sessions ... → full session management\n * pai projects ... → project management\n * pai registry ... → registry maintenance\n * pai memory ... → memory engine\n */\n\nimport { Command } from \"commander\";\nimport { readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { openRegistry } from \"../registry/db.js\";\nimport type { Database } from \"better-sqlite3\";\nimport { registerProjectsCommands } from \"./commands/project/projects-index.js\";\nimport { findMovedPath } from \"./commands/project/commands.js\";\nimport { existsSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport { registerSessionsCommands } from \"./commands/session/sessions-index.js\";\nimport { registerSessionCleanupCommand } from \"./commands/session-cleanup.js\";\nimport { registerRegistryCommands } from \"./commands/registry.js\";\nimport { registerMemoryCommands } from \"./commands/memory.js\";\nimport { registerMcpCommands } from \"./commands/mcp.js\";\nimport { registerDaemonCommands } from \"./commands/daemon.js\";\nimport { registerBackupCommands } from \"./commands/backup.js\";\nimport { registerRestoreCommands } from \"./commands/restore.js\";\nimport { registerSetupCommand } from \"./commands/setup.js\";\nimport { registerObsidianCommands } from \"./commands/obsidian.js\";\nimport { registerZettelCommands } from \"./commands/zettel.js\";\nimport { registerObservationCommands } from \"./commands/observation.js\";\nimport { registerUpdateCommand } from \"./commands/update.js\";\nimport { registerNotifyCommands } from \"./commands/notify.js\";\nimport { registerTopicCommands } from \"./commands/topic.js\";\nimport { registerKgCommands } from \"./commands/kg.js\";\nimport { registerDbCommands } from \"./commands/db.js\";\nimport { err, warn, ok, dim, shortenPath, encodeDir, now } from \"./utils.js\";\nimport { cmdPause } from \"./commands/session/pause.js\";\nimport { cmdEnd } from \"./commands/session/end.js\";\nimport { cmdGoto } from \"./commands/session/goto.js\";\nimport { cmdPauseAll } from \"./commands/session/pause-all.js\";\nimport { cmdList as cmdNotesList } from \"./commands/session/commands.js\";\nimport { resolveIdentifier } from \"./commands/project/helpers.js\";\nimport { cmdFind } from \"./commands/find.js\";\nimport { cmdMain } from \"./commands/main-resolver.js\";\n\n// ---------------------------------------------------------------------------\n// Version resolution\n// ---------------------------------------------------------------------------\n\nfunction getVersion(): string {\n try {\n const __dirname = dirname(fileURLToPath(import.meta.url));\n // Walk up from dist/cli/ or src/cli/ to find package.json\n const pkgPath = join(__dirname, \"../../package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as { version?: string };\n return pkg.version ?? \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Lazy database singleton — opened on first command that needs it\n// ---------------------------------------------------------------------------\n\nlet _db: Database | null = null;\n\nfunction getDb(): Database {\n if (!_db) {\n try {\n _db = openRegistry();\n } catch (e) {\n console.error(err(`Failed to open PAI registry: ${e}`));\n process.exit(1);\n }\n }\n return _db;\n}\n\n// ---------------------------------------------------------------------------\n// Program definition\n// ---------------------------------------------------------------------------\n\nconst program = new Command();\n\nprogram\n .name(\"pai\")\n .description(\"PAI Knowledge OS — Personal AI Infrastructure CLI\")\n .version(getVersion(), \"-V, --version\", \"Print version and exit\")\n .addHelpText(\n \"after\",\n `\nUsage:\n pai Show recent sessions (interactive picker)\n pai <topic> Find sessions by topic and launch the chosen one\n pai <uuid-prefix> Resume a specific session by UUID\n pai cd <name> cd to a project directory (no Claude launch)\n pai pause [all] Save state (or mass-pause every live session)\n pai end Finalize session: save state + mark note Completed\n\nExamples:\n pai mdf Find all sessions where you worked on MDF\n pai solar panels Free-text search across your prompt history\n pai 81c5c3dc Resume session by UUID prefix\n pai See the 20 most recent sessions\n\nPower-user namespaces (run \"pai sessions --help\" etc. for details):\n pai sessions ... Full session management (list, goto, info, ...)\n pai projects ... Project management (cd, rebind, list, ...)\n pai registry ... Registry maintenance (scan, ...)\n pai memory ... Memory engine (index, search, ...)\n\nShell integration:\n eval \"$(pai shell-init)\" # Add to ~/.zshrc for pai cd to work`\n );\n\n// ---------------------------------------------------------------------------\n// pai projects (canonical plural namespace)\n// ---------------------------------------------------------------------------\n\nconst projectsCmd = program\n .command(\"projects\")\n .description(\"Manage registered projects (list, cd, add, info, ...)\");\n\nregisterProjectsCommands(projectsCmd, getDb);\n\n// ---------------------------------------------------------------------------\n// pai sessions (canonical plural namespace)\n// Bare `pai sessions` runs the default `list` subcommand.\n// ---------------------------------------------------------------------------\n\nconst sessionsCmd = program\n .command(\"sessions\")\n .description(\"Session management (list, goto, pause, ...)\");\n\nregisterSessionsCommands(sessionsCmd, getDb);\nregisterSessionCleanupCommand(sessionsCmd, getDb);\n\n// ---------------------------------------------------------------------------\n// pai registry\n// ---------------------------------------------------------------------------\n\nconst registryCmd = program\n .command(\"registry\")\n .description(\"Registry maintenance: scan, migrate, stats, rebuild\");\n\nregisterRegistryCommands(registryCmd, getDb);\n\n// ---------------------------------------------------------------------------\n// pai memory\n// ---------------------------------------------------------------------------\n\nconst memoryCmd = program\n .command(\"memory\")\n .description(\"Memory engine: index, search, and status\");\n\nregisterMemoryCommands(memoryCmd, getDb);\n\n// ---------------------------------------------------------------------------\n// pai mcp\n// ---------------------------------------------------------------------------\n\nconst mcpCmd = program\n .command(\"mcp\")\n .description(\"MCP server management: install and status\");\n\nregisterMcpCommands(mcpCmd);\n\n// ---------------------------------------------------------------------------\n// pai daemon\n// ---------------------------------------------------------------------------\n\nconst daemonCmd = program\n .command(\"daemon\")\n .description(\"PAI daemon management: serve, status, restart, install, uninstall, logs\");\n\nregisterDaemonCommands(daemonCmd);\n\n// ---------------------------------------------------------------------------\n// pai backup / pai restore\n// ---------------------------------------------------------------------------\n\nregisterBackupCommands(program);\nregisterRestoreCommands(program);\n\n// ---------------------------------------------------------------------------\n// pai setup\n// ---------------------------------------------------------------------------\n\nregisterSetupCommand(program);\n\n// ---------------------------------------------------------------------------\n// pai update\n// ---------------------------------------------------------------------------\n\nregisterUpdateCommand(program);\n\n// ---------------------------------------------------------------------------\n// pai notify\n// ---------------------------------------------------------------------------\n\nconst notifyCmd = program\n .command(\"notify\")\n .description(\"Notification config: status, get, set, test, send\");\n\nregisterNotifyCommands(notifyCmd);\n\n// ---------------------------------------------------------------------------\n// pai topic\n// ---------------------------------------------------------------------------\n\nconst topicCmd = program\n .command(\"topic\")\n .description(\"Topic shift detection: check whether context has drifted to a different project\");\n\nregisterTopicCommands(topicCmd);\n\n// ---------------------------------------------------------------------------\n// pai kg\n// ---------------------------------------------------------------------------\n\nconst kgCmd = program\n .command(\"kg\")\n .description(\"Temporal knowledge graph: backfill, query, list, stats\");\n\nregisterKgCommands(kgCmd);\n\n// ---------------------------------------------------------------------------\n// pai db\n// ---------------------------------------------------------------------------\n\nconst dbCmd = program\n .command(\"db\")\n .description(\"Database inspection: query, tables, schema (sqlite or postgres)\");\n\nregisterDbCommands(dbCmd);\n\n// ---------------------------------------------------------------------------\n// pai obsidian\n// ---------------------------------------------------------------------------\n\nconst obsidianCmd = program\n .command(\"obsidian\")\n .description(\"Obsidian vault: sync project notes, view status, open in Obsidian\");\n\nregisterObsidianCommands(obsidianCmd, getDb);\n\n// ---------------------------------------------------------------------------\n// pai zettel\n// ---------------------------------------------------------------------------\n\nconst zettelCmd = program\n .command(\"zettel\")\n .description(\"Zettelkasten intelligence: explore, surprise, converse, themes, health, suggest\");\n\nregisterZettelCommands(zettelCmd, getDb);\n\n// ---------------------------------------------------------------------------\n// pai observation\n// ---------------------------------------------------------------------------\n\nconst observationCmd = program\n .command(\"observation\")\n .description(\"Observation capture: list, search, and stats\");\n\nregisterObservationCommands(observationCmd);\n\n// ---------------------------------------------------------------------------\n// DAILY VERBS — top-level short forms\n// ---------------------------------------------------------------------------\n\n// pai pause [all] [--dry-run] [--exit] [--wait <ms>]\n// When called as `pai pause all`, pauses every live session via AIBroker.\n// When called as `pai pause`, saves state for the current session.\nprogram\n .command(\"pause [target]\")\n .description(\n \"Save state and display safe-exit instructions for the current session.\\n\" +\n \"Writes a ## Continue checkpoint to the project's TODO.md.\\n\" +\n \"Use `pai pause all` to pause every live session via AIBroker.\\n\" +\n \"Long form: pai sessions pause\"\n )\n .option(\"--dry-run\", \"Preview changes without writing them\")\n .option(\"--exit\", \"(pause all only) Also send /exit to each session after pausing\")\n .option(\"--wait <ms>\", \"(pause all only) Milliseconds to wait before /exit (default: 5000)\", \"5000\")\n .action(async (target: string | undefined, opts: { dryRun?: boolean; exit?: boolean; wait?: string }) => {\n if (target === \"all\") {\n await cmdPauseAll({\n exit: opts.exit,\n dryRun: opts.dryRun,\n wait: opts.wait !== undefined ? parseInt(opts.wait, 10) : undefined,\n });\n } else {\n if (target !== undefined) {\n console.error(`Unknown target: ${target}. Did you mean 'pai pause all'?`);\n process.exitCode = 1;\n return;\n }\n cmdPause(getDb(), { dryRun: opts.dryRun });\n }\n });\n\n// pai end [--dry-run]\nprogram\n .command(\"end\")\n .description(\n \"Finalize a session: save state, mark note Completed, display safe-exit instructions.\\n\" +\n \"Long form: pai sessions end\"\n )\n .option(\"--dry-run\", \"Preview all changes without writing them\")\n .action((opts: { dryRun?: boolean }) => {\n cmdEnd(getDb(), opts);\n });\n\n// pai resume <name> [--dry-run] — compat alias for pai sessions goto\n// Power users can also use: pai sessions goto <name>\nprogram\n .command(\"resume <name>\")\n .description(\n \"Go to a session by name or UUID: resume if resumable, start fresh otherwise.\\n\" +\n \"Also: pai <name> (shorter) | pai sessions goto <name> (long form)\"\n )\n .option(\"--dry-run\", \"Print the exact argv and cwd, then exit without launching\")\n .action(\n (name: string, opts: { dryRun?: boolean }) => {\n cmdGoto(getDb(), name, { dryRun: opts.dryRun });\n }\n );\n\n// pai cd <identifier> — prints path; shell wrapper does the actual cd\nprogram\n .command(\"cd <identifier>\")\n .description(\n \"cd to a project directory (shell wrapper handles the actual cd).\\n\" +\n \"Long form: pai projects cd <identifier>\\n\" +\n \"The shell function installed by pai shell-init intercepts this command\\n\" +\n \"and calls builtin cd with the resolved path.\\n\" +\n \"Auto-detects moved projects when the registered path no longer exists.\"\n )\n .action((identifier: string) => {\n const db = getDb();\n const project = resolveIdentifier(db, identifier);\n if (!project) {\n console.error(`Project not found: ${identifier}`);\n process.exit(1);\n return;\n }\n\n if (existsSync(project.root_path)) {\n process.stdout.write(project.root_path + \"\\n\");\n return;\n }\n\n // Path missing — search for moved location\n process.stderr.write(\n warn(`Path not found: ${project.root_path}\\n`) +\n dim(\" Searching for moved location...\\n\")\n );\n\n const result = findMovedPath(project.root_path);\n\n if (result.found) {\n const newPath = result.found;\n const newEncoded = encodeDir(newPath);\n const ts = now();\n db.prepare(\n \"UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?\"\n ).run(newPath, newEncoded, ts, project.id);\n process.stderr.write(\n ok(`Project moved: ${shortenPath(project.root_path, 50)}\\n`) +\n dim(` → ${newPath}\\n`) +\n ok(\"Registry updated.\\n\")\n );\n process.stdout.write(newPath + \"\\n\");\n return;\n }\n\n if (result.ambiguous) {\n process.stderr.write(\n warn(`Multiple directories named \"${basename(project.root_path)}\" found:\\n`)\n );\n for (const candidate of result.ambiguous!) {\n process.stderr.write(dim(` ${candidate}\\n`));\n }\n process.stderr.write(\n dim(`\\n Disambiguate with: pai projects rebind ${project.slug} <path>\\n`)\n );\n process.exitCode = 1;\n return;\n }\n\n process.stderr.write(\n err(\n `Project \"${project.slug}\" root_path \"${project.root_path}\" does not exist on disk\\n` +\n ` and no folder named \"${basename(project.root_path)}\" was found in scan dirs.\\n`\n ) +\n dim(` Fix with: pai projects rebind ${project.slug} <new-path>\\n`)\n );\n process.exitCode = 1;\n });\n\n// Note: `pai sessions` and `pai projects` are now defined as the canonical\n// namespaces above (with `isDefault: true` on their `list` subcommands).\n// Bare invocation (`pai sessions`, `pai projects`) triggers the default list.\n\n// ---------------------------------------------------------------------------\n// pai notes — markdown session notes (power user, still accessible)\n// ---------------------------------------------------------------------------\n\nconst notesCmd = program\n .command(\"notes [project-slug]\")\n .description(\n \"Markdown session notes — list notes, optionally filtered to a project.\"\n )\n .option(\"--limit <n>\", \"Maximum number of notes to show\", \"20\")\n .option(\"--status <status>\", \"Filter by status: open | completed | compacted\")\n .action(\n (\n projectSlug: string | undefined,\n opts: { limit?: string; status?: string }\n ) => {\n cmdNotesList(getDb(), projectSlug, opts);\n }\n );\n\n// pai notes list [project-slug] — explicit sub-action (same behaviour)\nnotesCmd\n .command(\"list [project-slug]\")\n .description(\"List markdown session notes (same as: pai notes)\")\n .option(\"--limit <n>\", \"Maximum number of notes to show\", \"20\")\n .option(\"--status <status>\", \"Filter by status: open | completed | compacted\")\n .action(\n (\n projectSlug: string | undefined,\n opts: { limit?: string; status?: string }\n ) => {\n cmdNotesList(getDb(), projectSlug, opts);\n }\n );\n\n// ---------------------------------------------------------------------------\n// pai find <query> — explicit history search (power user alias)\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"find <query>\")\n .description(\n \"Search prompt history for matching sessions.\\n\" +\n \"Same as: pai <query> (the default command does history search too)\"\n )\n .option(\"-n, --n <count>\", \"Maximum number of sessions to show\", \"20\")\n .option(\"--json\", \"Output as JSON array\")\n .action(async (query: string, opts: { n?: string; json?: boolean }) => {\n await cmdFind(query, opts);\n });\n\n// ---------------------------------------------------------------------------\n// pai shell-init — emit shell function for eval \"$(pai shell-init)\"\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"shell-init\")\n .description(\n \"Emit shell integration code. Add to ~/.zshrc:\\n\" +\n ' eval \"$(pai shell-init)\"'\n )\n .action(() => {\n // The shell function intercepts commands that need a real `cd`:\n // pai cd <name> → resolves via pai cd, then builtin cd\n // pai projects cd <name> → same (long form)\n // Everything else is passed through to the real pai binary unchanged.\n process.stdout.write(\n `# PAI shell integration — generated by: pai shell-init\npai() {\n if [[ \"$1\" == \"cd\" ]]; then\n local dir=$(command pai cd \"$2\" 2>/dev/null)\n if [[ -n \"$dir\" && -d \"$dir\" ]]; then\n builtin cd \"$dir\"\n echo \"-> $dir\"\n else\n echo \"Project not found: $2\" >&2\n return 1\n fi\n elif [[ \"$1\" == \"projects\" && \"$2\" == \"cd\" ]]; then\n local dir=$(command pai projects cd \"$3\" 2>/dev/null)\n if [[ -n \"$dir\" && -d \"$dir\" ]]; then\n builtin cd \"$dir\"\n echo \"-> $dir\"\n else\n echo \"Project not found: $3\" >&2\n return 1\n fi\n else\n command pai \"$@\"\n fi\n}\n`\n );\n });\n\n// ---------------------------------------------------------------------------\n// pai [query] [pick] — DEFAULT COMMAND: topic-first session resolver (v0.10.0)\n//\n// Must be registered LAST so named subcommands take priority.\n// Commander matches named commands first; if nothing matches, this catches it.\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"query [query] [pick]\", { isDefault: true, hidden: true })\n .description(\n \"Find and launch a session by topic, name, or UUID.\\n\" +\n \"No arg → recent sessions picker\\n\" +\n \"Topic → history search → candidate list → pick #\\n\" +\n \"UUID → direct resume via filesystem scan\"\n )\n .option(\"-y, --auto\", \"Auto-pick #1 without prompting\")\n .option(\"--dry-run\", \"Print what would happen without launching\")\n .option(\"-n, --n <count>\", \"Max candidates for history search\", \"20\")\n .action(async (query: string | undefined, pick: string | undefined, opts: { auto?: boolean; dryRun?: boolean; n?: string }) => {\n const pickN = pick !== undefined ? parseInt(pick, 10) : undefined;\n await cmdMain(getDb(), query, pickN, opts);\n });\n\n// ---------------------------------------------------------------------------\n// Error handling — show clean message instead of stack trace for user errors\n// ---------------------------------------------------------------------------\n\nprogram.configureOutput({\n writeErr: (str) => process.stderr.write(err(str)),\n});\n\nprogram.exitOverride((error) => {\n if (error.code === \"commander.helpDisplayed\" || error.code === \"commander.version\") {\n process.exit(0);\n }\n if (\n error.code === \"commander.missingArgument\" ||\n error.code === \"commander.unknownOption\" ||\n error.code === \"commander.unknownCommand\"\n ) {\n // Commander has already printed the message\n process.exit(1);\n }\n // For unexpected errors, show message but not stack trace\n console.error(err(error.message));\n process.exit(1);\n});\n\n// ---------------------------------------------------------------------------\n// Parse\n// ---------------------------------------------------------------------------\n\nprogram.parse(process.argv);\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["getProject","upsertTag","cmdList","cmdInfo","cmdTag","upsertTag","getProject","cmdHealth","getProject","toTitleCase","cmdList","getProject","toTitleCase","HANDOVER_TODO_LOCATIONS","findProjectTodo","stripContinueSection","probeResume","findNotesDir","findClaudeNotesDir","cmdMigrate","slugify","cmdStats","tierColor","CONFIG_FILE","CLAUDE_JSON_PATH","readClaudeJson","writeClaudeJson","cmdInstall","cmdStatus","HOME","cmdStatus","HOME","REGISTRY_DB","CONFIG_FILE","BACKUPS_DIR","DOCKER_CONTAINER","PG_DATABASE","PG_USER","line","CONFIG_FILE","getTemplatesDir","getTemplatesDir","getTemplatesDir","getTemplatesDir","useSymlinks","CONFIG_FILE","CONFIG_FILE","CONFIG_FILE","cmdStatus","fmtTs","cmdList","cmdStats","makeClient","getPool","cmdQuery","HISTORY_FILE","shortenProject","fmtTs","searchHistory"],"sources":["../../src/session/promote.ts","../../src/cli/commands/project/helpers.ts","../../src/cli/commands/project/commands.ts","../../src/cli/commands/project/session-config.ts","../../src/cli/commands/project/health.ts","../../src/cli/commands/project/projects-index.ts","../../src/session/slug-generator.ts","../../src/cli/commands/session/helpers.ts","../../src/cli/commands/session/commands.ts","../../src/cli/commands/session/checkpoint.ts","../../src/cli/commands/session/handover.ts","../../src/cli/lib/session-scan.ts","../../src/cli/lib/aibroker-client.ts","../../src/cli/commands/session/recent.ts","../../src/cli/commands/session/goto.ts","../../src/cli/commands/session/pause.ts","../../src/cli/commands/session/end.ts","../../src/cli/commands/session/pause-all.ts","../../src/cli/commands/session/clear-names.ts","../../src/cli/commands/session/sessions-index.ts","../../src/cli/commands/session-cleanup/types.ts","../../src/cli/commands/session-cleanup/rename.ts","../../src/cli/commands/session-cleanup/scanner.ts","../../src/cli/commands/session-cleanup/executor.ts","../../src/cli/commands/session-cleanup/index.ts","../../src/cli/commands/registry/migrate.ts","../../src/cli/commands/registry/index.ts","../../src/cli/commands/memory/embed.ts","../../src/cli/commands/memory/index-cmd.ts","../../src/cli/commands/memory/search.ts","../../src/cli/commands/memory/stats.ts","../../src/cli/commands/memory/index.ts","../../src/cli/commands/mcp.ts","../../src/cli/commands/daemon.ts","../../src/cli/commands/backup.ts","../../src/cli/commands/restore.ts","../../src/cli/commands/setup/utils.ts","../../src/cli/commands/setup/steps/01-welcome.ts","../../src/cli/commands/setup/steps/02-storage.ts","../../src/cli/commands/setup/steps/03-embedding.ts","../../src/cli/commands/setup/steps/04-claude-md.ts","../../src/cli/commands/setup/steps/05-pai-skill.ts","../../src/cli/commands/setup/steps/06-steering-rules.ts","../../src/cli/commands/setup/steps/07-skill-stubs.ts","../../src/cli/commands/setup/steps/08-hooks.ts","../../src/cli/commands/setup/steps/09-ts-hooks.ts","../../src/cli/commands/settings-manager.ts","../../src/cli/commands/setup/steps/10-settings.ts","../../src/cli/commands/setup/steps/11-daemon.ts","../../src/cli/commands/setup/steps/12-mcp.ts","../../src/cli/commands/setup/steps/13-directories.ts","../../src/cli/commands/setup/steps/14-initial-index.ts","../../src/cli/commands/setup/steps/15-verify.ts","../../src/cli/commands/setup/index.ts","../../src/obsidian/sync/symlinks.ts","../../src/obsidian/sync/generate.ts","../../src/obsidian/sync/walk.ts","../../src/obsidian/sync/master.ts","../../src/obsidian/status.ts","../../src/cli/commands/obsidian.ts","../../src/cli/commands/zettel/utils.ts","../../src/cli/commands/zettel/explore.ts","../../src/cli/commands/zettel/health.ts","../../src/cli/commands/zettel/surprise.ts","../../src/cli/commands/zettel/suggest.ts","../../src/cli/commands/zettel/converse.ts","../../src/cli/commands/zettel/themes.ts","../../src/cli/commands/zettel/index.ts","../../src/cli/commands/observation.ts","../../src/cli/commands/update.ts","../../src/cli/commands/notify.ts","../../src/cli/commands/topic.ts","../../src/memory/kg-backfill.ts","../../src/cli/commands/kg.ts","../../src/cli/commands/db.ts","../../src/cli/commands/find.ts","../../src/cli/lib/history-search.ts","../../src/cli/commands/main-resolver.ts","../../src/cli/index.ts"],"sourcesContent":["/**\n * pai project promote --from-session <path> --to <new-project-path> [--name \"Name\"]\n *\n * Promotes a session note into a new standalone project:\n * 1. Validates inputs\n * 2. Scaffolds the new project directory\n * 3. Copies the session note and creates a MEMORY.md\n * 4. Registers the new project in the PAI registry\n * 5. Optionally backlinks from the source project's TODO.md\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport {\n existsSync,\n mkdirSync,\n copyFileSync,\n readFileSync,\n appendFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { join, basename, dirname, resolve } from \"node:path\";\nimport {\n ok,\n warn,\n err,\n dim,\n bold,\n slugify,\n encodeDir,\n resolvePath,\n scaffoldProjectDirs,\n now,\n} from \"../cli/utils.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface PromoteOptions {\n fromSession: string;\n to: string;\n name?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Derive a human-readable project name from a session note filename.\n * \"0042 - 2026-02-24 - Dark Mode Implementation.md\" → \"Dark Mode Implementation\"\n */\nfunction deriveNameFromFilename(filename: string): string {\n const base = basename(filename, \".md\");\n // Strip leading \"NNNN - YYYY-MM-DD - \" prefix if present\n const match = base.match(/^\\d+ - \\d{4}-\\d{2}-\\d{2} - (.+)$/);\n return match ? match[1] : base;\n}\n\n/**\n * Extract the first N H2 (##) section headings from markdown content.\n */\nfunction extractH2Headings(content: string, limit = 2): string[] {\n const headings: string[] = [];\n for (const m of content.matchAll(/^## .+$/gm)) {\n headings.push(m[0]);\n if (headings.length >= limit) break;\n }\n return headings;\n}\n\n/**\n * Find the source project root from the session note path.\n * Convention: session note lives at {project-root}/Notes/{filename}\n * So: dirname(dirname(sessionPath)) = project root.\n */\nfunction findSourceProjectRoot(sessionPath: string): string {\n const notesDir = dirname(resolve(sessionPath));\n return dirname(notesDir);\n}\n\n// ---------------------------------------------------------------------------\n// Main implementation\n// ---------------------------------------------------------------------------\n\nexport function cmdPromote(db: Database, opts: PromoteOptions): void {\n const sessionPath = resolvePath(opts.fromSession);\n const targetPath = resolvePath(opts.to);\n\n // ---- Validate inputs ----\n\n if (!existsSync(sessionPath)) {\n console.error(err(`Session note not found: ${sessionPath}`));\n process.exit(1);\n }\n if (!sessionPath.endsWith(\".md\")) {\n console.error(err(`--from-session must be a markdown (.md) file: ${sessionPath}`));\n process.exit(1);\n }\n if (existsSync(targetPath)) {\n console.error(err(`Target path already exists: ${targetPath}`));\n process.exit(1);\n }\n\n // ---- Derive project name and slug ----\n\n const displayName = opts.name ?? deriveNameFromFilename(sessionPath);\n const slug = slugify(displayName);\n\n if (!slug) {\n console.error(err(`Could not derive a valid slug from name: \"${displayName}\"`));\n process.exit(1);\n }\n\n // ---- Check registry for conflicts ----\n\n const encodedDir = encodeDir(targetPath);\n const existing = db\n .prepare(\"SELECT id FROM projects WHERE slug = ? OR root_path = ? OR encoded_dir = ?\")\n .get(slug, targetPath, encodedDir);\n if (existing) {\n console.error(\n err(`A project with slug \"${slug}\" or path \"${targetPath}\" is already registered.`)\n );\n process.exit(1);\n }\n\n // ---- Scaffold new project ----\n\n scaffoldProjectDirs(targetPath);\n\n // ---- Copy session note ----\n\n const today = new Date().toISOString().slice(0, 10);\n const sourceBasename = basename(sessionPath);\n const sourceProjectRoot = findSourceProjectRoot(sessionPath);\n const sourceProjectName = basename(sourceProjectRoot);\n\n const destNoteName = `0001 - ${today} - Promoted from ${sourceProjectName}.md`;\n const destNotePath = join(targetPath, \"Notes\", destNoteName);\n\n copyFileSync(sessionPath, destNotePath);\n\n // ---- Create MEMORY.md ----\n\n const sessionContent = readFileSync(sessionPath, \"utf-8\");\n const headings = extractH2Headings(sessionContent);\n\n const memoryContent = [\n `# Project Memory`,\n ``,\n `## Origin`,\n ``,\n `- **Promoted from:** ${sessionPath}`,\n `- **Source project:** ${sourceProjectRoot}`,\n `- **Date of promotion:** ${today}`,\n `- **Original session note:** ${sourceBasename}`,\n ``,\n `## Initial Context`,\n ``,\n headings.length > 0\n ? `Key topics from the source session:\\n\\n${headings.map((h) => `- ${h.replace(/^## /, \"\")}`).join(\"\\n\")}`\n : `(Extracted from session note — see ${destNoteName})`,\n ``,\n ].join(\"\\n\");\n\n writeFileSync(join(targetPath, \"Notes\", \"MEMORY.md\"), memoryContent, \"utf-8\");\n\n // ---- Register in PAI registry ----\n\n const ts = now();\n db.prepare(\n `INSERT INTO projects\n (slug, display_name, root_path, encoded_dir, type, status, created_at, updated_at)\n VALUES (?, ?, ?, ?, 'local', 'active', ?, ?)`\n ).run(slug, displayName, targetPath, encodedDir, ts, ts);\n\n // ---- Backlink in source TODO.md ----\n\n const sourceTodoPath = join(sourceProjectRoot, \"Notes\", \"TODO.md\");\n if (existsSync(sourceTodoPath)) {\n const backlink = `\\n- Promoted to project: [${slug}](${targetPath})\\n`;\n appendFileSync(sourceTodoPath, backlink, \"utf-8\");\n console.log(dim(` Backlink added to: ${sourceTodoPath}`));\n }\n\n // ---- Success output ----\n\n console.log();\n console.log(ok(`Project promoted: ${bold(slug)}`));\n console.log(dim(` Display name: ${displayName}`));\n console.log(dim(` Path: ${targetPath}`));\n console.log(dim(` Slug: ${slug}`));\n console.log(dim(` Session note: ${destNoteName}`));\n console.log(dim(` Memory: Notes/MEMORY.md created`));\n console.log();\n console.log(dim(` Next: cd ${targetPath} && pai session list ${slug}`));\n}\n","/**\n * Shared database helper functions for project sub-commands.\n * All functions accept a Database instance and are pure query wrappers.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport type { ProjectRow } from \"./types.js\";\nimport { err } from \"../../utils.js\";\n\nexport function getProject(db: Database, slug: string): ProjectRow | undefined {\n const direct = db\n .prepare(\"SELECT * FROM projects WHERE slug = ?\")\n .get(slug) as ProjectRow | undefined;\n if (direct) return direct;\n\n const alias = db\n .prepare(\n `SELECT p.* FROM projects p\n JOIN aliases a ON a.project_id = p.id\n WHERE a.alias = ?`\n )\n .get(slug) as ProjectRow | undefined;\n return alias;\n}\n\nexport function requireProject(db: Database, slug: string): ProjectRow {\n const project = getProject(db, slug);\n if (!project) {\n console.error(err(`Project not found: ${slug}`));\n process.exit(1);\n }\n return project;\n}\n\n/**\n * Resolve an identifier that may be a list index number or a slug.\n */\nexport function resolveIdentifier(db: Database, identifier: string): ProjectRow | undefined {\n const num = parseInt(identifier, 10);\n if (!isNaN(num) && num > 0 && String(num) === identifier) {\n const rows = db.prepare(\n \"SELECT * FROM projects ORDER BY status ASC, updated_at DESC\"\n ).all() as ProjectRow[];\n if (num <= rows.length) return rows[num - 1];\n }\n return getProject(db, identifier);\n}\n\nexport function getProjectTags(db: Database, projectId: number): string[] {\n const rows = db\n .prepare(\n `SELECT t.name FROM tags t\n JOIN project_tags pt ON pt.tag_id = t.id\n WHERE pt.project_id = ?\n ORDER BY t.name`\n )\n .all(projectId) as { name: string }[];\n return rows.map((r) => r.name);\n}\n\nexport function getProjectAliases(db: Database, projectId: number): string[] {\n const rows = db\n .prepare(\"SELECT alias FROM aliases WHERE project_id = ? ORDER BY alias\")\n .all(projectId) as { alias: string }[];\n return rows.map((r) => r.alias);\n}\n\nexport function getSessionCount(db: Database, projectId: number): number {\n const row = db\n .prepare(\"SELECT COUNT(*) AS cnt FROM sessions WHERE project_id = ?\")\n .get(projectId) as { cnt: number };\n return row.cnt;\n}\n\nexport function getLastSessionDate(db: Database, projectId: number): number | null {\n const row = db\n .prepare(\n `SELECT created_at FROM sessions WHERE project_id = ?\n ORDER BY created_at DESC LIMIT 1`\n )\n .get(projectId) as { created_at: number } | undefined;\n return row ? row.created_at : null;\n}\n\nexport function upsertTag(db: Database, tagName: string): number {\n db.prepare(\"INSERT OR IGNORE INTO tags (name) VALUES (?)\").run(tagName);\n const row = db.prepare(\"SELECT id FROM tags WHERE name = ?\").get(tagName) as { id: number };\n return row.id;\n}\n","/**\n * Core project CRUD commands: add, list, info, archive, unarchive, move, tag,\n * alias, edit, detect, consolidate, go, rebind — plus private helpers levenshtein,\n * containsIgnoreCase, findProjectNotesDirs, and findMovedPath.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport {\n existsSync,\n readdirSync,\n statSync,\n mkdirSync,\n renameSync,\n} from \"node:fs\";\nimport { join, basename, resolve } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport {\n ok,\n warn,\n err,\n dim,\n bold,\n header,\n slugFromPath,\n encodeDir,\n resolvePath,\n scaffoldProjectDirs,\n renderTable,\n shortenPath,\n fmtDate,\n now,\n} from \"../../utils.js\";\nimport {\n detectProject,\n formatDetection,\n formatDetectionJson,\n} from \"../detect.js\";\nimport { ensurePaiMarker } from \"../../../registry/pai-marker.js\";\nimport type { ProjectRow, SessionRow } from \"./types.js\";\nimport {\n requireProject,\n resolveIdentifier,\n getProject,\n getProjectTags,\n getProjectAliases,\n getSessionCount,\n getLastSessionDate,\n upsertTag,\n} from \"./helpers.js\";\nimport { loadScanConfig, resolveHome } from \"../registry/scan.js\";\n\n// ---------------------------------------------------------------------------\n// Private helpers\n// ---------------------------------------------------------------------------\n\nfunction levenshtein(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n const dp: number[][] = Array.from({ length: m + 1 }, (_, i) =>\n Array.from({ length: n + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0))\n );\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n dp[i][j] =\n a[i - 1] === b[j - 1]\n ? dp[i - 1][j - 1]\n : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);\n }\n }\n return dp[m][n];\n}\n\nfunction containsIgnoreCase(haystack: string, needle: string): boolean {\n return haystack.toLowerCase().includes(needle.toLowerCase());\n}\n\nfunction findProjectNotesDirs(project: ProjectRow): {\n encodedDir: string;\n fullPath: string;\n notesPath: string;\n noteCount: number;\n}[] {\n const claudeProjects = join(homedir(), \".claude\", \"projects\");\n if (!existsSync(claudeProjects)) return [];\n\n const results: {\n encodedDir: string;\n fullPath: string;\n notesPath: string;\n noteCount: number;\n }[] = [];\n const rootEncoded = encodeDir(project.root_path);\n\n try {\n for (const entry of readdirSync(claudeProjects)) {\n const full = join(claudeProjects, entry);\n try {\n if (!statSync(full).isDirectory()) continue;\n } catch {\n continue;\n }\n\n if (entry !== rootEncoded && !entry.startsWith(rootEncoded)) continue;\n\n const notesPath = join(full, \"Notes\");\n if (!existsSync(notesPath)) continue;\n\n let noteCount = 0;\n try {\n noteCount = readdirSync(notesPath).filter(\n (f) => f.endsWith(\".md\") || f.endsWith(\".txt\")\n ).length;\n } catch {\n // count stays 0\n }\n\n results.push({ encodedDir: entry, fullPath: full, notesPath, noteCount });\n }\n } catch {\n // Unreadable — ignore\n }\n\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// Moved-project detection\n// ---------------------------------------------------------------------------\n\n// Directories to skip during filesystem walk\nconst SKIP_DIRS = new Set([\n \".git\", \"node_modules\", \".next\", \".nuxt\", \"dist\", \"build\", \"coverage\",\n \".cache\", \"__pycache__\", \"vendor\", \".svn\", \".hg\", \"venv\", \".venv\",\n \"target\", // Rust/Java build dir\n \"Notes\", // PAI session notes — not project roots\n]);\n\n/**\n * Walk `dir` up to `maxDepth` levels deep, collecting all subdirectory paths\n * whose basename matches `targetBasename` (case-insensitive on macOS/case-sensitive\n * on Linux, whichever is appropriate — we use exact case matching to keep it\n * correct and fast).\n *\n * Skips hidden directories and known build/tool dirs.\n * Stops walking a branch if the deadline is exceeded (returns partial results).\n */\nfunction walkForBasename(\n dir: string,\n targetBasename: string,\n maxDepth: number,\n deadline: number,\n results: string[]\n): void {\n if (Date.now() > deadline) return;\n if (maxDepth <= 0) return;\n\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n\n for (const entry of entries) {\n if (Date.now() > deadline) return;\n if (entry.startsWith(\".\")) continue;\n if (SKIP_DIRS.has(entry)) continue;\n\n const full = join(dir, entry);\n let isDir = false;\n try {\n isDir = statSync(full).isDirectory();\n } catch {\n continue;\n }\n if (!isDir) continue;\n\n // Case-insensitive comparison for the target basename (handles macOS APFS)\n if (entry.toLowerCase() === targetBasename.toLowerCase()) {\n results.push(full);\n // Don't recurse into the match itself — we want the directory, not children\n continue;\n }\n\n walkForBasename(full, targetBasename, maxDepth - 1, deadline, results);\n }\n}\n\n/**\n * Try to find where a project has moved.\n *\n * Searches scan_dirs from config (plus a set of common fallback dirs) for a\n * directory whose basename matches the basename of the registered root_path.\n *\n * Returns:\n * { found: string } — exactly one match found\n * { ambiguous: string[] } — multiple matches found\n * { found: undefined } — no match found (or timed out)\n */\nexport function findMovedPath(registeredPath: string): { found?: string; ambiguous?: string[] } {\n const targetBasename = basename(registeredPath);\n const config = loadScanConfig();\n const deadline = Date.now() + 5_000; // 5-second max\n\n // Build the list of top-level directories to search\n const home = homedir();\n const configDirs = (config.scan_dirs ?? []).map((d) => resolveHome(d));\n\n // Walk UP the registered path to find the deepest existing ancestor.\n // This is the best search root when a project has moved within the same tree.\n // e.g. ~/Cloud/Ablage/2025/Project → ~/Cloud/Ablage/2026/Project\n // Deepest existing ancestor: ~/Cloud/Ablage (if 2025 is gone but Ablage exists)\n function deepestExistingAncestor(p: string): string | null {\n let current = p;\n while (true) {\n const parent = join(current, \"..\");\n if (parent === current) return null; // filesystem root\n if (parent === home) return null; // don't use home itself as search root\n if (existsSync(parent)) return parent;\n current = parent;\n }\n }\n\n const ancestorRoot = deepestExistingAncestor(registeredPath);\n\n const fallbackDirs = [\n join(home, \"dev\"),\n join(home, \"Desktop\"),\n join(home, \"Projects\"),\n join(home, \"Documents\"),\n // Common cloud-storage root folders (Synology Drive, iCloud, etc.)\n join(home, \"Cloud\"),\n join(home, \"Daten\", \"Cloud\"),\n join(home, \"Daten\"),\n join(home, \"Library\", \"Mobile Documents\", \"com~apple~CloudDocs\"), // iCloud Drive\n // The deepest ancestor of the registered path that still exists on disk\n ...(ancestorRoot ? [ancestorRoot] : []),\n ];\n\n // Deduplicate and filter to existing directories\n const searchRoots = [...new Set([...configDirs, ...fallbackDirs])]\n .map((d) => {\n try { return resolve(d); } catch { return null; }\n })\n .filter((d): d is string => d !== null && existsSync(d));\n\n const results: string[] = [];\n for (const root of searchRoots) {\n if (Date.now() > deadline) break;\n walkForBasename(root, targetBasename, 6, deadline, results);\n }\n\n // Deduplicate results (different roots might overlap)\n const unique = [...new Set(results)];\n\n if (unique.length === 0) return {};\n if (unique.length === 1) return { found: unique[0] };\n return { ambiguous: unique };\n}\n\n// ---------------------------------------------------------------------------\n// Command implementations\n// ---------------------------------------------------------------------------\n\nexport function cmdAdd(\n db: Database,\n rawPath: string,\n opts: { slug?: string; type?: string; displayName?: string }\n): void {\n const rootPath = resolvePath(rawPath);\n const slug = opts.slug ?? slugFromPath(rootPath);\n const encodedDir = encodeDir(rootPath);\n const displayName = opts.displayName ?? slug;\n const type = opts.type ?? \"local\";\n\n const validTypes = [\"local\", \"central\", \"obsidian-linked\", \"external\"];\n if (!validTypes.includes(type)) {\n console.error(err(`Invalid type \"${type}\". Valid: ${validTypes.join(\", \")}`));\n process.exit(1);\n }\n\n const existing = db\n .prepare(\"SELECT id FROM projects WHERE slug = ? OR root_path = ?\")\n .get(slug, rootPath);\n if (existing) {\n console.error(\n err(`Project already registered (slug: ${slug} or path: ${rootPath})`)\n );\n process.exit(1);\n }\n\n const dirName = basename(rootPath).toLowerCase();\n const similar = db\n .prepare(\n `SELECT slug, root_path FROM projects WHERE status = 'active' AND slug != ?`\n )\n .all(slug) as { slug: string; root_path: string }[];\n const matches = similar.filter(\n (s) =>\n basename(s.root_path).toLowerCase() === dirName ||\n s.slug.replace(/-\\d+$/, \"\") === slug.replace(/-\\d+$/, \"\")\n );\n if (matches.length > 0) {\n console.log(warn(`Similar project(s) already registered:`));\n for (const m of matches) {\n console.log(dim(` ${bold(m.slug)} ${shortenPath(m.root_path, 50)}`));\n }\n console.log(\n dim(\n ` Consider: pai project alias ${matches[0].slug} <name> (to link them)`\n )\n );\n console.log(\n dim(` Or: pai project archive ${slug} (if this is a duplicate)`)\n );\n console.log();\n }\n\n const ts = now();\n db.prepare(\n `INSERT INTO projects\n (slug, display_name, root_path, encoded_dir, type, status, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, 'active', ?, ?)`\n ).run(slug, displayName, rootPath, encodedDir, type, ts, ts);\n\n scaffoldProjectDirs(rootPath);\n\n try {\n ensurePaiMarker(rootPath, slug, displayName);\n } catch {\n // Non-fatal — warn but don't fail the add command.\n }\n\n console.log(ok(`Project added: ${bold(slug)}`));\n console.log(dim(` Path: ${rootPath}`));\n console.log(dim(` Encoded dir: ${encodedDir}`));\n console.log(dim(` Type: ${type}`));\n}\n\nexport function cmdList(\n db: Database,\n opts: { status?: string; tag?: string; type?: string; all?: boolean }\n): void {\n let query = `\n SELECT p.*,\n (SELECT COUNT(*) FROM sessions s WHERE s.project_id = p.id) AS session_count,\n (SELECT MAX(s.created_at) FROM sessions s WHERE s.project_id = p.id) AS last_active\n FROM projects p\n `;\n const params: unknown[] = [];\n const where: string[] = [];\n\n // Default: active only, unless --all or explicit --status given\n if (opts.status) {\n where.push(\"p.status = ?\");\n params.push(opts.status);\n } else if (!opts.all) {\n where.push(\"p.status = 'active'\");\n }\n if (opts.type) {\n where.push(\"p.type = ?\");\n params.push(opts.type);\n }\n if (opts.tag) {\n where.push(`p.id IN (\n SELECT pt.project_id FROM project_tags pt\n JOIN tags t ON t.id = pt.tag_id WHERE t.name = ?\n )`);\n params.push(opts.tag);\n }\n\n if (where.length) query += \" WHERE \" + where.join(\" AND \");\n query += \" ORDER BY p.status ASC, p.updated_at DESC\";\n\n const rows = db.prepare(query).all(...params) as (ProjectRow & {\n session_count: number;\n last_active: number | null;\n })[];\n\n if (!rows.length) {\n console.log(warn(\"No projects found.\"));\n return;\n }\n\n const tableRows = rows.map((r, i) => [\n dim(String(i + 1)),\n bold(r.slug),\n dim(shortenPath(r.root_path, 50)),\n r.status === \"active\" ? chalk.green(r.status) : chalk.yellow(r.status),\n dim(r.type),\n String(r.session_count),\n fmtDate(r.last_active),\n ]);\n\n console.log(\n renderTable(\n [\"#\", \"Slug\", \"Path\", \"Status\", \"Type\", \"Sessions\", \"Last Active\"],\n tableRows\n )\n );\n console.log();\n\n // When showing active-only, inform the user about hidden archived projects\n const totalRow = db.prepare(\"SELECT COUNT(*) AS cnt FROM projects\").get() as { cnt: number };\n const hiddenCount = totalRow.cnt - rows.length;\n if (!opts.all && !opts.status && hiddenCount > 0) {\n console.log(dim(` ${rows.length} active project(s) (${hiddenCount} archived — use --all to show)`));\n } else {\n console.log(dim(` ${rows.length} project(s)`));\n }\n}\n\nexport function cmdInfo(db: Database, identifier: string): void {\n const project =\n resolveIdentifier(db, identifier) ?? requireProject(db, identifier);\n const tags = getProjectTags(db, project.id);\n const aliases = getProjectAliases(db, project.id);\n const sessionCount = getSessionCount(db, project.id);\n const lastSession = getLastSessionDate(db, project.id);\n\n const recentSessions = db\n .prepare(\n `SELECT * FROM sessions WHERE project_id = ?\n ORDER BY created_at DESC LIMIT 5`\n )\n .all(project.id) as SessionRow[];\n\n console.log();\n console.log(header(` ${project.display_name}`));\n console.log();\n console.log(` ${bold(\"Slug:\")} ${project.slug}`);\n console.log(` ${bold(\"Path:\")} ${project.root_path}`);\n console.log(` ${bold(\"Encoded dir:\")} ${project.encoded_dir}`);\n console.log(` ${bold(\"Type:\")} ${project.type}`);\n console.log(\n ` ${bold(\"Status:\")} ${\n project.status === \"active\"\n ? chalk.green(project.status)\n : chalk.yellow(project.status)\n }`\n );\n console.log(\n ` ${bold(\"Tags:\")} ${\n tags.length ? tags.map((t) => chalk.cyan(t)).join(\", \") : dim(\"none\")\n }`\n );\n console.log(\n ` ${bold(\"Aliases:\")} ${aliases.length ? aliases.join(\", \") : dim(\"none\")}`\n );\n console.log(` ${bold(\"Sessions:\")} ${sessionCount}`);\n console.log(` ${bold(\"Last active:\")} ${fmtDate(lastSession)}`);\n console.log(` ${bold(\"Created:\")} ${fmtDate(project.created_at)}`);\n if (project.archived_at) {\n console.log(` ${bold(\"Archived:\")} ${fmtDate(project.archived_at)}`);\n }\n\n if (recentSessions.length) {\n console.log();\n console.log(` ${bold(\"Recent sessions:\")}`);\n const sessionRows = recentSessions.map((s) => [\n dim(`#${s.number}`),\n s.date,\n s.title.length > 50 ? s.title.slice(0, 47) + \"...\" : s.title,\n s.status === \"completed\"\n ? chalk.green(s.status)\n : chalk.yellow(s.status),\n ]);\n console.log(\n renderTable([\"#\", \"Date\", \"Title\", \"Status\"], sessionRows)\n .split(\"\\n\")\n .map((l) => \" \" + l)\n .join(\"\\n\")\n );\n }\n console.log();\n}\n\nexport function cmdArchive(db: Database, slug: string): void {\n const project = requireProject(db, slug);\n if (project.status === \"archived\") {\n console.log(warn(`Project ${slug} is already archived.`));\n return;\n }\n const ts = now();\n db.prepare(\n \"UPDATE projects SET status = 'archived', archived_at = ?, updated_at = ? WHERE id = ?\"\n ).run(ts, ts, project.id);\n console.log(ok(`Archived: ${bold(slug)}`));\n}\n\nexport function cmdUnarchive(db: Database, slug: string): void {\n const project = requireProject(db, slug);\n if (project.status !== \"archived\") {\n console.log(\n warn(`Project ${slug} is not archived (status: ${project.status}).`)\n );\n return;\n }\n const ts = now();\n db.prepare(\n \"UPDATE projects SET status = 'active', archived_at = NULL, updated_at = ? WHERE id = ?\"\n ).run(ts, project.id);\n console.log(ok(`Unarchived: ${bold(slug)}`));\n}\n\nexport function cmdMove(db: Database, slug: string, newPath: string): void {\n const project = requireProject(db, slug);\n const resolvedNew = resolvePath(newPath);\n const newEncoded = encodeDir(resolvedNew);\n const ts = now();\n\n db.prepare(\n \"UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?\"\n ).run(resolvedNew, newEncoded, ts, project.id);\n\n console.log(ok(`Moved: ${bold(slug)}`));\n console.log(dim(` Old path: ${project.root_path}`));\n console.log(dim(` New path: ${resolvedNew}`));\n}\n\nexport function cmdTag(\n db: Database,\n slug: string,\n tags: string[]\n): void {\n const project = requireProject(db, slug);\n const added: string[] = [];\n const skipped: string[] = [];\n\n for (const tagName of tags) {\n const tagId = upsertTag(db, tagName);\n const exists = db\n .prepare(\n \"SELECT 1 FROM project_tags WHERE project_id = ? AND tag_id = ?\"\n )\n .get(project.id, tagId);\n if (exists) {\n skipped.push(tagName);\n } else {\n db.prepare(\n \"INSERT INTO project_tags (project_id, tag_id) VALUES (?, ?)\"\n ).run(project.id, tagId);\n added.push(tagName);\n }\n }\n\n if (added.length) {\n console.log(\n ok(\n `Tagged ${bold(slug)}: ${added.map((t) => chalk.cyan(t)).join(\", \")}`\n )\n );\n }\n if (skipped.length) {\n console.log(dim(` Already present: ${skipped.join(\", \")}`));\n }\n}\n\nexport function cmdAlias(\n db: Database,\n slug: string,\n alias: string\n): void {\n requireProject(db, slug);\n\n const conflict = db\n .prepare(\"SELECT id FROM projects WHERE slug = ?\")\n .get(alias);\n if (conflict) {\n console.error(\n err(`\"${alias}\" is already a project slug — cannot use as alias.`)\n );\n process.exit(1);\n }\n\n const project = getProject(db, slug)!;\n try {\n db.prepare(\n \"INSERT INTO aliases (alias, project_id) VALUES (?, ?)\"\n ).run(alias, project.id);\n console.log(ok(`Alias added: ${bold(alias)} → ${slug}`));\n } catch {\n console.error(err(`Alias \"${alias}\" is already registered.`));\n process.exit(1);\n }\n}\n\nexport function cmdEdit(\n db: Database,\n slug: string,\n opts: { displayName?: string; type?: string }\n): void {\n const project = requireProject(db, slug);\n\n if (!opts.displayName && !opts.type) {\n console.log(warn(\"Nothing to update. Use --display-name or --type.\"));\n return;\n }\n\n const validTypes = [\"local\", \"central\", \"obsidian-linked\", \"external\"];\n if (opts.type && !validTypes.includes(opts.type)) {\n console.error(\n err(`Invalid type \"${opts.type}\". Valid: ${validTypes.join(\", \")}`)\n );\n process.exit(1);\n }\n\n const ts = now();\n if (opts.displayName) {\n db.prepare(\n \"UPDATE projects SET display_name = ?, updated_at = ? WHERE id = ?\"\n ).run(opts.displayName, ts, project.id);\n console.log(ok(`Display name updated: ${bold(opts.displayName)}`));\n }\n if (opts.type) {\n db.prepare(\n \"UPDATE projects SET type = ?, updated_at = ? WHERE id = ?\"\n ).run(opts.type, ts, project.id);\n console.log(ok(`Type updated: ${bold(opts.type)}`));\n }\n}\n\nexport function cmdDetect(\n db: Database,\n pathArg: string | undefined,\n opts: { json?: boolean }\n): void {\n const cwd = pathArg ? resolvePath(pathArg) : process.cwd();\n const detection = detectProject(db, cwd);\n\n if (!detection) {\n if (opts.json) {\n console.log(JSON.stringify({ error: \"no_match\", cwd }, null, 2));\n } else {\n console.log(warn(`No registered project found for: ${cwd}`));\n console.log(dim(\" Run 'pai project add .' to register this directory.\"));\n }\n process.exit(0);\n return;\n }\n\n if (opts.json) {\n console.log(formatDetectionJson(detection));\n return;\n }\n\n console.log();\n console.log(header(\" Project Detection Result\"));\n console.log();\n console.log(\n formatDetection(detection)\n .split(\"\\n\")\n .map((l) => \" \" + l)\n .join(\"\\n\")\n );\n console.log();\n}\n\nexport function cmdConsolidate(\n db: Database,\n identifier: string,\n opts: { yes?: boolean; dryRun?: boolean }\n): void {\n const project =\n resolveIdentifier(db, identifier) ?? requireProject(db, identifier);\n\n console.log();\n console.log(header(` Consolidate: ${project.slug}`));\n console.log(` Target: ${project.root_path}`);\n console.log();\n\n const dirs = findProjectNotesDirs(project);\n\n if (dirs.length === 0) {\n console.log(warn(\" No scattered notes directories found for this project.\"));\n return;\n }\n\n const canonicalNotes = join(project.root_path, \"Notes\");\n const toMerge = dirs.filter((d) => d.notesPath !== canonicalNotes);\n\n if (toMerge.length === 0) {\n console.log(ok(\" All notes are already in the canonical location.\"));\n console.log(dim(` ${canonicalNotes}`));\n return;\n }\n\n console.log(\n ` Found ${toMerge.length} scattered Notes directory(ies) to consolidate:`\n );\n console.log();\n\n for (const d of toMerge) {\n console.log(` ${bold(d.encodedDir)}`);\n console.log(dim(` Notes: ${d.notesPath} (${d.noteCount} file(s))`));\n }\n\n console.log();\n console.log(` Destination: ${canonicalNotes}`);\n console.log();\n\n if (opts.dryRun) {\n console.log(warn(\" Dry run — no changes made. Remove --dry-run to proceed.\"));\n return;\n }\n\n if (!opts.yes) {\n console.log(\n warn(\n \" Run with --yes to perform consolidation, or --dry-run to preview changes.\"\n )\n );\n return;\n }\n\n mkdirSync(canonicalNotes, { recursive: true });\n\n let movedCount = 0;\n for (const d of toMerge) {\n try {\n const files = readdirSync(d.notesPath);\n for (const f of files) {\n if (!f.endsWith(\".md\") && !f.endsWith(\".txt\")) continue;\n const src = join(d.notesPath, f);\n const dest = join(canonicalNotes, f);\n if (!existsSync(dest)) {\n renameSync(src, dest);\n console.log(ok(` Moved: ${f}`));\n movedCount++;\n } else {\n console.log(warn(` Skipped (exists): ${f}`));\n }\n }\n } catch (e) {\n console.error(err(` Error reading ${d.notesPath}: ${e}`));\n }\n }\n\n console.log();\n console.log(ok(` Consolidated ${movedCount} file(s) into ${canonicalNotes}`));\n}\n\n/**\n * Attempt moved-path recovery for a project whose registered root_path is\n * missing. Updates the DB if exactly one candidate is found.\n *\n * Returns the new path on success, or undefined if ambiguous/not found.\n * Prints all messages to stderr (safe for shell-wrapper stdout capture).\n */\nfunction tryRecoverMovedProject(db: Database, project: ProjectRow): string | undefined {\n process.stderr.write(\n warn(`Path not found: ${project.root_path}\\n`) +\n dim(\" Searching for moved location...\\n\")\n );\n\n const result = findMovedPath(project.root_path);\n\n if (result.found) {\n const newPath = result.found;\n const newEncoded = encodeDir(newPath);\n const ts = now();\n db.prepare(\n \"UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?\"\n ).run(newPath, newEncoded, ts, project.id);\n process.stderr.write(\n ok(`Project moved: ${shortenPath(project.root_path, 50)}\\n`) +\n dim(` → ${newPath}\\n`) +\n ok(\"Registry updated.\\n\")\n );\n return newPath;\n }\n\n if (result.ambiguous) {\n process.stderr.write(\n warn(`Multiple directories named \"${basename(project.root_path)}\" found:\\n`)\n );\n for (const candidate of result.ambiguous) {\n process.stderr.write(dim(` ${candidate}\\n`));\n }\n process.stderr.write(\n dim(`\\n Disambiguate with: pai projects rebind ${project.slug} <path>\\n`)\n );\n return undefined;\n }\n\n process.stderr.write(\n err(\n `Project \"${project.slug}\" root_path \"${project.root_path}\" does not exist on disk\\n` +\n ` and no folder named \"${basename(project.root_path)}\" was found in scan dirs.\\n`\n ) +\n dim(` Fix with: pai projects rebind ${project.slug} <new-path>\\n`)\n );\n return undefined;\n}\n\nexport function cmdGo(db: Database, query: string): void {\n const all = db\n .prepare(\n \"SELECT * FROM projects WHERE status = 'active' ORDER BY updated_at DESC\"\n )\n .all() as ProjectRow[];\n\n if (!all.length) {\n console.error(\n err(\"No active projects registered. Run: pai project add <path>\")\n );\n process.exit(1);\n }\n\n const q = query.trim().toLowerCase();\n\n // 1. Exact slug or alias match\n const exact = getProject(db, query);\n if (exact) {\n if (!existsSync(exact.root_path)) {\n const recovered = tryRecoverMovedProject(db, exact);\n if (!recovered) { process.exitCode = 1; return; }\n process.stdout.write(recovered + \"\\n\");\n return;\n }\n process.stdout.write(exact.root_path + \"\\n\");\n return;\n }\n\n // 2. Substring match against slug, display_name, or root_path basename\n const partial = all.filter(\n (p) =>\n containsIgnoreCase(p.slug, q) ||\n containsIgnoreCase(p.display_name, q) ||\n containsIgnoreCase(basename(p.root_path), q)\n );\n\n if (partial.length === 1) {\n const p = partial[0];\n if (!existsSync(p.root_path)) {\n const recovered = tryRecoverMovedProject(db, p);\n if (!recovered) { process.exitCode = 1; return; }\n process.stdout.write(recovered + \"\\n\");\n return;\n }\n process.stdout.write(p.root_path + \"\\n\");\n return;\n }\n\n if (partial.length > 1) {\n console.error(\n err(`Ambiguous: \"${query}\" matches ${partial.length} projects:\\n`)\n );\n partial.forEach((p, i) => {\n console.error(\n ` ${dim(String(i + 1).padStart(2))} ${bold(p.slug.padEnd(30))} ${dim(\n shortenPath(p.root_path, 50)\n )}`\n );\n });\n console.error();\n console.error(dim(\" Use a more specific name or the exact slug.\"));\n process.exit(1);\n }\n\n // 3. No match — Levenshtein suggestions\n const scored = all\n .map((p) => {\n const distSlug = levenshtein(q, p.slug.toLowerCase());\n const distName = levenshtein(q, p.display_name.toLowerCase());\n return { project: p, dist: Math.min(distSlug, distName) };\n })\n .sort((a, b) => a.dist - b.dist);\n\n const threshold = 4;\n const suggestions =\n scored.filter((s) => s.dist <= threshold).length > 0\n ? scored.filter((s) => s.dist <= threshold).slice(0, 3)\n : scored.slice(0, 3);\n\n console.error(err(`Project not found: \"${query}\"\\n`));\n if (suggestions.length) {\n console.error(warn(\" Did you mean?\"));\n for (const s of suggestions) {\n console.error(\n ` ${bold(s.project.slug.padEnd(30))} ${dim(\n shortenPath(s.project.root_path, 50)\n )}`\n );\n }\n console.error();\n console.error(dim(\" Run: pai project list (to see all projects)\"));\n }\n process.exit(1);\n}\n\nexport function cmdRebind(\n db: Database,\n slug: string,\n newPath: string\n): void {\n const project = requireProject(db, slug);\n const resolved = resolve(newPath.startsWith(\"~/\") ? join(homedir(), newPath.slice(2)) : newPath);\n\n if (!existsSync(resolved)) {\n console.error(err(`Path does not exist: ${resolved}`));\n process.exit(1);\n }\n\n let isDir = false;\n try {\n isDir = statSync(resolved).isDirectory();\n } catch {\n // stat failed\n }\n if (!isDir) {\n console.error(err(`Path is not a directory: ${resolved}`));\n process.exit(1);\n }\n\n const newEncoded = encodeDir(resolved);\n\n // Check if another project already owns this path\n const conflict = db\n .prepare(\"SELECT slug FROM projects WHERE encoded_dir = ? AND id != ?\")\n .get(newEncoded, project.id) as { slug: string } | undefined;\n if (conflict) {\n console.error(\n err(`Path is already registered to project: ${bold(conflict.slug)}\\n`) +\n dim(` ${resolved}\\n`) +\n dim(` Archive or move that project first, or choose a different path.`)\n );\n process.exit(1);\n }\n\n const ts = now();\n db.prepare(\n \"UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?\"\n ).run(resolved, newEncoded, ts, project.id);\n\n console.log(ok(`Rebound: ${bold(slug)}`));\n console.log(dim(` Old path: ${project.root_path}`));\n console.log(dim(` New path: ${resolved}`));\n console.log(dim(` Encoded: ${newEncoded}`));\n}\n","/**\n * Session launch configuration for projects.\n * Implements per-project and global config CRUD (pai project config),\n * and the name/unname/names commands for curated project shortlists.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport { ok, warn, err, dim, bold, header, shortenPath, fmtDate, now, renderTable } from \"../../utils.js\";\nimport type { ProjectRow, SessionConfig, ConfigOption } from \"./types.js\";\nimport { resolveIdentifier, requireProject, getProjectAliases } from \"./helpers.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nexport const CONFIG_OPTIONS: ConfigOption[] = [\n { key: 'permission', type: 'string', description: 'Permission preset: full | trusted | default', examples: ['full', 'trusted', 'default'] },\n { key: 'flags', type: 'string', description: 'Raw Claude CLI flags', examples: ['--dangerously-skip-permissions', '--allowedTools Edit,Read'] },\n { key: 'env', type: 'object', description: 'Environment variables as key=value pairs', examples: ['IS_SANDBOX=1', 'CLAUDE_MODEL=opus'] },\n { key: 'autoStart', type: 'boolean', description: 'Auto-start session (skip interactive prompt)', examples: ['true', 'false'] },\n { key: 'prompt', type: 'string', description: 'Initial prompt sent to Claude on launch', examples: ['go', 'continue', 'run tests'] },\n { key: 'model', type: 'string', description: 'Model override for the session', examples: ['opus', 'sonnet', 'haiku'] },\n];\n\nexport const PERMISSION_PRESETS: Record<string, Partial<SessionConfig>> = {\n full: { permission: 'full', flags: '--dangerously-skip-permissions', env: { IS_SANDBOX: '1' }, autoStart: true, prompt: 'go' },\n trusted: { permission: 'trusted', flags: '', env: {}, autoStart: true, prompt: 'go' },\n default: { permission: 'default', flags: '', env: {}, autoStart: false },\n};\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport function getSessionConfig(project: ProjectRow): SessionConfig {\n return project.session_config ? JSON.parse(project.session_config) : {};\n}\n\nexport function getGlobalDefaults(): SessionConfig {\n try {\n const configPath = join(homedir(), '.config', 'pai', 'config.json');\n if (existsSync(configPath)) {\n const config = JSON.parse(readFileSync(configPath, 'utf-8'));\n return config.sessionDefaults ?? {};\n }\n } catch { /* ignore */ }\n return {};\n}\n\nexport function saveGlobalDefaults(defaults: SessionConfig): void {\n const configPath = join(homedir(), '.config', 'pai', 'config.json');\n let config: Record<string, unknown> = {};\n try {\n if (existsSync(configPath)) {\n config = JSON.parse(readFileSync(configPath, 'utf-8'));\n }\n } catch { /* ignore */ }\n config.sessionDefaults = defaults;\n mkdirSync(join(homedir(), '.config', 'pai'), { recursive: true });\n writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n');\n}\n\nfunction applyPreset(config: SessionConfig, preset: string): SessionConfig {\n const presetConfig = PERMISSION_PRESETS[preset];\n if (!presetConfig) {\n return { ...config, permission: 'custom', flags: preset };\n }\n return { ...config, ...presetConfig };\n}\n\nfunction parseConfigValue(key: string, value: string): unknown {\n const option = CONFIG_OPTIONS.find(o => o.key === key);\n if (!option) return value;\n\n switch (option.type) {\n case 'boolean':\n return value === 'true' || value === '1' || value === 'yes';\n case 'object':\n if (key === 'env') {\n const [k, ...vParts] = value.split('=');\n return { [k]: vParts.join('=') || '1' };\n }\n return value;\n default:\n return value;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Commands\n// ---------------------------------------------------------------------------\n\nexport function cmdName(\n db: Database,\n identifier: string,\n shortname: string,\n opts: { permission?: string }\n): void {\n const project = resolveIdentifier(db, identifier) ?? requireProject(db, identifier);\n\n if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(shortname)) {\n console.error(err(`Invalid name \"${shortname}\". Use letters, digits, hyphens, underscores. Must start with a letter.`));\n process.exit(1);\n }\n\n const conflictProject = db\n .prepare(\"SELECT id FROM projects WHERE slug = ? AND id != ?\")\n .get(shortname, project.id) as { id: number } | undefined;\n if (conflictProject) {\n console.error(err(`\"${shortname}\" is already a project slug.`));\n process.exit(1);\n }\n\n const conflictAlias = db\n .prepare(\"SELECT project_id FROM aliases WHERE alias = ?\")\n .get(shortname) as { project_id: number } | undefined;\n if (conflictAlias && conflictAlias.project_id !== project.id) {\n console.error(err(`\"${shortname}\" is already used by another project.`));\n process.exit(1);\n }\n\n if (!conflictAlias) {\n db.prepare(\"INSERT INTO aliases (alias, project_id) VALUES (?, ?)\").run(shortname, project.id);\n }\n\n if (opts.permission) {\n const existing = getSessionConfig(project);\n const config = applyPreset(existing, opts.permission);\n db.prepare(\"UPDATE projects SET session_config = ?, updated_at = ? WHERE id = ?\")\n .run(JSON.stringify(config), now(), project.id);\n }\n\n console.log(ok(`Named: ${bold(shortname)} → ${project.slug} (${shortenPath(project.root_path, 50)})`));\n if (opts.permission) {\n console.log(dim(` Permission: ${opts.permission}`));\n }\n}\n\nexport function cmdUnname(db: Database, shortname: string): void {\n const alias = db\n .prepare(\"SELECT project_id FROM aliases WHERE alias = ?\")\n .get(shortname) as { project_id: number } | undefined;\n\n if (!alias) {\n console.error(err(`No named project found: \"${shortname}\"`));\n process.exit(1);\n }\n\n db.prepare(\"DELETE FROM aliases WHERE alias = ?\").run(shortname);\n\n const remaining = db\n .prepare(\"SELECT COUNT(*) AS cnt FROM aliases WHERE project_id = ?\")\n .get(alias.project_id) as { cnt: number };\n\n console.log(ok(`Removed name: ${bold(shortname)}`));\n if (remaining.cnt === 0) {\n console.log(dim(\" Project has no remaining names.\"));\n }\n}\n\nexport function cmdNames(db: Database, opts: { json?: boolean }): void {\n const rows = db.prepare(`\n SELECT p.*, a.alias AS name,\n (SELECT COUNT(*) FROM sessions s WHERE s.project_id = p.id) AS session_count,\n (SELECT MAX(s.created_at) FROM sessions s WHERE s.project_id = p.id) AS last_active\n FROM projects p\n JOIN aliases a ON a.project_id = p.id\n WHERE p.status = 'active'\n ORDER BY p.updated_at DESC\n `).all() as (ProjectRow & { name: string; session_count: number; last_active: number | null; session_config: string | null })[];\n\n if (opts.json) {\n const grouped = new Map<number, unknown>();\n for (const row of rows) {\n if (!grouped.has(row.id)) {\n grouped.set(row.id, {\n name: row.name,\n names: [row.name],\n slug: row.slug,\n display_name: row.display_name,\n root_path: row.root_path,\n session_count: row.session_count,\n last_active: row.last_active ? new Date(row.last_active).toISOString() : null,\n session_config: row.session_config ? JSON.parse(row.session_config) : null,\n });\n } else {\n (grouped.get(row.id) as { names: string[] }).names.push(row.name);\n }\n }\n console.log(JSON.stringify([...grouped.values()], null, 2));\n return;\n }\n\n if (!rows.length) {\n console.log(warn(\"No named projects. Use: pai project name <slug-or-number> <shortname>\"));\n return;\n }\n\n const seen = new Set<number>();\n const tableRows: string[][] = [];\n for (const row of rows) {\n if (seen.has(row.id)) continue;\n seen.add(row.id);\n\n const allNames = rows.filter(r => r.id === row.id).map(r => r.name);\n const config = row.session_config ? JSON.parse(row.session_config) as SessionConfig : null;\n const perm = config?.permission ?? dim('default');\n\n tableRows.push([\n bold(allNames.join(', ')),\n row.slug,\n dim(shortenPath(row.root_path, 40)),\n typeof perm === 'string' ? perm : dim('default'),\n String(row.session_count),\n fmtDate(row.last_active),\n ]);\n }\n\n console.log();\n console.log(header(\" Named Projects\"));\n console.log();\n console.log(renderTable([\"Name(s)\", \"Slug\", \"Path\", \"Permission\", \"Sessions\", \"Last Active\"], tableRows));\n console.log();\n console.log(dim(` ${tableRows.length} named project(s)`));\n}\n\nexport function cmdConfig(\n db: Database,\n identifier: string | undefined,\n opts: {\n set?: string[];\n unset?: string[];\n preset?: string;\n defaults?: boolean;\n options?: boolean;\n json?: boolean;\n reset?: boolean;\n }\n): void {\n // Discovery: list available options\n if (opts.options) {\n if (opts.json) {\n console.log(JSON.stringify({\n options: CONFIG_OPTIONS,\n presets: Object.entries(PERMISSION_PRESETS).map(([name, config]) => ({ name, ...config })),\n }, null, 2));\n return;\n }\n\n console.log();\n console.log(header(\" Session Config Options\"));\n console.log();\n console.log(bold(\" Available keys:\"));\n console.log();\n for (const opt of CONFIG_OPTIONS) {\n console.log(` ${bold(opt.key.padEnd(14))} ${dim(`(${opt.type})`)} ${opt.description}`);\n console.log(` ${' '.repeat(14)} ${dim('e.g.')} ${opt.examples.map(e => chalk.cyan(e)).join(', ')}`);\n }\n console.log();\n console.log(bold(\" Permission presets:\"));\n console.log();\n for (const [name, config] of Object.entries(PERMISSION_PRESETS)) {\n const parts = [];\n if (config.flags) parts.push(`flags: ${config.flags}`);\n if (config.env && Object.keys(config.env).length) parts.push(`env: ${Object.entries(config.env).map(([k, v]) => `${k}=${v}`).join(' ')}`);\n if (config.autoStart) parts.push('autoStart');\n if (config.prompt) parts.push(`prompt: \"${config.prompt}\"`);\n console.log(` ${bold(name.padEnd(10))} ${dim(parts.join(', ') || '(vanilla)')}`);\n }\n console.log();\n return;\n }\n\n // Global defaults mode\n if (opts.defaults) {\n let defaults = getGlobalDefaults();\n\n if (opts.reset) {\n saveGlobalDefaults({});\n console.log(ok(\"Global session defaults reset.\"));\n return;\n }\n\n if (opts.preset) {\n defaults = applyPreset(defaults, opts.preset);\n saveGlobalDefaults(defaults);\n console.log(ok(`Global defaults set to preset: ${bold(opts.preset)}`));\n return;\n }\n\n if (opts.set?.length) {\n for (const pair of opts.set) {\n const eqIdx = pair.indexOf('=');\n if (eqIdx === -1) { console.error(err(`Invalid format: \"${pair}\". Use key=value.`)); continue; }\n const key = pair.substring(0, eqIdx);\n const value = pair.substring(eqIdx + 1);\n if (!CONFIG_OPTIONS.find(o => o.key === key)) { console.error(err(`Unknown key: \"${key}\". Run: pai project config --options`)); continue; }\n if (key === 'env') {\n defaults.env = { ...(defaults.env ?? {}), ...(parseConfigValue('env', value) as Record<string, string>) };\n } else {\n (defaults as Record<string, unknown>)[key] = parseConfigValue(key, value);\n }\n }\n saveGlobalDefaults(defaults);\n console.log(ok(\"Global defaults updated.\"));\n }\n\n if (opts.unset?.length) {\n for (const key of opts.unset) {\n if (key.startsWith('env.')) {\n const envKey = key.substring(4);\n if (defaults.env) delete defaults.env[envKey];\n } else {\n delete (defaults as Record<string, unknown>)[key];\n }\n }\n saveGlobalDefaults(defaults);\n console.log(ok(\"Global defaults updated.\"));\n }\n\n if (opts.json) {\n console.log(JSON.stringify(defaults, null, 2));\n } else {\n console.log();\n console.log(header(\" Global Session Defaults\"));\n console.log();\n if (Object.keys(defaults).length === 0) {\n console.log(dim(\" No defaults set. New sessions use vanilla Claude.\"));\n console.log(dim(\" Set with: pai project config --defaults --preset full\"));\n } else {\n for (const [key, value] of Object.entries(defaults)) {\n const display = typeof value === 'object' ? JSON.stringify(value) : String(value);\n console.log(` ${bold(key.padEnd(14))} ${display}`);\n }\n }\n console.log();\n }\n return;\n }\n\n // Per-project config\n if (!identifier) {\n console.error(err(\"Specify a project: pai project config <name-or-slug>\"));\n console.error(dim(\" Or use --defaults for global defaults, --options for available keys.\"));\n process.exit(1);\n }\n\n const project = resolveIdentifier(db, identifier) ?? requireProject(db, identifier);\n let config = getSessionConfig(project);\n\n if (opts.reset) {\n db.prepare(\"UPDATE projects SET session_config = NULL, updated_at = ? WHERE id = ?\").run(now(), project.id);\n console.log(ok(`Config reset for ${bold(project.slug)}. Will use global defaults.`));\n return;\n }\n\n if (opts.preset) {\n config = applyPreset(config, opts.preset);\n db.prepare(\"UPDATE projects SET session_config = ?, updated_at = ? WHERE id = ?\")\n .run(JSON.stringify(config), now(), project.id);\n console.log(ok(`Applied preset ${bold(opts.preset)} to ${bold(project.slug)}`));\n }\n\n if (opts.set?.length) {\n for (const pair of opts.set) {\n const eqIdx = pair.indexOf('=');\n if (eqIdx === -1) { console.error(err(`Invalid format: \"${pair}\". Use key=value.`)); continue; }\n const key = pair.substring(0, eqIdx);\n const value = pair.substring(eqIdx + 1);\n if (!CONFIG_OPTIONS.find(o => o.key === key)) { console.error(err(`Unknown key: \"${key}\". Run: pai project config --options`)); continue; }\n if (key === 'env') {\n config.env = { ...(config.env ?? {}), ...(parseConfigValue('env', value) as Record<string, string>) };\n } else {\n (config as Record<string, unknown>)[key] = parseConfigValue(key, value);\n }\n }\n db.prepare(\"UPDATE projects SET session_config = ?, updated_at = ? WHERE id = ?\")\n .run(JSON.stringify(config), now(), project.id);\n console.log(ok(`Config updated for ${bold(project.slug)}`));\n }\n\n if (opts.unset?.length) {\n for (const key of opts.unset) {\n if (key.startsWith('env.')) {\n const envKey = key.substring(4);\n if (config.env) delete config.env[envKey];\n } else {\n delete (config as Record<string, unknown>)[key];\n }\n }\n db.prepare(\"UPDATE projects SET session_config = ?, updated_at = ? WHERE id = ?\")\n .run(JSON.stringify(config), now(), project.id);\n console.log(ok(`Config updated for ${bold(project.slug)}`));\n }\n\n const effective = { ...getGlobalDefaults(), ...config };\n const aliases = getProjectAliases(db, project.id);\n\n if (opts.json) {\n console.log(JSON.stringify({\n project: project.slug,\n names: aliases,\n root_path: project.root_path,\n config,\n global_defaults: getGlobalDefaults(),\n effective,\n }, null, 2));\n return;\n }\n\n console.log();\n console.log(header(` Config: ${project.slug}`));\n if (aliases.length) console.log(dim(` Names: ${aliases.join(', ')}`));\n console.log(dim(` Path: ${project.root_path}`));\n console.log();\n\n if (Object.keys(config).length === 0) {\n console.log(dim(\" No project-specific config. Using global defaults.\"));\n } else {\n console.log(bold(\" Project config:\"));\n for (const [key, value] of Object.entries(config)) {\n const display = typeof value === 'object' ? JSON.stringify(value) : String(value);\n console.log(` ${bold(key.padEnd(14))} ${display}`);\n }\n }\n\n const globalDefaults = getGlobalDefaults();\n if (Object.keys(globalDefaults).length > 0) {\n console.log();\n console.log(dim(\" Global defaults (overridden by project config):\"));\n for (const [key, value] of Object.entries(globalDefaults)) {\n const overridden = key in config;\n const display = typeof value === 'object' ? JSON.stringify(value) : String(value);\n console.log(` ${dim(key.padEnd(14))} ${overridden ? chalk.strikethrough(display) + ' ' + dim('(overridden)') : display}`);\n }\n }\n\n console.log();\n console.log(bold(\" Effective (what AIBroker uses):\"));\n for (const [key, value] of Object.entries(effective)) {\n const display = typeof value === 'object' ? JSON.stringify(value) : String(value);\n console.log(` ${bold(key.padEnd(14))} ${display}`);\n }\n console.log();\n}\n","/**\n * Project health check command — audits registered projects for missing paths,\n * moved directories, and orphaned note directories.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport { existsSync, readdirSync, statSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport { ok, warn, err, dim, bold, header, shortenPath, now, renderTable, encodeDir } from \"../../utils.js\";\nimport type { HealthRow, HealthCategory, ProjectHealth, ProjectRow } from \"./types.js\";\n\nfunction findOrphanedNotesDirs(project: ProjectRow): string[] {\n const claudeProjects = join(homedir(), \".claude\", \"projects\");\n if (!existsSync(claudeProjects)) return [];\n\n const expected = encodeDir(project.root_path);\n const results: string[] = [];\n\n try {\n for (const entry of readdirSync(claudeProjects)) {\n const full = join(claudeProjects, entry);\n try {\n if (!statSync(full).isDirectory()) continue;\n } catch {\n continue;\n }\n if (entry === expected || entry === project.encoded_dir) {\n const notesDir = join(full, \"Notes\");\n if (existsSync(notesDir)) {\n results.push(notesDir);\n }\n }\n }\n } catch {\n // Unreadable — ignore\n }\n return results;\n}\n\nfunction suggestMovedPath(project: ProjectRow): string | undefined {\n const name = basename(project.root_path);\n const candidates = [\n join(homedir(), \"dev\", name),\n join(homedir(), \"dev\", \"ai\", name),\n join(homedir(), \"Desktop\", name),\n join(homedir(), \"Projects\", name),\n ];\n for (const candidate of candidates) {\n if (existsSync(candidate)) return candidate;\n }\n return undefined;\n}\n\nexport function cmdHealth(\n db: Database,\n opts: { fix?: boolean; json?: boolean; status?: string }\n): void {\n const rows = db\n .prepare(\n `SELECT p.*,\n (SELECT COUNT(*) FROM sessions s WHERE s.project_id = p.id) AS session_count\n FROM projects p\n ORDER BY p.status ASC, p.updated_at DESC`\n )\n .all() as HealthRow[];\n\n const results: ProjectHealth[] = rows.map((project) => {\n const pathExists = existsSync(project.root_path);\n const orphaned = findOrphanedNotesDirs(project);\n\n let category: HealthCategory;\n let suggestedPath: string | undefined;\n\n if (pathExists) {\n category = \"active\";\n } else {\n suggestedPath = suggestMovedPath(project);\n category = suggestedPath ? \"stale\" : \"dead\";\n }\n\n return {\n project,\n category,\n suggestedPath,\n claudeNotesExists: orphaned.length > 0,\n orphanedNotesDirs: orphaned,\n };\n });\n\n const filtered = opts.status ? results.filter((r) => r.category === opts.status) : results;\n\n if (opts.json) {\n console.log(JSON.stringify(\n filtered.map((r) => ({\n slug: r.project.slug,\n root_path: r.project.root_path,\n status: r.project.status,\n health: r.category,\n session_count: r.project.session_count,\n suggested_path: r.suggestedPath ?? null,\n claude_notes_exists: r.claudeNotesExists,\n orphaned_notes_dirs: r.orphanedNotesDirs,\n })),\n null,\n 2\n ));\n return;\n }\n\n const active = filtered.filter((r) => r.category === \"active\");\n const stale = filtered.filter((r) => r.category === \"stale\");\n const dead = filtered.filter((r) => r.category === \"dead\");\n\n console.log();\n console.log(header(\" PAI Project Health Report\"));\n console.log();\n console.log(\n ` ${chalk.green(\"Active:\")} ${active.length} ${chalk.yellow(\"Stale (moved?):\")} ${stale.length} ${chalk.red(\"Dead (missing):\")} ${dead.length}`\n );\n console.log();\n\n if (active.length) {\n console.log(bold(\" Active projects (path exists):\"));\n const tableRows = active.map((r) => [\n bold(r.project.slug),\n dim(shortenPath(r.project.root_path, 50)),\n String(r.project.session_count),\n r.claudeNotesExists ? chalk.green(\"yes\") : dim(\"no\"),\n ]);\n console.log(\n renderTable([\"Slug\", \"Path\", \"Sessions\", \"Claude Notes\"], tableRows)\n .split(\"\\n\").map((l) => \" \" + l).join(\"\\n\")\n );\n console.log();\n }\n\n if (stale.length) {\n console.log(warn(\" Stale projects (path missing, possible new location found):\"));\n for (const r of stale) {\n console.log(` ${bold(r.project.slug)}`);\n console.log(dim(` Old path: ${r.project.root_path}`));\n console.log(chalk.cyan(` Found at: ${r.suggestedPath}`));\n if (r.claudeNotesExists) {\n console.log(chalk.green(` Notes: ${r.orphanedNotesDirs.join(\", \")}`));\n }\n if (opts.fix && r.suggestedPath) {\n const ts = now();\n const newEncoded = encodeDir(r.suggestedPath);\n db.prepare(\"UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?\")\n .run(r.suggestedPath, newEncoded, ts, r.project.id);\n console.log(ok(` Auto-fixed: updated path to ${r.suggestedPath}`));\n } else if (r.suggestedPath) {\n console.log(dim(` Fix: pai project move ${r.project.slug} ${r.suggestedPath}`));\n }\n }\n console.log();\n }\n\n if (dead.length) {\n console.log(err(\" Dead projects (path missing, no match found):\"));\n for (const r of dead) {\n console.log(` ${bold(r.project.slug)} ${dim(r.project.root_path)}`);\n if (r.claudeNotesExists) {\n console.log(chalk.yellow(` Notes: ${r.orphanedNotesDirs.join(\", \")}`));\n }\n if (r.project.session_count === 0 && opts.fix) {\n db.prepare(\"UPDATE projects SET status = 'archived', archived_at = ?, updated_at = ? WHERE id = ?\")\n .run(now(), now(), r.project.id);\n console.log(ok(\" Auto-fixed: archived (0 sessions, path gone)\"));\n } else {\n console.log(dim(` Fix: pai project archive ${r.project.slug} (or pai project move ...)`));\n }\n }\n console.log();\n }\n\n console.log(dim(` ${rows.length} total: ${active.length} active, ${stale.length} stale, ${dead.length} dead`));\n\n if (!opts.fix && (stale.length > 0 || dead.length > 0)) {\n console.log();\n console.log(warn(\" Run with --fix to auto-remediate where possible.\"));\n }\n}\n","/**\n * Commander registration for all `pai projects` sub-commands (plural namespace).\n *\n * This is the canonical namespace. `pai project <subcmd>` (singular) is a\n * deprecated alias that forwards here with a stderr notice.\n */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport { cmdPromote } from \"../../../session/promote.js\";\nimport {\n cmdAdd,\n cmdList,\n cmdInfo,\n cmdArchive,\n cmdUnarchive,\n cmdMove,\n cmdTag,\n cmdAlias,\n cmdEdit,\n cmdDetect,\n cmdConsolidate,\n cmdGo,\n cmdRebind,\n findMovedPath,\n} from \"./commands.js\";\nimport { existsSync } from \"node:fs\";\nimport { ok, warn, err, dim, bold, shortenPath, encodeDir, now } from \"../../utils.js\";\nimport { basename } from \"node:path\";\nimport { cmdName, cmdUnname, cmdNames, cmdConfig } from \"./session-config.js\";\nimport { cmdHealth } from \"./health.js\";\nimport { resolveIdentifier } from \"./helpers.js\";\n\nexport { cmdGo };\n\nexport function registerProjectsCommands(\n projectsCmd: Command,\n getDb: () => Database\n): void {\n // pai projects list — default when `pai projects` is invoked bare\n projectsCmd\n .command(\"list\", { isDefault: true })\n .description(\n \"List registered projects. Short form: pai projects (bare, no subcommand)\\n\" +\n \"Default: active projects only. Use --all to include archived.\"\n )\n .option(\"--all\", \"Include archived projects (default: active only)\")\n .option(\"--status <status>\", \"Filter by status: active | archived\")\n .option(\"--tag <tag>\", \"Filter by tag\")\n .option(\"--type <type>\", \"Filter by type\")\n .action((opts: { all?: boolean; status?: string; tag?: string; type?: string }) => {\n cmdList(getDb(), opts);\n });\n\n // pai projects cd <identifier>\n projectsCmd\n .command(\"cd <identifier>\")\n .description(\n \"cd to a project directory. Short form: pai cd <name>\\n\" +\n \"(The shell wrapper handles the actual cd; pure output here.)\\n\" +\n \"Auto-detects moved projects when the registered path no longer exists.\"\n )\n .action((identifier: string) => {\n const db = getDb();\n const project = resolveIdentifier(db, identifier);\n if (!project) {\n console.error(`Project not found: ${identifier}`);\n process.exit(1);\n return;\n }\n\n if (existsSync(project.root_path)) {\n process.stdout.write(project.root_path + \"\\n\");\n return;\n }\n\n // Path missing — search for moved location\n process.stderr.write(\n warn(`Path not found: ${project.root_path}\\n`) +\n dim(\" Searching for moved location...\\n\")\n );\n\n const result = findMovedPath(project.root_path);\n\n if (result.found) {\n const newPath = result.found;\n const newEncoded = encodeDir(newPath);\n const ts = now();\n db.prepare(\n \"UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?\"\n ).run(newPath, newEncoded, ts, project.id);\n process.stderr.write(\n ok(`Project moved: ${shortenPath(project.root_path, 50)}\\n`) +\n dim(` → ${newPath}\\n`) +\n ok(\"Registry updated.\\n\")\n );\n process.stdout.write(newPath + \"\\n\");\n return;\n }\n\n if (result.ambiguous) {\n process.stderr.write(\n warn(`Multiple directories named \"${basename(project.root_path)}\" found:\\n`)\n );\n for (const candidate of result.ambiguous) {\n process.stderr.write(dim(` ${candidate}\\n`));\n }\n process.stderr.write(\n dim(`\\n Disambiguate with: pai projects rebind ${project.slug} <path>\\n`)\n );\n process.exitCode = 1;\n return;\n }\n\n process.stderr.write(\n err(\n `Project \"${project.slug}\" root_path \"${project.root_path}\" does not exist on disk\\n` +\n ` and no folder named \"${basename(project.root_path)}\" was found in scan dirs.\\n`\n ) +\n dim(` Fix with: pai projects rebind ${project.slug} <new-path>\\n`)\n );\n process.exitCode = 1;\n });\n\n // pai projects add <path>\n projectsCmd\n .command(\"add <path>\")\n .description(\"Register a project directory in the PAI registry\")\n .option(\"--slug <slug>\", \"Override auto-generated slug\")\n .option(\"--type <type>\", \"Project type: local | central | obsidian-linked | external\", \"local\")\n .option(\"--display-name <name>\", \"Human-readable display name\")\n .action(\n (\n rawPath: string,\n opts: { slug?: string; type?: string; displayName?: string }\n ) => {\n cmdAdd(getDb(), rawPath, opts);\n }\n );\n\n // pai projects info <slug>\n projectsCmd\n .command(\"info <slug>\")\n .description(\"Show full details for a project\")\n .action((slug: string) => {\n cmdInfo(getDb(), slug);\n });\n\n // pai projects archive <slug>\n projectsCmd\n .command(\"archive <slug>\")\n .description(\"Archive a project\")\n .action((slug: string) => {\n cmdArchive(getDb(), slug);\n });\n\n // pai projects unarchive <slug>\n projectsCmd\n .command(\"unarchive <slug>\")\n .description(\"Restore an archived project to active status\")\n .action((slug: string) => {\n cmdUnarchive(getDb(), slug);\n });\n\n // pai projects move <slug> <new-path>\n projectsCmd\n .command(\"move <slug> <new-path>\")\n .description(\"Update the root path for a project\")\n .action((slug: string, newPath: string) => {\n cmdMove(getDb(), slug, newPath);\n });\n\n // pai projects rebind <slug> <new-path>\n projectsCmd\n .command(\"rebind <slug> <new-path>\")\n .description(\n \"Manually update the root_path for a project (for when auto-detect found multiple matches).\\n\" +\n \"Validates the new path exists and is a directory, then updates the registry.\"\n )\n .action((slug: string, newPath: string) => {\n cmdRebind(getDb(), slug, newPath);\n });\n\n // pai projects tag <slug> <tags...>\n projectsCmd\n .command(\"tag <slug> <tags...>\")\n .description(\"Add one or more tags to a project\")\n .action((slug: string, tags: string[]) => {\n cmdTag(getDb(), slug, tags);\n });\n\n // pai projects alias <slug> <alias>\n projectsCmd\n .command(\"alias <slug> <alias>\")\n .description(\"Register an alternative slug for a project\")\n .action((slug: string, alias: string) => {\n cmdAlias(getDb(), slug, alias);\n });\n\n // pai projects edit <slug>\n projectsCmd\n .command(\"edit <slug>\")\n .description(\"Edit project metadata\")\n .option(\"--display-name <name>\", \"New display name\")\n .option(\"--type <type>\", \"New type\")\n .action(\n (slug: string, opts: { displayName?: string; type?: string }) => {\n cmdEdit(getDb(), slug, opts);\n }\n );\n\n // pai projects detect [path]\n projectsCmd\n .command(\"detect [path]\")\n .description(\"Detect which registered project the given path (or CWD) belongs to\")\n .option(\"--json\", \"Output raw JSON instead of human-readable text\")\n .action((pathArg: string | undefined, opts: { json?: boolean }) => {\n cmdDetect(getDb(), pathArg, opts);\n });\n\n // pai projects health\n projectsCmd\n .command(\"health\")\n .description(\n \"Audit all registered projects: check which paths still exist, find moved/dead projects\"\n )\n .option(\"--fix\", \"Auto-remediate where possible\")\n .option(\"--json\", \"Output raw JSON report\")\n .option(\"--status <category>\", \"Filter output to: active | stale | dead\")\n .action((opts: { fix?: boolean; json?: boolean; status?: string }) => {\n cmdHealth(getDb(), opts);\n });\n\n // pai projects consolidate <identifier>\n projectsCmd\n .command(\"consolidate <identifier>\")\n .description(\n \"Consolidate scattered ~/.claude/projects/.../Notes/ directories for a project into its canonical Notes/ location\"\n )\n .option(\"--yes\", \"Perform consolidation without confirmation prompt\")\n .option(\"--dry-run\", \"Preview what would be moved without making changes\")\n .action(\n (identifier: string, opts: { yes?: boolean; dryRun?: boolean }) => {\n cmdConsolidate(getDb(), identifier, opts);\n }\n );\n\n // pai projects promote\n projectsCmd\n .command(\"promote\")\n .description(\"Promote a session note into a new standalone project\")\n .requiredOption(\"--from-session <path>\", \"Path to the session note markdown file\")\n .requiredOption(\"--to <path>\", \"Directory path for the new project (must not exist)\")\n .option(\"--name <name>\", \"Display name for the new project (derived from filename if omitted)\")\n .action((opts: { fromSession: string; to: string; name?: string }) => {\n cmdPromote(getDb(), opts);\n });\n\n // pai projects go <query>\n projectsCmd\n .command(\"go <query>\")\n .description(\n \"Print the root path for a project by slug, partial name, or fuzzy match.\\n\" +\n \"Designed for shell integration: cd $(pai projects go <query>)\"\n )\n .action((query: string) => {\n cmdGo(getDb(), query);\n });\n\n // pai projects name <identifier> <shortname>\n projectsCmd\n .command(\"name <identifier> <shortname>\")\n .description(\"Give a project a short name for quick access\")\n .option(\"--permission <level>\", \"Permission level: full | trusted | default\")\n .action(\n (\n identifier: string,\n shortname: string,\n opts: { permission?: string }\n ) => {\n cmdName(getDb(), identifier, shortname, opts);\n }\n );\n\n // pai projects unname <shortname>\n projectsCmd\n .command(\"unname <shortname>\")\n .description(\"Remove a project's short name\")\n .action((shortname: string) => {\n cmdUnname(getDb(), shortname);\n });\n\n // pai projects names\n projectsCmd\n .command(\"names\")\n .description(\"List named projects (your curated shortlist)\")\n .option(\"--json\", \"Output JSON for AIBroker consumption\")\n .action((opts: { json?: boolean }) => {\n cmdNames(getDb(), opts);\n });\n\n // pai projects config [identifier]\n projectsCmd\n .command(\"config [identifier]\")\n .description(\n \"View or modify session launch config for a project.\\n\" +\n \"Use --options to discover available keys and presets.\"\n )\n .option(\n \"--set <key=value...>\",\n \"Set config values (repeatable)\",\n (v: string, prev: string[]) => [...prev, v],\n [] as string[]\n )\n .option(\n \"--unset <key...>\",\n \"Remove config keys (repeatable, use env.KEY for env vars)\",\n (v: string, prev: string[]) => [...prev, v],\n [] as string[]\n )\n .option(\"--preset <name>\", \"Apply a permission preset: full | trusted | default\")\n .option(\"--defaults\", \"Manage global session defaults instead of a project\")\n .option(\"--options\", \"List available config keys and presets\")\n .option(\"--json\", \"Output JSON\")\n .option(\"--reset\", \"Reset config to empty (inherit global defaults)\")\n .action(\n (\n identifier: string | undefined,\n opts: {\n set?: string[];\n unset?: string[];\n preset?: string;\n defaults?: boolean;\n options?: boolean;\n json?: boolean;\n reset?: boolean;\n }\n ) => {\n cmdConfig(getDb(), identifier, opts);\n }\n );\n}\n","/**\n * Slug generator for PAI session notes.\n *\n * Extracts a 1-3 word descriptive slug from Claude Code JSONL transcripts\n * using keyword frequency analysis — no LLM required.\n *\n * JSONL format (one JSON object per line):\n * - type \"user\": { type: \"user\", message: { role: \"user\", content: string | [{ type, text }] } }\n * - type \"assistant\": { type: \"assistant\", message: { role: \"assistant\", content: [{ type, text }] } }\n */\n\nimport { readFileSync, existsSync, readdirSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { STOP_WORDS } from \"../utils/stop-words.js\";\n\n// ---------------------------------------------------------------------------\n// Public API types\n// ---------------------------------------------------------------------------\n\nexport interface SlugOptions {\n /** Maximum number of words in the generated slug (default: 3) */\n maxWords?: number;\n /** Maximum character length of the generated slug (default: 30) */\n maxLength?: number;\n}\n\n// STOP_WORDS imported from utils/stop-words.ts\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Extract plain text from a parsed JSONL message object.\n */\nfunction extractText(obj: Record<string, unknown>): string | null {\n const msg = obj.message as Record<string, unknown> | undefined;\n if (!msg) return null;\n\n const content = msg.content;\n\n // Array of content blocks (modern assistant messages)\n if (Array.isArray(content)) {\n const texts: string[] = [];\n for (const block of content) {\n const b = block as Record<string, unknown>;\n if (b.type === \"text\" && typeof b.text === \"string\") {\n texts.push(b.text);\n }\n }\n return texts.join(\" \") || null;\n }\n\n // Plain string content (user messages)\n if (typeof content === \"string\") {\n return content || null;\n }\n\n return null;\n}\n\n/**\n * Tokenize a string into lowercase words, filtering stop words and short tokens.\n */\nfunction tokenize(text: string): string[] {\n return text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \" \")\n .split(/\\s+/)\n .filter((w) => {\n if (w.length < 3) return false;\n if (/^\\d+$/.test(w)) return false;\n if (STOP_WORDS.has(w)) return false;\n return true;\n });\n}\n\n// ---------------------------------------------------------------------------\n// Public functions\n// ---------------------------------------------------------------------------\n\n/**\n * Find the latest JSONL transcript for a project's Claude encoded directory.\n *\n * @param encodedDir Claude-encoded project directory name,\n * e.g. \"-Users-alice-dev-ai-PAI\"\n * @returns Absolute path to the most recently modified .jsonl file, or null.\n */\nexport function findLatestTranscript(encodedDir: string): string | null {\n const sessionsDir = join(\n homedir(),\n \".claude\",\n \"projects\",\n encodedDir,\n \"sessions\"\n );\n\n if (!existsSync(sessionsDir)) return null;\n\n let entries: string[];\n try {\n entries = readdirSync(sessionsDir).filter((f) => f.endsWith(\".jsonl\"));\n } catch {\n return null;\n }\n\n if (!entries.length) return null;\n\n // Sort by mtime descending, return the newest\n const sorted = entries\n .map((f) => {\n const fullPath = join(sessionsDir, f);\n try {\n return { path: fullPath, mtime: statSync(fullPath).mtimeMs };\n } catch {\n return { path: fullPath, mtime: 0 };\n }\n })\n .sort((a, b) => b.mtime - a.mtime);\n\n return sorted[0].path;\n}\n\n/**\n * Read the last `count` human/assistant message pairs from a JSONL file.\n *\n * Synchronous — reads the whole file in memory. JSONL transcripts are\n * bounded by context window size so this is safe even for large sessions.\n *\n * @param jsonlPath Absolute path to a Claude Code .jsonl transcript file.\n * @param count Number of conversation pairs to read (default: 15).\n * @returns Array of plain-text message strings (up to count*2 entries).\n */\nexport function readLastMessages(\n jsonlPath: string,\n count: number = 15\n): string[] {\n if (!existsSync(jsonlPath)) return [];\n\n let raw: string;\n try {\n raw = readFileSync(jsonlPath, \"utf8\");\n } catch {\n return [];\n }\n\n const lines = raw.split(\"\\n\").filter((l) => l.trim().length > 0);\n const messages: string[] = [];\n const limit = count * 2;\n\n // Walk from the end to collect the most recent messages first\n for (let i = lines.length - 1; i >= 0 && messages.length < limit; i--) {\n let obj: Record<string, unknown>;\n try {\n obj = JSON.parse(lines[i]) as Record<string, unknown>;\n } catch {\n continue;\n }\n\n const type = obj.type as string | undefined;\n if (type !== \"assistant\" && type !== \"user\") continue;\n\n const text = extractText(obj);\n if (text && text.trim().length > 0) {\n // Prepend to maintain chronological order\n messages.unshift(text);\n }\n }\n\n return messages;\n}\n\n/**\n * Generate a descriptive 1-3 word slug from conversation message strings.\n *\n * Algorithm:\n * 1. Tokenize all messages, remove stop words and short tokens\n * 2. Count word frequency\n * 3. Pick the top `maxWords` most frequent tokens\n * 4. Join with hyphen, lowercase\n *\n * @param messages Array of plain-text conversation messages.\n * @param opts Optional slug configuration.\n * @returns A lowercase hyphen-joined slug, e.g. \"memory-engine-refactor\",\n * or \"unnamed-session\" if no meaningful words are found.\n */\nexport function generateSlug(messages: string[], opts?: SlugOptions): string {\n const maxWords = opts?.maxWords ?? 3;\n const maxLength = opts?.maxLength ?? 30;\n\n if (!messages.length) return \"unnamed-session\";\n\n // Count word frequencies across all messages\n const freq = new Map<string, number>();\n for (const msg of messages) {\n for (const token of tokenize(msg)) {\n freq.set(token, (freq.get(token) ?? 0) + 1);\n }\n }\n\n if (!freq.size) return \"unnamed-session\";\n\n // Sort by frequency descending, take top maxWords\n const topWords = Array.from(freq.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, maxWords)\n .map(([word]) => word);\n\n if (!topWords.length) return \"unnamed-session\";\n\n // Join and trim to maxLength (truncate at last hyphen boundary)\n let slug = topWords.join(\"-\");\n if (slug.length > maxLength) {\n slug = slug.slice(0, maxLength).replace(/-[^-]*$/, \"\");\n }\n\n return slug || \"unnamed-session\";\n}\n","/**\n * Shared DB helpers, formatting, and path utilities for session sub-commands.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport { err } from \"../../utils.js\";\nimport type { SessionRow, ProjectRow } from \"./types.js\";\n\nexport function getProject(db: Database, slug: string): ProjectRow | undefined {\n return db\n .prepare(\n \"SELECT id, slug, display_name, root_path, encoded_dir FROM projects WHERE slug = ?\"\n )\n .get(slug) as ProjectRow | undefined;\n}\n\nexport function statusColor(status: string): string {\n switch (status) {\n case \"completed\":\n return chalk.green(status);\n case \"compacted\":\n return chalk.blue(status);\n default:\n return chalk.yellow(status);\n }\n}\n\n/** Convert a slug to title-cased display name: \"memory-engine\" → \"Memory Engine\" */\nexport function toTitleCase(slug: string): string {\n return slug\n .replace(/-/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\n/** Notes directory for a project: ~/.claude/projects/<encoded_dir>/Notes/ */\nexport function getNotesDir(project: ProjectRow): string {\n return join(homedir(), \".claude\", \"projects\", project.encoded_dir, \"Notes\");\n}\n\n/** Format a session filename: number=27, date=\"2026-02-23\" → \"0027 - 2026-02-23 - Title.md\" */\nexport function formatFilename(\n number: number,\n date: string,\n titleSlug: string\n): string {\n const n = String(number).padStart(4, \"0\");\n return `${n} - ${date} - ${titleSlug}.md`;\n}\n\n/** Resolve a session by project + number or \"latest\". Exits on failure. */\nexport function resolveSession(\n db: Database,\n project: ProjectRow,\n numberOrLatest: string\n): SessionRow {\n let session: SessionRow | undefined;\n\n if (numberOrLatest === \"latest\") {\n session = db\n .prepare(\n \"SELECT * FROM sessions WHERE project_id = ? ORDER BY number DESC LIMIT 1\"\n )\n .get(project.id) as SessionRow | undefined;\n } else {\n const num = parseInt(numberOrLatest, 10);\n if (isNaN(num)) {\n console.error(err(`Invalid session number: ${numberOrLatest}`));\n process.exit(1);\n }\n session = db\n .prepare(\"SELECT * FROM sessions WHERE project_id = ? AND number = ?\")\n .get(project.id, num) as SessionRow | undefined;\n }\n\n if (!session) {\n console.error(\n err(`Session ${numberOrLatest} not found in project ${project.slug}`)\n );\n process.exit(1);\n }\n\n return session;\n}\n\nexport function upsertTag(db: Database, tagName: string): number {\n db.prepare(\"INSERT OR IGNORE INTO tags (name) VALUES (?)\").run(tagName);\n const row = db\n .prepare(\"SELECT id FROM tags WHERE name = ?\")\n .get(tagName) as { id: number };\n return row.id;\n}\n\nexport function getSessionTags(db: Database, sessionId: number): string[] {\n const rows = db\n .prepare(\n `SELECT t.name FROM tags t\n JOIN session_tags st ON st.tag_id = t.id\n WHERE st.session_id = ?\n ORDER BY t.name`\n )\n .all(sessionId) as { name: string }[];\n return rows.map((r) => r.name);\n}\n","/**\n * Session CRUD commands: list, info, rename, slug, tag, route, active, auto-route.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport {\n existsSync,\n readdirSync,\n renameSync,\n readFileSync,\n writeFileSync,\n statSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport {\n ok,\n warn,\n err,\n dim,\n bold,\n header,\n renderTable,\n fmtDate,\n} from \"../../utils.js\";\nimport {\n findLatestTranscript,\n readLastMessages,\n generateSlug,\n} from \"../../../session/slug-generator.js\";\nimport type { SessionRow, ProjectRow } from \"./types.js\";\nimport {\n getProject,\n statusColor,\n toTitleCase,\n getNotesDir,\n formatFilename,\n resolveSession,\n upsertTag,\n getSessionTags,\n} from \"./helpers.js\";\n\n// ---------------------------------------------------------------------------\n// list\n// ---------------------------------------------------------------------------\n\nexport function cmdList(\n db: Database,\n projectSlug: string | undefined,\n opts: { limit?: string; status?: string }\n): void {\n const limit = parseInt(opts.limit ?? \"20\", 10);\n const params: unknown[] = [];\n\n let query = `\n SELECT s.*, p.slug AS project_slug, p.display_name AS project_name\n FROM sessions s\n JOIN projects p ON p.id = s.project_id\n `;\n\n const where: string[] = [];\n if (projectSlug) {\n const project = getProject(db, projectSlug);\n if (!project) {\n console.error(err(`Project not found: ${projectSlug}`));\n process.exit(1);\n }\n where.push(\"s.project_id = ?\");\n params.push(project.id);\n }\n if (opts.status) {\n where.push(\"s.status = ?\");\n params.push(opts.status);\n }\n\n if (where.length) query += \" WHERE \" + where.join(\" AND \");\n query += \" ORDER BY s.date DESC, s.number DESC\";\n query += ` LIMIT ${limit}`;\n\n const rows = db.prepare(query).all(...params) as (SessionRow & {\n project_slug: string;\n project_name: string;\n })[];\n\n if (!rows.length) {\n console.log(warn(\"No sessions found.\"));\n return;\n }\n\n const showProject = !projectSlug;\n const headers = showProject\n ? [\"#\", \"Project\", \"Date\", \"Title\", \"Status\", \"Tokens\"]\n : [\"#\", \"Date\", \"Title\", \"Status\", \"Tokens\"];\n\n const tableRows = rows.map((r) => {\n const title = r.title.length > 45 ? r.title.slice(0, 42) + \"...\" : r.title;\n const tokens =\n r.token_count != null ? dim(r.token_count.toLocaleString()) : dim(\"—\");\n if (showProject) {\n return [\n dim(`#${r.number}`),\n r.project_slug,\n r.date,\n title,\n statusColor(r.status),\n tokens,\n ];\n }\n return [dim(`#${r.number}`), r.date, title, statusColor(r.status), tokens];\n });\n\n console.log();\n if (projectSlug) {\n const project = getProject(db, projectSlug)!;\n console.log(` ${bold(project.display_name)} sessions:`);\n console.log();\n }\n console.log(renderTable(headers, tableRows));\n console.log();\n console.log(dim(` ${rows.length} session(s) shown (limit: ${limit})`));\n}\n\n// ---------------------------------------------------------------------------\n// info\n// ---------------------------------------------------------------------------\n\nexport function cmdInfo(\n db: Database,\n projectSlug: string,\n sessionNumber: string\n): void {\n const project = getProject(db, projectSlug);\n if (!project) {\n console.error(err(`Project not found: ${projectSlug}`));\n process.exit(1);\n }\n\n const session = resolveSession(db, project, sessionNumber);\n\n console.log();\n console.log(header(` Session #${session.number}: ${session.title}`));\n console.log();\n console.log(\n ` ${bold(\"Project:\")} ${project.display_name} (${project.slug})`\n );\n console.log(` ${bold(\"Date:\")} ${session.date}`);\n console.log(` ${bold(\"Status:\")} ${statusColor(session.status)}`);\n console.log(` ${bold(\"Filename:\")} ${session.filename}`);\n console.log(` ${bold(\"Slug:\")} ${session.slug}`);\n if (session.claude_session_id) {\n console.log(\n ` ${bold(\"Claude ID:\")} ${dim(session.claude_session_id)}`\n );\n }\n if (session.token_count != null) {\n console.log(\n ` ${bold(\"Tokens:\")} ${session.token_count.toLocaleString()}`\n );\n }\n console.log(` ${bold(\"Created:\")} ${fmtDate(session.created_at)}`);\n if (session.closed_at) {\n console.log(` ${bold(\"Closed:\")} ${fmtDate(session.closed_at)}`);\n }\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// rename\n// ---------------------------------------------------------------------------\n\nexport function cmdRename(\n db: Database,\n projectSlug: string,\n numberOrLatest: string,\n newSlug: string\n): void {\n const project = getProject(db, projectSlug);\n if (!project) {\n console.error(err(`Project not found: ${projectSlug}`));\n process.exit(1);\n }\n\n const session = resolveSession(db, project, numberOrLatest);\n const notesDir = getNotesDir(project);\n\n if (!existsSync(notesDir)) {\n console.error(err(`Notes directory not found: ${notesDir}`));\n process.exit(1);\n }\n\n const titleSlug = toTitleCase(newSlug);\n const newFilename = formatFilename(session.number, session.date, titleSlug);\n const oldPath = join(notesDir, session.filename);\n const newPath = join(notesDir, newFilename);\n\n if (existsSync(oldPath)) {\n if (oldPath !== newPath) {\n try {\n renameSync(oldPath, newPath);\n } catch (e) {\n console.error(err(`Failed to rename file: ${e}`));\n process.exit(1);\n }\n }\n } else {\n console.log(\n warn(` Note: file not found at expected path: ${session.filename}`)\n );\n console.log(warn(` Skipping disk rename. Database will still be updated.`));\n }\n\n if (existsSync(newPath)) {\n try {\n const content = readFileSync(newPath, \"utf8\");\n const lines = content.split(\"\\n\");\n let h1Updated = false;\n const updated = lines.map((line) => {\n if (!h1Updated && line.startsWith(\"# \")) {\n h1Updated = true;\n return `# ${titleSlug}`;\n }\n return line;\n });\n if (!h1Updated) updated.unshift(`# ${titleSlug}`, \"\");\n writeFileSync(newPath, updated.join(\"\\n\"), \"utf8\");\n } catch (e) {\n console.error(err(`Failed to update H1 in file: ${e}`));\n }\n }\n\n const normalizedSlug = newSlug\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n\n db.prepare(\n \"UPDATE sessions SET slug = ?, title = ?, filename = ? WHERE id = ?\"\n ).run(normalizedSlug, titleSlug, newFilename, session.id);\n\n console.log();\n console.log(ok(` Session #${session.number} renamed.`));\n console.log(` ${bold(\"Old:\")} ${session.filename}`);\n console.log(` ${bold(\"New:\")} ${newFilename}`);\n console.log(` ${bold(\"Slug:\")} ${normalizedSlug}`);\n console.log(` ${bold(\"Title:\")} ${titleSlug}`);\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// slug\n// ---------------------------------------------------------------------------\n\nexport function cmdSlug(\n db: Database,\n projectSlug: string,\n numberOrLatest: string,\n opts: { apply?: boolean }\n): void {\n const project = getProject(db, projectSlug);\n if (!project) {\n console.error(err(`Project not found: ${projectSlug}`));\n process.exit(1);\n }\n\n const session = resolveSession(db, project, numberOrLatest);\n const transcriptPath = findLatestTranscript(project.encoded_dir);\n\n if (!transcriptPath) {\n console.log(warn(` No JSONL transcripts found for project ${projectSlug}`));\n console.log(\"unnamed-session\");\n return;\n }\n\n const messages = readLastMessages(transcriptPath);\n\n if (messages.length < 2) {\n console.log(\n warn(` Too few messages found (${messages.length}) in transcript`)\n );\n console.log(\"unnamed-session\");\n return;\n }\n\n const generatedSlug = generateSlug(messages);\n console.log(generatedSlug);\n\n if (opts.apply) {\n console.log();\n console.log(dim(` Applying slug to session #${session.number}...`));\n cmdRename(db, projectSlug, String(session.number), generatedSlug);\n }\n}\n\n// ---------------------------------------------------------------------------\n// tag\n// ---------------------------------------------------------------------------\n\nexport function cmdTag(\n db: Database,\n projectSlug: string,\n sessionNumber: string,\n rawTags: string[]\n): void {\n const project = getProject(db, projectSlug);\n if (!project) {\n console.error(err(`Project not found: ${projectSlug}`));\n process.exit(1);\n }\n\n const session = resolveSession(db, project, sessionNumber);\n\n if (rawTags.length === 0) {\n const current = getSessionTags(db, session.id);\n console.log();\n if (current.length === 0) {\n console.log(dim(` Session #${session.number} has no tags.`));\n } else {\n console.log(\n ` ${bold(`Session #${session.number}`)} tags: ${current\n .map((t) => chalk.cyan(t))\n .join(\", \")}`\n );\n }\n console.log();\n return;\n }\n\n const tags = rawTags\n .flatMap((t) => t.split(\",\"))\n .map((t) => t.trim().toLowerCase())\n .filter((t) => t.length > 0);\n\n if (tags.length === 0) {\n console.log(warn(\"No valid tags provided.\"));\n return;\n }\n\n const added: string[] = [];\n const skipped: string[] = [];\n\n for (const tagName of tags) {\n const tagId = upsertTag(db, tagName);\n const exists = db\n .prepare(\"SELECT 1 FROM session_tags WHERE session_id = ? AND tag_id = ?\")\n .get(session.id, tagId);\n if (exists) {\n skipped.push(tagName);\n } else {\n db.prepare(\n \"INSERT INTO session_tags (session_id, tag_id) VALUES (?, ?)\"\n ).run(session.id, tagId);\n added.push(tagName);\n }\n }\n\n console.log();\n if (added.length) {\n console.log(\n ok(\n ` Tagged session #${session.number}: ${added\n .map((t) => chalk.cyan(t))\n .join(\", \")}`\n )\n );\n }\n if (skipped.length) {\n console.log(dim(` Already present: ${skipped.join(\", \")}`));\n }\n\n const allTags = getSessionTags(db, session.id);\n console.log(\n ` ${bold(\"All tags:\")} ${allTags.map((t) => chalk.cyan(t)).join(\", \")}`\n );\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// route\n// ---------------------------------------------------------------------------\n\nexport function cmdRoute(\n db: Database,\n projectSlug: string,\n sessionNumber: string,\n targetProjectSlug: string,\n opts: { type?: string }\n): void {\n const project = getProject(db, projectSlug);\n if (!project) {\n console.error(err(`Project not found: ${projectSlug}`));\n process.exit(1);\n }\n\n const session = resolveSession(db, project, sessionNumber);\n\n const targetProject = db\n .prepare(\"SELECT id, slug, display_name FROM projects WHERE slug = ?\")\n .get(targetProjectSlug) as\n | { id: number; slug: string; display_name: string }\n | undefined;\n\n if (!targetProject) {\n console.error(err(`Target project not found: ${targetProjectSlug}`));\n process.exit(1);\n }\n\n const validTypes = [\"related\", \"follow-up\", \"reference\"];\n const linkType = opts.type ?? \"related\";\n if (!validTypes.includes(linkType)) {\n console.error(\n err(`Invalid link type \"${linkType}\". Valid: ${validTypes.join(\", \")}`)\n );\n process.exit(1);\n }\n\n try {\n db.prepare(\n `INSERT INTO links (session_id, target_project_id, link_type, created_at)\n VALUES (?, ?, ?, ?)`\n ).run(session.id, targetProject.id, linkType, Date.now());\n } catch {\n console.log(\n warn(\n ` Link already exists: session #${session.number} → ${targetProjectSlug}`\n )\n );\n return;\n }\n\n console.log();\n console.log(\n ok(\n ` Linked session #${session.number} (${project.slug}) → ${targetProject.display_name} (${targetProjectSlug})`\n )\n );\n console.log(dim(` Link type: ${linkType}`));\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// active\n// ---------------------------------------------------------------------------\n\nexport function cmdActive(\n db: Database,\n opts: { minutes?: string; json?: boolean }\n): void {\n const minutes = parseInt(opts.minutes ?? \"60\", 10);\n const cutoff = Date.now() - minutes * 60 * 1000;\n const claudeProjectsDir = join(homedir(), \".claude\", \"projects\");\n\n if (!existsSync(claudeProjectsDir)) {\n console.log(err(\"Claude projects directory not found.\"));\n return;\n }\n\n interface ActiveSession {\n slug: string;\n displayName: string;\n rootPath: string;\n encodedDir: string;\n lastModified: Date;\n jsonlFile: string;\n }\n\n const active: ActiveSession[] = [];\n const entries = readdirSync(claudeProjectsDir);\n\n for (const entry of entries) {\n const projectDir = join(claudeProjectsDir, entry);\n try {\n if (!statSync(projectDir).isDirectory()) continue;\n } catch {\n continue;\n }\n\n let latestJsonl: string | null = null;\n let latestMtime = 0;\n\n try {\n for (const file of readdirSync(projectDir)) {\n if (!file.endsWith(\".jsonl\")) continue;\n const filePath = join(projectDir, file);\n try {\n const mtime = statSync(filePath).mtimeMs;\n if (mtime > latestMtime) {\n latestMtime = mtime;\n latestJsonl = filePath;\n }\n } catch {\n continue;\n }\n }\n } catch {\n continue;\n }\n\n if (!latestJsonl || latestMtime < cutoff) continue;\n\n const project = db\n .prepare(\n \"SELECT slug, display_name, root_path FROM projects WHERE encoded_dir = ?\"\n )\n .get(entry) as\n | { slug: string; display_name: string; root_path: string }\n | undefined;\n\n active.push({\n slug: project?.slug ?? entry,\n displayName: project?.display_name ?? project?.slug ?? entry,\n rootPath: project?.root_path ?? \"\",\n encodedDir: entry,\n lastModified: new Date(latestMtime),\n jsonlFile: latestJsonl,\n });\n }\n\n active.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());\n\n const seen = new Set<string>();\n const deduped = active.filter((a) => {\n const key = a.slug.replace(/-\\d+$/, \"\");\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n\n if (opts.json) {\n console.log(\n JSON.stringify(\n deduped.map((a) => ({\n slug: a.slug,\n display_name: a.displayName,\n root_path: a.rootPath,\n last_modified: a.lastModified.toISOString(),\n })),\n null,\n 2\n )\n );\n return;\n }\n\n if (deduped.length === 0) {\n console.log(dim(`No active sessions in the last ${minutes} minutes.`));\n return;\n }\n\n console.log(\n header(`Currently Active Sessions`) +\n dim(` (modified in last ${minutes}min)`)\n );\n console.log();\n\n const rows = deduped.map((a) => {\n const time = a.lastModified.toTimeString().slice(0, 5);\n const dirName = a.rootPath\n ? a.rootPath.replace(homedir(), \"~\").split(\"/\").pop() ?? a.slug\n : a.slug;\n return [chalk.cyan(dirName), dim(a.slug), chalk.green(time)];\n });\n\n console.log(renderTable([\"Directory\", \"Project\", \"Last Active\"], rows));\n}\n\n// ---------------------------------------------------------------------------\n// auto-route\n// ---------------------------------------------------------------------------\n\nexport async function cmdAutoRoute(opts: {\n cwd?: string;\n context?: string;\n json?: boolean;\n}): Promise<void> {\n const { autoRoute, formatAutoRoute, formatAutoRouteJson } = await import(\n \"../../../session/auto-route.js\"\n );\n const { openRegistry } = await import(\"../../../registry/db.js\");\n const { createStorageBackend } = await import(\"../../../storage/factory.js\");\n const { loadConfig } = await import(\"../../../daemon/config.js\");\n\n const config = loadConfig();\n const registryDb = openRegistry();\n const federation = await createStorageBackend(config);\n\n const targetCwd = opts.cwd ?? process.cwd();\n const result = await autoRoute(\n registryDb,\n federation,\n targetCwd,\n opts.context\n );\n\n if (!result) {\n console.log();\n console.log(warn(\" No project match found for: \" + targetCwd));\n console.log();\n console.log(\n dim(\" Tried: path match, PAI.md marker walk\") +\n (opts.context ? dim(\", topic detection\") : \"\")\n );\n console.log();\n console.log(dim(\" Run 'pai project add .' to register this directory.\"));\n console.log();\n return;\n }\n\n if (opts.json) {\n console.log(formatAutoRouteJson(result));\n return;\n }\n\n console.log();\n console.log(header(\" PAI Auto-Route\"));\n console.log();\n console.log(` ${bold(\"Project:\")} ${result.display_name}`);\n console.log(` ${bold(\"Slug:\")} ${result.slug}`);\n console.log(` ${bold(\"Root path:\")} ${result.root_path}`);\n console.log(` ${bold(\"Method:\")} ${result.method}`);\n console.log(\n ` ${bold(\"Confidence:\")} ${(result.confidence * 100).toFixed(0)}%`\n );\n console.log();\n console.log(ok(\" Routed to: \") + bold(result.slug));\n console.log();\n}\n","/**\n * Session checkpoint command — appends a timestamped block to the active\n * session note. Designed for use in hooks; fast, silent, rate-limited.\n */\n\nimport { existsSync, readdirSync, statSync, readFileSync, writeFileSync, renameSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir, tmpdir } from \"node:os\";\n\nfunction findNotesDirForCwd(): string | null {\n const cwd = process.cwd();\n const claudeProjectsDir = join(homedir(), \".claude\", \"projects\");\n\n if (!existsSync(claudeProjectsDir)) return null;\n\n const expectedEncoded = cwd.replace(/[/\\s.\\-]/g, \"-\");\n let encodedDir: string | null = null;\n\n try {\n const entries = readdirSync(claudeProjectsDir);\n\n if (entries.includes(expectedEncoded)) {\n encodedDir = expectedEncoded;\n } else {\n for (const entry of entries) {\n const full = join(claudeProjectsDir, entry);\n try {\n if (!statSync(full).isDirectory()) continue;\n } catch {\n continue;\n }\n const candidate = entry.replace(/-+$/, \"\");\n const expected = expectedEncoded.replace(/-+$/, \"\");\n if (candidate === expected) {\n encodedDir = entry;\n break;\n }\n }\n }\n } catch {\n return null;\n }\n\n if (!encodedDir) return null;\n const notesDir = join(claudeProjectsDir, encodedDir, \"Notes\");\n return existsSync(notesDir) ? notesDir : null;\n}\n\nfunction findLatestNoteFile(notesDir: string): string | null {\n let entries: string[];\n try {\n entries = readdirSync(notesDir);\n } catch {\n return null;\n }\n\n const mdFiles = entries.filter((e) => e.endsWith(\".md\"));\n if (mdFiles.length === 0) return null;\n\n let latestPath: string | null = null;\n let latestMtime = 0;\n\n for (const file of mdFiles) {\n const full = join(notesDir, file);\n try {\n const { mtimeMs } = statSync(full);\n if (mtimeMs > latestMtime) {\n latestMtime = mtimeMs;\n latestPath = full;\n }\n } catch {\n // skip unreadable files\n }\n }\n\n return latestPath;\n}\n\nfunction checkpointTooRecent(notesDir: string, minGapSeconds: number): boolean {\n const safeKey = notesDir.replace(/[^a-zA-Z0-9]/g, \"-\").slice(-80);\n const tmpFile = join(tmpdir(), `pai-checkpoint-${safeKey}`);\n if (!existsSync(tmpFile)) return false;\n try {\n const { mtimeMs } = statSync(tmpFile);\n return Date.now() - mtimeMs < minGapSeconds * 1000;\n } catch {\n return false;\n }\n}\n\nfunction touchCheckpointSentinel(notesDir: string): void {\n const safeKey = notesDir.replace(/[^a-zA-Z0-9]/g, \"-\").slice(-80);\n const tmpFile = join(tmpdir(), `pai-checkpoint-${safeKey}`);\n try {\n writeFileSync(tmpFile, String(Date.now()), \"utf8\");\n } catch {\n // Non-fatal — rate limiting is best-effort\n }\n}\n\nexport function cmdCheckpoint(\n message: string,\n opts: { minGap?: string }\n): void {\n const minGapSeconds = parseInt(opts.minGap ?? \"300\", 10);\n\n const notesDir = findNotesDirForCwd();\n if (!notesDir) process.exit(0);\n\n if (checkpointTooRecent(notesDir, minGapSeconds)) process.exit(0);\n\n const notePath = findLatestNoteFile(notesDir);\n if (!notePath) process.exit(0);\n\n const timestamp = new Date().toISOString();\n const block = `\\n## Checkpoint — ${timestamp}\\n${message}\\n`;\n\n const tmpPath = `${notePath}.checkpoint.tmp`;\n try {\n const existing = readFileSync(notePath, \"utf8\");\n writeFileSync(tmpPath, existing + block, \"utf8\");\n renameSync(tmpPath, notePath);\n } catch {\n try {\n if (existsSync(tmpPath)) renameSync(tmpPath, tmpPath + \".dead\");\n } catch {\n /* ignore */\n }\n process.exit(0);\n }\n\n touchCheckpointSentinel(notesDir);\n process.exit(0);\n}\n","/**\n * Session handover command — writes a ## Continue section to the project's\n * TODO.md. Called from session-stop and pre-compact hooks.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { SessionRow, ProjectRow } from \"./types.js\";\n\nconst HANDOVER_TODO_LOCATIONS = [\n \"Notes/TODO.md\",\n \".claude/Notes/TODO.md\",\n \"tasks/todo.md\",\n \"TODO.md\",\n];\n\nfunction findProjectTodo(\n rootPath: string\n): { path: string; content: string } | null {\n for (const rel of HANDOVER_TODO_LOCATIONS) {\n const full = join(rootPath, rel);\n if (existsSync(full)) {\n try {\n return { path: full, content: readFileSync(full, \"utf8\") };\n } catch {\n // unreadable — try next\n }\n }\n }\n return null;\n}\n\nfunction stripContinueSection(content: string): string {\n const lines = content.split(\"\\n\");\n const startIdx = lines.findIndex((l) => l.trim() === \"## Continue\");\n if (startIdx === -1) return content;\n\n let endIdx = lines.length;\n for (let i = startIdx + 1; i < lines.length; i++) {\n const trimmed = lines[i].trim();\n if (\n trimmed === \"---\" ||\n (trimmed.startsWith(\"##\") && trimmed !== \"## Continue\")\n ) {\n endIdx = i;\n break;\n }\n }\n\n let trailingEnd = endIdx;\n if (trailingEnd < lines.length && lines[trailingEnd].trim() === \"---\") {\n trailingEnd += 1;\n }\n\n const before = lines.slice(0, startIdx);\n const after = lines.slice(trailingEnd);\n while (after.length > 0 && after[0].trim() === \"\") after.shift();\n\n return [...before, ...after].join(\"\\n\");\n}\n\nexport function cmdHandover(\n db: Database,\n projectSlug: string | undefined,\n numberOrLatest: string | undefined\n): void {\n // ---- 1. Resolve project ----\n let project: ProjectRow | undefined;\n\n if (projectSlug) {\n project = db\n .prepare(\n \"SELECT id, slug, display_name, root_path, encoded_dir FROM projects WHERE slug = ?\"\n )\n .get(projectSlug) as ProjectRow | undefined;\n if (!project) process.exit(0);\n } else {\n const cwd = process.cwd();\n const row = db\n .prepare(\n `SELECT id, slug, display_name, root_path, encoded_dir\n FROM projects\n WHERE ? LIKE root_path || '%'\n ORDER BY length(root_path) DESC\n LIMIT 1`\n )\n .get(cwd) as ProjectRow | undefined;\n if (!row) process.exit(0);\n project = row;\n }\n\n // ---- 2. Resolve session ----\n let session: SessionRow | undefined;\n const nol = numberOrLatest ?? \"latest\";\n\n if (nol === \"latest\") {\n session = db\n .prepare(\n \"SELECT * FROM sessions WHERE project_id = ? ORDER BY number DESC LIMIT 1\"\n )\n .get(project!.id) as SessionRow | undefined;\n } else {\n const num = parseInt(nol, 10);\n if (!isNaN(num)) {\n session = db\n .prepare(\"SELECT * FROM sessions WHERE project_id = ? AND number = ?\")\n .get(project!.id, num) as SessionRow | undefined;\n }\n }\n\n // ---- 3. Find or create TODO ----\n const todo = findProjectTodo(project!.root_path);\n let todoPath: string;\n let existingContent: string;\n\n if (todo) {\n todoPath = todo.path;\n existingContent = todo.content;\n } else {\n const notesDir = join(project!.root_path, \"Notes\");\n try {\n if (!existsSync(notesDir)) mkdirSync(notesDir, { recursive: true });\n } catch {\n process.exit(0);\n }\n todoPath = join(notesDir, \"TODO.md\");\n existingContent = \"\";\n }\n\n // ---- 4. Build ## Continue block ----\n const timestamp = new Date().toISOString();\n const cwd = process.cwd();\n\n let sessionLine: string;\n if (session) {\n const num = String(session.number).padStart(4, \"0\");\n const titlePart = session.title || session.slug || \"Session\";\n sessionLine = `${num} - ${session.date} - ${titlePart}`;\n } else {\n sessionLine = \"Unknown session\";\n }\n\n const continueBlock = [\n \"## Continue\",\n \"\",\n `> **Last session:** ${sessionLine}`,\n `> **Paused at:** ${timestamp}`,\n \">\",\n `> Working directory: ${cwd}. Check the latest session note for details.`,\n \"\",\n \"---\",\n \"\",\n ].join(\"\\n\");\n\n // ---- 5. Prepend, stripping old ## Continue ----\n const stripped = stripContinueSection(existingContent).trimStart();\n const newContent = continueBlock + stripped;\n\n // ---- 6. Write atomically ----\n const tmpPath = `${todoPath}.handover.tmp`;\n try {\n writeFileSync(tmpPath, newContent, \"utf8\");\n renameSync(tmpPath, todoPath);\n } catch {\n try {\n if (existsSync(tmpPath)) renameSync(tmpPath, `${tmpPath}.dead`);\n } catch {\n /* ignore */\n }\n process.exit(0);\n }\n\n process.exit(0);\n}\n","/**\n * Shared scanner for Claude Code sessions stored in ~/.claude/projects/ .\n *\n * Storage layout (Claude Code v2.1.143+):\n *\n * <project>/<uuid>.jsonl — top-level: metadata, system snapshots, hook events.\n * REQUIRED for claude --resume to work.\n * <project>/sessions/<uuid>.jsonl — full transcript: user/assistant/attachment lines.\n * 3000+ files, almost none resumable on their own.\n *\n * Resumability rule (empirically verified):\n * A session is resumable iff the TOP-LEVEL jsonl exists AND contains at least one\n * line of type \"system\". Sessions that only have a sessions/ counterpart cannot be\n * resumed by Claude Code regardless of how much transcript content they have.\n *\n * Stale-UUID problem (fixed in this version):\n * The clc session.json registry stores ONE uuid per named session — the uuid Claude Code\n * had when the user last named the session. If the user resumes and the session gets a\n * new uuid (or they Ctrl+C and start fresh), the registry entry still points to the OLD\n * uuid. The scanner now resolves names to the MOST RECENT top-level jsonl in the project\n * directory, not the clc-cached uuid.\n *\n * Resolution strategy:\n * 1. Walk top-level <project>/<uuid>.jsonl files (Pass 1).\n * 2. For each, attach the clc name if the uuid matches (exact hit).\n * 3. After Pass 1, for every clc registry entry:\n * a. If the cached uuid was found → already handled.\n * b. Find the encodedDir for this entry's directory.\n * c. Check ALL sessions in that encodedDir (already in our Pass-1 results).\n * d. Pick the MOST RECENT resumable session in that dir and attach the name to it.\n * If no resumable session, fall through to transcript-only pass.\n * 4. This means the displayed uuid for \"Jobs Matthias\" is always today's active session,\n * not the stale cached one.\n *\n * Used by: pai sessions, pai resume\n */\n\nimport { readdirSync, statSync, readFileSync, existsSync, realpathSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type { Database } from \"better-sqlite3\";\nimport { smartDecodeDir } from \"../utils.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Display status for a session:\n * resumable — top-level jsonl exists with ≥1 system line (claude --resume works)\n * stub — in clc registry, top-level jsonl exists but 0 system lines (Ctrl+C exit)\n * transcript-only — in clc registry but no top-level jsonl at all\n * orphan — not in clc registry, only sessions/ transcript exists\n */\nexport type SessionStatus = \"resumable\" | \"stub\" | \"transcript-only\" | \"orphan\";\n\nexport interface ScannedSession {\n /** Full UUID from the filename. */\n uuid: string;\n /** Short 8-char prefix for display. */\n shortId: string;\n /** Encoded project directory name (e.g. \"-Users-foo-myproject\"). */\n encodedDir: string;\n /** Decoded project root path (via smartDecodeDir). */\n decodedPath: string;\n\n // Top-level jsonl — the resumability source\n /** Full path to the top-level <project>/<uuid>.jsonl (may not exist). */\n topLevelPath: string;\n /** Number of \"system\" lines in the top-level jsonl. */\n topLevelSystemLines: number;\n /** Size of the top-level jsonl in bytes. */\n topLevelSize: number;\n /**\n * True iff the top-level jsonl has at least one \"system\" line.\n * This is the canonical resumability gate — matches Claude Code's --resume behaviour.\n */\n resumable: boolean;\n /** Categorised display status. */\n sessionStatus: SessionStatus;\n\n // Sessions/ transcript data (best-effort, may not exist)\n /** Full path to sessions/<uuid>.jsonl, if present. */\n sessionJsonlPath?: string;\n /** Number of \"user\" type lines in the transcript. */\n userLines: number;\n /** Last user message text (truncated to 80 chars). */\n lastUserPrompt: string;\n /** Total line count in the transcript file. */\n msgCount: number;\n /** Auto-generated title from the last ai-title line in the transcript. */\n aiTitle?: string;\n\n /** mtime in epoch ms — top-level file mtime (primary), transcript mtime (fallback). */\n mtime: number;\n\n /**\n * Human-readable friendly name (for display and /Name restoration).\n * Priority: clc session.json name → ai-title → project dir basename.\n * NEVER slugified — preserves original spaces and capitalisation.\n */\n friendlyName?: string;\n /**\n * Literal directory from the clc session registry for this UUID.\n * May contain symlinks and emoji (e.g. /Users/foo/dev/... or .../🧠 Vault/...).\n * When present, realpathSync(clcDirectory) gives the correct cwd for --resume.\n */\n clcDirectory?: string;\n /** Project root path from PAI registry (authoritative cwd for resume). */\n registryRootPath?: string;\n}\n\n// ---------------------------------------------------------------------------\n// clc session registry (~/.claude/session.json)\n// ---------------------------------------------------------------------------\n\nconst CLC_SESSIONS_FILE = join(homedir(), \".claude\", \"session.json\");\n\ninterface ClcEntry {\n name?: string;\n resume?: string;\n session?: string;\n directory?: string;\n}\n\ninterface ClcRegistry {\n sessions?: ClcEntry[];\n}\n\nexport interface ClcInfo {\n name: string;\n /** Literal directory from clc registry (may contain symlinks, emoji, spaces). */\n directory?: string;\n}\n\n/** Load clc's session registry → uuid → ClcInfo map. Verbatim, never slugified. */\nfunction buildClcInfoMap(): Map<string, ClcInfo> {\n const map = new Map<string, ClcInfo>();\n try {\n const raw = readFileSync(CLC_SESSIONS_FILE, \"utf8\");\n const data = JSON.parse(raw) as ClcRegistry;\n for (const entry of data.sessions ?? []) {\n const name = entry.name?.trim();\n const uuid = (entry.resume ?? entry.session ?? \"\").trim();\n if (name && uuid) {\n map.set(uuid, { name, directory: entry.directory?.trim() || undefined });\n }\n }\n } catch {\n // Missing or malformed — not fatal\n }\n return map;\n}\n\n// ---------------------------------------------------------------------------\n// PAI registry\n// ---------------------------------------------------------------------------\n\ninterface RegistryProject {\n root_path: string;\n encoded_dir: string;\n}\n\n/** Build encoded_dir → root_path map from PAI registry (authoritative cwd). */\nfunction buildRegistryRootPathMap(db: Database): Map<string, string> {\n try {\n const rows = db\n .prepare(\n `SELECT root_path, encoded_dir FROM projects\n WHERE encoded_dir IS NOT NULL AND encoded_dir != ''`\n )\n .all() as RegistryProject[];\n const map = new Map<string, string>();\n for (const row of rows) {\n map.set(row.encoded_dir, row.root_path);\n }\n return map;\n } catch {\n return new Map();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Top-level jsonl parser\n// ---------------------------------------------------------------------------\n\ninterface TopLevelInfo {\n systemLines: number;\n size: number;\n mtime: number;\n}\n\nfunction parseTopLevel(filePath: string): TopLevelInfo {\n let systemLines = 0;\n let size = 0;\n let mtime = 0;\n try {\n const st = statSync(filePath);\n size = st.size;\n mtime = st.mtimeMs;\n const content = readFileSync(filePath, \"utf8\");\n for (const line of content.split(\"\\n\")) {\n const t = line.trim();\n if (!t) continue;\n if (t.includes('\"type\":\"system\"') || t.includes('\"type\": \"system\"')) {\n systemLines++;\n }\n }\n } catch {\n /* ignore */\n }\n return { systemLines, size, mtime };\n}\n\n// ---------------------------------------------------------------------------\n// Sessions/ transcript parser\n// ---------------------------------------------------------------------------\n\ninterface TranscriptInfo {\n userLines: number;\n lastUserPrompt: string;\n msgCount: number;\n aiTitle?: string;\n mtime: number;\n}\n\nfunction parseTranscript(filePath: string): TranscriptInfo {\n let userLines = 0;\n let lastUserPrompt = \"\";\n let msgCount = 0;\n let aiTitle: string | undefined;\n let mtime = 0;\n\n try {\n mtime = statSync(filePath).mtimeMs;\n } catch {\n return { userLines: 0, lastUserPrompt: \"\", msgCount: 0, mtime: 0 };\n }\n\n let content: string;\n try {\n content = readFileSync(filePath, \"utf8\");\n } catch {\n return { userLines: 0, lastUserPrompt: \"\", msgCount: 0, mtime };\n }\n\n for (const line of content.split(\"\\n\")) {\n const t = line.trim();\n if (!t) continue;\n msgCount++;\n\n const hasUser = t.includes('\"type\":\"user\"') || t.includes('\"type\": \"user\"');\n const hasTitle =\n t.includes('\"type\":\"ai-title\"') || t.includes('\"type\": \"ai-title\"');\n if (!hasUser && !hasTitle) continue;\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(t) as Record<string, unknown>;\n } catch {\n continue;\n }\n\n if (parsed.type === \"ai-title\") {\n const v = parsed.title ?? parsed.name;\n if (typeof v === \"string\" && v.trim()) aiTitle = v.trim();\n continue;\n }\n\n if (parsed.type !== \"user\") continue;\n userLines++;\n\n const msg = parsed.message as Record<string, unknown> | undefined;\n if (!msg) continue;\n const c = msg.content;\n let text = \"\";\n if (typeof c === \"string\") {\n text = c;\n } else if (Array.isArray(c) && c.length > 0) {\n const first = c[0] as Record<string, unknown>;\n if (typeof first.text === \"string\") text = first.text;\n }\n if (text) lastUserPrompt = text.slice(0, 80).replace(/\\n/g, \" \");\n }\n\n return { userLines, lastUserPrompt, msgCount, aiTitle, mtime };\n}\n\n// ---------------------------------------------------------------------------\n// Main scanner\n// ---------------------------------------------------------------------------\n\nexport interface ScanOptions {\n /** Maximum results to return after sorting (default: 200). */\n limit?: number;\n /**\n * Filter mode:\n * \"named\" (default) — resumable OR in clc registry (named sessions the user cares about)\n * \"all\" — everything including unnamed orphans\n * \"resumable\" — only sessions claude --resume accepts (top-level + system lines)\n */\n filter?: \"named\" | \"all\" | \"resumable\";\n /**\n * @deprecated Use filter instead. When true → filter:\"resumable\", false → filter:\"named\".\n * Kept for backwards compatibility with callers that predate the filter field.\n */\n resumableOnly?: boolean;\n}\n\nconst CLAUDE_PROJECTS_DIR = join(homedir(), \".claude\", \"projects\");\n\n// UUID format: 8-4-4-4-12 hex digits\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Compute the effective filter mode, resolving the legacy resumableOnly flag.\n */\nfunction resolveFilter(opts: ScanOptions): \"named\" | \"all\" | \"resumable\" {\n if (opts.filter) return opts.filter;\n if (opts.resumableOnly === true) return \"resumable\";\n if (opts.resumableOnly === false) return \"all\";\n return \"named\"; // default\n}\n\n/**\n * Scan ~/.claude/projects/ for all Claude Code sessions.\n *\n * Pass 1: walk top-level <project>/<uuid>.jsonl files (the resumability source).\n * Pass 2: handle clc registry entries whose cached UUID was not found in Pass 1.\n * For each such entry, scan the entry's project dir for the FRESHEST session\n * and attach the registry name to it. This fixes the stale-UUID bug where\n * clc's session.json points to an old uuid after a fresh start.\n * Pass 3: any remaining clc entries with truly no top-level jsonl → transcript-only.\n *\n * Filter modes:\n * \"named\" — resumable + registry-known stubs + transcript-only (default)\n * \"all\" — all of the above + unnamed orphans from sessions/ subdirs\n * \"resumable\" — only sessions claude --resume accepts\n *\n * Returns results sorted by mtime descending.\n */\nexport function scanSessions(\n db: Database,\n opts: ScanOptions = {}\n): ScannedSession[] {\n const limit = opts.limit ?? 200;\n const filterMode = resolveFilter(opts);\n\n if (!existsSync(CLAUDE_PROJECTS_DIR)) return [];\n\n const clcInfoMap = buildClcInfoMap();\n const rootPathMap = buildRegistryRootPathMap(db);\n const results: ScannedSession[] = [];\n // Track which UUIDs we've already added (from the jsonl walk)\n const seenUuids = new Set<string>();\n // Track which clc names we've already attached to a session\n const attachedClcUuids = new Set<string>();\n\n let encodedDirs: string[];\n try {\n encodedDirs = readdirSync(CLAUDE_PROJECTS_DIR);\n } catch {\n return [];\n }\n\n // Build a map: encodedDir → all top-level sessions found in Pass 1\n // This is used in Pass 2 to find the freshest session for a clc registry entry.\n const sessionsByEncodedDir = new Map<string, ScannedSession[]>();\n\n // ---- Pass 1: walk top-level <project>/<uuid>.jsonl files ----\n for (const encodedDir of encodedDirs) {\n const projectDir = join(CLAUDE_PROJECTS_DIR, encodedDir);\n try {\n if (!statSync(projectDir).isDirectory()) continue;\n } catch {\n continue;\n }\n\n let files: string[];\n try {\n files = readdirSync(projectDir);\n } catch {\n continue;\n }\n\n const decodedPath =\n smartDecodeDir(encodedDir) ?? encodedDir.replace(/-/g, \"/\");\n const registryRootPath = rootPathMap.get(encodedDir);\n const projectBasename = basename(decodedPath);\n\n for (const file of files) {\n if (!file.endsWith(\".jsonl\")) continue;\n const uuid = file.slice(0, -6);\n if (!UUID_RE.test(uuid)) continue;\n\n const topLevelPath = join(projectDir, file);\n const topInfo = parseTopLevel(topLevelPath);\n const resumable = topInfo.systemLines > 0;\n const clcInfo = clcInfoMap.get(uuid);\n const inRegistry = !!clcInfo;\n\n // Determine status\n const sessionStatus: SessionStatus = resumable\n ? \"resumable\"\n : inRegistry\n ? \"stub\"\n : \"orphan\";\n\n // Apply filter — but include ALL in this pass so Pass 2 can use them\n // We'll filter again when deciding what to add to `results`.\n const passesFilter =\n filterMode === \"resumable\"\n ? resumable\n : filterMode === \"named\"\n ? resumable || inRegistry\n : true;\n\n // Join with sessions/ transcript (best-effort)\n const sessionJsonlPath = join(projectDir, \"sessions\", `${uuid}.jsonl`);\n const hasTranscript = existsSync(sessionJsonlPath);\n const transcript: TranscriptInfo = hasTranscript\n ? parseTranscript(sessionJsonlPath)\n : { userLines: 0, lastUserPrompt: \"\", msgCount: 0, mtime: 0 };\n\n // Use top-level mtime as primary\n const mtime = topInfo.mtime || transcript.mtime;\n\n // Name resolution: clc registry (verbatim) → ai-title → project basename\n const friendlyName =\n clcInfo?.name ?? transcript.aiTitle ?? projectBasename ?? undefined;\n\n const session: ScannedSession = {\n uuid,\n shortId: uuid.slice(0, 8),\n encodedDir,\n decodedPath,\n topLevelPath,\n topLevelSystemLines: topInfo.systemLines,\n topLevelSize: topInfo.size,\n resumable,\n sessionStatus,\n sessionJsonlPath: hasTranscript ? sessionJsonlPath : undefined,\n userLines: transcript.userLines,\n lastUserPrompt: transcript.lastUserPrompt,\n msgCount: transcript.msgCount,\n aiTitle: transcript.aiTitle,\n mtime,\n friendlyName,\n clcDirectory: clcInfo?.directory,\n registryRootPath,\n };\n\n // Track in per-dir map for Pass 2 lookups (all sessions, regardless of filter)\n if (!sessionsByEncodedDir.has(encodedDir)) {\n sessionsByEncodedDir.set(encodedDir, []);\n }\n sessionsByEncodedDir.get(encodedDir)!.push(session);\n\n seenUuids.add(uuid);\n if (inRegistry) attachedClcUuids.add(uuid);\n\n if (passesFilter) {\n results.push(session);\n }\n }\n }\n\n // ---- Pass 2: clc registry entries whose cached UUID wasn't found as a top-level file ----\n // For each such entry, find the project's encodedDir and look for the FRESHEST\n // resumable session there. Attach the clc name to it.\n // This fixes the stale-UUID bug: clc may store a uuid that's no longer valid,\n // but the project dir has a new active session the user is actually in.\n if (filterMode !== \"resumable\") {\n for (const [cachedUuid, clcInfo] of clcInfoMap) {\n if (attachedClcUuids.has(cachedUuid)) continue; // already named in Pass 1\n\n // Derive the encodedDir from clcInfo.directory\n let foundEncodedDir: string | undefined;\n if (clcInfo.directory) {\n const real = realpathSyncSafe(clcInfo.directory);\n if (real) {\n const encoded = encodeProjectDir(real);\n if (existsSync(join(CLAUDE_PROJECTS_DIR, encoded))) {\n foundEncodedDir = encoded;\n }\n }\n }\n\n // Fallback: check if the cached uuid exists as a sessions/ transcript somewhere\n if (!foundEncodedDir) {\n for (const encodedDir of encodedDirs) {\n if (existsSync(join(CLAUDE_PROJECTS_DIR, encodedDir, \"sessions\", `${cachedUuid}.jsonl`))) {\n foundEncodedDir = encodedDir;\n break;\n }\n }\n }\n\n if (foundEncodedDir) {\n const dirSessions = sessionsByEncodedDir.get(foundEncodedDir) ?? [];\n\n // Find the freshest RESUMABLE session in this dir that doesn't yet have a name\n const freshestResumable = dirSessions\n .filter((s) => s.resumable && !s.friendlyName)\n .sort((a, b) => b.mtime - a.mtime)[0];\n\n if (freshestResumable) {\n // Attach the clc name to this fresher session\n freshestResumable.friendlyName = clcInfo.name;\n freshestResumable.clcDirectory =\n freshestResumable.clcDirectory ?? clcInfo.directory;\n freshestResumable.sessionStatus = \"resumable\";\n attachedClcUuids.add(freshestResumable.uuid);\n\n // Add to results if not already there (it might have been filtered out as orphan)\n if (!seenUuids.has(freshestResumable.uuid) || !results.includes(freshestResumable)) {\n // It was added to sessionsByEncodedDir but might have been filtered from results\n if (!results.includes(freshestResumable)) {\n results.push(freshestResumable);\n }\n }\n continue; // Done with this clc entry\n }\n\n // No unnamed resumable session found. The project might have resumable sessions\n // that already have a name (different name). Or only stubs/orphans.\n // Fall through to transcript-only handling below.\n }\n\n // ---- Pass 3: truly no top-level jsonl → transcript-only ----\n if (!foundEncodedDir) {\n // We couldn't find the project dir at all\n const encodedDir = \"\";\n const decodedPath = clcInfo.directory ?? cachedUuid;\n const registryRootPath = undefined;\n\n const transcript: TranscriptInfo = { userLines: 0, lastUserPrompt: \"\", msgCount: 0, mtime: 0 };\n const mtime = transcript.mtime;\n\n seenUuids.add(cachedUuid);\n results.push({\n uuid: cachedUuid,\n shortId: cachedUuid.slice(0, 8),\n encodedDir,\n decodedPath,\n topLevelPath: \"\",\n topLevelSystemLines: 0,\n topLevelSize: 0,\n resumable: false,\n sessionStatus: \"transcript-only\",\n sessionJsonlPath: undefined,\n userLines: 0,\n lastUserPrompt: \"\",\n msgCount: 0,\n mtime,\n friendlyName: clcInfo.name,\n clcDirectory: clcInfo.directory,\n registryRootPath,\n });\n continue;\n }\n\n // Project dir was found but no unnamed resumable session.\n // Check sessions/ for the cached uuid transcript.\n const foundTranscriptPath = existsSync(\n join(CLAUDE_PROJECTS_DIR, foundEncodedDir, \"sessions\", `${cachedUuid}.jsonl`)\n )\n ? join(CLAUDE_PROJECTS_DIR, foundEncodedDir, \"sessions\", `${cachedUuid}.jsonl`)\n : undefined;\n\n const decodedPath =\n clcInfo.directory ??\n (smartDecodeDir(foundEncodedDir) ?? foundEncodedDir.replace(/-/g, \"/\"));\n const registryRootPath = rootPathMap.get(foundEncodedDir);\n const topLevelPath = join(\n CLAUDE_PROJECTS_DIR,\n foundEncodedDir,\n `${cachedUuid}.jsonl`\n );\n\n const transcript: TranscriptInfo = foundTranscriptPath\n ? parseTranscript(foundTranscriptPath)\n : { userLines: 0, lastUserPrompt: \"\", msgCount: 0, mtime: 0 };\n\n seenUuids.add(cachedUuid);\n results.push({\n uuid: cachedUuid,\n shortId: cachedUuid.slice(0, 8),\n encodedDir: foundEncodedDir,\n decodedPath,\n topLevelPath,\n topLevelSystemLines: 0,\n topLevelSize: 0,\n resumable: false,\n sessionStatus: \"transcript-only\",\n sessionJsonlPath: foundTranscriptPath,\n userLines: transcript.userLines,\n lastUserPrompt: transcript.lastUserPrompt,\n msgCount: transcript.msgCount,\n aiTitle: transcript.aiTitle,\n mtime: transcript.mtime,\n friendlyName: clcInfo.name,\n clcDirectory: clcInfo.directory,\n registryRootPath,\n });\n }\n }\n\n results.sort((a, b) => b.mtime - a.mtime);\n return results.slice(0, limit);\n}\n\n// ---------------------------------------------------------------------------\n// Path encoding helpers\n// ---------------------------------------------------------------------------\n\n/** Claude Code's project-dir encoding: replace / . - (space) with - */\nfunction encodeProjectDir(realPath: string): string {\n // Leading slash becomes leading -; all / . - and space → -\n return realPath.replace(/[/.\\- ]/g, \"-\");\n}\n\n/** realpathSync that returns null instead of throwing */\nfunction realpathSyncSafe(p: string): string | null {\n try {\n return realpathSync(p);\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Age formatter\n// ---------------------------------------------------------------------------\n\nexport function fmtAge(mtime: number): string {\n const diffMs = Date.now() - mtime;\n const diffMin = Math.floor(diffMs / 60_000);\n const diffHr = Math.floor(diffMs / 3_600_000);\n const diffDay = Math.floor(diffMs / 86_400_000);\n\n if (diffMin < 60) return `${diffMin}m`;\n if (diffHr < 24) return `${diffHr}h`;\n if (diffDay < 30) return `${diffDay}d`;\n return `${Math.floor(diffDay / 30)}mo`;\n}\n\n// ---------------------------------------------------------------------------\n// Resolve a name/id/prefix to a session entry\n// ---------------------------------------------------------------------------\n\nexport interface ResolvedSession {\n session: ScannedSession;\n /** The friendly name to use verbatim in the /Name command. Never slugified. */\n friendlyName?: string;\n}\n\n/**\n * Filesystem-level UUID scan: walk ALL ~/.claude/projects/<encoded-dir>/<uuid>.jsonl\n * looking for top-level files whose UUID starts with `prefix`.\n *\n * Returns a ScannedSession-like object (minimal fields) for each match, or an empty\n * array if nothing is found. This is used as a fallback when the regular catalog\n * (limited to named/recent sessions) doesn't contain the requested UUID.\n *\n * Complexity: O(number of project directories + files per dir). Typically <200 dirs\n * with a handful of top-level jsonl each — fast enough for an interactive CLI.\n */\nfunction scanFilesystemForUuidPrefix(prefix: string): ScannedSession[] {\n if (!existsSync(CLAUDE_PROJECTS_DIR)) return [];\n\n const prefixLower = prefix.toLowerCase();\n const matches: ScannedSession[] = [];\n\n let encodedDirs: string[];\n try {\n encodedDirs = readdirSync(CLAUDE_PROJECTS_DIR);\n } catch {\n return [];\n }\n\n for (const encodedDir of encodedDirs) {\n const projectDir = join(CLAUDE_PROJECTS_DIR, encodedDir);\n try {\n if (!statSync(projectDir).isDirectory()) continue;\n } catch {\n continue;\n }\n\n let files: string[];\n try {\n files = readdirSync(projectDir);\n } catch {\n continue;\n }\n\n for (const file of files) {\n if (!file.endsWith(\".jsonl\")) continue;\n const uuid = file.slice(0, -6);\n if (!UUID_RE.test(uuid)) continue;\n if (!uuid.toLowerCase().startsWith(prefixLower)) continue;\n\n const topLevelPath = join(projectDir, file);\n const topInfo = parseTopLevel(topLevelPath);\n const resumable = topInfo.systemLines > 0;\n const decodedPath = smartDecodeDir(encodedDir) ?? encodedDir.replace(/-/g, \"/\");\n\n // Check sessions/ transcript for prompt and title\n const sessionJsonlPath = join(projectDir, \"sessions\", `${uuid}.jsonl`);\n const hasTranscript = existsSync(sessionJsonlPath);\n const transcript: TranscriptInfo = hasTranscript\n ? parseTranscript(sessionJsonlPath)\n : { userLines: 0, lastUserPrompt: \"\", msgCount: 0, mtime: 0 };\n\n matches.push({\n uuid,\n shortId: uuid.slice(0, 8),\n encodedDir,\n decodedPath,\n topLevelPath,\n topLevelSystemLines: topInfo.systemLines,\n topLevelSize: topInfo.size,\n resumable,\n sessionStatus: resumable ? \"resumable\" : \"stub\",\n sessionJsonlPath: hasTranscript ? sessionJsonlPath : undefined,\n userLines: transcript.userLines,\n lastUserPrompt: transcript.lastUserPrompt,\n msgCount: transcript.msgCount,\n aiTitle: transcript.aiTitle,\n mtime: topInfo.mtime || transcript.mtime,\n friendlyName: transcript.aiTitle ?? basename(decodedPath),\n clcDirectory: undefined,\n registryRootPath: undefined,\n });\n }\n }\n\n return matches.sort((a, b) => b.mtime - a.mtime);\n}\n\n/**\n * Resolve a name-or-id-or-prefix to a single ScannedSession.\n *\n * Comparisons are case-insensitive; stored casing is preserved in output.\n *\n * Priority:\n * 1. Exact case-insensitive match on friendlyName\n * 2. Partial case-insensitive match (contains)\n * 3. UUID prefix match against the in-memory catalog\n * 4. UUID prefix match against the full filesystem (fallback for any session)\n */\nexport function resolveSessionByNameOrId(\n sessions: ScannedSession[],\n query: string\n): ResolvedSession {\n const qLower = query.toLowerCase().trim();\n\n // 1. Exact match (case-insensitive, preserve stored casing)\n const byExact = sessions.filter(\n (s) => s.friendlyName && s.friendlyName.toLowerCase() === qLower\n );\n if (byExact.length >= 1) {\n return { session: byExact[0], friendlyName: byExact[0].friendlyName };\n }\n\n // 2. Partial match\n const byPartial = sessions.filter(\n (s) => s.friendlyName && s.friendlyName.toLowerCase().includes(qLower)\n );\n if (byPartial.length === 1) {\n return { session: byPartial[0], friendlyName: byPartial[0].friendlyName };\n }\n if (byPartial.length > 1) {\n const candidates = byPartial\n .slice(0, 5)\n .map(\n (s, i) =>\n ` ${i + 1}. ${s.shortId} ${s.friendlyName ?? s.decodedPath} (${fmtAge(s.mtime)} ago)`\n )\n .join(\"\\n\");\n throw new Error(\n `Ambiguous name \"${query}\" — ${byPartial.length} matches:\\n${candidates}\\n\\nBe more specific or use a UUID prefix.`\n );\n }\n\n // 3. UUID prefix against catalog\n const byUuid = sessions.filter((s) => s.uuid.startsWith(qLower));\n if (byUuid.length === 1) {\n return { session: byUuid[0], friendlyName: byUuid[0].friendlyName };\n }\n if (byUuid.length > 1) {\n const candidates = byUuid\n .slice(0, 5)\n .map(\n (s, i) =>\n ` ${i + 1}. ${s.shortId} ${s.friendlyName ?? s.decodedPath} (${fmtAge(s.mtime)} ago)`\n )\n .join(\"\\n\");\n throw new Error(\n `UUID prefix \"${query}\" is ambiguous — ${byUuid.length} matches:\\n${candidates}\\n\\nProvide more characters.`\n );\n }\n\n // 4. Filesystem fallback — scan ALL ~/.claude/projects/*/<uuid>.jsonl files\n // This handles any session UUID, even if not in the named/recent catalog.\n // Only attempted when the query looks like a UUID prefix (hex + dashes).\n const UUID_PREFIX_RE = /^[0-9a-f-]{4,36}$/i;\n if (UUID_PREFIX_RE.test(qLower)) {\n const fsSessions = scanFilesystemForUuidPrefix(qLower);\n if (fsSessions.length === 1) {\n return { session: fsSessions[0], friendlyName: fsSessions[0].friendlyName };\n }\n if (fsSessions.length > 1) {\n const candidates = fsSessions\n .slice(0, 5)\n .map(\n (s, i) =>\n ` ${i + 1}. ${s.shortId} ${s.friendlyName ?? s.decodedPath} (${fmtAge(s.mtime)} ago)`\n )\n .join(\"\\n\");\n throw new Error(\n `UUID prefix \"${query}\" is ambiguous — ${fsSessions.length} matches:\\n${candidates}\\n\\nProvide more characters.`\n );\n }\n }\n\n throw new Error(\n `No session found matching \"${query}\".\\n\\nRun: pai sessions to list sessions.\\nRun: pai sessions --all to include transcript-only sessions.\\nRun: pai find <words> to search prompt history.`\n );\n}\n","/**\n * aibroker-client.ts — Lightweight IPC client for AIBroker daemon.\n *\n * Connects to the AIBroker Unix Domain Socket, sends a JSON-RPC request,\n * and reads a single newline-terminated JSON response. No class needed —\n * just a thin async function matching the WatcherClient protocol.\n *\n * Socket path: /tmp/aibroker.sock (default; override via AIBROKER_SOCKET env).\n */\n\nimport { connect } from \"node:net\";\nimport { randomUUID } from \"node:crypto\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Lightweight session metadata returned by the AIBroker `sessions` IPC method.\n * Does NOT include scrollback content — use `session_content` for that.\n */\nexport interface AiBrokerSessionMeta {\n index: number;\n sessionId: string;\n /** iTerm2 tab title or profile name */\n name: string;\n /** PAI session name set via /Name; null for bare shells */\n paiName: string | null;\n atPrompt: boolean;\n /** \"claude\" for Claude Code panes, \"shell\" for bare terminals */\n kind: \"claude\" | \"shell\";\n /** Whether this is the currently focused pane */\n active: boolean;\n}\n\ninterface AiBrokerSessionsResult {\n sessions: AiBrokerSessionMeta[];\n}\n\n// ---------------------------------------------------------------------------\n// Core call\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_SOCKET = process.env.AIBROKER_SOCKET ?? \"/tmp/aibroker.sock\";\n\n/**\n * Call an AIBroker IPC method and return the result.\n *\n * Resolves with the `result` field of a successful response.\n * Rejects if the socket is not available, the call times out, or the\n * daemon returns an error.\n *\n * @param method IPC method name (e.g. \"session_content\", \"send_to_session\")\n * @param params Method parameters object\n * @param timeoutMs Connection + response timeout in milliseconds (default: 8 000)\n */\nexport function callAiBroker(\n method: string,\n params: Record<string, unknown> = {},\n timeoutMs = 8_000\n): Promise<Record<string, unknown>> {\n return new Promise((resolve, reject) => {\n const socketPath = DEFAULT_SOCKET;\n let done = false;\n let buffer = \"\";\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n function finish(err: Error | null, value?: Record<string, unknown>): void {\n if (done) return;\n done = true;\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n try {\n socket.destroy();\n } catch {\n /* ignore */\n }\n if (err) reject(err);\n else resolve(value!);\n }\n\n const socket = connect(socketPath, () => {\n const request = {\n id: randomUUID(),\n sessionId: process.env.TERM_SESSION_ID ?? \"pai-cli\",\n method,\n params,\n };\n const itermId = process.env.ITERM_SESSION_ID;\n if (itermId) Object.assign(request, { itermSessionId: itermId });\n socket.write(JSON.stringify(request) + \"\\n\");\n });\n\n socket.on(\"data\", (chunk: Buffer) => {\n buffer += chunk.toString(\"utf8\");\n const nl = buffer.indexOf(\"\\n\");\n if (nl === -1) return;\n const line = buffer.slice(0, nl);\n\n let response: { ok: boolean; result?: Record<string, unknown>; error?: string };\n try {\n response = JSON.parse(line);\n } catch {\n finish(new Error(`AIBroker IPC parse error: ${line.slice(0, 120)}`));\n return;\n }\n\n if (!response.ok) {\n finish(new Error(response.error ?? \"AIBroker IPC call failed\"));\n } else {\n finish(null, response.result ?? {});\n }\n });\n\n socket.on(\"error\", (e: NodeJS.ErrnoException) => {\n if (e.code === \"ENOENT\" || e.code === \"ECONNREFUSED\") {\n finish(new Error(\"AIBroker not running (socket not found).\"));\n } else {\n finish(e);\n }\n });\n\n socket.on(\"end\", () => {\n if (!done) finish(new Error(\"AIBroker IPC connection closed before response.\"));\n });\n\n timer = setTimeout(\n () => finish(new Error(\"AIBroker IPC call timed out.\")),\n timeoutMs\n );\n });\n}\n\n// ---------------------------------------------------------------------------\n// Typed helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Fetch all live iTerm2 session metadata from AIBroker via the `sessions` method.\n * Returns an empty array if AIBroker is not running.\n *\n * This is metadata-only (no scrollback). It is faster than `session_content`\n * and the correct source for listing/routing purposes.\n */\nexport async function fetchLiveSessions(): Promise<AiBrokerSessionMeta[]> {\n try {\n const result = await callAiBroker(\"sessions\", {});\n const sessions = (result as unknown as AiBrokerSessionsResult).sessions;\n if (!Array.isArray(sessions)) return [];\n return sessions;\n } catch {\n return [];\n }\n}\n\n/**\n * Send text to a specific AIBroker session by its iTerm2 sessionId.\n */\nexport async function sendToSession(\n sessionId: string,\n text: string\n): Promise<{ ok: boolean; error?: string }> {\n try {\n await callAiBroker(\"send_to_session\", { sessionId, text });\n return { ok: true };\n } catch (e) {\n return { ok: false, error: String(e) };\n }\n}\n","/**\n * pai session recent [-n N] [--all] [--json]\n *\n * List named sessions — resumable OR in the clc registry.\n * Default: \"named\" filter (resumable + stubs + transcript-only from clc registry).\n * --all: also include unnamed orphan sessions (only in sessions/ subdir, not in registry).\n *\n * Status column (always shown):\n * resumable — top-level jsonl with system snapshot (claude --resume works) — green\n * stub — in registry, top-level exists but no system lines (Ctrl+C exit) — yellow\n * transcript-only — in registry but no top-level jsonl — yellow\n * orphan — not in registry, only transcript exists — dim (--all only)\n *\n * When AIBroker is running, a \"Live Sessions\" section is shown first,\n * listing currently-active iTerm2 panes that have an active Claude session.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport chalk from \"chalk\";\nimport { renderTable, err, dim, header } from \"../../utils.js\";\nimport { scanSessions, fmtAge, type SessionStatus } from \"../../lib/session-scan.js\";\nimport { fetchLiveSessions, type AiBrokerSessionMeta } from \"../../lib/aibroker-client.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction fmtSize(bytes: number): string {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1_048_576) return `${Math.round(bytes / 1024)}K`;\n return `${(bytes / 1_048_576).toFixed(1)}M`;\n}\n\nfunction fmtStatus(status: SessionStatus): string {\n switch (status) {\n case \"resumable\":\n return chalk.green(\"resumable\");\n case \"stub\":\n return chalk.yellow(\"stub\");\n case \"transcript-only\":\n return chalk.yellow(\"transcript-only\");\n case \"orphan\":\n return chalk.dim(\"orphan\");\n }\n}\n\n// ---------------------------------------------------------------------------\n// Live-sessions rendering (AIBroker integration)\n// ---------------------------------------------------------------------------\n\nfunction renderLiveSessions(allLiveSessions: AiBrokerSessionMeta[], allTabs: boolean): void {\n // Default: Claude sessions only (kind === \"claude\"). --all-tabs includes shells.\n const displayed = allTabs\n ? allLiveSessions\n : allLiveSessions.filter((s) => s.kind !== \"shell\");\n const skippedCount = allLiveSessions.length - displayed.length;\n\n if (displayed.length === 0) return;\n\n console.log(\"\\n\" + header(\"Live Sessions\") + \"\\n\");\n\n const liveHeaders = [\"#\", \"id\", \"name\", \"at prompt\", \"kind\"];\n const liveRows = displayed.map((s, idx) => {\n const shortId = s.sessionId.slice(0, 8);\n // Prefer paiName for Claude sessions, fall back to iTerm2 tab name\n const rawName = s.paiName ?? s.name;\n const name = rawName.length > 36 ? rawName.slice(0, 35) + \"…\" : rawName;\n const atPrompt = s.atPrompt ? chalk.green(\"yes\") : chalk.yellow(\"busy\");\n const kind = s.kind === \"claude\"\n ? chalk.cyan(s.kind)\n : chalk.dim(s.kind);\n\n return [\n chalk.dim(String(idx + 1)),\n chalk.cyan(shortId),\n name,\n atPrompt,\n kind,\n ];\n });\n\n console.log(renderTable(liveHeaders, liveRows));\n\n if (skippedCount > 0) {\n console.log(\n dim(` (${skippedCount} shell tab${skippedCount === 1 ? \"\" : \"s\"} hidden — use --all-tabs to show)`)\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport async function cmdRecent(\n db: Database,\n opts: { n?: string; all?: boolean; allTabs?: boolean; json?: boolean }\n): Promise<void> {\n const limit = parseInt(opts.n ?? \"20\", 10);\n const includeAll = opts.all === true;\n const allTabs = opts.allTabs === true;\n\n // Fetch live sessions from AIBroker (silently no-ops if not running).\n const liveSessions = await fetchLiveSessions();\n\n const sessions = scanSessions(db, {\n limit,\n filter: includeAll ? \"all\" : \"named\",\n });\n\n if (opts.json) {\n const output = {\n live: liveSessions.map((s) => ({\n index: s.index,\n sessionId: s.sessionId,\n name: s.name,\n paiName: s.paiName,\n atPrompt: s.atPrompt,\n kind: s.kind,\n active: s.active,\n })),\n paused: sessions.map((s, idx) => ({\n idx: idx + 1,\n uuid: s.uuid,\n shortId: s.shortId,\n resumable: s.resumable,\n sessionStatus: s.sessionStatus,\n age: fmtAge(s.mtime),\n mtime: s.mtime,\n name: s.friendlyName,\n lastUserPrompt: s.lastUserPrompt,\n userLines: s.userLines,\n msgCount: s.msgCount,\n topLevelSize: s.topLevelSize,\n decodedPath: s.decodedPath,\n })),\n };\n console.log(JSON.stringify(output, null, 2));\n return;\n }\n\n // ── Live section ──────────────────────────────────────────────────────────\n const hasClaudeLive = liveSessions.some((s) => s.kind === \"claude\");\n if (liveSessions.length > 0) {\n renderLiveSessions(liveSessions, allTabs);\n }\n\n // ── Paused / disk-scan section ────────────────────────────────────────────\n if (sessions.length === 0) {\n if (!hasClaudeLive) {\n // Nothing at all\n if (includeAll) {\n console.log(err(\"No sessions found in ~/.claude/projects/.\"));\n } else {\n console.log(\n err(\n \"No named sessions found.\\n\\n\" +\n \" Named sessions appear when you have entries in ~/.claude/session.json\\n\" +\n \" (set via /Name inside Claude Code) or resumable top-level jsonl files.\\n\" +\n \" Run: pai session recent --all to list all sessions including unnamed orphans.\"\n )\n );\n }\n }\n // If we had live Claude sessions, nothing more to print.\n return;\n }\n\n const title = includeAll\n ? \"Paused / All Sessions (named + orphans)\"\n : \"Paused / Resumable Sessions\";\n console.log(\"\\n\" + header(title) + \"\\n\");\n\n const headers = [\"#\", \"id\", \"age\", \"name / project\", \"last prompt\", \"msgs\", \"size\", \"status\"];\n\n const rows = sessions.map((s, idx) => {\n const name = s.friendlyName ?? s.decodedPath;\n const prompt = s.lastUserPrompt\n ? chalk.dim(\n s.lastUserPrompt.length > 40\n ? s.lastUserPrompt.slice(0, 39) + \"…\"\n : s.lastUserPrompt\n )\n : dim(\"—\");\n\n return [\n chalk.dim(String(idx + 1)),\n chalk.cyan(s.shortId),\n dim(fmtAge(s.mtime)),\n name.length > 26 ? name.slice(0, 25) + \"…\" : name,\n prompt,\n s.userLines > 0 ? String(s.userLines) : dim(\"—\"),\n dim(fmtSize(s.topLevelSize)),\n fmtStatus(s.sessionStatus),\n ];\n });\n\n console.log(renderTable(headers, rows));\n\n console.log(\n \"\\n\" +\n dim(\"Go to a session: \") +\n chalk.white(\"pai resume <name>\") +\n \"\\n\" +\n (hasClaudeLive\n ? dim(\"Pause all live: \") + chalk.white(\"pai pause all\") + \"\\n\"\n : \"\") +\n (includeAll\n ? \"\"\n : dim(\"Show all sessions: \") +\n chalk.white(\"pai sessions --all\") +\n \"\\n\")\n );\n}\n","/**\n * pai resume <name> / pai sessions goto <name> [--dry-run]\n *\n * Smart session launcher with auto-fallback:\n *\n * 1. Resolve name → (uuid?, projectDir, friendlyName)\n * 2. chdir(projectDir)\n * 3. If uuid is set:\n * a. Probe: claude --resume <uuid> --print --output-format=json \"_\"\n * (timeout 5s; exits non-zero / logs \"No conversation found\" if invalid)\n * b. If probe succeeds:\n * exec claude --resume <uuid> --name \"<friendlyName>\" \"/Name <friendlyName>\\ngo\"\n * c. Else (probe failed):\n * print clear stderr line: \"Resume failed. Starting fresh session in same dir.\"\n * exec claude --name \"<friendlyName>\" \"/Name <friendlyName>\\ngo\"\n * 4. Else (no uuid known):\n * exec claude --name \"<friendlyName>\" \"/Name <friendlyName>\\ngo\"\n *\n * Why both --name AND /Name?\n * --name <friendlyName> → sets Claude Code's internal session label\n * \"/Name <friendlyName>\\ngo\" → runs the /Name slash command via AIBroker, which updates\n * iTerm tab title, statusline, and AIBroker session registry;\n * the \\ngo on the next line triggers PAI's ## Continue resume\n *\n * The probe adds ~300ms on the happy path. On the fallback path, total latency is the same\n * as a fresh start.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport { spawnSync } from \"node:child_process\";\nimport { realpathSync } from \"node:fs\";\nimport chalk from \"chalk\";\nimport { err } from \"../../utils.js\";\nimport {\n scanSessions,\n resolveSessionByNameOrId,\n fmtAge,\n type ScannedSession,\n} from \"../../lib/session-scan.js\";\n\n// ---------------------------------------------------------------------------\n// Probe helper\n// ---------------------------------------------------------------------------\n\ninterface ProbeResult {\n ok: boolean;\n reason?: string;\n}\n\n/**\n * Run claude --resume <uuid> --print --output-format=json \"_\" in the given cwd.\n * Returns ok=true if the session is resumable (exit 0 and no \"No conversation found\"\n * in stderr). Timeout: 5 seconds.\n */\nfunction probeResume(uuid: string, cwd: string): ProbeResult {\n const result = spawnSync(\n \"claude\",\n [\"--resume\", uuid, \"--print\", \"--output-format=json\", \"_\"],\n {\n cwd,\n timeout: 5_000,\n env: process.env,\n // Capture stderr for inspection; suppress stdout (tty)\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\n }\n );\n\n if (result.error) {\n return { ok: false, reason: `spawn error: ${result.error.message}` };\n }\n\n const stderr = result.stderr?.toString(\"utf8\") ?? \"\";\n\n if (\n stderr.toLowerCase().includes(\"no conversation found\") ||\n stderr.toLowerCase().includes(\"session not found\")\n ) {\n return { ok: false, reason: \"No conversation found for this UUID\" };\n }\n\n if (result.status !== 0) {\n return {\n ok: false,\n reason: `claude exited ${result.status ?? \"signal\"}${stderr ? `: ${stderr.slice(0, 120).trim()}` : \"\"}`,\n };\n }\n\n return { ok: true };\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport function cmdGoto(\n db: Database,\n query: string,\n opts: { dryRun?: boolean }\n): void {\n // ---- 1. Resolve name → session ----\n const allSessions = scanSessions(db, { limit: 500, filter: \"named\" });\n\n let resolved;\n try {\n resolved = resolveSessionByNameOrId(allSessions, query);\n } catch (resolveErr) {\n let msg = String(resolveErr).replace(/^Error: /, \"\");\n // Append pai find suggestion for \"not found\" errors\n if (msg.includes(\"No session found matching\")) {\n msg += `\\n\\nTip: pai find \"${query}\" — search prompt history by keywords`;\n }\n console.error(err(msg));\n process.exit(1);\n }\n\n const { session: matchedSession, friendlyName } = resolved;\n\n // ---- 2. Find best resumable UUID for this project ----\n let resumableUuid: string | undefined;\n let resumableSession: ScannedSession | undefined;\n\n if (matchedSession.resumable) {\n resumableUuid = matchedSession.uuid;\n resumableSession = matchedSession;\n } else if (matchedSession.encodedDir) {\n // Find the most-recently-modified resumable session in the same project dir\n const sameProject = allSessions.filter(\n (s) => s.encodedDir === matchedSession.encodedDir && s.resumable\n );\n sameProject.sort((a, b) => b.mtime - a.mtime);\n if (sameProject.length > 0) {\n resumableSession = sameProject[0];\n resumableUuid = resumableSession.uuid;\n }\n }\n\n // ---- 3. Determine project dir (realpathSync for --resume cwd correctness) ----\n const rawDir =\n matchedSession.clcDirectory ??\n matchedSession.registryRootPath ??\n matchedSession.decodedPath;\n\n let projectDir: string;\n try {\n projectDir = realpathSync(rawDir);\n } catch {\n console.error(\n err(\n `session \"${query}\": directory does not exist or cannot be resolved.\\n` +\n ` Registry says: ${rawDir}\\n` +\n ` The directory may have moved or been deleted.`\n )\n );\n process.exit(1);\n }\n\n // ---- 4. Build argv components ----\n const name = friendlyName ?? query;\n // Initial prompt: /Name sets tab/statusline via AIBroker; \\ngo triggers ## Continue\n const promptArg = `/Name ${name}\\ngo`;\n\n // ---- 5. Dry-run mode ----\n if (opts.dryRun) {\n if (resumableUuid) {\n const argvResume = `claude --resume ${resumableUuid} --name \"${name}\" \"/Name ${name}\\\\ngo\"`;\n const argvFresh = `claude --name \"${name}\" \"/Name ${name}\\\\ngo\"`;\n console.log(\n \"\\n\" + chalk.bold(\"Dry run — would probe then exec (RESUME path):\") + \"\\n\"\n );\n console.log(` cwd: ${chalk.cyan(projectDir)}`);\n console.log(` probe: claude --resume ${resumableUuid} --print --output-format=json \"_\"`);\n console.log(` argv: ${chalk.white(argvResume)}`);\n console.log(` fallback: ${chalk.yellow(argvFresh)}`);\n if (resumableSession) {\n console.log(`\\n uuid: ${resumableSession.uuid}`);\n console.log(` age: ${fmtAge(resumableSession.mtime)}`);\n console.log(` status: ${resumableSession.sessionStatus}`);\n console.log(` sys: ${resumableSession.topLevelSystemLines} system lines`);\n }\n } else {\n const argvFresh = `claude --name \"${name}\" \"/Name ${name}\\\\ngo\"`;\n console.log(\n \"\\n\" + chalk.bold(\"Dry run — would exec (FRESH path, no resumable UUID):\") + \"\\n\"\n );\n console.log(` cwd: ${chalk.cyan(projectDir)}`);\n console.log(` argv: ${chalk.white(argvFresh)}`);\n }\n console.log();\n return;\n }\n\n // ---- 6. Live execution ----\n if (resumableUuid) {\n // Probe first\n const probe = probeResume(resumableUuid, projectDir);\n\n if (probe.ok) {\n // Happy path: session is resumable\n const result = spawnSync(\n \"claude\",\n [\"--resume\", resumableUuid, \"--name\", name, promptArg],\n {\n cwd: projectDir,\n stdio: \"inherit\",\n env: process.env,\n }\n );\n if (result.error) {\n console.error(err(`Failed to launch claude: ${result.error.message}`));\n process.exit(1);\n }\n process.exit(result.status ?? 0);\n } else {\n // Fallback path\n process.stderr.write(\n chalk.yellow(\n `\\n Resume failed for ${resumableUuid.slice(0, 8)}: ${probe.reason ?? \"unknown error\"}\\n` +\n ` Starting fresh session in same directory.\\n\\n`\n )\n );\n const result = spawnSync(\n \"claude\",\n [\"--name\", name, promptArg],\n {\n cwd: projectDir,\n stdio: \"inherit\",\n env: process.env,\n }\n );\n if (result.error) {\n console.error(err(`Failed to launch claude: ${result.error.message}`));\n process.exit(1);\n }\n process.exit(result.status ?? 0);\n }\n } else {\n // No UUID at all — fresh start\n const result = spawnSync(\n \"claude\",\n [\"--name\", name, promptArg],\n {\n cwd: projectDir,\n stdio: \"inherit\",\n env: process.env,\n }\n );\n if (result.error) {\n console.error(err(`Failed to launch claude: ${result.error.message}`));\n process.exit(1);\n }\n process.exit(result.status ?? 0);\n }\n}\n","/**\n * pai session pause [--dry-run]\n *\n * Writes a \"## Continue\" checkpoint to the project's TODO.md, then prints a\n * prominent reminder about HOW to exit Claude Code safely.\n *\n * The write logic mirrors cmdHandover exactly — it finds the project by cwd,\n * finds (or creates) TODO.md, and prepends a ## Continue block. The difference\n * is that we do NOT call process.exit() after writing, so the warning is shown.\n *\n * WHY Ctrl+C is dangerous:\n * Claude Code's stop-hook fires only on a clean exit (e.g. /exit inside the\n * Claude prompt, or claude process receiving SIGTERM). When you Ctrl+C from\n * a terminal that's running claude, the stop-hook is bypassed, which means:\n * - The top-level <project>/<uuid>.jsonl does NOT get its final system snapshot.\n * - Claude Code's --resume cannot find the session (0 system lines).\n * - The session becomes orphaned — transcript exists but is unresumable.\n *\n * If the project TODO.md gets its ## Continue checkpoint (written by this\n * command), the *content* can still be recovered. But the *session context*\n * (conversation history) is lost unless you exit cleanly via /exit.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport {\n existsSync,\n readFileSync,\n writeFileSync,\n renameSync,\n mkdirSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\nimport type { ProjectRow, SessionRow } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// TODO.md discovery (same locations as handover.ts)\n// ---------------------------------------------------------------------------\n\nconst HANDOVER_TODO_LOCATIONS = [\n \"Notes/TODO.md\",\n \".claude/Notes/TODO.md\",\n \"tasks/todo.md\",\n \"TODO.md\",\n];\n\nfunction findProjectTodo(\n rootPath: string\n): { path: string; content: string } | null {\n for (const rel of HANDOVER_TODO_LOCATIONS) {\n const full = join(rootPath, rel);\n if (existsSync(full)) {\n try {\n return { path: full, content: readFileSync(full, \"utf8\") };\n } catch {\n // unreadable — try next\n }\n }\n }\n return null;\n}\n\nfunction stripContinueSection(content: string): string {\n const lines = content.split(\"\\n\");\n const startIdx = lines.findIndex((l) => l.trim() === \"## Continue\");\n if (startIdx === -1) return content;\n\n let endIdx = lines.length;\n for (let i = startIdx + 1; i < lines.length; i++) {\n const trimmed = lines[i].trim();\n if (\n trimmed === \"---\" ||\n (trimmed.startsWith(\"##\") && trimmed !== \"## Continue\")\n ) {\n endIdx = i;\n break;\n }\n }\n\n let trailingEnd = endIdx;\n if (trailingEnd < lines.length && lines[trailingEnd].trim() === \"---\") {\n trailingEnd += 1;\n }\n\n const before = lines.slice(0, startIdx);\n const after = lines.slice(trailingEnd);\n while (after.length > 0 && after[0].trim() === \"\") after.shift();\n\n return [...before, ...after].join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport function cmdPause(\n db: Database,\n opts: { dryRun?: boolean }\n): void {\n // ---- 1. Resolve project by cwd ----\n const cwd = process.cwd();\n const project = db\n .prepare(\n `SELECT id, slug, display_name, root_path, encoded_dir\n FROM projects\n WHERE ? LIKE root_path || '%'\n ORDER BY length(root_path) DESC\n LIMIT 1`\n )\n .get(cwd) as ProjectRow | undefined;\n\n if (!project) {\n console.error(\n chalk.red(\"pai session pause: \") +\n \"Current directory is not within a registered PAI project.\\n\" +\n ` cwd: ${cwd}`\n );\n process.exit(1);\n }\n\n // ---- 2. Resolve latest session ----\n const session = db\n .prepare(\n \"SELECT * FROM sessions WHERE project_id = ? ORDER BY number DESC LIMIT 1\"\n )\n .get(project.id) as SessionRow | undefined;\n\n // ---- 3. Find or create TODO.md ----\n const todo = findProjectTodo(project.root_path);\n let todoPath: string;\n let existingContent: string;\n\n if (todo) {\n todoPath = todo.path;\n existingContent = todo.content;\n } else {\n const notesDir = join(project.root_path, \"Notes\");\n if (!opts.dryRun) {\n try {\n if (!existsSync(notesDir)) mkdirSync(notesDir, { recursive: true });\n } catch (mkdirErr) {\n console.error(\n chalk.red(\"pai session pause: \") +\n `Could not create Notes directory: ${String(mkdirErr)}`\n );\n process.exit(1);\n }\n }\n todoPath = join(notesDir, \"TODO.md\");\n existingContent = \"\";\n }\n\n // ---- 4. Build ## Continue block ----\n const timestamp = new Date().toISOString();\n\n let sessionLine: string;\n if (session) {\n const num = String(session.number).padStart(4, \"0\");\n const titlePart = session.title || session.slug || \"Session\";\n sessionLine = `${num} - ${session.date} - ${titlePart}`;\n } else {\n sessionLine = \"Unknown session\";\n }\n\n const continueBlock = [\n \"## Continue\",\n \"\",\n `> **Last session:** ${sessionLine}`,\n `> **Paused at:** ${timestamp}`,\n \">\",\n `> Working directory: ${cwd}. Check the latest session note for details.`,\n \"\",\n \"---\",\n \"\",\n ].join(\"\\n\");\n\n // ---- 5. Prepend, stripping old ## Continue ----\n const stripped = stripContinueSection(existingContent).trimStart();\n const newContent = continueBlock + stripped;\n\n // ---- 6. Write atomically (or dry-run preview) ----\n if (opts.dryRun) {\n console.log(\"\\n\" + chalk.bold(\"Dry run — would write to:\") + \" \" + chalk.cyan(todoPath));\n console.log(chalk.dim(\"─\".repeat(60)));\n // Show just the ## Continue block that would be prepended\n console.log(chalk.dim(continueBlock));\n console.log(chalk.dim(\"─\".repeat(60)));\n } else {\n const tmpPath = `${todoPath}.pause.tmp`;\n try {\n writeFileSync(tmpPath, newContent, \"utf8\");\n renameSync(tmpPath, todoPath);\n } catch (writeErr) {\n try {\n if (existsSync(tmpPath)) renameSync(tmpPath, `${tmpPath}.dead`);\n } catch {\n /* ignore */\n }\n console.error(\n chalk.red(\"pai session pause: \") +\n `Failed to write TODO.md: ${String(writeErr)}`\n );\n process.exit(1);\n }\n console.log(\n chalk.green(\" ## Continue written to: \") + chalk.cyan(todoPath)\n );\n }\n\n // ---- 7. Print the safety warning ----\n const box = [\n \"\",\n chalk.bgRed.white.bold(\n \" IMPORTANT: How to exit safely \"\n ),\n \"\",\n chalk.yellow(\n \" Inside the Claude Code session, type: \" +\n chalk.white.bold(\"/exit\") +\n chalk.yellow(\" (then press Enter)\")\n ),\n \"\",\n chalk.red.bold(\" DO NOT press Ctrl+C.\"),\n \"\",\n chalk.dim(\n \" Ctrl+C bypasses PAI's stop-hook, which means:\"\n ),\n chalk.dim(\" - No project snapshot is written to the top-level jsonl\"),\n chalk.dim(\" - The session becomes orphaned (cannot --resume next time)\"),\n chalk.dim(\n \" - pai session goto will launch a fresh session instead of resuming\"\n ),\n \"\",\n chalk.dim(\" The ## Continue checkpoint in TODO.md is already saved.\"),\n chalk.dim(\" Use /exit to preserve full session resumability.\"),\n \"\",\n ].join(\"\\n\");\n\n console.log(box);\n}\n","/**\n * pai end [--dry-run]\n *\n * Extends `pai pause` with two additional steps:\n * 1. Marks the current session note **Status: Completed** and adds a\n * Completed timestamp — mirrors what `finalizeSessionNote()` does in the\n * hooks lib but without importing the hooks-only module.\n * 2. Prints a slightly more final safe-exit reminder.\n *\n * The TODO.md ## Continue checkpoint is written first (same as `pai pause`),\n * so if the user exits without the session note step succeeding the content\n * checkpoint is still intact.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport {\n existsSync,\n readFileSync,\n writeFileSync,\n readdirSync,\n renameSync,\n mkdirSync,\n} from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { realpathSync } from \"node:fs\";\nimport chalk from \"chalk\";\nimport type { ProjectRow, SessionRow } from \"./types.js\";\nimport { cmdPause } from \"./pause.js\";\n\n// ---------------------------------------------------------------------------\n// Notes-directory resolution (mirrors paths.ts without importing hooks lib)\n// ---------------------------------------------------------------------------\n\n/** Encode a path the same way Claude Code does. */\nfunction encodePath(path: string): string {\n return path\n .replace(/\\//g, \"-\")\n .replace(/\\./g, \"-\")\n .replace(/ /g, \"-\");\n}\n\n/** PAI_DIR — mirrors pai-paths.ts resolution. */\nfunction getPaiDir(): string {\n const envDir = process.env.PAI_DIR;\n if (envDir) {\n try {\n return realpathSync(envDir);\n } catch {\n return envDir;\n }\n }\n return join(homedir(), \".claude\");\n}\n\n/**\n * Find the notes directory for a project — checks local first, then central.\n * Returns null if neither exists (dry-run safe: don't create).\n */\nfunction findNotesDir(\n rootPath: string,\n encodedDir: string\n): string | null {\n // Check local paths first\n for (const rel of [\"Notes\", \"notes\", \".claude/Notes\"]) {\n const p = join(rootPath, rel);\n if (existsSync(p)) return p;\n }\n // Central fallback: ~/.claude/projects/<encoded>/Notes\n const central = join(getPaiDir(), \"projects\", encodedDir, \"Notes\");\n if (existsSync(central)) return central;\n return null;\n}\n\n/**\n * Find the current (latest) session note inside notesDir.\n * Searches YYYY/MM subdirectory (current month, then previous month),\n * then flat notesDir as legacy fallback.\n */\nfunction findLatestNote(notesDir: string): string | null {\n const findIn = (dir: string): string | null => {\n if (!existsSync(dir)) return null;\n const files = readdirSync(dir)\n .filter((f) => /^\\d{3,4}[\\s_-].*\\.md$/.test(f))\n .sort((a, b) => {\n const na = parseInt(a.match(/^(\\d+)/)?.[1] ?? \"0\", 10);\n const nb = parseInt(b.match(/^(\\d+)/)?.[1] ?? \"0\", 10);\n return na - nb;\n });\n return files.length > 0 ? join(dir, files[files.length - 1]) : null;\n };\n\n const now = new Date();\n const year = String(now.getFullYear());\n const month = String(now.getMonth() + 1).padStart(2, \"0\");\n\n const current = findIn(join(notesDir, year, month));\n if (current) return current;\n\n const prev = new Date(now.getFullYear(), now.getMonth() - 1, 1);\n const py = String(prev.getFullYear());\n const pm = String(prev.getMonth() + 1).padStart(2, \"0\");\n const prevFound = findIn(join(notesDir, py, pm));\n if (prevFound) return prevFound;\n\n return findIn(notesDir);\n}\n\n// ---------------------------------------------------------------------------\n// Session-note finalization (mirrors finalizeSessionNote from session-notes.ts)\n// ---------------------------------------------------------------------------\n\nfunction finalizeNote(notePath: string): { finalized: boolean; path: string } {\n const content = readFileSync(notePath, \"utf-8\");\n\n if (content.includes(\"**Status:** Completed\")) {\n return { finalized: false, path: notePath }; // already done\n }\n\n let updated = content.replace(\"**Status:** In Progress\", \"**Status:** Completed\");\n\n if (!updated.includes(\"**Completed:**\")) {\n const completionTime = new Date().toISOString();\n updated = updated.replace(\n \"---\\n\\n## Work Done\",\n `**Completed:** ${completionTime}\\n\\n---\\n\\n## Work Done`\n );\n }\n\n // Write atomically\n const tmp = `${notePath}.end.tmp`;\n writeFileSync(tmp, updated, \"utf-8\");\n renameSync(tmp, notePath);\n\n return { finalized: true, path: notePath };\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport function cmdEnd(db: Database, opts: { dryRun?: boolean }): void {\n // ---- Step 1: Run pause logic (writes ## Continue, prints warning) ----\n // We call cmdPause first; it prints the ## Continue block and the initial\n // safe-exit box. We'll print the end-specific reminder afterwards.\n cmdPause(db, opts);\n\n // ---- Step 2: Locate the project ----\n const cwd = process.cwd();\n const project = db\n .prepare(\n `SELECT id, slug, display_name, root_path, encoded_dir\n FROM projects\n WHERE ? LIKE root_path || '%'\n ORDER BY length(root_path) DESC\n LIMIT 1`\n )\n .get(cwd) as ProjectRow | undefined;\n\n if (!project) {\n // cmdPause already printed an error. Nothing more to do.\n return;\n }\n\n // ---- Step 3: Find notes directory ----\n const notesDir = findNotesDir(project.root_path, project.encoded_dir ?? \"\");\n\n if (!notesDir) {\n console.log(\n chalk.dim(\" (No session note found — notes directory does not exist yet.)\")\n );\n printEndBox(opts.dryRun);\n return;\n }\n\n // ---- Step 4: Find latest session note ----\n const notePath = findLatestNote(notesDir);\n\n if (!notePath) {\n console.log(chalk.dim(\" (No session note found in notes directory.)\"));\n printEndBox(opts.dryRun);\n return;\n }\n\n // ---- Step 5: Mark as completed ----\n if (opts.dryRun) {\n console.log(\n \"\\n\" +\n chalk.bold(\"Dry run — would finalize session note:\") +\n \" \" +\n chalk.cyan(notePath)\n );\n console.log(\n chalk.dim(\n \" Would replace: **Status:** In Progress\\n\" +\n \" with: **Status:** Completed\"\n )\n );\n console.log(\n chalk.dim(\" Would add: **Completed:** <timestamp>\")\n );\n } else {\n const { finalized, path } = finalizeNote(notePath);\n if (finalized) {\n console.log(\n chalk.green(\" Session note finalized: \") + chalk.cyan(basename(path))\n );\n } else {\n console.log(\n chalk.dim(\n ` Session note already marked Completed: ${basename(notePath)}`\n )\n );\n }\n }\n\n // ---- Step 6: Print the more-final exit reminder ----\n printEndBox(opts.dryRun);\n}\n\nfunction printEndBox(dryRun?: boolean): void {\n const label = dryRun ? \" (dry-run)\" : \"\";\n const box = [\n \"\",\n chalk.bgRed.white.bold(\n ` SESSION ENDING${label}: How to exit safely `\n ),\n \"\",\n chalk.yellow(\n \" Inside the Claude Code session, type: \" +\n chalk.white.bold(\"/exit\") +\n chalk.yellow(\" (then press Enter)\")\n ),\n \"\",\n chalk.red.bold(\" DO NOT press Ctrl+C.\"),\n \"\",\n chalk.dim(\" Ctrl+C bypasses PAI's stop-hook, which means:\"),\n chalk.dim(\" - The session note is NOT written to by the stop-hook\"),\n chalk.dim(\" - The session becomes orphaned (cannot --resume next time)\"),\n chalk.dim(\" - The final session summary is never generated\"),\n \"\",\n chalk.dim(\n \" ## Continue checkpoint and session note status are already saved.\"\n ),\n chalk.dim(\" Use /exit to let PAI's stop-hook finalize the session fully.\"),\n \"\",\n ].join(\"\\n\");\n\n console.log(box);\n}\n","/**\n * pai pause all [--exit] [--dry-run]\n *\n * Pause every live Claude Code session that AIBroker knows about.\n *\n * For each live session returned by AIBroker's session_content IPC method:\n * 1. Send \"pause session\" to the iTerm2 pane via send_to_session.\n * 2. Optionally send \"\\n/exit\\n\" after a short delay (--exit flag).\n * 3. Print a summary of what was sent and to which sessions.\n *\n * If AIBroker is not running, prints a clear error and exits.\n * If no live sessions are found, reports \"nothing to pause\".\n */\n\nimport chalk from \"chalk\";\nimport {\n fetchLiveSessions,\n sendToSession,\n type AiBrokerSessionMeta,\n} from \"../../lib/aibroker-client.js\";\nimport { header, dim, ok, err, warn } from \"../../utils.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction sessionLabel(s: AiBrokerSessionMeta): string {\n const name = s.paiName ?? s.name;\n return `${chalk.cyan(s.sessionId.slice(0, 8))} ${chalk.bold(name)}`;\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport async function cmdPauseAll(opts: {\n exit?: boolean;\n dryRun?: boolean;\n wait?: number;\n}): Promise<void> {\n const waitMs = opts.wait ?? 5_000; // ms to wait after \"pause session\" before /exit\n\n // ── Fetch live sessions ────────────────────────────────────────────────────\n let liveSessions: AiBrokerSessionMeta[];\n try {\n liveSessions = await fetchLiveSessions();\n } catch (e) {\n console.error(err(\"AIBroker is not running. Cannot list live sessions.\"));\n console.error(dim(\" Start AIBroker or run `pai pause` from each session manually.\"));\n process.exitCode = 1;\n return;\n }\n\n // ── Filter: Claude sessions only (skip bare shells) ───────────────────────\n const claudeSessions = liveSessions.filter((s) => s.kind === \"claude\");\n const skipped = liveSessions.length - claudeSessions.length;\n\n if (skipped > 0) {\n process.stderr.write(\n `Skipping ${skipped} non-Claude tab${skipped === 1 ? \"\" : \"s\"} (bare shells).\\n`\n );\n }\n\n if (claudeSessions.length === 0) {\n console.log(warn(\"No live Claude sessions found via AIBroker. Nothing to pause.\"));\n return;\n }\n\n // ── Dry-run ───────────────────────────────────────────────────────────────\n if (opts.dryRun) {\n console.log(\"\\n\" + header(\"Dry Run — Would Pause These Sessions\") + \"\\n\");\n for (const s of claudeSessions) {\n console.log(\" \" + sessionLabel(s));\n console.log(dim(' → send: \"pause session\"'));\n if (opts.exit) {\n console.log(dim(` → wait ${waitMs}ms then send: \"/exit\"`));\n }\n }\n console.log();\n return;\n }\n\n // ── Live run ──────────────────────────────────────────────────────────────\n console.log(\n \"\\n\" +\n header(\"Pausing All Live Sessions\") +\n \"\\n\" +\n dim(` ${claudeSessions.length} Claude session(s) via AIBroker`) +\n \"\\n\"\n );\n\n const results: Array<{ session: AiBrokerSessionMeta; pauseOk: boolean; exitOk?: boolean; error?: string }> = [];\n\n for (const s of claudeSessions) {\n process.stdout.write(\" \" + sessionLabel(s) + \" … \");\n\n // Send \"pause session\" command\n const pauseResult = await sendToSession(s.sessionId, \"pause session\\n\");\n if (!pauseResult.ok) {\n console.log(err(\"FAILED: \" + (pauseResult.error ?? \"unknown error\")));\n results.push({ session: s, pauseOk: false, error: pauseResult.error });\n continue;\n }\n console.log(ok(\"paused\"));\n results.push({ session: s, pauseOk: true });\n }\n\n // ── Optional /exit after waiting ──────────────────────────────────────────\n if (opts.exit) {\n const pausedSessions = results.filter((r) => r.pauseOk).map((r) => r.session);\n if (pausedSessions.length > 0) {\n console.log(\n \"\\n\" +\n dim(` Waiting ${waitMs / 1000}s for sessions to save state…`)\n );\n await sleep(waitMs);\n\n console.log();\n for (const s of pausedSessions) {\n process.stdout.write(\" \" + sessionLabel(s) + \" exiting … \");\n const exitResult = await sendToSession(s.sessionId, \"/exit\\n\");\n const rec = results.find((r) => r.session.sessionId === s.sessionId)!;\n rec.exitOk = exitResult.ok;\n if (!exitResult.ok) {\n console.log(warn(\"exit failed: \" + (exitResult.error ?? \"unknown\")));\n } else {\n console.log(ok(\"exited\"));\n }\n }\n }\n }\n\n // ── Summary ───────────────────────────────────────────────────────────────\n const total = results.length;\n const succeeded = results.filter((r) => r.pauseOk).length;\n const failed = total - succeeded;\n\n console.log();\n if (failed === 0) {\n console.log(ok(`All ${total} session(s) paused successfully.`));\n } else {\n console.log(\n warn(`${succeeded}/${total} session(s) paused. `) +\n err(`${failed} failed.`)\n );\n for (const r of results.filter((r) => !r.pauseOk)) {\n const label = r.session.paiName ?? r.session.name ?? r.session.sessionId.slice(0, 8);\n console.log(err(` ${label}: ${r.error ?? \"unknown error\"}`));\n }\n }\n\n if (opts.exit) {\n const exitOk = results.filter((r) => r.exitOk).length;\n console.log(dim(` /exit sent to ${exitOk} session(s).`));\n }\n\n console.log();\n}\n","/**\n * pai sessions clear-names\n *\n * Recovery command: wipes corrupted iTerm2 session name state.\n *\n * Two-step reset:\n * 1. Clear ~/.aibroker/session-names.json (the persistent name store).\n * 2. Clear user.paiName variable from all live iTerm2 sessions.\n *\n * After this runs the user can `/Name` each tab fresh. Nothing is\n * set automatically — the user re-asserts names via the /Name skill.\n */\n\nimport { callAiBroker } from \"../../lib/aibroker-client.js\";\nimport { ok, err, dim, warn } from \"../../utils.js\";\n\nexport async function cmdClearNames(opts: { dryRun?: boolean }): Promise<void> {\n if (opts.dryRun) {\n console.log(dim(\"Dry run — would:\"));\n console.log(dim(\" 1. Call AIBroker clear_session_names (wipe session-names.json)\"));\n console.log(dim(\" 2. Call AIBroker clear_pai_names (wipe user.paiName from all iTerm sessions)\"));\n return;\n }\n\n let anyFailed = false;\n\n // Step 1: clear the persistent name store\n process.stdout.write(\"Clearing session-names.json … \");\n try {\n const r1 = await callAiBroker(\"clear_session_names\", {}, 10_000) as {\n cleared?: number;\n };\n const n = r1.cleared ?? 0;\n console.log(ok(`cleared ${n} entr${n === 1 ? \"y\" : \"ies\"}`));\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (msg.includes(\"ENOENT\") || msg.includes(\"ECONNREFUSED\")) {\n console.log(warn(\"AIBroker not running — session-names.json not cleared\"));\n anyFailed = true;\n } else {\n console.log(err(msg));\n anyFailed = true;\n }\n }\n\n // Step 2: clear user.paiName from all live iTerm2 sessions\n process.stdout.write(\"Clearing user.paiName from iTerm2 sessions … \");\n try {\n const r2 = await callAiBroker(\"clear_pai_names\", {}, 30_000) as {\n cleared?: number;\n };\n const n = r2.cleared ?? 0;\n console.log(ok(`cleared ${n} session${n === 1 ? \"\" : \"s\"}`));\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (msg.includes(\"ENOENT\") || msg.includes(\"ECONNREFUSED\")) {\n console.log(warn(\"AIBroker not running — iTerm2 paiName variables not cleared\"));\n anyFailed = true;\n } else {\n console.log(err(msg));\n anyFailed = true;\n }\n }\n\n if (anyFailed) {\n console.log(warn(\"\\nSome steps could not complete. Restart AIBroker and try again.\"));\n process.exitCode = 1;\n } else {\n console.log(ok(\"\\nDone. Use /Name to re-label each tab.\"));\n }\n}\n","/**\n * Commander registration for all `pai sessions` sub-commands (plural namespace).\n */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport {\n cmdList,\n cmdInfo,\n cmdRename,\n cmdSlug,\n cmdTag,\n cmdRoute,\n cmdActive,\n cmdAutoRoute,\n} from \"./commands.js\";\nimport { cmdCheckpoint } from \"./checkpoint.js\";\nimport { cmdHandover } from \"./handover.js\";\nimport { cmdRecent } from \"./recent.js\";\nimport { cmdGoto } from \"./goto.js\";\nimport { cmdPause } from \"./pause.js\";\nimport { cmdEnd } from \"./end.js\";\nimport { cmdPauseAll } from \"./pause-all.js\";\nimport { cmdClearNames } from \"./clear-names.js\";\n\nexport function registerSessionsCommands(\n sessionsCmd: Command,\n getDb: () => Database\n): void {\n // pai sessions list [-n N] [--all] [--json]\n // Default when `pai sessions` is invoked bare (isDefault).\n sessionsCmd\n .command(\"list\", { isDefault: true })\n .description(\n \"Resumable sessions catalog — named sessions with resume status.\\n\" +\n \"Short form: pai sessions (bare, no subcommand)\\n\" +\n \"Use --all to also show unnamed orphan sessions.\"\n )\n .option(\"-n <count>\", \"Maximum sessions to show (default: 20)\", \"20\")\n .option(\"--all\", \"Include unnamed orphan sessions (not in clc registry)\")\n .option(\"--all-tabs\", \"Show all iTerm2 tabs in Live Sessions, including bare shells\")\n .option(\"--json\", \"Output raw JSON instead of formatted table\")\n .action(async (opts: { n?: string; all?: boolean; allTabs?: boolean; json?: boolean }) => {\n await cmdRecent(getDb(), opts);\n });\n\n // pai sessions goto <name-or-id> [--dry-run]\n sessionsCmd\n .command(\"goto <name-or-id>\")\n .description(\n \"Go to a session: resume if a resumable snapshot exists, start fresh otherwise.\\n\" +\n \"Recommended short form: pai resume <name>\\n\" +\n \"Resolves by clc/registry name (case-insensitive) or UUID prefix.\"\n )\n .option(\"--dry-run\", \"Print the exact argv and cwd, then exit without launching\")\n .action(\n (nameOrId: string, opts: { dryRun?: boolean }) => {\n cmdGoto(getDb(), nameOrId, { dryRun: opts.dryRun });\n }\n );\n\n // pai sessions pause [--dry-run]\n sessionsCmd\n .command(\"pause\")\n .description(\n \"Write a ## Continue checkpoint to the project's TODO.md.\\n\" +\n \"Recommended short form: pai pause\\n\" +\n \"Use /exit inside Claude Code to preserve full session resumability.\"\n )\n .option(\"--dry-run\", \"Preview the ## Continue block without writing it\")\n .action((opts: { dryRun?: boolean }) => {\n cmdPause(getDb(), opts);\n });\n\n // pai sessions pause-all [--exit] [--wait <ms>] [--dry-run]\n sessionsCmd\n .command(\"pause-all\")\n .description(\n \"Pause every live Claude Code session via AIBroker.\\n\" +\n \"Requires AIBroker to be running. Sends 'pause session' to each live iTerm2 pane.\\n\" +\n \"Top-level short form: pai pause all\"\n )\n .option(\"--exit\", \"Also send /exit to each session after it has saved state\")\n .option(\"--wait <ms>\", \"Milliseconds to wait before /exit (default: 5000)\", \"5000\")\n .option(\"--dry-run\", \"Show what would be sent without actually sending\")\n .action(async (opts: { exit?: boolean; wait?: string; dryRun?: boolean }) => {\n await cmdPauseAll({\n exit: opts.exit,\n dryRun: opts.dryRun,\n wait: opts.wait !== undefined ? parseInt(opts.wait, 10) : undefined,\n });\n });\n\n // pai sessions end [--dry-run]\n sessionsCmd\n .command(\"end\")\n .description(\n \"Finalize a session: write ## Continue checkpoint + mark session note Completed.\\n\" +\n \"Recommended short form: pai end\\n\" +\n \"Use /exit inside Claude Code after running this command.\"\n )\n .option(\"--dry-run\", \"Preview all changes without writing them\")\n .action((opts: { dryRun?: boolean }) => {\n cmdEnd(getDb(), opts);\n });\n\n // pai sessions info <project-slug> <number>\n sessionsCmd\n .command(\"info <project-slug> <number>\")\n .description(\"Show full details for a specific session\")\n .action((projectSlug: string, number: string) => {\n cmdInfo(getDb(), projectSlug, number);\n });\n\n // pai sessions rename <project-slug> <number> <new-slug>\n sessionsCmd\n .command(\"rename <project-slug> <number> <new-slug>\")\n .description(\"Rename a session note — updates file on disk, H1 title, and registry\")\n .action((projectSlug: string, number: string, newSlug: string) => {\n cmdRename(getDb(), projectSlug, number, newSlug);\n });\n\n // pai sessions slug <project-slug> <number>\n sessionsCmd\n .command(\"slug <project-slug> <number>\")\n .description(\"Generate a descriptive slug from the session JSONL transcript\")\n .option(\"--apply\", \"Rename the session note using the generated slug\")\n .action(\n (projectSlug: string, number: string, opts: { apply?: boolean }) => {\n cmdSlug(getDb(), projectSlug, number, opts);\n }\n );\n\n // pai sessions tag <project-slug> <number> [tags...]\n sessionsCmd\n .command(\"tag <project-slug> <number> [tags...]\")\n .description(\"Set or show tags on a session.\")\n .action((projectSlug: string, number: string, tags: string[]) => {\n cmdTag(getDb(), projectSlug, number, tags);\n });\n\n // pai sessions route <project-slug> <number> <target-project>\n sessionsCmd\n .command(\"route <project-slug> <number> <target-project>\")\n .description(\"Create a cross-reference link from a session to a target project\")\n .option(\"--type <type>\", \"Link type: related | follow-up | reference\", \"related\")\n .action(\n (\n projectSlug: string,\n number: string,\n targetProject: string,\n opts: { type?: string }\n ) => {\n cmdRoute(getDb(), projectSlug, number, targetProject, opts);\n }\n );\n\n // pai sessions handover [project-slug] [session-id]\n sessionsCmd\n .command(\"handover [project-slug] [session-id]\")\n .description(\n \"Write a ## Continue section to the project's TODO.md.\\n\" +\n \"Called automatically from session-stop and pre-compact hooks.\"\n )\n .action(\n (projectSlug: string | undefined, sessionId: string | undefined) => {\n cmdHandover(getDb(), projectSlug, sessionId);\n }\n );\n\n // pai sessions checkpoint <message>\n sessionsCmd\n .command(\"checkpoint <message>\")\n .description(\n \"Append a timestamped checkpoint to the active session note.\\n\" +\n \"Rate-limited: skips silently if last checkpoint was < --min-gap seconds ago.\"\n )\n .option(\n \"--min-gap <seconds>\",\n \"Minimum seconds between checkpoints (default: 300 = 5 minutes)\",\n \"300\"\n )\n .action((message: string, opts: { minGap?: string }) => {\n cmdCheckpoint(message, opts);\n });\n\n // pai sessions active [--minutes N] [--json]\n sessionsCmd\n .command(\"active\")\n .description(\n \"Show currently active Claude Code sessions.\\n\" +\n \"Detects live sessions by checking which JSONL transcript files\\n\" +\n \"were recently modified in ~/.claude/projects/.\"\n )\n .option(\n \"--minutes <n>\",\n \"Consider sessions active if modified within N minutes (default: 60)\",\n \"60\"\n )\n .option(\"--json\", \"Output raw JSON instead of formatted display\")\n .action((opts: { minutes?: string; json?: boolean }) => {\n cmdActive(getDb(), opts);\n });\n\n // pai sessions clear-names [--dry-run]\n sessionsCmd\n .command(\"clear-names\")\n .description(\n \"Recovery: wipe corrupted iTerm2 session name state.\\n\" +\n \"Clears ~/.aibroker/session-names.json AND user.paiName from all live iTerm2 sessions.\\n\" +\n \"After running this, use /Name to re-label each tab.\"\n )\n .option(\"--dry-run\", \"Preview what would be cleared without making changes\")\n .action(async (opts: { dryRun?: boolean }) => {\n await cmdClearNames(opts);\n });\n\n // pai sessions auto-route [--cwd path] [--context \"text\"] [--json]\n sessionsCmd\n .command(\"auto-route\")\n .description(\n \"Auto-detect which project this session belongs to.\\n\" +\n \"Tries: (1) path match in registry, (2) Notes/PAI.md marker walk, (3) topic detection.\"\n )\n .option(\"--cwd <path>\", \"Working directory to detect from (default: process.cwd())\")\n .option(\"--context <text>\", \"Conversation context for topic-based fallback routing\")\n .option(\"--json\", \"Output raw JSON instead of formatted display\")\n .action(\n async (opts: { cwd?: string; context?: string; json?: boolean }) => {\n await cmdAutoRoute(opts);\n }\n );\n}\n","/**\n * Shared types and constants for the session-cleanup command.\n */\n\nexport interface ProjectRow {\n id: number;\n slug: string;\n display_name: string;\n root_path: string;\n encoded_dir: string;\n claude_notes_dir: string | null;\n}\n\nexport interface SessionRow {\n id: number;\n project_id: number;\n number: number;\n date: string;\n slug: string;\n title: string;\n filename: string;\n status: string;\n}\n\nexport type SessionClassification = \"EMPTY\" | \"UNNAMED\" | \"NAMED\" | \"LEGACY_FORMAT\";\n\nexport interface SessionCandidate {\n session: SessionRow | null; // null if file exists on disk but not in DB\n filename: string;\n filepath: string;\n sizeBytes: number;\n classification: SessionClassification;\n autoName?: string; // proposed name for UNNAMED sessions\n date: string;\n number: number;\n}\n\nexport interface NotesDirPlan {\n notesDir: string;\n toDelete: SessionCandidate[];\n toRename: SessionCandidate[];\n toMove: SessionCandidate[]; // survivors that need moving to YYYY/MM/ within this dir\n}\n\nexport interface CleanupPlan {\n project: ProjectRow;\n notesDirs: NotesDirPlan[]; // one entry per discovered Notes/ directory (up to 2)\n renumberMap: Map<number, number>; // old number → new number (global across both dirs)\n}\n\n// Template content indicators — if the file only contains these patterns, delete it.\nexport const TEMPLATE_INDICATORS = [\n \"<!-- PAI will add completed work here during session -->\",\n \"<!-- PAI will add completed work here -->\",\n \"Session completed.\",\n \"Session started and ready for your instructions\",\n];\n\n// Session filename patterns\nexport const MODERN_PATTERN = /^(\\d{4}) - (\\d{4}-\\d{2}-\\d{2}) - (.+)\\.md$/;\nexport const LEGACY_PATTERN = /^(\\d{4})_(\\d{4}-\\d{2}-\\d{2})_(.+)\\.md$/;\n","/**\n * Auto-name extraction and string helpers for session-cleanup.\n * Derives a meaningful session title from Markdown content.\n */\n\n// Meta-phrases indicating template/status text rather than real work.\nconst META_PHRASE_PATTERNS: RegExp[] = [\n /session initialized and ready for your instructions/i,\n /fresh session with no pending tasks/i,\n /starting new session.*checking for pending work/i,\n /fresh session with empty todo/i,\n /session started and ready/i,\n /^session\\b.*\\bready\\b/i,\n /^session\\b.*\\binitialized\\b/i,\n /^session\\b.*\\bno pending\\b/i,\n /^session\\b.*\\bno prior work\\b/i,\n /^no pending tasks/i,\n /^no prior work/i,\n];\n\nconst TITLE_CASE_MINOR_WORDS = new Set([\n \"a\", \"an\", \"the\", \"in\", \"on\", \"at\", \"for\", \"to\", \"of\", \"and\", \"or\",\n \"but\", \"via\", \"with\", \"from\", \"by\", \"as\", \"nor\",\n]);\n\nfunction cleanMarkdownLine(raw: string): string | null {\n let s = raw.trim();\n s = s.replace(/^[-*+]\\s+\\[[ xX]\\]\\s*/, \"\");\n s = s.replace(/^[-*+]\\s+/, \"\");\n s = s.replace(/^\\[[ xX]\\]\\s*/, \"\");\n s = s.replace(/\\*\\*\\s*([^*]+?)\\s*\\*\\*/g, \"$1\");\n s = s.replace(/\\*\\s*([^*]+?)\\s*\\*/g, \"$1\");\n s = s.replace(/`([^`]+)`/g, \"$1\");\n s = s.replace(/^\\*+\\s*/, \"\");\n s = s.replace(/^[.,;:]+/, \"\").replace(/[.,;:]+$/, \"\");\n s = s.replace(/\\s+/g, \" \").trim();\n return s.length >= 4 ? s : null;\n}\n\nfunction isMetaPhrase(text: string): boolean {\n return META_PHRASE_PATTERNS.some((re) => re.test(text));\n}\n\nfunction toTitleCase(text: string): string {\n const words = text.split(\" \");\n return words\n .map((word, i) => {\n const lower = word.toLowerCase();\n if (i !== 0 && TITLE_CASE_MINOR_WORDS.has(lower)) return lower;\n return word.charAt(0).toUpperCase() + word.slice(1);\n })\n .join(\" \");\n}\n\nexport function sanitizeName(raw: string): string {\n let s = raw.replace(/[\\/\\\\:*?\"<>|#`]/g, \"\");\n s = s.replace(/\\s+/g, \" \").trim();\n if (s.length > 60) {\n const truncated = s.slice(0, 60);\n const lastSpace = truncated.lastIndexOf(\" \");\n s = lastSpace > 20 ? truncated.slice(0, lastSpace) : truncated;\n }\n s = s.trim();\n return toTitleCase(s);\n}\n\nexport function extractAutoName(content: string): string {\n const lines = content.split(\"\\n\");\n\n const CONTENT_SECTION_HEADINGS = new Set([\n \"Work Done\", \"Summary\", \"Completed\", \"What Was Done\",\n \"Results\", \"Outcomes\", \"Changes\", \"Progress\",\n ]);\n const SKIP_SECTION_HEADINGS = new Set([\n \"Next Steps\", \"Tags\", \"TODO\", \"Blockers\", \"Notes\",\n \"Metadata\", \"Context\", \"Background\",\n ]);\n\n let pastH1 = false;\n let pastFirstHr = false;\n let currentSection: string | null = null;\n const contentSectionLines: string[] = [];\n const otherH2Headings: string[] = [];\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n if (trimmed.startsWith(\"# \")) {\n pastH1 = true;\n continue;\n }\n if (!pastH1) continue;\n\n if (!pastFirstHr) {\n if (trimmed === \"---\") pastFirstHr = true;\n continue;\n }\n\n if (trimmed.startsWith(\"## \")) {\n const headingText = trimmed.slice(3).trim();\n if (CONTENT_SECTION_HEADINGS.has(headingText)) {\n currentSection = \"content\";\n } else if (SKIP_SECTION_HEADINGS.has(headingText)) {\n currentSection = \"skip\";\n } else {\n currentSection = null;\n otherH2Headings.push(headingText);\n }\n continue;\n }\n\n if (trimmed.startsWith(\"#\")) continue;\n if (trimmed === \"-->\") continue;\n if (trimmed.startsWith(\"<!--\") && /^<!--.*-->$/.test(trimmed)) continue;\n\n if (currentSection === \"content\" && trimmed.length > 0) {\n const withoutComment = trimmed.replace(/^<!--.*?-->\\s*/, \"\");\n const effective = withoutComment.length > 0 ? withoutComment : trimmed;\n if (effective.length === 0) continue;\n contentSectionLines.push(effective);\n }\n }\n\n for (const raw of contentSectionLines) {\n const cleaned = cleanMarkdownLine(raw);\n if (!cleaned) continue;\n if (isMetaPhrase(cleaned)) continue;\n if (cleaned.startsWith(\"<!--\") || cleaned.includes(\"PAI will add\")) continue;\n if (cleaned.length < 5) continue;\n return sanitizeName(cleaned);\n }\n\n for (const heading of otherH2Headings) {\n const cleaned = cleanMarkdownLine(heading);\n if (!cleaned) continue;\n if (isMetaPhrase(cleaned)) continue;\n if (cleaned.length > 3 && cleaned.length < 80) {\n return sanitizeName(cleaned);\n }\n }\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"# \")) {\n const title = trimmed.slice(2).trim();\n if (/^Session \\d{4}/i.test(title)) continue;\n const cleaned = cleanMarkdownLine(title);\n if (cleaned && !isMetaPhrase(cleaned) && cleaned.length > 3) {\n return sanitizeName(cleaned);\n }\n }\n }\n\n return \"Unnamed Session\";\n}\n\n/** Format a 4-digit padded session number. */\nexport function padNum(n: number): string {\n return String(n).padStart(4, \"0\");\n}\n","/**\n * Analysis phase: scan Notes/ directories and build CleanupPlans.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport {\n existsSync,\n readdirSync,\n readFileSync,\n statSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type {\n ProjectRow,\n SessionRow,\n SessionCandidate,\n SessionClassification,\n NotesDirPlan,\n CleanupPlan,\n} from \"./types.js\";\nimport {\n TEMPLATE_INDICATORS,\n MODERN_PATTERN,\n LEGACY_PATTERN,\n} from \"./types.js\";\nimport { extractAutoName, padNum } from \"./rename.js\";\n\n// ---------------------------------------------------------------------------\n// DB helpers\n// ---------------------------------------------------------------------------\n\nexport function getAllProjects(db: Database): ProjectRow[] {\n return db\n .prepare(\n \"SELECT id, slug, display_name, root_path, encoded_dir, claude_notes_dir FROM projects WHERE status = 'active' ORDER BY slug\"\n )\n .all() as ProjectRow[];\n}\n\nexport function getProject(\n db: Database,\n slug: string\n): ProjectRow | undefined {\n return db\n .prepare(\n \"SELECT id, slug, display_name, root_path, encoded_dir, claude_notes_dir FROM projects WHERE slug = ?\"\n )\n .get(slug) as ProjectRow | undefined;\n}\n\nfunction getProjectSessions(\n db: Database,\n projectId: number\n): SessionRow[] {\n return db\n .prepare(\"SELECT * FROM sessions WHERE project_id = ? ORDER BY number ASC\")\n .all(projectId) as SessionRow[];\n}\n\n// ---------------------------------------------------------------------------\n// Notes directory discovery\n// ---------------------------------------------------------------------------\n\nfunction findRootNotesDir(rootPath: string): string | null {\n const canonical = join(rootPath, \"Notes\");\n if (existsSync(canonical)) return canonical;\n const alt = join(rootPath, \".claude\", \"Notes\");\n if (existsSync(alt)) return alt;\n return null;\n}\n\nfunction findClaudeNotesDir(\n project: ProjectRow,\n rootNotesDir: string | null\n): string | null {\n const candidate =\n project.claude_notes_dir ??\n join(homedir(), \".claude\", \"projects\", project.encoded_dir, \"Notes\");\n\n if (!existsSync(candidate)) return null;\n if (rootNotesDir && candidate === rootNotesDir) return null;\n return candidate;\n}\n\nexport function findAllNotesDirs(project: ProjectRow): string[] {\n const rootDir = findRootNotesDir(project.root_path);\n const claudeDir = findClaudeNotesDir(project, rootDir);\n const dirs: string[] = [];\n if (rootDir) dirs.push(rootDir);\n if (claudeDir) dirs.push(claudeDir);\n return dirs;\n}\n\n// ---------------------------------------------------------------------------\n// Content analysis\n// ---------------------------------------------------------------------------\n\nfunction isTemplateOnly(content: string): boolean {\n const hasTemplateMarker = TEMPLATE_INDICATORS.some((ind) =>\n content.includes(ind)\n );\n if (!hasTemplateMarker) return false;\n\n const lines = content.split(\"\\n\");\n let inWorkDone = false;\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed === \"## Work Done\") {\n inWorkDone = true;\n continue;\n }\n if (trimmed.startsWith(\"## \") && inWorkDone) break;\n if (!inWorkDone) continue;\n if (!trimmed) continue;\n if (trimmed.startsWith(\"<!--\") && trimmed.endsWith(\"-->\")) continue;\n if (trimmed.startsWith(\"<!--\")) continue;\n if (trimmed === \"-->\") continue;\n if (trimmed === \"Session completed.\") continue;\n if (trimmed === \"#Session\" || trimmed === \"**Tags:** #Session\") continue;\n return false;\n }\n\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Scan + analysis\n// ---------------------------------------------------------------------------\n\nexport function scanNotesDir(\n notesDir: string,\n dbByFilename: Map<string, SessionRow>\n): SessionCandidate[] {\n const candidates: SessionCandidate[] = [];\n\n let flatFiles: string[] = [];\n try {\n flatFiles = readdirSync(notesDir, { withFileTypes: true })\n .filter((d) => d.isFile() && d.name.endsWith(\".md\"))\n .map((d) => d.name);\n } catch {\n // Directory unreadable\n }\n\n const subDirFiles: { filename: string; filepath: string }[] = [];\n try {\n const topEntries = readdirSync(notesDir, { withFileTypes: true });\n for (const entry of topEntries) {\n if (!entry.isDirectory()) continue;\n if (!/^\\d{4}$/.test(entry.name)) continue;\n const yearDir = join(notesDir, entry.name);\n const monthDirs = readdirSync(yearDir, { withFileTypes: true });\n for (const mEntry of monthDirs) {\n if (!mEntry.isDirectory()) continue;\n const monthDir = join(yearDir, mEntry.name);\n const files = readdirSync(monthDir).filter((f) => f.endsWith(\".md\"));\n for (const f of files) {\n subDirFiles.push({ filename: f, filepath: join(monthDir, f) });\n }\n }\n }\n } catch {\n // Ignore errors scanning sub-dirs\n }\n\n const allFiles: { filename: string; filepath: string }[] = [\n ...flatFiles.map((f) => ({ filename: f, filepath: join(notesDir, f) })),\n ...subDirFiles,\n ];\n\n for (const { filename, filepath } of allFiles) {\n let num: number;\n let date: string;\n let namepart: string;\n let classification: SessionClassification;\n\n const modernMatch = MODERN_PATTERN.exec(filename);\n const legacyMatch = LEGACY_PATTERN.exec(filename);\n\n if (modernMatch) {\n num = parseInt(modernMatch[1], 10);\n date = modernMatch[2];\n namepart = modernMatch[3];\n classification = \"NAMED\";\n } else if (legacyMatch) {\n num = parseInt(legacyMatch[1], 10);\n date = legacyMatch[2];\n namepart = legacyMatch[3];\n classification = \"LEGACY_FORMAT\";\n } else {\n continue; // not a session file\n }\n\n let sizeBytes = 0;\n let content = \"\";\n try {\n sizeBytes = statSync(filepath).size;\n content = readFileSync(filepath, \"utf8\");\n } catch {\n continue;\n }\n\n const dbSession =\n dbByFilename.get(filename) ??\n dbByFilename.get(filepath.split(`${notesDir}/`)[1] ?? \"\") ??\n null;\n\n if (classification !== \"LEGACY_FORMAT\") {\n if (sizeBytes < 400 || isTemplateOnly(content)) {\n classification = \"EMPTY\";\n } else if (\n namepart === \"New Session\" ||\n namepart === (process.env.USER ?? \"\") ||\n namepart === \"session-started-and-ready-for-your-instructions\"\n ) {\n classification = \"UNNAMED\";\n }\n }\n\n const candidate: SessionCandidate = {\n session: dbSession,\n filename,\n filepath,\n sizeBytes,\n classification,\n date,\n number: num,\n };\n\n if (\n classification === \"UNNAMED\" ||\n classification === \"LEGACY_FORMAT\"\n ) {\n candidate.autoName = extractAutoName(content);\n }\n\n candidates.push(candidate);\n }\n\n return candidates;\n}\n\nfunction buildRenumberMap(\n survivors: SessionCandidate[]\n): Map<number, number> {\n const map = new Map<number, number>();\n const sorted = [...survivors].sort((a, b) => a.number - b.number);\n sorted.forEach((s, idx) => {\n const newNum = idx + 1;\n if (s.number !== newNum) map.set(s.number, newNum);\n });\n return map;\n}\n\nexport function analyzeProject(\n db: Database,\n project: ProjectRow\n): CleanupPlan | null {\n const notesDirPaths = findAllNotesDirs(project);\n if (notesDirPaths.length === 0) return null;\n\n const dbSessions = getProjectSessions(db, project.id);\n const dbByFilename = new Map<string, SessionRow>();\n for (const s of dbSessions) dbByFilename.set(s.filename, s);\n\n const notesDirPlans: NotesDirPlan[] = [];\n const allSurvivors: SessionCandidate[] = [];\n\n for (const notesDir of notesDirPaths) {\n const candidates = scanNotesDir(notesDir, dbByFilename);\n if (candidates.length === 0) continue;\n\n const toDelete = candidates.filter((c) => c.classification === \"EMPTY\");\n const toRename = candidates.filter(\n (c) =>\n c.classification === \"UNNAMED\" || c.classification === \"LEGACY_FORMAT\"\n );\n const survivors = candidates.filter((c) => c.classification !== \"EMPTY\");\n\n notesDirPlans.push({ notesDir, toDelete, toRename, toMove: survivors });\n allSurvivors.push(...survivors);\n }\n\n if (notesDirPlans.length === 0) return null;\n\n const renumberMap = buildRenumberMap(allSurvivors);\n\n return { project, notesDirs: notesDirPlans, renumberMap };\n}\n","/**\n * Dry-run display, Postgres vector DB path updates, and cleanup execution.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport {\n existsSync,\n readdirSync,\n readFileSync,\n writeFileSync,\n mkdirSync,\n renameSync,\n unlinkSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\nimport { ok, warn, err, dim, bold, header } from \"../../utils.js\";\nimport type { CleanupPlan, SessionCandidate } from \"./types.js\";\nimport { padNum } from \"./rename.js\";\n\n// ---------------------------------------------------------------------------\n// Postgres helpers\n// ---------------------------------------------------------------------------\n\nasync function countVectorDbPaths(oldPaths: string[]): Promise<number> {\n if (oldPaths.length === 0) return 0;\n try {\n const { loadConfig } = await import(\"../../../daemon/config.js\");\n const { PostgresBackend } = await import(\"../../../storage/postgres.js\");\n const config = loadConfig();\n if (config.storageBackend !== \"postgres\") return 0;\n const pgBackend = new PostgresBackend(config.postgres ?? {});\n const connErr = await pgBackend.testConnection();\n if (connErr) { await pgBackend.close(); return 0; }\n const pool = (pgBackend as unknown as {\n pool: { query: (sql: string, params: string[]) => Promise<{ rows: Array<{ n: string }> }> };\n }).pool;\n const placeholders = oldPaths.map((_, i) => `$${i + 1}`).join(\", \");\n const result = await pool.query(\n `SELECT COUNT(*)::text AS n FROM pai_files WHERE path IN (${placeholders})`,\n oldPaths\n );\n await pgBackend.close();\n return parseInt(result.rows[0]?.n ?? \"0\", 10);\n } catch {\n return 0;\n }\n}\n\nasync function updateVectorDbPaths(\n moves: Array<{ oldPath: string; newPath: string }>\n): Promise<number> {\n if (moves.length === 0) return 0;\n try {\n const { loadConfig } = await import(\"../../../daemon/config.js\");\n const { PostgresBackend } = await import(\"../../../storage/postgres.js\");\n const config = loadConfig();\n if (config.storageBackend !== \"postgres\") return 0;\n const pgBackend = new PostgresBackend(config.postgres ?? {});\n const connErr = await pgBackend.testConnection();\n if (connErr) {\n process.stderr.write(`[session-cleanup] Postgres unavailable (${connErr}). Skipping vector DB path update.\\n`);\n await pgBackend.close();\n return 0;\n }\n const pool = (pgBackend as unknown as {\n pool: { connect: () => Promise<{\n query: (sql: string, params: string[]) => Promise<{ rowCount: number | null }>;\n release: () => void;\n }> };\n }).pool;\n const client = await pool.connect();\n let filesUpdated = 0;\n try {\n await client.query(\"BEGIN\", []);\n for (const { oldPath, newPath } of moves) {\n const r = await client.query(\"UPDATE pai_files SET path = $1 WHERE path = $2\", [newPath, oldPath]);\n filesUpdated += r.rowCount ?? 0;\n await client.query(\"UPDATE pai_chunks SET path = $1 WHERE path = $2\", [newPath, oldPath]);\n }\n await client.query(\"COMMIT\", []);\n } catch (e) {\n await client.query(\"ROLLBACK\", []);\n throw e;\n } finally {\n client.release();\n }\n await pgBackend.close();\n return filesUpdated;\n } catch (e) {\n process.stderr.write(`[session-cleanup] Failed to update vector DB paths: ${e}\\n`);\n return -1;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Dry-run display\n// ---------------------------------------------------------------------------\n\nexport async function displayDryRun(plans: CleanupPlan[]): Promise<void> {\n let totalDelete = 0;\n let totalRename = 0;\n let totalMove = 0;\n let totalRenumber = 0;\n\n for (const plan of plans) {\n const hasWork =\n plan.notesDirs.some(\n (d) => d.toDelete.length > 0 || d.toRename.length > 0 || d.toMove.length > 0\n ) || plan.renumberMap.size > 0;\n if (!hasWork) continue;\n\n console.log();\n console.log(\n header(` Project: ${plan.project.display_name} (${plan.project.slug})`)\n );\n\n for (const dirPlan of plan.notesDirs) {\n console.log(dim(` Notes: ${dirPlan.notesDir}`));\n\n if (dirPlan.toDelete.length > 0) {\n console.log(bold(\" DELETE (empty/template-only sessions):\"));\n for (const c of dirPlan.toDelete) {\n console.log(\n ` ${chalk.red(\"DEL\")} ${dim(padNum(c.number))} - ${c.date} - ${\n c.filename.split(\" - \").slice(2).join(\" - \")\n } ${dim(`(${c.sizeBytes}b)`)}`\n );\n totalDelete++;\n }\n console.log();\n }\n\n if (dirPlan.toRename.length > 0) {\n console.log(bold(\" RENAME (unnamed or legacy-format sessions):\"));\n for (const c of dirPlan.toRename) {\n const autoName = c.autoName ?? \"Unnamed Session\";\n console.log(` ${chalk.yellow(\"REN\")} ${c.filename}`);\n console.log(` → ${padNum(c.number)} - ${c.date} - ${autoName}.md`);\n totalRename++;\n }\n console.log();\n }\n\n if (dirPlan.toMove.length > 0) {\n console.log(bold(\" MOVE TO YYYY/MM/ hierarchy:\"));\n for (const c of dirPlan.toMove) {\n const [year, month] = c.date.split(\"-\");\n console.log(` ${chalk.cyan(\"MOV\")} ${c.filename}`);\n console.log(` → ${year}/${month}/${c.filename}`);\n totalMove++;\n }\n console.log();\n }\n }\n\n if (plan.renumberMap.size > 0) {\n console.log(\n bold(\" RENUMBER (after deletions, global across all Notes/ dirs):\")\n );\n for (const [oldN, newN] of plan.renumberMap) {\n console.log(\n ` ${chalk.blue(\"NUM\")} #${padNum(oldN)} → #${padNum(newN)}`\n );\n totalRenumber++;\n }\n console.log();\n }\n }\n\n const wouldMovePaths: string[] = [];\n for (const plan of plans) {\n for (const dirPlan of plan.notesDirs) {\n for (const c of dirPlan.toMove) {\n const [year, month] = c.date.split(\"-\");\n const targetPath = join(dirPlan.notesDir, year, month, c.filename);\n if (c.filepath !== targetPath) wouldMovePaths.push(c.filepath);\n }\n }\n }\n\n const vectorDbCount = await countVectorDbPaths(wouldMovePaths);\n\n console.log();\n console.log(bold(\" Summary (dry-run):\"));\n console.log(` ${chalk.red(\"DEL\")} ${totalDelete} empty sessions to delete`);\n console.log(` ${chalk.yellow(\"REN\")} ${totalRename} unnamed sessions to rename`);\n console.log(` ${chalk.blue(\"NUM\")} ${totalRenumber} sessions to renumber`);\n console.log(` ${chalk.cyan(\"MOV\")} ${totalMove} sessions to move into YYYY/MM/ dirs`);\n if (vectorDbCount > 0) {\n console.log(\n ` ${chalk.magenta(\"VEC\")} ${vectorDbCount} file path(s) will be updated in the vector DB (embeddings preserved)`\n );\n } else if (wouldMovePaths.length > 0) {\n console.log(\n ` ${chalk.magenta(\"VEC\")} 0 file path(s) found in vector DB for moved files (no embeddings to preserve)`\n );\n }\n console.log();\n console.log(warn(\" This is a dry-run. Add --execute to apply changes.\"));\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// Execution\n// ---------------------------------------------------------------------------\n\nexport async function executeCleanup(\n db: Database,\n plans: CleanupPlan[],\n skipReindex: boolean\n): Promise<void> {\n let deleted = 0;\n let renamed = 0;\n let moved = 0;\n let renumbered = 0;\n let dbUpdated = 0;\n const vectorDbMoves: Array<{ oldPath: string; newPath: string }> = [];\n\n for (const plan of plans) {\n console.log();\n console.log(\n header(` Project: ${plan.project.display_name} (${plan.project.slug})`)\n );\n\n for (const dirPlan of plan.notesDirs) {\n const { notesDir } = dirPlan;\n if (plan.notesDirs.length > 1) console.log(dim(` Directory: ${notesDir}`));\n\n // Step 1: Delete empty sessions\n for (const c of dirPlan.toDelete) {\n try {\n unlinkSync(c.filepath);\n console.log(ok(` DEL ${c.filename}`));\n deleted++;\n } catch (e) {\n console.log(err(` FAIL to delete ${c.filename}: ${e}`));\n }\n if (c.session) {\n try {\n db.prepare(\"DELETE FROM sessions WHERE id = ?\").run(c.session.id);\n dbUpdated++;\n } catch (e) {\n console.log(err(` FAIL to remove session #${c.number} from DB: ${e}`));\n }\n }\n }\n\n // Step 2: Rename unnamed/legacy sessions\n for (const c of dirPlan.toRename) {\n const autoName = c.autoName ?? \"Unnamed Session\";\n const newFilename = `${padNum(c.number)} - ${c.date} - ${autoName}.md`;\n const newPath = join(notesDir, newFilename);\n\n if (c.filepath !== newPath) {\n try {\n renameSync(c.filepath, newPath);\n console.log(ok(` REN ${c.filename}`));\n console.log(dim(` → ${newFilename}`));\n renamed++;\n (c as { filename: string }).filename = newFilename;\n (c as { filepath: string }).filepath = newPath;\n } catch (e) {\n console.log(err(` FAIL rename ${c.filename}: ${e}`));\n continue;\n }\n }\n\n try {\n const content = readFileSync(newPath, \"utf8\");\n const lines = content.split(\"\\n\");\n let h1Updated = false;\n const updated = lines.map((line) => {\n if (!h1Updated && line.startsWith(\"# \")) {\n h1Updated = true;\n return `# ${autoName}`;\n }\n return line;\n });\n if (!h1Updated) updated.unshift(`# ${autoName}`, \"\");\n writeFileSync(newPath, updated.join(\"\\n\"), \"utf8\");\n } catch {\n // Non-fatal\n }\n\n if (c.session) {\n const normalizedSlug = autoName\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n try {\n db.prepare(\n \"UPDATE sessions SET slug = ?, title = ?, filename = ? WHERE id = ?\"\n ).run(normalizedSlug, autoName, newFilename, c.session.id);\n dbUpdated++;\n } catch (e) {\n console.log(err(` FAIL DB update for session #${c.number}: ${e}`));\n }\n }\n }\n\n // Step 3a: Renumber survivors\n if (plan.renumberMap.size > 0) {\n const toRenumber = dirPlan.toMove.filter((c) =>\n plan.renumberMap.has(c.number)\n );\n\n const tempFiles: {\n candidate: SessionCandidate;\n tempPath: string;\n newNum: number;\n }[] = [];\n for (const c of toRenumber) {\n const newNum = plan.renumberMap.get(c.number)!;\n const tempFilename = `__tmp_${padNum(c.number)}_${c.filename}`;\n const tempPath = join(notesDir, tempFilename);\n try {\n if (existsSync(c.filepath)) {\n renameSync(c.filepath, tempPath);\n tempFiles.push({ candidate: c, tempPath, newNum });\n }\n } catch (e) {\n console.log(err(` FAIL temp-rename #${c.number}: ${e}`));\n }\n }\n\n for (const { candidate: c, tempPath, newNum } of tempFiles) {\n const newFilename = c.filename.replace(/^\\d{4}/, padNum(newNum));\n const newPath = join(notesDir, newFilename);\n try {\n renameSync(tempPath, newPath);\n console.log(\n ok(` NUM #${padNum(c.number)} → #${padNum(newNum)}: ${newFilename}`)\n );\n renumbered++;\n (c as { filename: string }).filename = newFilename;\n (c as { filepath: string }).filepath = newPath;\n (c as { number: number }).number = newNum;\n } catch (e) {\n console.log(err(` FAIL final-rename #${newNum}: ${e}`));\n }\n\n if (existsSync(newPath)) {\n try {\n const content = readFileSync(newPath, \"utf8\");\n const lines = content.split(\"\\n\");\n const updated = lines.map((line) => {\n if (line.match(/^# Session \\d{4}:/)) {\n return line.replace(\n /^# Session \\d{4}:/,\n `# Session ${padNum(newNum)}:`\n );\n }\n return line;\n });\n writeFileSync(newPath, updated.join(\"\\n\"), \"utf8\");\n } catch {\n // Non-fatal\n }\n }\n }\n\n const dbRenumbers = tempFiles\n .filter(({ candidate: c }) => c.session != null)\n .map(({ candidate: c, newNum }) => ({\n session: c.session!,\n newNum,\n newFilename: c.filename,\n }));\n\n if (dbRenumbers.length > 0) {\n const renumberDb = db.transaction(() => {\n for (const { session, newNum } of dbRenumbers) {\n db.prepare(\"UPDATE sessions SET number = ? WHERE id = ?\").run(\n -newNum,\n session.id\n );\n }\n for (const { session, newNum, newFilename } of dbRenumbers) {\n db.prepare(\n \"UPDATE sessions SET number = ?, filename = ? WHERE id = ?\"\n ).run(newNum, newFilename, session.id);\n }\n });\n try {\n renumberDb();\n dbUpdated += dbRenumbers.length;\n } catch (e) {\n console.log(err(` FAIL DB renumber transaction: ${e}`));\n }\n }\n }\n\n // Step 3b: Move to YYYY/MM/\n for (const c of dirPlan.toMove) {\n const [year, month] = c.date.split(\"-\");\n const targetDir = join(notesDir, year, month);\n const targetPath = join(targetDir, c.filename);\n if (c.filepath === targetPath) continue;\n\n try {\n mkdirSync(targetDir, { recursive: true });\n } catch (e) {\n console.log(err(` FAIL mkdir ${targetDir}: ${e}`));\n continue;\n }\n\n const oldAbsPath = c.filepath;\n try {\n if (existsSync(c.filepath)) {\n renameSync(c.filepath, targetPath);\n console.log(ok(` MOV ${c.filename}`));\n console.log(dim(` → ${year}/${month}/${c.filename}`));\n moved++;\n vectorDbMoves.push({ oldPath: oldAbsPath, newPath: targetPath });\n }\n } catch (e) {\n console.log(err(` FAIL move ${c.filename}: ${e}`));\n continue;\n }\n\n const newFilenameInDb = `${year}/${month}/${c.filename}`;\n if (c.session) {\n try {\n db.prepare(\"UPDATE sessions SET filename = ? WHERE id = ?\").run(\n newFilenameInDb,\n c.session.id\n );\n dbUpdated++;\n } catch (e) {\n console.log(err(` FAIL DB update path for ${c.filename}: ${e}`));\n }\n }\n }\n }\n }\n\n // Step 5: Update Postgres vector DB paths\n let vectorDbUpdated = 0;\n if (vectorDbMoves.length > 0) {\n console.log();\n console.log(\n dim(\n ` Updating ${vectorDbMoves.length} file path(s) in vector DB to preserve embeddings...`\n )\n );\n const result = await updateVectorDbPaths(vectorDbMoves);\n if (result >= 0) {\n vectorDbUpdated = result;\n console.log(\n ok(` Updated ${vectorDbUpdated} file path(s) in Postgres (embeddings preserved)`)\n );\n } else {\n console.log(\n warn(\n \" Vector DB path update failed — embeddings may be orphaned (check logs)\"\n )\n );\n }\n }\n\n console.log();\n console.log(bold(\" Cleanup complete:\"));\n console.log(ok(` ${deleted} session(s) deleted`));\n console.log(ok(` ${renamed} session(s) renamed`));\n console.log(ok(` ${renumbered} session(s) renumbered`));\n console.log(ok(` ${moved} session(s) moved to YYYY/MM/ hierarchy`));\n console.log(ok(` ${dbUpdated} registry DB record(s) updated`));\n if (vectorDbMoves.length > 0) {\n console.log(\n ok(\n ` ${vectorDbUpdated} vector DB file path(s) updated (embeddings preserved)`\n )\n );\n }\n\n if (!skipReindex) {\n console.log();\n console.log(\n dim(\" Memory re-index: the PAI daemon will pick up changes within 5 minutes.\")\n );\n console.log(dim(\" To force immediate re-index: pai memory index --all\"));\n }\n\n console.log();\n}\n","/**\n * Commander registration for `pai session cleanup`.\n */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport { err, dim, ok, header } from \"../../utils.js\";\nimport { getAllProjects, getProject, analyzeProject } from \"./scanner.js\";\nimport { displayDryRun, executeCleanup } from \"./executor.js\";\n\nexport function registerSessionCleanupCommand(\n sessionCmd: Command,\n getDb: () => Database\n): void {\n sessionCmd\n .command(\"cleanup [project-slug]\")\n .description(\n \"Clean up session notes: delete empties, auto-name unnamed, move into YYYY/MM/ hierarchy, renumber\"\n )\n .option(\"--execute\", \"Actually perform the cleanup (default is dry-run)\")\n .option(\"--no-renumber\", \"Skip renumbering sessions after deletions\")\n .option(\"--no-reindex\", \"Skip triggering memory re-index after moves\")\n .action(\n async (\n projectSlug: string | undefined,\n opts: { execute?: boolean; renumber?: boolean; reindex?: boolean }\n ) => {\n const db = getDb();\n const dryRun = !opts.execute;\n const skipReindex = opts.reindex === false;\n\n let projects;\n if (projectSlug) {\n const p = getProject(db, projectSlug);\n if (!p) {\n console.error(err(`Project not found: ${projectSlug}`));\n process.exit(1);\n }\n projects = [p];\n } else {\n projects = getAllProjects(db);\n }\n\n console.log();\n console.log(\n header(\n dryRun\n ? \" pai session cleanup — DRY RUN (no changes will be made)\"\n : \" pai session cleanup — EXECUTING\"\n )\n );\n console.log(dim(` Analyzing ${projects.length} project(s)...`));\n\n const plans = [];\n for (const project of projects) {\n const plan = analyzeProject(db, project);\n if (plan) plans.push(plan);\n }\n\n const activePlans = plans.filter(\n (p) =>\n p.notesDirs.some(\n (d) =>\n d.toDelete.length > 0 ||\n d.toRename.length > 0 ||\n d.toMove.length > 0\n ) || p.renumberMap.size > 0\n );\n\n if (activePlans.length === 0) {\n console.log();\n console.log(ok(\" Nothing to do — all session notes are clean!\"));\n console.log();\n return;\n }\n\n if (dryRun) {\n await displayDryRun(activePlans);\n } else {\n await executeCleanup(db, activePlans, skipReindex);\n }\n }\n );\n}\n","/** Registry migrate command: import data from ~/.claude/session-registry.json. */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { ok, warn, err, dim } from \"../../utils.js\";\nimport { slugify, parseSessionFilename } from \"../../../registry/migrate.js\";\nimport { upsertProject, upsertSession } from \"./utils.js\";\nimport type { Database } from \"better-sqlite3\";\n\n// ---------------------------------------------------------------------------\n// Legacy JSON registry types\n// ---------------------------------------------------------------------------\n\ninterface LegacyProjectEntry {\n encoded_dir: string;\n original_path: string;\n notes?: string[];\n session_count?: number;\n note_count?: number;\n todo_exists?: boolean;\n last_modified?: string;\n path_exists?: boolean;\n}\n\ninterface LegacyJsonRegistry {\n version?: number;\n projects?: LegacyProjectEntry[];\n [key: string]: unknown;\n}\n\nconst SESSION_REGISTRY_PATH = join(homedir(), \".claude\", \"session-registry.json\");\n\n// ---------------------------------------------------------------------------\n// cmdMigrate\n// ---------------------------------------------------------------------------\n\nexport function cmdMigrate(db: Database): void {\n if (!existsSync(SESSION_REGISTRY_PATH)) {\n console.error(err(`session-registry.json not found: ${SESSION_REGISTRY_PATH}`));\n process.exit(1);\n }\n\n let registry: LegacyJsonRegistry;\n try {\n const raw = readFileSync(SESSION_REGISTRY_PATH, \"utf8\");\n registry = JSON.parse(raw) as LegacyJsonRegistry;\n } catch (e) {\n console.error(err(`Failed to parse session-registry.json: ${e}`));\n process.exit(1);\n }\n\n const projects = registry.projects ?? [];\n if (!projects.length) {\n console.log(warn(\"No projects found in session-registry.json.\"));\n return;\n }\n\n console.log(dim(`Migrating ${projects.length} project(s) from session-registry.json ...`));\n\n let projectsNew = 0;\n let projectsSkipped = 0;\n let sessionsNew = 0;\n const errors: string[] = [];\n\n for (const entry of projects) {\n if (!entry.original_path || !entry.encoded_dir) {\n errors.push(`Skipping entry with missing path: ${JSON.stringify(entry)}`);\n continue;\n }\n\n const rootPath = entry.original_path;\n const encodedDir = entry.encoded_dir;\n const slug = slugify(rootPath);\n\n try {\n const { isNew, id } = upsertProject(db, slug, rootPath, encodedDir);\n if (isNew) projectsNew++;\n else projectsSkipped++;\n\n const notes = entry.notes ?? [];\n for (const filename of notes) {\n const parsed = parseSessionFilename(filename);\n if (!parsed) continue;\n\n const isNewSession = upsertSession(db, id, parsed.number, parsed.date, parsed.slug, parsed.title, parsed.filename);\n if (isNewSession) sessionsNew++;\n }\n } catch (e) {\n errors.push(`Error processing ${entry.encoded_dir}: ${String(e)}`);\n }\n }\n\n console.log(ok(\"Migration complete.\"));\n console.log(dim(` Projects: ${projectsNew} new, ${projectsSkipped} already existed`));\n console.log(dim(` Sessions: ${sessionsNew} new`));\n\n if (errors.length) {\n console.log();\n console.log(warn(` ${errors.length} error(s) during migration:`));\n for (const e of errors.slice(0, 5)) {\n console.log(dim(` ${e}`));\n }\n if (errors.length > 5) {\n console.log(dim(` ... and ${errors.length - 5} more`));\n }\n }\n}\n","/** Registry command registration and simple sub-commands (stats, rebuild, lookup). */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport { ok, warn, err, dim, bold, fmtDate } from \"../../utils.js\";\nimport { homedir } from \"node:os\";\nimport { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { cmdScan, loadScanConfig, saveScanConfig, resolveHome } from \"./scan.js\";\nimport { cmdMigrate } from \"./migrate.js\";\n\n// ---------------------------------------------------------------------------\n// stats\n// ---------------------------------------------------------------------------\n\nfunction cmdStats(db: Database): void {\n const totalProjects = (db.prepare(\"SELECT COUNT(*) AS n FROM projects\").get() as { n: number }).n;\n const activeProjects = (db.prepare(\"SELECT COUNT(*) AS n FROM projects WHERE status = 'active'\").get() as { n: number }).n;\n const archivedProjects = (db.prepare(\"SELECT COUNT(*) AS n FROM projects WHERE status = 'archived'\").get() as { n: number }).n;\n const totalSessions = (db.prepare(\"SELECT COUNT(*) AS n FROM sessions\").get() as { n: number }).n;\n const totalTags = (db.prepare(\"SELECT COUNT(*) AS n FROM tags\").get() as { n: number }).n;\n\n const lastProject = db\n .prepare(\"SELECT updated_at FROM projects ORDER BY updated_at DESC LIMIT 1\")\n .get() as { updated_at: number } | undefined;\n\n const lastSession = db\n .prepare(\"SELECT created_at FROM sessions ORDER BY created_at DESC LIMIT 1\")\n .get() as { created_at: number } | undefined;\n\n console.log();\n console.log(bold(\" PAI Registry Stats\"));\n console.log();\n console.log(` ${bold(\"Projects:\")} ${totalProjects}`);\n console.log(` ${bold(\" Active:\")} ${activeProjects}`);\n console.log(` ${bold(\" Archived:\")} ${archivedProjects}`);\n console.log(` ${bold(\"Sessions:\")} ${totalSessions}`);\n console.log(` ${bold(\"Tags:\")} ${totalTags}`);\n if (lastProject) {\n console.log(` ${bold(\"Last updated:\")} ${fmtDate(lastProject.updated_at)}`);\n }\n if (lastSession) {\n console.log(` ${bold(\"Last session:\")} ${fmtDate(lastSession.created_at)}`);\n }\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// rebuild\n// ---------------------------------------------------------------------------\n\nfunction cmdRebuild(db: Database): void {\n console.log(warn(\"Rebuilding registry — all existing data will be erased.\"));\n console.log(dim(\"Clearing all tables ...\"));\n\n db.exec(`\n DELETE FROM compaction_log;\n DELETE FROM session_tags;\n DELETE FROM project_tags;\n DELETE FROM aliases;\n DELETE FROM sessions;\n DELETE FROM projects;\n DELETE FROM tags;\n DELETE FROM schema_version;\n `);\n\n console.log(dim(\"Registry cleared. Re-scanning ...\"));\n cmdScan(db);\n}\n\n// ---------------------------------------------------------------------------\n// lookup\n// ---------------------------------------------------------------------------\n\nfunction cmdLookup(db: Database, fsPath: string): void {\n const resolved = resolve(fsPath);\n\n const row = db\n .prepare(\"SELECT slug FROM projects WHERE root_path = ?\")\n .get(resolved) as { slug: string } | undefined;\n\n if (!row) {\n process.exit(1);\n }\n\n process.stdout.write(row.slug + \"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerRegistryCommands(\n registryCmd: Command,\n getDb: () => Database\n): void {\n // pai registry scan\n registryCmd\n .command(\"scan\")\n .description(\"Walk ~/.claude/projects/ and configured scan_dirs, upsert all projects\")\n .option(\"--add-dir <path>\", \"Add a directory to scan_dirs config\")\n .option(\"--remove-dir <path>\", \"Remove a directory from scan_dirs config\")\n .option(\"--show-dirs\", \"Show currently configured scan directories\")\n .option(\"--quick\", \"Minimal output mode (used by hooks and daemon)\")\n .action((opts: { addDir?: string; removeDir?: string; showDirs?: boolean; quick?: boolean }) => {\n if (opts.showDirs) {\n const config = loadScanConfig();\n if (!config.scan_dirs.length) {\n console.log(dim(\" No extra scan directories configured.\"));\n console.log(dim(\" Use --add-dir <path> to add one.\"));\n } else {\n console.log(bold(\" Configured scan directories:\"));\n for (const d of config.scan_dirs) {\n console.log(` ${d}`);\n }\n }\n return;\n }\n if (opts.addDir) {\n const config = loadScanConfig();\n const resolved = resolveHome(opts.addDir);\n if (!existsSync(resolved)) {\n console.error(err(`Directory not found: ${resolved}`));\n process.exit(1);\n }\n const display = resolved.startsWith(homedir())\n ? \"~\" + resolved.slice(homedir().length)\n : resolved;\n if (config.scan_dirs.includes(display) || config.scan_dirs.includes(resolved)) {\n console.log(warn(`Already configured: ${display}`));\n } else {\n config.scan_dirs.push(display);\n saveScanConfig(config);\n console.log(ok(`Added scan directory: ${bold(display)}`));\n }\n }\n if (opts.removeDir) {\n const config = loadScanConfig();\n const resolved = resolveHome(opts.removeDir);\n const display = resolved.startsWith(homedir())\n ? \"~\" + resolved.slice(homedir().length)\n : resolved;\n const before = config.scan_dirs.length;\n config.scan_dirs = config.scan_dirs.filter((d) => resolveHome(d) !== resolved);\n if (config.scan_dirs.length < before) {\n saveScanConfig(config);\n console.log(ok(`Removed scan directory: ${bold(display)}`));\n } else {\n console.log(warn(`Not found in config: ${display}`));\n }\n }\n if (!opts.addDir && !opts.removeDir) {\n cmdScan(getDb(), { quick: opts.quick });\n }\n });\n\n // pai registry migrate\n registryCmd\n .command(\"migrate\")\n .description(\"Import data from ~/.claude/session-registry.json\")\n .action(() => {\n cmdMigrate(getDb());\n });\n\n // pai registry stats\n registryCmd\n .command(\"stats\")\n .description(\"Show summary statistics for the registry\")\n .action(() => {\n cmdStats(getDb());\n });\n\n // pai registry rebuild\n registryCmd\n .command(\"rebuild\")\n .description(\"Erase all registry data and rebuild from the filesystem (destructive)\")\n .action(() => {\n cmdRebuild(getDb());\n });\n\n // pai registry lookup --path <path>\n registryCmd\n .command(\"lookup\")\n .description(\"Find the project slug for a filesystem path (for use in scripts)\")\n .requiredOption(\"--path <path>\", \"Filesystem path to look up\")\n .action((opts: { path: string }) => {\n cmdLookup(getDb(), opts.path);\n });\n}\n","/** Memory embed command: generate embeddings for un-embedded chunks. */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport { openFederation } from \"../../../memory/db.js\";\nimport { embedChunks } from \"../../../memory/indexer.js\";\nimport { dim, bold, ok, err } from \"../../utils.js\";\n\n// ---------------------------------------------------------------------------\n// Shared embed runner (used by both index --embed and embed sub-command)\n// ---------------------------------------------------------------------------\n\nexport async function runEmbed(\n federation: Database,\n projectId?: number,\n projectSlug?: string,\n batchSize = 50,\n): Promise<void> {\n const label = projectSlug ? `project ${projectSlug}` : \"all projects\";\n console.log(dim(`Generating embeddings for ${label} (this may take a while on first run)...`));\n\n const { chunksEmbedded } = await embedChunks(\n federation,\n projectId,\n batchSize,\n (done, total) => {\n process.stdout.write(`\\r ${done} / ${total} chunks embedded...`);\n },\n );\n\n process.stdout.write(\"\\r\");\n console.log(ok(`Done.`) + ` ${bold(String(chunksEmbedded))} chunks embedded`);\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerEmbedCommand(\n memoryCmd: Command,\n getDb: () => Database,\n): void {\n memoryCmd\n .command(\"embed [project-slug]\")\n .description(\"Generate embeddings for un-embedded chunks (Phase 2.5)\")\n .option(\"--batch-size <n>\", \"Chunks to embed per batch\", \"50\")\n .action(async (projectSlug: string | undefined, opts: { batchSize?: string }) => {\n const registryDb = getDb();\n\n let federation: Database;\n try {\n federation = openFederation();\n } catch (e) {\n console.error(err(`Failed to open federation database: ${e}`));\n process.exit(1);\n }\n\n if (projectSlug) {\n const project = registryDb\n .prepare(\"SELECT id, slug FROM projects WHERE slug = ?\")\n .get(projectSlug) as { id: number; slug: string } | undefined;\n\n if (!project) {\n console.error(err(`Project not found: ${projectSlug}`));\n process.exit(1);\n }\n\n await runEmbed(federation, project.id, project.slug, parseInt(opts.batchSize ?? \"50\", 10));\n } else {\n await runEmbed(federation, undefined, undefined, parseInt(opts.batchSize ?? \"50\", 10));\n }\n });\n}\n","/** Memory index command: index one or all projects into the memory store. */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport { openFederation } from \"../../../memory/db.js\";\nimport { indexProject, indexAll } from \"../../../memory/indexer.js\";\nimport { dim, bold, ok, err } from \"../../utils.js\";\nimport { PaiClient } from \"../../../daemon/ipc-client.js\";\nimport { loadConfig } from \"../../../daemon/config.js\";\nimport { runEmbed } from \"./embed.js\";\n\nexport function registerIndexCommand(\n memoryCmd: Command,\n getDb: () => Database,\n): void {\n memoryCmd\n .command(\"index [project-slug]\")\n .description(\"Index memory files for one project or all projects\")\n .option(\"--all\", \"Index all active projects (default when no slug given)\")\n .option(\"--embed\", \"Also generate embeddings for newly indexed chunks (Phase 2.5)\")\n .option(\"--direct\", \"Skip daemon IPC and run index directly (for debugging)\")\n .action(async (projectSlug: string | undefined, opts: { all?: boolean; embed?: boolean; direct?: boolean }) => {\n const registryDb = getDb();\n\n // If daemon is running and no --direct flag, trigger via IPC (non-blocking)\n if (!opts.direct && !projectSlug) {\n try {\n const config = loadConfig();\n const client = new PaiClient(config.socketPath);\n await client.triggerIndex();\n console.log(ok(\"Index triggered in daemon.\") + dim(\" Check daemon logs for progress.\"));\n console.log(dim(\" Run `pai daemon logs` to watch progress.\"));\n return;\n } catch {\n console.log(dim(\"Daemon not running. Running direct index...\"));\n }\n }\n\n let federation: Database;\n try {\n federation = openFederation();\n } catch (e) {\n console.error(err(`Failed to open federation database: ${e}`));\n process.exit(1);\n }\n\n if (projectSlug) {\n const project = registryDb\n .prepare(\"SELECT id, slug, display_name, root_path FROM projects WHERE slug = ? AND status = 'active'\")\n .get(projectSlug) as\n | { id: number; slug: string; display_name: string; root_path: string }\n | undefined;\n\n if (!project) {\n console.error(err(`Project not found or not active: ${projectSlug}`));\n process.exit(1);\n }\n\n console.log(dim(`Indexing ${project.display_name} (${project.slug})...`));\n const result = await indexProject(federation, project.id, project.root_path);\n\n console.log(\n ok(`Done.`) +\n ` ${bold(String(result.filesProcessed))} files indexed` +\n `, ${bold(String(result.chunksCreated))} chunks created` +\n `, ${dim(String(result.filesSkipped) + \" skipped (unchanged)\")}`,\n );\n\n if (opts.embed) {\n await runEmbed(federation, project.id, project.slug);\n }\n\n } else if (opts.all || !projectSlug) {\n console.log(dim(\"Indexing all active projects...\"));\n\n const { projects, result } = await indexAll(federation, registryDb);\n\n console.log(\n ok(`Done.`) +\n ` ${bold(String(projects))} projects` +\n `, ${bold(String(result.filesProcessed))} files indexed` +\n `, ${bold(String(result.chunksCreated))} chunks created` +\n `, ${dim(String(result.filesSkipped) + \" skipped (unchanged)\")}`,\n );\n\n if (opts.embed) {\n await runEmbed(federation);\n }\n }\n });\n}\n","/** Memory search command: BM25/semantic/hybrid search across federation.db. */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport chalk from \"chalk\";\nimport { openFederation } from \"../../../memory/db.js\";\nimport { searchMemory, populateSlugs, type SearchResult } from \"../../../memory/search.js\";\nimport { dim, bold, ok, warn, err } from \"../../utils.js\";\nimport { loadConfig } from \"../../../daemon/config.js\";\nimport { createStorageBackend } from \"../../../storage/factory.js\";\n\n// ---------------------------------------------------------------------------\n// Helper\n// ---------------------------------------------------------------------------\n\nfunction tierColor(tier: string): string {\n switch (tier) {\n case \"evergreen\": return chalk.green(tier);\n case \"daily\": return chalk.yellow(tier);\n case \"topic\": return chalk.blue(tier);\n case \"session\": return chalk.dim(tier);\n default: return tier;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerSearchCommand(\n memoryCmd: Command,\n getDb: () => Database,\n): void {\n memoryCmd\n .command(\"search <query>\")\n .description(\"Search indexed memory (BM25 keyword, semantic, or hybrid)\")\n .option(\"--project <slug>\", \"Restrict search to a specific project\")\n .option(\"--source <source>\", \"Restrict to 'memory' or 'notes'\")\n .option(\"--limit <n>\", \"Maximum results to return\")\n .option(\"--mode <mode>\", \"Search mode: keyword (default), semantic, hybrid\")\n .option(\"--no-rerank\", \"Skip cross-encoder reranking (reranking is on by default)\")\n .option(\"--recency <days>\", \"Apply recency boost: score halves every N days. 0 = off\")\n .action(\n async (\n query: string,\n opts: { project?: string; source?: string; limit?: string; mode?: string; rerank: boolean; recency?: string },\n ) => {\n const registryDb = getDb();\n\n let federation: Database;\n try {\n federation = openFederation();\n } catch (e) {\n console.error(err(`Failed to open federation database: ${e}`));\n process.exit(1);\n }\n\n const config = loadConfig();\n const searchConfig = config.search;\n\n const maxResults = parseInt(opts.limit ?? String(searchConfig.defaultLimit), 10);\n const mode = (opts.mode ?? searchConfig.mode) as \"keyword\" | \"semantic\" | \"hybrid\";\n\n if (![\"keyword\", \"semantic\", \"hybrid\"].includes(mode)) {\n console.error(err(`Invalid mode: ${mode}. Use keyword, semantic, or hybrid.`));\n process.exit(1);\n }\n\n let projectIds: number[] | undefined;\n if (opts.project) {\n const project = registryDb\n .prepare(\"SELECT id FROM projects WHERE slug = ?\")\n .get(opts.project) as { id: number } | undefined;\n\n if (!project) {\n console.error(warn(`Project not found: ${opts.project} — searching all projects`));\n } else {\n projectIds = [project.id];\n }\n }\n\n const sources = opts.source ? [opts.source] : undefined;\n const searchOpts = { projectIds, sources, maxResults };\n\n let results: SearchResult[];\n\n if (mode === \"keyword\") {\n results = searchMemory(federation, query, searchOpts);\n\n } else if (mode === \"semantic\" || mode === \"hybrid\") {\n const backend = await createStorageBackend(config);\n\n try {\n const { generateEmbedding } = await import(\"../../../memory/embeddings.js\");\n\n console.log(dim(\"Generating query embedding...\"));\n const queryEmbedding = await generateEmbedding(query, true);\n\n if (mode === \"semantic\") {\n results = await backend.searchSemantic(queryEmbedding, searchOpts);\n } else {\n // Hybrid: combine keyword (BM25) and semantic results with min-max normalization\n const [keywordResults, semanticResults] = await Promise.all([\n backend.searchKeyword(query, { ...searchOpts, maxResults: 500 }),\n backend.searchSemantic(queryEmbedding, { ...searchOpts, maxResults: 500 }),\n ]);\n\n if (keywordResults.length === 0 && semanticResults.length === 0) {\n results = [];\n } else {\n const keyFor = (r: SearchResult) =>\n `${r.projectId}:${r.path}:${r.startLine}:${r.endLine}`;\n\n function minMaxNormalize(items: SearchResult[]): Map<string, number> {\n if (items.length === 0) return new Map();\n const min = Math.min(...items.map((r) => r.score));\n const max = Math.max(...items.map((r) => r.score));\n const range = max - min;\n const m = new Map<string, number>();\n for (const r of items) {\n m.set(keyFor(r), range === 0 ? 1 : (r.score - min) / range);\n }\n return m;\n }\n\n const kwNorm = minMaxNormalize(keywordResults);\n const semNorm = minMaxNormalize(semanticResults);\n const allKeys = new Set<string>([\n ...keywordResults.map(keyFor),\n ...semanticResults.map(keyFor),\n ]);\n const metaMap = new Map<string, SearchResult>();\n for (const r of [...keywordResults, ...semanticResults]) {\n metaMap.set(keyFor(r), r);\n }\n\n const combined: SearchResult[] = [];\n for (const key of allKeys) {\n const meta = metaMap.get(key)!;\n const combinedScore = 0.5 * (kwNorm.get(key) ?? 0) + 0.5 * (semNorm.get(key) ?? 0);\n combined.push({ ...meta, score: combinedScore });\n }\n\n results = combined.sort((a, b) => b.score - a.score).slice(0, maxResults);\n }\n }\n } finally {\n await backend.close();\n }\n } else {\n results = [];\n }\n\n if (!results || results.length === 0) {\n console.log(dim(`No results found for: \"${query}\" (mode: ${mode})`));\n return;\n }\n\n // Cross-encoder reranking (on by default, skip with --no-rerank)\n if (opts.rerank !== false) {\n const { rerankResults } = await import(\"../../../memory/reranker.js\");\n console.log(dim(\"Reranking with cross-encoder...\"));\n results = await rerankResults(query, results, { topK: maxResults });\n }\n\n // Recency boost (applied after reranking)\n const recencyDays = parseInt(opts.recency ?? String(searchConfig.recencyBoostDays), 10);\n if (recencyDays > 0) {\n const { applyRecencyBoost } = await import(\"../../../memory/search.js\");\n console.log(dim(`Applying recency boost (half-life: ${recencyDays} days)...`));\n results = applyRecencyBoost(results, recencyDays);\n }\n\n const withSlugs = populateSlugs(results, registryDb);\n const rerankLabel = opts.rerank !== false ? \" +rerank\" : \"\";\n const modeLabel = mode !== \"keyword\" ? ` [${mode}${rerankLabel}]` : (opts.rerank !== false ? ` [rerank]` : \"\");\n\n console.log(\n `\\n ${bold(`Search results for: \"${query}\"`)}${modeLabel} ${dim(`(${withSlugs.length} found)`)}\\n`,\n );\n\n for (const result of withSlugs) {\n const projectLabel = result.projectSlug\n ? chalk.cyan(result.projectSlug)\n : chalk.cyan(String(result.projectId));\n\n const tierLabel = tierColor(result.tier);\n const scoreLabel = dim(`score: ${result.score.toFixed(4)}`);\n const locationLabel = dim(`${result.path}:${result.startLine}-${result.endLine}`);\n\n console.log(` ${projectLabel} ${tierLabel} ${locationLabel} ${scoreLabel}`);\n\n const snippetLines = result.snippet\n .split(\"\\n\")\n .slice(0, 3)\n .map((l) => ` ${l}`);\n console.log(snippetLines.join(\"\\n\"));\n console.log();\n }\n },\n );\n}\n","/** Memory status and settings commands for the PAI memory index. */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport chalk from \"chalk\";\nimport { openFederation } from \"../../../memory/db.js\";\nimport { renderTable, dim, bold, ok, err, warn, fmtDate } from \"../../utils.js\";\nimport { loadConfig, CONFIG_FILE, ensureConfigDir } from \"../../../daemon/config.js\";\n\nfunction tierColor(tier: string): string {\n switch (tier) {\n case \"evergreen\": return chalk.green(tier);\n case \"daily\": return chalk.yellow(tier);\n case \"topic\": return chalk.blue(tier);\n case \"session\": return chalk.dim(tier);\n default: return tier;\n }\n}\n\nexport function registerStatsCommands(\n memoryCmd: Command,\n getDb: () => Database,\n): void {\n\n // -------------------------------------------------------------------------\n // pai memory status [project-slug]\n // -------------------------------------------------------------------------\n\n memoryCmd\n .command(\"status [project-slug]\")\n .description(\"Show memory index statistics\")\n .action((projectSlug: string | undefined) => {\n const registryDb = getDb();\n\n let federation: Database;\n try {\n federation = openFederation();\n } catch (e) {\n console.error(err(`Failed to open federation database: ${e}`));\n process.exit(1);\n }\n\n if (projectSlug) {\n const project = registryDb\n .prepare(\"SELECT id, slug, display_name FROM projects WHERE slug = ?\")\n .get(projectSlug) as\n | { id: number; slug: string; display_name: string }\n | undefined;\n\n if (!project) {\n console.error(err(`Project not found: ${projectSlug}`));\n process.exit(1);\n }\n\n const fileStats = federation\n .prepare(\"SELECT COUNT(*) as files FROM memory_files WHERE project_id = ?\")\n .get(project.id) as { files: number };\n\n const chunkStats = federation\n .prepare(\"SELECT COUNT(*) as chunks FROM memory_chunks WHERE project_id = ?\")\n .get(project.id) as { chunks: number };\n\n const lastUpdate = federation\n .prepare(\"SELECT MAX(updated_at) as last_at FROM memory_chunks WHERE project_id = ?\")\n .get(project.id) as { last_at: number | null };\n\n const tierBreakdown = federation\n .prepare(\"SELECT tier, COUNT(*) as n FROM memory_chunks WHERE project_id = ? GROUP BY tier ORDER BY n DESC\")\n .all(project.id) as Array<{ tier: string; n: number }>;\n\n console.log(`\\n ${bold(project.display_name)} ${dim(`(${project.slug})`)}\\n`);\n console.log(` ${bold(\"Files indexed:\")} ${fileStats.files}`);\n console.log(` ${bold(\"Chunks:\")} ${chunkStats.chunks}`);\n console.log(` ${bold(\"Last indexed:\")} ${lastUpdate.last_at ? fmtDate(lastUpdate.last_at) : dim(\"never\")}`);\n\n if (tierBreakdown.length > 0) {\n console.log(`\\n ${bold(\"By tier:\")}`);\n for (const row of tierBreakdown) {\n console.log(` ${tierColor(row.tier).padEnd(20)} ${row.n} chunks`);\n }\n }\n console.log();\n\n } else {\n const globalFiles = federation.prepare(\"SELECT COUNT(*) as n FROM memory_files\").get() as { n: number };\n const globalChunks = federation.prepare(\"SELECT COUNT(*) as n FROM memory_chunks\").get() as { n: number };\n const lastUpdate = federation.prepare(\"SELECT MAX(updated_at) as last_at FROM memory_chunks\").get() as { last_at: number | null };\n\n const projectStats = federation\n .prepare(\n `SELECT\n mf.project_id,\n COUNT(DISTINCT mf.path) as files,\n COUNT(mc.id) as chunks,\n MAX(mc.updated_at) as last_at\n FROM memory_files mf\n LEFT JOIN memory_chunks mc ON mc.project_id = mf.project_id AND mc.path = mf.path\n GROUP BY mf.project_id\n ORDER BY chunks DESC`,\n )\n .all() as Array<{ project_id: number; files: number; chunks: number; last_at: number | null }>;\n\n const projectIds = projectStats.map((p) => p.project_id);\n const slugMap = new Map<number, string>();\n if (projectIds.length > 0) {\n const placeholders = projectIds.map(() => \"?\").join(\", \");\n const slugRows = registryDb\n .prepare(`SELECT id, slug, display_name FROM projects WHERE id IN (${placeholders})`)\n .all(...projectIds) as Array<{ id: number; slug: string; display_name: string }>;\n for (const row of slugRows) {\n slugMap.set(row.id, row.slug);\n }\n }\n\n console.log(`\\n ${bold(\"PAI Memory Index — Global Status\")}\\n`);\n console.log(` ${bold(\"Total files:\")} ${globalFiles.n} ${bold(\"Total chunks:\")} ${globalChunks.n}`);\n console.log(` ${bold(\"Last indexed:\")} ${lastUpdate.last_at ? fmtDate(lastUpdate.last_at) : dim(\"never\")}`);\n\n if (projectStats.length > 0) {\n console.log();\n const rows = projectStats.map((p) => [\n chalk.cyan(slugMap.get(p.project_id) ?? String(p.project_id)),\n String(p.files),\n String(p.chunks),\n p.last_at ? fmtDate(p.last_at) : dim(\"never\"),\n ]);\n console.log(renderTable([\"Project\", \"Files\", \"Chunks\", \"Last Indexed\"], rows));\n } else {\n console.log(dim(\"\\n No projects indexed yet. Run `pai memory index --all` to start.\"));\n }\n console.log();\n }\n });\n\n // -------------------------------------------------------------------------\n // pai memory settings [key] [value]\n // -------------------------------------------------------------------------\n\n memoryCmd\n .command(\"settings [key] [value]\")\n .description(\"View or modify search settings in ~/.config/pai/config.json\")\n .action((key: string | undefined, value: string | undefined) => {\n const config = loadConfig();\n const search = config.search;\n\n if (!key) {\n console.log(`\\n ${bold(\"PAI Memory — Search Settings\")}\\n`);\n console.log(` ${bold(\"mode:\")} ${search.mode}`);\n console.log(` ${bold(\"rerank:\")} ${search.rerank}`);\n console.log(` ${bold(\"recencyBoostDays:\")} ${search.recencyBoostDays}`);\n console.log(` ${bold(\"defaultLimit:\")} ${search.defaultLimit}`);\n console.log(` ${bold(\"snippetLength:\")} ${search.snippetLength}`);\n console.log();\n console.log(dim(` Config file: ${CONFIG_FILE}`));\n console.log(dim(` Edit directly or use: pai memory settings <key> <value>`));\n console.log();\n return;\n }\n\n if (!value) {\n const val = (search as unknown as Record<string, unknown>)[key];\n if (val === undefined) {\n console.error(err(`Unknown setting: ${key}`));\n console.log(dim(` Valid keys: mode, rerank, recencyBoostDays, defaultLimit, snippetLength`));\n process.exit(1);\n }\n console.log(String(val));\n return;\n }\n\n const validKeys = new Set([\"mode\", \"rerank\", \"recencyBoostDays\", \"defaultLimit\", \"snippetLength\"]);\n if (!validKeys.has(key)) {\n console.error(err(`Unknown setting: ${key}`));\n console.log(dim(` Valid keys: ${[...validKeys].join(\", \")}`));\n process.exit(1);\n }\n\n let fileConfig: Record<string, unknown> = {};\n if (existsSync(CONFIG_FILE)) {\n try {\n fileConfig = JSON.parse(readFileSync(CONFIG_FILE, \"utf-8\")) as Record<string, unknown>;\n } catch {\n console.error(err(`Could not parse ${CONFIG_FILE}`));\n process.exit(1);\n }\n }\n\n if (!fileConfig.search || typeof fileConfig.search !== \"object\") {\n fileConfig.search = {};\n }\n\n let parsed: string | number | boolean;\n if (key === \"mode\") {\n if (![\"keyword\", \"semantic\", \"hybrid\"].includes(value)) {\n console.error(err(`Invalid mode: ${value}. Must be keyword, semantic, or hybrid.`));\n process.exit(1);\n }\n parsed = value;\n } else if (key === \"rerank\") {\n parsed = value === \"true\" || value === \"1\" || value === \"on\";\n } else {\n parsed = parseInt(value, 10);\n if (isNaN(parsed)) {\n console.error(err(`Invalid number: ${value}`));\n process.exit(1);\n }\n }\n\n (fileConfig.search as Record<string, unknown>)[key] = parsed;\n\n try {\n ensureConfigDir();\n writeFileSync(CONFIG_FILE, JSON.stringify(fileConfig, null, 2) + \"\\n\", \"utf-8\");\n console.log(ok(`Set search.${key} = ${parsed}`));\n console.log(dim(` Restart daemon to apply: pai daemon restart`));\n } catch (e) {\n console.error(err(`Could not write config: ${e}`));\n process.exit(1);\n }\n });\n}\n","/** Barrel: registers all memory sub-commands and re-exports registerMemoryCommands. */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport { registerIndexCommand } from \"./index-cmd.js\";\nimport { registerEmbedCommand } from \"./embed.js\";\nimport { registerSearchCommand } from \"./search.js\";\nimport { registerStatsCommands } from \"./stats.js\";\n\nexport function registerMemoryCommands(\n memoryCmd: Command,\n getDb: () => Database,\n): void {\n registerIndexCommand(memoryCmd, getDb);\n registerEmbedCommand(memoryCmd, getDb);\n registerSearchCommand(memoryCmd, getDb);\n registerStatsCommands(memoryCmd, getDb);\n}\n","/**\n * pai mcp <sub-command>\n *\n * install — Register the PAI MCP server in ~/.claude.json\n * status — Show whether the PAI MCP server is registered and the binary exists\n */\n\nimport type { Command } from \"commander\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { ok, warn, err, dim, bold } from \"../utils.js\";\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nconst CLAUDE_JSON_PATH = join(homedir(), \".claude.json\");\n\n/**\n * Resolve the absolute path to the built MCP entry point.\n *\n * tsdown bundles all CLI commands into a single dist/cli/index.mjs file, so\n * import.meta.url always resolves to dist/cli/index.mjs at runtime.\n * From dist/cli/ we go up one level to dist/ and then into mcp/index.mjs.\n */\nfunction getMcpBinPath(): string {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n // dist/cli/index.mjs → dist/ → dist/mcp/index.mjs\n return join(__dirname, \"../mcp/index.mjs\");\n}\n\n// ---------------------------------------------------------------------------\n// Read / write ~/.claude.json safely\n// ---------------------------------------------------------------------------\n\nfunction readClaudeJson(): Record<string, unknown> {\n if (!existsSync(CLAUDE_JSON_PATH)) return {};\n try {\n const raw = readFileSync(CLAUDE_JSON_PATH, \"utf8\");\n return JSON.parse(raw) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nfunction writeClaudeJson(data: Record<string, unknown>): void {\n writeFileSync(CLAUDE_JSON_PATH, JSON.stringify(data, null, 2) + \"\\n\", \"utf8\");\n}\n\n// ---------------------------------------------------------------------------\n// install\n// ---------------------------------------------------------------------------\n\nfunction cmdInstall(): void {\n const mcpBin = getMcpBinPath();\n\n const config = readClaudeJson();\n\n // Ensure mcpServers key exists\n if (typeof config.mcpServers !== \"object\" || config.mcpServers === null) {\n config.mcpServers = {};\n }\n\n const servers = config.mcpServers as Record<string, unknown>;\n\n if (\"pai\" in servers) {\n console.log(warn(\"PAI MCP server is already registered in ~/.claude.json.\"));\n console.log(dim(` Entry: ${JSON.stringify(servers[\"pai\"])}`));\n console.log(dim(\" Use `pai mcp status` to verify the configuration.\"));\n return;\n }\n\n servers[\"pai\"] = {\n command: \"node\",\n args: [mcpBin],\n };\n\n try {\n writeClaudeJson(config);\n } catch (e) {\n console.error(err(`Failed to write ~/.claude.json: ${e}`));\n process.exit(1);\n }\n\n console.log(ok(\"PAI MCP server registered in ~/.claude.json.\"));\n console.log(dim(` Binary: ${mcpBin}`));\n console.log(dim(\"\"));\n console.log(dim(\" Restart Claude Code to activate the PAI MCP tools:\"));\n console.log(dim(\" memory_search, memory_get, project_info,\"));\n console.log(dim(\" project_list, session_list, registry_search\"));\n\n if (!existsSync(mcpBin)) {\n console.log();\n console.log(\n warn(` Note: MCP binary not found at ${mcpBin}`)\n );\n console.log(dim(\" Run `bun run build` to compile it first.\"));\n }\n}\n\n// ---------------------------------------------------------------------------\n// status\n// ---------------------------------------------------------------------------\n\nfunction cmdStatus(): void {\n const mcpBin = getMcpBinPath();\n const config = readClaudeJson();\n\n const servers =\n typeof config.mcpServers === \"object\" && config.mcpServers !== null\n ? (config.mcpServers as Record<string, unknown>)\n : {};\n\n const registered = \"pai\" in servers;\n const binExists = existsSync(mcpBin);\n\n console.log();\n console.log(bold(\" PAI MCP Server Status\"));\n console.log();\n\n if (registered) {\n const entry = servers[\"pai\"];\n console.log(ok(` Registered in ~/.claude.json`));\n console.log(dim(` Config: ${JSON.stringify(entry)}`));\n } else {\n console.log(warn(` NOT registered in ~/.claude.json`));\n console.log(dim(` Run: pai mcp install`));\n }\n\n console.log();\n\n if (binExists) {\n console.log(ok(` MCP binary found: ${mcpBin}`));\n } else {\n console.log(warn(` MCP binary NOT found: ${mcpBin}`));\n console.log(dim(\" Run: bun run build\"));\n }\n\n console.log();\n\n if (registered && binExists) {\n console.log(dim(\" Status: READY — restart Claude Code to use PAI tools\"));\n } else if (registered && !binExists) {\n console.log(warn(\" Status: NEEDS BUILD — run `bun run build`\"));\n } else {\n console.log(dim(\" Status: NOT INSTALLED — run `pai mcp install`\"));\n }\n\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerMcpCommands(mcpCmd: Command): void {\n mcpCmd\n .command(\"install\")\n .description(\n \"Register the PAI MCP server in ~/.claude.json (restart Claude Code to activate)\"\n )\n .action(() => {\n cmdInstall();\n });\n\n mcpCmd\n .command(\"status\")\n .description(\n \"Show whether the PAI MCP server is registered and the binary exists\"\n )\n .action(() => {\n cmdStatus();\n });\n}\n","/**\n * pai daemon <sub-command>\n *\n * serve — Start the PAI daemon in the foreground\n * status — Query daemon status via IPC\n * restart — Send SIGTERM to running daemon (launchd will restart it)\n * install — Write launchd plist + update ~/.claude.json to use the shim\n * uninstall — Remove launchd plist + revert ~/.claude.json to direct MCP\n * logs — Tail the daemon log file\n */\n\nimport type { Command } from \"commander\";\nimport {\n existsSync,\n readFileSync,\n writeFileSync,\n unlinkSync,\n mkdirSync,\n} from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { execSync, spawnSync } from \"node:child_process\";\nimport { ok, warn, err, dim, bold } from \"../utils.js\";\nimport { loadConfig } from \"../../daemon/config.js\";\nimport { PaiClient } from \"../../daemon/ipc-client.js\";\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nconst HOME = homedir();\nconst CLAUDE_JSON_PATH = join(HOME, \".claude.json\");\nconst PLIST_LABEL = \"com.pai.pai-daemon\";\nconst LAUNCH_AGENTS_DIR = join(HOME, \"Library\", \"LaunchAgents\");\nconst PLIST_PATH = join(LAUNCH_AGENTS_DIR, `${PLIST_LABEL}.plist`);\nconst DAEMON_LOG = \"/tmp/pai-daemon.log\";\n\n/**\n * Resolve the absolute path to the built daemon entry point.\n * tsdown bundles into dist/daemon/index.mjs (or similar).\n * From dist/cli/index.mjs → dist/ → dist/daemon/index.mjs\n */\nfunction getDaemonBinPath(): string {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n return join(__dirname, \"../daemon/index.mjs\");\n}\n\n/**\n * Resolve the absolute path to the built MCP shim entry point.\n * dist/cli/index.mjs → dist/ → dist/daemon-mcp/index.mjs\n */\nfunction getShimBinPath(): string {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n return join(__dirname, \"../daemon-mcp/index.mjs\");\n}\n\n// ---------------------------------------------------------------------------\n// claude.json helpers\n// ---------------------------------------------------------------------------\n\nfunction readClaudeJson(): Record<string, unknown> {\n if (!existsSync(CLAUDE_JSON_PATH)) return {};\n try {\n const raw = readFileSync(CLAUDE_JSON_PATH, \"utf8\");\n return JSON.parse(raw) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nfunction writeClaudeJson(data: Record<string, unknown>): void {\n writeFileSync(CLAUDE_JSON_PATH, JSON.stringify(data, null, 2) + \"\\n\", \"utf8\");\n}\n\n// ---------------------------------------------------------------------------\n// launchd plist generation\n// ---------------------------------------------------------------------------\n\nfunction generatePlist(daemonBin: string): string {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${PLIST_LABEL}</string>\n\n <key>ProgramArguments</key>\n <array>\n <string>/usr/local/bin/node</string>\n <string>${daemonBin}</string>\n <string>serve</string>\n </array>\n\n <key>KeepAlive</key>\n <true/>\n\n <key>ThrottleInterval</key>\n <integer>3</integer>\n\n <key>StandardOutPath</key>\n <string>${DAEMON_LOG}</string>\n\n <key>StandardErrorPath</key>\n <string>${DAEMON_LOG}</string>\n\n <key>RunAtLoad</key>\n <true/>\n\n <key>EnvironmentVariables</key>\n <dict>\n <key>PATH</key>\n <string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>\n </dict>\n</dict>\n</plist>\n`;\n}\n\n// ---------------------------------------------------------------------------\n// Command implementations\n// ---------------------------------------------------------------------------\n\nasync function cmdStatus(): Promise<void> {\n const config = loadConfig();\n const client = new PaiClient(config.socketPath);\n\n try {\n const status = await client.status();\n const s = status as Record<string, unknown>;\n\n console.log();\n console.log(bold(\" PAI Daemon Status\"));\n console.log();\n console.log(ok(` Daemon running`));\n console.log(dim(` Uptime: ${s[\"uptime\"]}s`));\n console.log(dim(` Socket: ${s[\"socketPath\"]}`));\n console.log(\n dim(\n ` Index: ${s[\"indexInProgress\"] ? \"in progress\" : \"idle\"} (interval: ${s[\"indexIntervalSecs\"]}s)`\n )\n );\n if (s[\"lastIndexTime\"]) {\n console.log(dim(` Last index: ${s[\"lastIndexTime\"]}`));\n }\n if (s[\"db\"]) {\n const db = s[\"db\"] as Record<string, unknown>;\n console.log(\n dim(\n ` DB: ${db[\"projects\"]} projects, ${db[\"files\"]} files, ${db[\"chunks\"]} chunks`\n )\n );\n }\n console.log();\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.log();\n console.log(warn(\" PAI Daemon Status\"));\n console.log();\n console.log(warn(` Daemon not running: ${msg}`));\n console.log(dim(\" Start with: pai daemon serve\"));\n console.log(dim(\" Or install as service: pai daemon install\"));\n console.log();\n process.exit(1);\n }\n}\n\nfunction cmdRestart(): void {\n // Find and signal the running daemon\n try {\n const result = spawnSync(\"pgrep\", [\"-f\", \"pai-daemon.*serve\"], {\n encoding: \"utf8\",\n });\n\n if (result.status !== 0 || !result.stdout.trim()) {\n console.log(warn(\"No running pai-daemon process found.\"));\n\n // If launchd is managing it, kick it via launchctl\n const unloadResult = spawnSync(\n \"launchctl\",\n [\"kickstart\", \"-k\", `gui/${process.getuid?.() ?? 501}/${PLIST_LABEL}`],\n { encoding: \"utf8\" }\n );\n if (unloadResult.status === 0) {\n console.log(ok(\"Sent kickstart to launchd.\"));\n } else {\n console.log(dim(\"Not managed by launchd either. Run: pai daemon serve\"));\n }\n return;\n }\n\n const pids = result.stdout\n .trim()\n .split(\"\\n\")\n .map((p) => p.trim());\n for (const pid of pids) {\n try {\n process.kill(parseInt(pid, 10), \"SIGTERM\");\n console.log(ok(`Sent SIGTERM to pid ${pid}.`));\n } catch {\n console.log(warn(`Could not signal pid ${pid}.`));\n }\n }\n console.log(dim(\"launchd will restart the daemon automatically.\"));\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(err(`restart error: ${msg}`));\n process.exit(1);\n }\n}\n\nfunction cmdInstall(): void {\n const daemonBin = getDaemonBinPath();\n const shimBin = getShimBinPath();\n\n console.log();\n console.log(bold(\" PAI Daemon Install\"));\n console.log();\n\n // 1. Write launchd plist\n if (!existsSync(LAUNCH_AGENTS_DIR)) {\n mkdirSync(LAUNCH_AGENTS_DIR, { recursive: true });\n }\n\n const plistContent = generatePlist(daemonBin);\n try {\n writeFileSync(PLIST_PATH, plistContent, \"utf8\");\n console.log(ok(` Wrote launchd plist: ${PLIST_PATH}`));\n } catch (e) {\n console.error(err(` Failed to write plist: ${e}`));\n process.exit(1);\n }\n\n // 2. Load the plist (unload first in case it was already there)\n try {\n spawnSync(\"launchctl\", [\"unload\", PLIST_PATH], { encoding: \"utf8\" });\n const loadResult = spawnSync(\"launchctl\", [\"load\", PLIST_PATH], {\n encoding: \"utf8\",\n });\n if (loadResult.status === 0) {\n console.log(ok(\" Loaded plist with launchctl.\"));\n } else {\n console.log(warn(` launchctl load: ${loadResult.stderr?.trim() ?? \"unknown error\"}`));\n }\n } catch {\n console.log(warn(\" Could not run launchctl. Load manually:\"));\n console.log(dim(` launchctl load ${PLIST_PATH}`));\n }\n\n // 3. Update ~/.claude.json to use the shim\n const config = readClaudeJson();\n\n if (typeof config.mcpServers !== \"object\" || config.mcpServers === null) {\n config.mcpServers = {};\n }\n\n const servers = config.mcpServers as Record<string, unknown>;\n\n // Check if already pointing to the shim\n const existing = servers[\"pai\"] as Record<string, unknown> | undefined;\n const existingArgs = existing?.args as string[] | undefined;\n const alreadyShim = existingArgs?.includes(shimBin);\n\n if (alreadyShim) {\n console.log(ok(\" ~/.claude.json already points to the shim.\"));\n } else {\n // Back up the existing entry if any\n if (existing) {\n servers[\"pai-legacy\"] = existing;\n console.log(dim(\" Backed up existing 'pai' entry as 'pai-legacy'.\"));\n }\n\n servers[\"pai\"] = {\n command: \"node\",\n args: [shimBin],\n };\n\n try {\n writeClaudeJson(config);\n console.log(ok(\" Updated ~/.claude.json to use daemon shim.\"));\n } catch (e) {\n console.error(err(` Failed to write ~/.claude.json: ${e}`));\n process.exit(1);\n }\n }\n\n // 4. Verify binaries exist\n console.log();\n if (!existsSync(daemonBin)) {\n console.log(warn(` Daemon binary not found: ${daemonBin}`));\n console.log(dim(\" Run: bun run build\"));\n } else {\n console.log(ok(` Daemon binary: ${daemonBin}`));\n }\n\n if (!existsSync(shimBin)) {\n console.log(warn(` Shim binary not found: ${shimBin}`));\n console.log(dim(\" Run: bun run build\"));\n } else {\n console.log(ok(` Shim binary: ${shimBin}`));\n }\n\n console.log();\n console.log(dim(\" Restart Claude Code to activate the daemon-backed PAI tools.\"));\n console.log();\n}\n\nfunction cmdUninstall(): void {\n console.log();\n console.log(bold(\" PAI Daemon Uninstall\"));\n console.log();\n\n // 1. Unload and remove plist\n if (existsSync(PLIST_PATH)) {\n try {\n spawnSync(\"launchctl\", [\"unload\", PLIST_PATH], { encoding: \"utf8\" });\n console.log(ok(\" Unloaded launchd plist.\"));\n } catch {\n console.log(warn(\" Could not unload plist via launchctl.\"));\n }\n try {\n unlinkSync(PLIST_PATH);\n console.log(ok(` Removed plist: ${PLIST_PATH}`));\n } catch (e) {\n console.log(warn(` Could not remove plist: ${e}`));\n }\n } else {\n console.log(dim(\" No launchd plist found.\"));\n }\n\n // 2. Revert ~/.claude.json to legacy direct MCP\n const config = readClaudeJson();\n const servers =\n typeof config.mcpServers === \"object\" && config.mcpServers !== null\n ? (config.mcpServers as Record<string, unknown>)\n : {};\n\n const legacy = servers[\"pai-legacy\"];\n if (legacy) {\n servers[\"pai\"] = legacy;\n delete servers[\"pai-legacy\"];\n try {\n writeClaudeJson(config);\n console.log(ok(\" Reverted ~/.claude.json to legacy direct MCP.\"));\n } catch (e) {\n console.log(warn(` Could not update ~/.claude.json: ${e}`));\n }\n } else {\n console.log(dim(\" No legacy PAI entry found in ~/.claude.json.\"));\n }\n\n console.log();\n console.log(dim(\" Restart Claude Code to deactivate daemon-backed tools.\"));\n console.log();\n}\n\nasync function cmdMigrate(connectionString?: string): Promise<void> {\n console.log();\n console.log(bold(\" PAI Daemon Migrate\"));\n console.log();\n console.log(dim(\" Running SQLite → PostgreSQL migration...\"));\n console.log();\n\n // Resolve the migration script path relative to this built file\n const { fileURLToPath } = await import(\"node:url\");\n const { dirname: pathDirname, join: pathJoin } = await import(\"node:path\");\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = pathDirname(__filename);\n\n // The migration script is at docker/migrate-sqlite.ts in the source tree\n // When built, we look for it relative to the package root\n const migrationScript = pathJoin(__dirname, \"../..\", \"docker\", \"migrate-sqlite.ts\");\n const { spawnSync: spawn } = await import(\"node:child_process\");\n\n // Use npx tsx to run the TypeScript migration script (bun doesn't support better-sqlite3)\n const spawnArgs = [\"tsx\", migrationScript];\n if (connectionString) {\n spawnArgs.push(\"--connection-string\", connectionString);\n }\n\n const result = spawn(\"npx\", spawnArgs, { stdio: \"inherit\", encoding: \"utf8\" });\n\n if (result.status !== 0) {\n console.error(err(\" Migration failed. Check output above.\"));\n process.exit(result.status ?? 1);\n }\n}\n\nfunction cmdLogs(opts: { lines?: string; follow?: boolean }): void {\n const lines = opts.lines ?? \"50\";\n\n if (!existsSync(DAEMON_LOG)) {\n console.log(warn(`No daemon log found at ${DAEMON_LOG}.`));\n console.log(dim(\"The daemon may not have run yet.\"));\n return;\n }\n\n if (opts.follow) {\n // exec stays running\n try {\n execSync(`tail -f -n ${lines} \"${DAEMON_LOG}\"`, { stdio: \"inherit\" });\n } catch {\n // User pressed Ctrl+C — that's fine\n }\n } else {\n try {\n execSync(`tail -n ${lines} \"${DAEMON_LOG}\"`, { stdio: \"inherit\" });\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(err(`Could not read log: ${msg}`));\n process.exit(1);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerDaemonCommands(daemonCmd: Command): void {\n daemonCmd\n .command(\"serve\")\n .description(\"Start the PAI daemon in the foreground\")\n .action(async () => {\n const { serve } = await import(\"../../daemon/daemon.js\");\n const { loadConfig: lc, ensureConfigDir } = await import(\n \"../../daemon/config.js\"\n );\n ensureConfigDir();\n const config = lc();\n await serve(config);\n });\n\n daemonCmd\n .command(\"migrate\")\n .description(\"Migrate federation data from SQLite to PostgreSQL\")\n .option(\n \"--connection-string <url>\",\n \"Postgres connection string (default: from config or postgresql://pai:pai@localhost:5432/pai)\"\n )\n .action(async (opts: { connectionString?: string }) => {\n await cmdMigrate(opts.connectionString);\n });\n\n daemonCmd\n .command(\"status\")\n .description(\"Query the running daemon status\")\n .action(async () => {\n await cmdStatus();\n });\n\n daemonCmd\n .command(\"restart\")\n .description(\"Send SIGTERM to the running daemon (launchd will restart it)\")\n .action(() => {\n cmdRestart();\n });\n\n daemonCmd\n .command(\"install\")\n .description(\n \"Install daemon as a launchd service and update ~/.claude.json to use the shim\"\n )\n .action(() => {\n cmdInstall();\n });\n\n daemonCmd\n .command(\"uninstall\")\n .description(\"Remove the launchd service and revert to direct MCP\")\n .action(() => {\n cmdUninstall();\n });\n\n daemonCmd\n .command(\"logs\")\n .description(`Tail the daemon log (${DAEMON_LOG})`)\n .option(\"-n, --lines <n>\", \"Number of lines to show\", \"50\")\n .option(\"-f, --follow\", \"Follow log output (like tail -f)\")\n .action((opts: { lines?: string; follow?: boolean }) => {\n cmdLogs(opts);\n });\n}\n","/**\n * pai backup — snapshot registry, config, and Postgres database.\n *\n * Creates a timestamped backup directory at:\n * ~/.pai/backups/YYYY-MM-DD-HHmmss/\n *\n * Contents:\n * registry.db — SQLite registry database\n * config.json — PAI daemon config\n * postgres-pai.sql — pg_dump of the Postgres \"pai\" database (via docker exec)\n */\n\nimport type { Command } from \"commander\";\nimport {\n existsSync,\n mkdirSync,\n copyFileSync,\n statSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { execSync } from \"node:child_process\";\nimport { ok, warn, err, dim, bold } from \"../utils.js\";\nimport { loadConfig } from \"../../daemon/config.js\";\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nconst HOME = homedir();\nconst REGISTRY_DB = join(HOME, \".pai\", \"registry.db\");\nconst CONFIG_FILE = join(HOME, \".config\", \"pai\", \"config.json\");\nconst BACKUPS_DIR = join(HOME, \".pai\", \"backups\");\nconst DOCKER_CONTAINER = \"pai-pgvector\";\nconst PG_DATABASE = \"pai\";\nconst PG_USER = \"pai\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction timestamp(): string {\n const now = new Date();\n const YYYY = now.getFullYear();\n const MM = String(now.getMonth() + 1).padStart(2, \"0\");\n const DD = String(now.getDate()).padStart(2, \"0\");\n const HH = String(now.getHours()).padStart(2, \"0\");\n const mm = String(now.getMinutes()).padStart(2, \"0\");\n const ss = String(now.getSeconds()).padStart(2, \"0\");\n return `${YYYY}-${MM}-${DD}-${HH}${mm}${ss}`;\n}\n\nfunction fmtBytes(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 fileSize(path: string): string {\n try {\n return fmtBytes(statSync(path).size);\n } catch {\n return \"unknown\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registration\n// ---------------------------------------------------------------------------\n\nexport function registerBackupCommands(program: Command): void {\n program\n .command(\"backup\")\n .description(\"Backup registry, config, and Postgres database to ~/.pai/backups/\")\n .option(\"--no-postgres\", \"Skip the Postgres pg_dump (faster, registry+config only)\")\n .action(async (opts: { postgres: boolean }) => {\n const ts = timestamp();\n const backupDir = join(BACKUPS_DIR, ts);\n\n console.log(dim(`Creating backup: ${backupDir}`));\n mkdirSync(backupDir, { recursive: true });\n\n const results: { label: string; path: string; size: string; status: string }[] = [];\n\n // ------------------------------------------------------------------\n // 1. Registry SQLite DB\n // ------------------------------------------------------------------\n\n if (existsSync(REGISTRY_DB)) {\n const dest = join(backupDir, \"registry.db\");\n try {\n copyFileSync(REGISTRY_DB, dest);\n results.push({ label: \"Registry DB\", path: dest, size: fileSize(dest), status: ok(\"ok\") });\n } catch (e) {\n results.push({ label: \"Registry DB\", path: dest, size: \"-\", status: err(`failed: ${e}`) });\n }\n } else {\n results.push({ label: \"Registry DB\", path: REGISTRY_DB, size: \"-\", status: warn(\"not found — skipped\") });\n }\n\n // ------------------------------------------------------------------\n // 2. Config file\n // ------------------------------------------------------------------\n\n if (existsSync(CONFIG_FILE)) {\n const dest = join(backupDir, \"config.json\");\n try {\n copyFileSync(CONFIG_FILE, dest);\n results.push({ label: \"Config\", path: dest, size: fileSize(dest), status: ok(\"ok\") });\n } catch (e) {\n results.push({ label: \"Config\", path: dest, size: \"-\", status: err(`failed: ${e}`) });\n }\n } else {\n results.push({ label: \"Config\", path: CONFIG_FILE, size: \"-\", status: warn(\"not found — skipped\") });\n }\n\n // ------------------------------------------------------------------\n // 3. Optional: Federation SQLite (legacy)\n // ------------------------------------------------------------------\n\n const federationDb = join(HOME, \".pai\", \"federation.db\");\n if (existsSync(federationDb)) {\n const dest = join(backupDir, \"federation.db\");\n try {\n copyFileSync(federationDb, dest);\n results.push({ label: \"Federation DB (legacy)\", path: dest, size: fileSize(dest), status: ok(\"ok\") });\n } catch (e) {\n results.push({ label: \"Federation DB (legacy)\", path: dest, size: \"-\", status: warn(`skipped: ${e}`) });\n }\n }\n\n // ------------------------------------------------------------------\n // 4. Postgres pg_dump via docker exec\n // ------------------------------------------------------------------\n\n if (opts.postgres) {\n const sqlDest = join(backupDir, \"postgres-pai.sql\");\n console.log(dim(` Running pg_dump on ${DOCKER_CONTAINER} (this may take a moment)...`));\n try {\n // Check Docker is running and container exists\n execSync(`docker inspect ${DOCKER_CONTAINER} --format='{{.State.Status}}'`, {\n stdio: \"pipe\",\n });\n\n execSync(\n `docker exec ${DOCKER_CONTAINER} pg_dump -U ${PG_USER} ${PG_DATABASE} > \"${sqlDest}\"`,\n { stdio: [\"pipe\", \"pipe\", \"pipe\"], shell: true as unknown as string }\n );\n results.push({ label: \"Postgres DB\", path: sqlDest, size: fileSize(sqlDest), status: ok(\"ok\") });\n } catch (e) {\n const msg = e instanceof Error ? e.message.split(\"\\n\")[0] : String(e);\n results.push({ label: \"Postgres DB\", path: sqlDest, size: \"-\", status: err(`failed: ${msg}`) });\n console.log(warn(` Postgres backup failed. Is Docker running with container '${DOCKER_CONTAINER}'?`));\n }\n } else {\n results.push({ label: \"Postgres DB\", path: \"-\", size: \"-\", status: dim(\"skipped (--no-postgres)\") });\n }\n\n // ------------------------------------------------------------------\n // Summary\n // ------------------------------------------------------------------\n\n console.log(`\\n${bold(\"Backup complete:\")} ${backupDir}\\n`);\n\n const labelWidth = Math.max(...results.map((r) => r.label.length)) + 2;\n for (const r of results) {\n const label = r.label.padEnd(labelWidth);\n console.log(` ${bold(label)} ${r.status} ${dim(r.size)}`);\n }\n\n console.log(`\\n ${dim(\"Path:\")} ${backupDir}`);\n console.log(` ${dim(\"To restore:\")} pai restore ${backupDir}\\n`);\n });\n}\n","/**\n * pai restore [path] — restore from a backup created by `pai backup`.\n *\n * If no path is given, lists available backups and restores the latest one\n * after prompting for confirmation.\n *\n * Restores:\n * registry.db — SQLite registry database\n * config.json — PAI daemon config\n * postgres-pai.sql — Postgres dump (piped into psql via docker exec)\n * federation.db — Legacy SQLite federation DB (if present)\n */\n\nimport type { Command } from \"commander\";\nimport {\n existsSync,\n readdirSync,\n statSync,\n copyFileSync,\n mkdirSync,\n readFileSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { execSync, spawnSync } from \"node:child_process\";\nimport { createInterface } from \"node:readline\";\nimport { ok, warn, err, dim, bold } from \"../utils.js\";\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nconst HOME = homedir();\nconst REGISTRY_DB = join(HOME, \".pai\", \"registry.db\");\nconst CONFIG_FILE = join(HOME, \".config\", \"pai\", \"config.json\");\nconst BACKUPS_DIR = join(HOME, \".pai\", \"backups\");\nconst DOCKER_CONTAINER = \"pai-pgvector\";\nconst PG_DATABASE = \"pai\";\nconst PG_USER = \"pai\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction confirm(question: string): Promise<boolean> {\n return new Promise((resolve) => {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n rl.question(`${question} [y/N] `, (answer) => {\n rl.close();\n resolve(answer.trim().toLowerCase() === \"y\");\n });\n });\n}\n\nfunction listBackups(): string[] {\n if (!existsSync(BACKUPS_DIR)) return [];\n return readdirSync(BACKUPS_DIR)\n .filter((name) => {\n const full = join(BACKUPS_DIR, name);\n return statSync(full).isDirectory() && /^\\d{4}-\\d{2}-\\d{2}-\\d{6}$/.test(name);\n })\n .sort()\n .reverse(); // newest first\n}\n\nfunction formatBackupDate(name: string): string {\n // YYYY-MM-DD-HHmmss → \"YYYY-MM-DD HH:mm:ss\"\n const m = name.match(/^(\\d{4}-\\d{2}-\\d{2})-(\\d{2})(\\d{2})(\\d{2})$/);\n if (!m) return name;\n return `${m[1]} ${m[2]}:${m[3]}:${m[4]}`;\n}\n\nfunction backupContents(backupDir: string): string[] {\n try {\n return readdirSync(backupDir);\n } catch {\n return [];\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registration\n// ---------------------------------------------------------------------------\n\nexport function registerRestoreCommands(program: Command): void {\n program\n .command(\"restore [backup-path]\")\n .description(\"Restore from a backup directory (created by pai backup)\")\n .option(\"--no-postgres\", \"Skip restoring the Postgres database\")\n .option(\"--yes\", \"Skip confirmation prompt (non-interactive)\")\n .action(async (backupPath: string | undefined, opts: { postgres: boolean; yes: boolean }) => {\n\n // ------------------------------------------------------------------\n // 1. Determine backup directory\n // ------------------------------------------------------------------\n\n let resolvedDir: string;\n\n if (backupPath) {\n resolvedDir = backupPath.startsWith(\"~\")\n ? backupPath.replace(/^~/, HOME)\n : backupPath;\n\n if (!existsSync(resolvedDir)) {\n console.error(err(`Backup directory not found: ${resolvedDir}`));\n process.exit(1);\n }\n } else {\n // No path given — pick from available backups\n const backups = listBackups();\n if (backups.length === 0) {\n console.error(err(`No backups found in ${BACKUPS_DIR}`));\n console.log(dim(\" Run 'pai backup' to create one.\"));\n process.exit(1);\n }\n\n console.log(`\\n${bold(\"Available backups\")} (newest first):\\n`);\n backups.forEach((name, i) => {\n const dir = join(BACKUPS_DIR, name);\n const contents = backupContents(dir).join(\", \");\n const marker = i === 0 ? ok(\" ← latest\") : \"\";\n console.log(` ${bold(String(i + 1).padStart(2))}. ${formatBackupDate(name)}${marker}`);\n console.log(` ${dim(dir)}`);\n console.log(` ${dim(\"Contents:\")} ${contents}`);\n });\n\n console.log();\n resolvedDir = join(BACKUPS_DIR, backups[0]);\n console.log(dim(`Using latest backup: ${resolvedDir}\\n`));\n }\n\n // ------------------------------------------------------------------\n // 2. Inventory what's in the backup\n // ------------------------------------------------------------------\n\n const hasRegistry = existsSync(join(resolvedDir, \"registry.db\"));\n const hasConfig = existsSync(join(resolvedDir, \"config.json\"));\n const hasSql = existsSync(join(resolvedDir, \"postgres-pai.sql\"));\n const hasFed = existsSync(join(resolvedDir, \"federation.db\"));\n\n console.log(`${bold(\"Backup contents:\")}`);\n console.log(` registry.db ${hasRegistry ? ok(\"present\") : warn(\"missing\")}`);\n console.log(` config.json ${hasConfig ? ok(\"present\") : warn(\"missing\")}`);\n console.log(` postgres-pai.sql ${hasSql && opts.postgres ? ok(\"present\") : hasSql ? warn(\"present (skipped via --no-postgres)\") : warn(\"missing\")}`);\n if (hasFed) {\n console.log(` federation.db ${ok(\"present\")} ${dim(\"(legacy)\")}`);\n }\n\n // ------------------------------------------------------------------\n // 3. Confirm\n // ------------------------------------------------------------------\n\n console.log(`\\n${warn(\"WARNING:\")} This will OVERWRITE your current PAI data.`);\n if (hasSql && opts.postgres) {\n console.log(warn(\" Postgres database will be dropped and recreated from the backup.\"));\n }\n\n const proceed = opts.yes || await confirm(\"Proceed with restore?\");\n if (!proceed) {\n console.log(dim(\"Restore cancelled.\"));\n process.exit(0);\n }\n\n console.log();\n\n const results: { label: string; status: string }[] = [];\n\n // ------------------------------------------------------------------\n // 4. Restore registry.db\n // ------------------------------------------------------------------\n\n if (hasRegistry) {\n try {\n mkdirSync(join(HOME, \".pai\"), { recursive: true });\n copyFileSync(join(resolvedDir, \"registry.db\"), REGISTRY_DB);\n results.push({ label: \"Registry DB\", status: ok(\"restored\") });\n } catch (e) {\n results.push({ label: \"Registry DB\", status: err(`failed: ${e}`) });\n }\n } else {\n results.push({ label: \"Registry DB\", status: warn(\"missing in backup — skipped\") });\n }\n\n // ------------------------------------------------------------------\n // 5. Restore config.json\n // ------------------------------------------------------------------\n\n if (hasConfig) {\n try {\n mkdirSync(join(HOME, \".config\", \"pai\"), { recursive: true });\n copyFileSync(join(resolvedDir, \"config.json\"), CONFIG_FILE);\n results.push({ label: \"Config\", status: ok(\"restored\") });\n } catch (e) {\n results.push({ label: \"Config\", status: err(`failed: ${e}`) });\n }\n } else {\n results.push({ label: \"Config\", status: warn(\"missing in backup — skipped\") });\n }\n\n // ------------------------------------------------------------------\n // 6. Restore federation.db (legacy)\n // ------------------------------------------------------------------\n\n if (hasFed) {\n try {\n copyFileSync(join(resolvedDir, \"federation.db\"), join(HOME, \".pai\", \"federation.db\"));\n results.push({ label: \"Federation DB (legacy)\", status: ok(\"restored\") });\n } catch (e) {\n results.push({ label: \"Federation DB (legacy)\", status: warn(`skipped: ${e}`) });\n }\n }\n\n // ------------------------------------------------------------------\n // 7. Restore Postgres via docker exec\n // ------------------------------------------------------------------\n\n if (hasSql && opts.postgres) {\n console.log(dim(\" Restoring Postgres database (this may take a while)...\"));\n try {\n // Verify container is running\n execSync(`docker inspect ${DOCKER_CONTAINER} --format='{{.State.Status}}'`, {\n stdio: \"pipe\",\n });\n\n // Drop and recreate the database, then restore\n const dropCreate = `docker exec ${DOCKER_CONTAINER} psql -U ${PG_USER} -c \"DROP DATABASE IF EXISTS ${PG_DATABASE}; CREATE DATABASE ${PG_DATABASE} OWNER ${PG_USER};\"`;\n execSync(dropCreate, { stdio: \"pipe\", shell: true as unknown as string });\n\n // Pipe the SQL dump into psql\n const sqlContent = readFileSync(join(resolvedDir, \"postgres-pai.sql\"), \"utf8\");\n const psqlResult = spawnSync(\n \"docker\",\n [\"exec\", \"-i\", DOCKER_CONTAINER, \"psql\", \"-U\", PG_USER, PG_DATABASE],\n { input: sqlContent, encoding: \"utf8\", stdio: [\"pipe\", \"pipe\", \"pipe\"] }\n );\n\n if (psqlResult.status !== 0) {\n const errMsg = psqlResult.stderr?.split(\"\\n\")[0] ?? \"unknown error\";\n results.push({ label: \"Postgres DB\", status: err(`failed: ${errMsg}`) });\n } else {\n results.push({ label: \"Postgres DB\", status: ok(\"restored\") });\n }\n } catch (e) {\n const msg = e instanceof Error ? e.message.split(\"\\n\")[0] : String(e);\n results.push({ label: \"Postgres DB\", status: err(`failed: ${msg}`) });\n console.log(warn(` Is Docker running with container '${DOCKER_CONTAINER}'?`));\n }\n } else if (!hasSql || !opts.postgres) {\n const reason = !hasSql ? \"no SQL dump in backup\" : \"--no-postgres\";\n results.push({ label: \"Postgres DB\", status: dim(`skipped (${reason})`) });\n }\n\n // ------------------------------------------------------------------\n // Summary\n // ------------------------------------------------------------------\n\n console.log(`\\n${bold(\"Restore complete:\")}\\n`);\n const labelWidth = Math.max(...results.map((r) => r.label.length)) + 2;\n for (const r of results) {\n console.log(` ${bold(r.label.padEnd(labelWidth))} ${r.status}`);\n }\n\n const hasErrors = results.some((r) => r.status.includes(\"\\u001b[31m\"));\n if (!hasErrors) {\n console.log(`\\n ${ok(\"All done.\")} You may need to restart the PAI daemon.\\n`);\n console.log(` ${dim(\"Restart:\")} pai daemon restart\\n`);\n } else {\n console.log(`\\n ${warn(\"Some items failed — check output above.\")}\\n`);\n process.exit(1);\n }\n });\n}\n","/**\n * Shared helpers for the PAI setup wizard: chalk colour shortcuts,\n * readline prompts, config read/write, and filesystem path finders.\n */\n\nimport { createInterface } from \"node:readline\";\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport { CONFIG_DIR, CONFIG_FILE } from \"../../../daemon/config.js\";\n\n// ---------------------------------------------------------------------------\n// Chalk colour helpers\n// ---------------------------------------------------------------------------\n\nexport const c = {\n bold: (s: string) => chalk.bold(s),\n dim: (s: string) => chalk.dim(s),\n green: (s: string) => chalk.green(s),\n yellow: (s: string) => chalk.yellow(s),\n cyan: (s: string) => chalk.cyan(s),\n red: (s: string) => chalk.red(s),\n blue: (s: string) => chalk.blue(s),\n ok: (s: string) => chalk.green(\" \" + s),\n warn: (s: string) => chalk.yellow(\" \" + s),\n err: (s: string) => chalk.red(\" \" + s),\n};\n\nexport function line(text = \"\"): void {\n console.log(text);\n}\n\nexport function section(title: string): void {\n line();\n console.log(chalk.bold.cyan(\" \" + title));\n console.log(chalk.dim(\" \" + \"─\".repeat(title.length)));\n}\n\n// ---------------------------------------------------------------------------\n// Readline prompt helpers\n// ---------------------------------------------------------------------------\n\nexport function createRl() {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n rl.on(\"SIGINT\", () => {\n line();\n line(c.dim(\" Setup cancelled. Run `pai setup` again to restart.\"));\n line();\n process.exit(0);\n });\n\n return rl;\n}\n\nexport type Rl = ReturnType<typeof createRl>;\n\nexport async function prompt(rl: Rl, question: string): Promise<string> {\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n resolve(answer.trim());\n });\n });\n}\n\n/** Prompt for a numbered menu selection. Returns 0-based index. */\nexport async function promptMenu(\n rl: Rl,\n options: Array<{ label: string; description?: string }>,\n defaultIdx = 0,\n): Promise<number> {\n for (let i = 0; i < options.length; i++) {\n const num = chalk.bold(` ${i + 1}.`);\n const label = i === defaultIdx ? chalk.cyan(options[i].label) : options[i].label;\n const marker = i === defaultIdx ? chalk.dim(\" (recommended)\") : \"\";\n console.log(`${num} ${label}${marker}`);\n if (options[i].description) {\n console.log(chalk.dim(` ${options[i].description}`));\n }\n }\n line();\n\n while (true) {\n const answer = await prompt(\n rl,\n chalk.bold(` Enter number [1-${options.length}] (default: ${defaultIdx + 1}): `),\n );\n\n if (answer === \"\") return defaultIdx;\n\n const n = parseInt(answer, 10);\n if (!isNaN(n) && n >= 1 && n <= options.length) {\n return n - 1;\n }\n\n console.log(c.warn(`Please enter a number between 1 and ${options.length}.`));\n }\n}\n\n/** Prompt for a yes/no answer. Returns true for yes. */\nexport async function promptYesNo(\n rl: Rl,\n question: string,\n defaultYes = true,\n): Promise<boolean> {\n const hint = defaultYes ? \"[Y/n]\" : \"[y/N]\";\n const answer = await prompt(rl, ` ${question} ${chalk.dim(hint)}: `);\n\n if (answer === \"\") return defaultYes;\n return answer.toLowerCase().startsWith(\"y\");\n}\n\n// ---------------------------------------------------------------------------\n// Config read/write helpers\n// ---------------------------------------------------------------------------\n\nexport function readConfigRaw(): Record<string, unknown> {\n if (!existsSync(CONFIG_FILE)) return {};\n try {\n return JSON.parse(readFileSync(CONFIG_FILE, \"utf-8\")) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nexport function writeConfigRaw(data: Record<string, unknown>): void {\n if (!existsSync(CONFIG_DIR)) {\n mkdirSync(CONFIG_DIR, { recursive: true });\n }\n writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2) + \"\\n\", \"utf-8\");\n}\n\nexport function mergeConfig(updates: Record<string, unknown>): void {\n const current = readConfigRaw();\n const merged = { ...current, ...updates };\n if (updates.postgres && typeof current.postgres === \"object\" && current.postgres !== null) {\n merged.postgres = { ...(current.postgres as object), ...(updates.postgres as object) };\n }\n writeConfigRaw(merged);\n}\n\n// ---------------------------------------------------------------------------\n// Docker and connection helpers\n// ---------------------------------------------------------------------------\n\nexport function hasDocker(): boolean {\n try {\n const result = spawnSync(\"docker\", [\"--version\"], { stdio: \"pipe\" });\n return result.status === 0;\n } catch {\n return false;\n }\n}\n\nexport function getDockerDir(): string {\n const candidates = [\n join(process.cwd(), \"docker\"),\n join(homedir(), \"dev\", \"ai\", \"PAI\", \"docker\"),\n join(\"/\", \"usr\", \"local\", \"lib\", \"node_modules\", \"@tekmidian\", \"pai\", \"docker\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(join(candidate, \"docker-compose.yml\"))) return candidate;\n }\n return join(process.cwd(), \"docker\");\n}\n\nexport async function testPostgresConnection(connectionString: string): Promise<boolean> {\n try {\n const pgModule = await import(\"pg\");\n const pg = pgModule.default ?? pgModule;\n const client = new pg.Client({ connectionString });\n await client.connect();\n await client.end();\n return true;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Filesystem path finders\n// ---------------------------------------------------------------------------\n\nexport function getTemplatesDir(): string {\n const candidates = [\n join(process.cwd(), \"templates\"),\n join(homedir(), \"dev\", \"ai\", \"PAI\", \"templates\"),\n join(\"/\", \"usr\", \"local\", \"lib\", \"node_modules\", \"@tekmidian\", \"pai\", \"templates\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(join(candidate, \"claude-md.template.md\"))) return candidate;\n }\n return join(process.cwd(), \"templates\");\n}\n\nexport function getHooksDir(): string {\n const candidates = [\n join(process.cwd(), \"src\", \"hooks\"),\n join(homedir(), \"dev\", \"ai\", \"PAI\", \"src\", \"hooks\"),\n join(\"/\", \"usr\", \"local\", \"lib\", \"node_modules\", \"@tekmidian\", \"pai\", \"src\", \"hooks\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(join(candidate, \"session-stop.sh\"))) return candidate;\n }\n return join(process.cwd(), \"src\", \"hooks\");\n}\n\nexport function getDistHooksDir(): string {\n const moduleDir = new URL(\".\", import.meta.url).pathname;\n const fromModule = join(moduleDir, \"..\", \"..\", \"hooks\");\n\n const candidates = [\n fromModule,\n join(process.cwd(), \"dist\", \"hooks\"),\n join(homedir(), \"dev\", \"ai\", \"PAI\", \"dist\", \"hooks\"),\n join(\"/\", \"usr\", \"local\", \"lib\", \"node_modules\", \"@tekmidian\", \"pai\", \"dist\", \"hooks\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(join(candidate, \"stop-hook.mjs\"))) return candidate;\n }\n return fromModule;\n}\n\nexport function getDistDir(): string {\n const moduleDir = new URL(\".\", import.meta.url).pathname;\n const fromModule = join(moduleDir, \"..\", \"..\", \"..\");\n\n const candidates = [\n fromModule,\n join(process.cwd(), \"dist\"),\n join(homedir(), \"dev\", \"ai\", \"PAI\", \"dist\"),\n join(\"/\", \"usr\", \"local\", \"lib\", \"node_modules\", \"@tekmidian\", \"pai\", \"dist\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(join(candidate, \"skills\"))) return candidate;\n }\n return fromModule;\n}\n\nexport function getStatuslineScript(): string | null {\n const candidates = [\n join(process.cwd(), \"statusline-command.sh\"),\n join(homedir(), \"dev\", \"ai\", \"PAI\", \"statusline-command.sh\"),\n join(\"/\", \"usr\", \"local\", \"lib\", \"node_modules\", \"@tekmidian\", \"pai\", \"statusline-command.sh\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(candidate)) return candidate;\n }\n return null;\n}\n\nexport function getTabColorScript(): string | null {\n const candidates = [\n join(process.cwd(), \"tab-color-command.sh\"),\n join(homedir(), \"dev\", \"ai\", \"PAI\", \"tab-color-command.sh\"),\n join(\"/\", \"usr\", \"local\", \"lib\", \"node_modules\", \"@tekmidian\", \"pai\", \"tab-color-command.sh\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(candidate)) return candidate;\n }\n return null;\n}\n","/** Step 1: Welcome banner displayed at the start of the setup wizard. */\n\nimport chalk from \"chalk\";\nimport { c, line } from \"../utils.js\";\n\nexport function stepWelcome(): void {\n line();\n line(chalk.bold.cyan(\" ╔════════════════════════════════════════╗\"));\n line(chalk.bold.cyan(\" ║ PAI Knowledge OS — Setup Wizard ║\"));\n line(chalk.bold.cyan(\" ╚════════════════════════════════════════╝\"));\n line();\n line(\" PAI is a personal knowledge system that indexes your files, generates\");\n line(\" semantic embeddings for intelligent search, and stores everything in a\");\n line(\" local database so you can search your knowledge base with natural language.\");\n line();\n line(c.dim(\" This wizard will guide you through the initial configuration.\"));\n line(c.dim(\" Press Ctrl+C at any time to cancel.\"));\n}\n","/** Step 2: Storage backend selection (SQLite or PostgreSQL) and Docker helper. */\n\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport { c, line, section, type Rl, prompt, promptMenu, promptYesNo, readConfigRaw, getDockerDir, hasDocker, testPostgresConnection } from \"../utils.js\";\n\nasync function startDocker(rl: Rl): Promise<boolean> {\n const dockerDir = getDockerDir();\n const composePath = join(dockerDir, \"docker-compose.yml\");\n\n if (!existsSync(composePath)) {\n console.log(c.warn(`docker-compose.yml not found at ${dockerDir}`));\n console.log(c.dim(\" You can start it manually later:\"));\n console.log(c.dim(\" docker compose up -d\"));\n return false;\n }\n\n console.log(c.dim(` Starting PostgreSQL container from ${dockerDir}...`));\n\n try {\n const result = spawnSync(\"docker\", [\"compose\", \"up\", \"-d\"], {\n cwd: dockerDir,\n stdio: \"inherit\",\n });\n\n if (result.status !== 0) {\n console.log(c.warn(\" Docker compose failed. You can start it manually:\"));\n console.log(c.dim(` cd ${dockerDir} && docker compose up -d`));\n return false;\n }\n\n console.log(c.ok(\"PostgreSQL container started.\"));\n return true;\n } catch (e) {\n console.log(c.warn(` Could not run docker compose: ${e}`));\n return false;\n }\n}\n\nexport async function stepStorage(rl: Rl): Promise<Record<string, unknown>> {\n section(\"Step 2: Storage Backend\");\n\n const existing = readConfigRaw();\n if (existing.storageBackend) {\n const backend = String(existing.storageBackend);\n if (backend === \"postgres\") {\n try {\n const result = spawnSync(\"docker\", [\"ps\", \"--filter\", \"name=pai-pgvector\", \"--format\", \"{{.Status}}\"], { stdio: \"pipe\" });\n const status = result.stdout?.toString().trim();\n if (status && status.includes(\"Up\")) {\n console.log(c.ok(`Storage backend: PostgreSQL (container running). Skipping.`));\n return existing;\n }\n console.log(c.dim(\" PostgreSQL container found but not running. Starting...\"));\n await startDocker(rl);\n await new Promise((r) => setTimeout(r, 3000));\n console.log(c.ok(\"PostgreSQL container started.\"));\n } catch {\n console.log(c.ok(`Storage backend: PostgreSQL (configured). Skipping.`));\n }\n return existing;\n } else {\n console.log(c.ok(`Storage backend: ${backend}. Skipping.`));\n return existing;\n }\n }\n\n line();\n line(\" Choose how PAI stores your indexed knowledge:\");\n line();\n\n const choice = await promptMenu(rl, [\n {\n label: \"PostgreSQL with pgvector\",\n description: \"Best for large collections, semantic search, and production use. Requires Docker or a Postgres server.\",\n },\n {\n label: \"SQLite\",\n description: \"Simple, no dependencies, zero configuration. Good for trying PAI out. Keyword search only.\",\n },\n ]);\n\n if (choice === 1) {\n line();\n console.log(c.ok(\"SQLite selected. No additional setup needed.\"));\n return { storageBackend: \"sqlite\" };\n }\n\n // PostgreSQL\n line();\n line(\" PostgreSQL requires a running Postgres server with the pgvector extension.\");\n line();\n\n if (hasDocker()) {\n console.log(c.ok(\"Docker is installed.\"));\n line();\n\n const useDocker = await promptYesNo(rl, \"Start the PAI PostgreSQL container with Docker? (recommended)\", true);\n\n if (useDocker) {\n line();\n await startDocker(rl);\n line();\n\n console.log(c.dim(\" Waiting 3 seconds for container to be ready...\"));\n await new Promise((r) => setTimeout(r, 3000));\n\n const connStr = \"postgresql://pai:pai@localhost:5432/pai\";\n console.log(c.dim(` Testing connection to ${connStr}...`));\n\n const ok2 = await testPostgresConnection(connStr);\n if (ok2) {\n console.log(c.ok(\"Connection successful!\"));\n } else {\n console.log(c.warn(\"Connection test failed. The container may still be starting.\"));\n console.log(c.dim(\" Using default connection string — you can verify with `pai daemon status`.\"));\n }\n return { storageBackend: \"postgres\", postgres: { connectionString: connStr } };\n }\n } else {\n console.log(c.dim(\" Docker not found. Using manual connection string entry.\"));\n }\n\n // Manual entry\n line();\n line(\" Enter your PostgreSQL connection details:\");\n line();\n\n const useConnStr = await promptYesNo(rl, \"Use a full connection string? (e.g. postgresql://user:pass@host:5432/dbname)\", true);\n\n if (useConnStr) {\n const connStr = await prompt(rl, chalk.bold(\" Connection string: \"));\n if (connStr) {\n console.log(c.dim(\" Testing connection...\"));\n const connected = await testPostgresConnection(connStr);\n if (connected) {\n console.log(c.ok(\"Connection successful!\"));\n } else {\n console.log(c.warn(\"Connection test failed — check credentials and try again later.\"));\n }\n return { storageBackend: \"postgres\", postgres: { connectionString: connStr } };\n }\n }\n\n const host = await prompt(rl, chalk.bold(\" Host [localhost]: \")) || \"localhost\";\n const portStr = await prompt(rl, chalk.bold(\" Port [5432]: \")) || \"5432\";\n const database = await prompt(rl, chalk.bold(\" Database [pai]: \")) || \"pai\";\n const user = await prompt(rl, chalk.bold(\" User [pai]: \")) || \"pai\";\n const password = await prompt(rl, chalk.bold(\" Password [pai]: \")) || \"pai\";\n\n const connStr = `postgresql://${user}:${password}@${host}:${portStr}/${database}`;\n console.log(c.dim(` Connection string: ${connStr}`));\n console.log(c.dim(\" Testing connection...\"));\n\n const connected = await testPostgresConnection(connStr);\n if (connected) {\n console.log(c.ok(\"Connection successful!\"));\n } else {\n console.log(c.warn(\"Connection test failed — check credentials and try again later.\"));\n }\n\n return { storageBackend: \"postgres\", postgres: { connectionString: connStr } };\n}\n","/** Step 3: Embedding model selection for semantic search. */\n\nimport { c, line, section, type Rl, promptMenu, readConfigRaw } from \"../utils.js\";\n\nexport async function stepEmbedding(rl: Rl): Promise<Record<string, unknown>> {\n section(\"Step 3: Embedding Model\");\n\n const existing = readConfigRaw();\n if (existing.embeddingModel) {\n console.log(c.ok(`Embedding model: ${existing.embeddingModel}. Skipping.`));\n return { embeddingModel: existing.embeddingModel };\n }\n\n line();\n line(\" An embedding model converts your text into vectors for semantic search.\");\n line(\" Models are downloaded from HuggingFace on first use.\");\n line();\n\n const choice = await promptMenu(rl, [\n {\n label: \"Snowflake Arctic Embed m v1.5\",\n description: \"768 dims, ~118MB download. Best retrieval quality per MB (MTEB score 55.14). Asymmetric retrieval — different handling for queries vs documents. Best for most users.\",\n },\n {\n label: \"BGE Small EN v1.5\",\n description: \"384 dims, ~32MB download. Lightweight and fast. Good for limited disk space or when faster embedding is more important than maximum quality. English only.\",\n },\n {\n label: \"Nomic Embed Text v1.5\",\n description: \"768 dims, ~100MB download. 8K token context window — excellent for long documents. Matryoshka dimensions (can truncate for speed/size tradeoffs).\",\n },\n {\n label: \"None — skip embeddings\",\n description: \"BM25/keyword search only. No model download needed. You can add embeddings later by running `pai memory embed` after selecting a model.\",\n },\n ]);\n\n const models: Record<number, string | null> = {\n 0: \"Snowflake/snowflake-arctic-embed-m-v1.5\",\n 1: \"BAAI/bge-small-en-v1.5\",\n 2: \"nomic-ai/nomic-embed-text-v1.5\",\n 3: null,\n };\n\n const selectedModel = models[choice];\n\n line();\n if (selectedModel) {\n console.log(c.ok(`Model selected: ${selectedModel}`));\n console.log(c.dim(\" The model will be downloaded on first use of `pai memory embed`.\"));\n } else {\n console.log(c.ok(\"Skipping embeddings. Keyword search will still work.\"));\n console.log(c.dim(\" Add later: update embeddingModel in ~/.config/pai/config.json\"));\n }\n\n return { embeddingModel: selectedModel ?? \"none\" };\n}\n","/** Step 4: CLAUDE.md generation from PAI template. */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\nimport { CONFIG_DIR } from \"../../../../daemon/config.js\";\nimport { c, line, section, type Rl, promptYesNo, getTemplatesDir } from \"../utils.js\";\n\nexport async function stepClaudeMd(rl: Rl): Promise<boolean> {\n section(\"Step 4: Agent Configuration (CLAUDE.md)\");\n line();\n line(\" PAI ships a CLAUDE.md template with agent orchestration patterns:\");\n line(\" swarm mode, model escalation, parallel execution, quality standards.\");\n line();\n\n const claudeDir = join(homedir(), \".claude\");\n const claudeMd = join(claudeDir, \"CLAUDE.md\");\n const agentPrefs = join(CONFIG_DIR, \"agent-prefs.md\");\n const templatesDir = getTemplatesDir();\n const templatePath = join(templatesDir, \"claude-md.template.md\");\n\n if (!existsSync(templatePath)) {\n console.log(c.warn(\"Template not found: \" + templatePath));\n console.log(c.dim(\" Skipping CLAUDE.md generation. You can copy it manually from templates/.\"));\n return false;\n }\n\n if (existsSync(claudeMd)) {\n const content = readFileSync(claudeMd, \"utf-8\");\n const isGenerated = content.includes(\"Generated by PAI Setup\");\n if (isGenerated) {\n console.log(c.dim(\" Found existing PAI-generated CLAUDE.md.\"));\n } else {\n console.log(c.yellow(\" Found existing CLAUDE.md (custom, not PAI-generated).\"));\n console.log(c.dim(\" A backup will be created before overwriting.\"));\n }\n line();\n\n const overwrite = await promptYesNo(rl, \"Update ~/.claude/CLAUDE.md with the latest PAI template?\", isGenerated);\n if (!overwrite) {\n console.log(c.dim(\" Keeping existing CLAUDE.md unchanged.\"));\n return false;\n }\n\n if (!isGenerated) {\n const backupPath = claudeMd + \".backup\";\n writeFileSync(backupPath, content, \"utf-8\");\n console.log(c.ok(`Backed up existing CLAUDE.md to ${backupPath}`));\n }\n }\n\n let template = readFileSync(templatePath, \"utf-8\");\n template = template.replace(/\\$\\{HOME\\}/g, homedir());\n\n if (!existsSync(claudeDir)) {\n mkdirSync(claudeDir, { recursive: true });\n }\n writeFileSync(claudeMd, template, \"utf-8\");\n\n line();\n console.log(c.ok(\"Generated ~/.claude/CLAUDE.md from PAI template.\"));\n\n if (!existsSync(agentPrefs)) {\n line();\n line(\" For personal settings (project mappings, notification preferences),\");\n line(\" copy the example and customize:\");\n line();\n console.log(chalk.cyan(` cp ${templatesDir}/agent-prefs.example.md ${agentPrefs}`));\n line();\n\n const createPrefs = await promptYesNo(rl, \"Copy the example agent-prefs.md now?\", true);\n if (createPrefs) {\n const examplePath = join(templatesDir, \"agent-prefs.example.md\");\n if (existsSync(examplePath)) {\n if (!existsSync(CONFIG_DIR)) {\n mkdirSync(CONFIG_DIR, { recursive: true });\n }\n writeFileSync(agentPrefs, readFileSync(examplePath, \"utf-8\"), \"utf-8\");\n console.log(c.ok(`Created ${agentPrefs}`));\n console.log(c.dim(\" Edit this file to add your personal preferences and project mappings.\"));\n } else {\n console.log(c.warn(\"Example file not found. Create agent-prefs.md manually.\"));\n }\n }\n } else {\n console.log(c.dim(\" Personal preferences: \" + agentPrefs));\n }\n\n return true;\n}\n","/** Step 5: PAI SKILL.md installation to ~/.claude/skills/PAI/. */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getTemplatesDir } from \"../utils.js\";\n\nexport async function stepPaiSkill(rl: Rl): Promise<boolean> {\n section(\"Step 5: PAI Skill Installation\");\n line();\n line(\" PAI ships a SKILL.md that tells Claude Code how to invoke PAI commands.\");\n line();\n\n const templatesDir = getTemplatesDir();\n const templatePath = join(templatesDir, \"pai-skill.template.md\");\n\n if (!existsSync(templatePath)) {\n console.log(c.warn(\"Skill template not found: \" + templatePath));\n console.log(c.dim(\" Skipping PAI skill installation.\"));\n return false;\n }\n\n const skillDir = join(homedir(), \".claude\", \"skills\", \"PAI\");\n const skillFile = join(skillDir, \"SKILL.md\");\n\n if (existsSync(skillFile)) {\n const content = readFileSync(skillFile, \"utf-8\");\n const isGenerated = content.includes(\"Generated by PAI Setup\");\n if (isGenerated) {\n console.log(c.dim(\" Found existing PAI-generated SKILL.md.\"));\n } else {\n console.log(c.yellow(\" Found existing SKILL.md (not PAI-generated).\"));\n console.log(c.dim(\" A backup will be created before overwriting.\"));\n }\n line();\n\n const overwrite = await promptYesNo(rl, \"Update ~/.claude/skills/PAI/SKILL.md with the latest PAI skill?\", isGenerated);\n if (!overwrite) {\n console.log(c.dim(\" Keeping existing SKILL.md unchanged.\"));\n return false;\n }\n\n if (!isGenerated) {\n const backupPath = skillFile + \".backup\";\n writeFileSync(backupPath, content, \"utf-8\");\n console.log(c.ok(`Backed up existing SKILL.md to ${backupPath}`));\n }\n } else {\n const install = await promptYesNo(rl, \"Install PAI skill to ~/.claude/skills/PAI/SKILL.md?\", true);\n if (!install) {\n console.log(c.dim(\" Skipping PAI skill installation.\"));\n return false;\n }\n }\n\n let template = readFileSync(templatePath, \"utf-8\");\n template = template.replace(/\\$\\{HOME\\}/g, homedir());\n\n if (!existsSync(skillDir)) {\n mkdirSync(skillDir, { recursive: true });\n }\n writeFileSync(skillFile, template, \"utf-8\");\n\n line();\n console.log(c.ok(\"Installed ~/.claude/skills/PAI/SKILL.md\"));\n return true;\n}\n","/** Step 6: AI Steering Rules installation to ~/.claude/skills/PAI/. */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getTemplatesDir } from \"../utils.js\";\n\nexport async function stepAiSteeringRules(rl: Rl): Promise<boolean> {\n section(\"Step 6: AI Steering Rules\");\n line();\n line(\" PAI ships a set of universal behavioral rules for AI assistants:\");\n line(\" surgical fixes, verification before assertion, root cause analysis,\");\n line(\" and honest failure modes. These rules load at startup as a skill.\");\n line();\n\n const templatesDir = getTemplatesDir();\n const templatePath = join(templatesDir, \"ai-steering-rules.template.md\");\n\n if (!existsSync(templatePath)) {\n console.log(c.warn(\"AI steering rules template not found: \" + templatePath));\n console.log(c.dim(\" Skipping AI steering rules installation.\"));\n return false;\n }\n\n const skillDir = join(homedir(), \".claude\", \"skills\", \"PAI\");\n const skillFile = join(skillDir, \"AI-STEERING-RULES.md\");\n\n if (existsSync(skillFile)) {\n const content = readFileSync(skillFile, \"utf-8\");\n const isGenerated = content.includes(\"Generated by PAI Setup\");\n if (isGenerated) {\n console.log(c.dim(\" Found existing PAI-generated AI-STEERING-RULES.md.\"));\n } else {\n console.log(c.yellow(\" Found existing AI-STEERING-RULES.md (not PAI-generated).\"));\n console.log(c.dim(\" A backup will be created before overwriting.\"));\n }\n line();\n\n const overwrite = await promptYesNo(rl, \"Update ~/.claude/skills/PAI/AI-STEERING-RULES.md with the latest rules?\", isGenerated);\n if (!overwrite) {\n console.log(c.dim(\" Keeping existing AI-STEERING-RULES.md unchanged.\"));\n return false;\n }\n\n if (!isGenerated) {\n const backupPath = skillFile + \".backup\";\n writeFileSync(backupPath, content, \"utf-8\");\n console.log(c.ok(`Backed up existing AI-STEERING-RULES.md to ${backupPath}`));\n }\n } else {\n const install = await promptYesNo(rl, \"Install AI steering rules to ~/.claude/skills/PAI/AI-STEERING-RULES.md?\", true);\n if (!install) {\n console.log(c.dim(\" Skipping AI steering rules installation.\"));\n return false;\n }\n }\n\n const template = readFileSync(templatePath, \"utf-8\");\n\n if (!existsSync(skillDir)) {\n mkdirSync(skillDir, { recursive: true });\n }\n writeFileSync(skillFile, template, \"utf-8\");\n\n line();\n console.log(c.ok(\"Installed ~/.claude/skills/PAI/AI-STEERING-RULES.md\"));\n return true;\n}\n","/**\n * Step 7: Install PAI MCP skill stubs to ~/.claude/skills/.\n *\n * Each PAI MCP prompt has a SKILL.md stub generated at build time\n * (dist/skills/<Name>/SKILL.md). This step symlinks (macOS/Linux) or\n * copies (Windows) them into Claude Code's skill discovery directory.\n *\n * Skills MUST live at ~/.claude/skills/<Name>/SKILL.md (top level),\n * NOT under a user/ subdirectory — Claude Code only scans one level deep.\n *\n * Symlinks keep the stubs in sync — rebuilding PAI updates them automatically.\n * Existing non-symlink directories with the same name are never overwritten.\n */\n\nimport {\n existsSync,\n readdirSync,\n symlinkSync,\n lstatSync,\n readlinkSync,\n copyFileSync,\n mkdirSync,\n unlinkSync,\n} from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getDistDir } from \"../utils.js\";\n\nexport async function stepSkillStubs(rl: Rl): Promise<boolean> {\n section(\"Step 7: MCP Skill Stubs\");\n line();\n line(\" PAI MCP prompts need SKILL.md stubs so Claude Code can discover them.\");\n line(\" Stubs are symlinked from dist/skills/ — rebuilding PAI keeps them in sync.\");\n line();\n\n const distSkills = join(getDistDir(), \"skills\");\n\n if (!existsSync(distSkills)) {\n console.log(c.warn(\"dist/skills/ not found — run `bun run build` first.\"));\n return false;\n }\n\n const skillNames = readdirSync(distSkills).filter((name) => {\n const p = join(distSkills, name);\n return existsSync(join(p, \"SKILL.md\"));\n });\n\n if (skillNames.length === 0) {\n console.log(c.warn(\"No skill stubs found in dist/skills/.\"));\n return false;\n }\n\n console.log(c.dim(` Found ${skillNames.length} skill stubs to install.`));\n line();\n\n const install = await promptYesNo(\n rl,\n `Symlink ${skillNames.length} PAI skill stubs to ~/.claude/skills/?`,\n true,\n );\n if (!install) {\n console.log(c.dim(\" Skipping skill stub installation.\"));\n return false;\n }\n\n const targetBase = join(homedir(), \".claude\", \"skills\");\n mkdirSync(targetBase, { recursive: true });\n\n // Clean up stale symlinks from old user/ location\n const oldUserBase = join(targetBase, \"user\");\n if (existsSync(oldUserBase)) {\n for (const name of skillNames) {\n const oldTarget = join(oldUserBase, name);\n if (existsSync(oldTarget) && lstatSync(oldTarget).isSymbolicLink()) {\n unlinkSync(oldTarget);\n }\n }\n }\n\n const useSymlinks = platform() !== \"win32\";\n let installed = 0;\n let skipped = 0;\n let updated = 0;\n\n for (const name of skillNames) {\n const source = resolve(join(distSkills, name));\n const target = join(targetBase, name);\n\n // If target exists and is not a symlink, never overwrite (user's own skill)\n if (existsSync(target) && !lstatSync(target).isSymbolicLink()) {\n console.log(c.dim(` SKIP ${name} (existing user skill, not a symlink)`));\n skipped++;\n continue;\n }\n\n // If target is an existing symlink, check if it already points to the right place\n if (existsSync(target) && lstatSync(target).isSymbolicLink()) {\n const currentTarget = readlinkSync(target);\n if (resolve(currentTarget) === source) {\n // Already correct\n installed++;\n continue;\n }\n // Stale symlink — remove and recreate\n unlinkSync(target);\n updated++;\n }\n\n if (useSymlinks) {\n symlinkSync(source, target);\n } else {\n // Windows: copy the SKILL.md\n mkdirSync(target, { recursive: true });\n copyFileSync(join(source, \"SKILL.md\"), join(target, \"SKILL.md\"));\n }\n installed++;\n }\n\n line();\n const method = useSymlinks ? \"symlinked\" : \"copied\";\n if (updated > 0) {\n console.log(c.ok(`${installed} stubs ${method}, ${updated} updated, ${skipped} skipped (user skills).`));\n } else {\n console.log(c.ok(`${installed} skill stubs ${method} to ~/.claude/skills/.`));\n if (skipped > 0) {\n console.log(c.dim(` ${skipped} skipped (existing user skills not managed by PAI).`));\n }\n }\n return true;\n}\n","/** Step 7: Shell lifecycle hooks installation (pre-compact, session-stop, statusline). */\n\nimport {\n existsSync, lstatSync, readlinkSync, symlinkSync, unlinkSync,\n copyFileSync, chmodSync, mkdirSync,\n} from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getHooksDir, getStatuslineScript, getTabColorScript } from \"../utils.js\";\n\nconst useSymlinks = platform() !== \"win32\";\n\nexport async function stepHooks(rl: Rl): Promise<boolean> {\n section(\"Step 7: Lifecycle Hooks\");\n line();\n line(\" PAI hooks fire on session stop and context compaction to save state,\");\n line(\" update notes, and display live statusline information.\");\n if (useSymlinks) {\n line(\" Files are symlinked so they auto-update when PAI is rebuilt.\");\n } else {\n line(\" Files are copied (Windows — re-run setup after PAI updates).\");\n }\n line();\n\n const install = await promptYesNo(rl, \"Install PAI lifecycle hooks (session stop, pre-compact, statusline)?\", true);\n if (!install) {\n console.log(c.dim(\" Skipping hook installation.\"));\n return false;\n }\n\n const hooksDir = getHooksDir();\n const statuslineSrc = getStatuslineScript();\n const tabColorSrc = getTabColorScript();\n\n const claudeDir = join(homedir(), \".claude\");\n const hooksTarget = join(claudeDir, \"Hooks\");\n\n if (!existsSync(hooksTarget)) {\n mkdirSync(hooksTarget, { recursive: true });\n }\n\n let anyInstalled = false;\n\n function installLink(src: string, dest: string, label: string): void {\n if (!existsSync(src)) {\n console.log(c.warn(` Source not found: ${src}`));\n return;\n }\n\n const absSrc = resolve(src);\n\n // Check existing target\n if (existsSync(dest) || (lstatSync(dest, { throwIfNoEntry: false })?.isSymbolicLink?.())) {\n try {\n const stat = lstatSync(dest);\n if (stat.isSymbolicLink()) {\n if (resolve(readlinkSync(dest)) === absSrc) {\n console.log(c.dim(` Current: ${label}`));\n return;\n }\n // Stale symlink — replace\n unlinkSync(dest);\n } else if (stat.isFile()) {\n // Old copy — replace with symlink\n unlinkSync(dest);\n } else {\n return; // Don't touch directories\n }\n } catch {\n // lstatSync failed — target doesn't exist, proceed\n }\n }\n\n if (useSymlinks) {\n symlinkSync(absSrc, dest);\n } else {\n copyFileSync(src, dest);\n chmodSync(dest, 0o755);\n }\n const verb = useSymlinks ? \"Linked\" : \"Installed\";\n console.log(c.ok(`${verb}: ${label}`));\n anyInstalled = true;\n }\n\n line();\n installLink(join(hooksDir, \"pre-compact.sh\"), join(hooksTarget, \"pai-pre-compact.sh\"), \"pai-pre-compact.sh\");\n installLink(join(hooksDir, \"session-stop.sh\"), join(hooksTarget, \"pai-session-stop.sh\"), \"pai-session-stop.sh\");\n\n if (statuslineSrc) {\n installLink(statuslineSrc, join(claudeDir, \"statusline-command.sh\"), \"statusline-command.sh\");\n } else {\n console.log(c.warn(\" statusline-command.sh not found — skipping statusline.\"));\n }\n\n if (tabColorSrc) {\n installLink(tabColorSrc, join(claudeDir, \"tab-color-command.sh\"), \"tab-color-command.sh\");\n } else {\n console.log(c.warn(\" tab-color-command.sh not found — skipping tab color.\"));\n }\n\n return anyInstalled;\n}\n","/** Step 7b: TypeScript (.mjs) hooks installation to ~/.claude/Hooks/. */\n\nimport {\n existsSync, readdirSync, lstatSync, readlinkSync,\n symlinkSync, unlinkSync, copyFileSync, chmodSync, mkdirSync,\n} from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getDistHooksDir } from \"../utils.js\";\n\nconst useSymlinks = platform() !== \"win32\";\n\nexport async function stepTsHooks(rl: Rl): Promise<boolean> {\n section(\"Step 7b: TypeScript Hooks Installation\");\n line();\n line(\" PAI ships compiled TypeScript hooks (.mjs) that fire on session events,\");\n line(\" tool use, and context compaction to capture context and update notes.\");\n if (useSymlinks) {\n line(\" Files are symlinked so they auto-update when PAI is rebuilt.\");\n } else {\n line(\" Files are copied (Windows — re-run setup after PAI updates).\");\n }\n line();\n\n const install = await promptYesNo(rl, \"Install PAI TypeScript hooks to ~/.claude/Hooks/?\", true);\n if (!install) {\n console.log(c.dim(\" Skipping TypeScript hooks installation.\"));\n return false;\n }\n\n const distHooksDir = getDistHooksDir();\n\n if (!existsSync(distHooksDir)) {\n console.log(c.warn(` dist/hooks/ directory not found at: ${distHooksDir}`));\n console.log(c.dim(\" Build the package first: bun run build\"));\n return false;\n }\n\n const claudeDir = join(homedir(), \".claude\");\n const hooksTarget = join(claudeDir, \"Hooks\");\n\n if (!existsSync(hooksTarget)) {\n mkdirSync(hooksTarget, { recursive: true });\n }\n\n let allFiles: string[];\n try {\n allFiles = readdirSync(distHooksDir).filter((f) => f.endsWith(\".mjs\"));\n } catch (e) {\n console.log(c.warn(` Could not read dist/hooks/: ${e}`));\n return false;\n }\n\n if (allFiles.length === 0) {\n console.log(c.warn(\" No .mjs files found in dist/hooks/. Build first: bun run build\"));\n return false;\n }\n\n line();\n let linkedCount = 0;\n let skippedCount = 0;\n let cleanedCount = 0;\n\n for (const filename of allFiles) {\n const src = resolve(join(distHooksDir, filename));\n const dest = join(hooksTarget, filename);\n\n // Check existing target\n let needsLink = true;\n if (existsSync(dest) || lstatSync(dest, { throwIfNoEntry: false })?.isSymbolicLink?.()) {\n try {\n const stat = lstatSync(dest);\n if (stat.isSymbolicLink()) {\n if (resolve(readlinkSync(dest)) === src) {\n console.log(c.dim(` Current: ${filename}`));\n skippedCount++;\n needsLink = false;\n } else {\n unlinkSync(dest); // Stale symlink\n }\n } else if (stat.isFile()) {\n unlinkSync(dest); // Old copy — replace with symlink\n }\n } catch {\n // lstatSync failed — target doesn't exist\n }\n }\n\n if (needsLink) {\n if (useSymlinks) {\n symlinkSync(src, dest);\n } else {\n copyFileSync(src, dest);\n chmodSync(dest, 0o755);\n }\n const verb = useSymlinks ? \"Linked\" : \"Installed\";\n console.log(c.ok(`${verb}: ${filename}`));\n linkedCount++;\n }\n\n // Clean up stale .ts files from pre-build era\n const staleTsPath = join(hooksTarget, filename.replace(/\\.mjs$/, \".ts\"));\n if (existsSync(staleTsPath)) {\n unlinkSync(staleTsPath);\n console.log(c.ok(`Cleaned up stale: ${filename.replace(/\\.mjs$/, \".ts\")}`));\n cleanedCount++;\n }\n }\n\n line();\n if (linkedCount > 0 || cleanedCount > 0) {\n const parts = [];\n const verb = useSymlinks ? \"linked\" : \"installed\";\n if (linkedCount > 0) parts.push(`${linkedCount} hook(s) ${verb}`);\n if (skippedCount > 0) parts.push(`${skippedCount} current`);\n if (cleanedCount > 0) parts.push(`${cleanedCount} stale .ts file(s) cleaned up`);\n console.log(c.ok(parts.join(\", \") + \".\"));\n } else {\n console.log(c.dim(` All ${skippedCount} hook(s) already current.`));\n }\n\n return linkedCount > 0 || cleanedCount > 0;\n}\n","/**\n * settings-manager — merge-not-overwrite utility for ~/.claude/settings.json\n *\n * Provides safe, idempotent writes to Claude Code's settings.json:\n * - env vars: added only if the key is absent (never overwrites)\n * - hooks: appended per hookType, deduplicated by command string\n * - statusLine: written only if the key is not already present\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nconst CLAUDE_DIR = join(homedir(), \".claude\");\nconst SETTINGS_FILE = join(CLAUDE_DIR, \"settings.json\");\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type HookType =\n | \"PreCompact\"\n | \"Stop\"\n | \"SessionStart\"\n | \"SessionEnd\"\n | \"UserPromptSubmit\"\n | \"PreToolUse\"\n | \"PostToolUse\"\n | \"SubagentStop\";\n\nexport interface HookEntry {\n hookType: HookType;\n matcher?: string;\n command: string;\n}\n\nexport interface SettingsMergeOptions {\n env?: Record<string, string>;\n hooks?: HookEntry[];\n statusLine?: { type: string; command: string };\n permissions?: {\n allow?: string[];\n deny?: string[];\n };\n flags?: Record<string, unknown>;\n}\n\nexport interface MergeResult {\n changed: boolean;\n report: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Internal shape of settings.json hooks\n// ---------------------------------------------------------------------------\n\ninterface HookCommand {\n type: string;\n command: string;\n}\n\ninterface HookRule {\n matcher?: string;\n hooks: HookCommand[];\n}\n\ntype HooksSection = Partial<Record<HookType, HookRule[]>>;\n\n// ---------------------------------------------------------------------------\n// Read / write helpers\n// ---------------------------------------------------------------------------\n\nexport function readSettingsJson(): Record<string, unknown> {\n if (!existsSync(SETTINGS_FILE)) return {};\n try {\n return JSON.parse(readFileSync(SETTINGS_FILE, \"utf-8\")) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nexport function writeSettingsJson(data: Record<string, unknown>): void {\n if (!existsSync(CLAUDE_DIR)) {\n mkdirSync(CLAUDE_DIR, { recursive: true });\n }\n writeFileSync(SETTINGS_FILE, JSON.stringify(data, null, 2) + \"\\n\", \"utf-8\");\n}\n\n// ---------------------------------------------------------------------------\n// Merge helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Merge env vars — add keys that are absent, never overwrite existing ones.\n */\nfunction mergeEnv(\n settings: Record<string, unknown>,\n incoming: Record<string, string>,\n report: string[],\n): boolean {\n let changed = false;\n\n const existing = (\n typeof settings[\"env\"] === \"object\" && settings[\"env\"] !== null\n ? settings[\"env\"]\n : {}\n ) as Record<string, string>;\n\n for (const [key, value] of Object.entries(incoming)) {\n if (Object.prototype.hasOwnProperty.call(existing, key)) {\n report.push(chalk.dim(` Skipped: env.${key} already set`));\n } else {\n existing[key] = value;\n report.push(chalk.green(` Added env: ${key}`));\n changed = true;\n }\n }\n\n settings[\"env\"] = existing;\n return changed;\n}\n\n/**\n * Strip file extension from a command basename for extension-agnostic dedup.\n * This ensures that e.g. \"context-compression-hook.ts\" and\n * \"context-compression-hook.mjs\" are treated as the same hook.\n */\nfunction commandStem(cmd: string): string {\n const base = cmd.split(\"/\").pop() ?? cmd;\n return base.replace(/\\.(mjs|ts|js|sh)$/, \"\");\n}\n\n/**\n * Collect every command string already registered for a given hookType.\n * Stores full command, basename, AND extension-stripped stem for flexible\n * matching (handles ${PAI_DIR}/Hooks/foo.sh vs /Users/.../Hooks/foo.sh,\n * and .ts → .mjs migrations).\n */\nfunction existingCommandsForHookType(rules: HookRule[]): Set<string> {\n const cmds = new Set<string>();\n for (const rule of rules) {\n for (const entry of rule.hooks) {\n cmds.add(entry.command);\n // Also add the basename so expanded paths match template paths\n const base = entry.command.split(\"/\").pop();\n if (base) cmds.add(base);\n // Also add extension-stripped stem for cross-extension dedup\n cmds.add(commandStem(entry.command));\n }\n }\n return cmds;\n}\n\n/**\n * Find and remove an existing rule whose command has the same stem\n * (extension-agnostic) as the incoming command. Returns true if a\n * replacement was made. This handles .ts → .mjs migrations cleanly.\n */\nfunction replaceStaleHook(\n existingRules: HookRule[],\n incomingStem: string,\n incomingCommand: string,\n incomingMatcher: string | undefined,\n): boolean {\n for (let i = 0; i < existingRules.length; i++) {\n const rule = existingRules[i];\n for (let j = 0; j < rule.hooks.length; j++) {\n const existingStem = commandStem(rule.hooks[j].command);\n if (existingStem === incomingStem && rule.hooks[j].command !== incomingCommand) {\n // Replace the old command with the new one\n rule.hooks[j].command = incomingCommand;\n // Update matcher if provided\n if (incomingMatcher !== undefined) {\n rule.matcher = incomingMatcher;\n }\n return true;\n }\n }\n }\n return false;\n}\n\n/**\n * Merge hooks — append entries, deduplicating by command string.\n * Extension-agnostic: a .mjs hook replaces an existing .ts hook with the\n * same stem, ensuring clean .ts → .mjs migrations without duplicates.\n */\nfunction mergeHooks(\n settings: Record<string, unknown>,\n incoming: HookEntry[],\n report: string[],\n): boolean {\n let changed = false;\n\n const hooksSection = (\n typeof settings[\"hooks\"] === \"object\" && settings[\"hooks\"] !== null\n ? settings[\"hooks\"]\n : {}\n ) as HooksSection;\n\n for (const entry of incoming) {\n const { hookType, matcher, command } = entry;\n\n const existingRules: HookRule[] = Array.isArray(hooksSection[hookType])\n ? (hooksSection[hookType] as HookRule[])\n : [];\n\n const basename = command.split(\"/\").pop() ?? command;\n const stem = commandStem(command);\n\n // Exact match — already registered, skip\n const existingCmds = existingCommandsForHookType(existingRules);\n if (existingCmds.has(command) || existingCmds.has(basename)) {\n report.push(chalk.dim(` Skipped: hook ${hookType} → ${basename} already registered`));\n continue;\n }\n\n // Stem match with different extension — replace old entry (.ts → .mjs migration)\n if (existingCmds.has(stem)) {\n if (replaceStaleHook(existingRules, stem, command, matcher)) {\n hooksSection[hookType] = existingRules;\n report.push(chalk.yellow(` Upgraded: hook ${hookType} → ${basename} (replaced stale extension)`));\n changed = true;\n continue;\n }\n }\n\n // No match at all — append new rule\n const newRule: HookRule = {\n hooks: [{ type: \"command\", command }],\n };\n if (matcher !== undefined) {\n newRule.matcher = matcher;\n }\n\n existingRules.push(newRule);\n hooksSection[hookType] = existingRules;\n\n report.push(chalk.green(` Added hook: ${hookType} → ${basename}`));\n changed = true;\n }\n\n settings[\"hooks\"] = hooksSection;\n return changed;\n}\n\n/**\n * Merge statusLine — write only if the key is not already present.\n */\nfunction mergeStatusLine(\n settings: Record<string, unknown>,\n incoming: { type: string; command: string },\n report: string[],\n): boolean {\n if (Object.prototype.hasOwnProperty.call(settings, \"statusLine\")) {\n report.push(chalk.dim(\" Skipped: statusLine already configured\"));\n return false;\n }\n\n settings[\"statusLine\"] = { ...incoming };\n report.push(chalk.green(\" Added statusLine\"));\n return true;\n}\n\n/**\n * Merge permissions — append allow/deny entries, deduplicating.\n */\nfunction mergePermissions(\n settings: Record<string, unknown>,\n incoming: { allow?: string[]; deny?: string[] },\n report: string[],\n): boolean {\n let changed = false;\n\n const perms = (\n typeof settings[\"permissions\"] === \"object\" && settings[\"permissions\"] !== null\n ? settings[\"permissions\"]\n : {}\n ) as Record<string, string[]>;\n\n for (const list of [\"allow\", \"deny\"] as const) {\n const entries = incoming[list];\n if (!entries || entries.length === 0) continue;\n\n const existing: string[] = Array.isArray(perms[list]) ? perms[list] : [];\n const existingSet = new Set(existing);\n\n for (const entry of entries) {\n if (existingSet.has(entry)) {\n report.push(chalk.dim(` Skipped: permissions.${list} \"${entry}\" already present`));\n } else {\n existing.push(entry);\n existingSet.add(entry);\n report.push(chalk.green(` Added permissions.${list}: ${entry}`));\n changed = true;\n }\n }\n\n perms[list] = existing;\n }\n\n settings[\"permissions\"] = perms;\n return changed;\n}\n\n/**\n * Merge flags — set keys only if not already present, never overwrite.\n */\nfunction mergeFlags(\n settings: Record<string, unknown>,\n incoming: Record<string, unknown>,\n report: string[],\n): boolean {\n let changed = false;\n\n for (const [key, value] of Object.entries(incoming)) {\n if (Object.prototype.hasOwnProperty.call(settings, key)) {\n report.push(chalk.dim(` Skipped: ${key} already set`));\n } else {\n settings[key] = value;\n report.push(chalk.green(` Added flag: ${key}`));\n changed = true;\n }\n }\n\n return changed;\n}\n\n// ---------------------------------------------------------------------------\n// Public orchestrator\n// ---------------------------------------------------------------------------\n\n/**\n * Merge env vars, hooks, and/or a statusLine entry into ~/.claude/settings.json.\n * Never overwrites existing values — only adds what is missing.\n *\n * Returns { changed, report } where report contains human-readable lines.\n */\nexport function mergeSettings(opts: SettingsMergeOptions): MergeResult {\n const settings = readSettingsJson();\n const report: string[] = [];\n let changed = false;\n\n if (opts.env !== undefined && Object.keys(opts.env).length > 0) {\n if (mergeEnv(settings, opts.env, report)) changed = true;\n }\n\n if (opts.hooks !== undefined && opts.hooks.length > 0) {\n if (mergeHooks(settings, opts.hooks, report)) changed = true;\n }\n\n if (opts.statusLine !== undefined) {\n if (mergeStatusLine(settings, opts.statusLine, report)) changed = true;\n }\n\n if (opts.permissions !== undefined) {\n if (mergePermissions(settings, opts.permissions, report)) changed = true;\n }\n\n if (opts.flags !== undefined && Object.keys(opts.flags).length > 0) {\n if (mergeFlags(settings, opts.flags, report)) changed = true;\n }\n\n if (changed) {\n writeSettingsJson(settings);\n }\n\n return { changed, report };\n}\n","/** Steps 8b and 8: Assistant name prompt and settings.json patch. */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\nimport { mergeSettings } from \"../../settings-manager.js\";\nimport { c, line, section, type Rl, prompt, promptYesNo } from \"../utils.js\";\n\nexport async function stepDaName(rl: Rl): Promise<string> {\n section(\"Step 8b: Assistant Name\");\n line();\n line(\" Choose a name for your AI assistant. This name appears in tab titles\");\n line(\" and session notes when hooks are active.\");\n line();\n\n const answer = await prompt(rl, chalk.bold(\" Assistant name [PAI]: \"));\n const daName = answer || \"PAI\";\n line();\n console.log(c.ok(`Assistant name set to: ${daName}`));\n return daName;\n}\n\nexport async function stepSettings(rl: Rl, daName: string): Promise<boolean> {\n section(\"Step 8: Settings Patch\");\n line();\n line(\" PAI will add env vars, all hook registrations, permissions, and flags\");\n line(\" to ~/.claude/settings.json. Existing values are never overwritten.\");\n line();\n\n const patch = await promptYesNo(rl, \"Patch ~/.claude/settings.json with PAI hooks, env vars, and settings?\", true);\n if (!patch) {\n console.log(c.dim(\" Skipping settings patch.\"));\n return false;\n }\n\n const paiDir = join(homedir(), \".claude\");\n\n const result = mergeSettings({\n env: {\n PAI_DIR: paiDir,\n CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: \"80\",\n CLAUDE_CODE_MAX_OUTPUT_TOKENS: \"64000\",\n DA: daName,\n },\n hooks: [\n { hookType: \"SessionStart\", command: \"${PAI_DIR}/Hooks/session-commands.mjs\" },\n { hookType: \"SessionStart\", command: \"${PAI_DIR}/Hooks/load-core-context.mjs\" },\n { hookType: \"SessionStart\", command: \"${PAI_DIR}/Hooks/load-project-context.mjs\" },\n { hookType: \"SessionStart\", command: \"${PAI_DIR}/Hooks/initialize-session.mjs\" },\n { hookType: \"SessionStart\", command: \"${PAI_DIR}/Hooks/capture-all-events.mjs --event-type SessionStart\" },\n { hookType: \"SessionStart\", matcher: \"compact\", command: \"${PAI_DIR}/Hooks/post-compact-inject.mjs\" },\n { hookType: \"UserPromptSubmit\", command: \"${PAI_DIR}/Hooks/cleanup-session-files.mjs\" },\n { hookType: \"UserPromptSubmit\", command: \"${PAI_DIR}/Hooks/update-tab-titles.mjs\" },\n { hookType: \"UserPromptSubmit\", command: \"${PAI_DIR}/Hooks/capture-all-events.mjs --event-type UserPromptSubmit\" },\n { hookType: \"PreToolUse\", matcher: \"Bash\", command: \"${PAI_DIR}/Hooks/security-validator.mjs\" },\n { hookType: \"PreToolUse\", matcher: \"*\", command: \"${PAI_DIR}/Hooks/capture-all-events.mjs --event-type PreToolUse\" },\n { hookType: \"PostToolUse\", matcher: \"TodoWrite\", command: \"${PAI_DIR}/Hooks/sync-todo-to-md.mjs\" },\n { hookType: \"PostToolUse\", matcher: \"*\", command: \"${PAI_DIR}/Hooks/capture-all-events.mjs --event-type PostToolUse\" },\n { hookType: \"PostToolUse\", matcher: \"*\", command: \"${PAI_DIR}/Hooks/capture-tool-output.mjs\" },\n { hookType: \"PostToolUse\", matcher: \"*\", command: \"${PAI_DIR}/Hooks/update-tab-on-action.mjs\" },\n { hookType: \"Stop\", command: \"${PAI_DIR}/Hooks/stop-hook.mjs\" },\n { hookType: \"Stop\", command: \"${PAI_DIR}/Hooks/capture-all-events.mjs --event-type Stop\" },\n { hookType: \"Stop\", command: \"${PAI_DIR}/Hooks/pai-session-stop.sh\" },\n { hookType: \"SubagentStop\", command: \"${PAI_DIR}/Hooks/subagent-stop-hook.mjs\" },\n { hookType: \"SubagentStop\", command: \"${PAI_DIR}/Hooks/capture-all-events.mjs --event-type SubagentStop\" },\n { hookType: \"SessionEnd\", command: \"${PAI_DIR}/Hooks/capture-session-summary.mjs\" },\n { hookType: \"SessionEnd\", command: \"${PAI_DIR}/Hooks/capture-all-events.mjs --event-type SessionEnd\" },\n { hookType: \"PreCompact\", command: \"${PAI_DIR}/Hooks/context-compression-hook.mjs\" },\n { hookType: \"PreCompact\", command: \"${PAI_DIR}/Hooks/capture-all-events.mjs --event-type PreCompact\" },\n { hookType: \"PreCompact\", matcher: \"\", command: \"${PAI_DIR}/Hooks/pai-pre-compact.sh\" },\n ],\n statusLine: {\n type: \"command\",\n command: \"bash ${PAI_DIR}/statusline-command.sh\",\n },\n permissions: {\n allow: [\"Bash\", \"Read\", \"Write\", \"Edit\", \"Glob\", \"Grep\", \"WebFetch\", \"WebSearch\", \"NotebookEdit\", \"TodoWrite\", \"ExitPlanMode\", \"mcp__pai\"],\n deny: [\"Bash(rm -rf /)\", \"Bash(rm -rf /*)\", \"Bash(rm -rf ~)\", \"Bash(rm -rf $HOME)\", \"Bash(sudo rm -rf /)\", \"Bash(sudo rm -rf /*)\"],\n },\n flags: {\n enableAllProjectMcpServers: true,\n },\n });\n\n line();\n for (const r of result.report) {\n console.log(r);\n }\n\n if (!result.changed) {\n console.log(c.dim(\" Settings already up-to-date. No changes made.\"));\n }\n\n return result.changed;\n}\n","/** Step 9: PAI daemon installation via launchd plist. */\n\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport { c, line, section, type Rl, promptYesNo } from \"../utils.js\";\n\nexport async function stepDaemon(rl: Rl): Promise<boolean> {\n section(\"Step 9: Daemon Install\");\n line();\n line(\" The PAI daemon indexes your projects every 5 minutes in the background.\");\n line();\n\n const plistPath = join(homedir(), \"Library\", \"LaunchAgents\", \"com.pai.pai-daemon.plist\");\n const exists = existsSync(plistPath);\n\n if (exists) {\n console.log(c.dim(\" PAI daemon plist already installed.\"));\n line();\n\n const reinstall = await promptYesNo(rl, \"Reinstall the PAI daemon launchd plist?\", false);\n if (!reinstall) {\n console.log(c.dim(\" Keeping existing daemon installation.\"));\n return false;\n }\n } else {\n const install = await promptYesNo(rl, \"Install the PAI daemon to run automatically at login?\", true);\n if (!install) {\n console.log(c.dim(\" Skipping daemon install. Run manually: pai daemon install\"));\n return false;\n }\n }\n\n line();\n const result = spawnSync(\"pai\", [\"daemon\", \"install\"], { stdio: \"inherit\" });\n\n if (result.status !== 0) {\n console.log(c.warn(\" Daemon install failed. Run manually: pai daemon install\"));\n return false;\n }\n\n console.log(c.ok(\"Daemon installed as com.pai.pai-daemon.\"));\n return true;\n}\n","/** Step 10: PAI MCP server registration in ~/.claude.json. */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport { c, line, section, type Rl, promptYesNo } from \"../utils.js\";\n\nexport async function stepMcp(rl: Rl): Promise<boolean> {\n section(\"Step 10: MCP Registration\");\n line();\n line(\" Registering the PAI MCP server lets Claude Code call PAI tools directly.\");\n line();\n\n const claudeJsonPath = join(homedir(), \".claude.json\");\n if (existsSync(claudeJsonPath)) {\n try {\n const raw = readFileSync(claudeJsonPath, \"utf-8\");\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n const mcpServers = parsed[\"mcpServers\"] as Record<string, unknown> | undefined;\n if (mcpServers && Object.prototype.hasOwnProperty.call(mcpServers, \"pai\")) {\n console.log(c.ok(\"PAI MCP server already registered in ~/.claude.json.\"));\n console.log(c.dim(\" Skipping MCP registration.\"));\n return false;\n }\n } catch {\n // continue\n }\n }\n\n const register = await promptYesNo(rl, \"Register the PAI MCP server in ~/.claude.json?\", true);\n if (!register) {\n console.log(c.dim(\" Skipping MCP registration. Run manually: pai mcp install\"));\n return false;\n }\n\n line();\n const result = spawnSync(\"pai\", [\"mcp\", \"install\"], { stdio: \"inherit\" });\n\n if (result.status !== 0) {\n console.log(c.warn(\" MCP registration failed. Run manually: pai mcp install\"));\n return false;\n }\n\n console.log(c.ok(\"PAI MCP server registered in ~/.claude.json.\"));\n return true;\n}\n","/** Step 11: Directory scanning configuration and registry scan prompt. */\n\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\nimport { line, section, type Rl, promptYesNo } from \"../utils.js\";\n\nexport async function stepDirectories(rl: Rl): Promise<void> {\n section(\"Step 11: Directories to Index\");\n line();\n line(\" PAI indexes files in your registered projects. You can register projects\");\n line(\" individually with `pai project add <path>`, or let the registry scanner\");\n line(\" discover them automatically with `pai registry scan`.\");\n line();\n\n const defaults = [\n join(homedir(), \"Projects\"),\n join(homedir(), \"Documents\"),\n join(homedir(), \"dev\"),\n ].filter(existsSync);\n\n if (defaults.length > 0) {\n line(\" These directories exist on your system:\");\n for (const d of defaults) {\n console.log(chalk.dim(` ${d}`));\n }\n line();\n }\n\n const runScan = await promptYesNo(rl, \"Run `pai registry scan` to auto-detect projects after setup?\", false);\n\n if (runScan) {\n line();\n console.log(chalk.dim(\" Registry scan will run after setup completes.\"));\n } else {\n console.log(chalk.dim(\" Add projects manually: pai project add <path>\"));\n console.log(chalk.dim(\" Or discover them later: pai registry scan\"));\n }\n\n (stepDirectories as { _runScan?: boolean })._runScan = runScan;\n}\n","/** Step 12: Initial index — optionally starts the daemon and runs registry scan. */\n\nimport { spawnSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport { c, line, section, type Rl, promptYesNo } from \"../utils.js\";\nimport { stepDirectories } from \"./13-directories.js\";\n\nexport async function stepInitialIndex(rl: Rl): Promise<void> {\n section(\"Step 12: Initial Index\");\n line();\n line(\" Indexing scans your registered projects and builds the search index.\");\n line(\" The daemon runs indexing automatically every 5 minutes once started.\");\n line();\n\n const willScan = (stepDirectories as { _runScan?: boolean })._runScan;\n\n if (willScan) {\n const startDaemon = await promptYesNo(rl, \"Start the PAI daemon now? (enables background indexing)\", true);\n\n if (startDaemon) {\n line();\n console.log(c.dim(\" Starting daemon...\"));\n\n try {\n const result = spawnSync(\"pai\", [\"daemon\", \"serve\", \"--background\"], { stdio: \"pipe\", timeout: 10000 });\n if (result.status === 0) {\n console.log(c.ok(\"Daemon started in background.\"));\n } else {\n console.log(c.warn(\"Could not start daemon. Run manually: pai daemon serve\"));\n }\n } catch {\n console.log(c.warn(\"Could not start daemon. Run manually: pai daemon serve\"));\n }\n\n line();\n console.log(c.dim(\" Running registry scan to detect projects...\"));\n\n try {\n const result = spawnSync(\"pai\", [\"registry\", \"scan\"], { stdio: \"inherit\", timeout: 30000 });\n if (result.status !== 0) {\n console.log(c.warn(\"Registry scan encountered issues. Run `pai registry scan` manually.\"));\n }\n } catch {\n console.log(c.warn(\"Could not run registry scan. Run manually: pai registry scan\"));\n }\n } else {\n console.log(chalk.dim(\" Start the daemon later: pai daemon serve\"));\n console.log(chalk.dim(\" Scan projects later: pai registry scan\"));\n }\n } else {\n console.log(chalk.dim(\" Register projects with: pai project add <path>\"));\n console.log(chalk.dim(\" Then index them with: pai memory index --all\"));\n console.log(chalk.dim(\" Or start the daemon: pai daemon serve\"));\n }\n}\n","/** Step 13: Setup summary — displays all configuration choices made during setup. */\n\nimport chalk from \"chalk\";\nimport { CONFIG_FILE } from \"../../../../daemon/config.js\";\nimport { line, section } from \"../utils.js\";\n\nexport function stepSummary(\n configUpdates: Record<string, unknown>,\n claudeMdGenerated: boolean,\n paiSkillInstalled: boolean,\n aiSteeringRulesInstalled: boolean,\n skillStubsInstalled: boolean,\n hooksInstalled: boolean,\n tsHooksInstalled: boolean,\n settingsPatched: boolean,\n daName: string,\n daemonInstalled: boolean,\n mcpRegistered: boolean,\n): void {\n section(\"Setup Complete\");\n line();\n console.log(chalk.green(\" PAI Knowledge OS is configured!\"));\n line();\n\n const backend = configUpdates.storageBackend as string;\n const model = configUpdates.embeddingModel as string;\n\n line(chalk.bold(\" Configuration saved to: \") + chalk.dim(CONFIG_FILE));\n line();\n console.log(chalk.dim(\" Storage backend: \") + chalk.cyan(backend ?? \"sqlite\"));\n console.log(chalk.dim(\" Embedding model: \") + chalk.cyan(model && model !== \"none\" ? model : \"(none — keyword search only)\"));\n console.log(chalk.dim(\" CLAUDE.md: \") + chalk.cyan(claudeMdGenerated ? \"~/.claude/CLAUDE.md (generated)\" : \"(unchanged)\"));\n console.log(chalk.dim(\" PAI skill: \") + chalk.cyan(paiSkillInstalled ? \"~/.claude/skills/PAI/SKILL.md (installed)\" : \"(unchanged)\"));\n console.log(chalk.dim(\" Steering rules: \") + chalk.cyan(aiSteeringRulesInstalled ? \"~/.claude/skills/PAI/AI-STEERING-RULES.md (installed)\" : \"(unchanged)\"));\n console.log(chalk.dim(\" Skill stubs: \") + chalk.cyan(skillStubsInstalled ? \"18 MCP prompt stubs symlinked to ~/.claude/skills/\" : \"(unchanged)\"));\n console.log(chalk.dim(\" Hooks (shell): \") + chalk.cyan(hooksInstalled ? \"pai-pre-compact.sh, pai-session-stop.sh (installed)\" : \"(unchanged)\"));\n console.log(chalk.dim(\" Hooks (TS): \") + chalk.cyan(tsHooksInstalled ? \"14 .mjs hooks installed to ~/.claude/Hooks/\" : \"(unchanged)\"));\n console.log(chalk.dim(\" Assistant name: \") + chalk.cyan(daName));\n console.log(chalk.dim(\" Settings: \") + chalk.cyan(settingsPatched ? \"env vars, hooks, permissions, flags (patched)\" : \"(unchanged)\"));\n console.log(chalk.dim(\" Daemon: \") + chalk.cyan(daemonInstalled ? \"com.pai.pai-daemon (installed)\" : \"(unchanged)\"));\n console.log(chalk.dim(\" MCP: \") + chalk.cyan(mcpRegistered ? \"registered in ~/.claude.json\" : \"(unchanged)\"));\n line();\n console.log(chalk.bold.yellow(\" → RESTART Claude Code to activate all changes.\"));\n line();\n\n line(chalk.bold(\" Next steps:\"));\n line();\n console.log(chalk.dim(\" # Register a project\"));\n console.log(chalk.cyan(\" pai project add ~/your/project\"));\n line();\n console.log(chalk.dim(\" # Index your files\"));\n console.log(chalk.cyan(\" pai memory index --all\"));\n line();\n console.log(chalk.dim(\" # Search your knowledge\"));\n console.log(chalk.cyan(\" pai memory search \\\"your query\\\"\"));\n line();\n if (model && model !== \"none\") {\n console.log(chalk.dim(\" # Generate embeddings for semantic search\"));\n console.log(chalk.cyan(\" pai memory embed\"));\n line();\n console.log(chalk.dim(\" # Semantic search\"));\n console.log(chalk.cyan(\" pai memory search --mode semantic \\\"your query\\\"\"));\n line();\n }\n console.log(chalk.dim(\" # Start the background daemon\"));\n console.log(chalk.cyan(\" pai daemon serve\"));\n line();\n console.log(chalk.dim(\" # Show all commands\"));\n console.log(chalk.cyan(\" pai --help\"));\n line();\n}\n","/**\n * PAI setup wizard — main entry point.\n * Orchestrates all setup steps in order and registers the Commander command.\n */\n\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { existsSync } from \"node:fs\";\nimport { CONFIG_FILE, loadConfig } from \"../../../daemon/config.js\";\nimport { createRl, prompt, promptYesNo, line, mergeConfig } from \"./utils.js\";\nimport {\n stepWelcome,\n stepStorage,\n stepEmbedding,\n stepClaudeMd,\n stepPaiSkill,\n stepAiSteeringRules,\n stepSkillStubs,\n stepHooks,\n stepTsHooks,\n stepDaName,\n stepSettings,\n stepDaemon,\n stepMcp,\n stepDirectories,\n stepInitialIndex,\n stepSummary,\n} from \"./steps/index.js\";\n\nasync function runSetup(): Promise<void> {\n const rl = createRl();\n\n try {\n if (existsSync(CONFIG_FILE)) {\n const current = loadConfig();\n line();\n console.log(\n chalk.yellow(\" Note: PAI is already configured.\") +\n chalk.dim(\" Proceeding will update your existing configuration.\"),\n );\n console.log(chalk.dim(` Config: ${CONFIG_FILE}`));\n console.log(chalk.dim(` Current backend: ${current.storageBackend}`));\n line();\n\n const proceed = await promptYesNo(rl, \"Continue and update configuration?\", true);\n if (!proceed) {\n rl.close();\n line(chalk.dim(\" Setup cancelled.\"));\n line();\n return;\n }\n }\n\n // Step 1: Welcome\n stepWelcome();\n line();\n await prompt(rl, chalk.dim(\" Press Enter to begin setup...\"));\n\n // Step 2: Storage\n const storageConfig = await stepStorage(rl);\n\n // Step 3: Embeddings\n const embeddingConfig = await stepEmbedding(rl);\n\n // Step 4: Agent configuration (CLAUDE.md)\n const claudeMdGenerated = await stepClaudeMd(rl);\n\n // Step 5: PAI Skill\n const paiSkillInstalled = await stepPaiSkill(rl);\n\n // Step 6: AI Steering Rules\n const aiSteeringRulesInstalled = await stepAiSteeringRules(rl);\n\n // Step 7: Skill Stubs\n const skillStubsInstalled = await stepSkillStubs(rl);\n\n // Step 8: Hooks (shell scripts)\n const hooksInstalled = await stepHooks(rl);\n\n // Step 7b: TypeScript hooks (.mjs files)\n const tsHooksInstalled = await stepTsHooks(rl);\n\n // Step 8b: DA name\n const daName = await stepDaName(rl);\n\n // Step 8: Settings.json\n const settingsPatched = await stepSettings(rl, daName);\n\n // Step 9: Daemon\n const daemonInstalled = await stepDaemon(rl);\n\n // Step 10: MCP\n const mcpRegistered = await stepMcp(rl);\n\n // Step 11: Directories (informational — no config written)\n await stepDirectories(rl);\n\n // Write config after gathering all choices\n const allUpdates = { ...storageConfig, ...embeddingConfig };\n mergeConfig(allUpdates);\n\n line();\n console.log(chalk.green(\" Configuration saved.\"));\n\n // Step 12: Initial index\n await stepInitialIndex(rl);\n\n // Step 13: Summary\n stepSummary(\n allUpdates,\n claudeMdGenerated,\n paiSkillInstalled,\n aiSteeringRulesInstalled,\n skillStubsInstalled,\n hooksInstalled,\n tsHooksInstalled,\n settingsPatched,\n daName,\n daemonInstalled,\n mcpRegistered,\n );\n\n } finally {\n rl.close();\n }\n}\n\nexport function registerSetupCommand(program: Command): void {\n program\n .command(\"setup\")\n .alias(\"install\")\n .description(\n \"Interactive setup wizard — configure storage, embeddings, agent config, and indexing\",\n )\n .action(async () => {\n await runSetup();\n });\n}\n","/** Symlink management: create, validate, migrate, and clean vault project symlinks. */\n\nimport {\n existsSync,\n mkdirSync,\n symlinkSync,\n readdirSync,\n lstatSync,\n unlinkSync,\n readlinkSync,\n writeFileSync,\n rmdirSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { Database } from \"better-sqlite3\";\nimport type { ProjectRow, SyncStats } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** Find the project-root Notes directory for a project. */\nexport function findNotesDir(rootPath: string): string | null {\n const canonical = join(rootPath, \"Notes\");\n if (existsSync(canonical)) return canonical;\n const alt = join(rootPath, \".claude\", \"Notes\");\n if (existsSync(alt)) return alt;\n return null;\n}\n\n/**\n * Find the Claude Code session notes directory from the registry-stored value.\n * Returns null if not set, missing on disk, or identical to notesDir.\n */\nexport function findClaudeNotesDir(\n claudeNotesDirFromRegistry: string | null,\n notesDir: string | null\n): string | null {\n if (!claudeNotesDirFromRegistry) return null;\n if (!existsSync(claudeNotesDirFromRegistry)) return null;\n if (notesDir && claudeNotesDirFromRegistry === notesDir) return null;\n return claudeNotesDirFromRegistry;\n}\n\n/** Check whether a path exists via lstat (does not follow symlinks). */\nfunction lstatExists(p: string): boolean {\n try {\n lstatSync(p);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Resolve slug collisions by appending -2, -3, etc. */\nexport function uniqueSlug(base: string, taken: Set<string>): string {\n if (!taken.has(base)) return base;\n let n = 2;\n while (taken.has(`${base}-${n}`)) n++;\n return `${base}-${n}`;\n}\n\n/**\n * Remove broken symlinks from a directory (one level deep).\n * Returns count of entries removed.\n */\nexport function cleanBrokenSymlinks(dir: string): number {\n let removed = 0;\n if (!existsSync(dir)) return removed;\n for (const entry of readdirSync(dir)) {\n const full = join(dir, entry);\n try {\n const stat = lstatSync(full);\n if (stat.isSymbolicLink()) {\n const target = readlinkSync(full);\n if (!existsSync(target)) {\n unlinkSync(full);\n removed++;\n }\n }\n } catch {\n // Skip unreadable entries\n }\n }\n return removed;\n}\n\n/**\n * Ensure a sub-symlink inside a project directory is correct.\n * Returns true if a new symlink was created, false otherwise.\n */\nexport function ensureSubSymlink(\n linkPath: string,\n target: string,\n errors: string[],\n label: string\n): boolean {\n if (lstatExists(linkPath)) {\n try {\n const stat = lstatSync(linkPath);\n if (stat.isSymbolicLink()) {\n const current = readlinkSync(linkPath);\n if (current === target) {\n return false; // Already correct\n }\n unlinkSync(linkPath);\n } else {\n errors.push(`${label}: path exists but is not a symlink — skipped`);\n return false;\n }\n } catch (e) {\n errors.push(`${label}: error checking existing path — ${e}`);\n return false;\n }\n }\n\n try {\n symlinkSync(target, linkPath);\n return true;\n } catch (e) {\n errors.push(`${label}: symlink creation failed — ${e}`);\n return false;\n }\n}\n\n/**\n * Migrate a legacy flat symlink at `slugPath` to a real directory.\n * If `slugPath` is already a real directory, this is a no-op.\n * If it is a symlink (legacy), it is removed so mkdirSync can create the dir.\n */\nexport function migrateToProjectDir(\n slugPath: string,\n errors: string[],\n slug: string\n): boolean {\n if (!lstatExists(slugPath)) {\n return true; // Does not exist yet — mkdirSync will create it\n }\n\n try {\n const stat = lstatSync(slugPath);\n\n if (stat.isDirectory()) {\n return true; // Already a real directory — nothing to migrate\n }\n\n if (stat.isSymbolicLink()) {\n unlinkSync(slugPath);\n return true; // Removed old symlink — now ready for mkdirSync\n }\n\n errors.push(`${slug}: path exists as non-directory, non-symlink — skipped`);\n return false;\n } catch (e) {\n errors.push(`${slug}: error during migration — ${e}`);\n return false;\n }\n}\n\n/** Remove a project directory from the vault if it is empty. */\nfunction removeProjectDirIfEmpty(slugPath: string): void {\n try {\n const entries = readdirSync(slugPath);\n if (entries.length === 0) {\n rmdirSync(slugPath);\n }\n } catch {\n // Non-fatal\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public: syncVault\n// ---------------------------------------------------------------------------\n\n/**\n * Sync all active project Notes directories into the Obsidian vault.\n *\n * For each active project with at least one Notes source, creates:\n * {vault}/{slug}/ — real directory\n * notes → {root}/Notes/\n * sessions → ~/.claude/projects/{enc}/Notes/ (if different)\n *\n * Archived projects get a stub markdown file in {vault}/_archive/.\n */\nexport function syncVault(vaultPath: string, db: Database): SyncStats {\n const stats: SyncStats = { created: 0, updated: 0, removed: 0, stubbed: 0, errors: [] };\n\n mkdirSync(vaultPath, { recursive: true });\n stats.removed += cleanBrokenSymlinks(vaultPath);\n\n const projects = db\n .prepare(\n `SELECT id, slug, display_name, root_path, encoded_dir, status, obsidian_link, claude_notes_dir\n FROM projects\n ORDER BY status ASC, slug ASC`\n )\n .all() as ProjectRow[];\n\n const takenSlugs = new Set<string>();\n\n for (const project of projects) {\n if (project.status === \"active\") {\n const notesDir = findNotesDir(project.root_path);\n const claudeNotesDir = findClaudeNotesDir(project.claude_notes_dir, notesDir);\n\n if (!notesDir && !claudeNotesDir) {\n continue;\n }\n\n const slug = uniqueSlug(project.slug, takenSlugs);\n takenSlugs.add(slug);\n const slugPath = join(vaultPath, slug);\n\n if (!migrateToProjectDir(slugPath, stats.errors, slug)) {\n continue;\n }\n\n try {\n mkdirSync(slugPath, { recursive: true });\n } catch (e) {\n stats.errors.push(`${slug}: failed to create project directory — ${e}`);\n continue;\n }\n\n if (notesDir) {\n const notesLink = join(slugPath, \"notes\");\n const created = ensureSubSymlink(notesLink, notesDir, stats.errors, `${slug}/notes`);\n if (created) stats.created++;\n else stats.updated++;\n }\n\n if (claudeNotesDir) {\n const sessionsLink = join(slugPath, \"sessions\");\n const created = ensureSubSymlink(sessionsLink, claudeNotesDir, stats.errors, `${slug}/sessions`);\n if (created) stats.created++;\n else stats.updated++;\n }\n\n try {\n db.prepare(\"UPDATE projects SET obsidian_link = ?, updated_at = ? WHERE id = ?\").run(\n slugPath,\n Date.now(),\n project.id\n );\n } catch {\n // Non-fatal\n }\n } else if (project.status === \"archived\") {\n const archiveDir = join(vaultPath, \"_archive\");\n mkdirSync(archiveDir, { recursive: true });\n const stubPath = join(archiveDir, `${project.slug}.md`);\n if (!existsSync(stubPath)) {\n const content = [\n `# ${project.display_name}`,\n \"\",\n \"> Archived project — no live notes available.\",\n \"\",\n `- **Slug:** ${project.slug}`,\n `- **Root:** ${project.root_path}`,\n \"\",\n ].join(\"\\n\");\n try {\n writeFileSync(stubPath, content, \"utf-8\");\n stats.stubbed++;\n } catch (e) {\n stats.errors.push(`${project.slug} (archive stub): ${e}`);\n }\n }\n }\n }\n\n return stats;\n}\n","/** Index and topic page generation — writes _index.md and _topics/{tag}.md. */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type { Database } from \"better-sqlite3\";\nimport { fmtDate } from \"../../cli/utils.js\";\nimport type { ProjectRow, SessionStats, TagRow } from \"./types.js\";\nimport { findNotesDir, findClaudeNotesDir } from \"./symlinks.js\";\n\n/**\n * Generate _index.md listing all projects with session counts, tags, and\n * indicators for which note sources are available (notes, sessions, or both).\n */\nexport function generateIndex(vaultPath: string, db: Database): void {\n mkdirSync(vaultPath, { recursive: true });\n\n const rows = db\n .prepare(\n `SELECT p.id, p.slug, p.display_name, p.status, p.root_path,\n p.encoded_dir, p.claude_notes_dir,\n (SELECT COUNT(*) FROM sessions s WHERE s.project_id = p.id) AS session_count,\n (SELECT MAX(s.created_at) FROM sessions s WHERE s.project_id = p.id) AS last_active\n FROM projects p\n ORDER BY p.status ASC, p.updated_at DESC`\n )\n .all() as (ProjectRow & SessionStats)[];\n\n const getTagsForProject = db.prepare(\n `SELECT t.name FROM tags t\n JOIN project_tags pt ON pt.tag_id = t.id\n WHERE pt.project_id = ?\n ORDER BY t.name`\n );\n\n const active = rows.filter((r) => r.status === \"active\");\n const archived = rows.filter((r) => r.status !== \"active\");\n\n const lines: string[] = [\n \"# PAI Project Index\",\n \"\",\n `> Auto-generated by PAI Knowledge OS — ${new Date().toISOString()}`,\n \"\",\n \"## Active Projects\",\n \"\",\n \"| Project | Sessions | Last Active | Sources | Tags |\",\n \"| ------- | -------- | ----------- | ------- | ---- |\",\n ];\n\n for (const row of active) {\n const tags = (getTagsForProject.all(row.id) as TagRow[]).map((t) => `\\`${t.name}\\``).join(\" \");\n const lastActive = fmtDate(row.last_active);\n\n const notesDir = findNotesDir(row.root_path);\n const claudeNotesDir = findClaudeNotesDir(row.claude_notes_dir, notesDir);\n const sources: string[] = [];\n if (notesDir) sources.push(\"notes\");\n if (claudeNotesDir) sources.push(\"sessions\");\n const sourcesLabel = sources.length > 0 ? sources.join(\", \") : \"—\";\n\n lines.push(\n `| [[${row.slug}/|${row.display_name}]] | ${row.session_count} | ${lastActive} | ${sourcesLabel} | ${tags || \"—\"} |`\n );\n }\n\n if (archived.length > 0) {\n lines.push(\n \"\",\n \"## Archived Projects\",\n \"\",\n \"| Project | Sessions | Tags |\",\n \"| ------- | -------- | ---- |\"\n );\n for (const row of archived) {\n const tags = (getTagsForProject.all(row.id) as TagRow[]).map((t) => `\\`${t.name}\\``).join(\" \");\n lines.push(\n `| [${row.display_name}](_archive/${row.slug}.md) | ${row.session_count} | ${tags || \"—\"} |`\n );\n }\n }\n\n lines.push(\n \"\",\n \"---\",\n `*${rows.length} projects total — ${active.length} active, ${archived.length} archived*`\n );\n\n writeFileSync(join(vaultPath, \"_index.md\"), lines.join(\"\\n\") + \"\\n\", \"utf-8\");\n}\n\n/**\n * Generate per-tag topic pages at _topics/{tag}.md.\n * Returns count of pages written.\n */\nexport function generateTopicPages(vaultPath: string, db: Database): number {\n const topicsDir = join(vaultPath, \"_topics\");\n mkdirSync(topicsDir, { recursive: true });\n\n const allTags = db\n .prepare(\"SELECT id, name FROM tags ORDER BY name\")\n .all() as { id: number; name: string }[];\n\n const getProjectsForTag = db.prepare(\n `SELECT p.id, p.slug, p.display_name, p.status,\n (SELECT COUNT(*) FROM sessions s WHERE s.project_id = p.id) AS session_count,\n (SELECT MAX(s.created_at) FROM sessions s WHERE s.project_id = p.id) AS last_active\n FROM projects p\n JOIN project_tags pt ON pt.project_id = p.id\n WHERE pt.tag_id = ?\n ORDER BY p.status ASC, p.updated_at DESC`\n );\n\n let written = 0;\n for (const tag of allTags) {\n const projects = getProjectsForTag.all(tag.id) as (ProjectRow & SessionStats)[];\n if (!projects.length) continue;\n\n const lines: string[] = [\n `# Topic: ${tag.name}`,\n \"\",\n `> Auto-generated by PAI Knowledge OS — ${new Date().toISOString()}`,\n \"\",\n \"## Projects\",\n \"\",\n \"| Project | Status | Sessions | Last Active |\",\n \"| ------- | ------ | -------- | ----------- |\",\n ];\n\n for (const p of projects) {\n const link =\n p.status === \"active\"\n ? `[[${p.slug}/|${p.display_name}]]`\n : `[${p.display_name}](../_archive/${p.slug}.md)`;\n lines.push(`| ${link} | ${p.status} | ${p.session_count} | ${fmtDate(p.last_active)} |`);\n }\n\n lines.push(\"\", \"---\", `*${projects.length} project(s) tagged \\`${tag.name}\\`*`);\n\n writeFileSync(join(topicsDir, `${tag.name}.md`), lines.join(\"\\n\") + \"\\n\", \"utf-8\");\n written++;\n }\n\n return written;\n}\n\n/**\n * Default vault path: ~/.pai/obsidian-vault\n */\nexport function defaultVaultPath(): string {\n return join(homedir(), \".pai\", \"obsidian-vault\");\n}\n","/** Directory walking and session file discovery for vault note generation. */\n\nimport {\n existsSync,\n readdirSync,\n lstatSync,\n readlinkSync,\n} from \"node:fs\";\nimport { join, relative } from \"node:path\";\nimport type { SessionFile } from \"./types.js\";\n\nexport const SESSION_FILENAME_RE = /^(\\d{4}) - (\\d{4}-\\d{2})-\\d{2} - .+\\.md$/;\n\n/**\n * Walk a directory (non-recursive root level, then one level of YYYY/MM subdirs).\n * Returns absolute paths to all .md files found, skipping master note files.\n */\nexport function walkNotesDir(dir: string): string[] {\n const results: string[] = [];\n if (!existsSync(dir)) return results;\n\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return results;\n }\n\n for (const entry of entries) {\n if (entry.startsWith(\".\")) continue;\n // Skip any master note file (pattern: _{slug}-master.md or legacy _master.md)\n if (entry === \"_master.md\" || /^_[^/]+-master\\.md$/.test(entry)) continue;\n const full = join(dir, entry);\n let stat: ReturnType<typeof lstatSync>;\n try {\n stat = lstatSync(full);\n } catch {\n continue;\n }\n if (stat.isDirectory()) {\n if (/^\\d{4}$/.test(entry)) {\n let monthEntries: string[];\n try {\n monthEntries = readdirSync(full);\n } catch {\n continue;\n }\n for (const month of monthEntries) {\n const monthPath = join(full, month);\n if (/^\\d{2}$/.test(month) && existsSync(monthPath)) {\n let monthFiles: string[];\n try {\n monthFiles = readdirSync(monthPath);\n } catch {\n continue;\n }\n for (const f of monthFiles) {\n if (f.endsWith(\".md\") && !f.startsWith(\".\")) {\n results.push(join(monthPath, f));\n }\n }\n }\n }\n }\n } else if (entry.endsWith(\".md\")) {\n results.push(full);\n }\n }\n return results;\n}\n\n/**\n * Extract YYYY/MM from a session file path.\n * Tries the path first (/YYYY/MM/ pattern), then falls back to filename date.\n */\nexport function extractYearMonth(filePath: string): string {\n const pathMatch = filePath.match(/\\/(\\d{4})\\/(\\d{2})\\//);\n if (pathMatch) return `${pathMatch[1]}/${pathMatch[2]}`;\n\n const basename = filePath.split(\"/\").pop() ?? \"\";\n const nameMatch = basename.match(/^\\d{4} - (\\d{4})-(\\d{2})-\\d{2}/);\n if (nameMatch) return `${nameMatch[1]}/${nameMatch[2]}`;\n\n return \"unknown\";\n}\n\n/**\n * Collect all session .md files from the notes and sessions symlinks\n * inside a vault project directory.\n */\nexport function collectSessionFiles(slugPath: string): SessionFile[] {\n const sessionFiles: SessionFile[] = [];\n const subLinks = [\"notes\", \"sessions\"];\n\n for (const subLink of subLinks) {\n const linkPath = join(slugPath, subLink);\n if (!existsSync(linkPath)) continue;\n\n let realDir: string;\n try {\n const stat = lstatSync(linkPath);\n if (stat.isSymbolicLink()) {\n realDir = readlinkSync(linkPath);\n } else if (stat.isDirectory()) {\n realDir = linkPath;\n } else {\n continue;\n }\n } catch {\n continue;\n }\n\n const files = walkNotesDir(realDir);\n for (const absPath of files) {\n const basename = absPath.split(\"/\").pop() ?? \"\";\n if (!SESSION_FILENAME_RE.test(basename)) continue;\n\n const relFromReal = relative(realDir, absPath);\n const vaultRelPath = `${subLink}/${relFromReal}`;\n const wikilinkTarget = vaultRelPath.replace(/\\.md$/, \"\");\n\n sessionFiles.push({\n absPath,\n vaultRelPath,\n wikilinkTarget,\n yearMonth: extractYearMonth(absPath),\n basename: basename.replace(/\\.md$/, \"\"),\n });\n }\n }\n\n return sessionFiles;\n}\n","/** Master note generation and session tag fixing for vault projects. */\n\nimport {\n existsSync,\n unlinkSync,\n writeFileSync,\n readFileSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { Database } from \"better-sqlite3\";\nimport type { ProjectRow } from \"./types.js\";\nimport { collectSessionFiles, walkNotesDir } from \"./walk.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Build the per-project master note filename. */\nexport function masterFilename(slug: string): string {\n return `_${slug}-master.md`;\n}\n\n/**\n * Remove any old/broken backlink footer from a session file.\n * Returns true if the file was modified.\n */\nfunction removeOldBacklink(filePath: string): boolean {\n let content: string;\n try {\n content = readFileSync(filePath, \"utf-8\");\n } catch {\n return false;\n }\n const oldPattern = /\\n---\\n\\[\\[\\.\\.\\/[^\\]]+\\]\\]\\n?$/;\n const newPattern = /\\n---\\n\\[\\[_[^\\]]*-master\\|[^\\]]*\\]\\]\\n?$/;\n const cleaned = content.replace(oldPattern, \"\").replace(newPattern, \"\");\n if (cleaned === content) return false;\n try {\n writeFileSync(filePath, cleaned, \"utf-8\");\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Append the master note backlink footer to a session file, idempotently.\n * Only writes if the sentinel string is not already present.\n */\nfunction appendBacklinkIfMissing(\n filePath: string,\n slug: string,\n displayName: string\n): boolean {\n let content: string;\n try {\n content = readFileSync(filePath, \"utf-8\");\n } catch {\n return false;\n }\n const masterName = masterFilename(slug).replace(/\\.md$/, \"\");\n const sentinel = `[[${masterName}|`;\n if (content.includes(sentinel)) return false;\n\n const footer = `\\n---\\n[[${masterName}|← ${displayName} Master]]\\n`;\n try {\n writeFileSync(filePath, content + footer, \"utf-8\");\n return true;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public: generateMasterNotes\n// ---------------------------------------------------------------------------\n\n/**\n * Generate _master.md files for projects that have >= threshold session files.\n *\n * For each qualifying project, writes a {vaultPath}/{slug}/_master.md containing:\n * - Project title, session count + date range\n * - Sessions grouped by YYYY/MM with Obsidian [[wikilinks]]\n *\n * Also appends a backlink footer to each session file (idempotently).\n *\n * @param vaultPath Absolute path to the PAI Obsidian vault\n * @param db Registry SQLite database\n * @param threshold Minimum session count to generate a master note (default: 5)\n * @returns Number of master notes written\n */\nexport function generateMasterNotes(\n vaultPath: string,\n db: Database,\n threshold = 5\n): number {\n if (!existsSync(vaultPath)) return 0;\n\n const projects = db\n .prepare(\n `SELECT id, slug, display_name, root_path, encoded_dir, status, obsidian_link, claude_notes_dir\n FROM projects\n WHERE status = 'active'\n ORDER BY slug ASC`\n )\n .all() as ProjectRow[];\n\n let written = 0;\n\n for (const project of projects) {\n const slugPath = join(vaultPath, project.slug);\n if (!existsSync(slugPath)) continue;\n\n // Remove legacy _master.md if present\n const legacyMaster = join(slugPath, \"_master.md\");\n if (existsSync(legacyMaster)) {\n try { unlinkSync(legacyMaster); } catch { /* Non-fatal */ }\n }\n\n const sessionFiles = collectSessionFiles(slugPath);\n if (sessionFiles.length < threshold) continue;\n\n // Sort by yearMonth then basename\n sessionFiles.sort((a, b) => {\n if (a.yearMonth !== b.yearMonth) return a.yearMonth.localeCompare(b.yearMonth);\n return a.basename.localeCompare(b.basename);\n });\n\n const byMonth = new Map<string, typeof sessionFiles>();\n for (const sf of sessionFiles) {\n if (!byMonth.has(sf.yearMonth)) byMonth.set(sf.yearMonth, []);\n byMonth.get(sf.yearMonth)!.push(sf);\n }\n\n const firstDate = sessionFiles[0]?.basename.match(/\\d{4}-\\d{2}-\\d{2}/)?.[0] ?? \"unknown\";\n const lastDate = sessionFiles[sessionFiles.length - 1]?.basename.match(/\\d{4}-\\d{2}-\\d{2}/)?.[0] ?? \"unknown\";\n\n const lines: string[] = [\n `# Project: ${project.display_name}`,\n \"\",\n `> Auto-generated by PAI Knowledge OS — ${new Date().toISOString()}`,\n \"\",\n \"## Overview\",\n \"\",\n `- **Sessions:** ${sessionFiles.length}`,\n `- **Date range:** ${firstDate} to ${lastDate}`,\n `- **Tags:** #${project.slug} #project`,\n \"\",\n \"## Sessions by Month\",\n \"\",\n ];\n\n for (const [ym, files] of byMonth) {\n lines.push(`### ${ym}`, \"\");\n for (const sf of files) {\n lines.push(`- [[${sf.wikilinkTarget}|${sf.basename}]]`);\n }\n lines.push(\"\");\n }\n\n lines.push(\"## Topics\", \"\", `#${project.slug} #project`, \"\");\n\n const masterPath = join(slugPath, masterFilename(project.slug));\n try {\n writeFileSync(masterPath, lines.join(\"\\n\"), \"utf-8\");\n written++;\n } catch {\n continue;\n }\n\n for (const sf of sessionFiles) {\n removeOldBacklink(sf.absPath);\n appendBacklinkIfMissing(sf.absPath, project.slug, project.display_name);\n }\n }\n\n return written;\n}\n\n// ---------------------------------------------------------------------------\n// Public: fixSessionTags\n// ---------------------------------------------------------------------------\n\n/**\n * Remove the generic #Session tag from session note files across all projects.\n *\n * Scans all session and notes directories for every active project and removes\n * #Session (with or without a trailing space) from any `**Tags:**` line.\n *\n * @param db Registry SQLite database\n * @returns Object with counts: { filesScanned, filesModified, errors }\n */\nexport function fixSessionTags(\n db: Database\n): { filesScanned: number; filesModified: number; errors: string[] } {\n const results = { filesScanned: 0, filesModified: 0, errors: [] as string[] };\n\n const projects = db\n .prepare(\n `SELECT id, slug, display_name, root_path, encoded_dir, status, obsidian_link, claude_notes_dir\n FROM projects\n WHERE status = 'active'\n ORDER BY slug ASC`\n )\n .all() as ProjectRow[];\n\n for (const project of projects) {\n const dirsToScan: string[] = [];\n\n // Inline findNotesDir to avoid a circular dependency with symlinks.ts\n const canonical = join(project.root_path, \"Notes\");\n const alt = join(project.root_path, \".claude\", \"Notes\");\n const notesDir = existsSync(canonical) ? canonical : existsSync(alt) ? alt : null;\n\n if (notesDir) dirsToScan.push(notesDir);\n\n if (\n project.claude_notes_dir &&\n existsSync(project.claude_notes_dir) &&\n project.claude_notes_dir !== notesDir\n ) {\n dirsToScan.push(project.claude_notes_dir);\n }\n\n for (const dir of dirsToScan) {\n const files = walkNotesDir(dir);\n for (const filePath of files) {\n results.filesScanned++;\n let content: string;\n try {\n content = readFileSync(filePath, \"utf-8\");\n } catch (e) {\n results.errors.push(`${filePath}: read error — ${e}`);\n continue;\n }\n\n if (!content.includes(\"#Session\")) continue;\n\n const updated = content.replace(\n /(\\*\\*Tags:\\*\\*[^\\n]*)#Session ?/g,\n \"$1\"\n );\n\n if (updated === content) continue;\n\n const cleaned = updated.replace(/(\\*\\*Tags:\\*\\*) *\\n/g, \"$1\\n\");\n\n try {\n writeFileSync(filePath, cleaned, \"utf-8\");\n results.filesModified++;\n } catch (e) {\n results.errors.push(`${filePath}: write error — ${e}`);\n }\n }\n }\n }\n\n return results;\n}\n","/**\n * obsidian/status.ts — Vault health checking for PAI Phase 4\n *\n * Vault structure (per project):\n *\n * {vault}/{slug}/ — real directory\n * notes → {root}/Notes/ (project root notes, optional)\n * sessions → ~/.claude/projects/{enc}/Notes/ (Claude Code session notes, optional)\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport { existsSync, readdirSync, lstatSync, readlinkSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface ProjectRow {\n id: number;\n slug: string;\n display_name: string;\n root_path: string;\n status: string;\n claude_notes_dir: string | null;\n obsidian_link: string | null;\n}\n\nexport interface SymlinkHealth {\n slug: string;\n linkPath: string;\n target: string | null;\n state: \"healthy\" | \"broken\" | \"orphaned\" | \"missing\";\n notes: string;\n}\n\nexport interface VaultHealthReport {\n vaultPath: string;\n healthy: SymlinkHealth[];\n broken: SymlinkHealth[];\n orphaned: SymlinkHealth[];\n missing: SymlinkHealth[];\n totalProjects: number;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Check the health of a sub-symlink inside a project directory.\n * Returns true if the symlink exists and its target exists on disk.\n */\nfunction subSymlinkHealthy(dir: string, name: string): boolean {\n const linkPath = join(dir, name);\n try {\n const stat = lstatSync(linkPath);\n if (!stat.isSymbolicLink()) return false;\n const target = readlinkSync(linkPath);\n return existsSync(target);\n } catch {\n return false;\n }\n}\n\n/**\n * Check if a sub-symlink exists (regardless of target validity).\n */\nfunction subSymlinkExists(dir: string, name: string): boolean {\n try {\n lstatSync(join(dir, name));\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Find a project root Notes dir (checks canonical and .claude/Notes fallback).\n */\nfunction findNotesDir(rootPath: string): string | null {\n const canonical = join(rootPath, \"Notes\");\n if (existsSync(canonical)) return canonical;\n const alt = join(rootPath, \".claude\", \"Notes\");\n if (existsSync(alt)) return alt;\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Check health of the Obsidian vault:\n *\n * Project entries are now real directories with sub-symlinks (`notes`, `sessions`).\n *\n * - healthy: project dir exists; at least one sub-symlink is present and resolves\n * - broken: project dir exists but all sub-symlinks have missing targets\n * - orphaned: directory entry in vault has no matching registry project\n * - missing: active project with at least one Notes source but no vault dir\n */\nexport function checkHealth(vaultPath: string, db: Database): VaultHealthReport {\n const report: VaultHealthReport = {\n vaultPath,\n healthy: [],\n broken: [],\n orphaned: [],\n missing: [],\n totalProjects: 0,\n };\n\n if (!existsSync(vaultPath)) {\n return report;\n }\n\n const projects = db\n .prepare(\n `SELECT id, slug, display_name, root_path, status, claude_notes_dir, obsidian_link\n FROM projects WHERE status = 'active'`\n )\n .all() as ProjectRow[];\n\n report.totalProjects = projects.length;\n\n // Build a map of slug → project for quick lookup\n const projectsBySlug = new Map<string, ProjectRow>();\n for (const p of projects) {\n projectsBySlug.set(p.slug, p);\n }\n\n // Scan vault directory\n const seenSlugs = new Set<string>();\n let entries: string[] = [];\n try {\n entries = readdirSync(vaultPath);\n } catch {\n return report;\n }\n\n for (const entry of entries) {\n // Skip internal PAI-generated dirs/files\n if (entry.startsWith(\"_\")) continue;\n\n const fullPath = join(vaultPath, entry);\n let stat;\n try {\n stat = lstatSync(fullPath);\n } catch {\n continue;\n }\n\n const project = projectsBySlug.get(entry);\n\n if (stat.isDirectory()) {\n // New-style project directory\n if (!project) {\n // Directory in vault with no matching registry project\n report.orphaned.push({\n slug: entry,\n linkPath: fullPath,\n target: null,\n state: \"orphaned\",\n notes: \"No matching active project in registry (directory)\",\n });\n continue;\n }\n\n seenSlugs.add(entry);\n\n const notesOk = subSymlinkHealthy(fullPath, \"notes\");\n const sessionsOk = subSymlinkHealthy(fullPath, \"sessions\");\n const notesExists = subSymlinkExists(fullPath, \"notes\");\n const sessionsExists = subSymlinkExists(fullPath, \"sessions\");\n\n if (notesOk || sessionsOk) {\n const parts: string[] = [];\n if (notesOk) parts.push(\"notes\");\n if (sessionsOk) parts.push(\"sessions\");\n report.healthy.push({\n slug: entry,\n linkPath: fullPath,\n target: null,\n state: \"healthy\",\n notes: `Sources: ${parts.join(\", \")}`,\n });\n } else {\n // At least one sub-link exists but none resolves\n const broken: string[] = [];\n if (notesExists) broken.push(\"notes\");\n if (sessionsExists) broken.push(\"sessions\");\n report.broken.push({\n slug: entry,\n linkPath: fullPath,\n target: null,\n state: \"broken\",\n notes: `Broken sub-symlinks: ${broken.join(\", \") || \"(empty directory)\"}`,\n });\n }\n } else if (stat.isSymbolicLink()) {\n // Legacy flat symlink — treat same as before\n let target: string | null = null;\n try {\n target = readlinkSync(fullPath);\n } catch {\n // Can't read symlink\n }\n\n const targetExists = target !== null && existsSync(target);\n\n if (!project) {\n report.orphaned.push({\n slug: entry,\n linkPath: fullPath,\n target,\n state: \"orphaned\",\n notes: \"No matching active project in registry (legacy symlink)\",\n });\n continue;\n }\n\n seenSlugs.add(entry);\n\n if (targetExists) {\n report.healthy.push({\n slug: entry,\n linkPath: fullPath,\n target,\n state: \"healthy\",\n notes: \"(legacy flat symlink — run sync to upgrade)\",\n });\n } else {\n report.broken.push({\n slug: entry,\n linkPath: fullPath,\n target,\n state: \"broken\",\n notes: `Target missing: ${target ?? \"(unknown)\"}`,\n });\n }\n }\n // Non-symlink, non-directory entries are ignored (e.g. stray files)\n }\n\n // Find active projects with Notes/ dirs that have no vault entry\n for (const project of projects) {\n if (seenSlugs.has(project.slug)) continue;\n\n const notesCanonical = join(project.root_path, \"Notes\");\n const notesAlt = join(project.root_path, \".claude\", \"Notes\");\n const hasProjectNotes = existsSync(notesCanonical) || existsSync(notesAlt);\n const hasClaudeNotes =\n project.claude_notes_dir !== null && existsSync(project.claude_notes_dir);\n\n if (hasProjectNotes || hasClaudeNotes) {\n const sources: string[] = [];\n if (hasProjectNotes) sources.push(\"notes\");\n if (hasClaudeNotes) sources.push(\"sessions\");\n report.missing.push({\n slug: project.slug,\n linkPath: join(vaultPath, project.slug),\n target: null,\n state: \"missing\",\n notes: `Has sources (${sources.join(\", \")}) but no vault entry — run 'pai obsidian sync'`,\n });\n }\n }\n\n return report;\n}\n","/**\n * pai obsidian <sub-command>\n *\n * sync — symlink project Notes/ dirs into vault, write _index.md + _topics/\n * status — health report: healthy, broken, orphaned, missing symlinks\n * open — open vault in Obsidian app (macOS)\n */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport { execSync } from \"node:child_process\";\nimport { ok, warn, err, dim, bold, header } from \"../utils.js\";\nimport {\n syncVault,\n generateIndex,\n generateTopicPages,\n generateMasterNotes,\n fixSessionTags,\n defaultVaultPath,\n} from \"../../obsidian/sync.js\";\nimport { checkHealth } from \"../../obsidian/status.js\";\nimport {\n loadConfig,\n expandHome,\n CONFIG_FILE,\n} from \"../../daemon/config.js\";\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport chalk from \"chalk\";\n\n// ---------------------------------------------------------------------------\n// Config helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Read obsidianVaultPath from ~/.config/pai/config.json.\n * Falls back to defaultVaultPath() if not set.\n */\nfunction getVaultPath(override?: string): string {\n if (override) return expandHome(override);\n\n try {\n const raw = readFileSync(CONFIG_FILE, \"utf-8\");\n const cfg = JSON.parse(raw) as Record<string, unknown>;\n if (typeof cfg.obsidianVaultPath === \"string\" && cfg.obsidianVaultPath) {\n return expandHome(cfg.obsidianVaultPath);\n }\n } catch {\n // Config missing or unreadable — use default\n }\n return defaultVaultPath();\n}\n\n/**\n * Persist obsidianVaultPath into config.json so future syncs use the same path.\n */\nfunction saveVaultPath(vaultPath: string): void {\n let cfg: Record<string, unknown> = {};\n try {\n cfg = JSON.parse(readFileSync(CONFIG_FILE, \"utf-8\")) as Record<string, unknown>;\n } catch {\n // Start fresh if file is missing/corrupt\n }\n cfg.obsidianVaultPath = vaultPath;\n mkdirSync(dirname(CONFIG_FILE), { recursive: true });\n writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2) + \"\\n\", \"utf-8\");\n}\n\n// ---------------------------------------------------------------------------\n// Command implementations\n// ---------------------------------------------------------------------------\n\nfunction cmdSync(\n db: Database,\n opts: { vault?: string; quiet?: boolean }\n): void {\n const vaultPath = getVaultPath(opts.vault);\n const q = opts.quiet ?? false;\n\n if (!q) {\n console.log();\n console.log(header(\" PAI Obsidian Sync\"));\n console.log(dim(` Vault: ${vaultPath}`));\n console.log();\n }\n\n // --- Symlinks ---\n if (!q) process.stdout.write(\" Syncing symlinks...\");\n const stats = syncVault(vaultPath, db);\n if (!q) {\n process.stdout.write(\n `\\r ${ok(\"Symlinks:\")} created ${stats.created} updated ${stats.updated} removed ${stats.removed} stubs ${stats.stubbed}\\n`\n );\n if (stats.errors.length) {\n for (const e of stats.errors) {\n console.log(warn(` Warning: ${e}`));\n }\n }\n }\n\n // --- Index ---\n if (!q) process.stdout.write(\" Generating index...\");\n generateIndex(vaultPath, db);\n if (!q) console.log(`\\r ${ok(\"Index:\")} _index.md written `);\n\n // --- Topic pages ---\n if (!q) process.stdout.write(\" Generating topic pages...\");\n const topicCount = generateTopicPages(vaultPath, db);\n if (!q) console.log(`\\r ${ok(\"Topics:\")} ${topicCount} topic page(s) written `);\n\n // --- Fix session tags (remove generic #Session) ---\n if (!q) process.stdout.write(\" Fixing session tags...\");\n const tagStats = fixSessionTags(db);\n if (!q) {\n console.log(\n `\\r ${ok(\"Tags:\")} ${tagStats.filesModified} file(s) updated (scanned ${tagStats.filesScanned}) `\n );\n if (tagStats.errors.length) {\n for (const e of tagStats.errors) {\n console.log(warn(` Warning: ${e}`));\n }\n }\n }\n\n // --- Master notes ---\n if (!q) process.stdout.write(\" Generating master notes...\");\n const masterCount = generateMasterNotes(vaultPath, db);\n if (!q) console.log(`\\r ${ok(\"Masters:\")} ${masterCount} master note(s) written `);\n\n // Persist vault path so status/open can use it\n saveVaultPath(vaultPath);\n\n if (!q) {\n console.log();\n console.log(ok(` Done. Vault ready at: ${vaultPath}`));\n console.log();\n }\n}\n\nfunction cmdStatus(db: Database, opts: { vault?: string }): void {\n const vaultPath = getVaultPath(opts.vault);\n const report = checkHealth(vaultPath, db);\n\n console.log();\n console.log(header(\" PAI Obsidian Vault Status\"));\n console.log(dim(` Vault: ${vaultPath}`));\n console.log();\n\n if (!existsSync(vaultPath)) {\n console.log(warn(\" Vault directory does not exist. Run: pai obsidian sync\"));\n console.log();\n return;\n }\n\n // Summary line\n const healthyCount = report.healthy.length;\n const brokenCount = report.broken.length;\n const orphanedCount = report.orphaned.length;\n const missingCount = report.missing.length;\n\n console.log(\n ` ${chalk.green(\"Healthy:\")} ${healthyCount} ` +\n `${chalk.red(\"Broken:\")} ${brokenCount} ` +\n `${chalk.yellow(\"Orphaned:\")} ${orphanedCount} ` +\n `${chalk.cyan(\"Missing:\")} ${missingCount}`\n );\n console.log();\n\n if (report.healthy.length && report.healthy.length <= 10) {\n console.log(bold(\" Healthy symlinks:\"));\n for (const h of report.healthy) {\n console.log(` ${chalk.green(\"✓\")} ${bold(h.slug)} ${dim(\"→ \" + (h.target ?? \"\"))}`);\n }\n console.log();\n } else if (report.healthy.length > 10) {\n console.log(bold(` ${report.healthy.length} healthy symlinks (all good)`));\n console.log();\n }\n\n if (report.broken.length) {\n console.log(err(\" Broken symlinks (target missing):\"));\n for (const b of report.broken) {\n console.log(` ${chalk.red(\"✗\")} ${bold(b.slug)} ${dim(b.notes)}`);\n }\n console.log(dim(\" Fix: pai obsidian sync\"));\n console.log();\n }\n\n if (report.orphaned.length) {\n console.log(warn(\" Orphaned symlinks (no registry project):\"));\n for (const o of report.orphaned) {\n console.log(` ${chalk.yellow(\"?\")} ${bold(o.slug)} ${dim(o.notes)}`);\n }\n console.log();\n }\n\n if (report.missing.length) {\n console.log(warn(\" Projects with Notes/ but no vault symlink:\"));\n for (const m of report.missing) {\n console.log(` ${chalk.cyan(\"→\")} ${bold(m.slug)} ${dim(m.notes)}`);\n }\n console.log();\n }\n\n if (brokenCount === 0 && orphanedCount === 0 && missingCount === 0) {\n console.log(ok(\" Vault is healthy.\"));\n console.log();\n }\n}\n\nfunction cmdOpen(opts: { vault?: string }): void {\n const vaultPath = getVaultPath(opts.vault);\n // Derive vault name from last path component (Obsidian uses folder name as vault name)\n const parts = vaultPath.split(\"/\").filter(Boolean);\n const vaultName = parts[parts.length - 1] ?? \"obsidian-vault\";\n\n if (!existsSync(vaultPath)) {\n console.error(err(`Vault not found at: ${vaultPath}`));\n console.error(dim(\" Run: pai obsidian sync\"));\n process.exit(1);\n }\n\n const url = `obsidian://open?vault=${encodeURIComponent(vaultName)}`;\n console.log(dim(` Opening: ${url}`));\n try {\n execSync(`open \"${url}\"`, { stdio: \"ignore\" });\n console.log(ok(` Opened vault \"${vaultName}\" in Obsidian.`));\n } catch (e) {\n console.error(err(` Failed to open Obsidian: ${e}`));\n console.error(dim(\" Ensure Obsidian is installed and the vault is registered in Obsidian.\"));\n process.exit(1);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerObsidianCommands(\n obsidianCmd: Command,\n getDb: () => Database\n): void {\n // pai obsidian sync\n obsidianCmd\n .command(\"sync\")\n .description(\"Sync project Notes/ dirs into vault, generate _index.md and topic pages\")\n .option(\"--vault <path>\", \"Override vault path (default: ~/.pai/obsidian-vault)\")\n .option(\"--quiet\", \"Minimal output — suitable for cron/hook use\")\n .action((opts: { vault?: string; quiet?: boolean }) => {\n cmdSync(getDb(), opts);\n });\n\n // pai obsidian status\n obsidianCmd\n .command(\"status\")\n .description(\"Show vault health: healthy, broken, orphaned, and missing symlinks\")\n .option(\"--vault <path>\", \"Override vault path\")\n .action((opts: { vault?: string }) => {\n cmdStatus(getDb(), opts);\n });\n\n // pai obsidian open\n obsidianCmd\n .command(\"open\")\n .description(\"Open the vault in Obsidian (macOS)\")\n .option(\"--vault <path>\", \"Override vault path\")\n .action((opts: { vault?: string }) => {\n cmdOpen(opts);\n });\n}\n","/** Shared utilities for zettel CLI commands. */\n\nimport type { Database } from \"better-sqlite3\";\nimport { openFederation } from \"../../../memory/db.js\";\nimport { err } from \"../../utils.js\";\n\n/** Shorten a vault path to just the last 2-3 components for display. */\nexport function shortPath(p: string, parts = 3): string {\n const segments = p.split(\"/\").filter(Boolean);\n return segments.slice(-parts).join(\"/\");\n}\n\n// Lazy-loaded federation DB singleton\nlet _fedDb: Database | null = null;\n\n/** Get (or lazily open) the PAI federation database. */\nexport function getFedDb(): Database {\n if (!_fedDb) {\n try {\n _fedDb = openFederation();\n } catch (e) {\n console.error(err(`Failed to open PAI federation DB: ${e}`));\n process.exit(1);\n }\n }\n return _fedDb;\n}\n","/** zettel explore: follow link chains from a starting note. */\n\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { ok, warn, err, dim, bold, header } from \"../../utils.js\";\nimport { getFedDb, shortPath } from \"./utils.js\";\n\nasync function cmdExplore(\n note: string,\n opts: { depth?: string; direction?: string; mode?: string }\n): Promise<void> {\n const depth = parseInt(opts.depth ?? \"3\", 10);\n const direction = (opts.direction ?? \"both\") as \"forward\" | \"backward\" | \"both\";\n const mode = (opts.mode ?? \"all\") as \"sequential\" | \"associative\" | \"all\";\n\n const { zettelExplore } = await import(\"../../../zettelkasten/index.js\");\n const db = getFedDb();\n\n const result = zettelExplore(db, { startNote: note, depth, direction, mode });\n\n console.log();\n console.log(header(\" PAI Zettel Explore\"));\n console.log(dim(` Starting note: ${note}`));\n console.log(dim(` Depth: ${depth} Direction: ${direction} Mode: ${mode}`));\n console.log();\n\n if (result.nodes.length === 0) {\n console.log(warn(\" No connected notes found. Check that the note path exists in the vault index.\"));\n console.log();\n return;\n }\n\n console.log(` ${chalk.cyan(\"●\")} ${bold(shortPath(result.root))} ${dim(\"(root)\")}`);\n\n const byDepth = new Map<number, typeof result.nodes>();\n for (const node of result.nodes) {\n const list = byDepth.get(node.depth) ?? [];\n list.push(node);\n byDepth.set(node.depth, list);\n }\n\n for (let d = 1; d <= depth; d++) {\n const nodes = byDepth.get(d) ?? [];\n if (nodes.length === 0) continue;\n\n console.log();\n console.log(dim(` ${\" \".repeat(d - 1)}Depth ${d}:`));\n for (const node of nodes) {\n const indent = \" \".repeat(d);\n const isBranching = result.branchingPoints.includes(node.path);\n const typeColor = node.linkType === \"sequential\" ? chalk.blue : chalk.magenta;\n const branchMark = isBranching ? chalk.yellow(\" ⑂ branching\") : \"\";\n const title = node.title ?? shortPath(node.path);\n const stats = dim(`in:${node.inbound} out:${node.outbound}`);\n console.log(\n ` ${indent}${typeColor(\"→\")} ${bold(title)}${branchMark} ${stats} ${dim(typeColor(node.linkType))}`\n );\n }\n }\n\n console.log();\n const edgeSummary = `${result.edges.length} edges (${result.edges.filter(e => e.type === \"sequential\").length} sequential, ${result.edges.filter(e => e.type === \"associative\").length} associative)`;\n console.log(dim(` ${edgeSummary}`));\n if (result.branchingPoints.length > 0) {\n console.log(ok(` ${result.branchingPoints.length} branching point(s) found`));\n }\n if (result.maxDepthReached) {\n console.log(warn(\" Max depth reached — use --depth to explore further\"));\n }\n console.log();\n}\n\nexport function registerExploreCommand(parent: Command): void {\n parent\n .command(\"explore <note>\")\n .description(\"Follow link chains from a starting note\")\n .option(\"--depth <n>\", \"Maximum traversal depth (1-10)\", \"3\")\n .option(\"--direction <d>\", \"Link direction: forward | backward | both\", \"both\")\n .option(\"--mode <m>\", \"Edge mode: sequential | associative | all\", \"all\")\n .action(async (note: string, opts: { depth?: string; direction?: string; mode?: string }) => {\n try {\n await cmdExplore(note, opts);\n } catch (e) {\n console.error(err(` Error: ${e}`));\n process.exit(1);\n }\n });\n}\n","/** zettel health: vault structural health audit. */\n\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { ok, warn, err, dim, bold, header } from \"../../utils.js\";\nimport { getFedDb, shortPath } from \"./utils.js\";\n\nasync function cmdHealth(opts: {\n scope?: string;\n project?: string;\n days?: string;\n include?: string;\n}): Promise<void> {\n const scope = (opts.scope ?? \"full\") as \"full\" | \"recent\" | \"project\";\n const projectPath = opts.project;\n const recentDays = parseInt(opts.days ?? \"30\", 10);\n const includeTypes = opts.include\n ? (opts.include.split(\",\").map(s => s.trim()) as Array<\"dead_links\" | \"orphans\" | \"disconnected\" | \"low_connectivity\">)\n : undefined;\n\n const { zettelHealth } = await import(\"../../../zettelkasten/index.js\");\n const db = getFedDb();\n\n const result = zettelHealth(db, { scope, projectPath, recentDays, include: includeTypes });\n\n console.log();\n console.log(header(\" PAI Zettel Health\"));\n console.log(dim(` Scope: ${scope}${scope === \"project\" ? ` Path: ${projectPath ?? \"(none)\"}` : \"\"}${scope === \"recent\" ? ` Days: ${recentDays}` : \"\"}`));\n console.log();\n\n const score = result.healthScore;\n const scoreColor = score >= 80 ? chalk.green : score >= 60 ? chalk.yellow : chalk.red;\n const barWidth = 30;\n const filled = Math.round((score / 100) * barWidth);\n const bar = scoreColor(\"█\".repeat(filled)) + dim(\"░\".repeat(barWidth - filled));\n console.log(` Health Score: ${scoreColor(bold(String(score)))}% [${bar}]`);\n console.log();\n console.log(dim(` Files: ${result.totalFiles} Links: ${result.totalLinks}`));\n console.log();\n\n if (result.deadLinks.length === 0) {\n console.log(ok(\" Dead links: none\"));\n } else {\n console.log(warn(` Dead links: ${result.deadLinks.length}`));\n const preview = result.deadLinks.slice(0, 10);\n for (const dl of preview) {\n console.log(` ${chalk.red(\"✗\")} ${dim(shortPath(dl.sourcePath))} → ${bold(dl.targetRaw)} ${dim(`(line ${dl.lineNumber})`)}`);\n }\n if (result.deadLinks.length > 10) {\n console.log(dim(` ... and ${result.deadLinks.length - 10} more`));\n }\n console.log();\n }\n\n if (result.orphans.length === 0) {\n console.log(ok(\" Orphan notes: none\"));\n } else {\n console.log(warn(` Orphan notes: ${result.orphans.length}`));\n const preview = result.orphans.slice(0, 10);\n for (const o of preview) {\n console.log(` ${chalk.yellow(\"○\")} ${dim(shortPath(o))}`);\n }\n if (result.orphans.length > 10) {\n console.log(dim(` ... and ${result.orphans.length - 10} more`));\n }\n console.log();\n }\n\n if (result.disconnectedClusters <= 1) {\n console.log(ok(\" Disconnected clusters: 1 (fully connected)\"));\n } else {\n console.log(warn(` Disconnected clusters: ${result.disconnectedClusters}`));\n }\n\n if (result.lowConnectivity.length === 0) {\n console.log(ok(\" Low-connectivity: none\"));\n } else {\n console.log(warn(` Low-connectivity: ${result.lowConnectivity.length} note(s) with ≤1 link`));\n const preview = result.lowConnectivity.slice(0, 5);\n for (const lc of preview) {\n console.log(` ${chalk.dim(\"—\")} ${dim(shortPath(lc))}`);\n }\n if (result.lowConnectivity.length > 5) {\n console.log(dim(` ... and ${result.lowConnectivity.length - 5} more`));\n }\n }\n\n console.log();\n}\n\nexport function registerHealthCommand(parent: Command): void {\n parent\n .command(\"health\")\n .description(\"Vault structural health audit: dead links, orphans, connectivity\")\n .option(\"--scope <s>\", \"Scope: full | recent | project\", \"full\")\n .option(\"--project <path>\", \"Project path prefix (requires --scope project)\")\n .option(\"--days <n>\", \"Look-back window in days (requires --scope recent)\", \"30\")\n .option(\"--include <types>\", \"Comma-separated subset: dead_links,orphans,disconnected,low_connectivity\")\n .action(async (opts: { scope?: string; project?: string; days?: string; include?: string }) => {\n try {\n await cmdHealth(opts);\n } catch (e) {\n console.error(err(` Error: ${e}`));\n process.exit(1);\n }\n });\n}\n","/** zettel surprise: find semantically similar but graph-distant notes. */\n\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { warn, err, dim, bold, header } from \"../../utils.js\";\nimport { getFedDb, shortPath } from \"./utils.js\";\n\nasync function cmdSurprise(\n note: string,\n opts: { vaultProjectId?: string; limit?: string; minSimilarity?: string; minDistance?: string }\n): Promise<void> {\n if (!opts.vaultProjectId) {\n console.error(err(\" --vault-project-id is required\"));\n process.exit(1);\n }\n\n const vaultProjectId = parseInt(opts.vaultProjectId, 10);\n const limit = parseInt(opts.limit ?? \"10\", 10);\n const minSimilarity = parseFloat(opts.minSimilarity ?? \"0.3\");\n const minGraphDistance = parseInt(opts.minDistance ?? \"3\", 10);\n\n const { zettelSurprise } = await import(\"../../../zettelkasten/index.js\");\n const db = getFedDb();\n\n console.log();\n console.log(header(\" PAI Zettel Surprise\"));\n console.log(dim(` Reference note: ${note}`));\n process.stdout.write(dim(\" Searching for surprising connections...\\n\"));\n\n const results = await zettelSurprise(db, {\n referencePath: note,\n vaultProjectId,\n limit,\n minSimilarity,\n minGraphDistance,\n });\n\n if (results.length === 0) {\n console.log(warn(\" No surprising connections found. Try lowering --min-similarity or --min-distance.\"));\n console.log();\n return;\n }\n\n console.log();\n console.log(bold(` Found ${results.length} surprising connection(s):`));\n console.log();\n\n for (let i = 0; i < results.length; i++) {\n const r = results[i];\n const title = r.title ?? shortPath(r.path);\n const surpriseBar = Math.round(r.surpriseScore * 10);\n console.log(` ${chalk.cyan(String(i + 1).padStart(2, \" \"))}. ${bold(title)}`);\n console.log(` ${dim(\"Surprise:\")} ${chalk.magenta(r.surpriseScore.toFixed(3))} ${\"■\".repeat(surpriseBar)}${\"□\".repeat(10 - surpriseBar)}`);\n console.log(` ${dim(\"Cosine:\")} ${r.cosineSimilarity.toFixed(3)} ${dim(\"Graph distance:\")} ${r.graphDistance}`);\n if (r.sharedSnippet) {\n console.log(` ${dim(\"Context:\")} ${r.sharedSnippet.slice(0, 120)}`);\n }\n console.log();\n }\n}\n\nexport function registerSurpriseCommand(parent: Command): void {\n parent\n .command(\"surprise <note>\")\n .description(\"Find semantically similar but graph-distant notes (surprising connections)\")\n .requiredOption(\"--vault-project-id <n>\", \"Project ID for the vault in the federation DB\")\n .option(\"--limit <n>\", \"Maximum results\", \"10\")\n .option(\"--min-similarity <f>\", \"Minimum cosine similarity (0–1)\", \"0.3\")\n .option(\"--min-distance <n>\", \"Minimum graph distance\", \"3\")\n .action(async (note: string, opts: { vaultProjectId?: string; limit?: string; minSimilarity?: string; minDistance?: string }) => {\n try {\n await cmdSurprise(note, opts);\n } catch (e) {\n console.error(err(` Error: ${e}`));\n process.exit(1);\n }\n });\n}\n","/** zettel suggest: suggest new wikilink connections for a note. */\n\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { warn, err, dim, bold, header } from \"../../utils.js\";\nimport { getFedDb, shortPath } from \"./utils.js\";\n\nasync function cmdSuggest(\n note: string,\n opts: { vaultProjectId?: string; limit?: string; excludeLinked?: boolean }\n): Promise<void> {\n if (!opts.vaultProjectId) {\n console.error(err(\" --vault-project-id is required\"));\n process.exit(1);\n }\n\n const vaultProjectId = parseInt(opts.vaultProjectId, 10);\n const limit = parseInt(opts.limit ?? \"5\", 10);\n const excludeLinked = opts.excludeLinked !== false;\n\n const { zettelSuggest } = await import(\"../../../zettelkasten/index.js\");\n const db = getFedDb();\n\n console.log();\n console.log(header(\" PAI Zettel Suggest\"));\n console.log(dim(` Note: ${note}`));\n process.stdout.write(dim(\" Computing suggestions...\\n\"));\n\n const suggestions = await zettelSuggest(db, {\n notePath: note,\n vaultProjectId,\n limit,\n excludeLinked,\n });\n\n if (suggestions.length === 0) {\n console.log(warn(\" No suggestions found. The note may be well-connected already.\"));\n console.log();\n return;\n }\n\n console.log();\n console.log(bold(` ${suggestions.length} suggested connection(s):`));\n console.log();\n\n for (let i = 0; i < suggestions.length; i++) {\n const s = suggestions[i];\n const title = s.title ?? shortPath(s.path);\n console.log(` ${chalk.green(String(i + 1).padStart(2, \" \"))}. ${bold(title)}`);\n console.log(` ${dim(\"Score:\")} ${chalk.green(s.score.toFixed(3))} ${dim(\"Semantic:\")} ${s.semanticScore.toFixed(2)} ${dim(\"Tag:\")} ${s.tagScore.toFixed(2)} ${dim(\"Neighbor:\")} ${s.neighborScore.toFixed(2)}`);\n console.log(` ${dim(\"Reason:\")} ${s.reason}`);\n console.log(` ${dim(\"Wikilink:\")} ${chalk.cyan(s.suggestedWikilink)}`);\n console.log();\n }\n}\n\nexport function registerSuggestCommand(parent: Command): void {\n parent\n .command(\"suggest <note>\")\n .description(\"Suggest new wikilink connections for a note\")\n .requiredOption(\"--vault-project-id <n>\", \"Project ID for the vault in the federation DB\")\n .option(\"--limit <n>\", \"Maximum suggestions\", \"5\")\n .option(\"--no-exclude-linked\", \"Include notes already linked from this one\")\n .action(async (note: string, opts: { vaultProjectId?: string; limit?: string; excludeLinked?: boolean }) => {\n try {\n await cmdSuggest(note, opts);\n } catch (e) {\n console.error(err(` Error: ${e}`));\n process.exit(1);\n }\n });\n}\n","/** zettel converse: ask the vault a question and get a synthesis prompt. */\n\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { warn, err, dim, bold, header } from \"../../utils.js\";\nimport { getFedDb, shortPath } from \"./utils.js\";\n\nasync function cmdConverse(\n question: string,\n opts: { vaultProjectId?: string; depth?: string; limit?: string }\n): Promise<void> {\n if (!opts.vaultProjectId) {\n console.error(err(\" --vault-project-id is required\"));\n process.exit(1);\n }\n\n const vaultProjectId = parseInt(opts.vaultProjectId, 10);\n const depth = parseInt(opts.depth ?? \"2\", 10);\n const limit = parseInt(opts.limit ?? \"15\", 10);\n\n const { zettelConverse } = await import(\"../../../zettelkasten/index.js\");\n const db = getFedDb();\n\n console.log();\n console.log(header(\" PAI Zettel Converse\"));\n console.log(dim(` Question: \"${question}\"`));\n process.stdout.write(dim(\" Searching vault for relevant notes...\\n\"));\n\n const result = await zettelConverse(db, { question, vaultProjectId, depth, limit });\n\n if (result.relevantNotes.length === 0) {\n console.log(warn(\" No relevant notes found. Try rephrasing your question.\"));\n console.log();\n return;\n }\n\n console.log();\n console.log(bold(` ${result.relevantNotes.length} relevant note(s) from ${result.domains.length} domain(s):`));\n console.log(dim(` Domains: ${result.domains.join(\", \")}`));\n console.log();\n\n for (const note of result.relevantNotes) {\n const title = note.title ?? shortPath(note.path);\n console.log(` ${chalk.cyan(\"◆\")} ${bold(title)} ${dim(`[${note.domain}] score: ${note.score.toFixed(3)}`)}`);\n if (note.snippet) {\n console.log(` ${dim(note.snippet.slice(0, 200))}`);\n }\n console.log();\n }\n\n if (result.connections.length > 0) {\n console.log(bold(\" Cross-domain connections:\"));\n for (const conn of result.connections.slice(0, 10)) {\n console.log(\n ` ${chalk.magenta(\"⟷\")} ${dim(conn.fromDomain)} ${chalk.dim(\"→\")} ${dim(conn.toDomain)} ` +\n `${dim(shortPath(conn.fromPath))} → ${dim(shortPath(conn.toPath))} ` +\n `${dim(`strength: ${conn.strength}`)}`\n );\n }\n console.log();\n }\n\n console.log(bold(\" Synthesis prompt (paste into your AI):\"));\n console.log();\n const promptLines = result.synthesisPrompt.split(\"\\n\");\n for (const line of promptLines) {\n console.log(` ${dim(line)}`);\n }\n console.log();\n}\n\nexport function registerConverseCommand(parent: Command): void {\n parent\n .command(\"converse <question>\")\n .description(\"Ask the vault a question and get a synthesis prompt with relevant notes\")\n .requiredOption(\"--vault-project-id <n>\", \"Project ID for the vault in the federation DB\")\n .option(\"--depth <n>\", \"Graph expansion depth around matched notes\", \"2\")\n .option(\"--limit <n>\", \"Maximum relevant notes to include\", \"15\")\n .action(async (question: string, opts: { vaultProjectId?: string; depth?: string; limit?: string }) => {\n try {\n await cmdConverse(question, opts);\n } catch (e) {\n console.error(err(` Error: ${e}`));\n process.exit(1);\n }\n });\n}\n","/** zettel themes: detect emerging theme clusters in recently edited notes. */\n\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { warn, err, dim, bold, header } from \"../../utils.js\";\nimport { getFedDb, shortPath } from \"./utils.js\";\n\nasync function cmdThemes(opts: {\n vaultProjectId?: string;\n days?: string;\n minSize?: string;\n maxThemes?: string;\n threshold?: string;\n}): Promise<void> {\n if (!opts.vaultProjectId) {\n console.error(err(\" --vault-project-id is required\"));\n process.exit(1);\n }\n\n const vaultProjectId = parseInt(opts.vaultProjectId, 10);\n const lookbackDays = parseInt(opts.days ?? \"30\", 10);\n const minClusterSize = parseInt(opts.minSize ?? \"3\", 10);\n const maxThemes = parseInt(opts.maxThemes ?? \"10\", 10);\n const similarityThreshold = parseFloat(opts.threshold ?? \"0.65\");\n\n const { zettelThemes } = await import(\"../../../zettelkasten/index.js\");\n const db = getFedDb();\n\n console.log();\n console.log(header(\" PAI Zettel Themes\"));\n console.log(dim(` Lookback: ${lookbackDays}d Min cluster: ${minClusterSize} Threshold: ${similarityThreshold}`));\n process.stdout.write(dim(\" Detecting emerging themes...\\n\"));\n\n const result = await zettelThemes(db, {\n vaultProjectId,\n lookbackDays,\n minClusterSize,\n maxThemes,\n similarityThreshold,\n });\n\n if (result.themes.length === 0) {\n console.log(warn(` No themes detected in the last ${lookbackDays} days. Try --days with a larger window.`));\n console.log();\n return;\n }\n\n const fromDate = new Date(result.timeWindow.from).toISOString().slice(0, 10);\n const toDate = new Date(result.timeWindow.to).toISOString().slice(0, 10);\n\n console.log();\n console.log(bold(` ${result.themes.length} theme(s) from ${result.totalNotesAnalyzed} notes [${fromDate} → ${toDate}]:`));\n console.log();\n\n for (let i = 0; i < result.themes.length; i++) {\n const cluster = result.themes[i];\n const diversityBar = Math.round(cluster.folderDiversity * 10);\n const indexSuggestion = cluster.suggestIndexNote ? chalk.yellow(\" ⚑ suggest index note\") : \"\";\n\n console.log(` ${chalk.cyan(String(i + 1).padStart(2, \" \"))}. ${bold(cluster.label)}${indexSuggestion}`);\n console.log(\n ` ${dim(\"Notes:\")} ${cluster.size} ` +\n `${dim(\"Diversity:\")} ${\"█\".repeat(diversityBar)}${\"░\".repeat(10 - diversityBar)} ${cluster.folderDiversity.toFixed(2)} ` +\n `${dim(\"Linked:\")} ${Math.round(cluster.linkedRatio * 100)}%`\n );\n\n const preview = cluster.notes.slice(0, 5);\n for (const note of preview) {\n const title = note.title ?? shortPath(note.path);\n console.log(` ${dim(\"•\")} ${title}`);\n }\n if (cluster.notes.length > 5) {\n console.log(dim(` ... and ${cluster.notes.length - 5} more`));\n }\n console.log();\n }\n}\n\nexport function registerThemesCommand(parent: Command): void {\n parent\n .command(\"themes\")\n .description(\"Detect emerging theme clusters in recently edited notes\")\n .requiredOption(\"--vault-project-id <n>\", \"Project ID for the vault in the federation DB\")\n .option(\"--days <n>\", \"Look-back window in days\", \"30\")\n .option(\"--min-size <n>\", \"Minimum notes per cluster\", \"3\")\n .option(\"--max-themes <n>\", \"Maximum themes to return\", \"10\")\n .option(\"--threshold <f>\", \"Similarity threshold for clustering (0–1)\", \"0.65\")\n .action(async (opts: { vaultProjectId?: string; days?: string; minSize?: string; maxThemes?: string; threshold?: string }) => {\n try {\n await cmdThemes(opts);\n } catch (e) {\n console.error(err(` Error: ${e}`));\n process.exit(1);\n }\n });\n}\n","/** Barrel: registers all zettel sub-commands and exports registerZettelCommands. */\n\nimport type { Command } from \"commander\";\nimport type { Database } from \"better-sqlite3\";\nimport { registerExploreCommand } from \"./explore.js\";\nimport { registerHealthCommand } from \"./health.js\";\nimport { registerSurpriseCommand } from \"./surprise.js\";\nimport { registerSuggestCommand } from \"./suggest.js\";\nimport { registerConverseCommand } from \"./converse.js\";\nimport { registerThemesCommand } from \"./themes.js\";\n\nexport function registerZettelCommands(\n parent: Command,\n _getDb: () => Database // registry DB (unused — federation DB is opened directly)\n): void {\n registerExploreCommand(parent);\n registerHealthCommand(parent);\n registerSurpriseCommand(parent);\n registerSuggestCommand(parent);\n registerConverseCommand(parent);\n registerThemesCommand(parent);\n}\n","/**\n * pai observation <sub-command>\n *\n * list — List recent observations with filtering options\n * search — Search observations by title/narrative\n * stats — Show observation statistics\n */\n\nimport type { Command } from \"commander\";\nimport { createConnection } from \"net\";\nimport { ok, warn, err, dim, bold, header } from \"../utils.js\";\nimport chalk from \"chalk\";\n\n// ---------------------------------------------------------------------------\n// IPC helper — communicates with PAI daemon via Unix socket\n// ---------------------------------------------------------------------------\n\nfunction ipcCall(method: string, params: Record<string, unknown>): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let settled = false;\n const settle = (fn: () => void) => { if (!settled) { settled = true; fn(); } };\n\n const client = createConnection(\"/tmp/pai.sock\", () => {\n client.write(JSON.stringify({ id: 1, method, params }) + \"\\n\");\n });\n let data = \"\";\n client.on(\"data\", (chunk) => {\n data += chunk.toString();\n // Daemon sends a single JSON line then closes — try parsing eagerly\n try {\n const resp = JSON.parse(data) as { ok: boolean; result: unknown; error?: string };\n client.end();\n settle(() => resp.ok ? resolve(resp.result) : reject(new Error(resp.error ?? \"IPC call failed\")));\n } catch {\n // Incomplete — wait for more data or end\n }\n });\n client.on(\"end\", () => {\n settle(() => {\n try {\n const resp = JSON.parse(data) as { ok: boolean; result: unknown; error?: string };\n resp.ok ? resolve(resp.result) : reject(new Error(resp.error ?? \"IPC call failed\"));\n } catch (e) { reject(e); }\n });\n });\n client.on(\"error\", (e) => settle(() => reject(e)));\n setTimeout(() => { client.destroy(); settle(() => reject(new Error(\"IPC timeout\"))); }, 10000);\n });\n}\n\n// ---------------------------------------------------------------------------\n// Type colour mapping\n// ---------------------------------------------------------------------------\n\nfunction typeColor(type: string): string {\n switch (type) {\n case \"decision\": return chalk.cyan(type);\n case \"bugfix\": return chalk.red(type);\n case \"feature\": return chalk.green(type);\n case \"refactor\": return chalk.yellow(type);\n case \"discovery\": return chalk.blue(type);\n case \"change\": return chalk.magenta(type);\n default: return chalk.white(type);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Date formatting helper\n// ---------------------------------------------------------------------------\n\nfunction fmtTs(ts: string | null | undefined): string {\n if (!ts) return dim(\"—\");\n try {\n const d = new Date(ts);\n const date = d.toISOString().slice(0, 10);\n const time = d.toISOString().slice(11, 16);\n return `${date} ${time}`;\n } catch {\n return dim(\"—\");\n }\n}\n\n// ---------------------------------------------------------------------------\n// Truncate a string to maxLen visible characters\n// ---------------------------------------------------------------------------\n\nfunction trunc(s: string, maxLen: number): string {\n if (!s) return \"\";\n if (s.length <= maxLen) return s;\n return s.slice(0, maxLen - 1) + \"…\";\n}\n\n// ---------------------------------------------------------------------------\n// Observation shape (from IPC responses)\n// ---------------------------------------------------------------------------\n\ninterface Observation {\n id: number;\n type: string;\n title: string;\n project_slug?: string | null;\n session_id?: string | null;\n created_at: string;\n narrative?: string | null;\n}\n\ninterface ObservationStats {\n total: number;\n by_type: Array<{ type: string; count: number }>;\n by_project: Array<{ project_slug: string | null; count: number }>;\n most_recent: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Command: pai observation list\n// ---------------------------------------------------------------------------\n\nasync function cmdList(opts: {\n project?: string;\n type?: string;\n session?: string;\n limit?: string;\n}): Promise<void> {\n const limit = parseInt(opts.limit ?? \"20\", 10);\n\n const params: Record<string, unknown> = { limit };\n if (opts.project) params.project_slug = opts.project;\n if (opts.type) params.type = opts.type;\n if (opts.session) params.session_id = opts.session;\n\n let observations: Observation[];\n try {\n observations = (await ipcCall(\"observation_list\", params)) as Observation[];\n } catch (e) {\n console.error(err(` Failed to reach PAI daemon: ${e}`));\n console.error(dim(\" Is the daemon running? Try: pai daemon status\"));\n process.exit(1);\n }\n\n console.log();\n console.log(header(\" PAI Observations\"));\n\n const filterParts: string[] = [];\n if (opts.project) filterParts.push(`project: ${opts.project}`);\n if (opts.type) filterParts.push(`type: ${opts.type}`);\n if (opts.session) filterParts.push(`session: ${opts.session}`);\n filterParts.push(`limit: ${limit}`);\n console.log(dim(` ${filterParts.join(\" | \")}`));\n console.log();\n\n if (!observations || observations.length === 0) {\n console.log(warn(\" No observations found.\"));\n console.log();\n return;\n }\n\n // Column widths\n const ID_W = 4;\n const TYPE_W = 10;\n const TITLE_W = 42;\n const PROJ_W = 14;\n const TS_W = 16;\n\n // Header row\n console.log(\n \" \" +\n bold(\"id\".padEnd(ID_W)) + \" \" +\n bold(\"type\".padEnd(TYPE_W)) + \" \" +\n bold(\"title\".padEnd(TITLE_W)) + \" \" +\n bold(\"project\".padEnd(PROJ_W)) + \" \" +\n bold(\"created_at\")\n );\n console.log(\n dim(\n \" \" +\n \"-\".repeat(ID_W) + \" \" +\n \"-\".repeat(TYPE_W) + \" \" +\n \"-\".repeat(TITLE_W) + \" \" +\n \"-\".repeat(PROJ_W) + \" \" +\n \"-\".repeat(TS_W)\n )\n );\n\n for (const obs of observations) {\n const idStr = String(obs.id).padStart(ID_W, \" \");\n const typeStr = typeColor(obs.type ?? \"\").padEnd(TYPE_W + (typeColor(obs.type ?? \"\").length - (obs.type ?? \"\").length));\n const titleStr = trunc(obs.title ?? \"\", TITLE_W).padEnd(TITLE_W);\n const projStr = trunc(obs.project_slug ?? \"—\", PROJ_W).padEnd(PROJ_W);\n const tsStr = fmtTs(obs.created_at);\n\n console.log(` ${idStr} ${typeStr} ${titleStr} ${projStr} ${dim(tsStr)}`);\n }\n\n console.log();\n console.log(dim(` ${observations.length} observation(s)`));\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// Command: pai observation search <query>\n// ---------------------------------------------------------------------------\n\nasync function cmdSearch(\n query: string,\n opts: { project?: string; type?: string; limit?: string }\n): Promise<void> {\n const limit = parseInt(opts.limit ?? \"20\", 10);\n\n const params: Record<string, unknown> = { query, limit };\n if (opts.project) params.project_slug = opts.project;\n if (opts.type) params.type = opts.type;\n\n let allObservations: Observation[];\n try {\n // Fetch more than requested to allow client-side text filtering\n allObservations = (await ipcCall(\"observation_query\", { ...params, limit: limit * 5, query: undefined })) as Observation[];\n } catch (e) {\n console.error(err(` Failed to reach PAI daemon: ${e}`));\n console.error(dim(\" Is the daemon running? Try: pai daemon status\"));\n process.exit(1);\n }\n\n // Client-side text filter (observation_query doesn't support text search)\n const q = query.toLowerCase();\n const observations = allObservations\n .filter(o =>\n (o.title ?? \"\").toLowerCase().includes(q) ||\n (o.narrative ?? \"\").toLowerCase().includes(q) ||\n (o.tool_input_summary ?? \"\").toLowerCase().includes(q)\n )\n .slice(0, limit);\n\n console.log();\n console.log(header(\" PAI Observation Search\"));\n console.log(dim(` Query: \"${query}\"${opts.project ? ` project: ${opts.project}` : \"\"}${opts.type ? ` type: ${opts.type}` : \"\"} limit: ${limit}`));\n console.log();\n\n if (!observations || observations.length === 0) {\n console.log(warn(` No observations matching \"${query}\".`));\n console.log();\n return;\n }\n\n for (let i = 0; i < observations.length; i++) {\n const obs = observations[i];\n const idx = chalk.dim(String(i + 1).padStart(3, \" \"));\n const type = typeColor(obs.type ?? \"\");\n const title = bold(obs.title ?? \"(untitled)\");\n const proj = obs.project_slug ? dim(`[${obs.project_slug}]`) : dim(\"[—]\");\n const ts = dim(fmtTs(obs.created_at));\n const id = dim(`#${obs.id}`);\n\n console.log(` ${idx} ${type.padEnd(12)} ${title}`);\n console.log(` ${proj} ${ts} ${id}`);\n\n if (obs.narrative) {\n const snippet = trunc(obs.narrative.replace(/\\n/g, \" \"), 160);\n console.log(` ${dim(snippet)}`);\n }\n console.log();\n }\n\n console.log(dim(` ${observations.length} result(s)`));\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// Command: pai observation stats\n// ---------------------------------------------------------------------------\n\nasync function cmdStats(): Promise<void> {\n let stats: ObservationStats;\n try {\n stats = (await ipcCall(\"observation_stats\", {})) as ObservationStats;\n } catch (e) {\n console.error(err(` Failed to reach PAI daemon: ${e}`));\n console.error(dim(\" Is the daemon running? Try: pai daemon status\"));\n process.exit(1);\n }\n\n console.log();\n console.log(header(\" PAI Observation Statistics\"));\n console.log();\n\n console.log(` ${bold(\"Total observations:\")} ${chalk.cyan(String(stats.total ?? 0))}`);\n if (stats.most_recent) {\n console.log(` ${bold(\"Most recent:\")} ${dim(fmtTs(stats.most_recent))}`);\n }\n console.log();\n\n // By type\n if (stats.by_type && stats.by_type.length > 0) {\n console.log(bold(\" By type:\"));\n const maxCount = Math.max(...stats.by_type.map((r) => r.count));\n for (const row of stats.by_type) {\n const barWidth = 20;\n const filled = Math.round((row.count / maxCount) * barWidth);\n const bar = chalk.cyan(\"█\".repeat(filled)) + dim(\"░\".repeat(barWidth - filled));\n const label = typeColor(row.type).padEnd(12 + (typeColor(row.type).length - row.type.length));\n console.log(` ${label} ${bar} ${String(row.count).padStart(5)}`);\n }\n console.log();\n }\n\n // By project\n if (stats.by_project && stats.by_project.length > 0) {\n console.log(bold(\" By project:\"));\n const maxCount = Math.max(...stats.by_project.map((r) => r.count));\n const show = stats.by_project.slice(0, 15);\n for (const row of show) {\n const barWidth = 20;\n const filled = Math.round((row.count / maxCount) * barWidth);\n const bar = chalk.green(\"█\".repeat(filled)) + dim(\"░\".repeat(barWidth - filled));\n const label = (row.project_slug ?? \"—\").padEnd(20);\n console.log(` ${dim(label)} ${bar} ${String(row.count).padStart(5)}`);\n }\n if (stats.by_project.length > 15) {\n console.log(dim(` ... and ${stats.by_project.length - 15} more project(s)`));\n }\n console.log();\n }\n\n if ((!stats.by_type || stats.by_type.length === 0) && (!stats.by_project || stats.by_project.length === 0)) {\n console.log(warn(\" No observation data yet.\"));\n console.log();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerObservationCommands(parent: Command): void {\n // pai observation list\n parent\n .command(\"list\")\n .description(\"List recent observations\")\n .option(\"--project <slug>\", \"Filter by project slug\")\n .option(\"--type <type>\", \"Filter by type (decision, bugfix, feature, refactor, discovery, change)\")\n .option(\"--session <id>\", \"Filter by session ID\")\n .option(\"--limit <n>\", \"Maximum results\", \"20\")\n .action(async (opts: { project?: string; type?: string; session?: string; limit?: string }) => {\n try {\n await cmdList(opts);\n } catch (e) {\n console.error(err(` Error: ${e}`));\n process.exit(1);\n }\n });\n\n // pai observation search <query>\n parent\n .command(\"search <query>\")\n .description(\"Search observations by title or narrative text\")\n .option(\"--project <slug>\", \"Filter by project slug\")\n .option(\"--type <type>\", \"Filter by type\")\n .option(\"--limit <n>\", \"Maximum results\", \"20\")\n .action(async (query: string, opts: { project?: string; type?: string; limit?: string }) => {\n try {\n await cmdSearch(query, opts);\n } catch (e) {\n console.error(err(` Error: ${e}`));\n process.exit(1);\n }\n });\n\n // pai observation stats\n parent\n .command(\"stats\")\n .description(\"Show observation statistics: totals, by type, by project\")\n .action(async () => {\n try {\n await cmdStats();\n } catch (e) {\n console.error(err(` Error: ${e}`));\n process.exit(1);\n }\n });\n}\n","/**\n * pai update — Update PAI from the GitHub repository without losing customizations.\n *\n * Steps:\n * 1. Verify we're in a git repo with a remote (origin)\n * 2. Stash any local changes\n * 3. Pull latest from origin/main\n * 4. Pop the stash if there was one (handle conflicts gracefully)\n * 5. Rebuild: bun install && bun run build\n * 6. Restart the daemon (SIGHUP or pai daemon restart)\n * 7. Check if the CLAUDE.md template has changed and offer to refresh\n * 8. Run pai registry scan to update markers\n * 9. Report what changed\n */\n\nimport type { Command } from \"commander\";\nimport {\n existsSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { execSync, spawnSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport { ok, warn, err, dim, bold } from \"../utils.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction line(text = \"\") {\n console.log(text);\n}\n\nfunction step(msg: string) {\n console.log(chalk.bold.cyan(`\\n ${msg}`));\n}\n\nfunction info(msg: string) {\n console.log(dim(` ${msg}`));\n}\n\nfunction success(msg: string) {\n console.log(ok(` ${msg}`));\n}\n\nfunction warning(msg: string) {\n console.log(warn(` ${msg}`));\n}\n\nfunction error(msg: string) {\n console.log(err(` ${msg}`));\n}\n\n/**\n * Run a shell command, streaming output to stdout.\n * Returns true on success, false on failure.\n */\nfunction run(cmd: string, cwd?: string): boolean {\n try {\n execSync(cmd, {\n stdio: \"inherit\",\n cwd: cwd ?? process.cwd(),\n env: process.env,\n });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Run a shell command silently, returning stdout or null on failure.\n */\nfunction capture(cmd: string, cwd?: string): string | null {\n try {\n return execSync(cmd, {\n stdio: \"pipe\",\n cwd: cwd ?? process.cwd(),\n env: process.env,\n }).toString().trim();\n } catch {\n return null;\n }\n}\n\n/**\n * Get the PAI source directory: walk up from dist/cli/ or src/cli/ to the\n * package root where package.json lives.\n */\nfunction getPaiSrcDir(): string {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n // dist/cli/index.mjs → dist/ → package root\n return join(__dirname, \"../..\");\n}\n\n/**\n * Locate the templates directory relative to the installed package.\n */\nfunction getTemplatesDir(): string {\n const candidates = [\n join(getPaiSrcDir(), \"templates\"),\n join(homedir(), \"dev\", \"ai\", \"PAI\", \"templates\"),\n join(\"/\", \"usr\", \"local\", \"lib\", \"node_modules\", \"@mnott\", \"pai\", \"templates\"),\n ];\n for (const c of candidates) {\n if (existsSync(join(c, \"claude-md.template.md\"))) return c;\n }\n return join(getPaiSrcDir(), \"templates\");\n}\n\n// ---------------------------------------------------------------------------\n// Update steps\n// ---------------------------------------------------------------------------\n\n/**\n * Step 1: Verify git repo + remote.\n * Returns the PAI source directory (repo root) or null if not a git repo.\n */\nfunction stepVerifyRepo(): string | null {\n step(\"Checking repository...\");\n\n const paiDir = getPaiSrcDir();\n\n // Check git repo\n const gitDir = capture(\"git rev-parse --show-toplevel\", paiDir);\n if (!gitDir) {\n error(\"Not a git repository. Cannot update automatically.\");\n info(\"Install PAI via npm to get automatic updates: npm install -g @mnott/pai\");\n return null;\n }\n\n // Check remote\n const remote = capture(\"git remote get-url origin\", gitDir);\n if (!remote) {\n error(\"No 'origin' remote configured. Cannot pull updates.\");\n info(\"Add a remote: git remote add origin https://github.com/mnott/PAI.git\");\n return null;\n }\n\n info(`Repository: ${gitDir}`);\n info(`Remote: ${remote}`);\n return gitDir;\n}\n\n/**\n * Step 2: Stash local changes.\n * Returns true if something was stashed (so we need to pop later).\n */\nfunction stepStash(repoDir: string): boolean {\n step(\"Stashing local changes...\");\n\n // Check if there are any changes to stash\n const status = capture(\"git status --porcelain\", repoDir);\n if (!status) {\n info(\"Working tree is clean — nothing to stash.\");\n return false;\n }\n\n info(\"Uncommitted changes found:\");\n const statusLines = status.split(\"\\n\").slice(0, 5);\n for (const l of statusLines) {\n info(` ${l}`);\n }\n if (status.split(\"\\n\").length > 5) {\n info(` ... and ${status.split(\"\\n\").length - 5} more`);\n }\n\n const result = capture(\"git stash push -m 'pai update: auto-stash'\", repoDir);\n if (result === null) {\n warning(\"Could not stash changes. Proceeding anyway — merge conflicts may occur.\");\n return false;\n }\n\n if (result.includes(\"No local changes to save\")) {\n info(\"Nothing to stash.\");\n return false;\n }\n\n success(\"Changes stashed.\");\n return true;\n}\n\n/**\n * Step 3: Pull latest from origin/main.\n * Returns the git log summary of new commits or null if already up to date.\n */\nfunction stepPull(repoDir: string): string | null {\n step(\"Pulling latest from origin/main...\");\n\n // Get current HEAD before pull\n const headBefore = capture(\"git rev-parse HEAD\", repoDir);\n\n const pulled = run(\"git pull origin main\", repoDir);\n if (!pulled) {\n warning(\"git pull failed. There may be merge conflicts with your stash.\");\n return null;\n }\n\n // Get HEAD after pull\n const headAfter = capture(\"git rev-parse HEAD\", repoDir);\n\n if (headBefore === headAfter) {\n success(\"Already up to date — no new commits.\");\n return null;\n }\n\n // Show what changed\n const log = capture(\n `git log --oneline ${headBefore ?? \"\"}..${headAfter ?? \"HEAD\"}`,\n repoDir,\n );\n if (log) {\n success(\"New commits pulled:\");\n for (const l of log.split(\"\\n\")) {\n info(` ${l}`);\n }\n }\n\n return log;\n}\n\n/**\n * Step 4: Pop stash (if we stashed anything).\n */\nfunction stepPopStash(repoDir: string): void {\n step(\"Restoring local changes (git stash pop)...\");\n\n const result = spawnSync(\"git\", [\"stash\", \"pop\"], {\n cwd: repoDir,\n stdio: \"pipe\",\n encoding: \"utf8\",\n });\n\n if (result.status === 0) {\n success(\"Local changes restored.\");\n } else {\n const stderr = result.stderr ?? \"\";\n if (stderr.includes(\"CONFLICT\")) {\n warning(\"Stash pop caused merge conflicts. Resolve them manually:\");\n info(\" git status\");\n info(\" # Edit conflicting files\");\n info(\" git add <files>\");\n info(\" git stash drop\");\n } else {\n warning(`Stash pop encountered an issue: ${stderr.trim() || \"unknown error\"}`);\n info(\" Run: git stash pop\");\n info(\" Or: git stash list (to see stashed changes)\");\n }\n }\n}\n\n/**\n * Step 5: Rebuild.\n */\nfunction stepBuild(repoDir: string): boolean {\n step(\"Rebuilding PAI (bun install && bun run build)...\");\n\n info(\"Installing dependencies...\");\n const installed = run(\"bun install\", repoDir);\n if (!installed) {\n error(\"bun install failed.\");\n return false;\n }\n\n info(\"Building...\");\n const built = run(\"bun run build\", repoDir);\n if (!built) {\n error(\"bun run build failed.\");\n return false;\n }\n\n success(\"Build complete.\");\n return true;\n}\n\n/**\n * Step 6: Restart the daemon.\n * Tries SIGHUP first (graceful reload); falls back to pai daemon restart.\n */\nfunction stepRestartDaemon(repoDir: string): void {\n step(\"Restarting PAI daemon...\");\n\n // Try to find the daemon PID from the socket/pid file\n const pidFile = \"/tmp/pai-daemon.pid\";\n if (existsSync(pidFile)) {\n try {\n const pid = parseInt(readFileSync(pidFile, \"utf8\").trim(), 10);\n if (!isNaN(pid) && pid > 0) {\n const killed = capture(`kill -HUP ${pid}`);\n if (killed !== null) {\n success(`Sent SIGHUP to daemon (PID ${pid}).`);\n return;\n }\n }\n } catch {\n // fall through to pai daemon restart\n }\n }\n\n // Fall back to pai daemon restart subcommand\n const result = spawnSync(\"pai\", [\"daemon\", \"restart\"], {\n cwd: repoDir,\n stdio: \"pipe\",\n encoding: \"utf8\",\n timeout: 10_000,\n });\n\n if (result.status === 0) {\n success(\"Daemon restarted.\");\n } else {\n warning(\"Could not restart daemon automatically.\");\n info(\" Restart manually: pai daemon restart\");\n info(\" Or: pai daemon serve\");\n }\n}\n\n/**\n * Step 7: Check if CLAUDE.md template changed and offer to refresh.\n */\nfunction stepRefreshClaudeMd(repoDir: string, newCommitsLog: string | null): void {\n step(\"Checking CLAUDE.md template...\");\n\n const templatesDir = getTemplatesDir();\n const templatePath = join(templatesDir, \"claude-md.template.md\");\n const claudeMd = join(homedir(), \".claude\", \"CLAUDE.md\");\n\n if (!existsSync(templatePath)) {\n info(\"Template not found — skipping CLAUDE.md check.\");\n return;\n }\n\n if (!existsSync(claudeMd)) {\n info(\"No ~/.claude/CLAUDE.md found — skipping.\");\n return;\n }\n\n const userContent = readFileSync(claudeMd, \"utf8\");\n const isPaiGenerated = userContent.includes(\"Generated by PAI Setup\");\n\n if (!isPaiGenerated) {\n warning(\"~/.claude/CLAUDE.md appears to be custom (not PAI-generated).\");\n info(\" Skipping auto-update. Review the new template manually:\");\n info(` ${templatePath}`);\n return;\n }\n\n // Check if the template was actually modified in the new commits\n let templateChanged = false;\n if (newCommitsLog) {\n // Check git diff on the template file for newly pulled commits\n const diff = capture(\n \"git diff HEAD~1..HEAD -- templates/claude-md.template.md\",\n repoDir,\n );\n templateChanged = !!(diff && diff.trim().length > 0);\n }\n\n if (!templateChanged) {\n info(\"CLAUDE.md template unchanged in this update.\");\n return;\n }\n\n info(\"CLAUDE.md template was updated in this release.\");\n info(\"Refreshing ~/.claude/CLAUDE.md (PAI-generated — safe to overwrite)...\");\n\n let template = readFileSync(templatePath, \"utf8\");\n // Substitute ${HOME} with actual home directory\n template = template.replace(/\\$\\{HOME\\}/g, homedir());\n writeFileSync(claudeMd, template, \"utf8\");\n\n success(\"~/.claude/CLAUDE.md refreshed from updated template.\");\n}\n\n/**\n * Step 8: Run pai registry scan.\n */\nfunction stepRegistryScan(repoDir: string): void {\n step(\"Running pai registry scan...\");\n\n const result = spawnSync(\"pai\", [\"registry\", \"scan\"], {\n cwd: repoDir,\n stdio: \"inherit\",\n timeout: 60_000,\n });\n\n if (result.status === 0) {\n success(\"Registry scan complete.\");\n } else {\n warning(\"Registry scan encountered issues.\");\n info(\" Run manually: pai registry scan\");\n }\n}\n\n// ---------------------------------------------------------------------------\n// Main update action\n// ---------------------------------------------------------------------------\n\nasync function runUpdate(): Promise<void> {\n line();\n console.log(chalk.bold.cyan(\" ╔═══════════════════════════════════╗\"));\n console.log(chalk.bold.cyan(\" ║ PAI Knowledge OS — Update ║\"));\n console.log(chalk.bold.cyan(\" ╚═══════════════════════════════════╝\"));\n line();\n\n // Step 1: Verify repo\n const repoDir = stepVerifyRepo();\n if (!repoDir) {\n line();\n process.exit(1);\n }\n\n // Step 2: Stash local changes\n const didStash = stepStash(repoDir);\n\n // Step 3: Pull\n const newCommitsLog = stepPull(repoDir);\n\n // Step 4: Pop stash (only if we stashed something)\n if (didStash) {\n stepPopStash(repoDir);\n }\n\n // If no new commits and no stash needed, we can short-circuit after\n // confirming things are current. But we still offer rebuild in case the\n // user's build is stale.\n\n // Step 5: Build\n const buildOk = stepBuild(repoDir);\n if (!buildOk) {\n line();\n error(\"Update failed at build step. Check errors above.\");\n line();\n process.exit(1);\n }\n\n // Step 6: Restart daemon\n stepRestartDaemon(repoDir);\n\n // Step 7: CLAUDE.md refresh\n stepRefreshClaudeMd(repoDir, newCommitsLog);\n\n // Step 8: Registry scan\n stepRegistryScan(repoDir);\n\n // Summary\n line();\n console.log(chalk.bold.cyan(\" ─────────────────────────────────────\"));\n if (newCommitsLog) {\n success(\"PAI updated successfully!\");\n } else {\n success(\"PAI is up to date and rebuilt successfully!\");\n }\n console.log(dim(\" Version: \") + chalk.cyan(capture(\"pai --version\", repoDir) ?? \"unknown\"));\n line();\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerUpdateCommand(program: Command): void {\n program\n .command(\"update\")\n .description(\n \"Update PAI from GitHub (git pull + rebuild + daemon restart). Preserves local customizations.\",\n )\n .action(async () => {\n await runUpdate();\n });\n}\n","/**\n * pai notify <sub-command>\n *\n * status — Show current notification mode and active channels\n * get — Alias for status\n * set — Set notification mode or channel/routing config\n * test — Send a test notification through configured channels\n * send — Send a notification (event + message)\n *\n * All sub-commands communicate with the running daemon via IPC.\n */\n\nimport type { Command } from \"commander\";\nimport { ok, warn, err, dim, bold, header } from \"../utils.js\";\nimport { loadConfig } from \"../../daemon/config.js\";\nimport { PaiClient } from \"../../daemon/ipc-client.js\";\nimport type {\n NotificationConfig,\n NotificationMode,\n NotificationEvent,\n} from \"../../notifications/types.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction makeClient(): PaiClient {\n const config = loadConfig();\n return new PaiClient(config.socketPath);\n}\n\nfunction modeColor(mode: NotificationMode): string {\n switch (mode) {\n case \"off\":\n return warn(mode);\n case \"voice\":\n return bold(ok(mode));\n case \"auto\":\n return ok(mode);\n default:\n return ok(mode);\n }\n}\n\nfunction printConfig(config: NotificationConfig, activeChannels: string[]): void {\n console.log();\n console.log(header(\" PAI Notification Config\"));\n console.log();\n console.log(` ${bold(\"Mode:\")} ${modeColor(config.mode)}`);\n console.log();\n console.log(` ${bold(\"Channels:\")}`);\n for (const [ch, cfg] of Object.entries(config.channels)) {\n const c = cfg as { enabled: boolean; url?: string; voiceName?: string };\n const status = c.enabled ? ok(\"enabled\") : dim(\"disabled\");\n let extra = \"\";\n if (ch === \"ntfy\" && c.url) extra = dim(` ${c.url}`);\n if (ch === \"voice\" && c.voiceName) extra = dim(` voice: ${c.voiceName}`);\n console.log(` ${bold(ch.padEnd(12))}${status}${extra}`);\n }\n console.log();\n console.log(` ${bold(\"Active:\")} ${activeChannels.length > 0 ? activeChannels.join(\", \") : dim(\"(none)\")}`);\n console.log();\n console.log(` ${bold(\"Routing:\")} ${dim(\"(auto mode only)\")}`);\n for (const [event, channels] of Object.entries(config.routing)) {\n const ch = (channels as string[]).join(\", \") || dim(\"(none)\");\n console.log(` ${event.padEnd(12)}→ ${ch}`);\n }\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// Command implementations\n// ---------------------------------------------------------------------------\n\nasync function cmdStatus(): Promise<void> {\n const client = makeClient();\n try {\n const { config, activeChannels } = await client.getNotificationConfig();\n printConfig(config, activeChannels);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.log();\n console.log(warn(\" Cannot reach PAI daemon.\"));\n console.log(dim(` ${msg}`));\n console.log(dim(\" Start it with: pai daemon serve\"));\n console.log();\n process.exit(1);\n }\n}\n\nasync function cmdSet(opts: {\n mode?: string;\n ntfyUrl?: string;\n ntfyPriority?: string;\n whatsappRecipient?: string;\n enableChannel?: string[];\n disableChannel?: string[];\n}): Promise<void> {\n const client = makeClient();\n\n // Build the patch object\n const patch: {\n mode?: NotificationMode;\n channels?: Record<string, unknown>;\n } = {};\n\n if (opts.mode) {\n const validModes: NotificationMode[] = [\n \"auto\", \"voice\", \"whatsapp\", \"ntfy\", \"macos\", \"cli\", \"off\",\n ];\n if (!validModes.includes(opts.mode as NotificationMode)) {\n console.error(err(`Invalid mode: ${opts.mode}. Valid: ${validModes.join(\", \")}`));\n process.exit(1);\n }\n patch.mode = opts.mode as NotificationMode;\n }\n\n // Channel enable/disable\n const channels: Record<string, unknown> = {};\n\n if (opts.enableChannel) {\n for (const ch of opts.enableChannel) {\n channels[ch] = { enabled: true };\n }\n }\n\n if (opts.disableChannel) {\n for (const ch of opts.disableChannel) {\n channels[ch] = { enabled: false };\n }\n }\n\n if (opts.ntfyUrl) {\n channels[\"ntfy\"] = {\n ...(channels[\"ntfy\"] as object ?? {}),\n url: opts.ntfyUrl,\n };\n }\n\n if (opts.ntfyPriority) {\n channels[\"ntfy\"] = {\n ...(channels[\"ntfy\"] as object ?? {}),\n priority: opts.ntfyPriority,\n };\n }\n\n if (opts.whatsappRecipient) {\n channels[\"whatsapp\"] = {\n ...(channels[\"whatsapp\"] as object ?? {}),\n recipient: opts.whatsappRecipient,\n };\n }\n\n if (Object.keys(channels).length > 0) {\n patch.channels = channels;\n }\n\n if (!patch.mode && !patch.channels) {\n console.error(err(\"No changes specified. Use --mode, --enable, --disable, etc.\"));\n console.log(dim(\" Example: pai notify set --mode voice\"));\n console.log(dim(\" Example: pai notify set --mode auto --enable macos --disable ntfy\"));\n process.exit(1);\n }\n\n try {\n const { config } = await client.setNotificationConfig(\n patch as Parameters<typeof client.setNotificationConfig>[0]\n );\n console.log();\n console.log(ok(\" Notification config updated.\"));\n console.log(` ${bold(\"Mode:\")} ${modeColor(config.mode)}`);\n console.log();\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(err(` Failed: ${msg}`));\n process.exit(1);\n }\n}\n\nasync function cmdTest(opts: { event?: string }): Promise<void> {\n const client = makeClient();\n const event = (opts.event ?? \"info\") as NotificationEvent;\n\n console.log();\n console.log(dim(` Sending test ${event} notification...`));\n\n try {\n const result = await client.sendNotification({\n event,\n message: `PAI test notification (${event})`,\n title: \"PAI Test\",\n });\n\n if (result.channelsSucceeded.length === 0 && result.channelsAttempted.length > 0) {\n console.log(warn(\" All channels failed.\"));\n } else if (result.channelsAttempted.length === 0) {\n console.log(warn(\" No channels active for this event. Check mode and channel config.\"));\n } else {\n console.log(ok(` Sent to: ${result.channelsSucceeded.join(\", \")}`));\n }\n\n if (result.channelsFailed.length > 0) {\n console.log(warn(` Failed: ${result.channelsFailed.join(\", \")}`));\n }\n\n console.log(dim(` Mode: ${result.mode}`));\n console.log();\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(err(` Failed: ${msg}`));\n process.exit(1);\n }\n}\n\nasync function cmdSend(\n event: string,\n message: string,\n opts: { title?: string }\n): Promise<void> {\n const client = makeClient();\n const validEvents: NotificationEvent[] = [\n \"error\", \"progress\", \"completion\", \"info\", \"debug\",\n ];\n\n if (!validEvents.includes(event as NotificationEvent)) {\n console.error(\n err(`Invalid event: ${event}. Valid: ${validEvents.join(\", \")}`)\n );\n process.exit(1);\n }\n\n try {\n const result = await client.sendNotification({\n event: event as NotificationEvent,\n message,\n title: opts.title,\n });\n\n if (result.channelsSucceeded.length > 0) {\n console.log(ok(` Sent to: ${result.channelsSucceeded.join(\", \")}`));\n } else {\n console.log(warn(\" No channels received the notification.\"));\n }\n\n if (result.channelsFailed.length > 0) {\n console.log(warn(` Failed: ${result.channelsFailed.join(\", \")}`));\n }\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(err(` Failed: ${msg}`));\n process.exit(1);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerNotifyCommands(notifyCmd: Command): void {\n // pai notify status (default / alias for get)\n notifyCmd\n .command(\"status\")\n .description(\"Show current notification mode and active channels\")\n .action(async () => {\n await cmdStatus();\n });\n\n // pai notify get (alias for status)\n notifyCmd\n .command(\"get\")\n .description(\"Show current notification config (alias for status)\")\n .action(async () => {\n await cmdStatus();\n });\n\n // pai notify set\n notifyCmd\n .command(\"set\")\n .description(\"Update notification mode or channel configuration\")\n .option(\n \"--mode <mode>\",\n \"Notification mode: auto | voice | whatsapp | ntfy | macos | cli | off\"\n )\n .option(\"--enable <channel...>\", \"Enable one or more channels\")\n .option(\"--disable <channel...>\", \"Disable one or more channels\")\n .option(\"--ntfy-url <url>\", \"Set the ntfy.sh topic URL\")\n .option(\n \"--ntfy-priority <level>\",\n \"Set ntfy priority: min | low | default | high | urgent\"\n )\n .option(\"--whatsapp-recipient <contact>\", \"Set WhatsApp recipient\")\n .action(\n async (opts: {\n mode?: string;\n enable?: string[];\n disable?: string[];\n ntfyUrl?: string;\n ntfyPriority?: string;\n whatsappRecipient?: string;\n }) => {\n await cmdSet({\n mode: opts.mode,\n enableChannel: opts.enable,\n disableChannel: opts.disable,\n ntfyUrl: opts.ntfyUrl,\n ntfyPriority: opts.ntfyPriority,\n whatsappRecipient: opts.whatsappRecipient,\n });\n }\n );\n\n // pai notify test\n notifyCmd\n .command(\"test\")\n .description(\"Send a test notification through configured channels\")\n .option(\n \"--event <event>\",\n \"Event type to test: error | progress | completion | info | debug\",\n \"info\"\n )\n .action(async (opts: { event?: string }) => {\n await cmdTest(opts);\n });\n\n // pai notify send <event> <message>\n notifyCmd\n .command(\"send <event> <message>\")\n .description(\"Send a notification with an explicit event type and message\")\n .option(\"--title <title>\", \"Optional notification title\")\n .action(async (event: string, message: string, opts: { title?: string }) => {\n await cmdSend(event, message, opts);\n });\n}\n","/**\n * pai topic <sub-command>\n *\n * check <context> — Check whether context text has drifted to a different project\n *\n * Communicates with the running daemon via IPC.\n * The daemon uses BM25 keyword search to match context against indexed memory.\n *\n * Examples:\n * pai topic check \"working on authentication and JWT tokens\"\n * pai topic check \"fixing the React component\" --current myapp\n * pai topic check \"database schema migration\" --threshold 0.7\n */\n\nimport type { Command } from \"commander\";\nimport { ok, warn, err, dim, bold, header } from \"../utils.js\";\nimport { loadConfig } from \"../../daemon/config.js\";\nimport { PaiClient } from \"../../daemon/ipc-client.js\";\nimport type { TopicCheckResult } from \"../../topics/detector.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction makeClient(): PaiClient {\n const config = loadConfig();\n return new PaiClient(config.socketPath);\n}\n\nfunction confidenceBar(confidence: number, width = 20): string {\n const filled = Math.round(confidence * width);\n const empty = width - filled;\n return \"[\" + \"=\".repeat(filled) + \" \".repeat(empty) + \"]\";\n}\n\nfunction printTopicResult(result: TopicCheckResult): void {\n console.log();\n console.log(header(\" PAI Topic Check\"));\n console.log();\n\n console.log(` ${bold(\"Current project:\")} ${result.currentProject ?? dim(\"(none)\")}`);\n console.log(` ${bold(\"Suggested project:\")} ${result.suggestedProject ?? dim(\"(none)\")}`);\n console.log(\n ` ${bold(\"Confidence:\")} ${confidenceBar(result.confidence)} ${(result.confidence * 100).toFixed(1)}%`\n );\n console.log(` ${bold(\"Chunks scored:\")} ${result.chunkCount}`);\n\n if (result.topProjects.length > 0) {\n console.log();\n console.log(` ${bold(\"Top matches:\")}`);\n for (const p of result.topProjects) {\n const bar = confidenceBar(p.score, 15);\n const marker = p.slug === result.currentProject ? dim(\" (current)\") : \"\";\n console.log(` ${p.slug.padEnd(30)} ${bar} ${(p.score * 100).toFixed(1)}%${marker}`);\n }\n }\n\n console.log();\n\n if (result.shifted) {\n console.log(\n warn(\" TOPIC SHIFT DETECTED\") +\n dim(` — conversation appears to be about \"${result.suggestedProject}\", not \"${result.currentProject}\"`)\n );\n console.log();\n } else if (result.suggestedProject && result.suggestedProject === result.currentProject) {\n console.log(ok(\" No shift detected\") + dim(\" — context matches current project\"));\n console.log();\n } else if (!result.currentProject) {\n if (result.suggestedProject) {\n console.log(\n ok(\" Best matching project: \") + bold(result.suggestedProject) +\n dim(` (confidence: ${(result.confidence * 100).toFixed(0)}%)`)\n );\n } else {\n console.log(dim(\" No matching project found in memory index.\"));\n }\n console.log();\n } else {\n console.log(\n dim(` No shift detected (top match \"${result.suggestedProject}\" has confidence ${(result.confidence * 100).toFixed(0)}% — below threshold)`)\n );\n console.log();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Command implementations\n// ---------------------------------------------------------------------------\n\nasync function cmdCheck(\n context: string,\n opts: {\n current?: string;\n threshold?: string;\n json?: boolean;\n }\n): Promise<void> {\n const client = makeClient();\n\n const threshold = opts.threshold ? parseFloat(opts.threshold) : undefined;\n\n if (threshold !== undefined && (isNaN(threshold) || threshold < 0 || threshold > 1)) {\n console.error(err(\" --threshold must be a number between 0 and 1\"));\n process.exit(1);\n }\n\n try {\n const result = await client.topicCheck({\n context,\n currentProject: opts.current,\n threshold,\n });\n\n if (opts.json) {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n\n printTopicResult(result);\n\n // Exit with code 1 if a shift was detected (useful for scripting / hooks)\n if (result.shifted) {\n process.exit(1);\n }\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.log();\n console.log(warn(\" Cannot reach PAI daemon.\"));\n console.log(dim(` ${msg}`));\n console.log(dim(\" Start it with: pai daemon serve\"));\n console.log();\n process.exit(1);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerTopicCommands(topicCmd: Command): void {\n // pai topic check <context>\n topicCmd\n .command(\"check <context>\")\n .description(\n \"Check whether context text has drifted to a different project.\\n\" +\n 'Example: pai topic check \"working on JWT authentication\"\\n' +\n \"Exit code 1 if a shift is detected (useful for hooks).\"\n )\n .option(\n \"--current <slug>\",\n \"The project this session is currently routed to\"\n )\n .option(\n \"--threshold <n>\",\n \"Confidence threshold [0-1] to declare a shift (default: 0.6)\"\n )\n .option(\"--json\", \"Output raw JSON result\")\n .action(\n async (\n context: string,\n opts: { current?: string; threshold?: string; json?: boolean }\n ) => {\n await cmdCheck(context, opts);\n }\n );\n}\n","/**\n * kg-backfill.ts — Populate the temporal knowledge graph from existing\n * session notes.\n *\n * Walks `Notes/YYYY/MM/*.md` for each registered project, runs the same\n * extractor that the session-summary-worker uses on every NEW summary, and\n * stores triples in Postgres. Idempotent: a state file at\n * ~/.config/pai/kg-backfill-state.json records which note paths have been\n * processed so re-runs skip them.\n *\n * Even without the state file the operation is safe — extractAndStoreTriples\n * uses supersession logic so re-extracting a note never produces duplicates.\n */\n\nimport { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type { Pool } from \"pg\";\n\nimport { openRegistry } from \"../registry/db.js\";\nimport { loadConfig } from \"../daemon/config.js\";\nimport { createStorageBackend } from \"../storage/factory.js\";\nimport { extractAndStoreTriples } from \"./kg-extraction.js\";\nimport { openFederation } from \"./db.js\";\n\n// ---------------------------------------------------------------------------\n// State file\n// ---------------------------------------------------------------------------\n\nconst STATE_FILE = join(homedir(), \".config\", \"pai\", \"kg-backfill-state.json\");\n\ninterface BackfillState {\n /** Map of absolute note path -> ISO timestamp of when it was processed. */\n processed: Record<string, string>;\n}\n\nfunction loadState(): BackfillState {\n try {\n if (existsSync(STATE_FILE)) {\n const raw = JSON.parse(readFileSync(STATE_FILE, \"utf-8\"));\n if (raw && typeof raw === \"object\" && raw.processed) return raw as BackfillState;\n }\n } catch { /* ignore */ }\n return { processed: {} };\n}\n\nfunction saveState(state: BackfillState): void {\n try {\n const dir = join(homedir(), \".config\", \"pai\");\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n writeFileSync(STATE_FILE, JSON.stringify(state, null, 2), \"utf-8\");\n } catch (e) {\n process.stderr.write(`[kg-backfill] failed to save state: ${e}\\n`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Note discovery\n// ---------------------------------------------------------------------------\n\ninterface ProjectRow {\n id: number;\n slug: string;\n root_path: string;\n}\n\ninterface ProjectNote {\n project: ProjectRow;\n notePath: string;\n}\n\n/**\n * Find all session notes under <project_root>/Notes/YYYY/MM/*.md.\n * Falls back to <project_root>/Notes/*.md (flat layout) when no year/month\n * subdirectories are present.\n */\nfunction findProjectNotes(project: ProjectRow): string[] {\n const notesRoot = join(project.root_path, \"Notes\");\n if (!existsSync(notesRoot)) return [];\n\n const found: Array<{ path: string; mtime: number }> = [];\n\n function walk(dir: string, depth: number): void {\n if (depth > 4) return;\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const name of entries) {\n const full = join(dir, name);\n let st;\n try {\n st = statSync(full);\n } catch {\n continue;\n }\n if (st.isDirectory()) {\n walk(full, depth + 1);\n } else if (st.isFile() && name.endsWith(\".md\") && /^\\d{3,4}/.test(name)) {\n found.push({ path: full, mtime: st.mtimeMs });\n }\n }\n }\n\n walk(notesRoot, 0);\n // Process oldest first so newer extractions can correctly supersede older facts\n found.sort((a, b) => a.mtime - b.mtime);\n return found.map((f) => f.path);\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport interface BackfillOptions {\n /** If set, only process this project's notes. */\n projectSlug?: string;\n /** Cap the total number of notes processed across all projects. */\n limit?: number;\n /** If true, do not call the extractor and do not write any state. */\n dryRun?: boolean;\n /** Progress callback fired before each note is processed. */\n onProgress?: (current: number, total: number, note: string) => void;\n}\n\nexport interface BackfillResult {\n notes_processed: number;\n triples_extracted: number;\n triples_added: number;\n triples_superseded: number;\n errors: number;\n}\n\n/**\n * Backfill the knowledge graph from existing session notes.\n *\n * Requires the Postgres backend (KG tables live there). Throws if Postgres\n * is unavailable, since this is an explicit user action.\n */\nexport async function backfillKgFromNotes(\n options: BackfillOptions = {}\n): Promise<BackfillResult> {\n const result: BackfillResult = {\n notes_processed: 0,\n triples_extracted: 0,\n triples_added: 0,\n triples_superseded: 0,\n errors: 0,\n };\n\n const config = loadConfig();\n if (config.storageBackend !== \"postgres\") {\n throw new Error(\n \"kg backfill requires the Postgres backend. \" +\n 'Set \"storageBackend\": \"postgres\" in ~/.config/pai/config.json.'\n );\n }\n\n const backend = await createStorageBackend(config);\n if (backend.backendType !== \"postgres\") {\n throw new Error(\n \"Postgres backend unavailable — fell back to SQLite. Cannot backfill KG.\"\n );\n }\n\n const pool: Pool = (backend as unknown as { getPool(): Pool }).getPool();\n\n // Open federation SQLite for entity upserts (kg_entities table)\n const federationDb = openFederation();\n\n // -- Load projects -------------------------------------------------------\n const registry = openRegistry();\n let projects: ProjectRow[];\n try {\n if (options.projectSlug) {\n const row = registry\n .prepare(\"SELECT id, slug, root_path FROM projects WHERE slug = ?\")\n .get(options.projectSlug) as ProjectRow | undefined;\n if (!row) {\n throw new Error(`Project not found: ${options.projectSlug}`);\n }\n projects = [row];\n } else {\n projects = registry\n .prepare(\n \"SELECT id, slug, root_path FROM projects WHERE status = 'active' ORDER BY slug\"\n )\n .all() as ProjectRow[];\n }\n } finally {\n registry.close();\n }\n\n // -- Build the work list -------------------------------------------------\n const state = loadState();\n const work: ProjectNote[] = [];\n for (const project of projects) {\n const notes = findProjectNotes(project);\n for (const notePath of notes) {\n if (state.processed[notePath]) continue;\n work.push({ project, notePath });\n }\n }\n\n const total = options.limit ? Math.min(options.limit, work.length) : work.length;\n\n // -- Process notes -------------------------------------------------------\n for (let i = 0; i < total; i++) {\n const { project, notePath } = work[i];\n options.onProgress?.(i + 1, total, notePath);\n\n if (options.dryRun) {\n result.notes_processed++;\n continue;\n }\n\n let noteContent: string;\n try {\n noteContent = readFileSync(notePath, \"utf-8\");\n } catch (e) {\n process.stderr.write(`[kg-backfill] read failed ${notePath}: ${e}\\n`);\n result.errors++;\n continue;\n }\n\n try {\n const stats = await extractAndStoreTriples(pool, {\n summaryText: noteContent,\n projectSlug: project.slug,\n projectId: project.id,\n sessionId: `backfill:${notePath}`,\n gitLog: \"\",\n model: \"sonnet\",\n federationDb,\n });\n\n result.notes_processed++;\n result.triples_extracted += stats.extracted;\n result.triples_added += stats.added;\n result.triples_superseded += stats.superseded;\n\n // Mark as processed (state file is the idempotency hint; the supersession\n // logic in the extractor is the actual safety net)\n state.processed[notePath] = new Date().toISOString();\n // Save incrementally so a crash mid-run doesn't lose progress\n if ((i + 1) % 5 === 0) saveState(state);\n } catch (e) {\n process.stderr.write(`[kg-backfill] extract failed ${notePath}: ${e}\\n`);\n result.errors++;\n }\n }\n\n if (!options.dryRun) saveState(state);\n federationDb.close();\n await backend.close();\n\n return result;\n}\n","/**\n * pai kg <sub-command>\n *\n * Temporal knowledge graph management:\n * pai kg backfill [--project SLUG] [--limit N] [--dry-run]\n * pai kg query [--subject S] [--predicate P] [--object O] [--as-of DATE] [--project SLUG]\n * pai kg list [--project SLUG] [--limit N]\n * pai kg stats\n *\n * All commands require the Postgres backend (KG tables live in Postgres).\n */\n\nimport type { Command } from \"commander\";\nimport type { Pool } from \"pg\";\n\nimport { ok, warn, err, dim, bold, header } from \"../utils.js\";\nimport { loadConfig } from \"../../daemon/config.js\";\nimport { createStorageBackend } from \"../../storage/factory.js\";\nimport { kgQuery } from \"../../memory/kg.js\";\nimport { backfillKgFromNotes } from \"../../memory/kg-backfill.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nasync function getPool(): Promise<{ pool: Pool; close: () => Promise<void> }> {\n const config = loadConfig();\n if (config.storageBackend !== \"postgres\") {\n console.error(err(\" KG commands require Postgres backend.\"));\n console.error(dim(' Set \"storageBackend\": \"postgres\" in ~/.config/pai/config.json'));\n process.exit(1);\n }\n const backend = await createStorageBackend(config);\n if (backend.backendType !== \"postgres\") {\n console.error(err(\" Postgres backend unavailable — fell back to SQLite.\"));\n process.exit(1);\n }\n const pool = (backend as unknown as { getPool(): Pool }).getPool();\n return { pool, close: () => backend.close() };\n}\n\nfunction shorten(s: string, n: number): string {\n return s.length <= n ? s : s.slice(0, n - 1) + \"…\";\n}\n\n// ---------------------------------------------------------------------------\n// pai kg backfill\n// ---------------------------------------------------------------------------\n\nasync function cmdBackfill(opts: {\n project?: string;\n limit?: string;\n dryRun?: boolean;\n}): Promise<void> {\n const limit = opts.limit ? parseInt(opts.limit, 10) : undefined;\n if (limit !== undefined && (isNaN(limit) || limit < 1)) {\n console.error(err(\" --limit must be a positive integer\"));\n process.exit(1);\n }\n\n console.log();\n console.log(header(\" PAI KG Backfill\"));\n console.log();\n if (opts.project) console.log(` ${bold(\"Project:\")} ${opts.project}`);\n if (limit) console.log(` ${bold(\"Limit:\")} ${limit}`);\n if (opts.dryRun) console.log(` ${bold(\"Mode:\")} ${warn(\"dry-run\")}`);\n console.log();\n\n try {\n const result = await backfillKgFromNotes({\n projectSlug: opts.project,\n limit,\n dryRun: opts.dryRun,\n onProgress: (current, total, note) => {\n const short = shorten(note.replace(process.env.HOME ?? \"\", \"~\"), 70);\n process.stdout.write(` [${current}/${total}] ${dim(short)}\\n`);\n },\n });\n\n console.log();\n console.log(ok(\" Backfill complete\"));\n console.log(` Notes processed: ${bold(String(result.notes_processed))}`);\n console.log(` Triples extracted: ${bold(String(result.triples_extracted))}`);\n console.log(` Triples added: ${bold(String(result.triples_added))}`);\n console.log(` Triples superseded: ${bold(String(result.triples_superseded))}`);\n if (result.errors > 0) {\n console.log(` ${warn(\"Errors:\")} ${result.errors}`);\n }\n console.log();\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(err(` ${msg}`));\n process.exit(1);\n }\n}\n\n// ---------------------------------------------------------------------------\n// pai kg query\n// ---------------------------------------------------------------------------\n\nasync function cmdQuery(opts: {\n subject?: string;\n predicate?: string;\n object?: string;\n asOf?: string;\n project?: string;\n json?: boolean;\n}): Promise<void> {\n const { pool, close } = await getPool();\n try {\n let projectId: number | undefined;\n if (opts.project) {\n const r = await pool.query<{ id: number }>(\n \"SELECT id FROM projects WHERE slug = $1 LIMIT 1\",\n [opts.project]\n );\n if (r.rows.length === 0) {\n console.error(warn(` Project not found in Postgres: ${opts.project}`));\n } else {\n projectId = r.rows[0].id;\n }\n }\n\n const asOf = opts.asOf ? new Date(opts.asOf) : undefined;\n if (asOf && isNaN(asOf.getTime())) {\n console.error(err(` Invalid --as-of date: ${opts.asOf}`));\n process.exit(1);\n }\n\n const triples = await kgQuery(pool, {\n subject: opts.subject,\n predicate: opts.predicate,\n object: opts.object,\n project_id: projectId,\n as_of: asOf,\n });\n\n if (opts.json) {\n console.log(JSON.stringify(triples, null, 2));\n return;\n }\n\n console.log();\n console.log(header(` ${triples.length} triple(s)`));\n console.log();\n for (const t of triples) {\n const validity = t.valid_to\n ? dim(`(invalidated ${t.valid_to.toISOString().slice(0, 10)})`)\n : dim(`(valid since ${t.valid_from.toISOString().slice(0, 10)})`);\n console.log(\n ` ${bold(t.subject)} ${dim(\"·\")} ${t.predicate} ${dim(\"·\")} ${t.object} ${validity}`\n );\n }\n console.log();\n } finally {\n await close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// pai kg list\n// ---------------------------------------------------------------------------\n\nasync function cmdList(opts: { project?: string; limit?: string }): Promise<void> {\n const limit = opts.limit ? parseInt(opts.limit, 10) : 50;\n const { pool, close } = await getPool();\n try {\n let projectId: number | undefined;\n if (opts.project) {\n const r = await pool.query<{ id: number }>(\n \"SELECT id FROM projects WHERE slug = $1 LIMIT 1\",\n [opts.project]\n );\n if (r.rows.length > 0) projectId = r.rows[0].id;\n }\n\n const triples = await kgQuery(pool, { project_id: projectId });\n const slice = triples.slice(0, limit);\n\n console.log();\n console.log(\n header(` ${slice.length} of ${triples.length} currently-valid triple(s)`)\n );\n console.log();\n for (const t of slice) {\n console.log(\n ` ${bold(t.subject)} ${dim(\"·\")} ${t.predicate} ${dim(\"·\")} ${t.object}`\n );\n }\n if (triples.length > slice.length) {\n console.log();\n console.log(dim(` (${triples.length - slice.length} more — increase --limit)`));\n }\n console.log();\n } finally {\n await close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// pai kg stats\n// ---------------------------------------------------------------------------\n\nasync function cmdStats(): Promise<void> {\n const { pool, close } = await getPool();\n try {\n const totals = await pool.query<{\n total: string;\n valid: string;\n invalidated: string;\n subjects: string;\n predicates: string;\n }>(\n `SELECT\n COUNT(*)::text AS total,\n COUNT(*) FILTER (WHERE valid_to IS NULL)::text AS valid,\n COUNT(*) FILTER (WHERE valid_to IS NOT NULL)::text AS invalidated,\n COUNT(DISTINCT subject)::text AS subjects,\n COUNT(DISTINCT predicate)::text AS predicates\n FROM kg_triples`\n );\n\n const contradictions = await pool.query<{ count: string }>(\n `SELECT COUNT(*)::text AS count FROM (\n SELECT subject, predicate\n FROM kg_triples\n WHERE valid_to IS NULL\n GROUP BY subject, predicate\n HAVING COUNT(*) > 1\n ) c`\n );\n\n const row = totals.rows[0] ?? {};\n console.log();\n console.log(header(\" PAI KG Stats\"));\n console.log();\n console.log(` ${bold(\"Total triples:\")} ${row.total ?? \"0\"}`);\n console.log(` ${bold(\"Currently valid:\")} ${row.valid ?? \"0\"}`);\n console.log(` ${bold(\"Invalidated:\")} ${row.invalidated ?? \"0\"}`);\n console.log(` ${bold(\"Distinct subjects:\")} ${row.subjects ?? \"0\"}`);\n console.log(` ${bold(\"Distinct predicates:\")} ${row.predicates ?? \"0\"}`);\n console.log(` ${bold(\"Contradictions:\")} ${contradictions.rows[0]?.count ?? \"0\"}`);\n console.log();\n } finally {\n await close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerKgCommands(kgCmd: Command): void {\n kgCmd\n .command(\"backfill\")\n .description(\"Populate the KG from existing session notes (idempotent)\")\n .option(\"--project <slug>\", \"Restrict backfill to a single project\")\n .option(\"--limit <n>\", \"Maximum number of notes to process\")\n .option(\"--dry-run\", \"List notes that would be processed without extracting\")\n .action(async (opts: { project?: string; limit?: string; dryRun?: boolean }) => {\n await cmdBackfill(opts);\n });\n\n kgCmd\n .command(\"query\")\n .description(\"Query KG triples by subject, predicate, object, time, or project\")\n .option(\"--subject <s>\", \"Filter by subject\")\n .option(\"--predicate <p>\", \"Filter by predicate\")\n .option(\"--object <o>\", \"Filter by object\")\n .option(\"--as-of <date>\", \"Point-in-time query (YYYY-MM-DD or ISO 8601)\")\n .option(\"--project <slug>\", \"Restrict to a project slug\")\n .option(\"--json\", \"Output raw JSON\")\n .action(\n async (opts: {\n subject?: string;\n predicate?: string;\n object?: string;\n asOf?: string;\n project?: string;\n json?: boolean;\n }) => {\n await cmdQuery(opts);\n }\n );\n\n kgCmd\n .command(\"list\")\n .description(\"List currently-valid triples\")\n .option(\"--project <slug>\", \"Restrict to a project slug\")\n .option(\"--limit <n>\", \"Maximum triples to print\", \"50\")\n .action(async (opts: { project?: string; limit?: string }) => {\n await cmdList(opts);\n });\n\n kgCmd\n .command(\"stats\")\n .description(\"Show triple counts and contradiction count\")\n .action(async () => {\n await cmdStats();\n });\n}\n","/**\n * pai db <sub-command>\n *\n * Quick database inspection commands for both SQLite and Postgres backends.\n *\n * pai db query <sql> [--db sqlite|postgres] [--json]\n * pai db tables [--db sqlite|postgres]\n * pai db schema <table> [--db sqlite|postgres]\n */\n\nimport type { Command } from \"commander\";\nimport { ok, warn, err, dim, bold, header, renderTable } from \"../utils.js\";\nimport { loadConfig } from \"../../daemon/config.js\";\nimport { openFederation } from \"../../memory/db.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype DbTarget = \"sqlite\" | \"postgres\";\n\ninterface CommonOpts {\n db?: DbTarget;\n json?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// SQLite helpers (synchronous — better-sqlite3)\n// ---------------------------------------------------------------------------\n\nfunction getSqliteDb() {\n return openFederation();\n}\n\nfunction sqliteQuery(sql: string): { columns: string[]; rows: unknown[][] } {\n const db = getSqliteDb();\n try {\n const stmt = db.prepare(sql);\n const raw = stmt.all() as Record<string, unknown>[];\n if (raw.length === 0) return { columns: [], rows: [] };\n const columns = Object.keys(raw[0]);\n const rows = raw.map((r) => columns.map((c) => r[c]));\n return { columns, rows };\n } finally {\n db.close();\n }\n}\n\nfunction sqliteTables(): string[] {\n const db = getSqliteDb();\n try {\n const rows = db\n .prepare(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name\"\n )\n .all() as { name: string }[];\n return rows.map((r) => r.name);\n } finally {\n db.close();\n }\n}\n\nfunction sqliteSchema(table: string): { columns: string[]; rows: unknown[][] } {\n const db = getSqliteDb();\n try {\n // Validate table name — only allow alphanumeric / underscores\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(table)) {\n throw new Error(`Invalid table name: ${table}`);\n }\n const rows = db.prepare(`PRAGMA table_info(${table})`).all() as {\n cid: number;\n name: string;\n type: string;\n notnull: number;\n dflt_value: string | null;\n pk: number;\n }[];\n if (rows.length === 0) {\n throw new Error(`Table not found: ${table}`);\n }\n const columns = [\"cid\", \"name\", \"type\", \"notnull\", \"default\", \"pk\"];\n const data = rows.map((r) => [\n r.cid,\n r.name,\n r.type,\n r.notnull ? \"NOT NULL\" : \"\",\n r.dflt_value ?? \"\",\n r.pk ? \"PK\" : \"\",\n ]);\n return { columns, data: data } as unknown as { columns: string[]; rows: unknown[][] };\n } finally {\n db.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Postgres helpers (async — pg Pool)\n// ---------------------------------------------------------------------------\n\nasync function getPool() {\n const config = loadConfig();\n const pgConfig = config.postgres ?? {};\n\n const { Pool } = await import(\"pg\");\n\n // Build connection params from config\n const poolConfig = pgConfig.connectionString\n ? { connectionString: pgConfig.connectionString }\n : {\n host: pgConfig.host ?? \"localhost\",\n port: pgConfig.port ?? 5432,\n database: pgConfig.database ?? \"pai\",\n user: pgConfig.user ?? \"pai\",\n password: pgConfig.password ?? \"pai\",\n connectionTimeoutMillis: pgConfig.connectionTimeoutMs ?? 5000,\n };\n\n const pool = new Pool(poolConfig);\n\n // Quick connectivity test\n try {\n const client = await pool.connect();\n client.release();\n } catch (e) {\n await pool.end();\n const msg = e instanceof Error ? e.message : String(e);\n throw new Error(`Cannot connect to Postgres: ${msg}`);\n }\n\n return pool;\n}\n\nasync function postgresQuery(\n sql: string\n): Promise<{ columns: string[]; rows: unknown[][] }> {\n const pool = await getPool();\n try {\n const result = await pool.query(sql);\n const columns = result.fields.map((f) => f.name);\n const rows = result.rows.map((r: Record<string, unknown>) =>\n columns.map((c) => r[c])\n );\n return { columns, rows };\n } finally {\n await pool.end();\n }\n}\n\nasync function postgresTables(): Promise<string[]> {\n const pool = await getPool();\n try {\n const result = await pool.query<{ tablename: string }>(\n \"SELECT tablename FROM pg_tables WHERE schemaname = 'public' ORDER BY tablename\"\n );\n return result.rows.map((r) => r.tablename);\n } finally {\n await pool.end();\n }\n}\n\nasync function postgresSchema(\n table: string\n): Promise<{ columns: string[]; rows: unknown[][] }> {\n const pool = await getPool();\n try {\n const result = await pool.query<{\n column_name: string;\n data_type: string;\n is_nullable: string;\n column_default: string | null;\n }>(\n `SELECT column_name, data_type, is_nullable, column_default\n FROM information_schema.columns\n WHERE table_schema = 'public' AND table_name = $1\n ORDER BY ordinal_position`,\n [table]\n );\n if (result.rows.length === 0) {\n throw new Error(`Table not found in public schema: ${table}`);\n }\n const columns = [\"column\", \"type\", \"nullable\", \"default\"];\n const rows = result.rows.map((r) => [\n r.column_name,\n r.data_type,\n r.is_nullable === \"YES\" ? \"YES\" : \"NO\",\n r.column_default ?? \"\",\n ]);\n return { columns, rows };\n } finally {\n await pool.end();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Output helpers\n// ---------------------------------------------------------------------------\n\nfunction printResult(\n result: { columns: string[]; rows: unknown[][] },\n json: boolean\n): void {\n if (json) {\n const objects = result.rows.map((row) => {\n const obj: Record<string, unknown> = {};\n result.columns.forEach((col, i) => {\n obj[col] = row[i];\n });\n return obj;\n });\n console.log(JSON.stringify(objects, null, 2));\n return;\n }\n\n if (result.rows.length === 0) {\n console.log();\n console.log(dim(\" (no rows)\"));\n console.log();\n return;\n }\n\n const stringRows = result.rows.map((row) =>\n row.map((cell) => {\n if (cell === null || cell === undefined) return dim(\"NULL\");\n if (typeof cell === \"object\") return JSON.stringify(cell);\n return String(cell);\n })\n );\n\n console.log();\n console.log(renderTable(result.columns, stringRows));\n console.log();\n console.log(dim(` ${result.rows.length} row(s)`));\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// pai db query\n// ---------------------------------------------------------------------------\n\nasync function cmdQuery(\n sql: string,\n opts: CommonOpts\n): Promise<void> {\n const target: DbTarget = opts.db ?? \"sqlite\";\n console.log();\n console.log(\n header(` Query [${target}]`) + dim(` ${sql.slice(0, 80)}${sql.length > 80 ? \"…\" : \"\"}`)\n );\n\n try {\n let result: { columns: string[]; rows: unknown[][] };\n if (target === \"postgres\") {\n result = await postgresQuery(sql);\n } else {\n result = sqliteQuery(sql);\n }\n printResult(result, opts.json ?? false);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(err(` ${msg}`));\n process.exit(1);\n }\n}\n\n// ---------------------------------------------------------------------------\n// pai db tables\n// ---------------------------------------------------------------------------\n\nasync function cmdTables(opts: CommonOpts): Promise<void> {\n const target: DbTarget = opts.db ?? \"sqlite\";\n console.log();\n console.log(header(` Tables [${target}]`));\n console.log();\n\n try {\n let tables: string[];\n if (target === \"postgres\") {\n tables = await postgresTables();\n } else {\n tables = sqliteTables();\n }\n\n if (opts.json) {\n console.log(JSON.stringify(tables, null, 2));\n return;\n }\n\n if (tables.length === 0) {\n console.log(dim(\" (no tables found)\"));\n } else {\n for (const t of tables) {\n console.log(` ${bold(t)}`);\n }\n }\n console.log();\n console.log(dim(` ${tables.length} table(s)`));\n console.log();\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(err(` ${msg}`));\n process.exit(1);\n }\n}\n\n// ---------------------------------------------------------------------------\n// pai db schema\n// ---------------------------------------------------------------------------\n\nasync function cmdSchema(table: string, opts: CommonOpts): Promise<void> {\n const target: DbTarget = opts.db ?? \"sqlite\";\n console.log();\n console.log(header(` Schema: ${table} [${target}]`));\n\n try {\n let result: { columns: string[]; rows: unknown[][] };\n if (target === \"postgres\") {\n result = await postgresSchema(table);\n } else {\n const raw = sqliteSchema(table) as unknown as {\n columns: string[];\n data: unknown[][];\n };\n result = { columns: raw.columns, rows: raw.data };\n }\n printResult(result, opts.json ?? false);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(err(` ${msg}`));\n process.exit(1);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Commander registration\n// ---------------------------------------------------------------------------\n\nexport function registerDbCommands(dbCmd: Command): void {\n dbCmd\n .command(\"query <sql>\")\n .description(\"Run a SQL query against the selected database\")\n .option(\"--db <target>\", \"Database target: sqlite (default) or postgres\", \"sqlite\")\n .option(\"--json\", \"Output results as JSON array\")\n .action(async (sql: string, opts: CommonOpts) => {\n await cmdQuery(sql, opts);\n });\n\n dbCmd\n .command(\"tables\")\n .description(\"List all tables in the selected database\")\n .option(\"--db <target>\", \"Database target: sqlite (default) or postgres\", \"sqlite\")\n .option(\"--json\", \"Output as JSON array\")\n .action(async (opts: CommonOpts) => {\n await cmdTables(opts);\n });\n\n dbCmd\n .command(\"schema <table>\")\n .description(\"Show column schema for a table\")\n .option(\"--db <target>\", \"Database target: sqlite (default) or postgres\", \"sqlite\")\n .option(\"--json\", \"Output as JSON array\")\n .action(async (table: string, opts: CommonOpts) => {\n await cmdSchema(table, opts);\n });\n}\n","/**\n * pai find <query> [--n=20] [--json]\n *\n * Content-based search across ~/.claude/history.jsonl.\n * Finds user prompts matching the query (case-insensitive substring),\n * groups by sessionId, sorts by most-recent match, and shows a table\n * with short UUID, date, project path, and the matching prompt snippet.\n *\n * The sessionId from history.jsonl is the same UUID used by claude --resume,\n * so rows are directly resumable via: pai resume <id>\n *\n * Note: older history.jsonl entries (before Claude Code ~2.0) may lack a\n * sessionId field. These are grouped under a synthetic \"no-session\" bucket\n * and shown in a separate note at the bottom.\n */\n\nimport { existsSync, createReadStream } from \"node:fs\";\nimport { createInterface } from \"node:readline\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport { renderTable, header, dim, warn, err } from \"../utils.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface HistoryEntry {\n display?: string;\n timestamp?: number;\n project?: string;\n sessionId?: string;\n pastedContents?: unknown;\n}\n\ninterface SessionMatch {\n sessionId: string | null;\n /** Most-recent timestamp among matching lines */\n lastMatchTs: number;\n /** Most-recent matching prompt text */\n lastMatchDisplay: string;\n /** Project path (from the most-recent match) */\n project: string;\n /** Number of matching lines in this session */\n matchCount: number;\n}\n\n// ---------------------------------------------------------------------------\n// History file location\n// ---------------------------------------------------------------------------\n\nconst HISTORY_FILE = join(homedir(), \".claude\", \"history.jsonl\");\n\n// ---------------------------------------------------------------------------\n// Shorten project path for display\n// ---------------------------------------------------------------------------\n\nfunction shortenProject(p: string, maxLen = 42): string {\n if (p.length <= maxLen) return p;\n // Keep tail (the meaningful part) and prefix with …\n return \"…\" + p.slice(-(maxLen - 1));\n}\n\n// ---------------------------------------------------------------------------\n// Format timestamp\n// ---------------------------------------------------------------------------\n\nfunction fmtTs(ts: number): string {\n const d = new Date(ts);\n const pad = (n: number) => String(n).padStart(2, \"0\");\n return (\n `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ` +\n `${pad(d.getHours())}:${pad(d.getMinutes())}`\n );\n}\n\n// ---------------------------------------------------------------------------\n// Core search function (streaming to avoid loading 26k lines into memory)\n// ---------------------------------------------------------------------------\n\nexport async function searchHistory(\n query: string,\n maxResults: number\n): Promise<SessionMatch[]> {\n if (!existsSync(HISTORY_FILE)) return [];\n\n const queryLower = query.toLowerCase();\n // Escape regex special chars from query for safety — but we're doing\n // string .includes() not regex, so no escaping needed.\n\n const bySession = new Map<string, SessionMatch>();\n const noSession: SessionMatch = {\n sessionId: null,\n lastMatchTs: 0,\n lastMatchDisplay: \"\",\n project: \"\",\n matchCount: 0,\n };\n\n const rl = createInterface({\n input: createReadStream(HISTORY_FILE, { encoding: \"utf8\" }),\n crlfDelay: Infinity,\n });\n\n for await (const line of rl) {\n const t = line.trim();\n if (!t) continue;\n\n let entry: HistoryEntry;\n try {\n entry = JSON.parse(t) as HistoryEntry;\n } catch {\n continue;\n }\n\n const display = entry.display ?? \"\";\n if (!display.toLowerCase().includes(queryLower)) continue;\n\n const ts = entry.timestamp ?? 0;\n const project = entry.project ?? \"\";\n const sessionId = entry.sessionId ?? null;\n\n if (!sessionId) {\n // Older entry without sessionId\n if (ts > noSession.lastMatchTs) {\n noSession.lastMatchTs = ts;\n noSession.lastMatchDisplay = display;\n noSession.project = project;\n }\n noSession.matchCount++;\n continue;\n }\n\n const existing = bySession.get(sessionId);\n if (!existing) {\n bySession.set(sessionId, {\n sessionId,\n lastMatchTs: ts,\n lastMatchDisplay: display,\n project,\n matchCount: 1,\n });\n } else {\n existing.matchCount++;\n if (ts > existing.lastMatchTs) {\n existing.lastMatchTs = ts;\n existing.lastMatchDisplay = display;\n existing.project = project;\n }\n }\n }\n\n // Sort by most-recent match descending\n const sorted = [...bySession.values()].sort(\n (a, b) => b.lastMatchTs - a.lastMatchTs\n );\n\n return sorted.slice(0, maxResults);\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport async function cmdFind(\n query: string,\n opts: { n?: string; json?: boolean }\n): Promise<void> {\n const maxResults = parseInt(opts.n ?? \"20\", 10);\n\n if (!existsSync(HISTORY_FILE)) {\n console.error(err(`~/.claude/history.jsonl not found.`));\n process.exitCode = 1;\n return;\n }\n\n const matches = await searchHistory(query, maxResults);\n\n if (opts.json) {\n console.log(JSON.stringify(matches, null, 2));\n return;\n }\n\n if (matches.length === 0) {\n console.log(warn(`No sessions found matching \"${query}\" in ~/.claude/history.jsonl.`));\n console.log(dim(` Try a shorter or different search term.`));\n return;\n }\n\n console.log(\"\\n\" + header(\"Session Search Results\") + \"\\n\");\n console.log(dim(` Query: \"${query}\" (${matches.length} session(s) with matching prompts)\\n`));\n\n const headers = [\"#\", \"id\", \"when\", \"project\", \"last matching prompt\"];\n const rows = matches.map((m, idx) => {\n const shortId = (m.sessionId ?? \"no-session\").slice(0, 8);\n const when = m.lastMatchTs > 0 ? fmtTs(m.lastMatchTs) : dim(\"—\");\n const project = shortenProject(m.project || dim(\"—\"));\n // Truncate the prompt snippet — strip newlines, trim\n const snippet = m.lastMatchDisplay\n .replace(/\\n+/g, \" \")\n .trim()\n .slice(0, 48);\n const display = snippet.length < m.lastMatchDisplay.replace(/\\n+/g, \" \").trim().length\n ? `\"${snippet}…\"`\n : `\"${snippet}\"`;\n\n return [\n dim(String(idx + 1)),\n chalk.cyan(shortId),\n when,\n dim(project),\n chalk.dim(display),\n ];\n });\n\n console.log(renderTable(headers, rows));\n console.log();\n console.log(dim(\" Resume: \") + chalk.white(\"pai resume <id>\"));\n console.log();\n}\n","/**\n * history-search.ts\n *\n * Content-based search across ~/.claude/history.jsonl.\n * Streams the file (avoids loading 26k+ lines into memory), groups matching\n * lines by sessionId, and returns results sorted by most-recent match.\n *\n * Used by: pai <query> (main resolver) and pai find (compat alias)\n */\n\nimport { existsSync, createReadStream } from \"node:fs\";\nimport { createInterface } from \"node:readline\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface HistoryEntry {\n display?: string;\n timestamp?: number;\n project?: string;\n sessionId?: string;\n pastedContents?: unknown;\n}\n\nexport interface SessionMatch {\n sessionId: string | null;\n /** Most-recent timestamp among matching lines */\n lastMatchTs: number;\n /** Most-recent matching prompt text */\n lastMatchDisplay: string;\n /** Project path (from the most-recent match) */\n project: string;\n /** Number of matching lines in this session */\n matchCount: number;\n}\n\n// ---------------------------------------------------------------------------\n// History file location\n// ---------------------------------------------------------------------------\n\nexport const HISTORY_FILE = join(homedir(), \".claude\", \"history.jsonl\");\n\n// ---------------------------------------------------------------------------\n// Core search function (streaming)\n// ---------------------------------------------------------------------------\n\n/**\n * Search ~/.claude/history.jsonl for prompts matching the query (case-insensitive\n * substring). Results are grouped by sessionId and sorted by most-recent match.\n *\n * Entries without a sessionId (old Claude Code versions) are excluded from\n * results since they can't be resumed.\n */\nexport async function searchHistory(\n query: string,\n maxResults: number\n): Promise<SessionMatch[]> {\n if (!existsSync(HISTORY_FILE)) return [];\n\n const queryLower = query.toLowerCase();\n const bySession = new Map<string, SessionMatch>();\n\n const rl = createInterface({\n input: createReadStream(HISTORY_FILE, { encoding: \"utf8\" }),\n crlfDelay: Infinity,\n });\n\n for await (const line of rl) {\n const t = line.trim();\n if (!t) continue;\n\n let entry: HistoryEntry;\n try {\n entry = JSON.parse(t) as HistoryEntry;\n } catch {\n continue;\n }\n\n const display = entry.display ?? \"\";\n if (!display.toLowerCase().includes(queryLower)) continue;\n\n const ts = entry.timestamp ?? 0;\n const project = entry.project ?? \"\";\n const sessionId = entry.sessionId ?? null;\n\n // Skip entries with no sessionId — can't resume them\n if (!sessionId) continue;\n\n const existing = bySession.get(sessionId);\n if (!existing) {\n bySession.set(sessionId, {\n sessionId,\n lastMatchTs: ts,\n lastMatchDisplay: display,\n project,\n matchCount: 1,\n });\n } else {\n existing.matchCount++;\n if (ts > existing.lastMatchTs) {\n existing.lastMatchTs = ts;\n existing.lastMatchDisplay = display;\n existing.project = project;\n }\n }\n }\n\n // Sort by most-recent match descending\n const sorted = [...bySession.values()].sort(\n (a, b) => b.lastMatchTs - a.lastMatchTs\n );\n\n return sorted.slice(0, maxResults);\n}\n","/**\n * main-resolver.ts\n *\n * `pai [<query>]` — topic-first session discovery and launcher (v0.10.0)\n *\n * Decision tree:\n * 1. No arg → show interactive picker of recent sessions (top 20 by mtime)\n * 2. UUID prefix → universal filesystem scan; auto-launch the match\n * 3. Known name → catalog name match; auto-launch immediately\n * 4. Free-text query → search history.jsonl; show candidates with excerpt; user picks\n *\n * The \"-y\" / \"--auto\" flag skips the interactive prompt and picks candidate #1.\n * \"pai <query> <N>\" also picks candidate #N directly.\n *\n * Launch logic (shared with goto.ts):\n * - Find best resumable UUID for the matched project dir\n * - Probe claude --resume to verify it's still valid\n * - If valid: claude --resume <uuid> --name <name> \"/Name <name>\\ngo\"\n * - If invalid: claude --name <name> \"/Name <name>\\ngo\" (fresh in same dir)\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport { createInterface } from \"node:readline\";\nimport { existsSync } from \"node:fs\";\nimport { realpathSync } from \"node:fs\";\nimport { spawnSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport { err, dim, warn, ok, header, bold, renderTable, fmtDate, shortenPath } from \"../utils.js\";\nimport {\n scanSessions,\n resolveSessionByNameOrId,\n fmtAge,\n type ScannedSession,\n} from \"../lib/session-scan.js\";\nimport { searchHistory, HISTORY_FILE, type SessionMatch } from \"../lib/history-search.js\";\nimport { fetchLiveSessions } from \"../lib/aibroker-client.js\";\n\n// ---------------------------------------------------------------------------\n// Probe helper (same as goto.ts)\n// ---------------------------------------------------------------------------\n\ninterface ProbeResult {\n ok: boolean;\n reason?: string;\n}\n\nfunction probeResume(uuid: string, cwd: string): ProbeResult {\n const result = spawnSync(\n \"claude\",\n [\"--resume\", uuid, \"--print\", \"--output-format=json\", \"_\"],\n {\n cwd,\n timeout: 5_000,\n env: process.env,\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\n }\n );\n\n if (result.error) {\n return { ok: false, reason: `spawn error: ${result.error.message}` };\n }\n\n const stderr = result.stderr?.toString(\"utf8\") ?? \"\";\n\n if (\n stderr.toLowerCase().includes(\"no conversation found\") ||\n stderr.toLowerCase().includes(\"session not found\")\n ) {\n return { ok: false, reason: \"No conversation found for this UUID\" };\n }\n\n if (result.status !== 0) {\n return {\n ok: false,\n reason: `claude exited ${result.status ?? \"signal\"}${stderr ? `: ${stderr.slice(0, 120).trim()}` : \"\"}`,\n };\n }\n\n return { ok: true };\n}\n\n// ---------------------------------------------------------------------------\n// Launch session\n// ---------------------------------------------------------------------------\n\n/**\n * Launch Claude for the given session. Handles resume probe + fallback to fresh.\n * Never returns on success (process.exit inside spawnSync block).\n */\nfunction launchSession(\n session: ScannedSession,\n allSessions: ScannedSession[],\n dryRun: boolean\n): void {\n // Find the best resumable UUID in this project dir\n let resumableUuid: string | undefined;\n let resumableSession: ScannedSession | undefined;\n\n if (session.resumable) {\n resumableUuid = session.uuid;\n resumableSession = session;\n } else if (session.encodedDir) {\n const sameProject = allSessions.filter(\n (s) => s.encodedDir === session.encodedDir && s.resumable\n );\n sameProject.sort((a, b) => b.mtime - a.mtime);\n if (sameProject.length > 0) {\n resumableSession = sameProject[0];\n resumableUuid = resumableSession.uuid;\n }\n }\n\n // Determine project dir\n const rawDir =\n session.clcDirectory ??\n session.registryRootPath ??\n session.decodedPath;\n\n let projectDir: string;\n try {\n projectDir = realpathSync(rawDir);\n } catch {\n console.error(\n err(\n `Session directory does not exist or cannot be resolved.\\n` +\n ` Path: ${rawDir}\\n` +\n ` The directory may have moved or been deleted.`\n )\n );\n process.exit(1);\n return;\n }\n\n const name = session.friendlyName ?? session.shortId;\n const promptArg = `/Name ${name}\\ngo`;\n\n if (dryRun) {\n if (resumableUuid) {\n console.log(\"\\n\" + chalk.bold(\"Dry run — would probe then exec (RESUME path):\") + \"\\n\");\n console.log(` cwd: ${chalk.cyan(projectDir)}`);\n console.log(` probe: claude --resume ${resumableUuid} --print --output-format=json \"_\"`);\n console.log(` argv: claude --resume ${resumableUuid} --name \"${name}\" \"/Name ${name}\\\\ngo\"`);\n console.log(` fallback: claude --name \"${name}\" \"/Name ${name}\\\\ngo\"`);\n } else {\n console.log(\"\\n\" + chalk.bold(\"Dry run — would exec (FRESH path):\") + \"\\n\");\n console.log(` cwd: ${chalk.cyan(projectDir)}`);\n console.log(` argv: claude --name \"${name}\" \"/Name ${name}\\\\ngo\"`);\n }\n console.log();\n return;\n }\n\n if (resumableUuid) {\n const probe = probeResume(resumableUuid, projectDir);\n if (probe.ok) {\n const result = spawnSync(\n \"claude\",\n [\"--resume\", resumableUuid, \"--name\", name, promptArg],\n { cwd: projectDir, stdio: \"inherit\", env: process.env }\n );\n if (result.error) {\n console.error(err(`Failed to launch claude: ${result.error.message}`));\n process.exit(1);\n }\n process.exit(result.status ?? 0);\n } else {\n process.stderr.write(\n chalk.yellow(\n `\\n Resume failed for ${resumableUuid.slice(0, 8)}: ${probe.reason ?? \"unknown error\"}\\n` +\n ` Starting fresh session in same directory.\\n\\n`\n )\n );\n const result = spawnSync(\n \"claude\",\n [\"--name\", name, promptArg],\n { cwd: projectDir, stdio: \"inherit\", env: process.env }\n );\n if (result.error) {\n console.error(err(`Failed to launch claude: ${result.error.message}`));\n process.exit(1);\n }\n process.exit(result.status ?? 0);\n }\n } else {\n const result = spawnSync(\n \"claude\",\n [\"--name\", name, promptArg],\n { cwd: projectDir, stdio: \"inherit\", env: process.env }\n );\n if (result.error) {\n console.error(err(`Failed to launch claude: ${result.error.message}`));\n process.exit(1);\n }\n process.exit(result.status ?? 0);\n }\n}\n\n// ---------------------------------------------------------------------------\n// History match → ScannedSession bridge\n// ---------------------------------------------------------------------------\n\n/**\n * Given a SessionMatch from history search, find the corresponding ScannedSession\n * (for launch). Falls back to a minimal synthetic session using the decodedPath\n * from the history entry's project field.\n */\nfunction matchToSession(\n match: SessionMatch,\n allSessions: ScannedSession[]\n): ScannedSession | null {\n if (!match.sessionId) return null;\n\n // Try catalog first (for friendlyName, registryRootPath, resumable status)\n const catalogMatch = allSessions.find((s) => s.uuid === match.sessionId);\n if (catalogMatch) return catalogMatch;\n\n // Not in catalog — synthesize a minimal session using the project path from history\n // The sessionId is UUID format, use it to find the top-level jsonl directly\n if (!match.project) return null;\n\n return {\n uuid: match.sessionId,\n shortId: match.sessionId.slice(0, 8),\n encodedDir: \"\",\n decodedPath: match.project,\n topLevelPath: \"\",\n topLevelSystemLines: 0,\n topLevelSize: 0,\n resumable: false,\n sessionStatus: \"transcript-only\",\n sessionJsonlPath: undefined,\n userLines: 0,\n lastUserPrompt: match.lastMatchDisplay.slice(0, 80),\n msgCount: 0,\n mtime: match.lastMatchTs,\n friendlyName: undefined,\n clcDirectory: undefined,\n registryRootPath: match.project,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Formatters\n// ---------------------------------------------------------------------------\n\nfunction fmtTs(ts: number): string {\n const d = new Date(ts);\n const pad = (n: number) => String(n).padStart(2, \"0\");\n return (\n `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ` +\n `${pad(d.getHours())}:${pad(d.getMinutes())}`\n );\n}\n\nfunction shortenProject(p: string, maxLen = 44): string {\n if (!p || p.length <= maxLen) return p || dim(\"—\");\n return \"…\" + p.slice(-(maxLen - 1));\n}\n\n// ---------------------------------------------------------------------------\n// Interactive picker prompt\n// ---------------------------------------------------------------------------\n\nasync function askForChoice(max: number): Promise<number | null> {\n return new Promise((resolve) => {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n rl.question(\n dim(`\\n Enter # to launch (1-${max}), or press Enter to cancel: `),\n (answer) => {\n rl.close();\n const n = parseInt(answer.trim(), 10);\n if (!isNaN(n) && n >= 1 && n <= max) {\n resolve(n);\n } else {\n resolve(null);\n }\n }\n );\n });\n}\n\n// ---------------------------------------------------------------------------\n// Main command\n// ---------------------------------------------------------------------------\n\nexport interface MainResolverOpts {\n auto?: boolean; // -y / --auto: pick #1 without prompting\n dryRun?: boolean; // --dry-run: show what would happen\n n?: string; // --n <count>: max candidates for history search\n}\n\nexport async function cmdMain(\n db: Database,\n query: string | undefined,\n pickN: number | undefined,\n opts: MainResolverOpts\n): Promise<void> {\n const maxResults = parseInt(opts.n ?? \"20\", 10);\n const allSessions = scanSessions(db, { limit: 500, filter: \"named\" });\n\n // -----------------------------------------------------------------------\n // Case 1: No query → recent session picker\n // -----------------------------------------------------------------------\n if (!query) {\n // Show live sessions from AIBroker + most recent disk sessions\n let liveSessions: Awaited<ReturnType<typeof fetchLiveSessions>> = [];\n try {\n liveSessions = await fetchLiveSessions();\n } catch {\n // AIBroker not running — show only disk sessions\n }\n\n const claudeLive = liveSessions.filter((s) => s.kind !== \"shell\");\n const recentDisk = allSessions.slice(0, maxResults);\n\n if (claudeLive.length === 0 && recentDisk.length === 0) {\n console.log(warn(\"No sessions found. Start Claude Code in a project directory first.\"));\n return;\n }\n\n // Live sessions section\n if (claudeLive.length > 0) {\n console.log(\"\\n\" + header(\"Live Sessions\") + \"\\n\");\n const liveHeaders = [\"#\", \"id\", \"name\", \"at prompt\"];\n const liveRows = claudeLive.map((s, i) => [\n dim(String(i + 1)),\n chalk.cyan(s.sessionId.slice(0, 8)),\n s.paiName ?? s.name ?? dim(\"—\"),\n s.atPrompt ? chalk.green(\"yes\") : chalk.dim(\"no\"),\n ]);\n console.log(renderTable(liveHeaders, liveRows));\n }\n\n // Recent disk sessions section\n if (recentDisk.length > 0) {\n console.log(\"\\n\" + header(\"Recent Sessions\") + \"\\n\");\n const diskHeaders = [\"#\", \"id\", \"age\", \"project\", \"last prompt\"];\n const diskRows = recentDisk.map((s, i) => {\n const snippet = s.lastUserPrompt.replace(/\\n+/g, \" \").trim().slice(0, 40);\n return [\n dim(String(i + 1)),\n chalk.cyan(s.shortId),\n dim(fmtAge(s.mtime)),\n dim(shortenProject(s.friendlyName ?? s.decodedPath, 30)),\n chalk.dim(snippet ? `\"${snippet}\"` : \"—\"),\n ];\n });\n console.log(renderTable(diskHeaders, diskRows));\n }\n\n console.log();\n console.log(dim(\" Resume a session: \") + chalk.white(\"pai <topic>\") + dim(\" or \") + chalk.white(\"pai <id>\"));\n console.log();\n return;\n }\n\n // -----------------------------------------------------------------------\n // Case 2: UUID prefix (8+ hex chars) → universal filesystem scan\n // -----------------------------------------------------------------------\n const UUID_PREFIX_RE = /^[0-9a-f-]{8,36}$/i;\n if (UUID_PREFIX_RE.test(query)) {\n // Try catalog first\n const byUuid = allSessions.filter((s) => s.uuid.startsWith(query.toLowerCase()));\n if (byUuid.length === 1) {\n launchSession(byUuid[0], allSessions, opts.dryRun ?? false);\n return;\n }\n if (byUuid.length > 1) {\n console.error(err(`UUID prefix \"${query}\" is ambiguous — ${byUuid.length} catalog matches.`));\n process.exitCode = 1;\n return;\n }\n // Fall through to name match / history search — UUID might be a partial word too\n }\n\n // -----------------------------------------------------------------------\n // Case 3: Known session name → auto-launch\n // -----------------------------------------------------------------------\n {\n const qLower = query.toLowerCase();\n const byExact = allSessions.filter(\n (s) => s.friendlyName && s.friendlyName.toLowerCase() === qLower\n );\n if (byExact.length >= 1) {\n launchSession(byExact[0], allSessions, opts.dryRun ?? false);\n return;\n }\n\n const byPartial = allSessions.filter(\n (s) => s.friendlyName && s.friendlyName.toLowerCase().includes(qLower)\n );\n if (byPartial.length === 1) {\n launchSession(byPartial[0], allSessions, opts.dryRun ?? false);\n return;\n }\n if (byPartial.length > 1) {\n // Multiple named sessions match — show as candidates\n console.log(\"\\n\" + header(`Sessions matching \"${query}\"`) + \"\\n\");\n const headers = [\"#\", \"id\", \"age\", \"name\", \"project\"];\n const rows = byPartial.slice(0, maxResults).map((s, i) => [\n dim(String(i + 1)),\n chalk.cyan(s.shortId),\n dim(fmtAge(s.mtime)),\n s.friendlyName ?? dim(\"—\"),\n dim(shortenProject(s.decodedPath, 36)),\n ]);\n console.log(renderTable(headers, rows));\n console.log();\n\n if (pickN !== undefined) {\n const idx = pickN - 1;\n if (idx >= 0 && idx < byPartial.length) {\n launchSession(byPartial[idx], allSessions, opts.dryRun ?? false);\n return;\n }\n console.error(err(`Invalid choice: ${pickN}`));\n process.exitCode = 1;\n return;\n }\n\n if (opts.auto) {\n launchSession(byPartial[0], allSessions, opts.dryRun ?? false);\n return;\n }\n\n const choice = await askForChoice(Math.min(byPartial.length, maxResults));\n if (choice !== null) {\n launchSession(byPartial[choice - 1], allSessions, opts.dryRun ?? false);\n }\n return;\n }\n }\n\n // -----------------------------------------------------------------------\n // Case 4: Free-text history search\n // -----------------------------------------------------------------------\n if (!existsSync(HISTORY_FILE)) {\n console.error(err(\"~/.claude/history.jsonl not found.\"));\n console.error(dim(\" No prompt history available for search.\"));\n process.exitCode = 1;\n return;\n }\n\n process.stderr.write(dim(` Searching prompt history for \"${query}\"...\\n`));\n const matches = await searchHistory(query, maxResults);\n\n if (matches.length === 0) {\n console.log(warn(`No sessions found matching \"${query}\".`));\n console.log(dim(` Try a shorter or different search term.`));\n console.log(dim(` Or run: `) + chalk.white(\"pai\") + dim(\" (no args) to see all recent sessions.\"));\n return;\n }\n\n console.log(\"\\n\" + header(`Sessions matching \"${query}\"`) + \"\\n\");\n const headers = [\"#\", \"id\", \"when\", \"project\", \"last matching prompt\"];\n const rows = matches.map((m, idx) => {\n const shortId = (m.sessionId ?? \"—\").slice(0, 8);\n const when = m.lastMatchTs > 0 ? fmtTs(m.lastMatchTs) : dim(\"—\");\n const project = shortenProject(m.project || \"—\");\n const snippet = m.lastMatchDisplay.replace(/\\n+/g, \" \").trim().slice(0, 48);\n const fullSnippet = m.lastMatchDisplay.replace(/\\n+/g, \" \").trim();\n const display = snippet.length < fullSnippet.length ? `\"${snippet}…\"` : `\"${snippet}\"`;\n return [\n dim(String(idx + 1)),\n chalk.cyan(shortId),\n when,\n dim(project),\n chalk.dim(display),\n ];\n });\n\n console.log(renderTable(headers, rows));\n console.log();\n\n // Direct pick by inline number\n if (pickN !== undefined) {\n const idx = pickN - 1;\n if (idx >= 0 && idx < matches.length) {\n const session = matchToSession(matches[idx], allSessions);\n if (!session) {\n console.error(err(\"Could not resolve session for launch (no project path).\"));\n process.exitCode = 1;\n return;\n }\n launchSession(session, allSessions, opts.dryRun ?? false);\n return;\n }\n console.error(err(`Invalid choice: ${pickN}`));\n process.exitCode = 1;\n return;\n }\n\n // Auto-pick first\n if (opts.auto) {\n const session = matchToSession(matches[0], allSessions);\n if (!session) {\n console.error(err(\"Could not resolve session for launch (no project path).\"));\n process.exitCode = 1;\n return;\n }\n launchSession(session, allSessions, opts.dryRun ?? false);\n return;\n }\n\n // Interactive pick\n const choice = await askForChoice(matches.length);\n if (choice !== null) {\n const session = matchToSession(matches[choice - 1], allSessions);\n if (!session) {\n console.error(err(\"Could not resolve session for launch (no project path).\"));\n process.exitCode = 1;\n return;\n }\n launchSession(session, allSessions, opts.dryRun ?? false);\n }\n}\n","#!/usr/bin/env node\n/**\n * PAI Knowledge OS — CLI entry point (v0.10.0 topic-first redesign)\n *\n * Top-level surface:\n * pai → recent sessions picker\n * pai <topic> → history search + candidate picker + launch\n * pai <uuid-prefix> → direct session resume via filesystem scan\n * pai cd <name> → cd to project directory (no Claude launch)\n * pai pause [all] → save state (or mass-pause every live session)\n * pai end → finalize session\n *\n * Power-user namespaces (still accessible, hidden from main help):\n * pai sessions ... → full session management\n * pai projects ... → project management\n * pai registry ... → registry maintenance\n * pai memory ... → memory engine\n */\n\nimport { Command } from \"commander\";\nimport { readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { openRegistry } from \"../registry/db.js\";\nimport type { Database } from \"better-sqlite3\";\nimport { registerProjectsCommands } from \"./commands/project/projects-index.js\";\nimport { findMovedPath } from \"./commands/project/commands.js\";\nimport { existsSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport { registerSessionsCommands } from \"./commands/session/sessions-index.js\";\nimport { registerSessionCleanupCommand } from \"./commands/session-cleanup.js\";\nimport { registerRegistryCommands } from \"./commands/registry.js\";\nimport { registerMemoryCommands } from \"./commands/memory.js\";\nimport { registerMcpCommands } from \"./commands/mcp.js\";\nimport { registerDaemonCommands } from \"./commands/daemon.js\";\nimport { registerBackupCommands } from \"./commands/backup.js\";\nimport { registerRestoreCommands } from \"./commands/restore.js\";\nimport { registerSetupCommand } from \"./commands/setup.js\";\nimport { registerObsidianCommands } from \"./commands/obsidian.js\";\nimport { registerZettelCommands } from \"./commands/zettel.js\";\nimport { registerObservationCommands } from \"./commands/observation.js\";\nimport { registerUpdateCommand } from \"./commands/update.js\";\nimport { registerNotifyCommands } from \"./commands/notify.js\";\nimport { registerTopicCommands } from \"./commands/topic.js\";\nimport { registerKgCommands } from \"./commands/kg.js\";\nimport { registerDbCommands } from \"./commands/db.js\";\nimport { err, warn, ok, dim, shortenPath, encodeDir, now } from \"./utils.js\";\nimport { cmdPause } from \"./commands/session/pause.js\";\nimport { cmdEnd } from \"./commands/session/end.js\";\nimport { cmdGoto } from \"./commands/session/goto.js\";\nimport { cmdPauseAll } from \"./commands/session/pause-all.js\";\nimport { cmdList as cmdNotesList } from \"./commands/session/commands.js\";\nimport { resolveIdentifier } from \"./commands/project/helpers.js\";\nimport { cmdFind } from \"./commands/find.js\";\nimport { cmdMain } from \"./commands/main-resolver.js\";\n\n// ---------------------------------------------------------------------------\n// Version resolution\n// ---------------------------------------------------------------------------\n\nfunction getVersion(): string {\n try {\n const __dirname = dirname(fileURLToPath(import.meta.url));\n // Walk up from dist/cli/ or src/cli/ to find package.json\n const pkgPath = join(__dirname, \"../../package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as { version?: string };\n return pkg.version ?? \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Lazy database singleton — opened on first command that needs it\n// ---------------------------------------------------------------------------\n\nlet _db: Database | null = null;\n\nfunction getDb(): Database {\n if (!_db) {\n try {\n _db = openRegistry();\n } catch (e) {\n console.error(err(`Failed to open PAI registry: ${e}`));\n process.exit(1);\n }\n }\n return _db;\n}\n\n// ---------------------------------------------------------------------------\n// Program definition\n// ---------------------------------------------------------------------------\n\nconst program = new Command();\n\nprogram\n .name(\"pai\")\n .description(\"PAI Knowledge OS — Personal AI Infrastructure CLI\")\n .version(getVersion(), \"-V, --version\", \"Print version and exit\")\n .addHelpText(\n \"after\",\n `\nUsage:\n pai Show recent sessions (interactive picker)\n pai <topic> Find sessions by topic and launch the chosen one\n pai <uuid-prefix> Resume a specific session by UUID\n pai cd <name> cd to a project directory (no Claude launch)\n pai pause [all] Save state (or mass-pause every live session)\n pai end Finalize session: save state + mark note Completed\n\nExamples:\n pai mdf Find all sessions where you worked on MDF\n pai solar panels Free-text search across your prompt history\n pai 81c5c3dc Resume session by UUID prefix\n pai See the 20 most recent sessions\n\nPower-user namespaces (run \"pai sessions --help\" etc. for details):\n pai sessions ... Full session management (list, goto, info, ...)\n pai projects ... Project management (cd, rebind, list, ...)\n pai registry ... Registry maintenance (scan, ...)\n pai memory ... Memory engine (index, search, ...)\n\nShell integration:\n eval \"$(pai shell-init)\" # Add to ~/.zshrc for pai cd to work`\n );\n\n// ---------------------------------------------------------------------------\n// pai projects (canonical plural namespace)\n// ---------------------------------------------------------------------------\n\nconst projectsCmd = program\n .command(\"projects\")\n .description(\"Manage registered projects (list, cd, add, info, ...)\");\n\nregisterProjectsCommands(projectsCmd, getDb);\n\n// ---------------------------------------------------------------------------\n// pai sessions (canonical plural namespace)\n// Bare `pai sessions` runs the default `list` subcommand.\n// ---------------------------------------------------------------------------\n\nconst sessionsCmd = program\n .command(\"sessions\")\n .description(\"Session management (list, goto, pause, ...)\");\n\nregisterSessionsCommands(sessionsCmd, getDb);\nregisterSessionCleanupCommand(sessionsCmd, getDb);\n\n// ---------------------------------------------------------------------------\n// pai registry\n// ---------------------------------------------------------------------------\n\nconst registryCmd = program\n .command(\"registry\")\n .description(\"Registry maintenance: scan, migrate, stats, rebuild\");\n\nregisterRegistryCommands(registryCmd, getDb);\n\n// ---------------------------------------------------------------------------\n// pai memory\n// ---------------------------------------------------------------------------\n\nconst memoryCmd = program\n .command(\"memory\")\n .description(\"Memory engine: index, search, and status\");\n\nregisterMemoryCommands(memoryCmd, getDb);\n\n// ---------------------------------------------------------------------------\n// pai mcp\n// ---------------------------------------------------------------------------\n\nconst mcpCmd = program\n .command(\"mcp\")\n .description(\"MCP server management: install and status\");\n\nregisterMcpCommands(mcpCmd);\n\n// ---------------------------------------------------------------------------\n// pai daemon\n// ---------------------------------------------------------------------------\n\nconst daemonCmd = program\n .command(\"daemon\")\n .description(\"PAI daemon management: serve, status, restart, install, uninstall, logs\");\n\nregisterDaemonCommands(daemonCmd);\n\n// ---------------------------------------------------------------------------\n// pai backup / pai restore\n// ---------------------------------------------------------------------------\n\nregisterBackupCommands(program);\nregisterRestoreCommands(program);\n\n// ---------------------------------------------------------------------------\n// pai setup\n// ---------------------------------------------------------------------------\n\nregisterSetupCommand(program);\n\n// ---------------------------------------------------------------------------\n// pai update\n// ---------------------------------------------------------------------------\n\nregisterUpdateCommand(program);\n\n// ---------------------------------------------------------------------------\n// pai notify\n// ---------------------------------------------------------------------------\n\nconst notifyCmd = program\n .command(\"notify\")\n .description(\"Notification config: status, get, set, test, send\");\n\nregisterNotifyCommands(notifyCmd);\n\n// ---------------------------------------------------------------------------\n// pai topic\n// ---------------------------------------------------------------------------\n\nconst topicCmd = program\n .command(\"topic\")\n .description(\"Topic shift detection: check whether context has drifted to a different project\");\n\nregisterTopicCommands(topicCmd);\n\n// ---------------------------------------------------------------------------\n// pai kg\n// ---------------------------------------------------------------------------\n\nconst kgCmd = program\n .command(\"kg\")\n .description(\"Temporal knowledge graph: backfill, query, list, stats\");\n\nregisterKgCommands(kgCmd);\n\n// ---------------------------------------------------------------------------\n// pai db\n// ---------------------------------------------------------------------------\n\nconst dbCmd = program\n .command(\"db\")\n .description(\"Database inspection: query, tables, schema (sqlite or postgres)\");\n\nregisterDbCommands(dbCmd);\n\n// ---------------------------------------------------------------------------\n// pai obsidian\n// ---------------------------------------------------------------------------\n\nconst obsidianCmd = program\n .command(\"obsidian\")\n .description(\"Obsidian vault: sync project notes, view status, open in Obsidian\");\n\nregisterObsidianCommands(obsidianCmd, getDb);\n\n// ---------------------------------------------------------------------------\n// pai zettel\n// ---------------------------------------------------------------------------\n\nconst zettelCmd = program\n .command(\"zettel\")\n .description(\"Zettelkasten intelligence: explore, surprise, converse, themes, health, suggest\");\n\nregisterZettelCommands(zettelCmd, getDb);\n\n// ---------------------------------------------------------------------------\n// pai observation\n// ---------------------------------------------------------------------------\n\nconst observationCmd = program\n .command(\"observation\")\n .description(\"Observation capture: list, search, and stats\");\n\nregisterObservationCommands(observationCmd);\n\n// ---------------------------------------------------------------------------\n// DAILY VERBS — top-level short forms\n// ---------------------------------------------------------------------------\n\n// pai pause [all] [--dry-run] [--exit] [--wait <ms>]\n// When called as `pai pause all`, pauses every live session via AIBroker.\n// When called as `pai pause`, saves state for the current session.\nprogram\n .command(\"pause [target]\")\n .description(\n \"Save state and display safe-exit instructions for the current session.\\n\" +\n \"Writes a ## Continue checkpoint to the project's TODO.md.\\n\" +\n \"Use `pai pause all` to pause every live session via AIBroker.\\n\" +\n \"Long form: pai sessions pause\"\n )\n .option(\"--dry-run\", \"Preview changes without writing them\")\n .option(\"--exit\", \"(pause all only) Also send /exit to each session after pausing\")\n .option(\"--wait <ms>\", \"(pause all only) Milliseconds to wait before /exit (default: 5000)\", \"5000\")\n .action(async (target: string | undefined, opts: { dryRun?: boolean; exit?: boolean; wait?: string }) => {\n if (target === \"all\") {\n await cmdPauseAll({\n exit: opts.exit,\n dryRun: opts.dryRun,\n wait: opts.wait !== undefined ? parseInt(opts.wait, 10) : undefined,\n });\n } else {\n if (target !== undefined) {\n console.error(`Unknown target: ${target}. Did you mean 'pai pause all'?`);\n process.exitCode = 1;\n return;\n }\n cmdPause(getDb(), { dryRun: opts.dryRun });\n }\n });\n\n// pai end [--dry-run]\nprogram\n .command(\"end\")\n .description(\n \"Finalize a session: save state, mark note Completed, display safe-exit instructions.\\n\" +\n \"Long form: pai sessions end\"\n )\n .option(\"--dry-run\", \"Preview all changes without writing them\")\n .action((opts: { dryRun?: boolean }) => {\n cmdEnd(getDb(), opts);\n });\n\n// pai resume <name> [--dry-run] — compat alias for pai sessions goto\n// Power users can also use: pai sessions goto <name>\nprogram\n .command(\"resume <name>\")\n .description(\n \"Go to a session by name or UUID: resume if resumable, start fresh otherwise.\\n\" +\n \"Also: pai <name> (shorter) | pai sessions goto <name> (long form)\"\n )\n .option(\"--dry-run\", \"Print the exact argv and cwd, then exit without launching\")\n .action(\n (name: string, opts: { dryRun?: boolean }) => {\n cmdGoto(getDb(), name, { dryRun: opts.dryRun });\n }\n );\n\n// pai cd <identifier> — prints path; shell wrapper does the actual cd\nprogram\n .command(\"cd <identifier>\")\n .description(\n \"cd to a project directory (shell wrapper handles the actual cd).\\n\" +\n \"Long form: pai projects cd <identifier>\\n\" +\n \"The shell function installed by pai shell-init intercepts this command\\n\" +\n \"and calls builtin cd with the resolved path.\\n\" +\n \"Auto-detects moved projects when the registered path no longer exists.\"\n )\n .action((identifier: string) => {\n const db = getDb();\n const project = resolveIdentifier(db, identifier);\n if (!project) {\n console.error(`Project not found: ${identifier}`);\n process.exit(1);\n return;\n }\n\n if (existsSync(project.root_path)) {\n process.stdout.write(project.root_path + \"\\n\");\n return;\n }\n\n // Path missing — search for moved location\n process.stderr.write(\n warn(`Path not found: ${project.root_path}\\n`) +\n dim(\" Searching for moved location...\\n\")\n );\n\n const result = findMovedPath(project.root_path);\n\n if (result.found) {\n const newPath = result.found;\n const newEncoded = encodeDir(newPath);\n const ts = now();\n db.prepare(\n \"UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?\"\n ).run(newPath, newEncoded, ts, project.id);\n process.stderr.write(\n ok(`Project moved: ${shortenPath(project.root_path, 50)}\\n`) +\n dim(` → ${newPath}\\n`) +\n ok(\"Registry updated.\\n\")\n );\n process.stdout.write(newPath + \"\\n\");\n return;\n }\n\n if (result.ambiguous) {\n process.stderr.write(\n warn(`Multiple directories named \"${basename(project.root_path)}\" found:\\n`)\n );\n for (const candidate of result.ambiguous!) {\n process.stderr.write(dim(` ${candidate}\\n`));\n }\n process.stderr.write(\n dim(`\\n Disambiguate with: pai projects rebind ${project.slug} <path>\\n`)\n );\n process.exitCode = 1;\n return;\n }\n\n process.stderr.write(\n err(\n `Project \"${project.slug}\" root_path \"${project.root_path}\" does not exist on disk\\n` +\n ` and no folder named \"${basename(project.root_path)}\" was found in scan dirs.\\n`\n ) +\n dim(` Fix with: pai projects rebind ${project.slug} <new-path>\\n`)\n );\n process.exitCode = 1;\n });\n\n// Note: `pai sessions` and `pai projects` are now defined as the canonical\n// namespaces above (with `isDefault: true` on their `list` subcommands).\n// Bare invocation (`pai sessions`, `pai projects`) triggers the default list.\n\n// ---------------------------------------------------------------------------\n// pai notes — markdown session notes (power user, still accessible)\n// ---------------------------------------------------------------------------\n\nconst notesCmd = program\n .command(\"notes [project-slug]\")\n .description(\n \"Markdown session notes — list notes, optionally filtered to a project.\"\n )\n .option(\"--limit <n>\", \"Maximum number of notes to show\", \"20\")\n .option(\"--status <status>\", \"Filter by status: open | completed | compacted\")\n .action(\n (\n projectSlug: string | undefined,\n opts: { limit?: string; status?: string }\n ) => {\n cmdNotesList(getDb(), projectSlug, opts);\n }\n );\n\n// pai notes list [project-slug] — explicit sub-action (same behaviour)\nnotesCmd\n .command(\"list [project-slug]\")\n .description(\"List markdown session notes (same as: pai notes)\")\n .option(\"--limit <n>\", \"Maximum number of notes to show\", \"20\")\n .option(\"--status <status>\", \"Filter by status: open | completed | compacted\")\n .action(\n (\n projectSlug: string | undefined,\n opts: { limit?: string; status?: string }\n ) => {\n cmdNotesList(getDb(), projectSlug, opts);\n }\n );\n\n// ---------------------------------------------------------------------------\n// pai find <query> — explicit history search (power user alias)\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"find <query>\")\n .description(\n \"Search prompt history for matching sessions.\\n\" +\n \"Same as: pai <query> (the default command does history search too)\"\n )\n .option(\"-n, --n <count>\", \"Maximum number of sessions to show\", \"20\")\n .option(\"--json\", \"Output as JSON array\")\n .action(async (query: string, opts: { n?: string; json?: boolean }) => {\n await cmdFind(query, opts);\n });\n\n// ---------------------------------------------------------------------------\n// pai shell-init — emit shell function for eval \"$(pai shell-init)\"\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"shell-init\")\n .description(\n \"Emit shell integration code. Add to ~/.zshrc:\\n\" +\n ' eval \"$(pai shell-init)\"'\n )\n .action(() => {\n // The shell function intercepts commands that need a real `cd`:\n // pai cd <name> → resolves via pai cd, then builtin cd\n // pai projects cd <name> → same (long form)\n // Everything else is passed through to the real pai binary unchanged.\n process.stdout.write(\n `# PAI shell integration — generated by: pai shell-init\npai() {\n if [[ \"$1\" == \"cd\" ]]; then\n local dir=$(command pai cd \"$2\" 2>/dev/null)\n if [[ -n \"$dir\" && -d \"$dir\" ]]; then\n builtin cd \"$dir\"\n echo \"-> $dir\"\n else\n echo \"Project not found: $2\" >&2\n return 1\n fi\n elif [[ \"$1\" == \"projects\" && \"$2\" == \"cd\" ]]; then\n local dir=$(command pai projects cd \"$3\" 2>/dev/null)\n if [[ -n \"$dir\" && -d \"$dir\" ]]; then\n builtin cd \"$dir\"\n echo \"-> $dir\"\n else\n echo \"Project not found: $3\" >&2\n return 1\n fi\n else\n command pai \"$@\"\n fi\n}\n`\n );\n });\n\n// ---------------------------------------------------------------------------\n// pai [query] [pick] — DEFAULT COMMAND: topic-first session resolver (v0.10.0)\n//\n// Must be registered LAST so named subcommands take priority.\n// Commander matches named commands first; if nothing matches, this catches it.\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"query [query] [pick]\", { isDefault: true, hidden: true })\n .description(\n \"Find and launch a session by topic, name, or UUID.\\n\" +\n \"No arg → recent sessions picker\\n\" +\n \"Topic → history search → candidate list → pick #\\n\" +\n \"UUID → direct resume via filesystem scan\"\n )\n .option(\"-y, --auto\", \"Auto-pick #1 without prompting\")\n .option(\"--dry-run\", \"Print what would happen without launching\")\n .option(\"-n, --n <count>\", \"Max candidates for history search\", \"20\")\n .action(async (query: string | undefined, pick: string | undefined, opts: { auto?: boolean; dryRun?: boolean; n?: string }) => {\n const pickN = pick !== undefined ? parseInt(pick, 10) : undefined;\n await cmdMain(getDb(), query, pickN, opts);\n });\n\n// ---------------------------------------------------------------------------\n// Error handling — show clean message instead of stack trace for user errors\n// ---------------------------------------------------------------------------\n\nprogram.configureOutput({\n writeErr: (str) => process.stderr.write(err(str)),\n});\n\nprogram.exitOverride((error) => {\n if (error.code === \"commander.helpDisplayed\" || error.code === \"commander.version\") {\n process.exit(0);\n }\n if (\n error.code === \"commander.missingArgument\" ||\n error.code === \"commander.unknownOption\" ||\n error.code === \"commander.unknownCommand\"\n ) {\n // Commander has already printed the message\n process.exit(1);\n }\n // For unexpected errors, show message but not stack trace\n console.error(err(error.message));\n process.exit(1);\n});\n\n// ---------------------------------------------------------------------------\n// Parse\n// ---------------------------------------------------------------------------\n\nprogram.parse(process.argv);\n"],"mappings":"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"}