{"version":3,"file":"index.mjs","names":["getProject","statusColor","toTitleCase","upsertTag","cmdList","getProject","warn","dim","statusColor","bold","renderTable","cmdInfo","toTitleCase","ok","cmdTag","upsertTag","loadWorkers","statusColor","out","listCommand","resolve","resolve","sleep","logDir","str","num","out","runCommand","dim","waitOnDeps","ok","load","ok","str","median","sh","sleep","out","fail","dim","ok","CONFIG_FILE","warn","sleep","DAY","num","ok","dim","aggregate","findNotesDir","dim","ok","warn","findNotesDir","dim","warn","ok","renderTable","dim","bold","ok","dim","warn","getProject","findClaudeNotesDir","dim","bold","warn","ok","getProject","dim","ok","cmdInfo","cmdTag","CONFIG_FILE","BACKUPS_DIR","DOCKER_CONTAINER","PG_DATABASE","PG_USER","dim","ok","warn","bold","dim","warn","bold","ok","renderTable","warn","dim","ok","fmtTs","warn","dim","renderTable","renderTable","bold","dim","prompt","ok","dim","bold","ok","warn","line","step","dim","ok","warn","run","getTemplatesDir","cmdNotesList","CONFIG_FILE","useSymlinks","PLIST_PATH","ok","warn","dim","bold","cmdMigrate","CONFIG_FILE","normalize","normalizeName","CONFIG_FILE","loadState","saveState","dim","ok","warn","bold","CONFIG_FILE","bold","schedulerStatePath","dim","ok","configCommand","dim","renderTable","bold","warn","bold","dim","ok","run","warn","dim","ok","bold","makeClient","warn","bold","ok","dim","dim","ok","bold","ok","bold","dim","warn","renderTable","norm","bold","dim","renderTable","warn","ok","bold","dim","warn","ok","dim","bold","ok","bold","dim","warn","ok","warn","dim","ok","warn","dim","slugify","ok","bold","ok","dim","warn","ok","dim","warn","bold","warn","dim","ok","run","CONFIG_FILE","loadState","bold","dim","warn","ok","configCommand","ok","dim","bold","dim","ok","bold","ok","dim","bold","warn","dim","bold","dim","bold","dim","ok","dim","CONFIG_FILE","bold","warn","ok","project","dim","warn","bold","ok","dim","bold","ok","warn","dim","warn","bold","dim","warn","bold","dim","warn","bold","dim","warn","bold","limit","ipcCall","fmtTs","dim","trunc","warn","bold","dim","warn","bold","unquote","readEntry","warn","ok","dim","renderTable","bold"],"sources":["../../src/cli/lib/exit.ts","../../src/cli/define.ts","../../src/cli/help.ts","../../src/cli/lib/harness.ts","../../src/cli/lib/health.ts","../../src/cli/lib/picker.ts","../../src/cli/lib/provider-choice.ts","../../src/cli/lib/setups.ts","../../src/cli/lib/names.ts","../../src/cli/lib/legacy.ts","../../src/session/slug-generator.ts","../../src/cli/commands/session/helpers.ts","../../src/cli/commands/session/commands.ts","../../src/cli/lib/worker-sessions.ts","../../src/cli/commands/session.ts","../../src/cli/commands/root.ts","../../src/cli/parse.ts","../../src/cli/run.ts","../../src/cli/docgen.ts","../../src/cli/commands/meta.ts","../../src/workers/agents.ts","../../src/workers/usage.ts","../../src/workers/wait.ts","../../src/workers/wait-probes.ts","../../src/cli/commands/worker.ts","../../src/cli/commands/providers.ts","../../src/workers/local-tiers.ts","../../src/workers/bench.ts","../../src/cli/lib/settings-manager.ts","../../src/workers/install.ts","../../src/cli/commands/worker-config.ts","../../src/router/cockpit-auth.ts","../../src/router/pool-panel.ts","../../src/router/cockpit-page.ts","../../src/router/sessions.ts","../../src/router/cockpit.ts","../../src/router/listen.ts","../../src/router/drain.ts","../../src/router/system.ts","../../src/router/restart.ts","../../src/cli/commands/router.ts","../../src/cli/commands/setups.ts","../../src/hooks/ts/lib/transcript-scan.ts","../../src/session/autosave.ts","../../src/hooks/ts/lib/context-fill.ts","../../src/hooks/ts/lib/worker-session.ts","../../src/session/context-handover-trigger.ts","../../src/cli/commands/session/pause.ts","../../src/cli/commands/session/autosave.ts","../../src/cli/commands/session/checkpoint.ts","../../src/cli/commands/session/clear-names.ts","../../src/cli/commands/session/end.ts","../../src/cli/commands/session/goto.ts","../../src/cli/commands/session/handover.ts","../../src/cli/commands/session/pause-all.ts","../../src/cli/commands/session/recent.ts","../../src/cli/commands/session/restore.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/cleanup.ts","../../src/cli/commands/session-notes.ts","../../src/cli/lib/shell-init.ts","../../src/storage/paths.ts","../../src/storage/backup.ts","../../src/cli/commands/lifecycle/backup.ts","../../src/cli/impl/project/helpers.ts","../../src/cli/impl/project/commands.ts","../../src/cli/commands/lifecycle/cd.ts","../../src/cli/commands/lifecycle/find.ts","../../src/cli/commands/lifecycle/launch.ts","../../src/cli/commands/lifecycle/restore.ts","../../src/cli/commands/lifecycle/update.ts","../../src/cli/commands/lifecycle.ts","../../src/cli/commands/hooks-db.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/setup/steps/10-settings.ts","../../src/config/claude-json.ts","../../src/service-manager.ts","../../src/cli/impl/daemon-status.ts","../../src/cli/impl/daemon.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/tasks/resolver.ts","../../src/tasks/providers/todoist.ts","../../src/cli/commands/setup/steps/16-task-bus.ts","../../src/cli/commands/setup/steps/17-workers.ts","../../src/cli/commands/setup/index.ts","../../src/cli/commands/setup.ts","../../src/config/voices-config.ts","../../src/storage/sqlite/legacy-migration.ts","../../src/tasks/dispatch.ts","../../src/tasks/scheduler.ts","../../src/tasks/poller.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/lib/obsidian.ts","../../src/config/adapter-content.ts","../../src/config/pai-files.ts","../../src/memory/kg-backfill.ts","../../src/workers/logs-migrate.ts","../../src/cli/impl/config.ts","../../src/cli/commands/config.ts","../../src/cli/commands/daemon.ts","../../src/storage/migrate-to-postgres.ts","../../src/storage/db-admin.ts","../../src/cli/impl/db.ts","../../src/cli/commands/db.ts","../../src/identity/index.ts","../../src/cli/impl/identity.ts","../../src/cli/commands/identity.ts","../../src/cli/impl/issue.ts","../../src/cli/commands/issue.ts","../../src/cli/impl/mcp.ts","../../src/cli/commands/mcp.ts","../../src/cli/impl/notify.ts","../../src/cli/commands/notify.ts","../../src/session/promote.ts","../../src/cli/impl/project/session-config.ts","../../src/cli/impl/project/relocate.ts","../../src/cli/impl/project/health.ts","../../src/cli/impl/project/init.ts","../../src/cli/impl/project/merge.ts","../../src/cli/impl/project/here.ts","../../src/cli/impl/project/unregister.ts","../../src/cli/impl/project/projects-index.ts","../../src/cli/commands/project.ts","../../src/cli/impl/registry/migrate.ts","../../src/cli/impl/registry/dedupe.ts","../../src/cli/impl/registry/reconnect.ts","../../src/cli/impl/registry/index.ts","../../src/cli/commands/projects-registry.ts","../../src/tasks/transport/aibroker.ts","../../src/tasks/schedule-install.ts","../../src/tasks/archive.ts","../../src/tasks/projects.ts","../../src/cli/impl/task.ts","../../src/cli/commands/task.ts","../../src/cli/impl/topic.ts","../../src/cli/commands/topic.ts","../../src/cli/commands/batch-c.ts","../../src/memory/reembed.ts","../../src/cli/commands/memory/backend.ts","../../src/cli/commands/memory/embed.ts","../../src/cli/commands/memory/index-cmd.ts","../../src/cli/commands/memory/pass.ts","../../src/cli/commands/memory/search.ts","../../src/cli/commands/memory/sources-cmd.ts","../../src/cli/commands/memory/stats.ts","../../src/cli/commands/memory.ts","../../src/cli/lib/kg.ts","../../src/cli/commands/kg.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/converse.ts","../../src/cli/commands/zettel/themes.ts","../../src/cli/commands/zettel/suggest.ts","../../src/cli/commands/zettel.ts","../../src/cli/lib/observation.ts","../../src/cli/commands/observation.ts","../../src/cli/commands/obsidian.ts","../../src/cli/lib/skill.ts","../../src/cli/commands/skill.ts","../../src/audit/tokens.ts","../../src/audit/files.ts","../../src/audit/hooks.ts","../../src/audit/first-turn.ts","../../src/audit/context-trigger.ts","../../src/audit/session.ts","../../src/audit/spawn.ts","../../src/audit/daemon.ts","../../src/audit/env.ts","../../src/audit/schedule.ts","../../src/audit/skills.ts","../../src/audit/severity.ts","../../src/audit/ladder.ts","../../src/audit/subagents.ts","../../src/audit/mcp.ts","../../src/cli/lib/audit.ts","../../src/cli/commands/audit.ts","../../src/cli/registry.ts","../../src/cli/index.ts"],"sourcesContent":["/**\n * Safe process termination for the CLI.\n *\n * Node's stdout/stderr are ASYNCHRONOUS when they point at a pipe (and\n * synchronous when they point at a TTY or a regular file). `process.exit()`\n * tears the process down immediately, before any still-in-flight async\n * write completes — so a command whose output is piped (`pai ... | less`,\n * `| wc -c`, a subshell capture, etc.) can have its output silently\n * truncated or dropped entirely, while the same command run against a TTY\n * or redirected to a file prints correctly. Exit code is still 0, so the\n * failure is invisible.\n *\n * The robust fix, used everywhere it is possible, is to never call\n * `process.exit()` at all: set `process.exitCode` and let the command\n * return normally. Once the event loop has nothing left to do, Node exits\n * on its own — and a natural exit always flushes pending writes first.\n *\n * `exitAfterFlush` exists for the rare call site where control flow\n * genuinely cannot return (e.g. deep inside a callback with no path back\n * to the caller). It waits for both stdout and stderr to drain before\n * calling `process.exit()`.\n */\n\nfunction streamDrained(stream: NodeJS.WriteStream): Promise<void> {\n  // `writableLength` is the number of bytes still buffered and not yet\n  // handed to the OS. If it's already 0, there is nothing to wait for.\n  if (!stream.writableLength) return Promise.resolve();\n  return new Promise((resolve) => {\n    stream.once(\"drain\", () => resolve());\n    // Belt-and-braces: also resolve on the next tick after a short poll,\n    // in case `drain` was already emitted before we attached the listener\n    // (rare, but writableLength can hit 0 without a fresh `drain` event on\n    // some platforms/versions).\n    const check = () => {\n      if (!stream.writableLength) resolve();\n    };\n    setImmediate(check);\n  });\n}\n\n/**\n * Wait for stdout and stderr to finish flushing, then exit with `code`.\n * Never returns (the return type documents that; callers should not\n * expect code after this call to run).\n */\nexport async function exitAfterFlush(code: number): Promise<never> {\n  await drainStdio();\n  process.exit(code);\n}\n\n/**\n * Wait for stdout and stderr to finish flushing, without exiting.\n *\n * Belt-and-suspenders backstop for cli/index.ts: every command path avoids\n * process.exit() so a natural process exit — which always flushes pending\n * writes — can do the flushing. This makes that explicit and awaited at the\n * one place every command path funnels through, instead of trusting that\n * nothing between here and process shutdown (a dependency's keep-alive\n * socket being unref'd, a timer, anything else that can make the event loop\n * look \"empty\") lets Node begin tearing down before a write that's still\n * asynchronously in flight to a pipe has actually reached the OS.\n */\nexport async function drainStdio(): Promise<void> {\n  await Promise.all([streamDrained(process.stdout), streamDrained(process.stderr)]);\n}\n","/**\n * The one shape every pai command is declared in. Help, the overview, the\n * Markdown reference (`pai doc`) and the argument parser all read it, so the\n * text people see and the behaviour they get cannot drift apart.\n */\n\nexport interface ArgDef {\n  /** Upper-case placeholder shown in Usage, e.g. NAME_OR_ID. */\n  name: string;\n  summary: string;\n  required?: boolean;\n  /** Collects every remaining positional argument. */\n  variadic?: boolean;\n}\n\nexport interface OptDef {\n  /** Long flag without dashes, e.g. \"provider\". The same name means the same thing on every command. */\n  name: string;\n  short?: string;\n  type: \"string\" | \"boolean\" | \"number\";\n  /** Value placeholder for string/number flags, e.g. NAME. */\n  value?: string;\n  default?: string | number | boolean;\n  summary: string;\n  /** May be given several times; every value is in RunContext.multi (opts holds the last). */\n  repeatable?: boolean;\n  /** String flag whose value may be left out (`--auto-exit` or `--auto-exit 30`); absent value reads as true. */\n  optionalValue?: boolean;\n}\n\nexport interface RunContext {\n  args: Record<string, string | string[] | undefined>;\n  opts: Record<string, string | number | boolean | undefined>;\n  /** Every value of a repeatable option, in order. */\n  multi?: Record<string, string[]>;\n  /** Global --dry-run: print the exec line instead of executing. */\n  dryRun: boolean;\n  /** Everything after a bare `--` (passed to claude verbatim). */\n  rest: string[];\n}\n\n/** Thrown by a command to fail with a plain message and the next thing to type. */\nexport class CliError extends Error {\n  constructor(message: string, readonly hint?: string, readonly exitCode = 1) {\n    super(message);\n  }\n}\n\nexport interface CommandDef {\n  name: string;\n  summary: string;\n  /** Longer description shown by `pai help <command>`. */\n  description: string;\n  args?: ArgDef[];\n  options?: OptDef[];\n  /** Real, runnable command lines. */\n  examples: string[];\n  /** Related commands, written as they are typed, e.g. \"pai open\". */\n  seeAlso?: string[];\n  subcommands?: CommandDef[];\n  /** Set on a top-level shortcut: the full `noun verb` it stands for. */\n  aliasOf?: string;\n  /** Listed on the first screen (`pai`); everything else is under `pai more`. */\n  daily?: boolean;\n  /** Works, but is not listed in help (an old spelling kept for callers). */\n  hidden?: boolean;\n  /** Accept `-- args` and pass them on. */\n  passthrough?: boolean;\n  /** Options and words this command does not know are kept, in order, in `rest` (for commands that wrap another program). */\n  allowUnknown?: boolean;\n  run?: (ctx: RunContext) => Promise<number | void> | number | void;\n}\n\nexport type GroupId = \"sessions\" | \"providers\" | \"help\" | \"workers\" | \"memory\" | \"daemon\";\n\nexport interface GroupDef {\n  id: GroupId;\n  title: string;\n  /** Shown on the overview screen; otherwise only under `pai help more`. */\n  daily: boolean;\n  commands: CommandDef[];\n}\n\n/** Shared flags: one spelling, one meaning, everywhere. */\nexport const OPT = {\n  project: { name: \"project\", type: \"string\", value: \"NAME\", summary: \"Only this project (name or path)\" },\n  provider: { name: \"provider\", type: \"string\", value: \"NAME\", summary: \"Provider to run on (see: pai providers)\" },\n  model: { name: \"model\", type: \"string\", value: \"ID\", summary: \"Model id on that provider (default: its default model)\" },\n  json: { name: \"json\", type: \"boolean\", default: false, summary: \"Machine-readable JSON output\" },\n  dryRun: { name: \"dry-run\", type: \"boolean\", default: false, summary: \"Print the exec line instead of executing\" },\n} as const satisfies Record<string, OptDef>;\n","/**\n * Help rendering and \"did you mean\" — the whole help experience in one file.\n *\n * Everything shown by `pai`, `pai help <command>`, `pai <command> --help`\n * and written by `pai doc` is derived from the CommandDef objects, so the\n * text next to a command's definition is the only copy.\n *\n * Colour only when stdout is a TTY and NO_COLOR is unset (FORCE_COLOR wins);\n * piped output is plain text.\n */\n\nimport { Chalk } from \"chalk\";\nimport type { CommandDef, GroupDef, OptDef } from \"./define.js\";\n\n// ---------------------------------------------------------------------------\n// Style\n// ---------------------------------------------------------------------------\n\nexport interface Style {\n  color: boolean;\n  bold(s: string): string;\n  dim(s: string): string;\n  cyan(s: string): string;\n  green(s: string): string;\n  yellow(s: string): string;\n  red(s: string): string;\n  head(s: string): string;\n}\n\nexport function colorEnabled(env: NodeJS.ProcessEnv = process.env, isTTY = !!process.stdout.isTTY): boolean {\n  if (env.NO_COLOR !== undefined && env.NO_COLOR !== \"\") return false;\n  if (env.FORCE_COLOR !== undefined && env.FORCE_COLOR !== \"\" && env.FORCE_COLOR !== \"0\") return true;\n  return isTTY && env.TERM !== \"dumb\";\n}\n\nexport function makeStyle(color: boolean): Style {\n  const c = new Chalk({ level: color ? 1 : 0 });\n  return {\n    color,\n    bold: (s) => c.bold(s),\n    dim: (s) => c.dim(s),\n    cyan: (s) => c.cyan(s),\n    green: (s) => c.green(s),\n    yellow: (s) => c.yellow(s),\n    red: (s) => c.red(s),\n    head: (s) => c.bold.underline(s),\n  };\n}\n\n// eslint-disable-next-line no-control-regex\nconst ANSI = /\\u001b\\[[0-9;]*m/g;\nconst visible = (s: string) => s.replace(ANSI, \"\").length;\nconst padEnd = (s: string, w: number) => s + \" \".repeat(Math.max(0, w - visible(s)));\n\n/** Aligned table with a header row. Cells may carry colour; widths ignore it. */\nexport function renderTable(headers: string[], rows: string[][], st: Style, indent = \"  \"): string {\n  const widths = headers.map((h, i) => Math.max(visible(h), ...rows.map((r) => visible(r[i] ?? \"\"))));\n  const line = (cells: string[]) =>\n    indent + cells.map((c, i) => (i === cells.length - 1 ? c : padEnd(c, widths[i]))).join(\"  \").trimEnd();\n  return [\n    line(headers.map((h) => st.dim(h))),\n    indent + st.dim(widths.map((w) => \"─\".repeat(w)).join(\"  \")),\n    ...rows.map(line),\n  ].join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Did you mean\n// ---------------------------------------------------------------------------\n\nfunction distance(a: string, b: string): number {\n  const d: number[][] = Array.from({ length: a.length + 1 }, (_, i) => [i, ...Array(b.length).fill(0)]);\n  for (let j = 1; j <= b.length; j++) d[0][j] = j;\n  for (let i = 1; i <= a.length; i++) {\n    for (let j = 1; j <= b.length; j++) {\n      d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));\n      if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);\n    }\n  }\n  return d[a.length][b.length];\n}\n\n/** Closest candidate to `input`, or null when nothing is plausibly meant. */\nexport function suggest(input: string, candidates: string[]): string | null {\n  const x = input.toLowerCase();\n  let best: string | null = null;\n  let bestScore = Infinity;\n  for (const c of candidates) {\n    const lc = c.toLowerCase();\n    let d = distance(x, lc);\n    if (x.length >= 2 && (lc.startsWith(x) || x.startsWith(lc))) d = Math.min(d, 1);\n    if (d < bestScore) {\n      bestScore = d;\n      best = c;\n    }\n  }\n  const limit = Math.max(1, Math.floor(x.length / 3) + (x.length > 4 ? 1 : 0));\n  return best !== null && bestScore <= limit ? best : null;\n}\n\n// ---------------------------------------------------------------------------\n// Command help\n// ---------------------------------------------------------------------------\n\nconst flagText = (o: OptDef) =>\n  `${o.short ? `-${o.short}, ` : \"    \"}--${o.name}${o.type !== \"boolean\" ? ` ${o.value ?? o.type.toUpperCase()}` : \"\"}`;\n\nexport function usageLine(path: string[], cmd: CommandDef): string {\n  const parts = [\"pai\", ...path];\n  if (cmd.subcommands?.length && !cmd.run) parts.push(\"<command>\");\n  for (const a of cmd.args ?? []) {\n    const n = a.variadic ? `${a.name}...` : a.name;\n    parts.push(a.required ? n : `[${n}]`);\n  }\n  if (cmd.subcommands?.length && cmd.run) parts.push(\"[command]\");\n  if (cmd.options?.length) parts.push(\"[options]\");\n  return parts.join(\" \");\n}\n\nfunction defaultText(o: OptDef): string {\n  if (o.default === undefined || o.default === false) return \"—\";\n  return String(o.default);\n}\n\n/** Full help for one command (what `pai help <cmd>` and `pai <cmd> --help` print). */\nexport function renderCommandHelp(path: string[], cmd: CommandDef, st: Style): string {\n  const out: string[] = [];\n  out.push(`${st.head(\"Usage\")}  ${st.bold(usageLine(path, cmd))}`, \"\");\n  out.push(cmd.description.trim(), \"\");\n  if (cmd.aliasOf) out.push(st.dim(`Shortcut for: pai ${cmd.aliasOf}`), \"\");\n\n  if (cmd.subcommands?.length) {\n    out.push(st.head(\"Commands\"));\n    out.push(\n      renderTable(\n        [\"Command\", \"Description\"],\n        cmd.subcommands.filter((s) => !s.hidden).map((s) => [st.cyan(s.name), s.summary]),\n        st\n      )\n    );\n    out.push(\"\");\n  }\n  if (cmd.args?.length) {\n    out.push(st.head(\"Arguments\"));\n    out.push(\n      renderTable(\n        [\"Argument\", \"Required\", \"Description\"],\n        cmd.args.map((a) => [st.cyan(a.variadic ? `${a.name}...` : a.name), a.required ? \"yes\" : \"no\", a.summary]),\n        st\n      ),\n      \"\"\n    );\n  }\n  const opts: OptDef[] = [\n    ...(cmd.options ?? []),\n    { name: \"help\", short: \"h\", type: \"boolean\", summary: \"Show this help\" },\n  ];\n  out.push(st.head(\"Options\"));\n  out.push(\n    renderTable(\n      [\"Flag\", \"Type\", \"Default\", \"Description\"],\n      opts.map((o) => [st.cyan(flagText(o)), o.type === \"boolean\" ? \"flag\" : o.type, defaultText(o), o.summary]),\n      st\n    ),\n    \"\"\n  );\n  if (cmd.examples.length) {\n    out.push(st.head(\"Examples\"));\n    for (const e of cmd.examples) out.push(`  ${st.green(\"$\")} ${e}`);\n    out.push(\"\");\n  }\n  if (cmd.seeAlso?.length) out.push(`${st.head(\"See also\")}  ${cmd.seeAlso.join(\", \")}`, \"\");\n  return out.join(\"\\n\").replace(/\\n+$/, \"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Overview\n// ---------------------------------------------------------------------------\n\nconst DAILY_EXAMPLES = [\n  [\"pai\", \"pick a session (type to filter, Enter opens)\"],\n  ['pai \"20 - Webseiten\"', \"open it; shows how it will start\"],\n  [\"pai ls\", \"the sessions as a table\"],\n  [\"pai setups\", \"orchestrator + worker combinations\"],\n  ['pai setups save \"my mix\"', \"keep the current combination\"],\n  ['pai webseiten --with \"glm only\"', \"scripting: start it that way, no questions\"],\n];\n\n/** `pai --help`: only the daily things, then a pointer to `pai more`. */\nexport function renderOverview(groups: GroupDef[], root: CommandDef, version: string, st: Style): string {\n  const daily = groups.flatMap((g) => g.commands).filter((c) => c.daily);\n  const out: string[] = [];\n  out.push(`${st.bold(\"pai\")} ${st.dim(version)} — find a session, start it, choose where it runs`, \"\");\n  out.push(`${st.head(\"Usage\")}  ${st.bold(\"pai [NAME] [options]\")}   ${st.dim(\"or\")}   ${st.bold(\"pai <command>\")}`, \"\");\n  out.push(st.head(\"Daily\"));\n  out.push(\n    renderList(\n      [\n        [st.cyan(\"pai\"), \"Pick a session: type to filter, Enter opens it\"],\n        [st.cyan(\"pai NAME\"), root.summary],\n        ...daily.map((c) => [st.cyan(`pai ${c.name}`), c.summary]),\n      ],\n      st\n    ),\n    \"\"\n  );\n  out.push(st.head(\"Examples\"));\n  for (const [cmd, why] of DAILY_EXAMPLES) out.push(`  ${st.green(\"$\")} ${cmd.padEnd(34)} ${st.dim(why)}`);\n  out.push(\"\");\n  out.push(`more commands: ${st.bold(\"pai more\")}   ${st.dim(\"·   details for one command: pai help <command>\")}`, \"\");\n  return out.join(\"\\n\");\n}\n\n/** `pai more`: every command that is not on the first screen, by group. */\nexport function renderMore(groups: GroupDef[], st: Style): string {\n  const out: string[] = [st.head(\"More commands\"), \"\"];\n  for (const g of groups) {\n    const cmds = g.commands.filter((c) => !c.daily);\n    if (!cmds.length) continue;\n    out.push(st.bold(g.title));\n    out.push(\n      renderTable(\n        [\"Command\", \"Description\"],\n        cmds.map((c) => [st.cyan(c.name), c.summary]),\n        st\n      ),\n      \"\"\n    );\n  }\n  out.push(st.dim(\"Details for one command: pai help <command>\"), \"\");\n  return out.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Markdown reference (pai doc)\n// ---------------------------------------------------------------------------\n\nexport function mdCommand(path: string[], cmd: CommandDef, level: number): string[] {\n  const h = \"#\".repeat(level);\n  const out = [`${h} pai ${path.join(\" \")}`, \"\", cmd.summary, \"\", \"```\", usageLine(path, cmd), \"```\", \"\", cmd.description.trim(), \"\"];\n  if (cmd.aliasOf) out.push(`Shortcut for \\`pai ${cmd.aliasOf}\\`.`, \"\");\n  if (cmd.args?.length) {\n    out.push(\"| Argument | Required | Description |\", \"|---|---|---|\");\n    for (const a of cmd.args) out.push(`| \\`${a.variadic ? `${a.name}...` : a.name}\\` | ${a.required ? \"yes\" : \"no\"} | ${a.summary} |`);\n    out.push(\"\");\n  }\n  if (cmd.options?.length) {\n    out.push(\"| Flag | Type | Default | Description |\", \"|---|---|---|---|\");\n    for (const o of cmd.options) {\n      out.push(`| \\`${flagText(o).trim()}\\` | ${o.type === \"boolean\" ? \"flag\" : o.type} | ${defaultText(o)} | ${o.summary} |`);\n    }\n    out.push(\"\");\n  }\n  if (cmd.examples.length) out.push(\"```sh\", ...cmd.examples, \"```\", \"\");\n  if (cmd.seeAlso?.length) out.push(`See also: ${cmd.seeAlso.map((s) => `\\`${s}\\``).join(\", \")}`, \"\");\n  for (const s of cmd.subcommands ?? []) out.push(...mdCommand([...path, s.name], s, level + 1));\n  return out;\n}\n\nexport function renderMarkdown(groups: GroupDef[], version: string): string {\n  const out = [`# pai ${version} — command reference`, \"\", \"_Generated by `pai doc` from the command definitions. Do not edit by hand._\", \"\"];\n  for (const g of groups) {\n    out.push(`## ${g.title}`, \"\");\n    for (const c of g.commands) out.push(...mdCommand([c.name], c, 3));\n  }\n  return out.join(\"\\n\").replace(/\\n{3,}/g, \"\\n\\n\");\n}\n\n/** Two-column list without a header row — the overview and `help more` use it. */\nfunction renderList(rows: string[][], st: Style, indent = \"  \"): string {\n  const w = Math.max(...rows.map((r) => visible(r[0])));\n  return rows.map((r) => `${indent}${padEnd(r[0], w)}  ${r.slice(1).join(\"  \")}`.trimEnd()).join(\"\\n\");\n}\n","/**\n * The command line that starts the Claude Code harness on a provider.\n *\n * This is the one builder behind `pai open`, `pai new` and (later) every\n * other launcher. The env, --settings pin, --model and extra arguments come\n * from buildLaunchPlan (src/workers/launch.ts) — the same code the old\n * `pai launch` runs — so the two cannot differ. Nothing here shells out to a\n * second `pai`.\n */\n\nimport { spawnSync } from \"node:child_process\";\nimport { buildLaunchPlan, maskLaunchEnv, type LaunchPlan } from \"../../workers/launch.js\";\nimport type { WorkersConfig } from \"../../workers/config.js\";\nimport { workersLogDir, ledgerPath } from \"../../workers/paths.js\";\nimport { appendLedger } from \"../../workers/ledger.js\";\nimport { printExitDir } from \"./exit-dir.js\";\n\nexport interface Harness {\n  cmd: \"claude\";\n  argv: string[];\n  env: NodeJS.ProcessEnv;\n  cwd: string;\n  provider: string;\n  model: string;\n}\n\n/** `--name N \"/Name N\"`: label the session and tab (same as launchPrompt in lib/launch.ts). */\nexport const nameArgs = (name: string): string[] => [\"--name\", name, `/Name ${name}`];\n\nexport async function buildHarness(\n  workers: Pick<WorkersConfig, \"providers\" | \"nativeModels\" | \"logDir\">,\n  opts: { provider: string; model?: string; cwd: string; resume?: string; name: string }\n): Promise<Harness> {\n  const extra = [...(opts.resume ? [\"--resume\", opts.resume] : []), ...nameArgs(opts.name)];\n  const plan: LaunchPlan = await buildLaunchPlan(workers, opts.provider, opts.model, extra, workersLogDir(workers));\n  return { cmd: \"claude\", argv: plan.argv, env: plan.env, cwd: opts.cwd, provider: plan.providerName, model: plan.model };\n}\n\nconst quote = (a: string) => (/[\\s\"'$`\\\\{}]/.test(a) ? JSON.stringify(a) : a);\n\n/** The exec line for --dry-run. The auth token is inside the --settings JSON, so it is masked here too. */\nexport function execLine(h: Harness): string {\n  const token = h.env.ANTHROPIC_AUTH_TOKEN;\n  const masked = token ? maskLaunchEnv({ ANTHROPIC_AUTH_TOKEN: token }).ANTHROPIC_AUTH_TOKEN : undefined;\n  const hide = (a: string) => (token && masked ? a.split(token).join(masked) : a);\n  return `cd ${quote(h.cwd)} && ${h.cmd} ${h.argv.map((a) => quote(hide(a))).join(\" \")}`;\n}\n\nexport function dryRunText(h: Harness): string {\n  const masked = maskLaunchEnv(h.env);\n  const env = Object.keys(masked)\n    .sort()\n    .filter((k) => /^(ANTHROPIC_|CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC|ENABLE_TOOL_SEARCH|PAI_WORKER)/.test(k))\n    .map((k) => `  ${k}=${masked[k]}`);\n  return [execLine(h), ...(env.length ? [\"env:\", ...env] : [])].join(\"\\n\");\n}\n\n/** Run the harness in the foreground; returns its exit code. */\nexport function execHarness(h: Harness, workers: Pick<WorkersConfig, \"logDir\">): number {\n  appendLedger(ledgerPath(workersLogDir(workers)), \"LAUNCH-START\", {\n    kind: \"launch\",\n    provider: h.provider,\n    model: h.model,\n    cwd: h.cwd,\n  });\n  const r = spawnSync(h.cmd, h.argv, { stdio: \"inherit\", env: h.env, cwd: h.cwd });\n  if (r.error) throw new Error(`could not start claude: ${r.error.message}`);\n  printExitDir(h.cwd);\n  return r.status ?? 0;\n}\n","/** Is a provider usable right now, and if not, the plain-words reason. */\n\nimport { ANTHROPIC_NATIVE, type WorkersConfig } from \"../../workers/config.js\";\nimport { budgetExceeded, budgetSkipReason, budgetWall, type BudgetReading } from \"../../workers/budget.js\";\n\nexport interface Health {\n  state: \"ok\" | \"limit\" | \"off\";\n  reason: string;\n}\n\nexport function providerHealth(\n  name: string,\n  cfg: Pick<WorkersConfig, \"providers\" | \"routing\">,\n  budget: Record<string, BudgetReading | undefined>,\n  now = new Date()\n): Health {\n  if (name !== ANTHROPIC_NATIVE) {\n    const p = cfg.providers[name];\n    if (!p) return { state: \"off\", reason: `${name}: not configured` };\n    if (!p.enabled) return { state: \"off\", reason: `${name}: disabled` };\n  }\n  const r = budget[name];\n  if (r && budgetExceeded(r, budgetWall(cfg.routing, name))) {\n    return { state: \"limit\", reason: `${budgetSkipReason(name, r, now).replace(\" \", \": at limit, \")}` };\n  }\n  return { state: \"ok\", reason: r?.percent != null ? `${Math.round(r.percent)}% used` : \"ok\" };\n}\n\nimport { budgetCache } from \"../../workers/budget.js\";\n\n/** Live budget readings, cached for 5 minutes by the worker engine; never waits longer than `ms`. */\nexport async function readBudget(workers: WorkersConfig, ms = 1500): Promise<Record<string, BudgetReading | undefined>> {\n  try {\n    return await Promise.race([\n      budgetCache.warm(workers),\n      new Promise<Record<string, BudgetReading>>((res) => setTimeout(() => res({}), ms).unref()),\n    ]);\n  } catch {\n    return {};\n  }\n}\n","/**\n * Interactive table picker: type to filter, arrows to move, Enter to choose,\n * Esc or Ctrl-C to cancel (resolves null — the caller exits 130 quietly).\n *\n * No prompt library: the picker needs aligned multi-column rows with a live\n * filter, which general prompt libraries do not render, and the whole thing is\n * ~150 lines. The state machine and the view are pure functions; the driver\n * takes any input/output pair, so tests run it with a fake TTY.\n */\n\nimport { emitKeypressEvents } from \"node:readline\";\nimport { renderTable, type Style } from \"../help.js\";\n\nexport interface Key {\n  name?: string;\n  ctrl?: boolean;\n  sequence?: string;\n}\n\nexport interface PickerState {\n  filter: string;\n  cursor: number;\n}\n\nexport type PickerAction = \"none\" | \"select\" | \"cancel\" | \"cd\";\n\n/** Pure: one keypress -> next state and what the driver should do. */\nexport function pickerKey(state: PickerState, str: string | undefined, key: Key, visibleCount: number): { state: PickerState; action: PickerAction } {\n  if ((key.ctrl && key.name === \"c\") || key.name === \"escape\") return { state, action: \"cancel\" };\n  if (key.name === \"return\" || key.name === \"enter\") return { state, action: visibleCount > 0 ? \"select\" : \"none\" };\n  if (key.name === \"up\") return { state: { ...state, cursor: Math.max(0, state.cursor - 1) }, action: \"none\" };\n  if (key.name === \"down\") return { state: { ...state, cursor: Math.min(Math.max(0, visibleCount - 1), state.cursor + 1) }, action: \"none\" };\n  if (key.name === \"backspace\") return { state: { filter: state.filter.slice(0, -1), cursor: 0 }, action: \"none\" };\n  if (key.ctrl && key.name === \"u\") return { state: { filter: \"\", cursor: 0 }, action: \"none\" };\n  if (key.ctrl && key.name === \"g\") return { state, action: \"cd\" };\n  if (str && !key.ctrl && str >= \" \" && str !== \"\\u007f\") return { state: { filter: state.filter + str, cursor: 0 }, action: \"none\" };\n  return { state, action: \"none\" };\n}\n\nexport interface PickerOpts<T> {\n  title?: string;\n  headers: string[];\n  items: T[];\n  cells(item: T): string[];\n  /** Text the filter matches against (case-insensitive, every word must occur). */\n  searchText(item: T): string;\n  initialFilter?: string;\n  footer: string;\n  /** Shown under the table for the highlighted row (e.g. a status reason). */\n  detail?(item: T): string;\n  maxRows?: number;\n  empty?: string;\n  /** Called when the user presses Ctrl-G on a row */\n  onCd?: (row: T) => void;\n}\n\nexport function filterItems<T>(items: T[], filter: string, text: (i: T) => string): T[] {\n  const words = filter.toLowerCase().split(/\\s+/).filter(Boolean);\n  return words.length ? items.filter((i) => words.every((w) => text(i).toLowerCase().includes(w))) : items;\n}\n\n/** Pure: the lines for the current state. */\nexport function pickerView<T>(state: PickerState, opts: PickerOpts<T>, st: Style): string[] {\n  const shown = filterItems(opts.items, state.filter, opts.searchText);\n  const max = opts.maxRows ?? 12;\n  const start = Math.min(Math.max(0, state.cursor - max + 1), Math.max(0, shown.length - max));\n  const page = shown.slice(start, start + max);\n  const lines: string[] = [];\n  if (opts.title) lines.push(st.bold(opts.title), \"\");\n  lines.push(`${st.dim(\"filter\")} ${state.filter}${st.dim(state.filter ? \"\" : \"type to filter\")}`, \"\");\n  if (page.length === 0) {\n    lines.push(`  ${st.yellow(opts.empty ?? \"nothing matches\")}`);\n  } else {\n    const table = renderTable(opts.headers, page.map(opts.cells), st).split(\"\\n\");\n    lines.push(table[0], table[1]);\n    page.forEach((_, i) => {\n      const sel = start + i === state.cursor;\n      lines.push((sel ? st.cyan(\"›\") : \" \") + \" \" + table[2 + i].slice(2));\n    });\n  }\n  if (shown.length > max) lines.push(st.dim(`  … ${shown.length} in total`));\n  const cur = shown[state.cursor];\n  const detail = cur && opts.detail ? opts.detail(cur) : \"\";\n  lines.push(\"\", detail ? `  ${detail}` : \"\", st.dim(opts.footer + \" · Ctrl-G cd\"));\n  return lines;\n}\n\n// ---------------------------------------------------------------------------\n// Driver\n// ---------------------------------------------------------------------------\n\nexport interface TtyIn {\n  isTTY?: boolean;\n  setRawMode?(on: boolean): unknown;\n  on(ev: \"keypress\", fn: (str: string | undefined, key: Key) => void): unknown;\n  off(ev: \"keypress\", fn: (str: string | undefined, key: Key) => void): unknown;\n  /** Input closed (EOF, hang-up): a prompt must settle, not wait forever. */\n  once(ev: \"end\" | \"close\", fn: () => void): unknown;\n  off(ev: \"end\" | \"close\", fn: () => void): unknown;\n  resume(): unknown;\n  pause(): unknown;\n}\nexport interface TtyOut {\n  write(s: string): unknown;\n}\n\nconst HIDE = \"\\u001b[?25l\";\nconst SHOW = \"\\u001b[?25h\";\n\n/** Resolves the chosen item, or null when cancelled. Always restores the terminal. */\nexport function runPicker<T>(opts: PickerOpts<T>, st: Style, input: TtyIn, output: TtyOut): Promise<T | null> {\n  return new Promise((resolve) => {\n    let state: PickerState = { filter: opts.initialFilter ?? \"\", cursor: 0 };\n    let drawn = 0;\n    const draw = () => {\n      const lines = pickerView(state, opts, st);\n      output.write((drawn ? `\\u001b[${drawn}A` : \"\") + \"\\r\\u001b[J\" + lines.join(\"\\n\") + \"\\n\");\n      drawn = lines.length;\n    };\n    const finish = (value: T | null) => {\n      input.off(\"keypress\", onKey);\n      input.off(\"end\", onEnd);\n      input.off(\"close\", onEnd);\n      input.setRawMode?.(false);\n      input.pause();\n      output.write(SHOW);\n      resolve(value);\n    };\n    const onKey = (str: string | undefined, key: Key) => {\n      const shown = filterItems(opts.items, state.filter, opts.searchText);\n      const r = pickerKey(state, str, key ?? {}, shown.length);\n      state = r.state;\n      if (r.action === \"cancel\") return finish(null);\n      if (r.action === \"select\") return finish(shown[state.cursor] ?? null);\n      if (r.action === \"cd\") {\n        const row = shown[state.cursor];\n        if (row) {\n          opts.onCd?.(row);\n          return finish(null);\n        }\n      }\n      draw();\n    };\n    const onEnd = () => finish(null);\n    emitKeypressEvents(input as unknown as NodeJS.ReadableStream);\n    input.setRawMode?.(true);\n    input.resume();\n    output.write(HIDE);\n    draw();\n    input.on(\"keypress\", onKey);\n    input.once(\"end\", onEnd);\n    input.once(\"close\", onEnd);\n  });\n}\n\n/** One keypress (for the launch screen). Resolves null on Ctrl-C / Esc. */\nexport function readOneKey(input: TtyIn): Promise<{ str?: string; key: Key } | null> {\n  return new Promise((resolve) => {\n    const done = () => {\n      input.off(\"keypress\", onKey);\n      input.off(\"end\", onEnd);\n      input.off(\"close\", onEnd);\n      input.setRawMode?.(false);\n      input.pause();\n    };\n    const onEnd = () => {\n      done();\n      resolve(null);\n    };\n    const onKey = (str: string | undefined, key: Key) => {\n      done();\n      const k = key ?? {};\n      resolve((k.ctrl && k.name === \"c\") || k.name === \"escape\" ? null : { str, key: k });\n    };\n    emitKeypressEvents(input as unknown as NodeJS.ReadableStream);\n    input.setRawMode?.(true);\n    input.resume();\n    input.on(\"keypress\", onKey);\n    input.once(\"end\", onEnd);\n    input.once(\"close\", onEnd);\n  });\n}\n","/**\n * Which provider (and model) a session opens on. One pure function, one\n * order, written down:\n *\n *   1. --provider on the command line — always wins, never overridden\n *   2. the provider the session's transcript ran on\n *   3. the active provider from workers.yaml\n *   4. anthropic (plain Claude Code login)\n *\n * Nothing falls back silently: an explicit provider that is not available is\n * an error naming the ones that are (see assertAvailable).\n */\n\nimport { ANTHROPIC_NATIVE, type WorkerProvider } from \"../../workers/config.js\";\nimport { stripModelVariant } from \"../../utils/model-window.js\";\nimport { CliError } from \"../define.js\";\n\nexport type ChoiceSource = \"flag\" | \"session\" | \"active\" | \"default\";\n\nexport interface SessionOrigin {\n  provider: string;\n  model: string;\n}\n\nexport interface Choice {\n  provider: string;\n  /** Undefined = the provider's default model. */\n  model?: string;\n  source: ChoiceSource;\n}\n\n/** Provider a transcript's model belongs to: a configured provider whose model\n *  table names it, else anthropic for a claude-* model, else unknown. */\nexport function inferOrigin(\n  model: string | null | undefined,\n  providers: Record<string, Pick<WorkerProvider, \"enabled\" | \"native\" | \"models\">>\n): SessionOrigin | null {\n  if (!model) return null;\n  const base = stripModelVariant(model);\n  for (const [name, p] of Object.entries(providers)) {\n    if (!p.enabled || p.native) continue;\n    if (Object.values(p.models).some((m) => !!m && stripModelVariant(m) === base)) return { provider: name, model };\n  }\n  return /^claude-/.test(base) ? { provider: ANTHROPIC_NATIVE, model } : null;\n}\n\nexport function chooseProvider(input: {\n  flagProvider?: string;\n  flagModel?: string;\n  origin: SessionOrigin | null;\n  active: string | null;\n}): Choice {\n  const { flagProvider, flagModel, origin, active } = input;\n  if (flagProvider) {\n    const same = origin?.provider === flagProvider;\n    return { provider: flagProvider, model: flagModel ?? (same ? origin.model : undefined), source: \"flag\" };\n  }\n  if (origin) return { provider: origin.provider, model: flagModel ?? origin.model, source: \"session\" };\n  if (active && active !== \"auto\") return { provider: active, model: flagModel, source: \"active\" };\n  return { provider: ANTHROPIC_NATIVE, model: flagModel, source: \"default\" };\n}\n\nconst shortList = (names: string[], max = 8) =>\n  names.length <= max ? names.join(\", \") : `${names.slice(0, max).join(\", \")} … (${names.length - max} more: pai providers)`;\n\n/** Fail with the reason and the providers that do work. */\nexport function assertAvailable(\n  provider: string,\n  available: string[],\n  retry: (alternative: string) => string\n): void {\n  if (available.includes(provider)) return;\n  const alt = available.find((n) => n !== provider) ?? ANTHROPIC_NATIVE;\n  throw new CliError(\n    `provider \"${provider}\" is not available (not configured or disabled). Available: ${shortList(available)}`,\n    retry(alt)\n  );\n}\n\n/** Keep a session's recorded model only when the target provider offers it. */\nexport function modelFor(provider: Pick<WorkerProvider, \"models\">, wanted: string | undefined): string | undefined {\n  if (!wanted) return undefined;\n  const base = stripModelVariant(wanted);\n  return Object.values(provider.models).find((m) => !!m && (m === wanted || stripModelVariant(m) === base)) ?? undefined;\n}\n","/**\n * Setups: which model orchestrates a session and which provider the worker\n * classes run on. Built-in setups are derived from the configured providers;\n * the user can save the current combination under a name.\n *\n * Pure functions here; the saved file path is a parameter so tests use a temp dir.\n */\n\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { paiHomePath } from \"../../config/pai-home.js\";\nimport {\n  ANTHROPIC_NATIVE,\n  getProviderOrNative,\n  type WorkersConfig,\n} from \"../../workers/config.js\";\nimport { stripModelVariant } from \"../../utils/model-window.js\";\n\nexport interface Setup {\n  name: string;\n  orchestrator: { provider: string; model?: string };\n  /** Provider the worker classes run on. */\n  workers: string;\n  kind: \"last\" | \"builtin\" | \"saved\";\n}\n\nexport const setupsPath = () => paiHomePath(\"setups.json\");\n\n/** \"claude-opus-5-5\" -> \"opus-5.5\"; \"glm-5.3[1m]\" -> \"glm-5.3\". */\nexport function shortModel(model: string | undefined): string {\n  if (!model) return \"default\";\n  const m = stripModelVariant(model);\n  const c = /^claude-([a-z]+)-(\\d+)-(\\d+)$/.exec(m);\n  return c ? `${c[1]}-${c[2]}.${c[3]}` : m.replace(/^claude-/, \"\");\n}\n\ntype Cfg = Pick<WorkersConfig, \"providers\" | \"active\" | \"nativeModels\">;\n\n/** Display label for a provider's worker role, e.g. \"glm-fast (glm)\". */\nexport function workersLabel(cfg: Cfg, provider: string): string {\n  const p = getProviderOrNative(cfg, provider);\n  const fast = p?.models.fast;\n  if (provider === ANTHROPIC_NATIVE) return \"anthropic (haiku)\";\n  return fast && fast !== p?.models.default ? `${provider}-fast (${provider})` : `${provider} (${provider})`;\n}\n\nexport function orchestratorLabel(cfg: Cfg, o: Setup[\"orchestrator\"]): string {\n  const model = o.model ?? getProviderOrNative(cfg, o.provider)?.models.default;\n  return `${shortModel(model)} (${o.provider})`;\n}\n\nconst PRIORITY = [\"glm\", \"kimi\"];\n\n/** Built-in setups from what is configured and enabled. */\nexport function builtinSetups(cfg: Cfg): Setup[] {\n  const enabled = (n: string) => n === ANTHROPIC_NATIVE || !!cfg.providers[n]?.enabled;\n  const out: Setup[] = [{ name: \"anthropic only\", orchestrator: { provider: ANTHROPIC_NATIVE }, workers: ANTHROPIC_NATIVE, kind: \"builtin\" }];\n  const names = [...PRIORITY, ...(cfg.active && cfg.active !== \"auto\" && !PRIORITY.includes(cfg.active) && cfg.active !== ANTHROPIC_NATIVE ? [cfg.active] : [])].filter(enabled);\n  for (const n of names) {\n    out.push({ name: `anthropic + ${n} workers`, orchestrator: { provider: ANTHROPIC_NATIVE }, workers: n, kind: \"builtin\" });\n  }\n  for (const n of names) out.push({ name: `${n} only`, orchestrator: { provider: n }, workers: n, kind: \"builtin\" });\n  return out;\n}\n\nexport function loadSaved(path = setupsPath()): Setup[] {\n  if (!existsSync(path)) return [];\n  try {\n    const data = JSON.parse(readFileSync(path, \"utf8\")) as Record<string, Omit<Setup, \"name\" | \"kind\">>;\n    return Object.entries(data).map(([name, s]) => ({ name, orchestrator: s.orchestrator, workers: s.workers, kind: \"saved\" as const }));\n  } catch {\n    return [];\n  }\n}\n\nexport function saveSetup(setup: Omit<Setup, \"kind\">, path = setupsPath()): void {\n  const data: Record<string, unknown> = {};\n  for (const s of loadSaved(path)) data[s.name] = { orchestrator: s.orchestrator, workers: s.workers };\n  data[setup.name] = { orchestrator: setup.orchestrator, workers: setup.workers };\n  mkdirSync(dirname(path), { recursive: true });\n  const tmp = `${path}.tmp-${process.pid}`;\n  writeFileSync(tmp, JSON.stringify(data, null, 2) + \"\\n\");\n  renameSync(tmp, path);\n}\n\n/** \"Last time\" first, then saved, then built-in; duplicates of Last time kept (they have names). */\nexport function allSetups(cfg: Cfg, last: Setup | null, saved: Setup[]): Setup[] {\n  return [...(last ? [last] : []), ...saved, ...builtinSetups(cfg)];\n}\n\n/** Find a setup by exact name (case-insensitive), else unique prefix. */\nexport function findSetup(setups: Setup[], query: string): Setup | null {\n  const q = query.toLowerCase().trim();\n  const exact = setups.find((s) => s.name.toLowerCase() === q);\n  if (exact) return exact;\n  const pre = setups.filter((s) => s.name.toLowerCase().startsWith(q));\n  return pre.length === 1 ? pre[0] : null;\n}\n","/**\n * Session names live in Claude's clc registry (~/.claude/session.json):\n * { sessions: [ { name, resume|session (uuid), directory } ] }.\n * Renaming rewrites that one entry, leaving every other field alone.\n */\n\nimport { existsSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const clcRegistryPath = (home = homedir()) => join(home, \".claude\", \"session.json\");\n\ninterface Entry {\n  name?: string;\n  resume?: string;\n  session?: string;\n  directory?: string;\n  [k: string]: unknown;\n}\n\nexport function renameSession(\n  uuid: string,\n  newName: string,\n  directory: string | undefined,\n  path = clcRegistryPath()\n): { previous?: string } {\n  const data: { sessions?: Entry[]; [k: string]: unknown } = existsSync(path)\n    ? (JSON.parse(readFileSync(path, \"utf8\")) as { sessions?: Entry[] })\n    : {};\n  const sessions = (data.sessions ??= []);\n  const hit = sessions.find((e) => e.resume === uuid || e.session === uuid);\n  let previous: string | undefined;\n  if (hit) {\n    previous = hit.name;\n    hit.name = newName;\n  } else {\n    sessions.push({ name: newName, resume: uuid, ...(directory ? { directory } : {}) });\n  }\n  const tmp = `${path}.tmp-${process.pid}`;\n  writeFileSync(tmp, JSON.stringify(data, null, 2) + \"\\n\");\n  renameSync(tmp, path);\n  return { previous };\n}\n","/**\n * Exit-code bridge for old-style commands. They report failure by setting\n * process.exitCode; the entry point exits with the number a command returns.\n */\nexport async function legacy(fn: () => Promise<void> | void): Promise<number> {\n  process.exitCode = undefined;\n  await fn();\n  const code = Number(process.exitCode ?? 0);\n  process.exitCode = undefined;\n  return code;\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 { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport { err } from \"../../lib/utils.js\";\nimport type { RegistryBackend } from \"../../../storage/registry-interface.js\";\nimport type { SessionRow, ProjectRow } from \"./types.js\";\n\nexport async function getProject(\n  registryBackend: RegistryBackend,\n  slug: string\n): Promise<ProjectRow | undefined> {\n  const project = await registryBackend.getProjectBySlug(slug);\n  return project ?? 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 async function resolveSession(\n  registryBackend: RegistryBackend,\n  project: ProjectRow,\n  numberOrLatest: string\n): Promise<SessionRow> {\n  let session: SessionRow | undefined;\n\n  if (numberOrLatest === \"latest\") {\n    session = (await registryBackend.getLatestSessionForProject(project.id)) ?? 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 = (await registryBackend.getSessionByNumber(project.id, num)) ?? 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 async function upsertTag(registryBackend: RegistryBackend, tagName: string): Promise<number> {\n  return registryBackend.upsertTag(tagName);\n}\n\nexport async function getSessionTags(registryBackend: RegistryBackend, sessionId: number): Promise<string[]> {\n  return registryBackend.listTagsForSession(sessionId);\n}\n","/**\n * Session CRUD commands: list, info, rename, slug, tag, route, active, auto-route.\n */\n\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 \"../../lib/utils.js\";\nimport {\n  findLatestTranscript,\n  readLastMessages,\n  generateSlug,\n} from \"../../../session/slug-generator.js\";\nimport type { RegistryBackend } from \"../../../storage/registry-interface.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 async function cmdList(\n  projectSlug: string | undefined,\n  opts: { limit?: string; status?: string }\n): Promise<void> {\n  const { getRegistryBackend } = await import(\"../../../storage/factory.js\");\n  const registryBackend = await getRegistryBackend();\n\n  const limit = parseInt(opts.limit ?? \"20\", 10);\n\n  let project: ProjectRow | undefined;\n  if (projectSlug) {\n    project = await getProject(registryBackend, projectSlug);\n    if (!project) {\n      console.error(err(`Project not found: ${projectSlug}`));\n      process.exitCode = 1;\n      return;\n    }\n  }\n\n  const rows = await registryBackend.listSessions({\n    projectId: project?.id,\n    status: opts.status,\n    limit,\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 (project) {\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 async function cmdInfo(\n  projectSlug: string,\n  sessionNumber: string\n): Promise<void> {\n  const { getRegistryBackend } = await import(\"../../../storage/factory.js\");\n  const registryBackend = await getRegistryBackend();\n\n  const project = await getProject(registryBackend, projectSlug);\n  if (!project) {\n    console.error(err(`Project not found: ${projectSlug}`));\n    process.exitCode = 1;\n    return;\n  }\n\n  const session = await resolveSession(registryBackend, 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 async function cmdRename(\n  projectSlug: string,\n  numberOrLatest: string,\n  newSlug: string\n): Promise<void> {\n  const { getRegistryBackend } = await import(\"../../../storage/factory.js\");\n  const registryBackend = await getRegistryBackend();\n  await cmdRenameWith(registryBackend, projectSlug, numberOrLatest, newSlug);\n}\n\nasync function cmdRenameWith(\n  registryBackend: RegistryBackend,\n  projectSlug: string,\n  numberOrLatest: string,\n  newSlug: string\n): Promise<void> {\n  const project = await getProject(registryBackend, projectSlug);\n  if (!project) {\n    console.error(err(`Project not found: ${projectSlug}`));\n    process.exitCode = 1;\n    return;\n  }\n\n  const session = await resolveSession(registryBackend, project, numberOrLatest);\n  const notesDir = getNotesDir(project);\n\n  if (!existsSync(notesDir)) {\n    console.error(err(`Notes directory not found: ${notesDir}`));\n    process.exitCode = 1;\n    return;\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.exitCode = 1;\n        return;\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  await registryBackend.updateSessionMeta(session.id, {\n    slug: normalizedSlug,\n    title: titleSlug,\n    filename: newFilename,\n  });\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 async function cmdSlug(\n  projectSlug: string,\n  numberOrLatest: string,\n  opts: { apply?: boolean }\n): Promise<void> {\n  const { getRegistryBackend } = await import(\"../../../storage/factory.js\");\n  const registryBackend = await getRegistryBackend();\n\n  const project = await getProject(registryBackend, projectSlug);\n  if (!project) {\n    console.error(err(`Project not found: ${projectSlug}`));\n    process.exitCode = 1;\n    return;\n  }\n\n  const session = await resolveSession(registryBackend, 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    await cmdRenameWith(registryBackend, projectSlug, String(session.number), generatedSlug);\n  }\n}\n\n// ---------------------------------------------------------------------------\n// tag\n// ---------------------------------------------------------------------------\n\nexport async function cmdTag(\n  projectSlug: string,\n  sessionNumber: string,\n  rawTags: string[]\n): Promise<void> {\n  const { getRegistryBackend } = await import(\"../../../storage/factory.js\");\n  const registryBackend = await getRegistryBackend();\n\n  const project = await getProject(registryBackend, projectSlug);\n  if (!project) {\n    console.error(err(`Project not found: ${projectSlug}`));\n    process.exitCode = 1;\n    return;\n  }\n\n  const session = await resolveSession(registryBackend, project, sessionNumber);\n\n  if (rawTags.length === 0) {\n    const current = await getSessionTags(registryBackend, 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 = await upsertTag(registryBackend, tagName);\n    const exists = await registryBackend.sessionHasTag(session.id, tagId);\n    if (exists) {\n      skipped.push(tagName);\n    } else {\n      await registryBackend.addSessionTag(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 = await getSessionTags(registryBackend, 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 async function cmdRoute(\n  projectSlug: string,\n  sessionNumber: string,\n  targetProjectSlug: string,\n  opts: { type?: string }\n): Promise<void> {\n  const { getRegistryBackend } = await import(\"../../../storage/factory.js\");\n  const registryBackend = await getRegistryBackend();\n\n  const project = await getProject(registryBackend, projectSlug);\n  if (!project) {\n    console.error(err(`Project not found: ${projectSlug}`));\n    process.exitCode = 1;\n    return;\n  }\n\n  const session = await resolveSession(registryBackend, project, sessionNumber);\n\n  const targetProject = await getProject(registryBackend, targetProjectSlug);\n\n  if (!targetProject) {\n    console.error(err(`Target project not found: ${targetProjectSlug}`));\n    process.exitCode = 1;\n    return;\n  }\n\n  const validTypes = [\"related\", \"follow-up\", \"reference\"];\n  const linkType = (opts.type ?? \"related\") as \"related\" | \"follow-up\" | \"reference\";\n  if (!validTypes.includes(linkType)) {\n    console.error(\n      err(`Invalid link type \"${linkType}\". Valid: ${validTypes.join(\", \")}`)\n    );\n    process.exitCode = 1;\n    return;\n  }\n\n  if (await registryBackend.linkExists(session.id, targetProject.id)) {\n    console.log(\n      warn(\n        `  Link already exists: session #${session.number} → ${targetProjectSlug}`\n      )\n    );\n    return;\n  }\n\n  await registryBackend.addLink({\n    sessionId: session.id,\n    targetProjectId: targetProject.id,\n    linkType,\n    createdAt: Date.now(),\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 async function cmdActive(\n  opts: { minutes?: string; json?: boolean }\n): Promise<void> {\n  const { getRegistryBackend } = await import(\"../../../storage/factory.js\");\n  const registryBackend = await getRegistryBackend();\n\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 = await registryBackend.getProjectByEncodedDir(entry);\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, formatAutoRouteJson } = await import(\n    \"../../../session/auto-route.js\"\n  );\n  const { getRegistryBackend, createStorageBackend } = await import(\"../../../storage/factory.js\");\n  const { loadConfig } = await import(\"../../../daemon/config.js\");\n\n  const config = loadConfig();\n  const registryBackend = await getRegistryBackend();\n  const federation = await createStorageBackend(config);\n\n  const targetCwd = opts.cwd ?? process.cwd();\n  const result = await autoRoute(\n    registryBackend,\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 * Which conversations were started by `pai worker run` (not by a person).\n * Worker runs record their Claude conversation id in `<id>.status` and work in\n * a git worktree under the worker log directory; either marks a session as a\n * worker's, so `pai ls` and the picker can hide them by default.\n */\n\nimport { readdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { readWorkersSection } from \"../../workers/config.js\";\nimport { workersLogDir } from \"../../workers/paths.js\";\n\nexport interface WorkerSessionFilter {\n  isWorker(s: { uuid: string; decodedPath: string }): boolean;\n}\n\nexport function workerLogDirOrNull(): string | null {\n  try {\n    return workersLogDir(readWorkersSection().workers);\n  } catch {\n    return null;\n  }\n}\n\n/** Conversation ids recorded by worker runs in `logDir`. Never throws. */\nexport function readWorkerSessionIds(logDir: string | null): Set<string> {\n  const ids = new Set<string>();\n  if (!logDir) return ids;\n  let files: string[];\n  try {\n    files = readdirSync(logDir).filter((f) => f.endsWith(\".status\"));\n  } catch {\n    return ids;\n  }\n  for (const f of files) {\n    try {\n      const rec = JSON.parse(readFileSync(join(logDir, f), \"utf8\")) as { claudeSession?: string };\n      if (rec.claudeSession) ids.add(rec.claudeSession);\n    } catch {\n      /* unreadable status file: not a worker marker */\n    }\n  }\n  return ids;\n}\n\nexport function workerSessionFilter(logDir: string | null = workerLogDirOrNull()): WorkerSessionFilter {\n  const ids = readWorkerSessionIds(logDir);\n  const marker = \"/workers/worktrees/\";\n  return {\n    isWorker: (s) => ids.has(s.uuid) || s.decodedPath.includes(marker),\n  };\n}\n","/**\n * Sessions: `pai session list|open|new|rename`, plus the daily shortcuts\n * `pai ls`, `pai open`, `pai new`, `pai rename`.\n */\n\nimport { realpathSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport { createInterface } from \"node:readline\";\nimport { readWorkersSection, type WorkersConfig } from \"../../workers/config.js\";\nimport { launchableProviderNames, LaunchError } from \"../../workers/launch.js\";\nimport { CliError, OPT, type CommandDef, type RunContext } from \"../define.js\";\nimport { renderTable, makeStyle, colorEnabled, type Style } from \"../help.js\";\nimport { fetchLiveSessions } from \"../lib/aibroker-client.js\";\nimport { dryRunText, buildHarness, execHarness, type Harness } from \"../lib/harness.js\";\nimport { probeResume, transcriptModelFor } from \"../lib/launch.js\";\nimport { providerHealth, readBudget, type Health } from \"../lib/health.js\";\nimport { renameSession } from \"../lib/names.js\";\nimport {\n  assertAvailable,\n  chooseProvider,\n  inferOrigin,\n  modelFor,\n  type Choice,\n  type SessionOrigin,\n} from \"../lib/provider-choice.js\";\nimport {\n  fmtAge,\n  resolveSessionByNameOrId,\n  scanSessions,\n  type ScannedSession,\n} from \"../lib/session-scan.js\";\nimport { legacy } from \"../lib/legacy.js\";\nimport { cmdRename } from \"./session/commands.js\";\nimport { workerSessionFilter } from \"../lib/worker-sessions.js\";\nimport { getProviderOrNative } from \"../../workers/config.js\";\n\n// ---------------------------------------------------------------------------\n// Shared helpers\n// ---------------------------------------------------------------------------\n\nfunction loadWorkers(): WorkersConfig {\n  try {\n    return readWorkersSection().workers;\n  } catch (e) {\n    throw new CliError(\n      `cannot read the provider configuration: ${e instanceof Error ? e.message : String(e)}`,\n      \"pai config path   (shows which file is read)\"\n    );\n  }\n}\n\n/** Where a session's working directory is: registry-known paths first, as the old resolver did. */\nexport function sessionDir(s: Pick<ScannedSession, \"clcDirectory\" | \"registryRootPath\" | \"decodedPath\">): string | undefined {\n  for (const c of [s.clcDirectory, s.registryRootPath, s.decodedPath]) {\n    if (!c) continue;\n    try {\n      return realpathSync(c);\n    } catch {\n      /* stale path, try the next */\n    }\n  }\n  return undefined;\n}\n\nconst projectOf = (s: ScannedSession) => basename(sessionDir(s) ?? s.decodedPath) || s.decodedPath;\nconst nameOf = (s: ScannedSession) => s.friendlyName ?? s.aiTitle ?? projectOf(s);\n\nexport interface SessionRow {\n  id: string;\n  uuid: string;\n  name: string;\n  project: string;\n  provider: string;\n  model: string;\n  lastActive: string;\n  lastActiveMs: number;\n  status: string;\n  /** Last thing the user typed, one line. */\n  message: string;\n  /** Working directory the session opens in, when it still exists. */\n  dir?: string;\n  /** Can the provider it last ran on be used right now? */\n  health: Health;\n}\n\n/** Last user text on one line, without the <tag> wrappers Claude Code adds to notifications. */\nexport function oneLine(text: string | undefined): string {\n  return (text ?? \"\").replace(/<[^>]{1,60}>/g, \" \").replace(/\\s+/g, \" \").trim();\n}\n\nfunction originOf(s: ScannedSession, workers: WorkersConfig | null): SessionOrigin | null {\n  const model = transcriptModelFor(s.uuid, s.encodedDir);\n  return inferOrigin(model, workers?.providers ?? {});\n}\n\nexport async function collectRows(opts: { project?: string; all?: boolean; workers?: boolean }): Promise<SessionRow[]> {\n  const { rows } = await collectView(opts);\n  return rows;\n}\n\nexport async function collectView(opts: { project?: string; all?: boolean; workers?: boolean }): Promise<{ rows: SessionRow[]; workers: WorkersConfig | null }> {\n  let workers: WorkersConfig | null = null;\n  try {\n    workers = readWorkersSection().workers;\n  } catch {\n    /* provider column falls back to what the model name says */\n  }\n  const [scanned, live] = await Promise.all([\n    scanSessions({ limit: opts.all ? 1000 : opts.workers ? 25 : 150, filter: opts.all ? \"all\" : \"named\" }),\n    fetchLiveSessions().catch(() => []),\n  ]);\n  const liveIds = new Set(live.filter((l) => l.kind === \"claude\").map((l) => l.sessionId));\n  const needle = opts.project?.toLowerCase();\n  const budget = workers ? await readBudget(workers) : {};\n  const workerFilter = opts.workers ? null : workerSessionFilter();\n  const rows = scanned\n    .filter((s) => !workerFilter?.isWorker(s))\n    .filter((s) => !needle || projectOf(s).toLowerCase().includes(needle) || s.decodedPath.toLowerCase().includes(needle))\n    .sort((a, b) => b.mtime - a.mtime)\n    .slice(0, opts.all ? 1000 : 25)\n    .map((s) => {\n      const origin = originOf(s, workers);\n      return {\n        id: s.shortId,\n        uuid: s.uuid,\n        name: nameOf(s),\n        project: projectOf(s),\n        provider: origin?.provider ?? \"—\",\n        model: origin?.model ?? \"—\",\n        lastActive: `${fmtAge(s.mtime)}${fmtAge(s.mtime) === \"now\" ? \"\" : \" ago\"}`,\n        lastActiveMs: s.mtime,\n        status: liveIds.has(s.uuid) ? \"live\" : s.sessionStatus,\n        message: oneLine(s.lastUserPrompt),\n        dir: sessionDir(s),\n        health: origin && workers ? providerHealth(origin.provider, workers, budget) : ({ state: \"ok\", reason: \"ok\" } as Health),\n      };\n    });\n  return { rows, workers };\n}\n\nconst statusColor = (st: Style, s: string) =>\n  s === \"live\" || s === \"resumable\" ? st.green(s) : s === \"orphan\" ? st.dim(s) : st.yellow(s);\n\nexport function rowsTable(rows: SessionRow[], st: Style, numbered = false): string {\n  const head = [...(numbered ? [\"#\"] : []), \"ID\", \"Name\", \"Project\", \"Provider\", \"Model\", \"Last active\", \"Status\"];\n  return renderTable(\n    head,\n    rows.map((r, i) => [\n      ...(numbered ? [st.dim(String(i + 1))] : []),\n      st.cyan(r.id),\n      r.name.length > 34 ? r.name.slice(0, 33) + \"…\" : r.name,\n      r.project.length > 24 ? r.project.slice(0, 23) + \"…\" : r.project,\n      r.provider,\n      r.model,\n      r.lastActive,\n      statusColor(st, r.status),\n    ]),\n    st\n  );\n}\n\nconst out = (s: string) => process.stdout.write(s + \"\\n\");\nconst style = () => makeStyle(colorEnabled());\n\nfunction ask(q: string): Promise<string> {\n  const rl = createInterface({ input: process.stdin, output: process.stdout });\n  return new Promise((res) => rl.question(q, (a) => (rl.close(), res(a))));\n}\n\n// ---------------------------------------------------------------------------\n// ls\n// ---------------------------------------------------------------------------\n\nconst listCommand: CommandDef = {\n  name: \"list\",\n  summary: \"List sessions, newest first\",\n  description:\n    \"Shows your named sessions with the provider and model each last ran on, how long ago it was active, and whether it can be reopened.\\n\" +\n    \"Status: live = running now · resumable = can be reopened · stub / transcript-only = partial history · orphan = unnamed (--all).\",\n  options: [\n    { ...OPT.project },\n    { name: \"all\", type: \"boolean\", default: false, summary: \"Include unnamed sessions and show more than the newest 25\" },\n    { name: \"workers\", type: \"boolean\", default: false, summary: \"Also show sessions started by background workers (hidden by default)\" },\n    { ...OPT.json },\n  ],\n  examples: [\"pai ls\", \"pai ls --project api\", \"pai ls --all --json\", \"pai ls --workers\"],\n  seeAlso: [\"pai open\", \"pai rename\", \"pai providers\"],\n  async run(ctx) {\n    const rows = await collectRows({ project: ctx.opts.project as string | undefined, all: ctx.opts.all === true, workers: ctx.opts.workers === true });\n    if (ctx.opts.json) return void out(JSON.stringify(rows, null, 2));\n    const st = style();\n    if (rows.length === 0) {\n      out(\"No sessions found\" + (ctx.opts.project ? ` for project \"${ctx.opts.project}\"` : \"\") + \".\");\n      out(st.dim(\"Start one with: pai new\"));\n      return;\n    }\n    out(rowsTable(rows, st));\n    out(\"\");\n    out(st.dim(`${rows.length} session${rows.length === 1 ? \"\" : \"s\"} · reopen one with: pai open <name or id>`));\n  },\n};\n\n// ---------------------------------------------------------------------------\n// open\n// ---------------------------------------------------------------------------\n\n/** Pure planning half of `open`, exported for tests: what provider, why. */\nexport function planOpen(input: {\n  workers: Pick<WorkersConfig, \"providers\" | \"active\" | \"nativeModels\">;\n  origin: SessionOrigin | null;\n  flagProvider?: string;\n  flagModel?: string;\n  name: string;\n}): Choice {\n  const { workers, origin, flagProvider, flagModel, name } = input;\n  const choice = chooseProvider({ flagProvider, flagModel, origin, active: workers.active });\n  assertAvailable(choice.provider, launchableProviderNames(workers), (alt) => `pai open \"${name}\" --provider ${alt}`);\n  return choice;\n}\n\nconst openCommand: CommandDef = {\n  name: \"open\",\n  summary: \"Reopen a session (on its own provider unless --provider is given)\",\n  description:\n    \"Continues a session with its full history. The provider is chosen in this order, and nothing else is consulted:\\n\" +\n    \"  1. --provider, if you give it (always wins; if it is unavailable the command fails and lists the ones that are)\\n\" +\n    \"  2. the provider the session last ran on\\n\" +\n    \"  3. the active provider (pai providers)\\n\" +\n    \"Without NAME_OR_ID you pick from a numbered list.\",\n  args: [{ name: \"NAME_OR_ID\", summary: \"Session name, or the id from `pai ls` (a prefix is enough)\" }],\n  options: [\n    { ...OPT.provider, summary: \"Open on this provider, whatever the session used before\" },\n    { ...OPT.model },\n    { ...OPT.dryRun },\n  ],\n  examples: [\"pai open\", \"pai open my-session\", \"pai open 3a9f1c2e --provider kimi\", \"pai open my-session --dry-run\"],\n  seeAlso: [\"pai ls\", \"pai new\", \"pai providers\"],\n  async run(ctx: RunContext) {\n    const st = style();\n    const workers = loadWorkers();\n    const sessions = await scanSessions({ limit: 500, filter: \"named\" });\n    let query = ctx.args.NAME_OR_ID as string | undefined;\n    if (!query) {\n      if (!process.stdin.isTTY || !process.stdout.isTTY) {\n        throw new CliError(\"no session given and no terminal to pick from\", \"pai ls   then   pai open <name or id>\");\n      }\n      const rows = (await collectRows({})).slice(0, 15);\n      if (rows.length === 0) throw new CliError(\"there are no sessions to open\", \"pai new\");\n      out(rowsTable(rows, st, true));\n      const a = (await ask(\"\\nOpen which? number, name or id: \")).trim();\n      if (!a) throw new CliError(\"nothing picked\", \"pai open <name or id>\");\n      query = /^\\d+$/.test(a) ? rows[Number(a) - 1]?.uuid : a;\n      if (!query) throw new CliError(`\"${a}\" is not a number from the list`, \"pai ls\");\n    }\n\n    let session: ScannedSession;\n    let name: string | undefined;\n    try {\n      ({ session, friendlyName: name } = resolveSessionByNameOrId(sessions, query));\n    } catch (e) {\n      throw new CliError(String(e instanceof Error ? e.message : e).split(\"\\n\\n\")[0], \"pai ls   (shows names and ids)\");\n    }\n    name = name ?? nameOf(session);\n    const cwd = sessionDir(session);\n    if (!cwd) {\n      throw new CliError(\n        `the folder of session \"${name}\" no longer exists (${session.decodedPath})`,\n        `cd <an existing folder> && pai new --name \"${name}\"`\n      );\n    }\n\n    const origin = originOf(session, workers);\n    const choice = planOpen({\n      workers,\n      origin,\n      flagProvider: ctx.opts.provider as string | undefined,\n      flagModel: ctx.opts.model as string | undefined,\n      name,\n    });\n    const provider = getProviderOrNative(workers, choice.provider)!;\n    const model = ctx.opts.model ? (ctx.opts.model as string) : modelFor(provider, choice.model);\n\n    const probe = probeResume(session.uuid, cwd);\n    if (!probe.ok) {\n      throw new CliError(\n        `session \"${name}\" has no history that can be continued (${probe.reason ?? \"unknown reason\"})`,\n        `cd ${JSON.stringify(cwd)} && pai new --name ${JSON.stringify(name)} --provider ${choice.provider}`\n      );\n    }\n\n    let harness: Harness;\n    try {\n      harness = await buildHarness(workers, { provider: choice.provider, model, cwd, resume: session.uuid, name });\n    } catch (e) {\n      if (e instanceof LaunchError || (e as Error).name === \"WorkersConfigError\") {\n        throw new CliError(\n          (e as Error).message,\n          `pai open \"${name}\" --provider ${launchableProviderNames(workers).find((n) => n !== choice.provider) ?? \"anthropic\"}`\n        );\n      }\n      throw e;\n    }\n\n    out(`Opening \"${name}\" on ${st.bold(harness.provider)} (${harness.model})`);\n    if (origin && origin.provider !== harness.provider) {\n      out(\n        st.yellow(\n          `Note: this session last ran on ${origin.provider} (${origin.model}). It continues with its history on ${harness.provider}; ` +\n            `if ${harness.provider} rejects that history, start fresh: cd ${JSON.stringify(cwd)} && pai new --name ${JSON.stringify(name)} --provider ${harness.provider}`\n        )\n      );\n    }\n    if (ctx.dryRun) return void out(dryRunText(harness));\n    return execHarness(harness, workers);\n  },\n};\n\n// ---------------------------------------------------------------------------\n// new\n// ---------------------------------------------------------------------------\n\nconst newCommand: CommandDef = {\n  name: \"new\",\n  summary: \"Start a fresh session in this folder\",\n  description:\n    \"Starts Claude Code in the current folder on the chosen provider. Without --provider it uses the active provider (see `pai providers`).\",\n  options: [\n    { ...OPT.provider, summary: \"Provider to start on (default: the active provider)\" },\n    { ...OPT.model },\n    { name: \"name\", type: \"string\", value: \"NAME\", summary: \"Session name (default: the folder name)\" },\n    { ...OPT.dryRun },\n  ],\n  examples: [\"pai new\", \"pai new --provider glm\", \"pai new --provider kimi --model k3 --name spike\", \"pai new --dry-run\"],\n  seeAlso: [\"pai open\", \"pai providers\"],\n  async run(ctx) {\n    const st = style();\n    const workers = loadWorkers();\n    const cwd = process.cwd();\n    const name = (ctx.opts.name as string | undefined) ?? (basename(cwd) || \"session\");\n    const choice = chooseProvider({\n      flagProvider: ctx.opts.provider as string | undefined,\n      flagModel: ctx.opts.model as string | undefined,\n      origin: null,\n      active: workers.active,\n    });\n    assertAvailable(choice.provider, launchableProviderNames(workers), (alt) => `pai new --provider ${alt}`);\n    let harness: Harness;\n    try {\n      harness = await buildHarness(workers, { provider: choice.provider, model: choice.model, cwd, name });\n    } catch (e) {\n      if (e instanceof LaunchError || (e as Error).name === \"WorkersConfigError\") {\n        throw new CliError((e as Error).message, \"pai providers   (lists what is available)\");\n      }\n      throw e;\n    }\n    out(`Starting \"${name}\" on ${st.bold(harness.provider)} (${harness.model})`);\n    if (ctx.dryRun) return void out(dryRunText(harness));\n    return execHarness(harness, workers);\n  },\n};\n\n// ---------------------------------------------------------------------------\n// rename\n// ---------------------------------------------------------------------------\n\nconst renameCommand: CommandDef = {\n  name: \"rename\",\n  summary: \"Give a session a new name\",\n  description: \"Changes the name `pai ls` shows and `pai open` accepts. The history is untouched.\",\n  args: [\n    { name: \"ID\", required: true, summary: \"Session id from `pai ls` (a prefix is enough) or its current name\" },\n    { name: \"NAME\", required: true, summary: \"The new name\" },\n    { name: \"NEW_SLUG\", summary: \"Only for the old note form `session rename PROJECT NUMBER NEW_SLUG`: renames a session note instead\" },\n  ],\n  options: [],\n  examples: [\"pai rename 3a9f1c2e api-spike\", 'pai rename old-name \"new name\"', \"pai session rename my-project 12 fix-login-bug\"],\n  seeAlso: [\"pai ls\", \"pai open\"],\n  async run(ctx) {\n    if (ctx.args.NEW_SLUG !== undefined) {\n      return legacy(() => cmdRename(ctx.args.ID as string, ctx.args.NAME as string, ctx.args.NEW_SLUG as string));\n    }\n    const sessions = await scanSessions({ limit: 500, filter: \"all\" });\n    let session: ScannedSession;\n    try {\n      ({ session } = resolveSessionByNameOrId(sessions, ctx.args.ID as string));\n    } catch (e) {\n      throw new CliError(String(e instanceof Error ? e.message : e).split(\"\\n\\n\")[0], \"pai ls   (shows names and ids)\");\n    }\n    const name = (ctx.args.NAME as string).trim();\n    if (!name) throw new CliError(\"the new name is empty\", 'pai rename <id> \"<new name>\"');\n    if (ctx.dryRun) return void out(`would rename ${session.shortId} to \"${name}\"`);\n    const { previous } = renameSession(session.uuid, name, sessionDir(session));\n    out(`Renamed ${session.shortId}${previous ? ` \"${previous}\"` : \"\"} → \"${name}\"`);\n  },\n};\n\n// ---------------------------------------------------------------------------\n// Exports: the group and its daily shortcuts\n// ---------------------------------------------------------------------------\n\nexport const sessionCommand: CommandDef = {\n  name: \"session\",\n  summary: \"Work with sessions: list, open, new, rename\",\n  description: \"Everything about sessions lives under `pai session`; the daily verbs also exist as short top-level commands.\",\n  examples: [\"pai session list\", \"pai session open my-session\", \"pai session new --provider glm\"],\n  seeAlso: [\"pai ls\", \"pai open\", \"pai new\"],\n  subcommands: [listCommand, openCommand, newCommand, renameCommand],\n};\n\nexport const lsCommand: CommandDef = { ...listCommand, name: \"ls\", aliasOf: \"session list\", daily: true };\nexport const openTop: CommandDef = { ...openCommand, aliasOf: \"session open\" };\nexport const newTop: CommandDef = { ...newCommand, aliasOf: \"session new\" };\nexport const renameTop: CommandDef = { ...renameCommand, aliasOf: \"session rename\" };\n","/**\n * The daily path: `pai` (picker), `pai NAME` (launch screen), `pai --with SETUP`.\n * Everything here is a small function over an injected terminal, so the\n * screens are testable with a fake TTY.\n */\n\nimport { basename } from \"node:path\";\nimport { readWorkersSection, ANTHROPIC_NATIVE, getProviderOrNative, type WorkersConfig } from \"../../workers/config.js\";\nimport { launchableProviderNames, LaunchError } from \"../../workers/launch.js\";\nimport { useProvider } from \"../../workers/providers.js\";\nimport { CliError, OPT, type CommandDef } from \"../define.js\";\nimport { colorEnabled, makeStyle, renderTable, suggest, type Style } from \"../help.js\";\nimport { buildHarness, dryRunText, execHarness, type Harness } from \"../lib/harness.js\";\nimport { providerHealth, readBudget, type Health } from \"../lib/health.js\";\nimport { probeResume } from \"../lib/launch.js\";\nimport { readOneKey, runPicker, type TtyIn, type TtyOut } from \"../lib/picker.js\";\nimport { assertAvailable, modelFor } from \"../lib/provider-choice.js\";\nimport { allSetups, findSetup, loadSaved, orchestratorLabel, shortModel, workersLabel, type Setup } from \"../lib/setups.js\";\nimport { collectView, type SessionRow } from \"./session.js\";\nimport { writeFileSync } from \"node:fs\";\n\nexport const CANCELLED = 130;\n\nexport interface Term {\n  input: TtyIn;\n  output: TtyOut;\n  isTTY: boolean;\n  st: Style;\n}\n\nexport function systemTerm(): Term {\n  return {\n    input: process.stdin as unknown as TtyIn,\n    output: process.stdout,\n    isTTY: !!process.stdin.isTTY && !!process.stdout.isTTY,\n    st: makeStyle(colorEnabled()),\n  };\n}\n\n// ---------------------------------------------------------------------------\n// Pure helpers\n// ---------------------------------------------------------------------------\n\nexport function truncate(s: string, n: number): string {\n  return s.length > n ? s.slice(0, n - 1) + \"…\" : s;\n}\n\nexport function ago(ms: number, now = Date.now()): string {\n  const m = Math.max(0, Math.round((now - ms) / 60000));\n  if (m < 1) return \"just now\";\n  if (m < 60) return `${m}m ago`;\n  const h = Math.round(m / 60);\n  if (h < 48) return `${h}h ago`;\n  return `${Math.round(h / 24)}d ago`;\n}\n\n/** Exact name (newest first) wins; then id prefix; then substring of name or project. */\nexport function matchRows(rows: SessionRow[], query: string): SessionRow[] {\n  const q = query.toLowerCase().trim();\n  const exact = rows.filter((r) => r.name.toLowerCase() === q);\n  if (exact.length) return [exact[0]];\n  const byId = rows.filter((r) => r.uuid.startsWith(q));\n  if (byId.length === 1) return byId;\n  return rows.filter((r) => r.name.toLowerCase().includes(q) || r.project.toLowerCase().includes(q));\n}\n\nconst healthText = (st: Style, h: Health) => (h.state === \"ok\" ? st.green(h.reason === \"ok\" ? \"ok\" : h.reason) : h.state === \"limit\" ? st.red(h.reason) : st.yellow(h.reason));\n\nexport function lastOnLabel(st: Style, r: SessionRow): string {\n  if (r.provider === \"—\") return st.dim(\"unknown\");\n  const t = `${shortModel(r.model)} (${r.provider})`;\n  return r.health.state === \"ok\" ? t : r.health.state === \"limit\" ? st.red(t) : st.yellow(t);\n}\n\nexport function pickerOpts(rows: SessionRow[], st: Style, initialFilter?: string) {\n  return {\n    headers: [\"Name\", \"Project\", \"Last active\", \"Last used on\", \"Last message\"],\n    items: rows,\n    cells: (r: SessionRow) => [\n      st.bold(truncate(r.name, 30)),\n      truncate(r.project, 20),\n      ago(r.lastActiveMs),\n      lastOnLabel(st, r),\n      st.dim(truncate(r.message || \"—\", 40)),\n    ],\n    searchText: (r: SessionRow) => `${r.name} ${r.project} ${r.provider} ${r.model} ${r.message} ${r.id}`,\n    initialFilter,\n    footer: \"↑↓ move · type to filter · Enter open · Esc quit\",\n    detail: (r: SessionRow) => (r.health.state === \"ok\" ? \"\" : healthText(st, r.health)),\n    empty: \"no session matches — Backspace to widen, or Esc and run: pai new\",\n  };\n}\n\n/** The picker as plain text (not a TTY): same columns, no cursor. */\nexport function plainTable(rows: SessionRow[], st: Style): string {\n  const o = pickerOpts(rows, st);\n  return renderTable(o.headers, rows.map(o.cells), st);\n}\n\n/** Setup that reproduces how a session last ran, workers as configured now. */\nexport function lastSetup(row: SessionRow | null, cfg: Pick<WorkersConfig, \"active\">): Setup {\n  const workers = cfg.active && cfg.active !== \"auto\" ? cfg.active : ANTHROPIC_NATIVE;\n  const orch = row && row.provider !== \"—\" ? { provider: row.provider, model: row.model } : { provider: workers };\n  return { name: \"Last time\", orchestrator: orch, workers, kind: \"last\" };\n}\n\nexport function launchScreen(row: SessionRow, last: Setup, cfg: Parameters<typeof workersLabel>[0], st: Style): string[] {\n  const state = row.health.state;\n  const ran = `${lastOnLabel(st, row)} with workers on ${workersLabel(cfg, last.workers).replace(/ \\(.*\\)$/, \"\")}`;\n  return [\n    `${st.dim(\"Session      \")} \"${row.name}\"   ${st.dim(\"last active \" + ago(row.lastActiveMs))}`,\n    `${st.dim(\"Last message \")} \"${truncate(row.message || \"—\", 60)}\"`,\n    `${st.dim(\"Last ran on  \")} ${ran}${state === \"ok\" ? \"\" : \"  \" + healthText(st, row.health)}`,\n    `${st.cyan(\">\")} ${st.bold(\"Enter\")}  Continue exactly like last time`,\n    `  ${st.bold(\"s\")}      Choose a different setup`,\n    `  ${st.bold(\"n\")}      Start fresh (same name, new conversation)`,\n  ];\n}\n\nexport function setupsTable(setups: Setup[], cfg: Parameters<typeof workersLabel>[0] & Pick<WorkersConfig, \"routing\">, budget: Parameters<typeof providerHealth>[2], st: Style) {\n  const status = (s: Setup): Health => {\n    const a = providerHealth(s.orchestrator.provider, cfg, budget);\n    return a.state !== \"ok\" ? a : providerHealth(s.workers, cfg, budget);\n  };\n  return {\n    headers: [\"#\", \"Setup\", \"Orchestrator\", \"Workers\", \"Status\"],\n    items: setups,\n    cells: (s: Setup) => [\n      st.dim(String(setups.indexOf(s) + 1)),\n      st.bold(s.name),\n      orchestratorLabel(cfg, s.orchestrator),\n      workersLabel(cfg, s.workers),\n      healthText(st, status(s)),\n    ],\n    searchText: (s: Setup) => `${s.name} ${s.orchestrator.provider} ${s.workers}`,\n    footer: \"↑↓ move · type to filter · Enter choose · Esc back out\",\n    detail: (s: Setup) => (status(s).state === \"ok\" ? \"\" : \"Not available right now — choosing it starts it anyway.\"),\n    status,\n  };\n}\n\n// ---------------------------------------------------------------------------\n// Starting\n// ---------------------------------------------------------------------------\n\nfunction loadWorkers(): WorkersConfig {\n  try {\n    return readWorkersSection().workers;\n  } catch (e) {\n    throw new CliError(`cannot read the provider configuration: ${e instanceof Error ? e.message : String(e)}`, \"pai providers\");\n  }\n}\n\ninterface StartArgs {\n  workers: WorkersConfig;\n  setup: Setup;\n  name: string;\n  cwd: string;\n  /** Session to continue; undefined = fresh conversation. */\n  resume?: SessionRow;\n  dryRun: boolean;\n  term: Term;\n}\n\nexport async function startSession(a: StartArgs): Promise<number> {\n  const { workers, setup, name, cwd, resume, dryRun, term } = a;\n  const st = term.st;\n  const out = (s: string) => term.output.write(s + \"\\n\");\n  const available = launchableProviderNames(workers);\n  const retry = (alt: string) => `pai ${JSON.stringify(name)} --with ${JSON.stringify(`${alt} only`)}`;\n  assertAvailable(setup.orchestrator.provider, available, retry);\n  assertAvailable(setup.workers, available, retry);\n\n  if (resume) {\n    const probe = probeResume(resume.uuid, cwd);\n    if (!probe.ok) {\n      throw new CliError(\n        `\"${name}\" has no conversation history to continue (${probe.reason ?? \"unknown reason\"})`,\n        `pai ${JSON.stringify(name)}   then press n to start fresh under the same name`\n      );\n    }\n  }\n  const provider = getProviderOrNative(workers, setup.orchestrator.provider)!;\n  let harness: Harness;\n  try {\n    harness = await buildHarness(workers, {\n      provider: setup.orchestrator.provider,\n      model: modelFor(provider, setup.orchestrator.model),\n      cwd,\n      resume: resume?.uuid,\n      name,\n    });\n  } catch (e) {\n    if (e instanceof LaunchError || (e as Error).name === \"WorkersConfigError\") {\n      throw new CliError((e as Error).message, \"pai providers\");\n    }\n    throw e;\n  }\n  const orch = `${shortModel(harness.model)} (${harness.provider})`;\n  out(`Starting \"${name}\": orchestrator ${st.bold(orch)}, workers ${st.bold(workersLabel(workers, setup.workers))}`);\n  if (resume && resume.provider !== \"—\" && (resume.provider !== harness.provider || shortModel(resume.model) !== shortModel(harness.model))) {\n    out(`Conversation history carries over to ${shortModel(harness.model)}.`);\n  }\n  const current = workers.active && workers.active !== \"auto\" ? workers.active : ANTHROPIC_NATIVE;\n  if (setup.workers !== current) {\n    if (dryRun) out(st.dim(`(dry run) would route workers to ${setup.workers}; they run on ${current} now`));\n    else {\n      useProvider(setup.workers);\n      out(`Workers now run on ${setup.workers} (was ${current}).`);\n    }\n  }\n  if (dryRun) {\n    out(dryRunText(harness));\n    return 0;\n  }\n  return execHarness(harness, workers);\n}\n\nasync function chooseSetup(term: Term, workers: WorkersConfig, last: Setup, budget: Parameters<typeof providerHealth>[2]): Promise<Setup | null> {\n  const setups = allSetups(workers, last, loadSaved());\n  const o = setupsTable(setups, workers, budget, term.st);\n  return runPicker({ ...o, title: \"Choose a setup\" }, term.st, term.input, term.output);\n}\n\nasync function openRow(row: SessionRow, workers: WorkersConfig, withName: string | undefined, dryRun: boolean, term: Term): Promise<number> {\n  const st = term.st;\n  const out = (s: string) => term.output.write(s + \"\\n\");\n  const budget = await readBudget(workers);\n  const last = lastSetup(row, workers);\n  const cwd = row.dir;\n  if (!cwd) {\n    throw new CliError(`the folder of \"${row.name}\" no longer exists`, `cd <an existing folder> && pai new --name ${JSON.stringify(row.name)}`);\n  }\n  const go = (setup: Setup, fresh: boolean) => startSession({ workers, setup, name: row.name, cwd, resume: fresh ? undefined : row, dryRun, term });\n\n  if (withName) {\n    const setup = findSetup(allSetups(workers, last, loadSaved()), withName);\n    if (!setup) throw unknownSetup(withName, allSetups(workers, last, loadSaved()));\n    return go(setup, false);\n  }\n  if (!term.isTTY) {\n    if (dryRun) return go(last, false);\n    throw new CliError(`\"${row.name}\" needs a terminal to choose how to start`, `pai ${JSON.stringify(row.name)} --with \"Last time\"`);\n  }\n  for (;;) {\n    for (const l of launchScreen(row, last, workers, st)) out(l);\n    const k = await readOneKey(term.input);\n    out(\"\");\n    if (!k) return CANCELLED;\n    if (k.key.name === \"return\" || k.key.name === \"enter\") {\n      if (row.health.state !== \"ok\") {\n        out(`${healthText(st, row.health)}. Press ${st.bold(\"s\")} to choose a setup deliberately.`);\n        continue;\n      }\n      return go(last, false);\n    }\n    if (k.str === \"n\") return go(last, true);\n    if (k.str === \"s\") {\n      const chosen = await chooseSetup(term, workers, last, budget);\n      return chosen ? go(chosen, false) : CANCELLED;\n    }\n    out(st.dim(\"Press Enter, s or n (Esc to quit).\"));\n  }\n}\n\nfunction unknownSetup(q: string, setups: Setup[]): CliError {\n  const near = suggest(q, setups.map((s) => s.name));\n  return new CliError(\n    `no setup called \"${q}\"${near ? ` — did you mean \"${near}\"?` : \"\"}. Known: ${setups.map((s) => s.name).join(\", \")}`,\n    \"pai setups\"\n  );\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport function finishCd(cdDir: string, term: Pick<Term, \"output\">, env: NodeJS.ProcessEnv = process.env): number {\n  if (env.PAI_PICK_OUT) {\n    writeFileSync(env.PAI_PICK_OUT, cdDir);\n  } else {\n    term.output.write(cdDir + \"\\n\");\n  }\n  return 0;\n}\n\nexport async function runRoot(\n  args: { name?: string; with?: string; dryRun: boolean },\n  term: Term = systemTerm()\n): Promise<number> {\n  const st = term.st;\n  const out = (s: string) => term.output.write(s + \"\\n\");\n  const workers = loadWorkers();\n\n  if (!args.name && args.with) {\n    const setup = findSetup(allSetups(workers, lastSetup(null, workers), loadSaved()), args.with);\n    if (!setup) throw unknownSetup(args.with, allSetups(workers, lastSetup(null, workers), loadSaved()));\n    return startSession({ workers, setup, name: basename(process.cwd()) || \"session\", cwd: process.cwd(), dryRun: args.dryRun, term });\n  }\n\n  const { rows } = await collectView({});\n  if (rows.length === 0) {\n    out(\"No sessions yet.\");\n    out(st.dim(\"Start one in this folder with: pai new\"));\n    return 0;\n  }\n\n  let picked: SessionRow | undefined;\n  let cdDir: string | undefined;\n  if (!args.name) {\n    if (!term.isTTY) {\n      out(plainTable(rows, st));\n      out(\"\");\n      out(st.dim(\"Open one: pai <name>   ·   more commands: pai more\"));\n      return 0;\n    }\n    picked = (await runPicker({ ...pickerOpts(rows, st), onCd: (row) => cdDir = row.dir }, st, term.input, term.output)) ?? undefined;\n    if (!picked) return cdDir !== undefined ? finishCd(cdDir, term) : CANCELLED;\n  } else {\n    const matches = matchRows(rows, args.name);\n    if (matches.length === 0) {\n      const near = suggest(args.name, rows.map((r) => r.name));\n      const cmd = suggest(args.name, [\"ls\", \"open\", \"new\", \"rename\", \"setups\", \"providers\", \"help\", \"doc\", \"more\"]);\n      throw new CliError(\n        `no session called \"${args.name}\"${near ? ` — closest name: \"${near}\"` : \"\"}${cmd ? ` (or did you mean the command \"${cmd}\"?)` : \"\"}`,\n        near ? `pai ${JSON.stringify(near)}` : \"pai   (shows every session)\",\n        2\n      );\n    }\n    if (matches.length === 1) picked = matches[0];\n    else if (!term.isTTY) {\n      out(plainTable(matches, st));\n      throw new CliError(`${matches.length} sessions match \"${args.name}\"`, `pai \"<a longer part of the name>\"`);\n    } else {\n      picked = (await runPicker({ ...pickerOpts(matches, st, undefined), onCd: (row) => cdDir = row.dir }, st, term.input, term.output)) ?? undefined;\n      if (!picked) return cdDir !== undefined ? finishCd(cdDir, term) : CANCELLED;\n    }\n  }\n\n  return openRow(picked, workers, args.with, args.dryRun, term);\n}\n\nexport const rootCommand: CommandDef = {\n  name: \"pai\",\n  summary: \"Open a session by name; shows how it will start\",\n  description:\n    \"`pai` shows your sessions, newest first; type to filter, Enter opens one.\\n\" +\n    \"`pai NAME` opens a session by name (an exact name wins; part of a name or an id also works).\",\n  args: [{ name: \"NAME\", summary: \"Session name, part of it, or its id\" }],\n  options: [\n    { name: \"with\", type: \"string\", value: \"SETUP\", summary: \"Scripting: start with this setup, no questions (see: pai setups)\" },\n    { ...OPT.dryRun },\n  ],\n  examples: [\"pai\", 'pai \"20 - Webseiten\"', 'pai webseiten --with \"glm only\"', 'pai --with \"anthropic only\"'],\n  seeAlso: [\"pai setups\", \"pai ls\", \"pai more\"],\n  run: (ctx) => runRoot({ name: ctx.args.NAME as string | undefined, with: ctx.opts.with as string | undefined, dryRun: ctx.dryRun }),\n};\n","/**\n * Argument parsing driven by CommandDef — no second description of the flags.\n * Errors are UsageErrors: they say what is wrong, and the dispatcher adds the\n * Usage line and a pointer to help (exit code 2).\n */\n\nimport type { CommandDef, OptDef, RunContext } from \"./define.js\";\nimport { suggest } from \"./help.js\";\n\nexport class UsageError extends Error {\n  constructor(message: string, readonly hint?: string) {\n    super(message);\n  }\n}\n\nconst GLOBAL_FLAGS: OptDef[] = [\n  { name: \"dry-run\", type: \"boolean\", summary: \"\" },\n  { name: \"help\", short: \"h\", type: \"boolean\", summary: \"\" },\n];\n\nexport interface Parsed extends RunContext {\n  help: boolean;\n}\n\nexport function parseArgs(cmd: CommandDef, argv: string[]): Parsed {\n  const defs = [...(cmd.options ?? []), ...GLOBAL_FLAGS.filter((g) => !(cmd.options ?? []).some((o) => o.name === g.name))];\n  const byLong = new Map(defs.map((o) => [o.name, o]));\n  const byShort = new Map(defs.filter((o) => o.short).map((o) => [o.short!, o]));\n  const opts: RunContext[\"opts\"] = {};\n  const positional: string[] = [];\n  const rest: string[] = [];\n  const multi: Record<string, string[]> = {};\n  const loose = cmd.allowUnknown === true;\n  let seenUnknown = false;\n\n  for (let i = 0; i < argv.length; i++) {\n    const tok = argv[i];\n    if (tok === \"--\") {\n      if (loose && seenUnknown) rest.push(\"--\");\n      rest.push(...argv.slice(i + 1));\n      break;\n    }\n    if (tok.startsWith(\"-\") && tok.length > 1 && !/^-\\d/.test(tok)) {\n      let def: OptDef | undefined;\n      let inline: string | undefined;\n      if (tok.startsWith(\"--\")) {\n        const eq = tok.indexOf(\"=\");\n        const name = eq < 0 ? tok.slice(2) : tok.slice(2, eq);\n        if (eq >= 0) inline = tok.slice(eq + 1);\n        def = byLong.get(name);\n        if (!def && loose) {\n          seenUnknown = true;\n          rest.push(tok);\n          continue;\n        }\n        if (!def) {\n          const near = suggest(name, [...byLong.keys()]);\n          throw new UsageError(\n            `unknown option --${name}${near ? ` — did you mean --${near}?` : \"\"}`,\n            \"pai help \" + \"<command> lists the options of a command\"\n          );\n        }\n      } else {\n        def = byShort.get(tok[1]);\n        if (!def && loose) {\n          seenUnknown = true;\n          rest.push(tok);\n          continue;\n        }\n        if (!def) throw new UsageError(`unknown option ${tok.slice(0, 2)}`);\n        if (tok.length > 2) inline = tok.slice(2);\n      }\n      if (def.type === \"boolean\") {\n        opts[def.name] = inline === undefined ? true : inline !== \"false\";\n        continue;\n      }\n      if (def.optionalValue && inline === undefined) {\n        const next = argv[i + 1];\n        if (next !== undefined && !next.startsWith(\"-\")) opts[def.name] = def.type === \"number\" ? Number(argv[++i]) : argv[++i];\n        else opts[def.name] = true;\n        continue;\n      }\n      const value = inline ?? argv[++i];\n      if (value === undefined || (inline === undefined && value.startsWith(\"--\"))) {\n        throw new UsageError(`option --${def.name} needs a value (${def.value ?? def.type.toUpperCase()})`);\n      }\n      if (def.type === \"number\") {\n        const n = Number(value);\n        if (!Number.isFinite(n)) throw new UsageError(`option --${def.name} needs a number, got \"${value}\"`);\n        opts[def.name] = n;\n      } else {\n        opts[def.name] = value;\n        if (def.repeatable) (multi[def.name] ??= []).push(value);\n      }\n      continue;\n    }\n    if (loose) rest.push(tok);\n    else positional.push(tok);\n  }\n\n  for (const o of defs) if (opts[o.name] === undefined && o.default !== undefined) opts[o.name] = o.default;\n\n  const args: RunContext[\"args\"] = {};\n  const argDefs = cmd.args ?? [];\n  let p = 0;\n  for (const a of argDefs) {\n    if (a.variadic) {\n      args[a.name] = positional.slice(p);\n      p = positional.length;\n    } else {\n      args[a.name] = positional[p++];\n    }\n    const v = args[a.name];\n    if (a.required && (v === undefined || (Array.isArray(v) && v.length === 0)) && !opts.help) {\n      throw new UsageError(`missing required argument ${a.name} — ${a.summary.replace(/\\.$/, \"\")}`);\n    }\n  }\n  if (p < positional.length && !opts.help) {\n    throw new UsageError(`unexpected argument \"${positional[p]}\"`);\n  }\n  if (rest.length && !cmd.passthrough && !loose) throw new UsageError(`unexpected arguments after --: ${rest.join(\" \")}`);\n\n  return { args, opts, multi, dryRun: opts[\"dry-run\"] === true, rest, help: opts.help === true };\n}\n","/**\n * Dispatcher: argv -> command -> run. Owns exit codes (0 ok, 1 failed, 2 bad\n * usage) and the wording of every usage error, so no command prints a stack\n * trace or a bare code.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { resolveFromModule } from \"../module-paths.js\";\nimport type { CommandDef, GroupDef } from \"./define.js\";\nimport { CliError } from \"./define.js\";\nimport { colorEnabled, makeStyle, renderCommandHelp, renderOverview, suggest, usageLine, type Style } from \"./help.js\";\nimport { parseArgs, UsageError } from \"./parse.js\";\n\nexport function cliVersion(): string {\n  try {\n    const pkg = JSON.parse(readFileSync(resolveFromModule(import.meta.url, \"package.json\"), \"utf8\")) as { version?: string };\n    return pkg.version ?? \"0.0.0\";\n  } catch {\n    return \"0.0.0\";\n  }\n}\n\nexport interface Io {\n  out(s: string): void;\n  err(s: string): void;\n  style: Style;\n}\n\nexport function defaultIo(): Io {\n  return {\n    out: (s) => process.stdout.write(s.endsWith(\"\\n\") ? s : s + \"\\n\"),\n    err: (s) => process.stderr.write(s.endsWith(\"\\n\") ? s : s + \"\\n\"),\n    style: makeStyle(colorEnabled()),\n  };\n}\n\nconst find = (cmds: CommandDef[], name: string) => cmds.find((c) => c.name === name);\n\nexport interface Resolved {\n  path: string[];\n  cmd: CommandDef;\n  rest: string[];\n}\n\n/** Walk argv down the command tree. Throws UsageError for an unknown command. */\nexport function resolve(groups: GroupDef[], argv: string[]): Resolved {\n  const top = groups.flatMap((g) => g.commands);\n  const first = argv[0];\n  const cmd = find(top, first);\n  if (!cmd) {\n    const near = suggest(first, top.map((c) => c.name));\n    throw new UsageError(`unknown command \"${first}\"${near ? ` — did you mean \"${near}\"?` : \"\"}`);\n  }\n  const path = [cmd.name];\n  let cur = cmd;\n  let rest = argv.slice(1);\n  while (cur.subcommands?.length && rest[0] !== undefined && !rest[0].startsWith(\"-\")) {\n    const sub = find(cur.subcommands, rest[0]);\n    if (!sub) {\n      if (cur.run) break; // `pai providers NAME`-style: the word is an argument\n      const near = suggest(rest[0], cur.subcommands.map((s) => s.name));\n      throw new UsageError(\n        `unknown command \"${path.join(\" \")} ${rest[0]}\"${near ? ` — did you mean \"${path.join(\" \")} ${near}\"?` : \"\"}`,\n        `pai help ${path.join(\" \")}`\n      );\n    }\n    path.push(sub.name);\n    cur = sub;\n    rest = rest.slice(1);\n  }\n  return { path, cmd: cur, rest };\n}\n\n/** Run the CLI. Returns the process exit code. */\nexport async function runCli(groups: GroupDef[], argv: string[], io: Io = defaultIo(), root?: CommandDef): Promise<number> {\n  const st = io.style;\n  const overview = () =>\n    renderOverview(groups, root ?? { name: \"pai\", summary: \"Open a session by name\", description: \"\", examples: [] }, cliVersion(), st);\n  if (argv.length === 0 && !root) {\n    io.out(overview());\n    return 0;\n  }\n  if (argv[0] === \"--version\" || argv[0] === \"-V\") {\n    io.out(cliVersion());\n    return 0;\n  }\n  if (argv[0] === \"--help\" || argv[0] === \"-h\") {\n    io.out(overview());\n    return 0;\n  }\n  // Not a command word: it is a session name (or --with ...) for the daily path.\n  const isCommand = argv.length > 0 && !argv[0].startsWith(\"-\") && groups.some((g) => g.commands.some((c) => c.name === argv[0]));\n  const target = !isCommand && root ? root : undefined;\n\n  let resolved: Resolved | undefined;\n  try {\n    resolved = target ? { path: [], cmd: target, rest: argv } : resolve(groups, argv);\n    const { cmd, path, rest } = resolved;\n    const parsed = parseArgs(cmd, rest);\n    const noArgs = rest.length === 0;\n    if (parsed.help || (noArgs && !cmd.run && cmd.subcommands?.length)) {\n      if (target) {\n        io.out(overview());\n        return 0;\n      }\n      io.out(renderCommandHelp(path, cmd, st));\n      return 0;\n    }\n    if (!cmd.run) {\n      io.err(st.red(`pai ${path.join(\" \")} has nothing to run.`));\n      io.err(\"See: pai help more\");\n      return 1;\n    }\n    return (await cmd.run(parsed)) ?? 0;\n  } catch (e) {\n    if (e instanceof UsageError) {\n      io.err(st.red(`pai: ${e.message}`));\n      if (resolved) {\n        io.err(`${st.bold(\"Usage\")}  ${usageLine(resolved.path, resolved.cmd)}`);\n        io.err(st.dim(`More: pai help ${resolved.path.join(\" \")}`));\n      } else {\n        io.err(st.dim(e.hint ? `More: ${e.hint}` : \"More: pai help   (lists every command)\"));\n      }\n      return 2;\n    }\n    if (e instanceof CliError) {\n      io.err(st.red(`pai: ${e.message}`));\n      if (e.hint) io.err(`Next: ${st.bold(e.hint)}`);\n      return e.exitCode;\n    }\n    io.err(st.red(`pai: unexpected failure: ${e instanceof Error ? e.message : String(e)}`));\n    io.err(\"Next: re-run with PAI_DEBUG=1 for the stack trace, or report it.\");\n    if (process.env.PAI_DEBUG && e instanceof Error) io.err(e.stack ?? \"\");\n    return 1;\n  }\n}\n\nexport { renderCommandHelp };\n","/**\n * Per-area command pages for docs/commands (what scripts/build-docs.mjs\n * publishes): one page per top-level command plus an index, generated from the\n * same definitions `pai help` reads. Hand-written examples are spliced in from\n * `<examplesDir>/<area>.md` when present.\n */\n\nimport { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { CommandDef, GroupDef } from \"./define.js\";\nimport { mdCommand } from \"./help.js\";\n\nexport const GEN_HEADER = \"<!-- GENERATED by `pai doc --dir` from the command definitions (src/cli). Do not edit by hand. -->\";\n\nconst esc = (s: string) => s.replace(/\\|/g, \"\\\\|\").replace(/\\r?\\n/g, \" \").trim();\n\nexport interface Page {\n  name: string;\n  markdown: string;\n}\n\nexport function renderAreaPage(cmd: CommandDef, allAreas: string[], examples: string): string {\n  // mdCommand starts with a `## pai <name>` heading; the page title replaces it.\n  const body = mdCommand([cmd.name], cmd, 2).slice(1);\n  const out = [GEN_HEADER, \"\", `# pai ${cmd.name}`, \"\", ...body];\n  if (cmd.aliasOf === undefined && cmd.subcommands?.length) {\n    out.push(\"## Subcommands\", \"\", \"| Command | Description |\", \"|---|---|\");\n    for (const s of cmd.subcommands) out.push(`| \\`pai ${cmd.name} ${s.name}\\` | ${esc(s.summary)} |`);\n    out.push(\"\");\n  }\n  if (examples) out.push(\"## Examples\", \"\", examples, \"\");\n  out.push(\"## See also\", \"\", allAreas.filter((a) => a !== cmd.name).map((a) => `[\\`pai ${a}\\`](${a}.md)`).join(\" · \"), \"\", \"Run `pai help <command>` to read any of these in the terminal.\", \"\");\n  return out.join(\"\\n\").replace(/\\n{3,}/g, \"\\n\\n\");\n}\n\nexport function renderIndex(areas: CommandDef[]): string {\n  const out = [GEN_HEADER, \"\", \"# PAI Command Reference\", \"\", \"Generated reference for every `pai` command. Read any page in the terminal with `pai help <command>`, or browse the files here.\", \"\"];\n  out.push(\"## Areas\", \"\", \"| Area | Description |\", \"|---|---|\");\n  for (const a of areas) out.push(`| [\\`pai ${a.name}\\`](${a.name}.md) | ${esc(a.summary)} |`);\n  out.push(\"\", \"## All commands\", \"\", \"| Command | Description |\", \"|---|---|\");\n  const flat: Array<[string, string]> = [];\n  const walk = (c: CommandDef, prefix: string) => {\n    flat.push([prefix, c.summary]);\n    for (const s of c.subcommands ?? []) walk(s, `${prefix} ${s.name}`);\n  };\n  for (const a of areas) walk(a, `pai ${a.name}`);\n  flat.sort((x, y) => x[0].localeCompare(y[0]));\n  for (const [c, d] of flat) out.push(`| \\`${esc(c)}\\` | ${esc(d)} |`);\n  out.push(\"\");\n  return out.join(\"\\n\");\n}\n\n/** Every top-level command, once, sorted by name (a shortcut that repeats a name is listed once). */\nexport function areasOf(groups: GroupDef[]): CommandDef[] {\n  const seen = new Map<string, CommandDef>();\n  for (const c of groups.flatMap((g) => g.commands)) if (!seen.has(c.name)) seen.set(c.name, c);\n  return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name));\n}\n\nexport function renderPages(groups: GroupDef[], examplesDir?: string): Page[] {\n  const areas = areasOf(groups);\n  const names = areas.map((a) => a.name);\n  const pages = areas.map((a) => {\n    const f = examplesDir ? join(examplesDir, `${a.name}.md`) : \"\";\n    const ex = f && existsSync(f) ? readFileSync(f, \"utf8\").trim() : \"\";\n    return { name: `${a.name}.md`, markdown: renderAreaPage(a, names, ex) + \"\\n\" };\n  });\n  pages.push({ name: \"README.md\", markdown: renderIndex(areas) + \"\\n\" });\n  return pages;\n}\n\n/** Replace the generated pages in `dir` (hand-written subfolders such as _examples are kept). */\nexport function writePages(dir: string, pages: Page[]): void {\n  mkdirSync(dir, { recursive: true });\n  for (const f of readdirSync(dir)) if (f.endsWith(\".md\")) rmSync(join(dir, f));\n  for (const p of pages) writeFileSync(join(dir, p.name), p.markdown);\n}\n","/** `pai help` and `pai doc`. */\n\nimport { writeFileSync } from \"node:fs\";\nimport { CliError, OPT, type CommandDef } from \"../define.js\";\nimport { colorEnabled, makeStyle, renderCommandHelp, renderMarkdown, renderMore, renderOverview, suggest } from \"../help.js\";\nimport { GROUPS } from \"../registry.js\";\nimport { rootCommand } from \"./root.js\";\nimport { cliVersion, resolve } from \"../run.js\";\nimport { UsageError } from \"../parse.js\";\nimport { renderPages, writePages } from \"../docgen.js\";\n\nexport const helpCommand: CommandDef = {\n  name: \"help\",\n  summary: \"Explain a command (same as `pai <command> --help`)\",\n  description: \"Without arguments shows the overview. `pai help more` lists every command that is not a daily one.\",\n  args: [{ name: \"COMMAND\", variadic: true, summary: \"Command and optional subcommand, e.g. `session open`\" }],\n  examples: [\"pai help\", \"pai help open\", \"pai help providers use\", \"pai help more\"],\n  seeAlso: [\"pai doc\"],\n  run(ctx) {\n    const st = makeStyle(colorEnabled());\n    const words = (ctx.args.COMMAND as string[]) ?? [];\n    if (words.length === 0) return void process.stdout.write(renderOverview(GROUPS, rootCommand, cliVersion(), st) + \"\\n\");\n    if (words[0] === \"more\") return void process.stdout.write(renderMore(GROUPS, st) + \"\\n\");\n    const r = resolve(GROUPS, words);\n    if (r.rest.length) {\n      const near = suggest(r.rest[0], r.cmd.subcommands?.map((s) => s.name) ?? []);\n      throw new UsageError(`no command \"${[...r.path, r.rest[0]].join(\" \")}\"${near ? ` — did you mean \"${[...r.path, near].join(\" \")}\"?` : \"\"}`);\n    }\n    process.stdout.write(renderCommandHelp(r.path, r.cmd, st));\n  },\n};\n\nexport const docCommand: CommandDef = {\n  name: \"doc\",\n  summary: \"Write the Markdown command reference\",\n  description:\n    \"Regenerates the reference from the same definitions `pai help` reads, so the docs cannot drift from the commands. Prints to stdout unless --out is given.\",\n  options: [\n    { name: \"out\", type: \"string\", value: \"FILE\", summary: \"Write to this file instead of stdout\" },\n    { name: \"dir\", type: \"string\", value: \"DIR\", summary: \"Write one page per command plus README.md into this folder (used by the docs build)\" },\n    { name: \"examples\", type: \"string\", value: \"DIR\", summary: \"With --dir: splice hand-written <command>.md examples from this folder\" },\n    { ...OPT.dryRun, summary: \"Show where it would write, write nothing\" },\n  ],\n  examples: [\"pai doc\", \"pai doc --out docs/cli.md\", \"pai doc --dir docs/commands --examples docs/commands/_examples\"],\n  seeAlso: [\"pai help\"],\n  run(ctx) {\n    const dir = ctx.opts.dir as string | undefined;\n    if (dir) {\n      const pages = renderPages(GROUPS, ctx.opts.examples as string | undefined);\n      if (ctx.dryRun) return void process.stdout.write(`would write ${pages.length} pages to ${dir}\\n`);\n      try {\n        writePages(dir, pages);\n      } catch (e) {\n        throw new CliError(`cannot write ${dir}: ${(e as Error).message}`, \"pai doc --dir <writable folder>\");\n      }\n      return void process.stdout.write(`Wrote ${pages.length} pages to ${dir}\\n`);\n    }\n    const md = renderMarkdown(GROUPS, cliVersion());\n    const file = ctx.opts.out as string | undefined;\n    if (!file) return void process.stdout.write(md + \"\\n\");\n    if (ctx.dryRun) return void process.stdout.write(`would write ${md.length} bytes to ${file}\\n`);\n    try {\n      writeFileSync(file, md + \"\\n\");\n    } catch (e) {\n      throw new CliError(`cannot write ${file}: ${(e as Error).message}`, \"pai doc --out <writable file>\");\n    }\n    process.stdout.write(`Wrote ${file}\\n`);\n  },\n};\n","/**\n * agents.ts — run agent definitions (~/.claude/agents/<name>.md) as workers.\n *\n * The file format is the Claude Code agent one: YAML front matter (`model`,\n * `tools`, `description`) followed by a Markdown body that is the agent's\n * system prompt. `pai worker run --agent <name>` loads it and maps it onto\n * the worker runner:\n *\n *   - body    → --append-system-prompt (prepended, so an explicit caller\n *               flag still wins);\n *   - tools   → --allowedTools;\n *   - model   → a task class via its tier (haiku→simple, sonnet→implement,\n *               opus→complex). Non-Anthropic ids map through the provider\n *               registry (models.fast → haiku tier, models.default → sonnet\n *               tier, a provider's modelTiers override if set), unless\n *               --class is given.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type { ModelTier, WorkerProvider } from \"./config.js\";\n\nexport type { ModelTier } from \"./config.js\";\n\nimport { UNLABELED } from \"./status.js\";\n\nexport interface AgentDef {\n  name: string;\n  /** Front-matter `model`, when present (any string; mapped via modelToClass). */\n  model?: string;\n  /** Front-matter `tools`, comma-separated. */\n  tools?: string[];\n  /** Front-matter `description`. */\n  description?: string;\n  /** Everything after the front matter — the agent's system prompt. */\n  body: string;\n  /** The file the definition was read from. */\n  path: string;\n}\n\n/** Directory the agent library lives in. */\nexport function agentsDir(): string {\n  return join(homedir(), \".claude\", \"agents\");\n}\n\nexport function agentPath(name: string): string {\n  return join(agentsDir(), `${name}.md`);\n}\n\n/** `key: value` (bare) or `key: [a, b]` / `key:\\n  - a\\n  - b` (list). */\nfunction parseFrontMatter(text: string): Record<string, string | string[]> {\n  const out: Record<string, string | string[]> = {};\n  let key = \"\";\n  for (const raw of text.split(\"\\n\")) {\n    const listItem = /^\\s+-\\s+(.*)$/.exec(raw);\n    if (listItem && key) {\n      const cur = out[key];\n      const item = listItem[1].trim().replace(/^[\"']|[\"']$/g, \"\");\n      out[key] = Array.isArray(cur) ? [...cur, item] : [item];\n      continue;\n    }\n    const kv = /^([A-Za-z0-9_-]+):\\s*(.*)$/.exec(raw);\n    if (!kv) continue;\n    key = kv[1];\n    const val = kv[2].trim();\n    if (!val) {\n      out[key] = [];\n    } else if (val.startsWith(\"[\") && val.endsWith(\"]\")) {\n      out[key] = val\n        .slice(1, -1)\n        .split(\",\")\n        .map((s) => s.trim().replace(/^[\"']|[\"']$/g, \"\"))\n        .filter(Boolean);\n    } else {\n      out[key] = val.replace(/^[\"']|[\"']$/g, \"\");\n    }\n  }\n  return out;\n}\n\n/** Parse an agent file's text (front matter + body). Throws on empty body. */\nexport function parseAgentFile(name: string, text: string, path: string): AgentDef {\n  const m = /^---\\n([\\s\\S]*?)\\n---\\n?([\\s\\S]*)$/.exec(text);\n  if (!m) {\n    throw new Error(\n      `${path}: expected YAML front matter (--- … ---) before the agent body`\n    );\n  }\n  const fm = parseFrontMatter(m[1]);\n  const body = m[2].trim();\n  if (!body) throw new Error(`${path}: the agent body (after the front matter) is empty`);\n  const def: AgentDef = { name, body, path };\n  if (typeof fm.model === \"string\" && fm.model) def.model = fm.model;\n  if (Array.isArray(fm.tools) && fm.tools.length) def.tools = fm.tools;\n  if (typeof fm.description === \"string\" && fm.description) def.description = fm.description;\n  return def;\n}\n\n/** Load ~/.claude/agents/<name>.md; the error names the library when missing. */\nexport function loadAgent(name: string): AgentDef {\n  const path = agentPath(name);\n  let text: string;\n  try {\n    text = readFileSync(path, \"utf8\");\n  } catch {\n    throw new Error(\n      `no agent named \"${name}\" at ${path}. ` +\n        `The agent library lives in ~/.claude/agents/<name>.md and runs on workers.`\n    );\n  }\n  return parseAgentFile(name, text, path);\n}\n\n/** Tier → task class: the cheap tier runs as simple, the middle as\n *  implement, the top as complex. */\nconst TIER_CLASS: Record<ModelTier, string> = {\n  haiku: \"simple\",\n  sonnet: \"implement\",\n  opus: \"complex\",\n};\n\n/** Which tier a model id belongs to: one of the CLI's tier aliases, or an\n *  exact match against a configured provider's models (a modelTiers override\n *  first, then fast → haiku tier, default → sonnet tier). null = no match. */\nexport function modelTier(\n  model: string,\n  providers?: Record<string, WorkerProvider>\n): ModelTier | null {\n  const m = model.toLowerCase();\n  if (m.includes(\"haiku\")) return \"haiku\";\n  if (m.includes(\"sonnet\")) return \"sonnet\";\n  if (m.includes(\"opus\")) return \"opus\";\n  if (providers) {\n    for (const p of Object.values(providers)) {\n      const override = p.modelTiers?.[model];\n      if (override) return override;\n      if (p.models.fast === model) return \"haiku\";\n      if (p.models.default === model) return \"sonnet\";\n    }\n  }\n  return null;\n}\n\n/** Model ids already warned about — one line per id, not one per run. */\nconst unmatchedTierLogged = new Set<string>();\n\n/** Map an agent's front-matter model to a worker class. An id that matches\n *  no tier is logged once and falls back to the middle tier's class rather\n *  than dropping the hint silently. */\nexport function modelToClass(\n  model: string | undefined,\n  providers?: Record<string, WorkerProvider>\n): string | undefined {\n  if (!model) return undefined;\n  const tier = modelTier(model, providers);\n  if (tier) return TIER_CLASS[tier];\n  if (!unmatchedTierLogged.has(model)) {\n    unmatchedTierLogged.add(model);\n    console.error(\n      `[agents] model \"${model}\" matches no known tier — ` +\n        `falling back to the ${TIER_CLASS.sonnet} class.`\n    );\n  }\n  return TIER_CLASS.sonnet;\n}\n\n/** Extra claude args the definition contributes (before the caller's own). */\nexport function agentClaudeArgs(def: AgentDef): string[] {\n  const args: string[] = [];\n  if (def.tools?.length) args.push(\"--allowedTools\", def.tools.join(\",\"));\n  args.push(\"--append-system-prompt\", def.body);\n  return args;\n}\n\n/** `<agent>: <first 50 chars of prompt>` — the default label for a run. */\nexport function agentLabel(name: string, prompt: string | null | undefined): string {\n  return `${name}: ${prompt ?? UNLABELED}`.slice(0, name.length + 2 + 50);\n}\n\n/**\n * Default `--label` for a bare `pai worker run` (no --chain/--agent, which\n * derive their own): the prompt's first non-blank line, whitespace-collapsed,\n * stripped of a leading markdown marker (#, *, -, >), truncated to 48 chars\n * with \"…\" appended if it was cut. An empty/whitespace-only prompt falls back\n * to \"worker <class>\" so the row is never blank.\n */\nexport function deriveLabel(prompt: string | null | undefined, klass: string): string {\n  const firstLine = (prompt ?? \"\")\n    .split(\"\\n\")\n    .map((l) => l.trim())\n    .find((l) => l.length > 0);\n  if (!firstLine) return `worker ${klass}`;\n  const stripped = firstLine.replace(/^[#*\\->]+\\s*/, \"\").replace(/\\s+/g, \" \").trim();\n  if (!stripped) return `worker ${klass}`;\n  return stripped.length > 48 ? `${stripped.slice(0, 48)}…` : stripped;\n}\n","/**\n * usage.ts — exact token tallies per model from stream events / transcripts.\n *\n * One summing function (`sumUsage`) serves `pai worker usage` (the workers of\n * an A/B arm) and `pai audit tokens usage` (the top session transcript). A\n * message id shows up in several stream events with growing usage; only the\n * last one counts.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { eventsPath } from \"./paths.js\";\nimport { loadStatuses, type WorkerStatus } from \"./status.js\";\nimport { isLocalProvider } from \"./policy.js\";\nimport type { UsageBlock } from \"../utils/input-side-tokens.js\";\n\nexport interface CacheCreationSplit {\n  ephemeral_5m_input_tokens?: number;\n  ephemeral_1h_input_tokens?: number;\n}\n\nexport type RichUsage = UsageBlock & { cache_creation?: CacheCreationSplit };\n\nexport interface Tally {\n  input: number;\n  cacheRead: number;\n  cacheWrite5m: number;\n  cacheWrite1h: number;\n  output: number;\n}\n\nexport const emptyTally = (): Tally => ({ input: 0, cacheRead: 0, cacheWrite5m: 0, cacheWrite1h: 0, output: 0 });\n\nexport function addTally(into: Tally, t: Tally): void {\n  into.input += t.input;\n  into.cacheRead += t.cacheRead;\n  into.cacheWrite5m += t.cacheWrite5m;\n  into.cacheWrite1h += t.cacheWrite1h;\n  into.output += t.output;\n}\n\n/** One usage block as a tally; a cache write without a 5m/1h split counts as 5m. */\nexport function tallyOf(u: RichUsage): Tally {\n  const split5 = u.cache_creation?.ephemeral_5m_input_tokens;\n  const split1 = u.cache_creation?.ephemeral_1h_input_tokens;\n  const hasSplit = split5 !== undefined || split1 !== undefined;\n  return {\n    input: u.input_tokens ?? 0,\n    cacheRead: u.cache_read_input_tokens ?? 0,\n    cacheWrite5m: hasSplit ? (split5 ?? 0) : (u.cache_creation_input_tokens ?? 0),\n    cacheWrite1h: hasSplit ? (split1 ?? 0) : 0,\n    output: u.output_tokens ?? 0,\n  };\n}\n\nexport interface SumResult {\n  byModel: Map<string, Tally>;\n  /** total_cost_usd of the last result event, when there was one. */\n  costUsd: number | null;\n}\n\n/** Sum assistant usage of JSONL text: each message id once (its last usage), grouped by model. */\nexport function sumUsage(jsonl: string): SumResult {\n  const byId = new Map<string, { model: string; usage: RichUsage }>();\n  let anon = 0;\n  let costUsd: number | null = null;\n  for (const line of jsonl.split(\"\\n\")) {\n    if (!line.trim()) continue;\n    let e: { type?: string; total_cost_usd?: number; message?: { id?: string; model?: string; usage?: RichUsage } };\n    try {\n      e = JSON.parse(line);\n    } catch {\n      continue;\n    }\n    if (e.type === \"result\" && typeof e.total_cost_usd === \"number\") costUsd = e.total_cost_usd;\n    if (e.type !== \"assistant\" || !e.message?.usage) continue;\n    const m = e.message;\n    byId.set(m.id ?? `anon-${anon++}`, { model: m.model ?? \"?\", usage: m.usage! });\n  }\n  const byModel = new Map<string, Tally>();\n  for (const { model, usage } of byId.values()) {\n    const t = byModel.get(model) ?? emptyTally();\n    addTally(t, tallyOf(usage));\n    byModel.set(model, t);\n  }\n  return { byModel, costUsd };\n}\n\nexport interface UsageRow {\n  provider: string;\n  model: string;\n  local: boolean;\n  workers: number;\n  tally: Tally;\n  /** Summed total_cost_usd; null for local rows. */\n  costUsd: number | null;\n}\n\nexport interface UsageReport {\n  rows: UsageRow[];\n  cloud: UsageRow;\n  local: UsageRow;\n}\n\nconst totalRow = (provider: string, local: boolean): UsageRow => ({\n  provider,\n  model: \"\",\n  local,\n  workers: 0,\n  tally: emptyTally(),\n  costUsd: local ? null : 0,\n});\n\nexport function finishReport(rows: UsageRow[]): UsageReport {\n  const cloud = totalRow(\"TOTAL cloud\", false);\n  const local = totalRow(\"TOTAL local\", true);\n  for (const r of rows) {\n    const t = r.local ? local : cloud;\n    t.workers += r.workers;\n    addTally(t.tally, r.tally);\n    if (!r.local) t.costUsd = (t.costUsd ?? 0) + (r.costUsd ?? 0);\n  }\n  return { rows, cloud, local };\n}\n\nexport interface WorkerSelect {\n  labelPrefix?: string;\n  since?: Date;\n  until?: Date;\n}\n\n/** \"1h\", \"30m\", \"2d\" relative to now, or an ISO timestamp. */\nexport function parseWhen(s: string, now = new Date()): Date {\n  const rel = /^(\\d+)([smhd])$/.exec(s.trim());\n  if (rel) {\n    const mult = { s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000 }[rel[2] as \"s\" | \"m\" | \"h\" | \"d\"];\n    return new Date(now.getTime() - Number(rel[1]) * mult);\n  }\n  const d = new Date(s);\n  if (Number.isNaN(d.getTime())) throw new Error(`cannot read \"${s}\" as a time; use an ISO timestamp or 1h / 30m`);\n  return d;\n}\n\n/** Status `started` is local \"YYYY-MM-DD HH:MM:SS\" (or ISO). */\nfunction startedAt(s: WorkerStatus): Date | null {\n  const d = new Date(s.started.includes(\"T\") ? s.started : s.started.replace(\" \", \"T\"));\n  return Number.isNaN(d.getTime()) ? null : d;\n}\n\nexport function selectWorkers(statuses: WorkerStatus[], sel: WorkerSelect): WorkerStatus[] {\n  return statuses.filter((s) => {\n    if (sel.labelPrefix && !(s.label ?? \"\").startsWith(sel.labelPrefix)) return false;\n    const at = startedAt(s);\n    if (sel.since && (!at || at < sel.since)) return false;\n    if (sel.until && (!at || at > sel.until)) return false;\n    return true;\n  });\n}\n\n/** Tally the selected workers; `baseUrls` maps provider name to its baseUrl (for the local mark). */\nexport function workerUsage(logDir: string, sel: WorkerSelect, baseUrls: Record<string, string>): UsageReport {\n  const groups = new Map<string, UsageRow>();\n  for (const st of selectWorkers(loadStatuses(logDir), sel)) {\n    const path = eventsPath(logDir, st.id);\n    if (!existsSync(path)) continue;\n    const { byModel, costUsd } = sumUsage(readFileSync(path, \"utf8\"));\n    if (byModel.size === 0) continue;\n    const local = isLocalProvider(baseUrls[st.provider] ?? \"\");\n    const models = [...byModel.keys()];\n    // the run's cost is one number; book it on the run's own model, else the last one seen\n    const costModel = models.includes(st.model) ? st.model : models[models.length - 1];\n    for (const [model, tally] of byModel) {\n      const key = `${st.provider}\\u0000${model}`;\n      const row = groups.get(key) ?? { provider: st.provider, model, local, workers: 0, tally: emptyTally(), costUsd: local ? null : 0 };\n      row.workers += 1;\n      addTally(row.tally, tally);\n      if (!local && model === costModel) row.costUsd = (row.costUsd ?? 0) + (costUsd ?? 0);\n      groups.set(key, row);\n    }\n  }\n  return finishReport([...groups.values()].sort((a, b) => a.provider.localeCompare(b.provider) || a.model.localeCompare(b.model)));\n}\n\n// ---------------------------------------------------------------------------\n// Transcript side (tier 0): cost from a price table\n// ---------------------------------------------------------------------------\n\n/** USD per million tokens. Edit here when prices change; cache read 0.1x, write 5m 1.25x, write 1h 2x of input. */\nexport const PRICE_TABLE: Record<\"opus\" | \"sonnet\" | \"haiku\", { input: number; output: number }> = {\n  opus: { input: 5, output: 25 },\n  sonnet: { input: 3, output: 15 },\n  haiku: { input: 1, output: 5 },\n};\nexport const CACHE_READ_X = 0.1;\nexport const CACHE_WRITE_5M_X = 1.25;\nexport const CACHE_WRITE_1H_X = 2;\n\nexport function priceTableText(): string {\n  const lines = Object.entries(PRICE_TABLE).map(([f, p]) => `  ${f.padEnd(7)} input $${p.input}/M, output $${p.output}/M`);\n  return `Prices (USD per million tokens, matched on the model name):\\n${lines.join(\"\\n\")}\\n  cache read ${CACHE_READ_X}x input, cache write 5m ${CACHE_WRITE_5M_X}x, cache write 1h ${CACHE_WRITE_1H_X}x`;\n}\n\n/** Cost of a tally by model family, or null when the model matches no family. */\nexport function priceTally(model: string, t: Tally): number | null {\n  const fam = (Object.keys(PRICE_TABLE) as Array<keyof typeof PRICE_TABLE>).find((f) => model.toLowerCase().includes(f));\n  if (!fam) return null;\n  const p = PRICE_TABLE[fam];\n  const inp = p.input / 1e6;\n  return t.input * inp + t.output * (p.output / 1e6) + t.cacheRead * inp * CACHE_READ_X + t.cacheWrite5m * inp * CACHE_WRITE_5M_X + t.cacheWrite1h * inp * CACHE_WRITE_1H_X;\n}\n\nexport function transcriptUsage(path: string): UsageReport {\n  const { byModel } = sumUsage(readFileSync(path, \"utf8\"));\n  const rows: UsageRow[] = [...byModel.entries()]\n    .sort((a, b) => a[0].localeCompare(b[0]))\n    .map(([model, tally]) => ({ provider: \"transcript\", model, local: false, workers: 0, tally, costUsd: priceTally(model, tally) }));\n  const rep = finishReport(rows);\n  rep.cloud.provider = \"TOTAL\";\n  return rep;\n}\n\n// ---------------------------------------------------------------------------\n// Rendering\n// ---------------------------------------------------------------------------\n\nconst n = (x: number) => x.toLocaleString(\"en-US\");\nconst usd = (x: number | null) => (x === null ? \"—\" : x.toFixed(4));\n\nexport function renderUsage(rep: UsageReport, opts: { workers: boolean }): string {\n  const head = [\"PROVIDER\", \"MODEL\", ...(opts.workers ? [\"WORKERS\"] : []), \"INPUT\", \"CACHE READ\", \"CACHE WRITE 5m\", \"CACHE WRITE 1h\", \"OUTPUT\", \"COST USD\"];\n  const cells = (r: UsageRow) => [\n    r.provider + (r.local && !r.provider.startsWith(\"TOTAL\") ? \" (local)\" : \"\"),\n    r.model,\n    ...(opts.workers ? [String(r.workers)] : []),\n    n(r.tally.input),\n    n(r.tally.cacheRead),\n    n(r.tally.cacheWrite5m),\n    n(r.tally.cacheWrite1h),\n    n(r.tally.output),\n    r.local ? \"—\" : usd(r.costUsd),\n  ];\n  const body = [...rep.rows.map(cells)];\n  const totals = opts.workers ? [cells(rep.cloud), cells(rep.local)] : [cells(rep.cloud)];\n  const all = [head, ...body, ...totals];\n  const w = head.map((_, i) => Math.max(...all.map((r) => r[i].length)));\n  const textCols = 2;\n  const fmt = (r: string[]) => r.map((c, i) => (i < textCols ? c.padEnd(w[i]) : c.padStart(w[i]))).join(\"  \");\n  return [fmt(head), ...body.map(fmt), fmt(w.map((x) => \"-\".repeat(x))), ...totals.map(fmt)].join(\"\\n\");\n}\n\nexport function usageJson(rep: UsageReport): unknown {\n  const row = (r: UsageRow) => ({ provider: r.provider, model: r.model, local: r.local, workers: r.workers, ...r.tally, costUsd: r.costUsd });\n  return { rows: rep.rows.map(row), totalCloud: row(rep.cloud), totalLocal: row(rep.local) };\n}\n","/**\n * wait.ts — `pai worker wait <id...>`: the deterministic poller.\n *\n * A script, not guidance: polls each worker's status file until every one is\n * terminal (done/failed/killed/lost), then returns their result texts. The\n * orchestrator's Bash call stays synchronous and gets the JSON on stdout,\n * with exit 1 naming whoever failed or timed out.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { eventsPath } from \"./paths.js\";\nimport { parseWorkerReport, type WorkerReport } from \"./report.js\";\nimport { loadStatus, type WorkerStatus } from \"./status.js\";\n\nexport interface WaitResult {\n  id: string;\n  label: string;\n  state: WorkerStatus[\"state\"];\n  rc: number | null;\n  secs: number | null;\n  ok: boolean;\n  /** The `result` text of the last result event in the worker's transcript. */\n  result: string | null;\n  report: WorkerReport | null;\n}\n\nexport interface WaitOptions {\n  /** Give up after this long; default 15 min. */\n  timeoutMs?: number;\n  /** Poll interval; default 2 s. */\n  intervalMs?: number;\n  /** Return as soon as ANY named worker is terminal (default: wait for all). */\n  any?: boolean;\n}\n\nconst TERMINAL: readonly WorkerStatus[\"state\"][] = [\"done\", \"failed\", \"killed\", \"lost\"];\n\nfunction sleep(ms: number): Promise<void> {\n  return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * The `result` text of the last result event in the worker's `<id>.jsonl`,\n * scanning the file backwards; null when the file or the event is absent.\n */\nfunction lastResultText(logDir: string, id: string): string | null {\n  let raw: string;\n  try {\n    raw = readFileSync(eventsPath(logDir, id), \"utf8\");\n  } catch {\n    return null;\n  }\n  const lines = raw.split(\"\\n\");\n  for (let i = lines.length - 1; i >= 0; i--) {\n    const line = lines[i].trim();\n    if (!line.startsWith(\"{\")) continue;\n    try {\n      const e = JSON.parse(line) as { type?: string; result?: unknown };\n      if (e.type === \"result\" && typeof e.result === \"string\") return e.result;\n    } catch {\n      // a torn line is skipped, not fatal\n    }\n  }\n  return null;\n}\n\nfunction toWaitResult(logDir: string, st: WorkerStatus): WaitResult {\n  const result = lastResultText(logDir, st.id);\n  return {\n    id: st.id,\n    label: st.label,\n    state: st.state,\n    rc: st.rc,\n    secs: st.secs,\n    ok: st.state === \"done\" && (st.rc ?? 0) === 0,\n    result,\n    report: parseWorkerReport(result ?? \"\"),\n  };\n}\n\n/**\n * Wait until every named worker reaches a terminal state. Unknown ids throw\n * immediately. On timeout nothing is thrown: the return carries what is known\n * and `timedOut` names the still-pending ids.\n */\nexport async function waitWorkers(\n  logDir: string,\n  ids: string[],\n  opts: WaitOptions = {}\n): Promise<{ results: WaitResult[]; timedOut: string[] }> {\n  const timeoutMs = opts.timeoutMs ?? 900_000;\n  const intervalMs = opts.intervalMs ?? 2_000;\n  const known = new Map<string, WorkerStatus>();\n  const pending = new Set<string>();\n  // the pre-sleep check: already-finished workers return without waiting\n  for (const id of ids) {\n    const st = loadStatus(logDir, id);\n    if (!st) throw new Error(`no worker named \"${id}\"`);\n    known.set(id, st);\n    if (!TERMINAL.includes(st.state)) pending.add(id);\n  }\n  const deadline = Date.now() + timeoutMs;\n  const anyDone = () => opts.any === true && pending.size < ids.length;\n  while (pending.size > 0 && !anyDone() && Date.now() < deadline) {\n    await sleep(Math.min(intervalMs, Math.max(1, deadline - Date.now())));\n    for (const id of [...pending]) {\n      const st = loadStatus(logDir, id);\n      if (st) known.set(id, st); // a vanished file keeps the last known status\n      if (!st || TERMINAL.includes(known.get(id)!.state)) pending.delete(id);\n    }\n  }\n  return { results: ids.map((id) => toWaitResult(logDir, known.get(id)!)), timedOut: [...pending] };\n}\n\nexport const WAIT_MAX_DEFAULT_SECS = 100;\nexport const WAIT_MAX_CAP_SECS = 590;\n\n/**\n * The `pai worker wait` verb: one JSON status line per id. With --max (default\n * 100, limit 115) it returns as soon as ANY worker finishes or the max elapses:\n * exit 0 all done, 1 a finished worker failed, 3 some still running (call again\n * with the unfinished ids). The legacy --timeout waits for ALL and exits 1 on timeout.\n */\nexport async function runWait(\n  logDir: string,\n  ids: string[],\n  o: { max?: number; timeout?: number },\n  out: (line: string) => void,\n  errOut: (line: string) => void\n): Promise<number> {\n  const legacy = o.timeout !== undefined;\n  const { results, timedOut } = await waitWorkers(logDir, ids, {\n    timeoutMs: (legacy ? o.timeout! : Math.min(o.max ?? WAIT_MAX_DEFAULT_SECS, WAIT_MAX_CAP_SECS)) * 1000,\n    any: !legacy,\n  });\n  for (const r of results) out(JSON.stringify(r));\n  const failed = results.filter((r) => !timedOut.includes(r.id) && !r.ok).map((r) => r.id);\n  if (legacy && timedOut.length) {\n    errOut(`pai worker wait: timed out waiting for ${timedOut.join(\", \")}`);\n    return 1;\n  }\n  if (failed.length) {\n    errOut(`pai worker wait: failed: ${failed.join(\", \")}`);\n    return 1;\n  }\n  if (timedOut.length) {\n    errOut(`pai worker wait: still running: ${timedOut.join(\" \")}; call wait again with these ids`);\n    return 3;\n  }\n  return 0;\n}\n","/**\n * wait-probes.ts — bounded waits for a server to answer and for files to appear:\n * `pai worker wait-url <url>` and `pai worker wait-file <path...>`. Siblings of\n * wait-on (a pid) and wait (child workers); each returns within --max seconds\n * (default 100, limit 590) so the caller loops a few times instead of sleeping.\n *\n * Exit codes: 0 ready, 3 not yet at --max, 2 unusable arguments.\n */\n\nimport { existsSync } from \"node:fs\";\n\nexport const PROBE_DEFAULT_SECS = 100;\nexport const PROBE_CAP_SECS = 590;\n\nexport interface ProbeDeps {\n  now: () => number;\n  sleep: (ms: number) => Promise<void>;\n  out: (line: string) => void;\n  /** HTTP status of a GET, or an error word (\"connection refused\", \"timeout\") when nothing answered. */\n  get: (url: string, timeoutMs: number) => Promise<number | string>;\n  exists: (path: string) => boolean;\n}\n\nexport const realProbeDeps: ProbeDeps = {\n  now: () => Date.now(),\n  sleep: (ms) => new Promise((r) => setTimeout(r, ms)),\n  out: (l) => console.log(l),\n  async get(url, timeoutMs) {\n    try {\n      const res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });\n      void res.body?.cancel();\n      return res.status;\n    } catch (e) {\n      const err = e as { name?: string; cause?: { code?: string } };\n      if (err.name === \"TimeoutError\" || err.name === \"AbortError\") return \"timeout\";\n      return err.cause?.code === \"ECONNREFUSED\" ? \"connection refused\" : (err.cause?.code ?? \"no answer\");\n    }\n  },\n  exists: existsSync,\n};\n\nconst maxMsOf = (secs: number | undefined): number => Math.min(secs ?? PROBE_DEFAULT_SECS, PROBE_CAP_SECS) * 1000;\nconst secsSince = (d: ProbeDeps, t0: number): number => Math.round((d.now() - t0) / 1000);\n\n/** Up once the server answers with any 2xx-4xx status (a 404 or 401 still proves the port serves HTTP). */\nexport async function waitUrl(o: { url: string; maxSecs?: number }, d: ProbeDeps = realProbeDeps): Promise<number> {\n  try {\n    new URL(o.url);\n  } catch {\n    d.out(`wait-url: invalid url ${o.url}`);\n    return 2;\n  }\n  const maxMs = maxMsOf(o.maxSecs);\n  const t0 = d.now();\n  let delay = 500;\n  let last: number | string = \"no answer\";\n  for (;;) {\n    last = await d.get(o.url, 3000);\n    if (typeof last === \"number\" && last >= 200 && last < 500) {\n      d.out(`up: ${o.url} answered HTTP ${last} after ${secsSince(d, t0)}s`);\n      return 0;\n    }\n    const left = maxMs - (d.now() - t0);\n    if (left <= 0) break;\n    await d.sleep(Math.min(delay, left));\n    delay = Math.min(delay * 2, 5000);\n  }\n  d.out(`not yet: ${o.url} (last: ${typeof last === \"number\" ? `HTTP ${last}` : last}) after ${secsSince(d, t0)}s; call wait-url again`);\n  return 3;\n}\n\n/** Ready once every path exists; one status line per path. */\nexport async function waitFile(o: { paths: string[]; maxSecs?: number }, d: ProbeDeps = realProbeDeps): Promise<number> {\n  if (o.paths.length === 0) {\n    d.out(\"wait-file: no path given\");\n    return 2;\n  }\n  const maxMs = maxMsOf(o.maxSecs);\n  const t0 = d.now();\n  const missing = () => o.paths.filter((p) => !d.exists(p));\n  while (missing().length > 0 && d.now() - t0 < maxMs) {\n    await d.sleep(Math.min(1000, Math.max(1, maxMs - (d.now() - t0))));\n  }\n  const gone = new Set(missing());\n  for (const p of o.paths) d.out(gone.has(p) ? `missing: ${p}` : `exists: ${p}`);\n  if (gone.size > 0) {\n    d.out(`not yet: ${gone.size} of ${o.paths.length} missing after ${secsSince(d, t0)}s; call wait-file again`);\n    return 3;\n  }\n  return 0;\n}\n","/**\n * Workers: `pai worker <verb>` — run a task on another provider, watch it,\n * talk to it, merge what it did. Thin: every verb calls src/workers/*, the\n * same code the MCP tools and the Agent hook use. Names, flags, stdout and\n * exit codes are the ones hooks, prompts, skills and shims already call.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { getProviderOrNative, isModelCapability, readWorkersSection, WorkersConfigError, type ModelCapability, type WorkerProvider } from \"../../workers/config.js\";\nimport { eventsPath, ledgerPath, workersLogDir } from \"../../workers/paths.js\";\nimport { longInlinePromptHint, parseRunnerArgs } from \"../../workers/args.js\";\nimport { runWorker, type RunOptions } from \"../../workers/run.js\";\nimport { runChain } from \"../../workers/chain.js\";\nimport { realDeps as waitOnDeps, statusMarker, waitOn } from \"../../workers/wait-on.js\";\nimport { readSpecPrompt, resolveSpecPath } from \"../../workers/specfile.js\";\nimport { agentClaudeArgs, agentLabel, deriveLabel, loadAgent, modelToClass } from \"../../workers/agents.js\";\nimport { followTailOutput, followWorkers,psOutput, replayOutput, statusLineOutput } from \"../../workers/viewer.js\";\nimport { assertPaneNotDisabled, checkPaneForWorker, closeEndedPanes, openFollowPane, openPaneForWorker, resolveHostSession } from \"../../workers/pane.js\";\nimport { renderUsage, usageJson, parseWhen, workerUsage } from \"../../workers/usage.js\";\nimport { loadStatus, ownsPid, saveStatus, setWorkerLabel, waitForTerminalStatus } from \"../../workers/status.js\";\nimport { callerDescription, requestKill } from \"../../workers/endreason.js\";\nimport { sayToWorker } from \"../../workers/operator.js\";\nimport { handoffFromInside } from \"../../workers/handoff.js\";\nimport { readMergeVerify } from \"../../workers/workers-config.js\";\nimport { discardWorker, formatKnowledge, gcWorktrees, leftoverWorktrees, mergeWorker, verifyWorker } from \"../../workers/worktree.js\";\nimport { formatPending, pendingGate, pendingResults } from \"../../workers/pending.js\";\nimport { runWait } from \"../../workers/wait.js\";\nimport { realProbeDeps, waitFile, waitUrl } from \"../../workers/wait-probes.js\";\nimport { describeModels, describeCapabilities, modelPrefsText, resolveProviderName, setCapabilityPreference, setProviderModel, unsetCapabilityPreference } from \"../../workers/providers.js\";\nimport { CliError, OPT, type CommandDef, type RunContext } from \"../define.js\";\nimport { dim, err, ok } from \"../lib/utils.js\";\n\n// ---------------------------------------------------------------------------\n// Shared helpers\n// ---------------------------------------------------------------------------\n\nfunction logDir(): string {\n  try {\n    return workersLogDir(readWorkersSection().workers);\n  } catch (e) {\n    throw new CliError(`cannot read the worker configuration: ${msgOf(e)}`, \"pai config path   (shows which file is read)\");\n  }\n}\n\nconst msgOf = (e: unknown) => (e instanceof Error ? e.message : String(e));\n\n/** Run a verb; any thrown error becomes one plain sentence (exit 1) with the next command. */\nasync function guard(next: string, fn: () => Promise<number | void> | number | void): Promise<number | void> {\n  try {\n    return await fn();\n  } catch (e) {\n    if (e instanceof CliError) throw e;\n    throw new CliError(msgOf(e), next);\n  }\n}\n\nconst str = (v: unknown): string | undefined => (typeof v === \"string\" ? v : undefined);\nconst flag = (ctx: RunContext, name: string): boolean => ctx.opts[name] === true;\nconst num = (v: unknown): number | undefined => (typeof v === \"number\" ? v : undefined);\nconst out = (s: string) => process.stdout.write(s + \"\\n\");\n\n// ---------------------------------------------------------------------------\n// run / resume\n// ---------------------------------------------------------------------------\n\n/**\n * Everything `worker run` hands to the engine, built from the parsed flags.\n * Pure apart from reading the spec/agent files, so the argv it produces can be\n * compared with the old command's.\n */\nexport type RunPlan =\n  | { kind: \"chain\"; chain: Parameters<typeof runChain>[0] }\n  | { kind: \"run\"; run: RunOptions };\n\nexport function buildRunPlan(ctx: RunContext): RunPlan {\n  const o = ctx.opts;\n  const className = str(o.class) ?? str(o.role);\n  const report = str(o.report);\n  if (report !== undefined && report !== \"json\" && report !== \"ag2\") {\n    throw new Error(`--report: expected \"json\" or \"ag2\", got \"${report}\"`);\n  }\n  const cwd = str(o.cwd);\n  if (cwd !== undefined) {\n    if (!existsSync(cwd)) throw new Error(`--cwd: directory does not exist: ${cwd}`);\n    if (!statSync(cwd).isDirectory()) throw new Error(`--cwd: not a directory: ${cwd}`);\n  }\n  const noPane = o[\"no-pane\"] === true;\n  let claudeArgs = ctx.rest;\n  let specPath: string | undefined;\n  const spec = str(o.spec);\n  if (spec !== undefined) {\n    if (parseRunnerArgs(ctx.rest).headless) {\n      throw new Error(\"--spec and -p/--print are mutually exclusive — pass the prompt one way, not both\");\n    }\n    const cwdForSpec = cwd ?? process.cwd();\n    specPath = resolveSpecPath(spec, cwdForSpec);\n    claudeArgs = [\"-p\", readSpecPrompt(spec, cwdForSpec), ...ctx.rest];\n  } else {\n    const hint = longInlinePromptHint(parseRunnerArgs(ctx.rest).prompt);\n    if (hint) process.stderr.write(hint + \"\\n\");\n  }\n\n  assertPaneNotDisabled(noPane);\n  let label = str(o.label);\n  let agentClass: string | undefined;\n  const agent = str(o.agent);\n  if (agent) {\n    const def = loadAgent(agent);\n    claudeArgs = [...agentClaudeArgs(def), ...claudeArgs];\n    let agentProviders: Record<string, WorkerProvider> | undefined;\n    try {\n      agentProviders = readWorkersSection().workers.providers;\n    } catch {\n      agentProviders = undefined;\n    }\n    agentClass = modelToClass(def.model, agentProviders);\n    if (!label) label = agentLabel(agent, parseRunnerArgs(claudeArgs).prompt);\n  }\n  const chain = str(o.chain);\n  if (chain) {\n    const brief = parseRunnerArgs(claudeArgs).prompt;\n    if (!brief) throw new Error(\"--chain needs the task as -p '<brief>'\");\n    return {\n      kind: \"chain\",\n      chain: {\n        stages: chain.split(\",\"),\n        className: className ?? agentClass,\n        providerFlag: str(o.provider),\n        modelFlag: str(o.model),\n        label,\n        noPane,\n        mcpFlag: str(o.mcp),\n        specPath,\n        cwd,\n        brief,\n        claudeArgs,\n      },\n    };\n  }\n  if (!label) label = deriveLabel(parseRunnerArgs(claudeArgs).prompt, className ?? agentClass ?? \"default\");\n  return {\n    kind: \"run\",\n    run: {\n      providerFlag: str(o.provider),\n      urgent: o.urgent === true ? true : undefined,\n      localOk: o[\"local-ok\"] === true ? true : undefined,\n      cloud: o.cloud === true ? true : undefined,\n      className: className ?? agentClass,\n      modelFlag: str(o.model),\n      label,\n      mcpFlag: str(o.mcp),\n      specPath,\n      cwd,\n      noPane,\n      noStagger: o[\"no-stagger\"] === true ? true : undefined,\n      noQueue: o[\"no-queue\"] === true ? true : undefined,\n      worktreeFlag: o[\"no-worktree\"] === true ? false : o.worktree === true ? true : undefined,\n      maxMinutes: num(o[\"max-minutes\"]),\n      deadline: str(o.deadline),\n      browser: o[\"no-browser\"] === true ? false : o.browser === true ? true : undefined,\n      printCmd: o[\"print-cmd\"] === true || ctx.dryRun ? true : undefined,\n      reportFormatFlag: report as \"json\" | \"ag2\" | undefined,\n      noReportRetry: o[\"no-report-retry\"] === true,\n      orchestrate: o.orchestrate === true ? true : undefined,\n      capabilityFlag: str(o.capability) ?? str(o.for),\n      imageOut: str(o.out),\n      imageSize: str(o.size),\n      imageTimeoutMs: num(o[\"timeout-ms\"]),\n      claudeArgs,\n    },\n  };\n}\n\nconst runCommand: CommandDef = {\n  name: \"run\",\n  summary: \"Run one task on a worker (another provider or model)\",\n  description:\n    \"Starts a Claude Code worker through the chosen provider and waits for it. The task goes in as -p '<task>' or, safer for long text, --spec FILE.\\n\" +\n    \"Options this command does not know (for example -p, --allowedTools) are handed to claude unchanged.\\n\" +\n    \"--label is the row shown in `ps`, `follow` and the status line; without it the first line of the task is used.\\n\" +\n    \"Give a worker an MCP tool by naming mcp__server__tool in --allowedTools; the server loads by itself.\",\n  allowUnknown: true,\n  options: [\n    { ...OPT.provider, summary: \"Provider to run on (default: the active one, else the routing order)\" },\n    { name: \"class\", type: \"string\", value: \"NAME\", summary: \"Use the provider set for this kind of work (draft, implement, review, research, spotcheck, simple, complex, image)\" },\n    { name: \"role\", type: \"string\", value: \"NAME\", summary: \"Old name of --class\" },\n    { name: \"capability\", type: \"string\", value: \"NAME\", summary: \"Run on whichever provider serves this capability (see `pai worker capability`), e.g. image\" },\n    { name: \"for\", type: \"string\", value: \"NAME\", summary: \"Short for --capability\" },\n    { name: \"out\", type: \"string\", value: \"FILE\", summary: \"Where an image run writes its PNG (default: next to the worker log)\" },\n    { name: \"size\", type: \"string\", value: \"WxH\", summary: \"Image size for an image run, e.g. 1024x1024 (default 1024x1024)\" },\n    { name: \"timeout-ms\", type: \"number\", value: \"N\", summary: \"How long an image run waits for its answer (default 120000)\" },\n    { name: \"chain\", type: \"string\", value: \"STAGES\", summary: \"Comma-separated stages run one after the other, e.g. draft,implement,review\" },\n    { name: \"agent\", type: \"string\", value: \"NAME\", summary: \"Run the agent definition ~/.claude/agents/NAME.md on a worker\" },\n    { ...OPT.model, summary: \"Model for this run (default: the model set for the class)\" },\n    { name: \"urgent\", type: \"boolean\", summary: \"Needs a fast answer: go to the cloud even when the policy prefers local (see `pai worker policy`)\" },\n    { name: \"local-ok\", type: \"boolean\", summary: \"This run may go to a local provider under the prefer-cloud policy\" },\n    { name: \"cloud\", type: \"boolean\", summary: \"This run goes to the cloud under the prefer-local or auto policy\" },\n    { name: \"label\", type: \"string\", value: \"TEXT\", summary: \"Short task label shown in ps, follow and the status line\" },\n    { name: \"spec\", type: \"string\", value: \"FILE\", summary: \"Read the task from this file (or - for stdin) instead of -p\" },\n    { name: \"cwd\", type: \"string\", value: \"DIR\", summary: \"Directory the worker runs in (default: here)\" },\n    { name: \"mcp\", type: \"string\", value: \"NAMES\", summary: \"MCP servers or sets this worker may use, comma-separated (see `pai worker mcp`)\" },\n    { name: \"no-pane\", type: \"boolean\", summary: \"Run without a visible pane (refused unless a worker or the operator override allows it)\" },\n    { name: \"no-queue\", type: \"boolean\", summary: \"Do not queue behind the provider's concurrent-run cap (workers.yaml maxConcurrent)\" },\n    { name: \"no-stagger\", type: \"boolean\", summary: \"Do not wait for an earlier run on the same provider to warm its prompt cache\" },\n    { name: \"worktree\", type: \"boolean\", summary: \"Work in a git worktree on branch worker/ID (default for implement and complex work in a git repo)\" },\n    { name: \"no-worktree\", type: \"boolean\", summary: \"Work in place, no worktree\" },\n    { name: \"max-minutes\", type: \"number\", value: \"N\", summary: \"Stop after N minutes and keep the work (0 = no limit; default from the config, 60)\" },\n    { name: \"deadline\", type: \"string\", value: \"HH:MM\", summary: \"Stop at this local time (tomorrow if already past); not with --max-minutes\" },\n    { name: \"browser\", type: \"boolean\", summary: \"Allow browser tools\" },\n    { name: \"no-browser\", type: \"boolean\", summary: \"Remove every browser tool from this worker\" },\n    { name: \"print-cmd\", type: \"boolean\", summary: \"Print the claude command as JSON and exit without starting anything\" },\n    { name: \"report\", type: \"string\", value: \"FORMAT\", summary: \"Final report format: json or ag2 (default ag2)\" },\n    { name: \"no-report-retry\", type: \"boolean\", summary: \"Do not ask once more when the final report is malformed\" },\n    { name: \"orchestrate\", type: \"boolean\", summary: \"This worker plans and reviews; code edits are denied and go to local units\" },\n  ],\n  examples: [\n    \"pai worker run --class implement --label 'fix the retry loop' -p 'Fix the retry loop in src/net.ts'\",\n    \"pai worker run --provider glm --spec /tmp/task.txt\",\n    \"pai worker run --chain draft,implement,review -p 'Add a --json flag to the export command'\",\n    \"pai worker run --print-cmd --class spotcheck -p 'Say hi'\",\n  ],\n  seeAlso: [\"pai worker ps\", \"pai worker follow\", \"pai worker merge\", \"pai worker resume\"],\n  run: (ctx) =>\n    guard(\"pai worker run --print-cmd -p 'test'   (shows the command without starting it)\", async () => {\n      const plan = buildRunPlan(ctx);\n      return plan.kind === \"chain\" ? await runChain(plan.chain) : await runWorker(plan.run);\n    }),\n};\n\nconst resumeCommand: CommandDef = {\n  name: \"resume\",\n  summary: \"Continue a finished worker with a new instruction\",\n  description: \"Starts a new worker on the same provider and model that picks up the conversation of a finished one (claude --resume).\",\n  args: [\n    { name: \"ID\", required: true, summary: \"Id of the finished worker (see `pai worker ps`)\" },\n    { name: \"TEXT\", required: true, summary: \"What the worker should do next\" },\n  ],\n  options: [\n    { name: \"print-id\", type: \"boolean\", summary: \"Print the new worker id on its own line\" },\n    { name: \"no-pane\", type: \"boolean\", summary: \"Run without a visible pane (refused unless allowed)\" },\n    { name: \"output-format\", type: \"string\", value: \"FORMAT\", summary: \"text (default), json or stream-json, as for `run`\" },\n    { name: \"label\", type: \"string\", value: \"TEXT\", summary: \"Short task label (default: the old label with a return arrow)\" },\n  ],\n  examples: [\"pai worker resume w-20260101-abc 'Also add a test'\", \"pai worker resume w-20260101-abc 'Continue' --print-id\"],\n  seeAlso: [\"pai worker ps\", \"pai worker run\"],\n  run: (ctx) =>\n    guard(\"pai worker ps   (lists worker ids)\", async () => {\n      const id = ctx.args.ID as string;\n      const text = ctx.args.TEXT as string;\n      const noPane = flag(ctx, \"no-pane\");\n      assertPaneNotDisabled(noPane);\n      const dir = logDir();\n      const old = loadStatus(dir, id);\n      if (!old) throw new Error(`no worker named \"${id}\"`);\n      if (!old.claudeSession) {\n        throw new Error(\n          `worker ${id} recorded no Claude session id — it predates resume support or ran through an engine that does not expose one`\n        );\n      }\n      const claudeArgs = [\"--resume\", old.claudeSession, \"-p\", text];\n      const fmt = str(ctx.opts[\"output-format\"]);\n      if (fmt !== undefined) claudeArgs.push(\"--output-format\", fmt);\n      let newId = \"\";\n      const rc = await runWorker({\n        providerFlag: old.provider,\n        modelFlag: old.model,\n        label: str(ctx.opts.label) || `↩ ${old.label}`,\n        noPane,\n        claudeArgs,\n        resumeOf: id,\n        onWorkerStart: (wid) => {\n          newId = wid;\n        },\n      });\n      if (flag(ctx, \"print-id\") && newId) out(newId);\n      return rc;\n    }),\n};\n\n// ---------------------------------------------------------------------------\n// Looking at workers\n// ---------------------------------------------------------------------------\n\nconst psCommand: CommandDef = {\n  name: \"ps\",\n  summary: \"List this session's workers, running and finished\",\n  description:\n    \"Shows the workers started from this terminal. When other sessions have live workers too, a MACHINE section lists every live worker on this computer with the session that owns it.\",\n  options: [{ name: \"all\", type: \"boolean\", summary: \"Workers of all sessions, not just this terminal's\" }],\n  examples: [\"pai worker ps\", \"pai worker ps --all\"],\n  seeAlso: [\"pai worker follow\", \"pai worker pending\", \"pai worker kill\"],\n  run: (ctx) =>\n    guard(\"pai worker ps --all\", () => {\n      const dir = logDir();\n      const pending = pendingResults(dir, process.cwd()).length;\n      if (pending) out(`${pending} unmerged result(s) — pai worker pending`);\n      const left = leftoverWorktrees(dir);\n      if (left) out(`worktrees: ${left} finished, not merged; pai worker gc --dry-run`);\n      out(psOutput(dir, flag(ctx, \"all\")));\n    }),\n};\n\nconst usageCommand: CommandDef = {\n  name: \"usage\",\n  summary: \"Token totals per provider and model for a set of workers\",\n  description:\n    \"Sums the exact tokens of the selected workers (each assistant message counted once) per provider and model: input, cache read, cache write 5m and 1h, output, plus the reported cost. Local providers are marked and carry no cost. \" +\n    \"This is the worker side of the A/B comparison; the top session's side is `pai audit tokens usage`.\",\n  options: [\n    { name: \"label\", type: \"string\", value: \"PREFIX\", summary: \"Only workers whose label starts with PREFIX\" },\n    { name: \"since\", type: \"string\", value: \"WHEN\", summary: \"Only workers started at or after WHEN (ISO time, or 1h / 30m)\" },\n    { name: \"until\", type: \"string\", value: \"WHEN\", summary: \"Only workers started at or before WHEN (ISO time, or 1h / 30m)\" },\n    OPT.json,\n  ],\n  examples: [\"pai worker usage --since 2h\", \"pai worker usage --label armA --since 2026-10-18T06:00:00 --json\"],\n  seeAlso: [\"pai audit tokens usage\", \"pai worker ps\"],\n  run: (ctx) =>\n    guard(\"pai worker ps\", () => {\n      const { workers } = readWorkersSection();\n      const baseUrls = Object.fromEntries(Object.entries(workers.providers).map(([k, v]) => [k, v.baseUrl]));\n      const rep = workerUsage(\n        logDir(),\n        {\n          labelPrefix: str(ctx.opts.label),\n          since: str(ctx.opts.since) ? parseWhen(str(ctx.opts.since)!) : undefined,\n          until: str(ctx.opts.until) ? parseWhen(str(ctx.opts.until)!) : undefined,\n        },\n        baseUrls\n      );\n      out(flag(ctx, \"json\") ? JSON.stringify(usageJson(rep), null, 2) : renderUsage(rep, { workers: true }));\n    }),\n};\n\nconst followCommand: CommandDef = {\n  name: \"follow\",\n  summary: \"Watch a worker live, or all running workers\",\n  description: \"Live view of one worker's transcript, or of every running worker in this session. Ctrl-C leaves it.\",\n  args: [{ name: \"ID\", summary: \"Worker id (default: all running workers of this session)\" }],\n  options: [\n    { name: \"all\", type: \"boolean\", summary: \"Follow the workers of all sessions\" },\n    { name: \"auto-exit\", type: \"number\", value: \"SECS\", optionalValue: true, summary: \"Leave SECS seconds after the workers end (default from the config)\" },\n    { name: \"live\", type: \"boolean\", summary: \"Stream even without a terminal (default there: print the tail and exit)\" },\n    { name: \"tail\", type: \"number\", value: \"N\", summary: \"Entries printed when there is no terminal (default 20)\" },\n  ],\n  examples: [\"pai worker follow\", \"pai worker follow w-20260101-abc\", \"pai worker follow --all --auto-exit 10\"],\n  seeAlso: [\"pai worker ps\", \"pai worker replay\", \"pai worker pane\"],\n  run: (ctx) =>\n    guard(\"pai worker ps\", async () => {\n      const { workers } = readWorkersSection();\n      const ae = ctx.opts[\"auto-exit\"];\n      const autoExit = ae === undefined || ae === false ? 0 : ae === true ? workers.pane.autoExitSecs : Number(ae);\n      const target = (ctx.args.ID as string | undefined) ?? null;\n      const tailText = followTailOutput(logDir(), target, {\n        live: flag(ctx, \"live\"),\n        autoExit: ae !== undefined && ae !== false,\n        tail: num(ctx.opts.tail),\n      });\n      if (tailText !== null) {\n        out(tailText);\n        return;\n      }\n      await followWorkers(logDir(), target, flag(ctx, \"all\"), autoExit);\n    }),\n};\n\nconst replayCommand: CommandDef = {\n  name: \"replay\",\n  summary: \"Print the transcript of one worker\",\n  description: \"Shows what a finished or running worker did, from its recorded events.\",\n  args: [{ name: \"ID\", required: true, summary: \"Worker id (see `pai worker ps`)\" }],\n  options: [{ name: \"tail\", type: \"number\", value: \"N\", summary: \"Only the last N lines\" }],\n  examples: [\"pai worker replay w-20260101-abc\", \"pai worker replay w-20260101-abc --tail 40\"],\n  seeAlso: [\"pai worker follow\", \"pai worker log\"],\n  run: (ctx) =>\n    guard(\"pai worker ps\", () => {\n      out(replayOutput(logDir(), ctx.args.ID as string, process.stdout.isTTY === true, num(ctx.opts.tail)));\n    }),\n};\n\nconst watchCommand: CommandDef = {\n  name: \"watch\",\n  summary: \"Worker list that refreshes every 2 seconds\",\n  description: \"Runs `pai worker ps --all` again every 2 seconds with the system `watch` program. Ctrl-C leaves it.\",\n  examples: [\"pai worker watch\", \"pai worker ps --all\"],\n  seeAlso: [\"pai worker ps\", \"pai worker follow\"],\n  run: () =>\n    new Promise<number>((resolve, reject) => {\n      const proc = spawn(\"watch\", [\"-n\", \"2\", \"pai worker ps --all\"], { stdio: \"inherit\" });\n      proc.on(\"error\", (e) => reject(new CliError(`cannot run watch: ${e.message}`, \"pai worker ps --all\")));\n      proc.on(\"close\", (code) => resolve(code ?? 1));\n    }),\n};\n\nconst paneCommand: CommandDef = {\n  name: \"pane\",\n  summary: \"Open a window pane that follows a worker\",\n  description:\n    \"Opens the follow pane for a worker: a tmux split when you are in tmux, else an iTerm2 pane on macOS, else it prints the follow command to run yourself.\",\n  args: [{ name: \"ID\", summary: \"Worker id (default: all running workers)\" }],\n  options: [\n    { name: \"check\", type: \"boolean\", summary: \"Only report whether the pane is open, with its profile file, font and window bounds\" },\n    { name: \"close-ended\", type: \"boolean\", summary: \"Close every open pane whose worker has ended\" },\n    { ...OPT.dryRun, summary: \"With --close-ended: only list what would be closed\" },\n  ],\n  examples: [\"pai worker pane\", \"pai worker pane w-20260101-abc --check\", \"pai worker pane --close-ended --dry-run\"],\n  seeAlso: [\"pai worker follow\", \"pai worker ps\"],\n  run: (ctx) =>\n    guard(\"pai worker follow\", async () => {\n      const { workers } = readWorkersSection();\n      const dir = logDir();\n      if (flag(ctx, \"close-ended\")) {\n        const r = await closeEndedPanes(\n          dir,\n          (wid) => {\n            const st = loadStatus(dir, wid);\n            return st !== null && st.state !== \"running\";\n          },\n          ctx.dryRun\n        );\n        for (const l of r.closed) out(`${ctx.dryRun ? \"would close\" : \"closed\"} ${l}`);\n        for (const l of r.kept) out(`kept ${l}`);\n        if (!r.closed.length) out(\"no ended worker panes open\");\n        return;\n      }\n      const host = await resolveHostSession();\n      const term = host.term;\n      process.stderr.write((host.via === \"none\" ? `host session: none (${host.reason})` : `host session: ${host.uid} via ${host.via}`) + \"\\n\");\n      const id = ctx.args.ID as string | undefined;\n      if (id) {\n        out(flag(ctx, \"check\") ? await checkPaneForWorker(id, workers.pane.fontSize, term) : (await openPaneForWorker(dir, workers, id, term)).message);\n      } else {\n        out(await openFollowPane(dir, workers, term, flag(ctx, \"check\")));\n      }\n    }),\n};\n\nconst logCommand: CommandDef = {\n  name: \"log\",\n  summary: \"Worker ledger or the raw events of one worker\",\n  description: \"No argument lists the worker logs. `all` prints the whole ledger, `tail` its last 20 lines, a worker id its last 30 raw events.\",\n  args: [{ name: \"WHAT\", summary: \"all, tail, or a worker id (default: list the logs)\" }],\n  examples: [\"pai worker log\", \"pai worker log tail\", \"pai worker log w-20260101-abc\"],\n  seeAlso: [\"pai worker replay\", \"pai worker errors\"],\n  run: (ctx) =>\n    guard(\"pai worker log   (lists the logs)\", () => {\n      const dir = logDir();\n      const what = ctx.args.WHAT as string | undefined;\n      const ledger = ledgerPath(dir);\n      if (what === \"all\") return void out(existsSync(ledger) ? readFileSync(ledger, \"utf8\") : dim(\"(no ledger yet)\"));\n      if (what === \"tail\") {\n        if (!existsSync(ledger)) return void out(dim(\"(no ledger yet)\"));\n        return void out(readFileSync(ledger, \"utf8\").trim().split(\"\\n\").slice(-20).join(\"\\n\"));\n      }\n      if (what) {\n        const path = eventsPath(dir, what);\n        if (!existsSync(path)) throw new Error(`no event log for ${what}`);\n        return void out(readFileSync(path, \"utf8\").trim().split(\"\\n\").slice(-30).join(\"\\n\"));\n      }\n      if (!existsSync(dir)) return void out(dim(\"(no workers yet)\"));\n      for (const f of readdirSync(dir).filter((x) => x.endsWith(\".jsonl\")).sort()) {\n        const st = statSync(`${dir}/${f}`);\n        out(`  ${f.replace(\".jsonl\", \"\")}  ${(st.size / 1024 / 1024).toFixed(1)} MB  ${st.mtime.toISOString().slice(0, 19).replace(\"T\", \" \")}`);\n      }\n    }),\n};\n\nconst errorsCommand: CommandDef = {\n  name: \"errors\",\n  summary: \"Tool errors workers hit, grouped by kind\",\n  description: \"Counts the errors workers ran into, with numbers and paths removed so equal errors group together.\",\n  options: [{ name: \"since\", type: \"string\", value: \"AGE\", default: \"24h\", summary: \"Only errors newer than this, e.g. 30m, 2h, 1d\" }],\n  examples: [\"pai worker errors\", \"pai worker errors --since 2h\"],\n  seeAlso: [\"pai worker log\"],\n  run: (ctx) =>\n    guard(\"pai worker errors --since 24h\", async () => {\n      const { errorsTable, groupToolErrors, parseSince, readToolErrors } = await import(\"../../workers/tool-errors.js\");\n      const since = str(ctx.opts.since) ?? \"24h\";\n      const ms = parseSince(since);\n      if (ms === null) throw new Error(`bad --since \"${since}\" (use 30m, 2h, 1d)`);\n      out(errorsTable(groupToolErrors(readToolErrors(logDir(), Date.now() - ms))));\n    }),\n};\n\nconst statusLineCommand: CommandDef = {\n  name: \"status-line\",\n  summary: \"One-line worker summary for a status bar\",\n  description:\n    \"Prints nothing when no worker is in scope. Called by the status line script with the terminal session id, the pane's directory and the Claude session id (which claims the workers that session's shell started).\",\n  args: [\n    { name: \"TERM\", summary: \"Terminal session id (default: $ITERM_SESSION_ID)\" },\n    { name: \"CWD\", summary: \"Directory of the pane (default: here)\" },\n    { name: \"SESSION\", summary: \"Claude session id\" },\n  ],\n  examples: [\"pai worker status-line\", \"pai worker status-line \\\"$ITERM_SESSION_ID\\\" \\\"$PWD\\\" ''\"],\n  seeAlso: [\"pai worker ps\"],\n  run: (ctx) =>\n    guard(\"pai worker ps\", () => {\n      const line = statusLineOutput(\n        logDir(),\n        (ctx.args.TERM as string | undefined) ?? process.env.ITERM_SESSION_ID ?? \"\",\n        (ctx.args.CWD as string | undefined) ?? process.cwd(),\n        (ctx.args.SESSION as string | undefined) ?? \"\",\n        new Date()\n      );\n      if (line) out(line);\n    }),\n};\n\n// ---------------------------------------------------------------------------\n// Talking to workers\n// ---------------------------------------------------------------------------\n\nconst sayCommand: CommandDef = {\n  name: \"say\",\n  summary: \"Send a message to a running worker\",\n  description: \"The message goes to the worker's open input, as if you had typed it in its pane. Prints ok.\",\n  args: [\n    { name: \"ID\", required: true, summary: \"Worker id (see `pai worker ps`)\" },\n    { name: \"TEXT\", required: true, summary: \"The message\" },\n  ],\n  options: [{ name: \"goal\", type: \"string\", value: \"TEXT\", summary: \"Rename the worker's goal in ps and its pane before sending\" }],\n  examples: [\"pai worker say w-20260101-abc 'Also cover the empty case'\", \"pai worker say w-20260101-abc 'Go on' --goal 'finish the export'\"],\n  seeAlso: [\"pai worker goal\", \"pai worker resume\"],\n  run: (ctx) =>\n    guard(\"pai worker ps\", async () => {\n      const dir = logDir();\n      const goal = str(ctx.opts.goal);\n      if (goal !== undefined) setWorkerLabel(dir, ctx.args.ID as string, goal);\n      await sayToWorker(dir, ctx.args.ID as string, ctx.args.TEXT as string);\n      out(\"ok\");\n    }),\n};\n\nconst goalCommand: CommandDef = {\n  name: \"goal\",\n  summary: \"Rename what a running worker is working on\",\n  description: \"Changes the label shown in ps and the worker's pane, without sending it a message. Prints ok.\",\n  args: [\n    { name: \"ID\", required: true, summary: \"Worker id (see `pai worker ps`)\" },\n    { name: \"TEXT\", required: true, summary: \"The new label\" },\n  ],\n  examples: [\"pai worker goal w-20260101-abc 'fix the retry loop'\", \"pai worker goal w-20260101-abc 'review the export tests'\"],\n  seeAlso: [\"pai worker say\", \"pai worker ps\"],\n  run: (ctx) =>\n    guard(\"pai worker ps\", () => {\n      setWorkerLabel(logDir(), ctx.args.ID as string, ctx.args.TEXT as string);\n      out(\"ok\");\n    }),\n};\n\nconst handoffCommand: CommandDef = {\n  name: \"handoff\",\n  summary: \"From inside a worker: pass a question or result to its parent\",\n  description:\n    'Adds a message to the parent\\'s inbox and, when the parent is running, says it to the parent. The payload is JSON: {\"kind\":\"proposal|question|blocker\",\"text\":\"...\",\"data\":{...}}. Sender and receiver come from the environment.',\n  args: [{ name: \"JSON\", required: true, summary: \"The payload as JSON\" }],\n  examples: [\n    `pai worker handoff '{\"kind\":\"question\",\"text\":\"Which branch should I target?\"}'`,\n    `pai worker handoff '{\"kind\":\"blocker\",\"text\":\"Tests need a database\",\"data\":{\"table\":\"users\"}}'`,\n  ],\n  seeAlso: [\"pai worker say\"],\n  run: (ctx) =>\n    guard(\"pai worker handoff '{\\\"kind\\\":\\\"question\\\",\\\"text\\\":\\\"...\\\"}'\", async () => {\n      const json = ctx.args.JSON as string;\n      let payload: unknown;\n      try {\n        payload = JSON.parse(json);\n      } catch {\n        throw new Error(`payload is not JSON: ${json}`);\n      }\n      const h = await handoffFromInside(logDir(), process.env, payload);\n      out(`ok → ${h.to} (${h.kind}); inbox ${h.to}.inbox.jsonl`);\n    }),\n};\n\n// ---------------------------------------------------------------------------\n// Taking the work back\n// ---------------------------------------------------------------------------\n\nconst mergeCommand: CommandDef = {\n  name: \"merge\",\n  summary: \"Merge a worker's branch into your checkout\",\n  description:\n    \"Merges branch worker/ID into the original checkout, then removes the worktree and deletes the branch. With --verify (or the workers.yaml `merge.verify` default, which --verify overrides and --no-verify skips) the command runs on the merged tree first; if it exits non-zero the merge is reverted (git revert, never reset), the branch and worktree are kept, and the exit code is 1. A built-in stub scan always runs after the merge: added lines under src/ matching `TODO: implement`, `Promise.resolve(0) // TODO` or `throw new Error(\\\"not implemented` revert the merge the same way.\",\n  args: [{ name: \"ID\", required: true, summary: \"Worker id (see `pai worker pending`)\" }],\n  options: [\n    { name: \"no-commit\", type: \"boolean\", summary: \"Apply the changes uncommitted; keep branch and worktree\" },\n    { name: \"patch\", type: \"boolean\", summary: \"Same as --no-commit\" },\n    { name: \"verify\", type: \"string\", value: \"CMD\", summary: \"Run CMD after the merge; revert the merge when it fails (default: workers.yaml merge.verify)\" },\n    { name: \"no-verify\", type: \"boolean\", summary: \"Skip the configured merge.verify default (prints a warning)\" },\n  ],\n  examples: [\n    \"pai worker merge w-20260101-abc\",\n    \"pai worker merge w-20260101-abc --no-commit\",\n    'pai worker merge 20261018-0301-1 --verify \"npx tsc --noEmit\"',\n  ],\n  seeAlso: [\"pai worker verify\", \"pai worker pending\", \"pai worker discard\"],\n  run: (ctx) =>\n    guard(\"pai worker pending\", () => {\n      let verify = str(ctx.opts.verify);\n      if (verify === undefined) {\n        const configured = readMergeVerify();\n        if (flag(ctx, \"no-verify\")) {\n          if (configured) out(`warning: --no-verify skips the configured merge.verify gate (${configured})`);\n        } else verify = configured;\n      }\n      out(mergeWorker(logDir(), ctx.args.ID as string, { noCommit: flag(ctx, \"no-commit\") || flag(ctx, \"patch\"), verify }));\n    }),\n};\n\nconst verifyCommand: CommandDef = {\n  name: \"verify\",\n  summary: \"Check that a worker's changes are already in your checkout\",\n  description:\n    \"Compares what the worker committed with the working tree (or --against a ref). Exit 0 when every file is identical, so discarding is safe; exit 1 otherwise. Also reads archived workers.\",\n  args: [{ name: \"ID\", required: true, summary: \"Worker id\" }],\n  options: [\n    { name: \"against\", type: \"string\", value: \"REF\", summary: \"Compare against this git ref instead of the working tree\" },\n    { ...OPT.json, summary: \"Print the result as JSON\" },\n  ],\n  examples: [\"pai worker verify w-20260101-abc\", \"pai worker verify w-20260101-abc --against main --json\"],\n  seeAlso: [\"pai worker merge\", \"pai worker discard\"],\n  run: (ctx) =>\n    guard(\"pai worker pending\", () => {\n      const r = verifyWorker(logDir(), ctx.args.ID as string, { against: str(ctx.opts.against) });\n      if (flag(ctx, \"json\")) out(JSON.stringify(r, null, 2));\n      else {\n        for (const f of r.files) out(`${f.state.padEnd(9)} ${f.path}`);\n        out(`${r.ok ? \"all identical\" : \"not applied\"} (${r.files.length} file(s), ${r.ref} vs ${r.against})`);\n      }\n      return r.ok ? 0 : 1;\n    }),\n};\n\nconst pendingCommand: CommandDef = {\n  name: \"pending\",\n  summary: \"Finished workers whose work is not merged yet\",\n  description:\n    \"Lists workers whose branch or worktree holds work that is not in this repository. With --gate it works as a release check: exit 1 with the list unless PAI_ALLOW_PENDING=1.\",\n  options: [\n    { name: \"all\", type: \"boolean\", summary: \"Every repository, not just this one\" },\n    { ...OPT.json, summary: \"Machine-readable output\" },\n    { name: \"gate\", type: \"boolean\", summary: \"Release check: exit 1 with the list unless PAI_ALLOW_PENDING=1\" },\n  ],\n  examples: [\"pai worker pending\", \"pai worker pending --all --json\", \"pai worker pending --gate\"],\n  seeAlso: [\"pai worker merge\", \"pai worker discard\"],\n  run: (ctx) =>\n    guard(\"pai worker ps\", () => {\n      const res = pendingResults(logDir(), flag(ctx, \"all\") ? undefined : process.cwd());\n      if (flag(ctx, \"gate\")) {\n        const g = pendingGate(res);\n        if (g.message) process.stderr.write(g.message + \"\\n\");\n        return g.code;\n      }\n      if (flag(ctx, \"json\")) out(JSON.stringify(res, null, 2));\n      else out(res.length ? formatPending(res, flag(ctx, \"all\")) : \"no pending worker results\");\n    }),\n};\n\nconst discardCommand: CommandDef = {\n  name: \"discard\",\n  summary: \"Throw away a worker's branch and worktree\",\n  description: \"Removes the worktree and branch of a worker and keeps nothing. Use `verify` first if you are unsure.\",\n  args: [{ name: \"ID\", required: true, summary: \"Worker id\" }],\n  examples: [\"pai worker discard w-20260101-abc\", \"pai worker verify w-20260101-abc && pai worker discard w-20260101-abc\"],\n  seeAlso: [\"pai worker verify\", \"pai worker merge\", \"pai worker gc\"],\n  run: (ctx) =>\n    guard(\"pai worker pending\", () => {\n      out(discardWorker(logDir(), ctx.args.ID as string));\n    }),\n};\n\nconst gcCommand: CommandDef = {\n  name: \"gc\",\n  summary: \"Archive leftover worker worktrees\",\n  description: \"Moves old finished workers' worktrees into refs/pai-archive/ID. Before a worktree is removed, gitignored knowledge files (Notes/ and the like; not node_modules, dist, coverage, logs) are copied into the main checkout; a worktree that cannot be salvaged is kept. --dry-run prints what each worktree carries.\",\n  options: [\n    { name: \"older-than\", type: \"string\", value: \"HOURS\", default: \"24\", summary: \"Minimum age of a finished worker, in hours\" },\n    { name: \"all-finished\", type: \"boolean\", summary: \"Any finished worker, regardless of age\" },\n    { ...OPT.dryRun, summary: \"Print what would be archived and what each worktree carries, change nothing\" },\n  ],\n  examples: [\"pai worker gc --dry-run\", \"pai worker gc --older-than 72\", \"pai worker gc --all-finished\"],\n  seeAlso: [\"pai worker discard\", \"pai worker pending\"],\n  run: (ctx) =>\n    guard(\"pai worker gc --dry-run\", () => {\n      const raw = str(ctx.opts[\"older-than\"]) ?? \"24\";\n      const hours = Number(raw);\n      if (!Number.isFinite(hours) || hours < 0) throw new Error(`bad --older-than \"${raw}\"`);\n      const res = gcWorktrees(logDir(), flag(ctx, \"all-finished\") ? 0 : hours, ctx.dryRun, Date.now(), true);\n      const verb = ctx.dryRun ? \"would archive\" : \"archived\";\n      for (const r of res) {\n        out(r.skipped ? `skipped ${r.id}: ${r.skipped}` : `${verb} ${r.id} -> ${r.archive}`);\n        if (ctx.dryRun && r.knowledge) {\n          const k = formatKnowledge(r.knowledge);\n          for (const l of k.length ? k : [\"carries nothing\"]) out(`  ${l}`);\n        }\n      }\n      const done = res.filter((r) => r.archive);\n      const kept = res.filter((r) => r.skipped);\n      const files = done.reduce((n, r) => n + (r.salvaged?.filter((l) => l.startsWith(\"salvaged \")).length ?? 0), 0);\n      out(\n        `${done.length} ${ctx.dryRun ? \"would be archived\" : \"archived\"}, salvaged ${files} file(s), kept ${kept.length}` +\n          (kept.length ? ` (${kept.map((r) => `${r.id}: ${r.skipped}`).join(\"; \")})` : \"\")\n      );\n    }),\n};\n\nconst killCommand: CommandDef = {\n  name: \"kill\",\n  summary: \"Stop a running worker\",\n  description: \"Sends SIGTERM to the worker; it saves its final state and exits. A worker whose process id has been reused by something else is marked lost and nothing is signalled.\",\n  args: [{ name: \"ID\", required: true, summary: \"Worker id (see `pai worker ps`)\" }],\n  examples: [\"pai worker kill w-20260101-abc\", \"pai worker ps   (find the id first)\"],\n  seeAlso: [\"pai worker ps\"],\n  run: (ctx) =>\n    guard(\"pai worker ps\", async () => {\n      const id = ctx.args.ID as string;\n      const dir = logDir();\n      const status = loadStatus(dir, id);\n      if (!status) throw new Error(`no worker named \"${id}\"`);\n      if (status.state !== \"running\") throw new Error(`worker ${id} is not running (state: ${status.state})`);\n      if (!status.pid || status.pid <= 0) throw new Error(`worker ${id} has no pid recorded`);\n      if (!ownsPid(status)) {\n        status.state = \"lost\";\n        status.last = `pid ${status.pid} no longer belongs to this worker (reused); marked lost`;\n        status.rc = null;\n        saveStatus(dir, status);\n        out(`worker ${id}: pid ${status.pid} is not this worker any more (started ${status.started}); marked lost, nothing signalled`);\n        return;\n      }\n      try {\n        requestKill(dir, ledgerPath(dir), id);\n        process.kill(status.pid, \"SIGTERM\");\n        // The run's own SIGTERM handler writes the terminal status; only write ours if it never did.\n        const selfReported = await waitForTerminalStatus(dir, id);\n        if (!selfReported) {\n          const latest = loadStatus(dir, id) ?? status;\n          latest.state = \"killed\";\n          latest.rc = latest.rc ?? 143;\n          latest.last = latest.last || \"killed by signal SIGTERM\";\n          latest.endReason = \"pai-worker-kill\";\n          latest.endBy = callerDescription();\n          saveStatus(dir, latest);\n        }\n        out(`sent SIGTERM to worker ${id} (pid ${status.pid})`);\n      } catch (e) {\n        if (msgOf(e).includes(\"ESRCH\")) throw new Error(`process ${status.pid} not found (already exited?)`);\n        throw e;\n      }\n    }),\n};\n\n// ---------------------------------------------------------------------------\n// Waiting\n// ---------------------------------------------------------------------------\n\nconst waitOnCommand: CommandDef = {\n  name: \"wait-on\",\n  summary: \"Wait for a background job to finish\",\n  description:\n    \"Waits at most --max seconds (default 110, limit 590; over ~110 s needs the Bash tool timeout raised, which the PAI hook does automatically) for a process id. Exit 0: it is gone. Exit 3: still running, call again. Exit 2: it never existed.\\n\" +\n    \"Recipe: nohup sh -c 'CMD; echo $? > /tmp/job.rc' > /tmp/job.log 2>&1 & echo $! > /tmp/job.pid, then repeat `pai worker wait-on $(cat /tmp/job.pid) --log /tmp/job.log` until it exits 0.\",\n  args: [{ name: \"PID\", required: true, summary: \"Process id of the job\" }],\n  options: [\n    { name: \"log\", type: \"string\", value: \"FILE\", summary: \"Log file whose last lines are printed on return; FILE.rc holds the exit code\" },\n    { name: \"max\", type: \"number\", value: \"SECS\", summary: \"Longest wait in seconds (default 110, limit 590; over ~110 s needs the Bash tool timeout raised, which the PAI hook does automatically)\" },\n    { name: \"tail\", type: \"number\", value: \"N\", summary: \"Log lines printed on return (default 20)\" },\n  ],\n  examples: [\"pai worker wait-on 12345\", \"pai worker wait-on $(cat /tmp/job.pid) --log /tmp/job.log --max 60\"],\n  seeAlso: [\"pai worker wait\", \"pai worker wait-url\", \"pai worker wait-file\"],\n  run: (ctx) =>\n    guard(\"pai worker wait-on <pid>\", async () => {\n      const wid = process.env.PAI_WORKER_ID;\n      const mark = wid ? statusMarker(logDir(), wid) : undefined;\n      // a killed wait-on must not leave a stale waitingOn behind\n      for (const sig of [\"SIGINT\", \"SIGTERM\"] as const) {\n        process.once(sig, () => {\n          mark?.(null);\n          process.exit(128 + (sig === \"SIGINT\" ? 2 : 15));\n        });\n      }\n      return await waitOn(\n        { pid: Number(ctx.args.PID), log: str(ctx.opts.log), maxSecs: num(ctx.opts.max), tail: num(ctx.opts.tail) },\n        { ...waitOnDeps, mark }\n      );\n    }),\n};\n\nconst waitCommand: CommandDef = {\n  name: \"wait\",\n  summary: \"Wait for child workers; returns when any finishes or --max elapses\",\n  description:\n    \"Prints one JSON status line per worker id and returns within --max seconds (default 100, limit 590; over ~110 s needs the Bash tool timeout raised, which the PAI hook does automatically) as soon as ANY of them finishes. Exit 0: all done. Exit 1: a finished worker failed. Exit 3: some still running, call again with the unfinished ids.\\n\" +\n    \"Use this instead of sleeping and re-checking `pai worker ps`. --timeout keeps the old behaviour: wait for ALL, exit 1 on timeout.\",\n  args: [{ name: \"IDS\", required: true, variadic: true, summary: \"One or more worker ids\" }],\n  options: [\n    { name: \"max\", type: \"number\", value: \"SECS\", summary: \"Longest wait in seconds (default 100, limit 590; over ~110 s needs the Bash tool timeout raised, which the PAI hook does automatically)\" },\n    { name: \"timeout\", type: \"number\", value: \"SECS\", summary: \"Wait for ALL workers, give up after this many seconds (exit 1)\" },\n  ],\n  examples: [\"pai worker wait w-20260101-abc\", \"pai worker wait w-20260101-abc w-20260101-def --max 60\"],\n  seeAlso: [\"pai worker ps\", \"pai worker wait-on\", \"pai worker wait-url\", \"pai worker wait-file\"],\n  run: (ctx) =>\n    guard(\"pai worker ps\", () =>\n      runWait(logDir(), ctx.args.IDS as string[], { max: num(ctx.opts.max), timeout: num(ctx.opts.timeout) }, out, (l) =>\n        process.stderr.write(err(l) + \"\\n\")\n      )\n    ),\n};\n\nconst waitUrlCommand: CommandDef = {\n  name: \"wait-url\",\n  summary: \"Wait for a server to answer HTTP\",\n  description:\n    \"Polls the URL with backoff for at most --max seconds (default 100, limit 590; over ~110 s needs the Bash tool timeout raised, which the PAI hook does automatically). Exit 0: it answered 2xx-4xx. Exit 3: not yet, call again.\",\n  args: [{ name: \"URL\", required: true, summary: \"e.g. http://127.0.0.1:8016/v1/models\" }],\n  options: [{ name: \"max\", type: \"number\", value: \"SECS\", summary: \"Longest wait in seconds (default 100, limit 590; over ~110 s needs the Bash tool timeout raised, which the PAI hook does automatically)\" }],\n  examples: [\"pai worker wait-url http://127.0.0.1:8016/v1/models\", \"pai worker wait-url http://127.0.0.1:8016/v1/models --max 60\"],\n  seeAlso: [\"pai worker wait\", \"pai worker wait-on\", \"pai worker wait-file\"],\n  run: (ctx) => guard(\"pai worker wait-url <url>\", () => waitUrl({ url: String(ctx.args.URL), maxSecs: num(ctx.opts.max) }, { ...realProbeDeps, out })),\n};\n\nconst waitFileCommand: CommandDef = {\n  name: \"wait-file\",\n  summary: \"Wait for files to appear\",\n  description:\n    \"Waits at most --max seconds (default 100, limit 590; over ~110 s needs the Bash tool timeout raised, which the PAI hook does automatically) until every path exists; prints one line per path. Exit 0: all exist. Exit 3: some missing, call again.\",\n  args: [{ name: \"PATHS\", required: true, variadic: true, summary: \"One or more file paths\" }],\n  options: [{ name: \"max\", type: \"number\", value: \"SECS\", summary: \"Longest wait in seconds (default 100, limit 590; over ~110 s needs the Bash tool timeout raised, which the PAI hook does automatically)\" }],\n  examples: [\"pai worker wait-file /tmp/job.rc\", \"pai worker wait-file /tmp/a.rc /tmp/b.rc --max 60\"],\n  seeAlso: [\"pai worker wait\", \"pai worker wait-on\", \"pai worker wait-url\"],\n  run: (ctx) =>\n    guard(\"pai worker wait-file <path...>\", () => waitFile({ paths: ctx.args.PATHS as string[], maxSecs: num(ctx.opts.max) }, { ...realProbeDeps, out })),\n};\n\n// ---------------------------------------------------------------------------\n// Which model, which provider\n// ---------------------------------------------------------------------------\n\nconst modelCommand: CommandDef = {\n  name: \"model\",\n  summary: \"Show or set the model a provider uses\",\n  description:\n    \"No arguments lists the models of every provider.\\n\" +\n    \"`model MODEL-ID` sets the default model of the active provider.\\n\" +\n    \"`model CAPABILITY` shows one capability (default, fast, image, ...); `model CAPABILITY MODEL-ID` sets it.\\n\" +\n    \"`pai worker capability` chooses which provider serves a capability.\",\n  args: [\n    { name: \"WHAT\", summary: \"A capability name, or a model id to set as default\" },\n    { name: \"MODEL\", summary: \"Model id to set for that capability\" },\n  ],\n  options: [\n    { ...OPT.provider, summary: \"Provider to read or change (default: the active one)\" },\n    { name: \"capability\", type: \"string\", value: \"NAME\", summary: \"Capability to show or set, with the model id as the only argument\" },\n  ],\n  examples: [\"pai worker model\", \"pai worker model fast\", \"pai worker model fast glm-fast --provider glm\"],\n  seeAlso: [\"pai worker capability\", \"pai providers\"],\n  run: (ctx) =>\n    guard(\"pai worker model\", () => {\n      const { workers } = readWorkersSection();\n      let what = ctx.args.WHAT as string | undefined;\n      let model = ctx.args.MODEL as string | undefined;\n      const cap = str(ctx.opts.capability);\n      const provider = str(ctx.opts.provider);\n      if (cap !== undefined) {\n        if (!isModelCapability(cap)) throw new WorkersConfigError(`\"${cap}\" is not a valid capability name`);\n        if (model !== undefined) throw new WorkersConfigError(\"with --capability pass only a model id\");\n        model = what;\n        what = cap;\n      }\n      if (what === undefined) {\n        for (const line of describeModels(workers)) out(`  ${line}`);\n        return;\n      }\n      let capability: ModelCapability = \"default\";\n      let id = what;\n      if (isModelCapability(what)) {\n        capability = what;\n        if (model === undefined) {\n          const name = resolveProviderName(workers, provider);\n          const p = getProviderOrNative(workers, name);\n          if (p) out(`  ${name} ${capability} ${p.models[capability] ?? \"(none)\"}`);\n          return;\n        }\n        id = model;\n      } else if (model !== undefined) {\n        throw new WorkersConfigError(`unexpected second argument \"${model}\" — usage: model [<capability>] <model-id>`);\n      }\n      const name = resolveProviderName(workers, provider);\n      const fresh = setProviderModel(name, capability, id);\n      const p = getProviderOrNative(fresh, name);\n      if (p) {\n        out(ok(`${name} ${capability} model: ${p.models[capability]}`));\n        out(dim(`  ${modelPrefsText(p)}`));\n      }\n    }),\n};\n\nconst capabilityCommand: CommandDef = {\n  name: \"capability\",\n  summary: \"Choose which provider serves a capability (e.g. image)\",\n  description:\n    \"No arguments lists every preference and what it resolves to now. `capability NAME PROVIDER[,PROVIDER...]` sets the list (the first usable one wins); `capability NAME --unset` removes it.\\n\" +\n    \"A provider of kind image preferred for \\\"image\\\" is what `pai worker run --capability image` calls.\",\n  args: [\n    { name: \"NAME\", summary: \"Capability name, e.g. image\" },\n    { name: \"PROVIDERS\", summary: \"Provider names, comma-separated, best first\" },\n  ],\n  options: [\n    { name: \"unset\", type: \"boolean\", summary: \"Remove the preference for NAME\" },\n    { ...OPT.json, summary: \"Machine-readable output\" },\n  ],\n  examples: [\"pai worker capability\", \"pai worker capability image imagegen,backup\", \"pai worker capability image --unset\"],\n  seeAlso: [\"pai worker model\", \"pai worker run\"],\n  run: (ctx) =>\n    guard(\"pai worker capability\", () => {\n      const name = ctx.args.NAME as string | undefined;\n      const providers = ctx.args.PROVIDERS as string | undefined;\n      if (name === undefined) {\n        const { workers } = readWorkersSection();\n        const lines = describeCapabilities(workers);\n        if (flag(ctx, \"json\")) out(JSON.stringify(lines));\n        else for (const line of lines) out(`  ${line}`);\n        return;\n      }\n      if (flag(ctx, \"unset\")) {\n        unsetCapabilityPreference(name);\n        out(ok(`removed the capability preference for \"${name}\"`));\n        return;\n      }\n      if (providers === undefined) {\n        throw new Error(`usage: pai worker capability ${name} <provider>[,<provider>…]  (or --unset to remove it)`);\n      }\n      const workers = setCapabilityPreference(name, providers.split(\",\").map((p) => p.trim()));\n      const line = describeCapabilities(workers).find((l) => l.startsWith(`${name}:`));\n      out(ok(`${name}: [${workers.capabilities[name]?.join(\", \")}]`));\n      if (line) out(`  -> ${line.split(\" -> \")[1]}`);\n    }),\n};\n\n// ---------------------------------------------------------------------------\n// The group\n// ---------------------------------------------------------------------------\n\nexport const workerCommand: CommandDef = {\n  name: \"worker\",\n  summary: \"Run tasks on other providers: run, ps, follow, merge, ...\",\n  description:\n    \"Workers are Claude Code runs on a provider you chose (cheaper, faster or local) that do one task while you keep working.\\n\" +\n    \"Start one with `run`, watch it with `ps` or `follow`, then bring its work back with `merge`.\",\n  examples: [\"pai worker run --class implement -p 'Fix the retry loop'\", \"pai worker ps\", \"pai worker merge w-20260101-abc\"],\n  seeAlso: [\"pai providers\", \"pai help worker run\"],\n  subcommands: [\n    runCommand,\n    resumeCommand,\n    psCommand,\n    usageCommand,\n    followCommand,\n    replayCommand,\n    watchCommand,\n    paneCommand,\n    logCommand,\n    errorsCommand,\n    statusLineCommand,\n    sayCommand,\n    goalCommand,\n    handoffCommand,\n    mergeCommand,\n    verifyCommand,\n    pendingCommand,\n    discardCommand,\n    gcCommand,\n    killCommand,\n    waitOnCommand,\n    waitUrlCommand,\n    waitFileCommand,\n    waitCommand,\n    modelCommand,\n    capabilityCommand,\n  ],\n};\n","/**\n * Providers: `pai providers` lists, `pai providers use NAME` switches the\n * active one. Reads and writes go through src/workers (readWorkersSection,\n * useProvider) — the same code `pai worker providers` uses.\n */\n\nimport { ANTHROPIC_NATIVE, nativeAnthropicProvider, readWorkersSection, type WorkersConfig } from \"../../workers/config.js\";\nimport type { BudgetReading } from \"../../workers/budget.js\";\nimport { readBudget } from \"../lib/health.js\";\nimport { useProvider } from \"../../workers/providers.js\";\nimport { ok } from \"../lib/utils.js\";\nimport { CliError, OPT, type CommandDef } from \"../define.js\";\nimport { colorEnabled, makeStyle, renderTable, type Style } from \"../help.js\";\n\nexport interface ProviderRow {\n  name: string;\n  model: string;\n  fastModel: string | null;\n  active: boolean;\n  available: boolean;\n  /** Percent of quota used in the fullest window; null = unknown. */\n  budgetPercent: number | null;\n  note: string;\n}\n\n/** Pure: workers config + budget readings -> display rows. */\nexport function providerRows(\n  workers: Pick<WorkersConfig, \"providers\" | \"active\" | \"nativeModels\">,\n  budget: Record<string, BudgetReading | undefined>\n): ProviderRow[] {\n  const active = workers.active ?? ANTHROPIC_NATIVE;\n  const native = nativeAnthropicProvider(workers.nativeModels);\n  const entries: Array<[string, { enabled: boolean; models: { default: string; fast?: string }; note?: string }]> = [\n    [ANTHROPIC_NATIVE, native],\n    ...Object.entries(workers.providers),\n  ];\n  return entries.map(([name, p]) => ({\n    name,\n    model: p.models.default,\n    fastModel: p.models.fast ?? null,\n    active: name === active,\n    available: p.enabled,\n    budgetPercent: budget[name]?.percent ?? null,\n    note: p.note ?? \"\",\n  }));\n}\n\nfunction budgetCell(r: ProviderRow, st: Style): string {\n  if (!r.available) return st.dim(\"disabled\");\n  if (r.budgetPercent === null) return st.dim(\"unknown\");\n  const t = `${Math.round(r.budgetPercent)}% used`;\n  return r.budgetPercent >= 95 ? st.red(t) : r.budgetPercent >= 80 ? st.yellow(t) : st.green(t);\n}\n\nexport function providersTable(rows: ProviderRow[], st: Style): string {\n  return renderTable(\n    [\"\", \"Provider\", \"Model\", \"Fast model\", \"Budget\"],\n    rows.map((r) => [\n      r.active ? st.green(\"●\") : \" \",\n      r.active ? st.bold(r.name) : r.available ? r.name : st.dim(r.name),\n      r.model,\n      r.fastModel ?? st.dim(\"—\"),\n      budgetCell(r, st),\n    ]),\n    st\n  );\n}\n\nfunction load(): WorkersConfig {\n  try {\n    return readWorkersSection().workers;\n  } catch (e) {\n    throw new CliError(\n      `cannot read the provider configuration: ${e instanceof Error ? e.message : String(e)}`,\n      \"pai worker config check\"\n    );\n  }\n}\n\nexport const useCommand: CommandDef = {\n  name: \"use\",\n  summary: \"Make a provider the active one\",\n  description:\n    \"The active provider is where `pai new` starts and where new workers go by default. Classes pinned to a provider are re-pointed.\",\n  args: [{ name: \"NAME\", required: true, summary: \"Provider name from `pai providers`\" }],\n  options: [{ ...OPT.dryRun }],\n  examples: [\"pai providers use glm\", \"pai providers use anthropic\"],\n  seeAlso: [\"pai providers\", \"pai new\"],\n  run(ctx) {\n    const name = ctx.args.NAME as string;\n    const workers = load();\n    const rows = providerRows(workers, {});\n    const row = rows.find((r) => r.name === name);\n    if (!row || !row.available) {\n      const ok = rows.filter((r) => r.available).map((r) => r.name);\n      throw new CliError(\n        `provider \"${name}\" is ${row ? \"disabled\" : \"not configured\"}. Available: ${ok.join(\", \")}`,\n        `pai providers use ${ok.find((n) => n !== name) ?? ANTHROPIC_NATIVE}`\n      );\n    }\n    if (ctx.dryRun) return void process.stdout.write(`would make ${name} the active provider\\n`);\n    const { repointed } = useProvider(name);\n    process.stdout.write(ok(`active provider: ${name}${repointed ? `; ${repointed} class(es) re-pointed` : \"\"}`) + \"\\n\");\n  },\n};\n\nexport const providersCommand: CommandDef = {\n  name: \"providers\",\n  summary: \"List providers (● = active); `providers use NAME` switches\",\n  description:\n    \"Shows every provider you can start a session on, its default model, and how much of its quota is used when that is known.\\n\" +\n    \"● marks the active provider.\",\n  options: [{ ...OPT.json }],\n  examples: [\"pai providers\", \"pai providers --json\", \"pai providers use glm\"],\n  seeAlso: [\"pai new\", \"pai open\", \"pai providers use\"],\n  subcommands: [useCommand],\n  async run(ctx) {\n    const workers = load();\n    const rows = providerRows(workers, await readBudget(workers, 4000));\n    if (ctx.opts.json) return void process.stdout.write(JSON.stringify(rows, null, 2) + \"\\n\");\n    const st = makeStyle(colorEnabled());\n    process.stdout.write(providersTable(rows, st) + \"\\n\\n\");\n    process.stdout.write(st.dim(\"Start on one: pai new --provider NAME · make it the default: pai providers use NAME\") + \"\\n\");\n  },\n};\n","/**\n * local-tiers.ts — one local model server at a time, chosen by tier.\n *\n * `pai worker local <tier>` stops every running serve_patched server and\n * starts the tier's model; `off` stops all; `status` reports what is up.\n * Tiers are config, not code: `local_tiers:` and `local_launcher:` in\n * workers.yaml. A tier names a worker provider (so port and served model\n * name default to that provider's url and default model).\n *\n * This never touches worker routing (`pai worker use` does that).\n */\n\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { parse } from \"yaml\";\nimport { expandHome } from \"./config.js\";\n\nexport class LocalTierError extends Error {}\n\nexport interface LocalLauncher {\n  python: string;\n  script: string;\n  /** Flags shared by every tier, as one shell-split string. */\n  flags: string[];\n  /** Chat-template kwargs JSON (thinking off by default). */\n  templateKwargs: string;\n  /** Environment for the server process. */\n  env: Record<string, string>;\n  /** Process-name fragment that identifies a running server. */\n  processMatch: string;\n}\n\nexport interface LocalTier {\n  tier: string;\n  provider: string;\n  /** Model path; a `*` path segment is expanded against the directory. */\n  model: string;\n  served: string;\n  port: number;\n  flags: string[];\n}\n\nexport interface LocalConfig {\n  launcher: LocalLauncher;\n  tiers: Record<string, LocalTier>;\n}\n\nexport const DEFAULT_COMMON_FLAGS =\n  \"--host 127.0.0.1 --continuous-batching --enable-prefix-cache --cache-memory-mb 8000 \" +\n  \"--gpu-memory-utilization 0.30 --chunked-prefill-tokens 4096 --max-num-seqs 3 \" +\n  \"--max-request-tokens 131072 --max-tokens 32768 --enable-auto-tool-choice \" +\n  \"--tool-call-parser qwen3_coder --reasoning-parser qwen3\";\n\nexport function defaultLauncher(): LocalLauncher {\n  const home = homedir();\n  return {\n    python: join(home, \".local/share/uv/tools/vllm-mlx/bin/python\"),\n    script: join(home, \".local/share/vllm-mlx-serve/serve_patched_fold_text.py\"),\n    flags: DEFAULT_COMMON_FLAGS.split(/\\s+/),\n    templateKwargs: '{\"enable_thinking\": false}',\n    env: { PF_FORCE_TEXT: \"1\" },\n    processMatch: \"serve_patched\",\n  };\n}\n\nfunction str(v: unknown, what: string): string | undefined {\n  if (v === undefined || v === null) return undefined;\n  if (typeof v !== \"string\" || v === \"\") throw new LocalTierError(`${what}: must be a non-empty string`);\n  return v;\n}\n\nfunction flagList(v: unknown, what: string): string[] {\n  if (v === undefined || v === null) return [];\n  if (Array.isArray(v) && v.every((x) => typeof x === \"string\")) return v as string[];\n  const s = str(v, what);\n  return s ? s.split(/\\s+/) : [];\n}\n\n/**\n * Parse the local_launcher / local_tiers keys of a workers.yaml root.\n * `providerUrls`/`providerModels` supply the port and served-name fallbacks.\n */\nexport function parseLocalConfig(\n  root: Record<string, unknown>,\n  providers: Record<string, { url?: string; model?: string }>,\n): LocalConfig {\n  const launcher = defaultLauncher();\n  const lr = root.local_launcher;\n  if (lr !== undefined && lr !== null) {\n    if (typeof lr !== \"object\" || Array.isArray(lr)) throw new LocalTierError(\"local_launcher: must be a mapping\");\n    const l = lr as Record<string, unknown>;\n    const python = str(l.python, \"local_launcher.python\");\n    const script = str(l.script, \"local_launcher.script\");\n    if (python) launcher.python = expandHome(python);\n    if (script) launcher.script = expandHome(script);\n    if (l.flags !== undefined) launcher.flags = flagList(l.flags, \"local_launcher.flags\");\n    const tk = str(l.template_kwargs, \"local_launcher.template_kwargs\");\n    if (tk) launcher.templateKwargs = tk;\n    const pm = str(l.process_match, \"local_launcher.process_match\");\n    if (pm) launcher.processMatch = pm;\n    if (l.env && typeof l.env === \"object\") {\n      launcher.env = Object.fromEntries(Object.entries(l.env as Record<string, unknown>).map(([k, v]) => [k, String(v)]));\n    }\n  }\n\n  const tiers: Record<string, LocalTier> = {};\n  const tr = root.local_tiers;\n  if (tr !== undefined && tr !== null) {\n    if (typeof tr !== \"object\" || Array.isArray(tr)) throw new LocalTierError(\"local_tiers: must be a mapping\");\n    for (const [name, raw] of Object.entries(tr as Record<string, unknown>)) {\n      const where = `local_tiers.${name}`;\n      if (!raw || typeof raw !== \"object\") throw new LocalTierError(`${where}: must be a mapping`);\n      const t = raw as Record<string, unknown>;\n      const provider = str(t.provider, `${where}.provider`);\n      if (!provider) throw new LocalTierError(`${where}.provider: required`);\n      const model = str(t.model, `${where}.model`);\n      if (!model) throw new LocalTierError(`${where}.model: required`);\n      const p = providers[provider];\n      let port = t.port === undefined ? undefined : Number(t.port);\n      if (port === undefined && p?.url) {\n        const m = /:(\\d+)(?:\\/|$)/.exec(p.url);\n        if (m) port = Number(m[1]);\n      }\n      if (port === undefined || !Number.isInteger(port) || port <= 0) {\n        throw new LocalTierError(`${where}.port: missing and not derivable from provider \"${provider}\"`);\n      }\n      const served = str(t.served, `${where}.served`) ?? p?.model;\n      if (!served) throw new LocalTierError(`${where}.served: missing and provider \"${provider}\" has no default model`);\n      tiers[name] = { tier: name, provider, model: expandHome(model), served, port, flags: flagList(t.flags, `${where}.flags`) };\n    }\n  }\n  return { launcher, tiers };\n}\n\n/** Read the local config from a workers.yaml file. */\nexport function readLocalConfig(yamlPath: string): LocalConfig {\n  let root: Record<string, unknown> = {};\n  if (existsSync(yamlPath)) {\n    const doc = parse(readFileSync(yamlPath, \"utf8\"));\n    if (doc && typeof doc === \"object\") root = doc as Record<string, unknown>;\n  }\n  const providers: Record<string, { url?: string; model?: string }> = {};\n  const pr = root.providers;\n  if (pr && typeof pr === \"object\") {\n    for (const [name, raw] of Object.entries(pr as Record<string, unknown>)) {\n      const p = (raw ?? {}) as { url?: unknown; models?: { default?: unknown } };\n      providers[name] = {\n        url: typeof p.url === \"string\" ? p.url : undefined,\n        model: typeof p.models?.default === \"string\" ? p.models.default : undefined,\n      };\n    }\n  }\n  return parseLocalConfig(root, providers);\n}\n\n/** Expand `*` path segments to the first (sorted) existing entry. Unresolvable → input unchanged. */\nexport function resolveModelPath(pattern: string): string {\n  if (!pattern.includes(\"*\")) return pattern;\n  const segs = pattern.split(\"/\");\n  let cur = segs[0] === \"\" ? \"/\" : segs[0];\n  for (const seg of segs.slice(1)) {\n    if (!seg.includes(\"*\")) {\n      cur = join(cur, seg);\n      continue;\n    }\n    const re = new RegExp(\"^\" + seg.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\*/g, \".*\") + \"$\");\n    let hit: string | undefined;\n    try {\n      hit = readdirSync(cur).filter((e) => re.test(e)).sort()[0];\n    } catch {\n      return pattern;\n    }\n    if (!hit) return pattern;\n    cur = join(cur, hit);\n  }\n  return cur;\n}\n\nfunction shq(s: string): string {\n  return /^[A-Za-z0-9_@%+=:,./-]+$/.test(s) ? s : `'${s.replace(/'/g, `'\\\\''`)}'`;\n}\n\nexport interface StartPlan {\n  env: Record<string, string>;\n  argv: string[];\n  logPath: string;\n  /** Shell rendering of the above, log redirect included. */\n  line: string;\n}\n\nexport function logPathFor(provider: string): string {\n  return join(homedir(), \".cache/pai/local-server\", `${provider}.log`);\n}\n\nexport function planStart(cfg: LocalConfig, tier: LocalTier): StartPlan {\n  const l = cfg.launcher;\n  const argv = [\n    l.python, l.script, \"serve\", resolveModelPath(tier.model),\n    \"--served-model-name\", tier.served,\n    \"--port\", String(tier.port),\n    ...l.flags, ...tier.flags,\n    \"--default-chat-template-kwargs\", l.templateKwargs,\n  ];\n  const logPath = logPathFor(tier.provider);\n  const envPart = Object.entries(l.env).map(([k, v]) => `${k}=${shq(v)}`).join(\" \");\n  const line = `${envPart ? envPart + \" \" : \"\"}nohup ${argv.map(shq).join(\" \")} > ${shq(logPath)} 2>&1 < /dev/null &`;\n  return { env: l.env, argv, logPath, line };\n}\n\nexport function stopLine(cfg: LocalConfig): string {\n  return `for p in $(pgrep -f ${shq(cfg.launcher.processMatch)}); do kill $p; done`;\n}\n\nexport interface RunningServer {\n  pid: number;\n  rssKb: number;\n  port: number | null;\n  tier: string | null;\n  provider: string | null;\n}\n\n/** Parse `ps -axo pid=,rss=,command=` output for servers matching processMatch. */\nexport function parseServers(psOut: string, cfg: LocalConfig, selfPid = process.pid): RunningServer[] {\n  const out: RunningServer[] = [];\n  for (const line of psOut.split(\"\\n\")) {\n    const m = /^\\s*(\\d+)\\s+(\\d+)\\s+(.*)$/.exec(line);\n    if (!m) continue;\n    const pid = Number(m[1]);\n    const cmd = m[3];\n    if (pid === selfPid || !cmd.includes(cfg.launcher.processMatch)) continue;\n    // Skip the CLI / shells that merely mention the match string.\n    if (!/\\bserve\\b/.test(cmd) || !cmd.includes(\"--port\")) continue;\n    const pm = /--port\\s+(\\d+)/.exec(cmd);\n    const port = pm ? Number(pm[1]) : null;\n    const tier = Object.values(cfg.tiers).find((t) => t.port === port) ?? null;\n    out.push({ pid, rssKb: Number(m[2]), port, tier: tier?.tier ?? null, provider: tier?.provider ?? null });\n  }\n  return out;\n}\n\nexport function fmtRss(kb: number): string {\n  return kb >= 1048576 ? `${(kb / 1048576).toFixed(1)} GB` : `${Math.round(kb / 1024)} MB`;\n}\n\nconst norm = (s: string): string => s.toLowerCase().replace(/[^a-z0-9]/g, \"\");\n\n/** Model directories on disk: HF snapshots and LM Studio models, nothing else. */\nexport function installedModelDirs(home = homedir()): { name: string; path: string }[] {\n  const out: { name: string; path: string }[] = [];\n  const ls = (d: string): string[] => {\n    try {\n      return readdirSync(d).sort();\n    } catch {\n      return [];\n    }\n  };\n  const hub = join(home, \".cache/huggingface/hub\");\n  for (const e of ls(hub)) {\n    if (!e.startsWith(\"models--\")) continue;\n    const snap = ls(join(hub, e, \"snapshots\"))[0];\n    if (snap) out.push({ name: e.split(\"--\").slice(2).join(\"--\"), path: join(hub, e, \"snapshots\", snap) });\n  }\n  const lms = join(home, \".lmstudio/models\");\n  for (const org of ls(lms)) {\n    for (const m of ls(join(lms, org))) out.push({ name: m, path: join(lms, org, m) });\n  }\n  return out;\n}\n\n/**\n * Model path for a provider's default model name (e.g. \"qwen3-coder-30b\"):\n * the first installed directory whose name contains it, ignoring case and\n * punctuation. Null when nothing matches.\n */\nexport function findModelPath(served: string, dirs: { name: string; path: string }[] = installedModelDirs()): string | null {\n  const want = norm(served);\n  if (!want) return null;\n  return dirs.find((d) => norm(d.name).includes(want))?.path ?? null;\n}\n","/**\n * `pai worker bench` core: plan expansion, load gate and result aggregation.\n * The suite (a directory with tasks/<name>/, run.sh, grade.sh) stays project-specific; this only orchestrates.\n */\n\nimport { execFileSync, spawn } from \"node:child_process\";\nimport { existsSync, readdirSync, readFileSync, writeFileSync, appendFileSync } from \"node:fs\";\nimport { loadavg } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const PROMPTS = [\"task\", \"spec\", \"precise\"] as const;\nexport type Prompt = (typeof PROMPTS)[number];\n\nexport interface Arm {\n  task: string;\n  provider: string;\n  prompt: Prompt;\n  repeat: number;\n}\n\nexport interface GradeRow {\n  task: string;\n  provider: string;\n  prompt: Prompt;\n  verdict: string;\n  wall?: number;\n  turns?: number;\n}\n\nexport function suiteTasks(suite: string): string[] {\n  const dir = join(suite, \"tasks\");\n  return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();\n}\n\n/** task × provider × repeat, task-major so one task's arms start together. */\nexport function planArms(tasks: string[], providers: string[], prompt: Prompt, repeat: number): Arm[] {\n  const arms: Arm[] = [];\n  for (const task of tasks) for (const provider of providers) for (let r = 1; r <= repeat; r++) arms.push({ task, provider, prompt, repeat: r });\n  return arms;\n}\n\n/** Start now only when a slot is free and the machine is quiet enough. */\nexport function gateDecision(load: number, maxLoad: number, running: number, maxParallel: number): \"go\" | \"wait-slot\" | \"wait-load\" {\n  if (running >= maxParallel) return \"wait-slot\";\n  return load > maxLoad ? \"wait-load\" : \"go\";\n}\n\n/** Arm id is <task>-<provider>[-spec|-precise]-<hhmmss>; calibration rows (`fix`, `parent`) have none. */\nexport function parseRow(line: string): GradeRow | undefined {\n  const c = line.split(\"\\t\");\n  const [task, result, verdict] = c;\n  if (!task || !result || result === \"fix\" || result === \"parent\" || result === \"result\") return undefined;\n  const m = result.startsWith(`${task}-`) ? /^(.+?)(?:-(spec|precise))?-\\d{6}$/.exec(result.slice(task.length + 1)) : null;\n  if (!m) return undefined;\n  const num = (s?: string) => (s !== undefined && s !== \"\" && !Number.isNaN(Number(s)) ? Number(s) : undefined);\n  return { task, provider: m[1], prompt: (m[2] as Prompt) ?? \"task\", verdict, wall: num(c[7]), turns: num(c[8]) };\n}\n\nexport function parseRows(tsv: string): GradeRow[] {\n  return tsv.split(\"\\n\").map(parseRow).filter((r): r is GradeRow => !!r);\n}\n\nfunction median(xs: number[]): string {\n  if (!xs.length) return \"-\";\n  const s = [...xs].sort((a, b) => a - b);\n  const m = s.length >> 1;\n  return String(s.length % 2 ? s[m] : Math.round(((s[m - 1] + s[m]) / 2) * 10) / 10);\n}\n\n/** One line per provider × prompt: pass/total (void excluded), median wall minutes, median turns. */\nexport function aggregate(rows: GradeRow[]): string {\n  const groups = new Map<string, GradeRow[]>();\n  for (const r of rows) {\n    const k = `${r.provider}\\t${r.prompt}`;\n    groups.set(k, [...(groups.get(k) ?? []), r]);\n  }\n  const out = [\"| provider | prompt | pass/total | median wall (min) | median turns |\", \"| --- | --- | --- | --- | --- |\"];\n  for (const [k, rs] of [...groups].sort(([a], [b]) => a.localeCompare(b))) {\n    const graded = rs.filter((r) => r.verdict === \"pass\" || r.verdict === \"fail\");\n    const pass = graded.filter((r) => r.verdict === \"pass\").length;\n    const [provider, prompt] = k.split(\"\\t\");\n    out.push(`| ${provider} | ${prompt} | ${pass}/${graded.length} | ${median(rs.flatMap((r) => (r.wall === undefined ? [] : [r.wall])))} | ${median(rs.flatMap((r) => (r.turns === undefined ? [] : [r.turns])))} |`);\n  }\n  return out.join(\"\\n\");\n}\n\nexport function readLoad(): number {\n  return loadavg()[0];\n}\n\nexport function readThermal(): string {\n  try {\n    return execFileSync(\"notifyutil\", [\"-g\", \"com.apple.system.thermalpressurelevel\"], { encoding: \"utf-8\" }).trim().split(/\\s+/).pop() ?? \"?\";\n  } catch {\n    return \"?\";\n  }\n}\n\nfunction sh(script: string, args: string[], cwd: string, env: NodeJS.ProcessEnv): Promise<string> {\n  return new Promise((resolve) => {\n    const p = spawn(\"sh\", [script, ...args], { cwd, env, stdio: [\"ignore\", \"pipe\", \"inherit\"] });\n    let out = \"\";\n    p.stdout.on(\"data\", (d) => (out += d));\n    p.on(\"close\", () => resolve(out));\n    p.on(\"error\", () => resolve(out));\n  });\n}\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\nexport interface BenchOptions {\n  suite: string;\n  arms: Arm[];\n  maxParallel: number;\n  maxLoad: number;\n  log: (s: string) => void;\n}\n\n/** Run every arm through the suite's run.sh then grade.sh; returns this run's grade rows. */\nexport async function runBench(o: BenchOptions): Promise<GradeRow[]> {\n  const rows: GradeRow[] = [];\n  const envFile = join(o.suite, \"bench-env.tsv\");\n  if (!existsSync(envFile)) writeFileSync(envFile, \"arm\\tload\\tthermal\\n\");\n  let running = 0;\n  const jobs: Promise<void>[] = [];\n  for (const arm of o.arms) {\n    for (;;) {\n      const d = gateDecision(readLoad(), o.maxLoad, running, o.maxParallel);\n      if (d === \"go\") break;\n      if (d === \"wait-load\") o.log(`load ${readLoad().toFixed(1)} > ${o.maxLoad}: waiting before ${arm.task}/${arm.provider}`);\n      await sleep(d === \"wait-load\" ? 30_000 : 5_000);\n    }\n    running++;\n    const load = readLoad().toFixed(1);\n    const thermal = readThermal();\n    jobs.push(\n      (async () => {\n        try {\n          const env = { ...process.env, PROMPT: arm.prompt };\n          const started = await sh(join(o.suite, \"run.sh\"), [arm.task, arm.provider], o.suite, env);\n          const id = /^arm (\\S+) ended/m.exec(started)?.[1];\n          if (!id) return o.log(`${arm.task}/${arm.provider}: no arm id in run.sh output`);\n          appendFileSync(envFile, `${id}\\t${load}\\t${thermal}\\n`);\n          const graded = await sh(join(o.suite, \"grade.sh\"), [arm.task, id], o.suite, process.env);\n          const row = parseRow(graded.trim().split(\"\\n\").pop() ?? \"\");\n          if (row) rows.push(row);\n          o.log(`${id}: ${row?.verdict ?? \"ungraded\"} (load ${load}, thermal ${thermal})`);\n        } finally {\n          running--;\n        }\n      })(),\n    );\n  }\n  await Promise.all(jobs);\n  return rows;\n}\n\nexport function readSuiteRows(suite: string): GradeRow[] {\n  return parseRows(readFileSync(join(suite, \"rows.tsv\"), \"utf-8\"));\n}\n\nexport function writeResults(suite: string, rows: GradeRow[]): string {\n  const file = join(suite, \"results.md\");\n  writeFileSync(file, `# A/B results\\n\\nGenerated by \\`pai worker bench\\`; void rows excluded from pass/total.\\n\\n${aggregate(rows)}\\n`);\n  return file;\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\";\nimport { readJsonStrict, writeJsonAtomic } from \"../../config/json-store.js\";\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\n/**\n * ~/.claude/settings.json is Claude Code's own file, not ours: hooks, env,\n * permissions, statusline, enabled plugins. PAI only ever adds to it. Returning\n * {} for a damaged file and then writing meant a stray parse error could strip\n * every hook registration the user had.\n */\nexport function readSettingsJson(): Record<string, unknown> {\n  return readJsonStrict(SETTINGS_FILE, \"~/.claude/settings.json\");\n}\n\nexport function writeSettingsJson(data: Record<string, unknown>): void {\n  writeJsonAtomic(SETTINGS_FILE, data, { label: \"~/.claude/settings.json\" });\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 ${ADAPTER_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","/**\n * install.ts — `pai worker install`: one command, whole migration.\n *\n * Three things:\n *\n *   1. ~/.claude/settings.json: point the PreToolUse \"Agent\" hook at\n *      ${PAI_DIR}/Hooks/route-agents-to-worker.mjs, replacing the old\n *      route-agents-to-glm.sh registration if present.\n *   2. ~/.local/bin compatibility shims (glm, glm-run, glm-ps, glm-log) that\n *      exec the pai equivalents, so every existing habit, alias and script\n *      keeps working. Anything already at those paths is moved aside to\n *      <name>.pre-pai once — never deleted.\n *   3. Remove the old route-agents-to-glm.sh from ~/.claude/Hooks (the\n *      settings.json entry is gone, so the file is dead).\n *\n * Idempotent: running it twice changes nothing the second time.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, mkdirSync, renameSync, unlinkSync, writeFileSync, chmodSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { readSettingsJson, writeSettingsJson } from \"../cli/lib/settings-manager.js\";\n\nconst CLAUDE_DIR = join(homedir(), \".claude\");\nconst HOOKS_DIR = join(CLAUDE_DIR, \"Hooks\");\nconst LOCAL_BIN = join(homedir(), \".local\", \"bin\");\n\nexport const NEW_AGENT_HOOK = \"${PAI_DIR}/Hooks/route-agents-to-worker.mjs\";\nconst OLD_AGENT_HOOK_STEM = \"route-agents-to-glm\";\n\n/**\n * The PreToolUse matcher for the subagent gate. It covers both names the\n * harness has used for the in-process subagent tool — the current \"Agent\" and\n * the older \"Task\" — because a matcher that names only one of them leaves the\n * gate registered but never invoked after a rename.\n */\nexport const AGENT_MATCHER = \"Agent|Task\";\n\n/** Matchers a previous PAI version wrote for this same hook. */\nconst LEGACY_AGENT_MATCHERS = [\"Agent\", \"Task\"];\n\nexport interface InstallResult {\n  lines: string[];\n  changed: boolean;\n}\n\n/** Absolute path of the pai executable to hard-wire into the shims. */\nfunction paiPath(): string {\n  try {\n    return execFileSync(\"/usr/bin/which\", [\"pai\"], { encoding: \"utf8\" }).trim() || \"pai\";\n  } catch {\n    return \"pai\";\n  }\n}\n\n// ---------------------------------------------------------------------------\n// 1. settings.json: Agent hook migration\n// ---------------------------------------------------------------------------\n\n/**\n * Bring the subagent-gate registration up to date inside an already-read\n * settings object. Pure: it mutates `settings` and reports, the caller decides\n * whether to write. Exported so the registration can be tested against a copy\n * of a real settings.json instead of the live file.\n *\n * Three things must hold afterwards, and each one has been wrong at some point:\n *   - the rule exists,\n *   - its matcher covers every subagent tool name (AGENT_MATCHER), not just\n *     the one the harness happened to emit when it was written,\n *   - its command points at the current hook.\n */\nexport function patchAgentHookSettings(\n  settings: Record<string, unknown>,\n  lines: string[]\n): boolean {\n  const hooks = (typeof settings[\"hooks\"] === \"object\" && settings[\"hooks\"] !== null\n    ? settings[\"hooks\"]\n    : {}) as Record<string, unknown>;\n\n  const preToolUse = Array.isArray(hooks[\"PreToolUse\"])\n    ? (hooks[\"PreToolUse\"] as Array<Record<string, unknown>>)\n    : [];\n\n  let changed = false;\n  let found = false;\n\n  for (const rule of preToolUse) {\n    const matcher = typeof rule[\"matcher\"] === \"string\" ? rule[\"matcher\"] : \"\";\n    if (matcher !== AGENT_MATCHER && !LEGACY_AGENT_MATCHERS.includes(matcher)) continue;\n\n    const entries = Array.isArray(rule[\"hooks\"])\n      ? (rule[\"hooks\"] as Array<Record<string, unknown>>)\n      : [];\n    let ours = false;\n    for (const entry of entries) {\n      const cmd = typeof entry[\"command\"] === \"string\" ? entry[\"command\"] : \"\";\n      if (cmd === NEW_AGENT_HOOK) ours = true;\n      if (cmd.includes(OLD_AGENT_HOOK_STEM)) {\n        entry[\"command\"] = NEW_AGENT_HOOK;\n        lines.push(`settings.json: subagent hook migrated to ${NEW_AGENT_HOOK}`);\n        changed = true;\n        ours = true;\n      }\n    }\n    if (!ours) continue; // someone else's rule on the same matcher — leave it\n\n    found = true;\n    if (matcher !== AGENT_MATCHER) {\n      rule[\"matcher\"] = AGENT_MATCHER;\n      lines.push(`settings.json: subagent matcher widened \"${matcher}\" → \"${AGENT_MATCHER}\"`);\n      changed = true;\n    }\n  }\n\n  if (!found) {\n    preToolUse.push({\n      matcher: AGENT_MATCHER,\n      hooks: [{ type: \"command\", command: NEW_AGENT_HOOK }],\n    });\n    lines.push(`settings.json: subagent hook added (${AGENT_MATCHER}) → ${NEW_AGENT_HOOK}`);\n    changed = true;\n  }\n\n  if (changed) {\n    hooks[\"PreToolUse\"] = preToolUse;\n    settings[\"hooks\"] = hooks;\n  } else {\n    lines.push(`settings.json: subagent hook already current`);\n  }\n  return changed;\n}\n\nfunction patchAgentHook(lines: string[]): boolean {\n  const settings = readSettingsJson();\n  const changed = patchAgentHookSettings(settings, lines);\n  if (changed) writeSettingsJson(settings);\n  return changed;\n}\n\n// ---------------------------------------------------------------------------\n// 2. ~/.local/bin shims\n// ---------------------------------------------------------------------------\n\nconst GLM_PS_SHIM = (pai: string) => `# pai worker shim — replaces the python glm-ps of the same name\ncase \"\\${1-}\" in\n  \"\"|\"ps\") exec ${pai} worker ps ;;\n  follow|watch|pane|log) exec ${pai} worker \"$@\" ;;\n  --status) shift; exec ${pai} worker status-line \"\\${ITERM_SESSION_ID:-}\" \"\\${PWD:-$HOME}\" ;;\n  -*) exec ${pai} worker ps \"$@\" ;;\n  *) exec ${pai} worker replay \"$@\" ;;\nesac\n`;\n\n/**\n * Names whose shim starts the harness itself on that provider (`pai launch`)\n * rather than a subagent worker — the historical meaning of typing \"glm\":\n * an interactive session pinned to the glm endpoint, not whatever provider\n * happens to be `active` in workers.yaml right now.\n */\nconst LAUNCH_SHIM_PROVIDERS = [\"glm\", \"kimi\"];\n\nexport function shim(name: string, pai: string): string {\n  if (name === \"glm-ps\") return GLM_PS_SHIM(pai);\n  if (name === \"glm-log\") return `#!/bin/sh\\n# pai worker shim — replaces the shell glm-log of the same name\\nexec ${pai} worker log \"$@\"\\n`;\n  if (name === \"worker-say\") return `#!/bin/sh\\n# pai worker shim — say one line to a running worker\\nexec ${pai} worker say \"$@\"\\n`;\n  if (LAUNCH_SHIM_PROVIDERS.includes(name)) {\n    return `#!/bin/sh\\n# pai launch shim — starts an interactive Claude Code session pinned to the ${name} provider\\nexec ${pai} launch --provider ${name} \"$@\"\\n`;\n  }\n  // glm-run: pass everything through to a headless/worker run; `pai worker\n  // run` forwards unknown options to claude and drops --output-format/\n  // --verbose itself\n  return `#!/bin/sh\\n# pai worker shim — replaces the previous ${name} of the same name\\nexec ${pai} worker run \"$@\"\\n`;\n}\n\nfunction installShims(lines: string[]): boolean {\n  const pai = paiPath();\n  let changed = false;\n  for (const name of [\"glm\", \"glm-run\", \"glm-ps\", \"glm-log\", \"worker-say\", \"kimi\"]) {\n    const path = join(LOCAL_BIN, name);\n    const ours = `# pai worker shim`;\n    let current: string | null = null;\n    if (existsSync(path)) {\n      try {\n        current = execFileSync(\"head\", [\"-c\", \"64\", path], { encoding: \"utf8\" });\n      } catch {\n        current = null;\n      }\n    }\n    if (current !== null && !current.includes(ours)) {\n      // one-time move-aside; never clobber what a later run moved there\n      const aside = join(LOCAL_BIN, `${name}.pre-pai`);\n      if (!existsSync(aside)) {\n        renameSync(path, aside);\n        lines.push(`${name}: previous version kept as ${name}.pre-pai`);\n      } else {\n        unlinkSync(path);\n        lines.push(`${name}: replaced (${name}.pre-pai already exists)`);\n      }\n      changed = true;\n    } else if (current === null) {\n      changed = true;\n    } else {\n      lines.push(`${name}: shim already current`);\n      continue;\n    }\n    const body = shim(name, pai);\n    mkdirSync(LOCAL_BIN, { recursive: true });\n    writeFileSync(path, body, { encoding: \"utf8\", mode: 0o755 });\n    try {\n      chmodSync(path, 0o755);\n    } catch {\n      /* mode set on create where supported */\n    }\n    const target = LAUNCH_SHIM_PROVIDERS.includes(name) ? `${pai} launch --provider ${name}` : `${pai} worker …`;\n    lines.push(`${name}: shim installed → ${target}`);\n  }\n  return changed;\n}\n\n// ---------------------------------------------------------------------------\n// 3. old hook file\n// ---------------------------------------------------------------------------\n\nfunction removeOldHook(lines: string[]): boolean {\n  const old = join(HOOKS_DIR, \"route-agents-to-glm.sh\");\n  if (!existsSync(old)) return false;\n  unlinkSync(old);\n  lines.push(`removed ${OLD_AGENT_HOOK_STEM}.sh (registration migrated above)`);\n  return true;\n}\n\n// ---------------------------------------------------------------------------\n\nexport function installWorkers(): InstallResult {\n  const lines: string[] = [];\n  let changed = patchAgentHook(lines);\n  if (installShims(lines)) changed = true;\n  if (removeOldHook(lines)) changed = true;\n  if (!existsSync(LOCAL_BIN)) {\n    lines.push(`note: ${LOCAL_BIN} did not exist; make sure it is on PATH`);\n  }\n  return { lines, changed };\n}\n","/**\n * `pai worker` configuration commands: providers, classes (alias roles), config,\n * local, bench, policy, fallback, pool, proxy, mcp, on, off, install, controls.\n * Names, flags and stdout match the old CLI (hooks, prompts and MCP tools call\n * them). `pai providers ...` shares the provider subcommands defined here.\n */\n\nimport { duplicateModelIds } from \"../../router/router.js\";\nimport { execFileSync, spawn } from \"node:child_process\";\nimport { closeSync, existsSync, mkdirSync, openSync, readFileSync, statSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { parseDocument } from \"yaml\";\nimport { CONFIG_FILE } from \"../../daemon/config.js\";\nimport { PROVIDER_TAGS, WorkersConfigError, expandHome, readWorkersSection, type ClassTarget } from \"../../workers/config.js\";\nimport { workersLogDir } from \"../../workers/paths.js\";\nimport {\n  inlineWorkersYamlKeys,\n  initWorkersYaml,\n  migrateWorkersToYaml,\n  needsWorkersYamlRelocation,\n  parseWorkersYamlDocument,\n  relocateWorkersYaml,\n  workersYamlLegacyNotice,\n  workersYamlPath,\n} from \"../../workers/workers-config.js\";\nimport { testProvider } from \"../../workers/run.js\";\nimport {\n  addProvider,\n  classTargetText,\n  describeProviders,\n  removeProvider,\n  setClass,\n  setProviderEnabled,\n  setWorkersEnabled,\n  unsetClass,\n  updateProvider,\n} from \"../../workers/providers.js\";\nimport { fmtRss, LocalTierError, parseServers, planStart, readLocalConfig, stopLine, type LocalConfig, type LocalTier, type RunningServer, type StartPlan } from \"../../workers/local-tiers.js\";\nimport { PROMPTS, aggregate, planArms, readSuiteRows, runBench, suiteTasks, writeResults, type Prompt } from \"../../workers/bench.js\";\nimport {\n  POLICY_MODES,\n  clearPolicy,\n  findLocal,\n  onBattery,\n  parseDuration,\n  readPolicy,\n  routeRun,\n  writePolicy,\n  type PolicyMode,\n} from \"../../workers/policy.js\";\nimport { fallbackOff, fallbackOn, fallbackStatus, fallbackStatusText } from \"../../workers/fallback.js\";\nimport { installWorkers } from \"../../workers/install.js\";\nimport { describeMcp } from \"../../workers/mcp.js\";\nimport { clickrControlsArgv, markControlsHeld } from \"../../workers/controls.js\";\nimport { loadStatus } from \"../../workers/status.js\";\nimport { DEFAULT_PROXY_PORT, ensureProxyRunning, stopProxy } from \"../../workers/proxy/server.js\";\nimport { CliError, type CommandDef, type OptDef } from \"../define.js\";\nimport { dim, err, ok, warn } from \"../lib/utils.js\";\nimport { useCommand } from \"./providers.js\";\n\nconst out = (s = \"\") => void process.stdout.write(s + \"\\n\");\n\n/** Turn any failure into a plain sentence plus the command to try next. */\nexport function fail(e: unknown, hint?: string): never {\n  if (e instanceof CliError) throw e;\n  throw new CliError(e instanceof Error ? e.message : String(e), hint);\n}\n\nconst logDir = (): string => workersLogDir(readWorkersSection().workers);\n\n// ---------------------------------------------------------------------------\n// Value parsers (same rules and messages as the old CLI)\n// ---------------------------------------------------------------------------\n\nexport function collectEnv(pairs: string[] | undefined): Record<string, string> {\n  const o: Record<string, string> = {};\n  for (const p of pairs ?? []) {\n    const eq = p.indexOf(\"=\");\n    if (eq <= 0) throw new WorkersConfigError(`--env expects NAME=VALUE, got \"${p}\"`);\n    o[p.slice(0, eq)] = p.slice(eq + 1);\n  }\n  return o;\n}\n\nexport function parseCostTier(v: string): number {\n  const n = parseInt(v, 10);\n  if (!Number.isInteger(n) || n < 1 || n > 5) {\n    throw new WorkersConfigError(`cost tier must be an integer 1 (cheapest) … 5 (most expensive), got \"${v}\"`);\n  }\n  return n;\n}\n\nexport function collectTags(vals: string[] | undefined): string[] {\n  const o: string[] = [];\n  for (const v of vals ?? []) {\n    for (const t of v.split(\",\").map((s) => s.trim()).filter(Boolean)) {\n      if (!(PROVIDER_TAGS as readonly string[]).includes(t)) {\n        throw new WorkersConfigError(`\"${t}\" is not a tag (from: ${PROVIDER_TAGS.join(\", \")})`);\n      }\n      if (!o.includes(t)) o.push(t);\n    }\n  }\n  return o;\n}\n\nconst csv = (v: string) => v.split(\",\").map((s) => s.trim()).filter(Boolean);\n\nexport interface ClassSetOpts {\n  provider?: string;\n  mcp?: string;\n  maxCostTier?: number;\n  requireTags?: string[];\n  order?: string;\n}\n\n/** Positional target + constraint flags → a ClassTarget for setClass. */\nexport function buildClassTarget(target: string | undefined, opts: ClassSetOpts): ClassTarget {\n  const hasConstraints =\n    opts.provider !== undefined || opts.mcp !== undefined || opts.maxCostTier !== undefined ||\n    opts.requireTags !== undefined || opts.order !== undefined;\n  if (target !== undefined && !hasConstraints) return target;\n  const obj: Exclude<ClassTarget, string> = {};\n  if (target !== undefined) obj.provider = target.split(\"/\")[0];\n  if (opts.provider !== undefined) obj.provider = opts.provider;\n  if (opts.mcp !== undefined) obj.mcp = csv(opts.mcp);\n  if (opts.maxCostTier !== undefined) obj.maxCostTier = opts.maxCostTier;\n  if (opts.requireTags !== undefined) obj.requireTags = opts.requireTags;\n  if (opts.order !== undefined) obj.order = csv(opts.order);\n  if (target !== undefined && target.includes(\"/\") && obj.provider) {\n    const alias = target.split(\"/\")[1];\n    if (alias && alias !== \"default\") {\n      throw new WorkersConfigError(\n        `a provider alias (\"${alias}\") cannot be combined with constraint flags — use the plain \"<target>\" form for pinning`\n      );\n    }\n  }\n  if (!Object.keys(obj).length) {\n    throw new WorkersConfigError(\"give a target (provider, provider/fast) or a constraint (--max-cost-tier, --require-tags, …)\");\n  }\n  return obj;\n}\n\n// ---------------------------------------------------------------------------\n// providers\n// ---------------------------------------------------------------------------\n\nconst PROVIDERS_HINT = \"pai worker providers\";\nconst tagsHelp = `Capability tags, comma-separated (from: ${PROVIDER_TAGS.join(\", \")})`;\nconst costTierOpt: OptDef = { name: \"cost-tier\", type: \"string\", value: \"1-5\", summary: \"Cost tier: 1 (cheapest) to 5 (most expensive; default 3)\" };\n\nfunction printProviders(): void {\n  const { workers } = readWorkersSection();\n  out();\n  for (const line of describeProviders(workers)) out(`  ${line}`);\n  out();\n  out(dim(`  workers are ${workers.enabled ? \"on\" : \"off\"} — pai worker ${workers.enabled ? \"off\" : \"on\"}`));\n  const legacyNotice = workersYamlLegacyNotice();\n  if (legacyNotice) out(dim(`  ${legacyNotice}`));\n  out();\n}\n\nconst providersListCommand: CommandDef = {\n  name: \"list\",\n  summary: \"List providers, their models and whether workers are on\",\n  description: \"Shows every configured provider with its models, cost tier and state. This is also what `pai worker providers` prints on its own.\",\n  examples: [\"pai worker providers list\", \"pai worker providers\"],\n  seeAlso: [\"pai providers\", \"pai worker providers add\"],\n  run() {\n    try {\n      printProviders();\n    } catch (e) {\n      fail(e, \"pai worker config check\");\n    }\n  },\n};\n\nconst providersAddCommand: CommandDef = {\n  name: \"add\",\n  summary: \"Add a provider (the first one also turns workers on)\",\n  description:\n    \"Adds a provider that workers can run on. The first provider you add also turns workers on and sets up the default classes.\\n\" +\n    \"Give the API token with --key (stored in workers.yaml, file kept private) or --key-file (path to a private file holding it), not both.\\n\" +\n    \"OpenAI-style APIs: --protocol openai --upstream-url URL (runs through the local proxy).\\n\" +\n    \"ChatGPT plan through the Codex CLI: --engine codex. Image generation: --engine image.\",\n  args: [{ name: \"NAME\", required: true, summary: \"Name for the new provider, e.g. glm\" }],\n  options: [\n    { name: \"base-url\", type: \"string\", value: \"URL\", summary: \"Anthropic-compatible API base URL (required unless --protocol openai)\" },\n    { name: \"url\", type: \"string\", value: \"URL\", summary: \"Same as --base-url\" },\n    { name: \"model\", type: \"string\", value: \"ID\", summary: \"Default model id for this provider (required)\" },\n    { name: \"key\", type: \"string\", value: \"TOKEN\", summary: \"API token, written into workers.yaml; use instead of --key-file\" },\n    { name: \"key-file\", type: \"string\", value: \"PATH\", summary: \"File holding the API token (kept private); leave out for a local server\" },\n    { name: \"fast-model\", type: \"string\", value: \"ID\", summary: \"Cheaper model for quick checks and routing\" },\n    { name: \"env\", type: \"string\", value: \"NAME=VALUE\", repeatable: true, summary: \"Extra environment variable for runs (repeat for several)\" },\n    { name: \"note\", type: \"string\", value: \"TEXT\", summary: \"Short note shown in the provider list\" },\n    { name: \"protocol\", type: \"string\", value: \"KIND\", summary: \"anthropic (default) or openai\" },\n    { name: \"upstream-url\", type: \"string\", value: \"URL\", summary: \"Chat Completions base URL (required for --protocol openai)\" },\n    { name: \"engine\", type: \"string\", value: \"KIND\", summary: \"claude (default), codex, or image\" },\n    { name: \"context-window\", type: \"number\", value: \"TOKENS\", summary: \"Context window for the usage meter (default 200000)\" },\n    { name: \"quota-probe\", type: \"string\", value: \"URL\", summary: \"URL whose JSON first number is the quota percent (0-100)\" },\n    { ...costTierOpt },\n    { name: \"tags\", type: \"string\", value: \"LIST\", repeatable: true, summary: tagsHelp },\n  ],\n  examples: [\n    \"pai worker providers add glm --url https://api.example.invalid/anthropic --key-file ~/glm.key --model glm-5.3 --fast-model glm-5.3-flash\",\n    \"pai worker providers add local --protocol openai --upstream-url http://127.0.0.1:8080/v1 --model qwen\",\n  ],\n  seeAlso: [\"pai worker providers use\", \"pai worker providers list\"],\n  run(ctx) {\n    try {\n      const o = ctx.opts;\n      const name = ctx.args.NAME as string;\n      const protocol = o.protocol as \"anthropic\" | \"openai\" | undefined;\n      const engine = o.engine as \"claude\" | \"codex\" | \"image\" | undefined;\n      if (o.model === undefined) throw new WorkersConfigError(\"--model is required (the default model id for this provider)\");\n      if (protocol && protocol !== \"anthropic\" && protocol !== \"openai\") {\n        throw new WorkersConfigError(`--protocol must be anthropic or openai, got \"${protocol}\"`);\n      }\n      if (engine && engine !== \"claude\" && engine !== \"codex\" && engine !== \"image\") {\n        throw new WorkersConfigError(`--engine must be claude, codex, or image, got \"${engine}\"`);\n      }\n      const baseUrl = (o[\"base-url\"] ?? o.url) as string | undefined;\n      if (protocol !== \"openai\" && !baseUrl) {\n        throw new WorkersConfigError(\"--base-url (or --url) is required (only --protocol openai goes without it)\");\n      }\n      if (o.key && o[\"key-file\"]) throw new WorkersConfigError(\"--key and --key-file are alternatives — pass one, not both\");\n      const costTier = o[\"cost-tier\"] !== undefined ? parseCostTier(String(o[\"cost-tier\"])) : undefined;\n      const tags = collectTags(ctx.multi?.tags);\n      addProvider({\n        name,\n        baseUrl: baseUrl ?? \"\",\n        keyFile: (o[\"key-file\"] as string | undefined) ?? null,\n        ...(o.key ? { inlineKey: o.key as string } : {}),\n        model: o.model as string,\n        fastModel: o[\"fast-model\"] as string | undefined,\n        env: collectEnv(ctx.multi?.env),\n        note: o.note as string | undefined,\n        ...(protocol ? { protocol } : {}),\n        ...(o[\"upstream-url\"] ? { upstreamUrl: o[\"upstream-url\"] as string } : {}),\n        ...(engine ? { engine } : {}),\n        ...(o[\"context-window\"] ? { contextWindow: o[\"context-window\"] as number } : {}),\n        quotaProbe: o[\"quota-probe\"] as string | undefined,\n        ...(costTier !== undefined ? { costTier } : {}),\n        ...(tags.length ? { tags } : {}),\n      });\n      const { workers } = readWorkersSection();\n      out(ok(`provider ${name} added`));\n      if (workers.active === name) out(`  active: ${name} — workers ${workers.enabled ? \"on\" : \"off\"}`);\n      for (const line of describeProviders(workers)) out(dim(`  ${line}`));\n    } catch (e) {\n      fail(e, PROVIDERS_HINT);\n    }\n  },\n};\n\nconst providersUpdateCommand: CommandDef = {\n  name: \"update\",\n  summary: \"Change a provider's cost tier and tags\",\n  description: \"Routing constraints (--max-cost-tier, --require-tags on a class) look at these two settings.\",\n  args: [{ name: \"NAME\", required: true, summary: \"Provider to change\" }],\n  options: [\n    { ...costTierOpt },\n    { name: \"tags\", type: \"string\", value: \"LIST\", repeatable: true, summary: `${tagsHelp}; --tags '' clears them` },\n  ],\n  examples: [\"pai worker providers update glm --cost-tier 2\", \"pai worker providers update glm --tags long-context,reasoning\"],\n  seeAlso: [\"pai worker providers list\"],\n  run(ctx) {\n    try {\n      const name = ctx.args.NAME as string;\n      const hasTier = ctx.opts[\"cost-tier\"] !== undefined;\n      const hasTags = ctx.multi?.tags !== undefined;\n      if (!hasTier && !hasTags) throw new WorkersConfigError(\"nothing to update — pass --cost-tier and/or --tags\");\n      const costTier = hasTier ? parseCostTier(String(ctx.opts[\"cost-tier\"])) : undefined;\n      const tags = hasTags ? collectTags(ctx.multi!.tags) : undefined;\n      updateProvider(name, { ...(costTier !== undefined ? { costTier } : {}), ...(tags !== undefined ? { tags } : {}) });\n      out(ok(`provider ${name} updated`));\n      const { workers } = readWorkersSection();\n      for (const line of describeProviders(workers)) out(dim(`  ${line}`));\n    } catch (e) {\n      fail(e, PROVIDERS_HINT);\n    }\n  },\n};\n\nconst providersRemoveCommand: CommandDef = {\n  name: \"remove\",\n  summary: \"Remove a provider and the classes pointing at it\",\n  description: \"Deletes the provider from workers.yaml. Classes pinned to it are removed too.\",\n  args: [{ name: \"NAME\", required: true, summary: \"Provider to remove\" }],\n  examples: [\"pai worker providers remove glm\", \"pai worker providers list\"],\n  seeAlso: [\"pai worker providers add\"],\n  run(ctx) {\n    try {\n      removeProvider(ctx.args.NAME as string);\n      out(ok(`provider ${ctx.args.NAME} removed`));\n    } catch (e) {\n      fail(e, PROVIDERS_HINT);\n    }\n  },\n};\n\nconst enableDisable = (enable: boolean): CommandDef => ({\n  name: enable ? \"enable\" : \"disable\",\n  summary: enable ? \"Turn a provider on (also clears its cooldown)\" : \"Turn a provider off (automatic routing skips it)\",\n  description: enable\n    ? \"Makes the provider usable again and clears any cooldown after hitting a limit.\"\n    : \"Automatic routing skips a disabled provider. Naming it with --provider still works.\",\n  args: [{ name: \"NAME\", required: true, summary: \"Provider name\" }],\n  examples: [`pai worker providers ${enable ? \"enable\" : \"disable\"} glm`, \"pai worker providers list\"],\n  seeAlso: [`pai worker providers ${enable ? \"disable\" : \"enable\"}`],\n  run(ctx) {\n    try {\n      setProviderEnabled(ctx.args.NAME as string, enable);\n      out(ok(`provider ${ctx.args.NAME} ${enable ? \"enabled\" : \"disabled\"}`));\n    } catch (e) {\n      fail(e, PROVIDERS_HINT);\n    }\n  },\n});\n\nconst providersTestCommand: CommandDef = {\n  name: \"test\",\n  summary: \"Send one word through a provider to check it answers\",\n  description: \"Sends a tiny prompt and prints the reply and the time it took. Exits 1 if the provider does not answer. Without a name it tests the active provider.\",\n  args: [{ name: \"NAME\", summary: \"Provider to test (default: the active one)\" }],\n  examples: [\"pai worker providers test\", \"pai worker providers test glm\"],\n  seeAlso: [\"pai worker providers list\"],\n  async run(ctx) {\n    try {\n      const name = ctx.args.NAME as string | undefined;\n      const { workers } = readWorkersSection();\n      const pname = name ?? workers.active ?? \"\";\n      if (!pname || !workers.providers[pname]) {\n        throw new WorkersConfigError(name ? `no provider named \"${name}\"` : \"no active provider — name one explicitly\");\n      }\n      out(dim(`  probing ${pname} (${workers.providers[pname].models.default}) …`));\n      const r = await testProvider(pname, workers.providers[pname], workersLogDir(workers), undefined, workers.caveman);\n      const head = `${r.provider} ${r.model}  ${(r.latencyMs / 1000).toFixed(1)}s`;\n      if (r.skipped) {\n        out(dim(`  ${head}  ${r.skipped}`));\n        out(`  reply: ${r.result.slice(0, 200)}`);\n        return 0;\n      }\n      out(r.ok ? ok(`  ${head}`) : err(`  ${head}`));\n      out(`  reply: ${r.result.slice(0, 200)}`);\n      return r.ok ? 0 : 1;\n    } catch (e) {\n      fail(e, PROVIDERS_HINT);\n    }\n  },\n};\n\n/** The provider subcommands except `use` (shared with `pai providers`, see providers.ts). */\nexport const PROVIDER_EDIT_COMMANDS: CommandDef[] = [\n  providersAddCommand,\n  providersUpdateCommand,\n  providersRemoveCommand,\n  enableDisable(true),\n  enableDisable(false),\n  providersTestCommand,\n];\n\nexport const workerProvidersCommand: CommandDef = {\n  name: \"providers\",\n  summary: \"Providers workers can run on: list, add, remove, use, enable, disable, test\",\n  description:\n    \"Without a subcommand, lists the providers. `use` makes one the active provider (the same command as `pai providers use`).\\n\" +\n    \"The configuration lives in workers.yaml (see `pai worker config path`).\",\n  examples: [\"pai worker providers\", \"pai worker providers use glm\", \"pai worker providers test glm\"],\n  seeAlso: [\"pai providers\", \"pai worker classes\", \"pai worker config\"],\n  subcommands: [providersListCommand, useCommand, ...PROVIDER_EDIT_COMMANDS],\n  run: providersListCommand.run,\n};\n\n// ---------------------------------------------------------------------------\n// classes (alias: roles)\n// ---------------------------------------------------------------------------\n\nfunction printClasses(): void {\n  const { workers } = readWorkersSection();\n  const entries = Object.entries(workers.classes);\n  if (!entries.length) return void out(dim(\"  no classes set — runs use the active provider (or auto routing)\"));\n  for (const [cls, target] of entries) out(`  ${cls.padEnd(12)} ${classTargetText(target)}`);\n}\n\nconst classListCommand: CommandDef = {\n  name: \"list\",\n  summary: \"List classes and where each one runs\",\n  description: \"A class is a kind of job (draft, implement, review, …). This shows the provider each one is sent to.\",\n  examples: [\"pai worker classes list\", \"pai worker classes\"],\n  seeAlso: [\"pai worker classes set\"],\n  run() {\n    try {\n      printClasses();\n    } catch (e) {\n      fail(e, \"pai worker config check\");\n    }\n  },\n};\n\nconst classSetCommand: CommandDef = {\n  name: \"set\",\n  summary: \"Send a class to a provider, or give it routing limits\",\n  description:\n    \"Point a class at a provider (or provider/fast for its cheap model), or give only limits and let routing choose, for example:\\n\" +\n    \"pai worker classes set research --max-cost-tier 2 --require-tags long-context,reasoning\",\n  args: [\n    { name: \"CLASS\", required: true, summary: \"Class name, e.g. draft, implement, review\" },\n    { name: \"TARGET\", summary: \"Provider, or provider/fast\" },\n  ],\n  options: [\n    { name: \"provider\", type: \"string\", value: \"NAME\", summary: \"Pin the class to this provider (object form)\" },\n    { name: \"mcp\", type: \"string\", value: \"NAMES\", summary: \"MCP servers or sets for runs of this class (comma-separated)\" },\n    { name: \"max-cost-tier\", type: \"string\", value: \"1-5\", summary: \"Automatic routing considers only providers up to this cost tier\" },\n    { name: \"require-tags\", type: \"string\", value: \"LIST\", summary: \"Automatic routing needs these tags (comma-separated)\" },\n    { name: \"order\", type: \"string\", value: \"LIST\", summary: \"Provider order for this class (comma-separated)\" },\n  ],\n  examples: [\"pai worker classes set draft glm\", \"pai worker classes set research --max-cost-tier 2 --require-tags long-context\"],\n  seeAlso: [\"pai worker classes unset\", \"pai worker classes list\"],\n  run(ctx) {\n    try {\n      const o = ctx.opts;\n      const built = buildClassTarget(ctx.args.TARGET as string | undefined, {\n        provider: o.provider as string | undefined,\n        mcp: o.mcp as string | undefined,\n        maxCostTier: o[\"max-cost-tier\"] !== undefined ? parseCostTier(String(o[\"max-cost-tier\"])) : undefined,\n        requireTags: o[\"require-tags\"] !== undefined ? csv(String(o[\"require-tags\"])) : undefined,\n        order: o.order as string | undefined,\n      });\n      setClass(ctx.args.CLASS as string, built);\n      out(ok(`class ${ctx.args.CLASS} → ${classTargetText(built)}`));\n    } catch (e) {\n      fail(e, \"pai worker classes list\");\n    }\n  },\n};\n\nconst classUnsetCommand: CommandDef = {\n  name: \"unset\",\n  summary: \"Remove a class (runs then use the active provider)\",\n  description: \"Deletes the class setting. Jobs of that class go to the active provider.\",\n  args: [{ name: \"CLASS\", required: true, summary: \"Class to remove\" }],\n  examples: [\"pai worker classes unset draft\", \"pai worker classes list\"],\n  seeAlso: [\"pai worker classes set\"],\n  run(ctx) {\n    try {\n      unsetClass(ctx.args.CLASS as string);\n      out(ok(`class ${ctx.args.CLASS} removed`));\n    } catch (e) {\n      fail(e, \"pai worker classes list\");\n    }\n  },\n};\n\nexport const workerClassesCommand: CommandDef = {\n  name: \"classes\",\n  summary: \"Which provider serves draft, implement, review, …\",\n  description: \"Classes are kinds of worker jobs. Each can be pinned to a provider or given routing limits. Without a subcommand, lists them.\",\n  examples: [\"pai worker classes\", \"pai worker classes set review kimi\"],\n  seeAlso: [\"pai worker providers\", \"pai worker policy\"],\n  subcommands: [classListCommand, classSetCommand, classUnsetCommand],\n  run: classListCommand.run,\n};\n\nexport const workerRolesCommand: CommandDef = {\n  ...workerClassesCommand,\n  name: \"roles\",\n  summary: \"Old name of `classes`\",\n  description: \"Roles were renamed to classes. Same data, kept so old scripts keep working.\",\n  hidden: true,\n  examples: [\"pai worker roles\", \"pai worker classes\"],\n};\n\n// ---------------------------------------------------------------------------\n// config\n// ---------------------------------------------------------------------------\n\nconst CONFIG_HINT = \"pai worker config check\";\n\nconst configPathCommand: CommandDef = {\n  name: \"path\",\n  summary: \"Print where workers.yaml is\",\n  description: \"Prints the path of the file that holds your providers and classes.\",\n  examples: [\"pai worker config path\", \"pai worker config check\"],\n  seeAlso: [\"pai worker config check\"],\n  run() {\n    out(workersYamlPath());\n  },\n};\n\nconst configInitCommand: CommandDef = {\n  name: \"init\",\n  summary: \"Write a commented starter workers.yaml\",\n  description: \"Creates workers.yaml with explanations. Refuses if one already exists.\",\n  examples: [\"pai worker config init\", \"pai worker config path\"],\n  seeAlso: [\"pai worker config check\"],\n  run() {\n    try {\n      out(ok(`wrote ${initWorkersYaml()}`));\n    } catch (e) {\n      fail(e, \"pai worker config path\");\n    }\n  },\n};\n\nconst configMigrateCommand: CommandDef = {\n  name: \"migrate\",\n  summary: \"Move or build workers.yaml from an older setup\",\n  description:\n    \"Does one of two things, chosen from what is on disk:\\n\" +\n    \"  - workers.yaml is still at an old location: moved unchanged to the current one; the old file is renamed to workers.yaml.migrated-DATE.\\n\" +\n    \"  - no workers.yaml yet: built from the workers section of config.json, which is backed up to workers.json.migrated-DATE.\",\n  options: [\n    { name: \"force\", type: \"boolean\", summary: \"Overwrite an existing workers.yaml\" },\n    { name: \"dry-run\", type: \"boolean\", summary: \"Print the plan without writing anything\" },\n  ],\n  examples: [\"pai worker config migrate --dry-run\", \"pai worker config migrate\"],\n  seeAlso: [\"pai worker config check\"],\n  run(ctx) {\n    try {\n      const force = ctx.opts.force === true;\n      if (needsWorkersYamlRelocation()) {\n        const r = relocateWorkersYaml({ force, dryRun: ctx.dryRun });\n        if (r.dryRun) return void out(dim(`# would move ${r.fromPath} → ${r.toPath}`));\n        return void out(ok(`moved ${r.fromPath} → ${r.toPath}`));\n      }\n      const r = migrateWorkersToYaml(CONFIG_FILE, { force, dryRun: ctx.dryRun });\n      if (r.dryRun) {\n        out(dim(`# would write ${r.yamlPath}:`));\n        out(r.yamlText);\n        return;\n      }\n      out(ok(`wrote ${r.yamlPath}`));\n      out(`  JSON workers section backed up to ${r.backupPath}`);\n      out();\n      const { workers } = readWorkersSection(CONFIG_FILE);\n      for (const line of describeProviders(workers)) out(`  ${line}`);\n    } catch (e) {\n      fail(e, \"pai worker config migrate --dry-run\");\n    }\n  },\n};\n\nconst configCheckCommand: CommandDef = {\n  name: \"check\",\n  summary: \"Check workers.yaml for mistakes\",\n  description: \"Validates workers.yaml (or the file at PATH). On an error it exits 1 and names the file and line.\",\n  args: [{ name: \"PATH\", summary: \"File to check (default: your workers.yaml)\" }],\n  examples: [\"pai worker config check\", \"pai worker config check ./workers.yaml\"],\n  seeAlso: [\"pai worker config path\"],\n  run(ctx) {\n    try {\n      const target = (ctx.args.PATH as string | undefined) ?? workersYamlPath();\n      if (!existsSync(target)) return void out(dim(`no workers.yaml at ${target} — JSON config fallback is in effect`));\n      const doc = parseDocument(readFileSync(target, \"utf8\"));\n      if (doc.errors.length) throw new WorkersConfigError(`${target}: ${doc.errors[0]!.message}`);\n      const data = parseWorkersYamlDocument(doc, target);\n      out(ok(`${target} OK — ${Object.keys(data.providers).length} provider(s), ${Object.keys(data.classes).length} class(es)`));\n      const both = Object.entries(data.providers).filter(([, p]) => p.key && p.keyFile).map(([n]) => n);\n      if (both.length) out(dim(`  note: ${both.join(\", \")} — both key and key_file set; key wins`));\n      for (const d of duplicateModelIds(data.providers)) {\n        out(warn(`  warning: model id ${d.model} is declared by ${d.providers.join(\", \")} — the router prefers the one that is up; give them distinct ids or disable one`));\n      }\n      const mode = statSync(target).mode & 0o777;\n      if (Object.values(data.providers).some((p) => p.key) && mode !== 0o600) {\n        out(warn(`  warning: ${target} contains a key: field but is mode ${mode.toString(8)} — chmod 600 ${target}`));\n      }\n      const legacyNotice = workersYamlLegacyNotice();\n      if (legacyNotice) out(dim(`  ${legacyNotice}`));\n    } catch (e) {\n      fail(e, \"pai worker config path\");\n    }\n  },\n};\n\nconst configInlineKeysCommand: CommandDef = {\n  name: \"inline-keys\",\n  summary: \"Move key files' contents into workers.yaml\",\n  description: \"For each provider that uses a key file, writes the key into workers.yaml. The key files stay on disk.\",\n  options: [{ name: \"dry-run\", type: \"boolean\", summary: \"Show the plan (provider names and paths, never keys); write nothing\" }],\n  examples: [\"pai worker config inline-keys --dry-run\", \"pai worker config inline-keys\"],\n  seeAlso: [\"pai worker config check\"],\n  run(ctx) {\n    try {\n      const r = inlineWorkersYamlKeys({ dryRun: ctx.dryRun });\n      if (!r.inlined.length) return void out(dim(\"no providers have a key_file without an inline key — nothing to do\"));\n      for (const { provider, keyFilePath } of r.inlined) {\n        out((r.dryRun ? dim(\"  would inline \") : ok(\"  inlined \")) + `${provider} (was ${keyFilePath})`);\n      }\n      if (r.dryRun) return void out(dim(\"  dry run — nothing written\"));\n      out(ok(`wrote ${r.yamlPath}`));\n      out(dim(\"  key files left on disk — remove them yourself if you no longer need them:\"));\n      for (const { keyFilePath } of r.inlined) out(dim(`    ${expandHome(keyFilePath)}`));\n    } catch (e) {\n      fail(e, CONFIG_HINT);\n    }\n  },\n};\n\nexport const workerConfigCommand: CommandDef = {\n  name: \"config\",\n  summary: \"The workers.yaml file itself: path, init, migrate, check, inline-keys\",\n  description: \"`pai worker providers` and `classes` change what is inside workers.yaml; these commands deal with the file.\",\n  examples: [\"pai worker config path\", \"pai worker config check\"],\n  seeAlso: [\"pai worker providers\", \"pai worker classes\"],\n  subcommands: [configPathCommand, configInitCommand, configMigrateCommand, configCheckCommand, configInlineKeysCommand],\n};\n\n// ---------------------------------------------------------------------------\n// local\n// ---------------------------------------------------------------------------\n\nconst READY_TIMEOUT_MS = 10 * 60 * 1000;\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\nexport function listServers(cfg: LocalConfig): RunningServer[] {\n  const o = execFileSync(\"ps\", [\"-axo\", \"pid=,rss=,command=\"], { encoding: \"utf8\", maxBuffer: 64 * 1024 * 1024 });\n  return parseServers(o, cfg);\n}\n\nexport async function stopAll(cfg: LocalConfig): Promise<void> {\n  for (const s of listServers(cfg)) process.kill(s.pid, \"SIGTERM\");\n  const deadline = Date.now() + 60_000;\n  while (listServers(cfg).length > 0 && Date.now() < deadline) await sleep(1000);\n  for (const s of listServers(cfg)) process.kill(s.pid, \"SIGKILL\");\n}\n\nexport async function isUp(port: number): Promise<boolean> {\n  try {\n    return (await fetch(`http://127.0.0.1:${port}/v1/models`, { signal: AbortSignal.timeout(3000) })).ok;\n  } catch {\n    return false;\n  }\n}\n\n/** Stop every local server, start `tier`'s and wait until it answers. */\nexport async function startTier(cfg: LocalConfig, target: string, tier: LocalTier, plan: StartPlan = planStart(cfg, tier)): Promise<void> {\n  await stopAll(cfg);\n  mkdirSync(dirname(plan.logPath), { recursive: true });\n  const fd = openSync(plan.logPath, \"w\");\n  const [cmd, ...args] = plan.argv;\n  const child = spawn(cmd, args, { env: { ...process.env, ...plan.env }, detached: true, stdio: [\"ignore\", fd, fd] });\n  child.unref();\n  closeSync(fd);\n  out(dim(`starting ${tier.provider} (${tier.served}) on :${tier.port}, log ${plan.logPath}`));\n  const deadline = Date.now() + READY_TIMEOUT_MS;\n  while (!(await isUp(tier.port))) {\n    if (child.exitCode !== null) throw new LocalTierError(`server exited (${child.exitCode}); see ${plan.logPath}`);\n    if (Date.now() > deadline) throw new LocalTierError(`not ready after 10 min; see ${plan.logPath}`);\n    await sleep(5000);\n  }\n  const s = listServers(cfg).find((x) => x.port === tier.port);\n  out(ok(`${target} up: ${tier.provider} on port ${tier.port}${s ? `, rss ${fmtRss(s.rssKb)}` : \"\"}`));\n}\n\nfunction printLocalStatus(cfg: LocalConfig): void {\n  const servers = listServers(cfg);\n  if (servers.length === 0) out(dim(\"no local model server running\"));\n  for (const s of servers) {\n    out(`${s.tier ?? \"?\"}  provider=${s.provider ?? \"?\"}  port=${s.port ?? \"?\"}  pid=${s.pid}  rss=${fmtRss(s.rssKb)}`);\n  }\n  out(dim(`tiers: ${Object.keys(cfg.tiers).join(\", \") || \"(none configured)\"}`));\n}\n\nexport const workerLocalCommand: CommandDef = {\n  name: \"local\",\n  summary: \"Switch which local model server runs\",\n  description:\n    \"TARGET is a tier name (stops every local server, starts that tier), `off` (stops all) or `status` (what is up).\\n\" +\n    \"Tiers are listed under local_tiers: in workers.yaml. This does not change where workers are routed (see `pai worker policy`).\",\n  args: [{ name: \"TARGET\", required: true, summary: \"A tier name, off, or status\" }],\n  options: [{ name: \"dry-run\", type: \"boolean\", summary: \"Print the exact commands without starting or stopping anything\" }],\n  examples: [\"pai worker local status\", \"pai worker local big --dry-run\"],\n  seeAlso: [\"pai worker policy\", \"pai worker config path\"],\n  async run(ctx) {\n    try {\n      const target = ctx.args.TARGET as string;\n      const cfg = readLocalConfig(workersYamlPath());\n      if (target === \"status\") return void printLocalStatus(cfg);\n      if (target === \"off\") {\n        if (ctx.dryRun) return void out(stopLine(cfg));\n        await stopAll(cfg);\n        return void out(ok(\"local model servers stopped\"));\n      }\n      const tier = cfg.tiers[target];\n      if (!tier) throw new LocalTierError(`no tier \"${target}\" (configured: ${Object.keys(cfg.tiers).join(\", \") || \"none\"})`);\n      const plan = planStart(cfg, tier);\n      if (ctx.dryRun) {\n        out(stopLine(cfg));\n        out(\"sleep 10\");\n        out(plan.line);\n        out(`until curl -s -m 3 http://127.0.0.1:${tier.port}/v1/models > /dev/null; do sleep 5; done`);\n        return;\n      }\n      await startTier(cfg, target, tier, plan);\n    } catch (e) {\n      fail(e, \"pai worker local status\");\n    }\n  },\n};\n\n// ---------------------------------------------------------------------------\n// bench\n// ---------------------------------------------------------------------------\n\nexport const workerBenchCommand: CommandDef = {\n  name: \"bench\",\n  summary: \"Compare providers on a set of real tasks\",\n  description:\n    \"For each task × provider × repeat, runs the suite's run.sh and then grade.sh, prints pass counts, median time and turns per provider, and writes results.md into the suite folder.\\n\" +\n    \"Providers are named explicitly: a measurement ignores `pai worker policy`.\",\n  options: [\n    { name: \"suite\", type: \"string\", value: \"DIR\", summary: \"Suite folder with run.sh, grade.sh and tasks/NAME/ (required)\" },\n    { name: \"providers\", type: \"string\", value: \"LIST\", summary: \"Comma-separated providers, e.g. anthropic,local30\" },\n    { name: \"tasks\", type: \"string\", value: \"LIST\", summary: \"Comma-separated task names (default: all)\" },\n    { name: \"prompt\", type: \"string\", value: \"KIND\", default: \"task\", summary: `Prompt file kind: ${PROMPTS.join(\"|\")}` },\n    { name: \"repeat\", type: \"string\", value: \"N\", default: \"1\", summary: \"Runs per task and provider\" },\n    { name: \"max-parallel\", type: \"string\", value: \"N\", default: \"3\", summary: \"Runs going at once\" },\n    { name: \"max-load\", type: \"string\", value: \"N\", default: \"60\", summary: \"Start no run while the 1-minute load average is above this\" },\n    { name: \"dry-run\", type: \"boolean\", summary: \"Print the plan only\" },\n    { name: \"report\", type: \"boolean\", summary: \"Only summarise the suite's existing rows.tsv into results.md; run nothing\" },\n  ],\n  examples: [\"pai worker bench --suite ./suite --providers anthropic,glm --dry-run\", \"pai worker bench --suite ./suite --report\"],\n  seeAlso: [\"pai worker providers\"],\n  async run(ctx) {\n    try {\n      const o = ctx.opts;\n      if (o.suite === undefined) throw new Error(\"--suite is required (the suite folder)\");\n      const suite = resolve(o.suite as string);\n      if (o.report) {\n        const rows = readSuiteRows(suite);\n        out(aggregate(rows));\n        out(writeResults(suite, rows));\n        return;\n      }\n      const prompt = String(o.prompt);\n      if (!PROMPTS.includes(prompt as Prompt)) throw new Error(`--prompt must be one of ${PROMPTS.join(\", \")}`);\n      if (!o.providers) throw new Error(\"--providers is required\");\n      for (const f of [\"run.sh\", \"grade.sh\"]) if (!existsSync(join(suite, f))) throw new Error(`${suite}/${f} missing`);\n      const all = suiteTasks(suite);\n      const tasks = o.tasks ? String(o.tasks).split(\",\") : all;\n      const unknown = tasks.filter((t) => !all.includes(t));\n      if (unknown.length) throw new Error(`unknown task(s): ${unknown.join(\", \")}`);\n      const arms = planArms(tasks, String(o.providers).split(\",\"), prompt as Prompt, Math.max(1, Number(o.repeat) || 1));\n      out(`${arms.length} arm(s), prompt ${prompt}, max ${o[\"max-parallel\"]} parallel, load gate ${o[\"max-load\"]}`);\n      if (ctx.dryRun) {\n        for (const a of arms) out(`${a.task}\\t${a.provider}\\t${a.prompt}\\t#${a.repeat}`);\n        return;\n      }\n      const rows = await runBench({ suite, arms, maxParallel: Number(o[\"max-parallel\"]), maxLoad: Number(o[\"max-load\"]), log: (s) => out(s) });\n      out(aggregate(rows));\n      out(writeResults(suite, rows));\n    } catch (e) {\n      fail(e, \"pai help worker bench\");\n    }\n  },\n};\n\n// ---------------------------------------------------------------------------\n// policy\n// ---------------------------------------------------------------------------\n\nexport const workerPolicyCommand: CommandDef = {\n  name: \"policy\",\n  summary: \"Where workers run: cloud, local, or a mix\",\n  description:\n    \"With no mode, shows the policy and the route the next run would take. A mode sets it; `clear` removes it (class and active-provider routing as before).\\n\" +\n    \"  cloud-only    never local (battery, on the road)\\n\" +\n    \"  local-only    never cloud (saves tokens); fails if no local server answers\\n\" +\n    \"  prefer-local  local first; a local run with a bad report is repeated once on the cloud\\n\" +\n    \"  prefer-cloud  cloud first (fast turnaround); local only when the cloud is unavailable\\n\" +\n    \"  auto          prefer-local, but cloud on battery and for an --urgent run\\n\" +\n    \"Per run you can add --urgent, --local-ok or --cloud; --provider beats the policy.\",\n  args: [{ name: \"MODE\", summary: `${POLICY_MODES.join(\", \")} or clear` }],\n  options: [{ name: \"for\", type: \"string\", value: \"DURATION\", summary: \"Expire the policy after 30m, 2h, 1d …\" }],\n  examples: [\"pai worker policy\", \"pai worker policy prefer-local --for 2h\"],\n  seeAlso: [\"pai worker local\", \"pai worker providers\"],\n  async run(ctx) {\n    try {\n      const mode = ctx.args.MODE as string | undefined;\n      const config = readWorkersSection().workers;\n      const dir = workersLogDir(config);\n      if (mode === \"clear\") {\n        clearPolicy(dir);\n        return void out(\"policy: none (class/active routing)\");\n      }\n      if (mode !== undefined) {\n        if (!(POLICY_MODES as readonly string[]).includes(mode)) {\n          throw new Error(`policy: expected one of ${POLICY_MODES.join(\", \")}, clear — got \"${mode}\"`);\n        }\n        const p = writePolicy(dir, mode as PolicyMode, ctx.opts.for ? parseDuration(String(ctx.opts.for)) : undefined);\n        return void out(`policy: ${p.mode}${p.until ? ` until ${p.until}` : \"\"}`);\n      }\n      const p = readPolicy(dir);\n      out(`policy: ${p ? `${p.mode}${p.until ? ` until ${p.until}` : \"\"}` : \"none (class/active routing)\"}`);\n      const local = await findLocal(config, dir);\n      out(`local:   ${local.name ?? local.why}`);\n      out(`battery: ${onBattery() ? \"yes\" : \"no\"}`);\n      const r = await routeRun(config, dir, {}).catch((e: Error) => ({ error: e.message }));\n      if (r && \"error\" in r) out(`next run: fails — ${r.error}`);\n      else if (r) out(`next run: ${r.providerFlag ?? \"cloud\"} (${r.route.reason})`);\n    } catch (e) {\n      fail(e, \"pai worker policy\");\n    }\n  },\n};\n\n// ---------------------------------------------------------------------------\n// fallback, pool, proxy, mcp, on, off, install, controls\n// ---------------------------------------------------------------------------\n\nexport const workerFallbackCommand: CommandDef = {\n  name: \"fallback\",\n  summary: \"Run every new Claude Code process on a worker provider\",\n  description:\n    \"`on [PROVIDER]` switches (default: the active provider) by writing env and a model pin into settings.json; `off` restores settings.json exactly; `status` shows the state and running sessions (also the default).\\n\" +\n    \"Set CLAUDE_SETTINGS_PATH to another settings.json for a dry run.\",\n  args: [\n    { name: \"ACTION\", summary: \"on, off or status (default: status)\" },\n    { name: \"PROVIDER\", summary: \"With on: provider to switch to\" },\n  ],\n  examples: [\"pai worker fallback status\", \"CLAUDE_SETTINGS_PATH=/tmp/settings.json pai worker fallback on glm\"],\n  seeAlso: [\"pai worker providers use\", \"pai worker policy\"],\n  run(ctx) {\n    try {\n      const action = ctx.args.ACTION as string | undefined;\n      if (action === undefined || action === \"status\") return void out(fallbackStatusText(fallbackStatus()).join(\"\\n\"));\n      if (action === \"on\") {\n        const r = fallbackOn(ctx.args.PROVIDER as string | undefined);\n        out(\n          r.alreadyOn\n            ? `fallback already on — provider ${r.provider}, env re-applied`\n            : `fallback on — provider ${r.provider}; every new Claude Code process uses it`\n        );\n        out(`  settings.json env: ${r.envKeys.join(\", \")}`);\n        out(`  model pin: ${r.model}`);\n        out(\"running sessions keep their current provider until restarted (see: pai worker fallback status)\");\n        return;\n      }\n      if (action === \"off\") {\n        const r = fallbackOff();\n        out(`fallback off — provider ${r.provider} released, settings.json restored`);\n        out(`  restored keys: ${r.envKeys.join(\", \")}`);\n        return;\n      }\n      throw new WorkersConfigError(`unknown fallback action \"${action}\" (expected: on, off, status)`);\n    } catch (e) {\n      fail(e, \"pai worker fallback status\");\n    }\n  },\n};\n\nexport const workerPoolCommand: CommandDef = {\n  name: \"pool\",\n  summary: \"Keep headless worker sessions warm between small runs\",\n  description:\n    \"Long-lived headless claude slots that successive small runs talk to, so the provider's prompt cache carries over.\\n\" +\n    \"`on` starts the background host and enables routing, `off` stops both, `status` (default) shows slots, tasks served and cache savings. Default is off.\",\n  args: [{ name: \"ACTION\", summary: \"on, off or status (default: status)\" }],\n  examples: [\"pai worker pool status\", \"pai worker pool on\"],\n  seeAlso: [\"pai worker on\"],\n  async run(ctx) {\n    try {\n      const action = ctx.args.ACTION as string | undefined;\n      const { poolOn, poolOff, poolStatusText } = await import(\"../../workers/pool-cli.js\");\n      if (action === undefined || action === \"status\") return void out(await poolStatusText(logDir()));\n      if (action === \"on\") return void (await poolOn(logDir())).forEach((l) => out(l));\n      if (action === \"off\") return void (await poolOff(logDir())).forEach((l) => out(l));\n      throw new WorkersConfigError(`unknown pool action \"${action}\" (expected: on, off, status)`);\n    } catch (e) {\n      fail(e, \"pai worker pool status\");\n    }\n  },\n};\n\nexport const workerProxyCommand: CommandDef = {\n  name: \"proxy\",\n  summary: \"The local proxy for OpenAI-style providers\",\n  description: \"Starts (or checks) the loopback-only Anthropic-to-OpenAI proxy. `run` starts it on demand anyway; `proxy stop` stops it again.\",\n  args: [{ name: \"STOP\", summary: \"Write stop to stop the proxy\" }],\n  options: [{ name: \"port\", type: \"number\", value: \"N\", summary: `Port to listen on (default ${DEFAULT_PROXY_PORT})` }],\n  examples: [\"pai worker proxy\", \"pai worker proxy stop\"],\n  seeAlso: [\"pai worker providers add\"],\n  async run(ctx) {\n    try {\n      const stop = ctx.args.STOP as string | undefined;\n      if (stop === \"stop\") return void out(stopProxy(logDir()));\n      if (stop) throw new WorkersConfigError(`unknown proxy argument \"${stop}\" (expected: stop)`);\n      const url = await ensureProxyRunning((ctx.opts.port as number | undefined) ?? DEFAULT_PROXY_PORT, logDir());\n      out(`proxy listening on ${url} — point openai providers at ${url}/<provider>`);\n    } catch (e) {\n      fail(e, \"pai worker proxy\");\n    }\n  },\n};\n\nexport const workerMcpCommand: CommandDef = {\n  name: \"mcp\",\n  summary: \"MCP servers workers may load\",\n  description: \"Lists the MCP servers a worker can be given with --mcp or a class, and the configured sets.\",\n  args: [{ name: \"LIST\", summary: \"Optional word list (the default)\" }],\n  examples: [\"pai worker mcp\", \"pai worker mcp list\"],\n  seeAlso: [\"pai worker classes set\"],\n  run(ctx) {\n    try {\n      const what = ctx.args.LIST as string | undefined;\n      if (what && what !== \"list\") throw new WorkersConfigError(`unknown mcp argument \"${what}\" (expected: list)`);\n      out(describeMcp(readWorkersSection().workers).join(\"\\n\"));\n    } catch (e) {\n      fail(e, \"pai worker mcp\");\n    }\n  },\n};\n\nexport const workerOnCommand: CommandDef = {\n  name: \"on\",\n  summary: \"Send Agent-tool subagents to workers\",\n  description: \"Turns worker routing on (the default once a provider exists). The Agent hook then denies the Agent tool and suggests `pai worker run`.\",\n  examples: [\"pai worker on\", \"pai worker off\"],\n  seeAlso: [\"pai worker off\"],\n  run() {\n    try {\n      setWorkersEnabled(true);\n      out(\"workers on — the Agent hook now denies and suggests `pai worker run`\");\n    } catch (e) {\n      fail(e, \"pai worker providers\");\n    }\n  },\n};\n\nexport const workerOffCommand: CommandDef = {\n  name: \"off\",\n  summary: \"Stop routing: Agent tool runs on Anthropic again\",\n  description: \"Turns worker routing off. Subagents started with the Agent tool run on Anthropic as usual.\",\n  examples: [\"pai worker off\", \"pai worker on\"],\n  seeAlso: [\"pai worker on\"],\n  run() {\n    try {\n      setWorkersEnabled(false);\n      out(\"workers off — Agent-tool subagents run on Anthropic\");\n    } catch (e) {\n      fail(e, \"pai worker providers\");\n    }\n  },\n};\n\nexport const workerInstallCommand: CommandDef = {\n  name: \"install\",\n  summary: \"Set up the Agent hook and command shims\",\n  description: \"Adds the Agent hook to settings.json, writes the glm* shims in ~/.local/bin and cleans up old scripts. Safe to run again.\",\n  examples: [\"pai worker install\", \"pai worker providers\"],\n  seeAlso: [\"pai worker on\"],\n  run() {\n    try {\n      for (const line of installWorkers().lines) out(`  ${line}`);\n    } catch (e) {\n      fail(e, \"pai worker providers\");\n    }\n  },\n};\n\n/** The argv `controls` hands to clickr (kept separate so it can be tested). */\nexport function controlsArgv(id: string, who: string, pid: number): string[] {\n  return clickrControlsArgv(id, who as \"you\" | \"me\", pid);\n}\n\nexport const workerControlsCommand: CommandDef = {\n  name: \"controls\",\n  summary: \"Hand the desktop controls to a worker, or take them back\",\n  description:\n    \"WHO is `you` (the worker becomes the exclusive holder of the desktop controls; needs clickr with --agent support) or `me` (you take them back).\\n\" +\n    \"Inside a worker's pane, typing \\\"your controls\\\" does the same.\",\n  args: [\n    { name: \"ID\", required: true, summary: \"Worker id (see `pai worker ps`)\" },\n    { name: \"WHO\", required: true, summary: \"you or me\" },\n  ],\n  examples: [\"pai worker controls 20260101-120000-1234 you\", \"pai worker controls 20260101-120000-1234 me\"],\n  seeAlso: [\"pai worker ps\"],\n  run(ctx) {\n    const id = ctx.args.ID as string;\n    const who = ctx.args.WHO as string;\n    if (who !== \"you\" && who !== \"me\") throw new CliError(`<who> must be \"you\" or \"me\"`, `pai worker controls ${id} me`);\n    let status: ReturnType<typeof loadStatus>;\n    const dir = (() => {\n      try {\n        return logDir();\n      } catch (e) {\n        return fail(e, \"pai worker config check\");\n      }\n    })();\n    try {\n      status = loadStatus(dir, id);\n    } catch (e) {\n      fail(e, \"pai worker ps\");\n    }\n    if (!status) throw new CliError(`no worker named \"${id}\"`, \"pai worker ps\");\n    return new Promise<number>((done) => {\n      const proc = spawn(\"clickr\", controlsArgv(id, who, status!.pid), { stdio: \"inherit\" });\n      proc.on(\"error\", (e) => {\n        process.stderr.write(err(`pai: cannot run clickr controls: ${e.message} (is clickr installed?)`) + \"\\n\");\n        done(1);\n      });\n      proc.on(\"close\", (code) => {\n        if (code === 0) {\n          if (who === \"you\") markControlsHeld(dir, id);\n          out(`controls → ${who === \"you\" ? `${id} (exclusive; needs clickr with --agent support)` : \"operator\"}`);\n        }\n        done(code ?? 1);\n      });\n    });\n  },\n};\n\n/** The `worker` subcommands this file owns, in help order. */\nexport const WORKER_CONFIG_COMMANDS: CommandDef[] = [\n  workerProvidersCommand,\n  workerClassesCommand,\n  workerRolesCommand,\n  workerConfigCommand,\n  workerPolicyCommand,\n  workerLocalCommand,\n  workerFallbackCommand,\n  workerPoolCommand,\n  workerProxyCommand,\n  workerMcpCommand,\n  workerOnCommand,\n  workerOffCommand,\n  workerInstallCommand,\n  workerControlsCommand,\n  workerBenchCommand,\n];\n","/**\n * cockpit-auth.ts — the router beyond loopback.\n *\n * The router's default bind is 127.0.0.1 and none of this file runs. Setting\n * `router.bind` in the main config (\"tailscale\" — the utun address inside\n * Tailscale's CGNAT range, or an explicit IPv4) opens a second listener on\n * that address and changes two things:\n *\n *   - /v1/* (the ANTHROPIC_BASE_URL route, whose requests carry the Max login\n *     pass-through) still only accepts remote addresses that are loopback or\n *     inside 100.64.0.0/10; anything else gets 403 before any routing.\n *   - the cockpit GET routes need the cockpit token (?token=, Authorization:\n *     Bearer, or the cookie the first token-authenticated hit sets). It is\n *     generated on first start and kept at ~/.claude/pai/router-cockpit.token,\n *     mode 0600; the full token URL is printed once at startup.\n *\n * 0.0.0.0 is refused outright: the loopback listener already serves the local\n * side, and a wildcard bind would put /v1 on the LAN.\n */\n\nimport { createHash, randomBytes, timingSafeEqual } from \"node:crypto\";\nimport { chmodSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { networkInterfaces, type NetworkInterfaceInfo } from \"node:os\";\nimport { dirname } from \"node:path\";\nimport type http from \"node:http\";\nimport { paiHomePath } from \"../config/pai-home.js\";\n\nexport const LOOPBACK = \"127.0.0.1\";\n\n/** The /v1 remote rule: loopback (any 127/8, ::1, v4-mapped) or the tailnet. */\nexport function defaultV1Guard(remoteAddress: string | undefined): boolean {\n  return isLoopbackAddress(remoteAddress) || inTailscaleRange(remoteAddress);\n}\n\n/** Strip the ::ffff: IPv4-mapped prefix (Node reports v4 peers this way on v6 sockets). */\nexport function normalizeIp(ip: string | undefined): string {\n  const a = (ip ?? \"\").trim().toLowerCase();\n  return a.startsWith(\"::ffff:\") ? a.slice(7) : a;\n}\n\nexport function isLoopbackAddress(ip: string | undefined): boolean {\n  const a = normalizeIp(ip);\n  return a === \"::1\" || a.startsWith(\"127.\");\n}\n\nfunction ipv4ToInt(ip: string): number | null {\n  const parts = ip.split(\".\");\n  if (parts.length !== 4) return null;\n  let out = 0;\n  for (const p of parts) {\n    if (!/^\\d{1,3}$/.test(p)) return null;\n    const n = Number(p);\n    if (n > 255) return null;\n    out = out * 256 + n;\n  }\n  return out;\n}\n\nexport function inTailscaleRange(ip: string | undefined): boolean {\n  const n = ipv4ToInt(normalizeIp(ip));\n  if (n === null) return false;\n  const mask = 0xffc00000; // /10\n  return (n & mask) === 0x64400000; // 100.64.0.0\n}\n\n/**\n * router.bind → a concrete address. \"loopback\" (also empty/unset) is\n * 127.0.0.1; \"tailscale\" resolves the utun/tailscale interface's IPv4 inside\n * 100.64.0.0/10 (null when Tailscale is down); anything else must be a dotted\n * quad — 0.0.0.0 and every non-IPv4 value are refused (null).\n */\nexport function resolveBindAddress(\n  bind: string | undefined,\n  interfaces: () => NodeJS.Dict<NetworkInterfaceInfo[]> = networkInterfaces\n): string | null {\n  const v = (bind ?? \"\").trim().toLowerCase();\n  if (!v || v === \"loopback\") return LOOPBACK;\n  if (v === \"tailscale\") {\n    let fallback: string | undefined;\n    for (const [name, addrs] of Object.entries(interfaces() ?? {})) {\n      for (const a of addrs ?? []) {\n        if (a.family !== \"IPv4\" && (a.family as unknown) !== 4) continue;\n        if (!inTailscaleRange(a.address)) continue;\n        // Tailscale names its interface utun<n> on macOS, tailscale0 elsewhere.\n        if (/^(utun|tailscale)/i.test(name)) return a.address;\n        fallback ??= a.address;\n      }\n    }\n    return fallback ?? null;\n  }\n  if (v === \"0.0.0.0\" || v === \"::\" || v === \"*\") return null;\n  return ipv4ToInt(v) !== null ? v : null;\n}\n\n/** Lazy so tests (PAI_HOME redirected) and installs never bake in a home path. */\nexport function cockpitTokenFile(): string {\n  return paiHomePath(\"router-cockpit.token\");\n}\n\n/**\n * The cockpit token: read from disk, generated on first start (32 random\n * bytes, base64url) and written 0600.\n */\nexport function readOrCreateCockpitToken(file: string = cockpitTokenFile()): string {\n  try {\n    const t = readFileSync(file, \"utf8\").trim();\n    if (t) return t;\n  } catch {\n    /* first start */\n  }\n  const token = randomBytes(32).toString(\"base64url\");\n  mkdirSync(dirname(file), { recursive: true, mode: 0o700 });\n  writeFileSync(file, token + \"\\n\", { mode: 0o600 });\n  chmodSync(file, 0o600); // umask can soften the create mode\n  return token;\n}\n\n/** Length-independent constant-time compare: both sides digest to 32 bytes. */\nfunction tokenEq(a: string, b: string): boolean {\n  const digest = (s: string) => createHash(\"sha256\").update(s).digest();\n  return timingSafeEqual(digest(a), digest(b));\n}\n\n/**\n * True when the request carries the cockpit token: ?token=, an\n * `Authorization: Bearer <token>` header, or the pai_cockpit cookie the first\n * token-authenticated response set (so the page's own fetches pass).\n */\nexport function cockpitTokenOk(\n  url: string | undefined,\n  headers: http.IncomingHttpHeaders,\n  token: string\n): boolean {\n  let q: string | null = null;\n  try {\n    q = new URL(url ?? \"/\", \"http://localhost\").searchParams.get(\"token\");\n  } catch {\n    /* unparseable url — fall through to header/cookie */\n  }\n  if (q && tokenEq(q, token)) return true;\n  const m = /^Bearer\\s+(\\S+)$/i.exec((headers.authorization ?? \"\").trim());\n  if (m && tokenEq(m[1]!, token)) return true;\n  for (const part of (headers.cookie ?? \"\").split(\";\")) {\n    const [k, ...rest] = part.trim().split(\"=\");\n    if (k === \"pai_cockpit\" && tokenEq(rest.join(\"=\"), token)) return true;\n  }\n  return false;\n}\n\n/** Anything the cockpit handler serves (handleCockpit) — kept import-free. */\nexport type CockpitHandler = (req: http.IncomingMessage, res: http.ServerResponse) => boolean;\n\nexport function isCockpitPath(path: string): boolean {\n  return path === \"/cockpit\" || path.startsWith(\"/cockpit/\");\n}\n\n/**\n * Wrap the cockpit handler with the token check for non-loopback binds. The\n * first token-authenticated response sets an HttpOnly cookie so the page's\n * relative fetches (/cockpit/data, ...) authenticate without carrying the\n * token themselves. Non-cockpit paths pass through untouched (the router\n * 404s them).\n */\nexport function withCockpitToken(token: string, inner: CockpitHandler): CockpitHandler {\n  return (req, res) => {\n    const path = (req.url ?? \"/\").split(\"?\")[0]!;\n    if (!isCockpitPath(path)) return inner(req, res);\n    if (!cockpitTokenOk(req.url, req.headers, token)) {\n      res.writeHead(401, { \"content-type\": \"text/plain; charset=utf-8\" });\n      res.end(\"pai cockpit: token required (?token= or Authorization: Bearer)\");\n      return true;\n    }\n    const cookie = req.headers.cookie;\n    if (!cookie || !cookie.split(\";\").some((c) => c.trim().startsWith(\"pai_cockpit=\"))) {\n      res.setHeader(\"Set-Cookie\", `pai_cockpit=${token}; Path=/; HttpOnly; SameSite=Lax`);\n    }\n    return inner(req, res);\n  };\n}\n\n/**\n * The address to show a human for a bind address: the tailnet hostname\n * (macbook-mn style, from `tailscale status --json`) when the address is the\n * machine's own Tailscale IP, else the address itself.\n */\nexport async function displayHost(addr: string): Promise<string> {\n  if (!inTailscaleRange(addr)) return addr;\n  try {\n    const { execFile } = await import(\"node:child_process\");\n    const { promisify } = await import(\"node:util\");\n    const { stdout } = await promisify(execFile)(\"tailscale\", [\"status\", \"--json\"], { timeout: 3000 });\n    const self = (JSON.parse(stdout) as { Self?: { DNSName?: string; TailscaleIPs?: string[] } }).Self;\n    if (self?.DNSName && self.TailscaleIPs?.includes(addr)) return self.DNSName.replace(/\\.$/, \"\");\n  } catch {\n    /* no tailscale CLI in scope — the IP is fine */\n  }\n  return addr;\n}\n","/**\n * pool-panel.ts — the cockpit \"Worker pool\" panel: whether the worker session\n * pool is on, whether its host is up, what every slot has carried, and how\n * many runs of the last 24 h did NOT go through it (and why). Kept out of\n * cockpit.ts: the data function is pure over its inputs, the loader reads the\n * pool state, metrics and ledger tail, and the page gets its section and\n * script as strings.\n */\n\nimport { closeSync, existsSync, fstatSync, openSync, readSync } from \"node:fs\";\nimport { warmOrCold, poolCarry, poolHostAlive, poolMetricsPath, poolSettings, readPoolMetrics, readPoolState } from \"../workers/pool.js\";\nimport type { PoolMetricRow, PoolSlotRecord, PoolState } from \"../workers/pool.js\";\nimport { readWorkersSection } from \"../workers/config.js\";\nimport { parseLedger, type LedgerLine } from \"../workers/ledger.js\";\nimport { ledgerPath, workersLogDir } from \"../workers/paths.js\";\n\nexport interface PoolPanelSlot {\n  provider: string;\n  model: string;\n  cwd: string;\n  state: \"idle\" | \"busy\";\n  ageSecs: number;\n  idleSecs: number;\n  tasks: number;\n  tokensCarried: number;\n  /** First-request cache hit ratio over this slot's tasks (null = no task yet). */\n  hitRatio: number | null;\n  /** Task the slot is serving right now, and its branch (slot-worktree slots). */\n  task: string | null;\n  branch: string | null;\n  /** Was the last task's first request served from the prefix cache (\"warm\") or prefilled (\"cold\")? */\n  firstRequest: \"warm\" | \"cold\" | null;\n}\n\nexport interface PoolPanelData {\n  enabled: boolean;\n  hostUp: boolean;\n  port: number;\n  maxSlots: number;\n  idleKillSecs: number;\n  hostSince: string | null;\n  slots: PoolPanelSlot[];\n  totals: { tasksServed: number; tokensCarried: number; hitRatio: number | null; carriedTasks: number };\n  last24h: {\n    runs: number;\n    pooled: number;\n    notPooled: number;\n    /** Reasons the pool gate or host gave, most frequent first. */\n    reasons: Array<{ reason: string; runs: number }>;\n    topReason: string | null;\n    /** Runs not pooled that predate reason logging, or ran while the pool was off. */\n    unattributed: number;\n  };\n  /** One plain sentence for the operator. */\n  message: string;\n}\n\nexport interface PoolPanelInput {\n  enabled: boolean;\n  hostUp: boolean;\n  port: number;\n  maxSlots: number;\n  idleSecs: number;\n  state: PoolState | null;\n  metrics: PoolMetricRow[];\n  ledger: LedgerLine[];\n  nowMs: number;\n}\n\n/** Ledger stamps are local wall time. */\nfunction stampMs(stamp: string): number {\n  return new Date(stamp.replace(\" \", \"T\")).getTime();\n}\n\nconst DAY_MS = 24 * 3600_000;\n\nexport function buildPoolPanel(i: PoolPanelInput): PoolPanelData {\n  const slots: PoolPanelSlot[] = (i.state?.slots ?? []).map((s: PoolSlotRecord) => {\n    const rows = s.claudeSession ? i.metrics.filter((m) => m.slotSession === s.claudeSession) : [];\n    const c = poolCarry(rows);\n    return {\n      provider: s.provider,\n      model: s.model,\n      cwd: s.cwd,\n      state: s.busyWith ? \"busy\" : \"idle\",\n      ageSecs: Math.max(0, Math.round((i.nowMs - stampMs(s.spawnedAt)) / 1000)),\n      idleSecs: Math.max(0, Math.round((i.nowMs - stampMs(s.lastUsedAt)) / 1000)),\n      tasks: s.tasks,\n      tokensCarried: c.tokensCarried,\n      hitRatio: rows.length ? c.hitRatio : null,\n      task: s.busyWith,\n      branch: s.branch ?? null,\n      firstRequest: warmOrCold(s.firstShare),\n    };\n  });\n  const carry = poolCarry(i.metrics);\n\n  const since = i.nowMs - DAY_MS;\n  let runs = 0;\n  let pooled = 0;\n  const reasons = new Map<string, number>();\n  let skipped = 0;\n  for (const l of i.ledger) {\n    if (stampMs(l.stamp) < since) continue;\n    if (l.event === \"WORKER-START\") {\n      runs++;\n      if (l.fields.mode === \"pooled\") pooled++;\n    } else if (l.event === \"WORKER-POOL-SKIP\") {\n      skipped++;\n      const r = l.fields.reason ?? \"unknown\";\n      reasons.set(r, (reasons.get(r) ?? 0) + 1);\n    }\n  }\n  const notPooled = Math.max(0, runs - pooled);\n  const sorted = [...reasons.entries()].sort((a, b) => b[1] - a[1]).map(([reason, n]) => ({ reason, runs: n }));\n  const last24h = {\n    runs,\n    pooled,\n    notPooled,\n    reasons: sorted,\n    topReason: sorted[0]?.reason ?? null,\n    unattributed: Math.max(0, notPooled - skipped),\n  };\n\n  let message: string;\n  if (!i.enabled) message = \"Pool is OFF (workers.pool.enabled is false): every worker spawns a fresh claude. Turn on with `pai worker pool on`.\";\n  else if (!i.hostUp) message = \"Pool is enabled but its host is DOWN: every worker spawns fresh. Start it with `pai worker pool on`.\";\n  else if (!carry.tasks && !pooled) message = \"Pool is up but has not served a task yet (no slot spawns before the first pooled task).\";\n  else message = `Pool served ${i.state?.tasksServed ?? carry.tasks} task(s) since the host started; ${pooled} of ${runs} runs in 24 h were pooled.`;\n\n  return {\n    enabled: i.enabled,\n    hostUp: i.hostUp,\n    port: i.port,\n    maxSlots: i.maxSlots,\n    idleKillSecs: i.idleSecs,\n    hostSince: i.state?.startedAt ?? null,\n    slots,\n    totals: {\n      tasksServed: i.state?.tasksServed ?? 0,\n      tokensCarried: carry.tokensCarried,\n      hitRatio: carry.tasks ? carry.hitRatio : null,\n      carriedTasks: carry.tasks,\n    },\n    last24h,\n    message,\n  };\n}\n\n/** The last `bytes` of a (possibly 30+ MB) ledger, from a line boundary. */\nexport function readLedgerTail(path: string, bytes = 6_000_000): LedgerLine[] {\n  if (!existsSync(path)) return [];\n  const fd = openSync(path, \"r\");\n  try {\n    const size = fstatSync(fd).size;\n    const len = Math.min(size, bytes);\n    const buf = Buffer.alloc(len);\n    readSync(fd, buf, 0, len, size - len);\n    let text = buf.toString(\"utf8\");\n    if (size > len) text = text.slice(text.indexOf(\"\\n\") + 1);\n    return parseLedger(text);\n  } finally {\n    closeSync(fd);\n  }\n}\n\n/** Panel data from the live workers config and logDir; cached 15 s. */\nexport function cachedPoolPanel(): () => Promise<PoolPanelData | null> {\n  let at = 0;\n  let cached: PoolPanelData | null = null;\n  return async () => {\n    if (Date.now() - at < 15_000) return cached;\n    at = Date.now();\n    try {\n      const { workers } = readWorkersSection();\n      const logDir = workersLogDir(workers);\n      const s = poolSettings(workers);\n      cached = buildPoolPanel({\n        enabled: s.enabled,\n        hostUp: await poolHostAlive(s.port),\n        port: s.port,\n        maxSlots: s.maxSlots,\n        idleSecs: s.idleSecs,\n        state: readPoolState(logDir),\n        metrics: readPoolMetrics(poolMetricsPath(logDir)),\n        ledger: readLedgerTail(ledgerPath(logDir)),\n        nowMs: Date.now(),\n      });\n    } catch {\n      cached = null;\n    }\n    return cached;\n  };\n}\n\n/** Process-wide instance so the 15 s cache survives across requests. */\nexport const sharedPoolPanel = cachedPoolPanel();\n\n/** The card, placed in the page body. */\nexport const POOL_PANEL_HTML = `<section class=\"card c12\"><h2>Worker pool<span class=\"tip\" id=\"pooltip\" tabindex=\"0\" aria-label=\"Worker pool status\" data-tip=\"Worker pool status\">ⓘ</span></h2><div id=\"poolpanel\"></div></section>`;\n\n/** Page script (no template literals, ES5): defines loadPool(); uses the page's esc, fmt, pct, table helpers. */\nexport const POOL_PANEL_JS = String.raw`\nfunction renderPool(p){\n var el=$(\"poolpanel\");if(!p){el.innerHTML='<span class=\"dim\">pool status unavailable</span>';return}\n function dur(s){return s<90?s+\"s\":s<5400?Math.round(s/60)+\"m\":Math.round(s/3600)+\"h\"}\n var head='<div class=\"kpis\">'+\n  [[\"pool\",p.enabled?'<span class=\"ok\">enabled</span>':'<span class=\"bad\">disabled</span>',\"max \"+p.maxSlots+\" slots · idle kill \"+(p.idleKillSecs?dur(p.idleKillSecs):\"never\")],\n   [\"host\",p.hostUp?'<span class=\"ok\">up</span>':'<span class=\"bad\">down</span>',\"port \"+p.port+(p.hostSince?\" · since \"+esc(p.hostSince):\"\")],\n   [\"tasks served\",p.totals.tasksServed,fmt(p.totals.tokensCarried)+\" cache-read tokens carried\"],\n   [\"first-request hit\",p.totals.hitRatio==null?\"n/a\":pct(p.totals.hitRatio),p.totals.carriedTasks+\" pooled task(s)\"],\n   [\"not pooled (24 h)\",p.last24h.notPooled+\" of \"+p.last24h.runs,p.last24h.topReason?\"top reason: \"+esc(p.last24h.topReason):(p.last24h.notPooled?\"reasons not logged yet\":\"\")]\n  ].map(function(k){return'<div class=\"kpi\"><b>'+k[1]+'</b><span>'+k[0]+'</span><span style=\"display:block\">'+k[2]+'</span></div>'}).join(\"\")+'</div>';\n var rows=p.slots.length?table([\"provider\",\"model\",\"cwd\",\"state\",\"task\",\"branch\",\"first req\",\"age\",\"idle\",\"tasks\",\"carried\",\"hit%\"],p.slots.map(function(s){return[esc(s.provider),esc(s.model),'<span title=\"'+esc(s.cwd)+'\">'+esc(s.cwd.split(\"/\").slice(-2).join(\"/\"))+'</span>','<span class=\"'+(s.state===\"busy\"?\"warn\":\"ok\")+'\">'+s.state+'</span>',s.task?esc(s.task):\"-\",s.branch?esc(s.branch):\"-\",s.firstRequest?'<span class=\"'+(s.firstRequest===\"warm\"?\"ok\":\"warn\")+'\">'+s.firstRequest+'</span>':\"-\",dur(s.ageSecs),dur(s.idleSecs),s.tasks,fmt(s.tokensCarried),s.hitRatio==null?\"-\":pct(s.hitRatio)]})):'<span class=\"dim\" title=\"A slot spawns with the first pooled task.\">no slots</span>';\n var rs=p.last24h.reasons.length?\"\\nRejections (24 h): \"+p.last24h.reasons.map(function(r){return r.reason+\" x\"+r.runs}).join(\", \")+(p.last24h.unattributed?\" · \"+p.last24h.unattributed+\" without a logged reason\":\"\"):\"\";\n setTip($(\"pooltip\"),p.message+rs);\n el.innerHTML=head+rows;\n}\nfunction loadPool(){fetch(\"/cockpit/pool\",{cache:\"no-store\"}).then(function(r){return r.json()}).then(renderPool).catch(function(){})}\n`;\n","/**\n * cockpit-page.ts — the single static page served at /cockpit. Inline CSS,\n * inline JS, inline SVG; no build step, no dependency. The script uses no\n * template literals so this module can hold it in one template string.\n */\n\nimport { POOL_PANEL_HTML, POOL_PANEL_JS } from \"./pool-panel.js\";\n\nexport const COCKPIT_HTML = String.raw`<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>PAI router cockpit</title>\n<style>\n:root{--bg:#f6f7f9;--card:#fff;--fg:#1d2330;--dim:#6b7385;--line:#e3e6ec;--ok:#2e9e5b;--warn:#d98a1c;--bad:#d64545;--acc:#3b6fe0}\n@media(prefers-color-scheme:dark){:root{--bg:#10131a;--card:#181c26;--fg:#e4e8f1;--dim:#8b93a7;--line:#2a3040;--ok:#46c47a;--warn:#e8a43a;--bad:#ef6b6b;--acc:#6b95f5}}\n*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--fg);font:13px/1.45 -apple-system,BlinkMacSystemFont,\"SF Pro Text\",sans-serif}\nheader{flex-wrap:wrap;display:flex;align-items:center;gap:16px;padding:8px 14px;border-bottom:1px solid var(--line);position:sticky;top:0;background:var(--bg);z-index:2}\nh1{font-size:15px;margin:0;font-weight:600}h2{font-size:12px;margin:0 0 6px;text-transform:uppercase;letter-spacing:.06em;color:var(--dim);font-weight:600}\n.tabs button{background:var(--card);color:var(--fg);border:1px solid var(--line);padding:3px 12px;cursor:pointer;font:inherit}\n.tabs button:first-child{border-radius:6px 0 0 6px}.tabs button:last-child{border-radius:0 6px 6px 0}\n.tabs button.on{background:var(--acc);border-color:var(--acc);color:#fff}\n#stamp{margin-left:auto;color:var(--dim)}#stamp.stale{color:var(--bad)}\nmain{padding:10px 14px;display:grid;gap:10px;grid-template-columns:repeat(12,1fr)}\n.card{background:var(--card);border:1px solid var(--line);border-radius:8px;padding:8px 10px;min-width:0}.tw{overflow-x:auto}\n.c12{grid-column:span 12}.c8{grid-column:span 8}.c6{grid-column:span 6}.c4{grid-column:span 4}\n@media(max-width:1000px){.c8,.c6,.c4{grid-column:span 12}}\n.gauges{display:flex;flex-wrap:wrap;gap:10px 14px}.gauge{text-align:center;min-width:110px}\n.gauge .n{font-weight:600}.gauge .s{color:var(--dim);font-size:12px}\ntable{border-collapse:collapse;width:100%}th,td{text-align:right;padding:2px 6px;border-bottom:1px solid var(--line);white-space:nowrap}\nth{color:var(--dim);font-weight:500}th:first-child,td:first-child{text-align:left}\n.bar{display:inline-block;height:8px;border-radius:3px;background:var(--ok);vertical-align:middle}\n.bad{color:var(--bad)}.warn{color:var(--warn)}.ok{color:var(--ok)}.dim{color:var(--dim)}\n.kpis{display:flex;gap:8px 20px;flex-wrap:wrap}.kpi b{font-size:20px;display:block;font-weight:600}.kpi span{color:var(--dim)}\n.groups{display:flex;gap:16px;flex-wrap:wrap;align-items:flex-start}.grp{min-width:0}.grp-cloud{flex:1 1 560px}.grp-local{flex:0 1 auto;width:min-content;min-width:200px;max-width:34%}.grp-local .gauges{flex-wrap:nowrap;flex-direction:column;align-items:flex-start}.grp+.grp{border-left:1px solid var(--line);padding-left:24px}@media(max-width:1000px){.grp-local{max-width:none;width:auto;flex:1 1 100%}.grp-local .gauges{flex-direction:row;flex-wrap:wrap}.grp+.grp{border-left:0;padding-left:0}}.chips.lowmodels{margin-top:6px}.ctl{display:flex;gap:10px;align-items:center;margin-bottom:6px;flex-wrap:wrap}.ctl button,.ctl select{background:var(--card);color:var(--fg);border:1px solid var(--line);border-radius:6px;padding:2px 10px;font:inherit;cursor:pointer}.ctl button.on{background:var(--acc);border-color:var(--acc);color:#fff}\n.legend{display:flex;flex-wrap:wrap;gap:4px 14px;margin-top:6px;color:var(--dim);font-size:12px}\n.legend i{display:inline-block;width:10px;height:10px;border-radius:2px;margin-right:5px;vertical-align:-1px}\n.rings{display:grid;grid-template-columns:repeat(auto-fill,minmax(92px,1fr));gap:6px}.ring{text-align:center;min-width:0}.ring .n,.ring .s{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ring .n{font-weight:600}.ring .s{color:var(--dim);font-size:11px}.ring.low{opacity:.45}.rings h4{grid-column:1/-1;margin:0;font-size:11px;font-weight:400;color:var(--dim)}\n.rings .solo{grid-column:1/-1;display:flex;align-items:center;justify-content:center;gap:16px;width:100%;flex-wrap:wrap}.bignum{font-size:26px;font-weight:600;text-align:center}.bignum span{display:block;font-size:11px;color:var(--dim);font-weight:400}\nsvg text{fill:var(--dim);font-size:10px}\n.tip{position:relative;display:inline-block;margin-left:5px;width:14px;height:14px;line-height:14px;text-align:center;font:11px/14px inherit;font-style:normal;text-transform:none;letter-spacing:0;color:var(--dim);cursor:help;border-radius:50%;vertical-align:middle}\n.tip:hover,.tip:focus{color:var(--acc);outline:none}\n.tip::after{content:attr(data-tip);display:none;position:absolute;z-index:5;top:18px;left:-6px;width:max-content;max-width:min(340px,72vw);padding:6px 9px;background:var(--fg);color:var(--bg);border-radius:6px;font-size:12px;font-weight:400;line-height:1.4;white-space:pre-line;text-align:left;text-transform:none;letter-spacing:0;box-shadow:0 2px 8px rgba(0,0,0,.3)}\n.tip:hover::after,.tip:focus::after{display:block}\n.downs{width:100%;color:var(--dim)}.downs summary{cursor:pointer}.downs .chip{margin:2px 2px 0 0}.chips{display:flex;flex-wrap:wrap;gap:4px;align-items:center}.chip{background:var(--bg);border:1px solid var(--line);border-radius:10px;padding:0 8px;font-size:12px;white-space:nowrap}.chip b{font-weight:600}\n#sessions td:first-child span[title]{display:inline-block;max-width:480px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}\n</style></head><body>\n<header><h1>PAI router cockpit</h1>\n<div class=\"tabs\" id=\"tabs\"><button data-w=\"1h\">1 hour</button><button data-w=\"24h\">Today</button><button data-w=\"7d\">Week</button></div>\n<span id=\"stamp\">loading...</span></header>\n<main>\n<section class=\"card c12\"><div class=\"groups\"><div class=\"grp grp-cloud\"><h2>Cloud</h2><div class=\"gauges\" id=\"cloud\"></div></div><div class=\"grp grp-local\"><h2>Local</h2><div class=\"gauges\" id=\"local\"></div></div></div></section>\n<section class=\"card c12\"><h2>System — memory, load, temperature, power</h2><div class=\"kpis\" id=\"sys\"></div><div id=\"sysservers\" style=\"margin-top:6px\"></div></section>\n${POOL_PANEL_HTML}\n<section class=\"card c12\"><div class=\"kpis\" id=\"kpis\"></div></section>\n<section class=\"card c8\"><h2>Tokens per bucket by kind<span class=\"tip\" id=\"wnote\" tabindex=\"0\" aria-label=\"Token counts\" data-tip=\"Token counts\">ⓘ</span></h2><div class=\"ctl\"><span id=\"mode\"><button data-m=\"weighted\">weighted</button><button data-m=\"raw\">raw</button></span><select id=\"msel\"></select></div><div id=\"series\"></div><div class=\"legend\" id=\"legend\"></div>\n<h2 style=\"margin-top:8px\">Power and GPU/CPU load<span class=\"tip\" tabindex=\"0\" aria-label=\"Same buckets as above. W on the left axis, load % on the fixed 0-100 right axis.\" data-tip=\"Same buckets as above. W on the left axis, load % on the fixed 0-100 right axis.\">ⓘ</span></h2><div id=\"power\"></div><div class=\"legend\" id=\"plegend\"></div></section>\n<section class=\"card c4\"><h2>Cache hit ratio<span class=\"tip\" tabindex=\"0\" aria-label=\"cache_read / (read + write + fresh input), per provider / model.\" data-tip=\"cache_read / (read + write + fresh input), per provider / model.\">ⓘ</span></h2><div id=\"donut\" class=\"rings\"></div><div id=\"donutlow\" class=\"chips lowmodels\"></div><div id=\"donutnote\" class=\"chips\"></div>\n<h2 id=\"donutlocaltitle\" style=\"margin-top:8px;display:none\">Local prefix cache<span class=\"tip\" tabindex=\"0\" aria-label=\"Server counters cover the time since that server started; router log rows only since the router restarted, so the two windows differ. A hit skips re-computing the cached prompt part (latency), not money.\" data-tip=\"Server counters cover the time since that server started; router log rows only since the router restarted, so the two windows differ. A hit skips re-computing the cached prompt part (latency), not money.\">ⓘ</span></h2><div id=\"donutlocal\"></div>\n<h2 style=\"margin-top:8px\">Providers<span class=\"tip\" tabindex=\"0\" aria-label=\"* server counter (prefix cache since server start; this provider reports no cache fields per request). avg ms is end-to-end incl. queueing and prefill, ttft p50 is time to first token.\" data-tip=\"* server counter (prefix cache since server start; this provider reports no cache fields per request). avg ms is end-to-end incl. queueing and prefill, ttft p50 is time to first token.\">ⓘ</span></h2><div id=\"providers\"></div></section>\n<section class=\"card c12\"><h2>Speed per model<span class=\"tip\" tabindex=\"0\" aria-label=\"TTFT = request sent to first content token. Read = input tokens / TTFT (prefill incl. cached for local models; for Anthropic input excludes cache reads). Write = output tokens / decode time, replies of 16+ tokens only. Medians (p50) unless marked.\" data-tip=\"TTFT = request sent to first content token. Read = input tokens / TTFT (prefill incl. cached for local models; for Anthropic input excludes cache reads). Write = output tokens / decode time, replies of 16+ tokens only. Medians (p50) unless marked.\">ⓘ</span></h2><div id=\"speed\"></div></section>\n<section class=\"card c12\"><h2>Cache verdict by pause<span class=\"tip\" tabindex=\"0\" aria-label=\"Per model. Pause = time since the previous request of the same session. A drop in hits after 5m means the cache expired between turns: a keepalive under 5m would have kept it.\" data-tip=\"Per model. Pause = time since the previous request of the same session. A drop in hits after 5m means the cache expired between turns: a keepalive under 5m would have kept it.\">ⓘ</span></h2><div id=\"pause\"></div></section>\n<section class=\"card c12\"><h2>Sessions by tokens</h2><div id=\"sessions\"></div></section>\n<section class=\"card c6\"><h2>Worker runs</h2><div id=\"workers\"></div></section>\n<section class=\"card c6\"><h2>Tokens per day per model (14 d)</h2><div id=\"daily\"></div><div class=\"legend\" id=\"dlegend\"></div></section>\n</main>\n<script>\n(function(){\nvar KINDS=[[\"input\",\"fresh input\",\"#3b6fe0\"],[\"cacheWrite\",\"cache write\",\"#e0703b\"],[\"cacheRead\",\"cache read\",\"#2e9e5b\"],[\"output\",\"output\",\"#b04fd0\"]],mode=localStorage.getItem(\"cockpitMode\")||\"weighted\",msel=\"\";var win=localStorage.getItem(\"cockpitWin\")||\"1h\",data=null,lim=null,lastOk=0,PAL=[\"#3b6fe0\",\"#e0703b\",\"#2e9e5b\",\"#b04fd0\",\"#d9b31c\",\"#2bb3c0\",\"#d64570\",\"#7a8a99\",\"#8fbf3f\",\"#a0522d\"];\nvar colors={};function colorOf(k){for(var i=0;i<KINDS.length;i++)if(KINDS[i][0]===k)return KINDS[i][2];if(!colors[k])colors[k]=PAL[Object.keys(colors).length%PAL.length];return colors[k]}\nfunction $(id){return document.getElementById(id)}\nfunction esc(s){return String(s==null?\"\":s).replace(/[&<>\"]/g,function(c){return{\"&\":\"&amp;\",\"<\":\"&lt;\",\">\":\"&gt;\",'\"':\"&quot;\"}[c]})}\nfunction fmt(n){n=n||0;if(n>=1e9)return(n/1e9).toFixed(2)+\"G\";if(n>=1e6)return(n/1e6).toFixed(2)+\"M\";if(n>=1e3)return(n/1e3).toFixed(1)+\"k\";return String(Math.round(n))}\n/** Tooltip icon: the long text lives in data-tip (CSS hover/focus) and aria-label (screen readers). */\nfunction tip(t){return'<span class=\"tip\" tabindex=\"0\" aria-label=\"'+esc(t)+'\" data-tip=\"'+esc(t)+'\">ⓘ</span>'}\nfunction setTip(el,t){el.setAttribute(\"aria-label\",t);el.setAttribute(\"data-tip\",t)}\nfunction pct(x){return(x*100).toFixed(1)+\"%\"}\nfunction tot(t){return t.input+t.output+t.cacheWrite+t.cacheRead}\nfunction dur(ms){if(ms<=0)return\"now\";var s=Math.floor(ms/1000),d=Math.floor(s/86400),h=Math.floor(s%86400/3600),m=Math.floor(s%3600/60);return d?d+\"d \"+h+\"h\":h?h+\"h \"+String(m).padStart(2,\"0\")+\"m\":m+\"m \"+String(s%60).padStart(2,\"0\")+\"s\"}\nfunction ago(iso){var s=(Date.now()-Date.parse(iso))/1000;return s<90?Math.round(s)+\"s\":s<5400?Math.round(s/60)+\"m\":s<172800?Math.round(s/3600)+\"h\":Math.round(s/86400)+\"d\"}\nfunction sev(p){return p>=90?\"var(--bad)\":p>=70?\"var(--warn)\":\"var(--ok)\"}\n\nfunction gauge(l,w,age){\n var p=Math.max(0,Math.min(100,w.percent)),r=44,cx=60,cy=56,a=Math.PI*p/100;\n var x=cx-r*Math.cos(a),y=cy-r*Math.sin(a);\n var arc='<path d=\"M '+(cx-r)+' '+cy+' A '+r+' '+r+' 0 0 1 '+(cx+r)+' '+cy+'\" fill=\"none\" stroke=\"var(--line)\" stroke-width=\"10\" stroke-linecap=\"round\"/>';\n if(p>0)arc+='<path d=\"M '+(cx-r)+' '+cy+' A '+r+' '+r+' 0 0 1 '+x.toFixed(1)+' '+y.toFixed(1)+'\" fill=\"none\" stroke=\"'+sev(p)+'\" stroke-width=\"10\" stroke-linecap=\"round\"/>';\n return '<div class=\"gauge\"><svg width=\"120\" height=\"70\" viewBox=\"0 0 120 70\">'+arc+'<text x=\"60\" y=\"54\" text-anchor=\"middle\" style=\"font-size:16px;font-weight:600;fill:var(--fg)\">'+Math.round(w.percent)+'%</text></svg>'+\n  '<div class=\"n\">'+esc(l.provider)+' '+esc(w.name)+(age?' <span class=\"warn\" title=\"'+esc(l.error||\"\")+'\">· '+esc(age)+' old</span>':'')+(w.estimated?' <span class=\"warn\">estimated</span>':'')+'</div>'+\n  '<div class=\"s\" data-reset=\"'+(w.resetAt||0)+'\">'+(w.resetAt?'resets in '+dur(w.resetAt-Date.now()):'reset unknown')+'</div></div>';\n}\nfunction renderLimits(){\n if(!lim)return;var c=\"\",lo=\"\",dn=[];\n lim.limits.forEach(function(l){\n  var h=\"\",age=l.error&&/usage reading /.test(l.error)&&l.fetchedAt&&l.fetchedAt<=Date.now()?ago(new Date(l.fetchedAt).toISOString()):\"\";\n  if(l.kind===\"local\"){h+='<div class=\"gauge\"><div class=\"n\">'+esc(l.provider)+'</div><div class=\"'+(l.up?\"ok\":\"bad\")+'\">'+(l.up?\"up\":\"down\")+'</div><div class=\"s\">'+(l.rssMb?l.rssMb+\" MB RSS\":\"\")+(l.tokensPerSec?\" · \"+l.tokensPerSec+\" tok/s\":\"\")+'</div>'+(l.served&&l.served.length?'<div class=\"s\">'+esc(l.served[0])+'</div>':'')+'</div>';if(l.up)lo+=h;else dn.push(l.provider);return}\n  l.windows.forEach(function(w){h+=gauge(l,w,age)});\n  if(!l.windows.length)h+='<div class=\"gauge\"><div class=\"n\">'+esc(l.provider)+'</div><div class=\"dim\">no reading</div><div class=\"s\">'+esc(l.error||\"\")+'</div></div>';\n  else if(l.error)h+='<div class=\"gauge dim\" title=\"'+esc(l.error)+'\"><div class=\"s\">'+esc(l.provider)+': '+esc(l.error)+'</div></div>';\n  c+=h;\n });\n $(\"cloud\").innerHTML=c||'<span class=\"dim\">no cloud limit sources</span>';\n if(dn.length)lo+='<details class=\"downs\"><summary>'+dn.length+' local model'+(dn.length===1?'':'s')+' down</summary>'+dn.map(function(n){return'<span class=\"chip bad\">'+esc(n)+'</span>'}).join(\" \")+'</details>';\n $(\"local\").innerHTML=lo||'<span class=\"dim\">no local providers</span>';\n}\nvar sys=null;\n/** 4th column: 0 = watts on the left axis (auto), 1 = percent on the fixed 0-100 right axis. */\nvar PKEYS=[[\"totalW\",\"power W\",\"#d98a1c\",0],[\"gpu\",\"GPU load\",\"#3b6fe0\",1],[\"cpu\",\"CPU load\",\"#2e9e5b\",1]];\nfunction gb(b,d){return b==null?\"n/a\":(b/1073741824).toFixed(d==null?1:d)}\n/** Last good readings per metric: a stale source keeps its number on the card instead of n/a. */\nvar LAST={};\n/** A reading the payload no longer carries: the last value stays, dimmed, with a ~age suffix. tip is pre-escaped. */\nfunction staleCell(m,now,fmt,tip){\n var s=Math.max(0,(now-m.at)/1000),a=s<90?Math.round(s)+\"s\":s<3600?Math.round(s/60)+\"m\":Math.round(s/3600)+\"h\";\n return'<span class=\"dim\"'+(tip?' title=\"'+tip+'\"':'')+'>'+fmt(m.v)+' <span class=\"warn\">~'+a+'</span></span>'\n}\nfunction renderSystem(){\n if(!sys)return;var c=sys.current||{};\n var tip=(sys.unavailable&&sys.unavailable.all)?sys.unavailable.all:((c.notes||[]).join(\"; \")||(\"source: \"+(c.source||\"none\")));\n function na(v){return v==null?'<span class=\"dim\" title=\"'+esc(tip)+'\">n/a</span>':v}\n /** Fresh number, or the last good one when the payload dropped it — the card never hides the value. */\n function val(key,fmt){\n  var v=c[key];\n  if(v!=null){LAST[key]={v:v,at:Date.now()};return fmt(v)}\n  var m=LAST[key];return m?staleCell(m,Date.now(),fmt,esc(tip)):na(null)\n }\n function pctf(v){return Math.round(v)+\"%\"}\n function w1(v){return v.toFixed(1)}\n var slowAge=sys.slowAgeS; // mactop probe (gpu/temp/power) rides the 1-min ring; say how old it is\n function tag(){return slowAge==null?\"\":\" · \"+(slowAge<90?\"probe \"+slowAge+\"s\":\"cached \"+(slowAge<3600?Math.round(slowAge/60)+\" min\":Math.round(slowAge/3600)+\" h\"))}\n var freq=c.gpuFreqMhz; // GPU clock for the load sublabel, remembered like the other probe values\n if(freq==null&&LAST.gpuFreqMhz)freq=LAST.gpuFreqMhz.v;else if(freq!=null)LAST.gpuFreqMhz={v:freq,at:Date.now()};\n var wh=(sys.series&&sys.series.wh&&sys.series.wh[win])||0,cov=(sys.series&&sys.series.coveredMin&&sys.series.coveredMin[win])||0;\n var kpis=[\n  [\"memory\",c.memUsed!=null?gb(c.memUsed):na(null),\"of \"+na(c.memTotal!=null?gb(c.memTotal):null)+\" · pressure \"+(c.pressureLevel!=null?(100-c.pressureLevel)+\"%\":na(null))],\n  [\"swap\",c.swapUsed!=null?gb(c.swapUsed):na(null),\"of \"+na(c.swapTotal!=null?gb(c.swapTotal):null)],\n  [\"wired\",c.wired!=null?gb(c.wired):na(null),\"vm_stat\"],\n  [\"compressed\",c.compressed!=null?gb(c.compressed):na(null),\"vm_stat\"],\n  [\"CPU load\",val(\"cpu\",pctf),\"all clusters\"],\n  [\"GPU load\",val(\"gpu\",pctf),(freq?(freq/1000).toFixed(1)+\" GHz\":\"IOReport\")+tag()],\n  [\"ANE load\",val(\"ane\",pctf),\"neural engine\"+tag(),\"ane\"],\n  [\"SoC temp\",val(\"socTemp\",function(v){return v.toFixed(1)+\" °C\"}),\"IOReport\"+tag()],\n  [\"CPU W\",val(\"cpuW\",w1),\"CPU rail\"+tag(),\"cpuW\"],\n  [\"GPU W\",val(\"gpuW\",w1),\"GPU rail\"+tag(),\"gpuW\"],\n  [\"ANE W\",val(\"aneW\",w1),\"neural engine\"+tag(),\"aneW\"],\n  [\"system W\",val(\"systemW\",w1),\"system rail\"+tag()],\n  [\"power\",val(\"totalW\",function(v){return w1(v)+\" W\"}),\"SoC total\"+tag()],\n  [\"energy \"+win,wh+\" Wh\",cov+\" min of 1-min samples\"]\n ];\n var hid=[],ss=(sys.series&&sys.series.series)||[];\n /** Unused metric: the window has samples for it and every one is 0 (no samples = unknown, stays visible). */\n function unused(key){var n=0;for(var i=0;i<ss.length;i++){var v=ss[i][key];if(v==null)continue;if(v!==0)return false;n++}return n>0&&!(c[key]>0)}\n kpis=kpis.filter(function(k){if(k[3]&&unused(k[3])){hid.push(k[0]);return false}return true});\n $(\"sys\").innerHTML=kpis.map(function(k){return'<div class=\"kpi\"><b>'+k[1]+'</b><span>'+esc(k[0])+'</span><span style=\"display:block;color:var(--dim)\">'+k[2]+'</span></div>'}).join(\"\")+(hid.length?'<div class=\"dim\" style=\"flex-basis:100%;font-size:11px\" title=\"Every sample in the selected window reads 0 for these; they reappear as soon as a non-zero reading arrives.\">hidden: '+esc(hid.join(\", \"))+' (0 in this window)</div>':\"\");\n var allSrv=sys.servers||[];\n var srv=allSrv.filter(function(s){return s.up!==false});\n function dec(p){var r=((data&&data.speed)||[]).filter(function(x){return x.provider===p&&(x.h1.writeTps!=null||x.h24.writeTps!=null)})[0];if(!r)return'<span class=\"dim\">-</span>';return fmt(r.h1.writeTps!=null?r.h1.writeTps:r.h24.writeTps)}\n$(\"sysservers\").innerHTML=srv.length?table([\"local server\",\"status\",\"RSS\",\"end-to-end tok/s\",\"decode tok/s\",\"prefix cache\"],srv.map(function(s){return[esc(s.provider),'<span class=\"'+(s.up?\"ok\":\"bad\")+'\">'+(s.up?\"up\":\"down\")+'</span>',s.rssMb!=null?s.rssMb+\" MB\":'<span class=\"dim\">n/a</span>',s.tokensPerSec!=null?s.tokensPerSec:'<span class=\"dim\">-</span>',dec(s.provider),s.prefixCache?pct(s.prefixCache.hitRate)+\" (\"+s.prefixCache.hits+\"/\"+(s.prefixCache.hits+s.prefixCache.misses)+\")\":lim?'<span class=\"dim\">not reported</span>':'<span class=\"dim\" title=\"loading: first limits poll pending\">…</span>']})):'<span class=\"dim\">'+(allSrv.length?\"all local model servers down\":\"no local model servers configured\")+'</span>';\n renderDonuts();\n}\nfunction trimEmpty(labels,rows,totalOf){\n var a=0,b=rows.length-1;\n while(a<=b&&!totalOf(rows[a]))a++;\n while(b>=a&&!totalOf(rows[b]))b--;\n return a>b?null:{labels:labels.slice(a,b+1),rows:rows.slice(a,b+1)}\n}\nfunction emptyChart(w,h){\n var padL=44,padB=18,padT=6,W=w-padL-6,H=h-padB-padT,svg='<svg width=\"100%\" viewBox=\"0 0 '+w+' '+h+'\" preserveAspectRatio=\"xMidYMid meet\">';\n for(var g=0;g<=3;g++){var yy=padT+H-H*g/3;svg+='<line x1=\"'+padL+'\" x2=\"'+(w-6)+'\" y1=\"'+yy+'\" y2=\"'+yy+'\" stroke=\"var(--line)\"/>'}\n svg+='<text x=\"'+(padL+W/2)+'\" y=\"'+(padT+H/2+3)+'\" text-anchor=\"middle\">no data in this window</text>';\n return svg+'</svg>';\n}\nfunction lineChart(el,labels,series,keys,w,h){\n var tr=trimEmpty(labels,series,function(s){var t=0;keys.forEach(function(k){t+=s[k[0]]||0});return t});\n if(!tr){el.innerHTML=emptyChart(w,h);return}\n labels=tr.labels;series=tr.rows;\n var padL=44,padR=34,padB=18,padT=6,W=w-padL-padR,H=h-padB-padT,n=labels.length,bw=W/n;\n var leftMax=1,hasR=false; // key[3]: 1 = percent, fixed 0-100 right axis; others share the auto left axis\n keys.forEach(function(k){if(k[3])hasR=true;else series.forEach(function(s){if((s[k[0]]||0)>leftMax)leftMax=s[k[0]]})});\n var PCT=[\"0\",\"33\",\"66\",\"100\"],svg='<svg width=\"100%\" viewBox=\"0 0 '+w+' '+h+'\" preserveAspectRatio=\"xMidYMid meet\">';\n for(var g=0;g<=3;g++){var yy=padT+H-H*g/3;svg+='<line x1=\"'+padL+'\" x2=\"'+(w-padR)+'\" y1=\"'+yy+'\" y2=\"'+yy+'\" stroke=\"var(--line)\"/><text x=\"'+(padL-4)+'\" y=\"'+(yy+3)+'\" text-anchor=\"end\">'+(leftMax*g/3).toFixed(1)+' W</text>'+\n  (hasR?'<text x=\"'+(w-2)+'\" y=\"'+(yy+3)+'\" text-anchor=\"end\">'+PCT[g]+'%</text>':'')}\n keys.forEach(function(k){\n  var scale=k[3]?100:leftMax,d=\"\",pen=false;\n  series.forEach(function(s,i){var v=s[k[0]];if(v==null){pen=false;return}\n   var x=padL+i*bw+bw/2,y=padT+H-(v/scale)*H;d+=(pen?\"L\":\"M\")+x.toFixed(1)+' '+y.toFixed(1);pen=true});\n  if(d)svg+='<path d=\"'+d+'\" fill=\"none\" stroke=\"'+k[2]+'\" stroke-width=\"1.5\"/>';\n });\n series.forEach(function(s,i){if(n<=30||i%Math.ceil(n/12)===0)svg+='<text x=\"'+(padL+i*bw+bw/2)+'\" y=\"'+(h-4)+'\" text-anchor=\"middle\">'+esc(labels[i])+'</text>'});\n el.innerHTML=svg+'</svg>';\n}\nfunction renderPower(){\n if(!sys||!sys.series)return;\n var ss=sys.series.series;\n lineChart($(\"power\"),ss.map(function(b){return label(b.t,sys.window)}),ss,PKEYS,760,150);\n $(\"plegend\").innerHTML=PKEYS.map(function(k){return'<span><i style=\"background:'+k[2]+'\"></i>'+esc(k[1])+'</span>'}).join(\"\");\n}\nfunction tick(){\n var els=document.querySelectorAll(\"[data-reset]\");\n for(var i=0;i<els.length;i++){var r=+els[i].getAttribute(\"data-reset\");if(r)els[i].textContent=\"resets in \"+dur(r-Date.now())}\n var st=$(\"stamp\"),age=(Date.now()-lastOk)/1000;\n st.textContent=lastOk?(age<15?\"live · \":\"stale \"+Math.round(age)+\"s · \")+new Date(lastOk).toLocaleTimeString():\"loading...\";\n st.className=lastOk&&age>=15?\"stale\":\"\";\n}\n/** Columns for the bar charts: runs of zero-total buckets collapse into one 0.4-width spacer. */\nfunction gapCols(totals){var cols=[],i=0;while(i<totals.length){if(totals[i]>0){cols.push({b:i,g:0});i++}else{var j=i;while(j<totals.length&&!totals[j])j++;cols.push({b:i,g:j-i});i=j}}return cols}\nfunction gapLayout(totals,w,padL){\n var cols=gapCols(totals),units=0;cols.forEach(function(c){units+=c.g?0.4:1});\n var rn=0;cols.forEach(function(c){if(!c.g)rn++});\n return{cols:cols,bw:(w-padL-6)/units,step:rn>30?Math.ceil(rn/12):0,compressed:cols.some(function(c){return c.g>0})}\n}\nfunction gapMark(x,top,bot){return'<line x1=\"'+x.toFixed(1)+'\" x2=\"'+x.toFixed(1)+'\" y1=\"'+top+'\" y2=\"'+bot+'\" stroke=\"var(--line)\" stroke-dasharray=\"2 3\"/>'}\nfunction stackedBars(el,labels,series,keys,w,h,tickFmt){\n var padL=44,padB=18,padT=6,H=h-padB-padT,max=1;\n var sums=series.map(function(s){var t=0;keys.forEach(function(k){t+=s[k]||0});return t});sums.forEach(function(v){if(v>max)max=v});\n var L=gapLayout(sums,w,padL),svg='<svg width=\"100%\" viewBox=\"0 0 '+w+' '+h+'\" preserveAspectRatio=\"xMidYMid meet\">';\n for(var g=0;g<=3;g++){var yy=padT+H-H*g/3;svg+='<line x1=\"'+padL+'\" x2=\"'+(w-6)+'\" y1=\"'+yy+'\" y2=\"'+yy+'\" stroke=\"var(--line)\"/><text x=\"'+(padL-4)+'\" y=\"'+(yy+3)+'\" text-anchor=\"end\">'+fmt(max*g/3)+'</text>'}\n var x=padL,ri=0;\n L.cols.forEach(function(c){\n  var cw=L.bw*(c.g?0.4:1);\n  if(c.g)svg+=gapMark(x+cw/2,padT,padT,padT+H);\n  else{var s=series[c.b],y=padT+H;\n   keys.forEach(function(k){var v=s[k]||0;if(!v)return;var bh=H*v/max;y-=bh;svg+='<rect x=\"'+(x+1).toFixed(1)+'\" y=\"'+y.toFixed(1)+'\" width=\"'+Math.max(1,cw-2).toFixed(1)+'\" height=\"'+bh.toFixed(1)+'\" fill=\"'+colorOf(k)+'\"><title>'+esc(labels[c.b])+' '+esc(k)+': '+fmt(v)+'</title></rect>'});\n   if(!L.step||ri%L.step===0)svg+='<text x=\"'+(x+cw/2).toFixed(1)+'\" y=\"'+(h-4)+'\" text-anchor=\"middle\">'+esc(labels[c.b])+'</text>';\n   ri++}\n  x+=cw;\n });\n el.innerHTML=svg+'</svg>';\n return L.compressed;\n}\nfunction twoToneModelBars(el,labels,buckets,models,w,h){\n var padL=44,padB=18,padT=6,W=w-padL-6,H=h-padB-padT,mc=models.length,max=1;\n models.forEach(function(m){colorOf(m)});\n var totals=buckets.map(function(arr){var t=0;arr.forEach(function(s){t+=s.total});return t});totals.forEach(function(v){if(v>max)max=v});\n var L=gapLayout(totals,w,padL),mwid=mc?Math.max(3,L.bw/mc):L.bw,svg='<svg width=\"100%\" viewBox=\"0 0 '+w+' '+h+'\" preserveAspectRatio=\"xMidYMid meet\">';\n for(var g=0;g<=3;g++){var yy=padT+H-H*g/3;svg+='<line x1=\"'+padL+'\" x2=\"'+(w-6)+'\" y1=\"'+yy+'\" y2=\"'+yy+'\" stroke=\"var(--line)\"/><text x=\"'+(padL-4)+'\" y=\"'+(yy+3)+'\" text-anchor=\"end\">'+fmt(max*g/3)+'</text>'}\n var x=padL,ri=0;\n L.cols.forEach(function(c){\n  var cw=L.bw*(c.g?0.4:1);\n  if(c.g)svg+=gapMark(x+cw/2,padT,padT,padT+H);\n  else{var arr=buckets[c.b];\n   arr.forEach(function(s){\n    var c2=colorOf(s.model),fresh=s.total-s.cacheRead,bx=(x+models.indexOf(s.model)*mwid+1).toFixed(1),bwid=Math.max(1,mwid-2).toFixed(1);\n    var tip=esc(s.model)+\" · \"+esc(labels[c.b])+\": total \"+fmt(s.total)+\" · non-cached \"+fmt(fresh)+\" · cached \"+fmt(s.cacheRead)+\" — \"+KINDS.map(function(k){return k[1]+\" \"+fmt(s[k[0]])}).join(\", \")+\" · \"+s.requests+\" req\";\n    svg+='<rect x=\"'+bx+'\" y=\"'+(padT+H-H*s.total/max).toFixed(1)+'\" width=\"'+bwid+'\" height=\"'+(H*s.total/max).toFixed(1)+'\" fill=\"'+c2+'\" fill-opacity=\".25\"><title>'+tip+'</title></rect>';\n    if(fresh>0)svg+='<rect x=\"'+bx+'\" y=\"'+(padT+H-H*fresh/max).toFixed(1)+'\" width=\"'+bwid+'\" height=\"'+(H*fresh/max).toFixed(1)+'\" fill=\"'+c2+'\"><title>'+tip+'</title></rect>';\n   });\n   if(!L.step||ri%L.step===0)svg+='<text x=\"'+(x+cw/2).toFixed(1)+'\" y=\"'+(h-4)+'\" text-anchor=\"middle\">'+esc(labels[c.b])+'</text>';\n   ri++}\n  x+=cw;\n });\n el.innerHTML=svg+'</svg>';\n return L.compressed;\n}\nfunction legend(el,keys){el.innerHTML=keys.map(function(k){return'<span><i style=\"background:'+colorOf(k)+'\"></i>'+esc(k)+'</span>'}).join(\"\")}\nfunction label(t,win){var d=new Date(t);return win===\"7d\"?d.toLocaleDateString([],{weekday:\"short\"}):win===\"24h\"?String(d.getHours()).padStart(2,\"0\")+\"h\":d.toLocaleTimeString([],{hour:\"2-digit\",minute:\"2-digit\"})}\nfunction donut(ratio,sz){\n var r=48,c=2*Math.PI*r,d=c*ratio;sz=sz||84;\n return'<svg width=\"'+sz+'\" height=\"'+sz+'\" viewBox=\"0 0 140 140\"><circle cx=\"70\" cy=\"70\" r=\"'+r+'\" fill=\"none\" stroke=\"var(--line)\" stroke-width=\"16\"/><circle cx=\"70\" cy=\"70\" r=\"'+r+'\" fill=\"none\" stroke=\"'+(ratio>.8?\"var(--ok)\":ratio>.5?\"var(--warn)\":\"var(--bad)\")+'\" stroke-width=\"16\" stroke-dasharray=\"'+d.toFixed(1)+' '+c.toFixed(1)+'\" transform=\"rotate(-90 70 70)\"/><text x=\"70\" y=\"'+(sz>120?79:76)+'\" text-anchor=\"middle\" style=\"font-size:'+(sz>120?28:22)+'px;font-weight:600;fill:var(--fg)\">'+pct(ratio)+'</text></svg>';\n}\nfunction emptyRing(sz){\n sz=sz||84;\n return'<svg width=\"'+sz+'\" height=\"'+sz+'\" viewBox=\"0 0 140 140\"><circle cx=\"70\" cy=\"70\" r=\"48\" fill=\"none\" stroke=\"var(--line)\" stroke-width=\"16\"/></svg>';\n}\nvar LOW_REQ=5;\n/** Models under LOW_REQ requests: one chip line under the grid, details on hover. */\nfunction lowChips(ms){\n var low=Object.keys(ms).map(function(k){return ms[k]}).filter(function(m){return m.requests<LOW_REQ});\n if(!low.length)return\"\";\n var tt=low.map(function(m){var dn=m.cacheRead+m.cacheWrite+m.input;return m.provider+\" · \"+m.model+\": \"+m.requests+\" req\"+(dn&&m.cacheRead+m.cacheWrite>0?\", hit \"+pct(m.cacheRead/dn):\"\")}).join(\"\\n\");\n return'<span class=\"chip dim\" title=\"'+esc(tt)+'\">+'+low.length+' model'+(low.length===1?'':'s')+' with &lt;'+LOW_REQ+' requests</span>';\n}\n/** One tile per provider/model, all the same shape: ring, provider, model + req. Loopback without cache fields: the server prefix-cache ring, else an empty ring. Models under LOW_REQ requests are dimmed, sorted last and never red. */\nfunction ringTiles(ms,servers){\n var list=Object.keys(ms).map(function(k){return ms[k]}).filter(function(m){return m.requests>=LOW_REQ});\n list.sort(function(a,b){var la=a.requests<LOW_REQ?1:0,lb=b.requests<LOW_REQ?1:0;return la!==lb?la-lb:b.requests-a.requests});\n return list.map(function(m){\n  var dn=m.cacheRead+m.cacheWrite+m.input,low=m.requests<LOW_REQ,ring,cap=\"\";\n  var srv=(servers||[]).filter(function(s){return s.provider===m.provider})[0];\n  if(m.cacheRead+m.cacheWrite>0){var r=dn?m.cacheRead/dn:0;ring=low?(r>0?donut(r):emptyRing()):donut(r)}\n  else if(srv&&srv.prefixCache&&!low){ring=donut(srv.prefixCache.hitRate);cap='<div class=\"s\" title=\"Hit rate of the local server\\'s own prefix cache (server counter since server start); this provider reports no cache fields per request.\">server prefix cache'+ageMark(srv)+'</div>'}\n  else{ring=emptyRing();cap='<div class=\"s\">'+(lim?\"not reported\":'<span class=\"dim\" title=\"loading: first limits poll pending\">…</span>')+'</div>'}\n  return'<div class=\"ring\">'+ring+'<div class=\"n\">'+esc(m.provider)+'</div><div class=\"s\">'+esc(m.model)+' · '+m.requests+' req</div>'+cap+'</div>'\n }).join(\"\");\n}\n/** hit% cell of the providers table: loopback providers report no cache fields, so show the server counter, marked. */\n/** \" (Ns old)\" when the server counter is a kept reading older than one poll interval (60 s). */\nfunction ageMark(s){\n return s&&s.prefixCacheAgeSec>60?' <span class=\"dim\">('+(s.prefixCacheAgeSec>=120?Math.round(s.prefixCacheAgeSec/60)+' min':s.prefixCacheAgeSec+'s')+' old)</span>':''\n}\nfunction provHit(p,servers){\n var srv=(servers||[]).filter(function(s){return s.provider===p.provider})[0];\n if(p.cacheRead+p.cacheWrite>0||!srv)return pct(p.hitRatio);\n return srv.prefixCache?'<span title=\"server prefix-cache counter since server start\">'+pct(srv.prefixCache.hitRate)+'*</span>'+ageMark(srv):'<span class=\"dim\">n/a</span>';\n}\n/** Line under a local server's ring: tokensSaved (server counter, since server start) against the router log's prompt total (since its first row). */\nfunction reuseLine(pc,tot,since){\n var w=since?\" since \"+since.slice(0,16).replace(\"T\",\" \"):\"\";\n if(!tot)return'<div class=\"s dim\" title=\"Prompt tokens served from cache: '+fmt(pc.tokensSaved)+' saved (server counter, since server start); no prompt tokens in the router log.\">cached '+fmt(pc.tokensSaved)+' · no log</div>';\n if(tot<pc.tokensSaved)return'<div class=\"s\" title=\"Prompt tokens served from cache: '+fmt(pc.tokensSaved)+' saved (server counter, since server start) vs '+fmt(tot)+' prompt tokens in the router log'+w+' — windows differ, no ratio.\">cached '+fmt(pc.tokensSaved)+' vs '+fmt(tot)+' (windows differ)</div>';\n return'<div class=\"s\" title=\"Prompt tokens served from cache: '+fmt(pc.tokensSaved)+' saved by the server since it started, of '+fmt(tot)+' prompt tokens in the router log'+w+'.\">cached '+pct(pc.tokensSaved/tot)+' · '+fmt(pc.tokensSaved)+' / '+fmt(tot)+'</div>';\n}\n/** One compact line per loopback provider (no ring: the providers grid tile already shows the hit rate). */\nfunction localDetail(prov,pc,tot,age){\n var mem=(pc.entries!=null?pc.entries+\" entries\":\"\")+(pc.entries!=null&&pc.memoryMb!=null?\" / \":\"\")+(pc.memoryMb!=null?Math.round(pc.memoryMb)+\" MB\":\"\");\n return'<div class=\"s\"><b>'+esc(prov)+'</b> · '+pc.hits+' hit / '+pc.misses+' miss · '+fmt(pc.tokensSaved)+' tokens saved (server, since start)'+(mem?' · '+mem:'')+(tot?' · router log: '+fmt(tot)+' prompt tokens (since restart)':'')+(age||'')+'</div>';\n}\nfunction table(head,rows,tips){return'<div class=\"tw\"><table><tr>'+head.map(function(h){return'<th'+(tips&&tips[h]?' title=\"'+esc(tips[h])+'\"':'')+'>'+h+'</th>'}).join(\"\")+'</tr>'+rows.map(function(r){return'<tr>'+r.map(function(c){return'<td>'+c+'</td>'}).join(\"\")+'</tr>'}).join(\"\")+'</table></div>'}\n/** Cache rings for the c4 card: cloud per model, then per model served by a local server (mapping from /cockpit/system). */\nfunction reuse(prov,pc){return reuseLine(pc,(data.promptByProvider||{})[prov],(data.promptSince||{})[prov])}\nfunction renderSpeed(){\n var sp=(data&&data.speed)||[];\n function c(st,k,u){return st[k]==null?'<span class=\"dim\">-</span>':fmt(st[k])+u}\n $(\"speed\").innerHTML=sp.length?table([\"provider\",\"model\",\"req 1h / 24h\",\"TTFT p50 1h\",\"p90 1h\",\"TTFT p50 24h\",\"p90 24h\",\"read tok/s 1h\",\"24h\",\"write tok/s 1h\",\"24h\"],sp.map(function(r){return[esc(r.provider),esc(r.model),r.h1.requests+\" / \"+r.h24.requests,c(r.h1,\"ttftP50\",\" ms\"),c(r.h1,\"ttftP90\",\" ms\"),c(r.h24,\"ttftP50\",\" ms\"),c(r.h24,\"ttftP90\",\" ms\"),c(r.h1,\"readTps\",\"\"),c(r.h24,\"readTps\",\"\"),c(r.h1,\"writeTps\",\"\"),c(r.h24,\"writeTps\",\"\")]}),{\"TTFT p50 1h\":\"Median time from request sent to first content token, last hour.\",\"TTFT p50 24h\":\"Median time from request sent to first content token, last 24 hours.\",\"p90 1h\":\"90th percentile TTFT, last hour.\",\"p90 24h\":\"90th percentile TTFT, last 24 hours.\",\"read tok/s 1h\":\"Input tokens / TTFT (prefill incl. cached for local models; Anthropic input excludes cache reads).\",\"write tok/s 1h\":\"Output tokens / decode time, replies of 16+ tokens only.\"}):'<span class=\"dim\" title=\"Timing is recorded for streamed replies since the router restart.\">no timed requests yet</span>';\n}\nfunction renderDonuts(){ renderSpeed();\n if(!data)return;var d=data,t=d.totals,ms={};\n d.byModel.forEach(function(m){var k=m.provider+\"\\u0000\"+m.model,o=ms[k]||(ms[k]={provider:m.provider,model:m.model,cacheRead:0,cacheWrite:0,input:0,requests:0});o.cacheRead+=m.cacheRead;o.cacheWrite+=m.cacheWrite;o.input+=m.input;o.requests+=m.requests});\n var mk=Object.keys(ms),dh=\"\",allSrv2=(sys&&sys.servers)||[];\n if(mk.length===1&&ms[mk[0]].cacheRead+ms[mk[0]].cacheWrite>0){var m=ms[mk[0]],dn=m.cacheRead+m.cacheWrite+m.input;\n  dh='<div class=\"solo\">'+donut(dn?m.cacheRead/dn:0,170)+'<div class=\"bignum\">'+pct(t.hitRatio)+'<span>overall hit ratio</span><span>'+esc(m.provider)+' · '+esc(m.model)+' · '+m.requests+' req</span></div></div>';\n }else dh=ringTiles(ms,allSrv2);\n var lowc=dh.indexOf('class=\"solo\"')>=0?\"\":lowChips(ms);\n $(\"donut\").innerHTML=dh||(lowc?\"\":'<span class=\"dim\">no data</span>');\n $(\"donutlow\").innerHTML=lowc;\n function chip(l,v,tt){return'<span class=\"chip\"'+(tt?' title=\"'+esc(tt)+'\"':'')+'>'+l+' <b>'+v+'</b></span>'}\n var note=chip(\"overall\",pct(t.hitRatio),\"cache_read / (read + write + fresh input), all providers and models\");\n var sk=d.sessionSplit;\n if(sk)note+=chip(\"continuous\",pct(sk.continuous.hitRatio)+\" (\"+sk.continuous.sessions+\" sess)\",\"continuous sessions: \"+sk.continuous.sessions+\" sessions, \"+sk.continuous.requests+\" requests\")+chip(\"one-shot\",pct(sk.oneShot.hitRatio)+\" (\"+sk.oneShot.sessions+\" sess)\",\"one-shot sessions: \"+sk.oneShot.sessions+\" sessions, \"+sk.oneShot.requests+\" requests\");\n if(d.pool&&d.pool.tasks)note+=chip(\"pool\",d.pool.tasks+\" task(s) · \"+fmt(d.pool.tokensCarried),d.pool.tasks+\" task(s), \"+fmt(d.pool.tokensCarried)+\" tokens carried\");\n $(\"donutnote\").innerHTML=note;\n var loc=(sys&&sys.servers||[]).filter(function(s){return s.up!==false});\n var lh=\"\";\n loc.forEach(function(s){if(s.prefixCache)lh+=localDetail(s.provider,s.prefixCache,(data.promptByProvider||{})[s.provider],ageMark(s))});\n $(\"donutlocaltitle\").style.display=lh?\"\":\"none\";\n $(\"donutlocal\").innerHTML=lh;\n}\nfunction cls(v,warn,bad){return v>=bad?\"bad\":v>=warn?\"warn\":\"\"}\nfunction render(){\n if(!data)return;var d=data,t=d.totals;\n $(\"kpis\").innerHTML=[[\"requests\",fmt(t.requests)],[\"input\",fmt(t.input)],[\"output\",fmt(t.output)],[\"cache read\",fmt(t.cacheRead)],[\"cache write\",fmt(t.cacheWrite)],[\"errors\",'<span class=\"'+cls(t.errors/Math.max(1,t.requests),.02,.1)+'\">'+t.errors+'</span>']].map(function(k){return'<div class=\"kpi\"><b>'+k[1]+'</b><span>'+k[0]+'</span></div>'}).join(\"\");\n var models=[];d.byModel.forEach(function(m){if(models.indexOf(m.model)<0)models.push(m.model)});\n var sel=$(\"msel\");if(msel&&models.indexOf(msel)<0)msel=\"\";\n sel.innerHTML='<option value=\"\">all models</option>'+models.map(function(m){return'<option'+(m===msel?' selected':'')+'>'+esc(m)+'</option>'}).join(\"\");\n sel.onchange=function(){msel=this.value;render()};\n var W=d.weights||{input:1,cacheWrite:1.25,cacheRead:.1,output:5},wt=mode===\"weighted\";\n var series=d.series.map(function(b){var o={};KINDS.forEach(function(k){o[k[0]]=0});Object.keys(b.models).forEach(function(m){if(msel&&m!==msel)return;KINDS.forEach(function(k){o[k[0]]+=(b.models[m][k[0]]||0)*(wt?W[k[0]]:1)})});return o});\n var buckets=d.series.map(function(b){var arr=[];models.forEach(function(m){var v=b.models[m];if(!v)return;var o={model:m,requests:v.requests||0};KINDS.forEach(function(k){o[k[0]]=(v[k[0]]||0)*(wt?W[k[0]]:1)});o.total=tot(o);if(o.total>0)arr.push(o)});return arr});\n var labs=d.series.map(function(b){return label(b.t,d.window)});\n var gap=msel?stackedBars($(\"series\"),labs,series,KINDS.map(function(k){return k[0]}),760,220):twoToneModelBars($(\"series\"),labs,buckets,models,760,220);\n $(\"legend\").innerHTML=KINDS.map(function(k){return'<span><i style=\"background:'+k[2]+'\"></i>'+k[1]+(wt?' x'+W[k[0]]:'')+'</span>'}).join(\"\")+(msel?\"\":'<span><i style=\"background:'+colorOf(\"input\")+';opacity:.25\"></i>total (light)</span><span><i style=\"background:'+colorOf(\"input\")+'\"></i>non-cached (solid)</span>'+models.map(function(m){return'<span><i style=\"background:'+colorOf(m)+'\"></i>'+esc(m)+'</span>'}).join(\"\"))+(gap?'<span class=\"dim\" title=\"Idle buckets are compressed: the x axis is not time-linear.\">idle compressed</span>':\"\");\n setTip($(\"wnote\"),wt?\"Weighted: tokens times Anthropic price ratios relative to fresh input (cache read \"+W.cacheRead+\", cache write \"+W.cacheWrite+\", output \"+W.output+\"). The same ratios are applied to every other provider, whatever its real prices.\":\"Raw: token counts as reported.\");\n var mb=document.querySelectorAll(\"#mode button\");for(var i=0;i<mb.length;i++){mb[i].className=mb[i].getAttribute(\"data-m\")===mode?\"on\":\"\";mb[i].onclick=function(){mode=this.getAttribute(\"data-m\");localStorage.setItem(\"cockpitMode\",mode);render()}}\n renderDonuts();\n var ph=d.cachePause.map(function(cm){\n  var cells=cm.buckets.map(function(b){if(!b.n)return'<td class=\"dim\">-</td>';var hr=b.hit/b.n;return'<td><span class=\"'+(hr<.5?\"bad\":hr<.8?\"warn\":\"ok\")+'\">'+Math.round(hr*100)+'%</span> <span class=\"dim\">'+b.n+'</span></td>'}).join(\"\");\n  return'<tr><td>'+esc(cm.model)+'</td>'+cells+'</tr>'}).join(\"\");\n var labs=d.cachePause.length?d.cachePause[0].buckets.map(function(b){return b.label}):[];\n $(\"pause\").innerHTML=ph?'<table><tr><th>model (hit% n)</th>'+labs.map(function(l){return'<th>'+esc(l)+'</th>'}).join(\"\")+'</tr>'+ph+'</table>':'<span class=\"dim\">no data</span>';\n $(\"providers\").innerHTML=table([\"provider\",\"req\",\"err%\",\"retries\",\"avg ms\",\"ttft p50\",\"hit%\",\"tokens\"],d.byProvider.map(function(p){var e=p.errors/Math.max(1,p.requests);return[esc(p.provider),p.requests,'<span class=\"'+cls(e,.02,.1)+'\">'+pct(e)+'</span>','<span class=\"'+(p.retries?\"warn\":\"\")+'\">'+p.retries+'</span>',p.avgMs,p.ttftP50!=null?fmt(p.ttftP50)+' ms':'<span class=\"dim\">-</span>',provHit(p,(sys&&sys.servers)||[]),fmt(tot(p))]}),{\"avg ms\":\"Average end-to-end milliseconds incl. queueing and prefill.\",\"ttft p50\":\"Median time from request sent to first token.\",\"retries\":\"Retried upstream calls.\",\"hit%\":\"cache_read / (read + write + fresh input); * = server counter.\"})+\n  '<div class=\"dim\">Busts: '+(Object.keys(d.bustParts).length?Object.keys(d.bustParts).map(function(k){return esc(k)+\" x\"+d.bustParts[k]}).join(\", \"):\"none\")+'</div>';\n $(\"sessions\").innerHTML=table([\"session\",\"models\",\"req\",\"tokens\",\"fresh in\",\"out\",\"hit%\",\"busts\",\"errors\",\"last\"],d.bySession.slice(0,20).map(function(s){var nm=s.name?esc(s.name)+' <span class=\"dim\">· '+esc(s.session.slice(0,8))+'</span>':esc(s.session.slice(0,8));var v=s.worker?' <span class=\"'+(s.verdict===\"ok\"?\"ok\":s.verdict===\"failed\"?\"bad\":\"dim\")+'\" title=\"worker '+esc(s.worker||\"\")+'\">'+esc(s.verdict||\"worker\")+'</span>':'';return['<span title=\"'+esc(s.session)+'\">'+nm+v+'</span>',esc(s.models.join(\", \")),s.requests,fmt(tot(s)),fmt(s.input+s.cacheWrite),fmt(s.output),'<span class=\"'+(s.hitRatio<.5?\"bad\":s.hitRatio<.8?\"warn\":\"ok\")+'\">'+pct(s.hitRatio)+'</span>','<span class=\"'+(s.busts?\"warn\":\"\")+'\">'+s.busts+'</span>',s.errors,ago(s.lastTs)]}));\n $(\"workers\").innerHTML=d.workers.length?table([\"worker\",\"label\",\"provider\",\"req\",\"tokens\",\"verdict\"],d.workers.slice(0,15).map(function(s){return[esc(s.worker),esc(s.name||\"-\"),esc(s.workerProvider||\"-\"),s.requests,fmt(tot(s)),'<span class=\"'+(s.verdict===\"ok\"?\"ok\":s.verdict===\"failed\"?\"bad\":\"dim\")+'\">'+esc(s.verdict||\"worker\")+'</span>']})):'<span class=\"dim\">no worker sessions in this window</span>';\n var dm=Object.keys(d.daily.models),ds=d.daily.days.map(function(t,i){var o={};dm.forEach(function(m){o[m]=d.daily.models[m][i]});return o});\n var dgap=stackedBars($(\"daily\"),d.daily.days.map(function(t){return new Date(t).toLocaleDateString([],{day:\"numeric\",month:\"numeric\"})}),ds,dm,520,200);\n legend($(\"dlegend\"),dm);if(dgap)$(\"dlegend\").innerHTML+='<span class=\"dim\" title=\"Idle buckets are compressed: the x axis is not time-linear.\">idle compressed</span>';\n}\nfunction load(){\n fetch(\"/cockpit/data?window=\"+win,{cache:\"no-store\"}).then(function(r){return r.json()}).then(function(j){data=j;lastOk=Date.now();render()}).catch(function(){});\n}\nfunction loadSystem(){fetch(\"/cockpit/system?window=\"+win,{cache:\"no-store\"}).then(function(r){return r.json()}).then(function(j){sys=j;renderSystem();renderPower();renderDonuts()}).catch(function(){})}\n${POOL_PANEL_JS}\nfunction loadLimits(){fetch(\"/cockpit/limits\",{cache:\"no-store\"}).then(function(r){return r.json()}).then(function(j){lim=j;renderLimits();if(sys)renderSystem();renderDonuts()}).catch(function(){})}\nfunction tabs(){var b=document.querySelectorAll(\"#tabs button\");for(var i=0;i<b.length;i++){b[i].className=b[i].getAttribute(\"data-w\")===win?\"on\":\"\";b[i].onclick=function(){win=this.getAttribute(\"data-w\");localStorage.setItem(\"cockpitWin\",win);tabs();load();loadSystem()}}}\ntabs();load();loadSystem();loadLimits();loadPool();setInterval(function(){load();loadSystem()},5000);setInterval(loadLimits,30000);setInterval(loadPool,15000);setInterval(tick,1000);\n})();\n</script></body></html>\n`;\n","/**\n * sessions.ts — Claude Code session id → display name, from PAI data only:\n * worker status files (claudeSession → label) and the agent-session registry\n * (session id → project). Unknown ids map to null.\n */\n\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport interface SessionInfo {\n  /** display name */\n  name: string;\n  /** worker id when the session is a worker run */\n  worker?: string;\n  state?: string;\n  /** worker outcome: ok, failed, or the raw state while running */\n  verdict?: string;\n  provider?: string;\n}\n\nexport interface SessionSources {\n  workerDirs: string[];\n  agentSessionsFile: string;\n  /** Claude Code transcript root: <dir>/<encoded cwd>/<session id>.jsonl */\n  projectsDir?: string;\n}\n\nexport function defaultSources(): SessionSources {\n  const home = homedir();\n  return {\n    workerDirs: [join(home, \".claude\", \"pai\", \"logs\", \"workers\"), join(home, \".claude\", \"logs\", \"workers\")],\n    agentSessionsFile: join(home, \".claude\", \"pai\", \"agent-sessions.json\"),\n    projectsDir: join(home, \".claude\", \"projects\"),\n  };\n}\n\nfunction readJson(file: string): Record<string, unknown> | undefined {\n  try {\n    const v = JSON.parse(readFileSync(file, \"utf8\")) as unknown;\n    return v && typeof v === \"object\" ? (v as Record<string, unknown>) : undefined;\n  } catch {\n    return undefined;\n  }\n}\n\nfunction workerVerdict(s: Record<string, unknown>): string {\n  // a run held in the stagger gate is running, but waiting on a cache warm-up\n  const w = s.warmup as { elapsedSec?: unknown; limitSec?: unknown } | undefined;\n  if (s.state === \"running\" && w && typeof w.elapsedSec === \"number\" && typeof w.limitSec === \"number\") {\n    return `waiting (warm-up) ${Math.max(0, Math.floor(w.elapsedSec))}s/${Math.floor(w.limitSec)}s`;\n  }\n  if (s.state !== \"done\" && s.state !== \"failed\" && s.state !== \"error\") return String(s.state ?? \"?\");\n  if (s.state !== \"done\" || (typeof s.rc === \"number\" && s.rc !== 0) || s.reportValid === false) return \"failed\";\n  return \"ok\";\n}\n\n/** Full map session id → info. Reads every status file once; callers cache it. */\nexport function loadSessionMap(src: SessionSources = defaultSources()): Map<string, SessionInfo> {\n  const map = new Map<string, SessionInfo>();\n  const agents = existsSync(src.agentSessionsFile) ? readJson(src.agentSessionsFile) : undefined;\n  if (agents) for (const [id, name] of Object.entries(agents)) if (typeof name === \"string\") map.set(id, { name });\n  for (const dir of src.workerDirs) {\n    if (!existsSync(dir)) continue;\n    for (const f of readdirSync(dir)) {\n      if (!f.endsWith(\".status\")) continue;\n      const s = readJson(join(dir, f));\n      if (!s || typeof s.claudeSession !== \"string\") continue;\n      map.set(s.claudeSession, {\n        name: typeof s.label === \"string\" && s.label ? s.label : String(s.id ?? f),\n        worker: typeof s.id === \"string\" ? s.id : undefined,\n        state: typeof s.state === \"string\" ? s.state : undefined,\n        verdict: workerVerdict(s),\n        provider: typeof s.provider === \"string\" ? s.provider : undefined,\n      });\n    }\n  }\n  // Last resort for interactive sessions the registry does not list: the project directory of the transcript.\n  if (src.projectsDir && existsSync(src.projectsDir)) {\n    for (const d of readdirSync(src.projectsDir)) {\n      const name = d.split(\"-\").filter(Boolean).pop();\n      if (!name) continue;\n      let files: string[] = [];\n      try {\n        files = readdirSync(join(src.projectsDir, d));\n      } catch {\n        continue;\n      }\n      for (const f of files) {\n        const id = f.endsWith(\".jsonl\") ? f.slice(0, -6) : \"\";\n        if (id && !map.has(id)) map.set(id, { name });\n      }\n    }\n  }\n  return map;\n}\n\n/** Display name of a session id, null when PAI does not know it. */\nexport function sessionDisplayName(id: string | undefined, map: Map<string, SessionInfo> = loadSessionMap()): string | null {\n  return (id && map.get(id)?.name) || null;\n}\n\n// ---- one-shot attribution -------------------------------------------------\n\n/**\n * Worker runs as time windows. One-shot flash runs often send their single\n * router request under a throwaway session id that no registry lists — the\n * status file's claudeSession belongs to the pane, not to the request. The\n * run window plus model/provider is then the only attribution PAI has, and\n * it is only used when it is unambiguous.\n */\nexport interface WorkerWindow {\n  worker: string;\n  label: string;\n  provider?: string;\n  model?: string;\n  startMs: number;\n  endMs: number;\n  claudeSession?: string;\n  state?: string;\n  verdict?: string;\n}\n\nfunction parseLocalTs(v: unknown): number | undefined {\n  return typeof v === \"string\" && Date.parse(v) ? Date.parse(v) : undefined;\n}\n\n/** Every status file as a run window; running runs end at `now`. */\nexport function loadWorkerWindows(src: SessionSources = defaultSources(), now: number = Date.now()): WorkerWindow[] {\n  const out: WorkerWindow[] = [];\n  for (const dir of src.workerDirs) {\n    if (!existsSync(dir)) continue;\n    let files: string[] = [];\n    try {\n      files = readdirSync(dir);\n    } catch {\n      continue;\n    }\n    for (const f of files) {\n      if (!f.endsWith(\".status\")) continue;\n      const s = readJson(join(dir, f));\n      if (!s) continue;\n      const startMs = parseLocalTs(s.started);\n      if (startMs === undefined) continue;\n      const endMs = parseLocalTs(s.updated) ?? (typeof s.secs === \"number\" ? startMs + s.secs * 1000 : undefined) ?? now;\n      out.push({\n        worker: String(s.id ?? f),\n        label: typeof s.label === \"string\" && s.label ? s.label : String(s.id ?? f),\n        provider: typeof s.provider === \"string\" ? s.provider : undefined,\n        model: typeof s.model === \"string\" ? s.model : undefined,\n        startMs,\n        endMs,\n        claudeSession: typeof s.claudeSession === \"string\" ? s.claudeSession : undefined,\n        state: typeof s.state === \"string\" ? s.state : undefined,\n        verdict: workerVerdict(s),\n      });\n    }\n  }\n  return out;\n}\n\nconst ATTRIBUTE_SLACK_MS = 120_000;\n\n/** Finished runs keep their exact window; only still-running ones get slack (their updated field trails the live request). */\nfunction windowBounds(w: WorkerWindow, now: number): { from: number; to: number } {\n  const finished = w.state === \"done\" || w.state === \"failed\" || w.state === \"error\";\n  return {\n    from: w.startMs,\n    to: finished ? w.endMs : Math.min(w.endMs + ATTRIBUTE_SLACK_MS, now + ATTRIBUTE_SLACK_MS),\n  };\n}\n\n/**\n * Extend an exact-match session map with time-attributed one-shot ids: a\n * session id that is not already a resolved worker run gets the worker label\n * when all of its requests fall inside a worker's run window (same model,\n * same provider). When several runs qualify — flash workers overlap — the\n * most recently started one wins: a one-shot request fires right after its\n * own run spawned, so the freshest live window is the likeliest owner.\n */\nexport function withWorkerAttribution(\n  map: Map<string, SessionInfo>,\n  rows: Array<{ ts: string; session_id?: string; model?: string; provider?: string }>,\n  windows: WorkerWindow[],\n  now: number = Date.now()\n): Map<string, SessionInfo> {\n  const unknown = new Map<string, Array<{ t: number; model?: string; provider?: string }>>();\n  for (const r of rows) {\n    if (!r.session_id || map.get(r.session_id)?.worker) continue;\n    const t = Date.parse(r.ts);\n    if (!Number.isFinite(t)) continue;\n    const list = unknown.get(r.session_id) ?? [];\n    list.push({ t, model: r.model, provider: r.provider });\n    unknown.set(r.session_id, list);\n  }\n  if (!unknown.size) return map;\n  const out = new Map(map);\n  for (const [id, reqs] of unknown) {\n    const hits = new Map<string, WorkerWindow>();\n    for (const w of windows) {\n      const { from, to } = windowBounds(w, now);\n      if (!reqs.some((r) => r.t >= from && r.t <= to)) continue;\n      if (reqs.some((r) => (w.model && r.model && w.model !== r.model) || (w.provider && r.provider && w.provider !== r.provider))) continue;\n      hits.set(w.worker, w);\n    }\n    if (!hits.size) continue;\n    // containment is required for every request; among the covering runs the freshest start wins\n    const first = Math.min(...reqs.map((r) => r.t));\n    const best = [...hits.values()].sort((a, b) => b.startMs - a.startMs)[0]!;\n    if (best.startMs > first) continue;\n    out.set(id, { name: best.label, worker: best.worker, state: best.state, verdict: best.verdict, provider: best.provider });\n  }\n  return out;\n}\n","/**\n * cockpit.ts — aggregation behind the router's /cockpit page. Pure functions\n * over usage rows; the HTTP handler at the bottom only serves them. The page\n * gets numbers and display names, never keys, bodies or prompt content.\n */\n\nimport { statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type http from \"node:http\";\nimport type { LimitsCache, ProviderLimits } from \"./limits.js\";\nimport { COCKPIT_HTML } from \"./cockpit-page.js\";\nimport { readUsage, splitBySessionKind, usageLogFile, type SessionKindSplit, type UsageRow } from \"./usage.js\";\nimport { loadSessionMap, loadWorkerWindows, withWorkerAttribution, type SessionInfo, type WorkerWindow } from \"./sessions.js\";\nimport { poolCarry, poolMetricsPath, readPoolMetrics, type PoolCarry } from \"../workers/pool.js\";\nimport { readWorkersSection } from \"../workers/config.js\";\nimport { workersLogDir } from \"../workers/paths.js\";import { sharedPoolPanel } from \"./pool-panel.js\";\nimport { buildSpeed, percentile } from \"./speed.js\";\nimport type { SystemMonitor } from \"./system.js\";\n\nexport type CockpitWindow = \"1h\" | \"24h\" | \"7d\";\n\nexport const WINDOWS: Record<CockpitWindow, { span: number; bucket: number }> = {\n  \"1h\": { span: 3_600_000, bucket: 300_000 },\n  \"24h\": { span: 86_400_000, bucket: 3_600_000 },\n  \"7d\": { span: 7 * 86_400_000, bucket: 86_400_000 },\n};\n\nconst DAY = 86_400_000;\nconst RETRY_WITHIN_MS = 120_000;\n\nexport const PAUSE_BUCKETS = [\"<1m\", \"1-5m\", \"5-60m\", \">60m\", \"first\"] as const;\n\nexport function pauseBucket(gapS: number | undefined): (typeof PAUSE_BUCKETS)[number] {\n  if (gapS === undefined) return \"first\";\n  if (gapS < 60) return \"<1m\";\n  if (gapS < 300) return \"1-5m\";\n  if (gapS < 3600) return \"5-60m\";\n  return \">60m\";\n}\n\nexport interface Tok {\n  input: number;\n  output: number;\n  cacheWrite: number;\n  cacheRead: number;\n}\n\n/** Price ratios relative to fresh input (Anthropic); applied to every provider. */\nexport const TOKEN_WEIGHTS = { input: 1, cacheWrite: 1.25, cacheRead: 0.1, output: 5 } as const;\n\nexport function weightedTokens(t: Tok): number {\n  return t.input * TOKEN_WEIGHTS.input + t.cacheWrite * TOKEN_WEIGHTS.cacheWrite + t.cacheRead * TOKEN_WEIGHTS.cacheRead + t.output * TOKEN_WEIGHTS.output;\n}\n\n/** Status strip groups: local = provider served from this machine (kind \"local\"), everything else is cloud. */\nexport function groupLimits<T extends { kind: string }>(limits: T[]): { cloud: T[]; local: T[] } {\n  return { cloud: limits.filter((l) => l.kind !== \"local\"), local: limits.filter((l) => l.kind === \"local\") };\n}\n\n/**\n * One key per model however the client spelled it: Claude Code sends\n * \"claude-opus-5-5[1m]\" for the 1M-context variant, snapshots carry a\n * -YYYYMMDD suffix, gateways a provider prefix. Only claude ids are folded;\n * other providers' ids are theirs and stay as served.\n */\nexport function canonicalModel(id: string): string {\n  const m = id.trim().toLowerCase().replace(/^anthropic\\//, \"\");\n  if (!m.startsWith(\"claude-\")) return id;\n  return m.replace(/\\[[^\\]]*\\]$/, \"\").replace(/-(?:latest|\\d{8})$/, \"\");\n}\n\n/**\n * Display owner of each model id: the ENABLED provider that serves it, from\n * workers.yaml (`models.*` values, canonical ids). Rows logged before local\n * workers went through the router carry the model id under a resolved\n * duplicate provider (a disabled one), not the provider name the operator\n * knows; this is how they fold into it.\n */\nexport function modelOwners(providers: Record<string, { enabled?: boolean; models?: Record<string, unknown> }>): Map<string, string> {\n  const owners = new Map<string, string>();\n  for (const [name, p] of Object.entries(providers)) {\n    if (p.enabled === false) continue;\n    for (const m of Object.values(p.models ?? {})) {\n      if (typeof m === \"string\" && !owners.has(canonicalModel(m))) owners.set(canonicalModel(m), name);\n    }\n  }\n  return owners;\n}\n\n/**\n * One provider key per row, the provider name: a row whose provider is not an\n * enabled one (a disabled duplicate such as the old model-resolved `coder8`)\n * is attributed to the enabled provider that serves its model. Rows of live\n * providers, and models nobody serves, stay as logged.\n */\nexport function foldProviders(rows: UsageRow[], providers: Record<string, { enabled?: boolean }>, owners: Map<string, string>): UsageRow[] {\n  if (!owners.size) return rows;\n  const live = (name: string) => name === \"anthropic\" || providers[name]?.enabled !== false && name in providers;\n  return rows.map((r) => {\n    if (live(r.provider)) return r;\n    const owner = owners.get(canonicalModel(r.model));\n    return owner && owner !== r.provider ? { ...r, provider: owner } : r;\n  });\n}\n\nconst tok = (): Tok => ({ input: 0, output: 0, cacheWrite: 0, cacheRead: 0 });\nconst addTok = (t: Tok, r: UsageRow) => {\n  t.input += r.input_tokens ?? 0;\n  t.output += r.output_tokens ?? 0;\n  t.cacheWrite += r.cache_creation_input_tokens ?? 0;\n  t.cacheRead += r.cache_read_input_tokens ?? 0;\n};\nconst hitRatio = (t: Tok) => {\n  const d = t.cacheRead + t.cacheWrite + t.input;\n  return d ? t.cacheRead / d : 0;\n};\n\nexport function bucketStart(ts: number, bucket: number): number {\n  return Math.floor(ts / bucket) * bucket;\n}\n\n/** Row indexes (in the given list) that are retries: same session, within 2 min of a failed request. */\nexport function retryRows(rows: UsageRow[]): Set<UsageRow> {\n  const out = new Set<UsageRow>();\n  const lastFail = new Map<string, number>();\n  const sorted = [...rows].sort((a, b) => a.ts.localeCompare(b.ts));\n  for (const r of sorted) {\n    const key = r.session_id ?? r.session;\n    if (!key) continue;\n    const t = Date.parse(r.ts);\n    const f = lastFail.get(key);\n    if (f !== undefined && t - f <= RETRY_WITHIN_MS) out.add(r);\n    if (r.status >= 400) lastFail.set(key, t);\n    else lastFail.delete(key);\n  }\n  return out;\n}\n\nfunction configuredProviders(): Record<string, { enabled?: boolean; models?: Record<string, unknown> }> {\n  try {\n    return readWorkersSection().workers.providers;\n  } catch {\n    return {};\n  }\n}\n\nexport function buildCockpitData(\n  allRows: UsageRow[],\n  win: CockpitWindow,\n  now: number = Date.now(),\n  sessions: Map<string, SessionInfo> = new Map(),\n  pool: PoolCarry | null = null,\n  providers: Record<string, { enabled?: boolean; models?: Record<string, unknown> }> = {}\n) {\n  allRows = foldProviders(allRows, providers, modelOwners(providers));\n  const { span, bucket } = WINDOWS[win];\n  const from = bucketStart(now - span, bucket);\n  const rows = allRows.filter((r) => Date.parse(r.ts) >= from);\n  const retries = retryRows(rows);\n\n  // series: one entry per bucket, per model token parts\n  const series: Array<{ t: number; models: Record<string, Tok & { requests: number }> }> = [];\n  for (let t = from; t <= now; t += bucket) series.push({ t, models: {} });\n  const total = tok();\n  let errors = 0;\n  const byModel = new Map<string, Tok & { model: string; provider: string; requests: number; errors: number }>();\n  const byProvider = new Map<string, Tok & { provider: string; requests: number; errors: number; retries: number; ms: number }>();\n  const ttftByProvider = new Map<string, number[]>();\n  const bySession = new Map<string, Tok & { session: string; name: string | null; requests: number; errors: number; busts: number; lastTs: string; models: Set<string> }>();\n  const pause = new Map<string, Map<string, { n: number; hit: number; write: number; miss: number; readTok: number; writeTok: number }>>();\n  const bustParts: Record<string, number> = {};\n\n  for (const r of rows) {\n    const model = canonicalModel(r.model);\n    const t = Date.parse(r.ts);\n    addTok(total, r);\n    const isErr = r.status >= 400;\n    if (isErr) errors++;\n\n    const b = series[Math.floor((bucketStart(t, bucket) - from) / bucket)];\n    if (b) {\n      const m = (b.models[model] ??= { ...tok(), requests: 0 });\n      addTok(m, r);\n      m.requests++;\n    }\n\n    const mk = `${r.provider}\\u0000${model}`;\n    const m = byModel.get(mk) ?? { ...tok(), model, provider: r.provider, requests: 0, errors: 0 };\n    addTok(m, r);\n    m.requests++;\n    if (isErr) m.errors++;\n    byModel.set(mk, m);\n\n    const p = byProvider.get(r.provider) ?? { ...tok(), provider: r.provider, requests: 0, errors: 0, retries: 0, ms: 0 };\n    addTok(p, r);\n    p.requests++;\n    p.ms += r.ms ?? 0;\n    if (isErr) p.errors++;\n    if (retries.has(r)) p.retries++;\n    byProvider.set(r.provider, p);\n    if (!isErr && r.ttftMs !== undefined) {\n      const ts = ttftByProvider.get(r.provider) ?? [];\n      ts.push(r.ttftMs);\n      ttftByProvider.set(r.provider, ts);\n    }\n\n    const sid = r.session_id ?? r.session ?? \"-\";\n    const s = bySession.get(sid) ?? {\n      ...tok(), session: sid, name: r.session_id ? (sessions.get(r.session_id)?.name ?? null) : null,\n      requests: 0, errors: 0, busts: 0, lastTs: r.ts, models: new Set<string>(),\n    };\n    addTok(s, r);\n    s.requests++;\n    if (isErr) s.errors++;\n    if (r.bust?.length) {\n      s.busts++;\n      for (const part of r.bust) bustParts[part] = (bustParts[part] ?? 0) + 1;\n    }\n    if (r.ts > s.lastTs) s.lastTs = r.ts;\n    s.models.add(model);\n    bySession.set(sid, s);\n\n    if (r.status < 400) {\n      const pm = pause.get(model) ?? new Map();\n      const pb = pauseBucket(r.gap_s);\n      const c = pm.get(pb) ?? { n: 0, hit: 0, write: 0, miss: 0, readTok: 0, writeTok: 0 };\n      c.n++;\n      c[r.cache]++;\n      c.readTok += r.cache_read_input_tokens ?? 0;\n      c.writeTok += r.cache_creation_input_tokens ?? 0;\n      pm.set(pb, c);\n      pause.set(model, pm);\n    }\n  }\n\n  const sessionRows = [...bySession.values()]\n    .map((s) => {\n      const info = sessions.get(s.session);\n      return {\n        ...s,\n        models: [...s.models],\n        hitRatio: hitRatio(s),\n        worker: info?.worker ?? null,\n        verdict: info?.verdict ?? null,\n        workerProvider: info?.provider ?? null,\n      };\n    })\n    .sort((a, b) => b.input + b.output + b.cacheWrite + b.cacheRead - (a.input + a.output + a.cacheWrite + a.cacheRead));\n\n  // tokens per day per model: always the last 14 days, whatever the window\n  const dayFrom = bucketStart(now, DAY) - 13 * DAY;\n  const days: number[] = [];\n  for (let t = dayFrom; t <= now; t += DAY) days.push(t);\n  const daily: Record<string, number[]> = {};\n  for (const r of allRows) {\n    const t = Date.parse(r.ts);\n    if (t < dayFrom) continue;\n    const arr = (daily[canonicalModel(r.model)] ??= days.map(() => 0));\n    arr[Math.floor((bucketStart(t, DAY) - dayFrom) / DAY)]! += (r.input_tokens ?? 0) + (r.output_tokens ?? 0) + (r.cache_creation_input_tokens ?? 0) + (r.cache_read_input_tokens ?? 0);\n  }\n\n  // prompt tokens per provider over the whole log: denominator for the local\n  // servers' tokensSaved counter, which the stats endpoint gives no total for\n  const promptByProvider: Record<string, number> = {};\n  // first row per provider: the log (router restart) is younger than a server's own counters\n  const promptSince: Record<string, string> = {};\n  for (const r of allRows) {\n    if (!promptSince[r.provider] || r.ts < promptSince[r.provider]!) promptSince[r.provider] = r.ts;\n    promptByProvider[r.provider] = (promptByProvider[r.provider] ?? 0) + (r.input_tokens ?? 0) + (r.cache_read_input_tokens ?? 0) + (r.cache_creation_input_tokens ?? 0);\n  }\n\n  const withRatio = <T extends Tok>(x: T) => ({ ...x, hitRatio: hitRatio(x) });\n  const sessionSplit: SessionKindSplit = splitBySessionKind(rows);\n  return {\n    window: win,\n    bucketMs: bucket,\n    weights: TOKEN_WEIGHTS,\n    from,\n    now,\n    totals: { ...withRatio(total), requests: rows.length, errors },\n    series,\n    byModel: [...byModel.values()].map(withRatio).sort((a, b) => b.requests - a.requests),\n    byProvider: [...byProvider.values()].map((p) => ({ ...withRatio(p), avgMs: p.requests ? Math.round(p.ms / p.requests) : 0, ttftP50: percentile(ttftByProvider.get(p.provider) ?? [], 0.5) })),\n    bySession: sessionRows,\n    cachePause: [...pause.entries()].map(([model, pm]) => ({\n      model,\n      buckets: PAUSE_BUCKETS.map((label) => ({ label, ...(pm.get(label) ?? { n: 0, hit: 0, write: 0, miss: 0, readTok: 0, writeTok: 0 }) })),\n    })),\n    bustParts,\n    speed: buildSpeed(allRows, now, canonicalModel),\n    promptByProvider,\n    promptSince,\n    workers: sessionRows.filter((s) => s.worker),\n    daily: { days, models: daily },\n    // continuous vs one-shot sessions: the overall ratio alone averages a\n    // cache-carrying population and a cold-spawn population into one number\n    // that predicts neither\n    sessionSplit,\n    // the worker session pool's carry-over: tasks served pooled and the\n    // cache_read tokens their first requests were spared (null = no pool)\n    pool: pool && pool.tasks > 0 ? pool : null,\n  };\n}\n\n/** Reread the usage log only when its size or mtime changed. */\nexport function cachedRows(file: string = usageLogFile()): () => UsageRow[] {\n  let key = \"\";\n  let rows: UsageRow[] = [];\n  return () => {\n    try {\n      const st = statSync(file);\n      const k = `${st.size}:${st.mtimeMs}`;\n      if (k !== key) {\n        rows = readUsage(file);\n        key = k;\n      }\n    } catch {\n      rows = [];\n    }\n    return rows;\n  };\n}\n\n/** Session map reloaded at most every 30 s (status files are many). */\nexport function cachedSessionMap(ttlMs = 30_000): () => Map<string, SessionInfo> {\n  let at = 0;\n  let map = new Map<string, SessionInfo>();\n  return () => {\n    if (Date.now() - at > ttlMs) {\n      map = loadSessionMap();\n      at = Date.now();\n    }\n    return map;\n  };\n}\n\n/** Worker run windows reloaded at most every 60 s (same status files, one pass). */\nexport function cachedWorkerWindows(ttlMs = 60_000): () => WorkerWindow[] {\n  let at = 0;\n  let windows: WorkerWindow[] = [];\n  return () => {\n    if (Date.now() - at > ttlMs) {\n      windows = loadWorkerWindows();\n      at = Date.now();\n    }\n    return windows;\n  };\n}\n\n/**\n * Session names for the cockpit: the exact map plus one-shot ids attributed\n * to their worker run window. The exact map is cached; attribution itself is\n * cheap (unknown ids in the window only) and runs per request against the\n * rows being rendered.\n */\nfunction namedSessionMap(rows: UsageRow[], sessionMap: () => Map<string, SessionInfo>, workerWindows: () => WorkerWindow[]): Map<string, SessionInfo> {\n  return withWorkerAttribution(sessionMap(), rows, workerWindows());\n}\n\nexport function parseWindow(q: string | null): CockpitWindow {\n  return q === \"24h\" || q === \"7d\" ? q : \"1h\";\n}\n\n/** Anything with a cached provider poll; LimitsCache in production, stubs in tests. */\nexport type LimitsSource = Pick<LimitsCache, \"get\">;\n\n/** Local model servers as the SYSTEM panel shows them (from the limits poll). */\nexport function systemServers(limits: ProviderLimits[]): Array<Pick<ProviderLimits, \"provider\" | \"up\" | \"rssMb\" | \"tokensPerSec\" | \"decodeTokPerSec\" | \"served\" | \"prefixCache\" | \"prefixCacheAgeSec\">> {\n  return limits\n    .filter((l) => l.kind === \"local\")\n    .map(({ provider, up, rssMb, tokensPerSec, decodeTokPerSec, served, prefixCache, prefixCacheAgeSec }) => ({\n      provider,\n      up,\n      rssMb,\n      tokensPerSec,\n      decodeTokPerSec,\n      ...(served ? { served } : {}),\n      ...(prefixCache ? { prefixCache } : {}),\n      ...(prefixCache && prefixCacheAgeSec ? { prefixCacheAgeSec } : {}),\n    }));\n}\n\n/**\n * Pool carry-over as /cockpit/data shows it, refreshed at most every 30 s:\n * reads the pool metrics file from the configured workers logDir; a pool\n * that never ran leaves the payload field null.\n */\nexport function cachedPoolCarry(): () => PoolCarry | null {\n  let at = 0;\n  let carry: PoolCarry | null = null;\n  return () => {\n    if (Date.now() - at > 30_000) {\n      at = Date.now();\n      carry = null;\n      try {\n        const { workers } = readWorkersSection();\n        carry = poolCarry(readPoolMetrics(poolMetricsPath(join(workersLogDir(workers)))));\n      } catch {\n        carry = null;\n      }\n    }\n    return carry;\n  };\n}\n\n/**\n * Serve /cockpit, /cockpit/data, /cockpit/system and /cockpit/limits. Returns\n * false when the path is not a cockpit path. GET only; the router binds\n * 127.0.0.1 unless router.bind opens a second listener (then the cockpit is\n * token-gated, see src/router/cockpit-auth.ts).\n */\nexport function handleCockpit(\n  req: http.IncomingMessage,\n  res: http.ServerResponse,\n  limits: LimitsSource,\n  readRows: () => UsageRow[] = readUsage,\n  sessionMap: () => Map<string, SessionInfo> = loadSessionMap,\n  system?: SystemMonitor,\n  workerWindows: () => WorkerWindow[] = loadWorkerWindows,\n  readPool: () => PoolCarry | null = cachedPoolCarry(),\n  readPoolPanel: typeof sharedPoolPanel = sharedPoolPanel): boolean {\n  const url = new URL(req.url ?? \"/\", \"http://localhost\");\n  if (url.pathname !== \"/cockpit\" && !url.pathname.startsWith(\"/cockpit/\")) return false;\n  const send = (status: number, type: string, body: string) => {\n    res.writeHead(status, { \"content-type\": type, \"cache-control\": \"no-store\" });\n    res.end(body);\n  };\n  if (req.method !== \"GET\") return send(405, \"text/plain\", \"GET only\"), true;\n  if (url.pathname === \"/cockpit\") send(200, \"text/html; charset=utf-8\", COCKPIT_HTML);\n  else if (url.pathname === \"/cockpit/data\") {\n    try {\n      const rows = readRows();\n      send(200, \"application/json\", JSON.stringify(buildCockpitData(rows, parseWindow(url.searchParams.get(\"window\")), Date.now(), namedSessionMap(rows, sessionMap, workerWindows), readPool(), configuredProviders())));\n    } catch (e) {\n      send(500, \"application/json\", JSON.stringify({ error: (e as Error).message }));\n    }\n  } else if (url.pathname === \"/cockpit/system\") {\n    if (!system) return send(503, \"application/json\", '{\"error\":\"system metrics not configured\"}'), true;\n    Promise.all([\n      system.snapshot(parseWindow(url.searchParams.get(\"window\"))),\n      limits.get().catch((): ProviderLimits[] => []),\n    ]).then(\n      ([snap, ls]) => send(200, \"application/json\", JSON.stringify({ ...snap, servers: systemServers(ls) })),\n      (e: Error) => send(500, \"application/json\", JSON.stringify({ error: e.message }))\n    );\n  } else if (url.pathname === \"/cockpit/pool\") {\n    readPoolPanel().then((p) => send(200, \"application/json\", JSON.stringify(p)));\n  } else if (url.pathname === \"/cockpit/limits\") {\n    limits.get().then(\n      (l) => send(200, \"application/json\", JSON.stringify({ now: Date.now(), limits: l })),\n      (e: Error) => send(500, \"application/json\", JSON.stringify({ error: e.message }))\n    );\n  } else send(404, \"text/plain\", \"not found\");\n  return true;\n}\n","import type http from \"node:http\";\n\nexport function listenRetry(\n  server: http.Server,\n  port: number,\n  host: string,\n  opts: { waitMs: number; intervalMs?: number }\n): Promise<void> {\n  const { waitMs, intervalMs = 100 } = opts;\n  const start = Date.now();\n\n  return new Promise((resolve, reject) => {\n    const onError = (err: NodeJS.ErrnoException) => {\n      if (err.code === \"EADDRINUSE\" && Date.now() - start < waitMs) {\n        setTimeout(() => {\n          server.listen(port, host);\n        }, intervalMs);\n      } else {\n        reject(err);\n      }\n    };\n\n    const onListening = () => {\n      resolve();\n    };\n\n    server.on(\"error\", onError);\n    server.on(\"listening\", onListening);\n\n    server.listen(port, host);\n\n    // Clean up listeners when settled\n    const cleanup = () => {\n      server.off(\"error\", onError);\n      server.off(\"listening\", onListening);\n    };\n\n    // Override resolve and reject to ensure cleanup\n    const originalResolve = resolve;\n    const originalReject = reject;\n\n    resolve = (value?: void | PromiseLike<void>) => {\n      cleanup();\n      originalResolve(value);\n    };\n\n    reject = (reason?: any) => {\n      cleanup();\n      originalReject(reason);\n    };\n  });\n}","import http from 'http';\n\nexport function drainServers(servers: http.Server[], opts?: { timeoutMs?: number }): Promise<void> {\n  const timeoutMs = opts?.timeoutMs ?? 120000;\n\n  // Track servers that are already closed or not listening\n  const closedServers = new Set<http.Server>();\n\n  // Track servers that are actively closing\n  const closingServers = new Set<http.Server>();\n\n  // Track the promises for each server's close operation\n  const closePromises: Promise<void>[] = [];\n\n  // Create a timer to track timeout\n  let timeoutId: NodeJS.Timeout | null = null;\n\n  // Create a timer to periodically call closeIdleConnections\n  let idleCloseTimer: NodeJS.Timeout | null = null;\n\n  // Function to handle server close completion\n  const handleServerClose = (server: http.Server) => {\n    closedServers.add(server);\n    closingServers.delete(server);\n\n    // If all servers are done, resolve the promise\n    if (closedServers.size === servers.length) {\n      if (timeoutId) {\n        clearTimeout(timeoutId);\n      }\n      if (idleCloseTimer) {\n        clearInterval(idleCloseTimer);\n      }\n    }\n  };\n\n  // Close each server\n  for (const server of servers) {\n    if (server.listening) {\n      closingServers.add(server);\n\n      // Create a promise for this server's close operation\n      const closePromise = new Promise<void>((resolve) => {\n        server.close((err) => {\n          // If server was already closed, err will be ERR_SERVER_NOT_RUNNING\n          if (err && (err as any).code === 'ERR_SERVER_NOT_RUNNING') {\n            handleServerClose(server);\n            resolve();\n          } else {\n            // Call closeIdleConnections after close callback\n            server.closeIdleConnections();\n            handleServerClose(server);\n            resolve();\n          }\n        });\n      });\n\n      closePromises.push(closePromise);\n    } else {\n      // Server not listening, treat as already closed\n      closedServers.add(server);\n    }\n  }\n\n  // If no servers to close, resolve immediately\n  if (closePromises.length === 0) {\n    return Promise.resolve();\n  }\n\n  // Set up periodic idle connection cleanup\n  idleCloseTimer = setInterval(() => {\n    for (const server of servers) {\n      if (closingServers.has(server)) {\n        server.closeIdleConnections();\n      }\n    }\n  }, 200);\n\n  // Create the main promise that resolves when all servers are closed\n  const mainPromise = Promise.all(closePromises);\n\n  // Set up timeout\n  const timeoutPromise = new Promise<void>((resolve) => {\n    timeoutId = setTimeout(() => {\n      // Force close all remaining servers\n      for (const server of servers) {\n        if (closingServers.has(server)) {\n          server.closeAllConnections();\n        }\n      }\n\n      if (idleCloseTimer) {\n        clearInterval(idleCloseTimer);\n      }\n\n      resolve();\n    }, timeoutMs);\n  });\n\n  // Race between main promise and timeout\n  return Promise.race([mainPromise, timeoutPromise]).then(() => {\n    // Cleanup\n    if (timeoutId) {\n      clearTimeout(timeoutId);\n    }\n    if (idleCloseTimer) {\n      clearInterval(idleCloseTimer);\n    }\n  });\n}","/**\n * system.ts — machine metrics for the cockpit SYSTEM panel. Sources, no sudo\n * anywhere:\n *\n *   direct   cheap no-sudo commands, polled per request (ttl 5 s): CPU load\n *            from `/usr/bin/top -l 2` (the second sample; refreshed at most\n *            every 10 s), swap from `sysctl vm.swapusage`, memory total and\n *            pressure from `sysctl`, wired/compressed/used from `vm_stat`\n *   mactop   the IOReport/SMC set — GPU/ANE load, SoC temperature, power\n *            rails — has no no-sudo alternative (powermetrics needs sudo), so\n *            it stays as an optional extra: one-shot\n *            `mactop --headless --count 2 --format json` (count 1 can return\n *            an empty frame) measures over an interval and takes ~10 s, so it\n *            runs as a background probe at the 1-min ring cadence, never\n *            blocking a request; its values are served from the last good\n *            probe for up to 5 min\n *\n * Every 1-min sample is appended to a small JSONL ring (7 days) so day and\n * week windows chart power and memory history and report watt-hours. Metrics\n * a source does not give stay undefined; the panel shows \"n/a\" with the\n * sample's notes as tooltip.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { paiHomePath } from \"../config/pai-home.js\";\nimport { dirname, join } from \"node:path\";\nimport { bucketStart, WINDOWS, type CockpitWindow } from \"./cockpit.js\";\n\nexport const systemRingFile = (): string => paiHomePath(\"logs\", \"router-system.jsonl\");\nconst MIN = 60_000;\nconst DAY = 86_400_000;\n/** direct commands are sub-second; never wait longer */\nconst CMD_TIMEOUT = 8_000;\n/** mactop measures over an interval; the old 8 s budget killed every probe */\nconst MACTOP_TIMEOUT = 15_000;\n/** count 1 often emits an empty frame before the first IOReport interval closes */\nconst MACTOP_COUNT = 2;\n/** last-known mactop values are served this long while the probe retries */\nconst SLOW_TTL = 5 * MIN;\n/** CPU load refresh cadence; a `top -l 2` pass costs ~2 s */\nconst TOP_TTL = 10_000;\nconst MACTOP_CANDIDATES = [\"mactop\", \"/opt/homebrew/bin/mactop\", \"/opt/local/bin/mactop\"];\n/** absolute: `top` is aliased to htop in some shells and PATH order is luck */\nconst TOP_CMD = \"/usr/bin/top\";\n\n/** One reading; undefined = this source did not report the metric. */\nexport interface SystemSample {\n  t: number;\n  /** percent */\n  cpu?: number;\n  gpu?: number;\n  ane?: number;\n  gpuFreqMhz?: number;\n  /** °C */\n  socTemp?: number;\n  /** watts */\n  cpuW?: number;\n  gpuW?: number;\n  aneW?: number;\n  systemW?: number;\n  totalW?: number;\n  /** bytes */\n  memTotal?: number;\n  memUsed?: number;\n  swapTotal?: number;\n  swapUsed?: number;\n  wired?: number;\n  compressed?: number;\n  /** kern.memorystatus_level, 1-100; higher = less pressure */\n  pressureLevel?: number;\n  source: \"direct\" | \"mactop\" | \"vmstat\";\n  /** why metrics are missing or stale, shown as tooltip */\n  notes?: string[];\n}\n\nconst num = (v: unknown): number | undefined => (typeof v === \"number\" && Number.isFinite(v) ? v : undefined);\n\n/** Parse `mactop --headless --count N --format json` (array of frames, last wins). */\nexport function parseMactopSample(stdout: string, t: number = Date.now()): SystemSample {\n  const j = JSON.parse(stdout) as Record<string, unknown> | Array<Record<string, unknown>>;\n  const s = (Array.isArray(j) ? j[j.length - 1] : j) ?? {};\n  const soc = (s.soc_metrics ?? {}) as Record<string, unknown>;\n  const mem = (s.memory ?? {}) as Record<string, unknown>;\n  const notes: string[] = [];\n  // some SoCs fold the CPU rail into system_power; say so instead of a bare 0\n  const cpuW = num(soc.cpu_power);\n  const systemW = num(soc.system_power);\n  if (cpuW === 0 && systemW && systemW > 1) notes.push(\"CPU rail reads 0 W on this SoC; it is part of the system rail\");\n  return {\n    t,\n    cpu: num(s.cpu_usage),\n    gpu: num(soc.gpu_active) ?? num(s.gpu_usage),\n    ane: num(soc.ane_active),\n    gpuFreqMhz: num(soc.gpu_freq_mhz),\n    socTemp: num(soc.soc_temp) ?? num(soc.cpu_temp),\n    cpuW,\n    gpuW: num(soc.gpu_power),\n    aneW: num(soc.ane_power),\n    systemW,\n    totalW: num(soc.total_power),\n    memTotal: num(mem.total),\n    memUsed: num(mem.used),\n    swapTotal: num(mem.swap_total),\n    swapUsed: num(mem.swap_used),\n    source: \"mactop\",\n    ...(notes.length ? { notes } : {}),\n  };\n}\n\nexport interface VmStat {\n  pageSize: number;\n  wired: number;\n  compressed: number;\n  used: number;\n  free: number;\n}\n\n/** `vm_stat` output → bytes; used = total - reclaimable (free+speculative+inactive). */\nexport function parseVmStat(out: string, memTotal: number | undefined): VmStat | undefined {\n  const pages = (label: string): number | undefined => {\n    const m = new RegExp(`${label}:\\\\s+(\\\\d+).`).exec(out);\n    return m ? Number(m[1]) : undefined;\n  };\n  const size = /page size of (\\d+) bytes/.exec(out);\n  const pageSize = size ? Number(size[1]) : 16_384;\n  const wired = pages(\"Pages wired down\");\n  const compressed = pages(\"Pages occupied by compressor\");\n  const free = pages(\"Pages free\");\n  const speculative = pages(\"Pages speculative\") ?? 0;\n  const inactive = pages(\"Pages inactive\") ?? 0;\n  if (wired === undefined || compressed === undefined || free === undefined) return undefined;\n  const reclaimable = (free + speculative + inactive) * pageSize;\n  const used = memTotal !== undefined ? Math.max(0, memTotal - reclaimable) : undefined;\n  return { pageSize, wired: wired * pageSize, compressed: compressed * pageSize, used: used ?? 0, free: free * pageSize };\n}\n\n/** `sysctl vm.swapusage` → bytes: \"total = 2048.00M  used = 1240.75M  free = ...\". */\nexport function parseSwapUsage(out: string): { total: number; used: number } | undefined {\n  const unit: Record<string, number> = { B: 1, K: 1024, M: 1024 ** 2, G: 1024 ** 3, T: 1024 ** 4 };\n  const grab = (label: string): number | undefined => {\n    const m = new RegExp(`${label} = ([\\\\d.]+)\\\\s*([BKMGTP])`).exec(out);\n    return m ? Number(m[1]) * unit[m[2]!] : undefined;\n  };\n  const total = grab(\"total\");\n  const used = grab(\"used\");\n  return total !== undefined && used !== undefined ? { total, used } : undefined;\n}\n\n/** `/usr/bin/top -l 2` → load in % of the whole SoC; the second sample is the\n *  real interval, the first covers the boot-to-now delta. */\nexport function parseTopCpu(out: string): number | undefined {\n  const line = out.match(/CPU usage:.*/g)?.at(-1);\n  const idle = line && /([\\d.]+)% idle/.exec(line);\n  return idle ? Math.max(0, Math.min(100, 100 - Number(idle[1]))) : undefined;\n}\n\nexport type SystemWindow = CockpitWindow;\n\nexport interface SystemBucket {\n  t: number;\n  n: number;\n  cpu?: number;\n  gpu?: number;\n  ane?: number;\n  socTemp?: number;\n  totalW?: number;\n  systemW?: number;\n  gpuW?: number;\n  cpuW?: number;\n  aneW?: number;\n  memPct?: number;\n}\n\nexport interface SystemSeries {\n  window: SystemWindow;\n  bucketMs: number;\n  series: SystemBucket[];\n  /** watt-hours per window, from the 1-min samples that carry totalW */\n  wh: Record<SystemWindow, number>;\n  /** minutes of samples behind each wh number (coverage, the ring has gaps) */\n  coveredMin: Record<SystemWindow, number>;\n}\n\nconst AVG_KEYS = [\"cpu\", \"gpu\", \"ane\", \"socTemp\", \"totalW\", \"systemW\", \"gpuW\", \"cpuW\", \"aneW\"] as const;\n\n/**\n * Bucket 1-min samples into the cockpit windows' buckets — the same from/bucket\n * math as buildCockpitData, so the power chart lines up with the token chart.\n */\nexport function buildSystemSeries(samples: SystemSample[], win: SystemWindow, now: number): SystemSeries {\n  const { span, bucket } = WINDOWS[win];\n  const from = bucketStart(now - span, bucket);\n  const series: SystemBucket[] = [];\n  for (let t = from; t <= now; t += bucket) series.push({ t, n: 0 });\n  const sums = series.map(() => new Map<string, { s: number; n: number }>());\n\n  const wh: Record<SystemWindow, number> = { \"1h\": 0, \"24h\": 0, \"7d\": 0 };\n  const coveredMin: Record<SystemWindow, number> = { \"1h\": 0, \"24h\": 0, \"7d\": 0 };\n  const spans: Record<SystemWindow, number> = { \"1h\": 3_600_000, \"24h\": DAY, \"7d\": 7 * DAY };\n\n  for (const s of samples) {\n    const b = Math.floor((bucketStart(s.t, bucket) - from) / bucket);\n    if (b >= 0 && b < series.length) {\n      const acc = sums[b]!;\n      series[b]!.n++;\n      for (const k of AVG_KEYS) {\n        const v = s[k];\n        if (typeof v === \"number\") {\n          const a = acc.get(k) ?? { s: 0, n: 0 };\n          a.s += v;\n          a.n++;\n          acc.set(k, a);\n        }\n      }\n      if (s.memUsed && s.memTotal) {\n        const a = acc.get(\"memPct\") ?? { s: 0, n: 0 };\n        a.s += (s.memUsed / s.memTotal) * 100;\n        a.n++;\n        acc.set(\"memPct\", a);\n      }\n    }\n    if (typeof s.totalW === \"number\") {\n      for (const w of Object.keys(spans) as SystemWindow[]) {\n        if (s.t >= now - spans[w]) {\n          wh[w] += s.totalW / 60; // one sample = one minute\n          coveredMin[w]++;\n        }\n      }\n    }\n  }\n\n  series.forEach((b, i) => {\n    const acc = sums[i]!;\n    for (const [k, a] of acc) {\n      const avg = a.n ? a.s / a.n : undefined;\n      if (avg !== undefined) (b as unknown as Record<string, number>)[k] = avg;\n    }\n  });\n  for (const w of Object.keys(wh) as SystemWindow[]) wh[w] = Math.round(wh[w] * 10) / 10;\n\n  return { window: win, bucketMs: bucket, series, wh, coveredMin };\n}\n\nexport interface SystemSnapshot {\n  now: number;\n  window: SystemWindow;\n  bucketMs: number;\n  current: SystemSample | null;\n  /** metric → reason, when no source produced a reading at all */\n  unavailable: Record<string, string>;\n  series: SystemSeries;\n  /** age in seconds of the mactop-sourced values (gpu/temp/power), when fresh */\n  slowAgeS?: number;\n}\n\nexport interface SystemDeps {\n  file?: string;\n  /** minimum distance between direct-source polls (the page asks every 5 s) */\n  ttlMs?: number;\n  /** ring cadence; the mactop probe rides it */\n  ringMs?: number;\n  /** ring retention */\n  keepMs?: number;\n  /** how old a served mactop value may be while the probe retries */\n  staleMs?: number;\n  /** CPU load refresh cadence */\n  topTtlMs?: number;\n  /** injectable command runner (tests) */\n  run?: (cmd: string, args: string[]) => Promise<string>;\n  now?: () => number;\n}\n\nfunction sh(cmd: string, args: string[], timeoutMs = CMD_TIMEOUT): Promise<string> {\n  return new Promise((resolve, reject) =>\n    execFile(cmd, args, { timeout: timeoutMs, maxBuffer: 4 * 1024 * 1024 }, (e, out) =>\n      e ? reject(e) : resolve(String(out))\n    )\n  );\n}\n\n/**\n * Polls the direct sources at most once per ttlMs; the mactop probe runs in\n * the background at the ring cadence and its last good reading is merged in.\n * Keeps the 1-min JSONL ring.\n */\nexport class SystemMonitor {\n  private readonly file: string;\n  private readonly ttlMs: number;\n  private readonly ringMs: number;\n  private readonly keepMs: number;\n  private readonly staleMs: number;\n  private readonly topTtlMs: number;\n  private readonly run: (cmd: string, args: string[]) => Promise<string>;\n  private readonly now: () => number;\n  private cached: SystemSample | null = null;\n  private cachedAt = 0;\n  private lastRingT = 0;\n  private ring: SystemSample[] | null = null;\n  /** last good mactop probe and when it landed */\n  private slow: SystemSample | null = null;\n  private slowAt = 0;\n  private slowErr = \"\";\n  private slowP: Promise<void> | null = null;\n  /** CPU load and when top last reported it */\n  private topCpu: number | undefined;\n  private topAt = 0;\n\n  constructor(deps: SystemDeps = {}) {\n    this.file = deps.file ?? systemRingFile();\n    this.ttlMs = deps.ttlMs ?? 5_000;\n    this.ringMs = deps.ringMs ?? MIN;\n    this.keepMs = deps.keepMs ?? 7 * DAY;\n    this.staleMs = deps.staleMs ?? SLOW_TTL;\n    this.topTtlMs = deps.topTtlMs ?? TOP_TTL;\n    this.run = deps.run ?? sh;\n    this.now = deps.now ?? Date.now;\n    void this.probe(); // warm power/temp/gpu so the first page does not wait ~10 s\n  }\n\n  /**\n   * Latest reading, direct sources first; sample is null only when every\n   * source failed (reason in the sample notes).\n   */\n  async current(): Promise<{ sample: SystemSample | null; why: string }> {\n    const now = this.now();\n    if (this.cached && now - this.cachedAt < this.ttlMs) return { sample: this.cached, why: \"\" };\n    void this.probe(); // power/temp/gpu arrive later; the direct sources answer now\n    const [vmOut, lvlOut, memOut, swOut, topOut] = await Promise.all([\n      this.run(\"vm_stat\", []).catch(() => undefined),\n      this.run(\"sysctl\", [\"-n\", \"kern.memorystatus_level\"]).catch(() => undefined),\n      this.run(\"sysctl\", [\"-n\", \"hw.memsize\"]).catch(() => undefined),\n      this.run(\"sysctl\", [\"vm.swapusage\"]).catch(() => undefined),\n      now - this.topAt >= this.topTtlMs\n        ? this.run(TOP_CMD, [\"-l\", \"2\", \"-n\", \"0\", \"-s\", \"1\"]).catch(() => undefined)\n        : Promise.resolve(undefined),\n    ]);\n    if (topOut !== undefined) {\n      const cpu = parseTopCpu(topOut);\n      if (cpu !== undefined) {\n        this.topCpu = cpu;\n        this.topAt = now;\n      }\n    }\n    const memTotal = num(Number(memOut?.trim()));\n    const vm = vmOut === undefined ? undefined : parseVmStat(vmOut, memTotal);\n    const level = lvlOut === undefined ? undefined : num(Number(lvlOut.trim()));\n    const swap = swOut === undefined ? undefined : parseSwapUsage(swOut);\n    const slowAge = this.slow ? now - this.slowAt : undefined;\n    const slow = this.slow && slowAge !== undefined && slowAge <= this.staleMs ? this.slow : undefined;\n    const notes: string[] = [];\n    if (slow && slowAge! >= MIN) notes.push(`power/temp/gpu cached from the mactop probe ${Math.round(slowAge! / MIN)} min ago`);\n    if (!slow) {\n      notes.push(\n        this.slow\n          ? `power/temp/gpu stale: mactop probe last good ${Math.round((slowAge ?? 0) / MIN)} min ago, retrying`\n          : `power/temp/gpu unavailable: ${this.slowErr || \"mactop probe running\"} (powermetrics would need sudo)`\n      );\n    }\n    const cpuFresh = this.topCpu !== undefined && now - this.topAt <= this.staleMs;\n    const sample: SystemSample = {\n      t: now,\n      ...(cpuFresh ? { cpu: this.topCpu } : {}),\n      ...(memTotal ? { memTotal } : {}),\n      ...(vm?.used ? { memUsed: vm.used } : {}),\n      ...(vm ? { wired: vm.wired, compressed: vm.compressed } : {}),\n      ...(swap ? { swapTotal: swap.total, swapUsed: swap.used } : {}),\n      ...(level !== undefined ? { pressureLevel: level } : {}),\n      // probe-exclusive fields only: memory and CPU load stay with the live sources\n      ...(slow\n        ? {\n            gpu: slow.gpu,\n            ane: slow.ane,\n            gpuFreqMhz: slow.gpuFreqMhz,\n            socTemp: slow.socTemp,\n            cpuW: slow.cpuW,\n            gpuW: slow.gpuW,\n            aneW: slow.aneW,\n            systemW: slow.systemW,\n            totalW: slow.totalW,\n          }\n        : {}),\n      source: \"direct\",\n      ...(notes.length ? { notes } : {}),\n    };\n    this.cached = sample;\n    this.cachedAt = now;\n    if (now - this.lastRingT >= this.ringMs) {\n      this.lastRingT = now;\n      this.appendRing(sample);\n    }\n    return { sample, why: \"\" };\n  }\n\n  /** Runs the mactop probe if none is in flight and the ring cadence allows it. */\n  probe(): Promise<void> {\n    if (this.slowP) return this.slowP;\n    const now = this.now();\n    if (this.slow && now - this.slowAt < this.ringMs) return Promise.resolve();\n    const p = this.mactop()\n      .then((s) => {\n        this.slow = s;\n        this.slowAt = now;\n        this.slowErr = \"\";\n      })\n      .catch((e: Error) => {\n        this.slowErr = e.message.split(\"\\n\")[0] ?? \"mactop probe failed\";\n      })\n      .finally(() => {\n        this.slowP = null;\n      });\n    this.slowP = p;\n    return p;\n  }\n\n  private async mactop(): Promise<SystemSample> {\n    let last: Error | undefined;\n    for (const bin of MACTOP_CANDIDATES) {\n      try {\n        return parseMactopSample(\n          await this.run(bin, [\"--headless\", \"--count\", String(MACTOP_COUNT), \"--format\", \"json\"]),\n          this.now()\n        );\n      } catch (e) {\n        last = e as Error;\n      }\n    }\n    throw last ?? new Error(\"mactop not found\");\n  }\n\n  private appendRing(s: SystemSample): void {\n    try {\n      if (!this.ring) this.ring = this.readRing();\n      this.ring.push(s);\n      mkdirSync(dirname(this.file), { recursive: true });\n      const keep = Math.ceil(this.keepMs / this.ringMs) + 240; // rewrite only past a day of slack\n      const cutoff = this.now() - this.keepMs;\n      if (this.ring.length > keep || this.ring[0]!.t < cutoff) {\n        this.ring = this.ring.filter((x) => x.t >= cutoff);\n        writeFileSync(this.file, this.ring.map((x) => JSON.stringify(x)).join(\"\\n\") + \"\\n\");\n      } else {\n        appendFileSync(this.file, JSON.stringify(s) + \"\\n\");\n      }\n    } catch {\n      /* the ring is best-effort; the panel works without history */\n    }\n  }\n\n  private readRing(): SystemSample[] {\n    try {\n      if (!existsSync(this.file)) return [];\n      const out: SystemSample[] = [];\n      for (const line of readFileSync(this.file, \"utf8\").split(\"\\n\")) {\n        if (!line.trim()) continue;\n        try {\n          const s = JSON.parse(line) as SystemSample;\n          if (typeof s.t === \"number\") out.push(s); // stale head is trimmed on the next append\n        } catch {\n          /* torn tail line from a crash: skip */\n        }\n      }\n      this.lastRingT = out.at(-1)?.t ?? 0;\n      return out;\n    } catch {\n      return [];\n    }\n  }\n\n  private samples(): SystemSample[] {\n    if (!this.ring) this.ring = this.readRing();\n    return this.ring;\n  }\n\n  async snapshot(win: SystemWindow): Promise<SystemSnapshot> {\n    const now = this.now();\n    const { sample, why } = await this.current();\n    const unavailable: Record<string, string> = {};\n    const hasMetric = sample && Object.entries(sample).some(([k, v]) => k !== \"t\" && typeof v === \"number\");\n    if (!hasMetric) unavailable.all = why || sample?.notes?.join(\"; \") || \"no system source available\";\n    const slowAge = this.slow ? now - this.slowAt : undefined;\n    return {\n      now,\n      window: win,\n      bucketMs: WINDOWS[win].bucket,\n      current: hasMetric ? sample : null,\n      unavailable,\n      series: buildSystemSeries(this.samples(), win, now),\n      ...(slowAge !== undefined && slowAge <= this.staleMs ? { slowAgeS: Math.round(slowAge / 1000) } : {}),\n    };\n  }\n}\n","export interface RestartDeps {\n  oldPid: number | undefined;                 // running router, if any\n  spawnNew: () => number | undefined;         // starts the new router (it waits for the port itself), returns its pid\n  signalOld: (pid: number) => void;           // SIGTERM to the old router\n  isUp: () => Promise<boolean>;               // true when GET /cockpit/system answers on the router port\n  now?: () => number;                         // default Date.now\n  pollMs?: number;                            // default 50\n  timeoutMs?: number;                         // default 30000\n  sleep?: (ms: number) => Promise<void>;      // default setTimeout based\n}\n\nexport interface RestartResult {\n  newPid: number | undefined;\n  oldPid: number | undefined;\n  gapMs: number | undefined;\n  readyMs: number\n}\n\nexport async function restartRouter(d: RestartDeps): Promise<RestartResult> {\n  const now = d.now || Date.now;\n  const pollMs = d.pollMs || 50;\n  const timeoutMs = d.timeoutMs || 30000;\n  const sleep = d.sleep || ((ms: number) => new Promise(resolve => setTimeout(resolve, ms)));\n\n  // Start the new router first\n  const newPid = d.spawnNew();\n\n  let oldPid = d.oldPid;\n  let signalTime: number | undefined = undefined;\n  let firstFailureTime: number | undefined = undefined;\n  let firstSuccessTime: number | undefined = undefined;\n  let consecutiveFailures = 0;\n  let maxGapMs = 0;\n\n  // If there's an old router, signal it to shut down\n  if (oldPid !== undefined) {\n    signalTime = now();\n    d.signalOld(oldPid);\n  }\n\n  const startTime = now();\n\n  // Poll until the new router is ready\n  while (true) {\n    const isUp = await d.isUp();\n\n    if (isUp) {\n      // First success after signal\n      if (firstSuccessTime === undefined) {\n        firstSuccessTime = now();\n        // Calculate gapMs as the longest run of consecutive failed polls\n        if (firstFailureTime !== undefined) {\n          maxGapMs = firstSuccessTime - firstFailureTime;\n        }\n      }\n      break;\n    } else {\n      // First failure after signal\n      if (firstFailureTime === undefined && signalTime !== undefined) {\n        firstFailureTime = now();\n      }\n\n      // Track consecutive failures to calculate gap\n      if (firstFailureTime !== undefined) {\n        consecutiveFailures++;\n        const currentGap = now() - firstFailureTime;\n        if (currentGap > maxGapMs) {\n          maxGapMs = currentGap;\n        }\n      }\n    }\n\n    // Check timeout\n    if (now() - startTime > timeoutMs) {\n      throw new Error(`new router did not answer within ${timeoutMs}ms`);\n    }\n\n    await sleep(pollMs);\n  }\n\n  // Calculate readyMs as time from signal (or spawn when no old) to first success\n  const readyMs = firstSuccessTime !== undefined ? firstSuccessTime - (signalTime || startTime) : 0;\n\n  // gapMs is undefined when oldPid was undefined, 0 if no failures, otherwise the gap\n  const gapMs = oldPid === undefined ? undefined :\n                firstFailureTime === undefined ? 0 :\n                maxGapMs;\n\n  return {\n    newPid,\n    oldPid,\n    gapMs,\n    readyMs\n  };\n}","/**\n * `pai router start|stop|status|run|stats` — local model router on port 8790.\n * See src/router/router.ts. Names, flags and stdout match the old CLI.\n */\n\nimport http from \"node:http\";\nimport { mkdirSync, unlinkSync, writeFileSync, readFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { routerHandler, ROUTER_PORT } from \"../../router/router.js\";\nimport { defaultV1Guard, displayHost, LOOPBACK, resolveBindAddress, withCockpitToken, type CockpitHandler } from \"../../router/cockpit-auth.js\";\nimport { cachedRows, cachedSessionMap, cachedWorkerWindows, handleCockpit } from \"../../router/cockpit.js\";\nimport { LimitsCache } from \"../../router/limits.js\";\nimport { listenRetry } from \"../../router/listen.js\";\nimport { drainServers } from \"../../router/drain.js\";\nimport { SystemMonitor } from \"../../router/system.js\";\nimport { readWorkersSection } from \"../../workers/config.js\";\nimport { aggregate, aggregateBySession, appendUsage, formatSessionStats, formatStats, parseSince, readUsage, type UsageRow } from \"../../router/usage.js\";\nimport { loadSessionMap, loadWorkerWindows, sessionDisplayName, withWorkerAttribution } from \"../../router/sessions.js\";\nimport { routerLogFile, routerPidFile, routerRunningPid, spawnRouter, probeRouter } from \"../../router/supervisor.js\";\nimport { loadConfig } from \"../../daemon/config.js\";\nimport { CliError, type CommandDef } from \"../define.js\";\nimport { dim, ok } from \"../lib/utils.js\";\nimport { restartRouter } from \"../../router/restart.js\";\n\nconst out = (s: string) => void process.stdout.write(s + \"\\n\");\n\nconst startCommand: CommandDef = {\n  name: \"start\",\n  summary: \"Start the router in the background\",\n  description: \"Starts the local model router (127.0.0.1:8790). If it is already running, says so and does nothing.\",\n  examples: [\"pai router start\", \"pai router status\"],\n  seeAlso: [\"pai router stop\", \"pai router status\"],\n  run() {\n    const pid = routerRunningPid();\n    if (pid) return void out(ok(`router already running (pid ${pid})`));\n    const started = spawnRouter(process.argv[1]!);\n    out(ok(`router starting on 127.0.0.1:${ROUTER_PORT} (pid ${started}), log ${routerLogFile()}`));\n  },\n};\n\nconst stopCommand: CommandDef = {\n  name: \"stop\",\n  summary: \"Stop the router\",\n  description: \"Stops the background router. If it is not running, says so.\",\n  examples: [\"pai router stop\", \"pai router status\"],\n  seeAlso: [\"pai router start\"],\n  run() {\n    const pid = routerRunningPid();\n    if (!pid) return void out(dim(\"router not running\"));\n    process.kill(pid, \"SIGTERM\");\n    try {\n      unlinkSync(routerPidFile());\n    } catch {\n      /* already gone */\n    }\n    out(ok(`router stopped (pid ${pid})`));\n  },\n};\n\nconst statusCommand: CommandDef = {\n  name: \"status\",\n  summary: \"Show whether the router is running\",\n  description: \"Says whether the router runs and whether the daemon restarts it when it is down.\",\n  examples: [\"pai router status\", \"pai router start\"],\n  seeAlso: [\"pai router start\", \"pai router stop\"],\n  run() {\n    const pid = routerRunningPid();\n    const managed = loadConfig().router.enabled;\n    const note = managed ? \"daemon-managed: restarted if down\" : \"not daemon-managed (router.enabled is false)\";\n    out(pid ? ok(`router running (pid ${pid}, port ${ROUTER_PORT}); ${note}`) : dim(`router not running; ${note}`));\n  },\n};\n\nconst runCommand: CommandDef = {\n  name: \"run\",\n  summary: \"Run the router in the foreground\",\n  description:\n    \"Runs the router in this terminal until you press Ctrl-C. `pai router start` uses this to run it in the background.\\n\" +\n    \"A port other than the default never touches the pid file.\",\n  options: [\n    { name: \"port\", type: \"string\", value: \"PORT\", default: String(ROUTER_PORT), summary: \"Port to listen on\" },\n    { name: \"usage-file\", type: \"string\", value: \"PATH\", summary: \"Read the cockpit's accounting from this file and record nothing (second instances)\" },\n    { name: \"wait-bind\", type: \"string\", value: \"SECONDS\", summary: \"Retry binding the port for up to this many seconds while another router still holds it (used by `pai router restart`)\" },\n  ],\n  examples: [\"pai router run\", \"pai router run --port 8791 --usage-file ./usage.jsonl\"],\n  seeAlso: [\"pai router start\"],\n  run(ctx) {\n    const port = Number(ctx.opts.port);\n    const usageFile = ctx.opts[\"usage-file\"] as string | undefined;\n    const managed = port === ROUTER_PORT;\n    // router.bind: \"loopback\" (default) | \"tailscale\" | an explicit IPv4; 0.0.0.0 is never accepted.\n    const bindName = loadConfig().router.bind ?? \"loopback\";\n    const addr = resolveBindAddress(bindName);\n    if (!addr) {\n      throw new CliError(`cannot resolve router.bind \"${bindName}\" — use \"loopback\", \"tailscale\", or an IPv4 (never 0.0.0.0)`, \"pai daemon status\");\n    }\n    const remote = addr !== LOOPBACK;\n    const token = undefined as string | undefined; // no auth ceremony on the tailnet\n    const rows = cachedRows(usageFile);\n    const limits = new LimitsCache({ providers: () => readWorkersSection().workers.providers, rows });\n    const system = new SystemMonitor();\n    const serve: CockpitHandler = (req, res) => handleCockpit(req, res, limits, rows, cachedSessionMap(), system, cachedWorkerWindows());\n    const cockpit = token ? withCockpitToken(token, serve) : serve;\n    const append = usageFile ? () => undefined : (row: UsageRow) => appendUsage(row);\n    const handler = routerHandler(undefined, undefined, undefined, append, cockpit, { ...(remote ? { remoteAllowed: defaultV1Guard } : {}), isUp: limits.isUp });\n    return new Promise<number>(() => {\n      const server = http.createServer(handler);\n      const tail = remote ? http.createServer(handler) : undefined;\n      const waitBindMs = Math.min(10, Number(ctx.opts[\"wait-bind\"] ?? 0) || 0) * 1000;\n      listenRetry(server, port, \"127.0.0.1\", { waitMs: waitBindMs }).then(() => {\n        if (!managed) return;\n        mkdirSync(dirname(routerPidFile()), { recursive: true });\n        writeFileSync(routerPidFile(), String(process.pid));\n      }, (e: unknown) => {\n        process.stderr.write(`pai: router: ${(e as Error).message}\\n`);\n        process.exit(1);\n      });\n      if (tail) {\n        listenRetry(tail, port, addr, { waitMs: waitBindMs }).then(async () => {\n          out(ok(`cockpit beyond loopback: http://${await displayHost(addr)}:${port}/cockpit`));\n        }, (e: unknown) => {\n          process.stderr.write(`pai: router ${addr}: ${(e as Error).message}\\n`);\n          process.exit(1);\n        });\n      }\n      const bye = () => {\n        void drainServers(tail ? [server, tail] : [server], { timeoutMs: 120000 }).then(() => {\n          if (managed) {\n            try {\n              if (readFileSync(routerPidFile(), \"utf8\").trim() === String(process.pid)) unlinkSync(routerPidFile());\n            } catch {\n              /* ignore */\n            }\n          }\n          process.exit(0);\n        });\n      };\n      process.on(\"SIGTERM\", bye);\n      process.on(\"SIGINT\", bye);\n    });\n  },\n};\n\nconst restartCommand: CommandDef = {\n  name: \"restart\",\n  summary: \"Restart the router without refusing connections\",\n  description: \"Starts a new router that waits for the port, then stops the old one, which finishes its in-flight streams first. Prints the longest refused interval.\",\n  examples: [\"pai router restart\", \"pai router status\"],\n  seeAlso: [\"pai router start\", \"pai router stop\"],\n  async run() {\n    const result = await restartRouter({\n      oldPid: routerRunningPid() ?? undefined,\n      spawnNew: () => spawnRouter(process.argv[1]!, [\"--wait-bind\", \"10\"]),\n      signalOld: (pid) => process.kill(pid, \"SIGTERM\"),\n      isUp: () => probeRouter(ROUTER_PORT, 500)\n    });\n    out(ok(`router restarted (new pid ${result.newPid}, old pid ${result.oldPid ?? \"none\"}, refused for ${result.gapMs ?? 0} ms)`));\n  },\n};\n\nconst statsCommand: CommandDef = {\n  name: \"stats\",\n  summary: \"Token and cache totals per provider and model\",\n  description: \"Reads the router's usage log and prints totals, grouped by provider (default) or by session.\",\n  options: [\n    { name: \"since\", type: \"string\", value: \"SPAN\", summary: \"Only the last span, e.g. 30m, 1h, 2d\" },\n    { name: \"by\", type: \"string\", value: \"KEY\", default: \"provider\", summary: \"Group by provider or session\" },\n  ],\n  examples: [\"pai router stats\", \"pai router stats --since 2d --by session\"],\n  seeAlso: [\"pai router status\"],\n  run(ctx) {\n    let since: number | undefined;\n    try {\n      since = ctx.opts.since ? parseSince(String(ctx.opts.since)) : undefined;\n    } catch (e) {\n      throw new CliError((e as Error).message, \"pai router stats --since 1h\");\n    }\n    if (ctx.opts.by === \"session\") {\n      const rows = readUsage();\n      const map = withWorkerAttribution(loadSessionMap(), rows, loadWorkerWindows());\n      out(formatSessionStats(aggregateBySession(rows, since, Date.now(), (id) => sessionDisplayName(id, map))));\n    } else out(formatStats(aggregate(readUsage(), since)));\n  },\n};\n\nexport const routerCommand: CommandDef = {\n  name: \"router\",\n  summary: \"Local model router: start, stop, restart, status, run, stats\",\n  description: `A local router on 127.0.0.1:${ROUTER_PORT}: set ANTHROPIC_BASE_URL to it and /model picks the provider.`,\n  examples: [\"pai router status\", \"pai router stats --since 1d\"],\n  seeAlso: [\"pai worker policy\", \"pai providers\"],\n  subcommands: [startCommand, stopCommand, restartCommand, statusCommand, runCommand, statsCommand],\n};\n","/** `pai setups` and `pai setups save NAME`, plus `pai more`. */\n\nimport { readWorkersSection, ANTHROPIC_NATIVE, type WorkersConfig } from \"../../workers/config.js\";\nimport { CliError, type CommandDef } from \"../define.js\";\nimport { colorEnabled, makeStyle, renderMore, renderTable } from \"../help.js\";\nimport { readBudget } from \"../lib/health.js\";\nimport { allSetups, loadSaved, saveSetup, setupsPath } from \"../lib/setups.js\";\nimport { GROUPS } from \"../registry.js\";\nimport { lastSetup, setupsTable } from \"./root.js\";\nimport { collectView } from \"./session.js\";\n\nfunction load(): WorkersConfig {\n  try {\n    return readWorkersSection().workers;\n  } catch (e) {\n    throw new CliError(`cannot read the provider configuration: ${e instanceof Error ? e.message : String(e)}`, \"pai providers\");\n  }\n}\n\nconst saveCommand: CommandDef = {\n  name: \"save\",\n  summary: \"Save the current combination under a name\",\n  description:\n    \"Saves how your most recent session ran (its orchestrator) together with the provider workers run on now. \" +\n    \"Name them explicitly with --orchestrator and --workers.\",\n  args: [{ name: \"NAME\", required: true, summary: \"Name for the setup, e.g. \\\"opus + glm\\\"\" }],\n  options: [\n    { name: \"orchestrator\", type: \"string\", value: \"PROVIDER\", summary: \"Provider that runs the main conversation (default: your latest session's)\" },\n    { name: \"workers\", type: \"string\", value: \"PROVIDER\", summary: \"Provider the worker classes run on (default: the active provider)\" },\n  ],\n  examples: ['pai setups save \"my mix\"', 'pai setups save \"kimi + glm\" --orchestrator kimi --workers glm'],\n  seeAlso: [\"pai setups\"],\n  async run(ctx) {\n    const workers = load();\n    const { rows } = await collectView({});\n    const last = lastSetup(rows[0] ?? null, workers);\n    const orchestrator = (ctx.opts.orchestrator as string | undefined) ?? last.orchestrator.provider;\n    const wk = (ctx.opts.workers as string | undefined) ?? last.workers;\n    const known = [ANTHROPIC_NATIVE, ...Object.keys(workers.providers)];\n    for (const p of [orchestrator, wk]) {\n      if (!known.includes(p)) throw new CliError(`provider \"${p}\" is not configured`, \"pai providers\");\n    }\n    const name = (ctx.args.NAME as string).trim();\n    if (ctx.dryRun) return void process.stdout.write(`would save \"${name}\": orchestrator ${orchestrator}, workers ${wk} → ${setupsPath()}\\n`);\n    saveSetup({ name, orchestrator: { provider: orchestrator, model: ctx.opts.orchestrator ? undefined : last.orchestrator.model }, workers: wk });\n    process.stdout.write(`Saved setup \"${name}\": orchestrator ${orchestrator}, workers ${wk}\\nUse it: pai <name> --with \"${name}\"\\n`);\n  },\n};\n\nexport const setupsCommand: CommandDef = {\n  name: \"setups\",\n  summary: \"Orchestrator + worker combinations (save your own)\",\n  description:\n    \"A setup says which model runs the main conversation and which provider the workers run on. \" +\n    \"\\\"Last time\\\" is how your latest session ran; the rest are built from your configured providers and anything you saved.\",\n  daily: true,\n  subcommands: [saveCommand],\n  examples: [\"pai setups\", 'pai setups save \"my mix\"', 'pai webseiten --with \"anthropic + glm workers\"'],\n  seeAlso: [\"pai\", \"pai providers\"],\n  async run() {\n    const st = makeStyle(colorEnabled());\n    const workers = load();\n    const [{ rows }, budget] = await Promise.all([collectView({}), readBudget(workers)]);\n    const setups = allSetups(workers, lastSetup(rows[0] ?? null, workers), loadSaved());\n    const o = setupsTable(setups, workers, budget, st);\n    process.stdout.write(renderTable(o.headers, setups.map(o.cells), st) + \"\\n\\n\");\n    process.stdout.write(st.dim('Use one: pai <session> --with \"<setup>\"   ·   keep the current mix: pai setups save \"<name>\"') + \"\\n\");\n  },\n};\n\nexport const moreCommand: CommandDef = {\n  name: \"more\",\n  summary: \"Every other command, by group\",\n  description: \"Lists the commands that are not on the first screen: scripting forms, providers, workers, projects, daemon and setup.\",\n  examples: [\"pai more\", \"pai help worker\"],\n  seeAlso: [\"pai help\"],\n  daily: false,\n  run() {\n    process.stdout.write(renderMore(GROUPS, makeStyle(colorEnabled())) + \"\\n\");\n  },\n};\n","/**\n * transcript-scan.ts — read a Claude Code transcript (.jsonl) without paying\n * for the whole file on every call.\n *\n * Transcripts only grow by appending, and the hooks that read them run every\n * few minutes for the life of a session. Two primitives keep that cost\n * proportional to what is new (or to what is asked for):\n *\n *   scanIncremental — folds lines into a running state, remembering the byte\n *     offset it stopped at, so the next call parses only the appended lines.\n *   readTailLines   — the last lines of the file, reading backwards from the\n *     end, for callers that only care about the most recent entries.\n *\n * Both fail soft: unreadable file → empty/initial result, never a throw.\n */\n\nimport { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, renameSync, statSync, writeFileSync } from \"node:fs\";\nimport { createHash } from \"node:crypto\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/** Bytes read per syscall while scanning forward. Bounds peak memory. */\nconst CHUNK_BYTES = 4 * 1024 * 1024;\n/** Leading bytes used to tell \"same file, grown\" from \"different file\". */\nconst FINGERPRINT_BYTES = 256;\n\ninterface ScanCache<S> {\n  version: 1;\n  /** Byte offset just past the last complete line folded into `state`. */\n  offset: number;\n  head: string;\n  ino: number;\n  state: S;\n}\n\nexport interface ScanStats {\n  /** Bytes actually read from the transcript this call. */\n  bytesRead: number;\n  /** Lines handed to the folder this call. */\n  linesParsed: number;\n  /** True when the cache was unusable (absent, shrunk, replaced) and the file was read from byte 0. */\n  full: boolean;\n}\n\nexport interface ScanOptions {\n  /** Directory for the cache file. Defaults to the OS temp dir. */\n  cacheDir?: string;\n  stats?: ScanStats;\n}\n\nfunction cachePath(path: string, kind: string, dir: string): string {\n  const key = createHash(\"sha1\").update(`${kind}\\0${path}`).digest(\"hex\").slice(0, 16);\n  return join(dir, `pai-transcript-scan-${kind}-${key}.json`);\n}\n\nfunction readBytes(fd: number, start: number, length: number): Buffer {\n  const buf = Buffer.alloc(length);\n  let got = 0;\n  while (got < length) {\n    const n = readSync(fd, buf, got, length - got, start + got);\n    if (n <= 0) break;\n    got += n;\n  }\n  return got === length ? buf : buf.subarray(0, got);\n}\n\n/**\n * Fold every complete line of `path` into a state, reading only what was\n * appended since the previous call with the same `kind`.\n *\n * `fold` must mutate/return a JSON-serialisable state; the state is persisted\n * beside the offset. A trailing line without a newline is left for the next\n * call, so a half-written entry is never parsed. If the file shrank, was\n * replaced, or the cache is unreadable, the scan restarts from byte 0 with a\n * fresh `init()`.\n */\nexport function scanIncremental<S>(\n  path: string,\n  kind: string,\n  init: () => S,\n  fold: (state: S, line: string) => S,\n  opts: ScanOptions = {}\n): S {\n  const stats = opts.stats;\n  let fd: number;\n  try {\n    fd = openSync(path, \"r\");\n  } catch {\n    return init();\n  }\n\n  try {\n    const st = statSync(path);\n    const size = st.size;\n    const ino = st.ino;\n    const headLen = Math.min(FINGERPRINT_BYTES, size);\n    const head = readBytes(fd, 0, headLen).toString(\"latin1\");\n\n    const dir = opts.cacheDir ?? tmpdir();\n    const cfile = cachePath(path, kind, dir);\n\n    let state: S | null = null;\n    let offset = 0;\n    try {\n      if (existsSync(cfile)) {\n        const c = JSON.parse(readFileSync(cfile, \"utf-8\")) as ScanCache<S>;\n        if (\n          c.version === 1 &&\n          c.offset <= size &&\n          c.ino === ino &&\n          head.startsWith(c.head)\n        ) {\n          state = c.state;\n          offset = c.offset;\n        }\n      }\n    } catch {\n      state = null;\n    }\n    const full = state === null;\n    if (state === null) {\n      state = init();\n      offset = 0;\n    }\n    if (stats) stats.full = full;\n\n    let pos = offset;\n    let carry = Buffer.alloc(0);\n    while (pos < size) {\n      const chunk = readBytes(fd, pos, Math.min(CHUNK_BYTES, size - pos));\n      if (chunk.length === 0) break;\n      if (stats) stats.bytesRead += chunk.length;\n      pos += chunk.length;\n      const buf = carry.length ? Buffer.concat([carry, chunk]) : chunk;\n      let start = 0;\n      for (;;) {\n        const nl = buf.indexOf(0x0a, start);\n        if (nl < 0) break;\n        const line = buf.toString(\"utf8\", start, nl);\n        start = nl + 1;\n        if (line.trim()) {\n          if (stats) stats.linesParsed++;\n          state = fold(state, line);\n        }\n      }\n      carry = Buffer.from(buf.subarray(start));\n    }\n    // Everything except the unterminated tail has been folded.\n    const consumed = pos - carry.length;\n\n    try {\n      const out: ScanCache<S> = {\n        version: 1,\n        offset: consumed,\n        head: head.slice(0, Math.min(headLen, consumed)),\n        ino,\n        state,\n      };\n      mkdirSync(dir, { recursive: true });\n      const tmp = `${cfile}.${process.pid}.tmp`;\n      writeFileSync(tmp, JSON.stringify(out), \"utf-8\");\n      renameSync(tmp, cfile);\n    } catch {\n      // Best-effort — a missing cache only costs a full rescan next time.\n    }\n    return state;\n  } catch {\n    return init();\n  } finally {\n    try {\n      closeSync(fd);\n    } catch {\n      // ignore\n    }\n  }\n}\n\n/**\n * The last complete lines of a file, oldest first, reading backwards from the\n * end in growing windows until `minLines` lines are available (or the whole\n * file has been read). `wantMore` lets a caller that is searching keep\n * extending the window: it receives the lines read so far and returns true to\n * read further back.\n */\nexport function readTailLines(\n  path: string,\n  minLines: number,\n  wantMore: (lines: string[]) => boolean = () => false\n): string[] {\n  let fd: number;\n  try {\n    fd = openSync(path, \"r\");\n  } catch {\n    return [];\n  }\n  try {\n    const size = statSync(path).size;\n    let window = 256 * 1024;\n    for (;;) {\n      const start = Math.max(0, size - window);\n      const buf = readBytes(fd, start, size - start);\n      let text = buf.toString(\"utf8\");\n      // The first line of a mid-file window is probably cut — drop it.\n      if (start > 0) {\n        const nl = text.indexOf(\"\\n\");\n        text = nl < 0 ? \"\" : text.slice(nl + 1);\n      }\n      const lines = text.split(\"\\n\").filter((l) => l.trim());\n      const more = lines.length < minLines || wantMore(lines);\n      if (start === 0 || !more) return lines;\n      window *= 4;\n    }\n  } catch {\n    return [];\n  } finally {\n    try {\n      closeSync(fd);\n    } catch {\n      // ignore\n    }\n  }\n}\n","/**\n * Rolling auto-checkpoint — the floor under every session.\n *\n * WHY THIS EXISTS\n * ---------------\n * A model-authored checkpoint is the best handover there is: it knows what is\n * unfinished, what is load-bearing, and what to do next. It has one fatal\n * property — it only exists if the model was asked to write one.\n *\n * The model is not invoked on `/exit`, and certainly not on Ctrl+C. No hook can\n * make it write prose at exit time. So a good checkpoint at exit can only exist\n * if something wrote it *before* exit. Exit-time is the wrong moment by\n * construction, and building the guarantee there is why sessions kept ending\n * with nothing to hand over.\n *\n * This module builds a mechanical digest instead — recent prompts, the state of\n * the working tree — cheap enough to run on a timer during the session. It is\n * strictly worse than what the model would write, and that is the point: it is\n * the floor, not the ceiling. `applyContinue` writes it in \"auto\" mode, so it\n * can never overwrite a model-authored checkpoint for the same session.\n */\n\nimport { existsSync, readdirSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { execFileSync } from \"node:child_process\";\nimport { encodeDir } from \"../cli/lib/utils.js\";\nimport { scanIncremental, type ScanOptions } from \"../hooks/ts/lib/transcript-scan.js\";\n\n/** How many recent user prompts to carry. */\nconst MAX_PROMPTS = 6;\n/** Longest a single quoted prompt may be before it is elided. */\nconst MAX_PROMPT_CHARS = 220;\n/** Newest prompts kept per transcript — headroom over MAX_PROMPTS for the duplicate collapse. */\nconst KEEP_PROMPTS = 64;\n\ninterface PromptScan {\n  entries: string[];\n  messages: string[];\n}\n\n/** Cap on listed working-tree entries, so a big refactor cannot flood TODO.md. */\nconst MAX_FILES = 20;\n\n// ---------------------------------------------------------------------------\n// Transcript\n// ---------------------------------------------------------------------------\n\n/**\n * Text of a JSONL message, or null when the entry carries none.\n * Mirrors the shape handled by slug-generator: content is either a plain\n * string (user turns) or an array of typed blocks (assistant turns).\n */\nfunction messageText(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  if (typeof content === \"string\") return content || null;\n\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\") texts.push(b.text);\n    }\n    return texts.join(\" \") || null;\n  }\n\n  return null;\n}\n\n/**\n * Strip injected machinery from a user turn, leaving what the user typed.\n *\n * Hooks do not get their own turn — `UserPromptSubmit` output is folded into\n * the same user message as the prompt itself. So this cannot be a predicate\n * that accepts or rejects whole messages: doing that discarded every prompt in\n * a session where a reminder happened to sort first, which is every session\n * here. The markup has to be cut out and the remainder kept.\n */\nfunction stripInjected(text: string): string {\n  return text\n    .replace(/<system-reminder>[\\s\\S]*?<\\/system-reminder>/g, \"\")\n    .replace(/<local-command-[a-z]*>[\\s\\S]*?<\\/local-command-[a-z]*>/g, \"\")\n    .replace(/<command-(name|message|args)>[\\s\\S]*?<\\/command-\\1>/g, \"\")\n    .replace(/<persisted-output>[\\s\\S]*?<\\/persisted-output>/g, \"\")\n    .trim();\n}\n\n/**\n * True for a turn that is entirely machinery — no user intent to recover.\n */\nfunction isMachinery(text: string): boolean {\n  const t = text.trimStart();\n  return (\n    t.startsWith(\"Caveat:\") ||\n    t.startsWith(\"[Request interrupted\") ||\n    t.startsWith(\"This session is being continued from a previous\")\n  );\n}\n\n/**\n * Every transcript file belonging to a session, oldest part first.\n *\n * A session's transcript is not one file. `load-project-context` archives all\n * but the newest `.jsonl` into `sessions/`, after which Claude Code opens a\n * fresh file under the same UUID — so the same session routinely has a large\n * archived half and a small live one. Reading only the newest file yields just\n * the current turn, which is how the first cut of this digest reported a\n * six-turn session as a single prompt.\n *\n * With no session id, falls back to the newest file in the project directory.\n */\n/**\n * Locate the Claude Code transcript directory for a project.\n *\n * The registry records an `encoded_dir`, but it records whatever was true when\n * the project was added and is not updated when the project moves. Audited on\n * 2026-08-01: of 114 active projects only 19 had an `encoded_dir` containing\n * any transcript at all — 20 named a directory that no longer exists and 75\n * named one holding no `.jsonl` files. AIBroker's still pointed at a path last\n * used in March, which silently disabled checkpoint capture for that project:\n * every lookup returned no transcript, so every digest came out empty and every\n * handover degraded to \"see the latest session note\".\n *\n * So the stored value is treated as a hint, not an answer. If it yields no\n * transcripts, the directory is re-derived from the project's current root\n * path — which is maintained — and that is used instead. This self-heals a\n * moved project on the next capture without a migration.\n */\nexport function resolveTranscriptDir(project: {\n  encoded_dir?: string | null;\n  root_path: string;\n}): string {\n  const base = join(homedir(), \".claude\", \"projects\");\n\n  const recorded = project.encoded_dir ? join(base, project.encoded_dir) : null;\n  if (recorded && hasTranscripts(recorded)) return recorded;\n\n  const derived = join(base, encodeDir(project.root_path));\n  if (hasTranscripts(derived)) return derived;\n\n  // Neither has anything. Return the recorded one so behaviour and any error\n  // message still reference what the registry actually claims.\n  return recorded ?? derived;\n}\n\nfunction hasTranscripts(dir: string): boolean {\n  if (!existsSync(dir)) return false;\n  try {\n    if (readdirSync(dir).some((e) => e.endsWith(\".jsonl\"))) return true;\n    const archived = join(dir, \"sessions\");\n    return existsSync(archived) && readdirSync(archived).some((e) => e.endsWith(\".jsonl\"));\n  } catch {\n    return false;\n  }\n}\n\nexport function findTranscripts(\n  projectDir: string,\n  sessionId?: string\n): string[] {\n  if (!existsSync(projectDir)) return [];\n\n  if (sessionId) {\n    const archived = join(projectDir, \"sessions\", `${sessionId}.jsonl`);\n    const live = join(projectDir, `${sessionId}.jsonl`);\n    const parts = [archived, live].filter((p) => existsSync(p));\n    if (parts.length > 0) return parts;\n  }\n\n  let newest: { path: string; mtime: number } | null = null;\n  let entries: string[];\n  try {\n    entries = readdirSync(projectDir);\n  } catch {\n    return [];\n  }\n\n  for (const entry of entries) {\n    if (!entry.endsWith(\".jsonl\")) continue;\n    const full = join(projectDir, entry);\n    try {\n      const { mtimeMs } = statSync(full);\n      if (!newest || mtimeMs > newest.mtime) newest = { path: full, mtime: mtimeMs };\n    } catch {\n      // Unreadable — skip.\n    }\n  }\n\n  return newest ? [newest.path] : [];\n}\n\n/**\n * The last few things the user actually asked for, oldest first.\n *\n * Claude Code writes a dedicated `last-prompt` entry per turn, holding the\n * prompt already separated from the hook output that shares its user message.\n * That is used when present; parsing user messages is the fallback for\n * transcripts written before those entries existed.\n */\nexport function recentPrompts(\n  transcriptPaths: string[],\n  limit = MAX_PROMPTS,\n  scanOpts: ScanOptions = {}\n): string[] {\n  // Per transcript, only the lines appended since the last call are parsed;\n  // the running state is just the newest prompts, which is all the digest\n  // needs (see scanIncremental).\n  const fromEntries: string[] = [];\n  const fromMessages: string[] = [];\n\n  const take = (text: string, into: string[]): void => {\n    const cleaned = stripInjected(text);\n    if (!cleaned || isMachinery(cleaned)) return;\n    into.push(\n      cleaned.length > MAX_PROMPT_CHARS\n        ? cleaned.slice(0, MAX_PROMPT_CHARS).trimEnd() + \" …\"\n        : cleaned\n    );\n    if (into.length > KEEP_PROMPTS * 2) into.splice(0, into.length - KEEP_PROMPTS);\n  };\n\n  for (const path of transcriptPaths) {\n    const state = scanIncremental<PromptScan>(\n      path,\n      \"prompts\",\n      () => ({ entries: [], messages: [] }),\n      (st, line) => {\n        // Cheap pre-filter: skip the JSON parse for lines that cannot matter.\n        if (!line.includes('\"last-prompt\"') && !line.includes('\"user\"')) return st;\n        let obj: Record<string, unknown>;\n        try {\n          obj = JSON.parse(line) as Record<string, unknown>;\n        } catch {\n          return st;\n        }\n\n        if (obj.type === \"last-prompt\") {\n          if (typeof obj.lastPrompt === \"string\") take(obj.lastPrompt, st.entries);\n        } else if (obj.type === \"user\") {\n          const text = messageText(obj);\n          if (text) take(text, st.messages);\n        }\n        return st;\n      },\n      scanOpts\n    );\n    fromEntries.push(...state.entries.slice(-KEEP_PROMPTS));\n    fromMessages.push(...state.messages.slice(-KEEP_PROMPTS));\n  }\n\n  const source = fromEntries.length > 0 ? fromEntries : fromMessages;\n\n  // Consecutive duplicates are common: a prompt is recorded both when it is\n  // submitted and again when the turn it belongs to is finalised.\n  const deduped = source.filter((p, i) => i === 0 || p !== source[i - 1]);\n  return deduped.slice(-limit);\n}\n\n// ---------------------------------------------------------------------------\n// Working tree\n// ---------------------------------------------------------------------------\n\nexport interface WorkingTree {\n  branch: string | null;\n  head: string | null;\n  /** Porcelain entries, capped. */\n  changes: string[];\n  /** How many entries were dropped by the cap. */\n  overflow: number;\n}\n\nexport function readWorkingTree(cwd: string): WorkingTree | null {\n  const git = (args: string[]): string | null => {\n    try {\n      return execFileSync(\"git\", args, {\n        cwd,\n        encoding: \"utf8\",\n        stdio: [\"ignore\", \"pipe\", \"ignore\"],\n      }).trim();\n    } catch {\n      return null;\n    }\n  };\n\n  const branch = git([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"]);\n  if (branch === null) return null; // Not a git repo — nothing to report.\n\n  const status = git([\"status\", \"--porcelain\"]) ?? \"\";\n  const all = status.split(\"\\n\").filter((l) => l.trim().length > 0);\n\n  return {\n    branch,\n    head: git([\"log\", \"-1\", \"--pretty=%h %s\"]),\n    changes: all.slice(0, MAX_FILES),\n    overflow: Math.max(0, all.length - MAX_FILES),\n  };\n}\n\n// ---------------------------------------------------------------------------\n// Digest\n// ---------------------------------------------------------------------------\n\nexport interface DigestInput {\n  cwd: string;\n  /** Every transcript file for this session, oldest part first. */\n  transcriptPaths?: string[];\n  timestamp?: string;\n}\n\n/**\n * Build the auto-checkpoint body, or \"\" when there is nothing worth recording.\n *\n * REQUIRING A PROMPT IS THE POINT\n * -------------------------------\n * A checkpoint that replaces an earlier one had better be worth more than what\n * it displaced. Until the user has said something, this session has produced no\n * state — and an uncommitted working tree is not this session's doing; it was\n * most likely already there when the session opened.\n *\n * Without this guard, opening a session in a dirty repo and touching one tool\n * is enough to overwrite the previous session's rich handover with \"clean tree,\n * nothing asked\". The handover survives in the session note either way, but\n * `## Continue` is what the next session is shown, so what sits there matters.\n */\n/**\n * Cap on `@n=` file-declaration lines. A handover reader gets no value from a\n * long file list — five paths are enough to identify what kind of change\n * happened, and the exact count and full list are always one `git status`\n * away via the `z=` trailer.\n */\nconst MAX_AT_LINES = 5;\n\n/** Flatten a prompt (or any field value) to a single trimmed line. */\nfunction flattenField(text: string): string {\n  return text.replace(/\\s*\\n\\s*/g, \" \").trim();\n}\n\n/**\n * Strip a git-porcelain status prefix off a `changes` entry, leaving the path.\n *\n * Porcelain lines are a 1-2 char status field, a space, then the path (e.g.\n * `\"M src/foo.ts\"`, `\" M src/foo.ts\"`, `\"?? x.ts\"`). The stripped result falls\n * back to the trimmed original whenever the pattern does not match, so a\n * surprising entry still produces a usable `@n=` line instead of an empty one.\n */\nfunction pathOf(entry: string): string {\n  const stripped = entry.replace(/^\\s*\\S{1,2}\\s+/, \"\").trim();\n  return stripped || entry.trim();\n}\n\n/**\n * Build the auto-checkpoint body as a single AG2 (Agentish v2) message of kind\n * `T` (task/handover). Pure function of its inputs so it can be unit-tested\n * without touching the filesystem or git.\n *\n * Field layout — see the AG2 validator for the authoritative rules:\n *   i = ckpt-<sanitised timestamp>\n *   g = the most recent prompt (goal)\n *   d = the earlier prompts, joined by \" | \" (\"?\" when there are none)\n *   t = \"?\" always — a mechanical handover runs no tests\n *   @n = one declaration per changed file, capped at MAX_AT_LINES so the\n *        whole message stays within AG2's 25-line limit\n *   z = a note summarising the working tree, capped at 200 chars\n */\nexport function ag2Checkpoint(\n  prompts: string[],\n  tree: WorkingTree | null,\n  ts: string\n): string {\n  const id = `ckpt-${ts.replace(/[^A-Za-z0-9._-]/g, \"-\")}`;\n  const g = flattenField(prompts[0]);\n  const d =\n    prompts.length > 1 ? prompts.slice(1).map(flattenField).join(\" | \") : \"?\";\n\n  const lines: string[] = [\"T\", `i=${id}`, `g=${g}`, `d=${d}`, \"t=?\"];\n\n  let extra = 0;\n  if (tree) {\n    const shown = tree.changes.slice(0, MAX_AT_LINES);\n    extra = Math.max(0, tree.changes.length - MAX_AT_LINES) + tree.overflow;\n    shown.forEach((entry, i) => lines.push(`@${i + 1}=${pathOf(entry)}`));\n  }\n\n  let z: string;\n  if (!tree) {\n    z = \"auto ckpt (no model)\";\n  } else {\n    const dirty = tree.changes.length;\n    const head = tree.head ?? \"nohead\";\n    z = `auto ckpt (no model); ${tree.branch}@${head}; ${dirty} dirty`;\n    if (extra > 0) z += `; +${extra} paths`;\n    z += \"; git status for detail\";\n  }\n  lines.push(`z=${z.length > 200 ? z.slice(0, 200) : z}`);\n\n  return lines.join(\"\\n\");\n}\n\nexport function buildAutosaveBody(input: DigestInput): string {\n  const ts = input.timestamp ?? new Date().toISOString();\n  const prompts = recentPrompts(input.transcriptPaths ?? []);\n  if (prompts.length === 0) return \"\";\n\n  const tree = readWorkingTree(input.cwd);\n  return ag2Checkpoint(prompts, tree, ts);\n}\n","/**\n * context-fill.ts — \"how full is this session's context window, right now?\"\n *\n * No hook payload carries this number directly (verified: PreCompact,\n * UserPromptSubmit and PostToolUse stdin never include `context_window`).\n * Two sources can reconstruct it, in order of preference:\n *\n *   1. STATUSLINE STATE FILE — statusline-command.sh receives the exact\n *      figure from Claude Code (`.context_window.used_percentage` /\n *      `.context_window.context_window_size`) on every render and persists\n *      it to `${TMPDIR}/pai-context-<session_id>.json`. This is authoritative\n *      but depends on the status line having rendered recently — a session\n *      whose terminal isn't drawing a status line (headless, backgrounded)\n *      leaves this file missing or stale.\n *\n *   2. TRANSCRIPT USAGE — every `message.usage` entry in the session's own\n *      .jsonl transcript already reports the token accounting for that one\n *      API call: `input_tokens + cache_read_input_tokens +\n *      cache_creation_input_tokens` on the MOST RECENT such entry *is* the\n *      current context fill, because each request resends the full context.\n *      This is why it must be the last entry's fields summed, never a sum\n *      across entries — summing across entries is a running token-spend\n *      total, not a fill reading (see calculateSessionTokens, a different\n *      metric answering a different question).\n *\n * When neither source is available, the answer is UNKNOWN, not zero. A zero\n * reads as \"plenty of room\" and would silently suppress every downstream\n * decision that depends on this number (the compaction handover chief among\n * them) for the entire session.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport { readTailLines, scanIncremental } from \"./transcript-scan.js\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport {\n  DEFAULT_CONTEXT_WINDOW,\n  contextWindowFromModelId,\n  stripModelVariant,\n} from \"../../../utils/model-window.js\";\nimport { inputSideTokens } from \"../../../utils/input-side-tokens.js\";\nimport { readWorkersSection } from \"../../../workers/config.js\";\n\nexport { DEFAULT_CONTEXT_WINDOW };\n\n/** How many trailing lines of the transcript to scan for a usage entry.\n *  A single turn is rarely more than a handful of JSONL lines (assistant\n *  text + tool_use/tool_result pairs), so 40 comfortably covers one turn\n *  even a busy one, without reading the whole file on every check. */\nconst TRANSCRIPT_TAIL_LINES = 40;\n\n/** A statusline reading older than this is treated as not there at all —\n *  a stale number is worse than none, because it looks confident. */\nconst STATUSLINE_STALE_MS = 5 * 60 * 1000; // 5 minutes\n\nexport type ContextFillSource = \"statusline\" | \"transcript\" | \"unknown\";\n\nexport interface ContextFillReading {\n  status: \"ok\" | \"unknown\";\n  /** Tokens currently occupying the context window, or null when unknown. */\n  usedTokens: number | null;\n  windowSize: number;\n  /** usedTokens / windowSize, or null when unknown. Not clamped to [0,1] —\n   *  callers that need a clamped display value do that themselves so the\n   *  raw reading (which can legitimately exceed 1 for a moment) is never\n   *  silently rewritten here. */\n  fraction: number | null;\n  source: ContextFillSource;\n}\n\nfunction unknownReading(windowSize: number): ContextFillReading {\n  return { status: \"unknown\", usedTokens: null, windowSize, fraction: null, source: \"unknown\" };\n}\n\n// ---------------------------------------------------------------------------\n// Source 1 — statusline state file\n// ---------------------------------------------------------------------------\n\nexport function statuslineStateFilePath(sessionId: string): string {\n  return join(tmpdir(), `pai-context-${sessionId}.json`);\n}\n\ninterface StatuslineState {\n  used_percentage?: number;\n  context_window_size?: number;\n  session_id?: string;\n  timestamp?: number;\n}\n\n/**\n * Read the state file statusline-command.sh writes on every render.\n * Returns null when the file is missing, unparsable, or older than\n * STATUSLINE_STALE_MS — all three mean \"not a source right now\".\n */\nexport function readStatuslineFill(sessionId: string, now = Date.now()): ContextFillReading | null {\n  if (!sessionId) return null;\n  const path = statuslineStateFilePath(sessionId);\n  if (!existsSync(path)) return null;\n\n  let raw: StatuslineState;\n  try {\n    raw = JSON.parse(readFileSync(path, \"utf-8\"));\n  } catch {\n    return null;\n  }\n\n  if (typeof raw.timestamp !== \"number\" || now - raw.timestamp > STATUSLINE_STALE_MS) {\n    return null; // stale — fall through to the transcript source\n  }\n  if (typeof raw.used_percentage !== \"number\" || typeof raw.context_window_size !== \"number\") {\n    return null;\n  }\n\n  const windowSize = raw.context_window_size;\n  const usedTokens = Math.round((raw.used_percentage / 100) * windowSize);\n  return {\n    status: \"ok\",\n    usedTokens,\n    windowSize,\n    fraction: raw.used_percentage / 100,\n    source: \"statusline\",\n  };\n}\n\n// ---------------------------------------------------------------------------\n// Source 2 — transcript usage (fallback)\n// ---------------------------------------------------------------------------\n\ninterface UsageEntry {\n  input_tokens?: number;\n  cache_read_input_tokens?: number;\n  cache_creation_input_tokens?: number;\n}\n\n/**\n * Sum the three context-carrying fields of ONE usage object — never across\n * usage objects. A single `message.usage` already reports what that one API\n * call sent as context; adding another entry's numbers to it produces a\n * cumulative spend figure, not a fill reading, and can exceed the window\n * size many times over on a long session (the exact defect this helper\n * exists to not repeat — see the PreCompact header fix in the same change).\n */\nfunction usageTotal(usage: UsageEntry): number {\n  return inputSideTokens(usage);\n}\n\n/**\n * Derive context fill from the tail of a .jsonl transcript: the most recent\n * `message.usage` entry, read from the end backwards so a trailing line with\n * no usage field (a plain text turn, a tool_result) doesn't hide one just\n * before it.\n *\n * The window size, when the caller has none, is derived from the transcript's\n * own last assistant model id (\"glm-5.3[1m]\" → 1,000,000) before falling back\n * to the assumed default — a session whose model declares its window should\n * not be measured against a guess.\n */\nexport function contextFillFromTranscript(\n  transcriptPath: string,\n  windowSize?: number\n): ContextFillReading {\n  if (!transcriptPath || !existsSync(transcriptPath)) {\n    return unknownReading(windowSize ?? DEFAULT_CONTEXT_WINDOW);\n  }\n\n  const tail = readTailLines(transcriptPath, TRANSCRIPT_TAIL_LINES).slice(-TRANSCRIPT_TAIL_LINES);\n\n  // one pass from the end: the first usage entry is the fill reading, and the\n  // last assistant model seen (before or at that point) declares the window\n  let lastModel: string | null = null;\n  for (let i = tail.length - 1; i >= 0; i--) {\n    let entry: { type?: string; message?: { usage?: UsageEntry; model?: unknown } };\n    try {\n      entry = JSON.parse(tail[i]);\n    } catch {\n      continue;\n    }\n    if (entry?.type === \"assistant\" && lastModel === null) {\n      const model = entry.message?.model;\n      if (typeof model === \"string\" && model !== \"\") lastModel = model;\n    }\n    const usage = entry?.message?.usage;\n    if (usage && typeof usage === \"object\") {\n      const window = windowSize ?? contextWindowFromModelId(lastModel) ?? DEFAULT_CONTEXT_WINDOW;\n      const usedTokens = usageTotal(usage);\n      return {\n        status: \"ok\",\n        usedTokens,\n        windowSize: window,\n        fraction: usedTokens / window,\n        source: \"transcript\",\n      };\n    }\n  }\n\n  const window = windowSize ?? contextWindowFromModelId(lastModel) ?? DEFAULT_CONTEXT_WINDOW;\n  return unknownReading(window);\n}\n\n// ---------------------------------------------------------------------------\n// Precedence — statusline (fresh) > transcript > unknown\n// ---------------------------------------------------------------------------\n\nexport function getContextFill(\n  input: { sessionId?: string; transcriptPath?: string; windowSize?: number },\n  now = Date.now()\n): ContextFillReading {\n  if (input.sessionId) {\n    const fromStatusline = readStatuslineFill(input.sessionId, now);\n    if (fromStatusline) return fromStatusline;\n  }\n\n  if (input.transcriptPath) {\n    // windowSize passes through only when the caller knows it; otherwise the\n    // transcript's own model id is asked first (see contextFillFromTranscript)\n    return contextFillFromTranscript(input.transcriptPath, input.windowSize);\n  }\n\n  return unknownReading(input.windowSize ?? DEFAULT_CONTEXT_WINDOW);\n}\n\n// ---------------------------------------------------------------------------\n// Display helper — clamp-and-flag rather than ever print an absurd number\n// ---------------------------------------------------------------------------\n\nexport interface FillDisplay {\n  /** e.g. \"63k\" or \"unknown\". Never a number larger than the window. */\n  text: string;\n  /** True when the raw reading exceeded the window size and had to be\n   *  clamped — that reading was a bug, not a fill, and callers may want to\n   *  log it even though the displayed text is already safe. */\n  flagged: boolean;\n}\n\nexport function formatContextFill(reading: ContextFillReading): FillDisplay {\n  if (reading.status !== \"ok\" || reading.usedTokens === null) {\n    return { text: \"unknown\", flagged: false };\n  }\n\n  let used = reading.usedTokens;\n  let flagged = false;\n  if (used > reading.windowSize) {\n    flagged = true;\n    used = reading.windowSize;\n  }\n\n  const text = used > 1000 ? `${Math.round(used / 1000)}k` : String(used);\n  return { text: flagged ? `${text}+ (clamped — reading exceeded window)` : text, flagged };\n}\n\n// ---------------------------------------------------------------------------\n// Threshold-triggered handover — WHEN to warm up / refresh the model-written\n// summary ahead of a compaction.\n// ---------------------------------------------------------------------------\n//\n// THE CONFIGURED VALUE PREDICTS THE TRIGGER ONLY WHILE THE PLATFORM'S FORMULA\n// HOLDS. CLAUDE_AUTOCOMPACT_PCT_OVERRIDE was 80 throughout, on this machine,\n// across BOTH of the following regimes — the same configured number, two\n// different truths:\n//\n//   2026-08-16 .. 2026-09-10   preTokens ~ 993,096 – 1,002,500  (~100% of a 1M window; override ignored)\n//   2026-09-12 .. 2026-09-20   preTokens ~   782,981 – 791,995  (1M window) and\n//                              preTokens ~   143,280 – 154,832  (200k window)\n//                              = pct/100 × (window − 20,000), measured 2026-09-20\n//\n// A threshold derived only from the configured override was wrong for the\n// first regime and will be wrong again the next time the formula moves — the\n// config is not ground truth, it is what Claude Code claims it will honor.\n// Two more facts (2026-09-20): the settings.json `env` value beats the\n// process env, so there is no per-launch override; and headless `-p`\n// sessions do not compact at the derived trigger at all.\n//\n// GROUND TRUTH IS ON DISK ALREADY: every real compaction writes a\n// `compact_boundary` system event to the transcript with\n// `compactMetadata.preTokens` — the platform's own count of context tokens\n// immediately before IT compacted. `measureCompactionTrigger` scans a\n// project's own transcripts for these and takes the MINIMUM of the most\n// recent three (minimum, not mean: warm-up must fire before the earliest\n// plausible boundary, not the average one). A project that has compacted\n// before learns its own trigger and needs no code change when the regime\n// moves again — the same derivation produced 998,508 pre-09-12 and ~784k\n// after, from one unchanged formula.\n//\n// The configured chain — env override, then a default — is the fallback,\n// used ONLY when a project has no compaction history yet:\n//\n//   effectiveTrigger = measured ?? configuredTrigger(windowSize, override ?? 80)\n//   configuredTrigger(windowSize, pct) = pct/100 * (windowSize - 20,000)\n//\n// DEFAULT IS 80, NOT 100, WHEN NOTHING IS KNOWN. 100 was considered — it\n// matches the FIRST regime above — and rejected: the costs are asymmetric.\n// Warming up too early wastes one summary; cheap, invisible. Warming up too\n// late produces exactly the degraded successor session this feature exists\n// to prevent. 80 is the conservative default until a project has its own\n// measured history to correct it.\n//\n// Margins below the effective trigger are absolute tokens, sized against the\n// largest single-turn context jump measured: 62,383 tokens (517,952 →\n// 580,335, about 27 seconds). warmup sits 100k below the trigger —\n// comfortably more than that jump, so a session cannot leap clean over\n// warmup straight into a compaction in one turn; refresh at 40k below\n// catches a session that sat above warmup for a while; fireNow at 15k below\n// is \"no time left for a clean crossing\" and fires immediately rather than\n// waiting for one.\nexport const DEFAULT_AUTOCOMPACT_PCT = 80;\n\n/**\n * The platform's actual compaction trigger for a window/pct pair. NOT\n * windowSize * pct/100 — that formula gives 800,000 on 1M/80 and 160,000 on\n * 200k/80, but the platform fires at pct/100 * (windowSize - 20,000):\n * 784,000 and 144,000, measured on every compact_boundary on this machine,\n * 2026-09-20 (see the module comment above).\n */\nexport function configuredTrigger(windowSize: number, pct: number): number {\n  return Math.round((pct / 100) * (windowSize - 20_000));\n}\n\n/** How many of the most recent compact_boundary events to consider, and to\n *  take the minimum of. */\nconst MEASURED_TRIGGER_SAMPLE_SIZE = 3;\n\nexport const THRESHOLD_MARGIN_TOKENS = {\n  warmup: 100_000,\n  refresh: 40_000,\n  immediate: 15_000,\n} as const;\n\n// ---------------------------------------------------------------------------\n// Measured trigger — scan a project's own transcript history for the\n// platform's own compact_boundary events, ground truth over configuration.\n// ---------------------------------------------------------------------------\n//\n// Deliberately re-implements the tiny bit of path encoding it needs (below)\n// rather than importing project-utils/paths.ts's encodePath: that module\n// chain ends in pai-paths.ts, which calls process.exit(1) if ADAPTER_DIR does\n// not resolve to an existing directory. context-fill.ts is specifically the\n// module a hook falls back on when its environment is unreliable — pulling\n// in a dependency that can kill the process on import would defeat that.\n// Claude Code's transcript directory is a fixed, home-relative convention\n// (~/.claude/projects/<encoded-cwd>/), not something ADAPTER_DIR governs, so\n// resolving it directly here is also just the more correct dependency, not\n// only the safer one.\n\n/** Real default — overridable per-call so tests never touch the user's\n *  actual ~/.claude/projects/ (a live directory this very session writes to). */\nexport const CLAUDE_PROJECTS_DIR = join(homedir(), \".claude\", \"projects\");\n\nfunction encodeProjectPath(cwd: string): string {\n  return cwd.replace(/[/\\s.-]/g, \"-\");\n}\n\nexport interface CompactBoundarySample {\n  preTokens: number;\n  timestampMs: number;\n  /** ISO string, kept alongside timestampMs so callers can print it. */\n  timestamp: string;\n  /** The event's own uuid when present — the dedup key. Claude Code's\n   *  `sessions/` archive directory mirrors the live project transcript, so\n   *  the SAME compact_boundary event can legitimately appear in two files;\n   *  without deduping by identity, \"most recent three\" can silently become\n   *  three copies of one event, which is a stale reading wearing a\n   *  plausible-looking sample size. */\n  uuid?: string;\n}\n\n/** Every `.jsonl` transcript belonging to a project — top-level (the live\n *  file) and `sessions/` (Claude Code's archive, which mirrors it). No file\n *  is excluded and no ordering is applied here: ordering by EVENT\n *  timestamp, not by file mtime, is the whole point (see\n *  readCompactBoundarySamples) — a file's mtime does not reliably track\n *  which events inside it are recent, and pre-filtering by mtime is exactly\n *  what caused this function to return a stale, pre-regime-change trigger\n *  on real data. */\nfunction listProjectTranscripts(cwd: string, projectsDir: string): string[] {\n  const projectDir = join(projectsDir, encodeProjectPath(cwd));\n  if (!existsSync(projectDir)) return [];\n\n  const paths: string[] = [];\n  const collect = (dir: string): void => {\n    if (!existsSync(dir)) return;\n    let entries: string[];\n    try {\n      entries = readdirSync(dir);\n    } catch {\n      return;\n    }\n    for (const entry of entries) {\n      if (entry.endsWith(\".jsonl\")) paths.push(join(dir, entry));\n    }\n  };\n  collect(projectDir);\n  collect(join(projectDir, \"sessions\"));\n  return paths;\n}\n\n/** Placeholder model id the platform writes on synthetic assistant turns. */\nconst SYNTHETIC_MODEL = \"<synthetic>\";\n\n/**\n * Which family of model wrote a transcript: a stable key derived from the id\n * itself, never from an Anthropic allowlist. \"claude\" is the platform's own;\n * any other id reduces to its base form before any bracketed variant suffix\n * (\"glm-5.3[1m]\" → \"glm-5.3\"), so the same model family always produces the\n * same key; \"unknown\" is no usable model field at all. Whether a family is\n * foreign is decided by comparison (see isForeignModelFamily), not by the\n * key — keying \"foreign\" here is what made non-Anthropic primary models lose\n * their own compaction history.\n */\nexport type TranscriptModelFamily = \"claude\" | \"unknown\" | (string & {});\n\nexport function modelFamily(model: string | null | undefined): TranscriptModelFamily {\n  if (typeof model !== \"string\" || model === \"\" || model === SYNTHETIC_MODEL) return \"unknown\";\n  const base = stripModelVariant(model);\n  return base.startsWith(\"claude-\") ? \"claude\" : base;\n}\n\n/**\n * Is a model id foreign to this machine's sessions? A model is native when it\n * is a Claude model or one the worker registry configures (any provider's\n * default or fast alias — the models this stack actually runs on). Anything\n * else, with a readable model id, is foreign. Doubt (no id, unreadable\n * registry) reads as native: the callers that skip work on \"foreign\" must not\n * skip it on doubt.\n */\nexport function isForeignModelFamily(\n  family: TranscriptModelFamily,\n  nativeFamilies?: Set<string>\n): boolean {\n  if (family === \"unknown\" || family === \"claude\") return false;\n  return !(nativeFamilies ?? nativeModelFamilies()).has(family);\n}\n\n/** Families of every model id the worker registry configures. Never throws:\n *  an unreadable or missing registry means \"nothing configured\", which leaves\n *  every non-Claude family foreign — the historical behaviour. */\nexport function nativeModelFamilies(configPath?: string): Set<string> {\n  const families = new Set<string>();\n  try {\n    for (const p of Object.values(readWorkersSection(configPath).workers.providers)) {\n      families.add(modelFamily(p.models.default));\n      if (p.models.fast) families.add(modelFamily(p.models.fast));\n    }\n  } catch {\n    return families;\n  }\n  return families;\n}\n\n/**\n * The family of the LAST assistant model in a transcript — scanned from the\n * end so a long transcript costs one read and a few lines of parsing.\n * Unreadable or model-less transcripts are \"unknown\", never foreign:\n * the callers that skip work on foreign models must not skip it on doubt.\n * Pair with isForeignModelFamily to judge the key.\n */\nexport function transcriptModelFamily(path: string): TranscriptModelFamily {\n  let found: TranscriptModelFamily = \"unknown\";\n  // Read back from the end, widening the window only while no model is found.\n  readTailLines(path, 1, (lines) => {\n    for (let i = lines.length - 1; i >= 0; i--) {\n      const line = lines[i];\n      if (!line.includes('\"model\"')) continue;\n      try {\n        const entry = JSON.parse(line) as { type?: string; message?: { model?: unknown } };\n        if (entry.type !== \"assistant\") continue;\n        const model = entry.message?.model;\n        if (typeof model === \"string\" && model !== SYNTHETIC_MODEL) {\n          found = modelFamily(model);\n          return false;\n        }\n      } catch {\n        continue;\n      }\n    }\n    return true;\n  });\n  return found;\n}\n\n/**\n * Every DISTINCT compact_boundary sample found across ALL of a project's\n * transcripts (live + archived), newest first by the event's OWN timestamp\n * — never by which file it came from or that file's mtime. Deduplicated by\n * the event's uuid (falling back to a timestamp+preTokens key for the rare\n * line with no uuid) so an event mirrored into `sessions/` is counted once.\n */\ninterface BoundaryScan {\n  lastModel: string | null;\n  boundaries: Array<CompactBoundarySample & { model: string | null }>;\n}\n\nfunction readCompactBoundarySamples(\n  cwd: string,\n  projectsDir: string,\n  nativeFamilies: Set<string>\n): CompactBoundarySample[] {\n  const byKey = new Map<string, CompactBoundarySample>();\n\n  for (const path of listProjectTranscripts(cwd, projectsDir)) {\n    // Incremental per file: the cache holds every compact_boundary seen so\n    // far with the model governing it, so only appended lines are parsed.\n    // The foreign-model judgement is applied here, per call, because the\n    // native-family set can change between calls.\n    const scan = scanIncremental<BoundaryScan>(\n      path,\n      \"compact\",\n      () => ({ lastModel: null, boundaries: [] }),\n      (st, line) => {\n        const isAssistant = line.includes('\"assistant\"');\n        if (!isAssistant && !line.includes(\"compact_boundary\")) return st;\n        let entry: {\n          type?: string;\n          subtype?: string;\n          timestamp?: string;\n          uuid?: string;\n          compactMetadata?: { preTokens?: number };\n          message?: { model?: unknown };\n        };\n        try {\n          entry = JSON.parse(line);\n        } catch {\n          return st;\n        }\n        if (entry.type === \"assistant\") {\n          const model = entry.message?.model;\n          if (typeof model === \"string\" && model !== SYNTHETIC_MODEL) st.lastModel = model;\n          return st;\n        }\n        if (entry.type !== \"system\" || entry.subtype !== \"compact_boundary\") return st;\n        const preTokens = entry.compactMetadata?.preTokens;\n        if (typeof preTokens !== \"number\" || !Number.isFinite(preTokens)) return st;\n        const timestampMs = entry.timestamp ? Date.parse(entry.timestamp) : NaN;\n        if (!Number.isFinite(timestampMs)) return st;\n        st.boundaries.push({ preTokens, timestampMs, timestamp: entry.timestamp!, uuid: entry.uuid, model: st.lastModel });\n        return st;\n      }\n    );\n\n    // The model governing each sample: the most recent assistant\n    // `message.model` seen in this file before the compact_boundary. A\n    // transcript written by a model foreign to this machine (a headless\n    // worker on another provider, with a different context window) compacts\n    // at a different size and must not shape THIS project's trigger — two\n    // such workers compacting at ~151k pulled a real project's trigger from\n    // ~784k to ~151k. Native is Claude plus every model the registry\n    // configures, so a non-Anthropic PRIMARY model keeps its own history.\n    // Samples with no model seen yet are kept: older transcripts may lack\n    // the field.\n    const lastModel = scan.lastModel;\n    let foreignDiscards = 0;\n\n    for (const b of scan.boundaries) {\n      if (isForeignModelFamily(modelFamily(b.model), nativeFamilies)) {\n        foreignDiscards++;\n        continue;\n      }\n      const key = b.uuid ?? `${b.timestampMs}:${b.preTokens}`;\n      if (!byKey.has(key)) {\n        byKey.set(key, { preTokens: b.preTokens, timestampMs: b.timestampMs, timestamp: b.timestamp, uuid: b.uuid });\n      }\n    }\n\n    if (foreignDiscards > 0) {\n      console.error(\n        `[context-fill] ignored ${foreignDiscards} compaction sample(s) from a ` +\n        `foreign-model transcript (model=${lastModel}): ${path}`\n      );\n    }\n  }\n\n  return [...byKey.values()].sort((a, b) => b.timestampMs - a.timestampMs);\n}\n\n/**\n * The most recent MEASURED_TRIGGER_SAMPLE_SIZE distinct compact_boundary\n * events for a project, newest first — exposed on its own (not just the\n * derived minimum) so the number `measureCompactionTrigger` returns is\n * checkable: print these and the timestamps prove which three events\n * produced it, rather than asking for trust.\n */\nexport function selectedCompactionSamples(\n  cwd: string,\n  projectsDir: string = CLAUDE_PROJECTS_DIR,\n  configPath?: string,\n  cacheDir?: string\n): CompactBoundarySample[] {\n  if (!cwd) return [];\n  const cacheFile = cacheDir\n    ? join(cacheDir, `pai-compaction-samples-${encodeProjectPath(cwd).slice(-100)}.json`)\n    : null;\n  if (cacheFile) {\n    try {\n      if (Date.now() - statSync(cacheFile).mtimeMs < SAMPLE_CACHE_MAX_AGE_MS) {\n        const cached = JSON.parse(readFileSync(cacheFile, \"utf-8\"));\n        if (Array.isArray(cached)) return cached as CompactBoundarySample[];\n      }\n    } catch {\n      // missing or unreadable — recompute\n    }\n  }\n  const samples = readCompactBoundarySamples(cwd, projectsDir, nativeModelFamilies(configPath))\n    .slice(0, MEASURED_TRIGGER_SAMPLE_SIZE);\n  if (cacheFile) {\n    try {\n      writeFileSync(cacheFile, JSON.stringify(samples), \"utf-8\");\n    } catch {\n      // best-effort — a missing cache only costs a recompute\n    }\n  }\n  return samples;\n}\n\n/** A cached sample set older than this is recomputed (lazily, by the next caller). */\nconst SAMPLE_CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000;\n\n/**\n * The measured compaction trigger for a project, or null when it has no\n * compaction history yet (a brand-new project, one whose transcripts this\n * process cannot read, or one whose only recent samples belong to a\n * different override regime — see the admissibility filter below).\n * Minimum of the most recent MEASURED_TRIGGER_SAMPLE_SIZE DISTINCT\n * compact_boundary events, ordered by the events' own timestamps across\n * every transcript the project has (live and archived) — see the module\n * comment above for why minimum, not mean, and readCompactBoundarySamples\n * for why \"distinct\" and \"own timestamp\" both matter (a file-mtime-ordered,\n * non-deduplicated version of this returned a stale pre-regime-change\n * trigger on real project data).\n *\n * When `configuredTriggerTokens` is given, a sample is admissible only when\n * preTokens >= 0.5 * configuredTriggerTokens — a compaction that fired far\n * below the currently configured trigger came from a lower override or a\n * smaller window, a different regime than the one in force now, and must\n * not drag the trigger down with it. Real case, 2026-09-20:\n * CLAUDE_AUTOCOMPACT_PCT_OVERRIDE was 20 for seven hours, this project\n * compacted three times at ~195k-198k under it, then the override went\n * back to 80 (configured ~784k) — those three samples are inadmissible and\n * must be discarded, not averaged into the new regime's trigger, or the\n * handover would fire at ~155k for days until three fresh ~784k\n * compactions pushed them out on their own.\n */\nexport function measureCompactionTrigger(\n  cwd: string,\n  projectsDir: string = CLAUDE_PROJECTS_DIR,\n  configPath?: string,\n  configuredTriggerTokens?: number,\n  cacheDir?: string\n): number | null {\n  const samples = selectedCompactionSamples(cwd, projectsDir, configPath, cacheDir);\n  if (samples.length === 0) return null;\n\n  let admissible = samples;\n  if (configuredTriggerTokens !== undefined) {\n    const floor = 0.5 * configuredTriggerTokens;\n    admissible = samples.filter((s) => s.preTokens >= floor);\n    const discarded = samples.length - admissible.length;\n    if (discarded > 0) {\n      console.error(\n        `[context-fill] discarded ${discarded} compaction sample(s) as other-regime for ${cwd} ` +\n        `(preTokens below ${Math.round(floor)}, half of configured=${configuredTriggerTokens}): ` +\n        samples.filter((s) => s.preTokens < floor).map((s) => `${s.preTokens}@${s.timestamp}`).join(\", \")\n      );\n    }\n    if (admissible.length === 0) return null;\n  }\n\n  const trigger = Math.min(...admissible.map((s) => s.preTokens));\n  console.error(\n    `[context-fill] measured trigger for ${cwd}: ${trigger} (minimum of ` +\n    admissible.map((s) => `${s.preTokens}@${s.timestamp}`).join(\", \") + \")\"\n  );\n  return trigger;\n}\n\n/**\n * Read CLAUDE_AUTOCOMPACT_PCT_OVERRIDE from the environment. Absent →\n * DEFAULT_AUTOCOMPACT_PCT. Present but not a finite number in (0, 100] →\n * also DEFAULT_AUTOCOMPACT_PCT, logged, so a typo in the override degrades\n * to the documented default instead of silently producing nonsense\n * thresholds (0, negative, or a fraction so large no session ever reaches\n * it).\n */\nexport function resolveAutocompactPct(env: NodeJS.ProcessEnv = process.env): number {\n  const raw = env.CLAUDE_AUTOCOMPACT_PCT_OVERRIDE;\n  if (raw === undefined || raw === \"\") return DEFAULT_AUTOCOMPACT_PCT;\n\n  const parsed = Number(raw);\n  if (!Number.isFinite(parsed) || parsed <= 0 || parsed > 100) {\n    console.error(\n      `[context-fill] CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=\"${raw}\" is not a usable percentage — ` +\n      `falling back to the default ${DEFAULT_AUTOCOMPACT_PCT}.`\n    );\n    return DEFAULT_AUTOCOMPACT_PCT;\n  }\n  return parsed;\n}\n\n/**\n * The two thresholds that actually fire a handover job. \"immediate\" is not a\n * third job — see `isImmediate` below — it only changes how urgently a\n * crossing of these two is treated.\n */\nexport type ThresholdName = \"warmup\" | \"refresh\";\n\n/** Which basis actually produced effectiveTriggerTokens — reported so the\n *  number is checkable rather than trusted. */\nexport type TriggerSource = \"measured\" | \"configured\" | \"measured-clamped\";\n\nexport interface ContextFillThresholds {\n  warmupTokens: number;\n  refreshTokens: number;\n  immediateTokens: number;\n  /** The derived compaction trigger these were measured back from —\n   *  min(measured, configured) when a measured value exists. */\n  effectiveTriggerTokens: number;\n  /** The raw measured value from this project's own compact_boundary\n   *  history, before any clamping — null when the project has no history.\n   *  Kept alongside effectiveTriggerTokens so a clamp is visible rather\n   *  than silent: a caller can see both what was measured and what was\n   *  actually used. */\n  measuredTriggerTokens: number | null;\n  /** The raw configured-chain value (env override or default, as a\n   *  fraction of the window) — always computed and reported, even when it\n   *  wasn't what ended up being used. */\n  configuredTriggerTokens: number;\n  /** The autocompact percentage from the configured chain. */\n  autocompactPct: number;\n  /** \"measured\": a measured value existed and was <= configured, so it was\n   *  used directly — the better estimate, since it reflects reality the\n   *  configured percentage cannot know.\n   *  \"measured-clamped\": a measured value existed but was HIGHER than\n   *  configured — it reflects a regime that may no longer apply (this\n   *  project's last compaction predates a since-changed trigger), so the\n   *  lower, safer configured value was used instead.\n   *  \"configured\": no measured value exists yet (no compaction history for\n   *  this project) — the configured chain is all there is. */\n  triggerSource: TriggerSource;\n  /** True when these were computed against a window size Claude Code itself\n   *  reported (the statusline source). False when the window is only an\n   *  assumed default (the transcript-fallback source never learns the real\n   *  window size) — callers should log this: a threshold silently computed\n   *  against the wrong basis is the same class of fault as printing a token\n   *  count larger than the window. */\n  windowConfirmed: boolean;\n}\n\nexport interface ContextFillThresholdOpts {\n  /** The session's project directory, used to look up its own compaction\n   *  history for the measured trigger. Omit when unknown — falls back to\n   *  the configured chain, same as a project with no history yet. */\n  cwd?: string;\n  /** Override the measured-trigger lookup instead of scanning transcripts —\n   *  primarily for tests. `null` forces the configured fallback even when\n   *  `cwd` is given; `undefined` (the default) does the real lookup. */\n  measuredTrigger?: number | null;\n  /** Directory holding the daily compaction-sample cache. When set, the\n   *  project's transcripts are rescanned at most once per 24 h; omit to\n   *  always scan. */\n  sampleCacheDir?: string;\n}\n\n/**\n * Derive warmup/refresh/immediate thresholds from a fill reading.\n *\n * effectiveTrigger = min(measured, configuredChainValue) when a measured\n * value exists — NOT the measured value outright. A project whose newest\n * compaction predates a regime change measures a stale-HIGH trigger: a real\n * case on this machine measured 998,267 for a project whose ACTUAL current\n * boundary (from a different project's fresher history, cross-checked\n * independently) is ~784,000 — using 998,267 directly would compute a\n * warm-up of 898,267, above the real boundary, so the handover would never\n * fire there. The measurement is honest; it is just old.\n *\n * The minimum is correct in both directions: the measured value is the\n * better estimate when it is LOWER than configured (it reflects reality the\n * configured percentage cannot know — see the module comment above, the\n * 100%-to-78% regime change this project itself lived through); it is\n * unsafe when it is HIGHER (it reflects a regime that no longer applies).\n * Taking the minimum costs nothing in the safe direction — one wasted\n * summary if the project's regime actually did move up — and prevents the\n * unsafe direction, where a stale-high measurement suppresses the handover\n * past the real boundary. That asymmetry is the same one the 80-not-100\n * default was chosen for.\n *\n * Margins below effectiveTrigger are clamped at 0 (and logged) for a window\n * small enough that a margin would otherwise go negative — a pathological\n * input should degrade to \"fire immediately\", never to a threshold below\n * zero.\n */\nexport function contextFillThresholds(\n  reading: ContextFillReading,\n  env: NodeJS.ProcessEnv = process.env,\n  opts: ContextFillThresholdOpts = {}\n): ContextFillThresholds {\n  const windowConfirmed = reading.source === \"statusline\";\n  const autocompactPct = resolveAutocompactPct(env);\n  const configuredTriggerTokens = configuredTrigger(reading.windowSize, autocompactPct);\n\n  const measuredTriggerTokens = opts.measuredTrigger !== undefined\n    ? opts.measuredTrigger\n    : opts.cwd\n      ? measureCompactionTrigger(opts.cwd, CLAUDE_PROJECTS_DIR, undefined, configuredTriggerTokens, opts.sampleCacheDir)\n      : null;\n\n  let effectiveTriggerTokens: number;\n  let triggerSource: TriggerSource;\n\n  if (measuredTriggerTokens === null) {\n    effectiveTriggerTokens = configuredTriggerTokens;\n    triggerSource = \"configured\";\n    console.error(\n      `[context-fill] trigger source: CONFIGURED — no compaction history for this project yet. ` +\n      `measured=none, configured=${configuredTriggerTokens} (${autocompactPct}% of a ${reading.windowSize}-token ` +\n      `window) -> using ${effectiveTriggerTokens}.`\n    );\n  } else if (measuredTriggerTokens <= configuredTriggerTokens) {\n    effectiveTriggerTokens = measuredTriggerTokens;\n    triggerSource = \"measured\";\n    console.error(\n      `[context-fill] trigger source: MEASURED — measured=${measuredTriggerTokens} ` +\n      `(minimum of the most recent ${MEASURED_TRIGGER_SAMPLE_SIZE} compact_boundary events), ` +\n      `configured=${configuredTriggerTokens} -> using ${effectiveTriggerTokens} (measured, ≤ configured).`\n    );\n  } else {\n    effectiveTriggerTokens = configuredTriggerTokens;\n    triggerSource = \"measured-clamped\";\n    console.error(\n      `[context-fill] trigger source: MEASURED-CLAMPED — measured=${measuredTriggerTokens} is HIGHER than ` +\n      `configured=${configuredTriggerTokens} (a stale regime this project's history predates) -> ` +\n      `using ${effectiveTriggerTokens} (configured, the safer bound).`\n    );\n  }\n\n  const clamp = (name: string, value: number): number => {\n    if (value >= 0) return value;\n    console.error(\n      `[context-fill] ${name} threshold went negative (${value}) for a ` +\n      `${reading.windowSize}-token window — clamping to 0.`\n    );\n    return 0;\n  };\n\n  return {\n    warmupTokens: clamp(\"warmup\", effectiveTriggerTokens - THRESHOLD_MARGIN_TOKENS.warmup),\n    refreshTokens: clamp(\"refresh\", effectiveTriggerTokens - THRESHOLD_MARGIN_TOKENS.refresh),\n    immediateTokens: clamp(\"immediate\", effectiveTriggerTokens - THRESHOLD_MARGIN_TOKENS.immediate),\n    effectiveTriggerTokens,\n    measuredTriggerTokens,\n    configuredTriggerTokens,\n    autocompactPct,\n    triggerSource,\n    windowConfirmed,\n  };\n}\n\n/**\n * Which named thresholds `usedTokens` has newly crossed, given the ones that\n * have already fired this session. Ascending order (warmup before refresh).\n *\n * A single check can return more than one name — e.g. a session first\n * observed at 99% of its window crosses warmup and refresh in the same tick,\n * because there was no earlier clean crossing to catch it at. That is the\n * \"fire immediately\" case: the caller enqueues one handover and marks every\n * newly-crossed name fired, rather than waiting for a crossing that already\n * happened.\n */\nexport function crossedThresholds(\n  usedTokens: number,\n  thresholds: ContextFillThresholds,\n  alreadyFired: ThresholdName[]\n): ThresholdName[] {\n  const fired = new Set(alreadyFired);\n  const ordered: Array<[ThresholdName, number]> = [\n    [\"warmup\", thresholds.warmupTokens],\n    [\"refresh\", thresholds.refreshTokens],\n  ];\n  return ordered.filter(([name, tokens]) => !fired.has(name) && usedTokens >= tokens).map(([name]) => name);\n}\n\n/**\n * True once a session is at or above the \"no time left for a clean crossing\"\n * floor (0.985 of the window). Purely informational for callers — it never\n * gates whether `crossedThresholds` fires (a poll-based check already fires\n * any unfired threshold the moment `usedTokens` reaches it, on whatever tick\n * observes it) — but it distinguishes \"this crossed on schedule\" from \"this\n * was first observed already almost out of room\", which is worth a different\n * log line and, for a caller that queues work, a higher priority.\n */\nexport function isImmediate(usedTokens: number, thresholds: ContextFillThresholds): boolean {\n  return usedTokens >= thresholds.immediateTokens;\n}\n","/**\n * Worker-session detection.\n *\n * A disposable headless worker (a `claude -p` run started by an orchestrating\n * session, possibly against a different model provider with a different\n * context window) shares the project directory and the hook configuration\n * with the real session that spawned it. Left alone, the hooks treat it as a\n * session in its own right: they inject project context into it, create and\n * rename a numbered session note for it, autosave it, and enqueue a\n * model-written handover for it. Its compactions also land in the project's\n * transcript folder, where they are indistinguishable from a real session's\n * and drag the measured compaction trigger down (observed 2026-09-17: two\n * workers compacting at ~151k tokens pulled a project's trigger from ~784k\n * to ~151k, and the real session's handover fired at ~50k tokens).\n *\n * The launcher marks such sessions with `PAI_WORKER=1`. Every hook that does\n * per-session bookkeeping returns immediately when this predicate is true.\n * Deliberately NOT guarded: the security validator (a worker's shell\n * commands must still be checked) and observability capture.\n */\nexport function isWorkerSession(env: NodeJS.ProcessEnv = process.env): boolean {\n  return env.PAI_WORKER === \"1\";\n}\n","/**\n * context-handover-trigger.ts — decide whether THIS check is the moment a\n * session should get its threshold-triggered pre-compaction handover, and\n * enqueue it with the daemon if so.\n *\n * Called from the same live hooks that already drive the AG2 rolling\n * autosave (UserPromptSubmit, PostToolUse) — see\n * cli/commands/session/autosave.ts. Reuses the context-fill reading and the\n * derived thresholds from hooks/ts/lib/context-fill.ts; this module only\n * adds the \"have we already fired this one\" bookkeeping and the enqueue.\n *\n * BUG FIXED (found in live use, session 77084e72-...): a threshold used to\n * be marked fired the moment the ENQUEUE call succeeded — recording intent,\n * not outcome. When the daemon worker then failed or was restarted mid-spawn\n * (observed: a daemon restart 90s after the enqueue), no handover cache was\n * ever written, but the marker already said \"done\" — so the session\n * compacted with the mechanical scrape only, and would never have retried,\n * ever, for that session. A threshold is now marked CONFIRMED only after a\n * handover cache file actually appears; an enqueue with no cache to show for\n * it within PENDING_TIMEOUT_MS is treated as failed and retried.\n *\n * Never throws. A daemon that isn't running, a stale reading, an unknown\n * fill — every one of those is a reason to do nothing this check and try\n * again next time, not a reason to interrupt the hook that called this.\n */\n\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport {\n  getContextFill,\n  contextFillThresholds,\n  crossedThresholds,\n  isForeignModelFamily,\n  isImmediate,\n  transcriptModelFamily,\n  type ContextFillReading,\n  type ThresholdName,\n} from \"../hooks/ts/lib/context-fill.js\";\nimport { isWorkerSession } from \"../hooks/ts/lib/worker-session.js\";\nimport { readContextHandoverCache, type ContextHandoverCache } from \"../hooks/ts/lib/context-handover-cache.js\";\nimport { PaiClient } from \"../daemon/ipc-client.js\";\n\n/** The enqueue only needs to hand the item to the daemon's queue file — the\n *  actual LLM call happens later, asynchronously, in the worker loop. A\n *  short bound keeps a rare (at most twice per session) daemon hiccup from\n *  making the calling hook wait anywhere near as long as a normal IPC call\n *  is allowed to. */\nconst ENQUEUE_TIMEOUT_MS = 2_000;\n\n/**\n * How long to wait for a handover cache to appear after an enqueue before\n * treating it as failed and retrying. Generous over the worker's own sonnet\n * timeout (120s, see session-summary-worker.ts's CLAUDE_TIMEOUT_MS) plus\n * queue latency, so a slow-but-working run isn't retried out from under\n * itself — and short enough that a genuinely dead attempt (a daemon restart\n * mid-spawn, exactly what happened in the live failure this fixes) is\n * retried well before the next compaction, not \"never\".\n */\nconst PENDING_TIMEOUT_MS = 5 * 60 * 1000;\n\n/** A confirmed handover older than this is regenerated while fill stays at/above refresh. */\nconst REARM_AGE_MS = 30 * 60 * 1000;\n\n/** Both thresholds confirmed — nothing left to check for the rest of the session. */\nconst ALL_THRESHOLDS: ThresholdName[] = [\"warmup\", \"refresh\"];\n\n// ---------------------------------------------------------------------------\n// Persisted state — outcome-based, not intent-based\n// ---------------------------------------------------------------------------\n\n/**\n * `confirmed`: thresholds whose handover was actually verified written.\n * `pending`: an enqueue attempt awaiting outcome confirmation — cleared\n * either when a fresher cache appears (success) or when PENDING_TIMEOUT_MS\n * elapses with nothing to show for it (treated as failed; retried).\n *\n * A file in the OLD shape (`{fired: [...]}`, no `confirmed` key — exactly\n * what the live failure left behind) parses with `confirmed` defaulting to\n * `[]` and `pending` to `null`: it is treated as \"nothing confirmed yet\",\n * which self-heals the stuck session that bug left behind rather than\n * requiring a manual cleanup step.\n */\nexport interface HandoverTriggerState {\n  confirmed: ThresholdName[];\n  pending: { thresholds: ThresholdName[]; enqueuedAt: string } | null;\n}\n\nexport function triggerStatePath(sessionId: string): string {\n  return join(tmpdir(), `pai-context-handover-fired-${sessionId}.json`);\n}\n\nexport function loadTriggerState(sessionId: string): HandoverTriggerState {\n  const path = triggerStatePath(sessionId);\n  if (!existsSync(path)) return { confirmed: [], pending: null };\n  try {\n    const raw = JSON.parse(readFileSync(path, \"utf-8\")) as Partial<HandoverTriggerState>;\n    return {\n      confirmed: Array.isArray(raw.confirmed) ? (raw.confirmed as ThresholdName[]) : [],\n      pending: raw.pending && Array.isArray(raw.pending.thresholds) && typeof raw.pending.enqueuedAt === \"string\"\n        ? raw.pending\n        : null,\n    };\n  } catch {\n    return { confirmed: [], pending: null };\n  }\n}\n\nfunction saveTriggerState(sessionId: string, state: HandoverTriggerState): void {\n  try {\n    writeFileSync(triggerStatePath(sessionId), JSON.stringify(state), \"utf-8\");\n  } catch {\n    // Best-effort — a missing write only costs a possible re-attempt later.\n  }\n}\n\n/**\n * Reset a session's fired/confirmed marker after a compaction, so the next\n * fill cycle gets its own handover instead of being judged against\n * thresholds this session already used up. Deliberately touches ONLY the\n * marker file (`triggerStatePath`) — never the handover cache\n * (`context-handover-cache.ts`), which is what actually gets injected at\n * the compaction boundary and must survive until that injection reads it.\n *\n * WHY THIS EXISTS (do not \"simplify\" it away): without this reset,\n * `confirmed` was permanent for the lifetime of a session id — nothing\n * anywhere ever cleared it. A session observed live on 2026-09-17\n * compacted once, both thresholds got confirmed, and every check for the\n * rest of that session's life then hit the `confirmed` short-circuit at\n * the top of `checkAndEnqueueContextHandover` and returned immediately —\n * even after a SECOND compaction, ~15 hours and a full context refill\n * later, whose injected \"handover\" was still the first one, generated\n * long before, and which said in its own text that nothing had changed\n * since it. A long-lived session compacts more than once; each compaction\n * starts a new fill cycle and deserves its own handover attempt.\n *\n * Call this exactly once per compaction, from the post-compact SessionStart\n * hook that already runs at that moment for other cleanup — see\n * post-compact-inject.ts.\n *\n * If a `pending` enqueue was outstanding at reset time, it is simply\n * dropped along with the rest of the marker — the in-flight daemon job\n * itself is NOT cancelled and will still write its cache entry when it\n * finishes (harmless: cache files are read by generation time, not by\n * whether a marker was watching for them). Because the marker is gone,\n * the next check has no pending entry to resolve, so it does not confirm\n * against that stray cache — it simply runs a fresh crossing check. So\n * this cannot double-enqueue: the only thing that ever calls `enqueue`\n * again is a genuinely fresh threshold crossing after the reset, of which\n * there is exactly one code path.\n *\n * Never throws: a session with no marker file (fresh session, or one\n * already reset) is a plain no-op, not an error.\n */\nexport function resetHandoverTriggerState(sessionId: string): void {\n  const path = triggerStatePath(sessionId);\n  try {\n    if (existsSync(path)) unlinkSync(path);\n  } catch {\n    // Best-effort — see saveTriggerState above. Worst case on failure: the\n    // next compaction re-serves one stale handover, exactly the bug this\n    // function fixes, not a corrupted or crashed session.\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Enqueue\n// ---------------------------------------------------------------------------\n\nexport interface HandoverTriggerInput {\n  sessionId: string;\n  cwd: string;\n  transcriptPath?: string;\n}\n\nasync function enqueueContextHandover(payload: {\n  sessionId: string;\n  cwd: string;\n  transcriptPath?: string;\n  threshold: ThresholdName;\n  urgent: boolean;\n}): Promise<void> {\n  const client = new PaiClient();\n  await client.call(\n    \"work_queue_enqueue\",\n    {\n      type: \"context-handover\",\n      // Below session-end (2), above session-summary (4) — this is rarer\n      // than session-summary (at most twice per session) and time-sensitive\n      // (it exists specifically to beat the compaction it is racing).\n      priority: 3,\n      payload: {\n        sessionId: payload.sessionId,\n        cwd: payload.cwd,\n        transcriptPath: payload.transcriptPath,\n        threshold: payload.threshold,\n        urgent: payload.urgent,\n      },\n    },\n    ENQUEUE_TIMEOUT_MS\n  );\n}\n\n// ---------------------------------------------------------------------------\n// Dependency injection — for testing without a real daemon or filesystem\n// ---------------------------------------------------------------------------\n\nexport interface HandoverTriggerDeps {\n  getReading: (input: HandoverTriggerInput) => ContextFillReading;\n  loadState: (sessionId: string) => HandoverTriggerState;\n  saveState: (sessionId: string, state: HandoverTriggerState) => void;\n  readCache: (sessionId: string) => ContextHandoverCache | null;\n  enqueue: typeof enqueueContextHandover;\n  now: () => number;\n}\n\nconst defaultDeps: HandoverTriggerDeps = {\n  getReading: (input) =>\n    getContextFill({ sessionId: input.sessionId, transcriptPath: input.transcriptPath }),\n  loadState: loadTriggerState,\n  saveState: saveTriggerState,\n  readCache: readContextHandoverCache,\n  enqueue: enqueueContextHandover,\n  now: () => Date.now(),\n};\n\n// ---------------------------------------------------------------------------\n// Main entry point\n// ---------------------------------------------------------------------------\n\nexport interface HandoverTriggerResult {\n  /** Newly enqueued this check — an attempt, not yet a confirmed outcome. */\n  attempted: ThresholdName[];\n  /** Newly CONFIRMED this check — a handover cache was actually verified. */\n  confirmed: ThresholdName[];\n}\n\nconst NOTHING: HandoverTriggerResult = { attempted: [], confirmed: [] };\n\n/**\n * Check this session's current context fill against the derived thresholds,\n * confirm or retry any in-flight attempt, and enqueue a context-handover job\n * for any newly-crossed threshold. Never marks a threshold done on the\n * strength of the enqueue call alone — only a verified cache file does that.\n *\n * A single check that crosses both warmup and refresh in one jump (a\n * session first observed already close to compaction) enqueues ONE job,\n * tagged with the more urgent of the two, and — once confirmed — marks both\n * done together, since one model-written handover already covers what\n * either alone would have.\n */\nexport async function checkAndEnqueueContextHandover(\n  input: HandoverTriggerInput,\n  deps: HandoverTriggerDeps = defaultDeps\n): Promise<HandoverTriggerResult> {\n  // A disposable worker session gets no handover: nobody resumes it, and its\n  // model may not even be the platform's (a different window, a different\n  // provider's bill). Two signals, either suffices: the launcher's env mark,\n  // or a transcript whose last assistant turn came from a non-Claude model.\n  if (isWorkerSession()) {\n    console.error(`[context-handover-trigger] session ${input.sessionId}: worker session (PAI_WORKER=1) — no handover.`);\n    return NOTHING;\n  }\n  // Native is Claude plus the registry's provider models: a primary session\n  // on a non-Anthropic provider still gets handovers; only transcripts from\n  // models this machine does not run are skipped.\n  if (\n    input.transcriptPath &&\n    isForeignModelFamily(transcriptModelFamily(input.transcriptPath))\n  ) {\n    console.error(`[context-handover-trigger] session ${input.sessionId}: transcript written by a model this machine does not run — no handover.`);\n    return NOTHING;\n  }\n\n  let state = deps.loadState(input.sessionId);\n  if (ALL_THRESHOLDS.every((t) => state.confirmed.includes(t))) {\n    // \"Confirmed\" must not mean \"done for the rest of the fill cycle\": a\n    // cache written hours before compaction (observed 2026-10-12: one from\n    // 17h earlier was injected) misses everything since. Re-arm once the\n    // confirmed cache is older than REARM_AGE_MS and fill is still at or\n    // above the refresh threshold.\n    const cache = deps.readCache(input.sessionId);\n    if (cache && deps.now() - Date.parse(cache.generatedAt) < REARM_AGE_MS) return NOTHING;\n    const reading = deps.getReading(input);\n    if (reading.status !== \"ok\" || reading.usedTokens === null) return NOTHING;\n    const t = contextFillThresholds(reading, process.env, { cwd: input.cwd, sampleCacheDir: tmpdir() });\n    if (reading.usedTokens < t.refreshTokens) return NOTHING;\n    state = { confirmed: [], pending: null };\n    deps.saveState(input.sessionId, state);\n  }\n\n  // Thresholds confirmed DURING this check (from the pending-resolution step\n  // below) — carried through every return path so a confirm that falls\n  // through to a fresh crossing-check doesn't get silently dropped.\n  let confirmedThisCheck: ThresholdName[] = [];\n\n  // -------------------------------------------------------------------\n  // Resolve any in-flight attempt first — confirm, keep waiting, or\n  // time it out and clear it for retry. This is the outcome check: a\n  // handover cache generated AFTER the enqueue is proof the worker ran\n  // and actually wrote something, not just that the enqueue call itself\n  // succeeded.\n  // -------------------------------------------------------------------\n  if (state.pending) {\n    const cache = deps.readCache(input.sessionId);\n    const cacheIsFromThisAttempt =\n      cache !== null && Date.parse(cache.generatedAt) > Date.parse(state.pending.enqueuedAt);\n\n    if (cacheIsFromThisAttempt) {\n      const newlyConfirmed = state.pending.thresholds.filter((t) => !state.confirmed.includes(t));\n      state = { confirmed: [...state.confirmed, ...newlyConfirmed], pending: null };\n      deps.saveState(input.sessionId, state);\n      confirmedThisCheck = newlyConfirmed;\n      console.error(\n        `[context-handover-trigger] session ${input.sessionId}: confirmed [${newlyConfirmed.join(\", \")}] ` +\n        `— handover cache verified (generated ${cache!.generatedAt}).`\n      );\n      if (ALL_THRESHOLDS.every((t) => state.confirmed.includes(t))) {\n        return { attempted: [], confirmed: confirmedThisCheck };\n      }\n      // Fall through — a session that jumped straight to refresh-confirmed\n      // territory may already have crossed further; keep checking below.\n    } else if (deps.now() - Date.parse(state.pending.enqueuedAt) < PENDING_TIMEOUT_MS) {\n      // Still within the window — no news is not yet failure.\n      return NOTHING;\n    } else {\n      console.error(\n        `[context-handover-trigger] session ${input.sessionId}: pending [${state.pending.thresholds.join(\", \")}] ` +\n        `timed out after ${PENDING_TIMEOUT_MS}ms with no handover cache written — ` +\n        `treating as FAILED and retrying (daemon worker likely died or was restarted mid-run).`\n      );\n      state = { ...state, pending: null };\n      deps.saveState(input.sessionId, state);\n    }\n  }\n\n  const reading = deps.getReading(input);\n  if (reading.status !== \"ok\" || reading.usedTokens === null) {\n    return { attempted: [], confirmed: confirmedThisCheck };\n  }\n\n  const thresholds = contextFillThresholds(reading, process.env, { cwd: input.cwd, sampleCacheDir: tmpdir() });\n  if (!thresholds.windowConfirmed) {\n    console.error(\n      `[context-handover-trigger] session ${input.sessionId}: window size not confirmed — ` +\n      `using the assumed default (${reading.windowSize} tokens) rather than a reading Claude Code reported.`\n    );\n  }\n\n  const newlyCrossed = crossedThresholds(reading.usedTokens, thresholds, state.confirmed);\n  if (newlyCrossed.length === 0) return { attempted: [], confirmed: confirmedThisCheck };\n\n  const urgent = isImmediate(reading.usedTokens, thresholds);\n  const mostUrgent = newlyCrossed[newlyCrossed.length - 1];\n\n  try {\n    await deps.enqueue({\n      sessionId: input.sessionId,\n      cwd: input.cwd,\n      transcriptPath: input.transcriptPath,\n      threshold: mostUrgent,\n      urgent,\n    });\n  } catch (err) {\n    console.error(\n      `[context-handover-trigger] session ${input.sessionId}: enqueue FAILED — will retry next check: ${err}`\n    );\n    // Nothing persisted — next check sees no pending and tries again.\n    return { attempted: [], confirmed: confirmedThisCheck };\n  }\n\n  state = { ...state, pending: { thresholds: newlyCrossed, enqueuedAt: new Date(deps.now()).toISOString() } };\n  deps.saveState(input.sessionId, state);\n  console.error(\n    `[context-handover-trigger] session ${input.sessionId}: enqueued [${newlyCrossed.join(\", \")}] ` +\n    `at ${reading.usedTokens} tokens (trigger=${thresholds.effectiveTriggerTokens}, ` +\n    `triggerSource=${thresholds.triggerSource}, autocompactPct=${thresholds.autocompactPct}, urgent=${urgent}) ` +\n    `— awaiting outcome confirmation before marking done.`\n  );\n\n  return { attempted: newlyCrossed, confirmed: confirmedThisCheck };\n}\n","/**\n * pai pause [--body-file <path>] [--session-id <uuid>] [--no-body] [--dry-run]\n *\n * Writes a \"## Continue\" checkpoint to the project's TODO.md and appends the\n * same checkpoint to the current session note, then prints a prominent\n * reminder about HOW to exit Claude Code safely.\n *\n * THE BODY IS THE POINT\n * ---------------------\n * This command used to emit a fixed four-line block: session name, timestamp,\n * working directory. That is metadata, not a checkpoint — it tells the next\n * session nothing about what was decided, what is blocked, or what to do next.\n * The actual checkpoint was written by the model and printed to the terminal,\n * where it died. Recovering it required the user to copy it off the screen by\n * hand.\n *\n * So `--body-file` is now mandatory. Omitting it is an error rather than a\n * silent downgrade, because a silent downgrade is precisely the failure this\n * fixes: everything looked like it worked, and the state was gone.\n * `--no-body` remains available for deliberate metadata-only use.\n *\n * WHY Ctrl+C is dangerous:\n *   Claude Code's stop-hook fires only on a clean exit (e.g. /exit inside the\n *   Claude prompt, or claude process receiving SIGTERM). When you Ctrl+C from\n *   a terminal that's running claude, the stop-hook is bypassed, which means:\n *     - The top-level <project>/<uuid>.jsonl does NOT get its final system snapshot.\n *     - Claude Code's --resume cannot find the session (0 system lines).\n *     - The session becomes orphaned — transcript exists but is unresumable.\n */\n\nimport { basename } from \"node:path\";\nimport { realpathSync } from \"node:fs\";\nimport chalk from \"chalk\";\nimport type { RegistryBackend } from \"../../../storage/registry-interface.js\";\nimport type { ProjectRow, SessionRow } from \"./types.js\";\nimport {\n  applyContinue,\n  appendCheckpointToNote,\n  findNotesDir,\n  findLatestNote,\n  readBodyFile,\n} from \"../../../session/checkpoint-block.js\";\n\nexport interface PauseOptions {\n  dryRun?: boolean;\n  /** Path to the model-authored checkpoint markdown, or \"-\" for stdin. */\n  bodyFile?: string;\n  /** Claude Code session UUID — becomes the `claude --resume` handle. */\n  sessionId?: string;\n  /** Deliberately write a metadata-only checkpoint. */\n  noBody?: boolean;\n}\n\n/** Format a session row as \"0003 - 2026-08-01 - Title\". */\nexport function formatSessionLine(session: SessionRow | undefined): string {\n  if (!session) return \"Unknown session\";\n  const num = String(session.number).padStart(4, \"0\");\n  const titlePart = session.title || session.slug || \"Session\";\n  return `${num} - ${session.date} - ${titlePart}`;\n}\n\n/**\n * Resolve the string that identifies a session in a ## Continue block.\n *\n * THIS IS THE PRESERVATION KEY. It decides whether an authored checkpoint\n * belongs to the current session and must be left alone, so every writer must\n * derive it identically — `pai pause`, `pai session handover`, and the hooks'\n * updateTodoContinue. Deriving it in more than one place is how a checkpoint\n * gets clobbered: one writer says \"0006 - …\" and the other says\n * \"Unknown session\", the keys do not match, and the unattended writer replaces\n * work it should have preserved.\n *\n * The registry row is preferred. When the project has no session rows — which\n * happens whenever one codebase is registered under several paths — it falls\n * back to the session note's filename, which is what the hooks use.\n */\nexport function resolveSessionLine(\n  project: ProjectRow,\n  session: SessionRow | undefined,\n  notePath: string | null\n): string {\n  // The session note filename wins.\n  //\n  // It is what the unattended writers use, and agreement is the entire point —\n  // a mismatch means the hooks replace a checkpoint they should have preserved.\n  //\n  // The registry row is the weaker source: it is resolved with\n  // `ORDER BY number DESC`, which stops meaning \"current\" once a project has\n  // been merged. Numbers from both merged rows then coexist, so the highest\n  // number can be a session from months earlier while the note on disk is\n  // today's. Observed live: the registry offered \"0184 - 2026-02-22\" while the\n  // current note was \"0007 - 2026-08-01\".\n  if (notePath) return basename(notePath).replace(/\\.md$/, \"\");\n  if (session) return formatSessionLine(session);\n  return \"Unknown session\";\n}\n\n/** Locate the latest session note for a project, or null. */\nexport function resolveNotePath(project: ProjectRow): string | null {\n  const notesDir = findNotesDir(project.root_path, project.encoded_dir ?? \"\");\n  return notesDir ? findLatestNote(notesDir) : null;\n}\n\n/**\n * Resolve the project containing cwd, or undefined.\n *\n * Tries the literal path first, then its resolved form. A symlinked path\n * prefix (`~/dev -> Cloud/Development`) makes one directory reachable under\n * two spellings; matching only the literal string means a session started from\n * the other spelling finds nothing, and the scanner then registers a second\n * project for the same directory — which splits session history in half.\n */\nexport async function resolveProjectByCwd(\n  registryBackend: RegistryBackend,\n  cwd: string\n): Promise<ProjectRow | undefined> {\n  const direct = await registryBackend.findProjectByCwdPrefix(cwd);\n  if (direct) return direct;\n\n  let resolved: string;\n  try {\n    resolved = realpathSync(cwd);\n  } catch {\n    return undefined;\n  }\n  if (resolved === cwd) return undefined;\n\n  const viaResolved = await registryBackend.findProjectByCwdPrefix(resolved);\n  return viaResolved ?? undefined;\n}\n\nfunction printMissingBodyError(): void {\n  console.error(\n    \"\\n\" +\n      chalk.bgRed.white.bold(\n        \"  CHECKPOINT BODY MISSING — nothing was written                \"\n      ) +\n      \"\\n\\n\" +\n      chalk.yellow(\n        \"  A checkpoint without a body is just a timestamp. The next session\\n\" +\n          \"  would learn nothing about what was decided or what is blocked.\\n\"\n      ) +\n      \"\\n\" +\n      chalk.white(\"  Write your checkpoint markdown to a file, then re-run:\\n\") +\n      \"\\n\" +\n      chalk.cyan(\"    pai pause --body-file /path/to/checkpoint.md \\\\\\n\") +\n      chalk.cyan(\"              --session-id <claude-session-uuid>\\n\") +\n      \"\\n\" +\n      chalk.dim(\"  Or pipe it:  \") +\n      chalk.cyan(\"cat checkpoint.md | pai pause --body-file -\\n\") +\n      \"\\n\" +\n      chalk.dim(\n        \"  If you genuinely want a metadata-only checkpoint, pass --no-body.\\n\"\n      )\n  );\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport async function cmdPause(opts: PauseOptions): Promise<void> {\n  // ---- 1. Body first — fail before touching anything ----\n  let body = \"\";\n\n  if (opts.bodyFile) {\n    try {\n      body = readBodyFile(opts.bodyFile).trim();\n    } catch (err) {\n      console.error(\n        chalk.red(\"pai pause: \") +\n          `Could not read checkpoint body from ${opts.bodyFile}: ${String(err)}`\n      );\n      process.exitCode = 1;\n      return;\n    }\n    if (!body) {\n      console.error(\n        chalk.red(\"pai pause: \") +\n          `Checkpoint body at ${opts.bodyFile} is empty.`\n      );\n      process.exitCode = 1;\n      return;\n    }\n  } else if (!opts.noBody) {\n    printMissingBodyError();\n    process.exitCode = 1;\n    return;\n  }\n\n  const { getRegistryBackend } = await import(\"../../../storage/factory.js\");\n  const registryBackend = await getRegistryBackend();\n\n  // ---- 2. Resolve project by cwd ----\n  const cwd = process.cwd();\n  const project = await resolveProjectByCwd(registryBackend, cwd);\n\n  if (!project) {\n    console.error(\n      chalk.red(\"pai pause: \") +\n        \"Current directory is not within a registered PAI project.\\n\" +\n        `  cwd: ${cwd}`\n    );\n    process.exitCode = 1;\n    return;\n  }\n\n  // ---- 3. Resolve latest session ----\n  const session = (await registryBackend.getLatestSessionForProject(project.id)) ?? undefined;\n\n  // ---- 3b. Agree with the hooks on how a session is identified ----\n  //\n  // The unattended writers (pre-compact hook, daemon work-queue worker) key a\n  // session by its note filename. This command keys it by the registry row.\n  // That string is the preservation key — it is what decides whether an\n  // authored checkpoint belongs to the current session and must be left alone —\n  // so the two must produce the same value or the hooks will clobber this\n  // command's work.\n  //\n  // They diverge whenever the registry has no session row for the resolved\n  // project, which happens when one codebase is registered under several paths.\n  // Falling back to the note filename makes them agree, and incidentally\n  // replaces a useless \"Unknown session\" label with the real one.\n  const notePath = resolveNotePath(project);\n  const sessionLine = resolveSessionLine(project, session, notePath);\n\n  // A registered project with zero sessions almost always means split identity:\n  // the same codebase registered under several paths (a dev copy and a synced\n  // copy, say), with the session history filed under a different row. The\n  // checkpoint still writes correctly, but \"Unknown session\" in TODO.md is a\n  // symptom worth naming rather than shipping silently.\n  if (!session) {\n    const siblings = await registryBackend.findSiblingProjectsBySlugPattern(\n      project.id,\n      project.slug,\n      3\n    );\n\n    console.log(\n      chalk.yellow(\n        `  Note: project '${project.slug}' has no registered sessions; the ` +\n          `checkpoint is labelled from the session note instead (\"${sessionLine}\").`\n      )\n    );\n    for (const s of siblings) {\n      console.log(\n        chalk.dim(\n          `        '${s.slug}' (${s.root_path}) has ${s.session_count} — likely the same project under another path.`\n        )\n      );\n    }\n  }\n\n  // ---- 4. Write the ## Continue block ----\n  const timestamp = new Date().toISOString();\n\n  // `authored` records WHO initiated the checkpoint, not whether it turned out\n  // to have anything in it. A pause is model-initiated by definition, so a\n  // bodyless one used to mislabel itself \"auto\" and silently forfeit the\n  // preservation rules that exist for exactly this kind of write. Substance is\n  // a separate question and is now asked separately (`hasSubstance`); an empty\n  // model block is still boilerplate-only, so it cannot freeze TODO.md — the\n  // `isBoilerplateOnly` guards on cases 1b and 2b already see to that.\n  const result = applyContinue({\n    rootPath: project.root_path,\n    authored: \"model\",\n    sessionLine,\n    sessionId: opts.sessionId,\n    cwd,\n    body,\n    timestamp,\n    dryRun: opts.dryRun,\n  });\n\n  if (result.action === \"failed\") {\n    console.error(\n      chalk.red(\"pai pause: \") + `Failed to write TODO.md: ${result.error}`\n    );\n    process.exitCode = 1;\n    return;\n  }\n\n  if (opts.dryRun) {\n    console.log(\n      \"\\n\" + chalk.bold(\"Dry run — would write to:\") + \" \" + chalk.cyan(result.path ?? \"?\")\n    );\n    console.log(chalk.dim(\"─\".repeat(60)));\n    console.log(chalk.dim(result.block));\n    console.log(chalk.dim(\"─\".repeat(60)));\n  } else {\n    console.log(\n      chalk.green(\"  ## Continue written to: \") + chalk.cyan(result.path ?? \"?\")\n    );\n  }\n\n  // ---- 5. Mirror the body into the session note ----\n  //\n  // TODO.md's ## Continue is a single slot that the next checkpoint overwrites.\n  // The session note is the durable record, so the body goes to both.\n  if (body) {\n    if (!notePath) {\n      console.log(\n        chalk.yellow(\n          \"  (No session note found — checkpoint saved to TODO.md only.)\"\n        )\n      );\n    } else if (opts.dryRun) {\n      console.log(\n        chalk.dim(`  Would append checkpoint to: ${basename(notePath)}`)\n      );\n    } else {\n      const { appended, error } = appendCheckpointToNote(\n        notePath,\n        body,\n        timestamp\n      );\n      if (appended) {\n        console.log(\n          chalk.green(\"  Checkpoint appended to note: \") +\n            chalk.cyan(basename(notePath))\n        );\n      } else if (error) {\n        console.log(\n          chalk.yellow(`  (Could not append to session note: ${error})`)\n        );\n      }\n    }\n  }\n\n  // ---- 6. Print the safety warning ----\n  const box = [\n    \"\",\n    chalk.bgRed.white.bold(\n      \"  IMPORTANT: How to exit safely                               \"\n    ),\n    \"\",\n    chalk.yellow(\n      \"  Inside the Claude Code session, type:  \" +\n        chalk.white.bold(\"/exit\") +\n        chalk.yellow(\"  (then press Enter)\")\n    ),\n    \"\",\n    chalk.red.bold(\"  DO NOT press Ctrl+C.\"),\n    \"\",\n    chalk.dim(\"  Ctrl+C bypasses PAI's stop-hook, which means:\"),\n    chalk.dim(\"    - No project snapshot is written to the top-level jsonl\"),\n    chalk.dim(\"    - The session becomes orphaned (cannot --resume next time)\"),\n    chalk.dim(\n      \"    - pai session goto will launch a fresh session instead of resuming\"\n    ),\n    \"\",\n    chalk.dim(\"  The ## Continue checkpoint in TODO.md is already saved.\"),\n    chalk.dim(\"  Use /exit to preserve full session resumability.\"),\n    \"\",\n  ].join(\"\\n\");\n\n  console.log(box);\n}\n","/**\n * pai session autosave [--session-id <uuid>] [--min-gap <seconds>] [--dry-run]\n *\n * The rolling floor. Runs unattended from live hooks during a session, so an\n * interrupted session still leaves a usable `## Continue` behind.\n *\n * It writes in \"auto\" mode, which is what makes it safe to run often: a\n * model-authored checkpoint for the same session is preserved untouched, so\n * this can never trade a good handover for a mechanical one. When there is no\n * authored checkpoint — the Ctrl+C case, the crash case, the \"never got round\n * to it\" case — this is what the next session finds instead of nothing.\n *\n * Silent and exit-0 on every path. It is wired to hooks that fire constantly;\n * anything it printed would be noise, and anything it threw would interrupt\n * Claude Code.\n */\n\nimport { join } from \"node:path\";\nimport { existsSync, statSync, writeFileSync } from \"node:fs\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { applyContinue } from \"../../../session/checkpoint-block.js\";\nimport {\n  buildAutosaveBody,\n  findTranscripts,\n  resolveTranscriptDir,\n} from \"../../../session/autosave.js\";\nimport { checkAndEnqueueContextHandover } from \"../../../session/context-handover-trigger.js\";\nimport { resolveSessionLine, resolveNotePath, resolveProjectByCwd } from \"./pause.js\";\nimport type { ProjectRow, SessionRow } from \"./types.js\";\n\nexport interface AutosaveOptions {\n  sessionId?: string;\n  minGap?: string;\n  dryRun?: boolean;\n}\n\n/** Default seconds between autosaves. Long enough to be free, short enough to matter. */\nconst DEFAULT_MIN_GAP = 240;\n\n// ---------------------------------------------------------------------------\n// Rate limiting\n// ---------------------------------------------------------------------------\n\n/**\n * One sentinel per project, shared by every trigger.\n *\n * The command is wired to more than one hook event, and they fire at wildly\n * different rates. Keying the gap on the project rather than the event means\n * \"at most once per N seconds\" holds across all of them, instead of once per\n * N seconds *each*.\n */\nfunction sentinelPath(rootPath: string): string {\n  const key = rootPath.replace(/[^a-zA-Z0-9]/g, \"-\").slice(-80);\n  return join(tmpdir(), `pai-autosave-${key}`);\n}\n\nfunction tooRecent(rootPath: string, minGapSeconds: number): boolean {\n  const p = sentinelPath(rootPath);\n  if (!existsSync(p)) return false;\n  try {\n    return Date.now() - statSync(p).mtimeMs < minGapSeconds * 1000;\n  } catch {\n    return false;\n  }\n}\n\nfunction touchSentinel(rootPath: string): void {\n  try {\n    writeFileSync(sentinelPath(rootPath), String(Date.now()), \"utf8\");\n  } catch {\n    // Best-effort — a missing sentinel only costs an extra write.\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport async function cmdAutosave(opts: AutosaveOptions): Promise<void> {\n  const minGap = parseInt(opts.minGap ?? String(DEFAULT_MIN_GAP), 10);\n  const cwd = process.cwd();\n\n  const { getRegistryBackend } = await import(\"../../../storage/factory.js\");\n  const registryBackend = await getRegistryBackend();\n\n  const project = (await resolveProjectByCwd(registryBackend, cwd)) as ProjectRow | undefined;\n  if (!project) return;\n\n  if (!opts.dryRun && tooRecent(project.root_path, minGap)) return;\n\n  // The transcript lives in the Claude Code project directory, which the\n  // registry already records as encoded_dir. Passing the session id matters:\n  // it selects this session's transcript — both halves of it — rather than\n  // whichever file happens to be newest.\n  // The registry's encoded_dir is a hint, not an answer — it goes stale when a\n  // project moves and silently disables capture. See resolveTranscriptDir.\n  const transcriptPaths = findTranscripts(\n    resolveTranscriptDir(project),\n    opts.sessionId\n  );\n\n  // Threshold-triggered pre-compaction handover — runs on the same cadence\n  // as the rolling checkpoint below (same rate-limit gate above), but is\n  // otherwise independent of it: it must still check even on a tick where\n  // there is no new autosave body to write, because a session can sit idle\n  // on the CLI side while its token count keeps climbing from tool output.\n  // Best-effort and bounded (see context-handover-trigger.ts) — never lets a\n  // daemon hiccup slow this hook down or throw out of it.\n  if (opts.sessionId && !opts.dryRun) {\n    const liveTranscript = transcriptPaths[transcriptPaths.length - 1];\n    try {\n      await checkAndEnqueueContextHandover({\n        sessionId: opts.sessionId,\n        cwd,\n        transcriptPath: liveTranscript,\n      });\n    } catch {\n      // checkAndEnqueueContextHandover already catches its own errors; this\n      // is only a backstop against a bug in that catching.\n    }\n  }\n\n  const body = buildAutosaveBody({ cwd, transcriptPaths });\n  // Nothing worth recording — leave whatever is already there alone.\n  if (!body) return;\n\n  const session = ((await registryBackend.getLatestSessionForProject(project.id)) ??\n    undefined) as SessionRow | undefined;\n\n  const result = applyContinue({\n    rootPath: project.root_path,\n    authored: \"auto\",\n    sessionLine: resolveSessionLine(project, session, resolveNotePath(project)),\n    sessionId: opts.sessionId,\n    cwd,\n    body,\n    dryRun: opts.dryRun,\n  });\n\n  if (opts.dryRun) {\n    console.log(result.block);\n    console.log(`\\n[dry-run] action would be: ${result.action}`);\n    return;\n  }\n\n  touchSentinel(project.root_path);\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) return;\n\n  if (checkpointTooRecent(notesDir, minGapSeconds)) return;\n\n  const notePath = findLatestNoteFile(notesDir);\n  if (!notePath) return;\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    return;\n  }\n\n  touchCheckpointSentinel(notesDir);\n}\n","/**\n * pai sessions clear-names\n *\n * Recovery command: wipes corrupted iTerm2 session name state.\n *\n * Two-step reset:\n *   1. Clear ~/.aibroker/session-names.json (the persistent name store).\n *   2. Clear user.paiName variable from all live iTerm2 sessions.\n *\n * After this runs the user can `/Name` each tab fresh. Nothing is\n * set automatically — the user re-asserts names via the /Name skill.\n */\n\nimport { callAiBroker } from \"../../lib/aibroker-client.js\";\nimport { ok, err, dim, warn } from \"../../lib/utils.js\";\n\nexport async function cmdClearNames(opts: { dryRun?: boolean }): Promise<void> {\n  if (opts.dryRun) {\n    console.log(dim(\"Dry run — would:\"));\n    console.log(dim(\"  1. Call AIBroker clear_session_names  (wipe session-names.json)\"));\n    console.log(dim(\"  2. Call AIBroker clear_pai_names      (wipe user.paiName from all iTerm sessions)\"));\n    return;\n  }\n\n  let anyFailed = false;\n\n  // Step 1: clear the persistent name store\n  process.stdout.write(\"Clearing session-names.json … \");\n  try {\n    const r1 = await callAiBroker(\"clear_session_names\", {}, 10_000) as {\n      cleared?: number;\n    };\n    const n = r1.cleared ?? 0;\n    console.log(ok(`cleared ${n} entr${n === 1 ? \"y\" : \"ies\"}`));\n  } catch (e) {\n    const msg = e instanceof Error ? e.message : String(e);\n    if (msg.includes(\"ENOENT\") || msg.includes(\"ECONNREFUSED\")) {\n      console.log(warn(\"AIBroker not running — session-names.json not cleared\"));\n      anyFailed = true;\n    } else {\n      console.log(err(msg));\n      anyFailed = true;\n    }\n  }\n\n  // Step 2: clear user.paiName from all live iTerm2 sessions\n  process.stdout.write(\"Clearing user.paiName from iTerm2 sessions … \");\n  try {\n    const r2 = await callAiBroker(\"clear_pai_names\", {}, 30_000) as {\n      cleared?: number;\n    };\n    const n = r2.cleared ?? 0;\n    console.log(ok(`cleared ${n} session${n === 1 ? \"\" : \"s\"}`));\n  } catch (e) {\n    const msg = e instanceof Error ? e.message : String(e);\n    if (msg.includes(\"ENOENT\") || msg.includes(\"ECONNREFUSED\")) {\n      console.log(warn(\"AIBroker not running — iTerm2 paiName variables not cleared\"));\n      anyFailed = true;\n    } else {\n      console.log(err(msg));\n      anyFailed = true;\n    }\n  }\n\n  if (anyFailed) {\n    console.log(warn(\"\\nSome steps could not complete. Restart AIBroker and try again.\"));\n    process.exitCode = 1;\n  } else {\n    console.log(ok(\"\\nDone. Use /Name to re-label each tab.\"));\n  }\n}\n","/**\n * pai end [--dry-run]\n *\n * Extends `pai pause` with two additional steps:\n *   1. Marks the current session note **Status: Completed** and adds a\n *      Completed timestamp — mirrors what `finalizeSessionNote()` does in the\n *      hooks lib but without importing the hooks-only module.\n *   2. Prints a slightly more final safe-exit reminder.\n *\n * The TODO.md ## Continue checkpoint is written first (same as `pai pause`),\n * so if the user exits without the session note step succeeding the content\n * checkpoint is still intact.\n */\n\nimport { readFileSync, writeFileSync, renameSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport chalk from \"chalk\";\nimport { cmdPause, type PauseOptions } from \"./pause.js\";\nimport {\n  findNotesDir,\n  findLatestNote,\n} from \"../../../session/checkpoint-block.js\";\n\n// ---------------------------------------------------------------------------\n// Session-note finalization (mirrors finalizeSessionNote from session-notes.ts)\n// ---------------------------------------------------------------------------\n\nfunction finalizeNote(notePath: string): { finalized: boolean; path: string } {\n  const content = readFileSync(notePath, \"utf-8\");\n\n  if (content.includes(\"**Status:** Completed\")) {\n    return { finalized: false, path: notePath }; // already done\n  }\n\n  let updated = content.replace(\"**Status:** In Progress\", \"**Status:** Completed\");\n\n  if (!updated.includes(\"**Completed:**\")) {\n    const completionTime = new Date().toISOString();\n    updated = updated.replace(\n      \"---\\n\\n## Work Done\",\n      `**Completed:** ${completionTime}\\n\\n---\\n\\n## Work Done`\n    );\n  }\n\n  // Write atomically\n  const tmp = `${notePath}.end.tmp`;\n  writeFileSync(tmp, updated, \"utf-8\");\n  renameSync(tmp, notePath);\n\n  return { finalized: true, path: notePath };\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport async function cmdEnd(opts: PauseOptions): Promise<void> {\n  // ---- Step 1: Run pause logic (writes ## Continue, prints warning) ----\n  // We call cmdPause first; it prints the ## Continue block and the initial\n  // safe-exit box. We'll print the end-specific reminder afterwards.\n  await cmdPause(opts);\n\n  // ---- Step 2: Locate the project ----\n  const { getRegistryBackend } = await import(\"../../../storage/factory.js\");\n  const registryBackend = await getRegistryBackend();\n  const cwd = process.cwd();\n  const project = await registryBackend.findProjectByCwdPrefix(cwd);\n\n  if (!project) {\n    // cmdPause already printed an error. Nothing more to do.\n    return;\n  }\n\n  // ---- Step 3: Find notes directory ----\n  const notesDir = findNotesDir(project.root_path, project.encoded_dir ?? \"\");\n\n  if (!notesDir) {\n    console.log(\n      chalk.dim(\"  (No session note found — notes directory does not exist yet.)\")\n    );\n    printEndBox(opts.dryRun);\n    return;\n  }\n\n  // ---- Step 4: Find latest session note ----\n  const notePath = findLatestNote(notesDir);\n\n  if (!notePath) {\n    console.log(chalk.dim(\"  (No session note found in notes directory.)\"));\n    printEndBox(opts.dryRun);\n    return;\n  }\n\n  // ---- Step 5: Mark as completed ----\n  if (opts.dryRun) {\n    console.log(\n      \"\\n\" +\n        chalk.bold(\"Dry run — would finalize session note:\") +\n        \" \" +\n        chalk.cyan(notePath)\n    );\n    console.log(\n      chalk.dim(\n        \"  Would replace: **Status:** In Progress\\n\" +\n          \"            with: **Status:** Completed\"\n      )\n    );\n    console.log(\n      chalk.dim(\"  Would add: **Completed:** <timestamp>\")\n    );\n  } else {\n    const { finalized, path } = finalizeNote(notePath);\n    if (finalized) {\n      console.log(\n        chalk.green(\"  Session note finalized: \") + chalk.cyan(basename(path))\n      );\n    } else {\n      console.log(\n        chalk.dim(\n          `  Session note already marked Completed: ${basename(notePath)}`\n        )\n      );\n    }\n  }\n\n  // ---- Step 6: Print the more-final exit reminder ----\n  printEndBox(opts.dryRun);\n}\n\nfunction printEndBox(dryRun?: boolean): void {\n  const label = dryRun ? \" (dry-run)\" : \"\";\n  const box = [\n    \"\",\n    chalk.bgRed.white.bold(\n      `  SESSION ENDING${label}: How to exit safely                   `\n    ),\n    \"\",\n    chalk.yellow(\n      \"  Inside the Claude Code session, type:  \" +\n        chalk.white.bold(\"/exit\") +\n        chalk.yellow(\"  (then press Enter)\")\n    ),\n    \"\",\n    chalk.red.bold(\"  DO NOT press Ctrl+C.\"),\n    \"\",\n    chalk.dim(\"  Ctrl+C bypasses PAI's stop-hook, which means:\"),\n    chalk.dim(\"    - The session note is NOT written to by the stop-hook\"),\n    chalk.dim(\"    - The session becomes orphaned (cannot --resume next time)\"),\n    chalk.dim(\"    - The final session summary is never generated\"),\n    \"\",\n    chalk.dim(\n      \"  ## Continue checkpoint and session note status are already saved.\"\n    ),\n    chalk.dim(\"  Use /exit to let PAI's stop-hook finalize the session fully.\"),\n    \"\",\n  ].join(\"\\n\");\n\n  console.log(box);\n}\n","/**\n * pai resume <name>  /  pai sessions goto <name>  [--dry-run]\n *\n * Smart session launcher with auto-fallback:\n *\n *   1. Resolve name → (uuid?, projectDir, friendlyName)\n *   2. chdir(projectDir)\n *   3. If uuid is set:\n *        a. Probe: is a transcript for <uuid> on disk under this cwd?\n *                  (see probeResume in lib/launch.ts — the filesystem answers,\n *                   and only an unrecognised layout falls back to spawning claude)\n *        b. If probe succeeds:\n *             exec  claude --resume <uuid> --name \"<friendlyName>\" \"/Name <friendlyName>\"\n *                  — through the provider the transcript ran on, when its model\n *                  matches one (providerResumePlan in lib/launch.ts)\n *        c. Else (probe failed):\n *             print clear stderr line: \"Resume failed. Starting fresh session in same dir.\"\n *             exec  claude --name \"<friendlyName>\" \"/Name <friendlyName>\"\n *   4. Else (no uuid known):\n *        exec  claude --name \"<friendlyName>\" \"/Name <friendlyName>\"\n *\n * Steps 3 and 4 are launchInDir's, shared — this file no longer carries its\n * own copy of the spawn dance.\n *\n * Why both --name AND /Name?\n *   --name <friendlyName>       → sets Claude Code's internal session label\n *   \"/Name <friendlyName>\"      → runs the /Name slash command via AIBroker, which updates\n *                                  iTerm tab title, statusline, and AIBroker session registry.\n *                                  No trailing `go`: the operator types it by hand when the\n *                                  TODO.md handover is wanted\n *\n * The probe is two `existsSync` calls on the happy path. On the fallback path, total latency\n * is the same as a fresh start.\n */\n\nimport { realpathSync } from \"node:fs\";\nimport chalk from \"chalk\";\nimport { err } from \"../../lib/utils.js\";\nimport {\n  scanSessions,\n  resolveSessionByNameOrId,\n  fmtAge,\n  type ScannedSession,\n} from \"../../lib/session-scan.js\";\nimport {\n  launchInDir,\n  launchPrompt,\n  providerResumePlan,\n  resumeArgvText,\n} from \"../../lib/launch.js\";\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport async function cmdGoto(\n  query: string,\n  opts: { dryRun?: boolean; noName?: boolean; noGo?: boolean }\n): Promise<void> {\n  // ---- 1. Resolve name → session ----\n  const allSessions = await scanSessions({ limit: 500, filter: \"named\" });\n\n  let resolved;\n  try {\n    resolved = resolveSessionByNameOrId(allSessions, query);\n  } catch (resolveErr) {\n    let msg = String(resolveErr).replace(/^Error: /, \"\");\n    // Append pai find suggestion for \"not found\" errors\n    if (msg.includes(\"No session found matching\")) {\n      msg += `\\n\\nTip: pai find \"${query}\"  — search prompt history by keywords`;\n    }\n    console.error(err(msg));\n    process.exitCode = 1;\n    return;\n  }\n\n  const { session: matchedSession, friendlyName } = resolved;\n\n  // ---- 2. Find best resumable UUID for this project ----\n  let resumableUuid: string | undefined;\n  let resumableSession: ScannedSession | undefined;\n\n  if (matchedSession.resumable) {\n    resumableUuid = matchedSession.uuid;\n    resumableSession = matchedSession;\n  } else if (matchedSession.encodedDir) {\n    // Find the most-recently-modified resumable session in the same project dir\n    const sameProject = allSessions.filter(\n      (s) => s.encodedDir === matchedSession.encodedDir && s.resumable\n    );\n    sameProject.sort((a, b) => b.mtime - a.mtime);\n    if (sameProject.length > 0) {\n      resumableSession = sameProject[0];\n      resumableUuid = resumableSession.uuid;\n    }\n  }\n\n  // ---- 3. Determine project dir (realpathSync for --resume cwd correctness) ----\n  const rawDir =\n    matchedSession.clcDirectory ??\n    matchedSession.registryRootPath ??\n    matchedSession.decodedPath;\n\n  let projectDir: string;\n  try {\n    projectDir = realpathSync(rawDir);\n  } catch {\n    console.error(\n      err(\n        `session \"${query}\": directory does not exist or cannot be resolved.\\n` +\n          `  Registry says: ${rawDir}\\n` +\n          `  The directory may have moved or been deleted.`\n      )\n    );\n    process.exitCode = 1;\n    return;\n  }\n\n  // ---- 4. Build argv components ----\n  const name = friendlyName ?? query;\n\n  // ---- 5. Dry-run mode ----\n  if (opts.dryRun) {\n    if (resumableUuid) {\n      const plan = providerResumePlan(resumableUuid, projectDir);\n      const argvResume = resumeArgvText(resumableUuid, name, plan);\n      const argvFresh = `claude --name \"${name}\" \"${launchPrompt(name)}\"`;\n      console.log(\n        \"\\n\" + chalk.bold(\"Dry run — would probe then exec (RESUME path):\") + \"\\n\"\n      );\n      console.log(`  cwd:      ${chalk.cyan(projectDir)}`);\n      console.log(`  probe:    claude --resume ${resumableUuid} --print --output-format=json \"_\"`);\n      if (plan) {\n        console.log(`  route:    ${plan.provider} (${plan.model})`);\n      }\n      console.log(`  argv:     ${chalk.white(argvResume)}`);\n      console.log(`  fallback: ${chalk.yellow(argvFresh)}`);\n      if (resumableSession) {\n        console.log(`\\n  uuid:     ${resumableSession.uuid}`);\n        console.log(`  age:      ${fmtAge(resumableSession.mtime)}`);\n        console.log(`  status:   ${resumableSession.sessionStatus}`);\n        console.log(`  sys:      ${resumableSession.topLevelSystemLines} system lines`);\n      }\n    } else {\n      const argvFresh = `claude --name \"${name}\" \"${launchPrompt(name)}\"`;\n      console.log(\n        \"\\n\" + chalk.bold(\"Dry run — would exec (FRESH path, no resumable UUID):\") + \"\\n\"\n      );\n      console.log(`  cwd:   ${chalk.cyan(projectDir)}`);\n      console.log(`  argv:  ${chalk.white(argvFresh)}`);\n    }\n    console.log();\n    return;\n  }\n\n  // ---- 6. Live execution ----\n  // The whole probe → resume → fresh-fallback dance lives in launchInDir and\n  // nowhere else. This file carried a near-verbatim copy of it, which is how\n  // probeResume was once fixed in one copy while `pai resume <name>` kept the\n  // bug. `engine: \"claude\"` keeps goto's fresh paths exactly what they were —\n  // plain claude, no worker routing — while the resume inherits the provider\n  // the transcript actually ran on.\n  launchInDir(projectDir, name, { resumableUuid, engine: \"claude\" });\n}\n","/**\n * Session handover command — writes a ## Continue section to the project's\n * TODO.md. Called from the session-stop and pre-compact hooks.\n *\n * PRESERVATION\n * ------------\n * This runs on every clean exit and before every compaction, unattended. It\n * used to regenerate a generic four-line block and strip whatever ## Continue\n * was already there — which meant a model-authored checkpoint written seconds\n * earlier by `pai pause` was destroyed by /exit.\n *\n * It now writes in \"auto\" mode: an authored checkpoint describing the *same*\n * session is left alone. One from an earlier session is stale and gets\n * replaced, so TODO.md never points at the wrong session.\n *\n * Still exits 0 on every path — this must never interrupt Claude Code.\n */\n\nimport type { RegistryBackend } from \"../../../storage/registry-interface.js\";\nimport type { SessionRow, ProjectRow } from \"./types.js\";\nimport { applyContinue } from \"../../../session/checkpoint-block.js\";\nimport {\n  resolveSessionLine,\n  resolveNotePath,\n  resolveProjectByCwd,\n} from \"./pause.js\";\nimport {\n  findTranscripts,\n  buildAutosaveBody,\n  resolveTranscriptDir,\n} from \"../../../session/autosave.js\";\n\n/**\n * Reconstruct a resumable digest for a session that is stopping.\n *\n * Best-effort by construction: this runs from a hook on every exit, so a failure\n * to read the transcript must degrade to an empty body — which the checkpoint\n * writer now treats as \"do not overwrite what is already there\" — rather than\n * throw and interrupt Claude Code.\n */\nfunction buildStopBody(project: ProjectRow, sessionId?: string): string {\n  try {\n    const transcriptPaths = findTranscripts(resolveTranscriptDir(project), sessionId);\n    return buildAutosaveBody({ cwd: process.cwd(), transcriptPaths });\n  } catch {\n    return \"\";\n  }\n}\n\nexport async function cmdHandover(\n  projectSlug: string | undefined,\n  numberOrLatest: string | undefined,\n  sessionId?: string\n): Promise<void> {\n  const { getRegistryBackend } = await import(\"../../../storage/factory.js\");\n  const registryBackend: RegistryBackend = await getRegistryBackend();\n\n  // ---- 1. Resolve project ----\n  let project: ProjectRow | undefined;\n\n  if (projectSlug) {\n    project = (await registryBackend.getProjectBySlug(projectSlug)) ?? undefined;\n    if (!project) return;\n  } else {\n    // Same resolver as `pai pause` — including the realpath fallback, so a\n    // session started via a symlinked path prefix resolves to the same project\n    // rather than silently finding nothing.\n    const row = await resolveProjectByCwd(registryBackend, process.cwd());\n    if (!row) return;\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 = (await registryBackend.getLatestSessionForProject(project!.id)) ?? undefined;\n  } else {\n    const num = parseInt(nol, 10);\n    if (!isNaN(num)) {\n      session = (await registryBackend.getSessionByNumber(project!.id, num)) ?? undefined;\n    }\n  }\n\n  // ---- 3. Write, preserving any authored checkpoint for this session ----\n  //\n  // The session line must be derived exactly as `pai pause` derives it — it is\n  // the fallback preservation key, and deriving it independently here is what\n  // let this command clobber a checkpoint written seconds earlier.\n  //\n  // The line alone is not enough. By the time this runs, `session-stop.sh` has\n  // already executed `session slug --apply` and `session cleanup --execute`,\n  // both of which rewrite the session note filename the line is derived from.\n  // The UUID is passed through precisely because it survives that.\n  // A body, not just a pointer. This used to write metadata only, on the\n  // assumption that the session note holds the real content — but the note is\n  // written by a different path that can fail or never run at all, and when it\n  // does the checkpoint names a file that does not exist. Reconstructing the\n  // same digest the autosave writes (recent prompts + working tree) costs one\n  // transcript read and means a stop-hook handover always carries something\n  // resumable on its own.\n  applyContinue({\n    rootPath: project!.root_path,\n    authored: \"auto\",\n    sessionLine: resolveSessionLine(project!, session, resolveNotePath(project!)),\n    sessionId,\n    cwd: process.cwd(),\n    body: buildStopBody(project!, sessionId),\n  });\n}\n","/**\n * pai pause all [--exit] [--dry-run]\n *\n * Pause every live Claude Code session that AIBroker knows about.\n *\n * For each live session returned by AIBroker's session_content IPC method:\n *   1. Send \"pause session\" to the iTerm2 pane via send_to_session.\n *   2. Optionally send \"\\n/exit\\n\" after a short delay (--exit flag).\n *   3. Print a summary of what was sent and to which sessions.\n *\n * If AIBroker is not running, prints a clear error and exits.\n * If no live sessions are found, reports \"nothing to pause\".\n */\n\nimport chalk from \"chalk\";\nimport {\n  fetchLiveSessions,\n  sendToSession,\n  type AiBrokerSessionMeta,\n} from \"../../lib/aibroker-client.js\";\nimport { header, dim, ok, err, warn } from \"../../lib/utils.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction sleep(ms: number): Promise<void> {\n  return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction sessionLabel(s: AiBrokerSessionMeta): string {\n  const name = s.paiName ?? s.name;\n  return `${chalk.cyan(s.sessionId.slice(0, 8))} ${chalk.bold(name)}`;\n}\n\n/** Give each session a moment to pick up the prompt before judging it idle. */\nconst SETTLE_MS = 4_000;\n/** How often to re-ask AIBroker which sessions are back at the prompt. */\nconst POLL_MS = 2_000;\n\n/**\n * Wait until each session is back at its prompt, i.e. has finished writing its\n * checkpoint.\n *\n * This used to be a flat 5-second sleep before sending /exit. Authoring a real\n * handover — reading state, composing it, writing the file — takes far longer\n * than five seconds, so the exit routinely arrived mid-write and killed the\n * session while it was saving the very thing the command exists to save.\n *\n * `atPrompt` is the signal that a session is idle again. Sessions still busy\n * when the deadline passes are returned as stragglers and deliberately NOT\n * exited: leaving a session open costs nothing, and killing one mid-write costs\n * the checkpoint.\n */\nasync function waitForSessionsIdle(\n  sessionIds: string[],\n  timeoutMs: number\n): Promise<{ idle: Set<string>; stragglers: Set<string> }> {\n  const pending = new Set(sessionIds);\n  const idle = new Set<string>();\n\n  await sleep(Math.min(SETTLE_MS, timeoutMs));\n\n  const deadline = Date.now() + timeoutMs;\n  while (pending.size > 0 && Date.now() < deadline) {\n    let current: AiBrokerSessionMeta[];\n    try {\n      current = await fetchLiveSessions();\n    } catch {\n      // AIBroker blipped — try again on the next tick rather than assuming\n      // anything about sessions we can no longer see.\n      await sleep(POLL_MS);\n      continue;\n    }\n\n    for (const s of current) {\n      if (pending.has(s.sessionId) && s.atPrompt) {\n        pending.delete(s.sessionId);\n        idle.add(s.sessionId);\n      }\n    }\n\n    // A session that vanished from the list has already gone; stop waiting.\n    const visible = new Set(current.map((s) => s.sessionId));\n    for (const id of [...pending]) {\n      if (!visible.has(id)) pending.delete(id);\n    }\n\n    if (pending.size > 0) await sleep(POLL_MS);\n  }\n\n  return { idle, stragglers: pending };\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\n/** Case-insensitive substring match on the session's name or id. */\nexport function matchesOnly(s: AiBrokerSessionMeta, only: string): boolean {\n  const needle = only.trim().toLowerCase();\n  if (!needle) return true;\n  const name = (s.paiName ?? s.name ?? \"\").toLowerCase();\n  return name.includes(needle) || s.sessionId.toLowerCase().includes(needle);\n}\n\nexport async function cmdPauseAll(opts: {\n  exit?: boolean;\n  dryRun?: boolean;\n  only?: string;\n  wait?: number;\n}): Promise<void> {\n  // Upper bound on how long to wait for a session to finish its checkpoint —\n  // not a fixed delay. Generous, because the cost of waiting is nothing and the\n  // cost of exiting too early is the checkpoint.\n  const waitMs = opts.wait ?? 180_000;\n\n  // ── Fetch live sessions ────────────────────────────────────────────────────\n  let liveSessions: AiBrokerSessionMeta[];\n  try {\n    liveSessions = await fetchLiveSessions();\n  } catch (e) {\n    console.error(err(\"AIBroker is not running. Cannot list live sessions.\"));\n    console.error(dim(\"  Start AIBroker or run `pai pause` from each session manually.\"));\n    process.exitCode = 1;\n    return;\n  }\n\n  // ── Filter: Claude sessions only (skip bare shells) ───────────────────────\n  const allClaude = liveSessions.filter((s) => s.kind === \"claude\");\n  const claudeSessions = opts.only\n    ? allClaude.filter((s) => matchesOnly(s, opts.only!))\n    : allClaude;\n  const skipped = liveSessions.length - allClaude.length;\n\n  if (opts.only) {\n    // Said out loud, because a filter that silently matches nothing looks\n    // identical to a filter that matched and did its job quietly.\n    console.log(\n      dim(\n        `  --only ${opts.only}: ${claudeSessions.length} of ${allClaude.length} ` +\n          `Claude session(s) matched.`\n      )\n    );\n    if (claudeSessions.length === 0) {\n      console.log(warn(`No live Claude session matches \"${opts.only}\". Nothing to pause.`));\n      return;\n    }\n  }\n\n  if (skipped > 0) {\n    process.stderr.write(\n      `Skipping ${skipped} non-Claude tab${skipped === 1 ? \"\" : \"s\"} (bare shells).\\n`\n    );\n  }\n\n  if (claudeSessions.length === 0) {\n    console.log(warn(\"No live Claude sessions found via AIBroker. Nothing to pause.\"));\n    return;\n  }\n\n  // ── Dry-run ───────────────────────────────────────────────────────────────\n  if (opts.dryRun) {\n    console.log(\"\\n\" + header(\"Dry Run — Would Pause These Sessions\") + \"\\n\");\n    for (const s of claudeSessions) {\n      console.log(\"  \" + sessionLabel(s));\n      console.log(dim('    → send: \"pause session\"'));\n      if (opts.exit) {\n        console.log(dim(`    → wait ${waitMs}ms then send: \"/exit\"`));\n      }\n    }\n    console.log();\n    return;\n  }\n\n  // ── Live run ──────────────────────────────────────────────────────────────\n  console.log(\n    \"\\n\" +\n      header(\"Pausing All Live Sessions\") +\n      \"\\n\" +\n      dim(`  ${claudeSessions.length} Claude session(s) via AIBroker`) +\n      \"\\n\"\n  );\n\n  const results: Array<{\n    session: AiBrokerSessionMeta;\n    pauseOk: boolean;\n    unconfirmed?: boolean;\n    exitOk?: boolean;\n    error?: string;\n  }> = [];\n\n  for (const s of claudeSessions) {\n    process.stdout.write(\"  \" + sessionLabel(s) + \" … \");\n\n    // Send \"pause session\" command\n    // No trailing newline: the transport appends Enter itself.\n    const pauseResult = await sendToSession(s.sessionId, \"pause session\");\n\n    // Three outcomes, not two. A timeout means the message reached the target's\n    // mailbox — the handler deposits before it waits for the submit ack — so the\n    // pause is under way and only the confirmation is missing.\n    //\n    // Printing that as FAILED is worse than useless: the obvious response is to\n    // run the command again, and a second \"pause session\" into a session that\n    // already paused nests a fresh \"carried forward\" block inside the one it\n    // just wrote. Measured across 15 sessions: 9 reported failed, 8 verifiably\n    // paused, and one refused the duplicate specifically to protect its own\n    // checkpoint.\n    if (pauseResult.timedOut) {\n      console.log(warn(\"sent — checkpoint still running (not confirmed)\"));\n      results.push({ session: s, pauseOk: true, unconfirmed: true });\n      continue;\n    }\n\n    if (!pauseResult.ok) {\n      console.log(err(\"FAILED: \" + (pauseResult.error ?? \"unknown error\")));\n      results.push({ session: s, pauseOk: false, error: pauseResult.error });\n      continue;\n    }\n    console.log(ok(\"paused\"));\n    results.push({ session: s, pauseOk: true });\n  }\n\n  // ── Optional /exit after waiting ──────────────────────────────────────────\n  if (opts.exit) {\n    const pausedSessions = results.filter((r) => r.pauseOk).map((r) => r.session);\n    if (pausedSessions.length > 0) {\n      console.log(\n        \"\\n\" +\n          dim(\n            `  Waiting for sessions to finish writing their checkpoints ` +\n              `(up to ${Math.round(waitMs / 1000)}s)…`\n          )\n      );\n\n      const { stragglers } = await waitForSessionsIdle(\n        pausedSessions.map((s) => s.sessionId),\n        waitMs\n      );\n\n      console.log();\n      for (const s of pausedSessions) {\n        // Never exit a session that is still working — it is most likely still\n        // writing the checkpoint, and /exit would destroy it mid-write.\n        if (stragglers.has(s.sessionId)) {\n          console.log(\n            \"  \" +\n              sessionLabel(s) +\n              \" \" +\n              warn(\"still busy — left open, not exited\")\n          );\n          continue;\n        }\n\n        process.stdout.write(\"  \" + sessionLabel(s) + \" exiting … \");\n        const exitResult = await sendToSession(s.sessionId, \"/exit\");\n        const rec = results.find((r) => r.session.sessionId === s.sessionId)!;\n        rec.exitOk = exitResult.ok;\n        if (!exitResult.ok) {\n          console.log(warn(\"exit failed: \" + (exitResult.error ?? \"unknown\")));\n        } else {\n          console.log(ok(\"exited\"));\n        }\n      }\n\n      if (stragglers.size > 0) {\n        console.log(\n          \"\\n\" +\n            dim(\n              `  ${stragglers.size} session(s) were still working. Leaving a ` +\n                `session open is harmless; exiting one mid-write is not.`\n            )\n        );\n      }\n    }\n  }\n\n  // ── Summary ───────────────────────────────────────────────────────────────\n  const total = results.length;\n  const succeeded = results.filter((r) => r.pauseOk).length;\n  const unconfirmed = results.filter((r) => r.unconfirmed).length;\n  const confirmed = succeeded - unconfirmed;\n  const failed = total - succeeded;\n\n  console.log();\n  if (failed === 0 && unconfirmed === 0) {\n    console.log(ok(`All ${total} session(s) paused successfully.`));\n  } else {\n    console.log(\n      ok(`${confirmed} confirmed`) +\n        (unconfirmed > 0 ? warn(`, ${unconfirmed} still writing`) : \"\") +\n        (failed > 0 ? err(`, ${failed} not delivered`) : \"\") +\n        dim(` — of ${total} session(s).`)\n    );\n    for (const r of results.filter((r) => !r.pauseOk)) {\n      const label = r.session.paiName ?? r.session.name ?? r.session.sessionId.slice(0, 8);\n      console.log(err(`  ${label}: ${r.error ?? \"unknown error\"}`));\n    }\n    if (unconfirmed > 0) {\n      // Said explicitly, because the whole cost of the old wording was that it\n      // read as \"try again\" when trying again is the one harmful move.\n      console.log(\n        dim(\n          `\\n  \"Still writing\" means delivered — the session is composing its\\n` +\n            `  checkpoint, which routinely outlasts the confirmation window.\\n` +\n            `  Do NOT re-run to catch them: a second pause nests a new\\n` +\n            `  \"carried forward\" block inside the one being written. Check with\\n` +\n            `  \\`pai session list\\` in a minute instead.`\n        )\n      );\n    }\n  }\n\n  if (opts.exit) {\n    const exitOk = results.filter((r) => r.exitOk).length;\n    console.log(dim(`  /exit sent to ${exitOk} session(s).`));\n  }\n\n  console.log();\n}\n","/**\n * pai session recent [-n N] [--all] [--json]\n *\n * List named sessions — resumable OR in the clc registry.\n *   Default: \"named\" filter (resumable + stubs + transcript-only from clc registry).\n *   --all: also include unnamed orphan sessions (only in sessions/ subdir, not in registry).\n *\n * Status column (always shown):\n *   resumable       — top-level jsonl with system snapshot (claude --resume works) — green\n *   stub            — in registry, top-level exists but no system lines (Ctrl+C exit) — yellow\n *   transcript-only — in registry but no top-level jsonl — yellow\n *   orphan          — not in registry, only transcript exists — dim (--all only)\n *\n * When AIBroker is running, a \"Live Sessions\" section is shown first,\n * listing currently-active iTerm2 panes that have an active Claude session.\n */\n\nimport chalk from \"chalk\";\nimport { renderTable, err, dim, header } from \"../../lib/utils.js\";\nimport { scanSessions, fmtAge, type SessionStatus } from \"../../lib/session-scan.js\";\nimport { fetchLiveSessions, type AiBrokerSessionMeta } from \"../../lib/aibroker-client.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction fmtSize(bytes: number): string {\n  if (bytes < 1024) return `${bytes}B`;\n  if (bytes < 1_048_576) return `${Math.round(bytes / 1024)}K`;\n  return `${(bytes / 1_048_576).toFixed(1)}M`;\n}\n\nfunction fmtStatus(status: SessionStatus): string {\n  switch (status) {\n    case \"resumable\":\n      return chalk.green(\"resumable\");\n    case \"stub\":\n      return chalk.yellow(\"stub\");\n    case \"transcript-only\":\n      return chalk.yellow(\"transcript-only\");\n    case \"orphan\":\n      return chalk.dim(\"orphan\");\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Live-sessions rendering (AIBroker integration)\n// ---------------------------------------------------------------------------\n\nfunction renderLiveSessions(allLiveSessions: AiBrokerSessionMeta[], allTabs: boolean): void {\n  // Default: Claude sessions only (kind === \"claude\"). --all-tabs includes shells.\n  const displayed = allTabs\n    ? allLiveSessions\n    : allLiveSessions.filter((s) => s.kind !== \"shell\");\n  const skippedCount = allLiveSessions.length - displayed.length;\n\n  if (displayed.length === 0) return;\n\n  console.log(\"\\n\" + header(\"Live Sessions\") + \"\\n\");\n\n  const liveHeaders = [\"#\", \"id\", \"name\", \"at prompt\", \"kind\"];\n  const liveRows = displayed.map((s, idx) => {\n    const shortId = s.sessionId.slice(0, 8);\n    // Prefer paiName for Claude sessions, fall back to iTerm2 tab name\n    const rawName = s.paiName ?? s.name;\n    const name = rawName.length > 36 ? rawName.slice(0, 35) + \"…\" : rawName;\n    const atPrompt = s.atPrompt ? chalk.green(\"yes\") : chalk.yellow(\"busy\");\n    const kind = s.kind === \"claude\"\n      ? chalk.cyan(s.kind)\n      : chalk.dim(s.kind);\n\n    return [\n      chalk.dim(String(idx + 1)),\n      chalk.cyan(shortId),\n      name,\n      atPrompt,\n      kind,\n    ];\n  });\n\n  console.log(renderTable(liveHeaders, liveRows));\n\n  if (skippedCount > 0) {\n    console.log(\n      dim(`  (${skippedCount} shell tab${skippedCount === 1 ? \"\" : \"s\"} hidden — use --all-tabs to show)`)\n    );\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport async function cmdRecent(\n  opts: { n?: string; all?: boolean; allTabs?: boolean; json?: boolean }\n): Promise<void> {\n  const limit = parseInt(opts.n ?? \"20\", 10);\n  const includeAll = opts.all === true;\n  const allTabs = opts.allTabs === true;\n\n  // Fetch live sessions from AIBroker (silently no-ops if not running).\n  const liveSessions = await fetchLiveSessions();\n\n  const sessions = await scanSessions({\n    limit,\n    filter: includeAll ? \"all\" : \"named\",\n  });\n\n  if (opts.json) {\n    const output = {\n      live: liveSessions.map((s) => ({\n        index: s.index,\n        sessionId: s.sessionId,\n        name: s.name,\n        paiName: s.paiName,\n        atPrompt: s.atPrompt,\n        kind: s.kind,\n        active: s.active,\n      })),\n      paused: sessions.map((s, idx) => ({\n        idx: idx + 1,\n        uuid: s.uuid,\n        shortId: s.shortId,\n        resumable: s.resumable,\n        sessionStatus: s.sessionStatus,\n        age: fmtAge(s.mtime),\n        mtime: s.mtime,\n        name: s.friendlyName,\n        lastUserPrompt: s.lastUserPrompt,\n        userLines: s.userLines,\n        msgCount: s.msgCount,\n        topLevelSize: s.topLevelSize,\n        decodedPath: s.decodedPath,\n      })),\n    };\n    console.log(JSON.stringify(output, null, 2));\n    return;\n  }\n\n  // ── Live section ──────────────────────────────────────────────────────────\n  const hasClaudeLive = liveSessions.some((s) => s.kind === \"claude\");\n  if (liveSessions.length > 0) {\n    renderLiveSessions(liveSessions, allTabs);\n  }\n\n  // ── Paused / disk-scan section ────────────────────────────────────────────\n  if (sessions.length === 0) {\n    if (!hasClaudeLive) {\n      // Nothing at all\n      if (includeAll) {\n        console.log(err(\"No sessions found in ~/.claude/projects/.\"));\n      } else {\n        console.log(\n          err(\n            \"No named sessions found.\\n\\n\" +\n              \"  Named sessions appear when you have entries in ~/.claude/session.json\\n\" +\n              \"  (set via /Name inside Claude Code) or resumable top-level jsonl files.\\n\" +\n              \"  Run: pai session recent --all  to list all sessions including unnamed orphans.\"\n          )\n        );\n      }\n    }\n    // If we had live Claude sessions, nothing more to print.\n    return;\n  }\n\n  const title = includeAll\n    ? \"Paused / All Sessions (named + orphans)\"\n    : \"Paused / Resumable Sessions\";\n  console.log(\"\\n\" + header(title) + \"\\n\");\n\n  const headers = [\"#\", \"id\", \"age\", \"name / project\", \"last prompt\", \"msgs\", \"size\", \"status\"];\n\n  const rows = sessions.map((s, idx) => {\n    const name = s.friendlyName ?? s.decodedPath;\n    const prompt = s.lastUserPrompt\n      ? chalk.dim(\n          s.lastUserPrompt.length > 40\n            ? s.lastUserPrompt.slice(0, 39) + \"…\"\n            : s.lastUserPrompt\n        )\n      : dim(\"—\");\n\n    return [\n      chalk.dim(String(idx + 1)),\n      chalk.cyan(s.shortId),\n      dim(fmtAge(s.mtime)),\n      name.length > 26 ? name.slice(0, 25) + \"…\" : name,\n      prompt,\n      s.userLines > 0 ? String(s.userLines) : dim(\"—\"),\n      dim(fmtSize(s.topLevelSize)),\n      fmtStatus(s.sessionStatus),\n    ];\n  });\n\n  console.log(renderTable(headers, rows));\n\n  console.log(\n    \"\\n\" +\n      dim(\"Go to a session:   \") +\n      chalk.white(\"pai resume <name>\") +\n      \"\\n\" +\n      (hasClaudeLive\n        ? dim(\"Pause all live:    \") + chalk.white(\"pai pause all\") + \"\\n\"\n        : \"\") +\n      (includeAll\n        ? \"\"\n        : dim(\"Show all sessions: \") +\n          chalk.white(\"pai sessions --all\") +\n          \"\\n\")\n  );\n}\n","/**\n * pai session restore — put displaced transcripts back where resume looks.\n *\n * PAI moved session transcripts out of `~/.claude/projects/<dir>/` into\n * `<dir>/sessions/`, and `claude --resume <uuid>` reads only the former. The\n * archiver no longer does that (it hardlinks), but the sessions already moved\n * stayed moved — and every checkpoint PAI ever wrote ends with\n * \"Resume with: claude --resume <uuid>\". For a displaced session that line is an\n * instruction that cannot work, and nothing says so.\n *\n * One project measured 1 transcript at the project root against 52 underneath.\n *\n * So this is a repair, and it is deliberately a COMMAND rather than another hook.\n * Silently relinking dozens of a user's files during an unrelated session start is\n * the same class of mistake as silently moving them was. Dry run is the default;\n * `--execute` is the only thing that touches disk.\n *\n * The link logic itself is imported, not rewritten. Today's other bug was a fix\n * that landed in one of three copies of `probeResume`, so `restoreTopLevel` stays\n * the single implementation.\n */\n\nimport { existsSync, readdirSync, statSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { restoreTopLevel, hasConversation } from \"../../lib/launch.js\";\nimport { claudeProjectsDir } from \"../../../registry/moved.js\";\nimport { smartDecodeDir, ok, warn, err, dim, bold } from \"../../lib/utils.js\";\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport interface DisplacedSession {\n  uuid: string;\n  /** The `~/.claude/projects/<encoded>` directory holding it. */\n  projectDir: string;\n  encodedDir: string;\n  /** Decoded working directory, when it can be recovered. */\n  cwd: string | null;\n  bytes: number;\n  mtime: number;\n  /** Files that tell the user to resume this id — the lie being repaired. */\n  promisedBy: string[];\n  /**\n   * Whether the transcript contains an actual exchange.\n   *\n   * False means restoring it changes nothing: `claude --resume` answers\n   * \"No conversation found\" for a file with no conversation in it, wherever it\n   * sits. Measured 2026-08-04 on 046bb712 — 537 bytes of `last-prompt`,\n   * `custom-title`, `agent-name`, `mode`, `permission-mode` and nothing else. It\n   * was restored, correctly hardlinked, and still would not resume.\n   *\n   * Worth reporting rather than hiding, because that id is named in a checkpoint\n   * telling the user to resume it. The honest answer is that the session was\n   * never there, not that the repair failed.\n   */\n  hasConversation: boolean;\n}\n\n/*\n * Stub detection lives in launch.ts, imported above, and this is the second time\n * today that duplicating a helper cost real work — so it stays imported.\n *\n * The version that was here read a bounded 256 KB head and looked only for an\n * assistant marker. AIBroker refuted it with measurements and both causes are\n * confirmed on this machine:\n *\n *   b3462801, 867 KB, a session we had BOTH verified claude --resume accepts:\n *     line 1 is 762,976 bytes — one hook-context attachment blob\n *     first \"type\":\"user\" at byte 766,830\n *   so any head shorter than 766 KB reports a working session as an empty stub.\n *\n *   b8cd4a5d, 2,626 bytes, 3 user lines and ZERO assistant lines: restored and\n *   probed, claude FINDS it. A user-only transcript is resumable, so requiring an\n *   assistant marker is simply the wrong test.\n *\n * Measured over this project's 52 archived transcripts: head+assistant-only said\n * 20 real / 32 stubs, chunked-full-scan + user-OR-assistant says 33 real / 19\n * stubs. Thirteen disagreements, every one of them head=stub / full=real — a 41%\n * false-stub rate, all in the direction that talks the user out of a recovery.\n * The \"745 KB stub\" I reported was one of these.\n *\n * Cost is not the tradeoff it looked like: a full scan short-circuits at the\n * first marker, so a real session stops early, and a genuine stub is small\n * enough to read whole. The head read paid 256 KB on every file including the\n * ones it then got wrong.\n */\n\n/**\n * Transcripts that exist under `sessions/` with no twin at the project root.\n *\n * A twin means nothing needs doing: the archiver hardlinks now, so the normal\n * state is both names pointing at one inode. Only the ones missing upstairs were\n * displaced by the old rename.\n */\nexport function findDisplaced(projectsDir = claudeProjectsDir()): DisplacedSession[] {\n  const out: DisplacedSession[] = [];\n\n  let encodedDirs: string[];\n  try {\n    encodedDirs = readdirSync(projectsDir);\n  } catch {\n    return out; // no projects dir at all — nothing to repair, not an error\n  }\n\n  for (const encodedDir of encodedDirs) {\n    const projectDir = join(projectsDir, encodedDir);\n    const sessionsDir = join(projectDir, \"sessions\");\n    if (!existsSync(sessionsDir)) continue;\n\n    let files: string[];\n    try {\n      files = readdirSync(sessionsDir);\n    } catch {\n      continue;\n    }\n\n    for (const file of files) {\n      if (!file.endsWith(\".jsonl\")) continue;\n      const uuid = file.slice(0, -6);\n      if (!UUID_RE.test(uuid)) continue;\n      if (existsSync(join(projectDir, file))) continue; // already has its twin\n\n      let bytes = 0;\n      let mtime = 0;\n      try {\n        const st = statSync(join(sessionsDir, file));\n        bytes = st.size;\n        mtime = st.mtimeMs;\n      } catch {\n        continue; // vanished under us — nothing to restore\n      }\n\n      out.push({\n        uuid,\n        projectDir,\n        encodedDir,\n        cwd: smartDecodeDir(encodedDir),\n        bytes,\n        mtime,\n        promisedBy: [],\n        hasConversation: hasConversation(join(sessionsDir, file)),\n      });\n    }\n  }\n\n  // Biggest first: transcript size is the best available proxy for how much work\n  // is currently unreachable, and it is what makes the report worth reading.\n  return out.sort((a, b) => b.bytes - a.bytes);\n}\n\n/**\n * Notes that instruct the user to resume this id.\n *\n * Bounded on purpose — the project's own TODO.md and note files, never a search\n * from the home directory. The point is to name the specific promise each\n * restore makes true, so a count of files is not enough.\n */\nexport function findResumePromises(uuid: string, cwd: string | null): string[] {\n  if (!cwd) return [];\n\n  const candidates: string[] = [];\n  for (const notesDir of [cwd, join(cwd, \"Notes\"), join(cwd, \"notes\")]) {\n    try {\n      for (const entry of readdirSync(notesDir)) {\n        if (entry.endsWith(\".md\")) candidates.push(join(notesDir, entry));\n      }\n    } catch {\n      /* not a directory here — try the next */\n    }\n  }\n\n  // Deduplicate by INODE, not by path text and not by realpath.\n  //\n  // macOS is case-insensitive, so `Notes` and `notes` above are the same\n  // directory and every note was reported twice, once per spelling. The obvious\n  // fix — resolve with realpathSync and compare — works under Bun and does NOT\n  // work under Node, whose non-native realpathSync leaves case alone; the tests\n  // caught exactly that split. Identity by (device, inode) is filesystem truth\n  // in either runtime, and it collapses symlinks for free, which matters because\n  // PAI symlinks note directories into the Obsidian vault.\n  const seen = new Set<string>();\n  const hits: string[] = [];\n  for (const file of candidates) {\n    let key: string;\n    try {\n      const st = statSync(file);\n      key = `${st.dev}:${st.ino}`;\n    } catch {\n      key = file.toLowerCase(); // cannot stat — path text is all that is left\n    }\n    if (seen.has(key)) continue;\n    seen.add(key);\n\n    try {\n      if (readFileSync(file, \"utf8\").includes(uuid)) hits.push(file);\n    } catch {\n      /* unreadable — silence is right, this is a report not a gate */\n    }\n  }\n  return hits;\n}\n\nfunction human(bytes: number): string {\n  if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MB`;\n  if (bytes >= 1024) return `${Math.round(bytes / 1024)} KB`;\n  return `${bytes} B`;\n}\n\nexport interface RestoreOptions {\n  execute?: boolean;\n  json?: boolean;\n  /** Only sessions a checkpoint tells the user to resume. */\n  promised?: boolean;\n  /** Only sessions belonging to this working directory. */\n  cwd?: string;\n  /** List every displaced session instead of the largest few. */\n  all?: boolean;\n  /**\n   * Also restore transcripts with no conversation in them.\n   *\n   * Off by default because it is pointless work, and because the raw count\n   * misrepresents the damage: of 2869 displaced transcripts measured 2026-08-04,\n   * 2240 were stubs and only 629 held an actual conversation. 1493 of those stubs\n   * came from one probe tool spawning sessions that were never used. Skipping\n   * them is what turns \"450 MB displaced\" into the honest \"417 MB recoverable\"\n   * without a special case for anybody's tooling.\n   */\n  includeStubs?: boolean;\n}\n\n/** How many to list before the report stops being readable. */\nconst LIST_LIMIT = 20;\n\n/**\n * Which of the displaced sessions a run is about, and which of those to link.\n *\n * Pure, and separate from the printing, for two reasons. The obvious one is that\n * it can be tested without pointing a test at the developer's real\n * `~/.claude/projects` — an earlier draft of the test did exactly that and would\n * have relinked live files. The other is that the report and the action must\n * agree: `--json` originally restored everything in scope while the formatted\n * path skipped stubs, so the two modes did different things with the same flags.\n */\nexport function selectTargets(\n  everything: DisplacedSession[],\n  opts: RestoreOptions\n): { inScope: DisplacedSession[]; toRestore: DisplacedSession[] } {\n  const inScope = everything.filter((d) => {\n    if (opts.promised && d.promisedBy.length === 0) return false;\n    if (opts.cwd && d.cwd !== opts.cwd) return false;\n    return true;\n  });\n\n  const toRestore = opts.includeStubs\n    ? inScope\n    : inScope.filter((d) => d.hasConversation);\n\n  return { inScope, toRestore };\n}\n\nexport function cmdRestore(opts: RestoreOptions = {}): void {\n  const everything = findDisplaced();\n  for (const d of everything) {\n    d.promisedBy = findResumePromises(d.uuid, d.cwd);\n  }\n\n  // Scoping exists because the first real dry run reported 2874 transcripts and\n  // 450 MB, machine-wide — not the ~52 of one project we had been reasoning\n  // about. Restoring is non-destructive and costs no disk (hardlinks), but\n  // relinking 2874 files across every project a user has ever opened is not\n  // something to do because they typed a command once, and an unscoped list of\n  // 2874 lines is not a report anyone can act on.\n  const { inScope: displaced, toRestore } = selectTargets(everything, opts);\n\n  if (opts.json) {\n    console.log(\n      JSON.stringify(\n        {\n          displaced,\n          // Named so a script can see that stubs were held back, rather than\n          // inferring it from a count that does not match.\n          wouldRestore: toRestore.map((d) => d.uuid),\n          executed: Boolean(opts.execute),\n        },\n        null,\n        2\n      )\n    );\n    if (opts.execute) for (const d of toRestore) restoreTopLevel(d.uuid, d.projectDir);\n    return;\n  }\n\n  console.log();\n  console.log(bold(\"  Displaced session transcripts\"));\n  console.log();\n\n  if (displaced.length === 0) {\n    if (everything.length > 0) {\n      console.log(ok(`  Nothing displaced in this scope.`));\n      console.log(dim(`  ${everything.length} displaced elsewhere — drop the filters to see them.`));\n    } else {\n      console.log(ok(\"  Nothing displaced — every archived transcript has its twin at the\"));\n      console.log(dim(\"  project root, which is where claude --resume looks.\"));\n    }\n    console.log();\n    return;\n  }\n\n  const promised = displaced.filter((d) => d.promisedBy.length > 0);\n  const totalBytes = displaced.reduce((n, d) => n + d.bytes, 0);\n\n  console.log(\n    warn(\n      `  ${displaced.length} transcript(s), ${human(totalBytes)}, currently unresumable.`\n    )\n  );\n  if (displaced.length !== everything.length) {\n    console.log(dim(`  (filtered from ${everything.length} machine-wide)`));\n  }\n  if (promised.length > 0) {\n    console.log(\n      err(\n        `  ${promised.length} named in a checkpoint that tells you to resume them — those lines are lies today.`\n      )\n    );\n  }\n  const stubs = displaced.filter((d) => !d.hasConversation);\n  if (stubs.length > 0) {\n    // Restoring these is harmless and pointless. Saying so is the difference\n    // between a report and a sales pitch: `claude --resume` answers\n    // \"No conversation found\" for an empty transcript wherever it sits.\n    console.log(\n      dim(\n        `  ${stubs.length} of them hold no conversation at all — restoring those changes nothing.`\n      )\n    );\n  }\n  console.log();\n\n  // Promised ones first regardless of size: a checkpoint pointing at a dead id is\n  // the concrete harm, and it is what --promised restores on its own.\n  const ordered = [...promised, ...displaced.filter((d) => d.promisedBy.length === 0)];\n  const shown = opts.all ? ordered : ordered.slice(0, LIST_LIMIT);\n\n  for (const d of shown) {\n    const where = d.cwd ?? dim(`(undecodable: ${d.encodedDir})`);\n    const stub = d.hasConversation ? \"\" : warn(\"  [stub — no conversation]\");\n    console.log(\n      `  ${bold(d.uuid.slice(0, 8))}  ${human(d.bytes).padStart(8)}  ${where}${stub}`\n    );\n    for (const file of d.promisedBy) {\n      // This is the line the repair makes true again — unless there is nothing\n      // in the file, in which case say so rather than implying a recovery.\n      console.log(\n        d.hasConversation\n          ? err(`      promised by  ${file}`)\n          : dim(`      promised by  ${file}  (promise cannot be kept: empty transcript)`)\n      );\n    }\n  }\n\n  if (shown.length < ordered.length) {\n    console.log();\n    console.log(\n      dim(`  … and ${ordered.length - shown.length} more. --all lists every one.`)\n    );\n  }\n\n  console.log();\n\n  if (!opts.execute) {\n    console.log(dim(\"  Dry run — nothing was touched.\"));\n    console.log(dim(\"  Restore hardlinks each transcript back to the project root: same inode,\"));\n    console.log(dim(\"  no copy, and the sessions/ archive is left exactly where it is.\"));\n    console.log();\n    console.log(dim(\"    --promised --execute   just the ones a checkpoint promises (start here)\"));\n    console.log(dim(\"    --cwd <path> --execute one project\"));\n    console.log(dim(\"    --execute              everything in scope\"));\n    console.log();\n    return;\n  }\n\n  // Report everything in scope, restore only what restoring can help. A stub is\n  // listed above with its label so the picture stays complete, but linking it\n  // would be work with a guaranteed null result.\n  const target = toRestore;\n  const skipped = displaced.length - target.length;\n\n  let restored = 0;\n  const failed: string[] = [];\n  for (const d of target) {\n    if (restoreTopLevel(d.uuid, d.projectDir)) restored++;\n    else failed.push(d.uuid);\n  }\n\n  console.log(ok(`  Restored ${restored} of ${target.length} recoverable.`));\n  if (skipped > 0) {\n    console.log(\n      dim(`  Skipped ${skipped} stub(s) — nothing in them to resume. --include-stubs overrides.`)\n    );\n  }\n  if (failed.length > 0) {\n    console.log(warn(`  Could not restore ${failed.length}:`));\n    for (const uuid of failed) console.log(dim(`    ${uuid}`));\n  }\n  console.log();\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 {\n  existsSync,\n  readdirSync,\n  readFileSync,\n  statSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type { RegistryBackend } from \"../../../storage/registry-interface.js\";\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 async function getAllProjects(registryBackend: RegistryBackend): Promise<ProjectRow[]> {\n  const projects = await registryBackend.listProjects({ status: \"active\", orderBy: \"slug\" });\n  return projects.map((p) => ({\n    id: p.id,\n    slug: p.slug,\n    display_name: p.display_name,\n    root_path: p.root_path,\n    encoded_dir: p.encoded_dir,\n    claude_notes_dir: p.claude_notes_dir,\n  }));\n}\n\nexport async function getProject(\n  registryBackend: RegistryBackend,\n  slug: string\n): Promise<ProjectRow | undefined> {\n  const p = await registryBackend.getProjectBySlug(slug);\n  if (!p) return undefined;\n  return {\n    id: p.id,\n    slug: p.slug,\n    display_name: p.display_name,\n    root_path: p.root_path,\n    encoded_dir: p.encoded_dir,\n    claude_notes_dir: p.claude_notes_dir,\n  };\n}\n\nasync function getProjectSessions(\n  registryBackend: RegistryBackend,\n  projectId: number\n): Promise<SessionRow[]> {\n  const sessions = await registryBackend.listSessionsForProject(projectId, {\n    orderBy: \"number_asc\",\n  });\n  return sessions 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\n/**\n * Renumbering is disabled. This always returns an empty map.\n *\n * It used to reassign every surviving note to its position in the sorted list\n * (`newNum = idx + 1`), which treats the number as a position. The number is\n * also the note's identity — handovers and other notes cite notes by number —\n * and a value that is both a position and an identity parts company with\n * itself the moment the set changes.\n *\n * What that cost, measured on a real corpus: deleting or adding one note\n * shifted the whole series, and because the number is also written into the H1\n * (`# Session 0006: ...`), the rewrite changed file *contents* as well as\n * names. Git could not pair them as renames — 0 of 262 rewritten notes were\n * byte-identical to anything in HEAD — so a single shift produced 261 deletions\n * plus 262 additions. The operator changed one source file and their prompt\n * reported 262 changes, which hides any real uncommitted work in the noise.\n *\n * Numbers are now minted once, at creation, and never reassigned. Gaps are\n * expected and harmless: a gap is information (a note was removed) and costs\n * nothing, whereas a renumber invalidates every existing reference silently.\n *\n * Kept as a function rather than deleted so the call site and the CleanupPlan\n * shape stay intact; collisions should be reported by the caller, never\n * resolved by shifting.\n */\nfunction buildRenumberMap(\n  _survivors: SessionCandidate[]\n): Map<number, number> {\n  return new Map();\n}\n\nexport async function analyzeProject(\n  registryBackend: RegistryBackend,\n  project: ProjectRow\n): Promise<CleanupPlan | null> {\n  const notesDirPaths = findAllNotesDirs(project);\n  if (notesDirPaths.length === 0) return null;\n\n  const dbSessions = await getProjectSessions(registryBackend, 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 {\n  existsSync,\n  readdirSync,\n  readFileSync,\n  writeFileSync,\n  mkdirSync,\n  renameSync,\n  statSync,\n} from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport chalk from \"chalk\";\nimport { ok, warn, err, dim, bold, header } from \"../../lib/utils.js\";\nimport type { CleanupPlan, SessionCandidate } from \"./types.js\";\nimport { padNum } from \"./rename.js\";\n\n// ---------------------------------------------------------------------------\n// Vector DB (StorageBackend) 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 { createStorageBackend } = await import(\"../../../storage/factory.js\");\n    const config = loadConfig();\n    if (config.storageBackend !== \"postgres\") return 0;\n    const backend = await createStorageBackend(config);\n    try {\n      if (!backend.supportsPostgresFeatures) return 0;\n      return await backend.countFilesWithPaths(oldPaths);\n    } finally {\n      await backend.close();\n    }\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 { createStorageBackend } = await import(\"../../../storage/factory.js\");\n    const config = loadConfig();\n    if (config.storageBackend !== \"postgres\") return 0;\n    const backend = await createStorageBackend(config);\n    try {\n      if (!backend.supportsPostgresFeatures) {\n        process.stderr.write(\"[session-cleanup] Postgres unavailable. Skipping vector DB path update.\\n\");\n        return 0;\n      }\n      return await backend.renameFilePaths(moves);\n    } finally {\n      await backend.close();\n    }\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(\"  ARCHIVE (empty/template-only sessions → .archive/):\"));\n        for (const c of dirPlan.toDelete) {\n          console.log(\n            `    ${chalk.yellow(\"ARCH\")} ${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.yellow(\"ARCH\")} ${totalDelete} empty sessions to archive (moved, not deleted)`);\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  plans: CleanupPlan[],\n  skipReindex: boolean\n): Promise<void> {\n  const { getRegistryBackend } = await import(\"../../../storage/factory.js\");\n  const registryBackend = await getRegistryBackend();\n\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: Retire \"empty\" sessions — by moving them, never by unlinking.\n      //\n      // This deleted four titled notes from Youdrill on 2026-08-03 (\"Cocoapods\n      // Bootstrap\", \"Youdrill Tools Pipeline Review\", and two more), and they\n      // were only recoverable because that session inspected the diff before\n      // committing. Anywhere this ran and was committed, the notes are gone.\n      //\n      // The classifier is the root problem: EMPTY is `sizeBytes < 400 ||\n      // isTemplateOnly`. Size is a proxy for worthlessness and a bad one — a\n      // real session that produced three lines and a good title is under 400\n      // bytes. But no threshold is worth trusting with an irreversible\n      // operation, so the fix is not a better number: it is that a cleanup tool\n      // has no business destroying anything a human might want back.\n      //\n      // Moving to .archive/ still achieves the actual goal (the note leaves the\n      // working listing and the numbering respreads) while keeping the file.\n      for (const c of dirPlan.toDelete) {\n        try {\n          const archiveDir = join(dirname(c.filepath), \".archive\");\n          mkdirSync(archiveDir, { recursive: true });\n          let dest = join(archiveDir, c.filename);\n          // Never clobber an earlier archived note with the same name.\n          if (existsSync(dest)) {\n            const stamp = statSync(c.filepath).mtimeMs.toString(36);\n            dest = join(archiveDir, `${c.filename.replace(/\\.md$/, \"\")} (${stamp}).md`);\n          }\n          renameSync(c.filepath, dest);\n          console.log(ok(`  ARCH ${c.filename}`) + dim(\" → .archive/\"));\n          deleted++;\n        } catch (e) {\n          console.log(err(`  FAIL to archive ${c.filename}: ${e}`));\n        }\n        if (c.session) {\n          try {\n            await registryBackend.deleteSession(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            await registryBackend.updateSessionMeta(c.session.id, {\n              slug: normalizedSlug,\n              title: autoName,\n              filename: newFilename,\n            });\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          try {\n            // Negate first, then set final numbers — same two-pass order as the\n            // old single SQL transaction, so mid-loop UNIQUE(project_id, number)\n            // collisions between swapping sessions are still avoided.\n            for (const { session, newNum } of dbRenumbers) {\n              await registryBackend.updateSessionNumber(session.id, -newNum);\n            }\n            for (const { session, newNum, newFilename } of dbRenumbers) {\n              await registryBackend.updateSessionNumber(session.id, newNum, {\n                filename: newFilename,\n              });\n            }\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            await registryBackend.updateSessionFilename(c.session.id, newFilenameInDb);\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 * `pai session cleanup`: delete empty session notes, name unnamed ones, move\n * them into YYYY/MM/ folders and renumber. Dry run unless `execute` is set.\n */\n\nimport { err, dim, ok, header } from \"../../lib/utils.js\";\nimport { getAllProjects, getProject, analyzeProject } from \"./scanner.js\";\nimport { displayDryRun, executeCleanup } from \"./executor.js\";\n\nexport async function runCleanup(\n  projectSlug: string | undefined,\n  opts: { execute?: boolean; renumber?: boolean; reindex?: boolean }\n): Promise<void> {\n  const { getRegistryBackend } = await import(\"../../../storage/factory.js\");\n  const registryBackend = await getRegistryBackend();\n\n  const dryRun = !opts.execute;\n  const skipReindex = opts.reindex === false;\n\n  let projects;\n  if (projectSlug) {\n    const p = await getProject(registryBackend, projectSlug);\n    if (!p) {\n      console.error(err(`Project not found: ${projectSlug}`));\n      process.exitCode = 1;\n      return;\n    }\n    projects = [p];\n  } else {\n    projects = await getAllProjects(registryBackend);\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 = await analyzeProject(registryBackend, project);\n    if (plan) plans.push(plan);\n  }\n\n  const activePlans = plans.filter(\n    (p) =>\n      p.notesDirs.some((d) => d.toDelete.length > 0 || d.toRename.length > 0 || 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(activePlans, skipReindex);\n  }\n}\n","/**\n * Session notes and checkpoints: `pai session info|tag|route|handover|checkpoint|autosave|active|recent|goto|pause|auto-route|cleanup|restore|slug`\n * and the plural `pai sessions ...` group. The logic lives in ./session/ and ./session-cleanup/;\n * this file declares the commands. Names, flags and output are the old ones (hooks and prompts call them).\n */\n\nimport { OPT, type CommandDef, type OptDef, type RunContext } from \"../define.js\";\nimport { cmdActive, cmdAutoRoute, cmdInfo, cmdRename, cmdRoute, cmdSlug, cmdTag } from \"./session/commands.js\";\nimport { cmdAutosave } from \"./session/autosave.js\";\nimport { cmdCheckpoint } from \"./session/checkpoint.js\";\nimport { cmdClearNames } from \"./session/clear-names.js\";\nimport { cmdEnd } from \"./session/end.js\";\nimport { cmdGoto } from \"./session/goto.js\";\nimport { cmdHandover } from \"./session/handover.js\";\nimport { cmdPause } from \"./session/pause.js\";\nimport { cmdPauseAll } from \"./session/pause-all.js\";\nimport { cmdRecent } from \"./session/recent.js\";\nimport { cmdRestore } from \"./session/restore.js\";\nimport { runCleanup } from \"./session-cleanup/cleanup.js\";\nimport { legacy } from \"../lib/legacy.js\";\nimport { lsCommand } from \"./session.js\";\n\nconst str = (v: unknown): string | undefined => (v === undefined ? undefined : String(v));\n\nconst SESSION_ID: OptDef = {\n  name: \"session-id\",\n  type: \"string\",\n  value: \"UUID\",\n  summary: \"Claude Code session id; recorded as the handle for `claude --resume`\",\n};\nconst BODY_FILE: OptDef = {\n  name: \"body-file\",\n  type: \"string\",\n  value: \"FILE\",\n  summary: \"File holding the checkpoint text to save ('-' reads standard input). Required unless --no-body\",\n};\nconst NO_BODY: OptDef = { name: \"no-body\", type: \"boolean\", default: false, summary: \"Save a checkpoint with metadata only, no text\" };\nconst JSON_RAW: OptDef = { ...OPT.json, summary: \"Print raw JSON instead of a table\" };\nconst COUNT: OptDef = { name: \"n\", short: \"n\", type: \"number\", value: \"COUNT\", default: 20, summary: \"Most sessions to show\" };\nconst ALL_ORPHANS: OptDef = { name: \"all\", type: \"boolean\", default: false, summary: \"Also show unnamed sessions\" };\n\nconst slugArgs = [\n  { name: \"PROJECT\", required: true, summary: \"Project slug, as shown by `pai project list`\" },\n  { name: \"NUMBER\", required: true, summary: \"Session note number (or `latest`)\" },\n];\n\nconst pauseOpts = (c: RunContext) => ({\n  dryRun: c.dryRun,\n  bodyFile: c.opts[\"body-file\"] as string | undefined,\n  sessionId: c.opts[\"session-id\"] as string | undefined,\n  noBody: c.opts[\"no-body\"] === true,\n});\n\nexport const infoCmd: CommandDef = {\n  name: \"info\",\n  summary: \"Show everything known about one session note\",\n  description: \"Prints title, date, status, tags, file and linked sessions of one session note.\",\n  args: slugArgs,\n  examples: [\"pai session info my-project 12\", \"pai session info my-project latest\"],\n  seeAlso: [\"pai session tag\", \"pai session list\"],\n  run: (c) => legacy(() => cmdInfo(c.args.PROJECT as string, c.args.NUMBER as string)),\n};\n\nconst renameNoteCmd: CommandDef = {\n  name: \"rename\",\n  summary: \"Rename a session note (file, title and registry)\",\n  description: \"Gives a session note a new slug: the file on disk, its first heading and the registry entry all change.\",\n  args: [...slugArgs, { name: \"NEW_SLUG\", required: true, summary: \"New short name for the note\" }],\n  examples: [\"pai sessions rename my-project 12 fix-login-bug\", \"pai sessions rename my-project 3 release-notes\"],\n  seeAlso: [\"pai session slug\"],\n  run: (c) => legacy(() => cmdRename(c.args.PROJECT as string, c.args.NUMBER as string, c.args.NEW_SLUG as string)),\n};\nexport { renameNoteCmd };\n\nexport const slugCmd: CommandDef = {\n  name: \"slug\",\n  summary: \"Suggest a short name for a session note from its transcript\",\n  description: \"Reads the conversation and proposes a descriptive slug. With --apply the note is renamed to it.\",\n  args: slugArgs,\n  options: [{ name: \"apply\", type: \"boolean\", default: false, summary: \"Rename the note to the suggested slug\" }],\n  examples: [\"pai session slug my-project 12\", \"pai session slug my-project latest --apply\"],\n  seeAlso: [\"pai sessions rename\"],\n  run: (c) => legacy(() => cmdSlug(c.args.PROJECT as string, c.args.NUMBER as string, { apply: c.opts.apply === true })),\n};\n\nexport const tagCmd: CommandDef = {\n  name: \"tag\",\n  summary: \"Show or set the tags of a session note\",\n  description: \"Without tags, prints the current ones. Tags can be separated by spaces or commas.\",\n  args: [...slugArgs, { name: \"TAGS\", variadic: true, summary: \"Tags to set\" }],\n  examples: [\"pai session tag my-project 12\", \"pai session tag my-project 12 bugfix,urgent\"],\n  seeAlso: [\"pai session info\"],\n  run: (c) => legacy(() => cmdTag(c.args.PROJECT as string, c.args.NUMBER as string, (c.args.TAGS as string[]) ?? [])),\n};\n\nexport const routeCmd: CommandDef = {\n  name: \"route\",\n  summary: \"Link a session note to another project\",\n  description: \"Adds a cross-reference from the note to a target project, so it shows up there too.\",\n  args: [...slugArgs, { name: \"TARGET\", required: true, summary: \"Slug of the project to link to\" }],\n  options: [{ name: \"type\", type: \"string\", value: \"KIND\", default: \"related\", summary: \"Kind of link: related, follow-up or reference\" }],\n  examples: [\"pai session route my-project 12 other-project\", \"pai session route my-project 12 other-project --type follow-up\"],\n  seeAlso: [\"pai session auto-route\"],\n  run: (c) =>\n    legacy(() => cmdRoute(c.args.PROJECT as string, c.args.NUMBER as string, c.args.TARGET as string, { type: c.opts.type as string })),\n};\n\nexport const handoverCmd: CommandDef = {\n  name: \"handover\",\n  summary: \"Write the \\\"Continue\\\" block of a project's TODO.md\",\n  description:\n    \"Records the last session, the time and the working directory in the project's TODO.md so the next session can pick up where this one stopped.\\n\" +\n    \"The stop and pre-compact hooks run it for you. Pass --session-id whenever it is known: it decides whether an existing checkpoint belongs to this session and must be kept.\",\n  args: [\n    { name: \"PROJECT\", summary: \"Project slug (default: the project of the current directory)\" },\n    { name: \"NUMBER\", summary: \"Session note number or `latest` (default: latest)\" },\n  ],\n  options: [SESSION_ID],\n  examples: [\"pai session handover\", \"pai session handover my-project latest --session-id 3a9f1c2e-0000-4000-8000-000000000000\"],\n  seeAlso: [\"pai pause\", \"pai session autosave\"],\n  run: (c) =>\n    legacy(() => cmdHandover(c.args.PROJECT as string | undefined, c.args.NUMBER as string | undefined, c.opts[\"session-id\"] as string | undefined)),\n};\n\nexport const checkpointCmd: CommandDef = {\n  name: \"checkpoint\",\n  summary: \"Add a timestamped line to the active session note\",\n  description: \"Meant for hooks: quick and silent. It does nothing if the last checkpoint is newer than --min-gap seconds.\",\n  args: [{ name: \"MESSAGE\", required: true, summary: \"Text to record\" }],\n  options: [{ name: \"min-gap\", type: \"number\", value: \"SECONDS\", default: 300, summary: \"Least seconds between two checkpoints\" }],\n  examples: ['pai session checkpoint \"finished the parser\"', 'pai session checkpoint \"tests green\" --min-gap 60'],\n  seeAlso: [\"pai session handover\"],\n  run: (c) => legacy(() => cmdCheckpoint(c.args.MESSAGE as string, { minGap: str(c.opts[\"min-gap\"]) })),\n};\n\nexport const autosaveCmd: CommandDef = {\n  name: \"autosave\",\n  summary: \"Refresh the \\\"Continue\\\" block from the transcript (for hooks)\",\n  description:\n    \"Runs unattended from live hooks so an interrupted session still leaves a usable handover. A checkpoint written by the session itself is never overwritten. Silent, and always exits 0.\\n\" +\n    \"With --dry-run it prints the block it would write.\",\n  options: [\n    SESSION_ID,\n    { name: \"min-gap\", type: \"number\", value: \"SECONDS\", default: 240, summary: \"Least seconds between two autosaves, across all triggers\" },\n  ],\n  examples: [\"pai session autosave --dry-run\", \"pai session autosave --session-id 3a9f1c2e-0000-4000-8000-000000000000 --min-gap 120\"],\n  seeAlso: [\"pai session handover\", \"pai pause\"],\n  run: (c) =>\n    legacy(() =>\n      cmdAutosave({ sessionId: c.opts[\"session-id\"] as string | undefined, minGap: str(c.opts[\"min-gap\"]), dryRun: c.dryRun })\n    ),\n};\n\nexport const activeCmd: CommandDef = {\n  name: \"active\",\n  summary: \"Sessions that were active in the last hour\",\n  description: \"Finds running sessions by looking at which transcripts changed recently.\",\n  options: [{ name: \"minutes\", type: \"number\", value: \"N\", default: 60, summary: \"Count a session as active if it changed within N minutes\" }, JSON_RAW],\n  examples: [\"pai session active\", \"pai session active --minutes 15 --json\"],\n  seeAlso: [\"pai ls\"],\n  run: (c) => legacy(() => cmdActive({ minutes: str(c.opts.minutes), json: c.opts.json === true })),\n};\n\nexport const recentCmd: CommandDef = {\n  name: \"recent\",\n  summary: \"Named sessions with their resume status, live ones first\",\n  description:\n    \"The classic session catalog. Status: resumable = can be reopened · stub / transcript-only = partial history · orphan = unnamed (--all).\\n\" +\n    \"`pai ls` is the friendlier table.\",\n  options: [COUNT, ALL_ORPHANS, { name: \"all-tabs\", type: \"boolean\", default: false, summary: \"Show every terminal tab under Live Sessions, including plain shells\" }, JSON_RAW],\n  examples: [\"pai session recent\", \"pai session recent -n 5 --json\"],\n  seeAlso: [\"pai ls\", \"pai session active\"],\n  run: (c) => legacy(() => cmdRecent({ n: str(c.opts.n), all: c.opts.all === true, allTabs: c.opts[\"all-tabs\"] === true, json: c.opts.json === true })),\n};\n\nexport const gotoCmd: CommandDef = {\n  name: \"goto\",\n  summary: \"Open a session by name or id (same as `pai open`)\",\n  description: \"Resumes the session when it can be resumed, otherwise starts a fresh one in the same directory.\",\n  args: [{ name: \"NAME_OR_ID\", required: true, summary: \"Session name or the first characters of its id\" }],\n  options: [\n    { name: \"skip-name\", type: \"boolean\", default: false, summary: \"Do not send /Name to label the tab\" },\n    { name: \"skip-go\", type: \"boolean\", default: false, summary: \"Ignored, kept for old scripts\" },\n  ],\n  examples: [\"pai session goto my-session --dry-run\", \"pai session goto 3a9f1c2e\"],\n  seeAlso: [\"pai open\", \"pai resume\"],\n  run: (c) =>\n    legacy(async () => {\n      process.stderr.write(\"Note: `pai session goto` works but `pai open` is the shorter form.\\n\");\n      await cmdGoto(c.args.NAME_OR_ID as string, { noName: c.opts[\"skip-name\"] === true, noGo: c.opts[\"skip-go\"] === true, dryRun: c.dryRun });\n    }),\n};\n\nexport const pauseCmd: CommandDef = {\n  name: \"pause\",\n  summary: \"Save a checkpoint before leaving (same as `pai pause`)\",\n  description:\n    \"Writes the \\\"Continue\\\" block of the project's TODO.md. Leave with /exit inside Claude Code; Ctrl+C skips the stop hook and the session can no longer be resumed.\",\n  options: [BODY_FILE, SESSION_ID, NO_BODY],\n  examples: [\"pai session pause --dry-run --no-body\", \"pai session pause --body-file notes.md\"],\n  seeAlso: [\"pai pause\", \"pai end\"],\n  run: (c) =>\n    legacy(async () => {\n      process.stderr.write(\"Note: `pai session pause` works but `pai pause` is the shorter form.\\n\");\n      await cmdPause(pauseOpts(c));\n    }),\n};\n\nexport const autoRouteCmd: CommandDef = {\n  name: \"auto-route\",\n  summary: \"Find out which project the current directory belongs to\",\n  description: \"Tries the registered project paths, then a Notes/PAI.md marker above the directory, then topic detection from --context.\",\n  options: [\n    { name: \"cwd\", type: \"string\", value: \"DIR\", summary: \"Directory to check (default: the current one)\" },\n    { name: \"context\", type: \"string\", value: \"TEXT\", summary: \"Conversation text for the topic-based fallback\" },\n    JSON_RAW,\n  ],\n  examples: [\"pai session auto-route\", \"pai session auto-route --cwd . --json\"],\n  seeAlso: [\"pai session route\"],\n  run: (c) =>\n    legacy(() => cmdAutoRoute({ cwd: c.opts.cwd as string | undefined, context: c.opts.context as string | undefined, json: c.opts.json === true })),\n};\n\nexport const cleanupCmd: CommandDef = {\n  name: \"cleanup\",\n  summary: \"Tidy session notes: drop empty ones, name, file and renumber\",\n  description:\n    \"Deletes empty notes, names unnamed ones, moves them into year/month folders and renumbers. Shows the plan only; nothing changes without --execute.\",\n  args: [{ name: \"PROJECT\", summary: \"Only this project slug (default: all projects)\" }],\n  options: [\n    { name: \"execute\", type: \"boolean\", default: false, summary: \"Really do it (the default is a preview)\" },\n    { name: \"no-renumber\", type: \"boolean\", default: false, summary: \"Keep the old numbers after deleting\" },\n    { name: \"no-reindex\", type: \"boolean\", default: false, summary: \"Do not refresh the memory index afterwards\" },\n  ],\n  examples: [\"pai session cleanup\", \"pai session cleanup my-project --execute\"],\n  seeAlso: [\"pai session restore\"],\n  run: (c) =>\n    legacy(() =>\n      runCleanup(c.args.PROJECT as string | undefined, {\n        execute: c.opts.execute === true,\n        renumber: c.opts[\"no-renumber\"] !== true,\n        reindex: c.opts[\"no-reindex\"] !== true,\n      })\n    ),\n};\n\nexport const restoreCmd: CommandDef = {\n  name: \"restore\",\n  summary: \"Make displaced transcripts resumable again\",\n  description:\n    \"An old archiver moved transcripts out of the folder `claude --resume` reads. This lists what cannot be resumed and which checkpoints promise it. It only previews until --execute is given.\",\n  options: [\n    { name: \"execute\", type: \"boolean\", default: false, summary: \"Really restore (links the files back)\" },\n    { name: \"promised\", type: \"boolean\", default: false, summary: \"Only sessions a checkpoint tells you to resume\" },\n    { name: \"cwd\", type: \"string\", value: \"DIR\", summary: \"Only sessions of this working directory\" },\n    { name: \"all\", type: \"boolean\", default: false, summary: \"List every displaced session, not just the largest\" },\n    { name: \"include-stubs\", type: \"boolean\", default: false, summary: \"Also restore transcripts without any conversation\" },\n    JSON_RAW,\n  ],\n  examples: [\"pai session restore\", \"pai session restore --promised --execute\"],\n  seeAlso: [\"pai session cleanup\"],\n  run: (c) =>\n    legacy(() =>\n      cmdRestore({\n        execute: c.opts.execute === true,\n        promised: c.opts.promised === true,\n        cwd: c.opts.cwd as string | undefined,\n        all: c.opts.all === true,\n        includeStubs: c.opts[\"include-stubs\"] === true,\n        json: c.opts.json === true,\n      })\n    ),\n};\n\n/** Subcommands added to `pai session` (list/open/new/rename are the foundation's). */\nexport const SESSION_NOTE_COMMANDS: CommandDef[] = [\n  infoCmd, slugCmd, tagCmd, routeCmd, handoverCmd, checkpointCmd, autosaveCmd, activeCmd,\n  recentCmd, gotoCmd, pauseCmd, autoRouteCmd, cleanupCmd, restoreCmd,\n];\n\n// ---------------------------------------------------------------------------\n// pai sessions (plural)\n// ---------------------------------------------------------------------------\n\nconst sessionsList: CommandDef = {\n  name: \"list\",\n  summary: \"Named sessions with their resume status\",\n  description: \"Same as `pai session recent`.\",\n  options: [COUNT, ALL_ORPHANS, { name: \"all-tabs\", type: \"boolean\", default: false, summary: \"Show every terminal tab under Live Sessions, including plain shells\" }, JSON_RAW],\n  examples: [\"pai sessions list\", \"pai sessions list -n 5 --json\"],\n  seeAlso: [\"pai ls\"],\n  run: recentCmd.run,\n};\n\nconst sessionsPause: CommandDef = {\n  name: \"pause\",\n  summary: \"Save a checkpoint for the current session\",\n  description: \"Same as `pai pause`.\",\n  options: [{ name: \"dry-run\", type: \"boolean\", summary: \"Preview the block without writing it\" }, BODY_FILE, SESSION_ID, NO_BODY],\n  examples: [\"pai sessions pause --dry-run --no-body\", \"pai sessions pause --body-file notes.md\"],\n  seeAlso: [\"pai pause\"],\n  run: (c) => legacy(() => cmdPause(pauseOpts(c))),\n};\n\nexport const sessionsPauseAll: CommandDef = {\n  name: \"pause-all\",\n  summary: \"Pause every live session (needs AIBroker)\",\n  description: \"Sends 'pause session' to each live terminal pane. Same as `pai pause all`.\",\n  options: [\n    { name: \"exit\", type: \"boolean\", default: false, summary: \"Also send /exit to each session after it has saved\" },\n    { name: \"wait\", type: \"number\", value: \"MS\", default: 5000, summary: \"Milliseconds to wait before /exit\" },\n    { name: \"only\", type: \"string\", value: \"NAME\", summary: \"Only sessions whose name or id contains NAME\" },\n  ],\n  examples: [\"pai sessions pause-all --dry-run\", \"pai sessions pause-all --only api --exit\"],\n  seeAlso: [\"pai pause\"],\n  run: (c) =>\n    legacy(() => cmdPauseAll({ exit: c.opts.exit === true, dryRun: c.dryRun, only: c.opts.only as string | undefined, wait: c.opts.wait as number | undefined })),\n};\n\nconst sessionsEnd: CommandDef = {\n  name: \"end\",\n  summary: \"Finish the current session\",\n  description: \"Same as `pai end`.\",\n  options: [BODY_FILE, SESSION_ID, NO_BODY],\n  examples: [\"pai sessions end --dry-run --no-body\", \"pai sessions end --body-file notes.md\"],\n  seeAlso: [\"pai end\"],\n  run: (c) => legacy(() => cmdEnd(pauseOpts(c))),\n};\n\nexport const clearNamesCmd: CommandDef = {\n  name: \"clear-names\",\n  summary: \"Wipe the stored tab names (recovery)\",\n  description:\n    \"Clears the AIBroker name store and the pai name from every live iTerm2 tab. Use it when tab names are corrupt; afterwards re-label each tab with /Name.\",\n  options: [{ ...OPT.dryRun, summary: \"Show what would be cleared, clear nothing\" }],\n  examples: [\"pai clear-names --dry-run\", \"pai clear-names\"],\n  seeAlso: [\"pai sessions pause-all\"],\n  run: (c) => legacy(() => cmdClearNames({ dryRun: c.dryRun })),\n};\n\nconst sessionsGoto: CommandDef = {\n  ...gotoCmd,\n  options: [],\n  examples: [\"pai sessions goto my-session --dry-run\", \"pai sessions goto 3a9f1c2e\"],\n  run: (c) => legacy(() => cmdGoto(c.args.NAME_OR_ID as string, { dryRun: c.dryRun })),\n};\n\nconst sessionsHandover: CommandDef = {\n  ...handoverCmd,\n  args: [\n    { name: \"PROJECT\", summary: \"Project slug (default: the project of the current directory)\" },\n    { name: \"SESSION_ID\", summary: \"Session id or note number\" },\n  ],\n  options: [],\n  examples: [\"pai sessions handover\", \"pai sessions handover my-project latest\"],\n  run: (c) => legacy(() => cmdHandover(c.args.PROJECT as string | undefined, c.args.SESSION_ID as string | undefined)),\n};\n\nexport const sessionsCommand: CommandDef = {\n  name: \"sessions\",\n  summary: \"Old plural session commands (use `ls` and `session`)\",\n  description:\n    \"Everything `pai session` does, under the plural name. Bare `pai sessions` lists sessions like `pai ls`.\",\n  options: lsCommand.options,\n  examples: [\"pai sessions\", \"pai sessions list\", \"pai sessions active\"],\n  seeAlso: [\"pai ls\", \"pai session\"],\n  subcommands: [\n    sessionsList, sessionsGoto, sessionsPause, sessionsPauseAll, sessionsEnd,\n    { ...infoCmd }, { ...renameNoteCmd }, { ...slugCmd }, { ...tagCmd }, { ...routeCmd },\n    sessionsHandover, { ...checkpointCmd }, { ...activeCmd }, clearNamesCmd, { ...autoRouteCmd },\n  ],\n  run: lsCommand.run,\n};\n","/** Shell integration printed by `pai shell-init` (eval \"$(pai shell-init)\"). Byte-identical to the old CLI. */\nexport const SHELL_INIT = `# 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 || \"$1\" == --provider || \"$1\" == --provider=* || \"$1\" == --model || \"$1\" == --model=* ]]; 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 * storage/paths.ts — the on-disk *.db path resolvers, re-exported from the\n * SQLite openers (src/storage/sqlite/federation-db.ts, registry-db.ts) so\n * that everything outside src/storage/ (config/pai-files.ts,\n * cli/commands/backup.ts, restore.ts, config.ts, workers/project-config.ts,\n * ...) can learn *where* the SQLite files live without importing\n * better-sqlite3 or openFederation/openRegistry directly — those imports are\n * what the boundary test (design doc §7) forbids outside src/storage/.\n */\n\nexport { federationDbPath, oldFederationPath } from \"./sqlite/federation-db.js\";\nexport { registryDbPath, oldRegistryPath } from \"./sqlite/registry-db.js\";\n\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { paiHomePath, migratePaiFile, type MigrateFileResult } from \"../config/pai-home.js\";\n\n/**\n * The orphaned ~/.config/pai/federation.db (a 0-byte leftover — the live\n * federation DB has lived at ~/.pai/federation.db since the Postgres\n * migration, nothing in src/ reads this copy). Lives here rather than in\n * config/pai-files.ts so that file stays free of *.db path literals.\n */\nexport function oldOrphanFederationDbPath(): string {\n  return join(homedir(), \".config\", \"pai\", \"federation.db\");\n}\n\nexport function migrateOrphanFederationDb(opts: { dryRun?: boolean } = {}): MigrateFileResult {\n  return migratePaiFile(paiHomePath(\"federation.db\"), [oldOrphanFederationDbPath()], opts);\n}\n","/**\n * storage/backup.ts — the only place `pai backup`/`pai restore` touch the\n * *.db filenames. SQLite backend: byte-copy registry.db (+ federation.db,\n * if present) into/from the backup dir. Postgres backend: the file-copy\n * step is a no-op — pg_dump/pg_restore (still in cli/commands/backup.ts and\n * restore.ts, via docker exec) is the real backup for that backend.\n */\n\nimport { existsSync, copyFileSync, mkdirSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type { PaiDaemonConfig } from \"../daemon/config.js\";\nimport { federationDbPath, registryDbPath } from \"./paths.js\";\n\nexport interface DbFileResult {\n  label: string;\n  path: string;\n  status: \"ok\" | \"skipped\" | \"failed\";\n  error?: string;\n}\n\n/** Copies registry.db (and federation.db, if present) into destDir. No-op on the postgres backend. */\nexport function backupStorageFiles(config: PaiDaemonConfig, destDir: string): DbFileResult[] {\n  if (config.storageBackend === \"postgres\") return [];\n\n  const results: DbFileResult[] = [];\n\n  const registrySrc = registryDbPath();\n  if (existsSync(registrySrc)) {\n    const dest = join(destDir, \"registry.db\");\n    try {\n      copyFileSync(registrySrc, dest);\n      results.push({ label: \"Registry DB\", path: dest, status: \"ok\" });\n    } catch (e) {\n      results.push({ label: \"Registry DB\", path: dest, status: \"failed\", error: String(e) });\n    }\n  } else {\n    results.push({ label: \"Registry DB\", path: registrySrc, status: \"skipped\", error: \"not found\" });\n  }\n\n  const fedSrc = federationDbPath();\n  if (existsSync(fedSrc)) {\n    const dest = join(destDir, \"federation.db\");\n    try {\n      copyFileSync(fedSrc, dest);\n      results.push({ label: \"Federation DB (legacy)\", path: dest, status: \"ok\" });\n    } catch (e) {\n      results.push({ label: \"Federation DB (legacy)\", path: dest, status: \"failed\", error: String(e) });\n    }\n  }\n\n  return results;\n}\n\nexport interface StorageBackupInventory {\n  hasRegistry: boolean;\n  hasFederation: boolean;\n}\n\n/** What a backup dir holds, independent of which backend is configured now. */\nexport function inspectStorageBackup(srcDir: string): StorageBackupInventory {\n  return {\n    hasRegistry: existsSync(join(srcDir, \"registry.db\")),\n    hasFederation: existsSync(join(srcDir, \"federation.db\")),\n  };\n}\n\n/** Restores registry.db (and federation.db, if present) from srcDir. No-op on the postgres backend. */\nexport function restoreStorageFiles(config: PaiDaemonConfig, srcDir: string): DbFileResult[] {\n  if (config.storageBackend === \"postgres\") return [];\n\n  const results: DbFileResult[] = [];\n  const inv = inspectStorageBackup(srcDir);\n\n  if (inv.hasRegistry) {\n    const dest = registryDbPath();\n    try {\n      mkdirSync(dirname(dest), { recursive: true });\n      copyFileSync(join(srcDir, \"registry.db\"), dest);\n      results.push({ label: \"Registry DB\", path: dest, status: \"ok\" });\n    } catch (e) {\n      results.push({ label: \"Registry DB\", path: dest, status: \"failed\", error: String(e) });\n    }\n  } else {\n    results.push({ label: \"Registry DB\", path: registryDbPath(), status: \"skipped\", error: \"missing in backup\" });\n  }\n\n  if (inv.hasFederation) {\n    const dest = federationDbPath();\n    try {\n      mkdirSync(dirname(dest), { recursive: true });\n      copyFileSync(join(srcDir, \"federation.db\"), dest);\n      results.push({ label: \"Federation DB (legacy)\", path: dest, status: \"ok\" });\n    } catch (e) {\n      results.push({ label: \"Federation DB (legacy)\", path: dest, status: \"failed\", error: String(e) });\n    }\n  }\n\n  return results;\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 file        — SQLite registry database (sqlite backend only)\n *   config.json          — PAI daemon config\n *   postgres-pai.sql     — pg_dump of the Postgres \"pai\" database (via docker exec)\n */\n\nimport {\n  existsSync,\n  mkdirSync,\n  copyFileSync,\n  statSync,\n  chmodSync,\n  openSync,\n  closeSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { execFileSync, spawnSync } from \"node:child_process\";\nimport { ok, warn, err, dim, bold } from \"../../lib/utils.js\";\nimport { loadConfig, paiConfigFilePath } from \"../../../daemon/config.js\";\nimport { backupStorageFiles } from \"../../../storage/backup.js\";\nimport { paiHomePath, resolvePaiFile } from \"../../../config/pai-home.js\";\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nconst CONFIG_FILE = paiConfigFilePath();\n\n/** Old backups dir, inside ~/.pai/ (pre-2026-09-19). */\nexport function oldBackupsDir(): string {\n  return join(homedir(), \".pai\", \"backups\");\n}\n\n/** Backups dir: PAI_HOME/backups if present, else the old ~/.pai/backups\n *  (one-time stderr notice), else the new path. */\nexport function backupsDirPath(): string {\n  return resolvePaiFile(paiHomePath(\"backups\"), [oldBackupsDir()], \"pai config migrate --backups\");\n}\n\nconst BACKUPS_DIR = backupsDirPath();\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\nexport async function runBackup(opts: { postgres: boolean }): Promise<void> {\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  const config = loadConfig();\n\n  // ------------------------------------------------------------------\n  // 1. Registry (+ legacy federation) SQLite DB, sqlite backend only\n  // ------------------------------------------------------------------\n\n  for (const r of backupStorageFiles(config, backupDir)) {\n    results.push({\n      label: r.label,\n      path: r.path,\n      size: r.status === \"ok\" ? fileSize(r.path) : \"-\",\n      status:\n        r.status === \"ok\" ? ok(\"ok\") : r.status === \"skipped\" ? warn(\"not found — skipped\") : err(`failed: ${r.error}`),\n    });\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      chmodSync(dest, 0o600);\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. 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      execFileSync(\"docker\", [\"inspect\", DOCKER_CONTAINER, \"--format={{.State.Status}}\"], {\n        stdio: \"pipe\",\n      });\n\n      const sqlFd = openSync(sqlDest, \"w\");\n      try {\n        const dump = spawnSync(\n          \"docker\",\n          [\"exec\", DOCKER_CONTAINER, \"pg_dump\", \"-U\", PG_USER, PG_DATABASE],\n          { stdio: [\"pipe\", sqlFd, \"pipe\"] }\n        );\n        if (dump.status !== 0) {\n          throw new Error(dump.stderr?.toString().trim() || `pg_dump exited with code ${dump.status}`);\n        }\n      } finally {\n        closeSync(sqlFd);\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 * Shared registry helper functions for project sub-commands.\n * Each function asks the process-wide RegistryBackend itself rather than\n * taking a database handle — callers just `await` them.\n */\n\nimport { getRegistryBackend } from \"../../../storage/factory.js\";\nimport type { ProjectRow } from \"./types.js\";\n\n/**\n * Find a project by slug or alias, case-insensitively.\n *\n * Slugs are conventionally lowercase, so exact matching went unnoticed until a\n * project was deliberately named `Solar` — after which `pai project info solar`\n * reported \"Project not found\" for a project sitting right there. Nobody types\n * a name back with its capitalisation intact, and a lookup that demands it will\n * be wrong more often than the user is.\n *\n * Exact match is still tried first: where two projects differ only by case, the\n * one asked for by its exact name wins rather than whichever the database\n * returns first.\n */\nexport async function getProject(slug: string): Promise<ProjectRow | undefined> {\n  const backend = await getRegistryBackend();\n\n  const direct = await backend.getProjectBySlug(slug);\n  if (direct) return direct;\n\n  const alias = await backend.getProjectByAlias(slug);\n  if (alias) return alias;\n\n  const insensitive = await backend.getProjectBySlug(slug, { caseInsensitive: true });\n  if (insensitive) return insensitive;\n\n  const aliasInsensitive = await backend.getProjectByAlias(slug, { caseInsensitive: true });\n  return aliasInsensitive ?? undefined;\n}\n\nexport async function requireProject(slug: string): Promise<ProjectRow> {\n  const project = await getProject(slug);\n  if (!project) {\n    // Throw rather than process.exit(1): it propagates to the top-level\n    // catch in cli/index.ts, which prints the message and sets\n    // process.exitCode, letting the process exit naturally so piped\n    // stdout/stderr is flushed (process.exit() does not guarantee that).\n    throw new Error(`Project not found: ${slug}`);\n  }\n  return project;\n}\n\n/**\n * Resolve an identifier that may be a list index number or a slug.\n */\nexport async function resolveIdentifier(identifier: string): Promise<ProjectRow | undefined> {\n  const num = parseInt(identifier, 10);\n  if (!isNaN(num) && num > 0 && String(num) === identifier) {\n    const backend = await getRegistryBackend();\n    const rows = await backend.listProjects({ orderBy: \"status_updated\" });\n    if (num <= rows.length) return rows[num - 1];\n  }\n  return getProject(identifier);\n}\n\nexport async function getProjectTags(projectId: number): Promise<string[]> {\n  const backend = await getRegistryBackend();\n  return backend.listTagsForProject(projectId);\n}\n\nexport async function getProjectAliases(projectId: number): Promise<string[]> {\n  const backend = await getRegistryBackend();\n  return backend.listAliasesForProject(projectId);\n}\n\nexport async function getSessionCount(projectId: number): Promise<number> {\n  const backend = await getRegistryBackend();\n  return backend.countSessionsForProject(projectId);\n}\n\nexport async function getLastSessionDate(projectId: number): Promise<number | null> {\n  const backend = await getRegistryBackend();\n  const sessions = await backend.listSessionsForProject(projectId, { orderBy: \"created_desc\", limit: 1 });\n  return sessions[0]?.created_at ?? null;\n}\n\nexport async function upsertTag(tagName: string): Promise<number> {\n  const backend = await getRegistryBackend();\n  return backend.upsertTag(tagName);\n}\n","/**\n * Core project CRUD commands: add, list, info, archive, unarchive, move, tag,\n * alias, edit, detect, consolidate, go, rebind — plus private helpers levenshtein,\n * containsIgnoreCase, findProjectNotesDirs, and findMovedPath.\n */\n\nimport {\n  existsSync,\n  readdirSync,\n  statSync,\n  mkdirSync,\n  renameSync,\n} from \"node:fs\";\nimport { join, basename, resolve } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport { unregistrableReason } from \"../../../registry/registrable.js\";\nimport { getRegistryBackend } from \"../../../storage/factory.js\";\nimport type { Project } from \"../../../storage/registry-interface.js\";\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 \"../../lib/utils.js\";\nimport {\n  detectProject,\n  formatDetection,\n  formatDetectionJson,\n} from \"../detect.js\";\nimport { ensurePaiMarker } from \"../../../registry/pai-marker.js\";\nimport type { ProjectRow, SessionRow } from \"./types.js\";\nimport {\n  requireProject,\n  resolveIdentifier,\n  getProject,\n  getProjectTags,\n  getProjectAliases,\n  getSessionCount,\n  getLastSessionDate,\n  upsertTag,\n} from \"./helpers.js\";\nimport { loadScanConfig, resolveHome } from \"../registry/scan.js\";\n\n// ---------------------------------------------------------------------------\n// Private helpers\n// ---------------------------------------------------------------------------\n\nfunction levenshtein(a: string, b: string): number {\n  const m = a.length;\n  const n = b.length;\n  const dp: number[][] = Array.from({ length: m + 1 }, (_, i) =>\n    Array.from({ length: n + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0))\n  );\n  for (let i = 1; i <= m; i++) {\n    for (let j = 1; j <= n; j++) {\n      dp[i][j] =\n        a[i - 1] === b[j - 1]\n          ? dp[i - 1][j - 1]\n          : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);\n    }\n  }\n  return dp[m][n];\n}\n\nfunction containsIgnoreCase(haystack: string, needle: string): boolean {\n  return haystack.toLowerCase().includes(needle.toLowerCase());\n}\n\nfunction findProjectNotesDirs(project: ProjectRow): {\n  encodedDir: string;\n  fullPath: string;\n  notesPath: string;\n  noteCount: number;\n}[] {\n  const claudeProjects = join(homedir(), \".claude\", \"projects\");\n  if (!existsSync(claudeProjects)) return [];\n\n  const results: {\n    encodedDir: string;\n    fullPath: string;\n    notesPath: string;\n    noteCount: number;\n  }[] = [];\n  const rootEncoded = encodeDir(project.root_path);\n\n  try {\n    for (const entry of readdirSync(claudeProjects)) {\n      const full = join(claudeProjects, entry);\n      try {\n        if (!statSync(full).isDirectory()) continue;\n      } catch {\n        continue;\n      }\n\n      if (entry !== rootEncoded && !entry.startsWith(rootEncoded)) continue;\n\n      const notesPath = join(full, \"Notes\");\n      if (!existsSync(notesPath)) continue;\n\n      let noteCount = 0;\n      try {\n        noteCount = readdirSync(notesPath).filter(\n          (f) => f.endsWith(\".md\") || f.endsWith(\".txt\")\n        ).length;\n      } catch {\n        // count stays 0\n      }\n\n      results.push({ encodedDir: entry, fullPath: full, notesPath, noteCount });\n    }\n  } catch {\n    // Unreadable — ignore\n  }\n\n  return results;\n}\n\n// ---------------------------------------------------------------------------\n// Moved-project detection\n// ---------------------------------------------------------------------------\n\n// Directories to skip during filesystem walk\nconst SKIP_DIRS = new Set([\n  \".git\", \"node_modules\", \".next\", \".nuxt\", \"dist\", \"build\", \"coverage\",\n  \".cache\", \"__pycache__\", \"vendor\", \".svn\", \".hg\", \"venv\", \".venv\",\n  \"target\",  // Rust/Java build dir\n  \"Notes\",   // PAI session notes — not project roots\n]);\n\n/**\n * Walk `dir` up to `maxDepth` levels deep, collecting all subdirectory paths\n * whose basename matches `targetBasename` (case-insensitive on macOS/case-sensitive\n * on Linux, whichever is appropriate — we use exact case matching to keep it\n * correct and fast).\n *\n * Skips hidden directories and known build/tool dirs.\n * Stops walking a branch if the deadline is exceeded (returns partial results).\n */\nfunction walkForBasename(\n  dir: string,\n  targetBasename: string,\n  maxDepth: number,\n  deadline: number,\n  results: string[]\n): void {\n  if (Date.now() > deadline) return;\n  if (maxDepth <= 0) return;\n\n  let entries: string[];\n  try {\n    entries = readdirSync(dir);\n  } catch {\n    return;\n  }\n\n  for (const entry of entries) {\n    if (Date.now() > deadline) return;\n    if (entry.startsWith(\".\")) continue;\n    if (SKIP_DIRS.has(entry)) continue;\n\n    const full = join(dir, entry);\n    let isDir = false;\n    try {\n      isDir = statSync(full).isDirectory();\n    } catch {\n      continue;\n    }\n    if (!isDir) continue;\n\n    // Case-insensitive comparison for the target basename (handles macOS APFS)\n    if (entry.toLowerCase() === targetBasename.toLowerCase()) {\n      results.push(full);\n      // Don't recurse into the match itself — we want the directory, not children\n      continue;\n    }\n\n    walkForBasename(full, targetBasename, maxDepth - 1, deadline, results);\n  }\n}\n\n/**\n * Try to find where a project has moved.\n *\n * Searches scan_dirs from config (plus a set of common fallback dirs) for a\n * directory whose basename matches the basename of the registered root_path.\n *\n * Returns:\n *   { found: string }   — exactly one match found\n *   { ambiguous: string[] }  — multiple matches found\n *   { found: undefined }     — no match found (or timed out)\n */\nexport function findMovedPath(registeredPath: string): { found?: string; ambiguous?: string[] } {\n  const targetBasename = basename(registeredPath);\n  const config = loadScanConfig();\n  const deadline = Date.now() + 5_000; // 5-second max\n\n  // Build the list of top-level directories to search\n  const home = homedir();\n  const configDirs = (config.scan_dirs ?? []).map((d) => resolveHome(d));\n\n  // Walk UP the registered path to find the deepest existing ancestor.\n  // This is the best search root when a project has moved within the same tree.\n  // e.g. ~/Cloud/Ablage/2025/Project → ~/Cloud/Ablage/2026/Project\n  //      Deepest existing ancestor: ~/Cloud/Ablage (if 2025 is gone but Ablage exists)\n  function deepestExistingAncestor(p: string): string | null {\n    let current = p;\n    while (true) {\n      const parent = join(current, \"..\");\n      if (parent === current) return null; // filesystem root\n      if (parent === home) return null;    // don't use home itself as search root\n      if (existsSync(parent)) return parent;\n      current = parent;\n    }\n  }\n\n  const ancestorRoot = deepestExistingAncestor(registeredPath);\n\n  const fallbackDirs = [\n    join(home, \"dev\"),\n    join(home, \"Desktop\"),\n    join(home, \"Projects\"),\n    join(home, \"Documents\"),\n    // Common cloud-storage root folders (Synology Drive, iCloud, etc.)\n    join(home, \"Cloud\"),\n    join(home, \"Library\", \"Mobile Documents\", \"com~apple~CloudDocs\"),  // iCloud Drive\n    // The deepest ancestor of the registered path that still exists on disk\n    ...(ancestorRoot ? [ancestorRoot] : []),\n  ];\n\n  // Deduplicate and filter to existing directories\n  const searchRoots = [...new Set([...configDirs, ...fallbackDirs])]\n    .map((d) => {\n      try { return resolve(d); } catch { return null; }\n    })\n    .filter((d): d is string => d !== null && existsSync(d));\n\n  const results: string[] = [];\n  for (const root of searchRoots) {\n    if (Date.now() > deadline) break;\n    walkForBasename(root, targetBasename, 6, deadline, results);\n  }\n\n  // Deduplicate results (different roots might overlap)\n  const unique = [...new Set(results)];\n\n  if (unique.length === 0) return {};\n  if (unique.length === 1) return { found: unique[0] };\n  return { ambiguous: unique };\n}\n\n// ---------------------------------------------------------------------------\n// Command implementations\n// ---------------------------------------------------------------------------\n\nexport async function cmdAdd(\n  rawPath: string,\n  opts: { slug?: string; type?: string; displayName?: string }\n): Promise<void> {\n  const backend = await getRegistryBackend();\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.exitCode = 1;\n    return;\n  }\n\n  // Disposable directories must not become projects. Two agent worktrees and two\n  // /private/tmp paths got in this way and between them carried 8 sessions\n  // attributed to locations with no future — and `pai <name>` will happily route\n  // someone into a worktree a cleanup can delete underneath them.\n  const ephemeral = unregistrableReason(rootPath);\n  if (ephemeral) {\n    console.error(err(`Refusing to register ${shortenPath(rootPath, 60)}`));\n    console.error(dim(`  That is ${ephemeral}.`));\n    console.error(dim(`  A project needs a durable location — move it, then register.`));\n    process.exitCode = 1;\n    return;\n  }\n\n  const existing = (await backend.getProjectBySlug(slug)) ?? (await backend.getProjectByRootPath(rootPath));\n  if (existing) {\n    console.error(\n      err(`Project already registered (slug: ${slug} or path: ${rootPath})`)\n    );\n    process.exitCode = 1;\n    return;\n  }\n\n  const dirName = basename(rootPath).toLowerCase();\n  const active = await backend.listProjects({ status: \"active\" });\n  const similar = active.filter((p) => p.slug !== slug).map((p) => ({ slug: p.slug, root_path: p.root_path }));\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  await backend.createProject({\n    slug,\n    displayName,\n    rootPath,\n    encodedDir,\n    type: type as Project[\"type\"],\n    status: \"active\",\n    createdAt: ts,\n    updatedAt: ts,\n  });\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 async function cmdList(\n  opts: { status?: string; tag?: string; type?: string; all?: boolean }\n): Promise<void> {\n  const backend = await getRegistryBackend();\n\n  let tagId: number | undefined;\n  if (opts.tag) {\n    const tags = await backend.listAllTags();\n    tagId = tags.find((t) => t.name === opts.tag)?.id;\n    if (tagId === undefined) {\n      console.log(warn(\"No projects found.\"));\n      return;\n    }\n  }\n\n  const rows = (\n    await backend.listProjectsWithSessionStats({\n      status: opts.status ? (opts.status as Project[\"status\"]) : opts.all ? undefined : \"active\",\n      tagId,\n      orderBy: \"status_updated\",\n    })\n  ).filter((p) => !opts.type || p.type === opts.type);\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.display_name ?? r.slug),\n    dim(r.slug),\n    dim(shortenPath(r.root_path, 44)),\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      [\"#\", \"Name\", \"Slug\", \"Path\", \"Status\", \"Type\", \"Sessions\", \"Last Active\"],\n      tableRows\n    )\n  );\n  console.log();\n\n  // When showing active-only, inform the user about hidden archived projects\n  const total = await backend.countProjects();\n  const hiddenCount = total - rows.length;\n  if (!opts.all && !opts.status && hiddenCount > 0) {\n    console.log(dim(`  ${rows.length} active project(s)  (${hiddenCount} archived — use --all to show)`));\n  } else {\n    console.log(dim(`  ${rows.length} project(s)`));\n  }\n}\n\nexport async function cmdInfo(identifier: string): Promise<void> {\n  const project =\n    (await resolveIdentifier(identifier)) ?? (await requireProject(identifier));\n  const backend = await getRegistryBackend();\n  const tags = await getProjectTags(project.id);\n  const aliases = await getProjectAliases(project.id);\n  const sessionCount = await getSessionCount(project.id);\n  const lastSession = await getLastSessionDate(project.id);\n\n  const recentSessions: SessionRow[] = await backend.listSessionsForProject(project.id, {\n    orderBy: \"created_desc\",\n    limit: 5,\n  });\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 async function cmdArchive(slug: string): Promise<void> {\n  const project = await requireProject(slug);\n  if (project.status === \"archived\") {\n    console.log(warn(`Project ${slug} is already archived.`));\n    return;\n  }\n  const ts = now();\n  const backend = await getRegistryBackend();\n  await backend.updateProjectStatus(project.id, \"archived\", { archivedAt: ts, updatedAt: ts });\n  console.log(ok(`Archived: ${bold(slug)}`));\n}\n\nexport async function cmdUnarchive(slug: string): Promise<void> {\n  const project = await requireProject(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  const backend = await getRegistryBackend();\n  await backend.updateProjectStatus(project.id, \"active\", { archivedAt: null, updatedAt: ts });\n  console.log(ok(`Unarchived: ${bold(slug)}`));\n}\n\nexport async function cmdMove(slug: string, newPath: string): Promise<void> {\n  const project = await requireProject(slug);\n  const resolvedNew = resolvePath(newPath);\n  const newEncoded = encodeDir(resolvedNew);\n  const ts = now();\n\n  const backend = await getRegistryBackend();\n  await backend.updateProjectPath(project.id, { rootPath: resolvedNew, encodedDir: newEncoded }, ts);\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 async function cmdTag(\n  slug: string,\n  tags: string[]\n): Promise<void> {\n  const project = await requireProject(slug);\n  const backend = await getRegistryBackend();\n  const added: string[] = [];\n  const skipped: string[] = [];\n\n  for (const tagName of tags) {\n    const tagId = await upsertTag(tagName);\n    const exists = await backend.projectHasTag(project.id, tagId);\n    if (exists) {\n      skipped.push(tagName);\n    } else {\n      await backend.addProjectTag(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 async function cmdAlias(\n  slug: string,\n  alias: string\n): Promise<void> {\n  await requireProject(slug);\n  const backend = await getRegistryBackend();\n\n  const conflict = await backend.getProjectBySlug(alias);\n  if (conflict) {\n    console.error(\n      err(`\"${alias}\" is already a project slug — cannot use as alias.`)\n    );\n    process.exitCode = 1;\n    return;\n  }\n\n  const project = (await getProject(slug))!;\n  try {\n    await backend.addAlias(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.exitCode = 1;\n  }\n}\n\nexport async function cmdEdit(\n  slug: string,\n  opts: { displayName?: string; type?: string }\n): Promise<void> {\n  const project = await requireProject(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.exitCode = 1;\n    return;\n  }\n\n  const ts = now();\n  const backend = await getRegistryBackend();\n  if (opts.displayName) {\n    await backend.updateProjectDisplayName(project.id, opts.displayName, ts);\n    console.log(ok(`Display name updated: ${bold(opts.displayName)}`));\n  }\n  if (opts.type) {\n    await backend.updateProjectType(project.id, opts.type as Project[\"type\"], ts);\n    console.log(ok(`Type updated: ${bold(opts.type)}`));\n  }\n}\n\nexport async function cmdDetect(\n  pathArg: string | undefined,\n  opts: { json?: boolean }\n): Promise<void> {\n  const cwd = pathArg ? resolvePath(pathArg) : process.cwd();\n  const detection = await detectProject(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    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 async function cmdConsolidate(\n  identifier: string,\n  opts: { yes?: boolean; dryRun?: boolean }\n): Promise<void> {\n  const project =\n    (await resolveIdentifier(identifier)) ?? (await requireProject(identifier));\n\n  console.log();\n  console.log(header(`  Consolidate: ${project.slug}`));\n  console.log(`  Target:  ${project.root_path}`);\n  console.log();\n\n  const dirs = findProjectNotesDirs(project);\n\n  if (dirs.length === 0) {\n    console.log(warn(\"  No scattered notes directories found for this project.\"));\n    return;\n  }\n\n  const canonicalNotes = join(project.root_path, \"Notes\");\n  const toMerge = dirs.filter((d) => d.notesPath !== canonicalNotes);\n\n  if (toMerge.length === 0) {\n    console.log(ok(\"  All notes are already in the canonical location.\"));\n    console.log(dim(`  ${canonicalNotes}`));\n    return;\n  }\n\n  console.log(\n    `  Found ${toMerge.length} scattered Notes directory(ies) to consolidate:`\n  );\n  console.log();\n\n  for (const d of toMerge) {\n    console.log(`    ${bold(d.encodedDir)}`);\n    console.log(dim(`      Notes: ${d.notesPath} (${d.noteCount} file(s))`));\n  }\n\n  console.log();\n  console.log(`  Destination: ${canonicalNotes}`);\n  console.log();\n\n  if (opts.dryRun) {\n    console.log(warn(\"  Dry run — no changes made. Remove --dry-run to proceed.\"));\n    return;\n  }\n\n  if (!opts.yes) {\n    console.log(\n      warn(\n        \"  Run with --yes to perform consolidation, or --dry-run to preview changes.\"\n      )\n    );\n    return;\n  }\n\n  mkdirSync(canonicalNotes, { recursive: true });\n\n  let movedCount = 0;\n  for (const d of toMerge) {\n    try {\n      const files = readdirSync(d.notesPath);\n      for (const f of files) {\n        if (!f.endsWith(\".md\") && !f.endsWith(\".txt\")) continue;\n        const src = join(d.notesPath, f);\n        const dest = join(canonicalNotes, f);\n        if (!existsSync(dest)) {\n          renameSync(src, dest);\n          console.log(ok(`    Moved: ${f}`));\n          movedCount++;\n        } else {\n          console.log(warn(`    Skipped (exists): ${f}`));\n        }\n      }\n    } catch (e) {\n      console.error(err(`    Error reading ${d.notesPath}: ${e}`));\n    }\n  }\n\n  console.log();\n  console.log(ok(`  Consolidated ${movedCount} file(s) into ${canonicalNotes}`));\n}\n\n/**\n * Attempt moved-path recovery for a project whose registered root_path is\n * missing. Updates the DB if exactly one candidate is found.\n *\n * Returns the new path on success, or undefined if ambiguous/not found.\n * Prints all messages to stderr (safe for shell-wrapper stdout capture).\n */\nasync function tryRecoverMovedProject(project: ProjectRow): Promise<string | undefined> {\n  process.stderr.write(\n    warn(`Path not found: ${project.root_path}\\n`) +\n    dim(\"  Searching for moved location...\\n\")\n  );\n\n  const result = findMovedPath(project.root_path);\n\n  if (result.found) {\n    const newPath = result.found;\n    const newEncoded = encodeDir(newPath);\n    const ts = now();\n    const backend = await getRegistryBackend();\n    await backend.updateProjectPath(project.id, { rootPath: newPath, encodedDir: newEncoded }, ts);\n    process.stderr.write(\n      ok(`Project moved: ${shortenPath(project.root_path, 50)}\\n`) +\n      dim(`  → ${newPath}\\n`) +\n      ok(\"Registry updated.\\n\")\n    );\n    return newPath;\n  }\n\n  if (result.ambiguous) {\n    process.stderr.write(\n      warn(`Multiple directories named \"${basename(project.root_path)}\" found:\\n`)\n    );\n    for (const candidate of result.ambiguous) {\n      process.stderr.write(dim(`  ${candidate}\\n`));\n    }\n    process.stderr.write(\n      dim(`\\n  Disambiguate with: pai projects rebind ${project.slug} <path>\\n`)\n    );\n    return undefined;\n  }\n\n  process.stderr.write(\n    err(\n      `Project \"${project.slug}\" root_path \"${project.root_path}\" does not exist on disk\\n` +\n      `  and no folder named \"${basename(project.root_path)}\" was found in scan dirs.\\n`\n    ) +\n    dim(`  Fix with: pai projects rebind ${project.slug} <new-path>\\n`)\n  );\n  return undefined;\n}\n\nexport async function cmdGo(query: string): Promise<void> {\n  const backend = await getRegistryBackend();\n  const all: ProjectRow[] = await backend.listProjects({ status: \"active\", orderBy: \"updated_desc\" });\n\n  if (!all.length) {\n    console.error(\n      err(\"No active projects registered. Run: pai project add <path>\")\n    );\n    process.exitCode = 1;\n    return;\n  }\n\n  const q = query.trim().toLowerCase();\n\n  // 1. Exact slug or alias match\n  const exact = await getProject(query);\n  if (exact) {\n    if (!existsSync(exact.root_path)) {\n      const recovered = await tryRecoverMovedProject(exact);\n      if (!recovered) { process.exitCode = 1; return; }\n      process.stdout.write(recovered + \"\\n\");\n      return;\n    }\n    process.stdout.write(exact.root_path + \"\\n\");\n    return;\n  }\n\n  // 2. Substring match against slug, display_name, or root_path basename\n  const partial = all.filter(\n    (p) =>\n      containsIgnoreCase(p.slug, q) ||\n      containsIgnoreCase(p.display_name, q) ||\n      containsIgnoreCase(basename(p.root_path), q)\n  );\n\n  if (partial.length === 1) {\n    const p = partial[0];\n    if (!existsSync(p.root_path)) {\n      const recovered = await tryRecoverMovedProject(p);\n      if (!recovered) { process.exitCode = 1; return; }\n      process.stdout.write(recovered + \"\\n\");\n      return;\n    }\n    process.stdout.write(p.root_path + \"\\n\");\n    return;\n  }\n\n  if (partial.length > 1) {\n    console.error(\n      err(`Ambiguous: \"${query}\" matches ${partial.length} projects:\\n`)\n    );\n    partial.forEach((p, i) => {\n      console.error(\n        `  ${dim(String(i + 1).padStart(2))}  ${bold(p.slug.padEnd(30))}  ${dim(\n          shortenPath(p.root_path, 50)\n        )}`\n      );\n    });\n    console.error();\n    console.error(dim(\"  Use a more specific name or the exact slug.\"));\n    process.exitCode = 1;\n    return;\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.exitCode = 1;\n}\n\nexport async function cmdRebind(\n  slug: string,\n  newPath: string\n): Promise<void> {\n  const project = await requireProject(slug);\n  const resolved = resolve(newPath.startsWith(\"~/\") ? join(homedir(), newPath.slice(2)) : newPath);\n\n  if (!existsSync(resolved)) {\n    console.error(err(`Path does not exist: ${resolved}`));\n    process.exitCode = 1;\n    return;\n  }\n\n  let isDir = false;\n  try {\n    isDir = statSync(resolved).isDirectory();\n  } catch {\n    // stat failed\n  }\n  if (!isDir) {\n    console.error(err(`Path is not a directory: ${resolved}`));\n    process.exitCode = 1;\n    return;\n  }\n\n  const newEncoded = encodeDir(resolved);\n  const backend = await getRegistryBackend();\n\n  // Check if another project already owns this path\n  const conflict = await backend.getProjectByEncodedDir(newEncoded, { excludeId: project.id });\n  if (conflict) {\n    console.error(\n      err(`Path is already registered to project: ${bold(conflict.slug)}\\n`) +\n      dim(`  ${resolved}\\n`) +\n      dim(`  Archive or move that project first, or choose a different path.`)\n    );\n    process.exitCode = 1;\n    return;\n  }\n\n  const ts = now();\n  await backend.updateProjectPath(project.id, { rootPath: resolved, encodedDir: newEncoded }, ts);\n\n  console.log(ok(`Rebound: ${bold(slug)}`));\n  console.log(dim(`  Old path: ${project.root_path}`));\n  console.log(dim(`  New path: ${resolved}`));\n  console.log(dim(`  Encoded:  ${newEncoded}`));\n}\n","/** `pai cd IDENTIFIER`: print a project's directory (the shell wrapper does the actual cd). */\n\nimport { existsSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport { getRegistryBackend } from \"../../../storage/factory.js\";\nimport { resolveIdentifier } from \"../../impl/project/helpers.js\";\nimport { findMovedPath } from \"../../impl/project/commands.js\";\nimport { err, warn, ok, dim, shortenPath, encodeDir, now } from \"../../lib/utils.js\";\n\nexport async function runCd(identifier: string): Promise<void> {\n    const project = await resolveIdentifier(identifier);\n    if (!project) {\n      console.error(`Project not found: ${identifier}`);\n      process.exitCode = 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      const registry = await getRegistryBackend();\n      await registry.updateProjectPath(project.id, { rootPath: newPath, encodedDir: newEncoded }, ts);\n      process.stderr.write(\n        ok(`Project moved: ${shortenPath(project.root_path, 50)}\\n`) +\n        dim(`  → ${newPath}\\n`) +\n        ok(\"Registry updated.\\n\")\n      );\n      process.stdout.write(newPath + \"\\n\");\n      return;\n    }\n\n    if (result.ambiguous) {\n      process.stderr.write(\n        warn(`Multiple directories named \"${basename(project.root_path)}\" found:\\n`)\n      );\n      for (const candidate of result.ambiguous!) {\n        process.stderr.write(dim(`  ${candidate}\\n`));\n      }\n      process.stderr.write(\n        dim(`\\n  Disambiguate with: pai projects rebind ${project.slug} <path>\\n`)\n      );\n      process.exitCode = 1;\n      return;\n    }\n\n    process.stderr.write(\n      err(\n        `Project \"${project.slug}\" root_path \"${project.root_path}\" does not exist on disk\\n` +\n        `  and no folder named \"${basename(project.root_path)}\" was found in scan dirs.\\n`\n      ) +\n      dim(`  Fix with: pai projects rebind ${project.slug} <new-path>\\n`)\n    );\n    process.exitCode = 1;\n}\n","/**\n * pai find <query> [--n=20] [--json]\n *\n * Content-based search across ~/.claude/history.jsonl.\n * Finds user prompts matching the query (case-insensitive substring),\n * groups by sessionId, sorts by most-recent match, and shows a table\n * with short UUID, date, project path, and the matching prompt snippet.\n *\n * The sessionId from history.jsonl is the same UUID used by claude --resume,\n * so rows are directly resumable via: pai resume <id>\n *\n * Note: older history.jsonl entries (before Claude Code ~2.0) may lack a\n * sessionId field. These are grouped under a synthetic \"no-session\" bucket\n * and shown in a separate note at the bottom.\n */\n\nimport { existsSync, createReadStream } from \"node:fs\";\nimport { createInterface } from \"node:readline\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport { renderTable, header, dim, warn, err } from \"../../lib/utils.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface HistoryEntry {\n  display?: string;\n  timestamp?: number;\n  project?: string;\n  sessionId?: string;\n  pastedContents?: unknown;\n}\n\ninterface SessionMatch {\n  sessionId: string | null;\n  /** Most-recent timestamp among matching lines */\n  lastMatchTs: number;\n  /** Most-recent matching prompt text */\n  lastMatchDisplay: string;\n  /** Project path (from the most-recent match) */\n  project: string;\n  /** Number of matching lines in this session */\n  matchCount: number;\n}\n\n// ---------------------------------------------------------------------------\n// History file location\n// ---------------------------------------------------------------------------\n\nconst HISTORY_FILE = () => join(homedir(), \".claude\", \"history.jsonl\");\n\n// ---------------------------------------------------------------------------\n// Shorten project path for display\n// ---------------------------------------------------------------------------\n\nfunction shortenProject(p: string, maxLen = 42): string {\n  if (p.length <= maxLen) return p;\n  // Keep tail (the meaningful part) and prefix with …\n  return \"…\" + p.slice(-(maxLen - 1));\n}\n\n// ---------------------------------------------------------------------------\n// Format timestamp\n// ---------------------------------------------------------------------------\n\nfunction fmtTs(ts: number): string {\n  const d = new Date(ts);\n  const pad = (n: number) => String(n).padStart(2, \"0\");\n  return (\n    `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ` +\n    `${pad(d.getHours())}:${pad(d.getMinutes())}`\n  );\n}\n\n// ---------------------------------------------------------------------------\n// Core search function (streaming to avoid loading 26k lines into memory)\n// ---------------------------------------------------------------------------\n\nexport async function searchHistory(\n  query: string,\n  maxResults: number\n): Promise<SessionMatch[]> {\n  if (!existsSync(HISTORY_FILE())) return [];\n\n  const queryLower = query.toLowerCase();\n  // Escape regex special chars from query for safety — but we're doing\n  // string .includes() not regex, so no escaping needed.\n\n  const bySession = new Map<string, SessionMatch>();\n  const noSession: SessionMatch = {\n    sessionId: null,\n    lastMatchTs: 0,\n    lastMatchDisplay: \"\",\n    project: \"\",\n    matchCount: 0,\n  };\n\n  const rl = createInterface({\n    input: createReadStream(HISTORY_FILE(), { encoding: \"utf8\" }),\n    crlfDelay: Infinity,\n  });\n\n  for await (const line of rl) {\n    const t = line.trim();\n    if (!t) continue;\n\n    let entry: HistoryEntry;\n    try {\n      entry = JSON.parse(t) as HistoryEntry;\n    } catch {\n      continue;\n    }\n\n    const display = entry.display ?? \"\";\n    if (!display.toLowerCase().includes(queryLower)) continue;\n\n    const ts = entry.timestamp ?? 0;\n    const project = entry.project ?? \"\";\n    const sessionId = entry.sessionId ?? null;\n\n    if (!sessionId) {\n      // Older entry without sessionId\n      if (ts > noSession.lastMatchTs) {\n        noSession.lastMatchTs = ts;\n        noSession.lastMatchDisplay = display;\n        noSession.project = project;\n      }\n      noSession.matchCount++;\n      continue;\n    }\n\n    const existing = bySession.get(sessionId);\n    if (!existing) {\n      bySession.set(sessionId, {\n        sessionId,\n        lastMatchTs: ts,\n        lastMatchDisplay: display,\n        project,\n        matchCount: 1,\n      });\n    } else {\n      existing.matchCount++;\n      if (ts > existing.lastMatchTs) {\n        existing.lastMatchTs = ts;\n        existing.lastMatchDisplay = display;\n        existing.project = project;\n      }\n    }\n  }\n\n  // Sort by most-recent match descending\n  const sorted = [...bySession.values()].sort(\n    (a, b) => b.lastMatchTs - a.lastMatchTs\n  );\n\n  return sorted.slice(0, maxResults);\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport async function cmdFind(\n  query: string,\n  opts: { n?: string; json?: boolean }\n): Promise<void> {\n  const maxResults = parseInt(opts.n ?? \"20\", 10);\n\n  if (!existsSync(HISTORY_FILE())) {\n    console.error(err(`~/.claude/history.jsonl not found.`));\n    process.exitCode = 1;\n    return;\n  }\n\n  const matches = await searchHistory(query, maxResults);\n\n  if (opts.json) {\n    console.log(JSON.stringify(matches, null, 2));\n    return;\n  }\n\n  if (matches.length === 0) {\n    console.log(warn(`No sessions found matching \"${query}\" in ~/.claude/history.jsonl.`));\n    console.log(dim(`  Try a shorter or different search term.`));\n    return;\n  }\n\n  console.log(\"\\n\" + header(\"Session Search Results\") + \"\\n\");\n  console.log(dim(`  Query: \"${query}\"  (${matches.length} session(s) with matching prompts)\\n`));\n\n  const headers = [\"#\", \"id\", \"when\", \"project\", \"last matching prompt\"];\n  const rows = matches.map((m, idx) => {\n    const shortId = (m.sessionId ?? \"no-session\").slice(0, 8);\n    const when = m.lastMatchTs > 0 ? fmtTs(m.lastMatchTs) : dim(\"—\");\n    const project = shortenProject(m.project || dim(\"—\"));\n    // Truncate the prompt snippet — strip newlines, trim\n    const snippet = m.lastMatchDisplay\n      .replace(/\\n+/g, \" \")\n      .trim()\n      .slice(0, 48);\n    const display = snippet.length < m.lastMatchDisplay.replace(/\\n+/g, \" \").trim().length\n      ? `\"${snippet}…\"`\n      : `\"${snippet}\"`;\n\n    return [\n      dim(String(idx + 1)),\n      chalk.cyan(shortId),\n      when,\n      dim(project),\n      chalk.dim(display),\n    ];\n  });\n\n  console.log(renderTable(headers, rows));\n  console.log();\n  console.log(dim(\"  Resume: \") + chalk.white(\"pai resume <id>\"));\n  console.log();\n}\n","/**\n * `pai launch` — start the Claude Code harness on any provider/model\n * configured in workers.yaml, in the current terminal.\n *\n * Claude Code's own `/model` picker only ever shows the models of whatever\n * endpoint the CURRENT process was started against (base URL + auth are\n * fixed for the life of the process) — started plainly it lists Anthropic\n * models, started through a provider's base URL it lists that provider's.\n * This command is the one place that turns \"start on <provider>[/<model>]\"\n * into a fresh `claude` process: pick from `pai launch` (interactive table)\n * or name one directly (`--provider`), then exec. Switching provider always\n * means starting a NEW session — a running process cannot do it.\n */\n\nimport { spawnSync } from \"node:child_process\";\nimport { createInterface } from \"node:readline\";\nimport {\n  readWorkersSection,\n  WorkersConfigError,\n  type WorkersConfig,\n} from \"../../../workers/config.js\";\nimport { workersLogDir, ledgerPath } from \"../../../workers/paths.js\";\nimport { appendLedger } from \"../../../workers/ledger.js\";\nimport {\n  buildLaunchPlan,\n  buildLaunchRows,\n  detectCurrentModel,\n  detectCurrentProviderName,\n  launchableProviderNames,\n  maskLaunchEnv,\n  type LaunchPlan,\n  type LaunchRow,\n} from \"../../../workers/launch.js\";\nimport { renderTable, err, dim, bold, ok } from \"../../lib/utils.js\";\n\nexport interface LaunchCliOpts {\n  provider?: string;\n  model?: string;\n  list?: boolean;\n  current?: boolean;\n  dryRun?: boolean;\n}\n\n/** Table rows for `--list`, optionally marking the row matching a detected\n *  current-session provider/model (the /providers skill's `--current`). */\nfunction tableLines(workers: WorkersConfig, opts: { current?: boolean }): string[] {\n  const rows = buildLaunchRows(workers);\n  const curProvider = opts.current ? detectCurrentProviderName(workers, process.env) : null;\n  const curModel = opts.current ? detectCurrentModel(process.env) : null;\n\n  const body = rows.map((r) => {\n    const flags: string[] = [r.capability];\n    if (r.configActive) flags.push(\"active\");\n    if (curProvider && r.provider === curProvider && curModel && r.model === curModel) {\n      flags.push(\"this session\");\n    }\n    return [String(r.index), r.provider, r.model, flags.join(\", \")];\n  });\n  const lines = renderTable([\"#\", \"provider\", \"model\", \"\"], body).split(\"\\n\");\n\n  if (opts.current) {\n    lines.push(\"\");\n    lines.push(\n      `  current session: ${bold(curProvider ?? \"?\")}  model: ${curModel ?? \"(session default)\"}`\n    );\n    lines.push(\n      dim(\"  Switching provider starts a new session: pai launch --provider <name> --model <model>\")\n    );\n  }\n  return lines;\n}\n\nfunction printTable(workers: WorkersConfig, opts: { current?: boolean }): void {\n  console.log();\n  for (const line of tableLines(workers, opts)) console.log(line);\n  console.log();\n}\n\n/** Answer text (\"3\", \"glm\", \"glm/glm-5.3-flash\") -> provider + optional model. */\nfunction parseChoice(answer: string, rows: LaunchRow[]): { provider: string; model?: string } | null {\n  const trimmed = answer.trim();\n  if (!trimmed) return null;\n  if (/^\\d+$/.test(trimmed)) {\n    const row = rows.find((r) => r.index === Number(trimmed));\n    return row ? { provider: row.provider, model: row.model } : null;\n  }\n  const slash = trimmed.indexOf(\"/\");\n  if (slash < 0) return { provider: trimmed };\n  return { provider: trimmed.slice(0, slash), model: trimmed.slice(slash + 1) };\n}\n\nfunction prompt(question: string): Promise<string> {\n  const rl = createInterface({ input: process.stdin, output: process.stdout });\n  return new Promise((resolve) => rl.question(question, (answer) => {\n    rl.close();\n    resolve(answer);\n  }));\n}\n\nfunction printDryRun(plan: LaunchPlan): void {\n  console.log();\n  console.log(bold(\"Dry run — would exec:\"));\n  console.log();\n  console.log(`  cwd:  ${process.cwd()}`);\n  console.log(`  argv: claude ${plan.argv.map((a) => (/\\s/.test(a) ? JSON.stringify(a) : a)).join(\" \")}`);\n  console.log(`  env:`);\n  const masked = maskLaunchEnv(plan.env);\n  for (const k of Object.keys(masked).sort()) {\n    if (!/^(ANTHROPIC_|CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC|ENABLE_TOOL_SEARCH|PAI_WORKER)/.test(k)) continue;\n    console.log(`    ${k}=${masked[k]}`);\n  }\n  console.log();\n}\n\nexport async function runLaunch(args: string[], opts: LaunchCliOpts): Promise<void> {\n  let workers: WorkersConfig;\n  try {\n    ({ workers } = readWorkersSection());\n  } catch (e) {\n    console.error(err(`pai launch: ${e instanceof Error ? e.message : String(e)}`));\n    process.exitCode = 1;\n    return;\n  }\n  const logDir = workersLogDir(workers);\n  const interactive = !!process.stdout.isTTY && !!process.stdin.isTTY;\n\n  if (opts.list || (!opts.provider && !interactive)) {\n    printTable(workers, { current: opts.current });\n    return;\n  }\n\n  let providerName = opts.provider;\n  let modelName = opts.model;\n\n  if (!providerName) {\n    printTable(workers, {});\n    const rows = buildLaunchRows(workers);\n    const answer = await prompt(dim(\"  Pick a number or provider/model: \"));\n    const picked = parseChoice(answer, rows);\n    if (!picked) {\n      console.error(\n        err(\n          `pai launch: \"${answer.trim()}\" is not a number from the table or a provider[/model] — ` +\n            `valid providers: ${launchableProviderNames(workers).join(\", \")}`\n        )\n      );\n      process.exitCode = 2;\n      return;\n    }\n    providerName = picked.provider;\n    modelName = picked.model;\n  }\n\n  let plan: LaunchPlan;\n  try {\n    plan = await buildLaunchPlan(workers, providerName, modelName, args, logDir);\n  } catch (e) {\n    if (e instanceof WorkersConfigError) {\n      console.error(err(`pai launch: ${e.message}`));\n      process.exitCode = 2;\n      return;\n    }\n    throw e;\n  }\n\n  if (opts.dryRun) {\n    printDryRun(plan);\n    return;\n  }\n\n  appendLedger(ledgerPath(logDir), \"LAUNCH-START\", {\n    kind: \"launch\",\n    provider: plan.providerName,\n    model: plan.model,\n    cwd: process.cwd(),\n  });\n\n  console.log(dim(`  starting claude on ${ok(plan.providerName)} (${plan.model}) …`));\n  const result = spawnSync(\"claude\", plan.argv, {\n    stdio: \"inherit\",\n    env: plan.env,\n    cwd: process.cwd(),\n  });\n  if (result.error) {\n    console.error(err(`pai launch: failed to start claude: ${result.error.message}`));\n    process.exitCode = 1;\n    return;\n  }\n  process.exitCode = result.status ?? 0;\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 database    — SQLite registry database\n *   config.json          — PAI daemon config\n *   postgres-pai.sql     — Postgres dump (piped into psql via docker exec)\n *   federation database  — Legacy SQLite federation DB (if present)\n */\n\nimport {\n  existsSync,\n  readdirSync,\n  statSync,\n  copyFileSync,\n  mkdirSync,\n  readFileSync,\n  chmodSync,\n} from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { execFileSync, spawnSync } from \"node:child_process\";\nimport { createInterface } from \"node:readline\";\nimport { ok, warn, err, dim, bold } from \"../../lib/utils.js\";\nimport { loadConfig, paiConfigFilePath } from \"../../../daemon/config.js\";\nimport { inspectStorageBackup, restoreStorageFiles } from \"../../../storage/backup.js\";\nimport { backupsDirPath } from \"./backup.js\";\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nconst CONFIG_FILE = paiConfigFilePath();\nconst BACKUPS_DIR = backupsDirPath();\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\nexport async function runRestore(backupPath: string | undefined, opts: { postgres: boolean; yes: boolean }): Promise<void> {\n\n  // ------------------------------------------------------------------\n  // 1. Determine backup directory\n  // ------------------------------------------------------------------\n\n  let resolvedDir: string;\n\n  if (backupPath) {\n    resolvedDir = backupPath.startsWith(\"~\")\n      ? backupPath.replace(/^~/, homedir())\n      : backupPath;\n\n    if (!existsSync(resolvedDir)) {\n      console.error(err(`Backup directory not found: ${resolvedDir}`));\n      process.exitCode = 1;\n      return;\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.exitCode = 1;\n      return;\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 inventory = inspectStorageBackup(resolvedDir);\n  const hasConfig   = existsSync(join(resolvedDir, \"config.json\"));\n  const hasSql      = existsSync(join(resolvedDir, \"postgres-pai.sql\"));\n\n  console.log(`${bold(\"Backup contents:\")}`);\n  console.log(`  Registry DB          ${inventory.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 (inventory.hasFederation) {\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    return;\n  }\n\n  console.log();\n\n  const results: { label: string; status: string }[] = [];\n\n  // ------------------------------------------------------------------\n  // 4. Restore registry (+ legacy federation) SQLite DB, sqlite backend only\n  // ------------------------------------------------------------------\n\n  for (const r of restoreStorageFiles(loadConfig(), resolvedDir)) {\n    results.push({\n      label: r.label,\n      status: r.status === \"ok\" ? ok(\"restored\") : r.status === \"skipped\" ? warn(\"missing in backup — skipped\") : err(`failed: ${r.error}`),\n    });\n  }\n\n  // ------------------------------------------------------------------\n  // 5. Restore config.json\n  // ------------------------------------------------------------------\n\n  if (hasConfig) {\n    try {\n      mkdirSync(dirname(CONFIG_FILE), { recursive: true });\n      copyFileSync(join(resolvedDir, \"config.json\"), CONFIG_FILE);\n      chmodSync(CONFIG_FILE, 0o600);\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 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      execFileSync(\"docker\", [\"inspect\", DOCKER_CONTAINER, \"--format={{.State.Status}}\"], {\n        stdio: \"pipe\",\n      });\n\n      // Drop and recreate the database, then restore\n      execFileSync(\n        \"docker\",\n        [\n          \"exec\",\n          DOCKER_CONTAINER,\n          \"psql\",\n          \"-U\",\n          PG_USER,\n          \"-c\",\n          `DROP DATABASE IF EXISTS ${PG_DATABASE}; CREATE DATABASE ${PG_DATABASE} OWNER ${PG_USER};`,\n        ],\n        { stdio: \"pipe\" }\n      );\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.exitCode = 1;\n    return;\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 {\n  existsSync,\n  readFileSync,\n  writeFileSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { packageRoot } from \"../../../module-paths.js\";\nimport { execSync, spawnSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport { ok, warn, err, dim, bold } from \"../../lib/utils.js\";\nimport { resolveDaemonPid } from \"../../../daemon/daemon-pid.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  return packageRoot(import.meta.url);\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 @tekmidian/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 pid = resolveDaemonPid();\n  if (pid) {\n    const killed = capture(`kill -HUP ${pid}`);\n    if (killed !== null) {\n      success(`Sent SIGHUP to daemon (PID ${pid}).`);\n      return;\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\nexport async 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.exitCode = 1;\n    return;\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.exitCode = 1;\n    return;\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 * Lifecycle shortcuts and maintenance: pause, end, resume, clear-names, cd, notes, list, find,\n * shell-init, launch, backup, restore, update. Logic: ./lifecycle/ and ./session/.\n */\n\nimport { CliError, OPT, type CommandDef } from \"../define.js\";\nimport { legacy } from \"../lib/legacy.js\";\nimport { SHELL_INIT } from \"../lib/shell-init.js\";\nimport { cmdList as cmdNotesList } from \"./session/commands.js\";\nimport { cmdEnd } from \"./session/end.js\";\nimport { cmdGoto } from \"./session/goto.js\";\nimport { cmdPause } from \"./session/pause.js\";\nimport { cmdPauseAll } from \"./session/pause-all.js\";\nimport { runBackup } from \"./lifecycle/backup.js\";\nimport { runCd } from \"./lifecycle/cd.js\";\nimport { cmdFind } from \"./lifecycle/find.js\";\nimport { runLaunch } from \"./lifecycle/launch.js\";\nimport { runRestore } from \"./lifecycle/restore.js\";\nimport { runUpdate } from \"./lifecycle/update.js\";\nimport { clearNamesCmd } from \"./session-notes.js\";\n\nconst bodyOpts: CommandDef[\"options\"] = [\n  { name: \"body-file\", type: \"string\", value: \"FILE\", summary: \"File holding the checkpoint text to save ('-' reads standard input). Required unless --no-body\" },\n  { name: \"session-id\", type: \"string\", value: \"UUID\", summary: \"Claude Code session id; recorded as the handle for `claude --resume`\" },\n  { name: \"no-body\", type: \"boolean\", default: false, summary: \"Save a checkpoint with metadata only, no text\" },\n];\n\nconst checkpointOf = (c: Parameters<NonNullable<CommandDef[\"run\"]>>[0]) => ({\n  dryRun: c.dryRun,\n  bodyFile: c.opts[\"body-file\"] as string | undefined,\n  sessionId: c.opts[\"session-id\"] as string | undefined,\n  noBody: c.opts[\"no-body\"] === true,\n});\n\nexport const pauseTop: CommandDef = {\n  name: \"pause\",\n  summary: \"Save your place before leaving a session\",\n  description:\n    \"Writes a \\\"Continue\\\" block into the project's TODO.md so the next session knows where you stopped, then tells you how to leave safely.\\n\" +\n    \"`pai pause all` does it for every live session through AIBroker. Leave with /exit inside Claude Code; Ctrl+C skips the stop hook.\",\n  args: [{ name: \"TARGET\", summary: \"Only the word `all`: pause every live session\" }],\n  options: [\n    ...bodyOpts!,\n    { name: \"exit\", type: \"boolean\", default: false, summary: \"With `all`: also send /exit to each session after it has saved\" },\n    { name: \"only\", type: \"string\", value: \"NAME\", summary: \"With `all`: only sessions whose name or id contains NAME (to rehearse on one tab first)\" },\n    { name: \"wait\", type: \"number\", value: \"MS\", default: 180000, summary: \"With `all`: longest wait for a session to finish its checkpoint before /exit; busy ones are left open, never killed\" },\n    { ...OPT.dryRun, summary: \"Show what would be written, write nothing\" },\n  ],\n  examples: [\"pai pause --dry-run --no-body\", \"pai pause --body-file checkpoint.md\", \"pai pause all --dry-run\", \"pai pause all --only api --exit\"],\n  seeAlso: [\"pai end\", \"pai session handover\"],\n  daily: true,\n  run(c) {\n    const target = c.args.TARGET as string | undefined;\n    if (target === \"all\") {\n      return legacy(() =>\n        cmdPauseAll({ exit: c.opts.exit === true, dryRun: c.dryRun, only: c.opts.only as string | undefined, wait: c.opts.wait as number | undefined })\n      );\n    }\n    if (target !== undefined) throw new CliError(`Unknown target: ${target}. Did you mean 'pai pause all'?`, \"pai pause all\");\n    return legacy(() => cmdPause(checkpointOf(c)));\n  },\n};\n\nexport const endTop: CommandDef = {\n  name: \"end\",\n  summary: \"Finish the current session\",\n  description: \"Saves the checkpoint, marks the session note Completed and tells you how to leave. Leave with /exit inside Claude Code afterwards.\",\n  options: [...bodyOpts!, { ...OPT.dryRun, summary: \"Show what would be written, write nothing\" }],\n  examples: [\"pai end --dry-run --no-body\", \"pai end --body-file checkpoint.md\"],\n  seeAlso: [\"pai pause\"],\n  daily: true,\n  run: (c) => legacy(() => cmdEnd(checkpointOf(c))),\n};\n\nexport const resumeTop: CommandDef = {\n  name: \"resume\",\n  summary: \"Open a session by name or id (same as `pai open`)\",\n  description: \"Kept for old scripts. Resumes the session when possible, otherwise starts a fresh one in the same directory.\",\n  args: [{ name: \"NAME\", required: true, summary: \"Session name or the first characters of its id\" }],\n  examples: [\"pai resume my-session --dry-run\", \"pai resume 3a9f1c2e\"],\n  seeAlso: [\"pai open\"],\n  run: (c) => legacy(() => cmdGoto(c.args.NAME as string, { dryRun: c.dryRun })),\n};\n\nexport const cdTop: CommandDef = {\n  name: \"cd\",\n  summary: \"Print a project's directory (the shell wrapper changes into it)\",\n  description: \"Needs the shell integration: add `eval \\\"$(pai shell-init)\\\"` to your shell profile. If the project moved on disk, the registry is updated.\",\n  args: [{ name: \"PROJECT\", required: true, summary: \"Project name, slug or path\" }],\n  examples: [\"pai cd my-project\", \"cd \\\"$(pai cd my-project)\\\"\"],\n  seeAlso: [\"pai shell-init\"],\n  run: (c) => legacy(() => runCd(c.args.PROJECT as string)),\n};\n\nconst notesOpts: CommandDef[\"options\"] = [\n  { name: \"limit\", type: \"number\", value: \"N\", default: 20, summary: \"Most notes to show\" },\n  { name: \"status\", type: \"string\", value: \"STATE\", summary: \"Only notes in this state: open, completed or compacted\" },\n];\nconst notesRun: CommandDef[\"run\"] = (c) =>\n  legacy(() => cmdNotesList(c.args.PROJECT as string | undefined, { limit: String(c.opts.limit), status: c.opts.status as string | undefined }));\n\nexport const notesTop: CommandDef = {\n  name: \"notes\",\n  summary: \"List the markdown session notes\",\n  description: \"Shows the session notes PAI keeps per project, newest first, optionally for one project.\",\n  args: [{ name: \"PROJECT\", summary: \"Project slug (default: all projects)\" }],\n  options: notesOpts,\n  examples: [\"pai notes\", \"pai notes my-project --status open --limit 5\"],\n  seeAlso: [\"pai session info\", \"pai ls\"],\n  subcommands: [\n    {\n      name: \"list\",\n      summary: \"List the markdown session notes (same as `pai notes`)\",\n      description: \"Same as `pai notes`.\",\n      args: [{ name: \"PROJECT\", summary: \"Project slug (default: all projects)\" }],\n      options: notesOpts,\n      examples: [\"pai notes list\", \"pai notes list my-project\"],\n      run: notesRun,\n    },\n  ],\n  run: notesRun,\n};\n\nexport const listTop: CommandDef = { ...notesTop, name: \"list\", aliasOf: \"notes\", subcommands: undefined };\n\nexport const findTop: CommandDef = {\n  name: \"find\",\n  summary: \"Search your prompt history for sessions\",\n  description: \"Looks for the words in everything you typed to Claude Code and lists the sessions where they appeared.\",\n  args: [{ name: \"QUERY\", required: true, summary: \"Words to search for\" }],\n  options: [\n    { name: \"n\", short: \"n\", type: \"number\", value: \"COUNT\", default: 20, summary: \"Most sessions to show\" },\n    { ...OPT.json, summary: \"Print a JSON array\" },\n  ],\n  examples: ['pai find \"retry loop\"', \"pai find login -n 5 --json\"],\n  seeAlso: [\"pai ls\", \"pai open\"],\n  run: (c) => legacy(() => cmdFind(c.args.QUERY as string, { n: String(c.opts.n), json: c.opts.json === true })),\n};\n\nexport const shellInitTop: CommandDef = {\n  name: \"shell-init\",\n  summary: \"Print the shell integration code\",\n  description:\n    \"Prints shell functions that let `pai cd` and the picker change your shell's directory. Add this line to ~/.zshrc or ~/.bashrc:\\n\" +\n    \"  eval \\\"$(pai shell-init)\\\"\",\n  examples: ['eval \"$(pai shell-init)\"', \"pai shell-init\"],\n  seeAlso: [\"pai cd\"],\n  run() {\n    process.stdout.write(SHELL_INIT);\n  },\n};\n\nexport const launchTop: CommandDef = {\n  name: \"launch\",\n  summary: \"Start Claude Code on a chosen provider (for scripts)\",\n  description:\n    \"Starts a fresh Claude Code process in the current terminal on the provider and model you name. A running session cannot change provider, so this always starts a new one.\\n\" +\n    \"Without --provider in a terminal it shows a numbered table to pick from; --list prints the table and stops. Arguments after `--` go to claude unchanged.\\n\" +\n    \"For everyday use prefer `pai new` and `pai open`.\",\n  args: [],\n  options: [\n    { ...OPT.provider, summary: \"Provider to start on (default: pick from the table)\" },\n    { ...OPT.model },\n    { name: \"list\", type: \"boolean\", default: false, summary: \"Print the provider and model table and stop\" },\n    { name: \"current\", type: \"boolean\", default: false, summary: \"With --list: mark this session's own provider and model\" },\n    { ...OPT.dryRun },\n  ],\n  passthrough: true,\n  examples: [\"pai launch --list\", \"pai launch --provider glm --dry-run\", \"pai launch --provider glm -- --resume 3a9f1c2e\"],\n  seeAlso: [\"pai new\", \"pai open\", \"pai providers\"],\n  run: (c) =>\n    legacy(() =>\n      runLaunch(c.rest, {\n        provider: c.opts.provider as string | undefined,\n        model: c.opts.model as string | undefined,\n        list: c.opts.list === true,\n        current: c.opts.current === true,\n        dryRun: c.dryRun,\n      })\n    ),\n};\n\nexport const backupTop: CommandDef = {\n  name: \"backup\",\n  summary: \"Back up the registry, config and Postgres database\",\n  description: \"Copies the registry database and config into a dated folder under PAI_HOME/backups and, unless told not to, dumps the Postgres database from its Docker container.\",\n  options: [{ name: \"no-postgres\", type: \"boolean\", default: false, summary: \"Skip the Postgres dump (faster; registry and config only)\" }],\n  examples: [\"pai backup\", \"pai backup --no-postgres\"],\n  seeAlso: [\"pai restore\"],\n  run: (c) => legacy(() => runBackup({ postgres: c.opts[\"no-postgres\"] !== true })),\n};\n\nexport const restoreTop: CommandDef = {\n  name: \"restore\",\n  summary: \"Restore PAI data from a backup\",\n  description: \"Overwrites the current registry, config and (unless skipped) the Postgres database with a backup made by `pai backup`. Without a path the newest backup is used. Asks before overwriting.\",\n  args: [{ name: \"BACKUP\", summary: \"Backup folder (default: the newest one)\" }],\n  options: [\n    { name: \"no-postgres\", type: \"boolean\", default: false, summary: \"Do not restore the Postgres database\" },\n    { name: \"yes\", type: \"boolean\", default: false, summary: \"Do not ask for confirmation\" },\n  ],\n  examples: [\"pai restore\", \"pai restore ~/.claude/pai/backups/20260101-120000 --no-postgres --yes\"],\n  seeAlso: [\"pai backup\"],\n  run: (c) => legacy(() => runRestore(c.args.BACKUP as string | undefined, { postgres: c.opts[\"no-postgres\"] !== true, yes: c.opts.yes === true })),\n};\n\nexport const updateTop: CommandDef = {\n  name: \"update\",\n  summary: \"Update PAI to the latest version\",\n  description: \"Pulls the latest source, rebuilds and restarts the daemon. Your local customisations are kept.\",\n  examples: [\"pai update\", \"pai update && pai daemon status\"],\n  seeAlso: [\"pai daemon status\"],\n  run: () => legacy(() => runUpdate()),\n};\n\nexport { clearNamesCmd };\n","/**\n * pai hooks-db <sub-command>\n *\n * Backing implementation for the session-stop and pre-compact shell hooks\n * (src/hooks/session-stop.sh, src/hooks/pre-compact.sh). Each hook used to\n * shell out to the `sqlite3` CLI directly against the registry database; this\n * subcommand replaces those calls with one `pai` process that goes through\n * RegistryBackend, so hooks work against either SQLite or Postgres\n * (docs/design/postgres-only.md §6/§8, unit 8).\n *\n * Never throws to the caller's shell: exits 0 regardless of outcome — the\n * hooks already run detached/best-effort and must never block a session.\n *\n *   pai hooks-db session-stop <projectSlug>\n *   pai hooks-db pre-compact  <projectSlug>\n */\n\nimport { getRegistryBackend } from \"../../storage/factory.js\";\nimport type { RegistryBackend, Session } from \"../../storage/registry-interface.js\";\nimport type { CommandDef } from \"../define.js\";\n\n// ---------------------------------------------------------------------------\n// Shared lookup\n// ---------------------------------------------------------------------------\n\n/** Latest session for the project whose status is one of `statuses`, newest created_at first — mirrors the old `ORDER BY created_at DESC LIMIT 1` SQL. */\nasync function findLatestSessionByStatus(\n  backend: RegistryBackend,\n  projectId: number,\n  statuses: Session[\"status\"][]\n): Promise<Session | null> {\n  const sessions = await backend.listSessionsForProject(projectId, { orderBy: \"created_desc\" });\n  return sessions.find((s) => statuses.includes(s.status)) ?? null;\n}\n\n// ---------------------------------------------------------------------------\n// session-stop: mark the latest open/compacted session completed\n// ---------------------------------------------------------------------------\n\nexport async function runSessionStopHook(backend: RegistryBackend, projectSlug: string): Promise<void> {\n  const project = await backend.getProjectBySlug(projectSlug);\n  if (!project) return;\n\n  const session = await findLatestSessionByStatus(backend, project.id, [\"open\", \"compacted\"]);\n  if (!session) return;\n\n  await backend.updateSessionStatus(session.id, \"completed\", { closedAt: Date.now() });\n}\n\n// ---------------------------------------------------------------------------\n// pre-compact: mark the latest open session compacted, log the event\n// ---------------------------------------------------------------------------\n\nexport async function runPreCompactHook(backend: RegistryBackend, projectSlug: string): Promise<void> {\n  const project = await backend.getProjectBySlug(projectSlug);\n  if (!project) return;\n\n  const session = await findLatestSessionByStatus(backend, project.id, [\"open\"]);\n  if (session) {\n    await backend.updateSessionStatus(session.id, \"compacted\");\n  }\n\n  await backend.appendCompactionLog({\n    projectId: project.id,\n    sessionId: session ? session.id : null,\n    trigger: \"precompact\",\n    filesWritten: \"\",\n    tokenCount: null,\n    createdAt: Date.now(),\n  });\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nasync function hook(run: (backend: RegistryBackend, slug: string) => Promise<void>, slug: string): Promise<number> {\n  try {\n    await run(await getRegistryBackend(), slug);\n  } catch {\n    // never surface a failure to the calling hook script\n  }\n  return 0;\n}\n\nexport const hooksDbCommand: CommandDef = {\n  name: \"hooks-db\",\n  summary: \"Registry updates for the stop and pre-compact hooks\",\n  description:\n    \"Called by the shell hooks, not by hand. Marks the latest session of a project completed (session-stop) or compacted (pre-compact). Always exits 0 so a hook can never block a session.\",\n  examples: [\"pai hooks-db session-stop my-project\", \"pai hooks-db pre-compact my-project\"],\n  seeAlso: [\"pai session handover\"],\n  subcommands: [\n    {\n      name: \"session-stop\",\n      summary: \"Mark the project's latest session completed\",\n      description: \"Used by the stop hook. Exits 0 whatever happens.\",\n      args: [{ name: \"PROJECT_SLUG\", required: true, summary: \"Project slug\" }],\n      examples: [\"pai hooks-db session-stop my-project\", \"pai hooks-db session-stop \\\"$PROJECT_SLUG\\\"\"],\n      run: (c) => hook(runSessionStopHook, c.args.PROJECT_SLUG as string),\n    },\n    {\n      name: \"pre-compact\",\n      summary: \"Mark the project's open session compacted and log it\",\n      description: \"Used by the pre-compact hook. Exits 0 whatever happens.\",\n      args: [{ name: \"PROJECT_SLUG\", required: true, summary: \"Project slug\" }],\n      examples: [\"pai hooks-db pre-compact my-project\", \"pai hooks-db pre-compact \\\"$PROJECT_SLUG\\\"\"],\n      run: (c) => hook(runPreCompactHook, c.args.PROJECT_SLUG as string),\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 } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport { readMainConfigRaw, writeMainConfigRaw } from \"../../../daemon/config.js\";\nimport { exitAfterFlush } from \"../../lib/exit.js\";\nimport { packageRoot } from \"../../../module-paths.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    // Control can't return here — readline is still blocking on input, so\n    // there is no natural exit to fall through to. exitAfterFlush() waits\n    // for the lines above to actually reach the OS before terminating.\n    void exitAfterFlush(0);\n  });\n\n  return rl;\n}\n\nexport type Rl = ReturnType<typeof createRl>;\n\n/** Unattended-mode switches (`pai setup --yes --storage …`), set once by the command. */\nexport const setupOptions: { yes: boolean; storage?: \"sqlite\" | \"postgres\" } = { yes: false };\n\n/**\n * The one place every wizard question passes through. Under --yes it answers\n * `defaultValue` without touching stdin; a question with no default cannot be\n * answered unattended, so it fails naming the flag instead of returning \"\".\n */\nexport async function prompt(rl: Rl, question: string, defaultValue?: string): Promise<string> {\n  if (setupOptions.yes) {\n    if (defaultValue === undefined) {\n      throw new Error(`\"${question.trim()}\" has no default; cannot answer it with --yes. Run without --yes, or skip the feature that asks it.`);\n    }\n    console.log(chalk.dim(`  ${question.trim()} -> ${defaultValue || \"(default)\"}`));\n    return defaultValue;\n  }\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\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\n/**\n * This file holds the Postgres connection string, the storage backend choice,\n * notification routing and any tracker API token — none of which the user can\n * reconstruct from memory. It previously returned {} on a parse failure and\n * then overwrote the file, so a damaged config was replaced by whatever the\n * current command happened to be setting.\n */\nexport function readConfigRaw(): Record<string, unknown> {\n  return readMainConfigRaw();\n}\n\nexport function writeConfigRaw(data: Record<string, unknown>): void {\n  writeMainConfigRaw(data);\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  return join(packageRoot(import.meta.url), \"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  return join(packageRoot(import.meta.url), \"templates\");\n}\n\nexport function getHooksDir(): string {\n  return join(packageRoot(import.meta.url), \"src\", \"hooks\");\n}\n\nexport function getDistHooksDir(): string {\n  return join(getDistDir(), \"hooks\");\n}\n\nexport function getDistDir(): string {\n  return join(packageRoot(import.meta.url), \"dist\");\n}\n\nexport function getStatuslineScript(): string | null {\n  const p = join(packageRoot(import.meta.url), \"statusline-command.sh\");\n  return existsSync(p) ? p : null;\n}\n\nexport function getTabColorScript(): string | null {\n  const p = join(packageRoot(import.meta.url), \"tab-color-command.sh\");\n  return existsSync(p) ? p : 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, setupOptions, 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  // Unattended: --storage wins, else Postgres only when the default local one answers.\n  const forced =\n    setupOptions.storage ??\n    (setupOptions.yes\n      ? (await testPostgresConnection(\"postgresql://pai:pai@localhost:5432/pai\")) ? \"postgres\" : \"sqlite\"\n      : undefined);\n\n  const choice = forced ? (forced === \"sqlite\" ? 1 : 0) : 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, promptYesNo, readConfigRaw } from \"../utils.js\";\nimport { CONFIG_FILE } from \"../../../../daemon/config.js\";\nimport { detectBackends, OLLAMA_ID, OLLAMA_DEFAULT_MODEL, OLLAMA_DEFAULT_URL } from \"../../../../memory/backends/index.js\";\n\n/**\n * Probe the embedding backends (also under --yes). A fresh install adopts the\n * fastest available one; an existing install (embeddingModel already set) may\n * hold vectors from the current backend, so it is only told what is available.\n */\nasync function stepBackend(rl: Rl, fresh: boolean): Promise<Record<string, unknown>> {\n  const { results, recommended } = await detectBackends();\n  line();\n  for (const r of results) console.log(`  ${r.ok ? c.ok(r.id) : c.dim(r.id)}: ${r.reason}`);\n  if (!recommended) return {};\n  if (!fresh) {\n    console.log(c.dim(`  Recommended: ${recommended}. Switch with \\`pai memory backend use ${recommended}\\` + \\`pai memory reembed\\`.`));\n    return {};\n  }\n  if (recommended === OLLAMA_ID && (await promptYesNo(rl, \"Use Ollama (GPU, several times faster)?\", true))) {\n    return { embedding: { backend: OLLAMA_ID, model: OLLAMA_DEFAULT_MODEL, ollama: { baseUrl: OLLAMA_DEFAULT_URL } } };\n  }\n  return {};\n}\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, ...(await stepBackend(rl, false)) };\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_FILE}`));\n  }\n\n  // Backends embed the Arctic model only; other choices stay on the in-process path.\n  const backendUpdate = selectedModel === models[0] ? await stepBackend(rl, true) : {};\n  return { embeddingModel: selectedModel ?? \"none\", ...backendUpdate };\n}\n","/** Step 4: CLAUDE.md generation from PAI template. */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\nimport { CONFIG_DIR } from \"../../../../daemon/config.js\";\nimport { c, line, section, type Rl, promptYesNo, getTemplatesDir } from \"../utils.js\";\n\nexport async function stepClaudeMd(rl: Rl): Promise<boolean> {\n  section(\"Step 4: Agent Configuration (CLAUDE.md)\");\n  line();\n  line(\"  PAI ships a CLAUDE.md template with agent orchestration patterns:\");\n  line(\"  swarm mode, model escalation, parallel execution, quality standards.\");\n  line();\n\n  const claudeDir = join(homedir(), \".claude\");\n  const claudeMd = join(claudeDir, \"CLAUDE.md\");\n  const agentPrefs = join(CONFIG_DIR, \"agent-prefs.md\");\n  const templatesDir = getTemplatesDir();\n  const templatePath = join(templatesDir, \"claude-md.template.md\");\n\n  if (!existsSync(templatePath)) {\n    console.log(c.warn(\"Template not found: \" + templatePath));\n    console.log(c.dim(\"  Skipping CLAUDE.md generation. You can copy it manually from templates/.\"));\n    return false;\n  }\n\n  if (existsSync(claudeMd)) {\n    const content = readFileSync(claudeMd, \"utf-8\");\n    const isGenerated = content.includes(\"Generated by PAI Setup\");\n    if (isGenerated) {\n      console.log(c.dim(\"  Found existing PAI-generated CLAUDE.md.\"));\n    } else {\n      console.log(c.yellow(\"  Found existing CLAUDE.md (custom, not PAI-generated).\"));\n      console.log(c.dim(\"  A backup will be created before overwriting.\"));\n    }\n    line();\n\n    const overwrite = await promptYesNo(rl, \"Update ~/.claude/CLAUDE.md with the latest PAI template?\", isGenerated);\n    if (!overwrite) {\n      console.log(c.dim(\"  Keeping existing CLAUDE.md unchanged.\"));\n      return false;\n    }\n\n    if (!isGenerated) {\n      const backupPath = claudeMd + \".backup\";\n      writeFileSync(backupPath, content, \"utf-8\");\n      console.log(c.ok(`Backed up existing CLAUDE.md to ${backupPath}`));\n    }\n  }\n\n  let template = readFileSync(templatePath, \"utf-8\");\n  template = template.replace(/\\$\\{HOME\\}/g, homedir());\n\n  if (!existsSync(claudeDir)) {\n    mkdirSync(claudeDir, { recursive: true });\n  }\n  writeFileSync(claudeMd, template, \"utf-8\");\n\n  line();\n  console.log(c.ok(\"Generated ~/.claude/CLAUDE.md from PAI template.\"));\n\n  if (!existsSync(agentPrefs)) {\n    line();\n    line(\"  For personal settings (project mappings, notification preferences),\");\n    line(\"  copy the example and customize:\");\n    line();\n    console.log(chalk.cyan(`    cp ${templatesDir}/agent-prefs.example.md ${agentPrefs}`));\n    line();\n\n    const createPrefs = await promptYesNo(rl, \"Copy the example agent-prefs.md now?\", true);\n    if (createPrefs) {\n      const examplePath = join(templatesDir, \"agent-prefs.example.md\");\n      if (existsSync(examplePath)) {\n        if (!existsSync(CONFIG_DIR)) {\n          mkdirSync(CONFIG_DIR, { recursive: true });\n        }\n        writeFileSync(agentPrefs, readFileSync(examplePath, \"utf-8\"), \"utf-8\");\n        console.log(c.ok(`Created ${agentPrefs}`));\n        console.log(c.dim(\"  Edit this file to add your personal preferences and project mappings.\"));\n      } else {\n        console.log(c.warn(\"Example file not found. Create agent-prefs.md manually.\"));\n      }\n    }\n  } else {\n    console.log(c.dim(\"  Personal preferences: \" + agentPrefs));\n  }\n\n  return true;\n}\n","/** Step 5: PAI SKILL.md installation to ~/.claude/skills/PAI/. */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getTemplatesDir } from \"../utils.js\";\n\nexport async function stepPaiSkill(rl: Rl): Promise<boolean> {\n  section(\"Step 5: PAI Skill Installation\");\n  line();\n  line(\"  PAI ships a SKILL.md that tells Claude Code how to invoke PAI commands.\");\n  line();\n\n  const templatesDir = getTemplatesDir();\n  const templatePath = join(templatesDir, \"pai-skill.template.md\");\n\n  if (!existsSync(templatePath)) {\n    console.log(c.warn(\"Skill template not found: \" + templatePath));\n    console.log(c.dim(\"  Skipping PAI skill installation.\"));\n    return false;\n  }\n\n  const skillDir = join(homedir(), \".claude\", \"skills\", \"PAI\");\n  const skillFile = join(skillDir, \"SKILL.md\");\n\n  if (existsSync(skillFile)) {\n    const content = readFileSync(skillFile, \"utf-8\");\n    const isGenerated = content.includes(\"Generated by PAI Setup\");\n    if (isGenerated) {\n      console.log(c.dim(\"  Found existing PAI-generated SKILL.md.\"));\n    } else {\n      console.log(c.yellow(\"  Found existing SKILL.md (not PAI-generated).\"));\n      console.log(c.dim(\"  A backup will be created before overwriting.\"));\n    }\n    line();\n\n    const overwrite = await promptYesNo(rl, \"Update ~/.claude/skills/PAI/SKILL.md with the latest PAI skill?\", isGenerated);\n    if (!overwrite) {\n      console.log(c.dim(\"  Keeping existing SKILL.md unchanged.\"));\n      return false;\n    }\n\n    if (!isGenerated) {\n      const backupPath = skillFile + \".backup\";\n      writeFileSync(backupPath, content, \"utf-8\");\n      console.log(c.ok(`Backed up existing SKILL.md to ${backupPath}`));\n    }\n  } else {\n    const install = await promptYesNo(rl, \"Install PAI skill to ~/.claude/skills/PAI/SKILL.md?\", true);\n    if (!install) {\n      console.log(c.dim(\"  Skipping PAI skill installation.\"));\n      return false;\n    }\n  }\n\n  let template = readFileSync(templatePath, \"utf-8\");\n  template = template.replace(/\\$\\{HOME\\}/g, homedir());\n\n  if (!existsSync(skillDir)) {\n    mkdirSync(skillDir, { recursive: true });\n  }\n  writeFileSync(skillFile, template, \"utf-8\");\n\n  line();\n  console.log(c.ok(\"Installed ~/.claude/skills/PAI/SKILL.md\"));\n  return true;\n}\n","/** Step 6: AI Steering Rules installation to ~/.claude/skills/PAI/. */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getTemplatesDir } from \"../utils.js\";\n\nexport async function stepAiSteeringRules(rl: Rl): Promise<boolean> {\n  section(\"Step 6: AI Steering Rules\");\n  line();\n  line(\"  PAI ships a set of universal behavioral rules for AI assistants:\");\n  line(\"  surgical fixes, verification before assertion, root cause analysis,\");\n  line(\"  and honest failure modes. These rules load at startup as a skill.\");\n  line();\n\n  const templatesDir = getTemplatesDir();\n  const templatePath = join(templatesDir, \"ai-steering-rules.template.md\");\n\n  if (!existsSync(templatePath)) {\n    console.log(c.warn(\"AI steering rules template not found: \" + templatePath));\n    console.log(c.dim(\"  Skipping AI steering rules installation.\"));\n    return false;\n  }\n\n  const skillDir = join(homedir(), \".claude\", \"skills\", \"PAI\");\n  const skillFile = join(skillDir, \"AI-STEERING-RULES.md\");\n\n  if (existsSync(skillFile)) {\n    const content = readFileSync(skillFile, \"utf-8\");\n    const isGenerated = content.includes(\"Generated by PAI Setup\");\n    if (isGenerated) {\n      console.log(c.dim(\"  Found existing PAI-generated AI-STEERING-RULES.md.\"));\n    } else {\n      console.log(c.yellow(\"  Found existing AI-STEERING-RULES.md (not PAI-generated).\"));\n      console.log(c.dim(\"  A backup will be created before overwriting.\"));\n    }\n    line();\n\n    const overwrite = await promptYesNo(rl, \"Update ~/.claude/skills/PAI/AI-STEERING-RULES.md with the latest rules?\", isGenerated);\n    if (!overwrite) {\n      console.log(c.dim(\"  Keeping existing AI-STEERING-RULES.md unchanged.\"));\n      return false;\n    }\n\n    if (!isGenerated) {\n      const backupPath = skillFile + \".backup\";\n      writeFileSync(backupPath, content, \"utf-8\");\n      console.log(c.ok(`Backed up existing AI-STEERING-RULES.md to ${backupPath}`));\n    }\n  } else {\n    const install = await promptYesNo(rl, \"Install AI steering rules to ~/.claude/skills/PAI/AI-STEERING-RULES.md?\", true);\n    if (!install) {\n      console.log(c.dim(\"  Skipping AI steering rules installation.\"));\n      return false;\n    }\n  }\n\n  const template = readFileSync(templatePath, \"utf-8\");\n\n  if (!existsSync(skillDir)) {\n    mkdirSync(skillDir, { recursive: true });\n  }\n  writeFileSync(skillFile, template, \"utf-8\");\n\n  line();\n  console.log(c.ok(\"Installed ~/.claude/skills/PAI/AI-STEERING-RULES.md\"));\n  return true;\n}\n","/**\n * Step 7: Install PAI MCP skill stubs to ~/.claude/skills/.\n *\n * Each PAI MCP prompt has a SKILL.md stub generated at build time\n * (dist/skills/<Name>/SKILL.md). This step symlinks (macOS/Linux) or\n * copies (Windows) them into Claude Code's skill discovery directory.\n *\n * Skills MUST live at ~/.claude/skills/<Name>/SKILL.md (top level),\n * NOT under a user/ subdirectory — Claude Code only scans one level deep.\n *\n * Symlinks keep the stubs in sync — rebuilding PAI updates them automatically.\n * Existing non-symlink directories with the same name are never overwritten.\n */\n\nimport {\n  existsSync,\n  readdirSync,\n  symlinkSync,\n  lstatSync,\n  readlinkSync,\n  copyFileSync,\n  mkdirSync,\n  unlinkSync,\n} from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getDistDir } from \"../utils.js\";\n\nexport async function stepSkillStubs(rl: Rl): Promise<boolean> {\n  section(\"Step 7: MCP Skill Stubs\");\n  line();\n  line(\"  PAI MCP prompts need SKILL.md stubs so Claude Code can discover them.\");\n  line(\"  Stubs are symlinked from dist/skills/ — rebuilding PAI keeps them in sync.\");\n  line();\n\n  const distSkills = join(getDistDir(), \"skills\");\n\n  if (!existsSync(distSkills)) {\n    console.log(c.warn(\"dist/skills/ not found — run `bun run build` first.\"));\n    return false;\n  }\n\n  const skillNames = readdirSync(distSkills).filter((name) => {\n    const p = join(distSkills, name);\n    return existsSync(join(p, \"SKILL.md\"));\n  });\n\n  if (skillNames.length === 0) {\n    console.log(c.warn(\"No skill stubs found in dist/skills/.\"));\n    return false;\n  }\n\n  console.log(c.dim(`  Found ${skillNames.length} skill stubs to install.`));\n  line();\n\n  const install = await promptYesNo(\n    rl,\n    `Symlink ${skillNames.length} PAI skill stubs to ~/.claude/skills/?`,\n    true,\n  );\n  if (!install) {\n    console.log(c.dim(\"  Skipping skill stub installation.\"));\n    return false;\n  }\n\n  const targetBase = join(homedir(), \".claude\", \"skills\");\n  mkdirSync(targetBase, { recursive: true });\n\n  // Clean up stale symlinks from old user/ location\n  const oldUserBase = join(targetBase, \"user\");\n  if (existsSync(oldUserBase)) {\n    for (const name of skillNames) {\n      const oldTarget = join(oldUserBase, name);\n      if (existsSync(oldTarget) && lstatSync(oldTarget).isSymbolicLink()) {\n        unlinkSync(oldTarget);\n      }\n    }\n  }\n\n  const useSymlinks = platform() !== \"win32\";\n  let installed = 0;\n  let skipped = 0;\n  let updated = 0;\n\n  for (const name of skillNames) {\n    const source = resolve(join(distSkills, name));\n    const target = join(targetBase, name);\n\n    // If target exists and is not a symlink, never overwrite (user's own skill)\n    if (existsSync(target) && !lstatSync(target).isSymbolicLink()) {\n      console.log(c.dim(`  SKIP ${name} (existing user skill, not a symlink)`));\n      skipped++;\n      continue;\n    }\n\n    // If target is an existing symlink, check if it already points to the right place\n    if (existsSync(target) && lstatSync(target).isSymbolicLink()) {\n      const currentTarget = readlinkSync(target);\n      if (resolve(currentTarget) === source) {\n        // Already correct\n        installed++;\n        continue;\n      }\n      // Stale symlink — remove and recreate\n      unlinkSync(target);\n      updated++;\n    }\n\n    if (useSymlinks) {\n      symlinkSync(source, target);\n    } else {\n      // Windows: copy the SKILL.md\n      mkdirSync(target, { recursive: true });\n      copyFileSync(join(source, \"SKILL.md\"), join(target, \"SKILL.md\"));\n    }\n    installed++;\n  }\n\n  line();\n  const method = useSymlinks ? \"symlinked\" : \"copied\";\n  if (updated > 0) {\n    console.log(c.ok(`${installed} stubs ${method}, ${updated} updated, ${skipped} skipped (user skills).`));\n  } else {\n    console.log(c.ok(`${installed} skill stubs ${method} to ~/.claude/skills/.`));\n    if (skipped > 0) {\n      console.log(c.dim(`  ${skipped} skipped (existing user skills not managed by PAI).`));\n    }\n  }\n  return true;\n}\n","/** Step 7: Shell lifecycle hooks installation (pre-compact, session-stop, statusline). */\n\nimport {\n  existsSync, lstatSync, readlinkSync, symlinkSync, unlinkSync,\n  copyFileSync, chmodSync, mkdirSync,\n} from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getHooksDir, getStatuslineScript, getTabColorScript } from \"../utils.js\";\n\nconst useSymlinks = platform() !== \"win32\";\n\nexport async function stepHooks(rl: Rl): Promise<boolean> {\n  section(\"Step 7: Lifecycle Hooks\");\n  line();\n  line(\"  PAI hooks fire on session stop and context compaction to save state,\");\n  line(\"  update notes, and display live statusline information.\");\n  if (useSymlinks) {\n    line(\"  Files are symlinked so they auto-update when PAI is rebuilt.\");\n  } else {\n    line(\"  Files are copied (Windows — re-run setup after PAI updates).\");\n  }\n  line();\n\n  const install = await promptYesNo(rl, \"Install PAI lifecycle hooks (session stop, pre-compact, statusline)?\", true);\n  if (!install) {\n    console.log(c.dim(\"  Skipping hook installation.\"));\n    return false;\n  }\n\n  const hooksDir = getHooksDir();\n  const statuslineSrc = getStatuslineScript();\n  const tabColorSrc = getTabColorScript();\n\n  const claudeDir = join(homedir(), \".claude\");\n  const hooksTarget = join(claudeDir, \"Hooks\");\n\n  if (!existsSync(hooksTarget)) {\n    mkdirSync(hooksTarget, { recursive: true });\n  }\n\n  let anyInstalled = false;\n\n  function installLink(src: string, dest: string, label: string): void {\n    if (!existsSync(src)) {\n      console.log(c.warn(`  Source not found: ${src}`));\n      return;\n    }\n\n    const absSrc = resolve(src);\n\n    // Check existing target\n    if (existsSync(dest) || (lstatSync(dest, { throwIfNoEntry: false })?.isSymbolicLink?.())) {\n      try {\n        const stat = lstatSync(dest);\n        if (stat.isSymbolicLink()) {\n          if (resolve(readlinkSync(dest)) === absSrc) {\n            console.log(c.dim(`  Current: ${label}`));\n            return;\n          }\n          // Stale symlink — replace\n          unlinkSync(dest);\n        } else if (stat.isFile()) {\n          // Old copy — replace with symlink\n          unlinkSync(dest);\n        } else {\n          return; // Don't touch directories\n        }\n      } catch {\n        // lstatSync failed — target doesn't exist, proceed\n      }\n    }\n\n    if (useSymlinks) {\n      symlinkSync(absSrc, dest);\n    } else {\n      copyFileSync(src, dest);\n      chmodSync(dest, 0o755);\n    }\n    const verb = useSymlinks ? \"Linked\" : \"Installed\";\n    console.log(c.ok(`${verb}: ${label}`));\n    anyInstalled = true;\n  }\n\n  line();\n  installLink(join(hooksDir, \"pre-compact.sh\"), join(hooksTarget, \"pai-pre-compact.sh\"), \"pai-pre-compact.sh\");\n  installLink(join(hooksDir, \"session-stop.sh\"), join(hooksTarget, \"pai-session-stop.sh\"), \"pai-session-stop.sh\");\n  installLink(join(hooksDir, \"session-autosave.sh\"), join(hooksTarget, \"pai-session-autosave.sh\"), \"pai-session-autosave.sh\");\n\n  if (statuslineSrc) {\n    installLink(statuslineSrc, join(claudeDir, \"statusline-command.sh\"), \"statusline-command.sh\");\n  } else {\n    console.log(c.warn(\"  statusline-command.sh not found — skipping statusline.\"));\n  }\n\n  if (platform() !== \"darwin\") {\n    console.log(c.dim(\"  Skipping tab-color-command.sh (iTerm2 only).\"));\n  } else if (tabColorSrc) {\n    installLink(tabColorSrc, join(claudeDir, \"tab-color-command.sh\"), \"tab-color-command.sh\");\n  } else {\n    console.log(c.warn(\"  tab-color-command.sh not found — skipping tab color.\"));\n  }\n\n  return anyInstalled;\n}\n","/** Step 7b: TypeScript (.mjs) hooks installation to ~/.claude/Hooks/. */\n\nimport {\n  existsSync, readdirSync, lstatSync, readlinkSync,\n  symlinkSync, unlinkSync, copyFileSync, chmodSync, mkdirSync,\n} from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { c, line, section, type Rl, promptYesNo, getDistHooksDir } from \"../utils.js\";\n\nconst useSymlinks = platform() !== \"win32\";\n\nexport async function stepTsHooks(rl: Rl): Promise<boolean> {\n  section(\"Step 7b: TypeScript Hooks Installation\");\n  line();\n  line(\"  PAI ships compiled TypeScript hooks (.mjs) that fire on session events,\");\n  line(\"  tool use, and context compaction to capture context and update notes.\");\n  if (useSymlinks) {\n    line(\"  Files are symlinked so they auto-update when PAI is rebuilt.\");\n  } else {\n    line(\"  Files are copied (Windows — re-run setup after PAI updates).\");\n  }\n  line();\n\n  const install = await promptYesNo(rl, \"Install PAI TypeScript hooks to ~/.claude/Hooks/?\", true);\n  if (!install) {\n    console.log(c.dim(\"  Skipping TypeScript hooks installation.\"));\n    return false;\n  }\n\n  const distHooksDir = getDistHooksDir();\n\n  if (!existsSync(distHooksDir)) {\n    console.log(c.warn(`  dist/hooks/ directory not found at: ${distHooksDir}`));\n    console.log(c.dim(\"  Build the package first: bun run build\"));\n    return false;\n  }\n\n  const claudeDir = join(homedir(), \".claude\");\n  const hooksTarget = join(claudeDir, \"Hooks\");\n\n  if (!existsSync(hooksTarget)) {\n    mkdirSync(hooksTarget, { recursive: true });\n  }\n\n  let allFiles: string[];\n  try {\n    allFiles = readdirSync(distHooksDir).filter((f) => f.endsWith(\".mjs\"));\n  } catch (e) {\n    console.log(c.warn(`  Could not read dist/hooks/: ${e}`));\n    return false;\n  }\n\n  if (allFiles.length === 0) {\n    console.log(c.warn(\"  No .mjs files found in dist/hooks/. Build first: bun run build\"));\n    return false;\n  }\n\n  line();\n  let linkedCount = 0;\n  let skippedCount = 0;\n  let cleanedCount = 0;\n\n  for (const filename of allFiles) {\n    const src = resolve(join(distHooksDir, filename));\n    const dest = join(hooksTarget, filename);\n\n    // Check existing target\n    let needsLink = true;\n    if (existsSync(dest) || lstatSync(dest, { throwIfNoEntry: false })?.isSymbolicLink?.()) {\n      try {\n        const stat = lstatSync(dest);\n        if (stat.isSymbolicLink()) {\n          if (resolve(readlinkSync(dest)) === src) {\n            console.log(c.dim(`  Current: ${filename}`));\n            skippedCount++;\n            needsLink = false;\n          } else {\n            unlinkSync(dest); // Stale symlink\n          }\n        } else if (stat.isFile()) {\n          unlinkSync(dest); // Old copy — replace with symlink\n        }\n      } catch {\n        // lstatSync failed — target doesn't exist\n      }\n    }\n\n    if (needsLink) {\n      if (useSymlinks) {\n        symlinkSync(src, dest);\n      } else {\n        copyFileSync(src, dest);\n        chmodSync(dest, 0o755);\n      }\n      const verb = useSymlinks ? \"Linked\" : \"Installed\";\n      console.log(c.ok(`${verb}: ${filename}`));\n      linkedCount++;\n    }\n\n    // Clean up stale .ts files from pre-build era\n    const staleTsPath = join(hooksTarget, filename.replace(/\\.mjs$/, \".ts\"));\n    if (existsSync(staleTsPath)) {\n      unlinkSync(staleTsPath);\n      console.log(c.ok(`Cleaned up stale: ${filename.replace(/\\.mjs$/, \".ts\")}`));\n      cleanedCount++;\n    }\n  }\n\n  line();\n  if (linkedCount > 0 || cleanedCount > 0) {\n    const parts = [];\n    const verb = useSymlinks ? \"linked\" : \"installed\";\n    if (linkedCount > 0) parts.push(`${linkedCount} hook(s) ${verb}`);\n    if (skippedCount > 0) parts.push(`${skippedCount} current`);\n    if (cleanedCount > 0) parts.push(`${cleanedCount} stale .ts file(s) cleaned up`);\n    console.log(c.ok(parts.join(\", \") + \".\"));\n  } else {\n    console.log(c.dim(`  All ${skippedCount} hook(s) already current.`));\n  }\n\n  return linkedCount > 0 || cleanedCount > 0;\n}\n","/** 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 \"../../../lib/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 adapterDir = join(homedir(), \".claude\");\n\n  const result = mergeSettings({\n    env: {\n      ADAPTER_DIR: adapterDir,\n      CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: \"80\",\n      CLAUDE_CODE_MAX_OUTPUT_TOKENS: \"64000\",\n      DA: daName,\n    },\n    hooks: [\n      { hookType: \"SessionStart\", command: \"${ADAPTER_DIR}/Hooks/load-core-context.mjs\" },\n      { hookType: \"SessionStart\", command: \"${ADAPTER_DIR}/Hooks/load-project-context.mjs\" },\n      { hookType: \"SessionStart\", command: \"${ADAPTER_DIR}/Hooks/initialize-session.mjs\" },\n      { hookType: \"SessionStart\", command: \"${ADAPTER_DIR}/Hooks/capture-all-events.mjs --event-type SessionStart\" },\n      { hookType: \"SessionStart\", matcher: \"compact\", command: \"${ADAPTER_DIR}/Hooks/post-compact-inject.mjs\" },\n      { hookType: \"UserPromptSubmit\", command: \"${ADAPTER_DIR}/Hooks/cleanup-session-files.mjs\" },\n      { hookType: \"UserPromptSubmit\", command: \"${ADAPTER_DIR}/Hooks/update-tab-titles.mjs\" },\n      { hookType: \"UserPromptSubmit\", command: \"${ADAPTER_DIR}/Hooks/capture-all-events.mjs --event-type UserPromptSubmit\" },\n      // Rolling checkpoint. The model is not invoked on /exit and never on\n      // Ctrl+C, so a handover written at exit time is impossible — it has to\n      // already exist. Rate-limited internally; safe on high-frequency events.\n      { hookType: \"UserPromptSubmit\", command: \"${ADAPTER_DIR}/Hooks/pai-session-autosave.sh\" },\n      { hookType: \"PreToolUse\", matcher: \"Bash\", command: \"${ADAPTER_DIR}/Hooks/security-validator.mjs\" },\n      { hookType: \"PreToolUse\", matcher: \"Bash|Edit|Write|MultiEdit|NotebookEdit|mcp__.*\", command: \"${ADAPTER_DIR}/Hooks/worker-guard.mjs\" },\n      { hookType: \"PreToolUse\", matcher: \"Read|Edit|Write|MultiEdit|NotebookEdit|Glob|Grep\", command: \"${ADAPTER_DIR}/Hooks/worker-path-repair.mjs\" },\n      // Dotted tool names (Glob.glob) die before any name-matched hook sees\n      // them, so this one must match every call and rewrite the name itself.\n      { hookType: \"PreToolUse\", matcher: \"*\", command: \"${ADAPTER_DIR}/Hooks/worker-tool-name-repair.mjs\" },\n      { hookType: \"PreToolUse\", matcher: \"*\", command: \"${ADAPTER_DIR}/Hooks/capture-all-events.mjs --event-type PreToolUse\" },\n      { hookType: \"PostToolUse\", matcher: \"TodoWrite\", command: \"${ADAPTER_DIR}/Hooks/sync-todo-to-md.mjs\" },\n      { hookType: \"PostToolUse\", matcher: \"*\", command: \"${ADAPTER_DIR}/Hooks/capture-all-events.mjs --event-type PostToolUse\" },\n      { hookType: \"PostToolUse\", matcher: \"*\", command: \"${ADAPTER_DIR}/Hooks/capture-tool-output.mjs\" },\n      { hookType: \"PostToolUse\", matcher: \"*\", command: \"${ADAPTER_DIR}/Hooks/update-tab-on-action.mjs\" },\n      { hookType: \"PostToolUse\", matcher: \"*\", command: \"${ADAPTER_DIR}/Hooks/pai-session-autosave.sh\" },\n      { hookType: \"Stop\", command: \"${ADAPTER_DIR}/Hooks/stop-hook.mjs\" },\n      { hookType: \"Stop\", command: \"${ADAPTER_DIR}/Hooks/capture-all-events.mjs --event-type Stop\" },\n      { hookType: \"Stop\", command: \"${ADAPTER_DIR}/Hooks/pai-session-stop.sh\" },\n      { hookType: \"SubagentStop\", command: \"${ADAPTER_DIR}/Hooks/subagent-stop-hook.mjs\" },\n      { hookType: \"SubagentStop\", command: \"${ADAPTER_DIR}/Hooks/capture-all-events.mjs --event-type SubagentStop\" },\n      { hookType: \"SessionEnd\", command: \"${ADAPTER_DIR}/Hooks/capture-session-summary.mjs\" },\n      { hookType: \"SessionEnd\", command: \"${ADAPTER_DIR}/Hooks/capture-all-events.mjs --event-type SessionEnd\" },\n      { hookType: \"PreCompact\", command: \"${ADAPTER_DIR}/Hooks/context-compression-hook.mjs\" },\n      { hookType: \"PreCompact\", command: \"${ADAPTER_DIR}/Hooks/capture-all-events.mjs --event-type PreCompact\" },\n      { hookType: \"PreCompact\", matcher: \"\", command: \"${ADAPTER_DIR}/Hooks/pai-pre-compact.sh\" },\n    ],\n    statusLine: {\n      type: \"command\",\n      command: \"bash ${ADAPTER_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","/**\n * claude-json.ts — safe read/write for ~/.claude.json\n *\n * This file is not ours. It holds every MCP server registration plus Claude\n * Code's own per-project state — on a working machine, hundreds of kilobytes\n * that no one can reconstruct by hand. PAI only ever adds one key to it.\n *\n * The previous implementation (duplicated in mcp.ts and daemon.ts) did:\n *\n *     try { return JSON.parse(read(path)); } catch { return {}; }\n *\n * followed by a full-file write. A malformed or transiently unreadable file\n * therefore became an empty object, and the next write replaced the user's\n * entire config with just PAI's entry. No error, no backup, exit code 0.\n *\n * The precondition is unlikely, which is exactly the problem: `pai setup` is\n * advertised as idempotent and safe to re-run, so it is what people run when\n * something is already broken — the state in which the file is most likely to\n * be malformed.\n */\n\nimport { existsSync, readFileSync, writeFileSync, copyFileSync, renameSync, unlinkSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/** Resolved at call time so a changed HOME (tests) is honoured. */\nexport function claudeJsonPath(): string {\n  return join(homedir(), \".claude.json\");\n}\n\n\n/**\n * Read ~/.claude.json.\n *\n * A missing file is a legitimate first-run state and yields {}. Anything else\n * — malformed JSON, permissions, a half-written file — throws. Returning {}\n * for an unreadable file is what turns a read problem into data loss on the\n * next write.\n */\nexport function readClaudeJson(): Record<string, unknown> {\n  if (!existsSync(claudeJsonPath())) return {};\n\n  let raw: string;\n  try {\n    raw = readFileSync(claudeJsonPath(), \"utf8\");\n  } catch (e) {\n    throw new Error(\n      `Could not read ${claudeJsonPath()}: ${e instanceof Error ? e.message : String(e)}\\n` +\n      `Refusing to continue — writing now would replace your MCP registrations with PAI's alone.`\n    );\n  }\n\n  try {\n    return JSON.parse(raw) as Record<string, unknown>;\n  } catch (e) {\n    throw new Error(\n      `${claudeJsonPath()} is not valid JSON: ${e instanceof Error ? e.message : String(e)}\\n` +\n      `Refusing to continue — overwriting it would destroy every MCP server registration it holds.\\n` +\n      `Fix the file, or move it aside and let Claude Code recreate it, then re-run this command.`\n    );\n  }\n}\n\n/**\n * Write ~/.claude.json, keeping a backup and never leaving it half-written.\n *\n * Writes to a temp file and renames: rename is atomic within a filesystem, so\n * a crash mid-write leaves the original intact rather than truncated. The\n * previous direct writeFileSync could truncate the file and then fail.\n */\nexport function writeClaudeJson(data: Record<string, unknown>): void {\n  const serialized = JSON.stringify(data, null, 2) + \"\\n\";\n\n  if (existsSync(claudeJsonPath())) {\n    try {\n      copyFileSync(claudeJsonPath(), backupPath());\n    } catch (e) {\n      throw new Error(\n        `Could not back up ${claudeJsonPath()} to ${backupPath()}: ${e instanceof Error ? e.message : String(e)}\\n` +\n        `Refusing to write without a backup.`\n      );\n    }\n  }\n\n  const tmp = `${claudeJsonPath()}.tmp-pai-${process.pid}`;\n  try {\n    writeFileSync(tmp, serialized, \"utf8\");\n    renameSync(tmp, claudeJsonPath());\n  } catch (e) {\n    try { if (existsSync(tmp)) unlinkSync(tmp); } catch { /* best effort */ }\n    throw new Error(\n      `Failed to write ${claudeJsonPath()}: ${e instanceof Error ? e.message : String(e)}\\n` +\n      `The original is unchanged${existsSync(backupPath()) ? `; a backup is at ${backupPath()}` : \"\"}.`\n    );\n  }\n}\n\nfunction backupPath(): string {\n  return `${claudeJsonPath()}.bak-pai`;\n}\n\n/** Where the pre-write backup is kept, for messages to the user. */\nexport function claudeJsonBackupPath(): string {\n  return backupPath();\n}\n","/**\n * service-manager.ts — the one guard in front of every launchctl / systemctl call.\n *\n * Unit and plist files land under $HOME, but launchctl and systemctl --user act\n * on the real account's session whatever HOME says. Run under a foreign HOME\n * (a temp dir, a test, a sandbox) they rewire the live service to that HOME's\n * files. So when HOME is not the account home the files are still written and\n * the service manager is left alone.\n */\n\nimport { homedir, userInfo } from \"node:os\";\n\n/** True when it is safe to call launchctl/systemctl; otherwise prints why not and how to finish by hand. */\nexport function serviceManagerAllowed(manualCommand: string): boolean {\n  if (homedir() === userInfo().homedir) return true;\n  console.log(`  service not (re)loaded because HOME is not the account home; as the real user run: ${manualCommand}`);\n  return false;\n}\n","/**\n * daemon-status.ts — the health part of `pai daemon status`, as data.\n *\n * The daemon has always sent its work-queue depth in the status payload and the\n * command has never printed it. That is the remaining half of the July finding:\n * the queue backing up is what an outage actually looks like from outside —\n * `session-end`, `session-summary` and `registry-scan` enqueued and never\n * drained, session notes for two days never written — and it was the one number\n * that would have said so.\n *\n * Kept separate from the printing, and pure, because \"does status report the\n * outage\" is exactly the assertion that was missing when status reported\n * \"Index: idle\" through a two-day outage. A function that only writes to stdout\n * cannot be asked that question.\n */\n\nimport type { BackendOutage } from \"../../storage/outage.js\";\nimport { humanDuration } from \"../../storage/outage.js\";\n\nexport type HealthSeverity = \"error\" | \"warn\" | \"info\";\n\nexport interface HealthLine {\n  severity: HealthSeverity;\n  text: string;\n}\n\n/** The subset of the daemon status payload that describes storage health. */\nexport interface StorageHealthStatus {\n  /** Failing scheduled passes by name; `at` is an ISO timestamp. */\n  passFailures?: Record<string, { at: string; error: string; attempts: number; gaveUp: boolean }> | null;\n  backendOutage?: BackendOutage | null;\n  workQueue?: {\n    pending?: number;\n    processing?: number;\n    completed?: number;\n    failed?: number;\n    total?: number;\n  } | null;\n}\n\n/**\n * Health lines to print above the index/DB figures, worst first.\n *\n * Empty when there is nothing wrong: a healthy daemon should not grow a\n * paragraph of reassurance, or the one time it says something real gets read as\n * more of the same.\n */\nexport function formatStorageHealth(\n  s: StorageHealthStatus,\n  now = Date.now()\n): HealthLine[] {\n  const lines: HealthLine[] = [];\n  const outage = s.backendOutage ?? null;\n\n  if (outage) {\n    lines.push({\n      severity: \"error\",\n      text: `BACKEND DOWN: ${outage.backend} unreachable for ${humanDuration(now - outage.since)}`,\n    });\n    lines.push({\n      severity: \"warn\",\n      text: `${outage.attempts} attempts — ${outage.lastError}`,\n    });\n  }\n\n  for (const [name, f] of Object.entries(s.passFailures ?? {})) {\n    lines.push({\n      severity: \"error\",\n      text:\n        `last ${name}: FAILED ${f.at} ${f.error}` +\n        (f.gaveUp ? \" (retries exhausted)\" : ` (attempt ${f.attempts}, retrying)`),\n    });\n  }\n\n  const queue = queueLine(s.workQueue, outage !== null);\n  if (queue) lines.push(queue);\n\n  if (outage) {\n    lines.push({\n      severity: \"info\",\n      text: \"Indexing, session notes and the work queue are stalled until it returns.\",\n    });\n  }\n\n  return lines;\n}\n\n/**\n * The queue depth, when it means something.\n *\n * During an outage it always means something — including a depth of zero, which\n * says the backlog is not the reason nothing is happening. Otherwise it is only\n * worth a line when work is actually sitting there: a queue that is empty\n * because everything drained is the normal case, and printing it every time\n * teaches the reader to skip the line.\n *\n * `failed` is reported whenever it is non-zero, outage or not. Items that\n * exhausted their retries are the silent-loss case this whole status block\n * exists for — they will never be retried and nothing else surfaces them.\n */\nfunction queueLine(\n  q: StorageHealthStatus[\"workQueue\"],\n  duringOutage: boolean\n): HealthLine | null {\n  if (!q) return null;\n\n  const pending = q.pending ?? 0;\n  const processing = q.processing ?? 0;\n  const failed = q.failed ?? 0;\n  const waiting = pending + processing;\n\n  if (!duringOutage && waiting === 0 && failed === 0) return null;\n\n  const parts = [`${pending} pending`];\n  if (processing > 0) parts.push(`${processing} processing`);\n  if (failed > 0) parts.push(`${failed} failed`);\n\n  return {\n    // A backlog while the backend is down is a symptom, not a fault of its own;\n    // failures that will never retry are a fault whether or not anything is down.\n    severity: failed > 0 ? \"error\" : duringOutage || waiting > 0 ? \"warn\" : \"info\",\n    text: `Work queue:  ${parts.join(\", \")}`,\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    — Restart the running daemon (launchd on macOS, systemd on Linux)\n * install    — Install as a service (launchd plist / systemd user unit) + update ~/.claude.json to use the shim\n * uninstall  — Remove the service + revert ~/.claude.json to direct MCP\n * logs       — Tail the daemon log file\n */\n\nimport {\n  existsSync,\n  readFileSync,\n  writeFileSync,\n  unlinkSync,\n  mkdirSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir, userInfo } from \"node:os\";\nimport { execFileSync, spawnSync } from \"node:child_process\";\nimport { ok, warn, err, dim, bold } from \"../lib/utils.js\";\nimport { loadConfig, paiConfigFilePath } from \"../../daemon/config.js\";\nimport { resolvedMainConfigPath } from \"../../config/main-config.js\";\nimport { resolveDaemonPid } from \"../../daemon/daemon-pid.js\";\nimport { PaiClient } from \"../../daemon/ipc-client.js\";\nimport { readClaudeJson, writeClaudeJson } from \"../../config/claude-json.js\";\nimport { daemonLogPath } from \"../../runtime-paths.js\";\nimport { resolveFromModule } from \"../../module-paths.js\";\nimport { serviceManagerAllowed } from \"../../service-manager.js\";\nimport { formatStorageHealth, type StorageHealthStatus } from \"./daemon-status.js\";\nimport {\n  loadSessionKeepaliveState,\n  sessionKeepaliveLedgerSummary,\n} from \"../../daemon/session-keepalive.js\";\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nconst HOME = homedir();\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 = daemonLogPath();\n\nconst SYSTEMD_UNIT_NAME = \"pai-daemon.service\";\nconst SYSTEMD_USER_DIR = join(HOME, \".config\", \"systemd\", \"user\");\nconst SYSTEMD_UNIT_PATH = join(SYSTEMD_USER_DIR, SYSTEMD_UNIT_NAME);\n\n/** Service name `daemon install` registers on this platform: systemd unit on Linux, launchd label elsewhere. */\nexport function daemonServiceName(plat: NodeJS.Platform = process.platform): string {\n  return plat === \"linux\" ? SYSTEMD_UNIT_NAME : PLIST_LABEL;\n}\n\n/**\n * Resolve the absolute path to the built daemon entry point.\n * tsdown may emit this module's own chunk directly under dist/ rather than\n * under dist/cli/, so a fixed \"../daemon/index.mjs\" is not reliable — walk up\n * from this module until dist/daemon/index.mjs is found instead.\n */\nfunction getDaemonBinPath(): string {\n  return resolveFromModule(import.meta.url, \"dist/daemon/index.mjs\");\n}\n\n/** Resolve the absolute path to the built MCP shim entry point. Same reasoning as getDaemonBinPath. */\nfunction getShimBinPath(): string {\n  return resolveFromModule(import.meta.url, \"dist/daemon-mcp/index.mjs\");\n}\n\n// ---------------------------------------------------------------------------\n// claude.json helpers\n// ---------------------------------------------------------------------------\n\n// Moved to src/config/claude-json.ts — see the note there on why returning {}\n// from a failed read was a data-loss bug rather than a convenience.\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// systemd user unit generation (Linux)\n// ---------------------------------------------------------------------------\n\nexport function generateSystemdUnit(daemonBin: string, nodeBin: string): string {\n  return `[Unit]\nDescription=PAI Daemon\nAfter=network.target\n\n[Service]\nExecStart=${nodeBin} ${daemonBin} serve\nRestart=on-failure\n\n[Install]\nWantedBy=default.target\n`;\n}\n\ntype LoginctlRunner = (args: string[]) => { status: number | null; stdout: string };\n\nconst runLoginctl: LoginctlRunner = (args) => {\n  const r = spawnSync(\"loginctl\", args, { encoding: \"utf8\" });\n  return { status: r.status, stdout: r.stdout ?? \"\" };\n};\n\n/**\n * Turns on systemd linger for the user (so the user unit survives logout) without sudo\n * or a prompt; prints the sudo command only when the system refuses. Linux only.\n */\nexport function ensureLinger(\n  plat: NodeJS.Platform = process.platform,\n  username: string = userInfo().username,\n  loginctl: LoginctlRunner = runLoginctl,\n  log: (s: string) => void = console.log,\n): void {\n  if (plat !== \"linux\") return;\n  const linger = () => {\n    const r = loginctl([\"show-user\", username, \"-p\", \"Linger\"]);\n    return r.status === 0 ? r.stdout.trim() : null;\n  };\n  const before = linger();\n  if (before === null || before === \"Linger=yes\") return;\n  loginctl([\"--no-ask-password\", \"enable-linger\", username]);\n  if (linger() === \"Linger=yes\") {\n    log(ok(\"  linger: on (daemon survives logout)\"));\n    return;\n  }\n  log(warn(`  Linger is off for ${username} — the daemon stops when you log out.`));\n  log(dim(`    Enable it: sudo loginctl enable-linger ${username}`));\n}\n\n// ---------------------------------------------------------------------------\n// Command implementations\n// ---------------------------------------------------------------------------\n\nasync function cmdStatus(): Promise<number> {\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(`    PID:         ${resolveDaemonPid() ?? \"unknown\"}`));\n    console.log(dim(`    Uptime:      ${s[\"uptime\"]}s`));\n    console.log(dim(`    Socket:      ${s[\"socketPath\"]}`));\n    if (s[\"dashboard\"]) console.log(dim(`    Dashboard:   ${s[\"dashboard\"]}`));\n\n    // Lead with the outage and the backlog. Everything below them — index\n    // state, totals — describes a system that is not moving, and printing those\n    // first is how \"Index: idle\" came to mean \"all is well\" during a two-day\n    // outage.\n    const health = formatStorageHealth(s as StorageHealthStatus);\n    if (health.length > 0) {\n      console.log();\n      for (const line of health) {\n        const paint = line.severity === \"error\" ? err : line.severity === \"warn\" ? warn : dim;\n        console.log(paint(`    ${line.text}`));\n      }\n      console.log();\n    }\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    return 1;\n  }\n  return 0;\n}\n\nfunction cmdRestart(): number {\n  if (!serviceManagerAllowed(\"pai daemon restart\")) return 0;\n  if (process.platform === \"linux\") {\n    const before = resolveDaemonPid();\n    const result = spawnSync(\"systemctl\", [\"--user\", \"restart\", SYSTEMD_UNIT_NAME], {\n      encoding: \"utf8\",\n    });\n    if (result.status === 0) {\n      console.log(ok(`Restarted daemon pid ${before ?? \"?\"} -> ${resolveDaemonPid() ?? \"?\"}`));\n    } else {\n      console.error(err(`systemctl restart failed: ${(result.stderr || \"\").trim()}`));\n      return 1;\n    }\n    return 0;\n  }\n\n  if (process.platform !== \"darwin\") {\n    console.error(\n      err(`Unsupported platform: ${process.platform}. Daemon restart supports macOS (launchd) and Linux (systemd user) only.`)\n    );\n    return 1;\n  }\n\n  // kickstart -k kills and restarts the launchd job; the old PID comes from launchd, never a pattern\n  try {\n    const before = resolveDaemonPid();\n    const kick = spawnSync(\n      \"launchctl\",\n      [\"kickstart\", \"-k\", `gui/${process.getuid?.() ?? 501}/${PLIST_LABEL}`],\n      { encoding: \"utf8\" }\n    );\n    if (kick.status !== 0) {\n      console.log(dim(\"Not managed by launchd. Run: pai daemon serve\"));\n      return 0;\n    }\n    console.log(ok(`Restarted daemon pid ${before ?? \"?\"} -> ${resolveDaemonPid() ?? \"?\"}`));\n  } catch (e) {\n    const msg = e instanceof Error ? e.message : String(e);\n    console.error(err(`restart error: ${msg}`));\n    return 1;\n  }\n  return 0;\n}\n\nfunction installServiceDarwin(daemonBin: string): boolean {\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    return false;\n  }\n\n  // 2. Load the plist (unload first in case it was already there)\n  if (!serviceManagerAllowed(`launchctl load ${PLIST_PATH}`)) return true;\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  return true;\n}\n\nfunction installServiceLinux(daemonBin: string): boolean {\n  // 1. Write systemd user unit\n  if (!existsSync(SYSTEMD_USER_DIR)) {\n    mkdirSync(SYSTEMD_USER_DIR, { recursive: true });\n  }\n\n  const unitContent = generateSystemdUnit(daemonBin, process.execPath);\n  try {\n    writeFileSync(SYSTEMD_UNIT_PATH, unitContent, \"utf8\");\n    console.log(ok(`  Wrote systemd unit: ${SYSTEMD_UNIT_PATH}`));\n  } catch (e) {\n    console.error(err(`  Failed to write systemd unit: ${e}`));\n    return false;\n  }\n\n  // 2. daemon-reload + enable --now\n  if (!serviceManagerAllowed(`systemctl --user daemon-reload && systemctl --user enable --now ${SYSTEMD_UNIT_NAME}`)) return true;\n  spawnSync(\"systemctl\", [\"--user\", \"daemon-reload\"], { encoding: \"utf8\" });\n  const enableResult = spawnSync(\"systemctl\", [\"--user\", \"enable\", \"--now\", SYSTEMD_UNIT_NAME], {\n    encoding: \"utf8\",\n  });\n  if (enableResult.status === 0) {\n    console.log(ok(\"  Enabled and started via systemd.\"));\n  } else {\n    console.log(warn(`  systemctl enable --now: ${(enableResult.stderr || \"\").trim()}`));\n  }\n\n  ensureLinger();\n  return true;\n}\n\nfunction cmdInstall(): number {\n  const daemonBin = getDaemonBinPath();\n  const shimBin = getShimBinPath();\n\n  console.log();\n  console.log(bold(\"  PAI Daemon Install\"));\n  console.log();\n\n  if (process.platform === \"darwin\") {\n    if (!installServiceDarwin(daemonBin)) return 1;\n  } else if (process.platform === \"linux\") {\n    if (!installServiceLinux(daemonBin)) return 1;\n  } else {\n    console.error(\n      err(`  Unsupported platform: ${process.platform}. Daemon install supports macOS (launchd) and Linux (systemd user) only.`)\n    );\n    return 1;\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      return 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  return 0;\n}\n\nfunction uninstallServiceDarwin(): void {\n  if (existsSync(PLIST_PATH)) {\n    if (serviceManagerAllowed(`launchctl unload ${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    }\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\nfunction uninstallServiceLinux(): void {\n  if (existsSync(SYSTEMD_UNIT_PATH)) {\n    const managed = serviceManagerAllowed(`systemctl --user disable --now ${SYSTEMD_UNIT_NAME}`);\n    if (managed) {\n      spawnSync(\"systemctl\", [\"--user\", \"disable\", \"--now\", SYSTEMD_UNIT_NAME], {\n        encoding: \"utf8\",\n      });\n      console.log(ok(\"  Disabled and stopped via systemd.\"));\n    }\n    try {\n      unlinkSync(SYSTEMD_UNIT_PATH);\n      console.log(ok(`  Removed unit: ${SYSTEMD_UNIT_PATH}`));\n    } catch (e) {\n      console.log(warn(`  Could not remove unit: ${e}`));\n    }\n    if (managed) spawnSync(\"systemctl\", [\"--user\", \"daemon-reload\"], { encoding: \"utf8\" });\n  } else {\n    console.log(dim(\"  No systemd unit found.\"));\n  }\n}\n\nfunction cmdUninstall(): number {\n  console.log();\n  console.log(bold(\"  PAI Daemon Uninstall\"));\n  console.log();\n\n  if (process.platform === \"darwin\") {\n    uninstallServiceDarwin();\n  } else if (process.platform === \"linux\") {\n    uninstallServiceLinux();\n  } else {\n    console.error(\n      err(`  Unsupported platform: ${process.platform}. Daemon uninstall supports macOS (launchd) and Linux (systemd user) only.`)\n    );\n    return 1;\n  }\n\n  // 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  return 0;\n}\n\nasync function cmdMigrate(connectionString?: string): Promise<number> {\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  // The migration script is at docker/migrate-sqlite.ts, a package-root\n  // sibling of dist/ that is not bundled — walk up to find it regardless of\n  // which dist/ chunk this module ended up in.\n  const migrationScript = resolveFromModule(import.meta.url, \"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    return result.status ?? 1;\n  }\n  return 0;\n}\n\nfunction cmdKeepalive(): number {\n  const config = loadConfig().sessions.cacheKeepalive;\n\n  console.log();\n  console.log(bold(\"  Session cache keepalive\"));\n  console.log();\n  console.log(config.enabled ? ok(`  enabled`) : warn(`  disabled`));\n  console.log(\n    dim(\n      `    idleMinutes: ${config.idleMinutes}  maxBeats: ${config.maxBeats}  ` +\n        `activeHours: ${config.activeHours}  minContextTokens: ${config.minContextTokens}  prompt: \"${config.prompt}\"`\n    )\n  );\n  if (!config.enabled) {\n    console.log();\n    console.log(dim(`  Enable it in ${resolvedMainConfigPath(paiConfigFilePath())}: sessions.cacheKeepalive.enabled: true`));\n    console.log(dim(`    pai config set sessions.cacheKeepalive.enabled true`));\n  }\n\n  const state = loadSessionKeepaliveState();\n  const ids = Object.keys(state);\n  console.log();\n  if (ids.length === 0) {\n    console.log(dim(\"  No per-session beat counters recorded yet.\"));\n  } else {\n    console.log(bold(\"  Per-session beat counters\"));\n    for (const id of ids) {\n      const e = state[id];\n      console.log(dim(`    ${id}: beats=${e.beats} lastBeatAt=${e.lastBeatAt ?? \"never\"}`));\n    }\n  }\n\n  const summary = sessionKeepaliveLedgerSummary();\n  console.log();\n  console.log(bold(`  Ledger: ${summary.sent} sent, ${summary.skipped} skipped`));\n  for (const line of summary.lastLines) {\n    console.log(dim(`    ${line}`));\n  }\n  console.log();\n  return 0;\n}\n\nfunction cmdLogs(opts: { lines?: string; follow?: boolean }): number {\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 0;\n  }\n\n  if (opts.follow) {\n    // exec stays running\n    try {\n      execFileSync(\"tail\", [\"-f\", \"-n\", lines, DAEMON_LOG], { stdio: \"inherit\" });\n    } catch {\n      // User pressed Ctrl+C — that's fine\n    }\n  } else {\n    try {\n      execFileSync(\"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      return 1;\n    }\n  }\n  return 0;\n}\n\nexport async function daemonServeCmd(): Promise<number> {\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  return 0;\n}\n\nexport function daemonMigrateCmd(opts: { connectionString?: string }): Promise<number> {\n  return cmdMigrate(opts.connectionString);\n}\n\nexport function daemonStatusCmd(): Promise<number> {\n  return cmdStatus();\n}\n\nexport function daemonRestartCmd(): number {\n  return cmdRestart();\n}\n\nexport function daemonInstallCmd(): number {\n  return cmdInstall();\n}\n\nexport function daemonUninstallCmd(): number {\n  return cmdUninstall();\n}\n\nexport function daemonKeepaliveCmd(): number {\n  return cmdKeepalive();\n}\n\nexport function daemonLogsCmd(opts: { lines?: string; follow?: boolean }): number {\n  return cmdLogs(opts);\n}\n","/** Step 9: PAI daemon installation (launchd plist on macOS, systemd user unit on Linux). */\n\nimport { existsSync } from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { join } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport { daemonServiceName } from \"../../../impl/daemon.js\";\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 linux = platform() === \"linux\";\n  if (linux && !existsSync(\"/run/systemd/system\")) {\n    console.log(c.dim(\"  systemd is not running here (container?). Skipping service install; start the daemon with: pai daemon serve\"));\n    return false;\n  }\n\n  const plistPath = linux\n    ? join(homedir(), \".config\", \"systemd\", \"user\", \"pai-daemon.service\")\n    : join(homedir(), \"Library\", \"LaunchAgents\", \"com.pai.pai-daemon.plist\");\n  const unit = linux ? \"systemd user unit\" : \"launchd plist\";\n  const exists = existsSync(plistPath);\n\n  if (exists) {\n    console.log(c.dim(`  PAI daemon ${unit} already installed.`));\n    line();\n\n    const reinstall = await promptYesNo(rl, `Reinstall the PAI daemon ${unit}?`, 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 ${daemonServiceName()}.`));\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 (pai) lets Claude Code\");\n  line(\"  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 (\n        mcpServers &&\n        Object.prototype.hasOwnProperty.call(mcpServers, \"pai\")\n      ) {\n        console.log(c.ok(\"PAI MCP server (pai) 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 (pai) 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 (pai) 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 { daemonServiceName } from \"../../../impl/daemon.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  plat: NodeJS.Platform = process.platform,\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 ? `${daemonServiceName(plat)} (installed)` : \"(not installed by setup)\"));\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 * resolver.ts — Ownership resolution for the task bus\n *\n * Maps a tracker task onto the PAI project that owns it.\n *\n *   1. an explicit `pai:<project>` label   (authoritative)\n *   2. the enclosing container name        (fallback, matches the sub-project mirror)\n *   3. otherwise UNROUTED                  (normal state — the findings inbox)\n *\n * Label wins because labels survive a task being moved between containers, and\n * because a task parked in the findings inbox has no meaningful container yet.\n *\n * See docs/task-bus.md.\n */\n\nimport { getRegistryBackend } from \"../storage/factory.js\";\nimport {\n  OWNER_LABEL_PREFIX,\n  UNROUTED,\n  type TaskOwner,\n} from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Alias map\n// ---------------------------------------------------------------------------\n\nexport interface AliasTarget {\n  /** The alias itself — what `pai <name>` and aibroker_pai_launch accept. */\n  alias: string;\n  slug: string;\n  rootPath: string;\n}\n\n/** Normalized alias → target. Keys are the output of `normalize()`. */\nexport type AliasMap = Map<string, AliasTarget>;\n\n/**\n * Fold a tracker-supplied string onto its comparable form.\n *\n * Container names are written for humans and carry decoration the registry\n * never sees: \"Reading List 📚\", \"AcmeAPI\", \"Client Work\". Strip the decoration\n * rather than demanding the user keep two naming schemes in sync.\n *\n * Emoji and pictographs are removed, separators collapse to a single hyphen,\n * and the result is lowercased — so \"Client Work\" and \"client-work\" agree.\n */\nexport function normalize(raw: string): string {\n  return raw\n    .normalize(\"NFKD\")\n    // Drop emoji, pictographs, dingbats and variation selectors.\n    .replace(/[\\u{1F000}-\\u{1FAFF}\\u{2600}-\\u{27BF}\\u{FE0F}\\u{200D}]/gu, \"\")\n    .replace(/[^\\p{L}\\p{N}]+/gu, \"-\")\n    .replace(/^-+|-+$/g, \"\")\n    .toLowerCase();\n}\n\n/**\n * Load every alias registered in the PAI registry.\n *\n * Reads the curated shortlist only — the `aliases` table — deliberately. The\n * full registry commonly carries hundreds of projects with no aliases and\n * genuine ambiguity — separate entries routinely share a display name at\n * different paths — so widening this query resolves tasks to the wrong\n * directory. That failure is silent, which makes it worse than no match. Bus\n * participation is opt-in via `pai project name <identifier> <shortname>`.\n */\nexport async function loadAliasMap(): Promise<AliasMap> {\n  const registry = await getRegistryBackend();\n  const rows = await registry.listAliasMap();\n\n  const map: AliasMap = new Map();\n  for (const row of rows) {\n    const target: AliasTarget = {\n      alias: row.alias,\n      slug: row.slug,\n      rootPath: row.root_path,\n    };\n    map.set(normalize(row.alias), target);\n    // The slug is also addressable, so a task may name either.\n    // Aliases win: they are the curated, intentional handle.\n    const slugKey = normalize(row.slug);\n    if (!map.has(slugKey)) map.set(slugKey, target);\n  }\n  return map;\n}\n\n// ---------------------------------------------------------------------------\n// Resolution\n// ---------------------------------------------------------------------------\n\n/** Extract the project named by a `pai:<project>` label, if any. */\nexport function ownerLabel(labels: string[]): string | null {\n  for (const label of labels) {\n    const trimmed = label.trim();\n    if (trimmed.toLowerCase().startsWith(OWNER_LABEL_PREFIX)) {\n      const value = trimmed.slice(OWNER_LABEL_PREFIX.length).trim();\n      if (value) return value;\n    }\n  }\n  return null;\n}\n\nexport interface ResolveInput {\n  labels: string[];\n  /** Name of the enclosing tracker container (sub-project), when there is one. */\n  container?: string | null;\n  /**\n   * Where a task marked only with the bare `pai` marker goes. Omit to leave\n   * such a task unrouted, which sends it to the findings inbox for triage.\n   */\n  defaultOwner?: string | null;\n}\n\n/**\n * Does this task carry the bare `pai` marker?\n *\n * The marker means \"an AI should take this, and I do not know which one yet\" —\n * the single thing a task's location cannot express, and the reason to keep any\n * label mechanism at all. Everything else is answered by the project it sits\n * in, which is single-valued, visible, and changed by the act of moving it.\n *\n * Deliberately distinct from `pai:<name>`: that names a specific owner, this\n * names none. Previously a bare `pai` was a near miss — it looked like an\n * address and resolved to nothing — which meant the most natural thing to type\n * was the one thing that silently did nothing.\n */\nexport function hasDefaultMarker(labels: string[]): boolean {\n  return labels.some((l) => l.trim().toLowerCase() === \"pai\");\n}\n\n/**\n * Resolve a task's owner.\n *\n * An unresolvable task is never an error: it returns UNROUTED with `rawHint`\n * set to whatever was tried, so a routine can say *why* it could not route it\n * rather than dropping it silently. A container like \"Reading List 📚\" maps to\n * no PAI project — that is expected, not a fault.\n */\nexport function resolveOwner(input: ResolveInput, aliases: AliasMap): TaskOwner {\n  // CONTAINER FIRST. A task sits in exactly one project; it can carry many\n  // labels. A multi-valued field cannot be the authoritative owner — with two\n  // `pai:` labels there is no principled winner, only whichever the code\n  // happens to see first.\n  //\n  // Labels also survive a move. On 2026-08-02 a task was moved from SampleTool to\n  // AIBroker in the tracker UI and a comment on it was delivered to SampleTool,\n  // because a label from its old home outranked the project it now sat in. The\n  // user had not seen the label and had not used labels in months. A field\n  // nobody is looking at should not outrank the one they just changed\n  // deliberately.\n  //\n  // So: the container decides whenever it resolves. Moving a task is then the\n  // whole act of re-assigning it, which is what someone dragging it between\n  // projects already believes they are doing.\n  if (input.container) {\n    const hit = aliases.get(normalize(input.container));\n    if (hit) {\n      return {\n        project: hit.alias,\n        rootPath: hit.rootPath,\n        source: \"container\",\n      };\n    }\n  }\n\n  // Labels remain the way to address a task whose container says nothing —\n  // sitting in the Inbox, at the bus root, or in a project that mirrors no PAI\n  // project. That is the case they are good at, and the only one where nothing\n  // else can express the intent.\n  const label = ownerLabel(input.labels);\n  if (label) {\n    const hit = aliases.get(normalize(label));\n    if (hit) {\n      return {\n        project: hit.alias,\n        rootPath: hit.rootPath,\n        source: \"label\",\n      };\n    }\n    // An explicit label that resolves to nothing is worth surfacing: someone\n    // meant to route this somewhere and named a project that does not exist.\n    return { ...UNROUTED, rawHint: label };\n  }\n\n  // The bare marker: an AI should take this, nobody said which. Last, because\n  // anything more specific is a better answer — this is the Inbox case, where\n  // there is no location to read and no owner named.\n  if (input.defaultOwner && hasDefaultMarker(input.labels)) {\n    const hit = aliases.get(normalize(input.defaultOwner));\n    if (hit) {\n      return { project: hit.alias, rootPath: hit.rootPath, source: \"label\" };\n    }\n    return { ...UNROUTED, rawHint: input.defaultOwner };\n  }\n\n  // Container present but unrecognised, and nothing else to go on. Report what\n  // was tried — \"Reading List 📚\" maps to no PAI project, which is expected\n  // rather than a fault.\n  if (input.container) return { ...UNROUTED, rawHint: input.container };\n\n  return { ...UNROUTED };\n}\n","/**\n * todoist.ts — Todoist provider for the task bus\n *\n * Talks to the Todoist REST API directly rather than going through the Todoist\n * MCP. The MCP is scoped to a Claude session; the daemon has no session, so an\n * MCP-only integration could never run the bus unattended.\n *\n * See docs/task-bus.md.\n */\n\nimport type {\n  ListOptions,\n  NewTask,\n  Task,\n  TaskPriority,\n  TaskProvider,\n  TodoistProviderConfig,\n} from \"../types.js\";\nimport { UNROUTED } from \"../types.js\";\nimport type { AliasMap } from \"../resolver.js\";\nimport { resolveOwner } from \"../resolver.js\";\n\n/**\n * Unified API v1. The older `/rest/v2` endpoints now return 410 Gone — they\n * were sunset, so anything still targeting them fails outright rather than\n * degrading.\n */\nconst API = \"https://api.todoist.com/api/v1\";\n\n// ---------------------------------------------------------------------------\n// Wire types (only the fields we rely on)\n// ---------------------------------------------------------------------------\n\n/** v1 wraps every collection and pages with an opaque cursor. */\ninterface WirePage<T> {\n  results: T[];\n  next_cursor?: string | null;\n}\n\ninterface WireProject {\n  id: string;\n  name: string;\n  parent_id?: string | null;\n  is_archived?: boolean;\n  is_deleted?: boolean;\n  /**\n   * True when the project has collaborators — i.e. someone other than the user\n   * can create tasks in it. See ownerContainers for why that matters here.\n   */\n  is_shared?: boolean;\n}\n\ninterface WireTask {\n  id: string;\n  content: string;\n  description?: string;\n  project_id: string;\n  section_id?: string | null;\n  labels?: string[];\n  priority?: number; // 4 = p1 … 1 = p4\n  due?: { date?: string; datetime?: string; string?: string; is_recurring?: boolean } | null;\n  /** v1 returns completed and tombstoned tasks inline; both must be filtered. */\n  checked?: boolean;\n  is_deleted?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// HTTP\n// ---------------------------------------------------------------------------\n\nasync function call<T>(\n  token: string,\n  path: string,\n  init?: { method?: string; body?: unknown },\n): Promise<T> {\n  const res = await fetch(`${API}${path}`, {\n    method: init?.method ?? \"GET\",\n    headers: {\n      Authorization: `Bearer ${token}`,\n      ...(init?.body ? { \"Content-Type\": \"application/json\" } : {}),\n    },\n    body: init?.body ? JSON.stringify(init.body) : undefined,\n  });\n\n  if (!res.ok) {\n    // 401 is by far the most common failure and the least self-explanatory,\n    // so name the cause rather than surfacing a bare status code.\n    const hint = res.status === 401 ? \" (token rejected — check it was copied in full)\" : \"\";\n    throw new Error(`Todoist ${res.status} ${res.statusText}${hint}`);\n  }\n\n  // 204 on delete/close — nothing to parse.\n  if (res.status === 204) return undefined as T;\n  return (await res.json()) as T;\n}\n\n/**\n * Drain a paginated collection.\n *\n * Every v1 list endpoint pages. Stopping at the first page would silently\n * truncate — the account here already exceeds one page of tasks — so follow the\n * cursor to exhaustion. The guard is a safety stop against a server that keeps\n * handing back the same cursor, not an intentional cap.\n */\nasync function collect<T>(token: string, path: string): Promise<T[]> {\n  const out: T[] = [];\n  let cursor: string | null | undefined;\n  let guard = 0;\n\n  do {\n    const sep = path.includes(\"?\") ? \"&\" : \"?\";\n    const url = cursor ? `${path}${sep}cursor=${encodeURIComponent(cursor)}` : path;\n    const page = await call<WirePage<T>>(token, url);\n    out.push(...(page.results ?? []));\n    cursor = page.next_cursor;\n  } while (cursor && ++guard < 100);\n\n  return out;\n}\n\n/**\n * List every live project on the account.\n *\n * Exported because setup uses it both to let the user pick the bus root and to\n * validate the token — a bad token fails here, at install time, rather than\n * silently returning nothing during a routine.\n */\n/** A bare calendar date — the only shape `due_date` accepts. */\nconst ISO_DATE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** A project inside the bus, and the sub-project whose name decides its owner. */\nexport interface OwnerContainer {\n  project: WireProject;\n  /** Name of the nearest ancestor directly under the root. Null at the root. */\n  ownerName: string | null;\n}\n\n/**\n * Every project in the bus subtree, each mapped to the name that owns it.\n *\n * Ownership belongs to the sub-projects directly under the root — they are the\n * ones mirroring PAI projects. Anything deeper is a FOLDER: a way to group work\n * inside one owner's inbox, not a new owner. So `Claude / Jobs Alpha /\n * Executive Search` is eighteen tasks belonging to Jobs Alpha, filed under a\n * heading.\n *\n * This used to take the root and its direct children only. A grandchild was not\n * merely unrouted, it was never QUERIED — its tasks did not exist as far as the\n * bus was concerned. Which is the worst possible failure for this: the user\n * organises work into a sub-project, it looks tidy, and nothing is ever\n * dispatched. Observed 2026-08-02 with exactly the tree above.\n *\n * Descends from the root rather than walking up from each project, so a cycle\n * in the parent chain — which the API should never produce, but a bad write\n * could — cannot hang the poller.\n */\nexport function ownerContainers(\n  projects: WireProject[],\n  root: string\n): Map<string, OwnerContainer> {\n  const live = projects.filter((p) => !p.is_archived && !p.is_deleted);\n  const childrenOf = new Map<string, WireProject[]>();\n  for (const p of live) {\n    if (!p.parent_id) continue;\n    const siblings = childrenOf.get(p.parent_id);\n    if (siblings) siblings.push(p);\n    else childrenOf.set(p.parent_id, [p]);\n  }\n\n  const out = new Map<string, OwnerContainer>();\n  const rootProject = live.find((p) => p.id === root);\n  if (!rootProject) return out;\n\n  // The root carries no owner name: a task sitting there is unrouted, which is\n  // how the findings inbox is meant to work.\n  out.set(root, { project: rootProject, ownerName: null });\n\n  const walk = (project: WireProject, ownerName: string): void => {\n    if (out.has(project.id)) return; // cycle guard\n\n    // A shared project is excluded, along with everything under it.\n    //\n    // Inheriting ownership down a subtree means a project only has to be MOVED\n    // under the root to gain the right to dispatch work into a session. For a\n    // project the user owns that is the entire point. For a shared one it is an\n    // escalation: a collaborator writes the task, and the task is a work order\n    // that spawns a terminal and types into it. Nobody reparents a project\n    // thinking about that.\n    //\n    // Excluded rather than merely unrouted, and announced rather than dropped\n    // quietly — a boundary that narrows in silence is the same defect as one\n    // that widens in silence. AIBroker reached the same conclusion from the\n    // other side on 2026-08-02 and made subtree grants opt-in; this is the\n    // equivalent for a poller that has no grant to opt into.\n    if (project.is_shared) {\n      process.stderr.write(\n        `pai: skipping shared project \"${project.name}\" (${project.id}) and anything under it — ` +\n          `tasks there can be written by a collaborator, and a dispatched task runs as you.\\n`\n      );\n      return;\n    }\n\n    out.set(project.id, { project, ownerName });\n    for (const child of childrenOf.get(project.id) ?? []) walk(child, ownerName);\n  };\n\n  for (const top of childrenOf.get(root) ?? []) walk(top, top.name);\n\n  return out;\n}\n\n/**\n * Report a write that the server silently shortened.\n *\n * Todoist caps a task description at 16,383 characters and enforces it by\n * TRUNCATION, not by rejection: the request returns 200 and the response says\n * success. Measured 2026-08-02 — a 19,457-character runbook stored as 16,383,\n * losing 3,074 characters off the end with no error anywhere. What went missing\n * was the close-out section, so a session working from it would have done the\n * job correctly and then never marked the task done.\n *\n * The general form is worth more than the specific limit, which is why this\n * compares what came back rather than checking a number: an API that reports\n * success for an operation that did not fully happen is invisible until someone\n * inspects the artifact instead of the return value. Any sink with an unknown\n * limit deserves the same treatment.\n *\n * Warns rather than throws. The task exists and is usable; failing the call\n * would discard work that mostly succeeded. But it must not pass silently.\n *\n * THIS DOES NOT PREVENT THE LOSS. The truncation still happens and the tail is\n * still gone — all this does is turn a silent data-loss bug into a loud one.\n * That is a large improvement and it is not a resolution, and the difference\n * matters because the obvious wrong conclusion from reading this function is\n * \"handled, so long content is fine now\". It is not: whether anything is done\n * about the warning depends on someone reading stderr. Long content belongs in\n * a file, with the field pointing at it. Visible failure is not absence of\n * failure.\n */\nexport function warnIfTruncated(field: string, sent: string | undefined, stored: string | undefined): void {\n  const before = sent?.length ?? 0;\n  const after = stored?.length ?? 0;\n  if (before === 0 || after >= before) return;\n\n  process.stderr.write(\n    `\\npai: WARNING — Todoist truncated the ${field}.\\n` +\n      `  sent ${before} characters, stored ${after} — ${before - after} lost, silently.\\n` +\n      `  The API reported success. Anything at the end of that text is gone.\\n` +\n      `  Put long content in a file and reference it from the ${field} instead.\\n\\n`\n  );\n}\n\n/**\n * Build the due portion of a task write.\n *\n * Exported for testing: the whole defect was a one-word field choice, so the\n * choice itself is what needs pinning.\n */\nexport function dueField(\n  due: string | null | undefined\n): Record<string, string> {\n  const value = due?.trim();\n  if (!value) return {};\n  return ISO_DATE.test(value) ? { due_date: value } : { due_string: value };\n}\n\nexport async function listProjects(token: string): Promise<Array<{ id: string; name: string }>> {\n  const projects = await collect<WireProject>(token, \"/projects\");\n  return projects\n    .filter((p) => !p.is_archived && !p.is_deleted)\n    .map((p) => ({ id: p.id, name: p.name }));\n}\n\n// ---------------------------------------------------------------------------\n// Mapping\n// ---------------------------------------------------------------------------\n\n/** Strip emoji and case so \"Whazaa 🐝\" and \"whazaa\" compare equal. */\nfunction normalizeName(raw: string): string {\n  return raw\n    .replace(/[\\u{1F000}-\\u{1FAFF}\\u{2600}-\\u{27BF}\\u{FE0F}\\u{200D}]/gu, \"\")\n    .trim()\n    .toLowerCase();\n}\n\n/** Todoist priority is inverted: 4 is urgent, 1 is lowest. */\nfunction toPriority(wire: number | undefined): TaskPriority {\n  switch (wire) {\n    case 4: return \"p1\";\n    case 3: return \"p2\";\n    case 2: return \"p3\";\n    default: return \"p4\";\n  }\n}\n\nfunction fromPriority(p: TaskPriority | undefined): number {\n  switch (p) {\n    case \"p1\": return 4;\n    case \"p2\": return 3;\n    case \"p3\": return 2;\n    default: return 1;\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Provider\n// ---------------------------------------------------------------------------\n\nexport class TodoistProvider implements TaskProvider {\n  readonly providerId = \"todoist\" as const;\n\n  constructor(\n    private readonly config: TodoistProviderConfig,\n    private readonly aliases: AliasMap,\n    /** Bus-level settings. Optional so existing callers keep working. */\n    private readonly taskConfig?: { defaultOwner?: string },\n  ) {}\n\n  /**\n   * Resolution order is config → env → unconfigured. A missing token disables\n   * the provider rather than throwing: a user without a tracker still gets a\n   * fully working PAI.\n   */\n  private token(): string | null {\n    return this.config.apiKey?.trim() || process.env.TODOIST_API_KEY?.trim() || null;\n  }\n\n  isConfigured(): boolean {\n    return Boolean(this.config.enabled && this.token() && this.config.rootProjectId);\n  }\n\n  /** Owner for a task carrying only the bare `pai` marker. See ResolveInput. */\n  private get defaultOwner(): string | null {\n    return this.taskConfig?.defaultOwner ?? null;\n  }\n\n  /**\n   * Fetch one task by id, open or completed.\n   *\n   * `listOpen` cannot serve this: a completed task is gone from that list, and\n   * the whole point of archiving is to run at or after completion. Ownership is\n   * resolved the same way as in listOpen — label first, then the container\n   * walked up to the sub-project directly under the bus root.\n   */\n  async getTask(id: string): Promise<Task | null> {\n    const token = this.token();\n    if (!token || !this.config.rootProjectId) return null;\n\n    let w: WireTask;\n    try {\n      w = await call<WireTask>(token, `/tasks/${encodeURIComponent(id)}`);\n    } catch {\n      return null;\n    }\n    if (!w?.id) return null;\n\n    const projects = await collect<WireProject>(token, \"/projects\");\n    const owners = ownerContainers(projects, this.config.rootProjectId);\n    const entry = owners.get(w.project_id ?? \"\");\n\n    const owner = resolveOwner(\n      { labels: w.labels ?? [], container: entry?.ownerName ?? null, defaultOwner: this.defaultOwner },\n      this.aliases\n    );\n\n    return {\n      id: w.id,\n      title: w.content,\n      body: w.description ?? \"\",\n      owner,\n      due: w.due?.datetime ?? w.due?.date ?? null,\n      recurrence: w.due?.is_recurring ? (w.due.string ?? null) : null,\n      priority: toPriority(w.priority),\n      labels: w.labels ?? [],\n      sourceUrl: `https://app.todoist.com/app/task/${w.id}`,\n    };\n  }\n\n  async listOpen(opts: ListOptions = {}): Promise<Task[]> {\n    const token = this.token();\n    if (!token || !this.config.rootProjectId) return [];\n\n    // Fetch the project tree once and keep only the bus subtree. Filtering\n    // client-side is deliberate: Todoist's project search returns zero results\n    // for names containing emoji, so a name query would silently find nothing.\n    const projects = await collect<WireProject>(token, \"/projects\");\n    const root = this.config.rootProjectId;\n    const owners = ownerContainers(projects, root);\n    if (owners.size === 0) return [];\n    const inBus = new Map([...owners].map(([id, o]) => [id, o.project]));\n\n    // Query per bus project rather than draining every task on the account.\n    // The account may hold thousands; the bus holds a handful.\n    const wire: WireTask[] = [];\n    for (const id of inBus.keys()) {\n      wire.push(...(await collect<WireTask>(token, `/tasks?project_id=${encodeURIComponent(id)}`)));\n    }\n\n    const out: Task[] = [];\n\n    for (const w of wire) {\n      if (w.checked || w.is_deleted) continue;\n\n      const entry = owners.get(w.project_id);\n      if (!entry) continue;\n\n      // The owning name is the nearest ancestor directly under the root, NOT\n      // the project the task literally sits in. The bus root itself is not an\n      // owner — a task at the root has no container hint.\n      const owner = resolveOwner(\n        { labels: w.labels ?? [], container: entry.ownerName, defaultOwner: this.defaultOwner },\n        this.aliases\n      );\n\n      if (opts.owner && owner.project !== opts.owner) continue;\n      if (opts.includeUnrouted === false && owner.project === null) continue;\n\n      const due = w.due?.datetime ?? w.due?.date ?? null;\n      if (opts.dueBefore) {\n        // No due date means no deadline to miss — such tasks are never overdue,\n        // so they are excluded from a date-bounded sweep rather than always shown.\n        if (!due || due.slice(0, 10) > opts.dueBefore) continue;\n      }\n\n      out.push({\n        id: w.id,\n        title: w.content,\n        body: w.description ?? \"\",\n        owner,\n        due,\n        recurrence: w.due?.is_recurring ? (w.due.string ?? null) : null,\n        priority: toPriority(w.priority),\n        labels: w.labels ?? [],\n        // v1 does not return a task URL. Build the deep link from the ID so a\n        // dispatched task is still one click from the tracker.\n        sourceUrl: `https://app.todoist.com/app/task/${w.id}`,\n      });\n\n      if (opts.limit && out.length >= opts.limit) break;\n    }\n\n    return out;\n  }\n\n  /**\n   * Choose the wire field for a due value.\n   *\n   * `due_date` takes an ISO date and nothing else — \"tomorrow\" or \"every day\"\n   * come back as HTTP 400. Recurrence in particular is only expressible through\n   * `due_string`, which is the field Todoist runs its natural-language parser\n   * over. Sending everything as a date is what made `pai task add --repeat`\n   * impossible to write, and a repeating task is the whole mechanism behind a\n   * self-rescheduling routine on the bus.\n   *\n   * Strict ISO still goes to `due_date` rather than through the parser. The\n   * parser is locale-sensitive and there is no reason to hand it a value that\n   * is already unambiguous — this keeps existing ISO callers on exactly the\n   * path they were on.\n   */\n  async add(task: NewTask): Promise<Task> {\n    const token = this.token();\n    if (!token || !this.config.rootProjectId) {\n      throw new Error(\"Todoist provider is not configured — run `pai setup`.\");\n    }\n\n    // Prefer LOCATION over a label. Ownership resolves from the project a task\n    // sits in, so filing it into the owner's sub-project says the same thing\n    // the label would — visibly, in one place, and in the field someone changes\n    // when they re-assign it. A label added here would only be a second, quieter\n    // answer to the same question, and one that survives a move: on 2026-08-02 a\n    // task moved between projects was still routed by its old label.\n    //\n    // Only when no sub-project mirrors this owner does the label do real work,\n    // and then it is the sole way to express the intent.\n    let ownerProjectId: string | null = null;\n    if (task.owner && !task.into) {\n      ownerProjectId = await this.subProjectForOwner(task.owner);\n    }\n\n    const labels = [...(task.labels ?? [])];\n    const needsLabel = task.owner && !task.into && !ownerProjectId;\n    if (\n      needsLabel &&\n      !labels.some((l) => l.toLowerCase() === `pai:${task.owner!.toLowerCase()}`)\n    ) {\n      labels.push(`pai:${task.owner}`);\n    }\n\n    // Unowned work lands in the findings inbox when one is configured, so it is\n    // triaged later rather than lost at the root.\n    const sectionId = task.owner || task.into ? undefined : this.config.findingsSectionId;\n\n    // Filing into a per-project sub-project keeps findings from piling up flat\n    // in the root, where they bury each other across projects.\n    const projectId = task.into\n      ? (await this.findOrCreateSubProject(task.into)).id\n      : (ownerProjectId ?? this.config.rootProjectId);\n\n    const created = await call<WireTask>(token, \"/tasks\", {\n      method: \"POST\",\n      body: {\n        content: task.title,\n        description: task.body,\n        project_id: projectId,\n        ...(sectionId ? { section_id: sectionId } : {}),\n        labels,\n        priority: fromPriority(task.priority),\n        ...dueField(task.due),\n      },\n    });\n\n    // The POST already returns what was stored, so this costs nothing.\n    warnIfTruncated(\"task description\", task.body, created.description ?? \"\");\n\n    return {\n      id: created.id,\n      title: created.content,\n      body: created.description ?? \"\",\n      owner: task.owner\n        ? resolveOwner({ labels, container: null, defaultOwner: this.defaultOwner }, this.aliases)\n        : { ...UNROUTED },\n      due: created.due?.datetime ?? created.due?.date ?? null,\n      priority: toPriority(created.priority),\n      labels: created.labels ?? [],\n      sourceUrl: `https://app.todoist.com/app/task/${created.id}`,\n    };\n  }\n\n  /**\n   * Replace a task's labels.\n   *\n   * Only ever sends `labels`. Never `due_date`: writing that field destroys a\n   * recurrence rule, silently turning a routine into a one-off. Measured — it\n   * cost me an invalid test before it cost anyone a schedule.\n   */\n  async setLabels(id: string, labels: string[]): Promise<void> {\n    const token = this.token();\n    if (!token) throw new Error(\"Todoist provider is not configured — run `pai task config`.\");\n    await call<WireTask>(token, `/tasks/${id}`, { method: \"POST\", body: { labels } });\n  }\n\n  /**\n   * Move a task's due date, expressed in Todoist's natural language.\n   *\n   * Always `due_string`, never `due_date`: writing a bare date to a recurring\n   * task drops the recurrence and turns a routine into a one-off, silently. To\n   * keep both, the caller passes the rule and the date together — Todoist\n   * accepts \"every day at 08:00 starting 2026-08-02\" and honours each half.\n   */\n  async setDue(id: string, dueString: string): Promise<void> {\n    const token = this.token();\n    if (!token) throw new Error(\"Todoist provider is not configured — run `pai task config`.\");\n    await call<WireTask>(token, `/tasks/${id}`, { method: \"POST\", body: { due_string: dueString } });\n  }\n\n  /**\n   * Find the sub-project named `name` under the bus root, creating it if absent.\n   *\n   * Per-project sub-projects are the filing convention: a flat pile in the root\n   * buries findings across projects. This exists so the convention can be\n   * executed rather than re-derived — a session that has to infer structure\n   * from the existing project list will sometimes infer cautiously and file\n   * flat, which is the mess the convention prevents.\n   *\n   * Matching is case-insensitive and ignores decoration, so \"Whazaa\" finds an\n   * existing \"Whazaa 🐝\" rather than creating a near-duplicate.\n   */\n  /**\n   * The sub-projects under the bus root — i.e. every address a task can be\n   * filed against. Archived and deleted ones are excluded: they cannot receive\n   * work, so listing them would overstate what is reachable.\n   */\n  /**\n   * The existing sub-project that resolves to this owner, if there is one.\n   *\n   * Matched by running the ordinary ownership resolution over each sub-project\n   * name rather than comparing strings, because a display name is rarely its\n   * PAI slug — \"AIBroker\" is `broker`, \"SL\" is `seriousletter`. Reusing the\n   * resolver means filing and reading cannot disagree about where a task\n   * belongs, which is a class of bug we have already paid for.\n   *\n   * Deliberately does NOT create anything. Filing a task should not bring a new\n   * address into existence as a side effect; that is `--into`'s job, asked for\n   * explicitly.\n   */\n  private async subProjectForOwner(owner: string): Promise<string | null> {\n    try {\n      for (const p of await this.listSubProjects()) {\n        const resolved = resolveOwner({ labels: [], container: p.name }, this.aliases);\n        if (resolved.project === owner) return p.id;\n      }\n    } catch {\n      // Unreachable tracker — fall back to the label, which still routes.\n    }\n    return null;\n  }\n\n  async listSubProjects(): Promise<Array<{ id: string; name: string }>> {\n    const token = this.token();\n    if (!token || !this.config.rootProjectId) {\n      throw new Error(\"Todoist provider is not configured — run `pai task config`.\");\n    }\n\n    const root = this.config.rootProjectId;\n    const projects = await collect<WireProject>(token, \"/projects\");\n    return projects\n      .filter((p) => !p.is_archived && !p.is_deleted && p.parent_id === root)\n      .map((p) => ({ id: p.id, name: p.name }));\n  }\n\n  async findOrCreateSubProject(name: string): Promise<{ id: string; created: boolean }> {\n    const token = this.token();\n    if (!token || !this.config.rootProjectId) {\n      throw new Error(\"Todoist provider is not configured — run `pai task config`.\");\n    }\n\n    const root = this.config.rootProjectId;\n    const projects = await collect<WireProject>(token, \"/projects\");\n    const want = normalizeName(name);\n\n    for (const p of projects) {\n      if (p.is_archived || p.is_deleted) continue;\n      if (p.parent_id === root && normalizeName(p.name) === want) {\n        return { id: p.id, created: false };\n      }\n    }\n\n    const created = await call<WireProject>(token, \"/projects\", {\n      method: \"POST\",\n      body: { name, parent_id: root },\n    });\n    return { id: created.id, created: true };\n  }\n\n  /** Append a comment — used to keep run history on the task itself. */\n  async comment(id: string, content: string): Promise<void> {\n    const token = this.token();\n    if (!token) throw new Error(\"Todoist provider is not configured — run `pai task config`.\");\n    const created = await call<{ content?: string }>(token, `/comments`, {\n      method: \"POST\",\n      body: { task_id: id, content },\n    });\n    // Comments have their own cap, and the same truncate-and-report-success\n    // behaviour. A run history that quietly loses its tail is worse than none,\n    // because it reads as complete.\n    warnIfTruncated(\"comment\", content, created?.content ?? \"\");\n  }\n\n  /** Comments on one task, oldest first. */\n  async listComments(\n    taskId: string\n  ): Promise<Array<{ id: string; content: string; postedAt?: string }>> {\n    const token = this.token();\n    if (!token) return [];\n    const wire = await collect<{ id: string; content?: string; posted_at?: string }>(\n      token,\n      `/comments?task_id=${encodeURIComponent(taskId)}`\n    );\n    // posted_at is carried because the archive is a record: a discussion with\n    // no dates cannot be reconciled against anything that happened around it.\n    return wire.map((c) => ({ id: c.id, content: c.content ?? \"\", postedAt: c.posted_at }));\n  }\n\n  async deleteComment(commentId: string): Promise<void> {\n    const token = this.token();\n    if (!token) throw new Error(\"Todoist provider is not configured — run `pai task config`.\");\n    await call<unknown>(token, `/comments/${commentId}`, { method: \"DELETE\" });\n  }\n\n  async complete(id: string): Promise<void> {\n    const token = this.token();\n    if (!token) throw new Error(\"Todoist provider is not configured — run `pai setup`.\");\n    await call<void>(token, `/tasks/${id}/close`, { method: \"POST\" });\n  }\n}\n","/** Step 16: Task bus — optional external tracker for cross-session work. */\n\nimport { c, line, section, type Rl, prompt, promptYesNo, readConfigRaw } from \"../utils.js\";\nimport { listProjects } from \"../../../../tasks/providers/todoist.js\";\nimport type { TaskConfig } from \"../../../../tasks/types.js\";\n\n/** Shape of the `tasks` block as it sits in the raw config file. */\ntype RawTaskConfig = Partial<TaskConfig> & {\n  providers?: { todoist?: { apiKey?: string; rootProjectId?: string; enabled?: boolean } };\n};\n\nexport async function stepTaskBus(rl: Rl): Promise<Record<string, unknown>> {\n  section(\"Step 16: Task Bus (Optional)\");\n\n  const existing = readConfigRaw();\n  const current = (existing.tasks ?? {}) as RawTaskConfig;\n  const todoist = current.providers?.todoist;\n\n  if (todoist?.apiKey && todoist?.rootProjectId) {\n    console.log(c.ok(\"Task bus already configured. Skipping.\"));\n    return { tasks: current };\n  }\n\n  line();\n  line(\"  The task bus lets any PAI session file work into a shared tracker, so a\");\n  line(\"  routine can pick it up later and hand it to the session that owns it.\");\n  line();\n  line(\"  PAI works fully without this. Skip it unless you want cross-session tasks.\");\n  line();\n\n  const wanted = await promptYesNo(rl, \"Enable the task bus (requires a Todoist account)?\", false);\n  if (!wanted) {\n    line();\n    console.log(c.ok(\"Skipping the task bus.\"));\n    console.log(c.dim(\"  Add later by re-running `pai setup`.\"));\n    return { tasks: { enabled: false } };\n  }\n\n  // -- Token -----------------------------------------------------------------\n  // Prefer an existing env token so the user need not paste a credential twice.\n  // Never read it from ~/.claude.json: the Todoist MCP keeps one there, but that\n  // belongs to a different tool and PAI must not scrape another program's secrets.\n  line();\n  line(\"  Get your API token from Todoist:\");\n  line(c.dim(\"    Settings -> Integrations -> Developer\"));\n  line(c.dim(\"    https://app.todoist.com/app/settings/integrations/developer\"));\n  line();\n  line(\"  It grants full read/write access to your Todoist. Treat it like a password.\");\n  line();\n\n  const envToken = process.env.TODOIST_API_KEY?.trim();\n  let apiKey = todoist?.apiKey ?? \"\";\n\n  if (!apiKey && envToken) {\n    const useEnv = await promptYesNo(rl, \"Found TODOIST_API_KEY in the environment. Use it?\", true);\n    if (useEnv) apiKey = envToken;\n  }\n\n  while (!apiKey) {\n    apiKey = (await prompt(rl, \"  Paste your Todoist API token here: \")).trim();\n    if (!apiKey) console.log(c.warn(\"  A token is required, or answer no to skip the task bus.\"));\n  }\n\n  // -- Root project ----------------------------------------------------------\n  // Listing projects doubles as token validation: a bad token fails here, at\n  // setup time, rather than silently returning nothing during a morning routine.\n  line();\n  line(\"  Fetching your Todoist projects...\");\n\n  let projects: Array<{ id: string; name: string }>;\n  try {\n    projects = await listProjects(apiKey);\n  } catch (e) {\n    line();\n    console.log(c.warn(`Could not reach Todoist: ${e instanceof Error ? e.message : String(e)}`));\n    console.log(c.dim(\"  The token may be wrong, or the network is unavailable.\"));\n    console.log(c.dim(\"  Saved nothing. Re-run `pai setup` to try again.\"));\n    return { tasks: { enabled: false } };\n  }\n\n  if (projects.length === 0) {\n    console.log(c.warn(\"  No projects found. Create one in Todoist first, then re-run setup.\"));\n    return { tasks: { enabled: false } };\n  }\n\n  line();\n  line(\"  Which project should hold the bus? Create a dedicated one if unsure.\");\n  line();\n  projects.forEach((p, i) => line(`    ${String(i + 1).padStart(2)}. ${p.name}`));\n  line();\n\n  let rootProjectId = \"\";\n  while (!rootProjectId) {\n    const answer = (await prompt(rl, `  Project number [1-${projects.length}]: `)).trim();\n    const idx = Number.parseInt(answer, 10) - 1;\n    if (Number.isInteger(idx) && idx >= 0 && idx < projects.length) {\n      rootProjectId = projects[idx]!.id;\n    } else {\n      console.log(c.warn(`  Enter a number between 1 and ${projects.length}.`));\n    }\n  }\n\n  const chosen = projects.find((p) => p.id === rootProjectId)!;\n\n  // The ID is stored, never the name. Todoist's project search silently returns\n  // zero results for names containing emoji, so resolving by name later would\n  // report \"no tasks\" instead of failing — the exact silent failure this\n  // subsystem exists to surface.\n  line();\n  console.log(c.ok(`Task bus enabled on \"${chosen.name}\" (id ${rootProjectId}).`));\n  console.log(c.dim(\"  Label a task `pai:<project>` to assign it to a PAI project.\"));\n  console.log(c.dim(\"  Only projects with an alias can be dispatched to — see `pai project name`.\"));\n\n  const autoDispatch = await promptYesNo(\n    rl,\n    \"Automatically hand tasks to the owning session (needs AIBroker)?\",\n    false,\n  );\n\n  return {\n    tasks: {\n      enabled: true,\n      autoDispatch,\n      providers: {\n        todoist: { enabled: true, apiKey, rootProjectId },\n      },\n    },\n  };\n}\n","/** Step 17: Worker providers — subagents on configured providers. */\n\nimport { c, line, section, type Rl, promptYesNo, readConfigRaw } from \"../utils.js\";\nimport { installWorkers } from \"../../../../workers/install.js\";\nimport { readWorkersSection } from \"../../../../workers/config.js\";\nimport { describeProviders, setWorkersEnabled } from \"../../../../workers/providers.js\";\n\nexport async function stepWorkers(rl: Rl): Promise<Record<string, unknown>> {\n  section(\"Step 17: Worker Providers (Optional)\");\n\n  const existing = readConfigRaw();\n  const current = existing.workers as { enabled?: boolean; providers?: Record<string, unknown> } | undefined;\n\n  if (current?.providers && Object.keys(current.providers).length > 0) {\n    if (current.enabled !== false) {\n      console.log(c.ok(\"Worker providers already configured. Running install to refresh shims…\"));\n      for (const l of installWorkers().lines) line(`  ${l}`);\n    }\n    return { workers: current };\n  }\n\n  line();\n  line(\"  PAI can run every subagent (worker) on a provider: the built-in\");\n  line(\"  `anthropic` one (your logged-in claude CLI, Max plan, no API key) or any\");\n  line(\"  Anthropic-compatible endpoint you add later.\");\n  line();\n  line(\"  `pai worker run` is the runner; ps / follow / replay watch the runs;\");\n  line(\"  the Agent tool is denied and rewritten to it while workers are on.\");\n  line();\n\n  const wanted = await promptYesNo(rl, \"Turn workers on now with the built-in anthropic provider?\", true);\n  if (!wanted) {\n    line();\n    console.log(c.ok(\"Skipping workers.\"));\n    console.log(c.dim(\"  Later: pai worker on   (other providers: pai worker providers add <name> … — see docs/worker.md)\"));\n    return { workers: { enabled: false, providers: {}, classes: {} } };\n  }\n\n  setWorkersEnabled(true);\n  const r = installWorkers();\n  for (const l of r.lines) line(`  ${l}`);\n  const { workers } = readWorkersSection();\n  console.log(c.ok(\"Workers on — provider: anthropic (built-in, no API key written).\"));\n  for (const l of describeProviders(workers)) line(c.dim(`  ${l}`));\n  return {};\n}\n","/**\n * PAI setup wizard — main entry point.\n * Orchestrates all setup steps in order and runs them.\n */\n\nimport chalk from \"chalk\";\nimport { existsSync } from \"node:fs\";\nimport { CONFIG_FILE, loadConfig } from \"../../../daemon/config.js\";\nimport { createRl, prompt, promptYesNo, line, mergeConfig, setupOptions } 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  stepTaskBus,\n  stepWorkers,\n  stepInitialIndex,\n  stepSummary,\n} from \"./steps/index.js\";\n\n/** Runs one wizard step; a throw is re-raised naming the step so unattended runs say where they died. */\nasync function step<T>(name: string, fn: () => T | Promise<T>): Promise<T> {\n  try {\n    return await fn();\n  } catch (e) {\n    throw new Error(`setup step \"${name}\" failed: ${e instanceof Error ? e.message : String(e)}`);\n  }\n}\n\nexport async 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 step(\"stepStorage\", () => stepStorage(rl));\n\n    // Step 3: Embeddings\n    const embeddingConfig = await step(\"stepEmbedding\", () => stepEmbedding(rl));\n\n    // Step 4: Agent configuration (CLAUDE.md)\n    const claudeMdGenerated = await step(\"stepClaudeMd\", () => stepClaudeMd(rl));\n\n    // Step 5: PAI Skill\n    const paiSkillInstalled = await step(\"stepPaiSkill\", () => stepPaiSkill(rl));\n\n    // Step 6: AI Steering Rules\n    const aiSteeringRulesInstalled = await step(\"stepAiSteeringRules\", () => stepAiSteeringRules(rl));\n\n    // Step 7: Skill Stubs\n    const skillStubsInstalled = await step(\"stepSkillStubs\", () => stepSkillStubs(rl));\n\n    // Step 8: Hooks (shell scripts)\n    const hooksInstalled = await step(\"stepHooks\", () => stepHooks(rl));\n\n    // Step 7b: TypeScript hooks (.mjs files)\n    const tsHooksInstalled = await step(\"stepTsHooks\", () => stepTsHooks(rl));\n\n    // Step 8b: DA name\n    const daName = await step(\"stepDaName\", () => stepDaName(rl));\n\n    // Step 8: Settings.json\n    const settingsPatched = await step(\"stepSettings\", () => stepSettings(rl, daName));\n\n    // Step 9: Daemon\n    const daemonInstalled = await step(\"stepDaemon\", () => stepDaemon(rl));\n\n    // Step 10: MCP\n    const mcpRegistered = await step(\"stepMcp\", () => stepMcp(rl));\n\n    // Step 11: Directories (informational — no config written)\n    await step(\"stepDirectories\", () => stepDirectories(rl));\n\n    // Step 11b: Task bus (optional external tracker)\n    const taskConfig = await step(\"stepTaskBus\", () => stepTaskBus(rl));\n\n    // Step 11c: Worker providers (optional subagent routing)\n    const workersConfig = await step(\"stepWorkers\", () => stepWorkers(rl));\n\n    // Write config after gathering all choices\n    const allUpdates = { ...storageConfig, ...embeddingConfig, ...taskConfig, ...workersConfig };\n    mergeConfig(allUpdates);\n\n    line();\n    console.log(chalk.green(\"  Configuration saved.\"));\n\n    // Step 12: Initial index\n    await step(\"stepInitialIndex\", () => 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","/** `pai setup`: the first-time setup wizard (steps live in ./setup/). */\n\nimport chalk from \"chalk\";\nimport type { CommandDef } from \"../define.js\";\nimport { legacy } from \"../lib/legacy.js\";\nimport { runSetup } from \"./setup/index.js\";\nimport { setupOptions } from \"./setup/utils.js\";\n\nexport const setupCommand: CommandDef = {\n  name: \"setup\",\n  summary: \"First-time setup: storage, search, hooks, daemon\",\n  description:\n    \"Walks through everything PAI needs: where data is stored, search embeddings, Claude Code hooks and settings, the background daemon and the first index.\\n\" +\n    \"Safe to run again. With --yes it takes every default and asks nothing.\",\n  options: [\n    { name: \"yes\", short: \"y\", type: \"boolean\", default: false, summary: \"Take every default and read no input\" },\n    { name: \"storage\", type: \"string\", value: \"BACKEND\", summary: \"Where to store data: sqlite or postgres (with --yes: sqlite unless a local Postgres answers)\" },\n  ],\n  examples: [\"pai setup\", \"pai setup --yes --storage sqlite\"],\n  seeAlso: [\"pai update\", \"pai daemon status\"],\n  run(c) {\n    const storage = c.opts.storage as string | undefined;\n    return legacy(async () => {\n      if (storage && storage !== \"sqlite\" && storage !== \"postgres\") {\n        console.error(`pai setup: --storage must be sqlite or postgres, got \"${storage}\"`);\n        process.exitCode = 1;\n        return;\n      }\n      setupOptions.yes = c.opts.yes === true;\n      setupOptions.storage = storage as \"sqlite\" | \"postgres\" | undefined;\n      try {\n        await runSetup();\n      } catch (e) {\n        console.error(chalk.red(`pai setup: ${e instanceof Error ? e.message : String(e)}`));\n        process.exitCode = 1;\n      }\n    });\n  },\n};\n\nexport const installAlias: CommandDef = { ...setupCommand, name: \"install\", aliasOf: \"setup\" };\n","/**\n * voices-config.ts — voices.json / voices.yaml, the same load-either /\n * migrate pattern as the main config (see main-config.ts) and workers.yaml.\n *\n * Nothing in src/ currently reads voices.json (see the note on\n * migrateVoicesJson in pai-files.ts) — this exists so the one place that\n * would ever load it has the YAML-preferring behaviour already in place,\n * and so `pai config yaml` has one JSON→YAML step to run for it, same as\n * every other PAI config file.\n */\n\nimport { paiHomePath } from \"./pai-home.js\";\nimport { readDualFormatConfigRaw, migrateMainConfigToYaml, type MainConfigMigrateResult } from \"./main-config.js\";\n\nexport function voicesJsonPath(): string {\n  return paiHomePath(\"voices.json\");\n}\n\nexport function voicesYamlPath(): string {\n  return paiHomePath(\"voices.yaml\");\n}\n\n/** voices.yaml if it exists, else voices.json, else `{}`. */\nexport function loadVoicesConfig(): Record<string, unknown> {\n  return readDualFormatConfigRaw(voicesJsonPath(), voicesYamlPath());\n}\n\n/** `pai config yaml`'s voices.json → voices.yaml step. */\nexport function migrateVoicesToYaml(opts: { dryRun?: boolean; force?: boolean } = {}): MainConfigMigrateResult {\n  return migrateMainConfigToYaml(voicesJsonPath(), { ...opts, yamlPath: voicesYamlPath(), sectionComments: {} });\n}\n","/**\n * PAI_HOME layout migration for the two legacy per-user SQLite files\n * (the orphaned ~/.config/pai/federation.db and the live ~/.pai/{federation,\n * registry}.db pair) — byte-relocation on disk, not application-level DB\n * access, but still routed through src/storage/ so `pai config migrate`\n * never has to name these files directly (boundary test, design doc §7).\n */\n\nimport { existsSync, renameSync, statSync } from \"node:fs\";\nimport { execFileSync } from \"node:child_process\";\nimport {\n  paiHomePath,\n  migratePaiFile,\n  PaiFileMigrationError,\n  type MigrateFileResult,\n} from \"../../config/pai-home.js\";\nimport {\n  federationDbPath,\n  oldFederationPath,\n  registryDbPath,\n  oldRegistryPath,\n  migrateOrphanFederationDb,\n} from \"../paths.js\";\n\n/** Paths for `pai config path`/`migrate` display, without exposing the raw openers by name. */\nexport function legacyDbDisplayPaths(): { registryDb: string; federationDb: string } {\n  return { registryDb: registryDbPath(), federationDb: federationDbPath() };\n}\n\n/**\n * The orphaned ~/.config/pai/federation.db (a 0-byte leftover — the live\n * federation DB has lived at ~/.pai/federation.db since the Postgres\n * migration, nothing in src/ reads this copy). Still checked for an open fd\n * before moving: if some other process ever does hold it, restart the\n * daemon (SIGTERM; launchd relaunches it) and give it a moment to let go\n * before renaming the file aside, per the migration runbook.\n */\nexport function migrateFederationDbOrphan(dryRun: boolean): MigrateFileResult {\n  const oldPath = oldFederationPath();\n  if (existsSync(oldPath) && !dryRun) {\n    let heldOpen = false;\n    try {\n      execFileSync(\"lsof\", [oldPath], { stdio: \"pipe\" });\n      heldOpen = true;\n    } catch {\n      // lsof exits non-zero when nothing has the file open — the expected case.\n    }\n    if (heldOpen) {\n      console.log(\"  federation database (orphan): held open by the daemon — restarting it first\");\n      execFileSync(\"pai\", [\"daemon\", \"restart\"], { stdio: \"inherit\" });\n      execFileSync(\"sleep\", [\"2\"]);\n    }\n  }\n  return migrateOrphanFederationDb({ dryRun });\n}\n\nfunction checkpointAndVerify(oldPath: string, newPath: string, label: string): void {\n  let hasSqlite3 = true;\n  try {\n    execFileSync(\"which\", [\"sqlite3\"], { stdio: \"pipe\" });\n  } catch {\n    hasSqlite3 = false;\n  }\n  if (hasSqlite3) {\n    try {\n      execFileSync(\"sqlite3\", [oldPath, \"PRAGMA wal_checkpoint(TRUNCATE);\"], { stdio: \"pipe\" });\n    } catch {\n      // Best-effort — the sidecar move below still catches an un-checkpointed WAL.\n    }\n  }\n\n  const stamp = new Date().toISOString().slice(0, 10).replace(/-/g, \"\");\n  for (const suffix of [\"-wal\", \"-shm\"]) {\n    const sidecar = `${oldPath}${suffix}`;\n    if (existsSync(sidecar)) {\n      try {\n        renameSync(sidecar, `${sidecar}.migrated-${stamp}`);\n      } catch {\n        // Non-fatal — surfaced by the integrity check below if it actually mattered.\n      }\n    }\n  }\n\n  if (hasSqlite3) {\n    try {\n      const check = execFileSync(\"sqlite3\", [newPath, \"PRAGMA integrity_check;\"], { stdio: \"pipe\" })\n        .toString()\n        .trim();\n      if (check !== \"ok\") {\n        throw new PaiFileMigrationError(\n          `${newPath}: PRAGMA integrity_check reported \"${check}\" — investigate before trusting this copy`\n        );\n      }\n    } catch (e) {\n      if (e instanceof PaiFileMigrationError) throw e;\n      // sqlite3 CLI failed for an unrelated reason (not installed, etc.) — the\n      // byte-identical copy migratePaiFile already verified is still trustworthy.\n    }\n  }\n}\n\n/**\n * ~/.pai/registry.db — a third per-user location, alongside PAI_HOME and the\n * ~/.claude adapter. Same open-fd caution as the federation database below\n * (the daemon holds this connection while it runs), plus SQLite-specific\n * care: a WAL-mode DB can have unflushed writes sitting in a `-wal` sidecar\n * file, so a plain byte-copy of the main file alone can silently drop them.\n * This checkpoints the WAL into the main file before copying, moves any\n * sidecar left behind anyway, and runs `PRAGMA integrity_check` on the copy\n * when the `sqlite3` CLI is available — on top of migratePaiFile's own\n * byte-identical verification, not instead of it.\n */\nexport function migrateRegistryDbFile(dryRun: boolean): MigrateFileResult {\n  const oldPath = oldRegistryPath();\n  const newPath = paiHomePath(\"registry.db\");\n\n  if (!existsSync(oldPath) || dryRun) {\n    return migratePaiFile(newPath, [oldPath], { dryRun });\n  }\n\n  let heldOpen = false;\n  try {\n    execFileSync(\"lsof\", [oldPath], { stdio: \"pipe\" });\n    heldOpen = true;\n  } catch {\n    // lsof exits non-zero when nothing has the file open — the expected case.\n  }\n  if (heldOpen) {\n    console.log(\"  registry database: held open by the daemon — restarting it first\");\n    execFileSync(\"pai\", [\"daemon\", \"restart\"], { stdio: \"inherit\" });\n    execFileSync(\"sleep\", [\"2\"]);\n  }\n\n  const result = migratePaiFile(newPath, [oldPath], { dryRun: false });\n  if (result.fromPath) checkpointAndVerify(oldPath, newPath, \"registry database\");\n  return result;\n}\n\n/**\n * ~/.pai/federation.db — the LIVE per-user SQLite federation DB. Despite the\n * comment on migrateFederationDbOrphan above, this one is not an orphan: the\n * sqlite storage backend opens it directly via the storage layer's default\n * path — independent of the storageBackend setting only for hosts still on\n * the sqlite backend. It shares its target filename with the harmless\n * 0-byte orphan migrated above, so that empty copy is renamed aside first\n * (never deleted) to free up the canonical name. Same open-fd,\n * WAL-checkpoint and integrity-check care as the registry database.\n */\nexport function migrateFederationDbFile(dryRun: boolean): MigrateFileResult {\n  const oldPath = oldFederationPath();\n  const newPath = paiHomePath(\"federation.db\");\n\n  if (!dryRun && existsSync(newPath) && existsSync(oldPath) && statSync(newPath).size === 0) {\n    const stamp = new Date().toISOString().slice(0, 10).replace(/-/g, \"\");\n    renameSync(newPath, `${newPath}.empty-orphan-${stamp}`);\n  }\n\n  if (!existsSync(oldPath) || dryRun) {\n    return migratePaiFile(newPath, [oldPath], { dryRun });\n  }\n\n  let heldOpen = false;\n  try {\n    execFileSync(\"lsof\", [oldPath], { stdio: \"pipe\" });\n    heldOpen = true;\n  } catch {\n    // lsof exits non-zero when nothing has the file open — the expected case.\n  }\n  if (heldOpen) {\n    console.log(\"  federation database (live): held open by the daemon — restarting it first\");\n    execFileSync(\"pai\", [\"daemon\", \"restart\"], { stdio: \"inherit\" });\n    execFileSync(\"sleep\", [\"2\"]);\n  }\n\n  const result = migratePaiFile(newPath, [oldPath], { dryRun: false });\n  if (result.fromPath) checkpointAndVerify(oldPath, newPath, \"federation database\");\n  return result;\n}\n","/**\n * dispatch.ts — Hand tasks to the PAI session that owns them\n *\n * Transport is AIBroker, and it is optional. With it, a task reaches a running\n * session or spawns one. Without it, dispatch degrades to reporting who owns\n * what and leaves acting to the user — PAI must stay useful on machines that\n * do not run iTerm2.\n *\n * See docs/task-bus.md.\n */\n\nimport type { DispatchResult, Task } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Transport\n// ---------------------------------------------------------------------------\n\n/** What a transport reports back. Mirrors DispatchOutcome minus the states\n *  the bus decides for itself (`unrouted`, and `skipped` when disabled). */\nexport interface TransportResult {\n  outcome: \"delivered\" | \"queued\" | \"spawned\" | \"unlaunchable\" | \"unreachable\" | \"skipped\";\n  session?: string;\n  reason?: string;\n}\n\n/**\n * The subset of AIBroker the bus needs.\n *\n * Deliberately one atomic call rather than liveSessions/launch/send. Deciding\n * \"is it running? no — launch it, then send\" is session-lifecycle logic, which\n * belongs to whoever owns sessions. Splitting it across a process boundary\n * would duplicate that logic here and race: a session can start or die between\n * the check and the send.\n *\n * Declared as an interface rather than imported so the module carries no hard\n * dependency on AIBroker being installed.\n */\nexport interface Transport {\n  dispatch(\n    project: string,\n    message: string,\n    opts: { spawnIfAbsent: boolean },\n  ): Promise<TransportResult>;\n}\n\nexport interface DispatchOptions {\n  transport?: Transport | null;\n  /** When false, report ownership without contacting any session. */\n  autoDispatch: boolean;\n  /** Launch a session when the owner is not running. Default: true. */\n  spawnIfAbsent?: boolean;\n  /**\n   * Reporting only — no session is contacted. Distinguishes \"no transport was\n   * looked for\" from \"a transport was looked for and not found\", which read\n   * identically without it and made a working setup look broken.\n   */\n  dryRun?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Message\n// ---------------------------------------------------------------------------\n\n/**\n * Render a task as a session-to-session message.\n *\n * The body is included in full and deliberately not summarised: the whole point\n * of the description convention is that a task is actionable months later\n * without re-deriving anything, and truncating it here would undo that.\n */\nexport function renderTask(task: Task): string {\n  const parts = [`Task from the bus: ${task.title}`];\n  if (task.due) parts.push(`Due: ${task.due}`);\n  if (task.priority !== \"p4\") parts.push(`Priority: ${task.priority}`);\n  if (task.body.trim()) parts.push(\"\", task.body.trim());\n  if (task.sourceUrl) parts.push(\"\", `Source: ${task.sourceUrl}`);\n  parts.push(\n    \"\",\n    `Tracker id: ${task.id}`,\n    `When done, run \\`pai task done ${task.id}\\` so it is not dispatched again.`,\n    \"No reply is expected — the process that sent this has already exited.\",\n    \"If you are blocked, file it back to the tracker with `pai task add` rather than replying.\",\n  );\n  return parts.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Dispatch\n// ---------------------------------------------------------------------------\n\nexport async function dispatchTask(\n  task: Task,\n  opts: DispatchOptions,\n): Promise<DispatchResult> {\n  if (task.owner.project === null) {\n    return {\n      task,\n      outcome: \"unrouted\",\n      reason: task.owner.rawHint\n        ? `\"${task.owner.rawHint}\" matches no PAI project`\n        : \"no owner label or container\",\n    };\n  }\n\n  const project = task.owner.project;\n\n  if (!opts.autoDispatch || !opts.transport) {\n    // A dry run deliberately passes no transport, so the absence means nothing\n    // here — reporting it as \"no transport available\" made a healthy system\n    // look broken, and sent at least one investigation after a fault that did\n    // not exist. Routing has already succeeded by this point; say so.\n    const reason = opts.dryRun\n      ? `would dispatch to ${project} (dry run — no session contacted)`\n      : opts.transport\n        ? \"autoDispatch is off\"\n        : \"no transport available — run `pai <project>` to pick this up\";\n\n    return { task, outcome: \"skipped\", session: project, reason };\n  }\n\n  let result: TransportResult;\n  try {\n    result = await opts.transport.dispatch(project, renderTask(task), {\n      spawnIfAbsent: opts.spawnIfAbsent !== false,\n    });\n  } catch (e) {\n    // A transport that throws is an infrastructure failure, not a routing\n    // decision — surface it rather than reporting the task as skipped.\n    return {\n      task,\n      outcome: \"unlaunchable\",\n      session: project,\n      reason: e instanceof Error ? e.message : String(e),\n    };\n  }\n\n  // Only projects with a curated alias can be launched. That is a setup gap,\n  // not a bug — name the fix so it is actionable without digging.\n  if (result.outcome === \"unlaunchable\" && !result.reason?.includes(\"pai project name\")) {\n    return {\n      task,\n      outcome: \"unlaunchable\",\n      session: result.session ?? project,\n      reason: `${result.reason ?? \"no session could be launched\"} — give it an alias with \\`pai project name <identifier> ${project}\\``,\n    };\n  }\n\n  return { task, outcome: result.outcome, session: result.session ?? project, reason: result.reason };\n}\n\n/**\n * Dispatch a batch, sequentially.\n *\n * Deliberately not parallel: dispatching spawns terminal tabs and types into\n * them. Racing that would interleave input across sessions.\n */\nexport async function dispatchAll(\n  tasks: Task[],\n  opts: DispatchOptions,\n): Promise<DispatchResult[]> {\n  const results: DispatchResult[] = [];\n  for (const task of tasks) {\n    results.push(await dispatchTask(task, opts));\n  }\n  return results;\n}\n","/**\n * scheduler.ts — Todoist-driven routine scheduler\n *\n * Todoist holds the schedule, the ordering, the dependencies and the run state.\n * This module is a dumb poller: it reads that state, decides what should happen\n * now, and reports. No LLM, so a tick costs nothing but an API call.\n *\n * Measured against the live Todoist API before this was written:\n *\n *   - Completing a recurring task does NOT create a new one. The SAME task\n *     rolls forward: same id, new due date, immediately unchecked. So \"is it\n *     closed?\" never detects completion for a recurring task — it is closed for\n *     an instant at most. Completion is \"the due date advanced\".\n *\n *   - Labels survive that roll-forward. A running marker will therefore NOT\n *     clear itself; the poller has to reconcile it, or the task looks\n *     permanently in flight and silently never runs again.\n *\n *   - Todoist never accumulates missed occurrences. Three days powered off\n *     leaves ONE overdue task, not three, and completing it jumps to the next\n *     future occurrence. There is no backlog to drain — but equally, Todoist\n *     cannot tell you how many windows were missed, so overdue-by-N is computed\n *     here.\n *\n *   - Writing `due_date` DESTROYS a recurrence rule, silently turning a routine\n *     into a one-off. This module must only ever write `due_string`.\n */\n\nimport type { Task } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Label vocabulary\n// ---------------------------------------------------------------------------\n\n/** Set when dispatched, cleared when the due date advances. */\nexport const RUNNING_LABEL = \"pai-running\";\n\n/** Run however late. The default when nothing is specified. */\nexport const CATCHUP_LABEL = \"pai-catchup\";\n\n/** `pai-skip-if-late:4h` — past that, the window is gone; wait for the next. */\nconst SKIP_IF_LATE_RE = /^pai-skip-if-late:(\\d+)([hm])$/i;\n\n/** Fallback when a task has no learned duration yet. */\nexport const DEFAULT_EXPECTED_MINUTES = 30;\n\n/** Probe once the run exceeds expected x this. */\nconst PROBE_FACTOR = 1.5;\n\n/** Consecutive unanswered probes before a run counts as stuck. */\nexport const STUCK_AFTER_FAILED_PROBES = 3;\n\n/**\n * Multiples of the expected duration after which a claim is released outright.\n *\n * The running label became an interlock once AIBroker's webhook started skipping\n * any task carrying it (0.17.1). That closed the duplicate-dispatch hole and\n * opened a worse one: nothing here ever took the label off except a completion,\n * so a session that died mid-turn left the task claimed forever — the webhook\n * skipping it, the poller probing it, and neither ever running it again. Silent\n * permanent non-execution is the exact failure this subsystem exists to prevent,\n * and it is worse than a duplicate run.\n *\n * Deliberately far beyond the probe threshold and NOT conditional on a probe\n * saying the session is gone. An empty session list is how the hub itself failed\n * on 2026-08-01, so \"absent\" is not trustworthy evidence; elapsed time is. A\n * sweep that has been in flight for five hours is not in flight.\n */\nexport const ABANDON_FACTOR = 10;\n\n/** Floor for the same, so a task with a short learned duration is not released early. */\nexport const ABANDON_FLOOR_MINUTES = 120;\n\n/** When a claim is old enough to be released regardless of what probes say. */\nexport function abandonAfterMinutes(observed: number[]): number {\n  return Math.max(expectedMinutes(observed) * ABANDON_FACTOR, ABANDON_FLOOR_MINUTES);\n}\n\n// ---------------------------------------------------------------------------\n// Decisions\n// ---------------------------------------------------------------------------\n\nexport type Decision =\n  /** Not due yet. */\n  | { action: \"wait\"; task: Task }\n  /**\n   * Ticked off in the tracker by hand — run it now, and put the schedule back.\n   *\n   * `restoreTo` is the due_string to write afterwards, or null to leave the\n   * advanced date alone (the occurrence that was just consumed is already past,\n   * so restoring it would only produce an immediately-overdue task).\n   */\n  | { action: \"triggered\"; task: Task; restoreTo: string | null }\n  /** Due (or overdue within policy) and not running — dispatch it. */\n  | { action: \"dispatch\"; task: Task; overdueMinutes: number }\n  /** Overdue past its skip-if-late window — leave for the next occurrence. */\n  | { action: \"skip\"; task: Task; reason: string }\n  /** Running and within its expected duration — leave alone. */\n  | { action: \"running\"; task: Task; elapsedMinutes: number }\n  /** Running and overrun — ask the session whether it is alive. */\n  | { action: \"probe\"; task: Task; elapsedMinutes: number; expectedMinutes: number }\n  /** Was running, due date has advanced — it finished. */\n  | { action: \"complete\"; task: Task; durationMinutes: number | null }\n  /**\n   * Claimed so long ago that no run is plausibly still going — release it.\n   *\n   * Not a re-dispatch: the claim comes off and nothing else happens this tick.\n   * The task is still overdue, so the next tick dispatches it through the normal\n   * path, claim and all. Splitting it that way keeps one dispatcher.\n   */\n  | { action: \"abandoned\"; task: Task; elapsedMinutes: number; thresholdMinutes: number }\n  /** Marked running but nothing knows about it — reconcile. */\n  | { action: \"orphaned\"; task: Task; reason: string };\n\n/** In-flight run bookkeeping. Transient: losing it costs one extra probe. */\nexport interface RunState {\n  /** Task id → epoch ms when dispatched. */\n  startedAt: Record<string, number>;\n  /** Task id → consecutive probes with no reply. */\n  failedProbes: Record<string, number>;\n}\n\nexport const EMPTY_RUN_STATE: RunState = { startedAt: {}, failedProbes: {} };\n\n// ---------------------------------------------------------------------------\n// Policy helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Did THIS poller start the run currently in flight?\n *\n * The distinction from {@link isClaimedByAnyone} has now caused three separate\n * defects in three functions across two days, so it is named rather than\n * re-derived from `startedAt` at each site.\n *\n * `startedAt` is set only when this poller dispatches. A run triggered through\n * the tracker's webhook is claimed by AIBroker before this poller ever sees the\n * task, so it has no `startedAt` here — and every piece of evidence that is\n * sound for our own runs is worthless for those. Twice that produced a\n * confident wrong answer: `overdue < 0` reported a webhook run finished on the\n * tick after it started, and an orphan sweep keyed on `startedAt` would have\n * discarded exactly the entries the webhook path depends on.\n *\n * Use this when the question is \"can I trust state I recorded myself\".\n */\nexport function isOurRun(state: RunState, taskId: string): boolean {\n  return state.startedAt[taskId] !== undefined;\n}\n\n/**\n * Is anyone running this task — us, or a session claimed through the tracker?\n *\n * The RUNNING label is the shared interlock: whoever claims a task sets it,\n * whichever side they are. Use this when the question is \"is a run in flight at\n * all\", which is what liveness means for anything scoped to a run.\n */\nexport function isClaimedByAnyone(task: Task, state: RunState): boolean {\n  return isRunning(task) || isOurRun(state, task.id);\n}\n\nexport function isRunning(task: Task): boolean {\n  return task.labels.some((l) => l.toLowerCase() === RUNNING_LABEL);\n}\n\n/** Minutes after which a missed window is abandoned, or null for \"always run\". */\nexport function skipIfLateMinutes(task: Task): number | null {\n  for (const label of task.labels) {\n    const m = SKIP_IF_LATE_RE.exec(label.trim());\n    if (m) {\n      const n = Number.parseInt(m[1]!, 10);\n      return m[2]!.toLowerCase() === \"h\" ? n * 60 : n;\n    }\n  }\n  return null;\n}\n\n/**\n * How overdue a task is, in minutes. Negative means still in the future.\n *\n * A date-only due (\"2026-08-01\") is treated as due at the start of that day,\n * which is what Todoist shows the user.\n */\nexport function overdueMinutes(task: Task, now: number): number {\n  if (!task.due) return Number.NEGATIVE_INFINITY;\n  const due = task.due.length <= 10 ? `${task.due}T00:00:00` : task.due;\n  const dueMs = new Date(due).getTime();\n  if (Number.isNaN(dueMs)) return Number.NEGATIVE_INFINITY;\n  return Math.floor((now - dueMs) / 60_000);\n}\n\n// ---------------------------------------------------------------------------\n// Manual trigger (the checkbox as a Run Now button)\n// ---------------------------------------------------------------------------\n\n/**\n * Why this is inferred from the due date rather than observed directly:\n *\n * Completing a recurring task leaves no completion event anywhere the API will\n * show us. Measured 2026-08-01 against the live account — a recurring task was\n * created, completed, and then queried through the completed-by-completion-date\n * endpoint: it is absent. The task itself comes straight back as open with the\n * due date advanced and the same id. So \"the due date jumped forward while we\n * were not running it\" is the ONLY evidence a human ticked the box.\n *\n * That evidence is ambiguous — dragging the date forward looks identical — so\n * the jump is checked against the recurrence period before it is believed. A\n * daily task that moves by exactly one day was ticked; one that moves by three\n * was rescheduled, and firing a sweep on that would both waste a run and undo\n * the user's edit.\n */\n\nconst MS_PER_DAY = 86_400_000;\n\n/**\n * Day-deltas consistent with one occurrence of `recurrence` elapsing.\n *\n * A set rather than a number because real rules are not fixed-length: a monthly\n * task advances by 28 to 31 days, and \"every weekday\" by one to three. Null\n * means the rule was not recognised, which the caller treats as permissive —\n * an unrecognised recurrence is still a recurrence, and refusing to fire is the\n * failure mode this whole mechanism exists to remove.\n */\nexport function expectedAdvanceDays(recurrence: string | null | undefined): number[] | null {\n  if (!recurrence) return null;\n  const r = recurrence.trim().toLowerCase();\n\n  if (/\\bevery\\s+(work ?day|weekday)/.test(r)) return [1, 2, 3];\n  if (/\\bevery\\s+other\\s+day\\b/.test(r)) return [2];\n  if (/\\bevery\\s+(\\d+)\\s+days?\\b/.test(r)) {\n    const n = Number.parseInt(/\\bevery\\s+(\\d+)\\s+days?\\b/.exec(r)![1]!, 10);\n    return n > 0 ? [n] : null;\n  }\n  if (/\\bevery\\s+day\\b/.test(r) || /\\bdaily\\b/.test(r)) return [1];\n  if (/\\bevery\\s+(\\d+)\\s+weeks?\\b/.test(r)) {\n    const n = Number.parseInt(/\\bevery\\s+(\\d+)\\s+weeks?\\b/.exec(r)![1]!, 10);\n    return n > 0 ? [n * 7] : null;\n  }\n  // Before the weekday rule: an unanchored \"mon\" alternative also matches the\n  // \"mon\" in \"month\", which classified a monthly routine as weekly.\n  if (/\\bevery\\s+(month|\\d+\\s+months?)\\b/.test(r) || /\\bmonthly\\b/.test(r)) return [28, 29, 30, 31];\n  if (/\\bevery\\s+year\\b/.test(r) || /\\byearly\\b/.test(r) || /\\bannually\\b/.test(r)) return [365, 366];\n  if (\n    /\\bevery\\s+(?:week|mondays?|tuesdays?|wednesdays?|thursdays?|fridays?|saturdays?|sundays?|mon|tue|tues|wed|thu|thur|thurs|fri|sat|sun)\\b/.test(r) ||\n    /\\bweekly\\b/.test(r)\n  ) {\n    return [7];\n  }\n\n  return null;\n}\n\n/** Whole days between two due values, rounded — times of day are preserved by\n *  both a completion and a drag, so they carry no signal either way. */\nfunction advanceDays(prevDue: string, newDue: string): number {\n  const a = new Date(prevDue.length <= 10 ? `${prevDue}T00:00:00` : prevDue).getTime();\n  const b = new Date(newDue.length <= 10 ? `${newDue}T00:00:00` : newDue).getTime();\n  if (Number.isNaN(a) || Number.isNaN(b)) return Number.NaN;\n  return Math.round((b - a) / MS_PER_DAY);\n}\n\n/**\n * Did this task advance because someone ticked it off?\n *\n * A non-recurring task is never a trigger: completing one closes it, and it\n * drops out of the open list rather than reappearing with a later date. So a\n * forward jump there is unambiguously a reschedule.\n */\nexport function wasTicked(task: Task, prevDue: string | null | undefined): boolean {\n  if (!prevDue || !task.due || !task.recurrence) return false;\n  if (task.due <= prevDue) return false;\n\n  const moved = advanceDays(prevDue, task.due);\n  if (!Number.isFinite(moved) || moved <= 0) return false;\n\n  const expected = expectedAdvanceDays(task.recurrence);\n  return expected === null ? true : expected.includes(moved);\n}\n\n/**\n * The due_string that puts a ticked task back where it was.\n *\n * Rule and date in one string on purpose. Writing the date alone through the\n * tracker's date field drops the recurrence and quietly demotes a routine to a\n * one-off — the failure the provider's `setDue` comment warns about. Todoist\n * parses \"every day at 08:00 starting 2026-08-02\" and honours both halves;\n * verified against the live API on 2026-08-01.\n */\nexport function restoreDueString(recurrence: string, prevDue: string): string {\n  const date = prevDue.slice(0, 10);\n  const hasTime = prevDue.length > 10;\n  const ruleCarriesTime = /\\bat\\s+\\d/.test(recurrence);\n\n  // \"every day\" plus a datetime due would come back as a date-only occurrence,\n  // losing the time of day. Carry it explicitly when the rule does not.\n  const time = hasTime && !ruleCarriesTime ? ` at ${prevDue.slice(11, 16)}` : \"\";\n  return `${recurrence}${time} starting ${date}`;\n}\n\n/**\n * When a restored slot actually falls due — the inverse of `restoreDueString`.\n *\n * Callers need this to decide whether putting the slot back is still useful.\n * Restoring one that has already passed writes a task that is overdue the\n * instant it lands, and the next tick dispatches it, which advances the date,\n * which restores it again: an unbounded loop that fires every tick for the rest\n * of the day. That is not hypothetical — it happened on 2026-08-04 with \"every\n * day at 9am\", which re-dispatched roughly every 30 minutes (two 900s ticks)\n * because the guard compared against the end of the day rather than 09:00.\n *\n * Both shapes `restoreDueString` can emit put the time after \"at\", so one match\n * covers \"every day at 9am starting …\" and \"every week at 08:00 starting …\".\n * A rule carrying no time at all is a genuine date-only occurrence, and for\n * those the slot really does run until the end of the day.\n */\nexport function triggeredSlotMs(keep: string, date: string): number {\n  const at = /\\bat\\s+(\\d{1,2})(?::(\\d{2}))?\\s*([ap]m)?/i.exec(keep);\n  if (!at) return new Date(`${date}T23:59:59`).getTime();\n\n  let hour = Number(at[1]);\n  const suffix = at[3]?.toLowerCase();\n  if (suffix === \"pm\" && hour < 12) hour += 12;\n  if (suffix === \"am\" && hour === 12) hour = 0;\n\n  const hh = String(hour).padStart(2, \"0\");\n  const mm = String(Number(at[2] ?? 0)).padStart(2, \"0\");\n  return new Date(`${date}T${hh}:${mm}:00`).getTime();\n}\n\n// ---------------------------------------------------------------------------\n// Duration learning\n// ---------------------------------------------------------------------------\n\n/**\n * Expected runtime for a task.\n *\n * The mean rather than the max, deliberately: with a liveness probe available,\n * guessing low costs one extra probe, while guessing high leaves a dead session\n * undetected for far longer. The probe is what makes a tight estimate safe.\n *\n * Runs that ended in stuck or re-dispatch must never be fed in here — one hung\n * run would inflate the mean and every later run would inherit a too-generous\n * threshold.\n */\nexport function expectedMinutes(observed: number[], fallback = DEFAULT_EXPECTED_MINUTES): number {\n  const clean = observed.filter((n) => Number.isFinite(n) && n > 0);\n  if (clean.length === 0) return fallback;\n  const recent = clean.slice(-5);\n  return Math.max(1, Math.round(recent.reduce((a, b) => a + b, 0) / recent.length));\n}\n\n// ---------------------------------------------------------------------------\n// The decision function\n// ---------------------------------------------------------------------------\n\nexport interface DecideOptions {\n  now: number;\n  state: RunState;\n  /** Task id → observed durations in minutes, most recent last. */\n  history?: Record<string, number[]>;\n  /**\n   * Task id → the due date seen on the previous tick.\n   *\n   * The only way to notice a task was ticked off by hand. Absent on the first\n   * tick after install, which is why a missing entry can never mean \"it moved\".\n   */\n  lastSeenDue?: Record<string, string>;\n  /**\n   * True when a tracker webhook is delivering completion events.\n   *\n   * When set, the due-date trigger inference is switched off: the webhook sees\n   * the real event, so the guess can only add false positives. Defaults to\n   * false so a machine with no webhook keeps the fallback — which is the case\n   * the inference was written for.\n   */\n  webhookActive?: boolean;\n  /**\n   * Task id → when this poller FIRST saw the task carrying a running claim.\n   *\n   * Needed because the claim is no longer only ours to make: AIBroker's webhook\n   * claims and dispatches on its own, so a claimed task frequently has no start\n   * time here. This is the clock for those runs — both to age them out and to\n   * tell a claim we have been watching from one we are seeing for the first time.\n   */\n  claimSeenAt?: Record<string, number>;\n}\n\n/**\n * Decide what should happen to one task on this tick.\n *\n * Pure: no I/O, no clock reads. Everything comes in via options so the state\n * machine can be tested against a fixed clock rather than by waiting.\n */\nexport function decide(task: Task, opts: DecideOptions): Decision {\n  const { now, state } = opts;\n  const running = isRunning(task);\n  const overdue = overdueMinutes(task, now);\n  const startedAt = state.startedAt[task.id];\n\n  if (running) {\n    const claimSeenAt = opts.claimSeenAt?.[task.id];\n    const prevDue = opts.lastSeenDue?.[task.id];\n    const dueAdvanced = prevDue !== undefined && task.due !== null && task.due > prevDue;\n    const ourRun = isOurRun(state, task.id);\n\n    // Completion is \"the due date moved into the future\" — not \"closed\". A\n    // recurring task is closed for an instant at most, so testing for closed\n    // would never fire.\n    //\n    // But `overdue < 0` alone is only evidence for a run WE started. A run\n    // dispatched by AIBroker's webhook is triggered by the user ticking the box,\n    // which advances the due date before the run even begins — so the due sits\n    // in the future for the whole run, and this branch reported it finished on\n    // the very next tick and stripped the interlock mid-flight. Measured against\n    // a live probe on 2026-08-01: \"✓ would clear pai-running\" while the sweep\n    // was still going.\n    //\n    // For someone else's run the completion signal is the due date advancing\n    // AGAIN — and only once we have watched the claim across a tick, since the\n    // first advance we see is the trigger itself, not an ending.\n    const finishedElsewhere = dueAdvanced && claimSeenAt !== undefined && claimSeenAt < now;\n\n    if ((ourRun && overdue < 0) || finishedElsewhere) {\n      const duration = startedAt ? Math.max(1, Math.round((now - startedAt) / 60_000)) : null;\n      return { action: \"complete\", task, durationMinutes: duration };\n    }\n\n    // Clock for everything below. A claim we did not make still has to age out,\n    // or a webhook run whose session died would hold the interlock forever.\n    const clock = startedAt ?? claimSeenAt;\n    if (clock === undefined) {\n      // First sighting of a claim with nothing to date it by. Either the state\n      // file was lost or another dispatcher just claimed it. Probing is the safe\n      // response — never assume it is dead and re-dispatch. The next tick has a\n      // claim time and can age it properly.\n      return {\n        action: \"orphaned\",\n        task,\n        reason: \"marked running but no start time is known — probe before assuming anything\",\n      };\n    }\n\n    const elapsed = Math.max(0, Math.round((now - clock) / 60_000));\n    const expected = expectedMinutes(opts.history?.[task.id] ?? []);\n\n    // Checked before probing: past this point the probe's answer no longer\n    // changes what should happen. Even a session replying \"yes, still working\"\n    // has been working ten times its usual run, and leaving the claim on is what\n    // turns one dead session into a routine that never runs again.\n    const abandonAfter = abandonAfterMinutes(opts.history?.[task.id] ?? []);\n    if (elapsed >= abandonAfter) {\n      return { action: \"abandoned\", task, elapsedMinutes: elapsed, thresholdMinutes: abandonAfter };\n    }\n\n    // Only interrogate our own runs. Asking after a session another dispatcher\n    // started tells us nothing actionable — we would not re-dispatch it either\n    // way — and the ageing above already bounds it.\n    if (!ourRun) return { action: \"running\", task, elapsedMinutes: elapsed };\n\n    if (elapsed >= expected * PROBE_FACTOR) {\n      return { action: \"probe\", task, elapsedMinutes: elapsed, expectedMinutes: expected };\n    }\n    return { action: \"running\", task, elapsedMinutes: elapsed };\n  }\n\n  // Checked before the wait branch, because a task that was just ticked always\n  // lands in the future — it would otherwise be indistinguishable from one that\n  // is simply not due yet, which is exactly how the checkbox came to look like\n  // a button that silently pushed the sweep out by a day.\n  const prevDue = opts.lastSeenDue?.[task.id];\n\n  // A claim we were watching until a moment ago means this advance is a run\n  // ENDING, not a box being ticked. Since aibroker 0.17.4 the finishing session\n  // drops the claim itself and then advances the recurrence with `pai task\n  // done` — so by the next tick the task is unclaimed with its due date exactly\n  // one period on, which is indistinguishable from a hand-tick and would have\n  // dispatched the same work a second time.\n  //\n  // The entry is still here because tick() clears it AFTER deciding, which is\n  // precisely the window this needs. When a whole run fits between two ticks we\n  // never see the claim at all — but then the due has advanced twice, once for\n  // the trigger and once for the completion, and the period check rejects it.\n  const wasClaimed = opts.claimSeenAt?.[task.id] !== undefined;\n\n  // Suppressed entirely where a webhook is live, and that is a deliberate\n  // precedence decision rather than a tuning knob.\n  //\n  // The inference reads \"due advanced by exactly one period, unclaimed\" as a\n  // tick. For a DAILY recurrence that is byte-identical to a human dragging the\n  // task forward one day, and to this poller repairing a due date it wrote\n  // wrongly itself. There is no local signal that separates them — it is an\n  // irreducible false positive, not something a tighter check can remove.\n  //\n  // A webhook has the actual item:completed event and no ambiguity at all. So\n  // where one is running, guessing is strictly worse than not guessing, and\n  // leaving both active means the fallback keeps firing on reschedules the\n  // webhook correctly ignored — which is how repairing one duplicate sweep\n  // produced a second on 2026-08-02.\n  //\n  // The costs are asymmetric, which is what settles it. A false positive\n  // dispatches real work nobody asked for — a sweep that scrapes, writes and\n  // sends mail. A false negative merely delays a hand-triggered run until the\n  // next tick, which then dispatches it on the ordinary overdue path. Guessing\n  // wrong in the expensive direction to save minutes in the cheap one is a bad\n  // trade wherever the unambiguous signal exists.\n  if (!opts.webhookActive && !wasClaimed && wasTicked(task, prevDue)) {\n    // Restore only a slot that has not passed. Putting back an occurrence that\n    // is already overdue leaves a task that reads as late the instant it is\n    // written, for a run that is starting right now.\n    const prevMs = new Date(prevDue!.length <= 10 ? `${prevDue}T00:00:00` : prevDue!).getTime();\n    const restoreTo =\n      task.recurrence && prevMs > now ? restoreDueString(task.recurrence, prevDue!) : null;\n    return { action: \"triggered\", task, restoreTo };\n  }\n\n  if (overdue < 0) return { action: \"wait\", task };\n\n  const limit = skipIfLateMinutes(task);\n  if (limit !== null && overdue > limit) {\n    return {\n      action: \"skip\",\n      task,\n      reason: `overdue by ${overdue}m, past its ${limit}m window — waiting for the next occurrence`,\n    };\n  }\n\n  return { action: \"dispatch\", task, overdueMinutes: Math.max(0, overdue) };\n}\n\n/**\n * Order tasks for dispatch.\n *\n * Subtasks of the same parent run in Todoist's own order, so a routine is\n * sequenced by dragging its steps in the UI. Everything else falls back to\n * priority then due time, so the most overdue urgent thing goes first.\n */\nexport function dispatchOrder(tasks: Task[]): Task[] {\n  const rank = { p1: 0, p2: 1, p3: 2, p4: 3 } as const;\n  return [...tasks].sort((a, b) => {\n    const p = rank[a.priority] - rank[b.priority];\n    if (p !== 0) return p;\n    return (a.due ?? \"\").localeCompare(b.due ?? \"\");\n  });\n}\n","/**\n * poller.ts — one scheduler tick\n *\n * Reads Todoist, decides, acts, reports. Run by launchd on an interval; carries\n * no LLM, so a tick that finds nothing to do costs one API call and no tokens.\n *\n * Run state (start times, probe counts) is deliberately local and transient.\n * Losing it costs one extra probe, not a wrong decision — `decide` returns\n * \"orphaned\" rather than assuming a task is dead. The durable state — schedule,\n * order, running flag, learned durations — all lives in Todoist, so a new\n * machine picks up where this one left off.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { execFileSync } from \"node:child_process\";\nimport { readJsonStrict, writeJsonAtomic } from \"../config/json-store.js\";\nimport { paiHomePath, resolvePaiFile, migratePaiFile, type MigrateFileResult } from \"../config/pai-home.js\";\nimport type { TodoistProvider } from \"./providers/todoist.js\";\nimport type { Transport } from \"./dispatch.js\";\nimport { dispatchTask } from \"./dispatch.js\";\nimport {\n  decide,\n  dispatchOrder,\n  isRunning,\n  isClaimedByAnyone,\n  RUNNING_LABEL,\n  STUCK_AFTER_FAILED_PROBES,\n  type Decision,\n  expectedMinutes,\n  type RunState,\n  EMPTY_RUN_STATE,\n  wasTicked,\n  restoreDueString,\n  triggeredSlotMs,\n} from \"./scheduler.js\";\nimport type { Task } from \"./types.js\";\n\n/** Old scheduler-state.json path, inside ~/.pai/ (pre-2026-09-19). */\nfunction oldStateFilePath(): string {\n  return join(homedir(), \".pai\", \"scheduler-state.json\");\n}\n\n/** Scheduler state path: PAI_HOME/scheduler-state.json if present, else the\n *  old ~/.pai/scheduler-state.json (one-time stderr notice), else the new path. */\nexport const STATE_FILE = resolvePaiFile(\n  paiHomePath(\"scheduler-state.json\"),\n  [oldStateFilePath()],\n  \"pai config migrate --scheduler-state\"\n);\n\nexport function migrateSchedulerState(opts: { dryRun?: boolean } = {}): MigrateFileResult {\n  return migratePaiFile(paiHomePath(\"scheduler-state.json\"), [oldStateFilePath()], opts);\n}\n\n/** How many past durations to keep per task. */\nconst HISTORY_LIMIT = 5;\n\nexport interface PersistedState extends RunState {\n  /** Task id → observed durations in minutes, most recent last. */\n  history: Record<string, number[]>;\n  /** Task id → epoch ms of the last completion we reported. */\n  lastReported: Record<string, number>;\n  /**\n   * Task id → consecutive dispatch attempts that did not land.\n   *\n   * Without this a task whose session has died to a shell is retried every\n   * tick, forever, reporting the same failure each time and escalating never —\n   * the \"silently never runs\" failure this whole subsystem exists to avoid.\n   */\n  failedDispatches: Record<string, number>;\n  /**\n   * Task id → epoch ms of the last time an alarm about it reached the user.\n   *\n   * Every tick that raised an alarm before this existed raised it into\n   * /tmp/pai-scheduler.log and nowhere else. Measured 2026-08-03: the daily\n   * check had logged \"unrouted — cannot dispatch\" 151 times across two days,\n   * on a task whose whole purpose was to notice things failing silently.\n   *\n   * Kept per task, and honoured for a day, because the two wrong answers are\n   * symmetric: notify every tick and the alarm is muted as noise within the\n   * hour, notify once ever and a fault fixed-then-reintroduced is silent the\n   * second time.\n   */\n  alarmedAt: Record<string, number>;\n  /**\n   * Task id → the due date this tick saw.\n   *\n   * Durable rather than transient, unlike the rest of this file: it is the only\n   * record that a recurring task's due date moved, and a lost entry means the\n   * next tick cannot tell a hand-ticked task from one that was never due. That\n   * costs a missed run, not a wrong one — the first tick after a reset simply\n   * re-learns every date and triggers nothing.\n   */\n  lastSeenDue: Record<string, string>;\n  /**\n   * Task id → the due_string a hand-triggered run has to get back.\n   *\n   * Restoring once at trigger time is not enough. The session ends its run with\n   * `pai task done`, which advances the recurrence a second time — so a sweep\n   * run by hand on Saturday evening silently ate Sunday morning's scheduled\n   * one. Held here until the completion is seen, then re-applied.\n   */\n  triggeredRestore: Record<string, string>;\n  /**\n   * Task id → when this poller first saw a running claim on it.\n   *\n   * The claim is no longer only ours to make — AIBroker's webhook claims and\n   * dispatches on its own — so a claimed task often has no start time here.\n   * This dates those runs, which is what lets them age out instead of holding\n   * the interlock forever, and what separates a claim we have been watching\n   * from one appearing for the first time.\n   */\n  claimSeenAt: Record<string, number>;\n  /**\n   * Task id → the failure that made this task stop being dispatched.\n   *\n   * `failedDispatches` counts attempts and gates the ALARM. It never gated the\n   * RETRY, so a task whose dispatch could not possibly succeed was re-attempted\n   * every tick for as long as it stayed due. On 2026-08-04 that meant a fresh\n   * terminal window every fifteen minutes for nine hours, against a project\n   * directory that had been renamed months earlier: each attempt opened a tab,\n   * failed its `cd`, waited out the full 90s readiness timeout and gave up,\n   * changing nothing before the next one.\n   *\n   * Parking is the missing state between \"failed\" and \"gave up\". A parked task\n   * is still visible, still reported, and still escalates once — it simply\n   * stops being launched at something that cannot accept it. It unparks when\n   * the task's due date moves (the user rescheduled it) or when its parked\n   * reason no longer applies, which is what keeps this from being a one-way\n   * door that quietly retires a task nobody notices is gone.\n   */\n  parked: Record<string, { reason: string; at: number; due?: string }>;\n  /**\n   * Task id → consecutive re-dispatch attempts triggered by a probe reply\n   * that starts with NOT-RECEIVED, for the claim currently in flight.\n   *\n   * Separate from `failedProbes`: that counter tracks silence, this one\n   * tracks an explicit \"I was never told about this\" reply, which needs its\n   * own cap so a dispatch that never lands stops being retried forever\n   * rather than every tick.\n   */\n  notReceivedAttempts: Record<string, number>;\n  /**\n   * Task id → epoch ms before which another NOT-RECEIVED reply must not\n   * trigger another re-dispatch.\n   *\n   * Deliberately not cleared on a \"delivered\"/\"queued\"/\"spawned\" transport\n   * result — that is exactly the false signal this field exists to survive:\n   * a transport reporting success does not mean the session received\n   * anything, so the backoff clock only resets on a confirmed-alive reply or\n   * on the claim being released.\n   */\n  notReceivedRetryAt: Record<string, number>;\n}\n\n/**\n * A fresh blank state, built per call rather than shared.\n *\n * This was a module-level constant spread into every load. A spread is shallow,\n * so any field the persisted file did not carry — a fresh install, or a file\n * written before that field existed — aliased the constant's own object, and\n * writes to it leaked into every later load in the process. Harmless for the\n * launchd tick, which loads once and exits, and quietly wrong everywhere else:\n * it made test runs contaminate each other and would do the same to any caller\n * that ticked twice.\n */\nfunction emptyState(): PersistedState {\n  return {\n    startedAt: {},\n    failedProbes: {},\n    history: {},\n    lastReported: {},\n    failedDispatches: {},\n    alarmedAt: {},\n    lastSeenDue: {},\n    triggeredRestore: {},\n    claimSeenAt: {},\n    parked: {},\n    notReceivedAttempts: {},\n    notReceivedRetryAt: {},\n  };\n}\n\n/** Consecutive failed dispatches before a task is reported as needing attention. */\nconst DISPATCH_FAILURES_BEFORE_ALARM = 3;\n\n/** How long an alarm about one task stays quiet before it is repeated. */\nconst ALARM_REPEAT_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Tell the user about a task that is scheduled and not running.\n *\n * The counterpart of the storage-backend escalation: a subsystem whose entire\n * job is noticing silent failure has to be the last thing that fails silently.\n * `report.stuck` counted these accurately and showed the count in a CLI line\n * that only appears if somebody runs a tick by hand — which, for a job launchd\n * runs every 15 minutes, is nobody.\n *\n * Never allowed to throw: a notification that cannot be delivered must not take\n * down the tick, because the tick is what dispatches everything else.\n */\nasync function escalate(\n  task: Task,\n  headline: string,\n  detail: string,\n  state: PersistedState,\n  now: number,\n  opts?: TickOptions\n): Promise<void> {\n  const last = state.alarmedAt[task.id];\n  if (last !== undefined && now - last < ALARM_REPEAT_MS) return;\n  state.alarmedAt[task.id] = now;\n\n  let delivered = false;\n  try {\n    const { routeNotification } = await import(\"../notifications/router.js\");\n    const { loadConfig } = await import(\"../daemon/config.js\");\n    const result = await routeNotification(\n      {\n        event: \"error\",\n        title: `PAI: ${headline}`,\n        // `title`, not `content`. Todoist's own API calls this field `content`\n        // and the provider renames it on the way in; writing the wire name here\n        // compiled to `undefined` and shipped in v0.27.0, so every alarm this\n        // path has ever sent read `\"undefined\" is due but has no owner`. The\n        // test below asserts the title appears, because the one that existed\n        // asserted only the tail of the sentence and passed all the way through.\n        message: `\"${task.title}\" ${detail}`,\n      },\n      loadConfig().notifications\n    );\n    delivered = result.channelsSucceeded.length > 0;\n  } catch {\n    /* see above — the tick matters more than the notification */\n  }\n\n  // Last resort: say it on the task itself.\n  //\n  // routeNotification returns which channels worked, and this used to throw\n  // that away. On 2026-08-04 the WhatsApp provider was dialling a socket that\n  // had stopped existing when Whazaa became a thin adapter, so `error` events\n  // silently failed — four escalations about a job that had not run in nine\n  // hours reached /tmp/pai-scheduler.log and nowhere else, and the user found\n  // out by looking.\n  //\n  // The tracker is the one channel that cannot be misconfigured out of\n  // existence, because it is where the task already lives and where the user\n  // already looks. A comment is durable, reaches the phone, and survives every\n  // notification channel being down at once.\n  if (delivered || !opts?.provider.comment) return;\n  try {\n    await opts.provider.comment(\n      task.id,\n      `${AGENT_MARK} **${headline}** — ${detail}\\n\\n` +\n        `_Posted here because no notification channel accepted this alert. ` +\n        `Check \\`pai notify status\\`._`\n    );\n  } catch {\n    /* nothing left to try; the tick still matters more */\n  }\n}\n\n/**\n * One best-effort PAILot alert at the moment a task transitions into parked.\n *\n * Parking is silent from the phone's point of view: the tracker note and the\n * scheduler log both say PARKED, but the only person who reads either is the\n * one who already knows. On 2026-09-27 both morning sweeps were parked on a\n * FALSE unreachable (the hub misread a statusline) and nothing surfaced it\n * until the day's run simply did not happen — the exact failure parking's own\n * docs promise to prevent.\n *\n * Deliberately NOT rate-limited beyond its call sites: it runs exactly once\n * per parking event (on the transition, not on every poll while parked), and\n * a re-park after a release is a new event that deserves a new alert. The\n * escalate() quiet window is untouched — this is the announcement, that is\n * the daily reminder.\n *\n * Never allowed to throw or linger: the hub gets a short deadline, and an\n * unreachable hub falls back to the poller log (launchd sends both streams to\n * /tmp/pai-scheduler.log, which already carries every PARKED line).\n */\nasync function alertParked(task: Task, reason: string): Promise<void> {\n  const text =\n    `PAI parked \"${task.title}\" (${task.id}) — the scheduler will not retry it.\\n` +\n    `Reason: ${reason}\\n` +\n    `To release it: fix the cause, then move the due date — due_string, natural ` +\n    `language only (e.g. \"tomorrow 9am\").`;\n  try {\n    const { callAiBroker } = await import(\"../cli/lib/aibroker-client.js\");\n    await callAiBroker(\"pailot_send\", { text }, 8_000);\n  } catch (e) {\n    console.error(\n      `[pai-poller] parked-task alert not delivered to PAILot ` +\n        `(${e instanceof Error ? e.message : String(e)}): ${text}`\n    );\n  }\n}\n\n/**\n * Run state is a rebuildable cache, so a damaged file must not block the\n * scheduler forever — starting fresh is the correct recovery here, which is\n * exactly the case json-store's guard is NOT for.\n */\nexport function loadState(file: string): PersistedState {\n  try {\n    const raw = readJsonStrict(file, \"~/.pai/scheduler-state.json\");\n    return { ...emptyState(), ...(raw as unknown as PersistedState) };\n  } catch {\n    return emptyState();\n  }\n}\n\nexport function saveState(state: PersistedState, file: string): void {\n  writeJsonAtomic(file, state as unknown as Record<string, unknown>, { backup: false });\n}\n\n/**\n * Fold in any completion a concurrent `pai task done` wrote while this tick\n * was busy with the rest of the list.\n *\n * A tick holds one in-memory `state` for its whole run and saves it exactly\n * once, at the end. For a morning routine with several due tasks that run is\n * not instantaneous — each dispatch waits out AIBroker's spawn-readiness\n * window in turn, so the tick can still be working through task 4 minutes\n * after task 1 was launched. `pai task done` has no such window: it loads,\n * releases the claim, stamps `lastSeenDue` to the next occurrence, and saves,\n * all in well under a second. If the session for task 1 finishes and closes\n * it while the tick is still on task 4, the CLI's write lands on disk first —\n * and the tick's blind save at the end, built from the snapshot it took\n * before any of this happened, overwrites it: `lastSeenDue` reverts to the\n * occurrence that was just completed, and `startedAt`/`claimSeenAt` come back\n * because this tick never learned they had been released. The next tick then\n * reads that reverted date as \"due advanced by one period, unclaimed\" — which\n * is `wasTicked`'s exact definition of a hand-tick — and redispatches a task\n * that already finished. Reproduced against a live poller run; see the\n * negative control referenced in Notes/TODO.md.\n *\n * `lastSeenDue` only ever moves forward in normal operation, so a disk value\n * strictly ahead of what this tick computed can only mean a concurrent writer\n * — there is exactly one, `pai task done` — saw a later completion than this\n * tick's stale snapshot knew about. Whenever that happens, this tick's own\n * value is the stale one: defer to disk for that task's date, and carry the\n * claim release forward too, since a more-advanced date with no such release\n * is not a state `pai task done` ever produces.\n *\n * Scoped to exactly the fields `pai task done` touches (`releaseClaim` plus\n * the `lastSeenDue` stamp) — the two writers race over nothing else, so a\n * broader merge would only add risk without removing any.\n */\nfunction reconcileWithDisk(state: PersistedState, stateFile: string): void {\n  const onDisk = loadState(stateFile);\n  for (const [id, oursDue] of Object.entries(state.lastSeenDue)) {\n    const diskDue = onDisk.lastSeenDue[id];\n    if (diskDue !== undefined && diskDue > oursDue) {\n      state.lastSeenDue[id] = diskDue;\n      delete state.startedAt[id];\n      delete state.claimSeenAt[id];\n      delete state.failedProbes[id];\n    }\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Liveness probe\n// ---------------------------------------------------------------------------\n\n/**\n * What a probe found.\n *\n * The distinction that matters is `busy` versus `silent`. Claude Code queues\n * typed input while mid-turn and does not read it until the turn ends, so a\n * session doing exactly the work we gave it stays silent for minutes. Since we\n * probe at expected x1.5 — precisely when a slow task is most likely still\n * working — treating silence as death would have declared healthy sessions\n * stuck on ordinary days and \"recovered\" them out from under themselves.\n *\n * AIBroker therefore decides liveness by sampling the screen before sending\n * anything: a working session animates, an idle one is static. `busy` is\n * positive evidence of life and must never count toward the stuck threshold.\n */\nexport type ProbeState = \"replied\" | \"busy\" | \"silent\" | \"absent\";\n\nexport interface ProbeAnswer {\n  state: ProbeState;\n  reply?: string;\n  reason?: string;\n}\n\nexport interface Prober {\n  /** Ask the owning session whether it is still working. */\n  ask(project: string, question: string): Promise<ProbeAnswer>;\n}\n\n/**\n * Multiple of the expected duration past which a task is flagged even though it\n * keeps reporting `busy`. A session can be alive and still wrong — stuck in a\n * loop, waiting on a prompt that will never come. Liveness is not progress.\n */\nconst GROSS_OVERRUN_FACTOR = 5;\n\n// ---------------------------------------------------------------------------\n// Tick\n// ---------------------------------------------------------------------------\n\nexport interface TickOptions {\n  provider: TodoistProvider;\n  transport: Transport | null;\n  prober: Prober | null;\n  autoDispatch: boolean;\n  dryRun: boolean;\n  now?: number;\n  /**\n   * Where run state is persisted. Overridable so the dispatch path can be\n   * tested at all: a real tick writes to disk, and the alternative — asserting\n   * only through `dryRun` — skips the transport entirely, which is precisely\n   * the code that misreported a delivered task as failed.\n   */\n  stateFile?: string;\n  /**\n   * Override webhook detection. Left unset, the tick asks AIBroker once.\n   *\n   * Present so tests can pin both sides of the precedence rule without a live\n   * daemon — the whole point of the flag is which of two subsystems decides,\n   * and that is not testable if it can only be discovered by shelling out.\n   */\n  webhookActive?: boolean;\n}\n\nexport interface TickReport {\n  decisions: Array<{ decision: Decision; note: string }>;\n  dispatched: number;\n  completed: number;\n  stuck: number;\n  probed: number;\n}\n\n/**\n * Is a tracker webhook delivering completion events on this machine?\n *\n * Asked of AIBroker, which owns the receiver: `todoist status` reports whether\n * an OAuth grant is on file, and without a grant no completion events arrive.\n * Deliberately conservative — anything unexpected reads as \"no webhook\", which\n * leaves the due-date inference running. That is the pre-existing behaviour and\n * degrades to guessing rather than to silence.\n *\n * Cheap enough to run per tick (one short-lived process, once), and read live\n * rather than cached because a grant can lapse at any moment — that happened\n * mid-afternoon on 2026-08-01, and a cached \"yes\" would have suppressed the\n * fallback exactly when it was needed.\n */\nfunction detectWebhook(bin = \"aibroker\"): boolean {\n  try {\n    const out = execFileSync(bin, [\"todoist\", \"status\"], {\n      timeout: 10_000,\n      encoding: \"utf-8\",\n      stdio: [\"ignore\", \"pipe\", \"pipe\"],\n    });\n    // A grant on file is the only thing that matters; without one the receiver\n    // can verify webhooks but cannot resolve them, so no completion lands.\n    return /authoris|authoriz|token on file|grant/i.test(out) && !/no (token|grant)/i.test(out);\n  } catch {\n    return false;\n  }\n}\n\n/**\n * Discard restore entries whose run is no longer in flight.\n *\n * `triggeredRestore` belongs to one run: it records the occurrence that run\n * consumed, to be handed back when the run completes. It was only ever consumed\n * on the completion path, so a run that ended any other way — abandoned, or\n * released by hand — left the entry behind, and it then fired on that task's\n * NEXT completion, whenever that came.\n *\n * Observed 2026-08-02: runs ended by hand the previous night left entries that\n * fired on the first unattended 08:00 completion and wrote the due date from\n * 08-03 back to 08-02. That date was already past, so the task was instantly\n * overdue and the following tick would have dispatched a duplicate sweep.\n *\n * Clearing it at each release site would work, and would have to be remembered\n * at every site added later. Deriving it instead makes the class unreachable:\n * an entry with no claim behind it describes a run that is over, whatever ended\n * it. The claim is the only thing that says a run is still in flight.\n */\nfunction dropOrphanedRestores(tasks: Task[], state: PersistedState): void {\n  const byId = new Map(tasks.map((t) => [t.id, t]));\n\n  for (const id of Object.keys(state.triggeredRestore)) {\n    const task = byId.get(id);\n\n    // Gone from the open list entirely — nothing will complete it again.\n    if (!task) {\n      delete state.triggeredRestore[id];\n      continue;\n    }\n\n    // Liveness means \"a run is in flight at all\", not \"a run we started\" —\n    // a webhook-triggered run is claimed before this poller sees the task, so\n    // keying on our own startedAt would discard exactly the entries the webhook\n    // path depends on, one tick after recording them.\n    if (!isClaimedByAnyone(task, state)) {\n      delete state.triggeredRestore[id];\n    }\n  }\n}\n\nexport async function tick(opts: TickOptions): Promise<TickReport> {\n  const now = opts.now ?? Date.now();\n  const stateFile = opts.stateFile ?? STATE_FILE;\n  const state = loadState(stateFile);\n  const report: TickReport = { decisions: [], dispatched: 0, completed: 0, stuck: 0, probed: 0 };\n\n  const tasks = await opts.provider.listOpen({ includeUnrouted: true });\n  const ordered = dispatchOrder(tasks);\n\n  if (!opts.dryRun) dropOrphanedRestores(tasks, state);\n\n  // Resolved once per tick, not per task: it is a property of the machine.\n  const webhookActive = opts.webhookActive ?? detectWebhook();\n\n  for (const task of ordered) {\n    // Captured before lastSeenDue is overwritten below — both the trigger\n    // detection and the webhook-trigger signature need the PREVIOUS tick's value.\n    const prevDue = state.lastSeenDue[task.id];\n\n    const d = decide(task, {\n      now,\n      state,\n      history: state.history,\n      lastSeenDue: state.lastSeenDue,\n      claimSeenAt: state.claimSeenAt,\n      webhookActive,\n    });\n    let note = \"\";\n\n    // Recorded for every task on every tick, whatever was decided — the next\n    // tick's ability to spot a hand-ticked task depends on it, and skipping the\n    // uninteresting cases is what would leave that blind.\n    if (!opts.dryRun && task.due) state.lastSeenDue[task.id] = task.due;\n\n    // Dated on first sight and dropped the moment the claim clears, so the next\n    // claim on the same task is timed from when IT appeared rather than from a\n    // stale entry belonging to a previous run.\n    if (!opts.dryRun) {\n      if (isRunning(task)) {\n        // A claim appearing at the same moment the due date advanced by exactly\n        // one period is the signature of a webhook-triggered run: the user\n        // ticked the box, AIBroker claimed it and dispatched, all before this\n        // tick. Recorded so the occurrence the tick consumed is given back after\n        // the run, the same as for a trigger this poller handled itself.\n        //\n        // Recorded ONLY — the date is not written back now. AIBroker decides\n        // whether the session may release its claim by checking that the due\n        // date has advanced past the occurrence it noted when claiming, so\n        // moving that date mid-run would make a finished run look unfinished\n        // and strand the claim.\n        if (state.claimSeenAt[task.id] === undefined && task.recurrence && prevDue !== undefined) {\n          if (wasTicked(task, prevDue)) {\n            state.triggeredRestore[task.id] = restoreDueString(task.recurrence, prevDue);\n          }\n        }\n        state.claimSeenAt[task.id] ??= now;\n      } else {\n        delete state.claimSeenAt[task.id];\n      }\n    }\n\n    switch (d.action) {\n      case \"wait\":\n        continue; // not worth reporting\n\n      case \"triggered\": {\n        const r = await handleDispatch(task, 0, opts, state, now);\n        note = `ticked off by hand — ${r.note}`;\n        if (!opts.dryRun) {\n          if (!r.parked) report.dispatched++;\n          if (r.alarm) report.stuck++;\n          note += await restoreSchedule(task, d.restoreTo, opts, state);\n          // Held for the completion, which advances the recurrence again.\n          if (d.restoreTo) state.triggeredRestore[task.id] = d.restoreTo;\n        }\n        break;\n      }\n\n      case \"skip\":\n        note = d.reason;\n        break;\n\n      case \"running\":\n        note = `${d.elapsedMinutes}m elapsed`;\n        break;\n\n      case \"dispatch\": {\n        const r = await handleDispatch(task, d.overdueMinutes, opts, state, now);\n        note = r.note;\n        if (!opts.dryRun) {\n          // A parked task is not dispatched, so it must not be counted as one.\n          // `dispatched: 1, stuck: 1` for a task nothing tried to launch reads\n          // as a dispatch that then went wrong — the opposite of what happened.\n          if (!r.parked) report.dispatched++;\n          if (r.alarm) report.stuck++;\n        }\n        break;\n      }\n\n      case \"complete\":\n        note = await handleComplete(task, d.durationMinutes, opts, state, now);\n        if (!opts.dryRun) report.completed++;\n        break;\n\n      case \"abandoned\": {\n        note = `claimed ${d.elapsedMinutes}m ago, past the ${d.thresholdMinutes}m limit — releasing the claim`;\n        if (!opts.dryRun) {\n          await releaseClaim(task, opts.provider, state);\n          // Reported as needing attention: a run that had to be released this\n          // way did not finish, and the next tick re-dispatching it is a repair,\n          // not business as usual.\n          report.stuck++;\n        }\n        break;\n      }\n\n      case \"probe\":\n      case \"orphaned\": {\n        const elapsed = d.action === \"probe\" ? d.elapsedMinutes : null;\n        const result = await handleProbe(task, elapsed, opts, state, now);\n        note = result.note;\n        if (!opts.dryRun) {\n          report.probed++;\n          if (result.stuck) report.stuck++;\n          if (result.dispatched) report.dispatched++;\n        }\n        break;\n      }\n    }\n\n    report.decisions.push({ decision: d, note });\n  }\n\n  if (!opts.dryRun) {\n    reconcileWithDisk(state, stateFile);\n    saveState(state, stateFile);\n  }\n  return report;\n}\n\n// ---------------------------------------------------------------------------\n// Actions\n// ---------------------------------------------------------------------------\n\n/**\n * Is this failure a verdict rather than a setback?\n *\n * Retrying is the right default — a session that is briefly busy, an iTerm that\n * has not finished launching, a machine under load all resolve on their own.\n * A filesystem that does not have the directory is not one of those.\n *\n * A backstop, NOT the main guard. The missing-root case that motivated all of\n * this is caught before dispatch by asking the disk directly (see\n * `handleDispatch`), because AIBroker never phrases it this way: its report for\n * a vanished root is `unreachable — Launched a session in <path> but it did not\n * become ready within 90s`, which is indistinguishable in text from a machine\n * under load. This function exists for reasons that DO name the fault — a\n * transport throwing ENOENT, a future AIBroker that says so — and it is\n * deliberately narrow, since anything vaguer would park transient failures and\n * stop tasks that were only briefly unlucky.\n */\nfunction isPermanentDispatchFailure(result: { outcome: string; reason?: string }): boolean {\n  const reason = result.reason ?? \"\";\n  return (\n    /project_root_missing/i.test(reason) ||\n    /which does not exist/i.test(reason) ||\n    /no such file or directory/i.test(reason)\n  );\n}\n\nasync function handleDispatch(\n  task: Task,\n  overdue: number,\n  opts: TickOptions,\n  state: PersistedState,\n  now: number\n): Promise<{ note: string; alarm: boolean; parked?: boolean }> {\n  const late = overdue > 5 ? ` (${overdue}m late)` : \"\";\n  if (opts.dryRun) {\n    return { note: `would dispatch to ${task.owner.project ?? \"nobody\"}${late}`, alarm: false };\n  }\n\n  // A parked task is not dispatched, but it is still reported every tick — the\n  // failure mode being avoided here is silence, not noise. Moving the due date\n  // is the unpark gesture: it is what a user does after fixing the cause, and\n  // it needs no command to remember.\n  const parked = state.parked[task.id];\n  if (parked) {\n    if (parked.due === (task.due ?? undefined)) {\n      // Re-escalated, not merely noted. `escalate` is idempotent inside its 24h\n      // window, so this costs one notification a day and buys the thing the\n      // whole v0.27.0 alarm existed for: a task that is not running cannot go\n      // quiet. Alarming once at the moment of parking and never again would\n      // rebuild the original failure in a new shape — a task scheduled every\n      // day, executing never, and saying so only in a log nobody tails.\n      await escalate(\n        task,\n        \"task is parked\",\n        `is still parked (${parked.reason}) and is not being retried. ` +\n          `Fix the cause and move its due date to release it.`,\n        state,\n        now,\n        opts\n      );\n      return {\n        note: `PARKED since ${new Date(parked.at).toISOString().slice(0, 16).replace(\"T\", \" \")} — ${parked.reason}`,\n        alarm: true,\n        parked: true,\n      };\n    }\n    // Rescheduled — the user has acted. Try again.\n    delete state.parked[task.id];\n    delete state.failedDispatches[task.id];\n  }\n\n  // Reaching here at all means the task is DUE: an undated task scores\n  // NEGATIVE_INFINITY overdue and is decided as `wait`, so it never arrives.\n  // That is what separates this from the findings inbox, where UNROUTED is the\n  // normal resting state and alarming would be pure noise — a finding carries\n  // no date and makes no promise. A task with a due date does: it claims it\n  // will run, and unrouted means it never can, by itself, ever.\n  //\n  // So this is the one dispatch failure that retrying cannot fix, and it used\n  // to be the only one that did not alarm. The daily check sat here from\n  // 2026-08-01 to 2026-08-03 reading \"recurring, every day at 9am\" while doing\n  // nothing, and was found by a human reading the tracker, not by this code.\n  if (!task.owner.project) {\n    const hint = task.owner.rawHint ? ` (${task.owner.rawHint} matches no project)` : \"\";\n    await escalate(\n      task,\n      \"scheduled task cannot run\",\n      `is due${late} but has no owner${hint}, so nothing will ever pick it up. ` +\n        `Move it into a session's sub-project, or give that container an alias.`,\n      state,\n      now,\n      opts\n    );\n    return { note: `unrouted — cannot dispatch${hint}${late}`, alarm: true };\n  }\n\n  // A registered root that is not on disk is checked HERE, before anything is\n  // spawned, because the transport cannot tell us about it.\n  //\n  // This is what the 2026-08-04 incident actually looked like from AIBroker's\n  // side: it opened the tab, the shell's `cd` failed, Claude never started, and\n  // it reported `unreachable — Launched a session in <path> but it did not\n  // become ready within 90s`. That sentence names the path and says nothing\n  // about it missing, so matching failure text for \"does not exist\" — the first\n  // shape of this guard — would have parked nothing at all while reading, in\n  // the diff, exactly like a fix. `/tmp/pai-scheduler.log` has the wording.\n  //\n  // Asked locally it is not a guess: the registry says where the project is,\n  // and either the directory is there or it is not. Nine hours of fifteen-minute\n  // retries, each costing a terminal window and a 90-second timeout, come down\n  // to one `existsSync`.\n  if (task.owner.rootPath && !existsSync(task.owner.rootPath)) {\n    const reason = `project root missing: ${task.owner.rootPath}`;\n    state.parked[task.id] = { reason, at: now, due: task.due ?? undefined };\n    await alertParked(task, reason);\n    await escalate(\n      task,\n      \"task is not running\",\n      `cannot be dispatched: its project directory ${task.owner.rootPath} does not exist ` +\n        `(renamed or moved?). It is now PARKED and will not be retried. Repoint the ` +\n        `project with \\`pai project\\` and move the task's due date to release it.`,\n      state,\n      now,\n      opts\n    );\n    return { note: `PARKED — ${reason}`, alarm: true, parked: true };\n  }\n\n  // Claim the task BEFORE dispatching, not after.\n  //\n  // This label is now the interlock between two independent dispatchers: this\n  // poller, and AIBroker's Todoist webhook, which fires on item:completed and\n  // skips any task already carrying it (aibroker 0.17.0). Setting it after the\n  // dispatch returned would leave a window — the whole length of a spawn — in\n  // which the webhook sees an unclaimed task and sends the same work order a\n  // second time. It was previously written only for crash visibility, where\n  // \"after\" was harmless; it is load-bearing now, so it has to come first.\n  //\n  // The cost is a label to undo when the dispatch does not land, which is the\n  // cheaper failure: a task wrongly marked running is visible and self-clears\n  // below, whereas a double-dispatched sweep reads Gmail twice and mails twice.\n  // Already present when this is a re-dispatch of a claim that never landed\n  // (handleNotReceived reaches here while the task is still marked running) —\n  // append only if missing, or the label array grows a duplicate every retry.\n  const claimed = task.labels.includes(RUNNING_LABEL) ? task.labels : [...task.labels, RUNNING_LABEL];\n  await opts.provider.setLabels(task.id, claimed);\n\n  const result = await dispatchTask(task, {\n    transport: opts.transport,\n    autoDispatch: opts.autoDispatch,\n    spawnIfAbsent: true,\n  });\n\n  // `queued` counts as delivery. The message is sitting in a live session's\n  // input box; Claude Code simply will not read it until the current turn ends.\n  // Treating it as a failure marked a sweep NOT RUNNING while it was running,\n  // and left the task without its running label so a later tick could send it\n  // again — the same reasoning that makes `busy` positive evidence on the probe\n  // path, where a working session is exactly the one that cannot answer.\n  if (result.outcome === \"delivered\" || result.outcome === \"queued\" || result.outcome === \"spawned\") {\n    state.startedAt[task.id] = now;\n    delete state.failedProbes[task.id];\n    delete state.failedDispatches[task.id];\n    delete state.parked[task.id];\n    // Dropped on success so the day-long quiet window never outlives the fault\n    // it described: a task fixed this morning and broken again tonight alarms\n    // tonight, rather than staying muted until tomorrow.\n    delete state.alarmedAt[task.id];\n    await markRunning(task, opts, now);\n    return { note: `${result.outcome} to ${result.session}${late}`, alarm: false };\n  }\n\n  // Nothing is running, so release the claim — otherwise the task looks in\n  // flight forever and the next tick reports it orphaned rather than retrying.\n  // Not `releaseClaim`: the claim never made it into `state` on this path (it\n  // is undone before `startedAt` would have been set), so there is nothing\n  // there to clear, and `task.labels` is already the pre-claim array.\n  await opts.provider.setLabels(task.id, task.labels);\n  await clearRunningMark(task, opts.provider);\n\n  const fails = (state.failedDispatches[task.id] ?? 0) + 1;\n  state.failedDispatches[task.id] = fails;\n  const detail = `${result.outcome}${result.reason ? \" — \" + result.reason : \"\"}`;\n\n  // Some failures are verdicts, not setbacks. A project whose root directory\n  // does not exist cannot be launched by trying harder, so it parks on the\n  // first occurrence rather than burning three 90-second spawn timeouts and\n  // then retrying anyway.\n  const permanent = isPermanentDispatchFailure(result);\n\n  if (permanent || fails >= DISPATCH_FAILURES_BEFORE_ALARM) {\n    // `unreachable` against a session that has exited to a shell will never\n    // resolve on its own: aibroker correctly refuses to type into it, and\n    // nothing here can close the tab. Retrying quietly forever is the failure —\n    // so this now stops retrying as well as reporting.\n    state.parked[task.id] = {\n      reason: detail,\n      at: now,\n      due: task.due ?? undefined,\n    };\n    await alertParked(task, detail);\n    await escalate(\n      task,\n      \"task is not running\",\n      `failed to dispatch ${permanent ? \"\" : `${fails} times `}(${detail}). ` +\n        `It is scheduled and nothing is executing it. It is now PARKED and will not ` +\n        `be retried until its due date moves — reschedule it once the cause is fixed.`,\n      state,\n      now,\n      opts\n    );\n    return {\n      note: `PARKED — ${permanent ? \"permanent failure\" : `${fails} failed dispatches`}: ${detail}`,\n      alarm: true,\n    };\n  }\n  return { note: `not dispatched (${fails}/${DISPATCH_FAILURES_BEFORE_ALARM}): ${detail}`, alarm: false };\n}\n\n/**\n * First line of the progress comment, and the only way it is ever found again.\n *\n * Deliberately matched by content rather than by a stored comment id. An id in\n * the state file is one more thing that goes stale — lose the file and the\n * comment is orphaned with nothing able to remove it. A sentinel is\n * self-healing: any later release finds and clears it, whatever happened in\n * between, and clearing is idempotent so doing it twice costs nothing.\n */\nconst RUNNING_COMMENT_MARK = \"**RUNNING**\";\n\n/**\n * The cross-repo \"this was written by a machine\" prefix.\n *\n * AIBroker mirrors Todoist comments to the session that owns a task — which is\n * wanted, and is how a human follow-up reaches the running work. Its echo guard\n * (`AGENT_MARK` in `daemon/todoist-webhook.ts`) is what stops that mirror from\n * feeding an agent its own writing, and it keys on this character.\n *\n * The progress marker below did not carry it. So: the poller dispatched the\n * sweep, posted \"RUNNING\" to Todoist, Todoist fired a `note:added` webhook, and\n * AIBroker mirrored the poller's own status line back as a work order — a\n * SECOND session for the same task, 19 seconds after the first. Observed\n * 2026-08-04 07:42:19 → 07:42:38 in `~/.aibroker/audit.jsonl`.\n *\n * Duplicated rather than imported because PAI must not depend on AIBroker. The\n * value is a protocol constant between them; if it ever changes, it changes in\n * both, and the mirror's own tests pin it on that side.\n */\nconst AGENT_MARK = \"🤖\";\n\n/**\n * Post the human-facing progress marker.\n *\n * This is NOT an interlock and nothing may ever decide from it. `pai-running`\n * is the machine-readable claim and both dispatchers read that alone. Two\n * mechanisms tracking one piece of state and disagreeing is the shape of every\n * defect found on 2026-08-01; this stays purely informational so it cannot\n * become the sixth.\n *\n * Failure is swallowed on purpose. A comment that could not be posted must not\n * cost the run — the sweep matters, the annotation does not.\n */\nasync function markRunning(task: Task, opts: TickOptions, startedAt: number): Promise<void> {\n  if (!opts.provider.comment) return;\n  const when = new Date(startedAt).toISOString().replace(\"T\", \" \").slice(0, 16);\n  try {\n    // Kept to one line on purpose. The first version explained the interlock\n    // design here and read as noise to the person it was written for — a\n    // progress marker's job is to say it is running, not to document why.\n    await opts.provider.comment(\n      task.id,\n      `${AGENT_MARK} ${RUNNING_COMMENT_MARK} — started ${when} UTC, ${task.owner.project}. ` +\n        `Disappears when it finishes.`\n    );\n  } catch {\n    // Deliberately silent: see above.\n  }\n}\n\n/**\n * Remove any progress marker left on a task.\n *\n * Called from every path that releases the claim, including the ones that\n * release it because something went wrong. A marker outliving its run is\n * exactly the stale-state problem this whole subsystem keeps producing, so the\n * cleanup is attached to the release rather than to the happy path.\n */\nasync function clearRunningMark(task: Task, provider: TodoistProvider): Promise<void> {\n  const { listComments, deleteComment } = provider;\n  if (!listComments || !deleteComment) return;\n  try {\n    const comments = await listComments.call(provider, task.id);\n    for (const c of comments) {\n      // `includes`, not `startsWith`: the marker gained an AGENT_MARK prefix,\n      // and markers posted before that change must still be found and cleared.\n      // The sentinel is meant to be self-healing — a matcher that only knows\n      // the current spelling orphans every comment written by the last one.\n      if (c.content.includes(RUNNING_COMMENT_MARK)) {\n        await deleteComment.call(provider, c.id);\n      }\n    }\n  } catch {\n    // Same reasoning as posting it: never let the annotation break the run.\n  }\n}\n\n/**\n * Release a claim on a task: strip the interlock label, remove the progress\n * marker, and forget the transient run state that made this poller believe\n * something was still working on it.\n *\n * This is every effect of ending a run except the tracker-side completion\n * itself — factored out so `handleComplete` and the \"abandoned\" release below\n * cannot drift apart, and so anything else that ends a claim (`pai task done`\n * included) gets the identical cleanup rather than a hand-rolled subset of it.\n * A partial version of this — closing the task but skipping this call — is\n * exactly what left `pai-running` and the transient state behind on every\n * `pai task done` before this existed: the next occurrence of a recurring task\n * inherited a live-looking claim it never made, and the poller spent the\n * morning asking a session whether it was still working on a task it had\n * already finished.\n *\n * Only ever removes `RUNNING_LABEL` from the label array — never replaces it —\n * so any other label the user put on the task survives. Only ever deletes this\n * task's own entries from `startedAt` / `claimSeenAt` / `failedProbes` —\n * `lastSeenDue` and `history` are untouched, exactly as `handleComplete` always\n * left them.\n */\nexport async function releaseClaim(\n  task: Task,\n  provider: TodoistProvider,\n  state: Pick<\n    PersistedState,\n    \"startedAt\" | \"claimSeenAt\" | \"failedProbes\" | \"notReceivedAttempts\" | \"notReceivedRetryAt\"\n  >\n): Promise<void> {\n  await provider.setLabels(\n    task.id,\n    task.labels.filter((l) => l.toLowerCase() !== RUNNING_LABEL)\n  );\n  await clearRunningMark(task, provider);\n  delete state.startedAt[task.id];\n  delete state.failedProbes[task.id];\n  delete state.claimSeenAt[task.id];\n  delete state.notReceivedAttempts[task.id];\n  delete state.notReceivedRetryAt[task.id];\n}\n\n/**\n * Put a hand-ticked task's schedule back.\n *\n * Deliberately after the dispatch and never in its place: the run is the point,\n * and a tracker that refuses the write must not cost the user the sweep they\n * asked for. A failure here is reported and nothing else — the task simply\n * keeps the advanced date, which is where it would have stayed anyway.\n */\nasync function restoreSchedule(\n  task: Task,\n  restoreTo: string | null,\n  opts: TickOptions,\n  state: PersistedState\n): Promise<string> {\n  if (!restoreTo) return \", schedule left at the next occurrence\";\n  if (!opts.provider.setDue) return \", schedule unchanged (provider cannot rewrite due dates)\";\n\n  try {\n    await opts.provider.setDue(task.id, restoreTo);\n    // Record what we wrote, not what we read — the next tick must compare\n    // against the restored date or it would read the restore as a fresh jump.\n    const restoredDate = /starting (\\d{4}-\\d{2}-\\d{2})/.exec(restoreTo)?.[1];\n    if (restoredDate) state.lastSeenDue[task.id] = restoredDate;\n    return `, schedule restored (${restoreTo})`;\n  } catch (e) {\n    return `, could NOT restore the schedule: ${e instanceof Error ? e.message : String(e)}`;\n  }\n}\n\nasync function handleComplete(\n  task: Task,\n  durationMinutes: number | null,\n  opts: TickOptions,\n  state: PersistedState,\n  now: number\n): Promise<string> {\n  if (opts.dryRun) return `would clear ${RUNNING_LABEL}, ${durationMinutes ?? \"?\"}m`;\n\n  const wasStuck = (state.failedProbes[task.id] ?? 0) > 0;\n  await releaseClaim(task, opts.provider, state);\n\n  // A run that needed probing may have been stalled for most of its wall time.\n  // Feeding that into the average would inflate every later threshold.\n  if (durationMinutes !== null && !wasStuck) {\n    const hist = state.history[task.id] ?? [];\n    hist.push(durationMinutes);\n    state.history[task.id] = hist.slice(-HISTORY_LIMIT);\n  }\n\n  const base =\n    durationMinutes === null\n      ? \"completed (duration unknown)\"\n      : `completed in ${durationMinutes}m${wasStuck ? \" — not recorded, run was probed\" : \"\"}`;\n\n  return base + (await keepTriggeredSchedule(task, opts, state, now));\n}\n\n/**\n * Give a hand-triggered run its schedule back, a second time.\n *\n * `pai task done` is the correct way to end a recurring run — the roll-forward\n * is how completion is detected at all — but it advances the date on top of the\n * advance the hand-tick already caused. Without this, using the checkbox as a\n * Run Now button quietly cancelled the next scheduled occurrence, which is the\n * exact behaviour the restore was added to prevent.\n *\n * Skipped when the slot has already passed: re-applying it would produce a task\n * that is overdue the moment it is written, and the next tick would dispatch it.\n */\nasync function keepTriggeredSchedule(\n  task: Task,\n  opts: TickOptions,\n  state: PersistedState,\n  now: number\n): Promise<string> {\n  const keep = state.triggeredRestore[task.id];\n  if (!keep) return \"\";\n  delete state.triggeredRestore[task.id];\n\n  const date = /starting (\\d{4}-\\d{2}-\\d{2})/.exec(keep)?.[1];\n  if (!date || !opts.provider.setDue) return \"\";\n  if (triggeredSlotMs(keep, date) <= now) {\n    return \", triggered-run slot already past — schedule left as it is\";\n  }\n\n  try {\n    await opts.provider.setDue(task.id, keep);\n    state.lastSeenDue[task.id] = date;\n    return `, schedule kept at ${date} (run was hand-triggered)`;\n  } catch (e) {\n    return `, could NOT keep the schedule: ${e instanceof Error ? e.message : String(e)}`;\n  }\n}\n\n/** Re-dispatch attempts allowed for one claim before NOT-RECEIVED gives up. */\nexport const NOT_RECEIVED_MAX_ATTEMPTS = 3;\n\n/** Minutes of backoff per attempt: try 1 waits 2m, try 2 waits 4m, try 3 waits 6m. */\nexport const NOT_RECEIVED_BACKOFF_BASE_MIN = 2;\n\n/**\n * Does a probe reply say \"alive, but the dispatch never reached me\"?\n *\n * The session answers the liveness probe with NOT-RECEIVED (or the \"alive, not\n * received\" spelling) when it is demonstrably live yet has no knowledge of the\n * work order — the dispatch was reported delivered and was not. That is not\n * silence (the session is fine) and not death (nothing needs recovering): it is\n * an UNDELIVERED dispatch, and the honest response is to send it again.\n *\n * Anchored to the first line so a reply merely discussing the phrase —\n * \"we discussed not received earlier\" — does not trip it.\n */\nexport function isNotReceivedReply(reply: string | undefined | null): boolean {\n  if (!reply) return false;\n  const first = reply.split(\"\\n\", 1)[0]!.trim().toLowerCase();\n  return first.startsWith(\"not-received\") || /^alive[,:]\\s*not[ -]received/.test(first);\n}\n\nasync function handleProbe(\n  task: Task,\n  elapsed: number | null,\n  opts: TickOptions,\n  state: PersistedState,\n  now: number\n): Promise<{ note: string; stuck: boolean; dispatched?: boolean }> {\n  const project = task.owner.project;\n  const el = elapsed === null ? \"unknown\" : `${elapsed}m`;\n\n  if (opts.dryRun) return { note: `would probe ${project ?? \"?\"} (${el} elapsed)`, stuck: false };\n\n  if (!opts.prober || !project) {\n    // No probe available. Do NOT guess — an unanswered question is not the same\n    // as a dead session, and re-dispatching a live sweep would double-run it.\n    return { note: `overrun (${el}) — no liveness probe available, leaving alone`, stuck: false };\n  }\n\n  const answer = await opts.prober.ask(\n    project,\n    `Are you still working on the task \"${task.title}\"? Reply in one short line.`\n  );\n\n  // Both of these are evidence of life. `busy` especially: it means AIBroker saw\n  // the session actively producing output and deliberately sent nothing, so it\n  // costs no tokens and must not count as a strike.\n  if (answer.state === \"replied\" || answer.state === \"busy\") {\n    state.failedProbes[task.id] = 0;\n\n    // Alive but the dispatch never landed. Only `replied` can carry this — `busy`\n    // means AIBroker sent nothing — and it must come before the overrun logic:\n    // an elapsed-time verdict about a run that never started is noise.\n    if (isNotReceivedReply(answer.reply)) {\n      const attempts = state.notReceivedAttempts[task.id] ?? 0;\n      const retryAt = state.notReceivedRetryAt[task.id] ?? 0;\n      if (now < retryAt) {\n        return {\n          note: `alive but NOT-RECEIVED — re-dispatch backed off ${Math.round((retryAt - now) / 60_000)}m (try ${attempts})`,\n          stuck: false,\n        };\n      }\n      if (attempts >= NOT_RECEIVED_MAX_ATTEMPTS) {\n        // A late-arriving original must not be double-run by an endless retry\n        // loop: after three re-dispatches the claim is a human's problem.\n        return {\n          note: `alive but NOT-RECEIVED x${attempts} — dispatch never landed, claim left for a human`,\n          stuck: true,\n        };\n      }\n      const next = attempts + 1;\n      state.notReceivedAttempts[task.id] = next;\n      state.notReceivedRetryAt[task.id] = now + next * NOT_RECEIVED_BACKOFF_BASE_MIN * 60_000;\n      const r = await handleDispatch(task, 0, opts, state, now);\n      return {\n        note: `alive but NOT-RECEIVED — re-dispatched (try ${next}): ${r.note}`,\n        stuck: r.alarm === true,\n        dispatched: true,\n      };\n    }\n\n    // A confirmed-alive reply that does NOT say not-received is evidence the\n    // dispatch DID land: the backoff clock resets (see notReceivedRetryAt docs).\n    if (answer.state === \"replied\") {\n      delete state.notReceivedAttempts[task.id];\n      delete state.notReceivedRetryAt[task.id];\n    }\n\n    const expected = expectedMinutes(state.history[task.id] ?? []);\n    if (elapsed !== null && elapsed > expected * GROSS_OVERRUN_FACTOR) {\n      // Alive but far past any plausible runtime. Do not recover it — killing a\n      // working session is worse — but do not stay quiet either.\n      return {\n        note: `alive but ${el} against an expected ${expected}m — ${GROSS_OVERRUN_FACTOR}x over, worth a look`,\n        stuck: true,\n      };\n    }\n\n    return answer.state === \"busy\"\n      ? { note: `busy after ${el} (mid-turn, nothing sent)`, stuck: false }\n      : { note: `alive after ${el}: ${answer.reply ?? \"(no detail)\"}`, stuck: false };\n  }\n\n  const fails = (state.failedProbes[task.id] ?? 0) + 1;\n  state.failedProbes[task.id] = fails;\n\n  // `absent` is stronger evidence than `silent`, but not acted on immediately:\n  // an empty session list is exactly how the hub failed on 2026-08-01, and\n  // re-dispatching on a lying hub would spawn a duplicate tab per task.\n  const what = answer.state === \"absent\" ? \"session gone\" : \"no reply\";\n\n  if (fails >= STUCK_AFTER_FAILED_PROBES) {\n    return {\n      note: `STUCK after ${el} — ${what} x${fails} (${answer.reason ?? \"no detail\"})`,\n      stuck: true,\n    };\n  }\n  return { note: `${what} ${fails}/${STUCK_AFTER_FAILED_PROBES} after ${el}`, stuck: false };\n}\n\nexport { isRunning };\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 { RegistryBackend } from \"../../storage/registry-interface.js\";\nimport type { SyncStats } from \"./types.js\";\nimport { worktreeReason } from \"../../registry/registrable.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 async function syncVault(vaultPath: string, registry: RegistryBackend): Promise<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  // ORDER BY status ASC, slug ASC — RegistryBackend has no combined orderBy\n  // for this pair, so sort client-side (plain string compare matches SQLite's\n  // default ASCII collation for these columns).\n  const projects = (await registry.listProjects()).sort((a, b) =>\n    a.status !== b.status ? a.status.localeCompare(b.status) : a.slug.localeCompare(b.slug)\n  );\n\n  const takenSlugs = new Set<string>();\n\n  for (const project of projects) {\n    if (project.status === \"active\") {\n      if (worktreeReason(project.root_path)) continue;\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        await registry.updateProjectObsidianLink(project.id, slugPath, Date.now());\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 { RegistryBackend } from \"../../storage/registry-interface.js\";\nimport { fmtDate } from \"../../cli/lib/utils.js\";\nimport { findNotesDir, findClaudeNotesDir } from \"./symlinks.js\";\nimport { paiHomePath, resolvePaiFile, migratePaiDir, type MigrateDirResult } from \"../../config/pai-home.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 async function generateIndex(vaultPath: string, registry: RegistryBackend): Promise<void> {\n  mkdirSync(vaultPath, { recursive: true });\n\n  const rows = await registry.listProjectsWithSessionStats();\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 = (await registry.listTagsForProject(row.id)).map((t) => `\\`${t}\\``).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 = (await registry.listTagsForProject(row.id)).map((t) => `\\`${t}\\``).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 async function generateTopicPages(vaultPath: string, registry: RegistryBackend): Promise<number> {\n  const topicsDir = join(vaultPath, \"_topics\");\n  mkdirSync(topicsDir, { recursive: true });\n\n  const allTags = await registry.listAllTags();\n\n  let written = 0;\n  for (const tag of allTags) {\n    const projects = await registry.listProjectsWithSessionStats({ tagId: tag.id });\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/** Old default vault path, inside ~/.pai/ (pre-2026-09-19). */\nexport function oldDefaultVaultPath(): string {\n  return join(homedir(), \".pai\", \"obsidian-vault\");\n}\n\n/**\n * Default vault path: PAI_HOME/obsidian-vault if present, else the old\n * ~/.pai/obsidian-vault (one-time stderr notice), else the new path.\n */\nexport function defaultVaultPath(): string {\n  return resolvePaiFile(paiHomePath(\"obsidian-vault\"), [oldDefaultVaultPath()], \"pai config migrate --obsidian-vault\");\n}\n\nexport function migrateObsidianVaultDir(opts: { dryRun?: boolean } = {}): MigrateDirResult {\n  return migratePaiDir(paiHomePath(\"obsidian-vault\"), [oldDefaultVaultPath()], opts);\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 { RegistryBackend } from \"../../storage/registry-interface.js\";\nimport { collectSessionFiles, walkNotesDir } from \"./walk.js\";\nimport { worktreeReason } from \"../../registry/registrable.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 * Return true if the note has a meaningful body — i.e. content beyond blank\n * lines, horizontal rules, and an existing master-note footer.\n *\n * Guards against stamping a \"← Master\" backlink onto an empty note, which\n * produces a misleading dangling stub (empty body + footer only). Whatever\n * emptied the note, we must not decorate it as if it were a real session.\n */\nfunction hasMeaningfulBody(content: string): boolean {\n  const body = content\n    .replace(/\\n---\\n\\[\\[_[^\\]]*-master\\|[^\\]]*\\]\\]\\n?$/, \"\") // drop existing master footer\n    .split(\"\\n\")\n    .filter((line) => {\n      const t = line.trim();\n      return t !== \"\" && t !== \"---\";\n    })\n    .join(\"\");\n  return body.length > 0;\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 AND the note has\n * a meaningful body (never stamp a footer onto an empty note).\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  // Never stamp a footer onto an empty note — that is exactly the dangling-stub\n  // signature (empty body + master backlink) we are trying to prevent.\n  if (!hasMeaningfulBody(content)) 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 registry   Registry backend\n * @param threshold  Minimum session count to generate a master note (default: 5)\n * @returns          Number of master notes written\n */\nexport async function generateMasterNotes(\n  vaultPath: string,\n  registry: RegistryBackend,\n  threshold = 5\n): Promise<number> {\n  if (!existsSync(vaultPath)) return 0;\n\n  const projects = await registry.listProjects({ status: \"active\", orderBy: \"slug\" });\n\n  let written = 0;\n\n  for (const project of projects) {\n    // A row rooted in a worktree must not stamp its slug (the worker id) into\n    // notes it shares with the main repo; the merge would publish the footer.\n    if (worktreeReason(project.root_path)) continue;\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 registry  Registry backend\n * @returns   Object with counts: { filesScanned, filesModified, errors }\n */\nexport async function fixSessionTags(\n  registry: RegistryBackend\n): Promise<{ filesScanned: number; filesModified: number; errors: string[] }> {\n  const results = { filesScanned: 0, filesModified: 0, errors: [] as string[] };\n\n  const projects = await registry.listProjects({ status: \"active\", orderBy: \"slug\" });\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 { existsSync, readdirSync, lstatSync, readlinkSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { Project, RegistryBackend } from \"../storage/registry-interface.js\";\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 async function checkHealth(vaultPath: string, registry: RegistryBackend): Promise<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 = await registry.listProjects({ status: \"active\" });\n\n  report.totalProjects = projects.length;\n\n  // Build a map of slug → project for quick lookup\n  const projectsBySlug = new Map<string, Project>();\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 { execFileSync } 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  readMainConfigRaw,\n  writeMainConfigRaw,\n} from \"../../daemon/config.js\";\nimport { getRegistryBackend } from \"../../storage/factory.js\";\nimport type { RegistryBackend } from \"../../storage/registry-interface.js\";\nimport { existsSync } from \"node:fs\";\nimport chalk from \"chalk\";\n\n// ---------------------------------------------------------------------------\n// Config helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Read obsidianVaultPath from the main config (config.yaml if it exists,\n * else config.json). Falls back to defaultVaultPath() if not set.\n */\nfunction getVaultPath(override?: string): string {\n  if (override) return expandHome(override);\n\n  try {\n    const cfg = readMainConfigRaw();\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 * Read obsidianVaultPath from the main config without falling back to\n * defaultVaultPath() — undefined means \"not set\", distinct from getVaultPath's\n * \"give me something usable\". Used by `pai config migrate` to decide whether\n * the stored path still points at the pre-2026-09-19 default.\n */\nexport function getConfigObsidianVaultPathRaw(): string | undefined {\n  try {\n    const cfg = readMainConfigRaw();\n    if (typeof cfg.obsidianVaultPath === \"string\" && cfg.obsidianVaultPath) {\n      return expandHome(cfg.obsidianVaultPath);\n    }\n  } catch {\n    // Config missing or unreadable — nothing set.\n  }\n  return undefined;\n}\n\n/** Persist obsidianVaultPath into the main config so future syncs use the\n *  same path (config.yaml if it exists, else config.json). */\nexport function saveVaultPath(vaultPath: string): void {\n  let cfg: Record<string, unknown> = {};\n  try {\n    cfg = readMainConfigRaw();\n  } catch {\n    // Start fresh if file is missing/corrupt\n  }\n  cfg.obsidianVaultPath = vaultPath;\n  writeMainConfigRaw(cfg);\n}\n\n// ---------------------------------------------------------------------------\n// Command implementations\n// ---------------------------------------------------------------------------\n\nexport async function obsidianSyncCmd(\n  opts: { vault?: string; quiet?: boolean }\n): Promise<number> {\n  const registry = await getRegistryBackend();\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 = await syncVault(vaultPath, registry);\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  await generateIndex(vaultPath, registry);\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 = await generateTopicPages(vaultPath, registry);\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 = await fixSessionTags(registry);\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 = await generateMasterNotes(vaultPath, registry);\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  return 0;\n}\n\nexport async function obsidianStatusCmd(\n  opts: { vault?: string }\n): Promise<number> {\n  const registry = await getRegistryBackend();\n  const vaultPath = getVaultPath(opts.vault);\n  const report = await checkHealth(vaultPath, registry);\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 0;\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  return 0;\n}\n\nexport function obsidianOpenCmd(\n  opts: { vault?: string }\n): number {\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.exitCode = 1;\n    return 1;\n  }\n\n  const url = `obsidian://open?vault=${encodeURIComponent(vaultName)}`;\n  console.log(dim(`  Opening: ${url}`));\n  try {\n    execFileSync(\"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.exitCode = 1;\n    return 1;\n  }\n\n  return 0;\n}\n\n","/**\n * adapter-content.ts — PAI_HOME/agents and PAI_HOME/commands: the operator's\n * authored Agent/Command .md content, made PAI-owned with an adapter symlink\n * back at ~/.claude/agents and ~/.claude/commands so Claude Code still loads\n * them from its fixed harness paths (confirmed empirically: Claude Code\n * follows a directory symlink for .claude/agents/ — see docs/workers-config.md,\n * \"Where PAI lives\"). Unlike Hooks/skills (individual files symlinked by the\n * build's --sync step), these were real, un-owned files with no PAI copy to\n * symlink from — this module is what gives them one.\n */\n\nimport { existsSync, lstatSync, symlinkSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { paiHomePath, migratePaiDir, type MigrateDirResult } from \"./pai-home.js\";\n\nexport interface MigrateContentDirResult extends MigrateDirResult {\n  symlinked: boolean;\n  symlinkNote?: string;\n}\n\nfunction oldAgentsDir(): string {\n  return join(homedir(), \".claude\", \"agents\");\n}\n\nfunction oldCommandsDir(): string {\n  return join(homedir(), \".claude\", \"commands\");\n}\n\nfunction migrateContentDirWithSymlink(\n  newDir: string,\n  oldDir: string,\n  opts: { dryRun?: boolean } = {}\n): MigrateContentDirResult {\n  // Already a symlink (presumably to newDir, from an earlier run) — nothing to do.\n  if (existsSync(oldDir) && lstatSync(oldDir).isSymbolicLink()) {\n    return {\n      fromDir: null,\n      toDir: newDir,\n      dryRun: !!opts.dryRun,\n      note: `already symlinked (${oldDir} → ${newDir})`,\n      symlinked: true,\n    };\n  }\n\n  const result = migratePaiDir(newDir, [oldDir], opts);\n  if (opts.dryRun || result.fromDir === null) {\n    return { ...result, symlinked: false };\n  }\n\n  if (existsSync(oldDir)) {\n    // migratePaiDir leaves the old dir in place (not renamed aside) when a\n    // same-named entry already existed at newDir — never symlink over\n    // whatever real content is left behind in that case.\n    return {\n      ...result,\n      symlinked: false,\n      symlinkNote: `${oldDir} still has entries after migration (${result.note ?? \"see above\"}) — not symlinked`,\n    };\n  }\n\n  try {\n    symlinkSync(newDir, oldDir);\n    return { ...result, symlinked: true };\n  } catch (e) {\n    return { ...result, symlinked: false, symlinkNote: e instanceof Error ? e.message : String(e) };\n  }\n}\n\n/** Move ~/.claude/agents/*.md into PAI_HOME/agents/, then symlink it back. */\nexport function migrateAgentsDir(opts: { dryRun?: boolean } = {}): MigrateContentDirResult {\n  return migrateContentDirWithSymlink(paiHomePath(\"agents\"), oldAgentsDir(), opts);\n}\n\n/** Move ~/.claude/commands/*.md into PAI_HOME/commands/, then symlink it back. */\nexport function migrateCommandsDir(opts: { dryRun?: boolean } = {}): MigrateContentDirResult {\n  return migrateContentDirWithSymlink(paiHomePath(\"commands\"), oldCommandsDir(), opts);\n}\n","/**\n * pai-files.ts — path resolution for the PAI per-user files that don't have\n * a dedicated module of their own: whisper-rules.md, advisor-mode.json, and\n * the stop-hook's session-state/ dir (kept here rather than in the hook\n * itself, since importing that hook module runs it — it calls main() at\n * import time).\n * config.json lives in daemon/config.ts, workers.yaml in\n * workers/workers-config.ts — this covers the rest of PAI_HOME.\n */\n\nimport { existsSync, statSync, renameSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport {\n  paiHomePath,\n  resolvePaiFile,\n  migratePaiFile,\n  migratePaiDir,\n  type MigrateFileResult,\n  type MigrateDirResult,\n} from \"./pai-home.js\";\n\nexport { paiHomePath };\n\nfunction oldWhisperRulesPath(): string {\n  return join(homedir(), \".claude\", \"whisper-rules.md\");\n}\n\nfunction oldAdvisorModePath(): string {\n  return join(homedir(), \".claude\", \"advisor-mode.json\");\n}\n\nfunction oldSessionStateDir(): string {\n  return join(homedir(), \".config\", \"pai\", \"session-state\");\n}\n\nexport function whisperRulesPath(): string {\n  return resolvePaiFile(paiHomePath(\"whisper-rules.md\"), [oldWhisperRulesPath()], \"pai config migrate\");\n}\n\nexport function advisorModePath(): string {\n  return resolvePaiFile(paiHomePath(\"advisor-mode.json\"), [oldAdvisorModePath()], \"pai config migrate\");\n}\n\nexport function migrateWhisperRules(opts: { dryRun?: boolean } = {}): MigrateFileResult {\n  return migratePaiFile(paiHomePath(\"whisper-rules.md\"), [oldWhisperRulesPath()], opts);\n}\n\nexport function migrateAdvisorMode(opts: { dryRun?: boolean } = {}): MigrateFileResult {\n  return migratePaiFile(paiHomePath(\"advisor-mode.json\"), [oldAdvisorModePath()], opts);\n}\n\n/** Read location: PAI_HOME's session-state/ if present, else the\n *  pre-2026-09-19 ~/.config/pai/session-state (one-time stderr notice). */\nexport function sessionStateDir(): string {\n  return resolvePaiFile(paiHomePath(\"session-state\"), [oldSessionStateDir()], \"pai config migrate\");\n}\n\nexport function migrateSessionStateDir(opts: { dryRun?: boolean } = {}): MigrateDirResult {\n  return migratePaiDir(paiHomePath(\"session-state\"), [oldSessionStateDir()], opts);\n}\n\n// ---------------------------------------------------------------------------\n// Orphans: files under the old ~/.config/pai that no code currently reads\n// (voices.json — no `voice` reference anywhere in src). Still moved by\n// `pai config migrate` so ~/.config/pai ends up with nothing but\n// *.migrated-* markers. The orphaned legacy federation database is handled\n// by storage/paths.ts's migrateOrphanFederationDb — this file stays free of\n// database path literals so it is not a database opener.\n// ---------------------------------------------------------------------------\n\nfunction oldLastHousekeepingPath(): string {\n  return join(homedir(), \".config\", \"pai\", \".last-housekeeping\");\n}\n\n/** session-stop.sh resolves this itself at runtime (it can't import TS) —\n *  this is only what `pai config migrate` uses to relocate a leftover. */\nexport function migrateLastHousekeeping(opts: { dryRun?: boolean } = {}): MigrateFileResult {\n  return migratePaiFile(paiHomePath(\".last-housekeeping\"), [oldLastHousekeepingPath()], opts);\n}\n\nfunction oldVoicesJsonPath(): string {\n  return join(homedir(), \".config\", \"pai\", \"voices.json\");\n}\n\nexport function migrateVoicesJson(opts: { dryRun?: boolean } = {}): MigrateFileResult {\n  return migratePaiFile(paiHomePath(\"voices.json\"), [oldVoicesJsonPath()], opts);\n}\n\n// ---------------------------------------------------------------------------\n// .session-stop.lock — a live mkdir-based mutex, not data. session-stop.sh\n// already creates new locks at PAI_HOME going forward (see src/hooks/\n// session-stop.sh); this only handles what's left at the old location.\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// History/, agent-sessions.json, session-routing.json, security-events.jsonl\n// — hook-written PAI state, read/written at runtime via the resolvers in\n// src/hooks/ts/lib/pai-paths.ts (ADAPTER_DIR fallback + notice, same as\n// everything here). Duplicated here rather than imported: pai-paths.ts\n// validates ADAPTER_DIR/HOOKS_DIR exist at import time (process.exit(1) if\n// not) since every hook that imports it genuinely needs that guarantee — the\n// CLI does not, and must not inherit a hook-only failure mode just to\n// migrate a file. Actively written by hooks in every live session, so unlike\n// most of this file the live move is NOT automatic — see `pai config migrate\n// --history` and its RUNNING-worker guard in src/cli/commands/config.ts.\n// ---------------------------------------------------------------------------\n\nfunction oldHistoryDir(): string {\n  return join(homedir(), \".claude\", \"History\");\n}\n\nexport function migrateHistoryDir(opts: { dryRun?: boolean } = {}): MigrateDirResult {\n  return migratePaiDir(paiHomePath(\"History\"), [oldHistoryDir()], opts);\n}\n\nfunction oldAgentSessionsPath(): string {\n  return join(homedir(), \".claude\", \"agent-sessions.json\");\n}\n\nexport function migrateAgentSessions(opts: { dryRun?: boolean } = {}): MigrateFileResult {\n  return migratePaiFile(paiHomePath(\"agent-sessions.json\"), [oldAgentSessionsPath()], opts);\n}\n\nfunction oldSessionRoutingPath(): string {\n  return join(homedir(), \".claude\", \"session-routing.json\");\n}\n\nexport function migrateSessionRouting(opts: { dryRun?: boolean } = {}): MigrateFileResult {\n  return migratePaiFile(paiHomePath(\"session-routing.json\"), [oldSessionRoutingPath()], opts);\n}\n\nfunction oldSecurityEventsPath(): string {\n  return join(homedir(), \".claude\", \"history\", \"security\", \"security-events.jsonl\");\n}\n\nexport function migrateSecurityEvents(opts: { dryRun?: boolean } = {}): MigrateFileResult {\n  return migratePaiFile(paiHomePath(\"History\", \"security\", \"security-events.jsonl\"), [oldSecurityEventsPath()], opts);\n}\n\nexport interface MigrateLockResult {\n  status: \"nothing-to-migrate\" | \"skipped-in-use\" | \"moved\";\n  note: string;\n}\n\n/** A lock this fresh means a session-stop.sh tail is genuinely running under\n *  it right now — moving it out from under that process would break the\n *  mutex it exists to provide. Matches the hook's own 600s abandonment\n *  window with headroom, since this runs far less often than every Stop. */\nconst SESSION_STOP_LOCK_ABANDONED_AFTER_MS = 60 * 60 * 1000;\n\nexport function migrateSessionStopLock(opts: { dryRun?: boolean } = {}): MigrateLockResult {\n  const oldPath = join(homedir(), \".config\", \"pai\", \".session-stop.lock\");\n  const newPath = paiHomePath(\".session-stop.lock\");\n  if (!existsSync(oldPath)) {\n    return { status: \"nothing-to-migrate\", note: `nothing to migrate (${newPath})` };\n  }\n\n  const ageMs = Date.now() - statSync(oldPath).mtimeMs;\n  if (ageMs <= SESSION_STOP_LOCK_ABANDONED_AFTER_MS) {\n    return {\n      status: \"skipped-in-use\",\n      note: `in use (${Math.round(ageMs / 1000)}s old) — left in place; new locks go to ${newPath}`,\n    };\n  }\n\n  const ageMin = Math.round(ageMs / 60000);\n  if (opts.dryRun) {\n    return { status: \"moved\", note: `would rename abandoned lock (${ageMin}m old) at ${oldPath}` };\n  }\n\n  const stamp = new Date().toISOString().slice(0, 10).replace(/-/g, \"\");\n  renameSync(oldPath, `${oldPath}.migrated-${stamp}`);\n  return { status: \"moved\", note: `abandoned lock (${ageMin}m old) renamed aside` };\n}\n","/**\n * kg-backfill.ts — Populate the temporal knowledge graph from existing\n * session notes.\n *\n * Walks `Notes/YYYY/MM/*.md` for each registered project, runs the same\n * extractor that the session-summary-worker uses on every NEW summary, and\n * stores triples in Postgres. Idempotent: a state file at\n * ~/.config/pai/kg-backfill-state.json records which note paths have been\n * processed so re-runs skip them.\n *\n * Even without the state file the operation is safe — extractAndStoreTriples\n * uses supersession logic so re-extracting a note never produces duplicates.\n */\n\nimport { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nimport { loadConfig, CONFIG_FILE } from \"../daemon/config.js\";\nimport { createStorageBackend, getRegistryBackend } from \"../storage/factory.js\";\nimport { extractAndStoreTriples } from \"./kg-extraction.js\";\nimport { paiHomePath, resolvePaiFile, migratePaiFile, type MigrateFileResult } from \"../config/pai-home.js\";\n\n// ---------------------------------------------------------------------------\n// State file\n// ---------------------------------------------------------------------------\n\nfunction oldStateFile(): string {\n  return join(homedir(), \".config\", \"pai\", \"kg-backfill-state.json\");\n}\n\nfunction statePath(): string {\n  return resolvePaiFile(paiHomePath(\"kg-backfill-state.json\"), [oldStateFile()], \"pai config migrate\");\n}\n\nexport function migrateKgBackfillState(opts: { dryRun?: boolean } = {}): MigrateFileResult {\n  return migratePaiFile(paiHomePath(\"kg-backfill-state.json\"), [oldStateFile()], opts);\n}\n\ninterface BackfillState {\n  /** Map of absolute note path -> ISO timestamp of when it was processed. */\n  processed: Record<string, string>;\n}\n\nfunction loadState(): BackfillState {\n  try {\n    const file = statePath();\n    if (existsSync(file)) {\n      const raw = JSON.parse(readFileSync(file, \"utf-8\"));\n      if (raw && typeof raw === \"object\" && raw.processed) return raw as BackfillState;\n    }\n  } catch { /* ignore */ }\n  return { processed: {} };\n}\n\nfunction saveState(state: BackfillState): void {\n  try {\n    const file = paiHomePath(\"kg-backfill-state.json\");\n    const dir = dirname(file);\n    if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n    writeFileSync(file, JSON.stringify(state, null, 2), \"utf-8\");\n  } catch (e) {\n    process.stderr.write(`[kg-backfill] failed to save state: ${e}\\n`);\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Note discovery\n// ---------------------------------------------------------------------------\n\ninterface ProjectRow {\n  id: number;\n  slug: string;\n  root_path: string;\n}\n\ninterface ProjectNote {\n  project: ProjectRow;\n  notePath: string;\n}\n\n/**\n * Find all session notes under <project_root>/Notes/YYYY/MM/*.md.\n * Falls back to <project_root>/Notes/*.md (flat layout) when no year/month\n * subdirectories are present.\n */\nfunction findProjectNotes(project: ProjectRow): string[] {\n  const notesRoot = join(project.root_path, \"Notes\");\n  if (!existsSync(notesRoot)) return [];\n\n  const found: Array<{ path: string; mtime: number }> = [];\n\n  function walk(dir: string, depth: number): void {\n    if (depth > 4) return;\n    let entries: string[];\n    try {\n      entries = readdirSync(dir);\n    } catch {\n      return;\n    }\n    for (const name of entries) {\n      const full = join(dir, name);\n      let st;\n      try {\n        st = statSync(full);\n      } catch {\n        continue;\n      }\n      if (st.isDirectory()) {\n        walk(full, depth + 1);\n      } else if (st.isFile() && name.endsWith(\".md\") && /^\\d{3,4}/.test(name)) {\n        found.push({ path: full, mtime: st.mtimeMs });\n      }\n    }\n  }\n\n  walk(notesRoot, 0);\n  // Process oldest first so newer extractions can correctly supersede older facts\n  found.sort((a, b) => a.mtime - b.mtime);\n  return found.map((f) => f.path);\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport interface BackfillOptions {\n  /** If set, only process this project's notes. */\n  projectSlug?: string;\n  /** Cap the total number of notes processed across all projects. */\n  limit?: number;\n  /** If true, do not call the extractor and do not write any state. */\n  dryRun?: boolean;\n  /** Progress callback fired before each note is processed. */\n  onProgress?: (current: number, total: number, note: string) => void;\n}\n\nexport interface BackfillResult {\n  notes_processed: number;\n  triples_extracted: number;\n  triples_added: number;\n  triples_superseded: number;\n  errors: number;\n}\n\n/**\n * Backfill the knowledge graph from existing session notes.\n *\n * Requires the Postgres backend (KG tables live there). Throws if Postgres\n * is unavailable, since this is an explicit user action.\n */\nexport async function backfillKgFromNotes(\n  options: BackfillOptions = {}\n): Promise<BackfillResult> {\n  const result: BackfillResult = {\n    notes_processed: 0,\n    triples_extracted: 0,\n    triples_added: 0,\n    triples_superseded: 0,\n    errors: 0,\n  };\n\n  const config = loadConfig();\n  if (config.storageBackend !== \"postgres\") {\n    throw new Error(\n      \"kg backfill requires the Postgres backend. \" +\n      `Set \"storageBackend\": \"postgres\" in ${CONFIG_FILE}.`\n    );\n  }\n\n  const backend = await createStorageBackend(config);\n  if (!backend.supportsPostgresFeatures) {\n    throw new Error(\n      \"Postgres backend unavailable — fell back to SQLite. Cannot backfill KG.\"\n    );\n  }\n\n  // -- Load projects -------------------------------------------------------\n  const registry = await getRegistryBackend();\n  let projects: ProjectRow[];\n  if (options.projectSlug) {\n    const row = await registry.getProjectBySlug(options.projectSlug);\n    if (!row) {\n      throw new Error(`Project not found: ${options.projectSlug}`);\n    }\n    projects = [row];\n  } else {\n    projects = await registry.listProjects({ status: \"active\", orderBy: \"slug\" });\n  }\n\n  // -- Build the work list -------------------------------------------------\n  const state = loadState();\n  const work: ProjectNote[] = [];\n  for (const project of projects) {\n    const notes = findProjectNotes(project);\n    for (const notePath of notes) {\n      if (state.processed[notePath]) continue;\n      work.push({ project, notePath });\n    }\n  }\n\n  const total = options.limit ? Math.min(options.limit, work.length) : work.length;\n\n  // -- Process notes -------------------------------------------------------\n  for (let i = 0; i < total; i++) {\n    const { project, notePath } = work[i];\n    options.onProgress?.(i + 1, total, notePath);\n\n    if (options.dryRun) {\n      result.notes_processed++;\n      continue;\n    }\n\n    let noteContent: string;\n    try {\n      noteContent = readFileSync(notePath, \"utf-8\");\n    } catch (e) {\n      process.stderr.write(`[kg-backfill] read failed ${notePath}: ${e}\\n`);\n      result.errors++;\n      continue;\n    }\n\n    try {\n      const stats = await extractAndStoreTriples(backend, {\n        summaryText: noteContent,\n        projectSlug: project.slug,\n        projectId: project.id,\n        sessionId: `backfill:${notePath}`,\n        gitLog: \"\",\n        model: \"sonnet\",\n      });\n\n      result.notes_processed++;\n      result.triples_extracted += stats.extracted;\n      result.triples_added += stats.added;\n      result.triples_superseded += stats.superseded;\n\n      // Mark as processed (state file is the idempotency hint; the supersession\n      // logic in the extractor is the actual safety net)\n      state.processed[notePath] = new Date().toISOString();\n      // Save incrementally so a crash mid-run doesn't lose progress\n      if ((i + 1) % 5 === 0) saveState(state);\n    } catch (e) {\n      process.stderr.write(`[kg-backfill] extract failed ${notePath}: ${e}\\n`);\n      result.errors++;\n    }\n  }\n\n  if (!options.dryRun) saveState(state);\n  await backend.close();\n\n  return result;\n}\n","/**\n * logs-migrate.ts — moving ~/.claude/logs/workers (status files, event\n * mirrors, pane registry, routing state — everything under one run's logDir)\n * into PAI_HOME. Directory-level counterpart to pai-home.ts's per-file\n * migratePaiFile, but simpler: an atomic renameSync of the whole directory\n * followed by a symlink left at the old path, so any process still holding an\n * fd open into the old directory (or writing to the old path by name) keeps\n * landing in the new one — every held fd follows the inode across a rename,\n * and the symlink covers new opens by path. Only falls back to copy + verify\n * + rename-aside (and only there does it need to refuse while a worker is\n * RUNNING against the old directory) when old and new are on different\n * volumes and the rename itself fails with EXDEV.\n */\n\nimport { existsSync, mkdirSync, cpSync, renameSync, symlinkSync, readdirSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { loadStatuses, isChatPane, isLive } from \"./status.js\";\n\nexport class WorkerLogsMigrationError extends Error {}\n\n/** RUNNING-and-alive statuses in logDir, excluding the terminal's own interactive pane. */\nexport function activeSpawnedWorkerCount(logDir: string): number {\n  return loadStatuses(logDir).filter((s) => isLive(s) && !isChatPane(s)).length;\n}\n\nfunction totalFileCount(dir: string): number {\n  if (!existsSync(dir)) return 0;\n  let count = 0;\n  for (const entry of readdirSync(dir, { withFileTypes: true })) {\n    const full = join(dir, entry.name);\n    count += entry.isDirectory() ? totalFileCount(full) : 1;\n  }\n  return count;\n}\n\nexport interface MigrateLogsResult {\n  fromPath: string | null;\n  toPath: string;\n  dryRun: boolean;\n  filesMoved?: number;\n  note?: string;\n}\n\n/**\n * Move the whole logDir tree into PAI_HOME. Normal case: renameSync the\n * directory (atomic, and every fd already open into it — including run.ts's\n * append-mode events file — follows the inode), then leave a symlink at the\n * old path so writes still addressed there land in the new directory too.\n * Falls back to recursive copy + verified file count + rename-aside (never\n * deleted) only on EXDEV, when old and new are on different volumes — that\n * path refuses while any spawned worker is RUNNING against the old\n * directory (check `pai worker ps` and retry once idle), since a copy can't\n * see writes still landing in the old tree.\n */\nexport function migrateWorkerLogs(\n  oldPath: string,\n  newPath: string,\n  opts: { dryRun?: boolean } = {}\n): MigrateLogsResult {\n  if (!existsSync(oldPath)) {\n    return {\n      fromPath: null,\n      toPath: newPath,\n      dryRun: !!opts.dryRun,\n      note: \"nothing to migrate — old logDir does not exist\",\n    };\n  }\n  if (existsSync(newPath)) {\n    return {\n      fromPath: oldPath,\n      toPath: newPath,\n      dryRun: !!opts.dryRun,\n      note: \"already at new location — resolve manually, nothing changed\",\n    };\n  }\n\n  if (opts.dryRun) return { fromPath: oldPath, toPath: newPath, dryRun: true };\n\n  mkdirSync(dirname(newPath), { recursive: true });\n\n  try {\n    renameSync(oldPath, newPath);\n    symlinkSync(newPath, oldPath);\n    return { fromPath: oldPath, toPath: newPath, dryRun: false, filesMoved: totalFileCount(newPath) };\n  } catch (e) {\n    if ((e as NodeJS.ErrnoException).code !== \"EXDEV\") throw e;\n  }\n\n  const active = activeSpawnedWorkerCount(oldPath);\n  if (active > 0) {\n    throw new WorkerLogsMigrationError(\n      `${active} worker(s) other than the interactive session are RUNNING and writing into ` +\n        `${oldPath} — wait for them to finish (see \\`pai worker ps\\`), then retry`\n    );\n  }\n\n  const stamp = new Date().toISOString().slice(0, 10).replace(/-/g, \"\");\n  cpSync(oldPath, newPath, { recursive: true });\n\n  const fromCount = totalFileCount(oldPath);\n  const toCount = totalFileCount(newPath);\n  if (fromCount !== toCount) {\n    throw new WorkerLogsMigrationError(\n      `${newPath}: copy produced ${toCount} files, expected ${fromCount} from ${oldPath} — ` +\n        `aborting, old directory left in place`\n    );\n  }\n\n  renameSync(oldPath, `${oldPath}.migrated-${stamp}`);\n  return { fromPath: oldPath, toPath: newPath, dryRun: false, filesMoved: toCount };\n}\n","/**\n * `pai config` — the PAI_HOME namespace dir (~/.claude/pai by default) and\n * moving the per-user files that live under it: config.json, workers.yaml,\n * whisper-rules.md, advisor-mode.json, session-state/, and the remaining\n * per-subsystem state files/dirs migrated from ~/.config/pai on 2026-09-19\n * (session-scan-cache.json, queries/, summary-cooldowns.json,\n * work-queue.json, kg-backfill-state.json, voices.json, the orphaned legacy\n * federation database), plus the legacy registry database file and the two\n * content dirs that get an adapter symlink back to ~/.claude, `agents/` and\n * `commands/`. History/, agent-sessions.json, session-routing.json and\n * security-events.jsonl move only with `--history`; logs/workers only with\n * `--logs` — both are written by hooks on every live session and are\n * refused while a worker is RUNNING.\n */\n\nimport { existsSync, renameSync, statSync, readdirSync, readFileSync, cpSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { execSync } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport {\n  paiHomeDir,\n  paiHomePath,\n  migratePaiFile,\n  PaiFileMigrationError,\n  type MigrateFileResult,\n  type MigrateDirResult,\n} from \"../../config/pai-home.js\";\nimport { paiConfigFilePath, paiConfigYamlFilePath, migrateConfigFile } from \"../../daemon/config.js\";\nimport { migrateMainConfigToYaml, MainConfigError } from \"../../config/main-config.js\";\nimport { voicesJsonPath, voicesYamlPath, migrateVoicesToYaml } from \"../../config/voices-config.js\";\nimport {\n  listConfigOp,\n  getConfigValueOp,\n  setConfigValueOp,\n  unsetConfigValueOp,\n  formatConfigGetOutput,\n  MainConfigOpsError,\n} from \"../../config/main-config-ops.js\";\nimport { workersYamlPath, relocateWorkersYaml } from \"../../workers/workers-config.js\";\nimport {\n  legacyDbDisplayPaths,\n  migrateFederationDbOrphan,\n  migrateRegistryDbFile,\n  migrateFederationDbFile,\n} from \"../../storage/sqlite/legacy-migration.js\";\nimport { STATE_FILE as schedulerStatePath, migrateSchedulerState } from \"../../tasks/poller.js\";\nimport { migrateIdentityFile } from \"../../memory/wakeup.js\";\nimport { migrateScanConfig } from \"./registry/scan.js\";\nimport { oldBackupsDir, backupsDirPath } from \"../commands/lifecycle/backup.js\";\nimport { defaultVaultPath, oldDefaultVaultPath, migrateObsidianVaultDir } from \"../../obsidian/sync/generate.js\";\nimport { getConfigObsidianVaultPathRaw, saveVaultPath as saveObsidianVaultPath } from \"../lib/obsidian.js\";\nimport { migrateAgentsDir, migrateCommandsDir, type MigrateContentDirResult } from \"../../config/adapter-content.js\";\nimport {\n  whisperRulesPath,\n  advisorModePath,\n  sessionStateDir,\n  migrateWhisperRules,\n  migrateAdvisorMode,\n  migrateSessionStateDir,\n  migrateVoicesJson,\n  migrateSessionStopLock,\n  migrateLastHousekeeping,\n  migrateHistoryDir,\n  migrateAgentSessions,\n  migrateSessionRouting,\n  migrateSecurityEvents,\n} from \"../../config/pai-files.js\";\nimport { scanCacheFilePath, migrateSessionScanCache } from \"../../cli/lib/session-scan.js\";\nimport { queriesDirPath, migrateQueriesDir } from \"../../zettelkasten/query-feedback.js\";\nimport { migrateSummaryCooldowns } from \"../../daemon/session-summary-worker.js\";\nimport { migrateWorkQueue } from \"../../daemon/work-queue.js\";\nimport { migrateKgBackfillState } from \"../../memory/kg-backfill.js\";\nimport { readWorkersSection, expandHome } from \"../../workers/config.js\";\nimport { migrateWorkerLogs, WorkerLogsMigrationError, activeSpawnedWorkerCount } from \"../../workers/logs-migrate.js\";\nimport { ok, err, dim, bold } from \"../lib/utils.js\";\n\nfunction sha256FileSync(path: string): string {\n  return createHash(\"sha256\").update(readFileSync(path)).digest(\"hex\");\n}\n\nfunction walkFilesRelative(dir: string, base: string = dir): string[] {\n  const out: string[] = [];\n  for (const entry of readdirSync(dir)) {\n    const full = join(dir, entry);\n    if (statSync(full).isDirectory()) {\n      out.push(...walkFilesRelative(full, base));\n    } else {\n      out.push(full.slice(base.length + 1));\n    }\n  }\n  return out;\n}\n\nexport interface MigrateBackupsLegacyResult {\n  fromDir: string | null;\n  toDir: string;\n  dryRun: boolean;\n  filesVerified?: number;\n  note?: string;\n}\n\n/**\n * ~/.pai/backups/ — pre-PAI_HOME `pai backup` snapshots (registry database,\n * config.json, federation database, postgres-pai.sql per timestamped run).\n * Moved into PAI_HOME/backups/legacy-pai-backups/ rather than merged into\n * PAI_HOME/backups/ directly, since that directory now holds newly created\n * backups going forward and the legacy runs are kept as a clearly-labelled\n * historical archive. Unlike migratePaiDir's plain rename-per-entry, this\n * copies the whole tree then verifies every file byte-identical (sha256)\n * before renaming the old dir aside — these snapshots include a 1.4GB\n * federation database and a 1GB pg_dump, worth the extra care.\n */\nfunction migrateBackupsLegacy(dryRun: boolean): MigrateBackupsLegacyResult {\n  const oldDir = oldBackupsDir();\n  const newDir = paiHomePath(\"backups\", \"legacy-pai-backups\");\n\n  if (!existsSync(oldDir)) {\n    return { fromDir: null, toDir: newDir, dryRun, note: \"nothing to migrate — directory does not exist yet\" };\n  }\n  if (existsSync(newDir)) {\n    return { fromDir: oldDir, toDir: newDir, dryRun, note: \"already at new location\" };\n  }\n  if (dryRun) {\n    return { fromDir: oldDir, toDir: newDir, dryRun: true };\n  }\n\n  cpSync(oldDir, newDir, { recursive: true });\n\n  const relFiles = walkFilesRelative(oldDir);\n  for (const rel of relFiles) {\n    const a = sha256FileSync(join(oldDir, rel));\n    const b = sha256FileSync(join(newDir, rel));\n    if (a !== b) {\n      throw new PaiFileMigrationError(\n        `${join(newDir, rel)}: copy did not match ${join(oldDir, rel)} (sha256 mismatch) — aborting, old dir left in place`\n      );\n    }\n  }\n\n  const stamp = new Date().toISOString().slice(0, 10).replace(/-/g, \"\");\n  renameSync(oldDir, `${oldDir}.migrated-${stamp}`);\n\n  return { fromDir: oldDir, toDir: newDir, dryRun: false, filesVerified: relFiles.length };\n}\n\nexport interface MigrateObsidianVaultPathConfigResult {\n  status: \"rewritten\" | \"would-rewrite\" | \"already-new\" | \"not-set\" | \"custom\";\n  from?: string;\n  to?: string;\n}\n\n/**\n * `pai obsidian sync --vault` used to save the then-default path\n * (~/.pai/obsidian-vault) into config.json's obsidianVaultPath explicitly.\n * migrateObsidianVaultDir above moves the directory but has no reason to\n * touch config.json, so without this every later sync (including the one\n * the session-stop hook runs) kept regenerating the vault at the old path.\n * Only rewrites when the stored value is EXACTLY the old default — a custom\n * vault path is left alone.\n */\nexport function migrateObsidianVaultPathConfig(dryRun: boolean): MigrateObsidianVaultPathConfigResult {\n  const current = getConfigObsidianVaultPathRaw();\n  const newPath = paiHomePath(\"obsidian-vault\");\n\n  if (!current) return { status: \"not-set\" };\n  if (current === newPath) return { status: \"already-new\", from: current, to: newPath };\n  if (current !== oldDefaultVaultPath()) return { status: \"custom\", from: current };\n  if (dryRun) return { status: \"would-rewrite\", from: current, to: newPath };\n\n  saveObsidianVaultPath(newPath);\n  return { status: \"rewritten\", from: current, to: newPath };\n}\n\nexport function configPathCmd(): number {\n  console.log(bold(\"PAI_HOME: \") + paiHomeDir());\n  console.log(`  config.json / config.yaml: ${paiConfigFilePath()} / ${paiConfigYamlFilePath()}`);\n  console.log(`  workers.yaml:             ${workersYamlPath()}`);\n  console.log(`  whisper-rules.md:         ${whisperRulesPath()}`);\n  console.log(`  advisor-mode.json:        ${advisorModePath()}`);\n  console.log(`  session-state/:           ${sessionStateDir()}`);\n  console.log(`  session-scan-cache.json:  ${scanCacheFilePath()}`);\n  console.log(`  queries/:                 ${queriesDirPath()}`);\n  console.log(`  summary-cooldowns.json, work-queue.json, kg-backfill-state.json:`);\n  console.log(`                            all under PAI_HOME`);\n  console.log(`  voices.json / voices.yaml: ${voicesJsonPath()} / ${voicesYamlPath()}`);\n  console.log(`  federation database (orphan): under PAI_HOME`);\n  console.log(`  logs/workers/:            ${paiHomePath(\"logs\", \"workers\")} (pass --logs to move)`);\n  console.log(`  History/:                 ${paiHomePath(\"History\")} (pass --history to move)`);\n  console.log(`  agent-sessions.json, session-routing.json, History/security/security-events.jsonl:`);\n  console.log(`                            all under PAI_HOME (pass --history to move)`);\n  console.log(`  registry database:        ${legacyDbDisplayPaths().registryDb}`);\n  console.log(`  federation database:      ${legacyDbDisplayPaths().federationDb}`);\n  console.log(`  backups/:                 ${backupsDirPath()}`);\n  console.log(`  obsidian-vault/:          ${defaultVaultPath()}`);\n  console.log(`  scheduler-state.json:     ${schedulerStatePath}`);\n  console.log(`  identity.txt:             ${paiHomePath(\"identity.txt\")} (L0 wake-up identity, optional)`);\n  console.log(`  registry-scan.json:       ${paiHomePath(\"registry-scan.json\")}`);\n  console.log(`  agents/:                  ${paiHomePath(\"agents\")} (~/.claude/agents symlinks here after migrate)`);\n  console.log(`  commands/:                ${paiHomePath(\"commands\")} (~/.claude/commands symlinks here after migrate)`);\n  return 0;\n}\n\nexport function configYamlCmd(opts: { dryRun?: boolean; force?: boolean }): number {\n  let hadError = false;\n  const run = (label: string, fn: () => ReturnType<typeof migrateMainConfigToYaml>) => {\n    try {\n      const r = fn();\n      if (r.dryRun) {\n        console.log(dim(`  ${label}: would write ${r.yamlPath}`));\n      } else {\n        console.log(ok(`  ${label}: `) + `${r.backupPath} → ${r.yamlPath}`);\n      }\n    } catch (e) {\n      hadError = true;\n      console.error(err(`  ${label}: `) + (e instanceof MainConfigError ? e.message : String(e instanceof Error ? e.message : e)));\n    }\n  };\n  run(\"config.yaml\", () => migrateMainConfigToYaml(paiConfigFilePath(), { dryRun: opts.dryRun, force: opts.force }));\n  run(\"voices.yaml\", () => migrateVoicesToYaml({ dryRun: opts.dryRun, force: opts.force }));\n  return hadError ? 1 : 0;\n}\n\nexport function configListCmd(opts: { all?: boolean; json?: boolean }): number {\n  const r = listConfigOp({ all: opts.all });\n  console.log(opts.json ? JSON.stringify(r.data, null, 2) : r.yaml.trimEnd());\n  return 0;\n}\n\nexport function configGetCmd(path: string, opts: { json?: boolean }): number {\n  try {\n    const r = getConfigValueOp(path);\n    if (!r.found) {\n      console.error(err(`Not set: ${path}`));\n      return 1;\n    }\n    console.log(formatConfigGetOutput(r.value, { json: opts.json }));\n  } catch (e) {\n    console.error(err((e instanceof MainConfigOpsError ? e.message : String(e instanceof Error ? e.message : e))));\n    return 1;\n  }\n  return 0;\n}\n\nexport function configSetCmd(path: string, value: string, opts: { force?: boolean }): number {\n  try {\n    const r = setConfigValueOp(path, value, { force: opts.force });\n    if (r.yamlCreated) console.log(dim(`  created ${r.yamlPath}`));\n    console.log(ok(`Set ${path} = `) + (typeof r.value === \"object\" ? JSON.stringify(r.value) : String(r.value)));\n  } catch (e) {\n    console.error(err((e instanceof MainConfigOpsError || e instanceof MainConfigError ? e.message : String(e instanceof Error ? e.message : e))));\n    return 1;\n  }\n  return 0;\n}\n\nexport function configUnsetCmd(path: string): number {\n  try {\n    const r = unsetConfigValueOp(path);\n    console.log(r.existed ? ok(`Unset ${path}`) : dim(`  ${path} was not set`));\n  } catch (e) {\n    console.error(err((e instanceof MainConfigOpsError ? e.message : String(e instanceof Error ? e.message : e))));\n    return 1;\n  }\n  return 0;\n}\n\nexport function configMigrateCmd(opts: { dryRun?: boolean; logs?: boolean; history?: boolean }): number {\n  const dryRun = !!opts.dryRun;\n  let hadError = false;\n\n  const report = (label: string, fn: () => MigrateFileResult) => {\n    try {\n      const r = fn();\n      if (r.fromPath === null) {\n        console.log(dim(`  ${label}: ${r.note ?? \"nothing to migrate\"} (${r.toPath})`));\n      } else if (r.dryRun) {\n        console.log(dim(`  ${label}: would move ${r.fromPath} → ${r.toPath}`));\n      } else {\n        console.log(ok(`  ${label}: `) + `${r.fromPath} → ${r.toPath}` + (r.note ? dim(` (${r.note})`) : \"\"));\n      }\n    } catch (e) {\n      hadError = true;\n      console.error(err(`  ${label}: `) + (e instanceof Error ? e.message : String(e)));\n    }\n  };\n\n  const reportDir = (label: string, fn: () => MigrateDirResult) => {\n    try {\n      const r = fn();\n      if (r.fromDir === null) {\n        console.log(dim(`  ${label}: ${r.note ?? \"nothing to migrate\"} (${r.toDir})`));\n      } else if (r.dryRun) {\n        console.log(dim(`  ${label}: would move ${r.movedCount ?? 0} entrie(s) from ${r.fromDir} → ${r.toDir}`));\n      } else {\n        console.log(\n          ok(`  ${label}: `) +\n            `${r.movedCount ?? 0} entrie(s) ${r.fromDir} → ${r.toDir}` +\n            (r.note ? dim(` (${r.note})`) : \"\")\n        );\n      }\n    } catch (e) {\n      hadError = true;\n      console.error(err(`  ${label}: `) + (e instanceof Error ? e.message : String(e)));\n    }\n  };\n\n  report(\"config.json\", () => migrateConfigFile({ dryRun }));\n  report(\"workers.yaml\", () => relocateWorkersYaml({ dryRun }));\n  report(\"whisper-rules.md\", () => migrateWhisperRules({ dryRun }));\n  report(\"advisor-mode.json\", () => migrateAdvisorMode({ dryRun }));\n  reportDir(\"session-state/\", () => migrateSessionStateDir({ dryRun }));\n  report(\"session-scan-cache.json\", () => migrateSessionScanCache({ dryRun }));\n  reportDir(\"queries/\", () => migrateQueriesDir({ dryRun }));\n  report(\"summary-cooldowns.json\", () => migrateSummaryCooldowns({ dryRun }));\n  report(\"work-queue.json\", () => migrateWorkQueue({ dryRun }));\n  report(\"kg-backfill-state.json\", () => migrateKgBackfillState({ dryRun }));\n  report(\"voices.json\", () => migrateVoicesJson({ dryRun }));\n  report(\"federation database (orphan)\", () => migrateFederationDbOrphan(dryRun));\n  report(\"registry database\", () => migrateRegistryDbFile(dryRun));\n  report(\"federation database (live)\", () => migrateFederationDbFile(dryRun));\n  reportDir(\"obsidian-vault/\", () => migrateObsidianVaultDir({ dryRun }));\n  try {\n    const r = migrateObsidianVaultPathConfig(dryRun);\n    const label = \"obsidian-vault path in config.json\";\n    switch (r.status) {\n      case \"not-set\":\n        console.log(dim(`  ${label}: not set`));\n        break;\n      case \"already-new\":\n        console.log(dim(`  ${label}: already at new location`));\n        break;\n      case \"custom\":\n        console.log(dim(`  ${label}: custom, left alone`));\n        break;\n      case \"would-rewrite\":\n        console.log(dim(`  ${label}: would rewrite ${r.from} → ${r.to}`));\n        break;\n      case \"rewritten\":\n        console.log(ok(`  ${label}: `) + `${r.from} → ${r.to}`);\n        break;\n    }\n  } catch (e) {\n    hadError = true;\n    console.error(err(\"  obsidian-vault path in config.json: \") + (e instanceof Error ? e.message : String(e)));\n  }\n  report(\"scheduler-state.json\", () => migrateSchedulerState({ dryRun }));\n  report(\"identity.txt\", () => migrateIdentityFile({ dryRun }));\n  report(\"registry-scan.json\", () => migrateScanConfig({ dryRun }));\n\n  try {\n    const r = migrateBackupsLegacy(dryRun);\n    if (r.fromDir === null) {\n      console.log(dim(`  backups/legacy-pai-backups/: ${r.note ?? \"nothing to migrate\"} (${r.toDir})`));\n    } else if (r.dryRun) {\n      console.log(dim(`  backups/legacy-pai-backups/: would copy+verify ${r.fromDir} → ${r.toDir}`));\n    } else {\n      console.log(\n        ok(`  backups/legacy-pai-backups/: `) +\n          `${r.filesVerified ?? 0} file(s) verified, ${r.fromDir} → ${r.toDir}` +\n          (r.note ? dim(` (${r.note})`) : \"\")\n      );\n    }\n  } catch (e) {\n    hadError = true;\n    console.error(err(`  backups/legacy-pai-backups/: `) + (e instanceof Error ? e.message : String(e)));\n  }\n\n  const reportContentDir = (label: string, fn: () => MigrateContentDirResult) => {\n    try {\n      const r = fn();\n      if (r.fromDir === null) {\n        console.log(dim(`  ${label}: ${r.note ?? \"nothing to migrate\"} (${r.toDir})`));\n      } else if (r.dryRun) {\n        console.log(dim(`  ${label}: would move ${r.movedCount ?? 0} entrie(s) from ${r.fromDir} → ${r.toDir}, then symlink`));\n      } else {\n        const symlinkPart = r.symlinked\n          ? \", adapter symlink created\"\n          : ` — NOT symlinked (${r.symlinkNote ?? \"see note\"})`;\n        console.log(\n          ok(`  ${label}: `) +\n            `${r.movedCount ?? 0} entrie(s) ${r.fromDir} → ${r.toDir}${symlinkPart}` +\n            (r.note ? dim(` (${r.note})`) : \"\")\n        );\n      }\n    } catch (e) {\n      hadError = true;\n      console.error(err(`  ${label}: `) + (e instanceof Error ? e.message : String(e)));\n    }\n  };\n  reportContentDir(\"agents/\", () => migrateAgentsDir({ dryRun }));\n  reportContentDir(\"commands/\", () => migrateCommandsDir({ dryRun }));\n\n  if (opts.logs) {\n    try {\n      const { workers } = readWorkersSection();\n      const oldLogDir = expandHome(workers.logDir);\n      const newLogDir = paiHomePath(\"logs\", \"workers\");\n      const r = migrateWorkerLogs(oldLogDir, newLogDir, { dryRun });\n      if (r.fromPath === null) {\n        console.log(dim(`  logs/workers: ${r.note ?? \"nothing to migrate\"} (${r.toPath})`));\n      } else if (r.dryRun) {\n        console.log(dim(`  logs/workers: would move ${r.fromPath} → ${r.toPath}`));\n      } else {\n        console.log(\n          ok(`  logs/workers: `) +\n            `${r.filesMoved ?? 0} file(s) ${r.fromPath} → ${r.toPath}` +\n            (r.note ? dim(` (${r.note})`) : \"\")\n        );\n      }\n    } catch (e) {\n      hadError = true;\n      const msg = e instanceof WorkerLogsMigrationError ? e.message : String(e instanceof Error ? e.message : e);\n      console.error(err(\"  logs/workers: \") + msg);\n    }\n  } else {\n    console.log(dim(`  logs/workers: pass --logs to move it (skipped by default — actively written by running workers)`));\n  }\n\n  if (opts.history) {\n    try {\n      const { workers } = readWorkersSection();\n      const active = activeSpawnedWorkerCount(expandHome(workers.logDir));\n      if (active > 0 && !dryRun) {\n        throw new Error(\n          `${active} worker(s) other than the interactive session are RUNNING (see \\`pai worker ps\\`) — ` +\n            `History/ etc. are written by every session's hooks, wait for them to finish and confirm no ` +\n            `other interactive \\`claude\\` session is active, then retry`\n        );\n      }\n      reportDir(\"History/\", () => migrateHistoryDir({ dryRun }));\n      report(\"agent-sessions.json\", () => migrateAgentSessions({ dryRun }));\n      report(\"session-routing.json\", () => migrateSessionRouting({ dryRun }));\n      report(\"History/security/security-events.jsonl\", () => migrateSecurityEvents({ dryRun }));\n    } catch (e) {\n      hadError = true;\n      console.error(err(\"  History/ etc.: \") + (e instanceof Error ? e.message : String(e)));\n    }\n  } else {\n    console.log(\n      dim(`  History/, agent-sessions.json, session-routing.json, security-events.jsonl: pass --history to move (skipped by default — actively written by hooks on every session)`)\n    );\n  }\n\n  report(\".last-housekeeping\", () => migrateLastHousekeeping({ dryRun }));\n\n  try {\n    const r = migrateSessionStopLock({ dryRun });\n    const line = `  .session-stop.lock: ${r.note}`;\n    console.log(r.status === \"moved\" && !dryRun ? ok(line) : dim(line));\n  } catch (e) {\n    hadError = true;\n    console.error(err(\"  .session-stop.lock: \") + (e instanceof Error ? e.message : String(e)));\n  }\n\n  return hadError ? 1 : 0;\n}\n","/**\n * `pai config`: front end for the config commands.\n * Behaviour lives in src/cli/impl/config.js; this file declares names, flags and help.\n */\n\nimport type { CommandDef } from \"../define.js\";\nimport { configPathCmd, configYamlCmd, configListCmd, configGetCmd, configSetCmd, configUnsetCmd, configMigrateCmd } from \"../impl/config.js\";\n\nexport const configSubcommands: CommandDef[] = [\n{\n  name: \"path\",\n  summary: \"Print where PAI keeps its settings\",\n  description: \"Print where PAI keeps its settings.\\n\\nPrint the PAI_HOME namespace dir and each resolved per-user file\",\n  examples: [\"pai config path\",\"pai config path\"],\n  seeAlso: [\"pai config yaml\"],\n  run: () => configPathCmd(),\n},\n{\n  name: \"yaml\",\n  summary: \"Convert config.json to config.yaml\",\n  description: \"Convert config.json to config.yaml.\\n\\nConvert config.json → config.yaml (and voices.json → voices.yaml), with a short\\n      # comment above every top-level section and any \\\"_comment\\\"/\\\"_...Note\\\" JSON-\\n      workaround key re-attached as a real comment. Verifies the generated YAML\\n      re-parses to the exact same data before writing anything; the JSON is then\\n      renamed to <name>.migrated-<YYYY-MM-DD> (never deleted). Every writer keeps\\n      working unmodified afterwards — see docs/config.md.\",\n  options: [{ name: \"dry-run\", type: \"boolean\", summary: \"Print the plan without writing anything\" }, { name: \"force\", type: \"boolean\", summary: \"Regenerate config.yaml (or voices.yaml) even if it already exists\" }],\n  examples: [\"pai config yaml --dry-run\",\"pai config yaml\"],\n  seeAlso: [\"pai config list\",\"pai config path\"],\n  run: (ctx) => configYamlCmd({ dryRun: ctx.opts[\"dry-run\"] === true, force: ctx.opts.force === true }),\n},\n{\n  name: \"list\",\n  summary: \"Print all settings (secrets hidden)\",\n  description: \"Print all settings (secrets hidden).\\n\\nPrint the main PAI config as YAML (secrets masked). Defaults to what the file explicitly sets.\",\n  options: [{ name: \"all\", type: \"boolean\", summary: \"Include every default value, not just what the file sets\" }, { name: \"json\", type: \"boolean\", summary: \"Print JSON instead of YAML\" }],\n  examples: [\"pai config list\",\"pai config list --json\"],\n  seeAlso: [\"pai config get\",\"pai config yaml\"],\n  run: (ctx) => configListCmd({ all: ctx.opts.all === true, json: ctx.opts.json === true }),\n},\n{\n  name: \"get\",\n  summary: \"Print one setting\",\n  description: \"Print one setting.\\n\\nPrint one config value (dotted path, e.g. search.recencyBoostDays), masked if it looks like a secret. An object/array subtree prints as YAML; pass --json for JSON.\",\n  args: [{ name: \"PATH\", summary: \"Setting name with dots, e.g. search.recencyBoostDays\", required: true }],\n  options: [{ name: \"json\", type: \"boolean\", summary: \"Print an object/array value as JSON instead of YAML\" }],\n  examples: [\"pai config get search.recencyBoostDays\",\"pai config get search.recencyBoostDays --json\"],\n  seeAlso: [\"pai config set\",\"pai config list\"],\n  run: (ctx) => configGetCmd(String(ctx.args.PATH), { json: ctx.opts.json === true }),\n},\n{\n  name: \"set\",\n  summary: \"Change one setting\",\n  description: \"Change one setting.\\n\\nSet one config value (dotted path). Value parsing: true/false, null, numbers,\\n      [...] / {...} as JSON, else a string. Creates config.yaml (from config.json,\\n      if any) on first use, then writes comment-preserving.\",\n  args: [{ name: \"PATH\", summary: \"Setting name with dots, e.g. search.recencyBoostDays\", required: true }, { name: \"VALUE\", summary: \"The new value\", required: true }],\n  options: [{ name: \"force\", type: \"boolean\", summary: \"Set an unknown top-level key, or a value of a different type than the default\" }],\n  examples: [\"pai config set search.recencyBoostDays 7\",\"pai config set search.recencyBoostDays 7 --force\"],\n  seeAlso: [\"pai config unset\",\"pai config get\"],\n  run: (ctx) => configSetCmd(String(ctx.args.PATH), String(ctx.args.VALUE), { force: ctx.opts.force === true }),\n},\n{\n  name: \"unset\",\n  summary: \"Reset one setting to its default\",\n  description: \"Reset one setting to its default.\\n\\nRemove one config value (dotted path), reverting it to the built-in default\",\n  args: [{ name: \"PATH\", summary: \"Setting name with dots, e.g. search.recencyBoostDays\", required: true }],\n  examples: [\"pai config unset search.recencyBoostDays\",\"pai config list\"],\n  seeAlso: [\"pai config migrate\",\"pai config set\"],\n  run: (ctx) => configUnsetCmd(String(ctx.args.PATH)),\n},\n{\n  name: \"migrate\",\n  summary: \"Move old settings files into the current layout\",\n  description: \"Move old settings files into the current layout.\\n\\nMove config.json, workers.yaml, whisper-rules.md, advisor-mode.json, session-state/,\\nsession-scan-cache.json, queries/, summary-cooldowns.json, work-queue.json,\\nkg-backfill-state.json, voices.json, the orphaned legacy federation database,\\nthe legacy registry database, the legacy live federation database, ~/.pai/backups/,\\n~/.pai/obsidian-vault/, ~/.pai/scheduler-state.json, and ~/.claude/agents/,\\n~/.claude/commands/ into PAI_HOME.\\nThe registry and federation databases each get an extra WAL checkpoint and PRAGMA\\nintegrity_check (sqlite3 CLI, if present) on top of the byte-identical copy.\\nbackups/ is copied and sha256-verified file-by-file into\\nbackups/legacy-pai-backups/ rather than merged in place. agents/ and commands/ each\\nget a symlink left at the old ~/.claude path once their content has moved, so\\nClaude Code still finds them at its fixed harness paths.\\nEach file is copied, verified byte-identical, then the old file is renamed to\\n<name>.migrated-<YYYYMMDD> (never deleted); directories move their contents, then\\nthe emptied old directory is renamed the same way. Idempotent — entries already at\\nthe new location, or with nothing old to migrate, are skipped.\",\n  options: [{ name: \"dry-run\", type: \"boolean\", summary: \"Print the plan without writing anything\" }, { name: \"logs\", type: \"boolean\", summary: \"Also move ~/.claude/logs/workers into PAI_HOME: an atomic rename plus a symlink left at the old path, so any worker still writing there keeps landing in the new directory (falls back to copy+verify across volumes, refusing while a worker is RUNNING — see `pai worker ps` — only in that fallback case)\" }, { name: \"history\", type: \"boolean\", summary: \"Also move ~/.claude/History/, agent-sessions.json, session-routing.json and history/security/security-events.jsonl into PAI_HOME. These are written by hooks on EVERY session's every turn, not just spawned workers — refuses while any worker other than the interactive session is RUNNING (see `pai worker ps`), but that guard cannot see OTHER interactive `claude` sessions on this machine. Confirm none are active (`ps aux | grep claude`, `aibroker sessions list`) before passing this flag.\" }],\n  examples: [\"pai config migrate --dry-run\",\"pai config migrate\"],\n  seeAlso: [\"pai config unset\"],\n  run: (ctx) => configMigrateCmd({ dryRun: ctx.opts[\"dry-run\"] === true, logs: ctx.opts.logs === true, history: ctx.opts.history === true }),\n},\n];\n\nexport const configCommand: CommandDef = {\n  name: \"config\",\n  summary: \"PAI settings: show, read, change\",\n  description: \"PAI settings: show, read, change.\",\n  examples: [\"pai config path\",\"pai config list\"],\n  subcommands: configSubcommands,\n};\n","/**\n * `pai daemon`: front end for the daemon commands.\n * Behaviour lives in src/cli/impl/daemon.js; this file declares names, flags and help.\n */\n\nimport type { CommandDef } from \"../define.js\";\nimport { daemonServeCmd, daemonMigrateCmd, daemonStatusCmd, daemonRestartCmd, daemonInstallCmd, daemonUninstallCmd, daemonKeepaliveCmd, daemonLogsCmd } from \"../impl/daemon.js\";\n\nexport const daemonSubcommands: CommandDef[] = [\n{\n  name: \"serve\",\n  summary: \"Run the daemon in the foreground\",\n  description: \"Run the daemon in the foreground.\\n\\nStart the PAI daemon in the foreground\",\n  examples: [\"pai daemon serve\",\"pai daemon status\"],\n  seeAlso: [\"pai daemon migrate\"],\n  run: () => daemonServeCmd(),\n},\n{\n  name: \"migrate\",\n  summary: \"Copy federation data from SQLite to PostgreSQL\",\n  description: \"Copy federation data from SQLite to PostgreSQL.\",\n  options: [{ name: \"connection-string\", type: \"string\", value: \"URL\", summary: \"Postgres connection string (default: from config or postgresql://pai:pai@localhost:5432/pai)\" }],\n  examples: [\"pai daemon migrate --connection-string postgres://localhost/pai\",\"pai daemon status\"],\n  seeAlso: [\"pai daemon status\",\"pai daemon serve\"],\n  run: (ctx) => daemonMigrateCmd({ connectionString: ctx.opts[\"connection-string\"] as string | undefined }),\n},\n{\n  name: \"status\",\n  summary: \"Show whether the daemon is running\",\n  description: \"Show whether the daemon is running.\",\n  examples: [\"pai daemon status\",\"pai daemon logs\"],\n  seeAlso: [\"pai daemon restart\",\"pai daemon migrate\"],\n  run: () => daemonStatusCmd(),\n},\n{\n  name: \"restart\",\n  summary: \"Restart the daemon\",\n  description: \"Restart the daemon.\\n\\nRestart the running daemon (launchd on macOS, systemd on Linux)\",\n  examples: [\"pai daemon restart\",\"pai daemon status\"],\n  seeAlso: [\"pai daemon install\",\"pai daemon status\"],\n  run: () => daemonRestartCmd(),\n},\n{\n  name: \"install\",\n  summary: \"Install the daemon as a login service\",\n  description: \"Install the daemon as a login service.\\n\\nInstall daemon as a service (launchd/systemd) and update ~/.claude.json to use the shim\",\n  examples: [\"pai daemon install\",\"pai daemon status\"],\n  seeAlso: [\"pai daemon uninstall\",\"pai daemon restart\"],\n  run: () => daemonInstallCmd(),\n},\n{\n  name: \"uninstall\",\n  summary: \"Remove the daemon service\",\n  description: \"Remove the daemon service.\\n\\nRemove the daemon service (launchd/systemd) and revert to direct MCP\",\n  examples: [\"pai daemon uninstall\",\"pai daemon status\"],\n  seeAlso: [\"pai daemon keepalive\",\"pai daemon install\"],\n  run: () => daemonUninstallCmd(),\n},\n{\n  name: \"keepalive\",\n  summary: \"Show session keepalive settings and recent activity\",\n  description: \"Show session keepalive settings and recent activity.\\n\\nShow interactive-session cache keepalive config, per-session beat counters, and recent ledger lines\",\n  examples: [\"pai daemon keepalive\",\"pai daemon status\"],\n  seeAlso: [\"pai daemon logs\",\"pai daemon uninstall\"],\n  run: () => daemonKeepaliveCmd(),\n},\n{\n  name: \"logs\",\n  summary: \"Show the end of the daemon log\",\n  description: \"Show the end of the daemon log.\\n\\nTail the daemon log (/tmp/pai-daemon.log)\",\n  options: [{ name: \"lines\", short: \"n\", type: \"string\", value: \"N\", summary: \"Number of lines to show\" }, { name: \"follow\", short: \"f\", type: \"boolean\", summary: \"Follow log output (like tail -f)\" }],\n  examples: [\"pai daemon logs\",\"pai daemon logs --lines 200 --follow\"],\n  seeAlso: [\"pai daemon keepalive\"],\n  run: (ctx) => daemonLogsCmd({ lines: ctx.opts.lines as string | undefined, follow: ctx.opts.follow === true }),\n},\n];\n\nexport const daemonCommand: CommandDef = {\n  name: \"daemon\",\n  summary: \"The background service: status, restart, logs\",\n  description: \"The background service: status, restart, logs.\",\n  examples: [\"pai daemon status\",\"pai daemon logs -n 50\"],\n  subcommands: daemonSubcommands,\n};\n","/**\n * migrate-to-postgres.ts — one-shot SQLite -> Postgres migration\n * (`pai db migrate-to-postgres`, docs/design/postgres-only.md §4, §8).\n *\n * Copies kg_entities, every registry table, and the memory/vault rows that\n * are absent from Postgres by natural key. Idempotent: every insert is\n * `ON CONFLICT DO NOTHING`, so a re-run (or a run against a database that\n * already has some rows) just verifies zero rows missing and copies nothing\n * new. Never deletes or renames the SQLite files — the operator does that\n * after reviewing the verify output.\n *\n * The only module allowed to hold both a better-sqlite3 handle and a `pg`\n * pool at once (it lives in src/storage/, where that is the point).\n */\n\nimport { execFileSync, spawnSync } from \"node:child_process\";\nimport { existsSync, mkdirSync, statSync, openSync, closeSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport BetterSqlite3 from \"better-sqlite3\";\nimport type { Database } from \"better-sqlite3\";\nimport pg from \"pg\";\nimport type { Pool, PoolClient } from \"pg\";\nimport { loadConfig } from \"../daemon/config.js\";\nimport { federationDbPath } from \"./sqlite/federation-db.js\";\nimport { registryDbPath } from \"./sqlite/registry-db.js\";\nimport { paiHomePath } from \"../config/pai-home.js\";\nimport { PaiClient } from \"../daemon/ipc-client.js\";\nimport type { PostgresConfig } from \"./postgres/config.js\";\n\nconst { Pool: PgPool } = pg;\n\nconst DEFAULT_CONTAINER = \"pai-pgvector\";\nconst PG_DATA_DEST = \"/var/lib/postgresql/data\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface MigrateOptions {\n  /** Preflight + counts only. No writes, no pg_dump. */\n  dryRun?: boolean;\n  /** Skip the pg_dump rollback artefact. Tests only. */\n  skipDump?: boolean;\n  /** Skip the \"daemon must not be running\" refusal. Tests only. */\n  allowRunning?: boolean;\n  /** docker container name. Default \"pai-pgvector\". */\n  container?: string;\n  /** Override the SQLite federation.db path (tests: a scratch file). */\n  sqliteFederationPath?: string;\n  /** Override the SQLite registry.db path (tests: a scratch file). */\n  sqliteRegistryPath?: string;\n  /** Override the Postgres connection (tests: a scratch database). */\n  pgConfig?: PostgresConfig;\n  /** Override the daemon IPC socket path (tests). */\n  socketPath?: string;\n  /** Skip the storageBackend:\"postgres\" config gate (tests only). */\n  skipConfigGate?: boolean;\n}\n\nexport interface TableReport {\n  table: string;\n  sqliteRows: number;\n  postgresRowsBefore: number;\n  copied: number;\n  missingAfter: number;\n}\n\nexport interface MigrationResult {\n  ok: boolean;\n  dryRun: boolean;\n  refused?: string;\n  dumpPath?: string;\n  tables: TableReport[];\n  embeddingsNote: string;\n}\n\n// ---------------------------------------------------------------------------\n// Preflight\n// ---------------------------------------------------------------------------\n\nfunction resolvePgConnectionString(config: PostgresConfig): string {\n  return (\n    config.connectionString ??\n    `postgresql://${config.user ?? \"pai\"}:${config.password ?? \"pai\"}@${config.host ?? \"localhost\"}:${config.port ?? 5432}/${config.database ?? \"pai\"}`\n  );\n}\n\nfunction resolvePgDatabaseName(config: PostgresConfig): string {\n  if (config.database) return config.database;\n  return new URL(resolvePgConnectionString(config)).pathname.slice(1) || \"pai\";\n}\n\ninterface DockerMount {\n  Type: string;\n  Source: string;\n  Destination: string;\n}\n\nfunction checkBindMount(container: string): { ok: true } | { ok: false; reason: string } {\n  let raw: string;\n  try {\n    raw = execFileSync(\"docker\", [\"inspect\", container, \"--format\", \"{{json .Mounts}}\"], {\n      encoding: \"utf-8\",\n    });\n  } catch (e) {\n    const msg = e instanceof Error ? e.message.split(\"\\n\")[0] : String(e);\n    return { ok: false, reason: `docker inspect ${container} failed — is the container running? (${msg})` };\n  }\n\n  let mounts: DockerMount[];\n  try {\n    mounts = JSON.parse(raw);\n  } catch {\n    return { ok: false, reason: `docker inspect ${container}: could not parse mount list` };\n  }\n\n  const dataMount = mounts.find((m) => m.Destination === PG_DATA_DEST);\n  if (!dataMount) {\n    return { ok: false, reason: `${container}: no mount at ${PG_DATA_DEST} — refusing` };\n  }\n  if (dataMount.Type !== \"bind\") {\n    return {\n      ok: false,\n      reason: `${container}: ${PG_DATA_DEST} is a \"${dataMount.Type}\" mount, not \"bind\" — refusing (the backup/rollback guarantee for the destination would not hold)`,\n    };\n  }\n  return { ok: true };\n}\n\nasync function checkDaemonNotRunning(socketPath: string): Promise<{ ok: true } | { ok: false; reason: string }> {\n  const client = new PaiClient(socketPath);\n  try {\n    await client.call(\"status\", {}, 1500);\n    return {\n      ok: false,\n      reason: \"PAI daemon is running — stop it first (pai daemon restart / launchctl unload) or pass --allow-running (tests only)\",\n    };\n  } catch {\n    // ENOENT/ECONNREFUSED/timeout all mean \"nothing answered\" — daemon not running.\n    return { ok: true };\n  }\n}\n\nfunction pgDump(\n  container: string,\n  pgConfig: PostgresConfig,\n  destPath: string\n): { ok: true } | { ok: false; reason: string } {\n  const database = resolvePgDatabaseName(pgConfig);\n  const user = pgConfig.user ?? \"pai\";\n  mkdirSync(dirname(destPath), { recursive: true });\n  const fd = openSync(destPath, \"w\");\n  try {\n    const dump = spawnSync(\n      \"docker\",\n      [\"exec\", container, \"pg_dump\", \"-U\", user, \"-Fc\", database],\n      { stdio: [\"pipe\", fd, \"pipe\"] }\n    );\n    if (dump.status !== 0) {\n      const msg = dump.stderr?.toString().trim() || `pg_dump exited with code ${dump.status}`;\n      return { ok: false, reason: `pg_dump failed: ${msg}` };\n    }\n  } catch (e) {\n    const msg = e instanceof Error ? e.message.split(\"\\n\")[0] : String(e);\n    return { ok: false, reason: `pg_dump failed: ${msg}` };\n  } finally {\n    closeSync(fd);\n  }\n  if (!existsSync(destPath) || statSync(destPath).size === 0) {\n    return { ok: false, reason: `pg_dump produced an empty file at ${destPath}` };\n  }\n  return { ok: true };\n}\n\n// ---------------------------------------------------------------------------\n// Generic copy-by-natural-key helper\n// ---------------------------------------------------------------------------\n\ninterface CopyPlan<TRow> {\n  table: string;\n  sqliteRows: TRow[];\n  /** Natural-key string for a row — used to diff SQLite against Postgres. */\n  keyFn: (row: TRow) => string;\n  /** SELECT that returns one column: the same key string, for every existing Postgres row. */\n  pgKeySql: string;\n  pgKeyParams?: unknown[];\n  insertSql: string;\n  paramsFn: (row: TRow) => unknown[];\n}\n\nasync function countRows(pool: Pool, sql: string, params: unknown[] = []): Promise<number> {\n  const r = await pool.query<{ n: string }>(sql, params);\n  return parseInt(r.rows[0]?.n ?? \"0\", 10);\n}\n\n/**\n * Insert every SQLite row whose natural key is absent from Postgres, in one\n * transaction, then report sqlite/postgres-before/copied/missing-after —\n * missing-after is recomputed from Postgres after the insert, so a partial\n * failure (caught, rolled back) is visible as a non-zero count rather than a\n * silent gap.\n */\nasync function copyByKey<TRow>(pool: Pool, dryRun: boolean, plan: CopyPlan<TRow>): Promise<TableReport> {\n  const sqliteRows = plan.sqliteRows;\n  const pgKeysBefore = await pool.query<{ k: string }>(plan.pgKeySql, plan.pgKeyParams ?? []);\n  const beforeSet = new Set(pgKeysBefore.rows.map((r) => r.k));\n  const postgresRowsBefore = beforeSet.size;\n\n  const missing = sqliteRows.filter((row) => !beforeSet.has(plan.keyFn(row)));\n\n  let copied = 0;\n  if (!dryRun && missing.length > 0) {\n    const client: PoolClient = await pool.connect();\n    try {\n      await client.query(\"BEGIN\");\n      for (const row of missing) {\n        const r = await client.query(plan.insertSql, plan.paramsFn(row));\n        copied += r.rowCount ?? 0;\n      }\n      await client.query(\"COMMIT\");\n    } catch (e) {\n      await client.query(\"ROLLBACK\");\n      throw e;\n    } finally {\n      client.release();\n    }\n  }\n\n  const pgKeysAfter = dryRun\n    ? pgKeysBefore\n    : await pool.query<{ k: string }>(plan.pgKeySql, plan.pgKeyParams ?? []);\n  const afterSet = dryRun ? beforeSet : new Set(pgKeysAfter.rows.map((r) => r.k));\n  const missingAfter = dryRun ? missing.length : sqliteRows.filter((row) => !afterSet.has(plan.keyFn(row))).length;\n\n  return {\n    table: plan.table,\n    sqliteRows: sqliteRows.length,\n    postgresRowsBefore,\n    copied: dryRun ? 0 : copied,\n    missingAfter,\n  };\n}\n\nasync function setSerialSequence(pool: Pool, table: string, column: string): Promise<void> {\n  await pool.query(\n    `SELECT setval(pg_get_serial_sequence($1, $2), GREATEST(COALESCE((SELECT MAX(${column}) FROM ${table}), 1), 1))`,\n    [table, column]\n  );\n}\n\n// ---------------------------------------------------------------------------\n// Registry tables (projects -> tags -> sessions -> project_tags ->\n// session_tags -> aliases -> links -> compaction_log)\n// ---------------------------------------------------------------------------\n\nasync function copyRegistryTables(\n  sqlite: Database,\n  pool: Pool,\n  dryRun: boolean,\n  tables: TableReport[]\n): Promise<void> {\n  type Row = Record<string, unknown>;\n  const all = (table: string): Row[] => sqlite.prepare(`SELECT * FROM ${table}`).all() as Row[];\n\n  tables.push(\n    await copyByKey<Row>(pool, dryRun, {\n      table: \"projects\",\n      sqliteRows: all(\"projects\"),\n      keyFn: (r) => String(r.id),\n      pgKeySql: \"SELECT id::text AS k FROM projects\",\n      insertSql: `INSERT INTO projects\n         (id, slug, display_name, root_path, encoded_dir, type, status, parent_id, obsidian_link, claude_notes_dir, session_config, created_at, updated_at, archived_at)\n         VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)\n         ON CONFLICT DO NOTHING`,\n      paramsFn: (r) => [\n        r.id, r.slug, r.display_name, r.root_path, r.encoded_dir, r.type, r.status,\n        r.parent_id, r.obsidian_link, r.claude_notes_dir, r.session_config, r.created_at, r.updated_at, r.archived_at,\n      ],\n    })\n  );\n  if (!dryRun) await setSerialSequence(pool, \"projects\", \"id\");\n\n  tables.push(\n    await copyByKey<Row>(pool, dryRun, {\n      table: \"tags\",\n      sqliteRows: all(\"tags\"),\n      keyFn: (r) => String(r.id),\n      pgKeySql: \"SELECT id::text AS k FROM tags\",\n      insertSql: `INSERT INTO tags (id, name) VALUES ($1,$2) ON CONFLICT DO NOTHING`,\n      paramsFn: (r) => [r.id, r.name],\n    })\n  );\n  if (!dryRun) await setSerialSequence(pool, \"tags\", \"id\");\n\n  tables.push(\n    await copyByKey<Row>(pool, dryRun, {\n      table: \"sessions\",\n      sqliteRows: all(\"sessions\"),\n      keyFn: (r) => String(r.id),\n      pgKeySql: \"SELECT id::text AS k FROM sessions\",\n      insertSql: `INSERT INTO sessions\n         (id, project_id, number, date, slug, title, filename, status, claude_session_id, token_count, created_at, closed_at)\n         VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12)\n         ON CONFLICT DO NOTHING`,\n      paramsFn: (r) => [\n        r.id, r.project_id, r.number, r.date, r.slug, r.title, r.filename, r.status,\n        r.claude_session_id, r.token_count, r.created_at, r.closed_at,\n      ],\n    })\n  );\n  if (!dryRun) await setSerialSequence(pool, \"sessions\", \"id\");\n\n  tables.push(\n    await copyByKey<Row>(pool, dryRun, {\n      table: \"project_tags\",\n      sqliteRows: all(\"project_tags\"),\n      keyFn: (r) => `${r.project_id}:${r.tag_id}`,\n      pgKeySql: \"SELECT project_id::text || ':' || tag_id::text AS k FROM project_tags\",\n      insertSql: `INSERT INTO project_tags (project_id, tag_id) VALUES ($1,$2) ON CONFLICT DO NOTHING`,\n      paramsFn: (r) => [r.project_id, r.tag_id],\n    })\n  );\n\n  tables.push(\n    await copyByKey<Row>(pool, dryRun, {\n      table: \"session_tags\",\n      sqliteRows: all(\"session_tags\"),\n      keyFn: (r) => `${r.session_id}:${r.tag_id}`,\n      pgKeySql: \"SELECT session_id::text || ':' || tag_id::text AS k FROM session_tags\",\n      insertSql: `INSERT INTO session_tags (session_id, tag_id) VALUES ($1,$2) ON CONFLICT DO NOTHING`,\n      paramsFn: (r) => [r.session_id, r.tag_id],\n    })\n  );\n\n  tables.push(\n    await copyByKey<Row>(pool, dryRun, {\n      table: \"aliases\",\n      sqliteRows: all(\"aliases\"),\n      keyFn: (r) => String(r.alias),\n      pgKeySql: \"SELECT alias AS k FROM aliases\",\n      insertSql: `INSERT INTO aliases (alias, project_id) VALUES ($1,$2) ON CONFLICT DO NOTHING`,\n      paramsFn: (r) => [r.alias, r.project_id],\n    })\n  );\n\n  tables.push(\n    await copyByKey<Row>(pool, dryRun, {\n      table: \"links\",\n      sqliteRows: all(\"links\"),\n      keyFn: (r) => String(r.id),\n      pgKeySql: \"SELECT id::text AS k FROM links\",\n      insertSql: `INSERT INTO links (id, session_id, target_project_id, link_type, created_at)\n         VALUES ($1,$2,$3,$4,$5) ON CONFLICT DO NOTHING`,\n      paramsFn: (r) => [r.id, r.session_id, r.target_project_id, r.link_type, r.created_at],\n    })\n  );\n  if (!dryRun) await setSerialSequence(pool, \"links\", \"id\");\n\n  tables.push(\n    await copyByKey<Row>(pool, dryRun, {\n      table: \"compaction_log\",\n      sqliteRows: all(\"compaction_log\"),\n      keyFn: (r) => String(r.id),\n      pgKeySql: \"SELECT id::text AS k FROM compaction_log\",\n      insertSql: `INSERT INTO compaction_log (id, project_id, session_id, trigger, files_written, token_count, created_at)\n         VALUES ($1,$2,$3,$4,$5,$6,$7) ON CONFLICT DO NOTHING`,\n      paramsFn: (r) => [r.id, r.project_id, r.session_id, r.trigger, r.files_written, r.token_count, r.created_at],\n    })\n  );\n  if (!dryRun) await setSerialSequence(pool, \"compaction_log\", \"id\");\n}\n\n// ---------------------------------------------------------------------------\n// kg_entities\n// ---------------------------------------------------------------------------\n\nasync function copyKgEntities(sqlite: Database, pool: Pool, dryRun: boolean, tables: TableReport[]): Promise<void> {\n  type Row = Record<string, unknown>;\n  const rows = sqlite.prepare(\"SELECT * FROM kg_entities\").all() as Row[];\n\n  tables.push(\n    await copyByKey<Row>(pool, dryRun, {\n      table: \"kg_entities\",\n      sqliteRows: rows,\n      keyFn: (r) => String(r.entity_id),\n      pgKeySql: \"SELECT entity_id AS k FROM kg_entities\",\n      insertSql: `INSERT INTO kg_entities\n         (entity_id, tenant_id, name, type, description, first_seen, last_seen, mention_count, feedback_weight)\n         VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)\n         ON CONFLICT (entity_id) DO NOTHING`,\n      paramsFn: (r) => [\n        r.entity_id, r.tenant_id, r.name, r.type, r.description,\n        r.first_seen, r.last_seen, r.mention_count, r.feedback_weight,\n      ],\n    })\n  );\n}\n\n// ---------------------------------------------------------------------------\n// memory_files/memory_chunks -> pai_files/pai_chunks\n//\n// Missing is decided at the (project_id, path) file level, not per chunk: a\n// path already indexed into Postgres may have been re-chunked differently on\n// a later run, so only paths Postgres has never seen at all are copied —\n// file row plus every chunk under that path.\n// ---------------------------------------------------------------------------\n\n/**\n * SQLite chunks may carry embeddings from an older model with a different\n * dimension than pai_chunks.embedding (vector(N)) — inserting a mismatched\n * literal fails the whole batch. `dimension` is the live column width; a\n * blob whose float count doesn't match it is treated the same as \"no\n * embedding\" so the row still copies and the daemon re-embeds it.\n */\nfunction blobToVectorLiteral(buf: Buffer | null, dimension: number | null): string | null {\n  if (!buf || buf.length === 0 || buf.length % 4 !== 0) return null;\n  const floatCount = buf.length / 4;\n  if (dimension !== null && floatCount !== dimension) return null;\n  const floats: number[] = [];\n  for (let i = 0; i < buf.length; i += 4) floats.push(buf.readFloatLE(i));\n  return \"[\" + floats.join(\",\") + \"]\";\n}\n\n/** Live width of pai_chunks.embedding (vector(N)), read once per run. Null if the column type can't be parsed. */\nasync function resolvePaiChunksEmbeddingDimension(pool: Pool): Promise<number | null> {\n  const r = await pool.query<{ type: string }>(\n    `SELECT format_type(atttypid, atttypmod) AS type\n     FROM pg_attribute\n     WHERE attrelid = 'pai_chunks'::regclass AND attname = 'embedding' AND NOT attisdropped`\n  );\n  const m = /vector\\((\\d+)\\)/.exec(r.rows[0]?.type ?? \"\");\n  return m ? parseInt(m[1], 10) : null;\n}\n\nasync function copyMemoryFilesAndChunks(\n  sqlite: Database,\n  pool: Pool,\n  dryRun: boolean,\n  tables: TableReport[],\n  embeddingDimension: number | null\n): Promise<string> {\n  type FileRow = { project_id: number; path: string; source: string; tier: string; hash: string; mtime: number; size: number };\n  type ChunkRow = {\n    id: string; project_id: number; source: string; tier: string; path: string;\n    start_line: number; end_line: number; hash: string; text: string; updated_at: number;\n    embedding: Buffer | null;\n  };\n\n  const fileRows = sqlite.prepare(\"SELECT * FROM memory_files\").all() as FileRow[];\n  const fileKey = (r: { project_id: number; path: string }) => `${r.project_id}:${r.path}`;\n\n  const pgFileKeys = await pool.query<{ k: string }>(\"SELECT project_id::text || ':' || path AS k FROM pai_files\");\n  const pgFileKeySet = new Set(pgFileKeys.rows.map((r) => r.k));\n  const missingFiles = fileRows.filter((r) => !pgFileKeySet.has(fileKey(r)));\n  const missingFileKeySet = new Set(missingFiles.map(fileKey));\n\n  const allChunkRows = sqlite.prepare(\"SELECT * FROM memory_chunks\").all() as ChunkRow[];\n  const chunksToCopy = allChunkRows.filter((c) => missingFileKeySet.has(fileKey(c)));\n\n  let filesCopied = 0;\n  let chunksCopied = 0;\n  let chunksWithEmbedding = 0;\n  let chunksWithoutEmbedding = 0;\n\n  if (!dryRun && missingFiles.length > 0) {\n    const client: PoolClient = await pool.connect();\n    try {\n      await client.query(\"BEGIN\");\n      for (const f of missingFiles) {\n        const r = await client.query(\n          `INSERT INTO pai_files (project_id, path, source, tier, hash, mtime, size)\n           VALUES ($1,$2,$3,$4,$5,$6,$7) ON CONFLICT (project_id, path) DO NOTHING`,\n          [f.project_id, f.path, f.source, f.tier, f.hash, f.mtime, f.size]\n        );\n        filesCopied += r.rowCount ?? 0;\n      }\n      for (const c of chunksToCopy) {\n        const vec = blobToVectorLiteral(c.embedding, embeddingDimension);\n        if (vec) chunksWithEmbedding++;\n        else chunksWithoutEmbedding++;\n        const r = await client.query(\n          `INSERT INTO pai_chunks (id, project_id, source, tier, path, start_line, end_line, hash, text, updated_at, embedding)\n           VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11::vector)\n           ON CONFLICT (id) DO NOTHING`,\n          [c.id, c.project_id, c.source, c.tier, c.path, c.start_line, c.end_line, c.hash, c.text.replace(/\\0/g, \"\"), c.updated_at, vec]\n        );\n        chunksCopied += r.rowCount ?? 0;\n      }\n      await client.query(\"COMMIT\");\n    } catch (e) {\n      await client.query(\"ROLLBACK\");\n      throw e;\n    } finally {\n      client.release();\n    }\n  } else {\n    for (const c of chunksToCopy) {\n      if (blobToVectorLiteral(c.embedding, embeddingDimension)) chunksWithEmbedding++;\n      else chunksWithoutEmbedding++;\n    }\n  }\n\n  const pgFileKeysAfter = dryRun\n    ? pgFileKeys\n    : await pool.query<{ k: string }>(\"SELECT project_id::text || ':' || path AS k FROM pai_files\");\n  const pgFileKeySetAfter = dryRun ? pgFileKeySet : new Set(pgFileKeysAfter.rows.map((r) => r.k));\n  const missingFilesAfter = fileRows.filter((r) => !pgFileKeySetAfter.has(fileKey(r))).length;\n\n  tables.push({\n    table: \"memory_files -> pai_files\",\n    sqliteRows: fileRows.length,\n    postgresRowsBefore: pgFileKeySet.size,\n    copied: dryRun ? 0 : filesCopied,\n    missingAfter: dryRun ? missingFiles.length : missingFilesAfter,\n  });\n\n  const pgChunkCountBefore = await countRows(pool, \"SELECT COUNT(*)::text AS n FROM pai_chunks\");\n  let missingChunksAfter = 0;\n  if (!dryRun) {\n    const pgChunkIds = await pool.query<{ id: string }>(\n      \"SELECT id FROM pai_chunks WHERE id = ANY($1)\",\n      [chunksToCopy.map((c) => c.id)]\n    );\n    const pgChunkIdSet = new Set(pgChunkIds.rows.map((r) => r.id));\n    missingChunksAfter = chunksToCopy.filter((c) => !pgChunkIdSet.has(c.id)).length;\n  } else {\n    missingChunksAfter = chunksToCopy.length;\n  }\n\n  tables.push({\n    table: \"memory_chunks -> pai_chunks\",\n    sqliteRows: allChunkRows.length,\n    postgresRowsBefore: pgChunkCountBefore,\n    copied: dryRun ? 0 : chunksCopied,\n    missingAfter: missingChunksAfter,\n  });\n\n  return `memory_chunks: ${chunksToCopy.length} chunk(s) eligible for copy under missing paths — ` +\n    `${chunksWithEmbedding} copied with embedding, ${chunksWithoutEmbedding} copied without ` +\n    `(dimension mismatch, re-embedded by the daemon).`;\n}\n\n// ---------------------------------------------------------------------------\n// Vault tables (vault_files, vault_aliases, vault_links, vault_name_index,\n// vault_health) — legacy SQLite tables from pre-Postgres-vault installs.\n// Migrated only if the SQLite db still physically has them.\n// ---------------------------------------------------------------------------\n\nfunction sqliteHasTable(sqlite: Database, table: string): boolean {\n  return !!sqlite.prepare(\"SELECT 1 FROM sqlite_master WHERE type='table' AND name = ?\").get(table);\n}\n\nasync function copyVaultTables(sqlite: Database, pool: Pool, dryRun: boolean, tables: TableReport[]): Promise<void> {\n  type Row = Record<string, unknown>;\n  const all = (table: string): Row[] => sqlite.prepare(`SELECT * FROM ${table}`).all() as Row[];\n\n  if (sqliteHasTable(sqlite, \"vault_files\")) {\n    tables.push(\n      await copyByKey<Row>(pool, dryRun, {\n        table: \"vault_files\",\n        sqliteRows: all(\"vault_files\"),\n        keyFn: (r) => String(r.vault_path),\n        pgKeySql: \"SELECT vault_path AS k FROM vault_files\",\n        insertSql: `INSERT INTO vault_files (vault_path, inode, device, hash, title, indexed_at)\n           VALUES ($1,$2,$3,$4,$5,$6) ON CONFLICT (vault_path) DO NOTHING`,\n        paramsFn: (r) => [r.vault_path, r.inode, r.device, r.hash, r.title, r.indexed_at],\n      })\n    );\n  }\n\n  if (sqliteHasTable(sqlite, \"vault_aliases\")) {\n    tables.push(\n      await copyByKey<Row>(pool, dryRun, {\n        table: \"vault_aliases\",\n        sqliteRows: all(\"vault_aliases\"),\n        keyFn: (r) => String(r.vault_path),\n        pgKeySql: \"SELECT vault_path AS k FROM vault_aliases\",\n        insertSql: `INSERT INTO vault_aliases (vault_path, canonical_path, inode, device)\n           VALUES ($1,$2,$3,$4) ON CONFLICT (vault_path) DO NOTHING`,\n        paramsFn: (r) => [r.vault_path, r.canonical_path, r.inode, r.device],\n      })\n    );\n  }\n\n  if (sqliteHasTable(sqlite, \"vault_links\")) {\n    tables.push(\n      await copyByKey<Row>(pool, dryRun, {\n        table: \"vault_links\",\n        sqliteRows: all(\"vault_links\"),\n        keyFn: (r) => `${r.source_path}\\u0001${r.target_raw}\\u0001${r.line_number}`,\n        pgKeySql: \"SELECT source_path || chr(1) || target_raw || chr(1) || line_number::text AS k FROM vault_links\",\n        insertSql: `INSERT INTO vault_links (source_path, target_raw, target_path, link_type, line_number)\n           VALUES ($1,$2,$3,$4,$5) ON CONFLICT (source_path, target_raw, line_number) DO NOTHING`,\n        paramsFn: (r) => [r.source_path, r.target_raw, r.target_path, r.link_type, r.line_number],\n      })\n    );\n  }\n\n  if (sqliteHasTable(sqlite, \"vault_name_index\")) {\n    tables.push(\n      await copyByKey<Row>(pool, dryRun, {\n        table: \"vault_name_index\",\n        sqliteRows: all(\"vault_name_index\"),\n        keyFn: (r) => `${r.name}\\u0001${r.vault_path}`,\n        pgKeySql: \"SELECT name || chr(1) || vault_path AS k FROM vault_name_index\",\n        insertSql: `INSERT INTO vault_name_index (name, vault_path) VALUES ($1,$2) ON CONFLICT (name, vault_path) DO NOTHING`,\n        paramsFn: (r) => [r.name, r.vault_path],\n      })\n    );\n  }\n\n  if (sqliteHasTable(sqlite, \"vault_health\")) {\n    tables.push(\n      await copyByKey<Row>(pool, dryRun, {\n        table: \"vault_health\",\n        sqliteRows: all(\"vault_health\"),\n        keyFn: (r) => String(r.vault_path),\n        pgKeySql: \"SELECT vault_path AS k FROM vault_health\",\n        insertSql: `INSERT INTO vault_health (vault_path, inbound_count, outbound_count, dead_link_count, is_orphan, computed_at)\n           VALUES ($1,$2,$3,$4,$5,$6) ON CONFLICT (vault_path) DO NOTHING`,\n        paramsFn: (r) => [r.vault_path, r.inbound_count, r.outbound_count, r.dead_link_count, r.is_orphan, r.computed_at],\n      })\n    );\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Spot check: 20 random rows per key-preserving table, field-by-field\n// ---------------------------------------------------------------------------\n\nexport interface SpotCheckMismatch {\n  table: string;\n  key: string;\n  field: string;\n  sqliteValue: unknown;\n  postgresValue: unknown;\n}\n\n/**\n * For kg_entities and projects (the two tables §4 step 6 calls out by name):\n * pull up to 20 random rows from SQLite and confirm every field matches the\n * Postgres row exactly. Returns the mismatches found (empty = all match).\n */\nasync function spotCheck(sqlite: Database, pool: Pool, table: string, keyCol: string): Promise<SpotCheckMismatch[]> {\n  const sample = sqlite\n    .prepare(`SELECT * FROM ${table} ORDER BY RANDOM() LIMIT 20`)\n    .all() as Array<Record<string, unknown>>;\n  const mismatches: SpotCheckMismatch[] = [];\n\n  for (const row of sample) {\n    const key = row[keyCol];\n    const r = await pool.query(`SELECT * FROM ${table} WHERE ${keyCol} = $1`, [key]);\n    const pgRow = r.rows[0] as Record<string, unknown> | undefined;\n    if (!pgRow) {\n      mismatches.push({ table, key: String(key), field: keyCol, sqliteValue: key, postgresValue: undefined });\n      continue;\n    }\n    for (const field of Object.keys(row)) {\n      if (!(field in pgRow)) continue;\n      const a = row[field];\n      const b = pgRow[field];\n      // BIGINT columns come back as strings from pg; normalise before compare.\n      const normA = typeof a === \"number\" ? String(a) : a;\n      const normB = typeof b === \"number\" ? String(b) : b;\n      if (String(normA ?? \"\") !== String(normB ?? \"\")) {\n        mismatches.push({ table, key: String(key), field, sqliteValue: a, postgresValue: b });\n      }\n    }\n  }\n  return mismatches;\n}\n\n// ---------------------------------------------------------------------------\n// Entry point\n// ---------------------------------------------------------------------------\n\nfunction refused(reason: string, dryRun: boolean): MigrationResult {\n  return { ok: false, dryRun, refused: reason, tables: [], embeddingsNote: \"\" };\n}\n\nexport async function migrateToPostgres(opts: MigrateOptions = {}): Promise<MigrationResult> {\n  const config = loadConfig();\n  const container = opts.container ?? DEFAULT_CONTAINER;\n  const dryRun = opts.dryRun ?? false;\n\n  if (!opts.skipConfigGate && config.storageBackend !== \"postgres\") {\n    return refused('config storageBackend is not \"postgres\" — set it first, then re-run', dryRun);\n  }\n  const pgConfig: PostgresConfig = opts.pgConfig ?? config.postgres ?? {};\n\n  const mountCheck = checkBindMount(container);\n  if (!mountCheck.ok) return refused(mountCheck.reason, dryRun);\n\n  // Read-only counting cannot lose a concurrent write the way a real copy\n  // can (see docs/design/postgres-only.md §6) — only the write path needs\n  // the daemon stopped.\n  if (!dryRun && !opts.allowRunning) {\n    const daemonCheck = await checkDaemonNotRunning(opts.socketPath ?? config.socketPath);\n    if (!daemonCheck.ok) return refused(daemonCheck.reason, dryRun);\n  }\n\n  const sqliteFederationPath = opts.sqliteFederationPath ?? federationDbPath();\n  const sqliteRegistryPath = opts.sqliteRegistryPath ?? registryDbPath();\n  if (!existsSync(sqliteFederationPath)) {\n    return refused(`federation SQLite db not found at ${sqliteFederationPath}`, dryRun);\n  }\n  if (!existsSync(sqliteRegistryPath)) {\n    return refused(`registry SQLite db not found at ${sqliteRegistryPath}`, dryRun);\n  }\n\n  let dumpPath: string | undefined;\n  if (!dryRun && !opts.skipDump) {\n    const stamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n    dumpPath = paiHomePath(\"backups\", `pg-pre-migration-${stamp}.dump`);\n    const dump = pgDump(container, pgConfig, dumpPath);\n    if (!dump.ok) return refused(dump.reason, dryRun);\n  }\n\n  // Idempotent DDL only (CREATE TABLE/DATABASE IF NOT EXISTS) — safe before a\n  // dry-run count too, since it never touches existing rows. Every other\n  // entry point (factory.ts) already calls this unconditionally.\n  const { PostgresBackend } = await import(\"./postgres.js\");\n  await PostgresBackend.ensureDatabase(pgConfig);\n\n  const sqlite = new BetterSqlite3(sqliteFederationPath, { readonly: true });\n  const registrySqlite =\n    sqliteRegistryPath === sqliteFederationPath ? sqlite : new BetterSqlite3(sqliteRegistryPath, { readonly: true });\n  const pool = new PgPool({\n    connectionString: resolvePgConnectionString(pgConfig),\n    max: 4,\n    connectionTimeoutMillis: pgConfig.connectionTimeoutMs ?? 5000,\n  });\n\n  const tables: TableReport[] = [];\n  let embeddingsNote = \"\";\n\n  try {\n    await copyRegistryTables(registrySqlite, pool, dryRun, tables);\n    await copyKgEntities(sqlite, pool, dryRun, tables);\n    const embeddingDimension = await resolvePaiChunksEmbeddingDimension(pool);\n    embeddingsNote = await copyMemoryFilesAndChunks(sqlite, pool, dryRun, tables, embeddingDimension);\n    await copyVaultTables(sqlite, pool, dryRun, tables);\n\n    let spotCheckOk = true;\n    if (!dryRun) {\n      const kgMismatches = await spotCheck(sqlite, pool, \"kg_entities\", \"entity_id\");\n      const projectMismatches = await spotCheck(registrySqlite, pool, \"projects\", \"id\");\n      const mismatches = [...kgMismatches, ...projectMismatches];\n      spotCheckOk = mismatches.length === 0;\n      if (!spotCheckOk) {\n        embeddingsNote +=\n          ` SPOT CHECK FAILED: ${mismatches.length} field mismatch(es) — ` +\n          mismatches.slice(0, 5).map((m) => `${m.table}.${m.key}.${m.field}`).join(\", \");\n      }\n    }\n\n    // A dry run's \"missing\" count is the whole point of running it — it is\n    // not a failure. Only a real copy is expected to reach zero missing.\n    const missingTotal = tables.reduce((n, t) => n + t.missingAfter, 0);\n    return {\n      ok: dryRun ? true : missingTotal === 0 && spotCheckOk,\n      dryRun,\n      dumpPath,\n      tables,\n      embeddingsNote,\n    };\n  } finally {\n    sqlite.close();\n    if (registrySqlite !== sqlite) registrySqlite.close();\n    await pool.end();\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Report rendering\n// ---------------------------------------------------------------------------\n\nexport function renderReport(result: MigrationResult): string {\n  const lines: string[] = [];\n  if (result.refused) {\n    lines.push(`REFUSED: ${result.refused}`);\n    return lines.join(\"\\n\");\n  }\n\n  lines.push(result.dryRun ? \"Dry run — no writes, no dump.\" : \"Migration complete.\");\n  if (result.dumpPath) lines.push(`Rollback dump: ${result.dumpPath}`);\n  lines.push(\"\");\n  const header = [\"table\", \"sqlite_rows\", \"postgres_before\", \"copied\", \"missing_after\"];\n  lines.push(header.join(\"\\t\"));\n  for (const t of result.tables) {\n    lines.push([t.table, t.sqliteRows, t.postgresRowsBefore, t.copied, t.missingAfter].join(\"\\t\"));\n  }\n  lines.push(\"\");\n  lines.push(result.embeddingsNote);\n  lines.push(\"\");\n  if (result.dryRun) {\n    lines.push(\"Dry run complete — \\\"missing_after\\\" is how many rows a real run would copy.\");\n  } else {\n    lines.push(result.ok ? \"OK: no missing rows.\" : \"FAILED: missing rows after copy — see table above.\");\n  }\n  return lines.join(\"\\n\");\n}\n","/**\n * Raw SQL introspection for `pai db query|tables|schema` — the one place\n * outside the backend classes themselves allowed to reach for the\n * underlying engine handle, so cli/commands/db.ts never has to import\n * better-sqlite3/pg or open its own connection (it goes through the\n * process-wide backend from storage/factory.ts instead).\n */\n\nimport type { StorageBackend } from \"./interface.js\";\nimport type { SQLiteBackend } from \"./sqlite.js\";\nimport type { PostgresBackend } from \"./postgres.js\";\n\nexport interface TableResult {\n  columns: string[];\n  rows: unknown[][];\n}\n\nfunction asSqlite(backend: StorageBackend): SQLiteBackend {\n  return backend as unknown as SQLiteBackend;\n}\n\nfunction asPostgresPool(backend: StorageBackend) {\n  return (backend as unknown as PostgresBackend).getPool();\n}\n\nexport async function runDbQuery(backend: StorageBackend, sql: string): Promise<TableResult> {\n  if (backend.backendType === \"postgres\") {\n    const result = await asPostgresPool(backend).query(sql);\n    const columns = result.fields.map((f) => f.name);\n    const rows = result.rows.map((r: Record<string, unknown>) => columns.map((c) => r[c]));\n    return { columns, rows };\n  }\n  const db = asSqlite(backend).getRawDb();\n  const stmt = db.prepare(sql);\n  const raw = stmt.all() as Record<string, unknown>[];\n  if (raw.length === 0) return { columns: [], rows: [] };\n  const columns = Object.keys(raw[0]);\n  const rows = raw.map((r) => columns.map((c) => r[c]));\n  return { columns, rows };\n}\n\nexport async function listDbTables(backend: StorageBackend): Promise<string[]> {\n  if (backend.backendType === \"postgres\") {\n    const result = await asPostgresPool(backend).query<{ tablename: string }>(\n      \"SELECT tablename FROM pg_tables WHERE schemaname = 'public' ORDER BY tablename\"\n    );\n    return result.rows.map((r) => r.tablename);\n  }\n  const db = asSqlite(backend).getRawDb();\n  const rows = db\n    .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name\")\n    .all() as { name: string }[];\n  return rows.map((r) => r.name);\n}\n\nexport async function getDbTableSchema(backend: StorageBackend, table: string): Promise<TableResult> {\n  if (backend.backendType === \"postgres\") {\n    const result = await asPostgresPool(backend).query<{\n      column_name: string;\n      data_type: string;\n      is_nullable: string;\n      column_default: string | null;\n    }>(\n      `SELECT column_name, data_type, is_nullable, column_default\n       FROM information_schema.columns\n       WHERE table_schema = 'public' AND table_name = $1\n       ORDER BY ordinal_position`,\n      [table]\n    );\n    if (result.rows.length === 0) {\n      throw new Error(`Table not found in public schema: ${table}`);\n    }\n    const columns = [\"column\", \"type\", \"nullable\", \"default\"];\n    const rows = result.rows.map((r) => [\n      r.column_name,\n      r.data_type,\n      r.is_nullable === \"YES\" ? \"YES\" : \"NO\",\n      r.column_default ?? \"\",\n    ]);\n    return { columns, rows };\n  }\n\n  const db = asSqlite(backend).getRawDb();\n  // Validate table name — only allow alphanumeric / underscores\n  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(table)) {\n    throw new Error(`Invalid table name: ${table}`);\n  }\n  const rows = db.prepare(`PRAGMA table_info(${table})`).all() as {\n    cid: number;\n    name: string;\n    type: string;\n    notnull: number;\n    dflt_value: string | null;\n    pk: number;\n  }[];\n  if (rows.length === 0) {\n    throw new Error(`Table not found: ${table}`);\n  }\n  const columns = [\"cid\", \"name\", \"type\", \"notnull\", \"default\", \"pk\"];\n  const outRows = rows.map((r) => [\n    r.cid,\n    r.name,\n    r.type,\n    r.notnull ? \"NOT NULL\" : \"\",\n    r.dflt_value ?? \"\",\n    r.pk ? \"PK\" : \"\",\n  ]);\n  return { columns, rows: outRows };\n}\n","/**\n * pai db <sub-command>\n *\n * Quick database inspection commands for both SQLite and Postgres backends.\n *\n *   pai db query  <sql>   [--db sqlite|postgres]  [--json]\n *   pai db tables         [--db sqlite|postgres]\n *   pai db schema <table> [--db sqlite|postgres]\n */\n\nimport { ok, warn, err, dim, bold, header, renderTable } from \"../lib/utils.js\";\nimport { loadConfig } from \"../../daemon/config.js\";\nimport { migrateToPostgres, renderReport } from \"../../storage/migrate-to-postgres.js\";\nimport { createStorageBackend } from \"../../storage/factory.js\";\nimport { runDbQuery, listDbTables, getDbTableSchema } from \"../../storage/db-admin.js\";\nimport type { StorageBackend } from \"../../storage/interface.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype DbTarget = \"sqlite\" | \"postgres\";\n\ninterface CommonOpts {\n  db?: DbTarget;\n  json?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Backend-agnostic helpers — a one-off backend for the requested target\n// (independent of the process's configured backend, since this tool is for\n// inspecting either engine), created via storage/factory.ts's single entry\n// point so this file never opens its own better-sqlite3/pg connection.\n// ---------------------------------------------------------------------------\n\nasync function backendForTarget(target: DbTarget): Promise<StorageBackend> {\n  const config = loadConfig();\n  return createStorageBackend({ ...config, storageBackend: target });\n}\n\n// ---------------------------------------------------------------------------\n// Output helpers\n// ---------------------------------------------------------------------------\n\nfunction printResult(\n  result: { columns: string[]; rows: unknown[][] },\n  json: boolean\n): void {\n  if (json) {\n    const objects = result.rows.map((row) => {\n      const obj: Record<string, unknown> = {};\n      result.columns.forEach((col, i) => {\n        obj[col] = row[i];\n      });\n      return obj;\n    });\n    console.log(JSON.stringify(objects, null, 2));\n    return;\n  }\n\n  if (result.rows.length === 0) {\n    console.log();\n    console.log(dim(\"  (no rows)\"));\n    console.log();\n    return;\n  }\n\n  const stringRows = result.rows.map((row) =>\n    row.map((cell) => {\n      if (cell === null || cell === undefined) return dim(\"NULL\");\n      if (typeof cell === \"object\") return JSON.stringify(cell);\n      return String(cell);\n    })\n  );\n\n  console.log();\n  console.log(renderTable(result.columns, stringRows));\n  console.log();\n  console.log(dim(`  ${result.rows.length} row(s)`));\n  console.log();\n}\n\n// ---------------------------------------------------------------------------\n// pai db query\n// ---------------------------------------------------------------------------\n\nexport async function cmdQuery(\n  sql: string,\n  opts: CommonOpts\n): Promise<number> {\n  const target: DbTarget = opts.db ?? \"sqlite\";\n  console.log();\n  console.log(\n    header(`  Query [${target}]`) + dim(`  ${sql.slice(0, 80)}${sql.length > 80 ? \"…\" : \"\"}`)\n  );\n\n  try {\n    let result: { columns: string[]; rows: unknown[][] };\n    const backend = await backendForTarget(target);\n    try {\n      result = await runDbQuery(backend, sql);\n    } finally {\n      await backend.close();\n    }\n    printResult(result, opts.json ?? false);\n    return 0;\n  } catch (e) {\n    const msg = e instanceof Error ? e.message : String(e);\n    console.error(err(`  ${msg}`));\n    return 1;\n  }\n}\n\n// ---------------------------------------------------------------------------\n// pai db tables\n// ---------------------------------------------------------------------------\n\nexport async function cmdTables(opts: CommonOpts): Promise<number> {\n  const target: DbTarget = opts.db ?? \"sqlite\";\n  console.log();\n  console.log(header(`  Tables [${target}]`));\n  console.log();\n\n  try {\n    let tables: string[];\n    const backend = await backendForTarget(target);\n    try {\n      tables = await listDbTables(backend);\n    } finally {\n      await backend.close();\n    }\n\n    if (opts.json) {\n      console.log(JSON.stringify(tables, null, 2));\n      return 0;\n    }\n\n    if (tables.length === 0) {\n      console.log(dim(\"  (no tables found)\"));\n    } else {\n      for (const t of tables) {\n        console.log(`  ${bold(t)}`);\n      }\n    }\n    console.log();\n    console.log(dim(`  ${tables.length} table(s)`));\n    console.log();\n    return 0;\n  } catch (e) {\n    const msg = e instanceof Error ? e.message : String(e);\n    console.error(err(`  ${msg}`));\n    return 1;\n  }\n}\n\n// ---------------------------------------------------------------------------\n// pai db schema\n// ---------------------------------------------------------------------------\n\nexport async function cmdSchema(table: string, opts: CommonOpts): Promise<number> {\n  const target: DbTarget = opts.db ?? \"sqlite\";\n  console.log();\n  console.log(header(`  Schema: ${table} [${target}]`));\n\n  try {\n    let result: { columns: string[]; rows: unknown[][] };\n    const backend = await backendForTarget(target);\n    try {\n      result = await getDbTableSchema(backend, table);\n    } finally {\n      await backend.close();\n    }\n    printResult(result, opts.json ?? false);\n    return 0;\n  } catch (e) {\n    const msg = e instanceof Error ? e.message : String(e);\n    console.error(err(`  ${msg}`));\n    return 1;\n  }\n}\n\n// ---------------------------------------------------------------------------\n// pai db migrate-to-postgres\n// ---------------------------------------------------------------------------\n\nexport async function cmdMigrateToPostgres(opts: { dryRun?: boolean; skipDump?: boolean; allowRunning?: boolean }): Promise<number> {\n  const result = await migrateToPostgres({\n    dryRun: opts.dryRun,\n    skipDump: opts.skipDump,\n    allowRunning: opts.allowRunning,\n  });\n  console.log(renderReport(result));\n  return result.ok ? 0 : 1;\n}\n","/**\n * `pai db`: front end for the db commands.\n * Behaviour lives in src/cli/impl/db.js; this file declares names, flags and help.\n */\n\nimport type { CommandDef } from \"../define.js\";\nimport { cmdQuery, cmdTables, cmdSchema, cmdMigrateToPostgres } from \"../impl/db.js\";\n\nexport const dbSubcommands: CommandDef[] = [\n{\n  name: \"query\",\n  summary: \"Run a SQL query\",\n  description: \"Run a SQL query.\\n\\nRun a SQL query against the selected database\",\n  args: [{ name: \"SQL\", summary: \"The SQL query, in quotes\", required: true }],\n  options: [{ name: \"db\", type: \"string\", value: \"TARGET\", summary: \"Database target: sqlite (default) or postgres\" }, { name: \"json\", type: \"boolean\", summary: \"Output results as JSON array\" }],\n  examples: [\"pai db query \\\"SELECT count(*) FROM projects\\\"\",\"pai db query \\\"SELECT slug FROM projects LIMIT 5\\\" --json\"],\n  seeAlso: [\"pai db tables\"],\n  run: (ctx) => cmdQuery(String(ctx.args.SQL), { db: ctx.opts.db as \"sqlite\" | \"postgres\" | undefined, json: ctx.opts.json === true }),\n},\n{\n  name: \"tables\",\n  summary: \"List the tables\",\n  description: \"List the tables.\\n\\nList all tables in the selected database\",\n  options: [{ name: \"db\", type: \"string\", value: \"TARGET\", summary: \"Database target: sqlite (default) or postgres\" }, { name: \"json\", type: \"boolean\", summary: \"Output as JSON array\" }],\n  examples: [\"pai db tables\",\"pai db tables --json\"],\n  seeAlso: [\"pai db schema\",\"pai db query\"],\n  run: (ctx) => cmdTables({ db: ctx.opts.db as \"sqlite\" | \"postgres\" | undefined, json: ctx.opts.json === true }),\n},\n{\n  name: \"schema\",\n  summary: \"Show the columns of a table\",\n  description: \"Show the columns of a table.\",\n  args: [{ name: \"TABLE\", summary: \"Table name (see: pai db tables)\", required: true }],\n  options: [{ name: \"db\", type: \"string\", value: \"TARGET\", summary: \"Database target: sqlite (default) or postgres\" }, { name: \"json\", type: \"boolean\", summary: \"Output as JSON array\" }],\n  examples: [\"pai db schema projects\",\"pai db schema projects --json\"],\n  seeAlso: [\"pai db migrate-to-postgres\",\"pai db tables\"],\n  run: (ctx) => cmdSchema(String(ctx.args.TABLE), { db: ctx.opts.db as \"sqlite\" | \"postgres\" | undefined, json: ctx.opts.json === true }),\n},\n{\n  name: \"migrate-to-postgres\",\n  summary: \"One-time copy of the data from SQLite to PostgreSQL\",\n  description: \"One-time copy of the data from SQLite to PostgreSQL.\\n\\nOne-shot migration of kg_entities, registry tables, and memory/vault rows from SQLite to Postgres\",\n  options: [{ name: \"dry-run\", type: \"boolean\", summary: \"Preflight + counts only — no writes, no pg_dump\" }, { name: \"skip-dump\", type: \"boolean\", summary: \"Skip the pg_dump rollback artefact (tests only)\" }, { name: \"allow-running\", type: \"boolean\", summary: \"Skip the daemon-not-running refusal (tests only)\" }],\n  examples: [\"pai db migrate-to-postgres --dry-run\",\"pai db migrate-to-postgres --skip-dump\"],\n  seeAlso: [\"pai db schema\"],\n  run: (ctx) => cmdMigrateToPostgres({\n    dryRun: ctx.opts[\"dry-run\"] === true,\n    skipDump: ctx.opts[\"skip-dump\"] === true,\n    allowRunning: ctx.opts[\"allow-running\"] === true\n  }),\n},\n];\n\nexport const dbCommand: CommandDef = {\n  name: \"db\",\n  summary: \"Look into the PAI database (read only)\",\n  description: \"Look into the PAI database (read only).\",\n  examples: [\"pai db tables\",\"pai db schema registry_projects\"],\n  subcommands: dbSubcommands,\n};\n","/**\n * Who \"me\" is.\n *\n * One question is asked of this module: **is this address the user's own?**\n * It exists because the alternative is an assistant deciding that per message,\n * from context, at the moment it is about to act — and \"that looks like his\n * address\" is not a judgement worth re-making every time. Here it is a lookup\n * against a list the user maintains.\n *\n * The rule the list serves: a mail whose recipients are all the user's own may\n * be sent; anything else is a draft for review. So a false positive here means\n * mail leaving without review. Everything below is written to fail closed —\n * an empty list means nothing is self-addressed, an unparseable address is not\n * self-addressed, and no pattern is ever inferred.\n *\n * Aliases are NOT matched by pattern, deliberately. `first.last+x@example.com`\n * and `owner.name@example.com` are the same mailbox, and Gmail also ignores\n * dots — but the general forms of those rules (\"same local part before a plus\",\n * \"same domain\") also match addresses belonging to other people. Gmail's own\n * normalisation is Gmail's; other providers treat `+` and `.` as ordinary\n * characters, and `first.last@company.com` is a different person from\n * `firstlast@company.com`. Anything that should count gets listed.\n */\n\n// ---------------------------------------------------------------------------\n// Normalisation\n// ---------------------------------------------------------------------------\n\n/**\n * Reduce an address to a comparable form: trimmed, unwrapped, lowercased.\n *\n * Handles `Display Name <addr@example.com>` because recipient fields carry that\n * form routinely. Returns null when there is nothing usable, which callers\n * treat as \"not the user's\" rather than as an error — an address that cannot be\n * parsed is one whose ownership cannot be established.\n */\nexport function normalizeEmail(raw: string | null | undefined): string | null {\n  if (!raw) return null;\n\n  let value = raw.trim();\n  if (!value) return null;\n\n  const angled = value.match(/<([^>]*)>/);\n  if (angled) value = angled[1].trim();\n\n  value = value.toLowerCase();\n\n  // Must look like an address at all: exactly one @, with something either side\n  // and a dot in the domain. This is a sanity check, not validation — its job\n  // is to reject junk before it can be compared, not to police correctness.\n  if (!/^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(value)) return null;\n\n  return value;\n}\n\n// ---------------------------------------------------------------------------\n// Ownership\n// ---------------------------------------------------------------------------\n\nexport interface IdentityLike {\n  selfEmails?: string[];\n  deliverTo?: string;\n  sendingAccount?: string;\n}\n\n/** Is this address one the user has declared as their own? */\nexport function isSelfAddress(address: string, identity: IdentityLike | undefined): boolean {\n  const candidate = normalizeEmail(address);\n  if (!candidate) return false;\n\n  const own = (identity?.selfEmails ?? [])\n    .map(normalizeEmail)\n    .filter((e): e is string => e !== null);\n\n  return own.includes(candidate);\n}\n\n/**\n * May a mail to these recipients be sent without review?\n *\n * Every recipient must be the user's own — To, Cc and Bcc together, since a\n * single outsider on Bcc is still an outsider. An empty recipient list is not\n * permission: there is nothing to check, so it is refused.\n *\n * Returns the offending addresses rather than a bare false, so the caller can\n * say which recipient forced the draft instead of reporting a flat refusal.\n */\nexport interface SendDecision {\n  allowed: boolean;\n  /** Recipients that are not the user's own. Empty when allowed. */\n  foreign: string[];\n  reason: string;\n}\n\nexport function maySendWithoutReview(\n  recipients: Array<string | null | undefined>,\n  identity: IdentityLike | undefined\n): SendDecision {\n  const listed = recipients.filter((r): r is string => Boolean(r && r.trim()));\n\n  if (listed.length === 0) {\n    return { allowed: false, foreign: [], reason: \"No recipients to check\" };\n  }\n\n  if (!identity?.selfEmails?.length) {\n    return {\n      allowed: false,\n      foreign: listed,\n      reason:\n        \"No identity.selfEmails configured — nothing counts as your own address yet. \" +\n        \"Run `pai identity add <email>`.\",\n    };\n  }\n\n  const foreign = listed.filter((r) => !isSelfAddress(r, identity));\n\n  if (foreign.length > 0) {\n    return {\n      allowed: false,\n      foreign,\n      reason: `Not your own address: ${foreign.join(\", \")} — draft for review instead`,\n    };\n  }\n\n  return { allowed: true, foreign: [], reason: \"All recipients are your own address\" };\n}\n\n// ---------------------------------------------------------------------------\n// Delivery reachability\n// ---------------------------------------------------------------------------\n\n/**\n * Would a mail from `sendingAccount` to `deliverTo` actually reach an inbox?\n *\n * Gmail files a message sent from an account to itself, or to one of its own\n * domain aliases, under Sent only — it never appears in the inbox. The send\n * reports success, so the failure is invisible and indistinguishable from\n * delivery. This cost a real digest on 2026-08-01: owner@example.ch →\n * owner@example.de, sent cleanly, never seen.\n *\n * Detecting it in general is not possible from the addresses alone — whether\n * two addresses are the same Google account is a fact about the account, not\n * about their spelling. So this reports what it can see and says so plainly:\n * an exact match is certain, a shared domain is suspicious and worth warning\n * about, and anything else is unknown rather than fine.\n */\nexport type Reachability = \"unreachable\" | \"suspect\" | \"unknown\";\n\nexport interface ReachabilityVerdict {\n  verdict: Reachability;\n  reason: string;\n}\n\nexport function checkDeliveryReachability(\n  deliverTo: string | undefined,\n  sendingAccount: string | undefined,\n  sendingAccountAliases: string[] = []\n): ReachabilityVerdict {\n  const to = normalizeEmail(deliverTo);\n  const from = normalizeEmail(sendingAccount);\n\n  if (!to || !from) {\n    return { verdict: \"unknown\", reason: \"Delivery address or sending account not configured\" };\n  }\n\n  if (to === from) {\n    return {\n      verdict: \"unreachable\",\n      reason:\n        `${to} is the sending account itself. Gmail files self-sends under Sent only — ` +\n        `the send will report success and never reach the inbox.`,\n    };\n  }\n\n  // The declared alias list is the only reliable signal, and it is why the list\n  // exists. The case that actually cost a digest was owner@example.ch sending to\n  // owner@example.de — one Google account behind two DIFFERENT domains, which no\n  // comparison of the addresses themselves can reveal. Whether two addresses\n  // are one mailbox is a fact about the account, not about their spelling.\n  const aliases = sendingAccountAliases\n    .map(normalizeEmail)\n    .filter((e): e is string => e !== null);\n\n  if (aliases.includes(to)) {\n    return {\n      verdict: \"unreachable\",\n      reason:\n        `${to} is a declared alias of the sending account (${from}). The send will report ` +\n        `success and never reach the inbox. Deliver by adding the INBOX label to the ` +\n        `written message instead.`,\n    };\n  }\n\n  const toDomain = to.slice(to.indexOf(\"@\") + 1);\n  const fromDomain = from.slice(from.indexOf(\"@\") + 1);\n\n  if (toDomain === fromDomain) {\n    return {\n      verdict: \"suspect\",\n      reason:\n        `${to} shares a domain with the sending account (${from}). If it is an alias of the ` +\n        `same account the send will silently not arrive. Deliver by adding the INBOX label ` +\n        `to the written message rather than relying on the send path.`,\n    };\n  }\n\n  // Deliberately not \"fine\". Cross-domain aliases of one account look exactly\n  // like this and are undetectable here — which is why label-based delivery is\n  // the safe default even when this returns unknown.\n  return {\n    verdict: \"unknown\",\n    reason:\n      \"Separate domains and not a declared alias — no suppression predictable from the \" +\n      \"addresses alone, which is not the same as delivery being guaranteed.\",\n  };\n}\n","/**\n * `pai identity` — declare who \"me\" is.\n *\n * The list this edits is an allowlist for actions that reach the outside world\n * without review, outbound mail being the case it was built for. Adding an\n * address widens what can be sent unreviewed, so every mutation here says what\n * it changed and the command warns rather than staying quiet when a setting\n * looks like it will not work.\n *\n * Edits the same PAI config file (`pai config path`) the daemon reads, preserving\n * everything else in the file — including the `_comment` keys a user may have\n * left for themselves.\n */\n\nimport { readMainConfigRaw, writeMainConfigRaw, paiConfigFilePath } from \"../../daemon/config.js\";\nimport {\n  normalizeEmail,\n  checkDeliveryReachability,\n  type IdentityLike,\n} from \"../../identity/index.js\";\nimport { ok, err, warn, dim, bold } from \"../lib/utils.js\";\n\ninterface ConfigShape {\n  identity?: IdentityLike & { sendingAccountAliases?: string[] };\n  [key: string]: unknown;\n}\n\n/** Null after printing the error: the caller exits 1. */\nfunction readConfig(): ConfigShape | null {\n  try {\n    return readMainConfigRaw() as ConfigShape;\n  } catch (e) {\n    console.error(err(\"pai identity: \") + `Could not read ${paiConfigFilePath()}: ${String(e)}`);\n    return null;\n  }\n}\n\n/** Writes through writeMainConfigRaw — comment-preserving into config.yaml\n *  when it exists, else atomic into config.json. */\nfunction writeConfig(config: ConfigShape): void {\n  writeMainConfigRaw(config);\n}\n\nfunction identityOf(config: ConfigShape) {\n  return config.identity ?? { selfEmails: [] };\n}\n\nfunction reportReachability(id: IdentityLike & { sendingAccountAliases?: string[] }): void {\n  const v = checkDeliveryReachability(\n    id.deliverTo,\n    id.sendingAccount,\n    id.sendingAccountAliases ?? []\n  );\n  if (v.verdict === \"unreachable\") {\n    console.log(err(\"  Unreachable: \") + v.reason);\n  } else if (v.verdict === \"suspect\") {\n    console.log(warn(\"  Suspect: \") + v.reason);\n  }\n}\n\nexport function identityShow(): number {\n  const config = readConfig();\n  if (!config) return 1;\n  const id = identityOf(config);\n  const emails = id.selfEmails ?? [];\n\n  console.log();\n  console.log(bold(\"  Identity\"));\n  console.log();\n  console.log(`  Deliver to:       ${id.deliverTo ?? dim(\"not set\")}`);\n  console.log(`  Sending account:  ${id.sendingAccount ?? dim(\"not set\")}`);\n  console.log();\n\n  if (emails.length === 0) {\n    console.log(warn(\"  No self addresses configured.\"));\n    console.log(\n      dim(\"  Nothing counts as your own address, so every mail is drafts-only.\")\n    );\n    console.log(dim(\"  Add one with: pai identity add <email>\"));\n  } else {\n    console.log(bold(`  Your own addresses (${emails.length}):`));\n    for (const e of emails) console.log(`    ${e}`);\n    console.log();\n    console.log(\n      dim(\"  Mail addressed only to these may be sent; anything else is a draft.\")\n    );\n  }\n  console.log();\n  reportReachability(id);\n  console.log();\n  return 0;\n}\n\nexport function identityAdd(email: string): number {\n  const normalized = normalizeEmail(email);\n  if (!normalized) {\n    console.error(err(\"pai identity: \") + `Not a usable email address: ${email}`);\n    return 1;\n  }\n\n  const config = readConfig();\n  if (!config) return 1;\n  const id = identityOf(config);\n  const emails = (id.selfEmails ?? []).slice();\n\n  if (emails.map(normalizeEmail).includes(normalized)) {\n    console.log(dim(`  ${normalized} is already listed. Nothing changed.`));\n    return 0;\n  }\n\n  emails.push(normalized);\n  config.identity = { ...id, selfEmails: emails };\n  writeConfig(config);\n\n  console.log(ok(\"Added. \") + `${normalized} now counts as your own address.`);\n  console.log(dim(`  Mail addressed only to your own addresses may now be sent unreviewed.`));\n  return 0;\n}\n\nexport function identityRemove(email: string): number {\n  const normalized = normalizeEmail(email);\n  const config = readConfig();\n  if (!config) return 1;\n  const id = identityOf(config);\n  const before = id.selfEmails ?? [];\n  const after = before.filter((e) => normalizeEmail(e) !== normalized);\n\n  if (after.length === before.length) {\n    console.log(dim(`  ${email} was not listed. Nothing changed.`));\n    return 0;\n  }\n\n  config.identity = { ...id, selfEmails: after };\n  writeConfig(config);\n  console.log(ok(\"Removed. \") + `${email} is no longer treated as your own.`);\n  return 0;\n}\n\nexport function identitySetDelivery(email: string): number {\n  const normalized = normalizeEmail(email);\n  if (!normalized) {\n    console.error(err(\"pai identity: \") + `Not a usable email address: ${email}`);\n    return 1;\n  }\n\n  const config = readConfig();\n  if (!config) return 1;\n  const id = identityOf(config);\n  config.identity = { ...id, deliverTo: normalized };\n  writeConfig(config);\n\n  console.log(ok(\"Delivery address set. \") + normalized);\n\n  // Warn immediately rather than at send time. A delivery address that\n  // cannot arrive fails silently — the send reports success — so the only\n  // useful moment to say so is when it is being configured.\n  reportReachability(config.identity);\n\n  if (!(id.selfEmails ?? []).map(normalizeEmail).includes(normalized)) {\n    console.log(\n      warn(\"  Note: \") +\n        `${normalized} is not in your self-address list, so mail to it would still be ` +\n        `drafted rather than sent. Run: pai identity add ${normalized}`\n    );\n  }\n  return 0;\n}\n","/**\n * `pai identity`: front end for the identity commands.\n * Behaviour lives in src/cli/impl/identity.js; this file declares names, flags and help.\n */\n\nimport type { CommandDef } from \"../define.js\";\nimport { identityShow, identityAdd, identityRemove, identitySetDelivery } from \"../impl/identity.js\";\n\nexport const identitySubcommands: CommandDef[] = [\n{\n  name: \"show\",\n  summary: \"Show your addresses and where mail is delivered\",\n  description: \"Show your addresses and where mail is delivered.\",\n  examples: [\"pai identity show\",\"pai identity show\"],\n  seeAlso: [\"pai identity add\"],\n  run: (ctx) => identityShow(),\n},\n{\n  name: \"add\",\n  summary: \"Add an address that is yours\",\n  description: \"Add an address that is yours.\\n\\nDeclare an address as your own (widens what can be sent unreviewed)\",\n  args: [{ name: \"EMAIL\", summary: \"Email address\", required: true }],\n  examples: [\"pai identity add me@example.com\",\"pai identity show\"],\n  seeAlso: [\"pai identity remove\",\"pai identity show\"],\n  run: (ctx) => identityAdd(String(ctx.args.EMAIL)),\n},\n{\n  name: \"remove\",\n  summary: \"Stop treating an address as yours\",\n  description: \"Stop treating an address as yours.\",\n  args: [{ name: \"EMAIL\", summary: \"Email address\", required: true }],\n  examples: [\"pai identity remove me@example.com\",\"pai identity show\"],\n  seeAlso: [\"pai identity set-delivery\",\"pai identity add\"],\n  run: (ctx) => identityRemove(String(ctx.args.EMAIL)),\n},\n{\n  name: \"set-delivery\",\n  summary: \"Choose where digests and \\\"mail me\\\" messages go\",\n  description: \"Choose where digests and \\\"mail me\\\" messages go.\",\n  args: [{ name: \"EMAIL\", summary: \"Email address\", required: true }],\n  examples: [\"pai identity set-delivery me@example.com\",\"pai identity show\"],\n  seeAlso: [\"pai identity remove\"],\n  run: (ctx) => identitySetDelivery(String(ctx.args.EMAIL)),\n},\n];\n\nexport const identityCommand: CommandDef = {\n  name: \"identity\",\n  summary: \"Say who you are, so mail to you can be told from mail to others\",\n  description: \"Say who you are, so mail to you can be told from mail to others.\",\n  examples: [\"pai identity show\",\"pai identity add me@example.com\"],\n  subcommands: identitySubcommands,\n};\n","/**\n * pai issue claim|release|claims — mark forge issues as being worked on\n * (assignee \"claude\" + label \"in-progress\"). Never closes an issue.\n */\nimport { err } from \"../lib/utils.js\";\nimport { formatStale, loadStatusConfig, upsertSection, writeSectionToForge } from \"../../session/forge-sink.js\";\nimport { issueClaim, issueClaims, issueStale } from \"../../session/issue-claims.js\";\n\nfunction num(s: string): number {\n  const n = Number(s.replace(/^#/, \"\"));\n  if (!Number.isInteger(n) || n < 1) throw new Error(`not an issue number: ${s}`);\n  return n;\n}\n\n/** Runs f; a thrown error prints as `pai issue: <message>` and exits 1. */\nfunction run(f: () => void): number {\n  try {\n    f();\n    return 0;\n  } catch (e) {\n    console.error(err(\"pai issue: \") + (e instanceof Error ? e.message : String(e)));\n    return 1;\n  }\n}\n\nfunction claimOrRelease(n: string, add: boolean, opts: { dryRun?: boolean }): number {\n  return run(() => {\n    const r = issueClaim(process.cwd(), num(n), add, { dryRun: opts.dryRun });\n    if (opts.dryRun) console.log(`DRY RUN (nothing sent): ${r.message}`);\n    else console.log(r.message);\n    for (const p of r.planned) console.log(`  would ${p}`);\n  });\n}\n\nexport function issueClaimCmd(n: string, opts: { dryRun?: boolean }): number {\n  return claimOrRelease(n, true, opts);\n}\n\nexport function issueReleaseCmd(n: string, opts: { dryRun?: boolean }): number {\n  return claimOrRelease(n, false, opts);\n}\n\nexport function issueClaimsCmd(): number {\n  return run(() => {\n    const rows = issueClaims(process.cwd());\n    if (!rows.length) console.log(\"no claimed issues\");\n    for (const r of rows) console.log(`#${r.issue}  ${r.title}  [${r.assignees.join(\", \")}]`);\n  });\n}\n\nexport function issueStaleCmd(opts: { days?: string; json?: boolean; dryRun?: boolean }): number {\n  return run(() => {\n    const raw = opts.days ?? \"7\";\n    const days = Number(raw);\n    if (!Number.isFinite(days) || days < 0) throw new Error(`bad --days: ${raw}`);\n    const rows = issueStale(process.cwd(), days);\n    console.log(opts.json ? JSON.stringify(rows, null, 2) : formatStale(rows));\n    if (opts.dryRun) {\n      const w = writeSectionToForge(process.cwd(), loadStatusConfig(process.cwd()), formatStale(rows), \"stale\", { dryRun: true });\n      console.log(`\\nDRY RUN: would write into handover #${w.issue}:\\n${upsertSection(\"\", formatStale(rows), \"stale\")}`);\n    }\n  });\n}\n","/**\n * `pai issue`: front end for the issue commands.\n * Behaviour lives in src/cli/impl/issue.js; this file declares names, flags and help.\n */\n\nimport type { CommandDef } from \"../define.js\";\nimport { issueClaimCmd, issueReleaseCmd, issueClaimsCmd, issueStaleCmd } from \"../impl/issue.js\";\n\nexport const issueSubcommands: CommandDef[] = [\n{\n  name: \"claim\",\n  summary: \"Mark issue N as yours (assignee and in-progress label)\",\n  description: \"Mark issue N as yours (assignee and in-progress label).\\n\\nClaim forge issue <n> of this project (assignee claude + label in-progress)\",\n  args: [{ name: \"N\", summary: \"Issue number\", required: true }],\n  options: [{ name: \"dry-run\", type: \"boolean\", summary: \"Print the requests that would modify the issue; send none\" }],\n  examples: [\"pai issue claim 12\",\"pai issue claim 12 --dry-run\"],\n  seeAlso: [\"pai issue release\"],\n  run: (ctx) => issueClaimCmd(String(ctx.args.N), { dryRun: ctx.opts[\"dry-run\"] === true }),\n},\n{\n  name: \"release\",\n  summary: \"Give issue N back\",\n  description: \"Give issue N back.\\n\\nRelease forge issue <n> of this project (assignee claude + label in-progress)\",\n  args: [{ name: \"N\", summary: \"Issue number\", required: true }],\n  options: [{ name: \"dry-run\", type: \"boolean\", summary: \"Print the requests that would modify the issue; send none\" }],\n  examples: [\"pai issue release 12\",\"pai issue release 12 --dry-run\"],\n  seeAlso: [\"pai issue claims\",\"pai issue claim\"],\n  run: (ctx) => issueReleaseCmd(String(ctx.args.N), { dryRun: ctx.opts[\"dry-run\"] === true }),\n},\n{\n  name: \"claims\",\n  summary: \"List the issues of this project marked in-progress\",\n  description: \"List the issues of this project marked in-progress.\\n\\nList open issues of this project carrying the in-progress label\",\n  examples: [\"pai issue claims\",\"pai issue claims\"],\n  seeAlso: [\"pai issue stale\",\"pai issue release\"],\n  run: (ctx) => issueClaimsCmd(),\n},\n{\n  name: \"stale\",\n  summary: \"List open issues whose work is already merged\",\n  description: \"List open issues whose work is already merged.\\n\\nList open issues that landed in the integration branch (merged branch named by number, or a 'fixes #N' commit subject; plain mentions do not count) or are idle (report only; never closes)\",\n  options: [{ name: \"days\", type: \"string\", value: \"N\", summary: \"Idle threshold in days\" }, { name: \"json\", type: \"boolean\", summary: \"JSON output\" }, { name: \"dry-run\", type: \"boolean\", summary: \"Also show the handover-issue section the daemon would write; send nothing\" }],\n  examples: [\"pai issue stale\",\"pai issue stale --days 14 --json\"],\n  seeAlso: [\"pai issue claims\"],\n  run: (ctx) => issueStaleCmd({ days: ctx.opts.days as string | undefined, json: ctx.opts.json === true, dryRun: ctx.opts[\"dry-run\"] === true }),\n},\n];\n\nexport const issueCommand: CommandDef = {\n  name: \"issue\",\n  summary: \"Claim and release forge issues for this project\",\n  description: \"Claim and release forge issues for this project.\",\n  examples: [\"pai issue claims\",\"pai issue claim 12\"],\n  subcommands: issueSubcommands,\n};\n","/**\n * pai mcp <sub-command>\n *\n * install  — Register the PAI MCP servers in ~/.claude.json\n * status   — Show whether the PAI MCP servers are registered and the binaries exist\n *\n * One server is managed: `pai` (memory/project tools, dist/daemon-mcp/index.mjs).\n * Registration is idempotent: an already-present entry is skipped\n * with a note, a missing one is added.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { ok, warn, err, dim, bold } from \"../lib/utils.js\";\nimport { readClaudeJson, writeClaudeJson } from \"../../config/claude-json.js\";\nimport { resolveFromModule } from \"../../module-paths.js\";\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\n/** Absolute path of a built entry under dist/ (chunk depth varies, so walk up — see module-paths). */\nfunction distEntry(entry: string): string {\n  return resolveFromModule(import.meta.url, `dist/${entry}`);\n}\n\ninterface McpServerSpec {\n  /** key under mcpServers in ~/.claude.json */\n  name: string;\n  /** dist/ relative entry path */\n  entry: string;\n  /** shown in install/status output */\n  label: string;\n  tools: string;\n}\n\nconst SERVERS: McpServerSpec[] = [\n  {\n    name: \"pai\",\n    entry: \"daemon-mcp/index.mjs\",\n    label: \"PAI MCP server\",\n    tools: \"memory_search, memory_get, memory_outline, project_info, project_list, session_list, registry_search\",\n  },\n];\n\n// ---------------------------------------------------------------------------\n// install\n// ---------------------------------------------------------------------------\n\nexport function mcpInstallCmd(): number {\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  let changed = false;\n\n  for (const spec of SERVERS) {\n    const bin = distEntry(spec.entry);\n\n    if (spec.name in servers) {\n      console.log(warn(`${spec.label} is already registered in ~/.claude.json as \"${spec.name}\".`));\n      console.log(dim(`  Entry: ${JSON.stringify(servers[spec.name])}`));\n      continue;\n    }\n\n    servers[spec.name] = {\n      command: \"node\",\n      args: [bin],\n    };\n    changed = true;\n\n    console.log(ok(`${spec.label} registered in ~/.claude.json as \"${spec.name}\".`));\n    console.log(dim(`  Binary: ${bin}`));\n    console.log(dim(\"\"));\n    console.log(dim(\"  Restart Claude Code to activate the tools:\"));\n    console.log(dim(`    ${spec.tools}`));\n\n    if (!existsSync(bin)) {\n      console.log();\n      console.log(warn(`  Note: MCP binary not found at ${bin}`));\n      console.log(dim(\"  Run `bun run build` to compile it first.\"));\n    }\n  }\n\n  if (!changed) {\n    console.log(dim(\"  Use `pai mcp status` to verify the configuration.\"));\n    return 0;\n  }\n\n  try {\n    writeClaudeJson(config);\n    return 0;\n  } catch (e) {\n    console.error(err(`Failed to write ~/.claude.json: ${e}`));\n    return 1;\n  }\n}\n\n// ---------------------------------------------------------------------------\n// status\n// ---------------------------------------------------------------------------\n\nexport function mcpStatusCmd(): number {\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  console.log();\n  console.log(bold(\"  PAI MCP Server Status\"));\n  console.log();\n\n  let allReady = true;\n\n  for (const spec of SERVERS) {\n    const bin = distEntry(spec.entry);\n    const registered = spec.name in servers;\n    const binExists = existsSync(bin);\n\n    if (registered) {\n      console.log(ok(`  ${spec.name}: registered in ~/.claude.json`));\n      console.log(dim(`    Config: ${JSON.stringify(servers[spec.name])}`));\n    } else {\n      console.log(warn(`  ${spec.name}: NOT registered in ~/.claude.json`));\n      console.log(dim(`    Run: pai mcp install`));\n    }\n\n    if (binExists) {\n      console.log(ok(`    Binary found: ${bin}`));\n    } else {\n      console.log(warn(`    Binary NOT found: ${bin}`));\n      console.log(dim(`      Run: bun run build`));\n    }\n\n    if (!registered || !binExists) allReady = false;\n    console.log();\n  }\n\n  console.log(\n    allReady\n      ? dim(\"  Status: READY — restart Claude Code to use the PAI tools\")\n      : warn(\"  Status: INCOMPLETE — run `pai mcp install` and/or `bun run build`\")\n  );\n\n  console.log();\n  return 0;\n}\n","/**\n * `pai mcp`: front end for the mcp commands.\n * Behaviour lives in src/cli/impl/mcp.js; this file declares names, flags and help.\n */\n\nimport type { CommandDef } from \"../define.js\";\nimport { mcpInstallCmd, mcpStatusCmd } from \"../impl/mcp.js\";\n\nexport const mcpSubcommands: CommandDef[] = [\n{\n  name: \"install\",\n  summary: \"Register the PAI MCP server with Claude Code\",\n  description: \"Register the PAI MCP server with Claude Code.\\n\\nRegister the PAI MCP server (pai) in ~/.claude.json (restart Claude Code to activate)\",\n  examples: [\"pai mcp install\",\"pai mcp status\"],\n  seeAlso: [\"pai mcp status\"],\n  run: (ctx) => mcpInstallCmd(),\n},\n{\n  name: \"status\",\n  summary: \"Show whether the PAI MCP server is registered\",\n  description: \"Show whether the PAI MCP server is registered.\\n\\nShow whether the PAI MCP server (pai) is registered and the binary exists\",\n  examples: [\"pai mcp status\",\"pai mcp status\"],\n  seeAlso: [\"pai mcp install\"],\n  run: (ctx) => mcpStatusCmd(),\n},\n];\n\nexport const mcpCommand: CommandDef = {\n  name: \"mcp\",\n  summary: \"Install the PAI tools for Claude Code\",\n  description: \"Install the PAI tools for Claude Code.\",\n  examples: [\"pai mcp status\",\"pai mcp install\"],\n  subcommands: mcpSubcommands,\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 { ok, warn, err, dim, bold, header } from \"../lib/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\nexport async function notifyStatusCmd(): Promise<number> {\n  const client = makeClient();\n  try {\n    const { config, activeChannels } = await client.getNotificationConfig();\n    printConfig(config, activeChannels);\n    return 0;\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    return 1;\n  }\n}\n\nexport async function notifySetCmd(opts: {\n  mode?: string;\n  ntfyUrl?: string;\n  ntfyPriority?: string;\n  whatsappRecipient?: string;\n  enableChannel?: string[];\n  disableChannel?: string[];\n}): Promise<number> {\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      return 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    return 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    return 0;\n  } catch (e) {\n    const msg = e instanceof Error ? e.message : String(e);\n    console.error(err(`  Failed: ${msg}`));\n    return 1;\n  }\n}\n\nexport async function notifyTestCmd(opts: { event?: string }): Promise<number> {\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    return 0;\n  } catch (e) {\n    const msg = e instanceof Error ? e.message : String(e);\n    console.error(err(`  Failed: ${msg}`));\n    return 1;\n  }\n}\n\nexport async function notifySendCmd(\n  event: string,\n  message: string,\n  opts: { title?: string }\n): Promise<number> {\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    return 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    return 0;\n  } catch (e) {\n    const msg = e instanceof Error ? e.message : String(e);\n    console.error(err(`  Failed: ${msg}`));\n    return 1;\n  }\n}\n","/**\n * `pai notify`: front end for the notify commands.\n * Behaviour lives in src/cli/impl/notify.js; this file declares names, flags and help.\n */\n\nimport type { CommandDef } from \"../define.js\";\nimport { notifyStatusCmd, notifySetCmd, notifyTestCmd, notifySendCmd } from \"../impl/notify.js\";\n\nexport const notifySubcommands: CommandDef[] = [\n{\n  name: \"status\",\n  summary: \"Show the notification mode and active channels\",\n  description: \"Show the notification mode and active channels.\\n\\nShow current notification mode and active channels\",\n  examples: [\"pai notify status\",\"pai notify status\"],\n  seeAlso: [\"pai notify get\"],\n  run: (ctx) => notifyStatusCmd(),\n},\n{\n  name: \"get\",\n  summary: \"Same as status\",\n  description: \"Same as status.\\n\\nShow current notification config (alias for status)\",\n  examples: [\"pai notify get\",\"pai notify status\"],\n  seeAlso: [\"pai notify set\",\"pai notify status\"],\n  run: (ctx) => notifyStatusCmd(),\n},\n{\n  name: \"set\",\n  summary: \"Change the notification mode or channels\",\n  description: \"Change the notification mode or channels.\\n\\nUpdate notification mode or channel configuration\",\n  args: [{ name: \"MORE\", summary: \"More values for the list option above\", variadic: true }],\n  options: [{ name: \"mode\", type: \"string\", value: \"MODE\", summary: \"Notification mode: auto | voice | whatsapp | ntfy | macos | cli | off\" }, { name: \"enable\", type: \"string\", value: \"CHANNEL\", summary: \"Enable one or more channels (several values allowed)\", repeatable: true }, { name: \"disable\", type: \"string\", value: \"CHANNEL\", summary: \"Disable one or more channels (several values allowed)\", repeatable: true }, { name: \"ntfy-url\", type: \"string\", value: \"URL\", summary: \"Set the ntfy.sh topic URL\" }, { name: \"ntfy-priority\", type: \"string\", value: \"LEVEL\", summary: \"Set ntfy priority: min | low | default | high | urgent\" }, { name: \"whatsapp-recipient\", type: \"string\", value: \"CONTACT\", summary: \"Set WhatsApp recipient\" }],\n  examples: [\"pai notify set --mode auto\",\"pai notify set --enable ntfy --disable whatsapp\"],\n  seeAlso: [\"pai notify test\",\"pai notify get\"],\n  run: (ctx) => notifySetCmd({\n    mode: ctx.opts.mode as string | undefined,\n    enableChannel: ctx.multi?.enable,\n    disableChannel: ctx.multi?.disable,\n    ntfyUrl: ctx.opts[\"ntfy-url\"] as string | undefined,\n    ntfyPriority: ctx.opts[\"ntfy-priority\"] as string | undefined,\n    whatsappRecipient: ctx.opts[\"whatsapp-recipient\"] as string | undefined,\n  }),\n},\n{\n  name: \"test\",\n  summary: \"Send a test notification\",\n  description: \"Send a test notification.\\n\\nSend a test notification through configured channels\",\n  options: [{ name: \"event\", type: \"string\", value: \"EVENT\", summary: \"Event type to test: error | progress | completion | info | debug\" }],\n  examples: [\"pai notify test\",\"pai notify test --event error\"],\n  seeAlso: [\"pai notify send\",\"pai notify set\"],\n  run: (ctx) => notifyTestCmd({ event: ctx.opts.event as string | undefined }),\n},\n{\n  name: \"send\",\n  summary: \"Send a notification with an event type and message\",\n  description: \"Send a notification with an event type and message.\\n\\nSend a notification with an explicit event type and message\",\n  args: [{ name: \"EVENT\", summary: \"Event type, e.g. info or error\", required: true }, { name: \"MESSAGE\", summary: \"Message text, in quotes\", required: true }],\n  options: [{ name: \"title\", type: \"string\", value: \"TITLE\", summary: \"Optional notification title\" }],\n  examples: [\"pai notify send info \\\"Build finished\\\"\",\"pai notify send error \\\"Build failed\\\" --title CI\"],\n  seeAlso: [\"pai notify test\"],\n  run: (ctx) => notifySendCmd(\n    String(ctx.args.EVENT),\n    String(ctx.args.MESSAGE),\n    { title: ctx.opts.title as string | undefined }\n  ),\n},\n];\n\nexport const notifyCommand: CommandDef = {\n  name: \"notify\",\n  summary: \"Notification settings: status, set, test, send\",\n  description: \"Notification settings: status, set, test, send.\",\n  examples: [\"pai notify status\",\"pai notify test\"],\n  subcommands: notifySubcommands,\n};\n","/**\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 {\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/lib/utils.js\";\nimport { unregistrableReason } from \"../registry/registrable.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 async function cmdPromote(opts: PromoteOptions): Promise<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  // ---- Refuse disposable locations ----\n  //\n  // Same guard as `pai project add`. Promoting a session INTO a worktree or a\n  // temp directory would create a project whose home is designed to be deleted.\n  const ephemeral = unregistrableReason(targetPath);\n  if (ephemeral) {\n    console.error(err(`Refusing to promote into ${targetPath}`));\n    console.error(dim(`  That is ${ephemeral}.`));\n    console.error(dim(`  Pick a durable location for the new project.`));\n    process.exit(1);\n  }\n\n  // ---- Check registry for conflicts ----\n\n  const { getRegistryBackend } = await import(\"../storage/factory.js\");\n  const registryBackend = await getRegistryBackend();\n\n  const encodedDir = encodeDir(targetPath);\n  const existing =\n    (await registryBackend.getProjectBySlug(slug)) ??\n    (await registryBackend.getProjectByRootPath(targetPath)) ??\n    (await registryBackend.getProjectByEncodedDir(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  await registryBackend.createProject({\n    slug,\n    displayName,\n    rootPath: targetPath,\n    encodedDir,\n    type: \"local\",\n    status: \"active\",\n    createdAt: ts,\n    updatedAt: ts,\n  });\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 * 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 { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport chalk from \"chalk\";\nimport { ok, warn, err, dim, bold, header, shortenPath, fmtDate, now, renderTable } from \"../../lib/utils.js\";\nimport type { ProjectRow, SessionConfig, ConfigOption } from \"./types.js\";\nimport { resolveIdentifier, requireProject, getProjectAliases, getProject } from \"./helpers.js\";\nimport { paiConfigFilePath } from \"../../../daemon/config.js\";\nimport { detectProject } from \"../detect.js\";\nimport { expandMcpNames } from \"../../../workers/mcp.js\";\nimport { readWorkersSection } from \"../../../workers/config.js\";\nimport { getRegistryBackend } from \"../../../storage/factory.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 = paiConfigFilePath();\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 = paiConfigFilePath();\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(dirname(configPath), { 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\n/** The registered project whose root_path covers `cwd` (default: process.cwd()). */\nasync function currentProject(cwd?: string): Promise<ProjectRow | undefined> {\n  const detected = await detectProject(cwd);\n  if (!detected) return undefined;\n  return getProject(detected.slug);\n}\n\nasync function saveSessionConfig(project: ProjectRow, config: SessionConfig): Promise<void> {\n  const backend = await getRegistryBackend();\n  await backend.updateProjectSessionConfig(project.id, JSON.stringify(config), now());\n}\n\nfunction splitNames(names: string[]): string[] {\n  return names.flatMap((n) => n.split(\",\").map((s) => s.trim()).filter(Boolean));\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 async function cmdName(\n  identifier: string,\n  shortname: string,\n  opts: { permission?: string }\n): Promise<void> {\n  const project = (await resolveIdentifier(identifier)) ?? (await requireProject(identifier));\n  const backend = await getRegistryBackend();\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.exitCode = 1;\n    return;\n  }\n\n  const conflictProject = await backend.getProjectBySlug(shortname, { excludeId: project.id });\n  if (conflictProject) {\n    console.error(err(`\"${shortname}\" is already a project slug.`));\n    process.exitCode = 1;\n    return;\n  }\n\n  const conflictAliasProjectId = await backend.resolveAlias(shortname);\n  if (conflictAliasProjectId !== null && conflictAliasProjectId !== project.id) {\n    console.error(err(`\"${shortname}\" is already used by another project.`));\n    process.exitCode = 1;\n    return;\n  }\n\n  if (conflictAliasProjectId === null) {\n    await backend.addAlias(shortname, project.id);\n  }\n\n  if (opts.permission) {\n    const existing = getSessionConfig(project);\n    const config = applyPreset(existing, opts.permission);\n    await backend.updateProjectSessionConfig(project.id, JSON.stringify(config), now());\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 async function cmdUnname(shortname: string): Promise<void> {\n  const backend = await getRegistryBackend();\n  const projectId = await backend.resolveAlias(shortname);\n\n  if (projectId === null) {\n    console.error(err(`No named project found: \"${shortname}\"`));\n    process.exitCode = 1;\n    return;\n  }\n\n  await backend.removeAlias(shortname);\n\n  const remaining = await backend.countAliasesForProject(projectId);\n\n  console.log(ok(`Removed name: ${bold(shortname)}`));\n  if (remaining === 0) {\n    console.log(dim(\"  Project has no remaining names.\"));\n  }\n}\n\nexport async function cmdNames(opts: { json?: boolean; all?: boolean }): Promise<void> {\n  // --all: includeUnnamed so projects WITHOUT a curated alias are included too\n  //        (for AIBroker's picker to search everything). Unnamed projects get\n  //        their slug as the launch key (pai project config resolves by slug).\n  const backend = await getRegistryBackend();\n  const rows = await backend.listNamedProjects({ includeUnnamed: opts.all });\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 ?? row.slug, // launch key: alias if named, else slug\n          names: row.name ? [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 if (row.name) {\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).filter(Boolean) as string[];\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.length ? allNames.join(', ') : row.slug),\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 async function cmdConfig(\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): Promise<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.exitCode = 1;\n    return;\n  }\n\n  const project = (await resolveIdentifier(identifier)) ?? (await requireProject(identifier));\n  const backend = await getRegistryBackend();\n  let config = getSessionConfig(project);\n\n  if (opts.reset) {\n    await backend.updateProjectSessionConfig(project.id, null, now());\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    await backend.updateProjectSessionConfig(project.id, JSON.stringify(config), now());\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    await backend.updateProjectSessionConfig(project.id, JSON.stringify(config), now());\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    await backend.updateProjectSessionConfig(project.id, JSON.stringify(config), now());\n    console.log(ok(`Config updated for ${bold(project.slug)}`));\n  }\n\n  const effective = { ...getGlobalDefaults(), ...config };\n  const aliases = await getProjectAliases(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// ---------------------------------------------------------------------------\n// MCP / tools pin for the interactive supervisor launch (pai worker run, no -p)\n// ---------------------------------------------------------------------------\n\n/**\n * `pai project mcp [--set names | --clear]` — the MCP servers this project's interactive\n * supervisor session loads. Operates on whichever registered project's\n * root_path covers `opts.cwd` (default: process.cwd()) — run it from inside\n * the project, same as `pai projects here`. Unset (the default) means every\n * configured server loads, today's behavior. Names are validated against\n * ~/.claude.json's mcpServers / workers.mcpSets the same way `--mcp` is.\n */\nexport async function cmdMcp(\n  names: string[],\n  opts: { clear?: boolean; set?: string; cwd?: string } = {}\n): Promise<void> {\n  if (names.length) {\n    console.error(err(\"use --set <names> to change the pin; `pai project mcp` shows it\"));\n    process.exitCode = 1;\n    return;\n  }\n  const project = await currentProject(opts.cwd);\n  if (!project) {\n    console.error(err(\"No registered project matches the current directory.\"));\n    console.error(dim(\"  Register it first: pai projects here <name>\"));\n    process.exitCode = 1;\n    return;\n  }\n  const config = getSessionConfig(project);\n\n  if (opts.clear) {\n    delete config.mcp;\n    await saveSessionConfig(project, config);\n    console.log(ok(`MCP servers unset for ${bold(project.slug)} — all servers load again.`));\n    return;\n  }\n\n  if (opts.set === undefined) {\n    console.log(config.mcp?.length ? config.mcp.join(\",\") : \"unset (all)\");\n    return;\n  }\n\n  const { workers } = readWorkersSection();\n  config.mcp = expandMcpNames(splitNames([opts.set]), workers); // unknown names fail fast\n  await saveSessionConfig(project, config);\n  console.log(ok(`MCP servers set for ${bold(project.slug)}: ${config.mcp.join(\", \")}`));\n}\n\n/**\n * `pai project tools [--set names | --clear]` — the built-in tool schemas this project's\n * interactive supervisor session loads (Read, Edit, Bash, …). Same cwd-based\n * project resolution and unset semantics as `cmdMcp`. Unlike MCP names, tool\n * names are not validated against a fixed list — the harness itself rejects\n * an unrecognised one at launch.\n */\nexport async function cmdTools(\n  names: string[],\n  opts: { clear?: boolean; set?: string; cwd?: string } = {}\n): Promise<void> {\n  if (names.length) {\n    console.error(err(\"use --set <names> to change the pin; `pai project tools` shows it\"));\n    process.exitCode = 1;\n    return;\n  }\n  const project = await currentProject(opts.cwd);\n  if (!project) {\n    console.error(err(\"No registered project matches the current directory.\"));\n    console.error(dim(\"  Register it first: pai projects here <name>\"));\n    process.exitCode = 1;\n    return;\n  }\n  const config = getSessionConfig(project);\n\n  if (opts.clear) {\n    delete config.tools;\n    await saveSessionConfig(project, config);\n    console.log(ok(`Tools unset for ${bold(project.slug)} — all built-in tools load again.`));\n    return;\n  }\n\n  if (opts.set === undefined) {\n    console.log(config.tools?.length ? config.tools.join(\",\") : \"unset (all)\");\n    return;\n  }\n\n  config.tools = splitNames([opts.set]);\n  await saveSessionConfig(project, config);\n  console.log(ok(`Tools set for ${bold(project.slug)}: ${config.tools.join(\", \")}`));\n}\n","/**\n * relocate.ts — find a registered project whose ancestor directory was renamed.\n *\n * The registry stores absolute root paths. Rename any directory ABOVE a project\n * and every project underneath it goes missing at once — `existsSync` fails, and\n * the health check reported them as dead and offered to archive them. Renaming\n * `Ideaverse` to `🧠 Ideaverse` orphaned a whole subtree that way: 32 entries, all\n * still on disk, none of them findable.\n *\n * The old suggestion logic could not see this. It took the project's BASENAME and\n * looked for it in four hardcoded directories (`~/dev`, `~/dev/ai`, `~/Desktop`,\n * `~/Projects`), so it could only recognise \"the leaf moved into a place I already\n * know about\". A renamed ancestor leaves the leaf exactly where it was.\n *\n * So walk down from the root while the path still exists — that lands on the\n * deepest surviving ancestor, which is precisely where the rename happened — then\n * match the remaining segments by NORMALISED name. Normalising strips the emoji,\n * the space and the case, so `🧠 Ideaverse` and `Ideaverse` both reduce to\n * \"ideaverse\". The general case is \"an ancestor was renamed decoratively\", which\n * is the whole class of failure rather than this one instance of it.\n *\n * Algorithm and the measurements behind it come from the AIBroker session, which\n * probed it and handed it over rather than implementing it in a file that was not\n * theirs.\n */\n\nimport { existsSync, readdirSync, statSync, realpathSync } from \"node:fs\";\nimport { join, sep, basename } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n/**\n * A directory name reduced to what a rename is unlikely to have changed.\n *\n * NFC first because macOS hands back decomposed Unicode: an \"ä\" typed in one\n * place and read from a directory listing in another are different byte\n * sequences, and comparing them raw fails for reasons no one can see. Then case\n * and every non-alphanumeric are dropped, which is what makes `🧠 Ideaverse`\n * match `Ideaverse` — and, unavoidably, what makes `my-project` match\n * `my project`. That looseness is the point, and the uniqueness rule below is\n * what keeps it safe.\n */\nexport function norm(s: string): string {\n  return s\n    .normalize(\"NFC\")\n    .toLowerCase()\n    .replace(/[^\\p{Letter}\\p{Number}]+/gu, \"\");\n}\n\n/** Hidden by Unix convention — tool state rather than a project directory. */\nfunction isHidden(name: string): boolean {\n  return name.startsWith(\".\");\n}\n\n/**\n * Drop a leading ordering prefix: `20 - Webseiten` -> `Webseiten`.\n *\n * This vault numbers directories to force sort order — `04 - Ablage`,\n * `08 - Others`, `01 - Base Setup`, `20 - Webseiten` — and adding or removing that\n * prefix is a rename of exactly the same kind as adding an emoji. `norm()` alone\n * cannot see it, because the digits survive normalisation: \"webseiten\" against\n * \"20webseiten\".\n *\n * Found because `Client.md` links to `Infrastructure/20 - Webseiten` while the\n * registry has a dead entry for plain `Infrastructure/Webseiten`. The note files\n * knew where it went.\n *\n * Deliberately NOT a general suffix match, and the reason is worth spelling out\n * because \"why not just match on the suffix\" is the obvious next question.\n *\n * `Client/Infrastructure/` is wall-to-wall numbered: `00 - Migration Analysis`,\n * `01 - Base Setup`, `02 - Storage Setup`, `03 - Network Setup`, and so on. A\n * suffix rule on a wanted `Setup` hits eight of them at once — so the uniqueness\n * rule below would veto it, and the danger would never show itself. The one that\n * bites is a wanted `Analysis` against `00 - Migration Analysis`: a single hit,\n * therefore silently relocated, therefore wrong, and uniqueness cannot save you\n * because there is nothing ambiguous about it.\n *\n * That is a failure mode this file's own safety rule would have HIDDEN rather than\n * caught. Only a leading run of digits with optional separator comes off.\n */\nfunction stripOrderingPrefix(name: string): string {\n  return name.replace(/^\\d+\\s*[-._)]?\\s*/, \"\");\n}\n\n/**\n * The names under which a directory can be recognised.\n *\n * Both spellings are offered from both sides, so the prefix can have been added\n * OR removed since the path was registered.\n */\nfunction matchKeys(name: string): Set<string> {\n  const keys = new Set<string>();\n  const bare = norm(name);\n  if (bare) keys.add(bare);\n  const stripped = norm(stripOrderingPrefix(name));\n  if (stripped) keys.add(stripped);\n  return keys;\n}\n\nfunction shareAKey(a: Set<string>, b: Set<string>): boolean {\n  for (const k of a) if (b.has(k)) return true;\n  return false;\n}\n\n/**\n * Resolve symlinks so two spellings of one directory compare equal.\n *\n * Falls back to the input when it cannot resolve — a path that does not exist has\n * no real path, and for the duplicate check below \"unresolvable\" must not\n * accidentally equal some other unresolvable path.\n */\nfunction realOrSelf(path: string): string {\n  try {\n    return realpathSync(path);\n  } catch {\n    return path;\n  }\n}\n\nfunction isDir(path: string): boolean {\n  try {\n    return statSync(path).isDirectory();\n  } catch {\n    return false;\n  }\n}\n\n/**\n * Where a project went when a directory above it was renamed.\n *\n * Returns undefined rather than a guess whenever it cannot be certain — see the\n * uniqueness rule inside. undefined means \"still dead as far as this can tell\",\n * which leaves the entry exactly as it was.\n */\nexport function relocateRenamedAncestor(rootPath: string): string | undefined {\n  // Nothing to relocate. Worth an explicit early return: a caller that asks\n  // about a live path must not be told it \"moved\" to itself.\n  if (existsSync(rootPath)) return undefined;\n\n  const segments = rootPath.split(sep).filter((s) => s.length > 0);\n  if (segments.length === 0) return undefined;\n\n  // Walk DOWN while the path still exists. What survives is the deepest existing\n  // ancestor, and the first segment that does not is the renamed one.\n  const absolute = rootPath.startsWith(sep);\n  let current = absolute ? sep : \"\";\n  let i = 0;\n  for (; i < segments.length; i++) {\n    const next = join(current, segments[i]!);\n    if (!existsSync(next)) break;\n    current = next;\n  }\n\n  // Every segment resolved, yet the whole path does not exist. Should be\n  // unreachable, and returning undefined is the safe reading of a contradiction.\n  if (i === segments.length) return undefined;\n\n  for (; i < segments.length; i++) {\n    const wanted = matchKeys(segments[i]!);\n    if (wanted.size === 0) return undefined; // punctuation only — nothing to match on\n\n    let children: string[];\n    try {\n      children = readdirSync(current);\n    } catch {\n      return undefined; // unreadable directory — cannot claim to know\n    }\n\n    const hits = children.filter(\n      (c) =>\n        isDir(join(current, c)) &&\n        shareAKey(matchKeys(c), wanted) &&\n        // A leading dot is not decoration, so it must not normalise away.\n        //\n        // Caught on the first real run: `~/PAI` was \"relocated\" to `~/.pai`,\n        // because norm() strips the dot and both reduce to \"pai\". `~/.pai` is\n        // PAI's own registry and state directory — not a project — and the\n        // uniqueness rule below could not save us, since there was exactly one\n        // match. A hidden sibling is a category difference (`.git`, `.claude`,\n        // `.pai` are tool state), not a rename of the visible one.\n        isHidden(c) === isHidden(segments[i]!)\n    );\n\n    // THE SAFETY PROPERTY. Two directories that normalise the same means this\n    // cannot tell which one the project was, and repointing a registered slug at\n    // the wrong project would silently attach it to someone else's notes — worse\n    // than leaving the entry dead, because it looks fixed. Ambiguity is not a\n    // relocation.\n    if (hits.length !== 1) return undefined;\n\n    current = join(current, hits[0]!);\n  }\n\n  return current;\n}\n\n/**\n * The pre-existing guess: the project's leaf name in one of a few usual places.\n *\n * Kept, and kept SECOND, because it answers a different question — \"the project\n * itself was moved somewhere I know about\" rather than \"an ancestor was renamed\".\n * It is the weaker of the two: matching on basename alone can point at an\n * unrelated directory that happens to share a name, so it only gets to answer\n * when the ancestor walk has declined.\n */\nfunction suggestByBasename(rootPath: string): string | undefined {\n  const name = basename(rootPath);\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\n/**\n * Where a missing project probably is, or undefined.\n *\n * Feeds the health check's `suggestedPath ? \"stale\" : \"dead\"` classification, so\n * an answer here moves an entry out of the dead list and into the one `--fix`\n * repairs.\n *\n * `otherRoots` is every OTHER registered project's root path, and it is what\n * stops a repair from becoming a duplicate. Caught by the AIBroker session on the\n * first version of this: `~/PAI` was \"recovered\" to `~/dev/PAI`, which is a\n * second spelling of `~/Cloud/Development/PAI` — a path an ACTIVE project\n * already owns. Two registry entries for one directory is the exact mess that was\n * merged out of this registry earlier the same day.\n *\n * The comparison must be by realpath, not string. String equality is precisely\n * what missed it: the two paths share not one character after `/Users/owner/`.\n */\nexport function suggestMovedPath(\n  rootPath: string,\n  otherRoots: readonly string[] = []\n): string | undefined {\n  const candidate = relocateRenamedAncestor(rootPath) ?? suggestByBasename(rootPath);\n  if (!candidate) return undefined;\n\n  const taken = new Set(otherRoots.map(realOrSelf));\n  if (taken.has(realOrSelf(candidate))) return undefined;\n\n  return candidate;\n}\n","/**\n * Project health check command — audits registered projects for missing paths,\n * moved directories, and orphaned note directories.\n */\n\nimport { existsSync, readdirSync, statSync, realpathSync } 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, now, renderTable, encodeDir } from \"../../lib/utils.js\";\nimport type {\n  HealthRow,\n  HealthCategory,\n  HealthReason,\n  ProjectHealth,\n  ProjectRow,\n} from \"./types.js\";\nimport { suggestMovedPath } from \"./relocate.js\";\nimport { unregistrableReason } from \"../../../registry/registrable.js\";\nimport { getRegistryBackend } from \"../../../storage/factory.js\";\nimport { formatCheck, isGitProject, projectChecks } from \"../../../session/project-setup.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\n/*\n * suggestMovedPath now lives in relocate.ts, which walks to the deepest surviving\n * ancestor and re-matches the rest by normalised name.\n *\n * What was here looked for the project's BASENAME in four hardcoded directories,\n * so it could only recognise \"the leaf moved somewhere I already know about\". It\n * could not see the failure that actually happened: renaming `Ideaverse` to\n * `🧠 Ideaverse` left 32 registered projects exactly where they were and made\n * every one of them unreachable, and this function reported them dead and offered\n * to archive them.\n *\n * The basename guess is kept, second, since it answers the other question.\n */\n\n/**\n * Why a row is unhealthy, and what to do about it.\n *\n * `category` said only dead / stale / active, and \"archive\" was offered as the\n * remedy for everything. Four different situations were collapsed into that, and\n * archiving is right for exactly one of them:\n *\n *   EPHEMERAL   the path is a worktree or a temp dir — it should never have been\n *               registered. Checked FIRST, deliberately: a temp path whose\n *               directory has also vanished is both ephemeral and dead, and\n *               \"should never have been registered\" is the stronger and more\n *               actionable statement. Without a stated precedence the same row\n *               gets different labels depending on evaluation order.\n *   DUPLICATE   the path is gone and another project owns where it went. Its\n *               sessions are the only thing of value on it, so the remedy is\n *               merge — archiving strands them.\n *   MISNAMED    the path EXISTS and another project owns a subtree of it, under a\n *               slug that has nothing to do with it. `pferde` on `08 - Others/Client`.\n *               health never reported this at all, because existsSync says yes.\n *   DEAD        the path is gone and nothing claims it. Archive is correct here.\n *\n * The action must never name a command that destroys a row holding sessions —\n * a wrong command in an action field reads as vetted.\n */\nfunction diagnose(\n  project: HealthRow,\n  pathExists: boolean,\n  others: HealthRow[]\n): { reason?: HealthReason; owner?: string; action?: string } {\n  const ephemeral = unregistrableReason(project.root_path);\n  if (ephemeral) {\n    return {\n      reason: \"ephemeral\",\n      action:\n        project.session_count > 0\n          ? `holds ${project.session_count} session(s) — pai project merge ${project.slug} <durable-project> --execute, then unregister`\n          : `pai project unregister ${project.slug} --execute  (${ephemeral})`,\n    };\n  }\n\n  if (pathExists) {\n    // A live directory that another project owns a piece of, under an unrelated\n    // slug. Only reported when the other project sits BENEATH this one, which is\n    // the shape actually observed; two unrelated roots are not each other's problem.\n    const nested = others.find(\n      (o) =>\n        o.status === \"active\" &&\n        o.root_path.startsWith(project.root_path + \"/\") &&\n        existsSync(o.root_path)\n    );\n    if (nested && project.status !== \"active\") {\n      return {\n        reason: \"misnamed\",\n        owner: nested.slug,\n        action: `live directory, but ${nested.slug} owns a subtree of it — rename, or pai project merge ${project.slug} ${nested.slug}`,\n      };\n    }\n    return {};\n  }\n\n  // Path is gone. Does an active project already own where this one would go?\n  const suggestion = suggestMovedPath(project.root_path, []);\n  if (suggestion) {\n    const owner = others.find(\n      (o) => o.root_path === suggestion || realpathEq(o.root_path, suggestion)\n    );\n    if (owner) {\n      return {\n        reason: \"duplicate\",\n        owner: owner.slug,\n        action:\n          project.session_count > 0\n            ? `pai project merge ${project.slug} ${owner.slug}  (moves ${project.session_count} session(s))`\n            : `pai project merge ${project.slug} ${owner.slug}  (no sessions — a plain drop)`,\n      };\n    }\n  }\n\n  return {\n    reason: \"dead\",\n    action:\n      project.session_count > 0\n        ? `holds ${project.session_count} session(s) — merge before archiving, or they become unreachable`\n        : `pai project archive ${project.slug}`,\n  };\n}\n\n/** Same directory under two spellings — symlinks, not string equality. */\nfunction realpathEq(a: string, b: string): boolean {\n  try {\n    return realpathSync(a) === realpathSync(b);\n  } catch {\n    return false;\n  }\n}\n\nexport async function cmdHealth(\n  opts: { fix?: boolean; json?: boolean; status?: string }\n): Promise<void> {\n  const backend = await getRegistryBackend();\n  const rows: HealthRow[] = await backend.listProjectsWithSessionStats();\n\n  const results: ProjectHealth[] = rows.map((project) => {\n    const pathExists = existsSync(project.root_path);\n    const orphaned = findOrphanedNotesDirs(project);\n\n    const others = rows.filter((r) => r.id !== project.id);\n\n    let category: HealthCategory;\n    let suggestedPath: string | undefined;\n\n    if (pathExists) {\n      category = \"active\";\n    } else {\n      // Every OTHER project's root, so a repair cannot point this entry at a\n      // directory another project already owns.\n      suggestedPath = suggestMovedPath(\n        project.root_path,\n        others.map((r) => r.root_path)\n      );\n      category = suggestedPath ? \"stale\" : \"dead\";\n    }\n\n    const { reason, owner, action } = diagnose(project, pathExists, others);\n\n    return {\n      project,\n      category,\n      suggestedPath,\n      claudeNotesExists: orphaned.length > 0,\n      orphanedNotesDirs: orphaned,\n      reason,\n      owner,\n      action,\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        // Added fields, not renamed ones: `health` keeps its old values so any\n        // existing consumer keeps working.\n        reason: r.reason ?? null,\n        owner: r.owner ?? null,\n        action: r.action ?? null,\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        await backend.updateProjectPath(r.project.id, { rootPath: r.suggestedPath, encodedDir: newEncoded }, ts);\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(\"  Unreachable projects (path missing):\"));\n    for (const r of dead) {\n      const label = r.reason && r.reason !== \"dead\" ? warn(`  [${r.reason}]`) : \"\";\n      console.log(`    ${bold(r.project.slug)}   ${dim(r.project.root_path)}${label}`);\n      if (r.claudeNotesExists) {\n        console.log(chalk.yellow(`      Notes:  ${r.orphanedNotesDirs.join(\", \")}`));\n      }\n      // Only archive rows that are genuinely dead. A duplicate's sessions are the\n      // only thing of value on it, and an ephemeral row wants unregistering, not\n      // filing away — auto-archiving either was the bug behind this whole item.\n      if (r.project.session_count === 0 && opts.fix && r.reason === \"dead\") {\n        await backend.updateProjectStatus(r.project.id, \"archived\", { archivedAt: now(), updatedAt: now() });\n        console.log(ok(\"      Auto-fixed: archived (0 sessions, path gone, nothing claims it)\"));\n      } else if (r.action) {\n        console.log(dim(`      Do:     ${r.action}`));\n      }\n    }\n    console.log();\n  }\n\n  // \"active\" is the one word in this output that means something else elsewhere,\n  // so say which thing it is.\n  //\n  // This command's \"active\" is \"the path exists on disk\". The registry's `status`\n  // column also says active, meaning \"not archived\". They are different\n  // predicates over the same rows and they disagree by a lot: measured\n  // 2026-08-04, 124 paths present against 99 registry-active, because 29 ARCHIVED\n  // projects still have their directory. A careful reader took the 124 as a\n  // registry figure today and drew a wrong conclusion from it, which is a fair\n  // reading of a line that just said \"124 active\".\n  const archivedButPresent = results.filter(\n    (r) => r.category === \"active\" && r.project.status !== \"active\"\n  ).length;\n\n  console.log(\n    dim(\n      `  ${rows.length} total: ${active.length} with the path present, ` +\n        `${stale.length} stale, ${dead.length} dead`\n    )\n  );\n  if (archivedButPresent > 0) {\n    console.log(\n      dim(\n        `  ${archivedButPresent} of those ${active.length} are archived in the registry — ` +\n          `\"present\" here is about the directory, not the registry status.`\n      )\n    );\n  }\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  if (isGitProject(process.cwd())) {\n    console.log();\n    console.log(bold(\"  This project (worker setup):\"));\n    for (const c of await projectChecks(process.cwd())) console.log(\"  \" + formatCheck(c));\n  }\n}\n","/** pai project init — thin wrapper over session/project-setup. */\nimport { err } from \"../../lib/utils.js\";\nimport { formatCheck, initProject, projectChecks, type InitOpts } from \"../../../session/project-setup.js\";\n\nexport async function cmdInit(opts: InitOpts): Promise<void> {\n  try {\n    const r = initProject(process.cwd(), opts);\n    console.log(`project: ${r.root} (origin: ${r.origin}, sink: ${r.sink})`);\n    for (const k of r.kept) console.log(`kept ${k} (exists; --force overwrites)`);\n    for (const m of r.message) console.log(m);\n    // a dry run writes nothing, so checking would only report the missing files again\n    if (opts.dryRun) return;\n    console.log();\n    for (const c of await projectChecks(r.root)) console.log(formatCheck(c));\n  } catch (e) {\n    console.error(err(\"pai project init: \") + (e instanceof Error ? e.message : String(e)));\n    process.exitCode = 1;\n  }\n}\n","/**\n * pai project merge <from> <into> — fold a duplicate registry row into the real one.\n *\n * Exists because `health` had one remedy for every unhealthy row — archive — and\n * archiving a duplicate strands its sessions on a row nobody will open again. The\n * sessions are the only thing of value on a duplicate.\n *\n * Preview by default. This deletes a registry row, and the five-table fan-out\n * behind it is not something a caller can eyeball afterwards, so the plan is\n * printed first and `--execute` is what touches the database.\n */\n\nimport { ok, warn, err, dim, bold, shortenPath } from \"../../lib/utils.js\";\nimport { MergeError } from \"../../../registry/merge.js\";\nimport { getRegistryBackend } from \"../../../storage/factory.js\";\n\nexport async function cmdMerge(\n  fromSlug: string,\n  intoSlug: string,\n  opts: { execute?: boolean } = {}\n): Promise<void> {\n  const backend = await getRegistryBackend();\n\n  let plan;\n  try {\n    plan = await backend.planProjectMerge(fromSlug, intoSlug);\n  } catch (e) {\n    if (e instanceof MergeError) {\n      console.error(err(`  ${e.message}`));\n      process.exitCode = 1;\n      return;\n    }\n    throw e;\n  }\n\n  const path = async (id: number): Promise<string> => {\n    const project = await backend.getProjectById(id);\n    return project ? shortenPath(project.root_path, 54) : \"(unknown)\";\n  };\n\n  console.log();\n  console.log(bold(`  Merge ${plan.fromSlug} into ${plan.intoSlug}`));\n  console.log();\n  console.log(dim(`    from  ${plan.fromSlug.padEnd(22)} ${await path(plan.fromId)}`));\n  console.log(dim(`    into  ${plan.intoSlug.padEnd(22)} ${await path(plan.intoId)}`));\n  console.log();\n\n  if (plan.sessions.length > 0) {\n    // The renumbering is the part a reader cannot predict — sessions are UNIQUE\n    // on (project_id, number), so incoming ones cannot keep their numbers.\n    console.log(`    ${plan.sessions.length} session(s) move and are renumbered:`);\n    for (const s of plan.sessions) {\n      console.log(dim(`      ${String(s.from).padStart(4)}  ->  ${String(s.to).padStart(4)}`));\n    }\n  } else {\n    console.log(dim(`    No sessions to move.`));\n  }\n\n  const extras: string[] = [];\n  if (plan.tags) extras.push(`${plan.tags} tag(s)`);\n  if (plan.aliases) extras.push(`${plan.aliases} existing alias(es)`);\n  if (plan.compactions) extras.push(`${plan.compactions} compaction record(s)`);\n  if (plan.links) extras.push(`${plan.links} inbound link(s)`);\n  if (extras.length > 0) {\n    console.log(dim(`    Also repointed: ${extras.join(\", \")}.`));\n  }\n\n  if (plan.aliasToAdd) {\n    console.log(\n      dim(`    \"${plan.aliasToAdd}\" is kept as an alias, so the old name still resolves.`)\n    );\n  } else {\n    console.log(\n      warn(`    \"${plan.fromSlug}\" cannot be kept as an alias — the name is already taken.`)\n    );\n  }\n\n  console.log(dim(`    Then the ${plan.fromSlug} row is deleted.`));\n  console.log();\n\n  if (!opts.execute) {\n    console.log(dim(\"  Preview — nothing was changed. Re-run with --execute to merge.\"));\n    console.log();\n    return;\n  }\n\n  await backend.applyProjectMerge(plan);\n  console.log(\n    ok(\n      `  Merged. ${plan.sessions.length} session(s) now belong to ${plan.intoSlug}.`\n    )\n  );\n  console.log();\n}\n","/**\n * `pai project here <name>` — \"this directory is that project\".\n *\n * Directories move. They get renamed, reorganised into an archive tree, reached\n * through a symlinked parent. Every existing way to fix the registry afterwards\n * asks for something the user has to look up — `rebind` and `move` want a slug\n * AND a path, `add` wants a path — which means the moment you are standing in\n * the directory and know perfectly well what it is, you still cannot just say so.\n *\n * This command is that sentence. Run it in the directory, name the project, and\n * the registry points here. If no project by that name exists, it is created.\n *\n * Two details that matter more than they look:\n *\n * 1. The path is canonicalised with realpath. `resolve()` keeps whichever\n *    spelling was typed, so a directory reachable through a symlinked parent\n *    encodes two different ways and can be registered TWICE as two unrelated\n *    projects — with sessions split across both. Storing the canonical path is\n *    what makes \"this directory\" mean one row.\n *\n * 2. Matching accepts the name a human uses. The display name, the slug, and\n *    all-query-words-present, in that order, so a project whose directory has\n *    since gained or lost a word is still reachable by the name its owner\n *    thinks of it by.\n */\n\nimport { realpathSync } from \"node:fs\";\nimport { ok, err, dim, bold, encodeDir, now } from \"../../lib/utils.js\";\nimport { getRegistryBackend } from \"../../../storage/factory.js\";\nimport { unregistrableReason } from \"../../../registry/registrable.js\";\n\ninterface ProjectRow {\n  id: number;\n  slug: string;\n  display_name: string;\n  root_path: string;\n  status: string;\n}\n\n/** Normalise for name comparison: lowercase, collapse whitespace and hyphens. */\nfunction norm(s: string): string {\n  return s.toLowerCase().replace(/[-_]+/g, \" \").replace(/\\s+/g, \" \").trim();\n}\n\n/**\n * Find the project a human means by `name`.\n *\n * Exact display-name or slug first. Only if that finds nothing do we fall back\n * to requiring every query word to appear, which is deliberately narrower than\n * a substring match: it reaches \"Jobs Search Beta\" from \"jobs beta\" without\n * also reaching a sibling project that merely shares the first word.\n */\nexport async function findProjectsByName(name: string): Promise<ProjectRow[]> {\n  const backend = await getRegistryBackend();\n  const rows: ProjectRow[] = await backend.listProjects();\n\n  const q = norm(name);\n  const exact = rows.filter((r) => norm(r.display_name) === q || norm(r.slug) === q);\n  if (exact.length > 0) return exact;\n\n  const words = q.split(\" \").filter(Boolean);\n  if (words.length === 0) return [];\n  return rows.filter((r) => {\n    const hay = `${norm(r.display_name)} ${norm(r.slug)}`;\n    return words.every((w) => hay.includes(w));\n  });\n}\n\n/** Slug from a display name: lowercase, non-alphanumerics to hyphens. */\nexport function slugFromName(name: string): string {\n  return (\n    name\n      .toLowerCase()\n      .replace(/[^a-z0-9]+/g, \"-\")\n      .replace(/^-+|-+$/g, \"\") || \"project\"\n  );\n}\n\nexport async function cmdHere(\n  name: string,\n  opts: { cwd?: string; dryRun?: boolean } = {}\n): Promise<void> {\n  // Canonicalise: see note 1 above. A symlinked parent must not create a second\n  // identity for the same directory.\n  let target: string;\n  try {\n    target = realpathSync(opts.cwd ?? process.cwd());\n  } catch {\n    console.error(err(`Cannot resolve the current directory.`));\n    process.exitCode = 1;\n    return;\n  }\n\n  const backend = await getRegistryBackend();\n  const encoded = encodeDir(target);\n  const matches = await findProjectsByName(name);\n\n  if (matches.length > 1) {\n    console.error(\n      err(`\"${name}\" matches ${matches.length} projects — name one exactly:\\n`) +\n        matches.map((m) => dim(`  ${m.slug}  (${m.display_name})\\n`)).join(\"\")\n    );\n    process.exitCode = 1;\n    return;\n  }\n\n  // Another project already owns this directory: refuse rather than create a\n  // duplicate row for a path that is already spoken for.\n  const owner = (await backend.getProjectByEncodedDir(encoded)) ?? undefined;\n\n  if (matches.length === 1) {\n    const p = matches[0];\n\n    if (owner && owner.id !== p.id) {\n      console.error(\n        err(`This directory is already registered to ${bold(owner.slug)}.\\n`) +\n          dim(`  ${target}\\n`) +\n          dim(`  Merge them: pai project merge ${owner.slug} ${p.slug} --execute`)\n      );\n      process.exitCode = 1;\n      return;\n    }\n\n    // Already correct — say so instead of writing an identical row. Compared by\n    // realpath, so a differently-spelled but equivalent path counts as correct.\n    let sameAlready = false;\n    try {\n      sameAlready = realpathSync(p.root_path) === target;\n    } catch {\n      sameAlready = false;\n    }\n    if (sameAlready) {\n      console.log(ok(`Already set: ${bold(p.display_name)}`));\n      console.log(dim(`  ${target}`));\n      return;\n    }\n\n    if (opts.dryRun) {\n      console.log(dim(`Would repoint ${p.slug}:`));\n      console.log(dim(`  from ${p.root_path}`));\n      console.log(dim(`  to   ${target}`));\n      return;\n    }\n\n    await backend.updateProjectPath(p.id, { rootPath: target, encodedDir: encoded }, now());\n\n    console.log(ok(`${bold(p.display_name)} is now here`));\n    console.log(dim(`  was: ${p.root_path}`));\n    console.log(dim(`  now: ${target}`));\n    return;\n  }\n\n  // No project by that name — create it pointing here.\n  if (owner) {\n    console.error(\n      err(`This directory is already registered as ${bold(owner.slug)} (${owner.display_name}).\\n`) +\n        dim(`  Rename it instead: pai project edit ${owner.slug} --display-name \"${name}\"`)\n    );\n    process.exitCode = 1;\n    return;\n  }\n\n  const slug = slugFromName(name);\n  const taken = await backend.getProjectBySlug(slug);\n  const finalSlug = taken ? `${slug}-${encoded.slice(-6)}` : slug;\n\n  if (opts.dryRun) {\n    console.log(dim(`Would create ${finalSlug} (${name}) at ${target}`));\n    return;\n  }\n\n  const unregistrable = unregistrableReason(target);\n  if (unregistrable) {\n    console.error(err(`Not registering ${target}: it is ${unregistrable}.`));\n    process.exitCode = 1;\n    return;\n  }\n\n  const ts = now();\n  await backend.createProject({\n    slug: finalSlug,\n    displayName: name,\n    rootPath: target,\n    encodedDir: encoded,\n    type: \"local\",\n    status: \"active\",\n    createdAt: ts,\n    updatedAt: ts,\n  });\n\n  console.log(ok(`Created ${bold(name)}`));\n  console.log(dim(`  slug: ${finalSlug}`));\n  console.log(dim(`  path: ${target}`));\n}\n","/**\n * pai project unregister <slug> — remove a row that should never have existed.\n *\n * The remedy for an EPHEMERAL project: one rooted in a git worktree or a temp\n * directory. Archiving is wrong for those — archiving means \"this was real and is\n * finished\", and these were never projects. Measured 2026-08-04 there were five:\n * two agent worktrees, `/private/tmp/ops-webui`, a scratchpad under\n * `/private/tmp/claude-501/…`, and `tmp` rooted at `/private/tmp` itself, active\n * since February.\n *\n * Written because `health` started printing \"unregister\" as the suggested action\n * and the command did not exist. An action field naming something unavailable is\n * worse than no action field: it reads as vetted.\n *\n * Refuses by default when the row holds sessions. Those sessions are the only\n * thing of value on it, and deleting the row would strand them — merge is the\n * right verb there, so this points at it rather than offering --force as the\n * obvious path.\n */\n\nimport { ok, warn, err, dim, bold } from \"../../lib/utils.js\";\nimport { getRegistryBackend } from \"../../../storage/factory.js\";\n\nexport async function cmdUnregister(\n  slug: string,\n  opts: { execute?: boolean; force?: boolean } = {}\n): Promise<void> {\n  const backend = await getRegistryBackend();\n  const project = await backend.getProjectBySlug(slug);\n  const row = project\n    ? {\n        id: project.id,\n        slug: project.slug,\n        root_path: project.root_path,\n        status: project.status,\n        session_count: await backend.countSessionsForProject(project.id),\n      }\n    : undefined;\n\n  if (!row) {\n    console.error(err(`  No project with slug \"${slug}\".`));\n    process.exitCode = 1;\n    return;\n  }\n\n  console.log();\n  console.log(bold(`  Unregister ${row.slug}`));\n  console.log(dim(`    ${row.root_path}`));\n  console.log(dim(`    status ${row.status}, ${row.session_count} session(s)`));\n  console.log();\n\n  if (row.session_count > 0 && !opts.force) {\n    console.log(warn(`  Refusing: ${row.session_count} session(s) would be stranded.`));\n    console.log(\n      dim(`  Those sessions are the only thing of value on this row. Move them first:`)\n    );\n    console.log(dim(`      pai project merge ${row.slug} <into> --execute`));\n    console.log(dim(`  Or pass --force if the sessions are genuinely worthless.`));\n    console.log();\n    process.exitCode = 1;\n    return;\n  }\n\n  if (!opts.execute) {\n    console.log(\n      dim(\"  Preview — nothing was changed. Re-run with --execute to unregister.\")\n    );\n    if (row.session_count > 0) {\n      console.log(warn(`  --force is set: ${row.session_count} session(s) WILL be deleted.`));\n    }\n    console.log();\n    return;\n  }\n\n  // Same five tables as merge. With foreign keys off, deleting only the project\n  // row would silently orphan the rest.\n  await backend.deleteProjectCascade(row.id);\n\n  console.log(ok(`  Unregistered ${row.slug}.`));\n  console.log(dim(`  The directory itself was not touched.`));\n  console.log();\n}\n","/**\n * Implementations for all `pai projects` sub-commands (plural namespace).\n *\n * This is the canonical namespace. `pai project <subcmd>` (singular) is a\n * deprecated alias that forwards here with a stderr notice.\n */\n\nimport { cmdPromote } from \"../../../session/promote.js\";\nimport {\n  cmdAdd,\n  cmdList,\n  cmdInfo,\n  cmdArchive,\n  cmdUnarchive,\n  cmdMove,\n  cmdTag,\n  cmdAlias,\n  cmdEdit,\n  cmdDetect,\n  cmdConsolidate,\n  cmdGo,\n  cmdRebind,\n  findMovedPath,\n} from \"./commands.js\";\nimport { existsSync } from \"node:fs\";\nimport { ok, warn, err, dim, bold, shortenPath, encodeDir, now } from \"../../lib/utils.js\";\nimport { basename } from \"node:path\";\nimport { cmdName, cmdUnname, cmdNames, cmdConfig, cmdMcp, cmdTools } from \"./session-config.js\";\nimport { cmdHealth } from \"./health.js\";\nimport { cmdInit } from \"./init.js\";\nimport { cmdMerge } from \"./merge.js\";\nimport { cmdHere } from \"./here.js\";\nimport { cmdUnregister } from \"./unregister.js\";\nimport { resolveIdentifier } from \"./helpers.js\";\nimport { pinNotices, projectLaunchConfig } from \"../../../workers/project-config.js\";\nimport { getRegistryBackend } from \"../../../storage/factory.js\";\n\nexport { cmdGo };\n\n// New functions for converted commands\nexport async function projectListCmd(opts: { all?: boolean; n?: string }): Promise<number> {\n  const { cmdMain } = await import(\"../../lib/main-resolver.js\");\n  await cmdMain(undefined, undefined, opts);\n  return 0;\n}\n\nexport async function projectListRawCmd(opts: { all?: boolean; status?: string; tag?: string; type?: string }): Promise<number> {\n  await cmdList(opts);\n  return 0;\n}\n\nexport async function projectCdCmd(identifier: string): Promise<number> {\n  const project = await resolveIdentifier(identifier);\n  if (!project) {\n    console.error(`Project not found: ${identifier}`);\n    return 1;\n  }\n\n  if (existsSync(project.root_path)) {\n    process.stdout.write(project.root_path + \"\\n\");\n    return 0;\n  }\n\n  // Path missing — search for moved location\n  process.stderr.write(\n    warn(`Path not found: ${project.root_path}\\n`) +\n    dim(\"  Searching for moved location...\\n\")\n  );\n\n  const result = findMovedPath(project.root_path);\n\n  if (result.found) {\n    const newPath = result.found;\n    const newEncoded = encodeDir(newPath);\n    const ts = now();\n    const backend = await getRegistryBackend();\n    await backend.updateProjectPath(project.id, { rootPath: newPath, encodedDir: newEncoded }, ts);\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 0;\n  }\n\n  if (result.ambiguous) {\n    process.stderr.write(\n      warn(`Multiple directories named \"${basename(project.root_path)}\" found:\\n`)\n    );\n    for (const candidate of result.ambiguous) {\n      process.stderr.write(dim(`  ${candidate}\\n`));\n    }\n    process.stderr.write(\n      dim(`\\n  Disambiguate with: pai projects rebind ${project.slug} <path>\\n`)\n    );\n    return 1;\n  }\n\n  process.stderr.write(\n    err(\n      `Project \"${project.slug}\" root_path \"${project.root_path}\" does not exist on disk\\n` +\n      `  and no folder named \"${basename(project.root_path)}\" was found in scan dirs.\\n`\n    ) +\n    dim(`  Fix with: pai projects rebind ${project.slug} <new-path>\\n`)\n  );\n  return 1;\n}\n\nexport async function projectAddCmd(rawPath: string, opts: { slug?: string; type?: string; displayName?: string }): Promise<number> {\n  await cmdAdd(rawPath, opts);\n  return 0;\n}\n\nexport async function projectInfoCmd(slug: string): Promise<number> {\n  await cmdInfo(slug);\n  return 0;\n}\n\nexport async function projectArchiveCmd(slug: string): Promise<number> {\n  await cmdArchive(slug);\n  return 0;\n}\n\nexport async function projectMergeCmd(from: string, into: string, opts: { execute?: boolean }): Promise<number> {\n  await cmdMerge(from, into, opts);\n  return 0;\n}\n\n// New functions for remaining converted commands\nexport async function projectUnregisterCmd(slug: string, opts: { execute?: boolean; force?: boolean }): Promise<number> {\n  await cmdUnregister(slug, opts);\n  return 0;\n}\n\nexport async function projectUnarchiveCmd(slug: string): Promise<number> {\n  await cmdUnarchive(slug);\n  return 0;\n}\n\nexport async function projectMoveCmd(slug: string, newPath: string): Promise<number> {\n  await cmdMove(slug, newPath);\n  return 0;\n}\n\nexport async function projectRebindCmd(slug: string, newPath: string): Promise<number> {\n  await cmdRebind(slug, newPath);\n  return 0;\n}\n\nexport async function projectHereCmd(name: string, opts: { dryRun?: boolean }): Promise<number> {\n  await cmdHere(name, { dryRun: opts.dryRun });\n  return 0;\n}\n\nexport async function projectTagCmd(slug: string, tags: string[]): Promise<number> {\n  await cmdTag(slug, tags);\n  return 0;\n}\n\nexport async function projectAliasCmd(slug: string, alias: string): Promise<number> {\n  await cmdAlias(slug, alias);\n  return 0;\n}\n\nexport async function projectEditCmd(slug: string, opts: { displayName?: string; type?: string }): Promise<number> {\n  await cmdEdit(slug, opts);\n  return 0;\n}\n\nexport async function projectDetectCmd(pathArg: string | undefined, opts: { json?: boolean }): Promise<number> {\n  await cmdDetect(pathArg, opts);\n  return 0;\n}\n\nexport async function projectHealthCmd(opts: { fix?: boolean; json?: boolean; status?: string }): Promise<number> {\n  await cmdHealth(opts);\n  return 0;\n}\n\nexport async function projectInitCmd(opts: { anchor?: number; marker?: string; force?: boolean; dryRun?: boolean }): Promise<number> {\n  await cmdInit(opts);\n  return 0;\n}\n\nexport async function projectConsolidateCmd(identifier: string, opts: { yes?: boolean; dryRun?: boolean }): Promise<number> {\n  await cmdConsolidate(identifier, opts);\n  return 0;\n}\n\nexport async function projectPromoteCmd(opts: { fromSession: string; to: string; name?: string }): Promise<number> {\n  // Ensure required fields are present (they are required in the function signature)\n  const promoteOpts = {\n    fromSession: opts.fromSession,\n    to: opts.to,\n    name: opts.name\n  };\n  await cmdPromote(promoteOpts);\n  return 0;\n}\n\nexport async function projectGoCmd(query: string): Promise<number> {\n  await cmdGo(query);\n  return 0;\n}\n\nexport async function projectNameCmd(identifier: string, shortname: string, opts: { permission?: string }): Promise<number> {\n  await cmdName(identifier, shortname, opts);\n  return 0;\n}\n\nexport async function projectUnnameCmd(shortname: string): Promise<number> {\n  await cmdUnname(shortname);\n  return 0;\n}\n\nexport async function projectNamesCmd(opts: { json?: boolean; all?: boolean }): Promise<number> {\n  await cmdNames(opts);\n  return 0;\n}\n\nexport async function projectConfigCmd(identifier: string | undefined, opts: { set?: string[]; unset?: string[]; preset?: string; defaults?: boolean; options?: boolean; json?: boolean; reset?: boolean }): Promise<number> {\n  await cmdConfig(identifier, opts);\n  return 0;\n}\n\nexport async function projectMcpCmd(names: string[], opts: { clear?: boolean; set?: string }): Promise<number> {\n  await cmdMcp(names, opts);\n  return 0;\n}\n\nexport async function projectPinsCmd(): Promise<number> {\n  for (const line of pinNotices(await projectLaunchConfig(process.cwd()))) console.log(line);\n  return 0;\n}\n\nexport async function projectToolsCmd(names: string[], opts: { clear?: boolean; set?: string }): Promise<number> {\n  await cmdTools(names, opts);\n  return 0;\n}\n","/**\n * `pai project` (also `pai projects`): front end for the project commands.\n * Behaviour lives in src/cli/impl/project/projects-index.js; this file declares names, flags and help.\n */\n\nimport type { CommandDef } from \"../define.js\";\nimport {\n  projectListCmd,\n  projectListRawCmd,\n  projectCdCmd,\n  projectAddCmd,\n  projectInfoCmd,\n  projectArchiveCmd,\n  projectMergeCmd,\n  projectUnregisterCmd,\n  projectUnarchiveCmd,\n  projectMoveCmd,\n  projectRebindCmd,\n  projectHereCmd,\n  projectTagCmd,\n  projectAliasCmd,\n  projectEditCmd,\n  projectDetectCmd,\n  projectHealthCmd,\n  projectInitCmd,\n  projectConsolidateCmd,\n  projectPromoteCmd,\n  projectGoCmd,\n  projectNameCmd,\n  projectUnnameCmd,\n  projectNamesCmd,\n  projectConfigCmd,\n  projectMcpCmd,\n  projectPinsCmd,\n  projectToolsCmd\n} from \"../impl/project/projects-index.js\";\nimport { CliError } from \"../define.js\";\n\nexport const projectSubcommands: CommandDef[] = [\n{\n  name: \"list\",\n  summary: \"Show your projects and sessions in one list\",\n  description: \"Show your projects and sessions in one list.\\n\\nShow the unified deduped listing (same as `pai`).\\nUse --all to include cold / zero-session / archived projects.\",\n  options: [{ name: \"all\", type: \"boolean\", summary: \"Show all entries including cold / zero-session / archived projects\" }, { name: \"n\", short: \"n\", type: \"string\", value: \"COUNT\", summary: \"Max rows to show\" }],\n  examples: [\"pai project list\",\"pai project list --all\"],\n  seeAlso: [\"pai project list-raw\"],\n  run: (ctx) => projectListCmd({ all: ctx.opts.all === true, n: ctx.opts.n as string | undefined }),\n},\n{\n  name: \"list-raw\",\n  summary: \"Plain table of every registered project\",\n  description: \"Plain table of every registered project.\\n\\nRaw registry listing with slug, path, status, last_active columns\",\n  options: [{ name: \"all\", type: \"boolean\", summary: \"Include archived projects (default: active only)\" }, { name: \"status\", type: \"string\", value: \"STATUS\", summary: \"Filter by status: active | archived\" }, { name: \"tag\", type: \"string\", value: \"TAG\", summary: \"Filter by tag\" }, { name: \"type\", type: \"string\", value: \"TYPE\", summary: \"Filter by type\" }],\n  examples: [\"pai project list-raw\",\"pai project list-raw --status active --type project\"],\n  seeAlso: [\"pai project cd\",\"pai project list\"],\n  run: (ctx) => projectListRawCmd({ all: ctx.opts.all === true, status: ctx.opts.status as string | undefined, tag: ctx.opts.tag as string | undefined, type: ctx.opts.type as string | undefined }),\n},\n{\n  name: \"cd\",\n  summary: \"Print how to change into a project's folder\",\n  description: \"Print how to change into a project's folder.\\n\\ncd to a project directory. Short form: pai cd <name>\\n(The shell wrapper handles the actual cd; pure output here.)\\nAuto-detects moved projects when the registered path no longer exists.\",\n  args: [{ name: \"IDENTIFIER\", summary: \"Project name, short name, number or part of its path\", required: true }],\n  examples: [\"pai project cd my-app\",\"pai project cd 2\"],\n  seeAlso: [\"pai project add\",\"pai project list-raw\"],\n  run: (ctx) => projectCdCmd(ctx.args.IDENTIFIER as string),\n},\n{\n  name: \"add\",\n  summary: \"Register a folder as a project\",\n  description: \"Register a folder as a project.\\n\\nRegister a project directory in the PAI registry\",\n  args: [{ name: \"PATH\", summary: \"Folder path\", required: true }],\n  options: [{ name: \"slug\", type: \"string\", value: \"SLUG\", summary: \"Override auto-generated slug\" }, { name: \"type\", type: \"string\", value: \"TYPE\", summary: \"Project type: local | central | obsidian-linked | external\" }, { name: \"display-name\", type: \"string\", value: \"NAME\", summary: \"Human-readable display name\" }],\n  examples: [\"pai project add ~/code/my-app\",\"pai project add ~/code/my-app --slug my-app --display-name \\\"My App\\\"\"],\n  seeAlso: [\"pai project info\",\"pai project cd\"],\n  run: (ctx) => projectAddCmd(ctx.args.PATH as string, { slug: ctx.opts.slug as string | undefined, type: ctx.opts.type as string | undefined, displayName: ctx.opts[\"display-name\"] as string | undefined }),\n},\n{\n  name: \"info\",\n  summary: \"Show everything known about one project\",\n  description: \"Show everything known about one project.\",\n  args: [{ name: \"SLUG\", summary: \"Project name (see: pai project list)\", required: true }],\n  examples: [\"pai project info my-app\",\"pai project info my-app\"],\n  seeAlso: [\"pai project archive\",\"pai project add\"],\n  run: (ctx) => projectInfoCmd(ctx.args.SLUG as string),\n},\n{\n  name: \"archive\",\n  summary: \"Hide a project you no longer work on\",\n  description: \"Hide a project you no longer work on.\",\n  args: [{ name: \"SLUG\", summary: \"Project name (see: pai project list)\", required: true }],\n  examples: [\"pai project archive old-app\",\"pai project list-raw --status archived\"],\n  seeAlso: [\"pai project merge\",\"pai project info\"],\n  run: (ctx) => projectArchiveCmd(ctx.args.SLUG as string),\n},\n{\n  name: \"merge\",\n  summary: \"Fold a duplicate project into another (preview unless --execute)\",\n  description: \"Fold a duplicate project into another (preview unless --execute).\\n\\nFold a duplicate project into another: move its sessions (renumbered), repoint its tags, aliases, compaction records and links, keep the old slug as an alias, then delete the row. Preview unless --execute is given.\",\n  args: [{ name: \"FROM\", summary: \"Project to fold away\", required: true }, { name: \"INTO\", summary: \"Project to keep\", required: true }],\n  options: [{ name: \"execute\", type: \"boolean\", summary: \"Actually perform the merge\" }],\n  examples: [\"pai project merge old-app my-app\",\"pai project merge old-app my-app --execute\"],\n  seeAlso: [\"pai project unregister\",\"pai project archive\"],\n  run: (ctx) => projectMergeCmd(ctx.args.FROM as string, ctx.args.INTO as string, { execute: ctx.opts.execute === true }),\n},\n{\n  name: \"unregister\",\n  summary: \"Remove a project that should never have been registered\",\n  description: \"Remove a project that should never have been registered.\\n\\nRemove a project row entirely, for paths that should never have been registered (worktrees, temp dirs). Refuses when the row holds sessions — merge those first. Preview unless --execute is given. The directory itself is never touched.\",\n  args: [{ name: \"SLUG\", summary: \"Project name (see: pai project list)\", required: true }],\n  options: [{ name: \"execute\", type: \"boolean\", summary: \"Actually remove the row\" }, { name: \"force\", type: \"boolean\", summary: \"Remove even though sessions would be deleted with it\" }],\n  examples: [\"pai project unregister stray-dir\",\"pai project unregister stray-dir --execute\"],\n  seeAlso: [\"pai project unarchive\",\"pai project merge\"],\n  run: (ctx) => projectUnregisterCmd(ctx.args.SLUG as string, { execute: ctx.opts.execute === true, force: ctx.opts.force === true }),\n},\n{\n  name: \"unarchive\",\n  summary: \"Bring an archived project back\",\n  description: \"Bring an archived project back.\\n\\nRestore an archived project to active status\",\n  args: [{ name: \"SLUG\", summary: \"Project name (see: pai project list)\", required: true }],\n  examples: [\"pai project unarchive old-app\",\"pai project info old-app\"],\n  seeAlso: [\"pai project move\",\"pai project unregister\"],\n  run: (ctx) => projectUnarchiveCmd(ctx.args.SLUG as string),\n},\n{\n  name: \"move\",\n  summary: \"Record that a project's folder has a new location\",\n  description: \"Record that a project's folder has a new location.\",\n  args: [{ name: \"SLUG\", summary: \"Project name (see: pai project list)\", required: true }, { name: \"NEW_PATH\", summary: \"The new folder path\", required: true }],\n  examples: [\"pai project move my-app ~/code/new/my-app\",\"pai project info my-app\"],\n  seeAlso: [\"pai project rebind\",\"pai project unarchive\"],\n  run: (ctx) => projectMoveCmd(ctx.args.SLUG as string, ctx.args.NEW_PATH as string),\n},\n{\n  name: \"rebind\",\n  summary: \"Point a project at a new folder after you moved it\",\n  description: \"Point a project at a new folder after you moved it.\\n\\nManually update the root_path for a project (for when auto-detect found multiple matches).\\nValidates the new path exists and is a directory, then updates the registry.\",\n  args: [{ name: \"SLUG\", summary: \"Project name (see: pai project list)\", required: true }, { name: \"NEW_PATH\", summary: \"The new folder path\", required: true }],\n  examples: [\"pai project rebind my-app ~/code/new/my-app\",\"pai project health\"],\n  seeAlso: [\"pai project here\",\"pai project move\"],\n  run: (ctx) => projectRebindCmd(ctx.args.SLUG as string, ctx.args.NEW_PATH as string),\n},\n{\n  name: \"here\",\n  summary: \"Declare the current folder to be this project\",\n  description: \"Declare the current folder to be this project.\\n\\nDeclare that the CURRENT directory is this project — no slug, no path.\\nRepoints an existing project of that name, or creates it. Stores the\\ncanonical (realpath) location, so a symlinked parent cannot register the\\nsame directory twice.\",\n  args: [{ name: \"NAME\", summary: \"Project name\", required: true }],\n  options: [{ name: \"dry-run\", type: \"boolean\", summary: \"Show what would change without writing\" }],\n  examples: [\"pai project here my-app\",\"pai project here my-app --dry-run\"],\n  seeAlso: [\"pai project tag\",\"pai project rebind\"],\n  run: (ctx) => projectHereCmd(ctx.args.NAME as string, { dryRun: ctx.opts[\"dry-run\"] === true }),\n},\n{\n  name: \"tag\",\n  summary: \"Add tags to a project\",\n  description: \"Add tags to a project.\\n\\nAdd one or more tags to a project\",\n  args: [{ name: \"SLUG\", summary: \"Project name (see: pai project list)\", required: true }, { name: \"TAGS\", summary: \"Comma-separated tags\", required: true, variadic: true }],\n  examples: [\"pai project tag my-app web,client\",\"pai project info my-app\"],\n  seeAlso: [\"pai project alias\",\"pai project here\"],\n  run: (ctx) => projectTagCmd(ctx.args.SLUG as string, (ctx.args.TAGS as string[] | undefined) ?? []),\n},\n{\n  name: \"alias\",\n  summary: \"Give a project a second name that also finds it\",\n  description: \"Give a project a second name that also finds it.\",\n  args: [{ name: \"SLUG\", summary: \"Project name (see: pai project list)\", required: true }, { name: \"ALIAS\", summary: \"The second name\", required: true }],\n  examples: [\"pai project alias my-app app\",\"pai project info my-app\"],\n  seeAlso: [\"pai project edit\",\"pai project tag\"],\n  run: (ctx) => projectAliasCmd(ctx.args.SLUG as string, ctx.args.ALIAS as string),\n},\n{\n  name: \"edit\",\n  summary: \"Change a project's display name or type\",\n  description: \"Change a project's display name or type.\",\n  args: [{ name: \"SLUG\", summary: \"Project name (see: pai project list)\", required: true }],\n  options: [{ name: \"display-name\", type: \"string\", value: \"NAME\", summary: \"New display name\" }, { name: \"type\", type: \"string\", value: \"TYPE\", summary: \"New type\" }],\n  examples: [\"pai project edit my-app --display-name \\\"My App\\\"\",\"pai project edit my-app --type client\"],\n  seeAlso: [\"pai project detect\",\"pai project alias\"],\n  run: (ctx) => projectEditCmd(ctx.args.SLUG as string, { displayName: ctx.opts[\"display-name\"] as string | undefined, type: ctx.opts.type as string | undefined }),\n},\n{\n  name: \"detect\",\n  summary: \"Find which registered project a folder belongs to\",\n  description: \"Find which registered project a folder belongs to.\\n\\nDetect which registered project the given path (or CWD) belongs to\",\n  args: [{ name: \"PATH\", summary: \"Folder path\" }],\n  options: [{ name: \"json\", type: \"boolean\", summary: \"Output raw JSON instead of human-readable text\" }],\n  examples: [\"pai project detect\",\"pai project detect ~/code/my-app --json\"],\n  seeAlso: [\"pai project health\",\"pai project edit\"],\n  run: (ctx) => projectDetectCmd(ctx.args.PATH as string | undefined, { json: ctx.opts.json === true }),\n},\n{\n  name: \"health\",\n  summary: \"Check that every project's folder still exists\",\n  description: \"Check that every project's folder still exists.\\n\\nAudit all registered projects: check which paths still exist, find moved/dead projects\",\n  options: [{ name: \"fix\", type: \"boolean\", summary: \"Auto-remediate where possible\" }, { name: \"json\", type: \"boolean\", summary: \"Output raw JSON report\" }, { name: \"status\", type: \"string\", value: \"CATEGORY\", summary: \"Filter output to: active | stale | dead\" }],\n  examples: [\"pai project health\",\"pai project health --json\"],\n  seeAlso: [\"pai project init\",\"pai project detect\"],\n  run: (ctx) => projectHealthCmd({ fix: ctx.opts.fix === true, json: ctx.opts.json === true, status: ctx.opts.status as string | undefined }),\n},\n{\n  name: \"init\",\n  summary: \"Set up this git project for PAI workers\",\n  description: \"Set up this git project for PAI workers.\\n\\nSet up this git project for PAI workers: .pai/project.yaml (sink from the origin) and a starter .pai/worker.md, then run the project checks\",\n  options: [{ name: \"anchor\", type: \"string\", value: \"N\", summary: \"Forge anchor issue number (sink forge)\" }, { name: \"marker\", type: \"string\", value: \"TEXT\", summary: \"Marker text that must occur in the anchor body\" }, { name: \"force\", type: \"boolean\", summary: \"Overwrite existing files (old copies kept as .bak)\" }, { name: \"dry-run\", type: \"boolean\", summary: \"Print what would be written; write nothing\" }],\n  examples: [\"pai project init --dry-run\",\"pai project init --force\"],\n  seeAlso: [\"pai project consolidate\",\"pai project health\"],\n  run: (ctx) => projectInitCmd({ anchor: ctx.opts.anchor as number | undefined, marker: ctx.opts.marker as string | undefined, force: ctx.opts.force === true, dryRun: ctx.opts[\"dry-run\"] === true }),\n},\n{\n  name: \"consolidate\",\n  summary: \"Gather scattered notes folders into the project's own Notes\",\n  description: \"Gather scattered notes folders into the project's own Notes.\\n\\nConsolidate scattered ~/.claude/projects/.../Notes/ directories for a project into its canonical Notes/ location\",\n  args: [{ name: \"IDENTIFIER\", summary: \"Project name, short name, number or part of its path\", required: true }],\n  options: [{ name: \"yes\", type: \"boolean\", summary: \"Perform consolidation without confirmation prompt\" }, { name: \"dry-run\", type: \"boolean\", summary: \"Preview what would be moved without making changes\" }],\n  examples: [\"pai project consolidate my-app --dry-run\",\"pai project consolidate my-app --yes\"],\n  seeAlso: [\"pai project promote\",\"pai project init\"],\n  run: (ctx) => projectConsolidateCmd(ctx.args.IDENTIFIER as string, { yes: ctx.opts.yes === true, dryRun: ctx.opts[\"dry-run\"] === true }),\n},\n{\n  name: \"promote\",\n  summary: \"Turn a session note into a new standalone project\",\n  description: \"Turn a session note into a new standalone project.\",\n  options: [{ name: \"from-session\", type: \"string\", value: \"PATH\", summary: \"Path to the session note markdown file\" }, { name: \"to\", type: \"string\", value: \"PATH\", summary: \"Directory path for the new project (must not exist)\" }, { name: \"name\", type: \"string\", value: \"NAME\", summary: \"Display name for the new project (derived from filename if omitted)\" }],\n  examples: [\"pai project promote --from-session ./Notes/0001-idea.md --name idea\",\"pai project promote --from-session ./Notes/0001-idea.md --to ~/code/idea\"],\n  seeAlso: [\"pai project go\",\"pai project consolidate\"],\n  run: (ctx) => {\n    if (ctx.opts[\"from-session\"] === undefined) throw new CliError(\"missing required option --from-session\", \"pai help project promote\", 2);\n    if (ctx.opts.to === undefined) throw new CliError(\"missing required option --to\", \"pai help project promote\", 2);\n    return projectPromoteCmd({ fromSession: ctx.opts[\"from-session\"] as string, to: ctx.opts.to as string, name: ctx.opts.name as string | undefined });\n  },\n},\n{\n  name: \"go\",\n  summary: \"Print the folder of a project (for shell scripts)\",\n  description: \"Print the folder of a project (for shell scripts).\\n\\nPrint the root path for a project by slug, partial name, or fuzzy match.\\nDesigned for shell integration: cd $(pai projects go <query>)\",\n  args: [{ name: \"QUERY\", summary: \"Name or part of a name\", required: true }],\n  examples: [\"pai project go my-app\",\"cd \\\"$(pai project go my-app)\\\"\"],\n  seeAlso: [\"pai project name\",\"pai project promote\"],\n  run: (ctx) => projectGoCmd(ctx.args.QUERY as string),\n},\n{\n  name: \"name\",\n  summary: \"Give a project a short name for quick access\",\n  description: \"Give a project a short name for quick access.\",\n  args: [{ name: \"IDENTIFIER\", summary: \"Project name, short name, number or part of its path\", required: true }, { name: \"SHORTNAME\", summary: \"The short name\", required: true }],\n  options: [{ name: \"permission\", type: \"string\", value: \"LEVEL\", summary: \"Permission level: full | trusted | default\" }],\n  examples: [\"pai project name my-app app\",\"pai project name my-app app --permission auto\"],\n  seeAlso: [\"pai project unname\",\"pai project go\"],\n  run: (ctx) => projectNameCmd(ctx.args.IDENTIFIER as string, ctx.args.SHORTNAME as string, { permission: ctx.opts.permission as string | undefined }),\n},\n{\n  name: \"unname\",\n  summary: \"Remove a project's short name\",\n  description: \"Remove a project's short name.\",\n  args: [{ name: \"SHORTNAME\", summary: \"The short name\", required: true }],\n  examples: [\"pai project unname app\",\"pai project names\"],\n  seeAlso: [\"pai project names\",\"pai project name\"],\n  run: (ctx) => projectUnnameCmd(ctx.args.SHORTNAME as string),\n},\n{\n  name: \"names\",\n  summary: \"List the projects that have a short name\",\n  description: \"List the projects that have a short name.\\n\\nList named projects (your curated shortlist)\",\n  options: [{ name: \"json\", type: \"boolean\", summary: \"Output JSON for AIBroker consumption\" }, { name: \"all\", type: \"boolean\", summary: \"Include ALL active registered projects, not just the named shortlist\" }],\n  examples: [\"pai project names\",\"pai project names --json\"],\n  seeAlso: [\"pai project config\",\"pai project unname\"],\n  run: (ctx) => projectNamesCmd({ json: ctx.opts.json === true, all: ctx.opts.all === true }),\n},\n{\n  name: \"config\",\n  summary: \"Show or change how sessions of a project start\",\n  description: \"Show or change how sessions of a project start.\\n\\nView or modify session launch config for a project.\\nUse --options to discover available keys and presets.\",\n  args: [{ name: \"IDENTIFIER\", summary: \"Project name, short name, number or part of its path\" }, { name: \"MORE\", summary: \"More values for the list option above\", variadic: true }],\n  options: [{ name: \"set\", type: \"string\", value: \"KEY=VALUE\", summary: \"Set config values (repeatable) (several values allowed)\", repeatable: true }, { name: \"unset\", type: \"string\", value: \"KEY\", summary: \"Remove config keys (repeatable, use env.KEY for env vars) (several values allowed)\", repeatable: true }, { name: \"preset\", type: \"string\", value: \"NAME\", summary: \"Apply a permission preset: full | trusted | default\" }, { name: \"defaults\", type: \"boolean\", summary: \"Manage global session defaults instead of a project\" }, { name: \"options\", type: \"boolean\", summary: \"List available config keys and presets\" }, { name: \"json\", type: \"boolean\", summary: \"Output JSON\" }, { name: \"reset\", type: \"boolean\", summary: \"Reset config to empty (inherit global defaults)\" }],\n  examples: [\"pai project config my-app\",\"pai project config my-app --options\"],\n  seeAlso: [\"pai project mcp\",\"pai project names\"],\n  run: (ctx) => projectConfigCmd(ctx.args.IDENTIFIER as string | undefined, { set: (ctx.multi?.set ?? []) as string[], unset: (ctx.multi?.unset ?? []) as string[], preset: ctx.opts.preset as string | undefined, defaults: ctx.opts.defaults === true, options: ctx.opts.options === true, json: ctx.opts.json === true, reset: ctx.opts.reset === true }),\n},\n{\n  name: \"mcp\",\n  summary: \"Show or set the MCP servers a project's session loads\",\n  description: \"Show or set the MCP servers a project's session loads.\\n\\nView or set the MCP servers this project's interactive supervisor session\\nloads (run from inside the project, like `pai projects here`).\\nNo args: print the current value. --set <names> (comma-separated): set it,\\nvalidated against ~/.claude.json's mcpServers. --clear: unset (all\\nservers load — today's default). Positional names are an error.\",\n  args: [{ name: \"NAMES\", summary: \"Comma-separated names (leave out to just show the current ones)\", variadic: true }],\n  options: [{ name: \"set\", type: \"string\", value: \"NAMES\", summary: \"Set the pin (comma-separated server or set names)\" }, { name: \"clear\", type: \"boolean\", summary: \"Unset — all MCP servers load again\" }],\n  examples: [\"pai project mcp\",\"pai project mcp --set pai\"],\n  seeAlso: [\"pai project pins\",\"pai project config\"],\n  run: (ctx) => projectMcpCmd((ctx.args.NAMES as string[] | undefined) ?? [], { clear: ctx.opts.clear === true, set: ctx.opts.set as string | undefined }),\n},\n{\n  name: \"pins\",\n  summary: \"List the MCP/tool pins of the current project\",\n  description: \"List the MCP/tool pins of the current project.\\n\\nPrint one line per active MCP/tools pin for the current directory's project (nothing when unpinned)\",\n  examples: [\"pai project pins\",\"pai project mcp\"],\n  seeAlso: [\"pai project tools\",\"pai project mcp\"],\n  run: (ctx) => projectPinsCmd(),\n},\n{\n  name: \"tools\",\n  summary: \"Show or set the built-in tools a project's session loads\",\n  description: \"Show or set the built-in tools a project's session loads.\\n\\nView or set the built-in tool schemas this project's interactive\\nsupervisor session loads (run from inside the project, like `pai projects\\nhere`). No args: print the current value. --set <names> (comma-separated):\\nset it. --clear: unset (all tools load — today's default). Positional\\nnames are an error.\",\n  args: [{ name: \"NAMES\", summary: \"Comma-separated names (leave out to just show the current ones)\", variadic: true }],\n  options: [{ name: \"set\", type: \"string\", value: \"NAMES\", summary: \"Set the pin (comma-separated tool names)\" }, { name: \"clear\", type: \"boolean\", summary: \"Unset — all built-in tools load again\" }],\n  examples: [\"pai project tools\",\"pai project tools --set Read,Bash\"],\n  seeAlso: [\"pai project pins\"],\n  run: (ctx) => projectToolsCmd((ctx.args.NAMES as string[] | undefined) ?? [], { clear: ctx.opts.clear === true, set: ctx.opts.set as string | undefined }),\n},\n];\n\nexport const projectCommand: CommandDef = {\n  name: \"project\",\n  summary: \"Your registered projects: list, add, rename, check\",\n  description: \"Your registered projects: list, add, rename, check.\",\n  examples: [\"pai project list\",\"pai project info my-app\"],\n  subcommands: projectSubcommands,\n};\n\nexport const projectAliasCommand: CommandDef = {\n  name: \"projects\",\n  summary: \"Same as project\",\n  description: \"Same as `pai project`: Your registered projects: list, add, rename, check.\",\n  examples: [\"pai projects list\",\"pai projects info my-app\"],\n  aliasOf: \"project\",\n  subcommands: projectSubcommands,\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 \"../../lib/utils.js\";\nimport { slugify, parseSessionFilename } from \"../../../registry/migrate.js\";\nimport { upsertProject, upsertSession } from \"./utils.js\";\nimport { getRegistryBackend } from \"../../../storage/factory.js\";\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 async function cmdMigrate(): Promise<void> {\n  if (!existsSync(SESSION_REGISTRY_PATH())) {\n    console.error(err(`session-registry.json not found: ${SESSION_REGISTRY_PATH()}`));\n    process.exitCode = 1;\n    return;\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.exitCode = 1;\n    return;\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  const backend = await getRegistryBackend();\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 upserted = await upsertProject(backend, slug, rootPath, encodedDir);\n      if (!upserted) {\n        projectsSkipped++;\n        continue;\n      }\n      const { isNew, id } = upserted;\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 = await upsertSession(backend, 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","/**\n * pai registry dedupe [--execute]\n *\n * Merges registry rows that describe the same project.\n *\n * WHY THIS HAPPENS\n * ----------------\n * `projects.root_path` is UNIQUE, so two spellings of the same directory\n * register as two projects. On a machine with symlinked path prefixes\n * (`~/dev -> Cloud/Development`, say) the same repo is reachable as\n * `~/dev/PAI` and `~/Cloud/Development/PAI`, and the scanner\n * registers both. Every session then lands under whichever spelling the shell\n * happened to be using, so history splits: one row accumulates 95 sessions and\n * the other has none, while both claim to be the same project.\n *\n * The symptom that surfaces it is a checkpoint labelled \"Unknown session\" —\n * the row matching the current directory genuinely has no session rows.\n *\n * WHY realpath IS THE RIGHT KEY\n * -----------------------------\n * It removes the judgement call. Two rows whose `root_path` resolves to the\n * same inode are the same project — not \"probably\", definitively — so the\n * merge is mechanical. Rows that merely look similar (same display name, one\n * path a prefix of another) are left alone: deciding those is the user's call,\n * not this command's.\n *\n * NO DATA LOSS\n * ------------\n * Everything referencing a merged row is repointed, never deleted:\n * sessions (renumbered only on collision), compaction_log, aliases, links,\n * project_tags, and any child project's parent_id. The DB is backed up before\n * anything is written, and the whole merge runs in one transaction.\n */\n\nimport { realpathSync, copyFileSync, existsSync } from \"node:fs\";\nimport { ok, warn, err, dim, bold } from \"../../lib/utils.js\";\nimport { getRegistryBackend } from \"../../../storage/factory.js\";\nimport type { RegistryBackend } from \"../../../storage/registry-interface.js\";\n\ninterface ProjectRow {\n  id: number;\n  slug: string;\n  display_name: string;\n  root_path: string;\n  encoded_dir: string;\n  status: string;\n  claude_notes_dir: string | null;\n  session_config: string | null;\n  created_at: number;\n}\n\ninterface MergeGroup {\n  realPath: string;\n  canonical: ProjectRow;\n  duplicates: ProjectRow[];\n}\n\ninterface Counts {\n  sessions: number;\n  compaction: number;\n  aliases: number;\n  links: number;\n  tags: number;\n  children: number;\n}\n\n// ---------------------------------------------------------------------------\n// Analysis\n// ---------------------------------------------------------------------------\n\n/** Resolve a path to its canonical form, or null when it no longer exists. */\nfunction safeRealPath(p: string): string | null {\n  try {\n    return realpathSync(p);\n  } catch {\n    return null;\n  }\n}\n\nasync function countRefs(backend: RegistryBackend, projectId: number): Promise<Counts> {\n  return {\n    sessions: await backend.countSessionsForProject(projectId),\n    compaction: await backend.countCompactionLogsForProject(projectId),\n    aliases: await backend.countAliasesForProject(projectId),\n    links: await backend.countLinksForProject(projectId),\n    tags: await backend.countProjectTagsForProject(projectId),\n    children: await backend.countChildProjects(projectId),\n  };\n}\n\n/**\n * Choose which row survives.\n *\n * The row whose stored path already IS the canonical filesystem path wins:\n * keeping it means `root_path` ends up correct without rewriting it, and any\n * external reference to that path keeps resolving. Failing that, the row with\n * the most sessions wins, then the oldest.\n */\nasync function pickCanonical(rows: ProjectRow[], realPath: string, backend: RegistryBackend): Promise<ProjectRow> {\n  const exact = rows.filter((r) => r.root_path === realPath);\n  const pool = exact.length > 0 ? exact : rows;\n\n  const withSessions = await Promise.all(\n    pool.map(async (r) => ({ row: r, sessions: (await countRefs(backend, r.id)).sessions }))\n  );\n  withSessions.sort((a, b) => {\n    if (a.sessions !== b.sessions) return b.sessions - a.sessions;\n    return a.row.created_at - b.row.created_at;\n  });\n  return withSessions[0].row;\n}\n\nexport async function analyzeDuplicates(backend: RegistryBackend): Promise<{\n  groups: MergeGroup[];\n  stalePaths: ProjectRow[];\n}> {\n  const rows: ProjectRow[] = await backend.listProjects({ orderBy: \"id\" });\n\n  const byRealPath = new Map<string, ProjectRow[]>();\n  const stalePaths: ProjectRow[] = [];\n\n  for (const row of rows) {\n    const rp = safeRealPath(row.root_path);\n    if (!rp) {\n      // The directory is gone. It cannot be proven identical to anything, so\n      // it is never merged automatically — only reported.\n      stalePaths.push(row);\n      continue;\n    }\n    const bucket = byRealPath.get(rp);\n    if (bucket) bucket.push(row);\n    else byRealPath.set(rp, [row]);\n  }\n\n  const groups: MergeGroup[] = [];\n  for (const [realPath, members] of byRealPath) {\n    if (members.length < 2) continue;\n    const canonical = await pickCanonical(members, realPath, backend);\n    groups.push({\n      realPath,\n      canonical,\n      duplicates: members.filter((m) => m.id !== canonical.id),\n    });\n  }\n\n  return { groups, stalePaths };\n}\n\n// ---------------------------------------------------------------------------\n// Merge\n// ---------------------------------------------------------------------------\n\ninterface MergeReport {\n  movedSessions: number;\n  foldedSessions: number;\n  renumbered: Array<{ from: number; to: number; title: string }>;\n  movedCompaction: number;\n  movedAliases: number;\n  movedLinks: number;\n  movedTags: number;\n  movedChildren: number;\n  deletedRows: number;\n  reclaimedSlugs: Array<{ from: string; to: string }>;\n}\n\n/**\n * Fold a duplicate session row into the one the canonical project already has\n * for the same note file.\n *\n * Both rows describe one file on disk — the double registration produced two\n * registry rows for it, often identical and created milliseconds apart. Moving\n * both would leave the merged project with two entries per note and force one\n * of them to a number that no longer matches its own filename.\n *\n * Nothing is discarded: tags and links are repointed at the surviving row, and\n * any field the survivor is missing is backfilled from the duplicate before it\n * is deleted.\n */\nasync function foldSession(backend: RegistryBackend, keepId: number, dropId: number): Promise<void> {\n  // session_tags PK is (session_id, tag_id) — skip tags the survivor has.\n  const tagIds = await backend.listSessionTagIds(dropId);\n  for (const tagId of tagIds) {\n    const clash = await backend.sessionHasTag(keepId, tagId);\n    if (clash) {\n      await backend.deleteSessionTag(dropId, tagId);\n    } else {\n      await backend.reassignSessionTag(dropId, keepId, tagId);\n    }\n  }\n\n  // links UNIQUE(session_id, target_project_id)\n  const links = await backend.listLinksForSession(dropId);\n  for (const l of links) {\n    const clash = await backend.linkExists(keepId, l.target_project_id);\n    if (clash) await backend.deleteLink(l.id);\n    else await backend.moveLinkToSession(l.id, keepId);\n  }\n\n  await backend.moveCompactionLogsBySession(dropId, keepId);\n\n  // Backfill anything the survivor lacks, and prefer a finished status.\n  await backend.backfillFoldedSession(keepId, dropId);\n\n  await backend.deleteSession(dropId);\n}\n\nasync function mergeGroup(backend: RegistryBackend, group: MergeGroup): Promise<MergeReport> {\n  const report: MergeReport = {\n    movedSessions: 0,\n    foldedSessions: 0,\n    renumbered: [],\n    movedCompaction: 0,\n    movedAliases: 0,\n    movedLinks: 0,\n    movedTags: 0,\n    movedChildren: 0,\n    deletedRows: 0,\n    reclaimedSlugs: [],\n  };\n\n  const canonicalId = group.canonical.id;\n\n  // Highest session number already used on the canonical row — collisions are\n  // appended above it so no existing number ever has to change.\n  let nextNumber = (await backend.getMaxSessionNumber(canonicalId)) + 1;\n\n  for (const dup of group.duplicates) {\n    const sessions = await backend.listSessionsForProject(dup.id, { orderBy: \"number_asc\" });\n\n    for (const s of sessions) {\n      // Same file already registered on the canonical row? Then this is the\n      // same session recorded twice, not a second session. Fold, do not move.\n      const twin = await backend.findSessionByFilename(canonicalId, s.filename);\n\n      if (twin) {\n        await foldSession(backend, twin.id, s.id);\n        report.foldedSessions++;\n        continue;\n      }\n\n      const clash = await backend.sessionNumberTaken(canonicalId, s.number);\n\n      if (clash) {\n        const assigned = nextNumber++;\n        await backend.moveSessionToProject(s.id, canonicalId, { number: assigned });\n        report.renumbered.push({\n          from: s.number,\n          to: assigned,\n          title: s.title,\n        });\n      } else {\n        await backend.moveSessionToProject(s.id, canonicalId);\n        if (s.number >= nextNumber) nextNumber = s.number + 1;\n      }\n      report.movedSessions++;\n    }\n\n    report.movedCompaction += await backend.moveCompactionLogsByProject(dup.id, canonicalId);\n\n    // aliases.alias is the PK, so an alias already pointing at the canonical\n    // row would collide. Repoint what can move, drop only exact duplicates.\n    const dupAliases = await backend.listAliasesForProject(dup.id);\n    for (const alias of dupAliases) {\n      const existingProjectId = await backend.resolveAlias(alias);\n      if (existingProjectId === canonicalId) continue;\n      await backend.reassignAlias(alias, canonicalId);\n      report.movedAliases++;\n    }\n\n    // links has UNIQUE(session_id, target_project_id).\n    const dupLinks = await backend.listLinksForProject(dup.id);\n    for (const l of dupLinks) {\n      const clash = await backend.linkExists(l.session_id, canonicalId);\n      if (clash) {\n        await backend.deleteLink(l.id);\n        continue;\n      }\n      await backend.retargetLink(l.id, canonicalId);\n      report.movedLinks++;\n    }\n\n    // project_tags has PRIMARY KEY(project_id, tag_id).\n    const dupTagIds = await backend.listProjectTagIds(dup.id);\n    for (const tagId of dupTagIds) {\n      const clash = await backend.projectHasTag(canonicalId, tagId);\n      if (clash) {\n        await backend.deleteProjectTag(dup.id, tagId);\n        continue;\n      }\n      await backend.reassignProjectTag(dup.id, canonicalId, tagId);\n      report.movedTags++;\n    }\n\n    report.movedChildren += await backend.reassignProjectParent(dup.id, canonicalId);\n\n    // Carry over settings the canonical row is missing rather than losing them.\n    if (!group.canonical.claude_notes_dir && dup.claude_notes_dir) {\n      await backend.updateProjectClaudeNotesDir(canonicalId, dup.claude_notes_dir);\n    }\n    if (!group.canonical.session_config && dup.session_config) {\n      await backend.updateProjectSessionConfig(canonicalId, dup.session_config);\n    }\n\n    await backend.deleteProject(dup.id);\n    report.deletedRows++;\n  }\n\n  // An active duplicate means the project is active, whatever the canonical\n  // row happened to say.\n  const anyActive =\n    group.canonical.status === \"active\" ||\n    group.duplicates.some((d) => d.status === \"active\");\n  if (anyActive && group.canonical.status !== \"active\") {\n    await backend.updateProjectStatus(canonicalId, \"active\");\n  }\n\n  // Reclaim the good slug.\n  //\n  // The duplicate rows were created in path order, so the plain slug (\"pai\")\n  // usually went to whichever spelling was seen first and the canonical row —\n  // chosen by filesystem path, not by name — ended up with the suffixed one\n  // (\"pai-2\"). Now that the duplicates are gone their names are free, so take\n  // the best one back rather than leaving the merged project called \"pai-2\".\n  const suffixed = /^(.*)-\\d+$/.exec(group.canonical.slug);\n  if (suffixed) {\n    const candidates = [\n      suffixed[1],\n      ...group.duplicates.map((d) => d.slug).filter((s) => !/-\\d+$/.test(s)),\n    ];\n    for (const candidate of candidates) {\n      const taken = await backend.getProjectBySlug(candidate);\n      if (taken) continue;\n      await backend.updateProjectSlug(canonicalId, candidate, Date.now());\n      report.reclaimedSlugs.push({\n        from: group.canonical.slug,\n        to: candidate,\n      });\n      break;\n    }\n  }\n\n  // Point the surviving row at the canonical filesystem path.\n  if (group.canonical.root_path !== group.realPath) {\n    const taken = await backend.getProjectByRootPath(group.realPath, { excludeId: canonicalId });\n    if (!taken) {\n      await backend.updateProjectPath(canonicalId, { rootPath: group.realPath }, Date.now());\n    }\n  }\n\n  return report;\n}\n\n/**\n * Rewrite any remaining root_path that is not already its canonical form.\n *\n * Merging fixes the rows that already split. This stops the rest from\n * splitting later: a project registered only as `~/dev/PAILot` still has a\n * non-canonical path, so the first session started from the other spelling\n * would create a second row and the whole problem recurs.\n *\n * Only rows whose canonical path is free are touched — a collision would mean\n * a duplicate, and those have already been merged.\n */\nasync function canonicalizePaths(\n  backend: RegistryBackend,\n  dryRun: boolean\n): Promise<Array<{ slug: string; from: string; to: string }>> {\n  const rows = await backend.listProjects();\n\n  const changed: Array<{ slug: string; from: string; to: string }> = [];\n\n  for (const row of rows) {\n    const rp = safeRealPath(row.root_path);\n    if (!rp || rp === row.root_path) continue;\n\n    const taken = await backend.getProjectByRootPath(rp, { excludeId: row.id });\n    if (taken) continue;\n\n    changed.push({ slug: row.slug, from: row.root_path, to: rp });\n    if (!dryRun) {\n      await backend.updateProjectPath(row.id, { rootPath: rp }, Date.now());\n    }\n  }\n\n  return changed;\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nfunction backupDb(dbPath: string): string | null {\n  if (!existsSync(dbPath)) return null;\n  const stamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n  const dest = `${dbPath}.bak-${stamp}`;\n  try {\n    copyFileSync(dbPath, dest);\n    // Copy the WAL too — without it the backup can miss committed pages.\n    for (const suffix of [\"-wal\", \"-shm\"]) {\n      if (existsSync(dbPath + suffix)) {\n        copyFileSync(dbPath + suffix, dest + suffix);\n      }\n    }\n    return dest;\n  } catch {\n    return null;\n  }\n}\n\nexport async function cmdDedupe(\n  opts: { execute?: boolean; dbPath?: string }\n): Promise<void> {\n  const backend = await getRegistryBackend();\n  const { groups, stalePaths } = await analyzeDuplicates(backend);\n\n  console.log();\n  console.log(\n    bold(\n      opts.execute\n        ? \"  pai registry dedupe\"\n        : \"  pai registry dedupe — DRY RUN (no changes will be made)\"\n    )\n  );\n  console.log();\n\n  if (groups.length === 0) {\n    console.log(ok(\"No duplicate projects found — every root_path resolves uniquely.\"));\n  }\n\n  for (const g of groups) {\n    console.log(`  ${bold(g.realPath)}`);\n    const cCounts = await countRefs(backend, g.canonical.id);\n    console.log(\n      `    ${ok(\"keep\")}  #${g.canonical.id} ${bold(g.canonical.slug)} ` +\n        dim(`(${g.canonical.root_path}) — ${cCounts.sessions} sessions, ${g.canonical.status}`)\n    );\n    for (const d of g.duplicates) {\n      const dc = await countRefs(backend, d.id);\n      const moving = [\n        dc.sessions ? `${dc.sessions} sessions` : null,\n        dc.compaction ? `${dc.compaction} compaction rows` : null,\n        dc.aliases ? `${dc.aliases} aliases` : null,\n        dc.links ? `${dc.links} links` : null,\n        dc.tags ? `${dc.tags} tags` : null,\n        dc.children ? `${dc.children} child projects` : null,\n      ]\n        .filter(Boolean)\n        .join(\", \");\n      console.log(\n        `    ${warn(\"merge\")} #${d.id} ${bold(d.slug)} ` +\n          dim(`(${d.root_path}) — ${moving || \"nothing to move\"}`)\n      );\n    }\n    console.log();\n  }\n\n  if (stalePaths.length > 0) {\n    console.log(\n      dim(\n        `  ${stalePaths.length} project(s) point at a path that no longer exists.`\n      )\n    );\n    console.log(\n      dim(\n        \"  These are never merged automatically — a missing directory cannot be\"\n      )\n    );\n    console.log(dim(\"  proven identical to anything else. Listed for review:\"));\n    for (const s of stalePaths) {\n      const sc = await countRefs(backend, s.id);\n      if (sc.sessions === 0 && sc.compaction === 0) continue;\n      console.log(\n        dim(\n          `    #${s.id} ${s.slug} (${s.root_path}) — ${sc.sessions} sessions, ${sc.compaction} compaction rows`\n        )\n      );\n    }\n    console.log();\n  }\n\n  const pathFixes = await canonicalizePaths(backend, true);\n  if (pathFixes.length > 0) {\n    console.log(\n      dim(\n        `  ${pathFixes.length} project(s) stored under a non-canonical path — these would`\n      )\n    );\n    console.log(\n      dim(\n        \"  split into a second row the first time a session runs from the other\"\n      )\n    );\n    console.log(dim(\"  spelling. They will be rewritten to their resolved path:\"));\n    for (const p of pathFixes.slice(0, 8)) {\n      console.log(dim(`    ${p.slug}: ${p.from} -> ${p.to}`));\n    }\n    if (pathFixes.length > 8) {\n      console.log(dim(`    ... and ${pathFixes.length - 8} more`));\n    }\n    console.log();\n  }\n\n  if (!opts.execute) {\n    if (groups.length > 0 || pathFixes.length > 0) {\n      console.log(dim(\"  Re-run with --execute to apply. The registry is backed up first.\"));\n      console.log();\n    }\n    return;\n  }\n\n  if (groups.length === 0 && pathFixes.length === 0) return;\n\n  // ---- Back up before touching anything ----\n  const dbPath = opts.dbPath;\n  if (dbPath) {\n    const backup = backupDb(dbPath);\n    if (backup) {\n      console.log(ok(`Registry backed up to ${bold(backup)}`));\n    } else {\n      console.error(\n        err(\"Could not back up the registry — refusing to merge without one.\")\n      );\n      process.exitCode = 1;\n      return;\n    }\n  }\n\n  // ---- Merge, all-or-nothing ----\n  const totals: MergeReport = {\n    movedSessions: 0,\n    foldedSessions: 0,\n    renumbered: [],\n    movedCompaction: 0,\n    movedAliases: 0,\n    movedLinks: 0,\n    movedTags: 0,\n    movedChildren: 0,\n    deletedRows: 0,\n    reclaimedSlugs: [],\n  };\n\n  let canonicalized = 0;\n\n  // Best-effort in sequence: RegistryBackend has no cross-row transaction\n  // primitive, so a failure partway through no longer rolls back everything\n  // the way the old raw-SQL transaction did — it leaves whatever merged\n  // successfully in place and reports the point of failure.\n  try {\n    for (const g of groups) {\n      const r = await mergeGroup(backend, g);\n      totals.movedSessions += r.movedSessions;\n      totals.foldedSessions += r.foldedSessions;\n      totals.renumbered.push(...r.renumbered);\n      totals.movedCompaction += r.movedCompaction;\n      totals.movedAliases += r.movedAliases;\n      totals.movedLinks += r.movedLinks;\n      totals.movedTags += r.movedTags;\n      totals.movedChildren += r.movedChildren;\n      totals.deletedRows += r.deletedRows;\n      totals.reclaimedSlugs.push(...r.reclaimedSlugs);\n    }\n    // After merging, the remaining collisions are gone, so the rest of the\n    // registry can be moved onto canonical paths safely.\n    canonicalized = (await canonicalizePaths(backend, false)).length;\n  } catch (e) {\n    console.error(err(`Merge failed partway through: ${String(e)}`));\n    process.exitCode = 1;\n    return;\n  }\n\n  console.log();\n  console.log(ok(`Merged ${totals.deletedRows} duplicate row(s).`));\n  console.log(`    sessions moved:        ${totals.movedSessions}`);\n  console.log(\n    `    sessions folded:       ${totals.foldedSessions} ` +\n      dim(\"(same note file registered twice)\")\n  );\n  console.log(`    compaction rows moved: ${totals.movedCompaction}`);\n  console.log(`    aliases moved:         ${totals.movedAliases}`);\n  console.log(`    links moved:           ${totals.movedLinks}`);\n  console.log(`    tags moved:            ${totals.movedTags}`);\n  console.log(`    child projects moved:  ${totals.movedChildren}`);\n  console.log(`    paths canonicalized:   ${canonicalized}`);\n\n  if (totals.reclaimedSlugs.length > 0) {\n    console.log();\n    console.log(ok(\"Slugs reclaimed from the merged rows:\"));\n    for (const s of totals.reclaimedSlugs) {\n      console.log(dim(`    ${s.from} -> ${s.to}`));\n    }\n  }\n\n  if (totals.renumbered.length > 0) {\n    console.log();\n    console.log(\n      warn(`${totals.renumbered.length} session(s) renumbered to avoid collisions:`)\n    );\n    for (const r of totals.renumbered) {\n      console.log(dim(`    ${r.from} -> ${r.to}  ${r.title}`));\n    }\n    console.log(\n      dim(\"    Session note filenames on disk are unchanged — only the registry number moved.\")\n    );\n    console.log();\n    console.log(\n      warn(\n        \"    A renumbered session's registry number no longer matches the number in its\"\n      )\n    );\n    console.log(\n      dim(\n        \"    filename. The scanner keys sessions by (project, number), so a note whose\\n\" +\n          \"    number is now occupied cannot be re-registered until numbering is reconciled.\\n\" +\n          \"    Run `pai session cleanup <slug> --execute` on the affected projects to renumber\\n\" +\n          \"    notes and registry together.\"\n      )\n    );\n  }\n  console.log();\n}\n","/**\n * `pai registry reconnect` — point projects back at the transcripts they lost.\n *\n * A project's `encoded_dir` is written once and never updated when the project\n * moves, so every transcript lookup for a moved project quietly returns\n * nothing: no handover, no session digest, no checkpoint. The failure is\n * invisible because an empty result and an unasked question look identical.\n *\n * Repairs are derived from the transcripts themselves — each records the `cwd`\n * it ran in — rather than from a naming rule, because the naming rule is what\n * broke. Dry-run by default: this rewrites registry rows, and a repair that\n * guesses wrong reattaches a project to someone else's history.\n */\n\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\nimport {\n  scanTranscriptFolders,\n  findMovedProjects,\n  transcriptFiles,\n  claudeProjectsDir,\n  type RegistryProjectRow,\n} from \"../../../registry/moved.js\";\nimport { getRegistryBackend } from \"../../../storage/factory.js\";\nimport { encodeDir } from \"../../lib/utils.js\";\nimport { dim, ok, warn } from \"../../lib/utils.js\";\n\nexport async function cmdReconnect(opts: { execute?: boolean }): Promise<void> {\n  const backend = await getRegistryBackend();\n  const active = await backend.listProjectsWithSessionStats({ status: \"active\" });\n  const rows: RegistryProjectRow[] = active.map((p) => ({\n    id: p.id,\n    slug: p.slug,\n    root_path: p.root_path,\n    encoded_dir: p.encoded_dir,\n    sessions: p.session_count,\n  }));\n\n  const base = claudeProjectsDir();\n  const has = (name: string | null): boolean =>\n    Boolean(name) && transcriptFiles(join(base, name!)).length > 0;\n\n  // Mirrors resolveTranscriptDir: stored value first, derived from the current\n  // root path second. A project either of those already handles is not broken,\n  // and rewriting it would be churn dressed as a repair.\n  const resolvesNow = (r: RegistryProjectRow): boolean =>\n    has(r.encoded_dir) || has(encodeDir(r.root_path));\n\n  const moved = findMovedProjects(rows, scanTranscriptFolders(base), resolvesNow);\n\n  console.log();\n  if (moved.length === 0) {\n    console.log(ok(\"  Nothing to reconnect. \"));\n    console.log(dim(\"  Every active project resolves to a folder holding transcripts.\"));\n    console.log();\n    return;\n  }\n\n  console.log(\n    chalk.bold(`  ${moved.length} project(s) point at the wrong transcript folder`)\n  );\n  console.log();\n  for (const m of moved) {\n    const cost = m.sessions > 0 ? chalk.yellow(`${m.sessions} sessions unreachable`) : dim(\"no sessions recorded\");\n    console.log(`  ${chalk.cyan(m.slug)}  ${cost}`);\n    console.log(dim(`    stored:  ${m.storedDir ?? \"(none)\"}`));\n    console.log(dim(`    correct: ${m.correctDir}  (${m.transcripts} transcripts)`));\n  }\n  console.log();\n\n  if (!opts.execute) {\n    console.log(dim(\"  Dry run. Re-run with --execute to write these.\"));\n    console.log();\n    return;\n  }\n\n  // Best-effort in sequence: a partial repair would leave the registry in a\n  // state nobody chose, but RegistryBackend has no cross-row transaction\n  // primitive to make this atomic the way the old raw-SQL transaction was.\n  try {\n    for (const m of moved) {\n      await backend.updateProjectPath(m.id, { encodedDir: m.correctDir });\n    }\n    console.log(ok(`  Reconnected ${moved.length} project(s).`));\n    const recovered = moved.reduce((n, m) => n + m.sessions, 0);\n    if (recovered > 0) {\n      console.log(dim(`  ${recovered} session(s) are reachable again.`));\n    }\n  } catch (e) {\n    console.error(warn(\"  Reconnect failed partway through: \") + (e instanceof Error ? e.message : String(e)));\n  }\n  console.log();\n}\n","/** Registry command implementation without a parser dependency. */\n\nimport { ok, warn, err, dim, bold, fmtDate } from \"../../lib/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\";\nimport { cmdDedupe } from \"./dedupe.js\";\nimport { cmdReconnect } from \"./reconnect.js\";\nimport { legacyDbDisplayPaths } from \"../../../storage/sqlite/legacy-migration.js\";\nimport { getRegistryBackend } from \"../../../storage/factory.js\";\nimport type { RegistryBackend } from \"../../../storage/registry-interface.js\";\n\n// ---------------------------------------------------------------------------\n// stats\n// ---------------------------------------------------------------------------\n\nexport async function registryStatsCmd(): Promise<number> {\n  const backend = await getRegistryBackend();\n  const totalProjects = await backend.countProjects();\n  const activeProjects = await backend.countProjects({ status: \"active\" });\n  const archivedProjects = await backend.countProjects({ status: \"archived\" });\n  const totalSessions = await backend.countSessions();\n  const totalTags = (await backend.listAllTags()).length;\n\n  const lastProjectUpdatedAt = await backend.getMostRecentProjectUpdatedAt();\n  const lastSessionCreatedAt = await backend.getMostRecentSessionCreatedAt();\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 (lastProjectUpdatedAt) {\n    console.log(`  ${bold(\"Last updated:\")} ${fmtDate(lastProjectUpdatedAt)}`);\n  }\n  if (lastSessionCreatedAt) {\n    console.log(`  ${bold(\"Last session:\")} ${fmtDate(lastSessionCreatedAt)}`);\n  }\n  console.log();\n  return 0;\n}\n\n// ---------------------------------------------------------------------------\n// rebuild\n// ---------------------------------------------------------------------------\n\nexport async function registryRebuildCmd(): Promise<number> {\n  const backend = await getRegistryBackend();\n  console.log(warn(\"Rebuilding registry — all existing data will be erased.\"));\n  console.log(dim(\"Clearing all tables ...\"));\n\n  await backend.resetRegistry();\n\n  console.log(dim(\"Registry cleared. Re-scanning ...\"));\n  await cmdScan();\n  return 0;\n}\n\n// ---------------------------------------------------------------------------\n// lookup\n// ---------------------------------------------------------------------------\n\nexport async function registryLookupCmd(fsPath: string): Promise<number> {\n  const backend = await getRegistryBackend();\n  const resolved = resolve(fsPath);\n\n  const row = await backend.getProjectByRootPath(resolved);\n\n  if (!row) {\n    return 1;\n  }\n\n  process.stdout.write(row.slug + \"\\n\");\n  return 0;\n}\n\n// ---------------------------------------------------------------------------\n// scan\n// ---------------------------------------------------------------------------\n\nexport async function registryScanCmd(opts: {\n  addDir?: string;\n  removeDir?: string;\n  showDirs?: boolean;\n  quick?: boolean\n}): Promise<number> {\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 0;\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      return 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    return 0;\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    return 0;\n  }\n  if (!opts.addDir && !opts.removeDir) {\n    await cmdScan({ quick: opts.quick });\n  }\n  return 0;\n}\n\n// ---------------------------------------------------------------------------\n// migrate\n// ---------------------------------------------------------------------------\n\nexport async function registryMigrateCmd(): Promise<number> {\n  await cmdMigrate();\n  return 0;\n}\n\n// ---------------------------------------------------------------------------\n// dedupe\n// ---------------------------------------------------------------------------\n\nexport async function registryDedupeCmd(opts: { execute?: boolean }): Promise<number> {\n  await cmdDedupe({\n    execute: opts.execute,\n    dbPath: legacyDbDisplayPaths().registryDb,\n  });\n  return 0;\n}\n\n// ---------------------------------------------------------------------------\n// reconnect\n// ---------------------------------------------------------------------------\n\nexport async function registryReconnectCmd(opts: { execute?: boolean }): Promise<number> {\n  await cmdReconnect({ execute: opts.execute });\n  return 0;\n}","/**\n * `pai registry`: front end for the project registry.\n * Behaviour lives in src/cli/impl/registry/index.js; this file declares names, flags and help.\n */\n\nimport type { CommandDef } from \"../define.js\";\nimport { CliError } from \"../define.js\";\nimport {\n  registryStatsCmd,\n  registryRebuildCmd,\n  registryLookupCmd,\n  registryScanCmd,\n  registryMigrateCmd,\n  registryDedupeCmd,\n  registryReconnectCmd\n} from \"../impl/registry/index.js\";\n\nconst registryScanCommand: CommandDef = {\n  name: \"scan\",\n  summary: \"Find projects on disk and register them\",\n  description: \"Find projects on disk and register them.\\n\\nWalk ~/.claude/projects/ and configured scan_dirs, upsert all projects\",\n  options: [\n    { name: \"add-dir\", type: \"string\", value: \"PATH\", summary: \"Add a directory to scan_dirs config\" },\n    { name: \"remove-dir\", type: \"string\", value: \"PATH\", summary: \"Remove a directory from scan_dirs config\" },\n    { name: \"show-dirs\", type: \"boolean\", summary: \"Show currently configured scan directories\" },\n    { name: \"quick\", type: \"boolean\", summary: \"Minimal output mode (used by hooks and daemon)\" }\n  ],\n  examples: [\"pai registry scan\",\"pai registry scan --show-dirs\"],\n  seeAlso: [\"pai registry migrate\"],\n  run: (ctx) => registryScanCmd({\n    addDir: ctx.opts[\"add-dir\"] as string | undefined,\n    removeDir: ctx.opts[\"remove-dir\"] as string | undefined,\n    showDirs: ctx.opts[\"show-dirs\"] === true,\n    quick: ctx.opts.quick === true\n  }),\n};\n\nconst registryMigrateCommand: CommandDef = {\n  name: \"migrate\",\n  summary: \"Import the old session-registry.json\",\n  description: \"Import the old session-registry.json.\\n\\nImport data from ~/.claude/session-registry.json\",\n  examples: [\"pai registry migrate\",\"pai registry stats\"],\n  seeAlso: [\"pai registry stats\",\"pai registry scan\"],\n  run: (ctx) => registryMigrateCmd(),\n};\n\nconst registryStatsCommand: CommandDef = {\n  name: \"stats\",\n  summary: \"Count what the registry holds\",\n  description: \"Count what the registry holds.\\n\\nShow summary statistics for the registry\",\n  examples: [\"pai registry stats\",\"pai registry stats\"],\n  seeAlso: [\"pai registry rebuild\",\"pai registry migrate\"],\n  run: (ctx) => registryStatsCmd(),\n};\n\nconst registryRebuildCommand: CommandDef = {\n  name: \"rebuild\",\n  summary: \"Erase the registry and rebuild it from disk (destructive)\",\n  description: \"Erase the registry and rebuild it from disk (destructive).\\n\\nErase all registry data and rebuild from the filesystem (destructive)\",\n  examples: [\"pai registry rebuild\",\"pai registry stats\"],\n  seeAlso: [\"pai registry dedupe\",\"pai registry stats\"],\n  run: (ctx) => registryRebuildCmd(),\n};\n\nconst registryDedupeCommand: CommandDef = {\n  name: \"dedupe\",\n  summary: \"Merge registry rows that describe the same project\",\n  description: \"Merge registry rows that describe the same project.\\n\\nMerge registry rows that describe the same project.\\nTwo spellings of one directory (e.g. via a symlinked path prefix) register\\nas separate projects and split session history between them. Rows are grouped\\nby resolved path, so a merge only happens when the paths are provably identical.\\nDry-run by default; --execute backs up the registry first and merges in one transaction.\",\n  options: [{ name: \"execute\", type: \"boolean\", summary: \"Actually perform the merge (default is dry-run)\" }],\n  examples: [\"pai registry dedupe\",\"pai registry dedupe --execute\"],\n  seeAlso: [\"pai registry reconnect\",\"pai registry rebuild\"],\n  run: (ctx) => registryDedupeCmd({ execute: ctx.opts.execute === true }),\n};\n\nconst registryReconnectCommand: CommandDef = {\n  name: \"reconnect\",\n  summary: \"Reconnect projects to transcripts they lost\",\n  description: \"Reconnect projects to transcripts they lost.\\n\\nPoint projects back at the transcripts they lost.\\nA project's encoded_dir is written once and never updated when the project\\nmoves, so handovers, session digests and checkpoints silently find nothing.\\nRepairs are read from the transcripts themselves — each records the cwd it ran\\nin — rather than re-derived from the naming rule that broke.\\nDry-run by default; --execute writes the corrected rows in one transaction.\",\n  options: [{ name: \"execute\", type: \"boolean\", summary: \"Actually write the corrections (default is dry-run)\" }],\n  examples: [\"pai registry reconnect\",\"pai registry reconnect --execute\"],\n  seeAlso: [\"pai registry lookup\",\"pai registry dedupe\"],\n  run: (ctx) => registryReconnectCmd({ execute: ctx.opts.execute === true }),\n};\n\nconst registryLookupCommand: CommandDef = {\n  name: \"lookup\",\n  summary: \"Print the project name for a folder (for scripts)\",\n  description: \"Print the project name for a folder (for scripts).\\n\\nFind the project slug for a filesystem path (for use in scripts)\",\n  options: [{ name: \"path\", type: \"string\", value: \"PATH\", summary: \"Filesystem path to look up\" }],\n  examples: [\"pai registry lookup --path ~/code/my-app\",\"pai registry lookup --path .\"],\n  seeAlso: [\"pai registry reconnect\"],\n  run: (ctx) => {\n    if (ctx.opts.path === undefined) throw new CliError(\"missing required option --path\", \"pai help registry lookup\", 2);\n    return registryLookupCmd(ctx.opts.path as string);\n  },\n};\n\nexport const registrySubcommands: CommandDef[] = [\n  registryScanCommand,\n  registryMigrateCommand,\n  registryStatsCommand,\n  registryRebuildCommand,\n  registryDedupeCommand,\n  registryReconnectCommand,\n  registryLookupCommand,\n];\n\nexport const registryCommand: CommandDef = {\n  name: \"registry\",\n  summary: \"Maintain the project registry: scan, stats, rebuild\",\n  description: \"Maintain the project registry: scan, stats, rebuild.\",\n  examples: [\"pai registry stats\",\"pai registry scan --quick\"],\n  subcommands: registrySubcommands,\n};","/**\n * aibroker.ts — AIBroker transport for the task bus\n *\n * Shells out to the `aibroker` CLI. PAI's CLI cannot call MCP tools, so the\n * MCP-only dispatch primitives are unreachable from a shell — this is the\n * bridge.\n *\n * Optional by design: when AIBroker is not installed, `detect()` returns null\n * and the bus reports ownership instead of delivering. PAI must stay useful on\n * machines that do not run iTerm2.\n *\n * See docs/task-bus.md.\n */\n\nimport { execFile } from \"node:child_process\";\nimport type { ProbeAnswer } from \"../poller.js\";\nimport type { Transport, TransportResult } from \"../dispatch.js\";\n\n/**\n * How long AIBroker may spend on one dispatch, in seconds.\n *\n * A cold spawn measured ~10s on an idle machine, but boot time is not bounded:\n * under load a session can take considerably longer to start accepting input.\n */\nconst DEFAULT_DISPATCH_TIMEOUT_SECS = 180;\n\n/**\n * Margin between AIBroker's own deadline and when we kill the process.\n *\n * These two timeouts must never disagree. If ours fired first we would kill a\n * dispatch mid-flight and report a transport failure that AIBroker cannot\n * reproduce from its own CLI — a phantom bug, in the other repo, with no trace\n * on either side. So we always pass our budget down via `--timeout` and give\n * it room to time out first and tell us why.\n */\nconst KILL_MARGIN_MS = 15_000;\n\ninterface WireResult {\n  outcome?: string;\n  project?: string;\n  session?: string;\n  reason?: string;\n}\n\nconst VALID_OUTCOMES = new Set([\n  \"delivered\",\n  \"queued\",\n  \"spawned\",\n  \"unlaunchable\",\n  \"unreachable\",\n  \"skipped\",\n]);\n\n/**\n * Run `aibroker dispatch`, feeding the message over stdin.\n *\n * stdin rather than argv is deliberate: task bodies carry the full procedure\n * and reasoning, so they are long, multi-line, and full of quotes and\n * backticks. argv would mangle them or hit length limits.\n */\nfunction run(bin: string, args: string[], stdin: string, killAfterMs: number): Promise<string> {\n  return new Promise((resolve, reject) => {\n    const child = execFile(\n      bin,\n      args,\n      { timeout: killAfterMs, maxBuffer: 1024 * 1024 },\n      (error, stdout, stderr) => {\n        // A non-zero exit carrying parseable JSON is a reported outcome, not a\n        // crash — let the caller interpret it rather than throwing here.\n        if (error && !stdout.trim().startsWith(\"{\")) {\n          reject(new Error(stderr.trim() || error.message));\n          return;\n        }\n        resolve(stdout);\n      },\n    );\n    child.stdin?.end(stdin);\n  });\n}\n\nexport class AiBrokerTransport implements Transport {\n  constructor(\n    private readonly bin = \"aibroker\",\n    private readonly timeoutSecs = DEFAULT_DISPATCH_TIMEOUT_SECS,\n  ) {}\n\n  async dispatch(\n    project: string,\n    message: string,\n    opts: { spawnIfAbsent: boolean },\n  ): Promise<TransportResult> {\n    const args = [\"dispatch\", project, \"--stdin\", \"--json\", \"--timeout\", String(this.timeoutSecs)];\n    if (!opts.spawnIfAbsent) args.push(\"--no-spawn\");\n\n    const stdout = await run(\n      this.bin,\n      args,\n      message,\n      this.timeoutSecs * 1000 + KILL_MARGIN_MS,\n    );\n\n    // The CLI prints diagnostics before its JSON, so take the last JSON object\n    // rather than assuming stdout is clean.\n    const start = stdout.lastIndexOf(\"{\");\n    if (start === -1) {\n      throw new Error(`aibroker returned no JSON: ${stdout.trim().slice(0, 200)}`);\n    }\n\n    let wire: WireResult;\n    try {\n      wire = JSON.parse(stdout.slice(start)) as WireResult;\n    } catch {\n      throw new Error(`aibroker returned malformed JSON: ${stdout.trim().slice(0, 200)}`);\n    }\n\n    // Treat an unrecognised outcome as unlaunchable rather than trusting it.\n    // Silently passing through an unknown string would report success for a\n    // task that never arrived.\n    if (!wire.outcome || !VALID_OUTCOMES.has(wire.outcome)) {\n      return {\n        outcome: \"unlaunchable\",\n        session: wire.session ?? project,\n        reason: `unexpected outcome from aibroker: ${String(wire.outcome)}`,\n      };\n    }\n\n    return {\n      outcome: wire.outcome as TransportResult[\"outcome\"],\n      session: wire.session ?? wire.project ?? project,\n      reason: wire.reason,\n    };\n  }\n}\n\n/**\n * Liveness prober backed by `aibroker ask`.\n *\n * Separate from Transport on purpose: asking must NEVER spawn. A probe that\n * creates the session it is probing for turns \"this session died\" into \"all is\n * well\", which is the failure the probe exists to catch.\n */\nexport class AiBrokerProber {\n  constructor(\n    private readonly bin = \"aibroker\",\n    private readonly timeoutSecs = 60,\n  ) {}\n\n  async ask(project: string, question: string): Promise<ProbeAnswer> {\n    let stdout: string;\n    try {\n      stdout = await run(\n        this.bin,\n        [\"ask\", project, \"--stdin\", \"--json\", \"--timeout\", String(this.timeoutSecs)],\n        question,\n        this.timeoutSecs * 1000 + KILL_MARGIN_MS,\n      );\n    } catch (e) {\n      return { state: \"silent\", reason: e instanceof Error ? e.message : String(e) };\n    }\n\n    const start = stdout.lastIndexOf(\"{\");\n    if (start === -1) return { state: \"silent\", reason: \"aibroker returned no JSON\" };\n\n    try {\n      const wire = JSON.parse(stdout.slice(start)) as {\n        replied?: boolean;\n        reply?: string;\n        reason?: string;\n        state?: string;\n      };\n\n      // Trust `state` when present. Fall back to `replied` for an older\n      // aibroker that predates it — but never infer \"busy\" from silence, which\n      // is the whole reason the fourth state exists.\n      const state = wire.state ?? (wire.replied === true ? \"replied\" : \"silent\");\n      if (state === \"replied\" || state === \"busy\" || state === \"silent\" || state === \"absent\") {\n        return { state, reply: wire.reply, reason: wire.reason };\n      }\n      // An unrecognised state must not read as healthy.\n      return { state: \"silent\", reason: `unexpected state from aibroker: ${String(wire.state)}` };\n    } catch {\n      return { state: \"silent\", reason: \"aibroker returned malformed JSON\" };\n    }\n  }\n}\n\n/**\n * Return a prober if the installed `aibroker` supports `ask`.\n *\n * Probes for the subcommand rather than the binary: an older AIBroker would\n * otherwise fail once per stuck task instead of the scheduler simply knowing it\n * has no liveness check and saying so.\n */\nexport async function detectProber(bin = \"aibroker\"): Promise<AiBrokerProber | null> {\n  try {\n    const help = await new Promise<string>((resolve, reject) => {\n      execFile(bin, [\"help\"], { timeout: 10_000 }, (error, stdout, stderr) => {\n        if (error && !stdout) reject(error);\n        else resolve(stdout + stderr);\n      });\n    });\n    return /\\bask\\b/.test(help) ? new AiBrokerProber(bin) : null;\n  } catch {\n    return null;\n  }\n}\n\n/**\n * Return a transport if the `aibroker` CLI is present and supports dispatch.\n *\n * Probing for the subcommand rather than just the binary matters: AIBroker\n * shipped for a long time without `dispatch`, and an older install would\n * otherwise fail once per task instead of degrading cleanly up front.\n */\nexport async function detectAiBroker(\n  bin = \"aibroker\",\n  timeoutSecs = DEFAULT_DISPATCH_TIMEOUT_SECS,\n): Promise<Transport | null> {\n  try {\n    const help = await new Promise<string>((resolve, reject) => {\n      execFile(bin, [\"help\"], { timeout: 10_000 }, (error, stdout, stderr) => {\n        if (error && !stdout) reject(error);\n        else resolve(stdout + stderr);\n      });\n    });\n    return help.includes(\"dispatch\") ? new AiBrokerTransport(bin, timeoutSecs) : null;\n  } catch {\n    return null;\n  }\n}\n","/**\n * schedule-install.ts — install the scheduler tick as a launchd agent\n *\n * Two speeds were considered and rejected in favour of one: a fixed interval.\n * A 15-minute tick is 96 runs a day, each one API call and zero tokens, and\n * daily routines do not need better than 15-minute granularity — a 09:00 sweep\n * starting at 09:12 is fine. Adaptive intervals would add a second thing that\n * can silently stop.\n *\n * StartInterval rather than StartCalendarInterval on purpose: the schedule\n * lives in Todoist, not here. This agent only decides how often to *look*.\n * That means one plist total, however many routines exist, and rescheduling a\n * routine never touches the machine.\n */\n\nimport { existsSync, mkdirSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { spawnSync } from \"node:child_process\";\nimport { schedulerLogPath } from \"../runtime-paths.js\";\nimport { serviceManagerAllowed } from \"../service-manager.js\";\n\nexport const SCHEDULE_LABEL = \"com.pai.task-scheduler\";\nconst LAUNCH_AGENTS = join(homedir(), \"Library\", \"LaunchAgents\");\nexport const SCHEDULE_PLIST = join(LAUNCH_AGENTS, `${SCHEDULE_LABEL}.plist`);\nexport const SCHEDULE_LOG = schedulerLogPath();\n\n/** Default tick, in seconds. */\nexport const DEFAULT_INTERVAL_SECS = 900;\n\n/**\n * Absolute path to the CLI entrypoint the agent should run.\n *\n * Resolving \"index.mjs\" next to this module was wrong. The bundler emits shared\n * code into chunks at dist/ root, so import.meta.url pointed there and the plist\n * was written against dist/index.mjs — the library barrel, which parses no argv,\n * prints nothing and exits 0. The agent ticked every 15 minutes and did nothing\n * at all, which looks identical to \"nothing was due\". Anchor on the package root\n * and refuse to install rather than write a plist around a file that is not a CLI.\n */\nfunction cliPath(): string {\n  let dir = dirname(fileURLToPath(import.meta.url));\n  for (let i = 0; i < 6; i++) {\n    for (const candidate of [join(dir, \"dist\", \"cli\", \"index.mjs\"), join(dir, \"cli\", \"index.mjs\")]) {\n      if (existsSync(candidate)) return candidate;\n    }\n    const parent = dirname(dir);\n    if (parent === dir) break;\n    dir = parent;\n  }\n  throw new Error(\n    \"cannot locate the PAI CLI entrypoint (dist/cli/index.mjs) — refusing to install a scheduler that would silently do nothing\"\n  );\n}\n\nexport function generateSchedulePlist(intervalSecs: number, cli: 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>${SCHEDULE_LABEL}</string>\n\n    <key>ProgramArguments</key>\n    <array>\n        <string>/usr/local/bin/node</string>\n        <string>${cli}</string>\n        <string>task</string>\n        <string>poll</string>\n    </array>\n\n    <key>StartInterval</key>\n    <integer>${intervalSecs}</integer>\n\n    <key>RunAtLoad</key>\n    <true/>\n\n    <key>StandardOutPath</key>\n    <string>${SCHEDULE_LOG}</string>\n\n    <key>StandardErrorPath</key>\n    <string>${SCHEDULE_LOG}</string>\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\nexport interface InstallResult {\n  plistPath: string;\n  intervalSecs: number;\n  loaded: boolean;\n  message: string;\n}\n\nexport function installSchedule(intervalSecs = DEFAULT_INTERVAL_SECS): InstallResult {\n  if (process.platform !== \"darwin\") {\n    return {\n      plistPath: SCHEDULE_PLIST,\n      intervalSecs,\n      loaded: false,\n      message: \"scheduler install supports launchd (macOS) only\",\n    };\n  }\n\n  if (!existsSync(LAUNCH_AGENTS)) mkdirSync(LAUNCH_AGENTS, { recursive: true });\n\n  const cli = cliPath();\n  const plist = generateSchedulePlist(intervalSecs, cli);\n\n  // Plain write: a plist is generated, not user data, so the json-store guard\n  // would be the wrong tool — there is nothing here anyone could not regenerate.\n  writeFileSync(SCHEDULE_PLIST, plist, \"utf8\");\n\n  if (!serviceManagerAllowed(`launchctl load ${SCHEDULE_PLIST}`)) {\n    return { plistPath: SCHEDULE_PLIST, intervalSecs, loaded: false, message: \"Plist written; not loaded (HOME is not the account home).\" };\n  }\n  spawnSync(\"launchctl\", [\"unload\", SCHEDULE_PLIST], { encoding: \"utf8\" });\n  const load = spawnSync(\"launchctl\", [\"load\", SCHEDULE_PLIST], { encoding: \"utf8\" });\n\n  return {\n    plistPath: SCHEDULE_PLIST,\n    intervalSecs,\n    loaded: load.status === 0,\n    message:\n      load.status === 0\n        ? `Scheduler installed — ticking every ${Math.round(intervalSecs / 60)} min.`\n        : `Plist written but launchctl load failed: ${(load.stderr || \"\").trim()}`,\n  };\n}\n\nexport function uninstallSchedule(): string {\n  if (!existsSync(SCHEDULE_PLIST)) return \"Scheduler is not installed.\";\n  if (serviceManagerAllowed(`launchctl unload ${SCHEDULE_PLIST}`)) {\n    spawnSync(\"launchctl\", [\"unload\", SCHEDULE_PLIST], { encoding: \"utf8\" });\n  }\n  unlinkSync(SCHEDULE_PLIST);\n  return \"Scheduler uninstalled.\";\n}\n\nexport function scheduleStatus(): { installed: boolean; running: boolean; detail: string } {\n  const installed = existsSync(SCHEDULE_PLIST);\n  if (!installed) return { installed: false, running: false, detail: \"Not installed.\" };\n\n  const list = spawnSync(\"launchctl\", [\"list\", SCHEDULE_LABEL], { encoding: \"utf8\" });\n  const running = list.status === 0;\n  return {\n    installed: true,\n    running,\n    detail: running\n      ? `Loaded. Log: ${SCHEDULE_LOG}`\n      : \"Plist present but not loaded — run `pai task schedule install` again.\",\n  };\n}\n","/**\n * archive.ts — keep the conversation that happened on a task.\n *\n * A task's comment thread is where the actual thinking ends up: a question,\n * the answer, the correction, the reason a decision went the way it did. When\n * the task is completed that thread stops being visible — it is not deleted,\n * but it leaves every list anyone looks at, and it lives in a tracker rather\n * than in the project it was about. Six months later the decision is\n * unrecoverable from the place you would look for it.\n *\n * So on completion the thread is written into the owning project's notes, where\n * the rest of that project's knowledge already is and where the memory indexer\n * will pick it up.\n *\n * One file per task, rewritten in full each time rather than appended to. The\n * whole thread is fetched on every archive, so rewriting is naturally\n * idempotent: archiving twice produces the same file, and a recurring task\n * accumulates its history in one place instead of scattering a note per\n * occurrence. Appending would need duplicate detection, and duplicate detection\n * is where this would grow a bug.\n */\n\nimport { mkdirSync, writeFileSync, existsSync, readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport type { Task } from \"./types.js\";\n\nexport interface ArchiveComment {\n  id: string;\n  content: string;\n  postedAt?: string;\n}\n\nexport interface ArchiveResult {\n  path: string;\n  /** False when the file already held exactly this content. */\n  written: boolean;\n  commentCount: number;\n  /** Set when nothing was written and the reason was not \"unchanged\". */\n  skipped?: \"no-discussion\";\n}\n\n/**\n * Filesystem-safe stem for a task, stable across re-archives.\n *\n * The id is included because titles change and two tasks can share one — the\n * id is what makes the path stable, and stability is what makes rewriting\n * idempotent rather than accumulating near-duplicate files.\n */\nexport function archiveSlug(task: Pick<Task, \"id\" | \"title\">): string {\n  const title = task.title\n    .toLowerCase()\n    .replace(/[^\\p{L}\\p{N}]+/gu, \"-\")\n    .replace(/^-+|-+$/g, \"\")\n    .slice(0, 60)\n    .replace(/-+$/g, \"\");\n  return title ? `${title} - ${task.id}` : task.id;\n}\n\n/** Where a task's thread belongs, given the project root that owns it. */\nexport function archivePath(projectRoot: string, task: Pick<Task, \"id\" | \"title\">): string {\n  return join(projectRoot, \"Notes\", \"tasks\", `${archiveSlug(task)}.md`);\n}\n\n/**\n * Render the task and its thread as a note.\n *\n * The description is included in full. It is usually the runbook or the\n * reasoning, and it is the half of the record that explains why the comments\n * say what they say.\n */\nexport function renderArchive(\n  task: Task,\n  comments: ArchiveComment[],\n  completedAt: string\n): string {\n  const out: string[] = [];\n\n  out.push(\"---\");\n  out.push(`task_id: ${task.id}`);\n  out.push(`title: ${JSON.stringify(task.title)}`);\n  if (task.owner.project) out.push(`owner: ${task.owner.project}`);\n  if (task.due) out.push(`due: ${task.due}`);\n  if (task.recurrence) out.push(`recurrence: ${JSON.stringify(task.recurrence)}`);\n  out.push(`completed: ${completedAt}`);\n  if (task.sourceUrl) out.push(`source: ${task.sourceUrl}`);\n  out.push(\"---\");\n  out.push(\"\");\n  out.push(`# ${task.title}`);\n  out.push(\"\");\n\n  if (task.body.trim()) {\n    out.push(\"## Task\");\n    out.push(\"\");\n    out.push(task.body.trim());\n    out.push(\"\");\n  }\n\n  out.push(`## Discussion (${comments.length})`);\n  out.push(\"\");\n\n  if (comments.length === 0) {\n    // Stated rather than omitted: an empty section says the thread was checked\n    // and was empty, where a missing section says nothing at all.\n    out.push(\"_No comments were posted on this task._\");\n    out.push(\"\");\n  } else {\n    for (const c of comments) {\n      const when = c.postedAt ? c.postedAt.slice(0, 19).replace(\"T\", \" \") : \"date unknown\";\n      out.push(`### ${when}`);\n      out.push(\"\");\n      out.push(c.content.trim());\n      out.push(\"\");\n    }\n  }\n\n  return out.join(\"\\n\");\n}\n\n/** Drop the `completed:` frontmatter line, so two archives are comparable. */\nfunction withoutStamp(body: string): string {\n  return body\n    .split(\"\\n\")\n    .filter((l) => !l.startsWith(\"completed: \"))\n    .join(\"\\n\");\n}\n\n/**\n * Write the archive, skipping the write when nothing changed.\n *\n * Returning `written: false` for an unchanged file matters for a recurring\n * task: it is archived on every completion, and rewriting an identical file\n * daily would churn mtimes and make the indexer re-read it for nothing.\n */\nexport function writeArchive(\n  projectRoot: string,\n  task: Task,\n  comments: ArchiveComment[],\n  completedAt: string\n): ArchiveResult {\n  const path = archivePath(projectRoot, task);\n\n  // Nothing was discussed, so there is nothing to keep. The point of this is to\n  // preserve a conversation that completing the task would otherwise bury —\n  // where no conversation happened, a file saying so is noise, and enough of it\n  // buries the notes that DO carry something. The task itself is not at risk:\n  // it stays in the tracker either way.\n  if (comments.length === 0) {\n    return { path, written: false, commentCount: 0, skipped: \"no-discussion\" };\n  }\n\n  const body = renderArchive(task, comments, completedAt);\n\n  if (existsSync(path)) {\n    try {\n      // Compare without the completion stamp. It is `new Date()` at call time,\n      // so including it makes every re-archive differ from the last by\n      // construction — the unchanged case could never fire, the file would be\n      // rewritten on every duplicate delivery, and the owning session would be\n      // notified again each time about a discussion it has already seen. The\n      // thread is what is being preserved; when to notice it is not part of it.\n      if (withoutStamp(readFileSync(path, \"utf-8\")) === withoutStamp(body)) {\n        return { path, written: false, commentCount: comments.length };\n      }\n    } catch {\n      // Unreadable for any reason — fall through and rewrite rather than\n      // treating a read failure as \"already up to date\".\n    }\n  }\n\n  mkdirSync(dirname(path), { recursive: true });\n  writeFileSync(path, body, \"utf-8\");\n  return { path, written: true, commentCount: comments.length };\n}\n","/**\n * projects.ts — reconcile sessions against the tracker projects that address them.\n *\n * A session can only be given work from a phone if something in the tracker\n * names it. That mapping drifts constantly and invisibly: sessions get renamed,\n * new ones appear, old ones stop being used, and nothing anywhere says \"these\n * five can receive work and these thirteen cannot\". The failure is silent in the\n * worst direction — filing a task for a session with no project looks exactly\n * like filing one that will be picked up.\n *\n * So this reports the mapping in both directions rather than just listing what\n * exists: sessions with no project (cannot be addressed) and projects with no\n * session (will queue until one launches, which is legitimate and worth\n * distinguishing from a mistake).\n */\n\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst run = promisify(execFile);\n\nexport interface SessionEntry {\n  name: string;\n  directory: string;\n}\n\nexport interface ProjectEntry {\n  id: string;\n  name: string;\n}\n\nexport type MappingState = \"mapped\" | \"session-only\" | \"project-only\";\n\nexport interface MappingRow {\n  name: string;\n  state: MappingState;\n  directory?: string;\n  projectId?: string;\n}\n\n/**\n * Read the session manifest from AIBroker.\n *\n * The manifest rather than a live enumeration, deliberately: it is the set of\n * sessions the user actually works with, which is the right unit for \"should\n * this have an inbox\". A live list would drop everything not open at this\n * instant and churn a permanent decision on a transient signal.\n *\n * Returns an empty list when AIBroker is absent — this command degrades to\n * \"here are your tracker projects\" rather than failing, in keeping with the\n * rest of the bus being optional.\n */\nexport async function readSessionManifest(bin = \"aibroker\"): Promise<SessionEntry[]> {\n  let stdout: string;\n  try {\n    ({ stdout } = await run(bin, [\"sessions\", \"list\"], { timeout: 20_000 }));\n  } catch {\n    return [];\n  }\n\n  const entries: SessionEntry[] = [];\n  for (const line of stdout.split(\"\\n\")) {\n    // Manifest rows are indented \"  Name<spaces>/absolute/path\". Anything\n    // without a leading absolute path is a header or a log line.\n    const m = line.match(/^\\s{2}(\\S.*?)\\s{2,}(\\/.*)$/);\n    if (!m) continue;\n    const name = m[1].trim();\n    const directory = m[2].trim();\n    if (!name || !directory) continue;\n    entries.push({ name, directory });\n  }\n  return entries;\n}\n\n/** Compare names the way a person scanning two lists would. */\nexport function normalizeName(name: string): string {\n  return name.trim().toLowerCase().replace(/\\s+/g, \" \");\n}\n\n/**\n * Join sessions and tracker projects into one ordered view.\n *\n * Sorted mapped-first then by name, so the actionable rows — sessions that\n * cannot yet be addressed — sit together rather than scattered through a list\n * of things that already work.\n */\nexport function reconcile(\n  sessions: SessionEntry[],\n  projects: ProjectEntry[]\n): MappingRow[] {\n  const byName = new Map<string, ProjectEntry>();\n  for (const p of projects) byName.set(normalizeName(p.name), p);\n\n  const seen = new Set<string>();\n  const rows: MappingRow[] = [];\n\n  for (const s of sessions) {\n    const key = normalizeName(s.name);\n    const project = byName.get(key);\n    if (project) seen.add(key);\n    rows.push({\n      name: s.name,\n      state: project ? \"mapped\" : \"session-only\",\n      directory: s.directory,\n      projectId: project?.id,\n    });\n  }\n\n  for (const p of projects) {\n    const key = normalizeName(p.name);\n    if (seen.has(key)) continue;\n    rows.push({ name: p.name, state: \"project-only\", projectId: p.id });\n  }\n\n  const rank: Record<MappingState, number> = {\n    mapped: 0,\n    \"session-only\": 1,\n    \"project-only\": 2,\n  };\n  return rows.sort(\n    (a, b) => rank[a.state] - rank[b.state] || a.name.localeCompare(b.name)\n  );\n}\n","/**\n * task.ts — `pai task` CLI\n *\n * Surface for the task bus: list what is open, file new work, dispatch tasks to\n * the sessions that own them, and close them out.\n *\n * See docs/task-bus.md.\n */\n\nimport chalk from \"chalk\";\nimport { chmodSync } from \"node:fs\";\nimport { createInterface } from \"node:readline\";\nimport { loadConfig, CONFIG_FILE } from \"../../daemon/config.js\";\nimport { readConfigRaw, writeConfigRaw } from \"../commands/setup/utils.js\";\nimport { getRegistryBackend } from \"../../storage/factory.js\";\nimport { loadAliasMap } from \"../../tasks/resolver.js\";\nimport { TodoistProvider } from \"../../tasks/providers/todoist.js\";\nimport { dispatchAll } from \"../../tasks/dispatch.js\";\nimport { detectAiBroker, detectProber } from \"../../tasks/transport/aibroker.js\";\nimport { tick, releaseClaim, loadState, saveState, STATE_FILE } from \"../../tasks/poller.js\";\nimport { installSchedule, uninstallSchedule, scheduleStatus, DEFAULT_INTERVAL_SECS } from \"../../tasks/schedule-install.js\";\nimport type { DispatchResult, Task, TaskProvider, TaskPriority } from \"../../tasks/types.js\";\nimport { writeArchive } from \"../../tasks/archive.js\";\nimport {\n  readSessionManifest,\n  reconcile,\n  normalizeName,\n  type MappingRow,\n} from \"../../tasks/projects.js\";\n\n/**\n * Make a tracker sub-project name resolve to the PAI project it belongs to.\n *\n * Ownership is decided by matching the container name against a curated alias.\n * A session's display name is almost never its PAI slug — \"SL\" is\n * `seriousletter`, \"Home\" is `owner` — so a freshly created sub-project\n * routes nowhere until someone connects the two. That gap bit twice on\n * 2026-08-02: the project existed, looked addressable in every listing, and\n * tasks filed into it came back unrouted.\n *\n * The session's working directory is what links them: the manifest gives the\n * directory, the registry gives the project at that path. Returns a line worth\n * printing, or null when nothing needed doing.\n */\nasync function ensureAlias(name: string, directory?: string): Promise<string | null> {\n  if (!directory) return `no directory for \"${name}\" — set the alias by hand`;\n\n  const registry = await getRegistryBackend();\n  const project = await registry.getProjectByRootPath(directory);\n  if (!project || project.status !== \"active\") {\n    return `no PAI project at ${directory} — tasks here will not route`;\n  }\n\n  // resolveAlias() is case-sensitive; the original SQL compared lower(alias)\n  // — a narrower check (see worker report: resolveAlias needs the same\n  // opts.caseInsensitive that getProjectByAlias already has).\n  const existingProjectId = await registry.resolveAlias(name);\n  if (existingProjectId !== null) {\n    // Repointing someone else's alias would silently steal their address.\n    return existingProjectId === project.id\n      ? null\n      : `alias \"${name}\" already points elsewhere — left alone`;\n  }\n\n  await registry.addAlias(name, project.id);\n  return `aliased → ${project.slug}`;\n}\n\n/**\n * Render the session/project mapping.\n *\n * The three states are printed with different weight on purpose. \"Cannot be\n * addressed\" is the only one that needs action, and it is the one that is\n * otherwise invisible — filing a task for a session with no project looks\n * exactly like filing one that works.\n */\nfunction printProjectMapping(rows: MappingRow[], noBroker: boolean): void {\n  const mapped = rows.filter((r) => r.state === \"mapped\");\n  const unmapped = rows.filter((r) => r.state === \"session-only\");\n  const orphan = rows.filter((r) => r.state === \"project-only\");\n\n  console.log();\n  console.log(chalk.bold(`  Can receive work (${mapped.length})`));\n  for (const r of mapped) console.log(chalk.green(\"    ✓ \") + r.name);\n  if (mapped.length === 0) console.log(dim(\"    none\"));\n\n  if (unmapped.length > 0) {\n    console.log();\n    console.log(chalk.bold(`  No project — cannot be addressed (${unmapped.length})`));\n    for (const r of unmapped) console.log(chalk.yellow(\"    · \") + r.name);\n    console.log();\n    console.log(dim(\"    pai task projects --create <name>...   or --create-all\"));\n  }\n\n  if (orphan.length > 0) {\n    console.log();\n    console.log(chalk.bold(`  Project with no session (${orphan.length})`));\n    for (const r of orphan) console.log(dim(\"    · \" + r.name));\n    console.log(dim(\"    Tasks filed here queue until that session launches — not a fault.\"));\n  }\n\n  if (noBroker) {\n    console.log();\n    console.log(dim(\"  AIBroker not reachable — showing tracker projects only.\"));\n  }\n  console.log();\n}\n\nconst dim = chalk.dim;\nconst bold = chalk.bold;\nconst warn = chalk.yellow;\nconst ok = chalk.green;\n\n// ---------------------------------------------------------------------------\n// Wiring\n// ---------------------------------------------------------------------------\n\nasync function buildProvider(): Promise<TodoistProvider | null> {\n  const config = loadConfig();\n  const tasks = config.tasks;\n  if (!tasks?.enabled) return null;\n\n  const aliases = await loadAliasMap();\n  const provider = new TodoistProvider(tasks.providers.todoist, aliases, {\n    defaultOwner: tasks.defaultOwner,\n  });\n  return provider.isConfigured() ? provider : null;\n}\n\n/** Print the one message that actually helps when nothing is configured. */\nfunction reportUnconfigured(): void {\n  console.error(chalk.yellow(\"  Task bus is not configured.\"));\n  console.error(dim(\"  Run `pai setup` and complete the Task Bus step.\"));\n}\n\nfunction todayIso(): string {\n  return new Date().toISOString().slice(0, 10);\n}\n\n// ---------------------------------------------------------------------------\n// Credentials\n// ---------------------------------------------------------------------------\n\n/**\n * Prompt for a secret without echoing it.\n *\n * Never accept a token as a command-line argument: argv is visible in `ps` to\n * every user on the machine and lands in shell history verbatim.\n */\nfunction promptSecret(question: string): Promise<string> {\n  return new Promise((resolve) => {\n    const rl = createInterface({ input: process.stdin, output: process.stdout, terminal: true });\n    const out = process.stdout as NodeJS.WriteStream & { muted?: boolean };\n    // Swap in a writer that swallows echoed keystrokes but still lets the\n    // prompt itself through.\n    const write = out.write.bind(out);\n    (rl as unknown as { _writeToOutput: (s: string) => void })._writeToOutput = (s: string) => {\n      if (!out.muted) write(s);\n    };\n    write(question);\n    out.muted = true;\n    rl.question(\"\", (answer) => {\n      out.muted = false;\n      write(\"\\n\");\n      rl.close();\n      resolve(answer.trim());\n    });\n  });\n}\n\n/** Show enough of a token to recognise it, never enough to use it. */\nfunction redact(token: string | undefined): string {\n  if (!token) return dim(\"(not set)\");\n  return token.length <= 8 ? \"********\" : `${token.slice(0, 4)}…${token.slice(-4)}`;\n}\n\ntype RawTasks = {\n  enabled?: boolean;\n  autoDispatch?: boolean;\n  dispatchTimeoutSecs?: number;\n  providers?: { todoist?: { enabled?: boolean; apiKey?: string; rootProjectId?: string; findingsSectionId?: string } };\n};\n\n// ---------------------------------------------------------------------------\n// Rendering\n// ---------------------------------------------------------------------------\n\nfunction renderOwner(task: Task): string {\n  if (task.owner.project) {\n    const via = task.owner.source === \"label\" ? \"label\" : \"container\";\n    return chalk.cyan(task.owner.project) + dim(` (${via})`);\n  }\n  return task.owner.rawHint\n    ? chalk.yellow(\"unrouted\") + dim(` (${task.owner.rawHint} matches no project)`)\n    : chalk.yellow(\"unrouted\");\n}\n\nfunction printTasks(tasks: Task[]): void {\n  if (tasks.length === 0) {\n    console.log(dim(\"  Nothing open.\"));\n    return;\n  }\n  console.log();\n  for (const t of tasks) {\n    const due = t.due ? dim(` due ${t.due.slice(0, 10)}`) : \"\";\n    const prio = t.priority !== \"p4\" ? dim(` ${t.priority}`) : \"\";\n    console.log(`  ${bold(t.title)}${prio}${due}`);\n    console.log(`    ${renderOwner(t)} ${dim(\"· \" + t.id)}`);\n  }\n  console.log();\n}\n\nfunction printResults(results: DispatchResult[]): void {\n  const symbol: Record<string, string> = {\n    delivered: chalk.green(\"→\"),\n    spawned: chalk.green(\"+\"),\n    unrouted: chalk.yellow(\"?\"),\n    unlaunchable: chalk.red(\"!\"),\n    unreachable: chalk.red(\"✗\"),\n    skipped: dim(\"·\"),\n  };\n\n  console.log();\n  for (const r of results) {\n    console.log(`  ${symbol[r.outcome]} ${r.task.title}`);\n    const detail = [r.session, r.reason].filter(Boolean).join(\" — \");\n    if (detail) console.log(`      ${dim(detail)}`);\n  }\n  console.log();\n\n  const counts = results.reduce<Record<string, number>>((acc, r) => {\n    acc[r.outcome] = (acc[r.outcome] ?? 0) + 1;\n    return acc;\n  }, {});\n  console.log(dim(\"  \" + Object.entries(counts).map(([k, v]) => `${v} ${k}`).join(\", \")));\n  console.log();\n}\n\n// ---------------------------------------------------------------------------\n// Commands\n// ---------------------------------------------------------------------------\n\nexport async function projectsCommand(opts: { create?: string[]; createAll?: boolean }): Promise<number> {\n  const provider = await buildProvider();\n  if (!provider) {\n    reportUnconfigured();\n    return 1;\n  }\n\n  if (!provider.listSubProjects || !provider.findOrCreateSubProject) {\n    console.log();\n    console.log(\n      chalk.yellow(`  ${provider.providerId} does not address work by sub-project.`)\n    );\n    console.log(dim(\"  Session-scoped inboxes need a tracker with nested projects.\"));\n    console.log();\n    return 1;\n  }\n\n  const [sessions, projects] = await Promise.all([\n    readSessionManifest(),\n    provider.listSubProjects(),\n  ]);\n\n  const rows = reconcile(sessions, projects);\n\n  const wanted = opts.createAll\n    ? rows.filter((r) => r.state === \"session-only\").map((r) => r.name)\n    : (opts.create ?? []);\n\n  if (wanted.length > 0) {\n    const addressable = new Set(\n      rows.filter((r) => r.state === \"session-only\").map((r) => normalizeName(r.name))\n    );\n    console.log();\n    for (const name of wanted) {\n      // Refuse to create a project for a session nobody has: it would look\n      // addressable and never be picked up, which is the failure this\n      // command exists to surface.\n      if (!addressable.has(normalizeName(name))) {\n        console.log(chalk.yellow(`  skipped  ${name}`) + dim(\" — no such session, or it already has a project\"));\n        continue;\n      }\n      const { id, created } = await provider.findOrCreateSubProject(name);\n      console.log(\n        (created ? chalk.green(\"  created  \") : dim(\"  exists   \")) + name + dim(`  ${id}`)\n      );\n\n      // A project alone does not route. Ownership resolves by matching the\n      // container name against a curated alias, and a session's display\n      // name is rarely its PAI slug — \"SL\" is `seriousletter`, \"Home\" is\n      // `owner`. Creating the project and stopping produced something\n      // that looked addressable and silently was not, twice, before this\n      // was wired in. The alias is not a separate step a user should have\n      // to know about; it is what makes the project mean anything.\n      const row = rows.find((r) => normalizeName(r.name) === normalizeName(name));\n      const note = await ensureAlias(name, row?.directory);\n      if (note) console.log(dim(`           ${note}`));\n    }\n    console.log();\n    return 0;\n  }\n\n  printProjectMapping(rows, sessions.length === 0);\n  return 0;\n}\n\nexport async function listCommand(opts: {\n  owner?: string;\n  due?: string;\n  today?: boolean;\n  routed?: boolean;\n  limit?: number\n}): Promise<number> {\n  const provider = await buildProvider();\n  if (!provider) {\n    reportUnconfigured();\n    return 1;\n  }\n\n  const tasks = await provider.listOpen({\n    owner: opts.owner,\n    dueBefore: opts.today ? todayIso() : opts.due,\n    includeUnrouted: !opts.routed,\n    limit: opts.limit,\n  });\n  printTasks(tasks);\n  return 0;\n}\n\nexport async function addCommand(\n  title: string,\n  opts: {\n    owner?: string;\n    body?: string;\n    due?: string;\n    priority?: string;\n    url?: string;\n    into?: string\n  }\n): Promise<number> {\n  const provider = await buildProvider();\n  if (!provider) {\n    reportUnconfigured();\n    return 1;\n  }\n\n  // A title alone is not actionable months later. Warn rather than block:\n  // refusing the write would just push people to file nothing at all.\n  if (!opts.body) {\n    console.log(dim(\"  No --body given. Add the procedure and reasoning so this\"));\n    console.log(dim(\"  is still actionable months from now, by you alone.\"));\n  }\n\n  const created = await provider.add({\n    title,\n    body: opts.body ?? \"\",\n    owner: opts.owner ?? null,\n    due: opts.due,\n    priority: opts.priority as TaskPriority | undefined,\n    sourceUrl: opts.url,\n    into: opts.into,\n  });\n\n  console.log(chalk.green(`  Filed: ${created.title}`));\n  console.log(`    ${renderOwner(created)} ${dim(\"· \" + created.id)}`);\n  return 0;\n}\n\nexport async function dispatchCommand(opts: {\n  owner?: string;\n  today?: boolean;\n  dryRun?: boolean;\n  noSpawn?: boolean\n}): Promise<number> {\n  const provider = await buildProvider();\n  if (!provider) {\n    reportUnconfigured();\n    return 1;\n  }\n\n  const config = loadConfig();\n  const tasks = await provider.listOpen({\n    owner: opts.owner,\n    dueBefore: opts.today ? todayIso() : undefined,\n    includeUnrouted: true,\n  });\n\n  if (tasks.length === 0) {\n    console.log(dim(\"  Nothing to dispatch.\"));\n    return 0;\n  }\n\n  // A dry run must never contact a session, so skip detection entirely\n  // rather than detecting and then declining to use it.\n  const transport = opts.dryRun\n    ? null\n    : await detectAiBroker(undefined, config.tasks?.dispatchTimeoutSecs);\n\n  if (!opts.dryRun && !transport) {\n    console.log(dim(\"  No aibroker CLI with `dispatch` found — reporting ownership only.\"));\n  }\n\n  const results = await dispatchAll(tasks, {\n    transport,\n    autoDispatch: opts.dryRun ? false : (config.tasks?.autoDispatch ?? false),\n    spawnIfAbsent: opts.noSpawn !== true,\n    dryRun: Boolean(opts.dryRun),\n  });\n\n  printResults(results);\n  return 0;\n}\n\nexport async function pollCommand(opts: { dryRun?: boolean }): Promise<number> {\n  const provider = await buildProvider();\n  if (!provider) {\n    reportUnconfigured();\n    return 1;\n  }\n\n  const config = loadConfig();\n  const transport = opts.dryRun ? null : await detectAiBroker(undefined, config.tasks?.dispatchTimeoutSecs);\n  const prober = opts.dryRun ? null : await detectProber();\n\n  const report = await tick({\n    provider: provider as TodoistProvider,\n    transport,\n    prober,\n    autoDispatch: config.tasks?.autoDispatch ?? false,\n    dryRun: Boolean(opts.dryRun),\n  });\n\n  if (report.decisions.length === 0) {\n    console.log(dim(\"  Nothing due, nothing running.\"));\n    return 0;\n  }\n\n  const mark: Record<string, string> = {\n    dispatch: chalk.green(\"→\"),\n    triggered: chalk.green(\"▶\"),\n    complete: chalk.green(\"✓\"),\n    running: dim(\"·\"),\n    probe: chalk.yellow(\"?\"),\n    orphaned: chalk.yellow(\"!\"),\n    abandoned: chalk.red(\"⨯\"),\n    skip: dim(\"–\"),\n    wait: dim(\" \"),\n  };\n\n  console.log();\n  for (const { decision, note } of report.decisions) {\n    console.log(`  ${mark[decision.action] ?? \" \"} ${decision.task.title}`);\n    if (note) console.log(`      ${dim(note)}`);\n  }\n  console.log();\n  console.log(\n    dim(\n      `  ${report.dispatched} dispatched, ${report.completed} completed, ` +\n      `${report.probed} probed, ${report.stuck} stuck`\n    )\n  );\n  console.log();\n  return 0;\n}\n\nexport function scheduleInstallCommand(opts: { interval?: number }): number {\n  const r = installSchedule(opts.interval || DEFAULT_INTERVAL_SECS);\n  console.log(r.loaded ? chalk.green(`  ${r.message}`) : chalk.yellow(`  ${r.message}`));\n  console.log(dim(`  ${r.plistPath}`));\n  console.log(dim(`  The schedule itself lives in Todoist — this only sets how often PAI looks.`));\n  return 0;\n}\n\nexport function scheduleUninstallCommand(): number {\n  console.log(chalk.green(`  ${uninstallSchedule()}`));\n  return 0;\n}\n\nexport function scheduleStatusCommand(): number {\n  const s = scheduleStatus();\n  console.log();\n  console.log(`  ${s.installed ? chalk.green(\"installed\") : dim(\"not installed\")}` +\n    `  ${s.installed ? (s.running ? chalk.green(\"· loaded\") : chalk.yellow(\"· not loaded\")) : \"\"}`);\n  console.log(dim(`  ${s.detail}`));\n  console.log();\n  return 0;\n}\n\nexport async function configCommand(opts: {\n  token?: boolean;\n  fromEnv?: boolean;\n  project?: string;\n  findings?: string;\n  timeout?: number;\n  autoDispatch?: string;\n  disable?: boolean\n}): Promise<number> {\n  const raw = readConfigRaw();\n  const tasks = (raw.tasks ?? {}) as RawTasks;\n  tasks.providers ??= {};\n  tasks.providers.todoist ??= {};\n  const todoist = tasks.providers.todoist;\n\n  const noChanges =\n    !opts.token && !opts.fromEnv && !opts.project && !opts.findings &&\n    opts.timeout === undefined && opts.autoDispatch === undefined && !opts.disable;\n\n  if (noChanges) {\n    console.log();\n    console.log(`  ${bold(\"Task bus\")}      ${tasks.enabled ? chalk.green(\"enabled\") : dim(\"disabled\")}`);\n    console.log(`  ${bold(\"Token\")}         ${redact(todoist.apiKey)}${\n      !todoist.apiKey && process.env.TODOIST_API_KEY ? dim(\"  (TODOIST_API_KEY is set in this shell)\") : \"\"\n    }`);\n    console.log(`  ${bold(\"Root project\")}  ${todoist.rootProjectId ?? dim(\"(not set)\")}`);\n    console.log(`  ${bold(\"Findings\")}      ${todoist.findingsSectionId ?? dim(\"(not set)\")}`);\n    console.log(`  ${bold(\"Auto-dispatch\")} ${tasks.autoDispatch ? chalk.green(\"on\") : dim(\"off\")}`);\n    console.log(`  ${bold(\"Timeout\")}       ${tasks.dispatchTimeoutSecs ?? dim(\"default (180s)\")}`);\n    console.log();\n    console.log(dim(`  ${CONFIG_FILE}`));\n    console.log();\n    return 0;\n  }\n\n  if (opts.token && opts.fromEnv) {\n    console.error(chalk.yellow(\"  Use either --token or --from-env, not both.\"));\n    return 1;\n  }\n\n  if (opts.fromEnv) {\n    const envToken = process.env.TODOIST_API_KEY?.trim();\n    if (!envToken) {\n      console.error(chalk.yellow(\"  TODOIST_API_KEY is not set in this environment.\"));\n      return 1;\n    }\n    todoist.apiKey = envToken;\n    todoist.enabled = true;\n    tasks.enabled = true;\n  }\n\n  if (opts.token) {\n    const entered = await promptSecret(\"  Todoist API token (hidden): \");\n    if (!entered) {\n      console.error(chalk.yellow(\"  No token entered. Nothing changed.\"));\n      return 1;\n    }\n    todoist.apiKey = entered;\n    todoist.enabled = true;\n    tasks.enabled = true;\n  }\n\n  if (opts.project) { todoist.rootProjectId = opts.project; tasks.enabled = true; }\n  if (opts.findings) todoist.findingsSectionId = opts.findings;\n  if (opts.timeout !== undefined) tasks.dispatchTimeoutSecs = opts.timeout;\n  if (opts.autoDispatch !== undefined) tasks.autoDispatch = opts.autoDispatch === \"true\";\n  if (opts.disable) tasks.enabled = false;\n\n  raw.tasks = tasks;\n  writeConfigRaw(raw);\n\n  // The file now holds a credential. Narrow it before saying anything else,\n  // so a crash between write and chmod cannot leave it world-readable.\n  try {\n    chmodSync(CONFIG_FILE, 0o600);\n  } catch {\n    console.log(chalk.yellow(`  Could not restrict permissions on ${CONFIG_FILE} — check them by hand.`));\n  }\n\n  console.log(chalk.green(\"  Saved.\"));\n  if (todoist.apiKey && !todoist.rootProjectId) {\n    console.log(dim(\"  No root project set yet — run `pai task config --project <id>`.\"));\n  }\n  return 0;\n}\n\nexport async function archiveCommand(\n  id: string,\n  opts: { quiet?: boolean; notify?: boolean }\n): Promise<number> {\n  const provider = await buildProvider();\n  if (!provider) {\n    reportUnconfigured();\n    return 1;\n  }\n  // Notifies by default: this command is the path something OUTSIDE the\n  // session took — a checkbox ticked in the tracker — so the session has no\n  // idea it happened. `task done` deliberately does not notify, because\n  // there the session is the one that just did it.\n  const saved = await cmdArchiveTask(provider, id, {\n    quiet: opts.quiet,\n    notify: opts.notify !== false,\n  });\n  // Non-zero on failure so a webhook caller can tell nothing was saved\n  // rather than assuming success — the whole point is not losing content.\n  if (!saved) return 1;\n  return 0;\n}\n\nexport async function doneCommand(\n  id: string,\n  opts: { archive?: boolean }\n): Promise<number> {\n  const provider = await buildProvider();\n  if (!provider) {\n    reportUnconfigured();\n    return 1;\n  }\n\n  // Archive BEFORE completing. A completed task is harder to read back on\n  // some providers, and losing the thread is the failure this exists to\n  // prevent — so the copy is taken while the task is unambiguously there.\n  let archived: { path: string; written: boolean; commentCount: number } | null = null;\n  if (opts.archive !== false) {\n    archived = await archiveTaskThread(provider, id);\n  }\n\n  await provider.complete(id);\n  console.log(chalk.green(`  Completed ${id}.`));\n\n  // Release the claim left behind by whichever run finished this task.\n  // For a recurring task, `complete` above only advanced the due date to\n  // the next occurrence — the `pai-running` label, the progress-marker\n  // comment, and this poller's transient run state (`startedAt` /\n  // `claimSeenAt` / `failedProbes`) all outlive it unless something\n  // clears them. Left alone, the next occurrence looks already claimed,\n  // and the scheduler classifies it as overrunning and starts probing a\n  // session that finished hours ago.\n  //\n  // Best-effort throughout: closing the task above is the action that\n  // has to succeed, and this cleanup must never turn a completed task\n  // into a failed command.\n  try {\n    const task = await provider.getTask(id);\n    if (task) {\n      const state = loadState(STATE_FILE);\n      await releaseClaim(task, provider, state);\n      // Record the due date this advanced to, the same way `tick()`\n      // records it every cycle. Without this, the next `pai task poll`\n      // has no way to tell this advance apart from a human dragging the\n      // task forward by hand — both look like \"due moved exactly one\n      // recurrence period, unclaimed\" — and would dispatch the task a\n      // second time for a run that just finished.\n      if (task.due) state.lastSeenDue[id] = task.due;\n      saveState(state, STATE_FILE);\n    }\n  } catch {\n    // Cleanup is best-effort — see comment above.\n  }\n\n  if (archived) {\n    const what = `${archived.commentCount} comment${archived.commentCount === 1 ? \"\" : \"s\"}`;\n    console.log(\n      archived.written\n        ? dim(`  Discussion saved (${what}) → ${archived.path}`)\n        : dim(`  Discussion already saved (${what}), unchanged.`)\n    );\n  }\n  return 0;\n}\n\n/**\n * `pai task archive <id>` — save a task's discussion without completing it.\n *\n * Split out from `done` so the webhook has something to call. Ticking the\n * checkbox in the tracker is the completion path people actually use, and it\n * never runs `pai task done` — so an archive wired only into that command\n * fires on the route nobody takes and not on the route everybody does.\n *\n * Safe to call repeatedly: the archive rewrites one file per task from the full\n * thread, so a duplicate webhook or a poller backstop covering the same\n * completion costs one no-op.\n */\nexport async function cmdArchiveTask(\n  provider: TaskProvider,\n  id: string,\n  opts: { quiet?: boolean; notify?: boolean } = {}\n): Promise<boolean> {\n  if (!provider.getTask || !provider.listComments) {\n    if (!opts.quiet) console.log(warn(`  ${provider.providerId} cannot read a task's discussion.`));\n    return false;\n  }\n\n  const task = await provider.getTask(id);\n  if (!task) {\n    if (!opts.quiet) console.log(warn(`  No task ${id}.`));\n    return false;\n  }\n\n  const root = task.owner.rootPath;\n  if (!task.owner.project || !root) {\n    if (!opts.quiet) {\n      console.log(\n        warn(\"  No owning project — nothing archived. \") +\n          dim(\"Label it `pai:<project>` to give it a home.\")\n      );\n    }\n    return false;\n  }\n\n  const comments = await provider.listComments(id);\n  const r = writeArchive(root, task, comments, new Date().toISOString());\n\n  if (r.written && opts.notify) {\n    await tellOwningSession(task, r.path, r.commentCount, opts.quiet);\n  }\n\n  if (!opts.quiet) {\n    const what = `${r.commentCount} comment${r.commentCount === 1 ? \"\" : \"s\"}`;\n    if (r.skipped === \"no-discussion\") {\n      console.log(dim(\"  No discussion on this task — nothing to archive.\"));\n    } else {\n      console.log(\n        r.written\n          ? ok(`  Saved (${what}) → `) + r.path\n          : dim(`  Already saved (${what}), unchanged.`)\n      );\n    }\n  }\n  return true;\n}\n\n/**\n * Tell the owning session that a discussion was filed, if it is running.\n *\n * `Notes/tasks/` is a default, not a decision. The session that owns a project\n * knows where things actually belong there — a dated triage folder, a ledger\n * row, an existing note the discussion continues. It can move or link the file;\n * this side cannot, because the convention lives in that project's head and not\n * in PAI.\n *\n * **Never spawns.** Launching a session to tell it a file exists inverts the\n * cost: the archive is already safely on disk, and a terminal opening by itself\n * because someone ticked a checkbox is worse than a note filed one directory\n * away from ideal. If nothing is running, the file simply waits.\n *\n * Best-effort throughout. The archive has already succeeded by the time this\n * runs, and a notification that cannot be delivered must never turn a saved\n * discussion into a reported failure.\n */\nasync function tellOwningSession(\n  task: Task,\n  path: string,\n  comments: number,\n  quiet?: boolean\n): Promise<void> {\n  const project = task.owner.project;\n  if (!project) return;\n\n  try {\n    const transport = await detectAiBroker();\n    if (!transport) return;\n\n    const message = [\n      `A discussion was archived from the tracker — ${comments} comment${comments === 1 ? \"\" : \"s\"} on \"${task.title}\".`,\n      \"\",\n      `Filed provisionally at: ${path}`,\n      `Tracker id: ${task.id}`,\n      \"\",\n      \"This is a notification, not a work order. Nothing is waiting on you and the\",\n      \"content is already safe on disk.\",\n      \"\",\n      \"Notes/tasks/ is PAI's default, chosen without knowing this project's\",\n      \"conventions. If it belongs somewhere else here — a dated folder, a ledger\",\n      \"row, an existing note the discussion continues — move or link it, then say\",\n      \"nothing. If the default is fine, leave it.\",\n    ].join(\"\\n\");\n\n    // spawnIfAbsent: false is the whole point — see the note above.\n    const result = await transport.dispatch(project, message, { spawnIfAbsent: false });\n\n    if (!quiet && result.outcome === \"delivered\") {\n      console.log(dim(`  Told the ${project} session where it landed.`));\n    }\n  } catch {\n    // Deliberately silent: the discussion is saved, which is what mattered.\n  }\n}\n\n/**\n * Copy a task's thread into the notes of the project that owns it.\n *\n * Best-effort by design: this runs as a side effect of completing a task, and\n * a failure to archive must never stop the task being completed. It reports\n * what went wrong rather than throwing, because a silent skip here would lose\n * exactly the content it exists to keep.\n */\nasync function archiveTaskThread(\n  provider: TaskProvider,\n  id: string\n): Promise<{ path: string; written: boolean; commentCount: number } | null> {\n  try {\n    if (!provider.listComments) return null;\n\n    const tasks = await provider.listOpen({ includeUnrouted: true });\n    const task = tasks.find((t) => t.id === id);\n    if (!task) {\n      console.log(dim(`  Not on the open list — nothing archived.`));\n      return null;\n    }\n\n    const project = task.owner.project;\n    const root = task.owner.rootPath;\n    if (!project || !root) {\n      console.log(\n        warn(`  No owning project — discussion not archived. `) +\n          dim(`Label it \\`pai:<project>\\` to give it a home.`)\n      );\n      return null;\n    }\n\n    const comments = await provider.listComments(id);\n    return writeArchive(root, task, comments, new Date().toISOString());\n  } catch (e) {\n    console.log(\n      warn(`  Could not archive the discussion: `) +\n        (e instanceof Error ? e.message : String(e))\n    );\n    return null;\n  }\n}","/**\n * `pai task` (also `pai tasks`): front end for the task commands.\n * Behaviour lives in src/cli/impl/task.js; this file declares names, flags and help.\n */\n\nimport type { CommandDef } from \"../define.js\";\nimport { projectsCommand, listCommand, addCommand, dispatchCommand, pollCommand, scheduleInstallCommand, scheduleUninstallCommand, scheduleStatusCommand, configCommand, archiveCommand, doneCommand } from \"../impl/task.js\";\n\nconst taskSubcommands: CommandDef[] = [\n  {\n    name: \"projects\",\n    summary: \"Show which projects can receive tasks, and create missing ones\",\n    description: \"Show which projects can receive tasks, and create missing ones.\\n\\nShow which sessions can be given work from the tracker, and create the missing projects\",\n    args: [],\n    options: [{ name: \"create\", type: \"string\", value: \"NAMES\", summary: \"Create tracker projects for these session names (several values allowed)\", repeatable: true }, { name: \"create-all\", type: \"boolean\", summary: \"Create a project for every session that has none\" }],\n    examples: [\"pai task projects\",\"pai task projects --create-all\"],\n    seeAlso: [\"pai task list\"],\n    run: async (ctx) => projectsCommand({ create: ctx.multi?.create, createAll: ctx.opts[\"create-all\"] === true })\n  },\n  {\n    name: \"list\",\n    summary: \"List open tasks\",\n    description: \"List open tasks.\\n\\nList open tasks on the bus\",\n    args: [],\n    options: [{ name: \"owner\", type: \"string\", value: \"PROJECT\", summary: \"Only tasks owned by this PAI project\" }, { name: \"due\", type: \"string\", value: \"DATE\", summary: \"Only tasks due on or before this ISO date\" }, { name: \"today\", type: \"boolean\", summary: \"Shorthand for --due <today>\" }, { name: \"routed\", type: \"boolean\", summary: \"Hide tasks with no resolved owner\" }, { name: \"limit\", type: \"string\", value: \"N\", summary: \"Maximum tasks to show\" }],\n    examples: [\"pai task list\",\"pai task list --today --owner my-app\"],\n    seeAlso: [\"pai task add\",\"pai task projects\"],\n    run: async (ctx) => listCommand({\n      owner: ctx.opts.owner as string | undefined,\n      due: ctx.opts.due as string | undefined,\n      today: ctx.opts.today === true,\n      routed: ctx.opts.routed === true,\n      limit: ctx.opts.limit ? Number.parseInt(ctx.opts.limit as string, 10) : undefined\n    })\n  },\n  {\n    name: \"add\",\n    summary: \"Add a task to the list\",\n    description: \"Add a task to the list.\",\n    args: [{ name: \"TITLE\", summary: \"What the task is, in a few words\", required: true }],\n    options: [{ name: \"owner\", type: \"string\", value: \"PROJECT\", summary: \"PAI project that owns this (adds a pai: label)\" }, { name: \"body\", type: \"string\", value: \"TEXT\", summary: \"Full procedure and reasoning — not just a restatement of the title\" }, { name: \"due\", type: \"string\", value: \"WHEN\", summary: \"Due date: ISO, natural language, or a recurrence (\\\"every monday at 9\\\")\" }, { name: \"priority\", type: \"string\", value: \"P\", summary: \"p1 (highest) … p4 (default)\" }, { name: \"url\", type: \"string\", value: \"URL\", summary: \"Reference — prefer a hook:// URL over a file path\" }, { name: \"into\", type: \"string\", value: \"SUB_PROJECT\", summary: \"File into this sub-project under the bus root, creating it if absent\" }],\n    examples: [\"pai task add \\\"Fix the login bug\\\"\",\"pai task add \\\"Write release notes\\\" --owner my-app --due tomorrow\"],\n    seeAlso: [\"pai task dispatch\",\"pai task list\"],\n    run: async (ctx) => addCommand(ctx.args.TITLE as string, {\n      owner: ctx.opts.owner as string | undefined,\n      body: ctx.opts.body as string | undefined,\n      due: ctx.opts.due as string | undefined,\n      priority: ctx.opts.priority as string | undefined,\n      url: ctx.opts.url as string | undefined,\n      into: ctx.opts.into as string | undefined\n    })\n  },\n  {\n    name: \"dispatch\",\n    summary: \"Hand open tasks to the sessions that own them\",\n    description: \"Hand open tasks to the sessions that own them.\\n\\nHand open tasks to the PAI sessions that own them\",\n    args: [],\n    options: [{ name: \"owner\", type: \"string\", value: \"PROJECT\", summary: \"Only dispatch tasks for this project\" }, { name: \"today\", type: \"boolean\", summary: \"Only tasks due today or overdue\" }, { name: \"dry-run\", type: \"boolean\", summary: \"Report what would happen without contacting any session\" }, { name: \"no-spawn\", type: \"boolean\", summary: \"Never launch a session; skip owners that are not running\" }],\n    examples: [\"pai task dispatch --dry-run\",\"pai task dispatch --owner my-app --no-spawn\"],\n    seeAlso: [\"pai task poll\",\"pai task add\"],\n    run: async (ctx) => dispatchCommand({\n      owner: ctx.opts.owner as string | undefined,\n      today: ctx.opts.today === true,\n      dryRun: ctx.opts[\"dry-run\"] === true,\n      noSpawn: ctx.opts[\"no-spawn\"] === true\n    })\n  },\n  {\n    name: \"poll\",\n    summary: \"Run one scheduler tick: dispatch what is due and report\",\n    description: \"Run one scheduler tick: dispatch what is due and report.\\n\\nOne scheduler tick: dispatch what is due, check what is running, report\",\n    args: [],\n    options: [{ name: \"dry-run\", type: \"boolean\", summary: \"Show what would happen without touching anything\" }],\n    examples: [\"pai task poll --dry-run\",\"pai task poll\"],\n    seeAlso: [\"pai task schedule\",\"pai task dispatch\"],\n    run: async (ctx) => pollCommand({ dryRun: ctx.opts[\"dry-run\"] === true })\n  },\n  {\n    name: \"schedule\",\n    summary: \"Install, remove or check the timer that runs the scheduler\",\n    description: \"Install, remove or check the timer that runs the scheduler.\\n\\nInstall, remove or inspect the launchd agent that ticks the scheduler\",\n    examples: [\"pai task schedule status\",\"pai task schedule install --interval 900\"],\n    seeAlso: [\"pai task config\",\"pai task poll\"],\n    subcommands: [\n      {\n        name: \"install\",\n        summary: \"Install the timer (default: every 15 minutes)\",\n        description: \"Install the timer (default: every 15 minutes).\\n\\nInstall the scheduler tick (default: every 15 minutes)\",\n        args: [],\n        options: [{ name: \"interval\", type: \"string\", value: \"SECS\", summary: \"Seconds between ticks\" }],\n        examples: [\"pai task schedule install\",\"pai task schedule install --interval 600\"],\n        seeAlso: [\"pai task schedule uninstall\"],\n        run: (ctx) => scheduleInstallCommand({ interval: ctx.opts.interval ? Number.parseInt(ctx.opts.interval as string, 10) : undefined })\n      },\n      {\n        name: \"uninstall\",\n        summary: \"Remove the timer\",\n        description: \"Remove the timer.\\n\\nRemove the scheduler agent\",\n        args: [],\n        options: [],\n        examples: [\"pai task schedule uninstall\",\"pai task schedule status\"],\n        seeAlso: [\"pai task schedule status\",\"pai task schedule install\"],\n        run: () => scheduleUninstallCommand()\n      },\n      {\n        name: \"status\",\n        summary: \"Show whether the timer is installed and running\",\n        description: \"Show whether the timer is installed and running.\\n\\nShow whether the scheduler agent is installed and loaded\",\n        args: [],\n        options: [],\n        examples: [\"pai task schedule status\",\"pai task schedule status\"],\n        seeAlso: [\"pai task schedule uninstall\"],\n        run: () => scheduleStatusCommand()\n      }\n    ],\n    run: undefined,\n  },\n  {\n    name: \"config\",\n    summary: \"Show or change task settings\",\n    description: \"Show or change task settings.\\n\\nView or change task bus settings without running the full setup wizard\",\n    args: [],\n    options: [{ name: \"token\", type: \"boolean\", summary: \"Prompt for the Todoist API token (input is hidden)\" }, { name: \"from-env\", type: \"boolean\", summary: \"Adopt the token from TODOIST_API_KEY in the environment\" }, { name: \"project\", type: \"string\", value: \"ID\", summary: \"Tracker project ID that roots the bus (an ID, never a name)\" }, { name: \"findings\", type: \"string\", value: \"ID\", summary: \"Section ID for the findings inbox\" }, { name: \"timeout\", type: \"string\", value: \"SECS\", summary: \"Seconds a single dispatch may take\" }, { name: \"auto-dispatch\", type: \"string\", value: \"BOOL\", summary: \"Hand tasks to owning sessions automatically (true/false)\" }, { name: \"disable\", type: \"boolean\", summary: \"Turn the task bus off without discarding its settings\" }],\n    examples: [\"pai task config\",\"pai task config --auto-dispatch true\"],\n    seeAlso: [\"pai task archive\",\"pai task schedule\"],\n    run: async (ctx) => configCommand({\n      token: ctx.opts.token === true,\n      fromEnv: ctx.opts[\"from-env\"] === true,\n      project: ctx.opts.project as string | undefined,\n      findings: ctx.opts.findings as string | undefined,\n      timeout: ctx.opts.timeout ? Number.parseInt(ctx.opts.timeout as string, 10) : undefined,\n      autoDispatch: ctx.opts[\"auto-dispatch\"] as string | undefined,\n      disable: ctx.opts.disable === true\n    })\n  },\n  {\n    name: \"archive\",\n    summary: \"Save a task's discussion into the project notes\",\n    description: \"Save a task's discussion into the project notes.\\n\\nSave a task's discussion into the owning project's notes, without completing it\",\n    args: [{ name: \"ID\", summary: \"Task id (see: pai task list)\", required: true }],\n    options: [{ name: \"quiet\", type: \"boolean\", summary: \"Print nothing (for hook and webhook callers)\" }, { name: \"no-notify\", type: \"boolean\", summary: \"Do not tell the owning session where the file landed\" }],\n    examples: [\"pai task archive 123456\",\"pai task archive 123456 --quiet\"],\n    seeAlso: [\"pai task done\",\"pai task config\"],\n    run: async (ctx) => archiveCommand(ctx.args.ID as string, {\n      quiet: ctx.opts.quiet === true,\n      notify: ctx.opts[\"no-notify\"] !== true\n    })\n  },\n  {\n    name: \"done\",\n    summary: \"Mark a task complete\",\n    description: \"Mark a task complete.\\n\\nMark a task complete on the tracker, keeping its discussion\",\n    args: [{ name: \"ID\", summary: \"Task id (see: pai task list)\", required: true }],\n    options: [{ name: \"no-archive\", type: \"boolean\", summary: \"Complete without saving the comment thread\" }],\n    examples: [\"pai task done 123456\",\"pai task done 123456 --no-archive\"],\n    seeAlso: [\"pai task archive\"],\n    run: async (ctx) => doneCommand(ctx.args.ID as string, {\n      archive: ctx.opts[\"no-archive\"] !== true\n    })\n  },\n];\n\nexport const taskCommand: CommandDef = {\n  name: \"task\",\n  summary: \"Cross-session task list: list, add, dispatch, done\",\n  description: \"Cross-session task list: list, add, dispatch, done.\",\n  examples: [\"pai task list\",\"pai task add \\\"Fix the login bug\\\" --owner my-app\"],\n  subcommands: taskSubcommands,\n};\n\nexport const taskAliasCommand: CommandDef = {\n  name: \"tasks\",\n  summary: \"Same as task\",\n  description: \"Same as `pai task`: Cross-session task list: list, add, dispatch, done.\",\n  examples: [\"pai tasks list\",\"pai tasks add \\\"Fix the login bug\\\" --owner my-app\"],\n  aliasOf: \"task\",\n  subcommands: taskSubcommands,\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 { ok, warn, err, dim, bold, header } from \"../lib/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\nexport async function topicCheck(\n  context: string,\n  opts: {\n    current?: string;\n    threshold?: string;\n    json?: boolean;\n  }\n): Promise<number> {\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    return 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 0;\n    }\n\n    printTopicResult(result);\n\n    // Exit with code 1 if a shift was detected (useful for scripting / hooks)\n    return result.shifted ? 1 : 0;\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    return 1;\n  }\n}\n","/**\n * `pai topic`: front end for the topic commands.\n * Behaviour lives in src/cli/impl/topic.js; this file declares names, flags and help.\n */\n\nimport type { CommandDef } from \"../define.js\";\nimport { topicCheck } from \"../impl/topic.js\";\n\nexport const topicSubcommands: CommandDef[] = [\n{\n  name: \"check\",\n  summary: \"Check whether a text belongs to a different project than the current one\",\n  description: \"Check whether a text belongs to a different project than the current one.\\n\\nCheck whether context text has drifted to a different project.\\nExample: pai topic check \\\"working on JWT authentication\\\"\\nExit code 1 if a shift is detected (useful for hooks).\",\n  args: [{ name: \"CONTEXT\", summary: \"The text to check, in quotes\", required: true }],\n  options: [{ name: \"current\", type: \"string\", value: \"SLUG\", summary: \"The project this session is currently routed to\" }, { name: \"threshold\", type: \"string\", value: \"N\", summary: \"Confidence threshold [0-1] to declare a shift (default: 0.6)\" }, { name: \"json\", type: \"boolean\", summary: \"Output raw JSON result\" }],\n  examples: [\"pai topic check \\\"fix the login page\\\"\",\"pai topic check \\\"fix the login page\\\" --current my-app --json\"],\n  run: (ctx) => topicCheck(String(ctx.args.CONTEXT), { current: ctx.opts.current as string | undefined, threshold: ctx.opts.threshold as string | undefined, json: ctx.opts.json === true }),\n},\n];\n\nexport const topicCommand: CommandDef = {\n  name: \"topic\",\n  summary: \"Detect when a conversation moved to another project\",\n  description: \"Detect when a conversation moved to another project.\",\n  examples: [\"pai topic check \\\"fix the login page\\\"\",\"pai topic check \\\"fix the login page\\\" --current my-app\"],\n  subcommands: topicSubcommands,\n};\n","/** Projects, tasks, config and infrastructure commands, by the group of `pai more` they are listed under. */\n\nimport type { CommandDef, GroupId } from \"../define.js\";\nimport { configCommand } from \"./config.js\";\nimport { daemonCommand } from \"./daemon.js\";\nimport { dbCommand } from \"./db.js\";\nimport { identityCommand } from \"./identity.js\";\nimport { issueCommand } from \"./issue.js\";\nimport { mcpCommand } from \"./mcp.js\";\nimport { notifyCommand } from \"./notify.js\";\nimport { projectAliasCommand, projectCommand } from \"./project.js\";\nimport { registryCommand } from \"./projects-registry.js\";\nimport { taskAliasCommand, taskCommand } from \"./task.js\";\nimport { topicCommand } from \"./topic.js\";\n\nexport const BATCH_C: Partial<Record<GroupId, CommandDef[]>> = {\n  memory: [projectCommand, projectAliasCommand, registryCommand, taskCommand, taskAliasCommand, issueCommand, identityCommand, topicCommand],\n  daemon: [daemonCommand, configCommand, mcpCommand, notifyCommand, dbCommand],\n};\n","/**\n * Switch an index to another embedding backend: null every vector in bounded\n * batches, then record the new binding. The normal embed loop refills.\n *\n * Order matters for crash safety. Clearing first leaves the old binding\n * intact until nothing of the old backend's vectors remains, so an\n * interrupted run is resumable (run it again) and never leaves new-backend\n * vectors beside old ones. Recording the binding first would let the embed\n * loop fill NULLs with the new backend while old vectors still exist.\n */\n\nimport type { StorageBackend } from \"../storage/interface.js\";\nimport type { EmbeddingBackend } from \"./backends/types.js\";\n\nexport async function reembedIndex(\n  storage: StorageBackend,\n  target: EmbeddingBackend,\n  opts: { batchSize?: number; onBatch?: (clearedSoFar: number) => void } = {},\n): Promise<number> {\n  const batch = opts.batchSize ?? 5000;\n  let total = 0;\n  for (;;) {\n    const n = await storage.clearEmbeddingsBatch(batch);\n    if (n === 0) break;\n    total += n;\n    opts.onBatch?.(total);\n  }\n  await storage.setEmbeddingBinding({ backend: target.id, model: target.model, dims: target.dims });\n  return total;\n}\n","/** Memory embedding backend commands: detect, use, provision, and reembed. */\n\nimport { getStorageBackend } from \"../../../storage/factory.js\";\nimport { dim, bold, ok, err } from \"../../lib/utils.js\";\nimport { loadConfig } from \"../../../daemon/config.js\";\nimport { setConfigValueOp } from \"../../../config/main-config-ops.js\";\nimport { BACKEND_IDS, OLLAMA_ID, OLLAMA_DEFAULT_MODEL, OLLAMA_DEFAULT_URL, createBackend, detectBackends } from \"../../../memory/backends/index.js\";\nimport { provisionOllama } from \"../../../memory/backends/ollama-provision.js\";\nimport { measureThroughput } from \"../../../memory/embedding-gate.js\";\nimport { reembedIndex } from \"../../../memory/reembed.js\";\n\nfunction fail(e: unknown): void {\n  console.error(err(e instanceof Error ? e.message : String(e)));\n  process.exitCode = 1;\n}\n\nexport async function memoryBackendDetectCmd(): Promise<number> {\n  try {\n    const cfg = loadConfig();\n    const { results, recommended } = await detectBackends(cfg);\n    for (const r of results) {\n      console.log(`  ${r.ok ? ok(r.id) : dim(r.id)}  ${r.ok ? \"available\" : \"unavailable\"}: ${r.reason}`);\n    }\n    console.log(`\\nConfigured: ${bold(cfg.embedding?.backend ?? \"transformers-cpu-q8\")}`);\n    console.log(recommended ? `Recommended: ${bold(recommended)}` : \"Recommended: none available\");\n    if (recommended && recommended !== cfg.embedding?.backend) {\n      console.log(dim(`  Switch with \\`pai memory backend use ${recommended}\\` (an existing index also needs \\`pai memory reembed\\`).`));\n    }\n    return 0;\n  } catch (e) {\n    fail(e);\n    return 1;\n  }\n}\n\nexport async function memoryBackendUseCmd(id: string, opts: { model?: string }): Promise<number> {\n  try {\n    const b = createBackend(id, loadConfig(), opts.model); // validates the id\n    setConfigValueOp(\"embedding.backend\", id);\n    if (opts.model) setConfigValueOp(\"embedding.model\", opts.model, { force: true });\n    console.log(ok(`embedding.backend = ${id}`) + (opts.model ? ` (model ${b.model})` : \"\"));\n    const avail = await b.available();\n    if (!avail.ok) console.log(dim(`  Note: not available right now: ${avail.reason}`));\n    console.log(dim(\"  If the index already holds vectors from another backend, run `pai memory reembed`.\"));\n    return 0;\n  } catch (e) {\n    fail(e);\n    return 1;\n  }\n}\n\nexport async function memoryBackendProvisionCmd(backend: string): Promise<number> {\n  try {\n    if (backend !== \"ollama\") throw new Error(`Cannot provision \"${backend}\"; only \"ollama\" needs provisioning`);\n    const cfg = loadConfig();\n    await provisionOllama({\n      baseUrl: cfg.embedding?.ollama?.baseUrl ?? OLLAMA_DEFAULT_URL,\n      model: cfg.embedding?.model ?? OLLAMA_DEFAULT_MODEL,\n    });\n    console.log(ok(\"Done.\") + dim(`  Use it with \\`pai memory backend use ${OLLAMA_ID}\\`.`));\n    return 0;\n  } catch (e) {\n    fail(e);\n    return 1;\n  }\n}\n\nexport async function memoryReembedCmd(opts: { backend?: string; yes?: boolean }): Promise<number> {\n  try {\n    const cfg = loadConfig();\n    const id = opts.backend ?? cfg.embedding?.backend ?? \"transformers-cpu-q8\";\n    const target = createBackend(id, cfg);\n    const avail = await target.available();\n    if (!avail.ok) throw new Error(`${id} is not available: ${avail.reason}`);\n\n    const storage = await getStorageBackend();\n    const { chunks } = await storage.getStats();\n    // ponytail: synthetic ~400-token texts; real chunk mix shifts the ETA somewhat.\n    const probe = Array.from({ length: 32 }, () => \"memory index chunk embedding vector query \".repeat(40));\n    const cps = await measureThroughput(target, probe);\n    const eta = cps > 0 ? Math.round(chunks / cps / 60) : NaN;\n    console.log(`Index: ${bold(String(chunks))} chunks. Backend ${bold(id)} runs ~${cps.toFixed(1)} chunks/s: ETA ~${eta} min.`);\n\n    if (!opts.yes) {\n      console.error(err(\"Refusing without --yes: this clears every stored vector (keyword search keeps working while it refills).\"));\n      process.exitCode = 1;\n      return 1;\n    }\n    const cleared = await reembedIndex(storage, target, {\n      onBatch: (n) => process.stdout.write(`\\r  ${n} vectors cleared...`),\n    });\n    process.stdout.write(\"\\r\");\n    if (opts.backend && opts.backend !== cfg.embedding?.backend) setConfigValueOp(\"embedding.backend\", id);\n    console.log(ok(`Cleared ${cleared} vectors; index bound to ${id} (${target.model}).`));\n    console.log(dim(\"  Refill with `pai memory embed --background` (the daemon pass also does it).\"));\n    return 0;\n  } catch (e) {\n    fail(e);\n    return 1;\n  }\n}\n","/** Memory embed command: generate embeddings for un-embedded chunks. */\n\nimport type { StorageBackend } from \"../../../storage/interface.js\";\nimport { getStorageBackend, getRegistryBackend } from \"../../../storage/factory.js\";\nimport { dim, bold, ok, err } from \"../../lib/utils.js\";\nimport { reexecBackground } from \"../../../daemon/pass-priority.js\";\nimport { retryTransient } from \"../../../memory/indexer/retry.js\";\nimport { CliError } from \"../../define.js\";\n\n// ---------------------------------------------------------------------------\n// Shared embed runner (used by both index --embed and embed sub-command)\n// ---------------------------------------------------------------------------\n\n/** Rows fetched per DB round-trip. Keeps memory flat regardless of backlog size. */\nconst DEFAULT_PAGE_SIZE = 500;\n\nexport async function runEmbed(\n  backend: StorageBackend,\n  projectId?: number,\n  projectSlug?: string,\n  batchSize = 50,\n  pageSize = DEFAULT_PAGE_SIZE,\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 { serializeEmbedding } = await import(\"../../../memory/embeddings.js\");\n  const { gateBackend } = await import(\"../../../memory/embedding-gate.js\");\n\n  const gate = await gateBackend(backend, { record: true });\n  if (!gate.ok) {\n    console.error(err(gate.reason));\n    process.exitCode = 1;\n    return;\n  }\n  const embedder = gate.backend;\n\n  // Estimate, not an exact unembedded count: cheap (one row) vs. scanning the\n  // whole backlog just to size a progress bar.\n  const { chunks: totalEstimate } =\n    projectId !== undefined ? await backend.getProjectStats(projectId) : await backend.getStats();\n\n  let done = 0;\n  let after: { projectId: number; id: string } | null = null;\n\n  while (true) {\n    const page = await retryTransient(() => backend.getUnembeddedChunkIds(projectId, pageSize, after));\n    if (page.length === 0) break;\n\n    for (let i = 0; i < page.length; i += batchSize) {\n      const batch = page.slice(i, i + batchSize);\n      const vecs = await retryTransient(() => embedder.embed(batch.map((r) => r.text)));\n      await Promise.all(\n        batch.map((row, j) =>\n          retryTransient(() => backend.updateEmbedding(row.id, serializeEmbedding(vecs[j])))\n        )\n      );\n      done += batch.length;\n      process.stdout.write(`\\r  ${done} / ~${totalEstimate} chunks embedded...`);\n    }\n\n    const last = page[page.length - 1];\n    after = { projectId: last.project_id, id: last.id };\n  }\n\n  process.stdout.write(\"\\r\");\n  console.log(ok(`Done.`) + `  ${bold(String(done))} chunks embedded`);\n}\n\n// ---------------------------------------------------------------------------\n// New command function\n// ---------------------------------------------------------------------------\n\nexport async function memoryEmbedCmd(projectSlug: string | undefined, opts: { batchSize?: number; background?: boolean }): Promise<number> {\n  if (opts.background) reexecBackground();\n  let backend: StorageBackend;\n  try {\n    backend = await getStorageBackend();\n  } catch (e) {\n    console.error(err(`Failed to open storage backend: ${e}`));\n    return 1;\n  }\n\n  if (projectSlug) {\n    const registry = await getRegistryBackend();\n    const project = await registry.getProjectBySlug(projectSlug);\n\n    if (!project) {\n      console.error(err(`Project not found: ${projectSlug}`));\n      return 1;\n    }\n\n    await runEmbed(backend, project.id, project.slug, opts.batchSize ?? 50);\n  } else {\n    await runEmbed(backend, undefined, undefined, opts.batchSize ?? 50);\n  }\n\n  return 0;\n}\n","/** Memory index command: index one or all projects into the memory store. */\n\nimport type { StorageBackend } from \"../../../storage/interface.js\";\nimport type { IndexResult } from \"../../../memory/indexer-backend.js\";\nimport { indexProjectWithBackend } from \"../../../memory/indexer-backend.js\";\nimport { getStorageBackend, getRegistryBackend } from \"../../../storage/factory.js\";\nimport { dim, bold, ok, err } from \"../../lib/utils.js\";\nimport { PaiClient } from \"../../../daemon/ipc-client.js\";\nimport { loadConfig } from \"../../../daemon/config.js\";\nimport { runEmbed } from \"./embed.js\";\nimport { reexecBackground } from \"../../../daemon/pass-priority.js\";\n\n/**\n * Index every active project through the StorageBackend. Loops\n * indexProjectWithBackend() per project rather than the sync indexer's\n * indexAll(), since that one still takes a raw registry Database handle.\n */\nasync function indexAllProjects(\n  backend: StorageBackend,\n): Promise<{ projects: number; result: IndexResult }> {\n  const registry = await getRegistryBackend();\n  const projects = await registry.listProjects({ status: \"active\" });\n\n  const totals: IndexResult = { filesProcessed: 0, chunksCreated: 0, filesSkipped: 0 };\n  for (const project of projects) {\n    const r = await indexProjectWithBackend(backend, project.id, project.root_path, project.claude_notes_dir);\n    totals.filesProcessed += r.filesProcessed;\n    totals.chunksCreated += r.chunksCreated;\n    totals.filesSkipped += r.filesSkipped;\n  }\n  return { projects: projects.length, result: totals };\n}\n\nexport async function memoryIndexCmd(\n  projectSlug: string | undefined,\n  opts: { all?: boolean; embed?: boolean; direct?: boolean; background?: boolean }\n): Promise<number> {\n  if (opts.background) {\n    opts.direct = true;\n    reexecBackground();\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 0;\n    } catch {\n      console.log(dim(\"Daemon not running. Running direct index...\"));\n    }\n  }\n\n  let backend: StorageBackend;\n  try {\n    backend = await getStorageBackend();\n  } catch (e) {\n    console.error(err(`Failed to open storage backend: ${e}`));\n    process.exitCode = 1;\n    return 1;\n  }\n\n  if (projectSlug) {\n    const registry = await getRegistryBackend();\n    const project = await registry.getProjectBySlug(projectSlug);\n\n    if (!project || project.status !== \"active\") {\n      console.error(err(`Project not found or not active: ${projectSlug}`));\n      process.exitCode = 1;\n      return 1;\n    }\n\n    console.log(dim(`Indexing ${project.display_name} (${project.slug})...`));\n    const result = await indexProjectWithBackend(backend, project.id, project.root_path, project.claude_notes_dir);\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(backend, 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 indexAllProjects(backend);\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(backend);\n    }\n  }\n\n  return 0;\n}\n","/**\n * Hidden `pai memory pass <index|embed|noop>`: the child entry the daemon\n * spawns at low priority (see daemon/pass-priority.ts). Reuses the daemon's\n * pass functions and reports one JSON line on stdout.\n */\n\nimport { embedChunksWithBackend } from \"../../../memory/indexer-backend.js\";\nimport { getStorageBackend, getRegistryBackend } from \"../../../storage/factory.js\";\nimport { reexecBackground, type PassName } from \"../../../daemon/pass-priority.js\";\n\n/** Budget for one embed pass; mirrors the daemon's in-process budget. */\nconst EMBED_MAX_MILLIS = 240_000;\n\nasync function runPass(name: PassName, sleepMs: number): Promise<unknown> {\n  if (name === \"noop\") {\n    await new Promise((r) => setTimeout(r, sleepMs));\n    return { pid: process.pid };\n  }\n  const storage = await getStorageBackend();\n  const registry = await getRegistryBackend();\n  if (name === \"index\") return await storage.indexAll(registry);\n  const projectNames = new Map<number, string>();\n  try {\n    for (const r of await registry.listProjects({ status: \"active\" })) projectNames.set(r.id, r.slug);\n  } catch { /* registry unavailable — IDs will be used instead */ }\n  const count = await embedChunksWithBackend(storage, () => false, projectNames, {\n    maxMillis: EMBED_MAX_MILLIS,\n  });\n  return { count };\n}\n\nexport async function memoryPassCmd(name: string, opts: { sleep?: string | number }): Promise<number> {\n  reexecBackground();\n  try {\n    const sleepValue = opts.sleep ?? 0;\n    const sleepMs = typeof sleepValue === 'string' ? parseInt(sleepValue, 10) : sleepValue;\n    const result = await runPass(name as PassName, sleepMs);\n    process.stdout.write(JSON.stringify({ ok: true, result }) + \"\\n\");\n    return 0;\n  } catch (e) {\n    process.stdout.write(\n      JSON.stringify({ ok: false, error: e instanceof Error ? e.message : String(e) }) + \"\\n\",\n    );\n    return 1;\n  }\n}\n","/** Memory search command: BM25/semantic/hybrid search across the memory store. */\n\nimport chalk from \"chalk\";\nimport { applyRecencyBoost, type SearchResult } from \"../../../memory/search.js\";\nimport { dim, bold, ok, warn, err } from \"../../lib/utils.js\";\nimport { loadConfig } from \"../../../daemon/config.js\";\nimport { getStorageBackend, getRegistryBackend } from \"../../../storage/factory.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\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/** Populate projectSlug on results by looking up the small set of distinct project ids involved. */\nasync function populateSlugsFromRegistry(results: SearchResult[]): Promise<SearchResult[]> {\n  if (results.length === 0) return results;\n  const registry = await getRegistryBackend();\n  const ids = [...new Set(results.map((r) => r.projectId))];\n  const slugMap = new Map<number, string>();\n  for (const id of ids) {\n    const project = await registry.getProjectById(id);\n    if (project) slugMap.set(id, project.slug);\n  }\n  return results.map((r) => ({ ...r, projectSlug: slugMap.get(r.projectId) }));\n}\n\n// ---------------------------------------------------------------------------\n// Command function\n// ---------------------------------------------------------------------------\n\nexport async function memorySearchCmd(\n  query: string,\n  opts: {\n    project?: string;\n    source?: string;\n    limit?: string;\n    mode?: string;\n    rerank?: boolean;\n    recency?: string;\n  },\n): Promise<number> {\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.exitCode = 1;\n    return 1;\n  }\n\n  let projectIds: number[] | undefined;\n  if (opts.project) {\n    const registry = await getRegistryBackend();\n    const project = await registry.getProjectBySlug(opts.project);\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  // getStorageBackend() returns the process-wide shared backend, closed\n  // once at process exit (src/storage/factory.ts's beforeExit hook) —\n  // this command must not close it itself.\n  const backend = await getStorageBackend();\n  let results: SearchResult[];\n\n  if (mode === \"keyword\") {\n    results = await backend.searchKeyword(query, searchOpts);\n\n  } else {\n    const { embedQuery } = await import(\"../../../memory/embedding-gate.js\");\n\n    console.log(dim(\"Generating query embedding...\"));\n    const q = await embedQuery(backend, query);\n    const queryEmbedding = q.vec;\n\n    if (!queryEmbedding) {\n      console.log(dim(`  ${q.note}`));\n      results = await backend.searchKeyword(query, searchOpts);\n    } else 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  }\n\n  if (!results || results.length === 0) {\n    console.log(dim(`No results found for: \"${query}\" (mode: ${mode})`));\n    return 0;\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    console.log(dim(`Applying recency boost (half-life: ${recencyDays} days)...`));\n    results = applyRecencyBoost(results, recencyDays);\n  }\n\n  const withSlugs = await populateSlugsFromRegistry(results);\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  return 0;\n}\n","/** `pai memory sources` — what the indexer has taken in. */\n\nimport { err, dim } from \"../../lib/utils.js\";\n\nexport async function memorySourcesCmd(opts: { limit?: number }): Promise<number> {\n  // Reads whichever backend is configured — unlike `memory status`, which\n  // reads the SQLite federation file directly and therefore has nothing to\n  // say when the live index lives in Postgres.\n  const { createStorageBackend } = await import(\"../../../storage/factory.js\");\n  const { loadConfig } = await import(\"../../../daemon/config.js\");\n  const { cmdMemorySources } = await import(\"./sources.js\");\n\n  let backend;\n  try {\n    backend = await createStorageBackend(loadConfig());\n  } catch (e) {\n    console.error(err(`Cannot reach the storage backend.`));\n    console.error(dim(`  ${e instanceof Error ? e.message : String(e)}`));\n    return 1;\n  }\n\n  try {\n    await cmdMemorySources(backend, {\n      limit: Math.max(1, opts.limit ?? 8),\n    });\n    return 0;\n  } finally {\n    await backend.close?.().catch?.(() => {});\n  }\n}\n","/** Memory status and settings commands for the PAI memory index. */\n\nimport { dim, bold, ok, err } from \"../../lib/utils.js\";\nimport { loadConfig, paiConfigFilePath, readMainConfigRaw, writeMainConfigRaw } from \"../../../daemon/config.js\";\nimport { getStorageBackend, getRegistryBackend } from \"../../../storage/factory.js\";\n\nexport async function memoryStatusCmd(projectSlug: string | undefined): Promise<number> {\n  const backend = await getStorageBackend();\n  const backendType = loadConfig().storageBackend;\n\n  if (projectSlug) {\n    const registry = await getRegistryBackend();\n    const project = await registry.getProjectBySlug(projectSlug);\n\n    if (!project) {\n      console.error(err(`Project not found: ${projectSlug}`));\n      return 1;\n    }\n\n    // StorageBackend has no per-project file/chunk/tier breakdown today\n    // (only the global getStats() total) — see worker report for the\n    // missing method (getMemoryStats(projectId?) with per-tier counts\n    // and last-indexed timestamp).\n    console.log(`\\n  ${bold(project.display_name)} ${dim(`(${project.slug})`)}\\n`);\n    console.log(dim(`  Per-project breakdown is not available via the storage backend yet.`));\n    console.log(dim(`  Authoritative counts:  ${bold(\"pai daemon status\")}`));\n    console.log();\n\n  } else {\n    const stats = await backend.getStats();\n\n    console.log(`\\n  ${bold(\"PAI Memory Index — Global Status\")}\\n`);\n    console.log(`  ${bold(\"Storage backend:\")} ${backendType}`);\n    console.log(`  ${bold(\"Total files:\")}   ${stats.files}   ${bold(\"Total chunks:\")}  ${stats.chunks}`);\n    console.log();\n    console.log(dim(`  Per-project breakdown is not available via the storage backend yet.`));\n    console.log(dim(`  Authoritative counts:  ${bold(\"pai daemon status\")}`));\n    console.log();\n  }\n\n  return 0;\n}\n\nexport async function memorySettingsCmd(key: string | undefined, value: string | undefined): Promise<number> {\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: ${paiConfigFilePath()}`));\n    console.log(dim(`  Edit directly or use: pai memory settings <key> <value>`));\n    console.log();\n    return 0;\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      return 1;\n    }\n    console.log(String(val));\n    return 0;\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    return 1;\n  }\n\n  let fileConfig: Record<string, unknown>;\n  try {\n    fileConfig = readMainConfigRaw();\n  } catch (e) {\n    console.error(err(`Could not read config: ${e instanceof Error ? e.message : String(e)}`));\n    return 1;\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      return 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      return 1;\n    }\n  }\n\n  (fileConfig.search as Record<string, unknown>)[key] = parsed;\n\n  try {\n    writeMainConfigRaw(fileConfig);\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    return 1;\n  }\n\n  return 0;\n}","/** `pai memory ...`: search and index what was written down. */\n\nimport { OPT, type CommandDef, type OptDef, CliError } from \"../define.js\";\nimport { memoryBackendDetectCmd, memoryBackendUseCmd, memoryBackendProvisionCmd, memoryReembedCmd } from \"./memory/backend.js\";\nimport { memoryEmbedCmd } from \"./memory/embed.js\";\nimport { memoryIndexCmd } from \"./memory/index-cmd.js\";\nimport { memoryPassCmd } from \"./memory/pass.js\";\nimport { memorySearchCmd } from \"./memory/search.js\";\nimport { memorySourcesCmd } from \"./memory/sources-cmd.js\";\nimport { memoryStatusCmd, memorySettingsCmd } from \"./memory/stats.js\";\n\nconst background: OptDef = { name: \"background\", type: \"boolean\", summary: \"Run at low priority so the computer stays responsive\" };\nconst projectSlug = { name: \"PROJECT\", summary: \"Project name as shown by `pai project list`\" };\n\nexport const memoryIndexCommand: CommandDef = {\n  name: \"index\",\n  summary: \"Read project notes and memory files into the search index\",\n  description:\n    \"Reads the notes and memory files of one project, or of all active projects, into the search index so `pai memory search` can find them. \" +\n    \"If the background service is running and no project is named, it is asked to do the work; otherwise it runs here.\",\n  args: [{ ...projectSlug, summary: \"Index only this project (default: all active projects)\" }],\n  options: [\n    { name: \"all\", type: \"boolean\", summary: \"All active projects (this is the default when no project is named)\" },\n    { name: \"embed\", type: \"boolean\", summary: \"Also compute the meaning vectors for new text, so semantic search can use it\" },\n    { name: \"direct\", type: \"boolean\", summary: \"Do not ask the background service; run here (for debugging)\" },\n    { ...background, summary: \"Run at low priority (implies --direct)\" },\n  ],\n  examples: [\"pai memory index\", \"pai memory index my-project --embed\", \"pai memory index --background\"],\n  seeAlso: [\"pai memory embed\", \"pai memory status\"],\n  run: (ctx) => memoryIndexCmd(ctx.args.PROJECT as string | undefined, {\n    all: ctx.opts.all === true,\n    embed: ctx.opts.embed === true,\n    direct: ctx.opts.direct === true,\n    background: ctx.opts.background === true,\n  }),\n};\n\nexport const memoryEmbedCommand: CommandDef = {\n  name: \"embed\",\n  summary: \"Compute meaning vectors for indexed text that has none yet\",\n  description: \"Semantic and hybrid search need a vector per text chunk. This fills in the missing ones, in batches; safe to stop and re-run.\",\n  args: [{ ...projectSlug, summary: \"Only this project (default: all)\" }],\n  options: [\n    { name: \"batch-size\", type: \"number\", value: \"N\", default: 50, summary: \"Chunks to compute per batch\" },\n    { ...background },\n  ],\n  examples: [\"pai memory embed\", \"pai memory embed my-project --batch-size 20\", \"pai memory embed --background\"],\n  seeAlso: [\"pai memory index\", \"pai memory backend detect\"],\n  run: (ctx) => memoryEmbedCmd(ctx.args.PROJECT as string | undefined, {\n    batchSize: ctx.opts[\"batch-size\"] as number | undefined,\n    background: ctx.opts.background === true,\n  }),\n};\n\nexport const memorySearchCommand: CommandDef = {\n  name: \"search\",\n  summary: \"Search what the memory index knows\",\n  description:\n    \"Finds notes and memory files that match a query: by keywords (default), by meaning (semantic), or both (hybrid). \" +\n    \"Results are re-ranked unless you turn that off, and recent items get a boost if you ask for one.\",\n  args: [{ name: \"QUERY\", required: true, summary: \"What to look for\" }],\n  options: [\n    { ...OPT.project, value: \"SLUG\", summary: \"Only this project\" },\n    { name: \"source\", type: \"string\", value: \"KIND\", summary: \"Only 'memory' files or only 'notes'\" },\n    { name: \"limit\", type: \"number\", value: \"N\", summary: \"Maximum results (default from `pai memory settings`)\" },\n    { name: \"mode\", type: \"string\", value: \"MODE\", summary: \"keyword (default), semantic or hybrid\" },\n    { name: \"no-rerank\", type: \"boolean\", summary: \"Skip the second ranking pass (faster, a little less precise)\" },\n    { name: \"recency\", type: \"number\", value: \"DAYS\", summary: \"Boost recent results: the score halves every DAYS days; 0 = off\" },\n  ],\n  examples: ['pai memory search \"release checklist\"', 'pai memory search \"retry loop\" --project my-project --mode hybrid', 'pai memory search deploy --no-rerank --limit 5'],\n  seeAlso: [\"pai memory settings\", \"pai find\"],\n  run: (ctx) => memorySearchCmd(ctx.args.QUERY as string, {\n    project: ctx.opts.project as string | undefined,\n    source: ctx.opts.source as string | undefined,\n    limit: ctx.opts.limit as string | undefined,\n    mode: ctx.opts.mode as string | undefined,\n    rerank: ctx.opts[\"no-rerank\"] === true ? false : true,\n    recency: ctx.opts.recency as string | undefined,\n  }),\n};\n\nexport const memoryStatusCommand: CommandDef = {\n  name: \"status\",\n  summary: \"How much the memory index holds\",\n  description: \"Shows the storage backend and the number of indexed files and chunks. For live counts per project use `pai daemon status`.\",\n  args: [{ ...projectSlug, summary: \"Show this project only\" }],\n  examples: [\"pai memory status\", \"pai memory status my-project\"],\n  seeAlso: [\"pai memory sources\", \"pai daemon status\"],\n  run: (ctx) => memoryStatusCmd(ctx.args.PROJECT as string | undefined),\n};\n\nexport const memorySettingsCommand: CommandDef = {\n  name: \"settings\",\n  summary: \"Show or change the search settings\",\n  description:\n    \"With no arguments, lists the search settings (mode, rerank, recencyBoostDays, defaultLimit, snippetLength). \" +\n    \"With a key, prints its value; with a key and a value, saves it to the PAI config file.\",\n  args: [\n    { name: \"KEY\", summary: \"Setting name\" },\n    { name: \"VALUE\", summary: \"New value\" },\n  ],\n  examples: [\"pai memory settings\", \"pai memory settings mode\", \"pai memory settings mode hybrid\"],\n  seeAlso: [\"pai config path\"],\n  run: (ctx) => memorySettingsCmd(ctx.args.KEY as string | undefined, ctx.args.VALUE as string | undefined),\n};\n\nexport const memorySourcesCommand: CommandDef = {\n  name: \"sources\",\n  summary: \"Where the indexed content comes from\",\n  description:\n    \"Shows what the indexer has taken in: composition by source, which roots content enters from, the heaviest single files, \" +\n    \"and how many chunks are rewritten per day (which tells a finite backlog from a treadmill).\",\n  options: [{ name: \"limit\", type: \"number\", value: \"N\", default: 8, summary: \"Rows per section\" }],\n  examples: [\"pai memory sources\", \"pai memory sources --limit 20\"],\n  seeAlso: [\"pai memory status\"],\n  run: (ctx) => memorySourcesCmd({ limit: ctx.opts.limit as number | undefined }),\n};\n\nexport const memoryBackendDetectCommand: CommandDef = {\n  name: \"detect\",\n  summary: \"Check which embedding backends work here\",\n  description: \"Probes the available embedding backends (ollama, then transformers-cpu) and recommends the fastest.\",\n  examples: [\"pai memory backend detect\", \"pai memory backend use ollama\"],\n  seeAlso: [\"pai memory backend use\"],\n  run: () => memoryBackendDetectCmd(),\n};\n\nexport const memoryBackendUseCommand: CommandDef = {\n  name: \"use\",\n  summary: \"Choose the embedding backend\",\n  description: \"Writes the backend to the config. It does not touch the index; an index that already holds vectors from another backend needs `pai memory reembed`.\",\n  args: [{ name: \"ID\", required: true, summary: \"Backend id, e.g. ollama or transformers-cpu-q8\" }],\n  options: [{ name: \"model\", type: \"string\", value: \"NAME\", summary: \"Model name on that backend\" }],\n  examples: [\"pai memory backend use ollama\", \"pai memory backend use transformers-cpu-q8 --model my-model\"],\n  seeAlso: [\"pai memory backend detect\", \"pai memory reembed\"],\n  run: (ctx) => memoryBackendUseCmd(ctx.args.ID as string, {\n    model: ctx.opts.model as string | undefined,\n  }),\n};\n\nexport const memoryBackendProvisionCommand: CommandDef = {\n  name: \"provision\",\n  summary: \"Download and set up a backend's model\",\n  description: \"Only ollama needs this: it downloads the embedding model file and registers it with ollama.\",\n  args: [{ name: \"BACKEND\", required: true, summary: \"Backend to set up (only: ollama)\" }],\n  examples: [\"pai memory backend provision ollama\", \"pai memory backend use ollama\"],\n  seeAlso: [\"pai memory backend use\"],\n  run: (ctx) => memoryBackendProvisionCmd(ctx.args.BACKEND as string),\n};\n\nexport const memoryBackendCommand: CommandDef = {\n  name: \"backend\",\n  summary: \"Embedding backends: detect, use, provision\",\n  description: \"An index is bound to the backend that embedded it. Pick a backend here; switching an existing index also needs `pai memory reembed`.\",\n  examples: [\"pai memory backend detect\", \"pai memory backend use ollama\"],\n  seeAlso: [\"pai memory reembed\"],\n  subcommands: [memoryBackendDetectCommand, memoryBackendUseCommand, memoryBackendProvisionCommand],\n};\n\nexport const memoryReembedCommand: CommandDef = {\n  name: \"reembed\",\n  summary: \"Switch the index to another embedding backend\",\n  description:\n    \"Clears every stored vector (in batches, resumable) so the index can be refilled by the new backend with `pai memory embed`. \" +\n    \"Keyword search keeps working meanwhile. Without --yes it only prints the plan and refuses.\",\n  options: [\n    { name: \"backend\", type: \"string\", value: \"ID\", summary: \"Target backend (default: the configured one); also written to the config\" },\n    { name: \"yes\", type: \"boolean\", summary: \"Go ahead; without it only the plan is printed\" },\n  ],\n  examples: [\"pai memory reembed\", \"pai memory reembed --backend ollama --yes\"],\n  seeAlso: [\"pai memory embed\", \"pai memory backend use\"],\n  run: (ctx) => memoryReembedCmd({\n    backend: ctx.opts.backend as string | undefined,\n    yes: ctx.opts.yes === true,\n  }),\n};\n\nconst memoryPassCommand: CommandDef = {\n  name: \"pass\",\n  summary: \"Internal: one background pass started by the daemon\",\n  description: \"Used by the background service to run an index or embed pass at low priority and report one JSON line. Not meant to be typed.\",\n  args: [{ name: \"NAME\", required: true, summary: \"index, embed or noop\" }],\n  options: [{ name: \"sleep\", type: \"number\", value: \"MS\", default: 0, summary: \"noop pass only: sleep this many milliseconds\" }],\n  examples: [\"pai memory pass noop --sleep 10\", \"pai memory pass index\"],\n  run: (ctx) => memoryPassCmd(ctx.args.NAME as string, { sleep: ctx.opts.sleep as number | undefined }),\n};\n\nexport const memoryCommand: CommandDef = {\n  name: \"memory\",\n  summary: \"Search and index your notes and memory files\",\n  description: \"The memory index makes notes and memory files searchable. `pai memory search` is the one you want most days.\",\n  examples: [\"pai memory search \\\"release checklist\\\"\", \"pai memory status\", \"pai memory index\"],\n  seeAlso: [\"pai find\", \"pai kg\", \"pai zettel\"],\n  subcommands: [\n    memorySearchCommand,\n    memoryStatusCommand,\n    memoryIndexCommand,\n    memoryEmbedCommand,\n    memorySettingsCommand,\n    memorySourcesCommand,\n    memoryBackendCommand,\n    memoryReembedCommand,\n    memoryPassCommand,\n  ],\n};\n","/**\n * pai kg <sub-command>\n *\n * Temporal knowledge graph management:\n *   pai kg backfill [--project SLUG] [--limit N] [--dry-run]\n *   pai kg query    [--subject S] [--predicate P] [--object O] [--as-of DATE] [--project SLUG]\n *   pai kg list     [--project SLUG] [--limit N]\n *   pai kg stats\n *\n * All commands require the Postgres backend (KG tables live in Postgres).\n */\n\nimport type { StorageBackend } from \"../../storage/interface.js\";\n\nimport { ok, warn, err, dim, bold, header } from \"./utils.js\";\nimport { loadConfig, CONFIG_FILE } from \"../../daemon/config.js\";\nimport { createStorageBackend, getRegistryBackend } from \"../../storage/factory.js\";\nimport { backfillKgFromNotes } from \"../../memory/kg-backfill.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nasync function getBackend(): Promise<StorageBackend> {\n  const config = loadConfig();\n  if (config.storageBackend !== \"postgres\") {\n    console.error(err(\"  KG commands require Postgres backend.\"));\n    console.error(dim(`  Set \"storageBackend\": \"postgres\" in ${CONFIG_FILE}`));\n    process.exit(1);\n  }\n  const backend = await createStorageBackend(config);\n  if (!backend.supportsPostgresFeatures) {\n    console.error(err(\"  Postgres backend unavailable — fell back to SQLite.\"));\n    process.exit(1);\n  }\n  return backend;\n}\n\nfunction shorten(s: string, n: number): string {\n  return s.length <= n ? s : s.slice(0, n - 1) + \"…\";\n}\n\n// ---------------------------------------------------------------------------\n// pai kg backfill\n// ---------------------------------------------------------------------------\n\nexport async function kgBackfillCmd(opts: {\n  project?: string;\n  limit?: string;\n  dryRun?: boolean;\n}): Promise<number> {\n  const limit = opts.limit ? parseInt(opts.limit, 10) : undefined;\n  if (limit !== undefined && (isNaN(limit) || limit < 1)) {\n    console.error(err(\"  --limit must be a positive integer\"));\n    process.exitCode = 1;\n    return 1;\n  }\n\n  console.log();\n  console.log(header(\"  PAI KG Backfill\"));\n  console.log();\n  if (opts.project) console.log(`  ${bold(\"Project:\")} ${opts.project}`);\n  if (limit) console.log(`  ${bold(\"Limit:\")}   ${limit}`);\n  if (opts.dryRun) console.log(`  ${bold(\"Mode:\")}    ${warn(\"dry-run\")}`);\n  console.log();\n\n  try {\n    const result = await backfillKgFromNotes({\n      projectSlug: opts.project,\n      limit,\n      dryRun: opts.dryRun,\n      onProgress: (current, total, note) => {\n        const short = shorten(note.replace(process.env.HOME ?? \"\", \"~\"), 70);\n        process.stdout.write(`  [${current}/${total}] ${dim(short)}\\n`);\n      },\n    });\n\n    console.log();\n    console.log(ok(\"  Backfill complete\"));\n    console.log(`    Notes processed:    ${bold(String(result.notes_processed))}`);\n    console.log(`    Triples extracted:  ${bold(String(result.triples_extracted))}`);\n    console.log(`    Triples added:      ${bold(String(result.triples_added))}`);\n    console.log(`    Triples superseded: ${bold(String(result.triples_superseded))}`);\n    if (result.errors > 0) {\n      console.log(`    ${warn(\"Errors:\")}             ${result.errors}`);\n    }\n    console.log();\n  } catch (e) {\n    const msg = e instanceof Error ? e.message : String(e);\n    console.error(err(`  ${msg}`));\n    process.exitCode = 1;\n    return 1;\n  }\n  return 0;\n}\n\n// ---------------------------------------------------------------------------\n// pai kg query\n// ---------------------------------------------------------------------------\n\nexport async function kgQueryCmd(opts: {\n  subject?: string;\n  predicate?: string;\n  object?: string;\n  asOf?: string;\n  project?: string;\n  json?: boolean;\n}): Promise<number> {\n  const backend = await getBackend();\n  const registry = await getRegistryBackend();\n  const close = () => backend.close();\n  try {\n    let projectId: number | undefined;\n    if (opts.project) {\n      const row = await registry.getProjectBySlug(opts.project);\n      if (!row) {\n        console.error(warn(`  Project not found in Postgres: ${opts.project}`));\n      } else {\n        projectId = row.id;\n      }\n    }\n\n    const asOf = opts.asOf ? new Date(opts.asOf) : undefined;\n    if (asOf && isNaN(asOf.getTime())) {\n      console.error(err(`  Invalid --as-of date: ${opts.asOf}`));\n      process.exitCode = 1;\n      return 1;\n    }\n\n    const triples = await backend.queryKgTriples({\n      subject: opts.subject,\n      predicate: opts.predicate,\n      object: opts.object,\n      project_id: projectId,\n      as_of: asOf,\n    });\n\n    if (opts.json) {\n      console.log(JSON.stringify(triples, null, 2));\n      return 0;\n    }\n\n    console.log();\n    console.log(header(`  ${triples.length} triple(s)`));\n    console.log();\n    for (const t of triples) {\n      const validity = t.valid_to\n        ? dim(`(invalidated ${t.valid_to.toISOString().slice(0, 10)})`)\n        : dim(`(valid since ${t.valid_from.toISOString().slice(0, 10)})`);\n      console.log(\n        `  ${bold(t.subject)} ${dim(\"·\")} ${t.predicate} ${dim(\"·\")} ${t.object}  ${validity}`\n      );\n    }\n    console.log();\n  } finally {\n    await close();\n  }\n  return 0;\n}\n\n// ---------------------------------------------------------------------------\n// pai kg list\n// ---------------------------------------------------------------------------\n\nexport async function kgListCmd(opts: { project?: string; limit?: string }): Promise<number> {\n  const limit = opts.limit ? parseInt(opts.limit, 10) : 50;\n  const backend = await getBackend();\n  const registry = await getRegistryBackend();\n  const close = () => backend.close();\n  try {\n    let projectId: number | undefined;\n    if (opts.project) {\n      const row = await registry.getProjectBySlug(opts.project);\n      if (row) projectId = row.id;\n    }\n\n    const triples = await backend.queryKgTriples({ project_id: projectId });\n    const slice = triples.slice(0, limit);\n\n    console.log();\n    console.log(\n      header(`  ${slice.length} of ${triples.length} currently-valid triple(s)`)\n    );\n    console.log();\n    for (const t of slice) {\n      console.log(\n        `  ${bold(t.subject)} ${dim(\"·\")} ${t.predicate} ${dim(\"·\")} ${t.object}`\n      );\n    }\n    if (triples.length > slice.length) {\n      console.log();\n      console.log(dim(`  (${triples.length - slice.length} more — increase --limit)`));\n    }\n    console.log();\n  } finally {\n    await close();\n  }\n  return 0;\n}\n\n// ---------------------------------------------------------------------------\n// pai kg stats\n// ---------------------------------------------------------------------------\n\nexport async function kgStatsCmd(): Promise<number> {\n  const backend = await getBackend();\n  try {\n    const stats = await backend.getKgStats();\n    console.log();\n    console.log(header(\"  PAI KG Stats\"));\n    console.log();\n    console.log(`  ${bold(\"Total triples:\")}        ${stats.total}`);\n    console.log(`  ${bold(\"Currently valid:\")}      ${stats.valid}`);\n    console.log(`  ${bold(\"Invalidated:\")}          ${stats.invalidated}`);\n    console.log(`  ${bold(\"Distinct subjects:\")}    ${stats.subjects}`);\n    console.log(`  ${bold(\"Distinct predicates:\")}  ${stats.predicates}`);\n    console.log(`  ${bold(\"Contradictions:\")}       ${stats.contradictions}`);\n    console.log();\n  } finally {\n    await backend.close();\n  }\n  return 0;\n}\n","/** `pai kg ...`: the knowledge graph of facts extracted from notes. */\n\nimport { OPT, type CommandDef, type OptDef } from \"../define.js\";\nimport { kgBackfillCmd, kgQueryCmd, kgListCmd, kgStatsCmd } from \"../lib/kg.js\";\n\nconst project: OptDef = { ...OPT.project, value: \"SLUG\", summary: \"Only this project\" };\n\nexport const kgCommand: CommandDef = {\n  name: \"kg\",\n  summary: \"Facts extracted from your notes, with the time they were true\",\n  description:\n    \"The knowledge graph stores facts as subject - predicate - object triples, each with the time span it was valid. \" +\n    \"Use it to ask what was true at a given date, or what changed.\",\n  examples: [\"pai kg stats\", \"pai kg list --limit 10\", \"pai kg query --subject PAI\"],\n  seeAlso: [\"pai memory search\", \"pai zettel\"],\n  subcommands: [\n    {\n      name: \"backfill\",\n      summary: \"Extract facts from existing session notes\",\n      description: \"Reads session notes that have not been processed yet and adds their facts to the graph. Safe to run again; finished notes are skipped.\",\n      options: [\n        project,\n        { name: \"limit\", type: \"number\", value: \"N\", summary: \"Process at most this many notes\" },\n        { ...OPT.dryRun, summary: \"List the notes that would be processed; extract nothing\" },\n      ],\n      examples: [\"pai kg backfill --dry-run\", \"pai kg backfill --project my-project --limit 20\"],\n      seeAlso: [\"pai kg stats\"],\n      run: async (ctx) => {\n        return await kgBackfillCmd({\n          project: ctx.opts.project as string | undefined,\n          limit: ctx.opts.limit as string | undefined,\n          dryRun: ctx.opts[\"dry-run\"] === true,\n        });\n      },\n    },\n    {\n      name: \"query\",\n      summary: \"Look up facts by subject, predicate, object or date\",\n      description: \"Prints the facts that match every filter you give. With --as-of it shows what was true on that date.\",\n      options: [\n        { name: \"subject\", type: \"string\", value: \"TEXT\", summary: \"Only facts about this subject\" },\n        { name: \"predicate\", type: \"string\", value: \"TEXT\", summary: \"Only this kind of relation\" },\n        { name: \"object\", type: \"string\", value: \"TEXT\", summary: \"Only facts with this object\" },\n        { name: \"as-of\", type: \"string\", value: \"DATE\", summary: \"What was true then (YYYY-MM-DD or ISO 8601)\" },\n        project,\n        { ...OPT.json, summary: \"Output raw JSON\" },\n      ],\n      examples: [\"pai kg query --subject PAI\", \"pai kg query --subject PAI --as-of 2026-01-01 --json\"],\n      seeAlso: [\"pai kg list\"],\n      run: async (ctx) => {\n        return await kgQueryCmd({\n          subject: ctx.opts.subject as string | undefined,\n          predicate: ctx.opts.predicate as string | undefined,\n          object: ctx.opts.object as string | undefined,\n          asOf: ctx.opts[\"as-of\"] as string | undefined,\n          project: ctx.opts.project as string | undefined,\n          json: ctx.opts.json === true,\n        });\n      },\n    },\n    {\n      name: \"list\",\n      summary: \"List the facts that are true now\",\n      description: \"Prints currently valid facts, newest first.\",\n      options: [project, { name: \"limit\", type: \"number\", value: \"N\", default: 50, summary: \"Maximum facts to print\" }],\n      examples: [\"pai kg list\", \"pai kg list --project my-project --limit 10\"],\n      seeAlso: [\"pai kg query\"],\n      run: async (ctx) => {\n        return await kgListCmd({\n          project: ctx.opts.project as string | undefined,\n          limit: ctx.opts.limit as string | undefined,\n        });\n      },\n    },\n    {\n      name: \"stats\",\n      summary: \"How many facts and contradictions the graph holds\",\n      description: \"Prints counts of facts and of facts that contradict each other.\",\n      examples: [\"pai kg stats\", \"pai kg list --limit 5\"],\n      seeAlso: [\"pai kg list\"],\n      run: async () => {\n        return await kgStatsCmd();\n      },\n    },\n  ],\n};\n","/** Shared utilities for zettel CLI commands. */\n\nimport type { StorageBackend } from \"../../../storage/interface.js\";\nimport { getStorageBackend } from \"../../../storage/factory.js\";\nimport { err } from \"../../lib/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/** Get the process-wide StorageBackend (SQLite or Postgres, per config). */\nexport async function getFedBackend(): Promise<StorageBackend> {\n  try {\n    return await getStorageBackend();\n  } catch (e) {\n    console.error(err(`Failed to open PAI storage backend: ${e}`));\n    process.exit(1);\n  }\n}\n","/** zettel explore: follow link chains from a starting note. */\n\nimport chalk from \"chalk\";\nimport { ok, warn, err, dim, bold, header } from \"../../lib/utils.js\";\nimport { getFedBackend, shortPath } from \"./utils.js\";\n\nexport async function zettelExploreCmd(\n  note: string,\n  opts: { depth?: number; direction?: string; mode?: string }\n): Promise<number> {\n  const depth = opts.depth ?? 3;\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 backend = await getFedBackend();\n\n  const result = await zettelExplore(backend, { 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 0;\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  return 0;\n}\n\n","/** zettel health: vault structural health audit. */\n\nimport chalk from \"chalk\";\nimport { ok, warn, err, dim, bold, header } from \"../../lib/utils.js\";\nimport { getFedBackend, shortPath } from \"./utils.js\";\n\nexport async function zettelHealthCmd(opts: {\n  scope?: string;\n  project?: string;\n  days?: number;\n  include?: string\n}): Promise<number> {\n  const scope = (opts.scope ?? \"full\") as \"full\" | \"recent\" | \"project\";\n  const projectPath = opts.project;\n  const recentDays = opts.days ?? 30;\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 backend = await getFedBackend();\n\n  const result = await zettelHealth(backend, { 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\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  return 0;\n}","/** zettel surprise: find semantically similar but graph-distant notes. */\n\nimport chalk from \"chalk\";\nimport { warn, err, dim, bold, header } from \"../../lib/utils.js\";\nimport { getFedBackend, shortPath } from \"./utils.js\";\nimport { CliError } from \"../../define.js\";\n\nexport async function zettelSurpriseCmd(\n  note: string,\n  opts: { vaultProjectId?: string | number; limit?: number; minSimilarity?: number; minDistance?: number }\n): Promise<number> {\n  if (opts.vaultProjectId === undefined) {\n    throw new CliError(\"--vault-project-id is required\", \"pai help zettel surprise\", 1);\n  }\n\n  const vaultProjectId = Number(opts.vaultProjectId);\n  const limit = opts.limit ?? 10;\n  const minSimilarity = opts.minSimilarity ?? 0.3;\n  const minGraphDistance = opts.minDistance ?? 3;\n\n  const { zettelSurprise } = await import(\"../../../zettelkasten/index.js\");\n  const backend = await getFedBackend();\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(backend, {\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 0;\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  return 0;\n}\n","/** zettel converse: ask the vault a question and get a synthesis prompt. */\n\nimport chalk from \"chalk\";\nimport { warn, err, dim, bold, header } from \"../../lib/utils.js\";\nimport { getFedBackend, shortPath } from \"./utils.js\";\nimport { CliError } from \"../../define.js\";\n\nexport async function zettelConverseCmd(\n  question: string,\n  opts: { vaultProjectId?: string | number; depth?: number; limit?: number }\n): Promise<number> {\n  if (opts.vaultProjectId === undefined) {\n    throw new CliError(\"--vault-project-id is required\", \"pai help zettel converse\", 1);\n  }\n\n  const vaultProjectId = Number(opts.vaultProjectId);\n  const depth = opts.depth ?? 2;\n  const limit = opts.limit ?? 15;\n\n  const { zettelConverse } = await import(\"../../../zettelkasten/index.js\");\n  const backend = await getFedBackend();\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(backend, { 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 0;\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  return 0;\n}\n","/** zettel themes: detect emerging theme clusters in recently edited notes. */\n\nimport chalk from \"chalk\";\nimport { warn, err, dim, bold, header } from \"../../lib/utils.js\";\nimport { getFedBackend, shortPath } from \"./utils.js\";\nimport { CliError } from \"../../define.js\";\n\nexport async function zettelThemesCmd(opts: {\n  vaultProjectId?: string | number;\n  days?: number;\n  minSize?: number;\n  maxThemes?: number;\n  threshold?: number\n}): Promise<number> {\n  if (opts.vaultProjectId === undefined) {\n    throw new CliError(\"--vault-project-id is required\", \"pai help zettel themes\", 1);\n  }\n\n  const vaultProjectId = Number(opts.vaultProjectId);\n  const lookbackDays = opts.days ?? 30;\n  const minClusterSize = opts.minSize ?? 3;\n  const maxThemes = opts.maxThemes ?? 10;\n  const similarityThreshold = opts.threshold ?? 0.65;\n\n  const { zettelThemes } = await import(\"../../../zettelkasten/index.js\");\n  const backend = await getFedBackend();\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(backend, {\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 0;\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  return 0;\n}\n","/** zettel suggest: suggest new wikilink connections for a note. */\n\nimport chalk from \"chalk\";\nimport { warn, err, dim, bold, header } from \"../../lib/utils.js\";\nimport { getFedBackend, shortPath } from \"./utils.js\";\nimport { CliError } from \"../../define.js\";\n\nexport async function zettelSuggestCmd(\n  note: string,\n  opts: { vaultProjectId?: string | number; limit?: number; excludeLinked?: boolean }\n): Promise<number> {\n  if (opts.vaultProjectId === undefined) {\n    throw new CliError(\"--vault-project-id is required\", \"pai help zettel suggest\", 1);\n  }\n\n  const vaultProjectId = Number(opts.vaultProjectId);\n  const limit = opts.limit ?? 5;\n  const excludeLinked = opts.excludeLinked !== true;\n\n  const { zettelSuggest } = await import(\"../../../zettelkasten/index.js\");\n  const backend = await getFedBackend();\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(backend, {\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 0;\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  return 0;\n}\n","/** `pai zettel ...`: ask questions of a linked-notes vault. */\n\nimport type { CommandDef, OptDef } from \"../define.js\";\nimport { zettelExploreCmd } from \"./zettel/explore.js\";\nimport { zettelHealthCmd } from \"./zettel/health.js\";\nimport { zettelSurpriseCmd } from \"./zettel/surprise.js\";\nimport { zettelConverseCmd } from \"./zettel/converse.js\";\nimport { zettelThemesCmd } from \"./zettel/themes.js\";\nimport { zettelSuggestCmd } from \"./zettel/suggest.js\";\n\nconst vaultId: OptDef = { name: \"vault-project-id\", type: \"string\", value: \"N\", summary: \"Required. Id of the vault's project (see `pai project list`)\" };\nconst limit = (n: number, what: string): OptDef => ({ name: \"limit\", type: \"number\", value: \"N\", default: n, summary: `Maximum ${what}` });\nconst NOTE = { name: \"NOTE\", required: true, summary: \"Note path or title\" };\n\nexport const zettelCommand: CommandDef = {\n  name: \"zettel\",\n  summary: \"Explore the links between your notes\",\n  description:\n    \"Works on a vault of linked notes: follow link chains, find surprising connections, ask questions, spot themes, check structural health. \" +\n    \"Commands that need to know which vault take --vault-project-id.\",\n  examples: [\"pai zettel health\", \"pai zettel explore my-note --depth 2\", \"pai zettel themes --vault-project-id 3\"],\n  seeAlso: [\"pai obsidian\", \"pai memory search\"],\n  subcommands: [\n    {\n      name: \"explore\",\n      summary: \"Follow link chains from a note\",\n      description: \"Starting at a note, walks the links outward and prints what it reaches.\",\n      args: [NOTE],\n      options: [\n        { name: \"depth\", type: \"number\", value: \"N\", default: 3, summary: \"How far to follow links (1-10)\" },\n        { name: \"direction\", type: \"string\", value: \"DIR\", default: \"both\", summary: \"forward, backward or both\" },\n        { name: \"mode\", type: \"string\", value: \"MODE\", default: \"all\", summary: \"sequential, associative or all\" },\n      ],\n      examples: [\"pai zettel explore my-note\", \"pai zettel explore my-note --depth 2 --direction forward\"],\n      seeAlso: [\"pai zettel suggest\"],\n      run: (ctx) => zettelExploreCmd(ctx.args.NOTE as string, {\n        depth: ctx.opts.depth === undefined ? undefined : Number(ctx.opts.depth),\n        direction: ctx.opts.direction === undefined ? undefined : String(ctx.opts.direction),\n        mode: ctx.opts.mode === undefined ? undefined : String(ctx.opts.mode),\n      }),\n    },\n    {\n      name: \"surprise\",\n      summary: \"Find notes that are alike in meaning but far apart in links\",\n      description: \"Surprising connections: pairs of notes that say similar things yet are not linked near each other.\",\n      args: [NOTE],\n      options: [\n        vaultId,\n        limit(10, \"results\"),\n        { name: \"min-similarity\", type: \"number\", value: \"F\", default: 0.3, summary: \"Least similarity to accept (0-1)\" },\n        { name: \"min-distance\", type: \"number\", value: \"N\", default: 3, summary: \"Least link distance to accept\" },\n      ],\n      examples: [\"pai zettel surprise my-note --vault-project-id 3\", \"pai zettel surprise my-note --vault-project-id 3 --limit 5\"],\n      seeAlso: [\"pai zettel suggest\"],\n      run: (ctx) => zettelSurpriseCmd(ctx.args.NOTE as string, {\n        vaultProjectId: ctx.opts[\"vault-project-id\"] as string | number | undefined,\n        limit: ctx.opts.limit as number | undefined,\n        minSimilarity: ctx.opts[\"min-similarity\"] as number | undefined,\n        minDistance: ctx.opts[\"min-distance\"] as number | undefined,\n      }),\n    },\n    {\n      name: \"converse\",\n      summary: \"Ask the vault a question\",\n      description: \"Finds the notes relevant to a question and prints a prompt that bundles them, ready to hand to a model.\",\n      args: [{ name: \"QUESTION\", required: true, summary: \"What you want to know\" }],\n      options: [\n        vaultId,\n        { name: \"depth\", type: \"number\", value: \"N\", default: 2, summary: \"How far to expand around matching notes\" },\n        limit(15, \"notes to include\"),\n      ],\n      examples: ['pai zettel converse \"what did we decide about caching?\" --vault-project-id 3', 'pai zettel converse \"why did the build break\" --vault-project-id 3 --depth 1'],\n      seeAlso: [\"pai memory search\"],\n      run: (ctx) => zettelConverseCmd(ctx.args.QUESTION as string, {\n        vaultProjectId: ctx.opts[\"vault-project-id\"] as string | number | undefined,\n        depth: ctx.opts.depth as number | undefined,\n        limit: ctx.opts.limit as number | undefined,\n      }),\n    },\n    {\n      name: \"themes\",\n      summary: \"Spot topics emerging in recent notes\",\n      description: \"Clusters recently edited notes by similarity and names the groups.\",\n      options: [\n        vaultId,\n        { name: \"days\", type: \"number\", value: \"N\", default: 30, summary: \"Look back this many days\" },\n        { name: \"min-size\", type: \"number\", value: \"N\", default: 3, summary: \"Fewest notes in a theme\" },\n        { name: \"max-themes\", type: \"number\", value: \"N\", default: 10, summary: \"Most themes to show\" },\n        { name: \"threshold\", type: \"number\", value: \"F\", default: 0.65, summary: \"How alike notes must be to group (0-1)\" },\n      ],\n      examples: [\"pai zettel themes --vault-project-id 3\", \"pai zettel themes --vault-project-id 3 --days 7\"],\n      seeAlso: [\"pai zettel health\"],\n      run: (ctx) => zettelThemesCmd({\n        vaultProjectId: ctx.opts[\"vault-project-id\"] as string | number | undefined,\n        days: ctx.opts.days as number | undefined,\n        minSize: ctx.opts[\"min-size\"] as number | undefined,\n        maxThemes: ctx.opts[\"max-themes\"] as number | undefined,\n        threshold: ctx.opts.threshold as number | undefined,\n      }),\n    },\n    {\n      name: \"health\",\n      summary: \"Check the vault for dead links and orphans\",\n      description: \"Structural audit: dead links, orphan notes, disconnected groups and weakly connected notes.\",\n      options: [\n        { name: \"scope\", type: \"string\", value: \"SCOPE\", default: \"full\", summary: \"full, recent or project\" },\n        { name: \"project\", type: \"string\", value: \"PATH\", summary: \"Project path prefix (needs --scope project)\" },\n        { name: \"days\", type: \"number\", value: \"N\", default: 30, summary: \"Look-back window (needs --scope recent)\" },\n        { name: \"include\", type: \"string\", value: \"LIST\", summary: \"Comma list of: dead_links, orphans, disconnected, low_connectivity\" },\n      ],\n      examples: [\"pai zettel health\", \"pai zettel health --scope recent --days 7\", \"pai zettel health --include dead_links,orphans\"],\n      seeAlso: [\"pai zettel themes\"],\n      run: (ctx) => zettelHealthCmd({\n        scope: ctx.opts.scope as string | undefined,\n        project: ctx.opts.project as string | undefined,\n        days: ctx.opts.days as number | undefined,\n        include: ctx.opts.include as string | undefined,\n      }),\n    },\n    {\n      name: \"suggest\",\n      summary: \"Suggest new links for a note\",\n      description: \"Proposes notes the given note should probably link to.\",\n      args: [NOTE],\n      options: [\n        vaultId,\n        limit(5, \"suggestions\"),\n        { name: \"no-exclude-linked\", type: \"boolean\", summary: \"Also suggest notes that are already linked\" },\n      ],\n      examples: [\"pai zettel suggest my-note --vault-project-id 3\", \"pai zettel suggest my-note --vault-project-id 3 --limit 10\"],\n      seeAlso: [\"pai zettel explore\"],\n      run: (ctx) => zettelSuggestCmd(ctx.args.NOTE as string, {\n        vaultProjectId: ctx.opts[\"vault-project-id\"] as string | number | undefined,\n        limit: ctx.opts.limit as number | undefined,\n        excludeLinked: ctx.opts[\"no-exclude-linked\"] !== true,\n      }),\n    },\n  ],\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 { createConnection } from \"net\";\nimport { ok, warn, err, dim, bold, header } from \"./utils.js\";\nimport chalk from \"chalk\";\nimport { paiSocketPath } from \"../../runtime-paths.js\";\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(paiSocketPath(), () => {\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  tool_input_summary?: 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\nexport async function observationListCmd(opts: {\n  project?: string;\n  type?: string;\n  session?: string;\n  limit?: string;\n}): Promise<number> {\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.exitCode = 1;\n    return 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 0;\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  return 0;\n}\n\n// ---------------------------------------------------------------------------\n// Command: pai observation search <query>\n// ---------------------------------------------------------------------------\n\nexport async function observationSearchCmd(\n  query: string,\n  opts: { project?: string; type?: string; limit?: string }\n): Promise<number> {\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.exitCode = 1;\n    return 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 0;\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  return 0;\n}\n\n// ---------------------------------------------------------------------------\n// Command: pai observation stats\n// ---------------------------------------------------------------------------\n\nexport async function observationStatsCmd(): Promise<number> {\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.exitCode = 1;\n    return 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  return 0;\n}\n","/** `pai observation ...`: what was noticed while working (decisions, bug fixes, discoveries). */\n\nimport { OPT, type CommandDef, type OptDef } from \"../define.js\";\nimport { observationListCmd, observationSearchCmd, observationStatsCmd } from \"../lib/observation.js\";\n\nconst project: OptDef = { ...OPT.project, value: \"SLUG\", summary: \"Only this project\" };\nconst type: OptDef = { name: \"type\", type: \"string\", value: \"KIND\", summary: \"Only this kind: decision, bugfix, feature, refactor, discovery, change\" };\nconst limit: OptDef = { name: \"limit\", type: \"number\", value: \"N\", default: 20, summary: \"Maximum results\" };\n\nexport const observationCommand: CommandDef = {\n  name: \"observation\",\n  summary: \"Notes the system took while you worked\",\n  description: \"Observations are short records of decisions, fixes and discoveries captured during sessions. List, search and count them here.\",\n  examples: [\"pai observation list\", 'pai observation search \"retry\"', \"pai observation stats\"],\n  seeAlso: [\"pai memory search\", \"pai kg\"],\n  subcommands: [\n    {\n      name: \"list\",\n      summary: \"Show recent observations\",\n      description: \"Lists the newest observations, optionally narrowed by project, kind or session.\",\n      options: [project, type, { name: \"session\", type: \"string\", value: \"ID\", summary: \"Only this session id\" }, limit],\n      examples: [\"pai observation list\", \"pai observation list --type decision --limit 5\"],\n      seeAlso: [\"pai observation search\"],\n      run: async (ctx) => {\n        return await observationListCmd({\n          project: ctx.opts.project as string | undefined,\n          type: ctx.opts.type as string | undefined,\n          session: ctx.opts.session as string | undefined,\n          limit: ctx.opts.limit?.toString(),\n        });\n      },\n    },\n    {\n      name: \"search\",\n      summary: \"Search observations by title or text\",\n      description: \"Finds observations whose title or narrative contains the query.\",\n      args: [{ name: \"QUERY\", required: true, summary: \"What to look for\" }],\n      options: [project, type, limit],\n      examples: ['pai observation search \"retry\"', 'pai observation search cache --type bugfix'],\n      seeAlso: [\"pai observation list\"],\n      run: async (ctx) => {\n        return await observationSearchCmd(ctx.args.QUERY as string, {\n          project: ctx.opts.project as string | undefined,\n          type: ctx.opts.type as string | undefined,\n          limit: ctx.opts.limit?.toString(),\n        });\n      },\n    },\n    {\n      name: \"stats\",\n      summary: \"Totals by kind and by project\",\n      description: \"Shows how many observations exist, split by kind and by project.\",\n      examples: [\"pai observation stats\", \"pai observation list --limit 5\"],\n      seeAlso: [\"pai observation list\"],\n      run: async () => {\n        return await observationStatsCmd();\n      },\n    },\n  ],\n};\n","/** `pai obsidian ...`: mirror project notes into an Obsidian vault. */\n\nimport type { CommandDef, OptDef } from \"../define.js\";\nimport { obsidianSyncCmd, obsidianStatusCmd, obsidianOpenCmd } from \"../lib/obsidian.js\";\n\nconst vault: OptDef = { name: \"vault\", type: \"string\", value: \"PATH\", summary: \"Use this vault folder instead of the configured one\" };\n\nexport const obsidianCommand: CommandDef = {\n  name: \"obsidian\",\n  summary: \"Mirror project notes into an Obsidian vault\",\n  description: \"Keeps an Obsidian vault in step with your project notes so you can browse them there. Sync, check health, or open the vault.\",\n  examples: [\"pai obsidian sync\", \"pai obsidian status\", \"pai obsidian open\"],\n  seeAlso: [\"pai zettel\", \"pai memory search\"],\n  subcommands: [\n    {\n      name: \"sync\",\n      summary: \"Copy project notes into the vault and rebuild its index pages\",\n      description: \"Links each project's Notes folder into the vault and regenerates the index and topic pages.\",\n      options: [vault, { name: \"quiet\", type: \"boolean\", summary: \"Minimal output, for scheduled runs\" }],\n      examples: [\"pai obsidian sync\", \"pai obsidian sync --quiet\"],\n      seeAlso: [\"pai obsidian status\"],\n      run: async (ctx) => {\n        return await obsidianSyncCmd({\n          vault: ctx.opts.vault as string | undefined,\n          quiet: ctx.opts.quiet === true,\n        });\n      },\n    },\n    {\n      name: \"status\",\n      summary: \"Check the vault: healthy, broken, orphaned and missing links\",\n      description: \"Counts the vault's links by state so you can see whether a sync is needed.\",\n      options: [{ ...vault, summary: \"Check this vault folder instead of the configured one\" }],\n      examples: [\"pai obsidian status\", \"pai obsidian status --vault ./vault\"],\n      seeAlso: [\"pai obsidian sync\"],\n      run: async (ctx) => {\n        return await obsidianStatusCmd({\n          vault: ctx.opts.vault as string | undefined,\n        });\n      },\n    },\n    {\n      name: \"open\",\n      summary: \"Open the vault in Obsidian (macOS)\",\n      description: \"Launches Obsidian on the vault folder.\",\n      options: [{ ...vault, summary: \"Open this vault folder instead of the configured one\" }],\n      examples: [\"pai obsidian open\", \"pai obsidian open --vault ./vault\"],\n      seeAlso: [\"pai obsidian sync\"],\n      run: (ctx) => {\n        return obsidianOpenCmd({\n          vault: ctx.opts.vault as string | undefined,\n        });\n      },\n    },\n  ],\n};\n","/**\n * pai skill <sub-command>\n *\n * telemetry — Show skill-invocation telemetry (self-educating skill system).\n *\n * The `skill` namespace is reserved for the wider self-educating loop\n * (search / install / audit / trial) — see Notes/swarm/skills-self-educating.md.\n */\n\nimport { createConnection } from \"net\";\nimport { warn, err, dim, bold, header } from \"./utils.js\";\nimport chalk from \"chalk\";\nimport { paiSocketPath } from \"../../runtime-paths.js\";\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(paiSocketPath(), () => {\n      client.write(JSON.stringify({ id: 1, method, params }) + \"\\n\");\n    });\n    let data = \"\";\n    client.on(\"data\", (chunk) => {\n      data += chunk.toString();\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// Helpers\n// ---------------------------------------------------------------------------\n\nfunction fmtTs(ts: string | null | undefined): string {\n  if (!ts) return dim(\"—\");\n  try {\n    const d = new Date(ts);\n    return `${d.toISOString().slice(0, 10)} ${d.toISOString().slice(11, 16)}`;\n  } catch {\n    return dim(\"—\");\n  }\n}\n\nfunction statusColor(status: string): string {\n  switch (status) {\n    case \"active\":   return chalk.green(status);\n    case \"trial\":    return chalk.yellow(status);\n    case \"archived\": return chalk.dim(status);\n    default:         return chalk.white(status);\n  }\n}\n\nfunction trunc(s: string, maxLen: number): string {\n  if (!s) return \"\";\n  return s.length <= maxLen ? s : s.slice(0, maxLen - 1) + \"…\";\n}\n\ninterface SkillTelemetry {\n  id: number;\n  scope: string;\n  skill_name: string;\n  source: string;\n  status: string;\n  trigger_count: number;\n  accept_count: number;\n  first_triggered: string;\n  last_triggered: string;\n  context_projects: string[];\n  hash: string | null;\n  audit_status: string | null;\n  last_audited: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Command: pai skill telemetry\n// ---------------------------------------------------------------------------\n\nexport async function skillTelemetryCmd(opts: {\n  scope?: string;\n  status?: string;\n  limit?: number;\n  json?: boolean;\n}): Promise<number> {\n  const params: Record<string, unknown> = {\n    scope: opts.scope ?? \"default\",\n    limit: opts.limit ?? 50,\n  };\n  if (opts.status) params.status = opts.status;\n\n  let rows: SkillTelemetry[];\n  try {\n    rows = (await ipcCall(\"skill_telemetry_query\", params)) as SkillTelemetry[];\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    return 1;\n  }\n\n  if (opts.json) {\n    console.log(JSON.stringify(rows ?? [], null, 2));\n    return 0;\n  }\n\n  console.log();\n  console.log(header(\"  Skill Telemetry\"));\n  console.log(dim(`  scope: ${params.scope}${opts.status ? `  |  status: ${opts.status}` : \"\"}`));\n  console.log();\n\n  if (!rows || rows.length === 0) {\n    console.log(warn(\"  No skill telemetry yet.\"));\n    console.log(dim(\"  Invoke a skill (e.g. /name) and it will be recorded.\"));\n    console.log();\n    return 0;\n  }\n\n  const NAME_W = 24;\n  const SRC_W  = 10;\n  const STAT_W = 9;\n  const TRIG_W = 8;\n  const SEEN_W = 16;\n\n  console.log(\n    \"  \" +\n    bold(\"skill\".padEnd(NAME_W)) + \"  \" +\n    bold(\"source\".padEnd(SRC_W)) + \"  \" +\n    bold(\"status\".padEnd(STAT_W)) + \"  \" +\n    bold(\"triggers\".padStart(TRIG_W)) + \"  \" +\n    bold(\"last used\".padEnd(SEEN_W)) + \"  \" +\n    bold(\"projects\")\n  );\n  console.log(dim(\"  \" + \"-\".repeat(NAME_W + SRC_W + STAT_W + TRIG_W + SEEN_W + 18)));\n\n  for (const r of rows) {\n    const name   = trunc(r.skill_name ?? \"\", NAME_W).padEnd(NAME_W);\n    const src    = trunc(r.source ?? \"\", SRC_W).padEnd(SRC_W);\n    const stat   = statusColor(r.status ?? \"\");\n    const statPad = stat.padEnd(STAT_W + (stat.length - (r.status ?? \"\").length));\n    const trig   = String(r.trigger_count ?? 0).padStart(TRIG_W);\n    const seen   = fmtTs(r.last_triggered).padEnd(SEEN_W);\n    const projs  = Array.isArray(r.context_projects) ? r.context_projects.join(\", \") : \"\";\n    console.log(`  ${chalk.white(name)}  ${dim(src)}  ${statPad}  ${chalk.cyan(trig)}  ${dim(seen)}  ${dim(trunc(projs, 30))}`);\n  }\n\n  console.log();\n  console.log(dim(`  ${rows.length} skill(s)`));\n  console.log();\n  return 0;\n}\n","/** `pai skill ...`: how skills are used. */\n\nimport { OPT, type CommandDef } from \"../define.js\";\nimport { skillTelemetryCmd } from \"../lib/skill.js\";\n\nexport const skillCommand: CommandDef = {\n  name: \"skill\",\n  summary: \"How your skills are used\",\n  description: \"Skills are reusable instructions a session can load. `pai skill telemetry` shows which ones were triggered and when.\",\n  examples: [\"pai skill telemetry\", \"pai skill telemetry --status active --json\"],\n  seeAlso: [\"pai audit skills\"],\n  subcommands: [\n    {\n      name: \"telemetry\",\n      summary: \"Which skills were used, when, and where\",\n      description: \"Lists skills with their trigger counts, last use and projects. Output with --json is the raw data.\",\n      options: [\n        { name: \"scope\", type: \"string\", value: \"NAME\", default: \"default\", summary: \"Governance scope to read\" },\n        { name: \"status\", type: \"string\", value: \"STATE\", summary: \"Only skills in this state: trial, active or archived\" },\n        { name: \"limit\", type: \"number\", value: \"N\", default: 50, summary: \"Maximum rows\" },\n        { ...OPT.json, summary: \"Output raw JSON\" },\n      ],\n      examples: [\"pai skill telemetry\", \"pai skill telemetry --status trial --limit 10\", \"pai skill telemetry --json\"],\n      seeAlso: [\"pai audit skills\"],\n      run: (ctx) => skillTelemetryCmd({\n        scope: ctx.opts.scope as string | undefined,\n        status: ctx.opts.status as string | undefined,\n        limit: ctx.opts.limit as number | undefined,\n        json: ctx.opts.json === true,\n      }),\n    },\n  ],\n};\n","/**\n * tokens.ts — the one tokenizer every audit section counts through.\n *\n * cl100k_base via js-tiktoken (pure JS, no native build). \"all\" + [] for the\n * allowed/disallowed-special-token arguments matches Python tiktoken's\n * `disallowed_special=()`: audited text (hook stdout, session transcripts)\n * routinely contains literal \"<|...|>\"-shaped substrings that are not actual\n * special tokens, and the strict default throws on sight of them.\n */\n\nimport { getEncoding, type Tiktoken } from \"js-tiktoken\";\n\nexport const TOKEN_ENCODING = \"cl100k_base\";\n\nlet cached: Tiktoken | null = null;\n\nfunction encoder(): Tiktoken {\n  if (!cached) cached = getEncoding(TOKEN_ENCODING);\n  return cached;\n}\n\nexport function countTokens(text: string): number {\n  if (!text) return 0;\n  return encoder().encode(text, \"all\", []).length;\n}\n","/**\n * `pai audit tokens files` — token cost of everything injected as static\n * memory: global/project CLAUDE.md files (and their @-imports), the CORE\n * skill, the whisper rules file, and this project's auto-memory index.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { countTokens, TOKEN_ENCODING } from \"./tokens.js\";\nimport { whisperRulesPath } from \"../config/pai-files.js\";\nimport { encodeDir } from \"../cli/lib/utils.js\";\n\nexport interface FileTokenReading {\n  path: string;\n  tokens: number;\n  missing: boolean;\n}\n\nexport interface FilesReport {\n  encoding: string;\n  readings: FileTokenReading[];\n  total: number;\n}\n\nconst SINGLE_FILE_LIMIT = 5000;\nconst TOTAL_LIMIT = 10000;\n\n/** Resolve one @-import token found in a CLAUDE.md line to an absolute path. */\nfunction resolveImport(token: string, fromDir: string): string {\n  if (token.startsWith(\"~/\")) return join(homedir(), token.slice(2));\n  if (token.startsWith(\"/\")) return token;\n  return resolve(fromDir, token);\n}\n\n/**\n * Find every `@path` import in a CLAUDE.md's contents: a line starting with\n * `@`, or containing `@./` or `@~/` anywhere (Claude Code's memory-import\n * syntax allows either position).\n */\nfunction findImports(contents: string, fileDir: string): string[] {\n  const found: string[] = [];\n  const tokenPattern = /@(~\\/[^\\s]+|\\.\\/[^\\s]+|\\/[^\\s]+)/g;\n  for (const line of contents.split(\"\\n\")) {\n    if (!line.startsWith(\"@\") && !line.includes(\" @./\") && !line.includes(\"@~/\") && !line.includes(\" @/\")) continue;\n    let match: RegExpExecArray | null;\n    tokenPattern.lastIndex = 0;\n    while ((match = tokenPattern.exec(line))) {\n      found.push(resolveImport(match[1], fileDir));\n    }\n  }\n  return found;\n}\n\n/** Every parent-directory CLAUDE.md from `startDir` up to (and including) `/`. */\nfunction parentClaudeMdFiles(startDir: string): string[] {\n  const found: string[] = [];\n  let dir = startDir;\n  for (;;) {\n    const candidate = join(dir, \"CLAUDE.md\");\n    if (existsSync(candidate)) found.push(candidate);\n    const parent = dirname(dir);\n    if (parent === dir) break;\n    dir = parent;\n  }\n  return found;\n}\n\n/** The default memory set the task describes, plus any @-imports they pull in. */\nexport function defaultFileSet(cwd: string): string[] {\n  const seeds = [\n    join(homedir(), \".claude\", \"CLAUDE.md\"),\n    join(cwd, \"CLAUDE.md\"),\n    join(cwd, \".claude\", \"CLAUDE.md\"),\n    ...parentClaudeMdFiles(dirname(cwd)),\n    join(homedir(), \".claude\", \"skills\", \"CORE\", \"SKILL.md\"),\n    whisperRulesPath(),\n    join(homedir(), \".claude\", \"projects\", encodeDir(cwd), \"memory\", \"MEMORY.md\"),\n  ];\n\n  const visited = new Set<string>();\n  const ordered: string[] = [];\n  const queue = [...seeds];\n  while (queue.length) {\n    const path = queue.shift()!;\n    if (visited.has(path)) continue;\n    visited.add(path);\n    ordered.push(path);\n    if (!existsSync(path)) continue;\n    const contents = readFileSync(path, \"utf8\");\n    for (const imported of findImports(contents, dirname(path))) {\n      if (!visited.has(imported)) queue.push(imported);\n    }\n  }\n  return ordered;\n}\n\nexport function readFileTokens(paths: string[]): FileTokenReading[] {\n  return paths.map((path) => {\n    if (!existsSync(path)) return { path, tokens: 0, missing: true };\n    const text = readFileSync(path, \"utf8\");\n    return { path, tokens: countTokens(text), missing: false };\n  });\n}\n\nexport function auditFiles(paths: string[]): FilesReport {\n  const readings = readFileTokens(paths);\n  const total = readings.reduce((sum, r) => sum + r.tokens, 0);\n  return { encoding: TOKEN_ENCODING, readings, total };\n}\n\nexport { SINGLE_FILE_LIMIT, TOTAL_LIMIT };\n\n// Referenced by the combined report's severity rules.\nexport function filesFindings(report: FilesReport): { path: string; tokens: number }[] {\n  return report.readings.filter((r) => r.tokens > SINGLE_FILE_LIMIT).map((r) => ({ path: r.path, tokens: r.tokens }));\n}\n","/**\n * `pai audit tokens hooks` — token cost of the hooks that fire before the\n * model ever sees a token: SessionStart and UserPromptSubmit run on every\n * new session / every prompt respectively, and whatever they print to\n * stdout is injected into context.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { countTokens, TOKEN_ENCODING } from \"./tokens.js\";\n\nconst HOOK_TIMEOUT_MS = 20_000;\n\ninterface HookEntry {\n  type?: string;\n  command?: string;\n  timeout?: number;\n}\n\ninterface HookGroup {\n  matcher?: string;\n  hooks?: HookEntry[];\n}\n\ninterface SettingsJson {\n  env?: Record<string, string>;\n  hooks?: Record<string, HookGroup[]>;\n}\n\nexport interface HookReading {\n  event: string;\n  matcher?: string;\n  command: string;\n  tokens: number;\n  error?: string;\n}\n\nexport interface HooksReport {\n  encoding: string;\n  settingsFiles: string[];\n  readings: HookReading[];\n  totalsByEvent: Record<string, number>;\n  preToolUseHooks: { matcher?: string; command: string }[];\n  preToolUseRewritesBash: boolean;\n}\n\nfunction settingsPaths(cwd: string): string[] {\n  return [join(homedir(), \".claude\", \"settings.json\"), join(cwd, \".claude\", \"settings.json\")].filter(existsSync);\n}\n\nfunction readSettings(path: string): SettingsJson {\n  try {\n    return JSON.parse(readFileSync(path, \"utf8\")) as SettingsJson;\n  } catch {\n    return {};\n  }\n}\n\n/** Expand ${PAI_DIR} and $HOME/${HOME} the way settings.json's own hooks do. */\nfunction expandCommand(command: string, env: Record<string, string>): string {\n  const paiDir = env.PAI_DIR ?? process.env.PAI_DIR ?? join(homedir(), \".claude\");\n  return command\n    .replace(/\\$\\{PAI_DIR\\}/g, paiDir)\n    .replace(/\\$\\{HOME\\}/g, homedir())\n    .replace(/\\$HOME\\b/g, homedir());\n}\n\nfunction syntheticStdin(cwd: string, eventName: string): string {\n  const sessionId = randomUUID();\n  return JSON.stringify({\n    session_id: sessionId,\n    cwd,\n    hook_event_name: eventName,\n    prompt: \"audit\",\n    transcript_path: join(homedir(), \".claude\", \"projects\", \"audit\", `${sessionId}.jsonl`),\n  });\n}\n\nfunction runHook(command: string, cwd: string, eventName: string): { stdout: string; error?: string } {\n  const result = spawnSync(command, {\n    shell: true,\n    cwd,\n    input: syntheticStdin(cwd, eventName),\n    encoding: \"utf8\",\n    timeout: HOOK_TIMEOUT_MS,\n  });\n  if (result.error) return { stdout: \"\", error: result.error.message };\n  if (result.signal) return { stdout: result.stdout ?? \"\", error: `killed by ${result.signal} (timeout?)` };\n  return { stdout: result.stdout ?? \"\" };\n}\n\nconst AUDITED_EVENTS = [\"SessionStart\", \"UserPromptSubmit\"];\n\n/** Seam for tests: a real shell under a 20s wall-clock kill is load-dependent. */\nexport type HookRunner = typeof runHook;\n\nexport function auditHooks(cwd: string, run: HookRunner = runHook): HooksReport {\n  const settingsFiles = settingsPaths(cwd);\n  const readings: HookReading[] = [];\n  const totalsByEvent: Record<string, number> = {};\n  const preToolUseHooks: { matcher?: string; command: string }[] = [];\n  let preToolUseRewritesBash = false;\n\n  for (const settingsPath of settingsFiles) {\n    const settings = readSettings(settingsPath);\n    const env = settings.env ?? {};\n\n    for (const eventName of AUDITED_EVENTS) {\n      const groups = settings.hooks?.[eventName] ?? [];\n      totalsByEvent[eventName] = totalsByEvent[eventName] ?? 0;\n      for (const group of groups) {\n        for (const entry of group.hooks ?? []) {\n          if (!entry.command) continue;\n          const command = expandCommand(entry.command, env);\n          const { stdout, error } = run(command, cwd, eventName);\n          const tokens = countTokens(stdout);\n          totalsByEvent[eventName] += tokens;\n          readings.push({ event: eventName, matcher: group.matcher, command, tokens, error });\n        }\n      }\n    }\n\n    for (const group of settings.hooks?.PreToolUse ?? []) {\n      for (const entry of group.hooks ?? []) {\n        if (!entry.command) continue;\n        preToolUseHooks.push({ matcher: group.matcher, command: expandCommand(entry.command, env) });\n        if (group.matcher === \"Bash\") preToolUseRewritesBash = true;\n      }\n    }\n  }\n\n  return { encoding: TOKEN_ENCODING, settingsFiles, readings, totalsByEvent, preToolUseHooks, preToolUseRewritesBash };\n}\n","/**\n * first-turn.ts — where a turn's context actually comes from, attributed\n * line by line from the transcript.\n *\n * The API reports one number per assistant turn (cache_read + cache_creation\n * + input). The transcript holds the pieces Claude Code injected since the\n * previous turn: SessionStart / UserPromptSubmit hook output, the skill\n * listing, the deferred-tool name list, MCP server instructions, the human\n * prompt, any isMeta expansion (a skill body, a pasted slash-command result),\n * tool results, and the previous turn's own visible output (re-sent as\n * context on every later turn). Each of those is counted in cl100k here;\n * what is left over is the system prompt, the built-in tool schemas, agents\n * and memory files, which never appear in the transcript.\n *\n * Only the live lineage counts. Lines chain by parentUuid, and a prompt the\n * user abandoned before a reply (Escape, re-submit) leaves a dead branch\n * whose lines were never sent — run 9 measured a session whose first\n * /Name was abandoned and read 24k where the true baseline is 30k, because\n * the 3.5k-token skill listing sat on the dead branch.\n *\n * `type:\"system\"` lines with subtype local_command hold a slash command's\n * raw stdout, and the model receives it verbatim in addition to the isMeta\n * markdown copy — run 11 measured both present on the same turn, so both are\n * counted.\n */\n\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { countTokens } from \"./tokens.js\";\n\nexport type FirstTurnKind =\n  | \"hook:SessionStart\"\n  | \"hook:UserPromptSubmit\"\n  | \"hook:other\"\n  | \"skill_listing\"\n  | \"deferred_tools\"\n  | \"mcp_instructions\"\n  | \"attachment:other\"\n  | \"prompt\"\n  | \"prompt:meta\"\n  | \"prompt:local_command\"\n  | \"system:display-only\"\n  | \"tool_result\"\n  | \"assistant:prev\";\n\nexport interface FirstTurnItem {\n  kind: FirstTurnKind;\n  tokens: number;\n  /** Hook name, attachment type, or the first 40 chars of a prompt. */\n  label: string;\n}\n\nexport interface FirstTurnBreakdown {\n  path: string;\n  /** cache_read + cache_creation + input on the first assistant turn; null if none. */\n  apiContext: number | null;\n  items: FirstTurnItem[];\n  /** Sum of every live-lineage item except system:display-only. */\n  transcriptTokens: number;\n  /** Per-kind sums over the live lineage (display-only excluded). */\n  byKind: Partial<Record<FirstTurnKind, number>>;\n  /** apiContext - transcriptTokens: system prompt, tool schemas, agents, memory (+ tokenizer delta). */\n  remainder: number | null;\n  /** Tokens on abandoned branches before the first turn — persisted but never sent. */\n  deadBranchTokens: number;\n  deadBranchLines: number;\n}\n\n/** Per-turn attribution of one API call's context growth (turn 2+; turn 1 reduces to the FirstTurnBreakdown shape). */\nexport interface TurnBreakdown {\n  path: string;\n  turn: number;\n  /** cache_read + cache_creation + input on this turn's API call. */\n  apiContext: number | null;\n  /** Same, for the previous API call; 0 for turn 1. */\n  prevApiContext: number;\n  /** apiContext - prevApiContext; null if this turn has no usage. */\n  billedDelta: number | null;\n  /** Previous call's usage.output_tokens (includes thinking); 0 for turn 1. */\n  prevOutputTokens: number;\n  items: FirstTurnItem[];\n  /** Sum of every item except system:display-only. */\n  transcriptTokens: number;\n  byKind: Partial<Record<FirstTurnKind, number>>;\n  /** cl100k count of the previous call's visible text + tool_use input (the assistant:prev item); not part of transcriptTokens. */\n  prevVisibleTokens: number;\n  /**\n   * billedDelta - transcriptTokens - prevOutputTokens. transcriptTokens\n   * excludes assistant:prev (the previous call's visible output is only\n   * present via prevOutputTokens, which also covers its redacted thinking),\n   * so this is a single-count residual: positive means cl100k undercounts\n   * the billing tokenizer for this turn's content; negative means the\n   * previous call's thinking was not re-sent (thinking is only re-sent\n   * inside a tool-use loop, not after a turn-ending text reply).\n   */\n  residual: number | null;\n}\n\ninterface ContentBlock {\n  type?: string;\n  text?: string;\n  input?: unknown;\n}\n\ninterface Line {\n  type?: string;\n  subtype?: string;\n  uuid?: string;\n  parentUuid?: string | null;\n  isMeta?: boolean;\n  content?: string;\n  attachment?: {\n    type?: string;\n    hookName?: string;\n    stdout?: string;\n    content?: string;\n    addedNames?: string[];\n    instructions?: unknown;\n    addedInstructions?: unknown;\n  };\n  message?: {\n    id?: string;\n    content?: unknown;\n    usage?: {\n      cache_read_input_tokens?: number;\n      cache_creation_input_tokens?: number;\n      input_tokens?: number;\n      output_tokens?: number;\n    };\n  };\n}\n\nfunction parseLines(path: string): Line[] {\n  const lines: Line[] = [];\n  for (const raw of readFileSync(path, \"utf8\").split(\"\\n\")) {\n    if (!raw.trim()) continue;\n    try {\n      lines.push(JSON.parse(raw) as Line);\n    } catch {\n      /* skip */\n    }\n  }\n  return lines;\n}\n\nfunction classify(line: Line): FirstTurnItem | null {\n  if (line.type === \"attachment\") {\n    const a = line.attachment ?? {};\n    switch (a.type) {\n      case \"hook_success\": {\n        const name = a.hookName ?? \"\";\n        const kind: FirstTurnKind = name.startsWith(\"SessionStart\")\n          ? \"hook:SessionStart\"\n          : name.startsWith(\"UserPromptSubmit\")\n            ? \"hook:UserPromptSubmit\"\n            : \"hook:other\";\n        return { kind, tokens: countTokens(a.stdout ?? a.content ?? \"\"), label: name };\n      }\n      case \"skill_listing\":\n        return { kind: \"skill_listing\", tokens: countTokens(a.content ?? \"\"), label: \"skill_listing\" };\n      case \"deferred_tools_delta\":\n        return { kind: \"deferred_tools\", tokens: countTokens((a.addedNames ?? []).join(\"\\n\")), label: \"deferred_tools_delta\" };\n      case \"mcp_instructions_delta\":\n        return {\n          kind: \"mcp_instructions\",\n          tokens: countTokens(JSON.stringify(a.instructions ?? a.addedInstructions ?? a)),\n          label: \"mcp_instructions_delta\",\n        };\n      default:\n        return { kind: \"attachment:other\", tokens: countTokens(JSON.stringify(a)), label: a.type ?? \"attachment\" };\n    }\n  }\n  if (line.type === \"user\") {\n    const c = line.message?.content;\n    const isToolResult = Array.isArray(c) && (c as ContentBlock[]).some((b) => b?.type === \"tool_result\");\n    const text = typeof c === \"string\" ? c : JSON.stringify(c ?? \"\");\n    return {\n      kind: isToolResult ? \"tool_result\" : line.isMeta ? \"prompt:meta\" : \"prompt\",\n      tokens: countTokens(text),\n      label: text.slice(0, 40).replace(/\\s+/g, \" \"),\n    };\n  }\n  if (line.type === \"system\") {\n    const kind: FirstTurnKind = line.subtype === \"local_command\" ? \"prompt:local_command\" : \"system:display-only\";\n    return { kind, tokens: countTokens(line.content ?? \"\"), label: line.subtype ?? \"system\" };\n  }\n  return null;\n}\n\nfunction apiContextOf(usage: NonNullable<Line[\"message\"]>[\"usage\"]): number | null {\n  if (!usage) return null;\n  return (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0) + (usage.input_tokens ?? 0);\n}\n\n/**\n * Attribute API call `turn`'s context growth to the transcript lines\n * injected since the previous call. Turn numbering matches\n * session-usage.ts's `turnIndex`: several assistant lines sharing one\n * message.id (one line per streamed content block) are one call. Returns\n * null if the transcript has fewer than `turn` calls.\n */\nexport function turnBreakdown(path: string, turn: number): TurnBreakdown | null {\n  if (!existsSync(path)) return null;\n  const lines = parseLines(path);\n  const byUuid = new Map<string, Line>();\n  for (const l of lines) if (l.uuid) byUuid.set(l.uuid, l);\n\n  const seen = new Set<string>();\n  let n = 0;\n  let target: Line | null = null;\n  for (const l of lines) {\n    if (l.type === \"assistant\" && l.message?.usage) {\n      const id = l.message.id ?? l.uuid ?? \"\";\n      if (!seen.has(id)) {\n        seen.add(id);\n        n++;\n        if (n === turn) {\n          target = l;\n          break;\n        }\n      }\n    }\n  }\n  if (!target) return null;\n\n  // Walk parentUuid back, collecting the live lineage since the previous\n  // call, until an assistant-with-usage line (a block of the previous call)\n  // is hit.\n  const rawChain: Line[] = [];\n  let prevLine: Line | null = null;\n  let cur = target.parentUuid ? byUuid.get(target.parentUuid) : undefined;\n  while (cur) {\n    if (cur.type === \"assistant\" && cur.message?.usage) {\n      prevLine = cur;\n      break;\n    }\n    rawChain.unshift(cur);\n    cur = cur.parentUuid ? byUuid.get(cur.parentUuid) : undefined;\n  }\n\n  const items: FirstTurnItem[] = [];\n  for (const l of rawChain) {\n    const item = classify(l);\n    if (item) items.push(item);\n  }\n\n  let prevApiContext = 0;\n  let prevOutputTokens = 0;\n  if (prevLine) {\n    prevApiContext = apiContextOf(prevLine.message?.usage) ?? 0;\n    prevOutputTokens = prevLine.message?.usage?.output_tokens ?? 0;\n\n    // Gather every content-block line of the previous call (streaming can\n    // split one call's thinking/text/tool_use blocks across several lines\n    // chained by parentUuid, all sharing one message.id).\n    const prevId = prevLine.message?.id ?? prevLine.uuid ?? \"\";\n    const prevBlocks: Line[] = [prevLine];\n    let cur2 = prevLine.parentUuid ? byUuid.get(prevLine.parentUuid) : undefined;\n    while (cur2 && cur2.type === \"assistant\" && (cur2.message?.id ?? cur2.uuid ?? \"\") === prevId) {\n      prevBlocks.unshift(cur2);\n      cur2 = cur2.parentUuid ? byUuid.get(cur2.parentUuid) : undefined;\n    }\n\n    let prevText = \"\";\n    let thinkingCount = 0;\n    for (const b of prevBlocks) {\n      const content = b.message?.content;\n      if (!Array.isArray(content)) continue;\n      for (const block of content as ContentBlock[]) {\n        if (block?.type === \"text\") prevText += block.text ?? \"\";\n        else if (block?.type === \"tool_use\") prevText += JSON.stringify(block.input ?? {});\n        else if (block?.type === \"thinking\") thinkingCount++;\n      }\n    }\n    const label = thinkingCount > 0 ? `previous call output (${thinkingCount} thinking block(s) redacted)` : \"previous call output\";\n    items.push({ kind: \"assistant:prev\", tokens: countTokens(prevText), label });\n  }\n\n  const byKind: Partial<Record<FirstTurnKind, number>> = {};\n  let transcriptTokens = 0;\n  let prevVisibleTokens = 0;\n  for (const item of items) {\n    if (item.kind === \"system:display-only\") continue;\n    if (item.kind === \"assistant:prev\") {\n      prevVisibleTokens = item.tokens;\n      continue;\n    }\n    transcriptTokens += item.tokens;\n    byKind[item.kind] = (byKind[item.kind] ?? 0) + item.tokens;\n  }\n\n  const apiContext = apiContextOf(target.message?.usage);\n  const billedDelta = apiContext === null ? null : apiContext - prevApiContext;\n  const residual = billedDelta === null ? null : billedDelta - transcriptTokens - prevOutputTokens;\n\n  return {\n    path,\n    turn,\n    apiContext,\n    prevApiContext,\n    billedDelta,\n    prevOutputTokens,\n    items,\n    transcriptTokens,\n    byKind,\n    prevVisibleTokens,\n    residual,\n  };\n}\n\n/** Attribute the first assistant turn's context to the transcript lines on its live lineage. */\nexport function firstTurnBreakdown(path: string): FirstTurnBreakdown {\n  const empty: FirstTurnBreakdown = {\n    path,\n    apiContext: null,\n    items: [],\n    transcriptTokens: 0,\n    byKind: {},\n    remainder: null,\n    deadBranchTokens: 0,\n    deadBranchLines: 0,\n  };\n  if (!existsSync(path)) return empty;\n\n  const tb = turnBreakdown(path, 1);\n  if (!tb) return empty;\n\n  const lines = parseLines(path);\n  const byUuid = new Map<string, Line>();\n  let firstIndex = -1;\n  for (let i = 0; i < lines.length; i++) {\n    const l = lines[i];\n    if (l.uuid) byUuid.set(l.uuid, l);\n    if (l.type === \"assistant\" && l.message?.usage && firstIndex < 0) firstIndex = i;\n  }\n  if (firstIndex < 0) return empty;\n  const first = lines[firstIndex];\n\n  const live = new Set<Line>();\n  let cur = first.parentUuid ? byUuid.get(first.parentUuid) : undefined;\n  while (cur && !live.has(cur)) {\n    live.add(cur);\n    cur = cur.parentUuid ? byUuid.get(cur.parentUuid) : undefined;\n  }\n\n  let deadBranchTokens = 0;\n  let deadBranchLines = 0;\n  for (let i = 0; i < firstIndex; i++) {\n    const l = lines[i];\n    if (!l.uuid || live.has(l)) continue;\n    const item = classify(l);\n    if (!item || item.kind === \"system:display-only\") continue;\n    deadBranchLines++;\n    deadBranchTokens += item.tokens;\n  }\n\n  return {\n    path,\n    apiContext: tb.apiContext,\n    items: tb.items,\n    transcriptTokens: tb.transcriptTokens,\n    byKind: tb.byKind,\n    remainder: tb.residual,\n    deadBranchTokens,\n    deadBranchLines,\n  };\n}\n\n/** One-line evidence string for the combined report's first-turn finding. */\nexport function firstTurnEvidence(b: FirstTurnBreakdown): string {\n  const parts: string[] = [];\n  const order: FirstTurnKind[] = [\n    \"skill_listing\",\n    \"mcp_instructions\",\n    \"deferred_tools\",\n    \"hook:SessionStart\",\n    \"hook:UserPromptSubmit\",\n    \"hook:other\",\n    \"prompt:meta\",\n    \"prompt:local_command\",\n    \"prompt\",\n    \"attachment:other\",\n  ];\n  for (const k of order) {\n    const v = b.byKind[k];\n    if (v) parts.push(`${k} ${v}`);\n  }\n  const dead = b.deadBranchTokens > 0 ? `; dead-branch ${b.deadBranchTokens} not sent` : \"\";\n  return `transcript-side ${b.transcriptTokens} (${parts.join(\", \")}), remainder ${b.remainder ?? \"n/a\"}${dead}`;\n}\n","/**\n * context-trigger.ts — the window size and compaction trigger a SPECIFIC\n * session transcript actually runs against, instead of a constant assumed\n * for every session.\n *\n * Run 4 of the token-waste audit (audits/token-waste/2026-09-20-run4.md)\n * rated a 1M-window session against a hardcoded 200k threshold, read the\n * resulting RED \"context growth\" finding at face value, and lowered\n * CLAUDE_AUTOCOMPACT_PCT_OVERRIDE to make the 1M session compact near 196k —\n * the opposite of what the 1M window exists for. See\n * audits/token-waste/reviewprompt.md, \"Rules the audit may not break\".\n *\n * Reuses, rather than re-implements, the same derivation the compaction-\n * handover hook uses (src/hooks/ts/lib/context-fill.ts) so the audit and the\n * hook can never disagree about what \"the trigger\" for a session is.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { basename, dirname } from \"node:path\";\nimport { DEFAULT_CONTEXT_WINDOW, contextWindowFromModelId } from \"../utils/model-window.js\";\nimport {\n  contextFillFromTranscript,\n  contextFillThresholds,\n  readStatuslineFill,\n  statuslineStateFilePath,\n  type ContextFillReading,\n  type TriggerSource,\n} from \"../hooks/ts/lib/context-fill.js\";\n\nconst TAIL_LINES = 40;\n\n/**\n * Where the session's context WINDOW SIZE came from, in the order they are\n * tried:\n *\n *   1. \"statusline-state\": the statusline's own persisted reading for this\n *      session id, fresh (written within the last 5 minutes) — see\n *      readStatuslineFill.\n *   2. \"statusline-state-stale\": the same file, older than 5 minutes —\n *      accepted for an audit run well after the session ended, because a\n *      session's window does not change after launch; a stale window number\n *      is not the same defect a stale FILL number would be.\n *   3. \"transcript-field\": a `context_window` field on a recent transcript\n *      line (checked before the model id, since it would be the platform's\n *      own number — no transcript on this project carries it as of\n *      2026-09-20, but a future platform version might).\n *   4. \"model-id\": the last assistant model id in the transcript carries a\n *      bracketed window variant (\"claude-fable-5-1[1m]\" -> 1,000,000).\n *   5. \"compaction-history\": this transcript's OWN compact_boundary events\n *      recorded a preTokens reading above the 200k default — proof the\n *      window is at least that large, rounded up to the nearest whole\n *      million (in practice 1,000,000). Catches exactly the fault this\n *      module exists to fix: a live 1M-window session whose transcript\n *      stores the bare model id, with no fresh or stale statusline file to\n *      fall back on.\n *   6. \"default\": DEFAULT_CONTEXT_WINDOW (200,000) — nothing above applied.\n */\nexport type WindowSource =\n  | \"statusline-state\"\n  | \"statusline-state-stale\"\n  | \"transcript-field\"\n  | \"model-id\"\n  | \"compaction-history\"\n  | \"default\";\n\n/** \"override\": `--ctx-threshold` was passed explicitly, bypassing derivation\n *  entirely — added to context-fill.ts's own TriggerSource values, which\n *  describe only the measured-vs-configured choice inside derivation. */\nexport type SessionTriggerSource = TriggerSource | \"override\";\n\nexport interface DerivedTrigger {\n  window: number;\n  windowSource: WindowSource;\n  trigger: number;\n  autocompactPct: number;\n  triggerSource: TriggerSource;\n}\n\nfunction readTranscript(transcriptPath: string): string | null {\n  try {\n    return readFileSync(transcriptPath, \"utf-8\");\n  } catch {\n    return null;\n  }\n}\n\n/**\n * A `context_window` field on a transcript line, when the platform ever\n * writes one — see WindowSource, step 3.\n */\nfunction contextWindowFromTranscriptField(transcriptPath: string): number | null {\n  const raw = readTranscript(transcriptPath);\n  if (raw === null) return null;\n  const lines = raw.trim().split(\"\\n\").filter((l) => l.trim());\n  const tail = lines.slice(-TAIL_LINES);\n  for (let i = tail.length - 1; i >= 0; i--) {\n    let entry: { context_window?: unknown; message?: { context_window?: unknown } };\n    try {\n      entry = JSON.parse(tail[i]);\n    } catch {\n      continue;\n    }\n    const value = entry.context_window ?? entry.message?.context_window;\n    if (typeof value === \"number\" && Number.isFinite(value) && value > 0) return value;\n  }\n  return null;\n}\n\n/** The last assistant `message.model` id in the transcript, scanned from the\n *  tail — see WindowSource, step 4. */\nfunction lastAssistantModelId(transcriptPath: string): string | null {\n  const raw = readTranscript(transcriptPath);\n  if (raw === null) return null;\n  const lines = raw.trim().split(\"\\n\").filter((l) => l.trim());\n  const tail = lines.slice(-TAIL_LINES);\n  for (let i = tail.length - 1; i >= 0; i--) {\n    let entry: { type?: string; message?: { model?: unknown } };\n    try {\n      entry = JSON.parse(tail[i]);\n    } catch {\n      continue;\n    }\n    if (entry?.type !== \"assistant\") continue;\n    const model = entry.message?.model;\n    if (typeof model === \"string\" && model !== \"\") return model;\n  }\n  return null;\n}\n\n/**\n * The largest `compactMetadata.preTokens` this transcript's own\n * compact_boundary events recorded, rounded up to the nearest whole million\n * — see WindowSource, step 5. Reads the WHOLE transcript (not just the tail):\n * a compaction event can sit anywhere in a long session's history, unlike\n * the model id / context_window checks above, which only ever need the most\n * recent turn.\n */\nfunction windowFromCompactionHistory(transcriptPath: string): number | null {\n  const raw = readTranscript(transcriptPath);\n  if (raw === null) return null;\n  let maxPreTokens = 0;\n  for (const line of raw.split(\"\\n\")) {\n    if (!line.trim()) continue;\n    let entry: { type?: string; subtype?: string; compactMetadata?: { preTokens?: unknown } };\n    try {\n      entry = JSON.parse(line);\n    } catch {\n      continue;\n    }\n    if (entry.type !== \"system\" || entry.subtype !== \"compact_boundary\") continue;\n    const preTokens = entry.compactMetadata?.preTokens;\n    if (typeof preTokens === \"number\" && Number.isFinite(preTokens) && preTokens > maxPreTokens) {\n      maxPreTokens = preTokens;\n    }\n  }\n  if (maxPreTokens <= DEFAULT_CONTEXT_WINDOW) return null;\n  return Math.ceil(maxPreTokens / 1_000_000) * 1_000_000;\n}\n\n/**\n * The statusline state file for a session id, read WITHOUT the 5-minute\n * staleness gate readStatuslineFill applies — WindowSource step 2. Reuses\n * readStatuslineFill itself (rather than re-parsing the file) by handing it\n * back the file's own timestamp as \"now\", so the freshness check it performs\n * internally always computes a zero age; every other validation (shape,\n * numeric fields) stays exactly as readStatuslineFill enforces it.\n */\nfunction readStatuslineFillIgnoringStaleness(sessionId: string): ContextFillReading | null {\n  const path = statuslineStateFilePath(sessionId);\n  const raw = readTranscript(path);\n  if (raw === null) return null;\n  let parsed: { timestamp?: unknown };\n  try {\n    parsed = JSON.parse(raw);\n  } catch {\n    return null;\n  }\n  if (typeof parsed.timestamp !== \"number\") return null;\n  return readStatuslineFill(sessionId, parsed.timestamp);\n}\n\n/**\n * The transcript's own project directory, as Claude Code encodes it under\n * `~/.claude/projects/<encoded-cwd>/` — recovered from the transcript's PATH\n * rather than accepted as a caller-supplied cwd, so the measured-trigger\n * history scan is always scoped to the transcript's OWN project, never the\n * auditing process's process.cwd() (a real fault: an audit of another\n * project's transcript logged \"measured trigger for <this process's cwd>\").\n *\n * An already-encoded directory NAME contains no `/`, whitespace or `.` — the\n * only characters encodeProjectPath (context-fill.ts) folds into `-` — so\n * re-encoding it is a no-op, and handing this name back as the `cwd` argument\n * to contextFillThresholds/measureCompactionTrigger reproduces the exact\n * directory via their own `join(projectsDir, encodeProjectPath(cwd))`, with\n * no changes needed there. A transcript inside Claude Code's `sessions/`\n * archive sits one level deeper than the live transcript, so its directory\n * name is one level further up.\n */\nfunction projectDirNameFromTranscript(transcriptPath: string): string {\n  const dir = dirname(transcriptPath);\n  return basename(dir) === \"sessions\" ? basename(dirname(dir)) : basename(dir);\n}\n\n/**\n * window: resolved via WindowSource's steps in order — never a window\n * assumed regardless of the session.\n *\n * trigger: contextFillThresholds's effectiveTriggerTokens for that window —\n * this transcript's own project's measured compact_boundary history when it\n * has any (the better estimate), else resolveAutocompactPct(env) applied to\n * the window.\n */\nexport function deriveSessionTrigger(\n  transcriptPath: string,\n  env: NodeJS.ProcessEnv = process.env,\n  now: number = Date.now()\n): DerivedTrigger {\n  const sessionId = basename(transcriptPath).replace(/\\.jsonl$/, \"\");\n\n  let window: number;\n  let windowSource: WindowSource;\n\n  const fresh = readStatuslineFill(sessionId, now);\n  const stale = fresh ? null : readStatuslineFillIgnoringStaleness(sessionId);\n  const fieldWindow = fresh || stale ? null : contextWindowFromTranscriptField(transcriptPath);\n  const modelWindow =\n    fresh || stale || fieldWindow !== null\n      ? null\n      : contextWindowFromModelId(lastAssistantModelId(transcriptPath));\n  const compactionWindow =\n    fresh || stale || fieldWindow !== null || modelWindow !== null\n      ? null\n      : windowFromCompactionHistory(transcriptPath);\n\n  if (fresh) {\n    window = fresh.windowSize;\n    windowSource = \"statusline-state\";\n  } else if (stale) {\n    window = stale.windowSize;\n    windowSource = \"statusline-state-stale\";\n  } else if (fieldWindow !== null) {\n    window = fieldWindow;\n    windowSource = \"transcript-field\";\n  } else if (modelWindow !== null) {\n    window = modelWindow;\n    windowSource = \"model-id\";\n  } else if (compactionWindow !== null) {\n    window = compactionWindow;\n    windowSource = \"compaction-history\";\n  } else {\n    window = DEFAULT_CONTEXT_WINDOW;\n    windowSource = \"default\";\n  }\n\n  const reading = contextFillFromTranscript(transcriptPath, window);\n  const thresholds = contextFillThresholds(reading, env, { cwd: projectDirNameFromTranscript(transcriptPath) });\n\n  return {\n    window,\n    windowSource,\n    trigger: thresholds.effectiveTriggerTokens,\n    autocompactPct: thresholds.autocompactPct,\n    triggerSource: thresholds.triggerSource,\n  };\n}\n\nexport { DEFAULT_CONTEXT_WINDOW };\n","/**\n * `pai audit tokens session` — where a live session's context budget\n * actually goes: cache reads vs. cache writes vs. fresh input vs. output,\n * and the cache-TTL split (ephemeral 5m vs 1h) that says which cache the\n * session is riding.\n */\n\nimport { readdirSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { parseSessionUsage, totalUsageTokens, type SessionUsageReport } from \"./session-usage.js\";\nimport { encodeDir } from \"../cli/lib/utils.js\";\nimport { firstTurnBreakdown, type FirstTurnBreakdown } from \"./first-turn.js\";\nimport { loadConfig } from \"../daemon/config.js\";\nimport { deriveSessionTrigger, type WindowSource, type SessionTriggerSource } from \"./context-trigger.js\";\n\n/** Newest *.jsonl under ~/.claude/projects, excluding subagents/. */\nexport function newestSessionLog(): string | null {\n  const projectsDir = join(homedir(), \".claude\", \"projects\");\n  let best: { path: string; mtime: number } | null = null;\n  let projectDirs: string[];\n  try {\n    projectDirs = readdirSync(projectsDir);\n  } catch {\n    return null;\n  }\n  for (const projectDir of projectDirs) {\n    const dirPath = join(projectsDir, projectDir);\n    let entries: string[];\n    try {\n      entries = readdirSync(dirPath);\n    } catch {\n      continue;\n    }\n    for (const entry of entries) {\n      if (!entry.endsWith(\".jsonl\")) continue;\n      const filePath = join(dirPath, entry);\n      let mtime: number;\n      try {\n        mtime = statSync(filePath).mtimeMs;\n      } catch {\n        continue;\n      }\n      if (!best || mtime > best.mtime) best = { path: filePath, mtime };\n    }\n  }\n  return best?.path ?? null;\n}\n\nexport interface SessionReportOutput extends SessionUsageReport {\n  totalTokens: number;\n  percentages: Record<string, number>;\n  firstTurn: FirstTurnBreakdown | null;\n  /** The session's own context window (from its transcript, or the last\n   *  assistant model id — see context-trigger.ts), never a constant assumed\n   *  for every session. */\n  window: number;\n  windowSource: WindowSource;\n  /** The compaction trigger this window derives to — what `threshold`\n   *  (turnsAboveThreshold, and the caller's ctxThreshold for severity) was\n   *  actually set from, unless `--ctx-threshold` overrode it. */\n  trigger: number;\n  autocompactPct: number;\n  triggerSource: SessionTriggerSource;\n}\n\n/**\n * `threshold`, when given (`--ctx-threshold`), is an explicit override for\n * both the per-turn \"above threshold\" count and the trigger reported below.\n * Left undefined, the trigger — and the threshold used to parse the\n * transcript — are DERIVED from the session's own window and this project's\n * own measured/configured compaction trigger (deriveSessionTrigger), never\n * assumed to be 200k regardless of the session.\n */\nexport async function auditSession(path?: string, threshold?: number): Promise<SessionReportOutput | null> {\n  const target = path ?? newestSessionLog();\n  if (!target) return null;\n  const keepaliveWord = loadConfig().sessions.cacheKeepalive.prompt;\n  const derived = deriveSessionTrigger(target);\n  const effectiveThreshold = threshold ?? derived.trigger;\n  const report = await parseSessionUsage(target, effectiveThreshold, keepaliveWord);\n  const total = totalUsageTokens(report.totals) || 1;\n  const percentages: Record<string, number> = {};\n  for (const [key, value] of Object.entries(report.totals)) {\n    percentages[key] = (100 * value) / total;\n  }\n  return {\n    ...report,\n    totalTokens: total,\n    percentages,\n    firstTurn: firstTurnBreakdown(target),\n    window: derived.window,\n    windowSource: derived.windowSource,\n    trigger: effectiveThreshold,\n    autocompactPct: derived.autocompactPct,\n    triggerSource: threshold !== undefined ? \"override\" : derived.triggerSource,\n  };\n}\n\nexport interface SessionHistoryRow {\n  path: string;\n  sessionId: string;\n  firstTurnAt: string | null;\n  firstTurnContext: number | null;\n  turns: number;\n  models: Record<string, number>;\n  switches: number;\n  fallbacks: number;\n}\n\n/**\n * First-turn context of every session transcript in this project's\n * `~/.claude/projects/<encoded-cwd>` directory, newest first — the readings\n * needed to see when a step change (model switch, hook growth) happened\n * across sessions without an ad-hoc script.\n */\nexport async function sessionHistory(cwd: string, limit = 20): Promise<SessionHistoryRow[]> {\n  const dir = join(homedir(), \".claude\", \"projects\", encodeDir(cwd));\n  let entries: string[];\n  try {\n    entries = readdirSync(dir).filter((f) => f.endsWith(\".jsonl\"));\n  } catch {\n    return [];\n  }\n\n  const rows: SessionHistoryRow[] = [];\n  for (const entry of entries) {\n    const path = join(dir, entry);\n    const report = await parseSessionUsage(path);\n    if (report.turns === 0) continue;\n    rows.push({\n      path,\n      sessionId: entry.replace(/\\.jsonl$/, \"\"),\n      firstTurnAt: report.firstTurnAt,\n      firstTurnContext: report.firstTurnContext,\n      turns: report.turns,\n      models: report.models,\n      switches: report.modelSwitches.length,\n      fallbacks: report.fallbacks.length,\n    });\n  }\n\n  rows.sort((a, b) => {\n    if (a.firstTurnAt === null && b.firstTurnAt === null) return 0;\n    if (a.firstTurnAt === null) return 1;\n    if (b.firstTurnAt === null) return -1;\n    return b.firstTurnAt.localeCompare(a.firstTurnAt);\n  });\n\n  return rows.slice(0, limit);\n}\n","/**\n * `pai audit tokens spawn` — spawn-overhead comparison from existing logs,\n * no new LLM calls: Agent-tool subagents vs. pai workers (split further into\n * interactive/pane vs. headless -p), each read for first-turn context.\n */\n\nimport { createReadStream, existsSync, readFileSync, readdirSync, statSync } from \"node:fs\";\nimport { createInterface } from \"node:readline\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { readWorkersSection } from \"../workers/config.js\";\nimport { workersLogDir } from \"../workers/paths.js\";\nimport { parseSessionUsage } from \"./session-usage.js\";\nimport { countTokens } from \"./tokens.js\";\n\nexport type SpawnGroupName = \"agentSubagents\" | \"workersInteractive\" | \"workersHeadless\";\n\nexport interface SpawnLogReading {\n  path: string;\n  firstTurnContext: number | null;\n  model: string | null;\n  promptTokens: number | null;\n  overhead: number | null;\n  group: SpawnGroupName;\n}\n\nexport interface SpawnGroupStats {\n  count: number;\n  min: number | null;\n  median: number | null;\n  max: number | null;\n  medianOverhead: number | null;\n  models: string[];\n}\n\nexport interface SpawnReport {\n  agentSubagents: SpawnGroupStats;\n  workersInteractive: SpawnGroupStats;\n  workersHeadless: SpawnGroupStats;\n  readings: SpawnLogReading[];\n}\n\nfunction newestN(paths: string[], n: number): string[] {\n  return paths\n    .map((path) => ({ path, mtime: statSync(path).mtimeMs }))\n    .sort((a, b) => b.mtime - a.mtime)\n    .slice(0, n)\n    .map((entry) => entry.path);\n}\n\n/** Newest N ~/.claude/projects/<project>/<session>/subagents/*.jsonl files. */\nexport function newestAgentSubagentLogs(n = 10): string[] {\n  const projectsDir = join(homedir(), \".claude\", \"projects\");\n  const found: string[] = [];\n  let projectDirs: string[];\n  try {\n    projectDirs = readdirSync(projectsDir);\n  } catch {\n    return [];\n  }\n  for (const projectDir of projectDirs) {\n    const sessionsRoot = join(projectsDir, projectDir);\n    let sessionDirs: string[];\n    try {\n      sessionDirs = readdirSync(sessionsRoot);\n    } catch {\n      continue;\n    }\n    for (const sessionDir of sessionDirs) {\n      const subagentsDir = join(sessionsRoot, sessionDir, \"subagents\");\n      let files: string[];\n      try {\n        files = readdirSync(subagentsDir);\n      } catch {\n        continue;\n      }\n      for (const file of files) {\n        if (file.endsWith(\".jsonl\")) found.push(join(subagentsDir, file));\n      }\n    }\n  }\n  return newestN(found, n);\n}\n\n/** Newest N pai-worker event-mirror *.jsonl files (excludes *.inbox.jsonl). */\nexport function newestWorkerLogs(logDir: string, n = 10): string[] {\n  let files: string[];\n  try {\n    files = readdirSync(logDir);\n  } catch {\n    return [];\n  }\n  const eventLogs = files\n    .filter((f) => f.endsWith(\".jsonl\") && !f.endsWith(\".inbox.jsonl\"))\n    .map((f) => join(logDir, f));\n  return newestN(eventLogs, n);\n}\n\nfunction workerIdFromLogPath(path: string): string {\n  return path\n    .split(\"/\")\n    .pop()!\n    .replace(/\\.jsonl$/, \"\");\n}\n\n/** Read `<id>.status` next to a worker event log; null if missing/unreadable. */\nfunction readWorkerStatus(logDir: string, id: string): { outputFormat?: string } | null {\n  const statusFile = join(logDir, `${id}.status`);\n  if (!existsSync(statusFile)) return null;\n  try {\n    return JSON.parse(readFileSync(statusFile, \"utf8\")) as { outputFormat?: string };\n  } catch {\n    return null;\n  }\n}\n\ninterface UserLine {\n  type?: string;\n  session_id?: string;\n  message?: { content?: string | Array<{ text?: string }> };\n}\n\n/** Token count of the first `type:\"user\"` message's text, or null if none found. */\nexport async function firstUserPromptTokens(path: string): Promise<number | null> {\n  const rl = createInterface({ input: createReadStream(path, \"utf8\"), crlfDelay: Infinity });\n  try {\n    for await (const raw of rl) {\n      if (!raw.trim()) continue;\n      let obj: UserLine;\n      try {\n        obj = JSON.parse(raw) as UserLine;\n      } catch {\n        continue;\n      }\n      if (obj.type !== \"user\") continue;\n      const content = obj.message?.content;\n      const text =\n        typeof content === \"string\"\n          ? content\n          : Array.isArray(content)\n            ? content.map((part) => part?.text ?? \"\").join(\" \")\n            : \"\";\n      return countTokens(text);\n    }\n  } finally {\n    rl.close();\n  }\n  return null;\n}\n\n/** `session_id` off the first record that carries one, or null if none do. */\nexport async function firstSessionId(path: string): Promise<string | null> {\n  const rl = createInterface({ input: createReadStream(path, \"utf8\"), crlfDelay: Infinity });\n  try {\n    for await (const raw of rl) {\n      if (!raw.trim()) continue;\n      let obj: UserLine;\n      try {\n        obj = JSON.parse(raw) as UserLine;\n      } catch {\n        continue;\n      }\n      if (obj.session_id) return obj.session_id;\n    }\n  } finally {\n    rl.close();\n  }\n  return null;\n}\n\n/** `~/.claude/projects/**\\/<sessionId>.jsonl`, searched only under the projects dir. */\nexport function findSessionTranscript(\n  sessionId: string,\n  projectsDir: string = join(homedir(), \".claude\", \"projects\")\n): string | null {\n  const target = `${sessionId}.jsonl`;\n  const stack = [projectsDir];\n  while (stack.length > 0) {\n    const dir = stack.pop()!;\n    let entries: import(\"node:fs\").Dirent[];\n    try {\n      entries = readdirSync(dir, { withFileTypes: true });\n    } catch {\n      continue;\n    }\n    for (const entry of entries) {\n      const full = join(dir, entry.name);\n      if (entry.isDirectory()) {\n        stack.push(full);\n      } else if (entry.name === target) {\n        return full;\n      }\n    }\n  }\n  return null;\n}\n\n/**\n * The mirror log has no `type:\"user\"` record (worker event mirrors carry\n * system/assistant/result records only, never the -p prompt itself), so\n * fall back to the session's own transcript under ~/.claude/projects,\n * located via whichever record in the mirror log carries a `session_id`.\n */\nasync function resolvePromptTokens(mirrorPath: string): Promise<number | null> {\n  const direct = await firstUserPromptTokens(mirrorPath);\n  if (direct !== null) return direct;\n  const sessionId = await firstSessionId(mirrorPath);\n  if (!sessionId) return null;\n  const transcript = findSessionTranscript(sessionId);\n  if (!transcript) return null;\n  return firstUserPromptTokens(transcript);\n}\n\nasync function readOne(path: string, group: SpawnGroupName): Promise<SpawnLogReading> {\n  const [report, promptTokens] = await Promise.all([parseSessionUsage(path), resolvePromptTokens(path)]);\n  const model = Object.keys(report.models)[0] ?? null;\n  const overhead =\n    report.firstTurnContext !== null && promptTokens !== null ? report.firstTurnContext - promptTokens : null;\n  return { path, firstTurnContext: report.firstTurnContext, model, promptTokens, overhead, group };\n}\n\nfunction median(values: number[]): number | null {\n  if (values.length === 0) return null;\n  const sorted = [...values].sort((a, b) => a - b);\n  const mid = Math.floor(sorted.length / 2);\n  return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;\n}\n\nfunction summarize(readings: SpawnLogReading[]): SpawnGroupStats {\n  const contexts = readings.map((r) => r.firstTurnContext).filter((v): v is number => v !== null).sort((a, b) => a - b);\n  const overheads = readings.map((r) => r.overhead).filter((v): v is number => v !== null);\n  const models = [...new Set(readings.map((r) => r.model).filter((m): m is string => !!m))];\n  if (contexts.length === 0) {\n    return { count: readings.length, min: null, median: null, max: null, medianOverhead: median(overheads), models };\n  }\n  return {\n    count: readings.length,\n    min: contexts[0],\n    median: median(contexts),\n    max: contexts[contexts.length - 1],\n    medianOverhead: median(overheads),\n    models,\n  };\n}\n\nexport async function auditSpawn(n = 10): Promise<SpawnReport> {\n  const { workers } = readWorkersSection();\n  const logDir = workersLogDir(workers);\n\n  const agentLogs = newestAgentSubagentLogs(n);\n  const agentReadings = await Promise.all(agentLogs.map((path) => readOne(path, \"agentSubagents\")));\n\n  const workerLogs = newestWorkerLogs(logDir, n);\n  const workerReadings = await Promise.all(workerLogs.map((path) => readOne(path, \"workersInteractive\")));\n\n  const interactive: SpawnLogReading[] = [];\n  const headless: SpawnLogReading[] = [];\n  for (const reading of workerReadings) {\n    const status = readWorkerStatus(logDir, workerIdFromLogPath(reading.path));\n    if (status?.outputFormat) headless.push({ ...reading, group: \"workersHeadless\" });\n    else interactive.push(reading);\n  }\n\n  return {\n    agentSubagents: summarize(agentReadings),\n    workersInteractive: summarize(interactive),\n    workersHeadless: summarize(headless),\n    readings: [...agentReadings, ...interactive, ...headless],\n  };\n}\n","/**\n * `pai audit tokens daemon` — how much LLM work the background daemon does\n * on its own, parsed from its plain-text log: model spawns, prompt sizes,\n * KG-extraction parse failures, and work-queue job throughput.\n */\n\nimport { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { daemonLogPath } from \"../runtime-paths.js\";\n\nconst PLIST_PATH = () => join(homedir(), \"Library\", \"LaunchAgents\", \"com.pai.pai-daemon.plist\");\n\n/** StandardErrorPath from the launchd plist, falling back to the runtime default. */\nexport function resolveDaemonLogPath(): string {\n  if (existsSync(PLIST_PATH())) {\n    const plist = readFileSync(PLIST_PATH(), \"utf8\");\n    const match = plist.match(/<key>StandardErrorPath<\\/key>\\s*<string>([^<]+)<\\/string>/);\n    if (match) return match[1];\n  }\n  return daemonLogPath();\n}\n\nexport interface ModelSpawnStats {\n  count: number;\n  avgPromptChars: number;\n}\n\nexport interface DaemonReport {\n  logPath: string;\n  windowStart: string | null;\n  spawnsByModel: Record<string, ModelSpawnStats>;\n  kgParseFailures: number;\n  /** Every \"[kg-extraction] ...\" log line, of any outcome (diagnostic only). */\n  kgExtractionLines: number;\n  /**\n   * kgParseFailures / total session-summary spawns. KG extraction runs once\n   * per successful session-summary spawn (session-summary-worker.ts calls\n   * extractAndStoreTriples() right after producing a summary) and only ever\n   * logs on failure — there is no per-call success line — so the spawn count\n   * is the only available denominator for a failure rate.\n   */\n  kgParseFailureRate: number;\n  jobCounts: Record<string, { processing: number; completed: number }>;\n}\n\nconst SPAWN_LINE = /\\[session-summary\\] Sending (\\d+) char prompt to (\\S+?)\\.\\.\\./;\nconst KG_EXTRACTION_LINE = /^\\[kg-extraction\\]/;\nconst KG_PARSE_FAILED = /\\[kg-extraction\\] JSON parse failed/;\nconst PROCESSING_LINE = /\\[work-queue-worker\\] Processing (\\S+) \\(/;\nconst COMPLETED_LINE = /\\[work-queue-worker\\] Completed (\\S+) \\(/;\n\nexport function auditDaemon(logPath = resolveDaemonLogPath()): DaemonReport {\n  const spawnsByModel: Record<string, { count: number; totalChars: number }> = {};\n  const jobCounts: Record<string, { processing: number; completed: number }> = {};\n  let kgParseFailures = 0;\n  let kgExtractionLines = 0;\n  let windowStart: string | null = null;\n\n  if (existsSync(logPath)) {\n    try {\n      windowStart = statSync(logPath).birthtime.toISOString();\n    } catch {\n      windowStart = null;\n    }\n    const lines = readFileSync(logPath, \"utf8\").split(\"\\n\");\n    for (const line of lines) {\n      const spawnMatch = line.match(SPAWN_LINE);\n      if (spawnMatch) {\n        const chars = Number(spawnMatch[1]);\n        const model = spawnMatch[2];\n        const entry = (spawnsByModel[model] ??= { count: 0, totalChars: 0 });\n        entry.count++;\n        entry.totalChars += chars;\n        continue;\n      }\n      if (KG_EXTRACTION_LINE.test(line)) {\n        kgExtractionLines++;\n        if (KG_PARSE_FAILED.test(line)) kgParseFailures++;\n        continue;\n      }\n      const processingMatch = line.match(PROCESSING_LINE);\n      if (processingMatch) {\n        (jobCounts[processingMatch[1]] ??= { processing: 0, completed: 0 }).processing++;\n        continue;\n      }\n      const completedMatch = line.match(COMPLETED_LINE);\n      if (completedMatch) {\n        (jobCounts[completedMatch[1]] ??= { processing: 0, completed: 0 }).completed++;\n      }\n    }\n  }\n\n  const spawns: Record<string, ModelSpawnStats> = {};\n  for (const [model, { count, totalChars }] of Object.entries(spawnsByModel)) {\n    spawns[model] = { count, avgPromptChars: count ? Math.round(totalChars / count) : 0 };\n  }\n\n  const totalSpawns = Object.values(spawns).reduce((sum, s) => sum + s.count, 0);\n  const kgParseFailureRate = totalSpawns ? kgParseFailures / totalSpawns : 0;\n\n  return {\n    logPath,\n    windowStart,\n    spawnsByModel: spawns,\n    kgParseFailures,\n    kgExtractionLines,\n    kgParseFailureRate,\n    jobCounts,\n  };\n}\n","/**\n * `pai audit tokens env` — is any live `claude` process pointed at a proxy?\n * A non-default ANTHROPIC_BASE_URL means every token that process sends and\n * receives passes through something other than Anthropic's own API.\n *\n * Uses `ps -axo pid=,etime=,comm=,args=` to enumerate processes (pgrep -x\n * fails to find processes when run from within a claude session). Filters for\n * comm=\"claude\" (also \"claude.exe\" or paths ending with \"/claude\"), then\n * excludes MCP child processes by checking args for patterns like \"npm exec\",\n * \"node\", or \"mcp-server\".\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { execFileSync } from \"node:child_process\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport type Deferral = \"on\" | \"OFF\" | \"n/a\";\n\nexport interface ClaudeProcessEnv {\n  pid: number;\n  baseUrl: string | null;\n  authTokenPresent: boolean;\n  defaultModels: Record<string, string>;\n  enableToolSearch: string | null;\n  workerId: string | null;\n  age: string | null;\n  toolsArg: string | null;\n  mcpServers: number | \"default\";\n  deferral: Deferral;\n}\n\nexport interface EnvReport {\n  processes: ClaudeProcessEnv[];\n  mcpServerCount: number;\n  settingsModel: string | null;\n  settingsEffortLevel: string | null;\n}\n\nexport function isClaudeProcess(line: string): boolean {\n  const parts = line.trim().split(/\\s+/);\n  if (parts.length < 3) return false;\n\n  const comm = parts[2];\n  const isClaudeComm =\n    comm === \"claude\" ||\n    comm === \"claude.exe\" ||\n    comm.endsWith(\"/claude\");\n\n  if (!isClaudeComm) return false;\n\n  const args = parts.slice(3).join(\" \");\n  const isMcpChild =\n    args.includes(\"npm exec\") ||\n    args === \"node\" ||\n    args.startsWith(\"node \") ||\n    args.includes(\"mcp-server\");\n\n  return !isMcpChild;\n}\n\nexport function findClaudePids(): number[] {\n  try {\n    const out = execFileSync(\"ps\", [\"-axo\", \"pid=,etime=,comm=,args=\"], {\n      encoding: \"utf8\",\n    });\n    const pids: number[] = [];\n    for (const line of out.split(\"\\n\")) {\n      if (!line.trim()) continue;\n      if (!isClaudeProcess(line)) continue;\n      const parts = line.trim().split(/\\s+/);\n      const pid = parseInt(parts[0], 10);\n      if (!isNaN(pid)) pids.push(pid);\n    }\n    return pids;\n  } catch {\n    return [];\n  }\n}\n\nfunction extract(pattern: RegExp, text: string): string | null {\n  const match = text.match(pattern);\n  return match ? match[1] : null;\n}\n\n/**\n * `--tools A,B` and `--tools=A,B` are both valid on the argv this process was\n * launched with; an explicit but empty value (`--tools \"\"` / `--tools=`)\n * means \"no tools at all\", distinct from the flag being absent entirely.\n */\nexport function extractToolsArg(line: string): string | null {\n  const eq = line.match(/--tools=(?:\"([^\"]*)\"|(\\S*))/);\n  if (eq) return eq[1] !== undefined ? eq[1] : eq[2];\n  const sp = line.match(/--tools\\s+(?:\"([^\"]*)\"|(\\S+))/);\n  if (sp) return sp[1] !== undefined ? sp[1] : sp[2];\n  return null;\n}\n\nexport function extractMcpConfigArg(line: string): string | null {\n  const eq = line.match(/--mcp-config=(?:\"([^\"]*)\"|(\\S*))/);\n  if (eq) return eq[1] !== undefined ? eq[1] : eq[2];\n  const sp = line.match(/--mcp-config\\s+(?:\"([^\"]*)\"|(\\S+))/);\n  if (sp) return sp[1] !== undefined ? sp[1] : sp[2];\n  return null;\n}\n\n/**\n * No `--tools` flag: the CLI default tool set applies, which includes\n * ToolSearch, so deferral is on. An explicit but empty list means no tools\n * at all, so deferral doesn't apply. Otherwise deferral is on only if the\n * explicit list still names ToolSearch.\n */\nexport function classifyDeferral(toolsArg: string | null): Deferral {\n  if (toolsArg === null) return \"on\";\n  if (toolsArg.trim() === \"\") return \"n/a\";\n  const tools = toolsArg.split(\",\").map((t) => t.trim());\n  return tools.includes(\"ToolSearch\") ? \"on\" : \"OFF\";\n}\n\nfunction countMcpServersInFile(path: string): number {\n  try {\n    const resolved = path.startsWith(\"~\") ? join(homedir(), path.slice(1)) : path;\n    const parsed = JSON.parse(readFileSync(resolved, \"utf8\")) as { mcpServers?: Record<string, unknown> };\n    return Object.keys(parsed.mcpServers ?? {}).length;\n  } catch {\n    return 0;\n  }\n}\n\nexport function readProcessEnvLine(pid: number): string | null {\n  try {\n    return execFileSync(\"ps\", [\"-Eww\", \"-p\", String(pid), \"-o\", \"command=\"], { encoding: \"utf8\" });\n  } catch {\n    return null;\n  }\n}\n\nfunction readProcessAge(pid: number): string | null {\n  try {\n    return execFileSync(\"ps\", [\"-p\", String(pid), \"-o\", \"etime=\"], { encoding: \"utf8\" }).trim() || null;\n  } catch {\n    return null;\n  }\n}\n\nexport function auditEnv(): EnvReport {\n  const processes: ClaudeProcessEnv[] = [];\n  for (const pid of findClaudePids()) {\n    const line = readProcessEnvLine(pid);\n    if (!line) continue;\n    const defaultModels: Record<string, string> = {};\n    const modelPattern = /ANTHROPIC_DEFAULT_(\\w+?)_MODEL=(\\S*)/g;\n    let modelMatch: RegExpExecArray | null;\n    while ((modelMatch = modelPattern.exec(line))) {\n      defaultModels[modelMatch[1]] = modelMatch[2];\n    }\n    const toolsArg = extractToolsArg(line);\n    const mcpConfigPath = extractMcpConfigArg(line);\n    processes.push({\n      pid,\n      baseUrl: extract(/ANTHROPIC_BASE_URL=(\\S*)/, line),\n      authTokenPresent: /ANTHROPIC_AUTH_TOKEN=\\S/.test(line),\n      defaultModels,\n      enableToolSearch: extract(/ENABLE_TOOL_SEARCH=(\\S*)/, line),\n      workerId: extract(/PAI_WORKER_ID=(\\S*)/, line),\n      age: readProcessAge(pid),\n      toolsArg,\n      mcpServers: mcpConfigPath ? countMcpServersInFile(mcpConfigPath) : \"default\",\n      deferral: classifyDeferral(toolsArg),\n    });\n  }\n\n  let mcpServerCount = 0;\n  const claudeJsonPath = join(homedir(), \".claude.json\");\n  if (existsSync(claudeJsonPath)) {\n    try {\n      const parsed = JSON.parse(readFileSync(claudeJsonPath, \"utf8\")) as { mcpServers?: Record<string, unknown> };\n      mcpServerCount = Object.keys(parsed.mcpServers ?? {}).length;\n    } catch {\n      mcpServerCount = 0;\n    }\n  }\n\n  let settingsModel: string | null = null;\n  let settingsEffortLevel: string | null = null;\n  const settingsPath = join(homedir(), \".claude\", \"settings.json\");\n  if (existsSync(settingsPath)) {\n    try {\n      const parsed = JSON.parse(readFileSync(settingsPath, \"utf8\")) as { model?: string; effortLevel?: string };\n      settingsModel = parsed.model ?? null;\n      settingsEffortLevel = parsed.effortLevel ?? null;\n    } catch {\n      /* leave nulls */\n    }\n  }\n\n  return { processes, mcpServerCount, settingsModel, settingsEffortLevel };\n}\n","/**\n * `pai audit tokens schedule` — anything that wakes up more often than the\n * measured cache TTL burns a full cache-write every time, since the\n * previous cache entry has already expired.\n */\n\nimport { readdirSync } from \"node:fs\";\nimport { execFileSync } from \"node:child_process\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { CacheCreationSplit } from \"./session-usage.js\";\n\nconst DEFAULT_TTL_SECONDS = 3600;\n\n/** Which ephemeral cache dominates a session's cache writes, as a TTL in seconds. */\nexport function cacheTtlSeconds(split: CacheCreationSplit | null | undefined): number {\n  if (!split || split.ephemeral5m + split.ephemeral1h === 0) return DEFAULT_TTL_SECONDS;\n  return split.ephemeral1h >= split.ephemeral5m ? 3600 : 300;\n}\n\nexport interface LaunchAgentEntry {\n  path: string;\n  label: string | null;\n  startInterval: number | null;\n  startCalendarInterval: unknown;\n  keepAlive: unknown;\n  programArgs: string[];\n  exceedsTtl: boolean;\n}\n\ninterface PlistJson {\n  Label?: string;\n  StartInterval?: number;\n  StartCalendarInterval?: unknown;\n  KeepAlive?: unknown;\n  ProgramArguments?: string[];\n}\n\nfunction readPlistJson(path: string): PlistJson | null {\n  try {\n    const out = execFileSync(\"plutil\", [\"-convert\", \"json\", \"-o\", \"-\", path], { encoding: \"utf8\" });\n    return JSON.parse(out) as PlistJson;\n  } catch {\n    return null;\n  }\n}\n\nexport function listLaunchAgents(ttlSeconds: number): LaunchAgentEntry[] {\n  const dir = join(homedir(), \"Library\", \"LaunchAgents\");\n  let files: string[];\n  try {\n    files = readdirSync(dir).filter((f) => f.endsWith(\".plist\"));\n  } catch {\n    return [];\n  }\n  const entries: LaunchAgentEntry[] = [];\n  for (const file of files) {\n    const path = join(dir, file);\n    const plist = readPlistJson(path);\n    const startInterval = plist?.StartInterval ?? null;\n    entries.push({\n      path,\n      label: plist?.Label ?? null,\n      startInterval,\n      startCalendarInterval: plist?.StartCalendarInterval ?? null,\n      keepAlive: plist?.KeepAlive ?? null,\n      programArgs: (plist?.ProgramArguments ?? []).slice(0, 2),\n      exceedsTtl: startInterval !== null && startInterval > ttlSeconds,\n    });\n  }\n  return entries;\n}\n\nexport function readCrontab(): string[] {\n  try {\n    const out = execFileSync(\"crontab\", [\"-l\"], { encoding: \"utf8\" });\n    return out.split(\"\\n\").filter((line) => line.trim() && !line.trim().startsWith(\"#\"));\n  } catch {\n    return [];\n  }\n}\n\nexport interface ScheduleReport {\n  ttlSeconds: number;\n  agents: LaunchAgentEntry[];\n  crontabLines: string[];\n}\n\nexport function auditSchedule(ttlSeconds = DEFAULT_TTL_SECONDS): ScheduleReport {\n  return { ttlSeconds, agents: listLaunchAgents(ttlSeconds), crontabLines: readCrontab() };\n}\n\nexport { DEFAULT_TTL_SECONDS };\n","/**\n * `pai audit tokens skills` — token cost of the whole skill/command/plugin\n * catalogue: every `SKILL.md` under ~/.claude/skills and ~/.claude/plugins,\n * plus every command under ~/.claude/commands, rendered as the one-line\n * `- name: description` entry Claude Code loads into context for each.\n *\n * Two failure modes matter more than the raw total: the same real file\n * reachable through two paths (a plugin's marketplaces/ checkout and its\n * cache/ install both point at one file), and two different real files that\n * collide on name (a skill and a command both called \"Sessions\") — the first\n * wastes nothing but must not be double-counted, the second silently shadows\n * one of the two in whichever catalogue order Claude Code loads them.\n */\n\nimport { existsSync, readdirSync, readFileSync, realpathSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { basename, dirname, join, relative, sep } from \"node:path\";\nimport { countTokens, TOKEN_ENCODING } from \"./tokens.js\";\n\nexport type CatalogueSource = \"skills\" | \"commands\" | \"plugins\";\n\nexport interface CatalogueEntry {\n  name: string;\n  description: string;\n  source: CatalogueSource;\n  path: string;\n  tokens: number;\n  /** Always true for skills/commands; for plugins, whether `enabledPlugins` turns it on. */\n  enabled: boolean;\n  /** Set on a plugin entry whose derived id has no matching `enabledPlugins` key. */\n  status?: \"unknown\";\n}\n\nexport interface DuplicateGroup {\n  name: string;\n  paths: string[];\n}\n\nexport interface SkillsReport {\n  encoding: string;\n  entries: CatalogueEntry[];\n  total: number;\n  totalsBySource: Record<CatalogueSource, number>;\n  countsBySource: Record<CatalogueSource, number>;\n  /** Sum of `tokens` for entries that are actually loaded into a session (enabled plugins + all skills/commands). */\n  enabledTotal: number;\n  enabledTotalsBySource: Record<CatalogueSource, number>;\n  duplicates: DuplicateGroup[];\n}\n\n/** Recursively collect every file under `dir` whose basename satisfies `matches`. */\nfunction findFilesRecursive(dir: string, matches: (name: string) => boolean): string[] {\n  let entries: import(\"node:fs\").Dirent[];\n  try {\n    entries = readdirSync(dir, { withFileTypes: true });\n  } catch {\n    return [];\n  }\n  const found: string[] = [];\n  for (const entry of entries) {\n    const full = join(dir, entry.name);\n    if (entry.isDirectory()) {\n      found.push(...findFilesRecursive(full, matches));\n    } else if (entry.isFile() && matches(entry.name)) {\n      found.push(full);\n    }\n  }\n  return found;\n}\n\ninterface RawFile {\n  path: string;\n  source: CatalogueSource;\n}\n\n/**\n * `<skillsDir>/*\\/SKILL.md`, one level deep only — this is what Claude Code\n * actually loads. `readdirSync(withFileTypes)` reports a symlinked skill\n * directory as neither file nor directory, so entries are resolved with\n * `statSync` (which follows symlinks) instead of trusting `Dirent`.\n */\nfunction findTopLevelSkillFiles(skillsDir: string): string[] {\n  let entries: string[];\n  try {\n    entries = readdirSync(skillsDir);\n  } catch {\n    return [];\n  }\n  const found: string[] = [];\n  for (const name of entries) {\n    const dir = join(skillsDir, name);\n    let isDir = false;\n    try {\n      isDir = statSync(dir).isDirectory();\n    } catch {\n      continue;\n    }\n    if (!isDir) continue;\n    const skillFile = join(dir, \"SKILL.md\");\n    if (existsSync(skillFile)) found.push(skillFile);\n  }\n  return found;\n}\n\n/** Every SKILL.md/*.md the three catalogue roots contribute, source-tagged. */\nexport function findCatalogueFiles(claudeDir: string): RawFile[] {\n  const found: RawFile[] = [];\n  for (const path of findTopLevelSkillFiles(join(claudeDir, \"skills\"))) {\n    found.push({ path, source: \"skills\" });\n  }\n  for (const path of findFilesRecursive(join(claudeDir, \"commands\"), (n) => n.endsWith(\".md\"))) {\n    found.push({ path, source: \"commands\" });\n  }\n  for (const path of findFilesRecursive(join(claudeDir, \"plugins\"), (n) => n === \"SKILL.md\")) {\n    found.push({ path, source: \"plugins\" });\n  }\n  return found;\n}\n\n/** Strip a single layer of matching quotes (\"...\" or '...') from a trimmed value. */\nfunction unquote(value: string): string {\n  if (value.length >= 2) {\n    const first = value[0];\n    const last = value[value.length - 1];\n    if ((first === '\"' && last === '\"') || (first === \"'\" && last === \"'\")) {\n      return value.slice(1, -1);\n    }\n  }\n  return value;\n}\n\ninterface Frontmatter {\n  name: string | null;\n  description: string;\n}\n\n/** Parse the `name:`/`description:` frontmatter fields out of a skill/command file. */\nexport function parseFrontmatter(text: string): Frontmatter {\n  const match = /^---\\n([\\s\\S]*?)\\n---/.exec(text);\n  const fm = match ? match[1] : \"\";\n  const nameMatch = /^name:\\s*(.+)$/m.exec(fm);\n  const descMatch = /(?:^|\\n)description:\\s*(.+?)(?=\\n\\w[\\w-]*:|$)/s.exec(fm);\n  return {\n    name: nameMatch ? unquote(nameMatch[1].trim()) : null,\n    description: descMatch ? unquote(descMatch[1].trim()) : \"\",\n  };\n}\n\nfunction nameFallback(path: string, source: CatalogueSource): string {\n  if (source === \"commands\") return basename(path).replace(/\\.md$/, \"\");\n  return basename(dirname(path));\n}\n\n/** Build one catalogue entry from a file, deriving its name/description/tokens. */\nfunction readEntry(path: string, source: CatalogueSource): CatalogueEntry {\n  let text = \"\";\n  try {\n    text = readFileSync(path, \"utf8\");\n  } catch {\n    /* unreadable file: treat as an empty entry rather than throwing */\n  }\n  const fm = parseFrontmatter(text);\n  const name = fm.name ?? nameFallback(path, source);\n  const line = `- ${name}: ${fm.description}`;\n  return { name, description: fm.description, source, path, tokens: countTokens(line), enabled: true };\n}\n\ninterface EnabledPluginsSettings {\n  enabledPlugins?: Record<string, boolean>;\n}\n\n/** `enabledPlugins` map from settings.json, or `{}` if the file is missing/unreadable. */\nfunction readEnabledPlugins(settingsPath: string): Record<string, boolean> {\n  try {\n    const parsed = JSON.parse(readFileSync(settingsPath, \"utf8\")) as EnabledPluginsSettings;\n    return parsed.enabledPlugins ?? {};\n  } catch {\n    return {};\n  }\n}\n\n/** `<relative-to-root>` split on the platform separator, or null if `path` is not under `root`. */\nfunction pathUnder(root: string, path: string): string[] | null {\n  const rel = relative(root, path);\n  if (rel.startsWith(\"..\")) return null;\n  return rel.split(sep);\n}\n\ninterface PluginStatus {\n  id: string;\n  enabled: boolean;\n  status?: \"unknown\";\n  /** Identifies \"the same plugin file reached through cache/ or marketplaces/\" for enabled-token dedup. */\n  dedupeKey: string;\n}\n\n/**\n * A cache install is `plugins/cache/<marketplace>/<plugin>/<version>/...`, so its id is exact.\n * A marketplace checkout is `plugins/marketplaces/<marketplace>/...` with no plugin segment, so\n * it can only be matched by marketplace name against whichever `enabledPlugins` key ends in\n * `@<marketplace>` — good enough for the single-plugin-per-marketplace installs this audits today.\n */\nfunction resolvePluginStatus(claudeDir: string, path: string, enabledPlugins: Record<string, boolean>): PluginStatus {\n  const cacheParts = pathUnder(join(claudeDir, \"plugins\", \"cache\"), path);\n  if (cacheParts && cacheParts.length >= 4) {\n    const [marketplace, plugin, , ...suffix] = cacheParts;\n    const id = `${plugin}@${marketplace}`;\n    const known = id in enabledPlugins;\n    return {\n      id,\n      enabled: known && enabledPlugins[id] === true,\n      status: known ? undefined : \"unknown\",\n      dedupeKey: `${id}::${suffix.join(\"/\")}`,\n    };\n  }\n\n  const marketplaceParts = pathUnder(join(claudeDir, \"plugins\", \"marketplaces\"), path);\n  if (marketplaceParts && marketplaceParts.length >= 2) {\n    const [marketplace, ...suffix] = marketplaceParts;\n    const matchKey = Object.keys(enabledPlugins).find((k) => k.endsWith(`@${marketplace}`));\n    if (matchKey) {\n      return {\n        id: matchKey,\n        enabled: enabledPlugins[matchKey] === true,\n        dedupeKey: `${matchKey}::${suffix.join(\"/\")}`,\n      };\n    }\n    return {\n      id: `unknown@${marketplace}`,\n      enabled: false,\n      status: \"unknown\",\n      dedupeKey: `unknown@${marketplace}::${suffix.join(\"/\")}`,\n    };\n  }\n\n  return { id: path, enabled: false, status: \"unknown\", dedupeKey: path };\n}\n\n/** Group entries by case-insensitive name; only groups with >1 real path. */\nexport function findDuplicates(entries: CatalogueEntry[]): DuplicateGroup[] {\n  const byName = new Map<string, Set<string>>();\n  for (const entry of entries) {\n    const key = entry.name.toLowerCase();\n    if (!byName.has(key)) byName.set(key, new Set());\n    byName.get(key)!.add(entry.path);\n  }\n  const duplicates: DuplicateGroup[] = [];\n  for (const [name, paths] of byName) {\n    if (paths.size > 1) duplicates.push({ name, paths: [...paths] });\n  }\n  return duplicates.sort((a, b) => a.name.localeCompare(b.name));\n}\n\nexport function auditSkills(\n  claudeDir: string = join(homedir(), \".claude\"),\n  settingsPath: string = join(claudeDir, \"settings.json\")\n): SkillsReport {\n  const rawFiles = findCatalogueFiles(claudeDir);\n\n  const seenReal = new Set<string>();\n  const deduped: RawFile[] = [];\n  for (const file of rawFiles) {\n    let real = file.path;\n    try {\n      real = realpathSync(file.path);\n    } catch {\n      /* keep the original path if it vanished mid-scan */\n    }\n    if (seenReal.has(real)) continue;\n    seenReal.add(real);\n    deduped.push(file);\n  }\n\n  const enabledPlugins = readEnabledPlugins(settingsPath);\n  const entries = deduped.map((f) => readEntry(f.path, f.source));\n  const dedupeKeys = new Map<string, string>();\n  for (const entry of entries) {\n    if (entry.source !== \"plugins\") continue;\n    const status = resolvePluginStatus(claudeDir, entry.path, enabledPlugins);\n    entry.enabled = status.enabled;\n    entry.status = status.status;\n    dedupeKeys.set(entry.path, status.dedupeKey);\n  }\n\n  const totalsBySource: Record<CatalogueSource, number> = { skills: 0, commands: 0, plugins: 0 };\n  const countsBySource: Record<CatalogueSource, number> = { skills: 0, commands: 0, plugins: 0 };\n  const enabledTotalsBySource: Record<CatalogueSource, number> = { skills: 0, commands: 0, plugins: 0 };\n  const seenEnabledPluginKeys = new Set<string>();\n  for (const entry of entries) {\n    totalsBySource[entry.source] += entry.tokens;\n    countsBySource[entry.source] += 1;\n    if (!entry.enabled) continue;\n    if (entry.source !== \"plugins\") {\n      enabledTotalsBySource[entry.source] += entry.tokens;\n      continue;\n    }\n    const dedupeKey = dedupeKeys.get(entry.path)!;\n    if (seenEnabledPluginKeys.has(dedupeKey)) continue;\n    seenEnabledPluginKeys.add(dedupeKey);\n    enabledTotalsBySource.plugins += entry.tokens;\n  }\n\n  return {\n    encoding: TOKEN_ENCODING,\n    entries,\n    total: entries.reduce((sum, e) => sum + e.tokens, 0),\n    totalsBySource,\n    countsBySource,\n    enabledTotal: enabledTotalsBySource.skills + enabledTotalsBySource.commands + enabledTotalsBySource.plugins,\n    enabledTotalsBySource,\n    duplicates: findDuplicates(entries),\n  };\n}\n\nexport function topByTokens(report: SkillsReport, n: number): CatalogueEntry[] {\n  return [...report.entries].sort((a, b) => b.tokens - a.tokens).slice(0, n);\n}\n\nexport const SKILL_CATALOGUE_AMBER = 3000;\nexport const SKILL_CATALOGUE_RED = 6000;\n","/**\n * severity.ts — the combined report's RED/AMBER/GREEN thresholds, kept in\n * one place so tightening or loosening a rule never means hunting through\n * each section's module.\n */\n\nimport type { FilesReport } from \"./files.js\";\nimport type { HooksReport } from \"./hooks.js\";\nimport type { SessionReportOutput } from \"./session.js\";\nimport type { DaemonReport } from \"./daemon.js\";\nimport type { EnvReport } from \"./env.js\";\nimport type { SkillsReport } from \"./skills.js\";\nimport type { SubagentsReport } from \"./subagents.js\";\nimport type { McpReport } from \"./mcp.js\";\nimport { firstTurnEvidence } from \"./first-turn.js\";\nimport { SINGLE_FILE_LIMIT, TOTAL_LIMIT } from \"./files.js\";\nimport { SKILL_CATALOGUE_AMBER, SKILL_CATALOGUE_RED } from \"./skills.js\";\n\nexport const HOOK_TOKEN_LIMIT = 1000;\nexport const PER_PROMPT_HOOK_COST_AMBER = 0.05;\nexport const PER_PROMPT_HOOK_COST_RED = 0.15;\nexport const FIRST_TURN_CONTEXT_LIMIT = 30_000;\nexport const DAEMON_FAILURE_RATE_LIMIT = 0.05;\n/** Ratios of the session's own derived trigger (SessionReportOutput.trigger\n *  — see context-trigger.ts), NOT absolute token counts: a 1M-window session\n *  and a 200k-window session have different triggers, and rating both\n *  against one constant is the exact defect these were changed to fix (a\n *  200k-yardstick audit drove a wrong config change on 2026-09-20 — see\n *  audits/token-waste/reviewprompt.md). */\nexport const CONTEXT_GROWTH_AVG_RED_RATIO = 0.5;\nexport const CONTEXT_GROWTH_MAX_AMBER_RATIO = 0.75;\nexport const MODEL_SWITCH_REBUILD_LIMIT = 20_000;\n\nexport type Severity = \"RED\" | \"AMBER\" | \"GREEN\";\n\nexport interface Finding {\n  finding: string;\n  severity: Severity;\n  evidence: string;\n}\n\nconst SEVERITY_RANK: Record<Severity, number> = { RED: 0, AMBER: 1, GREEN: 2 };\n\nexport function sortFindings(findings: Finding[]): Finding[] {\n  return [...findings].sort((a, b) => SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity]);\n}\n\nexport function buildFindings(input: {\n  files: FilesReport;\n  hooks: HooksReport;\n  session: SessionReportOutput | null;\n  daemon: DaemonReport;\n  env: EnvReport;\n  skills: SkillsReport;\n  subagents: SubagentsReport;\n  mcp: McpReport;\n  ctxThreshold: number;\n}): Finding[] {\n  const findings: Finding[] = [];\n\n  for (const proc of input.env.processes) {\n    if (proc.baseUrl) {\n      findings.push({\n        finding: `claude process ${proc.pid} routed through a proxy`,\n        severity: \"RED\",\n        evidence: `ANTHROPIC_BASE_URL=${proc.baseUrl}`,\n      });\n    }\n  }\n\n  const bigFiles = input.files.readings.filter((r) => r.tokens > SINGLE_FILE_LIMIT);\n  for (const f of bigFiles) {\n    findings.push({\n      finding: `memory file over ${SINGLE_FILE_LIMIT} tokens`,\n      severity: \"AMBER\",\n      evidence: `${f.path}: ${f.tokens} tokens`,\n    });\n  }\n  findings.push({\n    finding: \"total memory-file tokens\",\n    severity: input.files.total > TOTAL_LIMIT ? \"AMBER\" : \"GREEN\",\n    evidence: `${input.files.total} tokens across ${input.files.readings.length} files`,\n  });\n\n  const bigHooks = input.hooks.readings.filter((r) => r.tokens > HOOK_TOKEN_LIMIT);\n  for (const h of bigHooks) {\n    findings.push({\n      finding: `${h.event} hook over ${HOOK_TOKEN_LIMIT} tokens`,\n      severity: \"AMBER\",\n      evidence: `${h.command}: ${h.tokens} tokens`,\n    });\n  }\n  if (bigHooks.length === 0 && input.hooks.readings.length > 0) {\n    findings.push({\n      finding: \"SessionStart / UserPromptSubmit hook output\",\n      severity: \"GREEN\",\n      evidence: `${input.hooks.readings.length} hooks, all under ${HOOK_TOKEN_LIMIT} tokens`,\n    });\n  }\n\n  if (input.session) {\n    const first = input.session.firstTurnContext ?? 0;\n    // A session whose first prompt was abandoned (Escape, re-submit) reads\n    // low here because its skill listing sits on the dead branch; the\n    // dead-branch figure in the evidence says so.\n    const evidence =\n      typeof input.session.firstTurn?.apiContext === \"number\"\n        ? `${first} tokens: ${firstTurnEvidence(input.session.firstTurn)} (${input.session.path})`\n        : `${first} tokens (${input.session.path})`;\n    findings.push({\n      finding: \"first-turn context size\",\n      severity: first > FIRST_TURN_CONTEXT_LIMIT ? \"AMBER\" : \"GREEN\",\n      evidence,\n    });\n  }\n\n  const rate = input.daemon.kgParseFailureRate;\n  findings.push({\n    finding: \"daemon KG-extraction JSON parse failure rate\",\n    severity: rate > DAEMON_FAILURE_RATE_LIMIT ? \"AMBER\" : \"GREEN\",\n    evidence: `${(rate * 100).toFixed(1)}% (${input.daemon.kgParseFailures} failures over session-summary spawns)`,\n  });\n\n  if (!input.env.processes.some((p) => p.baseUrl)) {\n    findings.push({\n      finding: \"claude process base URL\",\n      severity: \"GREEN\",\n      evidence: `${input.env.processes.length} claude process(es), none proxied`,\n    });\n  }\n\n  const deferralOff = input.env.processes.filter((p) => {\n    const mcpCount = p.mcpServers === \"default\" ? input.env.mcpServerCount : p.mcpServers;\n    return p.deferral === \"OFF\" && mcpCount > 0;\n  });\n  findings.push({\n    finding: \"tool deferral on live processes\",\n    severity: deferralOff.length > 0 ? \"RED\" : \"GREEN\",\n    evidence:\n      deferralOff.length > 0\n        ? deferralOff\n            .map((p) => `pid ${p.pid}: ${p.mcpServers === \"default\" ? input.env.mcpServerCount : p.mcpServers} mcp servers, tools=${p.toolsArg}`)\n            .join(\"; \")\n        : `${input.env.processes.length} claude process(es) checked, none with an explicit --tools list missing ToolSearch and MCP servers registered`,\n  });\n\n  const skillTokens = input.skills.enabledTotal;\n  findings.push({\n    finding: \"skill catalogue tokens\",\n    severity: skillTokens > SKILL_CATALOGUE_RED ? \"RED\" : skillTokens > SKILL_CATALOGUE_AMBER ? \"AMBER\" : \"GREEN\",\n    evidence: `${skillTokens} tokens loaded (${input.skills.total} on disk) across ${input.skills.entries.length} entries` +\n      (input.skills.duplicates.length ? `, ${input.skills.duplicates.length} case-insensitive duplicate name(s)` : \"\"),\n  });\n\n  const inheriting = input.subagents.entries.filter((e) => e.model === \"inherits\");\n  if (input.subagents.entries.length === 0) {\n    findings.push({ finding: \"subagent model pinning\", severity: \"GREEN\", evidence: \"no agent files\" });\n  } else {\n    findings.push({\n      finding: \"subagent model pinning\",\n      severity: inheriting.length > 0 ? \"AMBER\" : \"GREEN\",\n      evidence: `${inheriting.length} of ${input.subagents.entries.length} inherit`,\n    });\n  }\n\n  const configuredCount = input.mcp.servers.filter((s) => s.configuredIn.length > 0).length;\n  const plainLaunch = input.mcp.liveProcesses.find((p) => p.mcpConfigPath === null);\n  if (!plainLaunch) {\n    findings.push({\n      finding: \"MCP servers loaded vs. used\",\n      severity: \"GREEN\",\n      evidence: `${configuredCount} configured, no plain launch found`,\n    });\n  } else {\n    const zeroCallServers = input.mcp.servers.filter((s) => s.configuredIn.length > 0 && s.calls30d === 0);\n    findings.push({\n      finding: \"MCP servers loaded vs. used\",\n      severity: zeroCallServers.length > 0 ? \"AMBER\" : \"GREEN\",\n      evidence: `${configuredCount} configured, ${zeroCallServers.length} loaded by pid ${plainLaunch.pid}`,\n    });\n  }\n\n  if (input.session) {\n    const avg = input.session.avgContext ?? 0;\n    const max = input.session.maxContext ?? 0;\n    const trigger = input.ctxThreshold;\n    const severity: Severity =\n      avg > CONTEXT_GROWTH_AVG_RED_RATIO * trigger || input.session.turnsAboveThreshold > 0\n        ? \"RED\"\n        : max > CONTEXT_GROWTH_MAX_AMBER_RATIO * trigger\n          ? \"AMBER\"\n          : \"GREEN\";\n    findings.push({\n      finding: \"context growth\",\n      severity,\n      evidence:\n        `avg ${avg.toLocaleString(\"en-US\")}, max ${max.toLocaleString(\"en-US\")} of trigger ${trigger.toLocaleString(\"en-US\")} ` +\n        `(window ${input.session.window.toLocaleString(\"en-US\")}, override ${input.session.autocompactPct}), ` +\n        `${input.session.turnsAboveThreshold} turns above`,\n    });\n\n    const perPromptHookCost = input.hooks.totalsByEvent.UserPromptSubmit;\n    const inputSent =\n      input.session.totals.cache_read_input_tokens +\n      input.session.totals.cache_creation_input_tokens +\n      input.session.totals.input_tokens;\n    if (perPromptHookCost !== undefined && inputSent > 0) {\n      const prompts = input.session.userPrompts;\n      const turns = input.session.turns;\n      const total = perPromptHookCost * input.session.promptExposure;\n      const pct = (total / inputSent) * 100;\n      findings.push({\n        finding: \"per-prompt hook cost\",\n        severity:\n          pct > PER_PROMPT_HOOK_COST_RED * 100\n            ? \"RED\"\n            : pct > PER_PROMPT_HOOK_COST_AMBER * 100\n              ? \"AMBER\"\n              : \"GREEN\",\n        evidence: `${perPromptHookCost} tokens/prompt x ${prompts} prompts, carried over ${turns} turns = ${total} tokens (${pct.toFixed(1)}% of ${inputSent} input tokens sent)`,\n      });\n    }\n\n    const compactions = input.session.compactions;\n    const autoCompactions = compactions.filter((c) => c.trigger === \"auto\");\n    const overshoots = autoCompactions.filter((c) => c.preTokens > 1.25 * input.ctxThreshold);\n    findings.push({\n      finding: \"compaction trigger\",\n      severity: overshoots.length > 0 ? \"RED\" : \"GREEN\",\n      evidence:\n        overshoots.length > 0\n          ? `override not honoured: ${overshoots.map((c) => `preTokens=${c.preTokens}`).join(\", \")} vs configured ${input.ctxThreshold}`\n          : compactions.length === 0\n            ? `0 compactions in ${input.session.turns} turns`\n            : `${compactions.length} compaction(s), all within 25% of configured ${input.ctxThreshold}`,\n    });\n\n    const switches = input.session.modelSwitches;\n    const fallbacks = input.session.fallbacks;\n    const rebuilds = switches.filter((s) => s.cacheCreation > MODEL_SWITCH_REBUILD_LIMIT);\n    const short = (model: string) => model.replace(/^claude-/, \"\");\n    let switchEvidence: string;\n    if (switches.length === 0) {\n      switchEvidence = `0 switches in ${input.session.turns} turns, models ${JSON.stringify(input.session.models)}`;\n    } else {\n      const list = switches\n        .map(\n          (s) =>\n            `${short(s.from)}->${short(s.to)} at turn ${s.turnIndex} (cache_read ${s.cacheRead}, cache_creation ${s.cacheCreation})`\n        )\n        .join(\"; \");\n      switchEvidence = `${switches.length} switch(es): ${list}`;\n      if (fallbacks.length > 0) {\n        switchEvidence += `; ${fallbacks.length} safeguard fallback(s): ${fallbacks.map((f) => f.category).join(\",\")}`;\n      }\n    }\n    findings.push({\n      finding: \"mid-session model switches\",\n      severity: switches.length === 0 ? \"GREEN\" : rebuilds.length > 0 ? \"RED\" : \"AMBER\",\n      evidence: switchEvidence,\n    });\n  }\n\n  return sortFindings(findings);\n}\n","/**\n * `pai audit tokens ladder` — LIVE: what each layer of headless-worker\n * scaffolding actually costs in first-turn context, measured by spawning the\n * real `claude -p` binary at increasing configuration and reading the\n * `usage` block of its `--output-format json` result. No section above this\n * one makes a network call; this is the one exception, gated behind `--live`.\n *\n * Rungs (each a superset of the last, except L3 which drops every flag):\n *   L0  empty MCP config, no tool schemas at all\n *   L1  same, plus the core file/shell tool schemas (`--tools`)\n *   L2  same as L1, but the empty MCP config is swapped for a real one\n *   L3  plain: none of --strict-mcp-config / --mcp-config / --tools\n */\n\nimport { spawn } from \"node:child_process\";\nimport { readdirSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { usageContextTokens } from \"../workers/run.js\";\nimport type { UsageBlock } from \"../utils/input-side-tokens.js\";\nimport { ensureNoMcpConfig } from \"../workers/paths.js\";\n\nexport const PROBE_PROMPT = \"Reply with exactly the single word OK and nothing else. Do not use any tool.\";\nexport const LADDER_TOOLS = \"Bash,Read,Edit,Write,Grep,Glob,Skill,ToolSearch\";\nexport const LADDER_TIMEOUT_MS = 180_000;\nexport const DEFAULT_LADDER_MODEL = \"haiku\";\n\nexport interface LadderRung {\n  id: string;\n  description: string;\n  /** Full argv (minus the `claude` binary itself). */\n  args: string[];\n}\n\n/** Build the four rungs' argv. Pure — no spawning, so it's unit-testable. */\nexport function buildLadderRungs(model: string, emptyMcpPath: string, realMcpPath: string): LadderRung[] {\n  const base = [\"-p\", PROBE_PROMPT, \"--model\", model, \"--output-format\", \"json\"];\n  return [\n    {\n      id: \"L0\",\n      description: \"empty MCP config, no tools\",\n      args: [...base, \"--strict-mcp-config\", \"--mcp-config\", emptyMcpPath, \"--tools\", \"\"],\n    },\n    {\n      id: \"L1\",\n      description: `empty MCP config, --tools ${LADDER_TOOLS}`,\n      args: [...base, \"--strict-mcp-config\", \"--mcp-config\", emptyMcpPath, \"--tools\", LADDER_TOOLS],\n    },\n    {\n      id: \"L2\",\n      description: `real MCP config, --tools ${LADDER_TOOLS}`,\n      args: [...base, \"--strict-mcp-config\", \"--mcp-config\", realMcpPath, \"--tools\", LADDER_TOOLS],\n    },\n    {\n      id: \"L3\",\n      description: \"plain (no --strict-mcp-config, no --mcp-config, no --tools)\",\n      args: [...base],\n    },\n  ];\n}\n\n/** Newest `*.mcp.json` in a worker log dir, or null if none exist. */\nexport function newestMcpConfig(logDir: string): string | null {\n  let files: string[];\n  try {\n    files = readdirSync(logDir);\n  } catch {\n    return null;\n  }\n  const candidates = files\n    .filter((f) => f.endsWith(\".mcp.json\"))\n    .map((f) => join(logDir, f));\n  if (candidates.length === 0) return null;\n  return candidates\n    .map((path) => ({ path, mtime: statSync(path).mtimeMs }))\n    .sort((a, b) => b.mtime - a.mtime)[0].path;\n}\n\nexport interface LadderReading {\n  id: string;\n  description: string;\n  firstTurnContext: number | null;\n  timedOut: boolean;\n  error?: string;\n  /** Delta vs. the previous rung's firstTurnContext; null when either side is missing. */\n  delta: number | null;\n}\n\nexport interface LadderReport {\n  model: string;\n  mcpConfigPath: string;\n  readings: LadderReading[];\n}\n\n/** Spawn one rung of `claude -p ...` and read its usage-derived first-turn context. */\nfunction runRung(rung: LadderRung, cwd: string): Promise<{ firstTurnContext: number | null; timedOut: boolean; error?: string }> {\n  return new Promise((resolvePromise) => {\n    const env = { ...process.env };\n    delete env.PAI_WORKER;\n\n    const proc = spawn(\"claude\", rung.args, { cwd, env, stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n    let out = \"\";\n    let timedOut = false;\n    const timer = setTimeout(() => {\n      timedOut = true;\n      try {\n        proc.kill(\"SIGKILL\");\n      } catch {\n        /* already gone */\n      }\n    }, LADDER_TIMEOUT_MS);\n\n    proc.stdout.on(\"data\", (chunk: Buffer) => {\n      out += chunk.toString(\"utf8\");\n    });\n    proc.on(\"error\", (e) => {\n      clearTimeout(timer);\n      resolvePromise({ firstTurnContext: null, timedOut: false, error: e.message });\n    });\n    proc.on(\"close\", () => {\n      clearTimeout(timer);\n      if (timedOut) {\n        resolvePromise({ firstTurnContext: null, timedOut: true });\n        return;\n      }\n      try {\n        const parsed = JSON.parse(out.trim()) as { usage?: UsageBlock };\n        resolvePromise({ firstTurnContext: usageContextTokens(parsed.usage), timedOut: false });\n      } catch (e) {\n        resolvePromise({ firstTurnContext: null, timedOut: false, error: (e as Error).message });\n      }\n    });\n  });\n}\n\nexport async function auditLadder(opts: {\n  model?: string;\n  mcpConfigPath?: string;\n  logDir: string;\n  cwd?: string;\n}): Promise<LadderReport> {\n  const model = opts.model ?? DEFAULT_LADDER_MODEL;\n  const emptyMcpPath = ensureNoMcpConfig(opts.logDir);\n  const realMcpPath = opts.mcpConfigPath ?? newestMcpConfig(opts.logDir) ?? emptyMcpPath;\n  const cwd = opts.cwd ?? process.cwd();\n\n  const rungs = buildLadderRungs(model, emptyMcpPath, realMcpPath);\n  const readings: LadderReading[] = [];\n  let previous: number | null = null;\n  for (const rung of rungs) {\n    const result = await runRung(rung, cwd);\n    const delta = result.firstTurnContext !== null && previous !== null ? result.firstTurnContext - previous : null;\n    readings.push({\n      id: rung.id,\n      description: rung.description,\n      firstTurnContext: result.firstTurnContext,\n      timedOut: result.timedOut,\n      error: result.error,\n      delta,\n    });\n    if (result.firstTurnContext !== null) previous = result.firstTurnContext;\n  }\n\n  return { model, mcpConfigPath: realMcpPath, readings };\n}\n","/**\n * `pai audit tokens subagents` — token cost of every Claude Code subagent\n * definition: `.claude/agents/*.md` under the user's home and (separately)\n * under the current project. A subagent with no `model:` frontmatter field\n * inherits the caller's model at runtime rather than costing a fixed model\n * choice, so it is reported as `\"inherits\"` rather than left blank.\n */\n\nimport { readdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { countTokens, TOKEN_ENCODING } from \"./tokens.js\";\n\nexport interface SubagentEntry {\n  path: string;\n  tokens: number;\n  model: string;\n}\n\nexport interface SubagentsReport {\n  encoding: string;\n  entries: SubagentEntry[];\n}\n\n/** Strip a single layer of matching quotes (\"...\" or '...') from a trimmed value. */\nfunction unquote(value: string): string {\n  if (value.length >= 2) {\n    const first = value[0];\n    const last = value[value.length - 1];\n    if ((first === '\"' && last === '\"') || (first === \"'\" && last === \"'\")) {\n      return value.slice(1, -1);\n    }\n  }\n  return value;\n}\n\n/** The top-level `model:` frontmatter field, or `\"inherits\"` if absent/empty. */\nfunction parseModel(text: string): string {\n  const match = /^---\\n([\\s\\S]*?)\\n---/.exec(text);\n  if (!match) return \"inherits\";\n  const modelMatch = /^model:\\s*(.+)$/m.exec(match[1]);\n  if (!modelMatch) return \"inherits\";\n  const value = unquote(modelMatch[1].trim());\n  return value === \"\" ? \"inherits\" : value;\n}\n\n/** Every top-level `.md` file directly under `agentsDir` (not recursive). */\nfunction findAgentFiles(agentsDir: string): string[] {\n  let names: string[];\n  try {\n    names = readdirSync(agentsDir);\n  } catch {\n    return [];\n  }\n  return names.filter((n) => n.endsWith(\".md\")).map((n) => join(agentsDir, n));\n}\n\nfunction readEntry(path: string): SubagentEntry {\n  let text = \"\";\n  try {\n    text = readFileSync(path, \"utf8\");\n  } catch {\n    /* unreadable file: treat as an empty entry rather than throwing */\n  }\n  return { path, tokens: countTokens(text), model: parseModel(text) };\n}\n\nexport function auditSubagents(homeDir: string, cwd: string): SubagentsReport {\n  const paths = [\n    ...findAgentFiles(join(homeDir, \".claude\", \"agents\")),\n    ...findAgentFiles(join(cwd, \".claude\", \"agents\")),\n  ];\n  return { encoding: TOKEN_ENCODING, entries: paths.map(readEntry) };\n}\n","/**\n * `pai audit mcp` — cross-reference every MCP server name found anywhere\n * (global/project config, project `.mcp.json`, live process argv, 30-day\n * transcript usage) against where it's configured, whether it's pinned via\n * `pai project mcp`, whether a live process actually loaded it, and how much\n * it was used in the last 30 days.\n *\n * The `--connect` path is the only side-effecting one: it launches every\n * configured stdio MCP server to call `listTools()`. It never runs unless\n * the caller passes `connect: true` explicitly — there is no default-on path.\n */\n\nimport { existsSync, readFileSync, readdirSync, statSync } from \"node:fs\";\nimport { createReadStream } from \"node:fs\";\nimport { createInterface } from \"node:readline\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { findClaudePids, extractMcpConfigArg, readProcessEnvLine } from \"./env.js\";\nimport { projectLaunchConfig } from \"../workers/project-config.js\";\n\nexport interface McpLiveProcess {\n  pid: number;\n  mcpConfigPath: string | null;\n  servers: string[] | null;\n}\n\nexport interface McpUsage {\n  distinctTools: number;\n  calls: number;\n}\n\nexport interface McpServerRow {\n  server: string;\n  configuredIn: string[];\n  disabled: boolean;\n  /** \"yes\" when the project pin lists this server, \"-\" when a pin exists without it, null when the project has no pin (every server loads). */\n  pinned: \"yes\" | \"-\" | null;\n  loadedLive: string;\n  toolsExposed: number | \"UNKNOWN\";\n  toolsUsed30d: number;\n  calls30d: number;\n}\n\nexport interface McpReport {\n  servers: McpServerRow[];\n  liveProcesses: McpLiveProcess[];\n}\n\nconst THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;\nconst MAX_TRANSCRIPT_BYTES = 50 * 1024 * 1024;\n\n/** Whether a transcript file is worth streaming: fresh enough and small enough. */\nexport function shouldScanTranscript(stat: { size: number; mtimeMs: number }, now: number): boolean {\n  if (stat.size > MAX_TRANSCRIPT_BYTES) return false;\n  if (now - stat.mtimeMs > THIRTY_DAYS_MS) return false;\n  return true;\n}\n\n/**\n * Whether a live process's resolved server set includes `serverName`.\n * `servers === null` means \"no --mcp-config flag\", i.e. every configured\n * server loads; an array (possibly empty) means only those names load.\n */\nexport function liveProcessLoadsServer(\n  proc: { servers: string[] | null },\n  serverName: string,\n  allConfiguredNames: string[]\n): boolean {\n  if (proc.servers === null) return allConfiguredNames.includes(serverName);\n  return proc.servers.includes(serverName);\n}\n\nfunction resolveHomePath(path: string, homeDir: string): string {\n  return path.startsWith(\"~\") ? join(homeDir, path.slice(1)) : path;\n}\n\nfunction readJsonFile<T>(path: string): T | null {\n  try {\n    return JSON.parse(readFileSync(path, \"utf8\")) as T;\n  } catch {\n    return null;\n  }\n}\n\ninterface ClaudeJsonShape {\n  mcpServers?: Record<string, unknown>;\n  projects?: Record<string, { mcpServers?: Record<string, unknown>; disabledMcpServers?: string[] }>;\n}\n\ninterface McpJsonShape {\n  mcpServers?: Record<string, unknown>;\n}\n\ninterface ConfiguredSources {\n  configuredIn: Map<string, Set<string>>;\n  disabled: Set<string>;\n  serverDefs: Map<string, unknown>;\n}\n\nfunction gatherConfiguredSources(cwd: string, homeDir: string): ConfiguredSources {\n  const configuredIn = new Map<string, Set<string>>();\n  const disabled = new Set<string>();\n  const serverDefs = new Map<string, unknown>();\n\n  const addTag = (name: string, tag: string) => {\n    if (!configuredIn.has(name)) configuredIn.set(name, new Set());\n    configuredIn.get(name)!.add(tag);\n  };\n\n  const claudeJsonPath = join(homeDir, \".claude.json\");\n  const claudeJson = readJsonFile<ClaudeJsonShape>(claudeJsonPath);\n  if (claudeJson) {\n    for (const [name, def] of Object.entries(claudeJson.mcpServers ?? {})) {\n      addTag(name, \"global\");\n      if (!serverDefs.has(name)) serverDefs.set(name, def);\n    }\n    const projectEntry = claudeJson.projects?.[resolve(cwd)];\n    if (projectEntry) {\n      for (const [name, def] of Object.entries(projectEntry.mcpServers ?? {})) {\n        addTag(name, \"project\");\n        if (!serverDefs.has(name)) serverDefs.set(name, def);\n      }\n      for (const name of projectEntry.disabledMcpServers ?? []) {\n        disabled.add(name);\n        if (!configuredIn.has(name)) configuredIn.set(name, new Set());\n      }\n    }\n  }\n\n  const mcpJsonPath = join(cwd, \".mcp.json\");\n  if (existsSync(mcpJsonPath)) {\n    const mcpJson = readJsonFile<McpJsonShape>(mcpJsonPath);\n    if (mcpJson) {\n      for (const [name, def] of Object.entries(mcpJson.mcpServers ?? {})) {\n        addTag(name, \"project .mcp.json\");\n        if (!serverDefs.has(name)) serverDefs.set(name, def);\n      }\n    }\n  }\n\n  return { configuredIn, disabled, serverDefs };\n}\n\nfunction gatherLiveProcesses(homeDir: string): McpLiveProcess[] {\n  const procs: McpLiveProcess[] = [];\n  for (const pid of findClaudePids()) {\n    const line = readProcessEnvLine(pid);\n    if (!line) continue;\n    const mcpConfigArg = extractMcpConfigArg(line);\n    if (!mcpConfigArg) {\n      procs.push({ pid, mcpConfigPath: null, servers: null });\n      continue;\n    }\n    const resolved = resolveHomePath(mcpConfigArg, homeDir);\n    const parsed = readJsonFile<McpJsonShape>(resolved);\n    const servers = parsed ? Object.keys(parsed.mcpServers ?? {}) : [];\n    procs.push({ pid, mcpConfigPath: resolved, servers });\n  }\n  return procs;\n}\n\n/** Recursively find every file under `dir` ending in `.jsonl`. */\nfunction findJsonlFiles(dir: string): string[] {\n  let entries: import(\"node:fs\").Dirent[];\n  try {\n    entries = readdirSync(dir, { withFileTypes: true });\n  } catch {\n    return [];\n  }\n  const found: string[] = [];\n  for (const entry of entries) {\n    const full = join(dir, entry.name);\n    if (entry.isDirectory()) {\n      found.push(...findJsonlFiles(full));\n    } else if (entry.isFile() && entry.name.endsWith(\".jsonl\")) {\n      found.push(full);\n    }\n  }\n  return found;\n}\n\ninterface ToolUseBlock {\n  type?: string;\n  name?: string;\n}\n\ninterface TranscriptLine {\n  type?: string;\n  name?: string;\n  message?: { content?: ToolUseBlock[] };\n}\n\nfunction foldToolUseBlock(block: ToolUseBlock, usage: Map<string, { tools: Set<string>; calls: number }>): void {\n  if (block.type !== \"tool_use\") return;\n  const name = block.name;\n  if (!name || !name.startsWith(\"mcp__\")) return;\n  const parts = name.split(\"__\");\n  const server = parts[1];\n  const tool = parts.slice(2).join(\"__\");\n  if (!server) return;\n  if (!usage.has(server)) usage.set(server, { tools: new Set(), calls: 0 });\n  const entry = usage.get(server)!;\n  entry.tools.add(tool);\n  entry.calls += 1;\n}\n\nasync function gatherUsage(transcriptsDir: string, now: number): Promise<Map<string, McpUsage>> {\n  const usage = new Map<string, { tools: Set<string>; calls: number }>();\n  for (const file of findJsonlFiles(transcriptsDir)) {\n    let stat: import(\"node:fs\").Stats;\n    try {\n      stat = statSync(file);\n    } catch {\n      continue;\n    }\n    if (!shouldScanTranscript(stat, now)) continue;\n\n    const rl = createInterface({ input: createReadStream(file, \"utf8\"), crlfDelay: Infinity });\n    for await (const raw of rl) {\n      if (!raw.includes('\"mcp__')) continue;\n      let parsed: TranscriptLine;\n      try {\n        parsed = JSON.parse(raw) as TranscriptLine;\n      } catch {\n        continue;\n      }\n      if (parsed.type === \"tool_use\") {\n        foldToolUseBlock(parsed as ToolUseBlock, usage);\n      }\n      if (Array.isArray(parsed.message?.content)) {\n        for (const block of parsed.message!.content!) {\n          foldToolUseBlock(block, usage);\n        }\n      }\n    }\n  }\n\n  const result = new Map<string, McpUsage>();\n  for (const [server, entry] of usage) {\n    result.set(server, { distinctTools: entry.tools.size, calls: entry.calls });\n  }\n  return result;\n}\n\ninterface StdioLikeDef {\n  command?: string;\n  args?: string[];\n  env?: Record<string, string>;\n}\n\nfunction isStdioDef(def: unknown): def is StdioLikeDef {\n  return typeof def === \"object\" && def !== null && typeof (def as StdioLikeDef).command === \"string\";\n}\n\n/** Connect to every stdio MCP server row and record how many tools it exposes. Side-effecting: only called when `opts.connect === true`. */\nasync function connectAndCountTools(\n  rows: McpServerRow[],\n  serverDefs: Map<string, unknown>\n): Promise<void> {\n  const { Client } = await import(\"@modelcontextprotocol/sdk/client/index.js\");\n  const { StdioClientTransport } = await import(\"@modelcontextprotocol/sdk/client/stdio.js\");\n\n  for (const row of rows) {\n    const def = serverDefs.get(row.server);\n    if (!isStdioDef(def)) continue;\n\n    const transport = new StdioClientTransport({\n      command: def.command!,\n      args: def.args ?? [],\n      env: def.env,\n    });\n    const client = new Client({ name: \"pai-audit-mcp\", version: \"1.0.0\" }, { capabilities: {} });\n\n    let settled = false;\n    const timeout = new Promise<\"UNKNOWN\">((res) => {\n      setTimeout(() => {\n        if (!settled) res(\"UNKNOWN\");\n      }, 10_000);\n    });\n\n    try {\n      const attempt = (async (): Promise<number | \"UNKNOWN\"> => {\n        await client.connect(transport);\n        const result = await client.listTools();\n        return result.tools.length;\n      })();\n      const outcome = await Promise.race([attempt, timeout]);\n      settled = true;\n      row.toolsExposed = outcome;\n    } catch {\n      settled = true;\n      row.toolsExposed = \"UNKNOWN\";\n    } finally {\n      try {\n        await client.close();\n      } catch {\n        /* already closed or never connected */\n      }\n    }\n  }\n}\n\nexport async function auditMcp(opts: {\n  cwd: string;\n  homeDir: string;\n  connect?: boolean;\n  projectsDbPath?: string;\n  transcriptsDir?: string;\n}): Promise<McpReport> {\n  const cwd = opts.cwd;\n  const homeDir = opts.homeDir;\n  const transcriptsDir = opts.transcriptsDir ?? join(homeDir, \".claude\", \"projects\");\n\n  const { configuredIn, disabled, serverDefs } = gatherConfiguredSources(cwd, homeDir);\n  const liveProcesses = gatherLiveProcesses(homeDir);\n  const usage = await gatherUsage(transcriptsDir, Date.now());\n  // `projectsDbPath` predates the storage-layer conversion, when this call opened\n  // a sqlite file by path; `projectLaunchConfig` now takes an injectable\n  // RegistryBackend instead, and no caller has passed one since, so it can only\n  // fall through to the process-wide backend here too.\n  const pinned = (await projectLaunchConfig(resolve(cwd)))?.mcp ?? null;\n\n  const allNames = new Set<string>([...configuredIn.keys(), ...usage.keys()]);\n  const allConfiguredNames = [...configuredIn.keys()];\n\n  const rows: McpServerRow[] = [];\n  for (const name of allNames) {\n    const liveMatches = liveProcesses.filter((p) => liveProcessLoadsServer(p, name, allConfiguredNames));\n    const serverUsage = usage.get(name);\n    rows.push({\n      server: name,\n      configuredIn: [...(configuredIn.get(name) ?? new Set())],\n      disabled: disabled.has(name),\n      pinned: pinned === null ? null : pinned.includes(name) ? \"yes\" : \"-\",\n      loadedLive: liveMatches.length ? liveMatches.map((p) => `pid ${p.pid}`).join(\", \") : \"-\",\n      toolsExposed: \"UNKNOWN\",\n      toolsUsed30d: serverUsage?.distinctTools ?? 0,\n      calls30d: serverUsage?.calls ?? 0,\n    });\n  }\n\n  rows.sort((a, b) => a.server.localeCompare(b.server));\n\n  if (opts.connect === true) {\n    await connectAndCountTools(rows, serverDefs);\n  }\n\n  return { servers: rows, liveProcesses };\n}\n","/**\n * pai audit tokens [section]\n *\n * files    — token count per memory file (CLAUDE.md chain, CORE skill,\n *            whisper rules, project auto-memory) and their total\n * hooks    — token cost of every SessionStart / UserPromptSubmit hook,\n *            plus which PreToolUse hooks rewrite Bash commands\n * session  — cache/input/output token split for one session transcript,\n *            plus a first-turn breakdown attributing turn-1 tokens to the\n *            live-lineage transcript lines (hooks, skill listing, deferred\n *            tools, MCP instructions, prompt) with dead-branch tokens\n *            (abandoned prompts, persisted but never sent) reported\n *            separately; `--history [n]` lists the first-turn context of\n *            the newest n sessions in this project's transcript directory,\n *            newest first; `--turn <n>` attributes API call n's context\n *            growth to the lines injected since the previous call (hooks,\n *            prompts, tool results, the previous call's re-sent output)\n *            plus the cl100k residual against the billing tokenizer\n * spawn    — first-turn context overhead: Agent-tool subagents vs. pai\n *            workers (interactive/pane vs. headless); `--detail` adds\n *            per-log prompt-token/overhead rows and a median-overhead column\n * daemon   — LLM spawns, KG-extraction parse failures, work-queue counts\n *            from the daemon log\n * env      — ANTHROPIC_BASE_URL / model-override env on every live claude\n *            process, MCP server count, configured model/effort\n * schedule — launchd agents / crontab entries that wake up more often than\n *            the measured prompt-cache TTL\n * skills   — token cost of the SKILL.md/command/plugin catalogue, top\n *            entries by tokens, per-source subtotals, case-insensitive\n *            duplicate names\n * ladder   — LIVE (spawns `claude -p`, requires `--live`): first-turn context\n *            at increasing headless-worker configuration (empty MCP/no\n *            tools → tools → real MCP → plain)\n *\n * With no section, all eight non-live sections run and one combined\n * RED/AMBER/GREEN table is printed. `--json` prints the same data as JSON\n * instead of a table.\n */\n\nimport { homedir } from \"node:os\";\nimport { mkdirSync, readdirSync, appendFileSync, writeFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { header, ok, warn, err, dim, bold, renderTable, stripAnsi } from \"./utils.js\";\nimport { TOKEN_ENCODING } from \"../../audit/tokens.js\";\nimport { auditFiles, defaultFileSet, type FilesReport } from \"../../audit/files.js\";\nimport { auditHooks, type HooksReport } from \"../../audit/hooks.js\";\nimport { auditSession, newestSessionLog, sessionHistory, type SessionReportOutput, type SessionHistoryRow } from \"../../audit/session.js\";\nimport { turnBreakdown, type TurnBreakdown } from \"../../audit/first-turn.js\";\nimport { auditSpawn, type SpawnGroupStats, type SpawnReport } from \"../../audit/spawn.js\";\nimport { auditDaemon, type DaemonReport } from \"../../audit/daemon.js\";\nimport { auditEnv, type EnvReport } from \"../../audit/env.js\";\nimport { auditSchedule, cacheTtlSeconds } from \"../../audit/schedule.js\";\nimport { buildFindings, type Finding, type Severity } from \"../../audit/severity.js\";\nimport { auditSkills, topByTokens, type SkillsReport } from \"../../audit/skills.js\";\nimport { auditLadder, type LadderReport } from \"../../audit/ladder.js\";\nimport { auditSubagents, type SubagentsReport } from \"../../audit/subagents.js\";\nimport { auditMcp, type McpReport } from \"../../audit/mcp.js\";\nimport { shortenPath } from \"./utils.js\";\nimport { readWorkersSection } from \"../../workers/config.js\";\nimport { workersLogDir } from \"../../workers/paths.js\";\n\nfunction severityColor(sev: Severity, text: string): string {\n  if (sev === \"RED\") return err(text);\n  if (sev === \"AMBER\") return warn(text);\n  return ok(text);\n}\n\nfunction printJson(data: unknown): void {\n  console.log(JSON.stringify(data, null, 2));\n}\n\n// ---------------------------------------------------------------------------\n// files\n// ---------------------------------------------------------------------------\n\nfunction printFiles(report: FilesReport): void {\n  console.log(header(`Memory files (encoding: ${report.encoding})`));\n  const rows = report.readings.map((r) => [\n    r.missing ? dim(\"MISSING\") : String(r.tokens),\n    r.path,\n  ]);\n  console.log(renderTable([\"tokens\", \"path\"], rows));\n  console.log(bold(`total: ${report.total} tokens`));\n}\n\nasync function cmdFiles(paths: string[], opts: { json?: boolean }): Promise<void> {\n  const targets = paths.length > 0 ? paths : defaultFileSet(process.cwd());\n  const report = auditFiles(targets);\n  if (opts.json) return printJson(report);\n  printFiles(report);\n}\n\n// ---------------------------------------------------------------------------\n// hooks\n// ---------------------------------------------------------------------------\n\nfunction printHooks(report: HooksReport): void {\n  console.log(header(`Hook output (encoding: ${report.encoding})`));\n  console.log(dim(`settings.json: ${report.settingsFiles.join(\", \") || \"none found\"}`));\n  const rows = report.readings.map((r) => [\n    r.event,\n    r.matcher ?? \"\",\n    String(r.tokens),\n    r.error ? err(r.error) : \"\",\n    r.command,\n  ]);\n  console.log(renderTable([\"event\", \"matcher\", \"tokens\", \"error\", \"command\"], rows));\n  for (const [event, total] of Object.entries(report.totalsByEvent)) {\n    console.log(bold(`${event} total: ${total} tokens`));\n  }\n  console.log(\n    dim(\n      `PreToolUse hooks: ${report.preToolUseHooks.length}` +\n        (report.preToolUseRewritesBash ? \", including a Bash-matcher rewrite hook\" : \"\")\n    )\n  );\n}\n\nfunction cmdHooks(opts: { json?: boolean }): void {\n  const report = auditHooks(process.cwd());\n  if (opts.json) return printJson(report);\n  printHooks(report);\n}\n\n// ---------------------------------------------------------------------------\n// session\n// ---------------------------------------------------------------------------\n\nfunction printSession(report: SessionReportOutput): void {\n  console.log(header(`Session usage: ${report.path}`));\n  console.log(dim(`size: ${report.sizeBytes} bytes, assistant turns with usage: ${report.turns}`));\n  console.log(\n    dim(\n      `window: ${report.window.toLocaleString(\"en-US\")} (${report.windowSource}), ` +\n        `trigger: ${report.trigger.toLocaleString(\"en-US\")} (${report.triggerSource}, ` +\n        `override ${report.autocompactPct}%)`\n    )\n  );\n  const rows = Object.entries(report.totals).map(([key, value]) => [\n    key,\n    value.toLocaleString(\"en-US\"),\n    `${report.percentages[key].toFixed(1)}%`,\n  ]);\n  rows.push([\"TOTAL\", report.totalTokens.toLocaleString(\"en-US\"), \"100.0%\"]);\n  console.log(renderTable([\"metric\", \"tokens\", \"share\"], rows));\n  console.log(`context on first turn: ${report.firstTurnContext ?? \"n/a\"}`);\n  if (report.firstTurn && report.firstTurn.apiContext !== null) {\n    const ft = report.firstTurn;\n    console.log(header(\"first-turn breakdown (live lineage, cl100k)\"));\n    const ftRows = ft.items.filter((i) => i.tokens > 0).map((i) => [i.kind, String(i.tokens), i.label]);\n    console.log(renderTable([\"kind\", \"tokens\", \"label\"], ftRows));\n    console.log(`transcript-side: ${ft.transcriptTokens}`);\n    console.log(`remainder (system prompt + tool schemas + agents + memory): ${ft.remainder}`);\n    console.log(`dead-branch: ${ft.deadBranchTokens} tokens on ${ft.deadBranchLines} lines, persisted but never sent`);\n  }\n  console.log(`context on last turn:  ${report.lastTurnContext ?? \"n/a\"}`);\n  console.log(\n    `cache_creation split: ephemeral_5m=${report.cacheCreationSplit.ephemeral5m}, ` +\n      `ephemeral_1h=${report.cacheCreationSplit.ephemeral1h}`\n  );\n  console.log(dim(`models: ${JSON.stringify(report.models)}`));\n  console.log(\n    `avg context: ${report.avgContext ?? \"n/a\"}, max context: ${report.maxContext ?? \"n/a\"}, ` +\n      `turns above threshold: ${report.turnsAboveThreshold}, cache-rebuild turns: ${report.cacheRebuildTurns}, ` +\n      `user prompts: ${report.userPrompts}, prompt exposure: ${report.promptExposure}`\n  );\n  console.log(`compactions: ${report.compactions.length}`);\n  for (const c of report.compactions) {\n    console.log(dim(`  ${c.trigger} at turn ${c.turnIndex}: preTokens=${c.preTokens}`));\n  }\n  console.log(`model switches: ${report.modelSwitches.length}`);\n  for (const s of report.modelSwitches) {\n    console.log(dim(`  ${s.from} -> ${s.to} at turn ${s.turnIndex}: cache_read=${s.cacheRead}, cache_creation=${s.cacheCreation}`));\n  }\n  if (report.fallbacks.length > 0) {\n    console.log(`safeguard fallbacks: ${report.fallbacks.length}`);\n    for (const f of report.fallbacks) {\n      console.log(dim(`  ${f.from} -> ${f.to} before turn ${f.turnIndex}: ${f.category} (${f.scope})`));\n    }\n  }\n  console.log(\n    dim(\n      `idle gaps > 60min: ${report.idleGapsOver60min}, keepalive beats: ${report.keepaliveBeats ?? \"n/a\"}`\n    )\n  );\n}\n\nfunction printTurnBreakdown(tb: TurnBreakdown): void {\n  console.log(header(`turn ${tb.turn} attribution (live lineage, cl100k)`));\n  const rows = tb.items\n    .filter((i) => i.tokens > 0)\n    .map((i) => [String(i.tokens), i.kind, i.label.replace(/\\s+/g, \" \").slice(0, 60)]);\n  console.log(renderTable([\"tokens\", \"kind\", \"label\"], rows));\n  console.log(`transcript-side ${tb.transcriptTokens}`);\n  console.log(`billed delta ${tb.billedDelta ?? \"n/a\"} (context ${tb.apiContext ?? \"n/a\"} after ${tb.prevApiContext})`);\n  console.log(`previous call output ${tb.prevOutputTokens} (includes thinking, re-sent on tool turns)`);\n  console.log(\n    `previous call visible output ${tb.prevVisibleTokens} (cl100k, listed as assistant:prev, not in transcript-side)`\n  );\n  console.log(\n    `residual ${tb.residual ?? \"n/a\"}: positive = cl100k undercount vs the billing tokenizer (1.2-1.45x measured on tool output and markdown tables); negative = the previous call's thinking was not re-sent (only re-sent inside a tool loop)`\n  );\n}\n\nfunction printSessionHistory(rows: SessionHistoryRow[]): void {\n  console.log(header(\"Session history (newest first)\"));\n  const tableRows = rows.map((r) => [\n    r.firstTurnAt ?? \"n/a\",\n    r.sessionId,\n    r.firstTurnContext === null ? \"n/a\" : String(r.firstTurnContext),\n    String(r.turns),\n    String(r.switches),\n    Object.entries(r.models)\n      .map(([name, count]) => `${name}:${count}`)\n      .join(\",\"),\n  ]);\n  console.log(renderTable([\"first turn (UTC)\", \"session\", \"first-turn ctx\", \"turns\", \"switches\", \"models\"], tableRows));\n}\n\nasync function cmdSession(\n  path: string | undefined,\n  opts: { json?: boolean; ctxThreshold?: string; history?: string | boolean; turn?: string }\n): Promise<void> {\n  if (opts.history !== undefined) {\n    const limit = typeof opts.history === \"string\" ? parseInt(opts.history, 10) : 20;\n    const rows = await sessionHistory(process.cwd(), limit);\n    if (opts.json) return printJson(rows);\n    printSessionHistory(rows);\n    return;\n  }\n  if (opts.turn !== undefined) {\n    const target = path ?? newestSessionLog();\n    if (!target) {\n      console.error(err(\"  No session transcript found.\"));\n      process.exitCode = 1;\n      return;\n    }\n    const tb = turnBreakdown(target, parseInt(opts.turn, 10));\n    if (!tb) {\n      console.error(err(`  Turn ${opts.turn} not found in ${target}.`));\n      process.exitCode = 1;\n      return;\n    }\n    if (opts.json) return printJson(tb);\n    printTurnBreakdown(tb);\n    return;\n  }\n  const threshold = opts.ctxThreshold ? parseInt(opts.ctxThreshold, 10) : undefined;\n  const report = await auditSession(path, threshold);\n  if (!report) {\n    console.error(err(\"  No session transcript found.\"));\n    process.exitCode = 1;\n    return;\n  }\n  if (opts.json) return printJson(report);\n  printSession(report);\n}\n\n// ---------------------------------------------------------------------------\n// spawn\n// ---------------------------------------------------------------------------\n\nfunction printSpawnGroup(name: string, stats: SpawnGroupStats): string[] {\n  return [\n    name,\n    String(stats.count),\n    stats.min === null ? \"n/a\" : String(stats.min),\n    stats.median === null ? \"n/a\" : String(stats.median),\n    stats.max === null ? \"n/a\" : String(stats.max),\n    stats.medianOverhead === null ? \"n/a\" : String(stats.medianOverhead),\n    stats.models.join(\", \"),\n  ];\n}\n\nconst SPAWN_GROUP_LABELS: Record<string, string> = {\n  agentSubagents: \"Agent-tool subagents\",\n  workersInteractive: \"pai workers (interactive/pane)\",\n  workersHeadless: \"pai workers (headless -p)\",\n};\n\nfunction printSpawn(report: SpawnReport, detail?: boolean): void {\n  if (detail) {\n    console.log(header(\"Spawn overhead — per-log detail\"));\n    const rows = report.readings.map((r) => [\n      SPAWN_GROUP_LABELS[r.group] ?? r.group,\n      r.firstTurnContext === null ? \"n/a\" : String(r.firstTurnContext),\n      r.promptTokens === null ? \"n/a\" : String(r.promptTokens),\n      r.overhead === null ? \"n/a\" : String(r.overhead),\n      r.model ?? \"\",\n      shortenPath(r.path, 70),\n    ]);\n    console.log(renderTable([\"group\", \"first-turn ctx\", \"prompt tok\", \"overhead\", \"model\", \"path\"], rows));\n  }\n  console.log(header(\"Spawn overhead (first-turn context tokens)\"));\n  const rows = [\n    printSpawnGroup(\"Agent-tool subagents\", report.agentSubagents),\n    printSpawnGroup(\"pai workers (interactive/pane)\", report.workersInteractive),\n    printSpawnGroup(\"pai workers (headless -p)\", report.workersHeadless),\n  ];\n  console.log(renderTable([\"group\", \"count\", \"min\", \"median\", \"max\", \"median overhead\", \"models\"], rows));\n}\n\nasync function cmdSpawn(opts: { json?: boolean; n?: string; detail?: boolean }): Promise<void> {\n  const n = opts.n ? parseInt(opts.n, 10) : 10;\n  const report = await auditSpawn(n);\n  if (opts.json) return printJson(report);\n  printSpawn(report, opts.detail);\n}\n\n// ---------------------------------------------------------------------------\n// daemon\n// ---------------------------------------------------------------------------\n\nfunction printDaemon(report: DaemonReport): void {\n  console.log(header(`Daemon log: ${report.logPath}`));\n  console.log(dim(`window start (log file birth time): ${report.windowStart ?? \"unknown\"}`));\n  const spawnRows = Object.entries(report.spawnsByModel).map(([model, s]) => [\n    model,\n    String(s.count),\n    String(s.avgPromptChars),\n  ]);\n  console.log(renderTable([\"model\", \"spawns\", \"avg prompt chars\"], spawnRows));\n  console.log(\n    `kg-extraction JSON parse failures: ${report.kgParseFailures} ` +\n      `(${(report.kgParseFailureRate * 100).toFixed(1)}% of session-summary spawns; ` +\n      `${report.kgExtractionLines} kg-extraction log lines total)`\n  );\n  const jobRows = Object.entries(report.jobCounts).map(([job, c]) => [job, String(c.processing), String(c.completed)]);\n  console.log(renderTable([\"job type\", \"processing\", \"completed\"], jobRows));\n}\n\nfunction cmdDaemon(opts: { json?: boolean }): void {\n  const report = auditDaemon();\n  if (opts.json) return printJson(report);\n  printDaemon(report);\n}\n\n// ---------------------------------------------------------------------------\n// env\n// ---------------------------------------------------------------------------\n\nfunction printEnv(report: EnvReport): void {\n  console.log(header(\"Live claude processes\"));\n  const rows = report.processes.map((p) => [\n    String(p.pid),\n    p.baseUrl ? err(p.baseUrl) : dim(\"(default)\"),\n    p.authTokenPresent ? \"present\" : \"absent\",\n    JSON.stringify(p.defaultModels),\n    p.enableToolSearch ?? \"\",\n    String(p.mcpServers),\n    p.deferral === \"OFF\" ? err(p.deferral) : p.deferral === \"n/a\" ? dim(p.deferral) : p.deferral,\n    p.workerId ?? \"\",\n    p.age ?? \"\",\n  ]);\n  console.log(\n    renderTable(\n      [\"pid\", \"base url\", \"auth token\", \"default models\", \"tool search\", \"tools\", \"deferral\", \"worker id\", \"age\"],\n      rows\n    )\n  );\n  for (const p of report.processes) {\n    if (p.baseUrl) console.log(err(`  RED: claude process ${p.pid} is routed through ${p.baseUrl}`));\n    const mcpCount = p.mcpServers === \"default\" ? report.mcpServerCount : p.mcpServers;\n    if (p.deferral === \"OFF\" && mcpCount > 0) {\n      console.log(err(`  RED: claude process ${p.pid} has --tools without ToolSearch (${mcpCount} MCP servers registered)`));\n    }\n  }\n  console.log(dim(`MCP servers registered: ${report.mcpServerCount}`));\n  console.log(dim(`settings.json model: ${report.settingsModel ?? \"unset\"}, effortLevel: ${report.settingsEffortLevel ?? \"unset\"}`));\n}\n\nfunction cmdEnv(opts: { json?: boolean }): void {\n  const report = auditEnv();\n  if (opts.json) return printJson(report);\n  printEnv(report);\n}\n\n// ---------------------------------------------------------------------------\n// schedule\n// ---------------------------------------------------------------------------\n\nasync function scheduleTtlSeconds(): Promise<number> {\n  const session = await auditSession(newestSessionLog() ?? undefined).catch(() => null);\n  return cacheTtlSeconds(session?.cacheCreationSplit);\n}\n\nfunction printSchedule(report: Awaited<ReturnType<typeof auditSchedule>>): void {\n  console.log(header(`Launchd agents (cache TTL: ${report.ttlSeconds}s)`));\n  const rows = report.agents.map((a) => [\n    a.label ?? \"\",\n    a.startInterval === null ? \"\" : String(a.startInterval),\n    a.startCalendarInterval ? \"yes\" : \"\",\n    a.keepAlive ? \"yes\" : \"\",\n    a.programArgs.join(\" \"),\n    a.exceedsTtl ? warn(\"exceeds TTL\") : \"\",\n  ]);\n  console.log(renderTable([\"label\", \"interval(s)\", \"calendar\", \"keepAlive\", \"program\", \"\"], rows));\n  console.log(header(\"crontab -l\"));\n  for (const line of report.crontabLines) console.log(dim(line));\n}\n\nasync function cmdSchedule(opts: { json?: boolean }): Promise<void> {\n  const ttl = await scheduleTtlSeconds();\n  const report = auditSchedule(ttl);\n  if (opts.json) return printJson(report);\n  printSchedule(report);\n}\n\n// ---------------------------------------------------------------------------\n// skills\n// ---------------------------------------------------------------------------\n\nfunction printSkills(report: SkillsReport, top: number): void {\n  console.log(header(`Skill/command/plugin catalogue (encoding: ${report.encoding})`));\n  console.log(dim(`${report.entries.length} entries, ${report.enabledTotal} tokens loaded (${report.total} on disk)`));\n  const subtotalRows = ([\"skills\", \"commands\", \"plugins\"] as const).map((source) => [\n    source,\n    String(report.countsBySource[source]),\n    String(report.totalsBySource[source]),\n    String(report.enabledTotalsBySource[source]),\n  ]);\n  console.log(renderTable([\"source\", \"entries\", \"tokens\", \"enabled\"], subtotalRows));\n\n  console.log(header(`Top ${top} by tokens`));\n  const topRows = topByTokens(report, top).map((e) => [String(e.tokens), e.source, e.enabled ? \"yes\" : \"no\", e.name, e.path]);\n  console.log(renderTable([\"tokens\", \"source\", \"on\", \"name\", \"path\"], topRows));\n\n  console.log(header(\"Case-insensitive duplicate names\"));\n  if (report.duplicates.length === 0) {\n    console.log(dim(\"none\"));\n  } else {\n    for (const dup of report.duplicates) {\n      console.log(`${bold(dup.name)}:`);\n      for (const path of dup.paths) console.log(`  ${path}`);\n    }\n  }\n}\n\nfunction cmdSkills(opts: { json?: boolean; top?: string }): void {\n  const top = opts.top ? parseInt(opts.top, 10) : 15;\n  const report = auditSkills();\n  if (opts.json) return printJson({ ...report, top: topByTokens(report, top) });\n  printSkills(report, top);\n}\n\n// ---------------------------------------------------------------------------\n// ladder\n// ---------------------------------------------------------------------------\n\nfunction printLadder(report: LadderReport): void {\n  console.log(header(`Context ladder (model: ${report.model}, mcp config: ${report.mcpConfigPath})`));\n  const rows = report.readings.map((r) => [\n    r.id,\n    r.description,\n    r.timedOut ? \"TIMEOUT\" : r.firstTurnContext === null ? `n/a${r.error ? ` (${r.error})` : \"\"}` : String(r.firstTurnContext),\n    r.delta === null ? \"n/a\" : String(r.delta),\n  ]);\n  console.log(renderTable([\"rung\", \"description\", \"first-turn tokens\", \"delta\"], rows));\n}\n\nasync function cmdLadder(opts: { json?: boolean; live?: boolean; model?: string; mcpConfig?: string }): Promise<void> {\n  if (!opts.live) {\n    console.log(\n      warn(\n        \"Refusing to run: `pai audit tokens ladder` spawns several real `claude -p` calls \" +\n          \"(billed API traffic). Pass --live to run it.\"\n      )\n    );\n    return;\n  }\n  const { workers } = readWorkersSection();\n  const logDir = workersLogDir(workers);\n  const report = await auditLadder({ model: opts.model, mcpConfigPath: opts.mcpConfig, logDir });\n  if (opts.json) return printJson(report);\n  printLadder(report);\n}\n\n// ---------------------------------------------------------------------------\n// subagents\n// ---------------------------------------------------------------------------\n\nfunction printSubagents(report: SubagentsReport): void {\n  console.log(header(`Subagent definitions (encoding: ${report.encoding})`));\n  if (report.entries.length === 0) {\n    console.log(dim(\"no agent files\"));\n    return;\n  }\n  const rows = report.entries.map((e) => [String(e.tokens), e.model === \"inherits\" ? dim(\"inherits\") : e.model, e.path]);\n  console.log(renderTable([\"tokens\", \"model\", \"path\"], rows));\n  const inheriting = report.entries.filter((e) => e.model === \"inherits\").length;\n  console.log(dim(`${inheriting} of ${report.entries.length} inherit the caller's model`));\n}\n\nfunction cmdSubagents(opts: { json?: boolean }): void {\n  const report = auditSubagents(homedir(), process.cwd());\n  if (opts.json) return printJson(report);\n  printSubagents(report);\n}\n\n// ---------------------------------------------------------------------------\n// mcp\n// ---------------------------------------------------------------------------\n\nfunction printMcp(report: McpReport): void {\n  console.log(header(\"MCP servers: configured vs. loaded vs. used\"));\n  const rows = report.servers.map((s) => [\n    s.server + (s.disabled ? dim(\" (disabled)\") : \"\"),\n    s.configuredIn.join(\", \") || dim(\"-\"),\n    s.pinned ?? dim(\"all\"),\n    s.loadedLive,\n    String(s.toolsExposed),\n    String(s.toolsUsed30d),\n    String(s.calls30d),\n  ]);\n  console.log(\n    renderTable(\n      [\"server\", \"configured-in\", \"pinned\", \"loaded-live\", \"tools-exposed\", \"tools-used-30d\", \"calls-30d\"],\n      rows\n    )\n  );\n  console.log(dim(`live claude processes: ${report.liveProcesses.length}`));\n}\n\nasync function cmdMcp(opts: { json?: boolean; connect?: boolean }): Promise<void> {\n  const report = await auditMcp({ cwd: process.cwd(), homeDir: homedir(), connect: opts.connect });\n  if (opts.json) return printJson(report);\n  printMcp(report);\n}\n\n// ---------------------------------------------------------------------------\n// combined\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_CTX_THRESHOLD = 200_000;\n\nexport interface CombinedData {\n  encoding: string;\n  findings: Finding[];\n  files: FilesReport;\n  hooks: HooksReport;\n  session: SessionReportOutput | null;\n  spawn: SpawnReport;\n  daemon: DaemonReport;\n  env: EnvReport;\n  schedule: Awaited<ReturnType<typeof auditSchedule>>;\n  skills: SkillsReport;\n  subagents: SubagentsReport;\n  mcp: McpReport;\n}\n\nasync function gatherCombined(): Promise<CombinedData> {\n  const cwd = process.cwd();\n  const files = auditFiles(defaultFileSet(cwd));\n  const hooks = auditHooks(cwd);\n  // No threshold override here: auditSession derives window+trigger from the\n  // session's own transcript (see context-trigger.ts) instead of rating\n  // every session against a fixed 200k — DEFAULT_CTX_THRESHOLD is only the\n  // last-resort fallback when a session has no transcript to derive from.\n  const session = await auditSession(newestSessionLog() ?? undefined).catch(() => null);\n  const daemon = auditDaemon();\n  const env = auditEnv();\n  const spawn = await auditSpawn();\n  const ttl = cacheTtlSeconds(session?.cacheCreationSplit);\n  const schedule = auditSchedule(ttl);\n  const skills = auditSkills();\n  const subagents = auditSubagents(homedir(), cwd);\n  const mcp = await auditMcp({ cwd, homeDir: homedir() });\n\n  const ctxThreshold = session?.trigger ?? DEFAULT_CTX_THRESHOLD;\n  const findings = buildFindings({ files, hooks, session, daemon, env, skills, subagents, mcp, ctxThreshold });\n\n  return { encoding: TOKEN_ENCODING, findings, files, hooks, session, spawn, daemon, env, schedule, skills, subagents, mcp };\n}\n\n/** Run `fn`, capturing everything it sends through `console.log` as plain text. */\nfunction captureOutput(fn: () => void): string {\n  const original = console.log;\n  const lines: string[] = [];\n  console.log = (...args: unknown[]) => {\n    lines.push(stripAnsi(args.map((a) => String(a)).join(\" \")));\n  };\n  try {\n    fn();\n  } finally {\n    console.log = original;\n  }\n  return lines.join(\"\\n\");\n}\n\nfunction combinedSectionOutputs(data: CombinedData): { name: string; text: string }[] {\n  const sections: { name: string; text: string }[] = [];\n  sections.push({ name: \"files\", text: captureOutput(() => printFiles(data.files)) });\n  sections.push({ name: \"hooks\", text: captureOutput(() => printHooks(data.hooks)) });\n  if (data.session) sections.push({ name: \"session\", text: captureOutput(() => printSession(data.session!)) });\n  sections.push({ name: \"spawn\", text: captureOutput(() => printSpawn(data.spawn)) });\n  sections.push({ name: \"daemon\", text: captureOutput(() => printDaemon(data.daemon)) });\n  sections.push({ name: \"env\", text: captureOutput(() => printEnv(data.env)) });\n  sections.push({ name: \"schedule\", text: captureOutput(() => printSchedule(data.schedule)) });\n  sections.push({ name: \"skills\", text: captureOutput(() => printSkills(data.skills, 15)) });\n  sections.push({ name: \"subagents\", text: captureOutput(() => printSubagents(data.subagents)) });\n  sections.push({ name: \"mcp\", text: captureOutput(() => printMcp(data.mcp)) });\n  return sections;\n}\n\n/**\n * Replace the home directory with `~` in both its plain form and the encoded\n * form Claude Code uses for transcript directories (`/Users/name/x` →\n * `-Users-name-x`), so a recorded run carries no account name.\n */\nexport function redactHome(text: string, home = homedir()): string {\n  const encoded = home.replace(/[\\/\\\\]/g, \"-\");\n  return text.split(home).join(\"~\").split(encoded).join(\"~\");\n}\n\n/** `--record <dir>`: write a dated, numbered markdown+JSON snapshot of the combined report, plus one summary line in `runs.md`. */\nexport function recordCombinedReport(dir: string, data: CombinedData): void {\n  mkdirSync(dir, { recursive: true });\n\n  // Next number is max(existing)+1, not count+1: a directory holding only\n  // run4 (earlier runs recorded elsewhere) must produce run5, not run2.\n  const existingNumbers = existsSync(dir)\n    ? readdirSync(dir).map((f) => /-run(\\d+)\\.md$/.exec(f)?.[1]).filter((n): n is string => n !== undefined).map(Number)\n    : [];\n  const runNumber = (existingNumbers.length ? Math.max(...existingNumbers) : 0) + 1;\n  const date = new Date().toISOString().slice(0, 10);\n  const baseName = `${date}-run${runNumber}`;\n\n  const findingsTable = [\n    \"| FINDING | SEVERITY | EVIDENCE |\",\n    \"|---------|----------|----------|\",\n    ...data.findings.map((f) => `| ${f.finding} | ${f.severity} | ${f.evidence} |`),\n  ].join(\"\\n\");\n\n  const sections = combinedSectionOutputs(data);\n  const sectionBlocks = sections.map((s) => `### ${s.name}\\n\\n\\`\\`\\`\\n${s.text}\\n\\`\\`\\`\\n`).join(\"\\n\");\n\n  const md = [\n    \"# pai audit tokens — recorded run\",\n    \"\",\n    `date: ${date}`,\n    `encoding: ${data.encoding}`,\n    \"\",\n    findingsTable,\n    \"\",\n    sectionBlocks,\n  ].join(\"\\n\");\n\n  const json = JSON.stringify(data, null, 2);\n\n  const mdRedacted = redactHome(md);\n  const jsonRedacted = redactHome(json);\n\n  writeFileSync(join(dir, `${baseName}.md`), mdRedacted, \"utf8\");\n  writeFileSync(join(dir, `${baseName}.json`), jsonRedacted, \"utf8\");\n\n  const runsPath = join(dir, \"runs.md\");\n  if (!existsSync(runsPath)) {\n    writeFileSync(runsPath, \"# Audit token-waste runs\\n\\n\", \"utf8\");\n  }\n  const counts = { RED: 0, AMBER: 0, GREEN: 0 } as Record<Severity, number>;\n  for (const f of data.findings) counts[f.severity]++;\n  appendFileSync(\n    runsPath,\n    `- ${date} run${runNumber}: ${counts.RED} RED, ${counts.AMBER} AMBER, ${counts.GREEN} GREEN — ${baseName}.md\\n`,\n    \"utf8\"\n  );\n}\n\nasync function cmdCombined(opts: { json?: boolean; record?: string }): Promise<void> {\n  const data = await gatherCombined();\n\n  if (opts.json) {\n    printJson(data);\n  } else {\n    console.log(header(\"pai audit tokens — combined report\"));\n    console.log(dim(`token encoding: ${data.encoding}`));\n    const rows = data.findings.map((f) => [severityColor(f.severity, f.finding), severityColor(f.severity, f.severity), f.evidence]);\n    console.log(renderTable([\"FINDING\", \"SEVERITY\", \"EVIDENCE\"], rows));\n  }\n\n  if (opts.record) {\n    recordCombinedReport(opts.record, data);\n  }\n}\n\n// ---------------------------------------------------------------------------\n// wiring\n// ---------------------------------------------------------------------------\n\n/**\n * `--json` is declared once, on `tokensCmd` itself, and read back from there\n * in every subcommand action below — not redeclared per subcommand. The parser\n * resolved an option to whichever command in the chain first declares it, so\n * a child command that *also* declares `--json` never sees the flag: it gets\n * consumed by the parent before the child's own `opts()` is populated, and\n * `--json` on `pai audit tokens files` silently produced the table output.\n */\n\n// ---------------------------------------------------------------------------\n// Exported command functions\n// ---------------------------------------------------------------------------\n\n/** Exported function for audit tokens files command */\nexport async function auditFilesCmd(paths: string[], opts: { json?: boolean }): Promise<number> {\n  const targets = paths.length > 0 ? paths : defaultFileSet(process.cwd());\n  const report = auditFiles(targets);\n  if (opts.json) {\n    printJson(report);\n    return 0;\n  }\n  printFiles(report);\n  return 0;\n}\n\n/** Exported function for audit tokens hooks command */\nexport function auditHooksCmd(opts: { json?: boolean }): number {\n  const report = auditHooks(process.cwd());\n  if (opts.json) {\n    printJson(report);\n    return 0;\n  }\n  printHooks(report);\n  return 0;\n}\n\n/** Exported function for audit tokens session command */\nexport async function auditSessionCmd(\n  path: string | undefined,\n  opts: { json?: boolean; ctxThreshold?: string; history?: string | boolean; turn?: string }\n): Promise<number> {\n  if (opts.history !== undefined) {\n    const limit = typeof opts.history === \"string\" ? parseInt(opts.history, 10) : 20;\n    const rows = await sessionHistory(process.cwd(), limit);\n    if (opts.json) {\n      printJson(rows);\n      return 0;\n    }\n    printSessionHistory(rows);\n    return 0;\n  }\n  if (opts.turn !== undefined) {\n    const target = path ?? newestSessionLog();\n    if (!target) {\n      console.error(err(\"  No session transcript found.\"));\n      return 1;\n    }\n    const tb = turnBreakdown(target, parseInt(opts.turn, 10));\n    if (!tb) {\n      console.error(err(`  Turn ${opts.turn} not found in ${target}.`));\n      return 1;\n    }\n    if (opts.json) {\n      printJson(tb);\n      return 0;\n    }\n    printTurnBreakdown(tb);\n    return 0;\n  }\n  const threshold = opts.ctxThreshold ? parseInt(opts.ctxThreshold, 10) : undefined;\n  const report = await auditSession(path, threshold);\n  if (!report) {\n    console.error(err(\"  No session transcript found.\"));\n    return 1;\n  }\n  if (opts.json) {\n    printJson(report);\n    return 0;\n  }\n  printSession(report);\n  return 0;\n}\n\n/** Exported function for audit tokens spawn command */\nexport async function auditSpawnCmd(opts: { json?: boolean; n?: string; detail?: boolean }): Promise<number> {\n  const n = opts.n ? parseInt(opts.n, 10) : 10;\n  const report = await auditSpawn(n);\n  if (opts.json) {\n    printJson(report);\n    return 0;\n  }\n  printSpawn(report, opts.detail);\n  return 0;\n}\n\n/** Exported function for audit tokens daemon command */\nexport function auditDaemonCmd(opts: { json?: boolean }): number {\n  const report = auditDaemon();\n  if (opts.json) {\n    printJson(report);\n    return 0;\n  }\n  printDaemon(report);\n  return 0;\n}\n\n/** Exported function for audit tokens env command */\nexport function auditEnvCmd(opts: { json?: boolean }): number {\n  const report = auditEnv();\n  if (opts.json) {\n    printJson(report);\n    return 0;\n  }\n  printEnv(report);\n  return 0;\n}\n\n/** Exported function for audit tokens schedule command */\nexport async function auditScheduleCmd(opts: { json?: boolean }): Promise<number> {\n  const ttl = await scheduleTtlSeconds();\n  const report = auditSchedule(ttl);\n  if (opts.json) {\n    printJson(report);\n    return 0;\n  }\n  printSchedule(report);\n  return 0;\n}\n\n/** Exported function for audit tokens skills command */\nexport function auditSkillsCmd(opts: { json?: boolean; top?: string }): number {\n  const top = opts.top ? parseInt(opts.top, 10) : 15;\n  const report = auditSkills();\n  if (opts.json) {\n    printJson({ ...report, top: topByTokens(report, top) });\n    return 0;\n  }\n  printSkills(report, top);\n  return 0;\n}\n\n/** Exported function for audit tokens subagents command */\nexport function auditSubagentsCmd(opts: { json?: boolean }): number {\n  const report = auditSubagents(homedir(), process.cwd());\n  if (opts.json) {\n    printJson(report);\n    return 0;\n  }\n  printSubagents(report);\n  return 0;\n}\n\n/** Exported function for audit tokens mcp command */\nexport async function auditMcpCmd(opts: { json?: boolean; connect?: boolean }): Promise<number> {\n  const report = await auditMcp({ cwd: process.cwd(), homeDir: homedir(), connect: opts.connect });\n  if (opts.json) {\n    printJson(report);\n    return 0;\n  }\n  printMcp(report);\n  return 0;\n}\n\n/** Exported function for audit tokens ladder command */\nexport async function auditLadderCmd(opts: { json?: boolean; live?: boolean; model?: string; mcpConfig?: string }): Promise<number> {\n  if (!opts.live) {\n    console.log(\n      warn(\n        \"Refusing to run: `pai audit tokens ladder` spawns several real `claude -p` calls \" +\n        \"(billed API traffic). Pass --live to run it.\"\n      )\n    );\n    return 0;\n  }\n  const { workers } = readWorkersSection();\n  const logDir = workersLogDir(workers);\n  const report = await auditLadder({ model: opts.model, mcpConfigPath: opts.mcpConfig, logDir });\n  if (opts.json) {\n    printJson(report);\n    return 0;\n  }\n  printLadder(report);\n  return 0;\n}\n\n/** Exported function for audit tokens command (combined) */\nexport async function auditTokensCmd(opts: { json?: boolean; record?: string }): Promise<number> {\n  const data = await gatherCombined();\n\n  if (opts.json) {\n    printJson(data);\n  } else {\n    console.log(header(\"pai audit tokens — combined report\"));\n    console.log(dim(`token encoding: ${data.encoding}`));\n    const rows = data.findings.map((f) => [severityColor(f.severity, f.finding), severityColor(f.severity, f.severity), f.evidence]);\n    console.log(renderTable([\"FINDING\", \"SEVERITY\", \"EVIDENCE\"], rows));\n  }\n\n  if (opts.record) {\n    recordCombinedReport(opts.record, data);\n  }\n  return 0;\n}\n","/** `pai audit ...`: where tokens (and money) go. */\n\nimport { OPT, type ArgDef, type CommandDef, type OptDef, type RunContext } from \"../define.js\";\nimport { auditFilesCmd, auditHooksCmd, auditSessionCmd, auditSpawnCmd, auditDaemonCmd, auditEnvCmd, auditScheduleCmd, auditSkillsCmd, auditSubagentsCmd, auditMcpCmd, auditLadderCmd, auditTokensCmd } from \"../lib/audit.js\";\nimport { priceTableText, renderUsage, transcriptUsage, usageJson } from \"../../workers/usage.js\";\n\nconst json: OptDef = { ...OPT.json, summary: \"Print JSON instead of a table\" };\nconst a = (def: Omit<CommandDef, \"run\">, run: (ctx: RunContext) => Promise<number> | number) =>\n  // `--json` is read from the `tokens` node by the implementation; it is added to every report command here.\n  ({ ...def, options: [...(def.options ?? []), json], run });\n\nconst t = (name: string, summary: string, description: string, extra: Partial<CommandDef> = {}) =>\n  a({ name, summary, description, examples: [`pai audit tokens ${name}`, `pai audit tokens ${name} --json`], seeAlso: [\"pai audit tokens\"], ...extra }, (ctx) => {\n    if (name === \"files\") return auditFilesCmd(ctx.args.PATHS as string[] | undefined ?? [], { json: ctx.opts.json === true });\n    if (name === \"hooks\") return auditHooksCmd({ json: ctx.opts.json === true });\n    if (name === \"session\") return auditSessionCmd(ctx.args.PATH as string | undefined, { json: ctx.opts.json === true, ctxThreshold: ctx.opts[\"ctx-threshold\"] as string | undefined, history: ctx.opts.history as string | boolean | undefined, turn: ctx.opts.turn as string | undefined });\n    if (name === \"spawn\") return auditSpawnCmd({ json: ctx.opts.json === true, n: ctx.opts.n as string | undefined, detail: ctx.opts.detail === true });\n    if (name === \"daemon\") return auditDaemonCmd({ json: ctx.opts.json === true });\n    if (name === \"env\") return auditEnvCmd({ json: ctx.opts.json === true });\n    if (name === \"schedule\") return auditScheduleCmd({ json: ctx.opts.json === true });\n    if (name === \"skills\") return auditSkillsCmd({ json: ctx.opts.json === true, top: ctx.opts.top as string | undefined });\n    if (name === \"subagents\") return auditSubagentsCmd({ json: ctx.opts.json === true });\n    if (name === \"mcp\") return auditMcpCmd({ json: ctx.opts.json === true, connect: ctx.opts.connect === true });\n    if (name === \"ladder\") return auditLadderCmd({ json: ctx.opts.json === true, live: ctx.opts.live === true, model: ctx.opts.model as string | undefined, mcpConfig: ctx.opts[\"mcp-config\"] as string | undefined });\n    return 0;\n  });\n\nexport const auditCommand: CommandDef = {\n  name: \"audit\",\n  summary: \"Find out where tokens are being spent\",\n  description: \"Read-only reports on what your setup costs in tokens: memory files, hooks, sessions, helper sessions, background service, skills, MCP servers.\",\n  examples: [\"pai audit tokens\", \"pai audit tokens session\", \"pai audit tokens mcp\"],\n  seeAlso: [\"pai skill telemetry\"],\n  subcommands: [\n    {\n      ...a({\n        name: \"tokens\",\n        summary: \"Token-waste report (all checks, or pick one below)\",\n        description:\n          \"Runs every check and prints one combined report with findings ranked by severity. Run one check on its own with a sub-command: \" +\n          \"files, hooks, session, spawn, daemon, env, schedule, skills, subagents, mcp, ladder.\",\n        options: [{ name: \"record\", type: \"string\", value: \"DIR\", summary: \"Also write a dated, numbered Markdown and JSON snapshot into DIR\" }],\n        examples: [\"pai audit tokens\", \"pai audit tokens --json\", \"pai audit tokens --record ./audits\"],\n        seeAlso: [\"pai audit tokens mcp\"],\n      }, (ctx) => auditTokensCmd({ json: ctx.opts.json === true, record: ctx.opts.record as string | undefined })),\n      subcommands: [\n        t(\"files\", \"Token count per memory file\", \"Counts tokens of the CLAUDE.md chain, core skill, rule files and auto-memory, or of the files you name.\", {\n          args: [{ name: \"PATHS\", variadic: true, summary: \"Files to count (default: the standard memory set)\" } as ArgDef],\n          examples: [\"pai audit tokens files\", \"pai audit tokens files ./CLAUDE.md --json\"],\n        }),\n        t(\"hooks\", \"Token cost of every start-up and prompt hook\", \"Measures what each SessionStart and UserPromptSubmit hook adds to the context.\"),\n        t(\"session\", \"Token split for one session transcript\", \"Shows cache, input and output tokens of a session (default: the newest).\", {\n          args: [{ name: \"PATH\", summary: \"Session transcript file (default: newest under ~/.claude/projects)\" }],\n          options: [\n            { name: \"ctx-threshold\", type: \"number\", value: \"N\", summary: \"Override the compaction trigger (default: derived from the session itself)\" },\n            { name: \"history\", type: \"string\", value: \"N\", summary: \"List the first-turn context of the newest N sessions of this project (try 20)\", optionalValue: true },\n            { name: \"turn\", type: \"number\", value: \"N\", summary: \"Explain where API call N's context growth came from\" },\n          ],\n          examples: [\"pai audit tokens session\", \"pai audit tokens session --history 20\", \"pai audit tokens session --turn 12\"],\n        }),\n        t(\"spawn\", \"Helper-session overhead: subagents vs workers\", \"Compares the start-up cost of in-session subagents with pai workers.\", {\n          options: [\n            { name: \"n\", short: \"n\", type: \"number\", value: \"COUNT\", summary: \"How many of the newest logs per group to read (default 10)\" },\n            { name: \"detail\", type: \"boolean\", summary: \"One row per log\" },\n          ],\n          examples: [\"pai audit tokens spawn\", \"pai audit tokens spawn -n 20 --detail\"],\n        }),\n        t(\"daemon\", \"What the background service spent\", \"Counts model calls, extraction parse failures and queue sizes from the daemon log.\"),\n        t(\"env\", \"Model and endpoint overrides on running sessions\", \"Lists ANTHROPIC_BASE_URL and model overrides of every live claude process.\"),\n        t(\"schedule\", \"Scheduled jobs that beat the cache lifetime\", \"Finds launchd and cron entries that run more often than the prompt cache lives.\"),\n        t(\"skills\", \"Token cost of the skill and command catalogue\", \"Sizes every SKILL.md, command and plugin, lists the top entries and duplicate names.\", {\n          options: [{ name: \"top\", type: \"number\", value: \"N\", summary: \"How many top entries to show (default 15)\" }],\n          examples: [\"pai audit tokens skills\", \"pai audit tokens skills --top 5\"],\n        }),\n        t(\"subagents\", \"Token cost of subagent definitions\", \"Sizes each Claude Code subagent definition and shows how its model is pinned.\"),\n        t(\"mcp\", \"MCP servers: configured, pinned, loaded, used\", \"Compares what is configured, what is pinned, what a live session loaded and what was used in the last 30 days.\", {\n          options: [{ name: \"connect\", type: \"boolean\", summary: \"Actually connect to every stdio server to count its tools (has side effects)\" }],\n          examples: [\"pai audit tokens mcp\", \"pai audit tokens mcp --connect\"],\n        }),\n        a({\n          name: \"usage\",\n          summary: \"Exact token totals per model for one session transcript\",\n          description:\n            \"Sums a Claude Code session transcript's assistant usage (each message id once, its final usage) per model. This is the tier-0 side of the A/B comparison: the top orchestrator session; its workers are `pai worker usage`.\\n\\n\" +\n            priceTableText(),\n          args: [{ name: \"TRANSCRIPT\", required: true, summary: \"Session transcript .jsonl\" }],\n          examples: [\"pai audit tokens usage ~/.claude/projects/-my-proj/SESSION.jsonl\", \"pai audit tokens usage ./session.jsonl --json\"],\n          seeAlso: [\"pai worker usage\", \"pai audit tokens session\"],\n        }, (ctx) => {\n          const file = ctx.args.TRANSCRIPT as string;\n          const rep = transcriptUsage(file);\n          console.log(ctx.opts.json === true ? JSON.stringify(usageJson(rep), null, 2) : renderUsage(rep, { workers: false }));\n          return 0;\n        }),\n        t(\"ladder\", \"LIVE: first-turn context per configuration step\", \"Starts real headless claude calls at growing configurations and measures the first-turn context. Refuses without --live because it costs money.\", {\n          options: [\n            { name: \"live\", type: \"boolean\", summary: \"Really start claude -p (billed); required\" },\n            { ...OPT.model, summary: \"Model to probe with (default haiku)\" },\n            { name: \"mcp-config\", type: \"string\", value: \"PATH\", summary: \"MCP config for the third step (default: newest *.mcp.json in the workers log folder)\" },\n          ],\n          examples: [\"pai audit tokens ladder\", \"pai audit tokens ladder --live --model haiku\"],\n        }),\n      ],\n    },\n  ],\n};\n","/** The command groups, in the order the help shows them. One file per group lives in ./commands. */\n\nimport type { CommandDef, GroupDef } from \"./define.js\";\nimport { docCommand, helpCommand } from \"./commands/meta.js\";\nimport { workerCommand } from \"./commands/worker.js\";\nimport { providersCommand } from \"./commands/providers.js\";\nimport { PROVIDER_EDIT_COMMANDS, WORKER_CONFIG_COMMANDS } from \"./commands/worker-config.js\";\nimport { routerCommand } from \"./commands/router.js\";\nimport { moreCommand, setupsCommand } from \"./commands/setups.js\";\nimport { SESSION_NOTE_COMMANDS, sessionsCommand } from \"./commands/session-notes.js\";\nimport { backupTop, cdTop, clearNamesCmd, endTop, findTop, launchTop, listTop, notesTop, pauseTop, restoreTop, resumeTop, shellInitTop, updateTop } from \"./commands/lifecycle.js\";\nimport { hooksDbCommand } from \"./commands/hooks-db.js\";\nimport { installAlias, setupCommand } from \"./commands/setup.js\";\nimport { lsCommand, newTop, openTop, renameTop, sessionCommand } from \"./commands/session.js\";\nimport { BATCH_C } from \"./commands/batch-c.js\";\nimport { memoryCommand } from \"./commands/memory.js\";\nimport { kgCommand } from \"./commands/kg.js\";\nimport { zettelCommand } from \"./commands/zettel.js\";\nimport { observationCommand } from \"./commands/observation.js\";\nimport { obsidianCommand } from \"./commands/obsidian.js\";\nimport { skillCommand } from \"./commands/skill.js\";\nimport { auditCommand } from \"./commands/audit.js\";\n\nconst sessionGroup: CommandDef = {\n  ...sessionCommand,\n  subcommands: [...(sessionCommand.subcommands ?? []), ...SESSION_NOTE_COMMANDS],\n};\n\nconst workerGroup: CommandDef = {\n  ...workerCommand,\n  summary: \"Run tasks on other providers: run, ps, follow, merge, providers, classes, config, ...\",\n  description: workerCommand.description + \"\\nSetup lives here too: providers, classes, config, policy, local, fallback, pool, proxy, mcp, on, off, install.\",\n  examples: [...(workerCommand.examples ?? []), \"pai worker providers\", \"pai worker policy\"],\n  subcommands: [...(workerCommand.subcommands ?? []), ...WORKER_CONFIG_COMMANDS],\n};\n\n// `pai providers add|remove|...` are the same command objects as `pai worker providers ...`.\nconst providersTop: CommandDef = {\n  ...providersCommand,\n  subcommands: [...(providersCommand.subcommands ?? []), ...PROVIDER_EDIT_COMMANDS],\n};\n\nconst titles = {\n  workers: \"Workers\",\n  memory: \"Memory & projects\",\n  daemon: \"Daemon & setup\",\n} as const;\n\nconst BATCH_B_EXTRA: Partial<Record<string, CommandDef[]>> = {\n  memory: [findTop, notesTop, listTop, cdTop],\n  daemon: [setupCommand, installAlias, updateTop, hooksDbCommand, backupTop, restoreTop, shellInitTop, launchTop],\n};\n\nexport const GROUPS: GroupDef[] = [\n  { id: \"sessions\", title: \"Sessions\", daily: true, commands: [lsCommand, setupsCommand, openTop, newTop, renameTop, sessionGroup, pauseTop, endTop, resumeTop, clearNamesCmd, sessionsCommand] },\n  { id: \"providers\", title: \"Providers\", daily: true, commands: [providersTop] },\n  { id: \"help\", title: \"Help\", daily: true, commands: [moreCommand, helpCommand, docCommand] },\n  ...([\"workers\", \"memory\", \"daemon\"] as const).map((id) => ({\n    id,\n    title: titles[id],\n    daily: false,\n    commands: [...(id === \"workers\" ? [workerGroup, routerCommand] : []), ...(BATCH_B_EXTRA[id] ?? []), ...(BATCH_C[id] ?? [])],\n  })),\n];\n\n// Memory, knowledge and audit commands (one file per group in ./commands).\nGROUPS.find((g) => g.id === \"memory\")!.commands.push(memoryCommand, kgCommand, zettelCommand, observationCommand, obsidianCommand, skillCommand);\nGROUPS.find((g) => g.id === \"daemon\")!.commands.push(auditCommand);\n","#!/usr/bin/env node\n/**\n * pai — entry point.\n *\n * The entry settles on every path: it awaits one promise (runCli), turns\n * Ctrl-C into a quiet exit 130 with the cursor restored, and ends with an\n * explicit process.exit. A command left pending when a prompt is cancelled would let the event loop\n * empty and Node print \"unsettled top-level await\".\n */\n\nimport { dropItermBundleId } from \"../utils/env.js\";\nimport { drainStdio } from \"./lib/exit.js\";\nimport { rootCommand } from \"./commands/root.js\";\nimport { GROUPS } from \"./registry.js\";\nimport { defaultIo, runCli } from \"./run.js\";\n\ndropItermBundleId();\n\nconst SHOW_CURSOR = \"\\u001b[?25h\";\nconst quit = (code: number): never => {\n  if (process.stdout.isTTY) process.stdout.write(SHOW_CURSOR);\n  process.exit(code);\n};\n// `pai worker ...` installs its own handlers (a run writes its final status on SIGTERM/SIGINT before exiting).\nif (process.argv[2] !== \"worker\") {\n  process.on(\"SIGINT\", () => quit(130));\n  process.on(\"SIGTERM\", () => quit(143));\n}\n\nlet code = 1;\ntry {\n  code = await runCli(GROUPS, process.argv.slice(2), defaultIo(), rootCommand);\n} catch (e) {\n  process.stderr.write(`pai: unexpected failure: ${e instanceof Error ? e.message : String(e)}\\nNext: re-run with PAI_DEBUG=1 for details.\\n`);\n  if (process.env.PAI_DEBUG && e instanceof Error) process.stderr.write((e.stack ?? \"\") + \"\\n\");\n}\nawait drainStdio();\nquit(code);\n"],"mappings":"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"}