@tekmidian/pai 0.12.2 → 0.13.0
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/dist/cli/index.mjs +2 -2
- package/dist/cli/program.d.mts.map +1 -1
- package/dist/cli/program.mjs +3 -2
- package/dist/cli/program.mjs.map +1 -1
- package/dist/{config-BuhHWyOK.mjs → config-C8m-tPhP.mjs} +22 -2
- package/dist/config-C8m-tPhP.mjs.map +1 -0
- package/dist/daemon/index.mjs +2 -2
- package/dist/{daemon-Ca7YXHUW.mjs → daemon-D2aKb4mJ.mjs} +2 -2
- package/dist/{daemon-Ca7YXHUW.mjs.map → daemon-D2aKb4mJ.mjs.map} +1 -1
- package/dist/daemon-mcp/index.mjs +581 -2
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/{pick-C5sUQ5g_.mjs → pick-CS_TmOqX.mjs} +1082 -445
- package/dist/pick-CS_TmOqX.mjs.map +1 -0
- package/dist/skills/Tasks/SKILL.md +101 -0
- package/docs/commands/README.md +6 -0
- package/docs/commands/backup.md +1 -1
- package/docs/commands/clear-names.md +1 -1
- package/docs/commands/daemon.md +1 -1
- package/docs/commands/db.md +1 -1
- package/docs/commands/end.md +1 -1
- package/docs/commands/help.md +1 -1
- package/docs/commands/kg.md +1 -1
- package/docs/commands/mcp.md +1 -1
- package/docs/commands/memory.md +1 -1
- package/docs/commands/notify.md +1 -1
- package/docs/commands/observation.md +1 -1
- package/docs/commands/obsidian.md +1 -1
- package/docs/commands/pause.md +1 -1
- package/docs/commands/project.md +1 -1
- package/docs/commands/projects.md +1 -1
- package/docs/commands/registry.md +1 -1
- package/docs/commands/restore.md +1 -1
- package/docs/commands/sessions.md +1 -1
- package/docs/commands/setup.md +1 -1
- package/docs/commands/shell-init.md +1 -1
- package/docs/commands/skill.md +1 -1
- package/docs/commands/task.md +90 -0
- package/docs/commands/topic.md +1 -1
- package/docs/commands/update.md +1 -1
- package/docs/commands/zettel.md +1 -1
- package/package.json +1 -1
- package/plugins/productivity/plugin.json +2 -2
- package/plugins/productivity/skills/Tasks/SKILL.md +126 -0
- package/dist/config-BuhHWyOK.mjs.map +0 -1
- package/dist/pick-C5sUQ5g_.mjs.map +0 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -6,10 +6,10 @@ import "../db-CmYbAVCD.mjs";
|
|
|
6
6
|
import "../helpers-IjZkXBhj.mjs";
|
|
7
7
|
import "../embeddings-BJPOcbik.mjs";
|
|
8
8
|
import "../search-HcdKtMla.mjs";
|
|
9
|
-
import "../pick-
|
|
9
|
+
import "../pick-CS_TmOqX.mjs";
|
|
10
10
|
import "../kg-extraction-C8DEUHTS.mjs";
|
|
11
11
|
import "../factory-Q88X1bAN.mjs";
|
|
12
|
-
import "../config-
|
|
12
|
+
import "../config-C8m-tPhP.mjs";
|
|
13
13
|
import "../main-resolver-uFxNiDy7.mjs";
|
|
14
14
|
import { buildProgram } from "./program.mjs";
|
|
15
15
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"program.d.mts","names":[],"sources":["../../src/cli/program.ts"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"program.d.mts","names":[],"sources":["../../src/cli/program.ts"],"mappings":";;;;;;;iBA4FgB,YAAA,CAAA,GAAgB,OAAA"}
|
package/dist/cli/program.mjs
CHANGED
|
@@ -5,10 +5,10 @@ import "../db-CmYbAVCD.mjs";
|
|
|
5
5
|
import "../helpers-IjZkXBhj.mjs";
|
|
6
6
|
import "../embeddings-BJPOcbik.mjs";
|
|
7
7
|
import "../search-HcdKtMla.mjs";
|
|
8
|
-
import { C as
|
|
8
|
+
import { C as registerDaemonCommands, D as registerProjectsCommands, E as registerRegistryCommands, O as findMovedPath, S as registerBackupCommands, T as registerMemoryCommands, _ as registerObservationCommands, a as cmdPauseAll, b as registerSetupCommand, c as cmdPause, d as registerKgCommands, f as registerTopicCommands, g as registerSkillCommands, h as registerUpdateCommand, i as cmdClearNames, k as resolveIdentifier, l as registerHelpCommand, m as registerNotifyCommands, n as cmdFind, o as cmdGoto, p as registerTaskCommands, r as cmdList, s as cmdEnd, t as cmdPick, u as registerDbCommands, v as registerZettelCommands, w as registerMcpCommands, x as registerRestoreCommands, y as registerObsidianCommands } from "../pick-CS_TmOqX.mjs";
|
|
9
9
|
import "../kg-extraction-C8DEUHTS.mjs";
|
|
10
10
|
import "../factory-Q88X1bAN.mjs";
|
|
11
|
-
import "../config-
|
|
11
|
+
import "../config-C8m-tPhP.mjs";
|
|
12
12
|
import { t as cmdMain } from "../main-resolver-uFxNiDy7.mjs";
|
|
13
13
|
import { existsSync, readFileSync } from "node:fs";
|
|
14
14
|
import { basename, dirname, join } from "node:path";
|
|
@@ -85,6 +85,7 @@ Examples:
|
|
|
85
85
|
registerSetupCommand(program);
|
|
86
86
|
registerUpdateCommand(program);
|
|
87
87
|
registerNotifyCommands(program.command("notify").description("Notification config: status, get, set, test, send"));
|
|
88
|
+
registerTaskCommands(program.command("task").alias("tasks").description("Task bus: list, add, dispatch, and complete cross-session work"));
|
|
88
89
|
registerTopicCommands(program.command("topic").description("Topic shift detection: check whether context has drifted to a different project"));
|
|
89
90
|
registerKgCommands(program.command("kg").description("Temporal knowledge graph: backfill, query, list, stats"));
|
|
90
91
|
registerDbCommands(program.command("db").description("Database inspection: query, tables, schema (sqlite or postgres)"));
|
package/dist/cli/program.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"program.mjs","names":[],"sources":["../../src/cli/program.ts"],"sourcesContent":["/**\n * PAI CLI — Commander program builder.\n *\n * Constructs the full `pai` command tree WITHOUT parsing argv. This separation\n * lets the documentation generator (scripts/build-docs.mjs) import and introspect\n * the live program object — the single source of truth for `--help`, the\n * generated man pages, and `pai help <area>`.\n *\n * The thin entry point (cli/index.ts) imports buildProgram() and calls parse().\n */\n\nimport { Command } from \"commander\";\nimport { readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { openRegistry } from \"../registry/db.js\";\nimport type { Database } from \"better-sqlite3\";\nimport { registerProjectsCommands } from \"./commands/project/projects-index.js\";\nimport { findMovedPath } from \"./commands/project/commands.js\";\nimport { existsSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport { 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 { registerSkillCommands } from \"./commands/skill.js\";\nimport { registerUpdateCommand } from \"./commands/update.js\";\nimport { registerNotifyCommands } from \"./commands/notify.js\";\nimport { registerTopicCommands } from \"./commands/topic.js\";\nimport { registerKgCommands } from \"./commands/kg.js\";\nimport { registerDbCommands } from \"./commands/db.js\";\nimport { registerHelpCommand } from \"./commands/help.js\";\nimport { err, warn, ok, dim, shortenPath, encodeDir, now } from \"./utils.js\";\nimport { cmdPause } from \"./commands/session/pause.js\";\nimport { cmdEnd } from \"./commands/session/end.js\";\nimport { cmdGoto } from \"./commands/session/goto.js\";\nimport { cmdPauseAll } from \"./commands/session/pause-all.js\";\nimport { cmdClearNames } from \"./commands/session/clear-names.js\";\nimport { cmdList as cmdNotesList } from \"./commands/session/commands.js\";\nimport { resolveIdentifier } from \"./commands/project/helpers.js\";\nimport { cmdFind } from \"./commands/find.js\";\nimport { cmdMain } from \"./commands/main-resolver.js\";\nimport { cmdPick } from \"./commands/pick.js\";\n\n// ---------------------------------------------------------------------------\n// Version resolution\n// ---------------------------------------------------------------------------\n\nfunction getVersion(): string {\n try {\n const __dirname = dirname(fileURLToPath(import.meta.url));\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\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 builder\n// ---------------------------------------------------------------------------\n\n/**\n * Build and return the fully configured `pai` Commander program.\n * Does NOT call parse — the caller (cli/index.ts) does that.\n */\nexport function buildProgram(): Command {\n const program = new Command();\n\n program\n .name(\"pai\")\n .description(\"PAI Knowledge OS — Personal AI Infrastructure CLI\")\n .version(getVersion(), \"-V, --version\", \"Print version and exit\")\n .addHelpText(\n \"after\",\n `\nDaily commands:\n pai Show your work: live tabs + recent sessions + recent projects\n pai <name> Switch / resume / fresh — universal\n pai pause [all] Pause current (or all live) sessions\n pai end Finalize current session\n\nSubcommands (rarely needed):\n pai projects ... Project registry management (cd, rebind, info, ...)\n pai registry ... Registry maintenance (scan, ...)\n pai memory ... Memory engine (index, search, ...)\n pai shell-init Shell integration (eval \"$(pai shell-init)\")\n\nRecovery:\n pai clear-names Wipe corrupt iTerm tab-name store\n\nExamples:\n pai aibroker Switch to the live AIBroker tab in iTerm\n pai mdf Find sessions where you worked on MDF\n pai 81c5c3dc Resume session by UUID prefix\n pai --all Show everything including cold and zero-session projects`\n );\n\n // -------------------------------------------------------------------------\n // pai projects (canonical plural namespace)\n // -------------------------------------------------------------------------\n\n const projectsCmd = program\n .command(\"projects\")\n .description(\"Manage registered projects (list, cd, add, info, ...)\");\n\n registerProjectsCommands(projectsCmd, getDb);\n\n // Singular alias: `pai project` → `pai projects`\n const projectCmd = program\n .command(\"project\")\n .description(\"Alias for `pai projects`\");\n\n registerProjectsCommands(projectCmd, getDb);\n\n // -------------------------------------------------------------------------\n // pai sessions (alias for the unified `pai` listing)\n // -------------------------------------------------------------------------\n\n program\n .command(\"sessions\")\n .description(\"Alias for `pai` — show the unified deduped listing.\")\n .option(\"--all\", \"Show all entries including cold / zero-session / archived projects\")\n .option(\"-n, --n <count>\", \"Max candidates for history search\", \"20\")\n .action(async (opts: { all?: boolean; n?: string }) => {\n await cmdMain(getDb(), undefined, undefined, opts);\n });\n\n // -------------------------------------------------------------------------\n // pai registry\n // -------------------------------------------------------------------------\n\n const registryCmd = program\n .command(\"registry\")\n .description(\"Registry maintenance: scan, migrate, stats, rebuild\");\n\n registerRegistryCommands(registryCmd, getDb);\n\n // -------------------------------------------------------------------------\n // pai memory\n // -------------------------------------------------------------------------\n\n const memoryCmd = program\n .command(\"memory\")\n .description(\"Memory engine: index, search, and status\");\n\n registerMemoryCommands(memoryCmd, getDb);\n\n // -------------------------------------------------------------------------\n // pai mcp\n // -------------------------------------------------------------------------\n\n const mcpCmd = program\n .command(\"mcp\")\n .description(\"MCP server management: install and status\");\n\n registerMcpCommands(mcpCmd);\n\n // -------------------------------------------------------------------------\n // pai daemon\n // -------------------------------------------------------------------------\n\n const daemonCmd = program\n .command(\"daemon\")\n .description(\"PAI daemon management: serve, status, restart, install, uninstall, logs\");\n\n registerDaemonCommands(daemonCmd);\n\n // -------------------------------------------------------------------------\n // pai backup / pai restore\n // -------------------------------------------------------------------------\n\n registerBackupCommands(program);\n registerRestoreCommands(program);\n\n // -------------------------------------------------------------------------\n // pai setup / pai update\n // -------------------------------------------------------------------------\n\n registerSetupCommand(program);\n registerUpdateCommand(program);\n\n // -------------------------------------------------------------------------\n // pai notify\n // -------------------------------------------------------------------------\n\n const notifyCmd = program\n .command(\"notify\")\n .description(\"Notification config: status, get, set, test, send\");\n\n registerNotifyCommands(notifyCmd);\n\n // -------------------------------------------------------------------------\n // pai topic\n // -------------------------------------------------------------------------\n\n const topicCmd = program\n .command(\"topic\")\n .description(\"Topic shift detection: check whether context has drifted to a different project\");\n\n registerTopicCommands(topicCmd);\n\n // -------------------------------------------------------------------------\n // pai kg\n // -------------------------------------------------------------------------\n\n const kgCmd = program\n .command(\"kg\")\n .description(\"Temporal knowledge graph: backfill, query, list, stats\");\n\n registerKgCommands(kgCmd);\n\n // -------------------------------------------------------------------------\n // pai db\n // -------------------------------------------------------------------------\n\n const dbCmd = program\n .command(\"db\")\n .description(\"Database inspection: query, tables, schema (sqlite or postgres)\");\n\n registerDbCommands(dbCmd);\n\n // -------------------------------------------------------------------------\n // pai obsidian\n // -------------------------------------------------------------------------\n\n const obsidianCmd = program\n .command(\"obsidian\")\n .description(\"Obsidian vault: sync project notes, view status, open in Obsidian\");\n\n registerObsidianCommands(obsidianCmd, getDb);\n\n // -------------------------------------------------------------------------\n // pai zettel\n // -------------------------------------------------------------------------\n\n const zettelCmd = program\n .command(\"zettel\")\n .description(\"Zettelkasten intelligence: explore, surprise, converse, themes, health, suggest\");\n\n registerZettelCommands(zettelCmd, getDb);\n\n // -------------------------------------------------------------------------\n // pai observation\n // -------------------------------------------------------------------------\n\n const observationCmd = program\n .command(\"observation\")\n .description(\"Observation capture: list, search, and stats\");\n\n registerObservationCommands(observationCmd);\n\n // -------------------------------------------------------------------------\n // pai skill\n // -------------------------------------------------------------------------\n\n const skillCmd = program\n .command(\"skill\")\n .description(\"Skill telemetry and (future) discovery for the self-educating skill system\");\n\n registerSkillCommands(skillCmd);\n\n // -------------------------------------------------------------------------\n // pai help [area] — rich man-page viewer for the generated docs\n // -------------------------------------------------------------------------\n\n registerHelpCommand(program);\n\n // -------------------------------------------------------------------------\n // DAILY VERBS — top-level\n // -------------------------------------------------------------------------\n\n // pai pause [all] [--dry-run] [--exit] [--wait <ms>]\n program\n .command(\"pause [target]\")\n .description(\n \"Save state and display safe-exit instructions for the current session.\\n\" +\n \"Writes a ## Continue checkpoint to the project's TODO.md.\\n\" +\n \"Use `pai pause all` to pause every live session via AIBroker.\"\n )\n .option(\"--dry-run\", \"Preview changes without writing them\")\n .option(\"--exit\", \"(pause all only) Also send /exit to each session after pausing\")\n .option(\"--wait <ms>\", \"(pause all only) Milliseconds to wait before /exit (default: 5000)\", \"5000\")\n .action(async (target: string | undefined, opts: { dryRun?: boolean; exit?: boolean; wait?: string }) => {\n if (target === \"all\") {\n await cmdPauseAll({\n exit: opts.exit,\n dryRun: opts.dryRun,\n wait: opts.wait !== undefined ? parseInt(opts.wait, 10) : undefined,\n });\n } else {\n if (target !== undefined) {\n console.error(err(`Unknown target: ${target}. Did you mean 'pai pause all'?`));\n process.exitCode = 1;\n return;\n }\n cmdPause(getDb(), { dryRun: opts.dryRun });\n }\n });\n\n // pai end [--dry-run]\n program\n .command(\"end\")\n .description(\n \"Finalize a session: save state, mark note Completed, display safe-exit instructions.\"\n )\n .option(\"--dry-run\", \"Preview all changes without writing them\")\n .action((opts: { dryRun?: boolean }) => {\n cmdEnd(getDb(), opts);\n });\n\n // pai clear-names [--dry-run] — recovery: wipe corrupt iTerm/JSON name store\n program\n .command(\"clear-names\")\n .description(\n \"Recovery: wipe corrupted iTerm2 session name state.\\n\" +\n \"Clears ~/.aibroker/session-names.json AND user.paiName from all live iTerm2 sessions.\\n\" +\n \"After running this, use /Name to re-label each tab.\"\n )\n .option(\"--dry-run\", \"Preview what would be cleared without making changes\")\n .action(async (opts: { dryRun?: boolean }) => {\n await cmdClearNames(opts);\n });\n\n // -------------------------------------------------------------------------\n // HIDDEN COMPAT ALIASES\n // -------------------------------------------------------------------------\n\n // pai resume <name> [--dry-run] — hidden; prefer `pai <name>`\n program\n .command(\"resume <name>\", { hidden: true })\n .description(\"Go to a session by name or UUID (prefer: pai <name>)\")\n .option(\"--dry-run\", \"Print the exact argv and cwd, then exit without launching\")\n .action((name: string, opts: { dryRun?: boolean }) => {\n cmdGoto(getDb(), name, { dryRun: opts.dryRun });\n });\n\n // pai cd <identifier> — hidden; shell wrapper uses this\n program\n .command(\"cd <identifier>\", { hidden: true })\n .description(\"cd to a project directory (shell wrapper handles the actual cd)\")\n .action((identifier: string) => {\n const db = getDb();\n const project = resolveIdentifier(db, identifier);\n if (!project) {\n console.error(`Project not found: ${identifier}`);\n process.exit(1);\n return;\n }\n\n if (existsSync(project.root_path)) {\n process.stdout.write(project.root_path + \"\\n\");\n return;\n }\n\n process.stderr.write(\n warn(`Path not found: ${project.root_path}\\n`) +\n dim(\" Searching for moved location...\\n\")\n );\n\n const result = findMovedPath(project.root_path);\n\n if (result.found) {\n const newPath = result.found;\n const newEncoded = encodeDir(newPath);\n const ts = now();\n db.prepare(\n \"UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?\"\n ).run(newPath, newEncoded, ts, project.id);\n process.stderr.write(\n ok(`Project moved: ${shortenPath(project.root_path, 50)}\\n`) +\n dim(` → ${newPath}\\n`) +\n ok(\"Registry updated.\\n\")\n );\n process.stdout.write(newPath + \"\\n\");\n return;\n }\n\n if (result.ambiguous) {\n process.stderr.write(\n warn(`Multiple directories named \"${basename(project.root_path)}\" found:\\n`)\n );\n for (const candidate of result.ambiguous!) {\n process.stderr.write(dim(` ${candidate}\\n`));\n }\n process.stderr.write(\n dim(`\\n Disambiguate with: pai projects rebind ${project.slug} <path>\\n`)\n );\n process.exitCode = 1;\n return;\n }\n\n process.stderr.write(\n err(\n `Project \"${project.slug}\" root_path \"${project.root_path}\" does not exist on disk\\n` +\n ` and no folder named \"${basename(project.root_path)}\" was found in scan dirs.\\n`\n ) +\n dim(` Fix with: pai projects rebind ${project.slug} <new-path>\\n`)\n );\n process.exitCode = 1;\n });\n\n // -------------------------------------------------------------------------\n // pai notes — hidden power-user alias; accessible but not in main help\n // -------------------------------------------------------------------------\n\n const notesCmd = program\n .command(\"notes [project-slug]\", { hidden: true })\n .description(\"Markdown session notes — list notes, optionally filtered to a project.\")\n .option(\"--limit <n>\", \"Maximum number of notes to show\", \"20\")\n .option(\"--status <status>\", \"Filter by status: open | completed | compacted\")\n .action(\n (projectSlug: string | undefined, opts: { limit?: string; status?: string }) => {\n cmdNotesList(getDb(), projectSlug, opts);\n }\n );\n\n notesCmd\n .command(\"list [project-slug]\")\n .description(\"List markdown session notes (same as: pai notes)\")\n .option(\"--limit <n>\", \"Maximum number of notes to show\", \"20\")\n .option(\"--status <status>\", \"Filter by status: open | completed | compacted\")\n .action(\n (projectSlug: string | undefined, opts: { limit?: string; status?: string }) => {\n cmdNotesList(getDb(), projectSlug, opts);\n }\n );\n\n // -------------------------------------------------------------------------\n // pai find <query> — hidden power-user alias for history search\n // -------------------------------------------------------------------------\n\n program\n .command(\"find <query>\", { hidden: true })\n .description(\"Search prompt history for matching sessions.\")\n .option(\"-n, --n <count>\", \"Maximum number of sessions to show\", \"20\")\n .option(\"--json\", \"Output as JSON array\")\n .action(async (query: string, opts: { n?: string; json?: boolean }) => {\n await cmdFind(query, opts);\n });\n\n // -------------------------------------------------------------------------\n // pai shell-init — emit shell function for eval \"$(pai shell-init)\"\n // -------------------------------------------------------------------------\n\n program\n .command(\"shell-init\")\n .description('Emit shell integration code. Add to ~/.zshrc: eval \"$(pai shell-init)\"')\n .action(() => {\n process.stdout.write(\n `# PAI shell integration — generated by: pai shell-init\npai() {\n if [[ \"$1\" == \"cd\" ]]; then\n local dir=$(command pai cd \"$2\" 2>/dev/null)\n if [[ -n \"$dir\" && -d \"$dir\" ]]; then\n builtin cd \"$dir\"\n echo \"-> $dir\"\n else\n echo \"Project not found: $2\" >&2\n return 1\n fi\n elif [[ \"$1\" == \"projects\" && \"$2\" == \"cd\" ]]; then\n local dir=$(command pai projects cd \"$3\" 2>/dev/null)\n if [[ -n \"$dir\" && -d \"$dir\" ]]; then\n builtin cd \"$dir\"\n echo \"-> $dir\"\n else\n echo \"Project not found: $3\" >&2\n return 1\n fi\n elif [[ $# -eq 0 ]]; then\n # Bare \\`pai\\` → interactive picker. fzf draws on /dev/tty; the picker writes\n # a chosen directory to PAI_PICK_OUT when you press Ctrl-G (cd only), which we\n # then cd into here (a child process can't change this shell's cwd itself).\n local __pai_f\n __pai_f=$(mktemp -t pai-pick 2>/dev/null) || { command pai; return $?; }\n PAI_PICK_OUT=\"$__pai_f\" command pai\n local __pai_rc=$?\n local __pai_dir=\"\"\n [[ -s \"$__pai_f\" ]] && __pai_dir=$(cat \"$__pai_f\")\n command rm -f \"$__pai_f\"\n if [[ -n \"$__pai_dir\" && -d \"$__pai_dir\" ]]; then\n builtin cd \"$__pai_dir\"\n echo \"-> $__pai_dir\"\n fi\n return $__pai_rc\n else\n command pai \"$@\"\n fi\n}\n\n# Print the working directory after an interactive Claude Code session exits.\n# A SessionEnd hook can't do this reliably: Claude Code does not fire SessionEnd\n# on /exit (anthropics/claude-code#17885) and may reap the hook mid-run (#41577).\n# Running it from the shell, after \\`claude\\` returns, sidesteps both.\nclaude() {\n local __pai_skip=0 __pai_arg\n for __pai_arg in \"$@\"; do\n case \"$__pai_arg\" in\n -p|--print) __pai_skip=1 ;;\n esac\n done\n command claude \"$@\"\n local __pai_rc=$?\n if [[ -t 1 && $__pai_skip -eq 0 ]]; then\n printf '\\\\n\\\\033[2m📂 Working directory:\\\\033[0m %s\\\\n\\\\033[2m cd \"%s\"\\\\033[0m\\\\n' \"$PWD\" \"$PWD\"\n fi\n return $__pai_rc\n}\n`\n );\n });\n\n // -------------------------------------------------------------------------\n // pai [query] [pick] — DEFAULT COMMAND (must be registered LAST)\n // -------------------------------------------------------------------------\n\n program\n .command(\"query [query] [pick]\", { isDefault: true, hidden: true })\n .description(\n \"Find and launch a session by topic, name, or UUID.\\n\" +\n \"No arg → deduped session listing\\n\" +\n \"Name → switch live tab, or resume, or start fresh\\n\" +\n \"UUID → direct resume via filesystem scan\\n\" +\n \"Topic → history search → candidate list → pick #\"\n )\n .option(\"-y, --auto\", \"Auto-pick #1 without prompting\")\n .option(\"--dry-run\", \"Print what would happen without launching\")\n .option(\"-n, --n <count>\", \"Max candidates for history search\", \"20\")\n .option(\"--all\", \"Show all entries including cold / zero-session / archived projects\")\n .option(\"--list\", \"Skip the interactive picker; print the static session listing\")\n .action(async (query: string | undefined, pick: string | undefined, opts: { auto?: boolean; dryRun?: boolean; n?: string; all?: boolean; list?: boolean }) => {\n // No query → interactive fzf picker (projects + sessions, go where you pick).\n // `--list` (or a non-TTY pipe, handled inside cmdPick) falls back to the\n // static listing. Any query still goes through the name/UUID/topic resolver.\n if (query === undefined && !opts.list) {\n await cmdPick(getDb(), { all: opts.all, dryRun: opts.dryRun });\n return;\n }\n const pickN = pick !== undefined ? parseInt(pick, 10) : undefined;\n await cmdMain(getDb(), query, pickN, opts);\n });\n\n // -------------------------------------------------------------------------\n // Error handling\n // -------------------------------------------------------------------------\n\n program.configureOutput({\n writeErr: (str) => process.stderr.write(err(str)),\n });\n\n program.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 process.exit(1);\n }\n console.error(err(error.message));\n process.exit(1);\n });\n\n return program;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"program.mjs","names":[],"sources":["../../src/cli/program.ts"],"sourcesContent":["/**\n * PAI CLI — Commander program builder.\n *\n * Constructs the full `pai` command tree WITHOUT parsing argv. This separation\n * lets the documentation generator (scripts/build-docs.mjs) import and introspect\n * the live program object — the single source of truth for `--help`, the\n * generated man pages, and `pai help <area>`.\n *\n * The thin entry point (cli/index.ts) imports buildProgram() and calls parse().\n */\n\nimport { Command } from \"commander\";\nimport { readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { openRegistry } from \"../registry/db.js\";\nimport type { Database } from \"better-sqlite3\";\nimport { registerProjectsCommands } from \"./commands/project/projects-index.js\";\nimport { findMovedPath } from \"./commands/project/commands.js\";\nimport { existsSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport { 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 { registerSkillCommands } from \"./commands/skill.js\";\nimport { registerUpdateCommand } from \"./commands/update.js\";\nimport { registerNotifyCommands } from \"./commands/notify.js\";\nimport { registerTaskCommands } from \"./commands/task.js\";\nimport { registerTopicCommands } from \"./commands/topic.js\";\nimport { registerKgCommands } from \"./commands/kg.js\";\nimport { registerDbCommands } from \"./commands/db.js\";\nimport { registerHelpCommand } from \"./commands/help.js\";\nimport { err, warn, ok, dim, shortenPath, encodeDir, now } from \"./utils.js\";\nimport { cmdPause } from \"./commands/session/pause.js\";\nimport { cmdEnd } from \"./commands/session/end.js\";\nimport { cmdGoto } from \"./commands/session/goto.js\";\nimport { cmdPauseAll } from \"./commands/session/pause-all.js\";\nimport { cmdClearNames } from \"./commands/session/clear-names.js\";\nimport { cmdList as cmdNotesList } from \"./commands/session/commands.js\";\nimport { resolveIdentifier } from \"./commands/project/helpers.js\";\nimport { cmdFind } from \"./commands/find.js\";\nimport { cmdMain } from \"./commands/main-resolver.js\";\nimport { cmdPick } from \"./commands/pick.js\";\n\n// ---------------------------------------------------------------------------\n// Version resolution\n// ---------------------------------------------------------------------------\n\nfunction getVersion(): string {\n try {\n const __dirname = dirname(fileURLToPath(import.meta.url));\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\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 builder\n// ---------------------------------------------------------------------------\n\n/**\n * Build and return the fully configured `pai` Commander program.\n * Does NOT call parse — the caller (cli/index.ts) does that.\n */\nexport function buildProgram(): Command {\n const program = new Command();\n\n program\n .name(\"pai\")\n .description(\"PAI Knowledge OS — Personal AI Infrastructure CLI\")\n .version(getVersion(), \"-V, --version\", \"Print version and exit\")\n .addHelpText(\n \"after\",\n `\nDaily commands:\n pai Show your work: live tabs + recent sessions + recent projects\n pai <name> Switch / resume / fresh — universal\n pai pause [all] Pause current (or all live) sessions\n pai end Finalize current session\n\nSubcommands (rarely needed):\n pai projects ... Project registry management (cd, rebind, info, ...)\n pai registry ... Registry maintenance (scan, ...)\n pai memory ... Memory engine (index, search, ...)\n pai shell-init Shell integration (eval \"$(pai shell-init)\")\n\nRecovery:\n pai clear-names Wipe corrupt iTerm tab-name store\n\nExamples:\n pai aibroker Switch to the live AIBroker tab in iTerm\n pai mdf Find sessions where you worked on MDF\n pai 81c5c3dc Resume session by UUID prefix\n pai --all Show everything including cold and zero-session projects`\n );\n\n // -------------------------------------------------------------------------\n // pai projects (canonical plural namespace)\n // -------------------------------------------------------------------------\n\n const projectsCmd = program\n .command(\"projects\")\n .description(\"Manage registered projects (list, cd, add, info, ...)\");\n\n registerProjectsCommands(projectsCmd, getDb);\n\n // Singular alias: `pai project` → `pai projects`\n const projectCmd = program\n .command(\"project\")\n .description(\"Alias for `pai projects`\");\n\n registerProjectsCommands(projectCmd, getDb);\n\n // -------------------------------------------------------------------------\n // pai sessions (alias for the unified `pai` listing)\n // -------------------------------------------------------------------------\n\n program\n .command(\"sessions\")\n .description(\"Alias for `pai` — show the unified deduped listing.\")\n .option(\"--all\", \"Show all entries including cold / zero-session / archived projects\")\n .option(\"-n, --n <count>\", \"Max candidates for history search\", \"20\")\n .action(async (opts: { all?: boolean; n?: string }) => {\n await cmdMain(getDb(), undefined, undefined, opts);\n });\n\n // -------------------------------------------------------------------------\n // pai registry\n // -------------------------------------------------------------------------\n\n const registryCmd = program\n .command(\"registry\")\n .description(\"Registry maintenance: scan, migrate, stats, rebuild\");\n\n registerRegistryCommands(registryCmd, getDb);\n\n // -------------------------------------------------------------------------\n // pai memory\n // -------------------------------------------------------------------------\n\n const memoryCmd = program\n .command(\"memory\")\n .description(\"Memory engine: index, search, and status\");\n\n registerMemoryCommands(memoryCmd, getDb);\n\n // -------------------------------------------------------------------------\n // pai mcp\n // -------------------------------------------------------------------------\n\n const mcpCmd = program\n .command(\"mcp\")\n .description(\"MCP server management: install and status\");\n\n registerMcpCommands(mcpCmd);\n\n // -------------------------------------------------------------------------\n // pai daemon\n // -------------------------------------------------------------------------\n\n const daemonCmd = program\n .command(\"daemon\")\n .description(\"PAI daemon management: serve, status, restart, install, uninstall, logs\");\n\n registerDaemonCommands(daemonCmd);\n\n // -------------------------------------------------------------------------\n // pai backup / pai restore\n // -------------------------------------------------------------------------\n\n registerBackupCommands(program);\n registerRestoreCommands(program);\n\n // -------------------------------------------------------------------------\n // pai setup / pai update\n // -------------------------------------------------------------------------\n\n registerSetupCommand(program);\n registerUpdateCommand(program);\n\n // -------------------------------------------------------------------------\n // pai notify\n // -------------------------------------------------------------------------\n\n const notifyCmd = program\n .command(\"notify\")\n .description(\"Notification config: status, get, set, test, send\");\n\n registerNotifyCommands(notifyCmd);\n\n // -------------------------------------------------------------------------\n // pai task\n // -------------------------------------------------------------------------\n\n const taskCmd = program\n .command(\"task\")\n .alias(\"tasks\")\n .description(\"Task bus: list, add, dispatch, and complete cross-session work\");\n\n registerTaskCommands(taskCmd);\n\n // -------------------------------------------------------------------------\n // pai topic\n // -------------------------------------------------------------------------\n\n const topicCmd = program\n .command(\"topic\")\n .description(\"Topic shift detection: check whether context has drifted to a different project\");\n\n registerTopicCommands(topicCmd);\n\n // -------------------------------------------------------------------------\n // pai kg\n // -------------------------------------------------------------------------\n\n const kgCmd = program\n .command(\"kg\")\n .description(\"Temporal knowledge graph: backfill, query, list, stats\");\n\n registerKgCommands(kgCmd);\n\n // -------------------------------------------------------------------------\n // pai db\n // -------------------------------------------------------------------------\n\n const dbCmd = program\n .command(\"db\")\n .description(\"Database inspection: query, tables, schema (sqlite or postgres)\");\n\n registerDbCommands(dbCmd);\n\n // -------------------------------------------------------------------------\n // pai obsidian\n // -------------------------------------------------------------------------\n\n const obsidianCmd = program\n .command(\"obsidian\")\n .description(\"Obsidian vault: sync project notes, view status, open in Obsidian\");\n\n registerObsidianCommands(obsidianCmd, getDb);\n\n // -------------------------------------------------------------------------\n // pai zettel\n // -------------------------------------------------------------------------\n\n const zettelCmd = program\n .command(\"zettel\")\n .description(\"Zettelkasten intelligence: explore, surprise, converse, themes, health, suggest\");\n\n registerZettelCommands(zettelCmd, getDb);\n\n // -------------------------------------------------------------------------\n // pai observation\n // -------------------------------------------------------------------------\n\n const observationCmd = program\n .command(\"observation\")\n .description(\"Observation capture: list, search, and stats\");\n\n registerObservationCommands(observationCmd);\n\n // -------------------------------------------------------------------------\n // pai skill\n // -------------------------------------------------------------------------\n\n const skillCmd = program\n .command(\"skill\")\n .description(\"Skill telemetry and (future) discovery for the self-educating skill system\");\n\n registerSkillCommands(skillCmd);\n\n // -------------------------------------------------------------------------\n // pai help [area] — rich man-page viewer for the generated docs\n // -------------------------------------------------------------------------\n\n registerHelpCommand(program);\n\n // -------------------------------------------------------------------------\n // DAILY VERBS — top-level\n // -------------------------------------------------------------------------\n\n // pai pause [all] [--dry-run] [--exit] [--wait <ms>]\n program\n .command(\"pause [target]\")\n .description(\n \"Save state and display safe-exit instructions for the current session.\\n\" +\n \"Writes a ## Continue checkpoint to the project's TODO.md.\\n\" +\n \"Use `pai pause all` to pause every live session via AIBroker.\"\n )\n .option(\"--dry-run\", \"Preview changes without writing them\")\n .option(\"--exit\", \"(pause all only) Also send /exit to each session after pausing\")\n .option(\"--wait <ms>\", \"(pause all only) Milliseconds to wait before /exit (default: 5000)\", \"5000\")\n .action(async (target: string | undefined, opts: { dryRun?: boolean; exit?: boolean; wait?: string }) => {\n if (target === \"all\") {\n await cmdPauseAll({\n exit: opts.exit,\n dryRun: opts.dryRun,\n wait: opts.wait !== undefined ? parseInt(opts.wait, 10) : undefined,\n });\n } else {\n if (target !== undefined) {\n console.error(err(`Unknown target: ${target}. Did you mean 'pai pause all'?`));\n process.exitCode = 1;\n return;\n }\n cmdPause(getDb(), { dryRun: opts.dryRun });\n }\n });\n\n // pai end [--dry-run]\n program\n .command(\"end\")\n .description(\n \"Finalize a session: save state, mark note Completed, display safe-exit instructions.\"\n )\n .option(\"--dry-run\", \"Preview all changes without writing them\")\n .action((opts: { dryRun?: boolean }) => {\n cmdEnd(getDb(), opts);\n });\n\n // pai clear-names [--dry-run] — recovery: wipe corrupt iTerm/JSON name store\n program\n .command(\"clear-names\")\n .description(\n \"Recovery: wipe corrupted iTerm2 session name state.\\n\" +\n \"Clears ~/.aibroker/session-names.json AND user.paiName from all live iTerm2 sessions.\\n\" +\n \"After running this, use /Name to re-label each tab.\"\n )\n .option(\"--dry-run\", \"Preview what would be cleared without making changes\")\n .action(async (opts: { dryRun?: boolean }) => {\n await cmdClearNames(opts);\n });\n\n // -------------------------------------------------------------------------\n // HIDDEN COMPAT ALIASES\n // -------------------------------------------------------------------------\n\n // pai resume <name> [--dry-run] — hidden; prefer `pai <name>`\n program\n .command(\"resume <name>\", { hidden: true })\n .description(\"Go to a session by name or UUID (prefer: pai <name>)\")\n .option(\"--dry-run\", \"Print the exact argv and cwd, then exit without launching\")\n .action((name: string, opts: { dryRun?: boolean }) => {\n cmdGoto(getDb(), name, { dryRun: opts.dryRun });\n });\n\n // pai cd <identifier> — hidden; shell wrapper uses this\n program\n .command(\"cd <identifier>\", { hidden: true })\n .description(\"cd to a project directory (shell wrapper handles the actual cd)\")\n .action((identifier: string) => {\n const db = getDb();\n const project = resolveIdentifier(db, identifier);\n if (!project) {\n console.error(`Project not found: ${identifier}`);\n process.exit(1);\n return;\n }\n\n if (existsSync(project.root_path)) {\n process.stdout.write(project.root_path + \"\\n\");\n return;\n }\n\n process.stderr.write(\n warn(`Path not found: ${project.root_path}\\n`) +\n dim(\" Searching for moved location...\\n\")\n );\n\n const result = findMovedPath(project.root_path);\n\n if (result.found) {\n const newPath = result.found;\n const newEncoded = encodeDir(newPath);\n const ts = now();\n db.prepare(\n \"UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?\"\n ).run(newPath, newEncoded, ts, project.id);\n process.stderr.write(\n ok(`Project moved: ${shortenPath(project.root_path, 50)}\\n`) +\n dim(` → ${newPath}\\n`) +\n ok(\"Registry updated.\\n\")\n );\n process.stdout.write(newPath + \"\\n\");\n return;\n }\n\n if (result.ambiguous) {\n process.stderr.write(\n warn(`Multiple directories named \"${basename(project.root_path)}\" found:\\n`)\n );\n for (const candidate of result.ambiguous!) {\n process.stderr.write(dim(` ${candidate}\\n`));\n }\n process.stderr.write(\n dim(`\\n Disambiguate with: pai projects rebind ${project.slug} <path>\\n`)\n );\n process.exitCode = 1;\n return;\n }\n\n process.stderr.write(\n err(\n `Project \"${project.slug}\" root_path \"${project.root_path}\" does not exist on disk\\n` +\n ` and no folder named \"${basename(project.root_path)}\" was found in scan dirs.\\n`\n ) +\n dim(` Fix with: pai projects rebind ${project.slug} <new-path>\\n`)\n );\n process.exitCode = 1;\n });\n\n // -------------------------------------------------------------------------\n // pai notes — hidden power-user alias; accessible but not in main help\n // -------------------------------------------------------------------------\n\n const notesCmd = program\n .command(\"notes [project-slug]\", { hidden: true })\n .description(\"Markdown session notes — list notes, optionally filtered to a project.\")\n .option(\"--limit <n>\", \"Maximum number of notes to show\", \"20\")\n .option(\"--status <status>\", \"Filter by status: open | completed | compacted\")\n .action(\n (projectSlug: string | undefined, opts: { limit?: string; status?: string }) => {\n cmdNotesList(getDb(), projectSlug, opts);\n }\n );\n\n notesCmd\n .command(\"list [project-slug]\")\n .description(\"List markdown session notes (same as: pai notes)\")\n .option(\"--limit <n>\", \"Maximum number of notes to show\", \"20\")\n .option(\"--status <status>\", \"Filter by status: open | completed | compacted\")\n .action(\n (projectSlug: string | undefined, opts: { limit?: string; status?: string }) => {\n cmdNotesList(getDb(), projectSlug, opts);\n }\n );\n\n // -------------------------------------------------------------------------\n // pai find <query> — hidden power-user alias for history search\n // -------------------------------------------------------------------------\n\n program\n .command(\"find <query>\", { hidden: true })\n .description(\"Search prompt history for matching sessions.\")\n .option(\"-n, --n <count>\", \"Maximum number of sessions to show\", \"20\")\n .option(\"--json\", \"Output as JSON array\")\n .action(async (query: string, opts: { n?: string; json?: boolean }) => {\n await cmdFind(query, opts);\n });\n\n // -------------------------------------------------------------------------\n // pai shell-init — emit shell function for eval \"$(pai shell-init)\"\n // -------------------------------------------------------------------------\n\n program\n .command(\"shell-init\")\n .description('Emit shell integration code. Add to ~/.zshrc: eval \"$(pai shell-init)\"')\n .action(() => {\n process.stdout.write(\n `# PAI shell integration — generated by: pai shell-init\npai() {\n if [[ \"$1\" == \"cd\" ]]; then\n local dir=$(command pai cd \"$2\" 2>/dev/null)\n if [[ -n \"$dir\" && -d \"$dir\" ]]; then\n builtin cd \"$dir\"\n echo \"-> $dir\"\n else\n echo \"Project not found: $2\" >&2\n return 1\n fi\n elif [[ \"$1\" == \"projects\" && \"$2\" == \"cd\" ]]; then\n local dir=$(command pai projects cd \"$3\" 2>/dev/null)\n if [[ -n \"$dir\" && -d \"$dir\" ]]; then\n builtin cd \"$dir\"\n echo \"-> $dir\"\n else\n echo \"Project not found: $3\" >&2\n return 1\n fi\n elif [[ $# -eq 0 ]]; then\n # Bare \\`pai\\` → interactive picker. fzf draws on /dev/tty; the picker writes\n # a chosen directory to PAI_PICK_OUT when you press Ctrl-G (cd only), which we\n # then cd into here (a child process can't change this shell's cwd itself).\n local __pai_f\n __pai_f=$(mktemp -t pai-pick 2>/dev/null) || { command pai; return $?; }\n PAI_PICK_OUT=\"$__pai_f\" command pai\n local __pai_rc=$?\n local __pai_dir=\"\"\n [[ -s \"$__pai_f\" ]] && __pai_dir=$(cat \"$__pai_f\")\n command rm -f \"$__pai_f\"\n if [[ -n \"$__pai_dir\" && -d \"$__pai_dir\" ]]; then\n builtin cd \"$__pai_dir\"\n echo \"-> $__pai_dir\"\n fi\n return $__pai_rc\n else\n command pai \"$@\"\n fi\n}\n\n# Print the working directory after an interactive Claude Code session exits.\n# A SessionEnd hook can't do this reliably: Claude Code does not fire SessionEnd\n# on /exit (anthropics/claude-code#17885) and may reap the hook mid-run (#41577).\n# Running it from the shell, after \\`claude\\` returns, sidesteps both.\nclaude() {\n local __pai_skip=0 __pai_arg\n for __pai_arg in \"$@\"; do\n case \"$__pai_arg\" in\n -p|--print) __pai_skip=1 ;;\n esac\n done\n command claude \"$@\"\n local __pai_rc=$?\n if [[ -t 1 && $__pai_skip -eq 0 ]]; then\n printf '\\\\n\\\\033[2m📂 Working directory:\\\\033[0m %s\\\\n\\\\033[2m cd \"%s\"\\\\033[0m\\\\n' \"$PWD\" \"$PWD\"\n fi\n return $__pai_rc\n}\n`\n );\n });\n\n // -------------------------------------------------------------------------\n // pai [query] [pick] — DEFAULT COMMAND (must be registered LAST)\n // -------------------------------------------------------------------------\n\n program\n .command(\"query [query] [pick]\", { isDefault: true, hidden: true })\n .description(\n \"Find and launch a session by topic, name, or UUID.\\n\" +\n \"No arg → deduped session listing\\n\" +\n \"Name → switch live tab, or resume, or start fresh\\n\" +\n \"UUID → direct resume via filesystem scan\\n\" +\n \"Topic → history search → candidate list → pick #\"\n )\n .option(\"-y, --auto\", \"Auto-pick #1 without prompting\")\n .option(\"--dry-run\", \"Print what would happen without launching\")\n .option(\"-n, --n <count>\", \"Max candidates for history search\", \"20\")\n .option(\"--all\", \"Show all entries including cold / zero-session / archived projects\")\n .option(\"--list\", \"Skip the interactive picker; print the static session listing\")\n .action(async (query: string | undefined, pick: string | undefined, opts: { auto?: boolean; dryRun?: boolean; n?: string; all?: boolean; list?: boolean }) => {\n // No query → interactive fzf picker (projects + sessions, go where you pick).\n // `--list` (or a non-TTY pipe, handled inside cmdPick) falls back to the\n // static listing. Any query still goes through the name/UUID/topic resolver.\n if (query === undefined && !opts.list) {\n await cmdPick(getDb(), { all: opts.all, dryRun: opts.dryRun });\n return;\n }\n const pickN = pick !== undefined ? parseInt(pick, 10) : undefined;\n await cmdMain(getDb(), query, pickN, opts);\n });\n\n // -------------------------------------------------------------------------\n // Error handling\n // -------------------------------------------------------------------------\n\n program.configureOutput({\n writeErr: (str) => process.stderr.write(err(str)),\n });\n\n program.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 process.exit(1);\n }\n console.error(err(error.message));\n process.exit(1);\n });\n\n return program;\n}\n"],"mappings":"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"}
|
|
@@ -44,6 +44,25 @@ const DEFAULT_NOTIFICATION_CONFIG = {
|
|
|
44
44
|
routing: DEFAULT_ROUTING
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/tasks/types.ts
|
|
49
|
+
/**
|
|
50
|
+
* Prefix marking a tracker label as a PAI ownership assertion.
|
|
51
|
+
* A task labelled `pai:acme-api` is owned by the `acme-api` project.
|
|
52
|
+
*/
|
|
53
|
+
const OWNER_LABEL_PREFIX = "pai:";
|
|
54
|
+
/** An unresolved owner. UNROUTED is a normal state, not an error. */
|
|
55
|
+
const UNROUTED = {
|
|
56
|
+
project: null,
|
|
57
|
+
rootPath: null,
|
|
58
|
+
source: "none"
|
|
59
|
+
};
|
|
60
|
+
const DEFAULT_TASK_CONFIG = {
|
|
61
|
+
enabled: false,
|
|
62
|
+
providers: { todoist: { enabled: false } },
|
|
63
|
+
autoDispatch: false
|
|
64
|
+
};
|
|
65
|
+
|
|
47
66
|
//#endregion
|
|
48
67
|
//#region src/daemon/config.ts
|
|
49
68
|
/**
|
|
@@ -82,6 +101,7 @@ const DEFAULTS = {
|
|
|
82
101
|
embeddingModel: "Snowflake/snowflake-arctic-embed-m-v1.5",
|
|
83
102
|
logLevel: "info",
|
|
84
103
|
notifications: DEFAULT_NOTIFICATION_CONFIG,
|
|
104
|
+
tasks: DEFAULT_TASK_CONFIG,
|
|
85
105
|
search: {
|
|
86
106
|
mode: "keyword",
|
|
87
107
|
rerank: true,
|
|
@@ -178,5 +198,5 @@ function ensureConfigDir() {
|
|
|
178
198
|
}
|
|
179
199
|
|
|
180
200
|
//#endregion
|
|
181
|
-
export { expandHome as a, ensureConfigDir as i, CONFIG_FILE as n, loadConfig as o, config_exports as r,
|
|
182
|
-
//# sourceMappingURL=config-
|
|
201
|
+
export { expandHome as a, UNROUTED as c, ensureConfigDir as i, DEFAULT_NOTIFICATION_CONFIG as l, CONFIG_FILE as n, loadConfig as o, config_exports as r, OWNER_LABEL_PREFIX as s, CONFIG_DIR as t };
|
|
202
|
+
//# sourceMappingURL=config-C8m-tPhP.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-C8m-tPhP.mjs","names":[],"sources":["../src/notifications/types.ts","../src/tasks/types.ts","../src/daemon/config.ts"],"sourcesContent":["/**\n * types.ts — Unified Notification Framework type definitions\n *\n * Defines the channel registry, event routing, and configuration schema\n * for PAI's notification subsystem.\n */\n\n// ---------------------------------------------------------------------------\n// Channel identifiers\n// ---------------------------------------------------------------------------\n\nexport type ChannelId = \"ntfy\" | \"whatsapp\" | \"macos\" | \"voice\" | \"cli\";\n\n// ---------------------------------------------------------------------------\n// Notification event types\n// ---------------------------------------------------------------------------\n\n/**\n * The semantic type of a notification event.\n * Used to route events to the appropriate channels.\n */\nexport type NotificationEvent =\n | \"error\"\n | \"progress\"\n | \"completion\"\n | \"info\"\n | \"debug\";\n\n// ---------------------------------------------------------------------------\n// Notification mode\n// ---------------------------------------------------------------------------\n\n/**\n * The current notification mode.\n *\n * - \"auto\" — Use the per-event routing table (default)\n * - \"voice\" — All events go to voice (WhatsApp TTS)\n * - \"whatsapp\" — All events go to WhatsApp text\n * - \"ntfy\" — All events go to ntfy.sh\n * - \"macos\" — All events go to macOS notifications\n * - \"cli\" — All events go to CLI stdout only\n * - \"off\" — Suppress all notifications\n */\nexport type NotificationMode =\n | \"auto\"\n | \"voice\"\n | \"whatsapp\"\n | \"ntfy\"\n | \"macos\"\n | \"cli\"\n | \"off\";\n\n// ---------------------------------------------------------------------------\n// Per-channel configuration\n// ---------------------------------------------------------------------------\n\nexport interface NtfyChannelConfig {\n enabled: boolean;\n /** ntfy.sh topic URL, e.g. \"https://ntfy.sh/my-topic\" */\n url?: string;\n /** ntfy priority: min | low | default | high | urgent */\n priority?: \"min\" | \"low\" | \"default\" | \"high\" | \"urgent\";\n}\n\nexport interface WhatsAppChannelConfig {\n enabled: boolean;\n /** Optional recipient (phone, JID, or contact name). Omit for self-chat. */\n recipient?: string;\n}\n\nexport interface MacOsChannelConfig {\n enabled: boolean;\n}\n\nexport interface VoiceChannelConfig {\n enabled: boolean;\n /** Kokoro voice name, e.g. \"bm_george\", \"af_bella\". Default: \"bm_george\" */\n voiceName?: string;\n}\n\nexport interface CliChannelConfig {\n enabled: boolean;\n}\n\nexport interface ChannelConfigs {\n ntfy: NtfyChannelConfig;\n whatsapp: WhatsAppChannelConfig;\n macos: MacOsChannelConfig;\n voice: VoiceChannelConfig;\n cli: CliChannelConfig;\n}\n\n// ---------------------------------------------------------------------------\n// Routing table\n// ---------------------------------------------------------------------------\n\n/**\n * Maps each event type to the ordered list of channels that should receive it.\n * Only channels that are enabled in `channels` and present in this list are used.\n */\nexport type RoutingTable = {\n [K in NotificationEvent]: ChannelId[];\n};\n\nexport const DEFAULT_ROUTING: RoutingTable = {\n error: [\"whatsapp\", \"macos\", \"ntfy\", \"cli\"],\n completion: [\"whatsapp\", \"macos\", \"ntfy\", \"cli\"],\n info: [\"cli\"],\n progress: [\"cli\"],\n debug: [],\n};\n\n// ---------------------------------------------------------------------------\n// Top-level notification config (embedded in PaiDaemonConfig)\n// ---------------------------------------------------------------------------\n\nexport interface NotificationConfig {\n /** Current routing mode. Default: \"auto\" */\n mode: NotificationMode;\n /** Per-channel configuration */\n channels: ChannelConfigs;\n /** Event → channel routing (used in \"auto\" mode) */\n routing: RoutingTable;\n}\n\nexport const DEFAULT_CHANNELS: ChannelConfigs = {\n ntfy: {\n enabled: false,\n url: undefined,\n priority: \"default\",\n },\n whatsapp: {\n enabled: true,\n recipient: undefined,\n },\n macos: {\n enabled: true,\n },\n voice: {\n enabled: false,\n voiceName: \"bm_george\",\n },\n cli: {\n enabled: true,\n },\n};\n\nexport const DEFAULT_NOTIFICATION_CONFIG: NotificationConfig = {\n mode: \"auto\",\n channels: DEFAULT_CHANNELS,\n routing: DEFAULT_ROUTING,\n};\n\n// ---------------------------------------------------------------------------\n// Notification payload\n// ---------------------------------------------------------------------------\n\nexport interface NotificationPayload {\n /** Semantic event type — used for routing */\n event: NotificationEvent;\n /** The notification message body */\n message: string;\n /** Optional title (used by macOS, ntfy) */\n title?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Provider interface\n// ---------------------------------------------------------------------------\n\nexport interface NotificationProvider {\n readonly channelId: ChannelId;\n /**\n * Send a notification.\n * Returns true on success, false on failure (failure is non-fatal).\n */\n send(payload: NotificationPayload, config: NotificationConfig): Promise<boolean>;\n}\n\n// ---------------------------------------------------------------------------\n// Send result\n// ---------------------------------------------------------------------------\n\nexport interface SendResult {\n channelsAttempted: ChannelId[];\n channelsSucceeded: ChannelId[];\n channelsFailed: ChannelId[];\n mode: NotificationMode;\n}\n","/**\n * types.ts — Task Bus type definitions\n *\n * Defines the provider registry, ownership resolution, and configuration schema\n * for PAI's cross-session task subsystem.\n *\n * The task bus routes work between PAI sessions through an external tracker.\n * A session files a task; a routine reads it later and dispatches it to the\n * session that owns it — spawning one if none is running.\n *\n * See Notes/docs/task-bus.md for the architecture and its constraints.\n */\n\n// ---------------------------------------------------------------------------\n// Provider identifiers\n// ---------------------------------------------------------------------------\n\nexport type ProviderId = \"todoist\";\n\n// ---------------------------------------------------------------------------\n// Ownership\n// ---------------------------------------------------------------------------\n\n/**\n * Prefix marking a tracker label as a PAI ownership assertion.\n * A task labelled `pai:acme-api` is owned by the `acme-api` project.\n */\nexport const OWNER_LABEL_PREFIX = \"pai:\";\n\n/**\n * How a task's owner was determined. Recorded so the routine can explain\n * itself, and so a mis-resolution is diagnosable rather than silent.\n *\n * - \"label\" — an explicit `pai:<project>` label (authoritative)\n * - \"container\" — the enclosing sub-project name matched a PAI alias (fallback)\n * - \"none\" — unresolved; the task stays in the findings inbox\n */\nexport type OwnerSource = \"label\" | \"container\" | \"none\";\n\nexport interface TaskOwner {\n /** Resolved PAI project short name, e.g. \"acme-api\". Null when UNROUTED. */\n project: string | null;\n /** Absolute path to the project root. Null when UNROUTED. */\n rootPath: string | null;\n source: OwnerSource;\n /**\n * The raw string that resolution was attempted against, kept for diagnostics\n * when `source` is \"none\" — e.g. a \"Reading List 📚\" container matches no\n * PAI project, which is expected rather than a fault.\n */\n rawHint?: string;\n}\n\n/** An unresolved owner. UNROUTED is a normal state, not an error. */\nexport const UNROUTED: TaskOwner = {\n project: null,\n rootPath: null,\n source: \"none\",\n};\n\n// ---------------------------------------------------------------------------\n// Tasks\n// ---------------------------------------------------------------------------\n\nexport type TaskPriority = \"p1\" | \"p2\" | \"p3\" | \"p4\";\n\nexport interface Task {\n /** Provider-native task ID. Opaque; never parsed. */\n id: string;\n title: string;\n /**\n * Full procedure AND reasoning — enough that the task is actionable months\n * later, or by the user alone, without re-deriving anything. Enforced at\n * filing time rather than left to discipline.\n */\n body: string;\n owner: TaskOwner;\n /** ISO 8601 date or datetime. Null when the task has no due date. */\n due: string | null;\n priority: TaskPriority;\n labels: string[];\n /**\n * Stable reference to the artifact this task is about. Prefer a `hook://`\n * URL over a filesystem path — it survives renames and moves, and opens in\n * DEVONthink To Go on iOS.\n */\n sourceUrl?: string;\n /** True for organizational headers that cannot be completed. */\n isHeader?: boolean;\n}\n\n/** A task being filed. `owner` is a project short name, resolved on write. */\nexport interface NewTask {\n title: string;\n body: string;\n owner?: string | null;\n due?: string;\n priority?: TaskPriority;\n labels?: string[];\n sourceUrl?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Provider interface\n// ---------------------------------------------------------------------------\n\nexport interface ListOptions {\n /** Only tasks due on or before this ISO date. Omit for all open tasks. */\n dueBefore?: string;\n /** Restrict to one resolved owner. Omit for every owner. */\n owner?: string;\n /** Include tasks that resolved to UNROUTED. Default: true. */\n includeUnrouted?: boolean;\n limit?: number;\n}\n\nexport interface TaskProvider {\n readonly providerId: ProviderId;\n\n /**\n * False when no credential is configured. The bus degrades to a no-op\n * rather than failing — a user without a tracker still gets working PAI.\n */\n isConfigured(): boolean;\n\n listOpen(opts: ListOptions): Promise<Task[]>;\n add(task: NewTask): Promise<Task>;\n complete(id: string): Promise<void>;\n}\n\n// ---------------------------------------------------------------------------\n// Configuration\n// ---------------------------------------------------------------------------\n\nexport interface TodoistProviderConfig {\n enabled: boolean;\n /**\n * API token. Resolution order is apiKey → TODOIST_API_KEY env → unconfigured.\n *\n * Never read this from another tool's config file. PAI ships as a product;\n * scraping ~/.claude.json for a key belonging to the Todoist MCP is not\n * acceptable even though the key is sitting there.\n */\n apiKey?: string;\n /**\n * Tracker project ID that roots the bus (the \"Claude 🤖\" project).\n *\n * Stored as an ID, never a name. Todoist's project search silently returns\n * zero results for names containing emoji — resolving by name would report\n * \"no tasks\" instead of failing, which is the exact class of silent failure\n * this subsystem exists to surface.\n */\n rootProjectId?: string;\n /** Section ID for the findings inbox. Tasks land here when UNROUTED. */\n findingsSectionId?: string;\n}\n\nexport interface TaskConfig {\n /** Master switch. When false the bus is inert. */\n enabled: boolean;\n providers: {\n todoist: TodoistProviderConfig;\n };\n /**\n * Dispatch work to the owning session automatically, spawning one if absent.\n * Requires AIBroker. When false — or when AIBroker is unavailable — PAI\n * reports which project owns each task and leaves acting to the user.\n */\n autoDispatch: boolean;\n}\n\nexport const DEFAULT_TASK_CONFIG: TaskConfig = {\n enabled: false,\n providers: {\n todoist: {\n enabled: false,\n },\n },\n autoDispatch: false,\n};\n\n// ---------------------------------------------------------------------------\n// Dispatch results\n// ---------------------------------------------------------------------------\n\n/**\n * What happened to one task during a dispatch run.\n *\n * - \"delivered\" — sent to an already-running session, and confirmed submitted\n * - \"spawned\" — none running; one was launched, came up, and received it\n * - \"unrouted\" — no owner resolved; left in the findings inbox\n * - \"unlaunchable\" — an owner resolved but no PAI alias exists to launch it\n * - \"unreachable\" — a session was launched but never became ready to accept input\n * - \"skipped\" — autoDispatch is off, or no transport; reported only\n *\n * `unlaunchable` and `unreachable` are distinct because the fixes differ:\n * the first is a setup gap (register an alias), the second is a runtime\n * failure (find out why the session did not come up). Collapsing them would\n * send users looking in the wrong place.\n *\n * None of these are errors. A task that could not be delivered is a routing\n * result to report, not an exception to throw.\n */\nexport type DispatchOutcome =\n | \"delivered\"\n | \"spawned\"\n | \"unrouted\"\n | \"unlaunchable\"\n | \"unreachable\"\n | \"skipped\";\n\nexport interface DispatchResult {\n task: Task;\n outcome: DispatchOutcome;\n /** Session the task reached, when it reached one. */\n session?: string;\n /** Why the task did not reach a session. Present on failure outcomes. */\n reason?: string;\n}\n","/**\n * config.ts — Configuration loader for PAI Daemon\n *\n * Loads config from ~/.config/pai/config.json (XDG convention).\n * Deep-merges with defaults so partial configs work fine.\n * Expands ~ in path values at runtime.\n */\n\nimport { existsSync, readFileSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { homedir, userInfo } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { NotificationConfig } from \"../notifications/types.js\";\nimport { DEFAULT_NOTIFICATION_CONFIG } from \"../notifications/types.js\";\nimport type { TaskConfig } from \"../tasks/types.js\";\nimport { DEFAULT_TASK_CONFIG } from \"../tasks/types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SearchConfig {\n /** Default search mode: 'keyword', 'semantic', or 'hybrid'. Default: 'keyword'. */\n mode: \"keyword\" | \"semantic\" | \"hybrid\";\n /** Enable cross-encoder reranking by default. Default: true. */\n rerank: boolean;\n /** Recency boost half-life in days. 0 = off. Default: 90. */\n recencyBoostDays: number;\n /** Default max results. Default: 10. */\n defaultLimit: number;\n /** Default snippet length for MCP results. Default: 200. */\n snippetLength: number;\n}\n\nexport interface PostgresConfig {\n /** Connection string — if set, overrides individual host/port/etc. fields */\n connectionString?: string;\n /** Postgres host (default: \"localhost\") */\n host?: string;\n /** Postgres port (default: 5432) */\n port?: number;\n /** Postgres database name (default: \"pai\") */\n database?: string;\n /** Postgres user (default: \"pai\") */\n user?: string;\n /** Postgres password (default: \"pai\") */\n password?: string;\n /** Maximum pool connections (default: 5) */\n maxConnections?: number;\n /** Connection timeout in ms (default: 5000) */\n connectionTimeoutMs?: number;\n}\n\nexport interface PaiDaemonConfig {\n /** Unix Domain Socket path for IPC */\n socketPath: string;\n\n /** Index schedule interval in seconds (default: 300 = 5 minutes) */\n indexIntervalSecs: number;\n\n /** Embedding schedule interval in seconds (default: 600 = 10 minutes) */\n embedIntervalSecs: number;\n\n /** Storage backend: \"sqlite\" (default) or \"postgres\" */\n storageBackend: \"sqlite\" | \"postgres\";\n\n /** PostgreSQL connection config (used when storageBackend = \"postgres\") */\n postgres?: PostgresConfig;\n\n /** Embedding model name (used for semantic/hybrid search) */\n embeddingModel: string;\n\n /** Log level */\n logLevel: \"debug\" | \"info\" | \"warn\" | \"error\";\n\n /** Obsidian vault root path for zettelkasten indexing. If set, vault indexing runs alongside project indexing. */\n vaultPath?: string;\n\n /** Registry project_id to use for vault chunks in memory_chunks. Default: auto-detected. */\n vaultProjectId?: number;\n\n /** Notification subsystem configuration */\n notifications: NotificationConfig;\n\n /** Search defaults — applied when MCP tool or CLI doesn't specify a value */\n search: SearchConfig;\n\n /** Task bus — optional external tracker for cross-session work */\n tasks: TaskConfig;\n}\n\n// ---------------------------------------------------------------------------\n// Per-user Postgres isolation\n// ---------------------------------------------------------------------------\n\n/** Derive a per-user Postgres database name: pai_<username> */\nfunction perUserDbName(): string {\n const username = userInfo().username;\n // Sanitize: only allow alphanumeric and underscore for Postgres identifiers\n const safe = username.replace(/[^a-zA-Z0-9_]/g, \"_\").toLowerCase();\n return `pai_${safe}`;\n}\n\n/** Derive the per-user connection string */\nfunction perUserConnectionString(): string {\n const db = perUserDbName();\n return `postgresql://pai:pai@localhost:5432/${db}`;\n}\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\nexport const DEFAULTS: PaiDaemonConfig = {\n socketPath: \"/tmp/pai.sock\",\n indexIntervalSecs: 300,\n embedIntervalSecs: 600,\n storageBackend: \"sqlite\",\n postgres: {\n connectionString: perUserConnectionString(),\n maxConnections: 5,\n connectionTimeoutMs: 5000,\n },\n embeddingModel: \"Snowflake/snowflake-arctic-embed-m-v1.5\",\n logLevel: \"info\",\n notifications: DEFAULT_NOTIFICATION_CONFIG,\n tasks: DEFAULT_TASK_CONFIG,\n search: {\n mode: \"keyword\",\n rerank: true,\n recencyBoostDays: 90,\n defaultLimit: 10,\n snippetLength: 200,\n },\n};\n\n/** Config template — generated at runtime so the DB name is per-user */\nfunction configTemplate(): string {\n return `{\n \"socketPath\": \"/tmp/pai.sock\",\n \"indexIntervalSecs\": 300,\n \"embedIntervalSecs\": 600,\n \"storageBackend\": \"sqlite\",\n \"postgres\": {\n \"connectionString\": \"${perUserConnectionString()}\",\n \"maxConnections\": 5,\n \"connectionTimeoutMs\": 5000\n },\n \"embeddingModel\": \"Snowflake/snowflake-arctic-embed-m-v1.5\",\n \"logLevel\": \"info\",\n \"vaultPath\": \"\",\n \"vaultProjectId\": 0,\n \"search\": {\n \"mode\": \"keyword\",\n \"rerank\": true,\n \"recencyBoostDays\": 90,\n \"defaultLimit\": 10,\n \"snippetLength\": 200\n }\n}\n`;\n}\n\n// ---------------------------------------------------------------------------\n// Path helpers\n// ---------------------------------------------------------------------------\n\n/** Expand a leading ~ to the real home directory */\nexport function expandHome(p: string): string {\n if (p === \"~\" || p.startsWith(\"~/\") || p.startsWith(\"~\\\\\")) {\n return join(homedir(), p.slice(1));\n }\n return p;\n}\n\nexport const CONFIG_DIR = join(homedir(), \".config\", \"pai\");\nexport const CONFIG_FILE = join(CONFIG_DIR, \"config.json\");\n\n// ---------------------------------------------------------------------------\n// Deep merge (handles nested objects, not arrays)\n// ---------------------------------------------------------------------------\n\nfunction deepMerge<T extends object>(\n target: T,\n source: Record<string, unknown>\n): T {\n const result = { ...target };\n for (const key of Object.keys(source)) {\n const srcVal = source[key];\n if (srcVal === undefined || srcVal === null) continue;\n const tgtVal = (target as Record<string, unknown>)[key];\n if (\n typeof srcVal === \"object\" &&\n !Array.isArray(srcVal) &&\n typeof tgtVal === \"object\" &&\n tgtVal !== null &&\n !Array.isArray(tgtVal)\n ) {\n (result as Record<string, unknown>)[key] = deepMerge(\n tgtVal as object,\n srcVal as Record<string, unknown>\n );\n } else {\n (result as Record<string, unknown>)[key] = srcVal;\n }\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Config loader\n// ---------------------------------------------------------------------------\n\n/**\n * Load configuration from ~/.config/pai/config.json.\n * Returns defaults merged with any values found in the file.\n */\nexport function loadConfig(): PaiDaemonConfig {\n if (!existsSync(CONFIG_FILE)) {\n return { ...DEFAULTS };\n }\n\n let raw: string;\n try {\n raw = readFileSync(CONFIG_FILE, \"utf-8\");\n } catch (e) {\n process.stderr.write(\n `[pai-daemon] Could not read config file at ${CONFIG_FILE}: ${e}\\n`\n );\n return { ...DEFAULTS };\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch (e) {\n process.stderr.write(\n `[pai-daemon] Config file is not valid JSON: ${e}\\n`\n );\n return { ...DEFAULTS };\n }\n\n // Compat: config.json may use \"obsidianVaultPath\" (legacy key) instead of \"vaultPath\".\n // Map it across so the daemon picks it up correctly.\n if (parsed.obsidianVaultPath && !parsed.vaultPath) {\n parsed.vaultPath = parsed.obsidianVaultPath;\n process.stderr.write(\n `[pai-daemon] Config: mapped obsidianVaultPath → vaultPath (${parsed.vaultPath})\\n`\n );\n }\n\n return deepMerge(DEFAULTS, parsed);\n}\n\n/**\n * Ensure ~/.config/pai/ exists and write a default config.json template\n * if none exists yet. Call this only from the `serve` command.\n */\nexport function ensureConfigDir(): void {\n if (!existsSync(CONFIG_DIR)) {\n mkdirSync(CONFIG_DIR, { recursive: true });\n process.stderr.write(\n `[pai-daemon] Created config directory: ${CONFIG_DIR}\\n`\n );\n }\n\n if (!existsSync(CONFIG_FILE)) {\n try {\n writeFileSync(CONFIG_FILE, configTemplate(), \"utf-8\");\n process.stderr.write(\n `[pai-daemon] Wrote default config to: ${CONFIG_FILE}\\n`\n );\n } catch (e) {\n process.stderr.write(\n `[pai-daemon] Could not write default config: ${e}\\n`\n );\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/daemon/index.mjs
CHANGED
|
@@ -9,8 +9,8 @@ import "../search-HcdKtMla.mjs";
|
|
|
9
9
|
import "../kg-extraction-C8DEUHTS.mjs";
|
|
10
10
|
import "../factory-Q88X1bAN.mjs";
|
|
11
11
|
import { t as PaiClient } from "../ipc-client-CoyUHPod.mjs";
|
|
12
|
-
import { i as ensureConfigDir, o as loadConfig } from "../config-
|
|
13
|
-
import { n as serve } from "../daemon-
|
|
12
|
+
import { i as ensureConfigDir, o as loadConfig } from "../config-C8m-tPhP.mjs";
|
|
13
|
+
import { n as serve } from "../daemon-D2aKb4mJ.mjs";
|
|
14
14
|
import "../state-BIlxNRUn.mjs";
|
|
15
15
|
import "../tools-Op3C2Nm6.mjs";
|
|
16
16
|
import "../detector-ZiOhHszd.mjs";
|
|
@@ -4,7 +4,7 @@ import { n as openFederation } from "./db-CmYbAVCD.mjs";
|
|
|
4
4
|
import { d as sha256 } from "./helpers-IjZkXBhj.mjs";
|
|
5
5
|
import { t as configureEmbeddingModel } from "./embeddings-BJPOcbik.mjs";
|
|
6
6
|
import { t as createStorageBackend } from "./factory-Q88X1bAN.mjs";
|
|
7
|
-
import {
|
|
7
|
+
import { l as DEFAULT_NOTIFICATION_CONFIG, n as CONFIG_FILE, t as CONFIG_DIR } from "./config-C8m-tPhP.mjs";
|
|
8
8
|
import { C as setStorageBackend, E as startTime, O as storageBackend, S as setStartTime, T as shutdownRequested, _ as setLastIndexTime, a as indexSchedulerTimer, b as setRegistryDb, c as lastVaultIndexTime, d as setDaemonConfig, f as setEmbedInProgress, g as setLastEmbedTime, h as setIndexSchedulerTimer, i as indexInProgress, k as vaultIndexInProgress, l as notificationConfig, m as setIndexInProgress, n as embedInProgress, o as lastEmbedTime, p as setEmbedSchedulerTimer, r as embedSchedulerTimer, s as lastIndexTime, t as daemonConfig, u as registryDb, v as setLastVaultIndexTime, w as setVaultIndexInProgress, x as setShutdownRequested, y as setNotificationConfig } from "./state-BIlxNRUn.mjs";
|
|
9
9
|
import { a as toolMemoryWakeup, c as toolSessionRoute, d as toolProjectInfo, f as toolProjectList, h as toolMemorySearch, i as toolMemoryTaxonomy, l as toolProjectDetect, m as toolMemoryGet, n as toolMemoryKgSearch, o as toolRegistrySearch, p as toolProjectTodo, r as toolMemoryFeedback, s as toolSessionList, u as toolProjectHealth } from "./tools-Op3C2Nm6.mjs";
|
|
10
10
|
import { t as detectTopicShift } from "./detector-ZiOhHszd.mjs";
|
|
@@ -1577,4 +1577,4 @@ var daemon_exports = /* @__PURE__ */ __exportAll({ serve: () => serve });
|
|
|
1577
1577
|
|
|
1578
1578
|
//#endregion
|
|
1579
1579
|
export { serve as n, daemon_exports as t };
|
|
1580
|
-
//# sourceMappingURL=daemon-
|
|
1580
|
+
//# sourceMappingURL=daemon-D2aKb4mJ.mjs.map
|