@tekmidian/pai 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +201 -56
- package/FEATURE.md +2 -2
- package/dist/cli/index.mjs +100 -3
- package/dist/cli/index.mjs.map +1 -1
- package/dist/daemon-mcp/index.mjs +37 -6
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/skills/Art/SKILL.md +32 -0
- package/dist/skills/Createskill/SKILL.md +23 -0
- package/dist/skills/Journal/SKILL.md +34 -0
- package/dist/skills/Name/SKILL.md +12 -0
- package/dist/skills/Observability/SKILL.md +25 -0
- package/dist/skills/Plan/SKILL.md +28 -0
- package/dist/skills/Research/SKILL.md +30 -0
- package/dist/skills/Review/SKILL.md +58 -0
- package/dist/skills/Route/SKILL.md +14 -0
- package/dist/skills/SearchHistory/SKILL.md +31 -0
- package/dist/skills/Sessions/SKILL.md +28 -0
- package/dist/skills/Share/SKILL.md +35 -0
- package/dist/skills/StoryExplanation/SKILL.md +21 -0
- package/dist/skills/VaultConnect/SKILL.md +10 -0
- package/dist/skills/VaultContext/SKILL.md +12 -0
- package/dist/skills/VaultEmerge/SKILL.md +10 -0
- package/dist/skills/VaultOrphans/SKILL.md +10 -0
- package/dist/skills/VaultTrace/SKILL.md +10 -0
- package/package.json +3 -2
- package/scripts/build-skill-stubs.mjs +202 -0
package/dist/cli/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["getProject","upsertTag","cmdList","cmdInfo","cmdTag","upsertTag","getProject","cmdHealth","getProject","toTitleCase","cmdList","getProject","toTitleCase","findClaudeNotesDir","slugify","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","CONFIG_FILE","CONFIG_FILE","CONFIG_FILE","cmdStatus","makeClient"],"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/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/commands/session/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/utils.ts","../../src/cli/commands/registry/scan.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/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/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 — plus private helpers levenshtein,\n * containsIgnoreCase, and findProjectNotesDirs.\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 } 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\";\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// 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 }\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 if (opts.status) {\n where.push(\"p.status = ?\");\n params.push(opts.status);\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 console.log(dim(` ${rows.length} project(s)`));\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\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 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 process.stdout.write(partial[0].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","/**\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 project` sub-commands.\n * Imports from focused sub-modules and wires up CLI options.\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} from \"./commands.js\";\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 registerProjectCommands(\n projectCmd: Command,\n getDb: () => Database\n): void {\n // pai project add <path>\n projectCmd\n .command(\"add <path>\")\n .description(\"Register a project directory in the PAI registry\")\n .option(\"--slug <slug>\", \"Override auto-generated slug\")\n .option(\n \"--type <type>\",\n \"Project type: local | central | obsidian-linked | external\",\n \"local\"\n )\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 project list\n projectCmd\n .command(\"list\")\n .description(\"List registered projects\")\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: { status?: string; tag?: string; type?: string }) => {\n cmdList(getDb(), opts);\n });\n\n // pai project info <slug>\n projectCmd\n .command(\"info <slug>\")\n .description(\"Show full details for a project\")\n .action((slug: string) => {\n cmdInfo(getDb(), slug);\n });\n\n // pai project archive <slug>\n projectCmd\n .command(\"archive <slug>\")\n .description(\"Archive a project\")\n .action((slug: string) => {\n cmdArchive(getDb(), slug);\n });\n\n // pai project unarchive <slug>\n projectCmd\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 project move <slug> <new-path>\n projectCmd\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 project tag <slug> <tags...>\n projectCmd\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 project alias <slug> <alias>\n projectCmd\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 project edit <slug>\n projectCmd\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 project cd <slug-or-number>\n projectCmd\n .command(\"cd <identifier>\")\n .description(\n \"Print the root path for a project (use with: cd $(pai project cd <id>))\"\n )\n .action((identifier: string) => {\n const project = resolveIdentifier(getDb(), identifier);\n if (!project) {\n console.error(`Project not found: ${identifier}`);\n process.exit(1);\n }\n process.stdout.write(project.root_path + \"\\n\");\n });\n\n // pai project detect [path]\n projectCmd\n .command(\"detect [path]\")\n .description(\n \"Detect which registered project the given path (or CWD) belongs to\"\n )\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 project health\n projectCmd\n .command(\"health\")\n .description(\n \"Audit all registered projects: check which paths still exist, find moved/dead projects\"\n )\n .option(\n \"--fix\",\n \"Auto-remediate where possible (update moved paths, archive dead zero-session projects)\"\n )\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 project consolidate <slug-or-number>\n projectCmd\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 project promote\n projectCmd\n .command(\"promote\")\n .description(\"Promote a session note into a new standalone project\")\n .requiredOption(\n \"--from-session <path>\",\n \"Path to the session note markdown file\"\n )\n .requiredOption(\n \"--to <path>\",\n \"Directory path for the new project (must not exist)\"\n )\n .option(\n \"--name <name>\",\n \"Display name for the new project (derived from filename if omitted)\"\n )\n .action((opts: { fromSession: string; to: string; name?: string }) => {\n cmdPromote(getDb(), opts);\n });\n\n // pai project go <query>\n projectCmd\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 project go <query>)\\n\" +\n \"Or set a shell alias: alias pcd='cd $(pai project go)'\"\n )\n .action((query: string) => {\n cmdGo(getDb(), query);\n });\n\n // pai project name <slug-or-number> <shortname>\n projectCmd\n .command(\"name <identifier> <shortname>\")\n .description(\n \"Give a project a short name for quick access (used by AIBroker to launch sessions)\"\n )\n .option(\n \"--permission <level>\",\n \"Permission level: full | trusted | default (or raw CLI flags)\"\n )\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 project unname <shortname>\n projectCmd\n .command(\"unname <shortname>\")\n .description(\"Remove a project's short name\")\n .action((shortname: string) => {\n cmdUnname(getDb(), shortname);\n });\n\n // pai project names\n projectCmd\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 project config [identifier]\n projectCmd\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 \"Use --defaults to manage global defaults for new sessions.\"\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(\n \"--preset <name>\",\n \"Apply a permission preset: full | trusted | default\"\n )\n .option(\n \"--defaults\",\n \"Manage global session defaults instead of a project\"\n )\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 * Commander registration for all `pai session` sub-commands.\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\";\n\nexport function registerSessionCommands(\n sessionCmd: Command,\n getDb: () => Database\n): void {\n // pai session list [project-slug]\n sessionCmd\n .command(\"list [project-slug]\")\n .description(\"List sessions, optionally filtered to a single project\")\n .option(\"--limit <n>\", \"Maximum number of sessions 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 cmdList(getDb(), projectSlug, opts);\n }\n );\n\n // pai session info <project-slug> <number>\n sessionCmd\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 session rename <project-slug> <number> <new-slug>\n sessionCmd\n .command(\"rename <project-slug> <number> <new-slug>\")\n .description(\n \"Rename a session note — updates file on disk, H1 title, and registry\"\n )\n .action((projectSlug: string, number: string, newSlug: string) => {\n cmdRename(getDb(), projectSlug, number, newSlug);\n });\n\n // pai session slug <project-slug> <number|latest>\n sessionCmd\n .command(\"slug <project-slug> <number>\")\n .description(\n \"Generate a descriptive slug from the session JSONL transcript\"\n )\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 session tag <project-slug> <number> [tags...]\n sessionCmd\n .command(\"tag <project-slug> <number> [tags...]\")\n .description(\n \"Set or show tags on a session. Tags can be space-separated or comma-separated.\"\n )\n .action((projectSlug: string, number: string, tags: string[]) => {\n cmdTag(getDb(), projectSlug, number, tags);\n });\n\n // pai session route <project-slug> <number> <target-project>\n sessionCmd\n .command(\"route <project-slug> <number> <target-project>\")\n .description(\n \"Create a cross-reference link from a session to a target project\"\n )\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 session handover [project-slug] [session-id]\n sessionCmd\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 \"Records the last session identifier, timestamp, and working directory\\n\" +\n \"so the next session can resume from the correct context.\"\n )\n .action(\n (projectSlug: string | undefined, sessionId: string | undefined) => {\n cmdHandover(getDb(), projectSlug, sessionId);\n }\n );\n\n // pai session checkpoint <message>\n sessionCmd\n .command(\"checkpoint <message>\")\n .description(\n \"Append a timestamped checkpoint to the active session note.\\n\" +\n \"Designed for hooks (PostToolUse, UserPromptSubmit) — fast and silent.\\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 session active [--minutes N] [--json]\n sessionCmd\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 session auto-route [--cwd path] [--context \"text\"] [--json]\n sessionCmd\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 \"Designed for use in CLAUDE.md session-start hooks.\"\n )\n .option(\n \"--cwd <path>\",\n \"Working directory to detect from (default: process.cwd())\"\n )\n .option(\n \"--context <text>\",\n \"Conversation context for topic-based fallback routing\"\n )\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","/** Shared database helpers for registry command operations. */\n\nimport type { Database } from \"better-sqlite3\";\nimport { now } from \"../../utils.js\";\n\n/**\n * Upsert a project row. Returns { id, isNew }.\n *\n * Matching priority:\n * 1. root_path — most reliable; handles slug collisions\n * 2. encoded_dir — Claude project dirs are canonical\n * 3. Insert with suffix-deduplication on slug collision\n */\nexport function upsertProject(\n db: Database,\n slug: string,\n rootPath: string,\n encodedDir: string\n): { id: number; isNew: boolean } {\n const ts = now();\n\n const byPath = db\n .prepare(\"SELECT id FROM projects WHERE root_path = ?\")\n .get(rootPath) as { id: number } | undefined;\n\n if (byPath) {\n const encodedOwner = db\n .prepare(\"SELECT id FROM projects WHERE encoded_dir = ?\")\n .get(encodedDir) as { id: number } | undefined;\n\n if (!encodedOwner || encodedOwner.id === byPath.id) {\n db.prepare(\n \"UPDATE projects SET encoded_dir = ?, updated_at = ? WHERE id = ?\"\n ).run(encodedDir, ts, byPath.id);\n }\n return { id: byPath.id, isNew: false };\n }\n\n const byEncoded = db\n .prepare(\"SELECT id FROM projects WHERE encoded_dir = ?\")\n .get(encodedDir) as { id: number } | undefined;\n\n if (byEncoded) {\n const pathOwner = db\n .prepare(\"SELECT id FROM projects WHERE root_path = ?\")\n .get(rootPath) as { id: number } | undefined;\n\n if (!pathOwner || pathOwner.id === byEncoded.id) {\n db.prepare(\n \"UPDATE projects SET root_path = ?, updated_at = ? WHERE id = ?\"\n ).run(rootPath, ts, byEncoded.id);\n }\n return { id: byEncoded.id, isNew: false };\n }\n\n // Insert — deduplicate slug with numeric suffix if needed.\n let finalSlug = slug;\n let attempt = 0;\n while (true) {\n const conflict = db\n .prepare(\"SELECT id FROM projects WHERE slug = ?\")\n .get(finalSlug) as { id: number } | undefined;\n if (!conflict) break;\n attempt++;\n finalSlug = `${slug}-${attempt}`;\n }\n\n const result = db\n .prepare(\n `INSERT OR IGNORE INTO projects\n (slug, display_name, root_path, encoded_dir, type, status, created_at, updated_at)\n VALUES (?, ?, ?, ?, 'local', 'active', ?, ?)`\n )\n .run(finalSlug, finalSlug, rootPath, encodedDir, ts, ts);\n\n if (result.changes === 0) {\n const fallback =\n (db.prepare(\"SELECT id FROM projects WHERE encoded_dir = ?\").get(encodedDir) as { id: number } | undefined) ??\n (db.prepare(\"SELECT id FROM projects WHERE root_path = ?\").get(rootPath) as { id: number } | undefined);\n\n if (fallback) {\n return { id: fallback.id, isNew: false };\n }\n\n throw new Error(\n `upsertProject: INSERT OR IGNORE was suppressed but no matching row found ` +\n `for root_path=${rootPath} encoded_dir=${encodedDir}`\n );\n }\n\n return { id: result.lastInsertRowid as number, isNew: true };\n}\n\n/** Upsert a session note. Returns true if newly inserted. */\nexport function upsertSession(\n db: Database,\n projectId: number,\n number: number,\n date: string,\n slug: string,\n title: string,\n filename: string\n): boolean {\n const existing = db\n .prepare(\"SELECT id FROM sessions WHERE project_id = ? AND number = ?\")\n .get(projectId, number);\n\n if (existing) return false;\n\n const ts = now();\n db.prepare(\n `INSERT INTO sessions\n (project_id, number, date, slug, title, filename, status, created_at)\n VALUES (?, ?, ?, ?, ?, ?, 'completed', ?)`\n ).run(projectId, number, date, slug, title, filename, ts);\n\n return true;\n}\n","/** Registry scan command: walk ~/.claude/projects/ and populate the registry. */\n\nimport { existsSync, readdirSync, statSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join, basename, resolve } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { ok, warn, err, dim, bold } from \"../../utils.js\";\nimport { encodeDir } from \"../../utils.js\";\nimport { decodeEncodedDir, slugify, parseSessionFilename, buildEncodedDirMap } from \"../../../registry/migrate.js\";\nimport { ensurePaiMarker, discoverPaiMarkers } from \"../../../registry/pai-marker.js\";\nimport { upsertProject, upsertSession } from \"./utils.js\";\nimport type { Database } from \"better-sqlite3\";\n\n// ---------------------------------------------------------------------------\n// Config helpers\n// ---------------------------------------------------------------------------\n\nconst CLAUDE_PROJECTS_DIR = join(homedir(), \".claude\", \"projects\");\nconst PAI_CONFIG_DIR = join(homedir(), \".pai\");\nconst PAI_CONFIG_FILE = join(PAI_CONFIG_DIR, \"config.json\");\n\ninterface PaiConfig {\n scan_dirs: string[];\n}\n\nexport function loadScanConfig(): PaiConfig {\n if (!existsSync(PAI_CONFIG_FILE)) return { scan_dirs: [] };\n try {\n return JSON.parse(readFileSync(PAI_CONFIG_FILE, \"utf8\")) as PaiConfig;\n } catch {\n return { scan_dirs: [] };\n }\n}\n\nexport function saveScanConfig(config: PaiConfig): void {\n mkdirSync(PAI_CONFIG_DIR, { recursive: true });\n writeFileSync(PAI_CONFIG_FILE, JSON.stringify(config, null, 2) + \"\\n\", \"utf8\");\n}\n\nexport function resolveHome(p: string): string {\n if (p.startsWith(\"~/\")) return join(homedir(), p.slice(2));\n return resolve(p);\n}\n\n// ---------------------------------------------------------------------------\n// File discovery\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively find all .md files in a directory, including YYYY/MM subdirectories.\n * Returns filenames (basename only).\n */\nexport function findNoteFiles(dir: string): string[] {\n const results: string[] = [];\n if (!existsSync(dir)) return results;\n\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.isFile() && entry.name.endsWith(\".md\")) {\n results.push(entry.name);\n } else if (entry.isDirectory() && /^\\d{4}$/.test(entry.name)) {\n const yearDir = join(dir, entry.name);\n for (const monthEntry of readdirSync(yearDir, { withFileTypes: true })) {\n if (monthEntry.isDirectory() && /^\\d{2}$/.test(monthEntry.name)) {\n const monthDir = join(yearDir, monthEntry.name);\n for (const noteEntry of readdirSync(monthDir, { withFileTypes: true })) {\n if (noteEntry.isFile() && noteEntry.name.endsWith(\".md\")) {\n results.push(noteEntry.name);\n }\n }\n }\n }\n }\n }\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// Scan result type\n// ---------------------------------------------------------------------------\n\nexport interface ScanResult {\n projectsScanned: number;\n projectsNew: number;\n projectsUpdated: number;\n sessionsScanned: number;\n sessionsNew: number;\n skipped: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Core scan logic\n// ---------------------------------------------------------------------------\n\nexport function performScan(db: Database): ScanResult {\n const result: ScanResult = {\n projectsScanned: 0,\n projectsNew: 0,\n projectsUpdated: 0,\n sessionsScanned: 0,\n sessionsNew: 0,\n skipped: [],\n };\n\n if (!existsSync(CLAUDE_PROJECTS_DIR)) {\n throw new Error(`Claude projects directory not found: ${CLAUDE_PROJECTS_DIR}`);\n }\n\n const entries = readdirSync(CLAUDE_PROJECTS_DIR).filter((name) => {\n const full = join(CLAUDE_PROJECTS_DIR, name);\n return statSync(full).isDirectory();\n });\n\n const lookupMap = buildEncodedDirMap();\n\n for (const encodedDir of entries) {\n const rootPath = decodeEncodedDir(encodedDir, lookupMap);\n\n if (!existsSync(rootPath)) {\n result.skipped.push(`${encodedDir} (decoded: ${rootPath} — path not found on disk)`);\n result.projectsScanned++;\n continue;\n }\n\n const slug = slugify(basename(rootPath) || encodedDir);\n const { id, isNew } = upsertProject(db, slug, rootPath, encodedDir);\n\n result.projectsScanned++;\n if (isNew) result.projectsNew++;\n else result.projectsUpdated++;\n\n try {\n ensurePaiMarker(rootPath, slug);\n } catch {\n // Non-fatal\n }\n\n const claudeNotesDir = join(CLAUDE_PROJECTS_DIR, encodedDir, \"Notes\");\n\n if (existsSync(claudeNotesDir)) {\n const rootNotesDir = join(rootPath, \"Notes\");\n if (claudeNotesDir !== rootNotesDir) {\n db.prepare(\n \"UPDATE projects SET claude_notes_dir = ?, updated_at = ? WHERE id = ?\"\n ).run(claudeNotesDir, Date.now(), id);\n }\n }\n\n if (!existsSync(claudeNotesDir)) continue;\n\n const noteFiles = findNoteFiles(claudeNotesDir);\n\n for (const filename of noteFiles) {\n const parsed = parseSessionFilename(filename);\n if (!parsed) continue;\n\n result.sessionsScanned++;\n const isNewSession = upsertSession(db, id, parsed.number, parsed.date, parsed.slug, parsed.title, parsed.filename);\n if (isNewSession) result.sessionsNew++;\n }\n }\n\n // Phase 2: Scan project-root Notes/ for all registered active projects\n {\n const activeProjects = db\n .prepare(\"SELECT id, slug, root_path FROM projects WHERE status = 'active'\")\n .all() as { id: number; slug: string; root_path: string }[];\n\n for (const project of activeProjects) {\n const notesDir = join(project.root_path, \"Notes\");\n if (!existsSync(notesDir)) continue;\n\n let files: string[];\n try {\n files = findNoteFiles(notesDir);\n } catch {\n continue;\n }\n\n for (const filename of files) {\n const parsed = parseSessionFilename(filename);\n if (!parsed) continue;\n\n result.sessionsScanned++;\n const isNewSession = upsertSession(db, project.id, parsed.number, parsed.date, parsed.slug, parsed.title, parsed.filename);\n if (isNewSession) result.sessionsNew++;\n }\n }\n }\n\n // Phase 3: Scan extra directories from config\n const config = loadScanConfig();\n if (config.scan_dirs.length) {\n for (const rawDir of config.scan_dirs) {\n const scanDir = resolveHome(rawDir);\n if (!existsSync(scanDir)) {\n result.skipped.push(`${rawDir} (configured scan_dir not found)`);\n continue;\n }\n\n const children = readdirSync(scanDir).filter((name) => {\n if (name.startsWith(\".\")) return false;\n const full = join(scanDir, name);\n try { return statSync(full).isDirectory(); } catch { return false; }\n });\n\n for (const child of children) {\n const childPath = join(scanDir, child);\n const childSlug = slugify(child);\n const childEncoded = encodeDir(childPath);\n\n const existing = db\n .prepare(\"SELECT id FROM projects WHERE root_path = ?\")\n .get(childPath) as { id: number } | undefined;\n\n if (existing) {\n result.projectsScanned++;\n result.projectsUpdated++;\n\n try { ensurePaiMarker(childPath, childSlug); } catch { /* non-fatal */ }\n\n const notesDir = join(childPath, \"Notes\");\n if (existsSync(notesDir)) {\n const noteFiles = readdirSync(notesDir).filter((f) => f.endsWith(\".md\"));\n for (const filename of noteFiles) {\n const parsed = parseSessionFilename(filename);\n if (!parsed) continue;\n result.sessionsScanned++;\n if (upsertSession(db, existing.id, parsed.number, parsed.date, parsed.slug, parsed.title, parsed.filename)) {\n result.sessionsNew++;\n }\n }\n }\n continue;\n }\n\n const { id, isNew } = upsertProject(db, childSlug, childPath, childEncoded);\n result.projectsScanned++;\n if (isNew) result.projectsNew++;\n else result.projectsUpdated++;\n\n try { ensurePaiMarker(childPath, childSlug); } catch { /* non-fatal */ }\n\n const notesDir = join(childPath, \"Notes\");\n if (existsSync(notesDir)) {\n const noteFiles = readdirSync(notesDir).filter((f) => f.endsWith(\".md\"));\n for (const filename of noteFiles) {\n const parsed = parseSessionFilename(filename);\n if (!parsed) continue;\n result.sessionsScanned++;\n if (upsertSession(db, id, parsed.number, parsed.date, parsed.slug, parsed.title, parsed.filename)) {\n result.sessionsNew++;\n }\n }\n }\n }\n }\n }\n\n // Phase 4: Discover PAI.md markers in scan_dirs\n if (config.scan_dirs.length) {\n const resolvedScanDirs = config.scan_dirs.map(resolveHome).filter(existsSync);\n const markers = discoverPaiMarkers(resolvedScanDirs);\n\n for (const marker of markers) {\n const registeredRow = db\n .prepare(\"SELECT id, root_path, slug FROM projects WHERE slug = ?\")\n .get(marker.slug) as { id: number; root_path: string; slug: string } | undefined;\n\n if (!registeredRow) continue;\n\n if (registeredRow.root_path !== marker.projectRoot) {\n const newEncoded = encodeDir(marker.projectRoot);\n const now4 = Date.now();\n\n const encodedOwner = db\n .prepare(\"SELECT id FROM projects WHERE encoded_dir = ?\")\n .get(newEncoded) as { id: number } | undefined;\n const pathOwner = db\n .prepare(\"SELECT id FROM projects WHERE root_path = ?\")\n .get(marker.projectRoot) as { id: number } | undefined;\n\n const encodedSafe = !encodedOwner || encodedOwner.id === registeredRow.id;\n const pathSafe = !pathOwner || pathOwner.id === registeredRow.id;\n\n if (encodedSafe && pathSafe) {\n db.prepare(\n \"UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?\"\n ).run(marker.projectRoot, newEncoded, now4, registeredRow.id);\n } else if (pathSafe) {\n db.prepare(\n \"UPDATE projects SET root_path = ?, updated_at = ? WHERE id = ?\"\n ).run(marker.projectRoot, now4, registeredRow.id);\n }\n }\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// cmdScan\n// ---------------------------------------------------------------------------\n\nexport function cmdScan(db: Database): void {\n const config = loadScanConfig();\n console.log(dim(\"Scanning ~/.claude/projects/ ...\"));\n if (config.scan_dirs.length) {\n console.log(dim(`Scanning ${config.scan_dirs.length} extra dir(s): ${config.scan_dirs.join(\", \")}`));\n }\n console.log(dim(\"Scanning project-root Notes/ directories ...\"));\n\n let result: ScanResult;\n try {\n result = performScan(db);\n } catch (e) {\n console.error(err(String(e)));\n process.exit(1);\n }\n\n console.log(\n ok(`Scanned ${bold(String(result.projectsScanned))} projects, ${bold(String(result.sessionsScanned))} session notes.`)\n );\n console.log(dim(` Projects: ${result.projectsNew} new, ${result.projectsUpdated} updated`));\n console.log(dim(` Sessions: ${result.sessionsNew} new`));\n\n if (result.skipped.length) {\n console.log();\n console.log(warn(` ${result.skipped.length} project(s) skipped (path not found on disk):`));\n for (const s of result.skipped.slice(0, 10)) {\n console.log(dim(` ${s}`));\n }\n if (result.skipped.length > 10) {\n console.log(dim(` ... and ${result.skipped.length - 10} more`));\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 .action((opts: { addDir?: string; removeDir?: string; showDirs?: 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());\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 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","/** Step 7: Shell lifecycle hooks installation (pre-compact, session-stop, statusline). */\n\nimport { existsSync, readFileSync, copyFileSync, chmodSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getHooksDir, getStatuslineScript, getTabColorScript } from \"../utils.js\";\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 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 installFile(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 srcContent = readFileSync(src, \"utf-8\");\n\n if (existsSync(dest)) {\n const destContent = readFileSync(dest, \"utf-8\");\n if (srcContent === destContent) {\n console.log(c.dim(` Unchanged: ${label}`));\n return;\n }\n }\n\n copyFileSync(src, dest);\n chmodSync(dest, 0o755);\n console.log(c.ok(`Installed: ${label}`));\n anyInstalled = true;\n }\n\n line();\n installFile(join(hooksDir, \"pre-compact.sh\"), join(hooksTarget, \"pai-pre-compact.sh\"), \"pai-pre-compact.sh\");\n installFile(join(hooksDir, \"session-stop.sh\"), join(hooksTarget, \"pai-session-stop.sh\"), \"pai-session-stop.sh\");\n\n if (statuslineSrc) {\n installFile(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 installFile(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 { existsSync, readFileSync, readdirSync, copyFileSync, chmodSync, unlinkSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getDistHooksDir } from \"../utils.js\";\n\nexport async function stepTsHooks(rl: Rl): Promise<boolean> {\n section(\"Step 7b: TypeScript Hooks Installation\");\n line();\n line(\" PAI ships 14 compiled TypeScript hooks (.mjs) that fire on session events,\");\n line(\" tool use, and context compaction to capture context and update notes.\");\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 copiedCount = 0;\n let skippedCount = 0;\n let cleanedCount = 0;\n\n for (const filename of allFiles) {\n const src = join(distHooksDir, filename);\n const dest = join(hooksTarget, filename);\n const srcContent = readFileSync(src, \"utf-8\");\n\n if (existsSync(dest)) {\n const destContent = readFileSync(dest, \"utf-8\");\n if (srcContent === destContent) {\n console.log(c.dim(` Unchanged: ${filename}`));\n skippedCount++;\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 continue;\n }\n }\n\n copyFileSync(src, dest);\n chmodSync(dest, 0o755);\n console.log(c.ok(`Installed: ${filename}`));\n copiedCount++;\n\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 (copiedCount > 0 || cleanedCount > 0) {\n const parts = [];\n if (copiedCount > 0) parts.push(`${copiedCount} hook(s) installed`);\n if (skippedCount > 0) parts.push(`${skippedCount} unchanged`);\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 up-to-date.`));\n }\n\n return copiedCount > 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/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 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(\" 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 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: 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 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 .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","#!/usr/bin/env node\n/**\n * PAI Knowledge OS — CLI entry point\n *\n * Command tree:\n * pai project add|list|info|archive|unarchive|move|tag|alias|edit\n * pai session list|info\n * pai registry scan|migrate|stats|rebuild\n * pai memory index|search|status\n * pai search <query> (placeholder — Phase 3)\n * pai update\n * pai version\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 { registerProjectCommands, cmdGo } from \"./commands/project.js\";\nimport { registerSessionCommands } from \"./commands/session.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 { err } from \"./utils.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\n// ---------------------------------------------------------------------------\n// pai project\n// ---------------------------------------------------------------------------\n\nconst projectCmd = program\n .command(\"project\")\n .description(\"Manage registered projects\");\n\nregisterProjectCommands(projectCmd, getDb);\n\n// ---------------------------------------------------------------------------\n// pai session\n// ---------------------------------------------------------------------------\n\nconst sessionCmd = program\n .command(\"session\")\n .description(\"Browse session notes\");\n\nregisterSessionCommands(sessionCmd, getDb);\nregisterSessionCleanupCommand(sessionCmd, 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 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// pai go <query> — top-level shortcut for pai project go\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"go <query>\")\n .description(\n \"Jump to a project directory by slug or partial name.\\n\" +\n \"Prints the root path to stdout — use with: cd $(pai go <query>)\\n\" +\n \"Example shell function in ~/.zshrc:\\n\" +\n \" pcd() { cd \\\"$(pai go \\\"$@\\\")\\\" }\"\n )\n .action((query: string) => {\n cmdGo(getDb(), query);\n });\n\n// ---------------------------------------------------------------------------\n// pai search <query> (Phase 3 placeholder)\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"search <query>\")\n .description(\"Full-text search across sessions and notes (Phase 3)\")\n .option(\"--projects <p1,p2>\", \"Restrict search to these project slugs (comma-separated)\")\n .option(\"--limit <n>\", \"Maximum results to return\", \"10\")\n .action((query: string, opts: { projects?: string; limit?: string }) => {\n console.log(\n `\\n Search is coming in Phase 3.\\n\\n` +\n ` Query: ${query}\\n` +\n ` Projects: ${opts.projects ?? \"(all)\"}\\n` +\n ` Limit: ${opts.limit ?? 10}\\n`\n );\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\n// If no sub-command given, print help\nif (process.argv.length <= 2) {\n program.help();\n}\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","findClaudeNotesDir","slugify","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","CONFIG_FILE","CONFIG_FILE","CONFIG_FILE","cmdStatus","makeClient"],"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/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/commands/session/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/utils.ts","../../src/cli/commands/registry/scan.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/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 — plus private helpers levenshtein,\n * containsIgnoreCase, and findProjectNotesDirs.\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 } 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\";\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// 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 }\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 if (opts.status) {\n where.push(\"p.status = ?\");\n params.push(opts.status);\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 console.log(dim(` ${rows.length} project(s)`));\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\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 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 process.stdout.write(partial[0].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","/**\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 project` sub-commands.\n * Imports from focused sub-modules and wires up CLI options.\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} from \"./commands.js\";\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 registerProjectCommands(\n projectCmd: Command,\n getDb: () => Database\n): void {\n // pai project add <path>\n projectCmd\n .command(\"add <path>\")\n .description(\"Register a project directory in the PAI registry\")\n .option(\"--slug <slug>\", \"Override auto-generated slug\")\n .option(\n \"--type <type>\",\n \"Project type: local | central | obsidian-linked | external\",\n \"local\"\n )\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 project list\n projectCmd\n .command(\"list\")\n .description(\"List registered projects\")\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: { status?: string; tag?: string; type?: string }) => {\n cmdList(getDb(), opts);\n });\n\n // pai project info <slug>\n projectCmd\n .command(\"info <slug>\")\n .description(\"Show full details for a project\")\n .action((slug: string) => {\n cmdInfo(getDb(), slug);\n });\n\n // pai project archive <slug>\n projectCmd\n .command(\"archive <slug>\")\n .description(\"Archive a project\")\n .action((slug: string) => {\n cmdArchive(getDb(), slug);\n });\n\n // pai project unarchive <slug>\n projectCmd\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 project move <slug> <new-path>\n projectCmd\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 project tag <slug> <tags...>\n projectCmd\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 project alias <slug> <alias>\n projectCmd\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 project edit <slug>\n projectCmd\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 project cd <slug-or-number>\n projectCmd\n .command(\"cd <identifier>\")\n .description(\n \"Print the root path for a project (use with: cd $(pai project cd <id>))\"\n )\n .action((identifier: string) => {\n const project = resolveIdentifier(getDb(), identifier);\n if (!project) {\n console.error(`Project not found: ${identifier}`);\n process.exit(1);\n }\n process.stdout.write(project.root_path + \"\\n\");\n });\n\n // pai project detect [path]\n projectCmd\n .command(\"detect [path]\")\n .description(\n \"Detect which registered project the given path (or CWD) belongs to\"\n )\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 project health\n projectCmd\n .command(\"health\")\n .description(\n \"Audit all registered projects: check which paths still exist, find moved/dead projects\"\n )\n .option(\n \"--fix\",\n \"Auto-remediate where possible (update moved paths, archive dead zero-session projects)\"\n )\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 project consolidate <slug-or-number>\n projectCmd\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 project promote\n projectCmd\n .command(\"promote\")\n .description(\"Promote a session note into a new standalone project\")\n .requiredOption(\n \"--from-session <path>\",\n \"Path to the session note markdown file\"\n )\n .requiredOption(\n \"--to <path>\",\n \"Directory path for the new project (must not exist)\"\n )\n .option(\n \"--name <name>\",\n \"Display name for the new project (derived from filename if omitted)\"\n )\n .action((opts: { fromSession: string; to: string; name?: string }) => {\n cmdPromote(getDb(), opts);\n });\n\n // pai project go <query>\n projectCmd\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 project go <query>)\\n\" +\n \"Or set a shell alias: alias pcd='cd $(pai project go)'\"\n )\n .action((query: string) => {\n cmdGo(getDb(), query);\n });\n\n // pai project name <slug-or-number> <shortname>\n projectCmd\n .command(\"name <identifier> <shortname>\")\n .description(\n \"Give a project a short name for quick access (used by AIBroker to launch sessions)\"\n )\n .option(\n \"--permission <level>\",\n \"Permission level: full | trusted | default (or raw CLI flags)\"\n )\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 project unname <shortname>\n projectCmd\n .command(\"unname <shortname>\")\n .description(\"Remove a project's short name\")\n .action((shortname: string) => {\n cmdUnname(getDb(), shortname);\n });\n\n // pai project names\n projectCmd\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 project config [identifier]\n projectCmd\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 \"Use --defaults to manage global defaults for new sessions.\"\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(\n \"--preset <name>\",\n \"Apply a permission preset: full | trusted | default\"\n )\n .option(\n \"--defaults\",\n \"Manage global session defaults instead of a project\"\n )\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 * Commander registration for all `pai session` sub-commands.\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\";\n\nexport function registerSessionCommands(\n sessionCmd: Command,\n getDb: () => Database\n): void {\n // pai session list [project-slug]\n sessionCmd\n .command(\"list [project-slug]\")\n .description(\"List sessions, optionally filtered to a single project\")\n .option(\"--limit <n>\", \"Maximum number of sessions 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 cmdList(getDb(), projectSlug, opts);\n }\n );\n\n // pai session info <project-slug> <number>\n sessionCmd\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 session rename <project-slug> <number> <new-slug>\n sessionCmd\n .command(\"rename <project-slug> <number> <new-slug>\")\n .description(\n \"Rename a session note — updates file on disk, H1 title, and registry\"\n )\n .action((projectSlug: string, number: string, newSlug: string) => {\n cmdRename(getDb(), projectSlug, number, newSlug);\n });\n\n // pai session slug <project-slug> <number|latest>\n sessionCmd\n .command(\"slug <project-slug> <number>\")\n .description(\n \"Generate a descriptive slug from the session JSONL transcript\"\n )\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 session tag <project-slug> <number> [tags...]\n sessionCmd\n .command(\"tag <project-slug> <number> [tags...]\")\n .description(\n \"Set or show tags on a session. Tags can be space-separated or comma-separated.\"\n )\n .action((projectSlug: string, number: string, tags: string[]) => {\n cmdTag(getDb(), projectSlug, number, tags);\n });\n\n // pai session route <project-slug> <number> <target-project>\n sessionCmd\n .command(\"route <project-slug> <number> <target-project>\")\n .description(\n \"Create a cross-reference link from a session to a target project\"\n )\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 session handover [project-slug] [session-id]\n sessionCmd\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 \"Records the last session identifier, timestamp, and working directory\\n\" +\n \"so the next session can resume from the correct context.\"\n )\n .action(\n (projectSlug: string | undefined, sessionId: string | undefined) => {\n cmdHandover(getDb(), projectSlug, sessionId);\n }\n );\n\n // pai session checkpoint <message>\n sessionCmd\n .command(\"checkpoint <message>\")\n .description(\n \"Append a timestamped checkpoint to the active session note.\\n\" +\n \"Designed for hooks (PostToolUse, UserPromptSubmit) — fast and silent.\\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 session active [--minutes N] [--json]\n sessionCmd\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 session auto-route [--cwd path] [--context \"text\"] [--json]\n sessionCmd\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 \"Designed for use in CLAUDE.md session-start hooks.\"\n )\n .option(\n \"--cwd <path>\",\n \"Working directory to detect from (default: process.cwd())\"\n )\n .option(\n \"--context <text>\",\n \"Conversation context for topic-based fallback routing\"\n )\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","/** Shared database helpers for registry command operations. */\n\nimport type { Database } from \"better-sqlite3\";\nimport { now } from \"../../utils.js\";\n\n/**\n * Upsert a project row. Returns { id, isNew }.\n *\n * Matching priority:\n * 1. root_path — most reliable; handles slug collisions\n * 2. encoded_dir — Claude project dirs are canonical\n * 3. Insert with suffix-deduplication on slug collision\n */\nexport function upsertProject(\n db: Database,\n slug: string,\n rootPath: string,\n encodedDir: string\n): { id: number; isNew: boolean } {\n const ts = now();\n\n const byPath = db\n .prepare(\"SELECT id FROM projects WHERE root_path = ?\")\n .get(rootPath) as { id: number } | undefined;\n\n if (byPath) {\n const encodedOwner = db\n .prepare(\"SELECT id FROM projects WHERE encoded_dir = ?\")\n .get(encodedDir) as { id: number } | undefined;\n\n if (!encodedOwner || encodedOwner.id === byPath.id) {\n db.prepare(\n \"UPDATE projects SET encoded_dir = ?, updated_at = ? WHERE id = ?\"\n ).run(encodedDir, ts, byPath.id);\n }\n return { id: byPath.id, isNew: false };\n }\n\n const byEncoded = db\n .prepare(\"SELECT id FROM projects WHERE encoded_dir = ?\")\n .get(encodedDir) as { id: number } | undefined;\n\n if (byEncoded) {\n const pathOwner = db\n .prepare(\"SELECT id FROM projects WHERE root_path = ?\")\n .get(rootPath) as { id: number } | undefined;\n\n if (!pathOwner || pathOwner.id === byEncoded.id) {\n db.prepare(\n \"UPDATE projects SET root_path = ?, updated_at = ? WHERE id = ?\"\n ).run(rootPath, ts, byEncoded.id);\n }\n return { id: byEncoded.id, isNew: false };\n }\n\n // Insert — deduplicate slug with numeric suffix if needed.\n let finalSlug = slug;\n let attempt = 0;\n while (true) {\n const conflict = db\n .prepare(\"SELECT id FROM projects WHERE slug = ?\")\n .get(finalSlug) as { id: number } | undefined;\n if (!conflict) break;\n attempt++;\n finalSlug = `${slug}-${attempt}`;\n }\n\n const result = db\n .prepare(\n `INSERT OR IGNORE INTO projects\n (slug, display_name, root_path, encoded_dir, type, status, created_at, updated_at)\n VALUES (?, ?, ?, ?, 'local', 'active', ?, ?)`\n )\n .run(finalSlug, finalSlug, rootPath, encodedDir, ts, ts);\n\n if (result.changes === 0) {\n const fallback =\n (db.prepare(\"SELECT id FROM projects WHERE encoded_dir = ?\").get(encodedDir) as { id: number } | undefined) ??\n (db.prepare(\"SELECT id FROM projects WHERE root_path = ?\").get(rootPath) as { id: number } | undefined);\n\n if (fallback) {\n return { id: fallback.id, isNew: false };\n }\n\n throw new Error(\n `upsertProject: INSERT OR IGNORE was suppressed but no matching row found ` +\n `for root_path=${rootPath} encoded_dir=${encodedDir}`\n );\n }\n\n return { id: result.lastInsertRowid as number, isNew: true };\n}\n\n/** Upsert a session note. Returns true if newly inserted. */\nexport function upsertSession(\n db: Database,\n projectId: number,\n number: number,\n date: string,\n slug: string,\n title: string,\n filename: string\n): boolean {\n const existing = db\n .prepare(\"SELECT id FROM sessions WHERE project_id = ? AND number = ?\")\n .get(projectId, number);\n\n if (existing) return false;\n\n const ts = now();\n db.prepare(\n `INSERT INTO sessions\n (project_id, number, date, slug, title, filename, status, created_at)\n VALUES (?, ?, ?, ?, ?, ?, 'completed', ?)`\n ).run(projectId, number, date, slug, title, filename, ts);\n\n return true;\n}\n","/** Registry scan command: walk ~/.claude/projects/ and populate the registry. */\n\nimport { existsSync, readdirSync, statSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join, basename, resolve } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { ok, warn, err, dim, bold } from \"../../utils.js\";\nimport { encodeDir } from \"../../utils.js\";\nimport { decodeEncodedDir, slugify, parseSessionFilename, buildEncodedDirMap } from \"../../../registry/migrate.js\";\nimport { ensurePaiMarker, discoverPaiMarkers } from \"../../../registry/pai-marker.js\";\nimport { upsertProject, upsertSession } from \"./utils.js\";\nimport type { Database } from \"better-sqlite3\";\n\n// ---------------------------------------------------------------------------\n// Config helpers\n// ---------------------------------------------------------------------------\n\nconst CLAUDE_PROJECTS_DIR = join(homedir(), \".claude\", \"projects\");\nconst PAI_CONFIG_DIR = join(homedir(), \".pai\");\nconst PAI_CONFIG_FILE = join(PAI_CONFIG_DIR, \"config.json\");\n\ninterface PaiConfig {\n scan_dirs: string[];\n}\n\nexport function loadScanConfig(): PaiConfig {\n if (!existsSync(PAI_CONFIG_FILE)) return { scan_dirs: [] };\n try {\n return JSON.parse(readFileSync(PAI_CONFIG_FILE, \"utf8\")) as PaiConfig;\n } catch {\n return { scan_dirs: [] };\n }\n}\n\nexport function saveScanConfig(config: PaiConfig): void {\n mkdirSync(PAI_CONFIG_DIR, { recursive: true });\n writeFileSync(PAI_CONFIG_FILE, JSON.stringify(config, null, 2) + \"\\n\", \"utf8\");\n}\n\nexport function resolveHome(p: string): string {\n if (p.startsWith(\"~/\")) return join(homedir(), p.slice(2));\n return resolve(p);\n}\n\n// ---------------------------------------------------------------------------\n// File discovery\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively find all .md files in a directory, including YYYY/MM subdirectories.\n * Returns filenames (basename only).\n */\nexport function findNoteFiles(dir: string): string[] {\n const results: string[] = [];\n if (!existsSync(dir)) return results;\n\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.isFile() && entry.name.endsWith(\".md\")) {\n results.push(entry.name);\n } else if (entry.isDirectory() && /^\\d{4}$/.test(entry.name)) {\n const yearDir = join(dir, entry.name);\n for (const monthEntry of readdirSync(yearDir, { withFileTypes: true })) {\n if (monthEntry.isDirectory() && /^\\d{2}$/.test(monthEntry.name)) {\n const monthDir = join(yearDir, monthEntry.name);\n for (const noteEntry of readdirSync(monthDir, { withFileTypes: true })) {\n if (noteEntry.isFile() && noteEntry.name.endsWith(\".md\")) {\n results.push(noteEntry.name);\n }\n }\n }\n }\n }\n }\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// Scan result type\n// ---------------------------------------------------------------------------\n\nexport interface ScanResult {\n projectsScanned: number;\n projectsNew: number;\n projectsUpdated: number;\n sessionsScanned: number;\n sessionsNew: number;\n skipped: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Core scan logic\n// ---------------------------------------------------------------------------\n\nexport function performScan(db: Database): ScanResult {\n const result: ScanResult = {\n projectsScanned: 0,\n projectsNew: 0,\n projectsUpdated: 0,\n sessionsScanned: 0,\n sessionsNew: 0,\n skipped: [],\n };\n\n if (!existsSync(CLAUDE_PROJECTS_DIR)) {\n throw new Error(`Claude projects directory not found: ${CLAUDE_PROJECTS_DIR}`);\n }\n\n const entries = readdirSync(CLAUDE_PROJECTS_DIR).filter((name) => {\n const full = join(CLAUDE_PROJECTS_DIR, name);\n return statSync(full).isDirectory();\n });\n\n const lookupMap = buildEncodedDirMap();\n\n for (const encodedDir of entries) {\n const rootPath = decodeEncodedDir(encodedDir, lookupMap);\n\n if (!existsSync(rootPath)) {\n result.skipped.push(`${encodedDir} (decoded: ${rootPath} — path not found on disk)`);\n result.projectsScanned++;\n continue;\n }\n\n const slug = slugify(basename(rootPath) || encodedDir);\n const { id, isNew } = upsertProject(db, slug, rootPath, encodedDir);\n\n result.projectsScanned++;\n if (isNew) result.projectsNew++;\n else result.projectsUpdated++;\n\n try {\n ensurePaiMarker(rootPath, slug);\n } catch {\n // Non-fatal\n }\n\n const claudeNotesDir = join(CLAUDE_PROJECTS_DIR, encodedDir, \"Notes\");\n\n if (existsSync(claudeNotesDir)) {\n const rootNotesDir = join(rootPath, \"Notes\");\n if (claudeNotesDir !== rootNotesDir) {\n db.prepare(\n \"UPDATE projects SET claude_notes_dir = ?, updated_at = ? WHERE id = ?\"\n ).run(claudeNotesDir, Date.now(), id);\n }\n }\n\n if (!existsSync(claudeNotesDir)) continue;\n\n const noteFiles = findNoteFiles(claudeNotesDir);\n\n for (const filename of noteFiles) {\n const parsed = parseSessionFilename(filename);\n if (!parsed) continue;\n\n result.sessionsScanned++;\n const isNewSession = upsertSession(db, id, parsed.number, parsed.date, parsed.slug, parsed.title, parsed.filename);\n if (isNewSession) result.sessionsNew++;\n }\n }\n\n // Phase 2: Scan project-root Notes/ for all registered active projects\n {\n const activeProjects = db\n .prepare(\"SELECT id, slug, root_path FROM projects WHERE status = 'active'\")\n .all() as { id: number; slug: string; root_path: string }[];\n\n for (const project of activeProjects) {\n const notesDir = join(project.root_path, \"Notes\");\n if (!existsSync(notesDir)) continue;\n\n let files: string[];\n try {\n files = findNoteFiles(notesDir);\n } catch {\n continue;\n }\n\n for (const filename of files) {\n const parsed = parseSessionFilename(filename);\n if (!parsed) continue;\n\n result.sessionsScanned++;\n const isNewSession = upsertSession(db, project.id, parsed.number, parsed.date, parsed.slug, parsed.title, parsed.filename);\n if (isNewSession) result.sessionsNew++;\n }\n }\n }\n\n // Phase 3: Scan extra directories from config\n const config = loadScanConfig();\n if (config.scan_dirs.length) {\n for (const rawDir of config.scan_dirs) {\n const scanDir = resolveHome(rawDir);\n if (!existsSync(scanDir)) {\n result.skipped.push(`${rawDir} (configured scan_dir not found)`);\n continue;\n }\n\n const children = readdirSync(scanDir).filter((name) => {\n if (name.startsWith(\".\")) return false;\n const full = join(scanDir, name);\n try { return statSync(full).isDirectory(); } catch { return false; }\n });\n\n for (const child of children) {\n const childPath = join(scanDir, child);\n const childSlug = slugify(child);\n const childEncoded = encodeDir(childPath);\n\n const existing = db\n .prepare(\"SELECT id FROM projects WHERE root_path = ?\")\n .get(childPath) as { id: number } | undefined;\n\n if (existing) {\n result.projectsScanned++;\n result.projectsUpdated++;\n\n try { ensurePaiMarker(childPath, childSlug); } catch { /* non-fatal */ }\n\n const notesDir = join(childPath, \"Notes\");\n if (existsSync(notesDir)) {\n const noteFiles = readdirSync(notesDir).filter((f) => f.endsWith(\".md\"));\n for (const filename of noteFiles) {\n const parsed = parseSessionFilename(filename);\n if (!parsed) continue;\n result.sessionsScanned++;\n if (upsertSession(db, existing.id, parsed.number, parsed.date, parsed.slug, parsed.title, parsed.filename)) {\n result.sessionsNew++;\n }\n }\n }\n continue;\n }\n\n const { id, isNew } = upsertProject(db, childSlug, childPath, childEncoded);\n result.projectsScanned++;\n if (isNew) result.projectsNew++;\n else result.projectsUpdated++;\n\n try { ensurePaiMarker(childPath, childSlug); } catch { /* non-fatal */ }\n\n const notesDir = join(childPath, \"Notes\");\n if (existsSync(notesDir)) {\n const noteFiles = readdirSync(notesDir).filter((f) => f.endsWith(\".md\"));\n for (const filename of noteFiles) {\n const parsed = parseSessionFilename(filename);\n if (!parsed) continue;\n result.sessionsScanned++;\n if (upsertSession(db, id, parsed.number, parsed.date, parsed.slug, parsed.title, parsed.filename)) {\n result.sessionsNew++;\n }\n }\n }\n }\n }\n }\n\n // Phase 4: Discover PAI.md markers in scan_dirs\n if (config.scan_dirs.length) {\n const resolvedScanDirs = config.scan_dirs.map(resolveHome).filter(existsSync);\n const markers = discoverPaiMarkers(resolvedScanDirs);\n\n for (const marker of markers) {\n const registeredRow = db\n .prepare(\"SELECT id, root_path, slug FROM projects WHERE slug = ?\")\n .get(marker.slug) as { id: number; root_path: string; slug: string } | undefined;\n\n if (!registeredRow) continue;\n\n if (registeredRow.root_path !== marker.projectRoot) {\n const newEncoded = encodeDir(marker.projectRoot);\n const now4 = Date.now();\n\n const encodedOwner = db\n .prepare(\"SELECT id FROM projects WHERE encoded_dir = ?\")\n .get(newEncoded) as { id: number } | undefined;\n const pathOwner = db\n .prepare(\"SELECT id FROM projects WHERE root_path = ?\")\n .get(marker.projectRoot) as { id: number } | undefined;\n\n const encodedSafe = !encodedOwner || encodedOwner.id === registeredRow.id;\n const pathSafe = !pathOwner || pathOwner.id === registeredRow.id;\n\n if (encodedSafe && pathSafe) {\n db.prepare(\n \"UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?\"\n ).run(marker.projectRoot, newEncoded, now4, registeredRow.id);\n } else if (pathSafe) {\n db.prepare(\n \"UPDATE projects SET root_path = ?, updated_at = ? WHERE id = ?\"\n ).run(marker.projectRoot, now4, registeredRow.id);\n }\n }\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// cmdScan\n// ---------------------------------------------------------------------------\n\nexport function cmdScan(db: Database): void {\n const config = loadScanConfig();\n console.log(dim(\"Scanning ~/.claude/projects/ ...\"));\n if (config.scan_dirs.length) {\n console.log(dim(`Scanning ${config.scan_dirs.length} extra dir(s): ${config.scan_dirs.join(\", \")}`));\n }\n console.log(dim(\"Scanning project-root Notes/ directories ...\"));\n\n let result: ScanResult;\n try {\n result = performScan(db);\n } catch (e) {\n console.error(err(String(e)));\n process.exit(1);\n }\n\n console.log(\n ok(`Scanned ${bold(String(result.projectsScanned))} projects, ${bold(String(result.sessionsScanned))} session notes.`)\n );\n console.log(dim(` Projects: ${result.projectsNew} new, ${result.projectsUpdated} updated`));\n console.log(dim(` Sessions: ${result.sessionsNew} new`));\n\n if (result.skipped.length) {\n console.log();\n console.log(warn(` ${result.skipped.length} project(s) skipped (path not found on disk):`));\n for (const s of result.skipped.slice(0, 10)) {\n console.log(dim(` ${s}`));\n }\n if (result.skipped.length > 10) {\n console.log(dim(` ... and ${result.skipped.length - 10} more`));\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 .action((opts: { addDir?: string; removeDir?: string; showDirs?: 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());\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 { existsSync, readFileSync, copyFileSync, chmodSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getHooksDir, getStatuslineScript, getTabColorScript } from \"../utils.js\";\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 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 installFile(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 srcContent = readFileSync(src, \"utf-8\");\n\n if (existsSync(dest)) {\n const destContent = readFileSync(dest, \"utf-8\");\n if (srcContent === destContent) {\n console.log(c.dim(` Unchanged: ${label}`));\n return;\n }\n }\n\n copyFileSync(src, dest);\n chmodSync(dest, 0o755);\n console.log(c.ok(`Installed: ${label}`));\n anyInstalled = true;\n }\n\n line();\n installFile(join(hooksDir, \"pre-compact.sh\"), join(hooksTarget, \"pai-pre-compact.sh\"), \"pai-pre-compact.sh\");\n installFile(join(hooksDir, \"session-stop.sh\"), join(hooksTarget, \"pai-session-stop.sh\"), \"pai-session-stop.sh\");\n\n if (statuslineSrc) {\n installFile(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 installFile(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 { existsSync, readFileSync, readdirSync, copyFileSync, chmodSync, unlinkSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getDistHooksDir } from \"../utils.js\";\n\nexport async function stepTsHooks(rl: Rl): Promise<boolean> {\n section(\"Step 7b: TypeScript Hooks Installation\");\n line();\n line(\" PAI ships 14 compiled TypeScript hooks (.mjs) that fire on session events,\");\n line(\" tool use, and context compaction to capture context and update notes.\");\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 copiedCount = 0;\n let skippedCount = 0;\n let cleanedCount = 0;\n\n for (const filename of allFiles) {\n const src = join(distHooksDir, filename);\n const dest = join(hooksTarget, filename);\n const srcContent = readFileSync(src, \"utf-8\");\n\n if (existsSync(dest)) {\n const destContent = readFileSync(dest, \"utf-8\");\n if (srcContent === destContent) {\n console.log(c.dim(` Unchanged: ${filename}`));\n skippedCount++;\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 continue;\n }\n }\n\n copyFileSync(src, dest);\n chmodSync(dest, 0o755);\n console.log(c.ok(`Installed: ${filename}`));\n copiedCount++;\n\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 (copiedCount > 0 || cleanedCount > 0) {\n const parts = [];\n if (copiedCount > 0) parts.push(`${copiedCount} hook(s) installed`);\n if (skippedCount > 0) parts.push(`${skippedCount} unchanged`);\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 up-to-date.`));\n }\n\n return copiedCount > 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/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 .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","#!/usr/bin/env node\n/**\n * PAI Knowledge OS — CLI entry point\n *\n * Command tree:\n * pai project add|list|info|archive|unarchive|move|tag|alias|edit\n * pai session list|info\n * pai registry scan|migrate|stats|rebuild\n * pai memory index|search|status\n * pai search <query> (placeholder — Phase 3)\n * pai update\n * pai version\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 { registerProjectCommands, cmdGo } from \"./commands/project.js\";\nimport { registerSessionCommands } from \"./commands/session.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 { err } from \"./utils.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\n// ---------------------------------------------------------------------------\n// pai project\n// ---------------------------------------------------------------------------\n\nconst projectCmd = program\n .command(\"project\")\n .description(\"Manage registered projects\");\n\nregisterProjectCommands(projectCmd, getDb);\n\n// ---------------------------------------------------------------------------\n// pai session\n// ---------------------------------------------------------------------------\n\nconst sessionCmd = program\n .command(\"session\")\n .description(\"Browse session notes\");\n\nregisterSessionCommands(sessionCmd, getDb);\nregisterSessionCleanupCommand(sessionCmd, 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 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// pai go <query> — top-level shortcut for pai project go\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"go <query>\")\n .description(\n \"Jump to a project directory by slug or partial name.\\n\" +\n \"Prints the root path to stdout — use with: cd $(pai go <query>)\\n\" +\n \"Example shell function in ~/.zshrc:\\n\" +\n \" pcd() { cd \\\"$(pai go \\\"$@\\\")\\\" }\"\n )\n .action((query: string) => {\n cmdGo(getDb(), query);\n });\n\n// ---------------------------------------------------------------------------\n// pai search <query> (Phase 3 placeholder)\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"search <query>\")\n .description(\"Full-text search across sessions and notes (Phase 3)\")\n .option(\"--projects <p1,p2>\", \"Restrict search to these project slugs (comma-separated)\")\n .option(\"--limit <n>\", \"Maximum results to return\", \"10\")\n .action((query: string, opts: { projects?: string; limit?: string }) => {\n console.log(\n `\\n Search is coming in Phase 3.\\n\\n` +\n ` Query: ${query}\\n` +\n ` Projects: ${opts.projects ?? \"(all)\"}\\n` +\n ` Limit: ${opts.limit ?? 10}\\n`\n );\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\n// If no sub-command given, print help\nif (process.argv.length <= 2) {\n program.help();\n}\n"],"mappings":"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"}
|