@tekmidian/pai 0.12.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/cli/index.mjs +2 -2
  2. package/dist/cli/program.d.mts.map +1 -1
  3. package/dist/cli/program.mjs +3 -2
  4. package/dist/cli/program.mjs.map +1 -1
  5. package/dist/{config-BuhHWyOK.mjs → config-C8m-tPhP.mjs} +22 -2
  6. package/dist/config-C8m-tPhP.mjs.map +1 -0
  7. package/dist/daemon/index.mjs +3 -3
  8. package/dist/{daemon-BldK8-57.mjs → daemon-D2aKb4mJ.mjs} +5 -5
  9. package/dist/{daemon-BldK8-57.mjs.map → daemon-D2aKb4mJ.mjs.map} +1 -1
  10. package/dist/daemon-mcp/index.mjs +581 -2
  11. package/dist/daemon-mcp/index.mjs.map +1 -1
  12. package/dist/{pick-7Kg9DX7S.mjs → pick-CS_TmOqX.mjs} +1105 -452
  13. package/dist/pick-CS_TmOqX.mjs.map +1 -0
  14. package/dist/skills/Tasks/SKILL.md +101 -0
  15. package/dist/{work-queue-worker-DHOIVVQ7.mjs → work-queue-worker-0tUMEhSS.mjs} +16 -1
  16. package/dist/{work-queue-worker-DHOIVVQ7.mjs.map → work-queue-worker-0tUMEhSS.mjs.map} +1 -1
  17. package/docs/commands/README.md +6 -0
  18. package/docs/commands/backup.md +1 -1
  19. package/docs/commands/clear-names.md +1 -1
  20. package/docs/commands/daemon.md +1 -1
  21. package/docs/commands/db.md +1 -1
  22. package/docs/commands/end.md +1 -1
  23. package/docs/commands/help.md +1 -1
  24. package/docs/commands/kg.md +1 -1
  25. package/docs/commands/mcp.md +1 -1
  26. package/docs/commands/memory.md +1 -1
  27. package/docs/commands/notify.md +1 -1
  28. package/docs/commands/observation.md +1 -1
  29. package/docs/commands/obsidian.md +1 -1
  30. package/docs/commands/pause.md +1 -1
  31. package/docs/commands/project.md +2 -1
  32. package/docs/commands/projects.md +2 -1
  33. package/docs/commands/registry.md +1 -1
  34. package/docs/commands/restore.md +1 -1
  35. package/docs/commands/sessions.md +1 -1
  36. package/docs/commands/setup.md +1 -1
  37. package/docs/commands/shell-init.md +1 -1
  38. package/docs/commands/skill.md +1 -1
  39. package/docs/commands/task.md +90 -0
  40. package/docs/commands/topic.md +1 -1
  41. package/docs/commands/update.md +1 -1
  42. package/docs/commands/zettel.md +1 -1
  43. package/package.json +1 -1
  44. package/plugins/productivity/plugin.json +2 -2
  45. package/plugins/productivity/skills/Tasks/SKILL.md +126 -0
  46. package/dist/config-BuhHWyOK.mjs.map +0 -1
  47. package/dist/pick-7Kg9DX7S.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { a as openRegistry, i as indexProject, n as indexAll, t as embedChunks } from "./sync-uPR4g438.mjs";
2
- import { _ as warn, a as fmtDate, c as ok, d as scaffoldProjectDirs, f as shortenPath, i as err, l as renderTable, m as slugify, n as dim, o as header, p as slugFromPath, r as encodeDir, s as now, t as bold, u as resolvePath } from "./utils-CqqgB0dH.mjs";
2
+ import { _ as warn, a as fmtDate, c as ok, d as scaffoldProjectDirs, f as shortenPath, i as err, l as renderTable, m as slugify, n as dim$1, o as header, p as slugFromPath, r as encodeDir, s as now, t as bold$1, u as resolvePath } from "./utils-CqqgB0dH.mjs";
3
3
  import { a as slugify$1, i as parseSessionFilename } from "./migrate-B02wDDgS.mjs";
4
4
  import { n as ensurePaiMarker } from "./pai-marker-HVBwwBW-.mjs";
5
5
  import { n as openFederation } from "./db-CmYbAVCD.mjs";
@@ -8,7 +8,7 @@ import { n as formatDetection, r as formatDetectionJson, t as detectProject } fr
8
8
  import { a as resolveHome, c as upsertSession, n as cmdScan, o as saveScanConfig, r as loadScanConfig, s as upsertProject, t as extractAndStoreTriples } from "./kg-extraction-C8DEUHTS.mjs";
9
9
  import { t as createStorageBackend } from "./factory-Q88X1bAN.mjs";
10
10
  import { t as PaiClient } from "./ipc-client-CoyUHPod.mjs";
11
- import { a as expandHome, i as ensureConfigDir, n as CONFIG_FILE$2, o as loadConfig, t as CONFIG_DIR } from "./config-BuhHWyOK.mjs";
11
+ import { a as expandHome, c as UNROUTED, i as ensureConfigDir, n as CONFIG_FILE$2, o as loadConfig, s as OWNER_LABEL_PREFIX, t as CONFIG_DIR } from "./config-C8m-tPhP.mjs";
12
12
  import { s as kgQuery } from "./kg-entity-LXblD7LZ.mjs";
13
13
  import { a as renderDedupedSessions, c as revealItermSession, d as fmtAge, f as resolveSessionByNameOrId, i as normalizeName, l as sendToSession, o as callAiBroker, p as scanSessions, r as buildDeduped, s as fetchLiveSessions, u as printExitDir } from "./main-resolver-uFxNiDy7.mjs";
14
14
  import { appendFileSync, chmodSync, copyFileSync, createReadStream, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
@@ -16,7 +16,7 @@ import { homedir, platform } from "node:os";
16
16
  import { basename, dirname, join, relative, resolve } from "node:path";
17
17
  import chalk from "chalk";
18
18
  import { fileURLToPath } from "node:url";
19
- import { execSync, spawn, spawnSync } from "node:child_process";
19
+ import { execFile, execSync, spawn, spawnSync } from "node:child_process";
20
20
  import { createInterface } from "node:readline";
21
21
  import { createConnection } from "net";
22
22
 
@@ -105,17 +105,17 @@ function cmdPromote(db, opts) {
105
105
  const sourceTodoPath = join(sourceProjectRoot, "Notes", "TODO.md");
106
106
  if (existsSync(sourceTodoPath)) {
107
107
  appendFileSync(sourceTodoPath, `\n- Promoted to project: [${slug}](${targetPath})\n`, "utf-8");
108
- console.log(dim(` Backlink added to: ${sourceTodoPath}`));
108
+ console.log(dim$1(` Backlink added to: ${sourceTodoPath}`));
109
109
  }
110
110
  console.log();
111
- console.log(ok(`Project promoted: ${bold(slug)}`));
112
- console.log(dim(` Display name: ${displayName}`));
113
- console.log(dim(` Path: ${targetPath}`));
114
- console.log(dim(` Slug: ${slug}`));
115
- console.log(dim(` Session note: ${destNoteName}`));
116
- console.log(dim(` Memory: Notes/MEMORY.md created`));
111
+ console.log(ok(`Project promoted: ${bold$1(slug)}`));
112
+ console.log(dim$1(` Display name: ${displayName}`));
113
+ console.log(dim$1(` Path: ${targetPath}`));
114
+ console.log(dim$1(` Slug: ${slug}`));
115
+ console.log(dim$1(` Session note: ${destNoteName}`));
116
+ console.log(dim$1(` Memory: Notes/MEMORY.md created`));
117
117
  console.log();
118
- console.log(dim(` Next: cd ${targetPath} && pai session list ${slug}`));
118
+ console.log(dim$1(` Next: cd ${targetPath} && pai session list ${slug}`));
119
119
  }
120
120
 
121
121
  //#endregion
@@ -345,9 +345,9 @@ function cmdAdd(db, rawPath, opts) {
345
345
  const matches = db.prepare(`SELECT slug, root_path FROM projects WHERE status = 'active' AND slug != ?`).all(slug).filter((s) => basename(s.root_path).toLowerCase() === dirName || s.slug.replace(/-\d+$/, "") === slug.replace(/-\d+$/, ""));
346
346
  if (matches.length > 0) {
347
347
  console.log(warn(`Similar project(s) already registered:`));
348
- for (const m of matches) console.log(dim(` ${bold(m.slug)} ${shortenPath(m.root_path, 50)}`));
349
- console.log(dim(` Consider: pai project alias ${matches[0].slug} <name> (to link them)`));
350
- console.log(dim(` Or: pai project archive ${slug} (if this is a duplicate)`));
348
+ for (const m of matches) console.log(dim$1(` ${bold$1(m.slug)} ${shortenPath(m.root_path, 50)}`));
349
+ console.log(dim$1(` Consider: pai project alias ${matches[0].slug} <name> (to link them)`));
350
+ console.log(dim$1(` Or: pai project archive ${slug} (if this is a duplicate)`));
351
351
  console.log();
352
352
  }
353
353
  const ts = now();
@@ -358,10 +358,10 @@ function cmdAdd(db, rawPath, opts) {
358
358
  try {
359
359
  ensurePaiMarker(rootPath, slug, displayName);
360
360
  } catch {}
361
- console.log(ok(`Project added: ${bold(slug)}`));
362
- console.log(dim(` Path: ${rootPath}`));
363
- console.log(dim(` Encoded dir: ${encodedDir}`));
364
- console.log(dim(` Type: ${type}`));
361
+ console.log(ok(`Project added: ${bold$1(slug)}`));
362
+ console.log(dim$1(` Path: ${rootPath}`));
363
+ console.log(dim$1(` Encoded dir: ${encodedDir}`));
364
+ console.log(dim$1(` Type: ${type}`));
365
365
  }
366
366
  function cmdList$3(db, opts) {
367
367
  let query = `
@@ -395,12 +395,12 @@ function cmdList$3(db, opts) {
395
395
  return;
396
396
  }
397
397
  const tableRows = rows.map((r, i) => [
398
- dim(String(i + 1)),
399
- bold(r.display_name ?? r.slug),
400
- dim(r.slug),
401
- dim(shortenPath(r.root_path, 44)),
398
+ dim$1(String(i + 1)),
399
+ bold$1(r.display_name ?? r.slug),
400
+ dim$1(r.slug),
401
+ dim$1(shortenPath(r.root_path, 44)),
402
402
  r.status === "active" ? chalk.green(r.status) : chalk.yellow(r.status),
403
- dim(r.type),
403
+ dim$1(r.type),
404
404
  String(r.session_count),
405
405
  fmtDate(r.last_active)
406
406
  ]);
@@ -416,8 +416,8 @@ function cmdList$3(db, opts) {
416
416
  ], tableRows));
417
417
  console.log();
418
418
  const hiddenCount = db.prepare("SELECT COUNT(*) AS cnt FROM projects").get().cnt - rows.length;
419
- if (!opts.all && !opts.status && hiddenCount > 0) console.log(dim(` ${rows.length} active project(s) (${hiddenCount} archived — use --all to show)`));
420
- else console.log(dim(` ${rows.length} project(s)`));
419
+ if (!opts.all && !opts.status && hiddenCount > 0) console.log(dim$1(` ${rows.length} active project(s) (${hiddenCount} archived — use --all to show)`));
420
+ else console.log(dim$1(` ${rows.length} project(s)`));
421
421
  }
422
422
  function cmdInfo(db, identifier) {
423
423
  const project = resolveIdentifier(db, identifier) ?? requireProject(db, identifier);
@@ -430,22 +430,22 @@ function cmdInfo(db, identifier) {
430
430
  console.log();
431
431
  console.log(header(` ${project.display_name}`));
432
432
  console.log();
433
- console.log(` ${bold("Slug:")} ${project.slug}`);
434
- console.log(` ${bold("Path:")} ${project.root_path}`);
435
- console.log(` ${bold("Encoded dir:")} ${project.encoded_dir}`);
436
- console.log(` ${bold("Type:")} ${project.type}`);
437
- console.log(` ${bold("Status:")} ${project.status === "active" ? chalk.green(project.status) : chalk.yellow(project.status)}`);
438
- console.log(` ${bold("Tags:")} ${tags.length ? tags.map((t) => chalk.cyan(t)).join(", ") : dim("none")}`);
439
- console.log(` ${bold("Aliases:")} ${aliases.length ? aliases.join(", ") : dim("none")}`);
440
- console.log(` ${bold("Sessions:")} ${sessionCount}`);
441
- console.log(` ${bold("Last active:")} ${fmtDate(lastSession)}`);
442
- console.log(` ${bold("Created:")} ${fmtDate(project.created_at)}`);
443
- if (project.archived_at) console.log(` ${bold("Archived:")} ${fmtDate(project.archived_at)}`);
433
+ console.log(` ${bold$1("Slug:")} ${project.slug}`);
434
+ console.log(` ${bold$1("Path:")} ${project.root_path}`);
435
+ console.log(` ${bold$1("Encoded dir:")} ${project.encoded_dir}`);
436
+ console.log(` ${bold$1("Type:")} ${project.type}`);
437
+ console.log(` ${bold$1("Status:")} ${project.status === "active" ? chalk.green(project.status) : chalk.yellow(project.status)}`);
438
+ console.log(` ${bold$1("Tags:")} ${tags.length ? tags.map((t) => chalk.cyan(t)).join(", ") : dim$1("none")}`);
439
+ console.log(` ${bold$1("Aliases:")} ${aliases.length ? aliases.join(", ") : dim$1("none")}`);
440
+ console.log(` ${bold$1("Sessions:")} ${sessionCount}`);
441
+ console.log(` ${bold$1("Last active:")} ${fmtDate(lastSession)}`);
442
+ console.log(` ${bold$1("Created:")} ${fmtDate(project.created_at)}`);
443
+ if (project.archived_at) console.log(` ${bold$1("Archived:")} ${fmtDate(project.archived_at)}`);
444
444
  if (recentSessions.length) {
445
445
  console.log();
446
- console.log(` ${bold("Recent sessions:")}`);
446
+ console.log(` ${bold$1("Recent sessions:")}`);
447
447
  const sessionRows = recentSessions.map((s) => [
448
- dim(`#${s.number}`),
448
+ dim$1(`#${s.number}`),
449
449
  s.date,
450
450
  s.title.length > 50 ? s.title.slice(0, 47) + "..." : s.title,
451
451
  s.status === "completed" ? chalk.green(s.status) : chalk.yellow(s.status)
@@ -467,7 +467,7 @@ function cmdArchive(db, slug) {
467
467
  }
468
468
  const ts = now();
469
469
  db.prepare("UPDATE projects SET status = 'archived', archived_at = ?, updated_at = ? WHERE id = ?").run(ts, ts, project.id);
470
- console.log(ok(`Archived: ${bold(slug)}`));
470
+ console.log(ok(`Archived: ${bold$1(slug)}`));
471
471
  }
472
472
  function cmdUnarchive(db, slug) {
473
473
  const project = requireProject(db, slug);
@@ -477,7 +477,7 @@ function cmdUnarchive(db, slug) {
477
477
  }
478
478
  const ts = now();
479
479
  db.prepare("UPDATE projects SET status = 'active', archived_at = NULL, updated_at = ? WHERE id = ?").run(ts, project.id);
480
- console.log(ok(`Unarchived: ${bold(slug)}`));
480
+ console.log(ok(`Unarchived: ${bold$1(slug)}`));
481
481
  }
482
482
  function cmdMove(db, slug, newPath) {
483
483
  const project = requireProject(db, slug);
@@ -485,9 +485,9 @@ function cmdMove(db, slug, newPath) {
485
485
  const newEncoded = encodeDir(resolvedNew);
486
486
  const ts = now();
487
487
  db.prepare("UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?").run(resolvedNew, newEncoded, ts, project.id);
488
- console.log(ok(`Moved: ${bold(slug)}`));
489
- console.log(dim(` Old path: ${project.root_path}`));
490
- console.log(dim(` New path: ${resolvedNew}`));
488
+ console.log(ok(`Moved: ${bold$1(slug)}`));
489
+ console.log(dim$1(` Old path: ${project.root_path}`));
490
+ console.log(dim$1(` New path: ${resolvedNew}`));
491
491
  }
492
492
  function cmdTag(db, slug, tags) {
493
493
  const project = requireProject(db, slug);
@@ -501,8 +501,8 @@ function cmdTag(db, slug, tags) {
501
501
  added.push(tagName);
502
502
  }
503
503
  }
504
- if (added.length) console.log(ok(`Tagged ${bold(slug)}: ${added.map((t) => chalk.cyan(t)).join(", ")}`));
505
- if (skipped.length) console.log(dim(` Already present: ${skipped.join(", ")}`));
504
+ if (added.length) console.log(ok(`Tagged ${bold$1(slug)}: ${added.map((t) => chalk.cyan(t)).join(", ")}`));
505
+ if (skipped.length) console.log(dim$1(` Already present: ${skipped.join(", ")}`));
506
506
  }
507
507
  function cmdAlias(db, slug, alias) {
508
508
  requireProject(db, slug);
@@ -513,7 +513,7 @@ function cmdAlias(db, slug, alias) {
513
513
  const project = getProject$1(db, slug);
514
514
  try {
515
515
  db.prepare("INSERT INTO aliases (alias, project_id) VALUES (?, ?)").run(alias, project.id);
516
- console.log(ok(`Alias added: ${bold(alias)} → ${slug}`));
516
+ console.log(ok(`Alias added: ${bold$1(alias)} → ${slug}`));
517
517
  } catch {
518
518
  console.error(err(`Alias "${alias}" is already registered.`));
519
519
  process.exit(1);
@@ -538,11 +538,11 @@ function cmdEdit(db, slug, opts) {
538
538
  const ts = now();
539
539
  if (opts.displayName) {
540
540
  db.prepare("UPDATE projects SET display_name = ?, updated_at = ? WHERE id = ?").run(opts.displayName, ts, project.id);
541
- console.log(ok(`Display name updated: ${bold(opts.displayName)}`));
541
+ console.log(ok(`Display name updated: ${bold$1(opts.displayName)}`));
542
542
  }
543
543
  if (opts.type) {
544
544
  db.prepare("UPDATE projects SET type = ?, updated_at = ? WHERE id = ?").run(opts.type, ts, project.id);
545
- console.log(ok(`Type updated: ${bold(opts.type)}`));
545
+ console.log(ok(`Type updated: ${bold$1(opts.type)}`));
546
546
  }
547
547
  }
548
548
  function cmdDetect(db, pathArg, opts) {
@@ -555,7 +555,7 @@ function cmdDetect(db, pathArg, opts) {
555
555
  }, null, 2));
556
556
  else {
557
557
  console.log(warn(`No registered project found for: ${cwd}`));
558
- console.log(dim(" Run 'pai project add .' to register this directory."));
558
+ console.log(dim$1(" Run 'pai project add .' to register this directory."));
559
559
  }
560
560
  process.exit(0);
561
561
  return;
@@ -585,14 +585,14 @@ function cmdConsolidate(db, identifier, opts) {
585
585
  const toMerge = dirs.filter((d) => d.notesPath !== canonicalNotes);
586
586
  if (toMerge.length === 0) {
587
587
  console.log(ok(" All notes are already in the canonical location."));
588
- console.log(dim(` ${canonicalNotes}`));
588
+ console.log(dim$1(` ${canonicalNotes}`));
589
589
  return;
590
590
  }
591
591
  console.log(` Found ${toMerge.length} scattered Notes directory(ies) to consolidate:`);
592
592
  console.log();
593
593
  for (const d of toMerge) {
594
- console.log(` ${bold(d.encodedDir)}`);
595
- console.log(dim(` Notes: ${d.notesPath} (${d.noteCount} file(s))`));
594
+ console.log(` ${bold$1(d.encodedDir)}`);
595
+ console.log(dim$1(` Notes: ${d.notesPath} (${d.noteCount} file(s))`));
596
596
  }
597
597
  console.log();
598
598
  console.log(` Destination: ${canonicalNotes}`);
@@ -633,23 +633,23 @@ function cmdConsolidate(db, identifier, opts) {
633
633
  * Prints all messages to stderr (safe for shell-wrapper stdout capture).
634
634
  */
635
635
  function tryRecoverMovedProject(db, project) {
636
- process.stderr.write(warn(`Path not found: ${project.root_path}\n`) + dim(" Searching for moved location...\n"));
636
+ process.stderr.write(warn(`Path not found: ${project.root_path}\n`) + dim$1(" Searching for moved location...\n"));
637
637
  const result = findMovedPath(project.root_path);
638
638
  if (result.found) {
639
639
  const newPath = result.found;
640
640
  const newEncoded = encodeDir(newPath);
641
641
  const ts = now();
642
642
  db.prepare("UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?").run(newPath, newEncoded, ts, project.id);
643
- process.stderr.write(ok(`Project moved: ${shortenPath(project.root_path, 50)}\n`) + dim(` → ${newPath}\n`) + ok("Registry updated.\n"));
643
+ process.stderr.write(ok(`Project moved: ${shortenPath(project.root_path, 50)}\n`) + dim$1(` → ${newPath}\n`) + ok("Registry updated.\n"));
644
644
  return newPath;
645
645
  }
646
646
  if (result.ambiguous) {
647
647
  process.stderr.write(warn(`Multiple directories named "${basename(project.root_path)}" found:\n`));
648
- for (const candidate of result.ambiguous) process.stderr.write(dim(` ${candidate}\n`));
649
- process.stderr.write(dim(`\n Disambiguate with: pai projects rebind ${project.slug} <path>\n`));
648
+ for (const candidate of result.ambiguous) process.stderr.write(dim$1(` ${candidate}\n`));
649
+ process.stderr.write(dim$1(`\n Disambiguate with: pai projects rebind ${project.slug} <path>\n`));
650
650
  return;
651
651
  }
652
- process.stderr.write(err(`Project "${project.slug}" root_path "${project.root_path}" does not exist on disk\n and no folder named "${basename(project.root_path)}" was found in scan dirs.\n`) + dim(` Fix with: pai projects rebind ${project.slug} <new-path>\n`));
652
+ process.stderr.write(err(`Project "${project.slug}" root_path "${project.root_path}" does not exist on disk\n and no folder named "${basename(project.root_path)}" was found in scan dirs.\n`) + dim$1(` Fix with: pai projects rebind ${project.slug} <new-path>\n`));
653
653
  }
654
654
  function cmdGo(db, query) {
655
655
  const all = db.prepare("SELECT * FROM projects WHERE status = 'active' ORDER BY updated_at DESC").all();
@@ -690,10 +690,10 @@ function cmdGo(db, query) {
690
690
  if (partial.length > 1) {
691
691
  console.error(err(`Ambiguous: "${query}" matches ${partial.length} projects:\n`));
692
692
  partial.forEach((p, i) => {
693
- console.error(` ${dim(String(i + 1).padStart(2))} ${bold(p.slug.padEnd(30))} ${dim(shortenPath(p.root_path, 50))}`);
693
+ console.error(` ${dim$1(String(i + 1).padStart(2))} ${bold$1(p.slug.padEnd(30))} ${dim$1(shortenPath(p.root_path, 50))}`);
694
694
  });
695
695
  console.error();
696
- console.error(dim(" Use a more specific name or the exact slug."));
696
+ console.error(dim$1(" Use a more specific name or the exact slug."));
697
697
  process.exit(1);
698
698
  }
699
699
  const scored = all.map((p) => {
@@ -709,9 +709,9 @@ function cmdGo(db, query) {
709
709
  console.error(err(`Project not found: "${query}"\n`));
710
710
  if (suggestions.length) {
711
711
  console.error(warn(" Did you mean?"));
712
- for (const s of suggestions) console.error(` ${bold(s.project.slug.padEnd(30))} ${dim(shortenPath(s.project.root_path, 50))}`);
712
+ for (const s of suggestions) console.error(` ${bold$1(s.project.slug.padEnd(30))} ${dim$1(shortenPath(s.project.root_path, 50))}`);
713
713
  console.error();
714
- console.error(dim(" Run: pai project list (to see all projects)"));
714
+ console.error(dim$1(" Run: pai project list (to see all projects)"));
715
715
  }
716
716
  process.exit(1);
717
717
  }
@@ -733,15 +733,15 @@ function cmdRebind(db, slug, newPath) {
733
733
  const newEncoded = encodeDir(resolved);
734
734
  const conflict = db.prepare("SELECT slug FROM projects WHERE encoded_dir = ? AND id != ?").get(newEncoded, project.id);
735
735
  if (conflict) {
736
- console.error(err(`Path is already registered to project: ${bold(conflict.slug)}\n`) + dim(` ${resolved}\n`) + dim(` Archive or move that project first, or choose a different path.`));
736
+ console.error(err(`Path is already registered to project: ${bold$1(conflict.slug)}\n`) + dim$1(` ${resolved}\n`) + dim$1(` Archive or move that project first, or choose a different path.`));
737
737
  process.exit(1);
738
738
  }
739
739
  const ts = now();
740
740
  db.prepare("UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?").run(resolved, newEncoded, ts, project.id);
741
- console.log(ok(`Rebound: ${bold(slug)}`));
742
- console.log(dim(` Old path: ${project.root_path}`));
743
- console.log(dim(` New path: ${resolved}`));
744
- console.log(dim(` Encoded: ${newEncoded}`));
741
+ console.log(ok(`Rebound: ${bold$1(slug)}`));
742
+ console.log(dim$1(` Old path: ${project.root_path}`));
743
+ console.log(dim$1(` New path: ${resolved}`));
744
+ console.log(dim$1(` Encoded: ${newEncoded}`));
745
745
  }
746
746
 
747
747
  //#endregion
@@ -884,8 +884,8 @@ function cmdName(db, identifier, shortname, opts) {
884
884
  const config = applyPreset(getSessionConfig(project), opts.permission);
885
885
  db.prepare("UPDATE projects SET session_config = ?, updated_at = ? WHERE id = ?").run(JSON.stringify(config), now(), project.id);
886
886
  }
887
- console.log(ok(`Named: ${bold(shortname)} → ${project.slug} (${shortenPath(project.root_path, 50)})`));
888
- if (opts.permission) console.log(dim(` Permission: ${opts.permission}`));
887
+ console.log(ok(`Named: ${bold$1(shortname)} → ${project.slug} (${shortenPath(project.root_path, 50)})`));
888
+ if (opts.permission) console.log(dim$1(` Permission: ${opts.permission}`));
889
889
  }
890
890
  function cmdUnname(db, shortname) {
891
891
  const alias = db.prepare("SELECT project_id FROM aliases WHERE alias = ?").get(shortname);
@@ -895,8 +895,8 @@ function cmdUnname(db, shortname) {
895
895
  }
896
896
  db.prepare("DELETE FROM aliases WHERE alias = ?").run(shortname);
897
897
  const remaining = db.prepare("SELECT COUNT(*) AS cnt FROM aliases WHERE project_id = ?").get(alias.project_id);
898
- console.log(ok(`Removed name: ${bold(shortname)}`));
899
- if (remaining.cnt === 0) console.log(dim(" Project has no remaining names."));
898
+ console.log(ok(`Removed name: ${bold$1(shortname)}`));
899
+ if (remaining.cnt === 0) console.log(dim$1(" Project has no remaining names."));
900
900
  }
901
901
  function cmdNames(db, opts) {
902
902
  const rows = db.prepare(`
@@ -904,15 +904,15 @@ function cmdNames(db, opts) {
904
904
  (SELECT COUNT(*) FROM sessions s WHERE s.project_id = p.id) AS session_count,
905
905
  (SELECT MAX(s.created_at) FROM sessions s WHERE s.project_id = p.id) AS last_active
906
906
  FROM projects p
907
- JOIN aliases a ON a.project_id = p.id
907
+ ${opts.all ? "LEFT JOIN" : "JOIN"} aliases a ON a.project_id = p.id
908
908
  WHERE p.status = 'active'
909
909
  ORDER BY p.updated_at DESC
910
910
  `).all();
911
911
  if (opts.json) {
912
912
  const grouped = /* @__PURE__ */ new Map();
913
913
  for (const row of rows) if (!grouped.has(row.id)) grouped.set(row.id, {
914
- name: row.name,
915
- names: [row.name],
914
+ name: row.name ?? row.slug,
915
+ names: row.name ? [row.name] : [],
916
916
  slug: row.slug,
917
917
  display_name: row.display_name,
918
918
  root_path: row.root_path,
@@ -920,7 +920,7 @@ function cmdNames(db, opts) {
920
920
  last_active: row.last_active ? new Date(row.last_active).toISOString() : null,
921
921
  session_config: row.session_config ? JSON.parse(row.session_config) : null
922
922
  });
923
- else grouped.get(row.id).names.push(row.name);
923
+ else if (row.name) grouped.get(row.id).names.push(row.name);
924
924
  console.log(JSON.stringify([...grouped.values()], null, 2));
925
925
  return;
926
926
  }
@@ -933,13 +933,13 @@ function cmdNames(db, opts) {
933
933
  for (const row of rows) {
934
934
  if (seen.has(row.id)) continue;
935
935
  seen.add(row.id);
936
- const allNames = rows.filter((r) => r.id === row.id).map((r) => r.name);
937
- const perm = (row.session_config ? JSON.parse(row.session_config) : null)?.permission ?? dim("default");
936
+ const allNames = rows.filter((r) => r.id === row.id).map((r) => r.name).filter(Boolean);
937
+ const perm = (row.session_config ? JSON.parse(row.session_config) : null)?.permission ?? dim$1("default");
938
938
  tableRows.push([
939
- bold(allNames.join(", ")),
939
+ bold$1(allNames.length ? allNames.join(", ") : row.slug),
940
940
  row.slug,
941
- dim(shortenPath(row.root_path, 40)),
942
- typeof perm === "string" ? perm : dim("default"),
941
+ dim$1(shortenPath(row.root_path, 40)),
942
+ typeof perm === "string" ? perm : dim$1("default"),
943
943
  String(row.session_count),
944
944
  fmtDate(row.last_active)
945
945
  ]);
@@ -956,7 +956,7 @@ function cmdNames(db, opts) {
956
956
  "Last Active"
957
957
  ], tableRows));
958
958
  console.log();
959
- console.log(dim(` ${tableRows.length} named project(s)`));
959
+ console.log(dim$1(` ${tableRows.length} named project(s)`));
960
960
  }
961
961
  function cmdConfig(db, identifier, opts) {
962
962
  if (opts.options) {
@@ -973,14 +973,14 @@ function cmdConfig(db, identifier, opts) {
973
973
  console.log();
974
974
  console.log(header(" Session Config Options"));
975
975
  console.log();
976
- console.log(bold(" Available keys:"));
976
+ console.log(bold$1(" Available keys:"));
977
977
  console.log();
978
978
  for (const opt of CONFIG_OPTIONS) {
979
- console.log(` ${bold(opt.key.padEnd(14))} ${dim(`(${opt.type})`)} ${opt.description}`);
980
- console.log(` ${" ".repeat(14)} ${dim("e.g.")} ${opt.examples.map((e) => chalk.cyan(e)).join(", ")}`);
979
+ console.log(` ${bold$1(opt.key.padEnd(14))} ${dim$1(`(${opt.type})`)} ${opt.description}`);
980
+ console.log(` ${" ".repeat(14)} ${dim$1("e.g.")} ${opt.examples.map((e) => chalk.cyan(e)).join(", ")}`);
981
981
  }
982
982
  console.log();
983
- console.log(bold(" Permission presets:"));
983
+ console.log(bold$1(" Permission presets:"));
984
984
  console.log();
985
985
  for (const [name, config] of Object.entries(PERMISSION_PRESETS)) {
986
986
  const parts = [];
@@ -988,7 +988,7 @@ function cmdConfig(db, identifier, opts) {
988
988
  if (config.env && Object.keys(config.env).length) parts.push(`env: ${Object.entries(config.env).map(([k, v]) => `${k}=${v}`).join(" ")}`);
989
989
  if (config.autoStart) parts.push("autoStart");
990
990
  if (config.prompt) parts.push(`prompt: "${config.prompt}"`);
991
- console.log(` ${bold(name.padEnd(10))} ${dim(parts.join(", ") || "(vanilla)")}`);
991
+ console.log(` ${bold$1(name.padEnd(10))} ${dim$1(parts.join(", ") || "(vanilla)")}`);
992
992
  }
993
993
  console.log();
994
994
  return;
@@ -1003,7 +1003,7 @@ function cmdConfig(db, identifier, opts) {
1003
1003
  if (opts.preset) {
1004
1004
  defaults = applyPreset(defaults, opts.preset);
1005
1005
  saveGlobalDefaults(defaults);
1006
- console.log(ok(`Global defaults set to preset: ${bold(opts.preset)}`));
1006
+ console.log(ok(`Global defaults set to preset: ${bold$1(opts.preset)}`));
1007
1007
  return;
1008
1008
  }
1009
1009
  if (opts.set?.length) {
@@ -1042,11 +1042,11 @@ function cmdConfig(db, identifier, opts) {
1042
1042
  console.log(header(" Global Session Defaults"));
1043
1043
  console.log();
1044
1044
  if (Object.keys(defaults).length === 0) {
1045
- console.log(dim(" No defaults set. New sessions use vanilla Claude."));
1046
- console.log(dim(" Set with: pai project config --defaults --preset full"));
1045
+ console.log(dim$1(" No defaults set. New sessions use vanilla Claude."));
1046
+ console.log(dim$1(" Set with: pai project config --defaults --preset full"));
1047
1047
  } else for (const [key, value] of Object.entries(defaults)) {
1048
1048
  const display = typeof value === "object" ? JSON.stringify(value) : String(value);
1049
- console.log(` ${bold(key.padEnd(14))} ${display}`);
1049
+ console.log(` ${bold$1(key.padEnd(14))} ${display}`);
1050
1050
  }
1051
1051
  console.log();
1052
1052
  }
@@ -1054,20 +1054,20 @@ function cmdConfig(db, identifier, opts) {
1054
1054
  }
1055
1055
  if (!identifier) {
1056
1056
  console.error(err("Specify a project: pai project config <name-or-slug>"));
1057
- console.error(dim(" Or use --defaults for global defaults, --options for available keys."));
1057
+ console.error(dim$1(" Or use --defaults for global defaults, --options for available keys."));
1058
1058
  process.exit(1);
1059
1059
  }
1060
1060
  const project = resolveIdentifier(db, identifier) ?? requireProject(db, identifier);
1061
1061
  let config = getSessionConfig(project);
1062
1062
  if (opts.reset) {
1063
1063
  db.prepare("UPDATE projects SET session_config = NULL, updated_at = ? WHERE id = ?").run(now(), project.id);
1064
- console.log(ok(`Config reset for ${bold(project.slug)}. Will use global defaults.`));
1064
+ console.log(ok(`Config reset for ${bold$1(project.slug)}. Will use global defaults.`));
1065
1065
  return;
1066
1066
  }
1067
1067
  if (opts.preset) {
1068
1068
  config = applyPreset(config, opts.preset);
1069
1069
  db.prepare("UPDATE projects SET session_config = ?, updated_at = ? WHERE id = ?").run(JSON.stringify(config), now(), project.id);
1070
- console.log(ok(`Applied preset ${bold(opts.preset)} to ${bold(project.slug)}`));
1070
+ console.log(ok(`Applied preset ${bold$1(opts.preset)} to ${bold$1(project.slug)}`));
1071
1071
  }
1072
1072
  if (opts.set?.length) {
1073
1073
  for (const pair of opts.set) {
@@ -1089,7 +1089,7 @@ function cmdConfig(db, identifier, opts) {
1089
1089
  else config[key] = parseConfigValue(key, value);
1090
1090
  }
1091
1091
  db.prepare("UPDATE projects SET session_config = ?, updated_at = ? WHERE id = ?").run(JSON.stringify(config), now(), project.id);
1092
- console.log(ok(`Config updated for ${bold(project.slug)}`));
1092
+ console.log(ok(`Config updated for ${bold$1(project.slug)}`));
1093
1093
  }
1094
1094
  if (opts.unset?.length) {
1095
1095
  for (const key of opts.unset) if (key.startsWith("env.")) {
@@ -1097,7 +1097,7 @@ function cmdConfig(db, identifier, opts) {
1097
1097
  if (config.env) delete config.env[envKey];
1098
1098
  } else delete config[key];
1099
1099
  db.prepare("UPDATE projects SET session_config = ?, updated_at = ? WHERE id = ?").run(JSON.stringify(config), now(), project.id);
1100
- console.log(ok(`Config updated for ${bold(project.slug)}`));
1100
+ console.log(ok(`Config updated for ${bold$1(project.slug)}`));
1101
1101
  }
1102
1102
  const effective = {
1103
1103
  ...getGlobalDefaults(),
@@ -1117,32 +1117,32 @@ function cmdConfig(db, identifier, opts) {
1117
1117
  }
1118
1118
  console.log();
1119
1119
  console.log(header(` Config: ${project.slug}`));
1120
- if (aliases.length) console.log(dim(` Names: ${aliases.join(", ")}`));
1121
- console.log(dim(` Path: ${project.root_path}`));
1120
+ if (aliases.length) console.log(dim$1(` Names: ${aliases.join(", ")}`));
1121
+ console.log(dim$1(` Path: ${project.root_path}`));
1122
1122
  console.log();
1123
- if (Object.keys(config).length === 0) console.log(dim(" No project-specific config. Using global defaults."));
1123
+ if (Object.keys(config).length === 0) console.log(dim$1(" No project-specific config. Using global defaults."));
1124
1124
  else {
1125
- console.log(bold(" Project config:"));
1125
+ console.log(bold$1(" Project config:"));
1126
1126
  for (const [key, value] of Object.entries(config)) {
1127
1127
  const display = typeof value === "object" ? JSON.stringify(value) : String(value);
1128
- console.log(` ${bold(key.padEnd(14))} ${display}`);
1128
+ console.log(` ${bold$1(key.padEnd(14))} ${display}`);
1129
1129
  }
1130
1130
  }
1131
1131
  const globalDefaults = getGlobalDefaults();
1132
1132
  if (Object.keys(globalDefaults).length > 0) {
1133
1133
  console.log();
1134
- console.log(dim(" Global defaults (overridden by project config):"));
1134
+ console.log(dim$1(" Global defaults (overridden by project config):"));
1135
1135
  for (const [key, value] of Object.entries(globalDefaults)) {
1136
1136
  const overridden = key in config;
1137
1137
  const display = typeof value === "object" ? JSON.stringify(value) : String(value);
1138
- console.log(` ${dim(key.padEnd(14))} ${overridden ? chalk.strikethrough(display) + " " + dim("(overridden)") : display}`);
1138
+ console.log(` ${dim$1(key.padEnd(14))} ${overridden ? chalk.strikethrough(display) + " " + dim$1("(overridden)") : display}`);
1139
1139
  }
1140
1140
  }
1141
1141
  console.log();
1142
- console.log(bold(" Effective (what AIBroker uses):"));
1142
+ console.log(bold$1(" Effective (what AIBroker uses):"));
1143
1143
  for (const [key, value] of Object.entries(effective)) {
1144
1144
  const display = typeof value === "object" ? JSON.stringify(value) : String(value);
1145
- console.log(` ${bold(key.padEnd(14))} ${display}`);
1145
+ console.log(` ${bold$1(key.padEnd(14))} ${display}`);
1146
1146
  }
1147
1147
  console.log();
1148
1148
  }
@@ -1226,12 +1226,12 @@ function cmdHealth$1(db, opts) {
1226
1226
  console.log(` ${chalk.green("Active:")} ${active.length} ${chalk.yellow("Stale (moved?):")} ${stale.length} ${chalk.red("Dead (missing):")} ${dead.length}`);
1227
1227
  console.log();
1228
1228
  if (active.length) {
1229
- console.log(bold(" Active projects (path exists):"));
1229
+ console.log(bold$1(" Active projects (path exists):"));
1230
1230
  const tableRows = active.map((r) => [
1231
- bold(r.project.slug),
1232
- dim(shortenPath(r.project.root_path, 50)),
1231
+ bold$1(r.project.slug),
1232
+ dim$1(shortenPath(r.project.root_path, 50)),
1233
1233
  String(r.project.session_count),
1234
- r.claudeNotesExists ? chalk.green("yes") : dim("no")
1234
+ r.claudeNotesExists ? chalk.green("yes") : dim$1("no")
1235
1235
  ]);
1236
1236
  console.log(renderTable([
1237
1237
  "Slug",
@@ -1244,8 +1244,8 @@ function cmdHealth$1(db, opts) {
1244
1244
  if (stale.length) {
1245
1245
  console.log(warn(" Stale projects (path missing, possible new location found):"));
1246
1246
  for (const r of stale) {
1247
- console.log(` ${bold(r.project.slug)}`);
1248
- console.log(dim(` Old path: ${r.project.root_path}`));
1247
+ console.log(` ${bold$1(r.project.slug)}`);
1248
+ console.log(dim$1(` Old path: ${r.project.root_path}`));
1249
1249
  console.log(chalk.cyan(` Found at: ${r.suggestedPath}`));
1250
1250
  if (r.claudeNotesExists) console.log(chalk.green(` Notes: ${r.orphanedNotesDirs.join(", ")}`));
1251
1251
  if (opts.fix && r.suggestedPath) {
@@ -1253,23 +1253,23 @@ function cmdHealth$1(db, opts) {
1253
1253
  const newEncoded = encodeDir(r.suggestedPath);
1254
1254
  db.prepare("UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?").run(r.suggestedPath, newEncoded, ts, r.project.id);
1255
1255
  console.log(ok(` Auto-fixed: updated path to ${r.suggestedPath}`));
1256
- } else if (r.suggestedPath) console.log(dim(` Fix: pai project move ${r.project.slug} ${r.suggestedPath}`));
1256
+ } else if (r.suggestedPath) console.log(dim$1(` Fix: pai project move ${r.project.slug} ${r.suggestedPath}`));
1257
1257
  }
1258
1258
  console.log();
1259
1259
  }
1260
1260
  if (dead.length) {
1261
1261
  console.log(err(" Dead projects (path missing, no match found):"));
1262
1262
  for (const r of dead) {
1263
- console.log(` ${bold(r.project.slug)} ${dim(r.project.root_path)}`);
1263
+ console.log(` ${bold$1(r.project.slug)} ${dim$1(r.project.root_path)}`);
1264
1264
  if (r.claudeNotesExists) console.log(chalk.yellow(` Notes: ${r.orphanedNotesDirs.join(", ")}`));
1265
1265
  if (r.project.session_count === 0 && opts.fix) {
1266
1266
  db.prepare("UPDATE projects SET status = 'archived', archived_at = ?, updated_at = ? WHERE id = ?").run(now(), now(), r.project.id);
1267
1267
  console.log(ok(" Auto-fixed: archived (0 sessions, path gone)"));
1268
- } else console.log(dim(` Fix: pai project archive ${r.project.slug} (or pai project move ...)`));
1268
+ } else console.log(dim$1(` Fix: pai project archive ${r.project.slug} (or pai project move ...)`));
1269
1269
  }
1270
1270
  console.log();
1271
1271
  }
1272
- console.log(dim(` ${rows.length} total: ${active.length} active, ${stale.length} stale, ${dead.length} dead`));
1272
+ console.log(dim$1(` ${rows.length} total: ${active.length} active, ${stale.length} stale, ${dead.length} dead`));
1273
1273
  if (!opts.fix && (stale.length > 0 || dead.length > 0)) {
1274
1274
  console.log();
1275
1275
  console.log(warn(" Run with --fix to auto-remediate where possible."));
@@ -1298,25 +1298,25 @@ function registerProjectsCommands(projectsCmd, getDb) {
1298
1298
  process.stdout.write(project.root_path + "\n");
1299
1299
  return;
1300
1300
  }
1301
- process.stderr.write(warn(`Path not found: ${project.root_path}\n`) + dim(" Searching for moved location...\n"));
1301
+ process.stderr.write(warn(`Path not found: ${project.root_path}\n`) + dim$1(" Searching for moved location...\n"));
1302
1302
  const result = findMovedPath(project.root_path);
1303
1303
  if (result.found) {
1304
1304
  const newPath = result.found;
1305
1305
  const newEncoded = encodeDir(newPath);
1306
1306
  const ts = now();
1307
1307
  db.prepare("UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?").run(newPath, newEncoded, ts, project.id);
1308
- process.stderr.write(ok(`Project moved: ${shortenPath(project.root_path, 50)}\n`) + dim(` → ${newPath}\n`) + ok("Registry updated.\n"));
1308
+ process.stderr.write(ok(`Project moved: ${shortenPath(project.root_path, 50)}\n`) + dim$1(` → ${newPath}\n`) + ok("Registry updated.\n"));
1309
1309
  process.stdout.write(newPath + "\n");
1310
1310
  return;
1311
1311
  }
1312
1312
  if (result.ambiguous) {
1313
1313
  process.stderr.write(warn(`Multiple directories named "${basename(project.root_path)}" found:\n`));
1314
- for (const candidate of result.ambiguous) process.stderr.write(dim(` ${candidate}\n`));
1315
- process.stderr.write(dim(`\n Disambiguate with: pai projects rebind ${project.slug} <path>\n`));
1314
+ for (const candidate of result.ambiguous) process.stderr.write(dim$1(` ${candidate}\n`));
1315
+ process.stderr.write(dim$1(`\n Disambiguate with: pai projects rebind ${project.slug} <path>\n`));
1316
1316
  process.exitCode = 1;
1317
1317
  return;
1318
1318
  }
1319
- process.stderr.write(err(`Project "${project.slug}" root_path "${project.root_path}" does not exist on disk\n and no folder named "${basename(project.root_path)}" was found in scan dirs.\n`) + dim(` Fix with: pai projects rebind ${project.slug} <new-path>\n`));
1319
+ process.stderr.write(err(`Project "${project.slug}" root_path "${project.root_path}" does not exist on disk\n and no folder named "${basename(project.root_path)}" was found in scan dirs.\n`) + dim$1(` Fix with: pai projects rebind ${project.slug} <new-path>\n`));
1320
1320
  process.exitCode = 1;
1321
1321
  });
1322
1322
  projectsCmd.command("add <path>").description("Register a project directory in the PAI registry").option("--slug <slug>", "Override auto-generated slug").option("--type <type>", "Project type: local | central | obsidian-linked | external", "local").option("--display-name <name>", "Human-readable display name").action((rawPath, opts) => {
@@ -1367,7 +1367,7 @@ function registerProjectsCommands(projectsCmd, getDb) {
1367
1367
  projectsCmd.command("unname <shortname>").description("Remove a project's short name").action((shortname) => {
1368
1368
  cmdUnname(getDb(), shortname);
1369
1369
  });
1370
- projectsCmd.command("names").description("List named projects (your curated shortlist)").option("--json", "Output JSON for AIBroker consumption").action((opts) => {
1370
+ projectsCmd.command("names").description("List named projects (your curated shortlist)").option("--json", "Output JSON for AIBroker consumption").option("--all", "Include ALL active registered projects, not just the named shortlist").action((opts) => {
1371
1371
  cmdNames(getDb(), opts);
1372
1372
  });
1373
1373
  projectsCmd.command("config [identifier]").description("View or modify session launch config for a project.\nUse --options to discover available keys and presets.").option("--set <key=value...>", "Set config values (repeatable)", (v, prev) => [...prev, v], []).option("--unset <key...>", "Remove config keys (repeatable, use env.KEY for env vars)", (v, prev) => [...prev, v], []).option("--preset <name>", "Apply a permission preset: full | trusted | default").option("--defaults", "Manage global session defaults instead of a project").option("--options", "List available config keys and presets").option("--json", "Output JSON").option("--reset", "Reset config to empty (inherit global defaults)").action((identifier, opts) => {
@@ -1397,7 +1397,7 @@ function cmdMigrate$1(db) {
1397
1397
  console.log(warn("No projects found in session-registry.json."));
1398
1398
  return;
1399
1399
  }
1400
- console.log(dim(`Migrating ${projects.length} project(s) from session-registry.json ...`));
1400
+ console.log(dim$1(`Migrating ${projects.length} project(s) from session-registry.json ...`));
1401
1401
  let projectsNew = 0;
1402
1402
  let projectsSkipped = 0;
1403
1403
  let sessionsNew = 0;
@@ -1425,13 +1425,13 @@ function cmdMigrate$1(db) {
1425
1425
  }
1426
1426
  }
1427
1427
  console.log(ok("Migration complete."));
1428
- console.log(dim(` Projects: ${projectsNew} new, ${projectsSkipped} already existed`));
1429
- console.log(dim(` Sessions: ${sessionsNew} new`));
1428
+ console.log(dim$1(` Projects: ${projectsNew} new, ${projectsSkipped} already existed`));
1429
+ console.log(dim$1(` Sessions: ${sessionsNew} new`));
1430
1430
  if (errors.length) {
1431
1431
  console.log();
1432
1432
  console.log(warn(` ${errors.length} error(s) during migration:`));
1433
- for (const e of errors.slice(0, 5)) console.log(dim(` ${e}`));
1434
- if (errors.length > 5) console.log(dim(` ... and ${errors.length - 5} more`));
1433
+ for (const e of errors.slice(0, 5)) console.log(dim$1(` ${e}`));
1434
+ if (errors.length > 5) console.log(dim$1(` ... and ${errors.length - 5} more`));
1435
1435
  }
1436
1436
  }
1437
1437
 
@@ -1446,20 +1446,20 @@ function cmdStats$2(db) {
1446
1446
  const lastProject = db.prepare("SELECT updated_at FROM projects ORDER BY updated_at DESC LIMIT 1").get();
1447
1447
  const lastSession = db.prepare("SELECT created_at FROM sessions ORDER BY created_at DESC LIMIT 1").get();
1448
1448
  console.log();
1449
- console.log(bold(" PAI Registry Stats"));
1449
+ console.log(bold$1(" PAI Registry Stats"));
1450
1450
  console.log();
1451
- console.log(` ${bold("Projects:")} ${totalProjects}`);
1452
- console.log(` ${bold(" Active:")} ${activeProjects}`);
1453
- console.log(` ${bold(" Archived:")} ${archivedProjects}`);
1454
- console.log(` ${bold("Sessions:")} ${totalSessions}`);
1455
- console.log(` ${bold("Tags:")} ${totalTags}`);
1456
- if (lastProject) console.log(` ${bold("Last updated:")} ${fmtDate(lastProject.updated_at)}`);
1457
- if (lastSession) console.log(` ${bold("Last session:")} ${fmtDate(lastSession.created_at)}`);
1451
+ console.log(` ${bold$1("Projects:")} ${totalProjects}`);
1452
+ console.log(` ${bold$1(" Active:")} ${activeProjects}`);
1453
+ console.log(` ${bold$1(" Archived:")} ${archivedProjects}`);
1454
+ console.log(` ${bold$1("Sessions:")} ${totalSessions}`);
1455
+ console.log(` ${bold$1("Tags:")} ${totalTags}`);
1456
+ if (lastProject) console.log(` ${bold$1("Last updated:")} ${fmtDate(lastProject.updated_at)}`);
1457
+ if (lastSession) console.log(` ${bold$1("Last session:")} ${fmtDate(lastSession.created_at)}`);
1458
1458
  console.log();
1459
1459
  }
1460
1460
  function cmdRebuild(db) {
1461
1461
  console.log(warn("Rebuilding registry — all existing data will be erased."));
1462
- console.log(dim("Clearing all tables ..."));
1462
+ console.log(dim$1("Clearing all tables ..."));
1463
1463
  db.exec(`
1464
1464
  DELETE FROM compaction_log;
1465
1465
  DELETE FROM session_tags;
@@ -1470,7 +1470,7 @@ function cmdRebuild(db) {
1470
1470
  DELETE FROM tags;
1471
1471
  DELETE FROM schema_version;
1472
1472
  `);
1473
- console.log(dim("Registry cleared. Re-scanning ..."));
1473
+ console.log(dim$1("Registry cleared. Re-scanning ..."));
1474
1474
  cmdScan(db);
1475
1475
  }
1476
1476
  function cmdLookup(db, fsPath) {
@@ -1484,10 +1484,10 @@ function registerRegistryCommands(registryCmd, getDb) {
1484
1484
  if (opts.showDirs) {
1485
1485
  const config = loadScanConfig();
1486
1486
  if (!config.scan_dirs.length) {
1487
- console.log(dim(" No extra scan directories configured."));
1488
- console.log(dim(" Use --add-dir <path> to add one."));
1487
+ console.log(dim$1(" No extra scan directories configured."));
1488
+ console.log(dim$1(" Use --add-dir <path> to add one."));
1489
1489
  } else {
1490
- console.log(bold(" Configured scan directories:"));
1490
+ console.log(bold$1(" Configured scan directories:"));
1491
1491
  for (const d of config.scan_dirs) console.log(` ${d}`);
1492
1492
  }
1493
1493
  return;
@@ -1504,7 +1504,7 @@ function registerRegistryCommands(registryCmd, getDb) {
1504
1504
  else {
1505
1505
  config.scan_dirs.push(display);
1506
1506
  saveScanConfig(config);
1507
- console.log(ok(`Added scan directory: ${bold(display)}`));
1507
+ console.log(ok(`Added scan directory: ${bold$1(display)}`));
1508
1508
  }
1509
1509
  }
1510
1510
  if (opts.removeDir) {
@@ -1515,7 +1515,7 @@ function registerRegistryCommands(registryCmd, getDb) {
1515
1515
  config.scan_dirs = config.scan_dirs.filter((d) => resolveHome(d) !== resolved);
1516
1516
  if (config.scan_dirs.length < before) {
1517
1517
  saveScanConfig(config);
1518
- console.log(ok(`Removed scan directory: ${bold(display)}`));
1518
+ console.log(ok(`Removed scan directory: ${bold$1(display)}`));
1519
1519
  } else console.log(warn(`Not found in config: ${display}`));
1520
1520
  }
1521
1521
  if (!opts.addDir && !opts.removeDir) cmdScan(getDb(), { quick: opts.quick });
@@ -1538,12 +1538,12 @@ function registerRegistryCommands(registryCmd, getDb) {
1538
1538
  //#region src/cli/commands/memory/embed.ts
1539
1539
  async function runEmbed(federation, projectId, projectSlug, batchSize = 50) {
1540
1540
  const label = projectSlug ? `project ${projectSlug}` : "all projects";
1541
- console.log(dim(`Generating embeddings for ${label} (this may take a while on first run)...`));
1541
+ console.log(dim$1(`Generating embeddings for ${label} (this may take a while on first run)...`));
1542
1542
  const { chunksEmbedded } = await embedChunks(federation, projectId, batchSize, (done, total) => {
1543
1543
  process.stdout.write(`\r ${done} / ${total} chunks embedded...`);
1544
1544
  });
1545
1545
  process.stdout.write("\r");
1546
- console.log(ok(`Done.`) + ` ${bold(String(chunksEmbedded))} chunks embedded`);
1546
+ console.log(ok(`Done.`) + ` ${bold$1(String(chunksEmbedded))} chunks embedded`);
1547
1547
  }
1548
1548
  function registerEmbedCommand(memoryCmd, getDb) {
1549
1549
  memoryCmd.command("embed [project-slug]").description("Generate embeddings for un-embedded chunks (Phase 2.5)").option("--batch-size <n>", "Chunks to embed per batch", "50").action(async (projectSlug, opts) => {
@@ -1573,11 +1573,11 @@ function registerIndexCommand(memoryCmd, getDb) {
1573
1573
  const registryDb = getDb();
1574
1574
  if (!opts.direct && !projectSlug) try {
1575
1575
  await new PaiClient(loadConfig().socketPath).triggerIndex();
1576
- console.log(ok("Index triggered in daemon.") + dim(" Check daemon logs for progress."));
1577
- console.log(dim(" Run `pai daemon logs` to watch progress."));
1576
+ console.log(ok("Index triggered in daemon.") + dim$1(" Check daemon logs for progress."));
1577
+ console.log(dim$1(" Run `pai daemon logs` to watch progress."));
1578
1578
  return;
1579
1579
  } catch {
1580
- console.log(dim("Daemon not running. Running direct index..."));
1580
+ console.log(dim$1("Daemon not running. Running direct index..."));
1581
1581
  }
1582
1582
  let federation;
1583
1583
  try {
@@ -1592,14 +1592,14 @@ function registerIndexCommand(memoryCmd, getDb) {
1592
1592
  console.error(err(`Project not found or not active: ${projectSlug}`));
1593
1593
  process.exit(1);
1594
1594
  }
1595
- console.log(dim(`Indexing ${project.display_name} (${project.slug})...`));
1595
+ console.log(dim$1(`Indexing ${project.display_name} (${project.slug})...`));
1596
1596
  const result = await indexProject(federation, project.id, project.root_path);
1597
- console.log(ok(`Done.`) + ` ${bold(String(result.filesProcessed))} files indexed, ${bold(String(result.chunksCreated))} chunks created, ${dim(String(result.filesSkipped) + " skipped (unchanged)")}`);
1597
+ console.log(ok(`Done.`) + ` ${bold$1(String(result.filesProcessed))} files indexed, ${bold$1(String(result.chunksCreated))} chunks created, ${dim$1(String(result.filesSkipped) + " skipped (unchanged)")}`);
1598
1598
  if (opts.embed) await runEmbed(federation, project.id, project.slug);
1599
1599
  } else if (opts.all || !projectSlug) {
1600
- console.log(dim("Indexing all active projects..."));
1600
+ console.log(dim$1("Indexing all active projects..."));
1601
1601
  const { projects, result } = await indexAll(federation, registryDb);
1602
- console.log(ok(`Done.`) + ` ${bold(String(projects))} projects, ${bold(String(result.filesProcessed))} files indexed, ${bold(String(result.chunksCreated))} chunks created, ${dim(String(result.filesSkipped) + " skipped (unchanged)")}`);
1602
+ console.log(ok(`Done.`) + ` ${bold$1(String(projects))} projects, ${bold$1(String(result.filesProcessed))} files indexed, ${bold$1(String(result.chunksCreated))} chunks created, ${dim$1(String(result.filesSkipped) + " skipped (unchanged)")}`);
1603
1603
  if (opts.embed) await runEmbed(federation);
1604
1604
  }
1605
1605
  });
@@ -1656,7 +1656,7 @@ function registerSearchCommand(memoryCmd, getDb) {
1656
1656
  const backend = await createStorageBackend(config);
1657
1657
  try {
1658
1658
  const { generateEmbedding } = await import("./embeddings-BJPOcbik.mjs").then((n) => n.i);
1659
- console.log(dim("Generating query embedding..."));
1659
+ console.log(dim$1("Generating query embedding..."));
1660
1660
  const queryEmbedding = await generateEmbedding(query, true);
1661
1661
  if (mode === "semantic") results = await backend.searchSemantic(queryEmbedding, searchOpts);
1662
1662
  else {
@@ -1700,29 +1700,29 @@ function registerSearchCommand(memoryCmd, getDb) {
1700
1700
  }
1701
1701
  } else results = [];
1702
1702
  if (!results || results.length === 0) {
1703
- console.log(dim(`No results found for: "${query}" (mode: ${mode})`));
1703
+ console.log(dim$1(`No results found for: "${query}" (mode: ${mode})`));
1704
1704
  return;
1705
1705
  }
1706
1706
  if (opts.rerank !== false) {
1707
1707
  const { rerankResults } = await import("./reranker-C08R99zn.mjs").then((n) => n.r);
1708
- console.log(dim("Reranking with cross-encoder..."));
1708
+ console.log(dim$1("Reranking with cross-encoder..."));
1709
1709
  results = await rerankResults(query, results, { topK: maxResults });
1710
1710
  }
1711
1711
  const recencyDays = parseInt(opts.recency ?? String(searchConfig.recencyBoostDays), 10);
1712
1712
  if (recencyDays > 0) {
1713
1713
  const { applyRecencyBoost } = await import("./search-HcdKtMla.mjs").then((n) => n.o);
1714
- console.log(dim(`Applying recency boost (half-life: ${recencyDays} days)...`));
1714
+ console.log(dim$1(`Applying recency boost (half-life: ${recencyDays} days)...`));
1715
1715
  results = applyRecencyBoost(results, recencyDays);
1716
1716
  }
1717
1717
  const withSlugs = populateSlugs(results, registryDb);
1718
1718
  const rerankLabel = opts.rerank !== false ? " +rerank" : "";
1719
1719
  const modeLabel = mode !== "keyword" ? ` [${mode}${rerankLabel}]` : opts.rerank !== false ? ` [rerank]` : "";
1720
- console.log(`\n ${bold(`Search results for: "${query}"`)}${modeLabel} ${dim(`(${withSlugs.length} found)`)}\n`);
1720
+ console.log(`\n ${bold$1(`Search results for: "${query}"`)}${modeLabel} ${dim$1(`(${withSlugs.length} found)`)}\n`);
1721
1721
  for (const result of withSlugs) {
1722
1722
  const projectLabel = result.projectSlug ? chalk.cyan(result.projectSlug) : chalk.cyan(String(result.projectId));
1723
1723
  const tierLabel = tierColor$1(result.tier);
1724
- const scoreLabel = dim(`score: ${result.score.toFixed(4)}`);
1725
- const locationLabel = dim(`${result.path}:${result.startLine}-${result.endLine}`);
1724
+ const scoreLabel = dim$1(`score: ${result.score.toFixed(4)}`);
1725
+ const locationLabel = dim$1(`${result.path}:${result.startLine}-${result.endLine}`);
1726
1726
  console.log(` ${projectLabel} ${tierLabel} ${locationLabel} ${scoreLabel}`);
1727
1727
  const snippetLines = result.snippet.split("\n").slice(0, 3).map((l) => ` ${l}`);
1728
1728
  console.log(snippetLines.join("\n"));
@@ -1762,12 +1762,12 @@ function registerStatsCommands(memoryCmd, getDb) {
1762
1762
  const chunkStats = federation.prepare("SELECT COUNT(*) as chunks FROM memory_chunks WHERE project_id = ?").get(project.id);
1763
1763
  const lastUpdate = federation.prepare("SELECT MAX(updated_at) as last_at FROM memory_chunks WHERE project_id = ?").get(project.id);
1764
1764
  const tierBreakdown = federation.prepare("SELECT tier, COUNT(*) as n FROM memory_chunks WHERE project_id = ? GROUP BY tier ORDER BY n DESC").all(project.id);
1765
- console.log(`\n ${bold(project.display_name)} ${dim(`(${project.slug})`)}\n`);
1766
- console.log(` ${bold("Files indexed:")} ${fileStats.files}`);
1767
- console.log(` ${bold("Chunks:")} ${chunkStats.chunks}`);
1768
- console.log(` ${bold("Last indexed:")} ${lastUpdate.last_at ? fmtDate(lastUpdate.last_at) : dim("never")}`);
1765
+ console.log(`\n ${bold$1(project.display_name)} ${dim$1(`(${project.slug})`)}\n`);
1766
+ console.log(` ${bold$1("Files indexed:")} ${fileStats.files}`);
1767
+ console.log(` ${bold$1("Chunks:")} ${chunkStats.chunks}`);
1768
+ console.log(` ${bold$1("Last indexed:")} ${lastUpdate.last_at ? fmtDate(lastUpdate.last_at) : dim$1("never")}`);
1769
1769
  if (tierBreakdown.length > 0) {
1770
- console.log(`\n ${bold("By tier:")}`);
1770
+ console.log(`\n ${bold$1("By tier:")}`);
1771
1771
  for (const row of tierBreakdown) console.log(` ${tierColor(row.tier).padEnd(20)} ${row.n} chunks`);
1772
1772
  }
1773
1773
  console.log();
@@ -1791,16 +1791,16 @@ function registerStatsCommands(memoryCmd, getDb) {
1791
1791
  const slugRows = registryDb.prepare(`SELECT id, slug, display_name FROM projects WHERE id IN (${placeholders})`).all(...projectIds);
1792
1792
  for (const row of slugRows) slugMap.set(row.id, row.slug);
1793
1793
  }
1794
- console.log(`\n ${bold("PAI Memory Index — Global Status")}\n`);
1795
- console.log(` ${bold("Total files:")} ${globalFiles.n} ${bold("Total chunks:")} ${globalChunks.n}`);
1796
- console.log(` ${bold("Last indexed:")} ${lastUpdate.last_at ? fmtDate(lastUpdate.last_at) : dim("never")}`);
1794
+ console.log(`\n ${bold$1("PAI Memory Index — Global Status")}\n`);
1795
+ console.log(` ${bold$1("Total files:")} ${globalFiles.n} ${bold$1("Total chunks:")} ${globalChunks.n}`);
1796
+ console.log(` ${bold$1("Last indexed:")} ${lastUpdate.last_at ? fmtDate(lastUpdate.last_at) : dim$1("never")}`);
1797
1797
  if (projectStats.length > 0) {
1798
1798
  console.log();
1799
1799
  const rows = projectStats.map((p) => [
1800
1800
  chalk.cyan(slugMap.get(p.project_id) ?? String(p.project_id)),
1801
1801
  String(p.files),
1802
1802
  String(p.chunks),
1803
- p.last_at ? fmtDate(p.last_at) : dim("never")
1803
+ p.last_at ? fmtDate(p.last_at) : dim$1("never")
1804
1804
  ]);
1805
1805
  console.log(renderTable([
1806
1806
  "Project",
@@ -1808,22 +1808,22 @@ function registerStatsCommands(memoryCmd, getDb) {
1808
1808
  "Chunks",
1809
1809
  "Last Indexed"
1810
1810
  ], rows));
1811
- } else console.log(dim("\n No projects indexed yet. Run `pai memory index --all` to start."));
1811
+ } else console.log(dim$1("\n No projects indexed yet. Run `pai memory index --all` to start."));
1812
1812
  console.log();
1813
1813
  }
1814
1814
  });
1815
1815
  memoryCmd.command("settings [key] [value]").description("View or modify search settings in ~/.config/pai/config.json").action((key, value) => {
1816
1816
  const search = loadConfig().search;
1817
1817
  if (!key) {
1818
- console.log(`\n ${bold("PAI Memory — Search Settings")}\n`);
1819
- console.log(` ${bold("mode:")} ${search.mode}`);
1820
- console.log(` ${bold("rerank:")} ${search.rerank}`);
1821
- console.log(` ${bold("recencyBoostDays:")} ${search.recencyBoostDays}`);
1822
- console.log(` ${bold("defaultLimit:")} ${search.defaultLimit}`);
1823
- console.log(` ${bold("snippetLength:")} ${search.snippetLength}`);
1818
+ console.log(`\n ${bold$1("PAI Memory — Search Settings")}\n`);
1819
+ console.log(` ${bold$1("mode:")} ${search.mode}`);
1820
+ console.log(` ${bold$1("rerank:")} ${search.rerank}`);
1821
+ console.log(` ${bold$1("recencyBoostDays:")} ${search.recencyBoostDays}`);
1822
+ console.log(` ${bold$1("defaultLimit:")} ${search.defaultLimit}`);
1823
+ console.log(` ${bold$1("snippetLength:")} ${search.snippetLength}`);
1824
1824
  console.log();
1825
- console.log(dim(` Config file: ${CONFIG_FILE$2}`));
1826
- console.log(dim(` Edit directly or use: pai memory settings <key> <value>`));
1825
+ console.log(dim$1(` Config file: ${CONFIG_FILE$2}`));
1826
+ console.log(dim$1(` Edit directly or use: pai memory settings <key> <value>`));
1827
1827
  console.log();
1828
1828
  return;
1829
1829
  }
@@ -1831,7 +1831,7 @@ function registerStatsCommands(memoryCmd, getDb) {
1831
1831
  const val = search[key];
1832
1832
  if (val === void 0) {
1833
1833
  console.error(err(`Unknown setting: ${key}`));
1834
- console.log(dim(` Valid keys: mode, rerank, recencyBoostDays, defaultLimit, snippetLength`));
1834
+ console.log(dim$1(` Valid keys: mode, rerank, recencyBoostDays, defaultLimit, snippetLength`));
1835
1835
  process.exit(1);
1836
1836
  }
1837
1837
  console.log(String(val));
@@ -1846,7 +1846,7 @@ function registerStatsCommands(memoryCmd, getDb) {
1846
1846
  ]);
1847
1847
  if (!validKeys.has(key)) {
1848
1848
  console.error(err(`Unknown setting: ${key}`));
1849
- console.log(dim(` Valid keys: ${[...validKeys].join(", ")}`));
1849
+ console.log(dim$1(` Valid keys: ${[...validKeys].join(", ")}`));
1850
1850
  process.exit(1);
1851
1851
  }
1852
1852
  let fileConfig = {};
@@ -1881,7 +1881,7 @@ function registerStatsCommands(memoryCmd, getDb) {
1881
1881
  ensureConfigDir();
1882
1882
  writeFileSync(CONFIG_FILE$2, JSON.stringify(fileConfig, null, 2) + "\n", "utf-8");
1883
1883
  console.log(ok(`Set search.${key} = ${parsed}`));
1884
- console.log(dim(` Restart daemon to apply: pai daemon restart`));
1884
+ console.log(dim$1(` Restart daemon to apply: pai daemon restart`));
1885
1885
  } catch (e) {
1886
1886
  console.error(err(`Could not write config: ${e}`));
1887
1887
  process.exit(1);
@@ -1930,8 +1930,8 @@ function cmdInstall$1() {
1930
1930
  const servers = config.mcpServers;
1931
1931
  if ("pai" in servers) {
1932
1932
  console.log(warn("PAI MCP server is already registered in ~/.claude.json."));
1933
- console.log(dim(` Entry: ${JSON.stringify(servers["pai"])}`));
1934
- console.log(dim(" Use `pai mcp status` to verify the configuration."));
1933
+ console.log(dim$1(` Entry: ${JSON.stringify(servers["pai"])}`));
1934
+ console.log(dim$1(" Use `pai mcp status` to verify the configuration."));
1935
1935
  return;
1936
1936
  }
1937
1937
  servers["pai"] = {
@@ -1945,15 +1945,15 @@ function cmdInstall$1() {
1945
1945
  process.exit(1);
1946
1946
  }
1947
1947
  console.log(ok("PAI MCP server registered in ~/.claude.json."));
1948
- console.log(dim(` Binary: ${mcpBin}`));
1949
- console.log(dim(""));
1950
- console.log(dim(" Restart Claude Code to activate the PAI MCP tools:"));
1951
- console.log(dim(" memory_search, memory_get, project_info,"));
1952
- console.log(dim(" project_list, session_list, registry_search"));
1948
+ console.log(dim$1(` Binary: ${mcpBin}`));
1949
+ console.log(dim$1(""));
1950
+ console.log(dim$1(" Restart Claude Code to activate the PAI MCP tools:"));
1951
+ console.log(dim$1(" memory_search, memory_get, project_info,"));
1952
+ console.log(dim$1(" project_list, session_list, registry_search"));
1953
1953
  if (!existsSync(mcpBin)) {
1954
1954
  console.log();
1955
1955
  console.log(warn(` Note: MCP binary not found at ${mcpBin}`));
1956
- console.log(dim(" Run `bun run build` to compile it first."));
1956
+ console.log(dim$1(" Run `bun run build` to compile it first."));
1957
1957
  }
1958
1958
  }
1959
1959
  function cmdStatus$3() {
@@ -1963,26 +1963,26 @@ function cmdStatus$3() {
1963
1963
  const registered = "pai" in servers;
1964
1964
  const binExists = existsSync(mcpBin);
1965
1965
  console.log();
1966
- console.log(bold(" PAI MCP Server Status"));
1966
+ console.log(bold$1(" PAI MCP Server Status"));
1967
1967
  console.log();
1968
1968
  if (registered) {
1969
1969
  const entry = servers["pai"];
1970
1970
  console.log(ok(` Registered in ~/.claude.json`));
1971
- console.log(dim(` Config: ${JSON.stringify(entry)}`));
1971
+ console.log(dim$1(` Config: ${JSON.stringify(entry)}`));
1972
1972
  } else {
1973
1973
  console.log(warn(` NOT registered in ~/.claude.json`));
1974
- console.log(dim(` Run: pai mcp install`));
1974
+ console.log(dim$1(` Run: pai mcp install`));
1975
1975
  }
1976
1976
  console.log();
1977
1977
  if (binExists) console.log(ok(` MCP binary found: ${mcpBin}`));
1978
1978
  else {
1979
1979
  console.log(warn(` MCP binary NOT found: ${mcpBin}`));
1980
- console.log(dim(" Run: bun run build"));
1980
+ console.log(dim$1(" Run: bun run build"));
1981
1981
  }
1982
1982
  console.log();
1983
- if (registered && binExists) console.log(dim(" Status: READY — restart Claude Code to use PAI tools"));
1983
+ if (registered && binExists) console.log(dim$1(" Status: READY — restart Claude Code to use PAI tools"));
1984
1984
  else if (registered && !binExists) console.log(warn(" Status: NEEDS BUILD — run `bun run build`"));
1985
- else console.log(dim(" Status: NOT INSTALLED — run `pai mcp install`"));
1985
+ else console.log(dim$1(" Status: NOT INSTALLED — run `pai mcp install`"));
1986
1986
  console.log();
1987
1987
  }
1988
1988
  function registerMcpCommands(mcpCmd) {
@@ -2073,16 +2073,16 @@ async function cmdStatus$2() {
2073
2073
  try {
2074
2074
  const s = await client.status();
2075
2075
  console.log();
2076
- console.log(bold(" PAI Daemon Status"));
2076
+ console.log(bold$1(" PAI Daemon Status"));
2077
2077
  console.log();
2078
2078
  console.log(ok(` Daemon running`));
2079
- console.log(dim(` Uptime: ${s["uptime"]}s`));
2080
- console.log(dim(` Socket: ${s["socketPath"]}`));
2081
- console.log(dim(` Index: ${s["indexInProgress"] ? "in progress" : "idle"} (interval: ${s["indexIntervalSecs"]}s)`));
2082
- if (s["lastIndexTime"]) console.log(dim(` Last index: ${s["lastIndexTime"]}`));
2079
+ console.log(dim$1(` Uptime: ${s["uptime"]}s`));
2080
+ console.log(dim$1(` Socket: ${s["socketPath"]}`));
2081
+ console.log(dim$1(` Index: ${s["indexInProgress"] ? "in progress" : "idle"} (interval: ${s["indexIntervalSecs"]}s)`));
2082
+ if (s["lastIndexTime"]) console.log(dim$1(` Last index: ${s["lastIndexTime"]}`));
2083
2083
  if (s["db"]) {
2084
2084
  const db = s["db"];
2085
- console.log(dim(` DB: ${db["projects"]} projects, ${db["files"]} files, ${db["chunks"]} chunks`));
2085
+ console.log(dim$1(` DB: ${db["projects"]} projects, ${db["files"]} files, ${db["chunks"]} chunks`));
2086
2086
  }
2087
2087
  console.log();
2088
2088
  } catch (e) {
@@ -2091,8 +2091,8 @@ async function cmdStatus$2() {
2091
2091
  console.log(warn(" PAI Daemon Status"));
2092
2092
  console.log();
2093
2093
  console.log(warn(` Daemon not running: ${msg}`));
2094
- console.log(dim(" Start with: pai daemon serve"));
2095
- console.log(dim(" Or install as service: pai daemon install"));
2094
+ console.log(dim$1(" Start with: pai daemon serve"));
2095
+ console.log(dim$1(" Or install as service: pai daemon install"));
2096
2096
  console.log();
2097
2097
  process.exit(1);
2098
2098
  }
@@ -2107,7 +2107,7 @@ function cmdRestart() {
2107
2107
  "-k",
2108
2108
  `gui/${process.getuid?.() ?? 501}/${PLIST_LABEL}`
2109
2109
  ], { encoding: "utf8" }).status === 0) console.log(ok("Sent kickstart to launchd."));
2110
- else console.log(dim("Not managed by launchd either. Run: pai daemon serve"));
2110
+ else console.log(dim$1("Not managed by launchd either. Run: pai daemon serve"));
2111
2111
  return;
2112
2112
  }
2113
2113
  const pids = result.stdout.trim().split("\n").map((p) => p.trim());
@@ -2117,7 +2117,7 @@ function cmdRestart() {
2117
2117
  } catch {
2118
2118
  console.log(warn(`Could not signal pid ${pid}.`));
2119
2119
  }
2120
- console.log(dim("launchd will restart the daemon automatically."));
2120
+ console.log(dim$1("launchd will restart the daemon automatically."));
2121
2121
  } catch (e) {
2122
2122
  const msg = e instanceof Error ? e.message : String(e);
2123
2123
  console.error(err(`restart error: ${msg}`));
@@ -2128,7 +2128,7 @@ function cmdInstall() {
2128
2128
  const daemonBin = getDaemonBinPath();
2129
2129
  const shimBin = getShimBinPath();
2130
2130
  console.log();
2131
- console.log(bold(" PAI Daemon Install"));
2131
+ console.log(bold$1(" PAI Daemon Install"));
2132
2132
  console.log();
2133
2133
  if (!existsSync(LAUNCH_AGENTS_DIR)) mkdirSync(LAUNCH_AGENTS_DIR, { recursive: true });
2134
2134
  const plistContent = generatePlist(daemonBin);
@@ -2146,7 +2146,7 @@ function cmdInstall() {
2146
2146
  else console.log(warn(` launchctl load: ${loadResult.stderr?.trim() ?? "unknown error"}`));
2147
2147
  } catch {
2148
2148
  console.log(warn(" Could not run launchctl. Load manually:"));
2149
- console.log(dim(` launchctl load ${PLIST_PATH}`));
2149
+ console.log(dim$1(` launchctl load ${PLIST_PATH}`));
2150
2150
  }
2151
2151
  const config = readClaudeJson();
2152
2152
  if (typeof config.mcpServers !== "object" || config.mcpServers === null) config.mcpServers = {};
@@ -2156,7 +2156,7 @@ function cmdInstall() {
2156
2156
  else {
2157
2157
  if (existing) {
2158
2158
  servers["pai-legacy"] = existing;
2159
- console.log(dim(" Backed up existing 'pai' entry as 'pai-legacy'."));
2159
+ console.log(dim$1(" Backed up existing 'pai' entry as 'pai-legacy'."));
2160
2160
  }
2161
2161
  servers["pai"] = {
2162
2162
  command: "node",
@@ -2173,19 +2173,19 @@ function cmdInstall() {
2173
2173
  console.log();
2174
2174
  if (!existsSync(daemonBin)) {
2175
2175
  console.log(warn(` Daemon binary not found: ${daemonBin}`));
2176
- console.log(dim(" Run: bun run build"));
2176
+ console.log(dim$1(" Run: bun run build"));
2177
2177
  } else console.log(ok(` Daemon binary: ${daemonBin}`));
2178
2178
  if (!existsSync(shimBin)) {
2179
2179
  console.log(warn(` Shim binary not found: ${shimBin}`));
2180
- console.log(dim(" Run: bun run build"));
2180
+ console.log(dim$1(" Run: bun run build"));
2181
2181
  } else console.log(ok(` Shim binary: ${shimBin}`));
2182
2182
  console.log();
2183
- console.log(dim(" Restart Claude Code to activate the daemon-backed PAI tools."));
2183
+ console.log(dim$1(" Restart Claude Code to activate the daemon-backed PAI tools."));
2184
2184
  console.log();
2185
2185
  }
2186
2186
  function cmdUninstall() {
2187
2187
  console.log();
2188
- console.log(bold(" PAI Daemon Uninstall"));
2188
+ console.log(bold$1(" PAI Daemon Uninstall"));
2189
2189
  console.log();
2190
2190
  if (existsSync(PLIST_PATH)) {
2191
2191
  try {
@@ -2200,7 +2200,7 @@ function cmdUninstall() {
2200
2200
  } catch (e) {
2201
2201
  console.log(warn(` Could not remove plist: ${e}`));
2202
2202
  }
2203
- } else console.log(dim(" No launchd plist found."));
2203
+ } else console.log(dim$1(" No launchd plist found."));
2204
2204
  const config = readClaudeJson();
2205
2205
  const servers = typeof config.mcpServers === "object" && config.mcpServers !== null ? config.mcpServers : {};
2206
2206
  const legacy = servers["pai-legacy"];
@@ -2213,16 +2213,16 @@ function cmdUninstall() {
2213
2213
  } catch (e) {
2214
2214
  console.log(warn(` Could not update ~/.claude.json: ${e}`));
2215
2215
  }
2216
- } else console.log(dim(" No legacy PAI entry found in ~/.claude.json."));
2216
+ } else console.log(dim$1(" No legacy PAI entry found in ~/.claude.json."));
2217
2217
  console.log();
2218
- console.log(dim(" Restart Claude Code to deactivate daemon-backed tools."));
2218
+ console.log(dim$1(" Restart Claude Code to deactivate daemon-backed tools."));
2219
2219
  console.log();
2220
2220
  }
2221
2221
  async function cmdMigrate(connectionString) {
2222
2222
  console.log();
2223
- console.log(bold(" PAI Daemon Migrate"));
2223
+ console.log(bold$1(" PAI Daemon Migrate"));
2224
2224
  console.log();
2225
- console.log(dim(" Running SQLite → PostgreSQL migration..."));
2225
+ console.log(dim$1(" Running SQLite → PostgreSQL migration..."));
2226
2226
  console.log();
2227
2227
  const { fileURLToPath } = await import("node:url");
2228
2228
  const { dirname: pathDirname, join: pathJoin } = await import("node:path");
@@ -2243,7 +2243,7 @@ function cmdLogs(opts) {
2243
2243
  const lines = opts.lines ?? "50";
2244
2244
  if (!existsSync(DAEMON_LOG)) {
2245
2245
  console.log(warn(`No daemon log found at ${DAEMON_LOG}.`));
2246
- console.log(dim("The daemon may not have run yet."));
2246
+ console.log(dim$1("The daemon may not have run yet."));
2247
2247
  return;
2248
2248
  }
2249
2249
  if (opts.follow) try {
@@ -2259,8 +2259,8 @@ function cmdLogs(opts) {
2259
2259
  }
2260
2260
  function registerDaemonCommands(daemonCmd) {
2261
2261
  daemonCmd.command("serve").description("Start the PAI daemon in the foreground").action(async () => {
2262
- const { serve } = await import("./daemon-BldK8-57.mjs").then((n) => n.t);
2263
- const { loadConfig: lc, ensureConfigDir } = await import("./config-BuhHWyOK.mjs").then((n) => n.r);
2262
+ const { serve } = await import("./daemon-D2aKb4mJ.mjs").then((n) => n.t);
2263
+ const { loadConfig: lc, ensureConfigDir } = await import("./config-C8m-tPhP.mjs").then((n) => n.r);
2264
2264
  ensureConfigDir();
2265
2265
  await serve(lc());
2266
2266
  });
@@ -2312,7 +2312,7 @@ function fileSize(path) {
2312
2312
  function registerBackupCommands(program) {
2313
2313
  program.command("backup").description("Backup registry, config, and Postgres database to ~/.pai/backups/").option("--no-postgres", "Skip the Postgres pg_dump (faster, registry+config only)").action(async (opts) => {
2314
2314
  const backupDir = join(BACKUPS_DIR$1, timestamp());
2315
- console.log(dim(`Creating backup: ${backupDir}`));
2315
+ console.log(dim$1(`Creating backup: ${backupDir}`));
2316
2316
  mkdirSync(backupDir, { recursive: true });
2317
2317
  const results = [];
2318
2318
  if (existsSync(REGISTRY_DB$1)) {
@@ -2385,7 +2385,7 @@ function registerBackupCommands(program) {
2385
2385
  }
2386
2386
  if (opts.postgres) {
2387
2387
  const sqlDest = join(backupDir, "postgres-pai.sql");
2388
- console.log(dim(` Running pg_dump on ${DOCKER_CONTAINER$1} (this may take a moment)...`));
2388
+ console.log(dim$1(` Running pg_dump on ${DOCKER_CONTAINER$1} (this may take a moment)...`));
2389
2389
  try {
2390
2390
  execSync(`docker inspect ${DOCKER_CONTAINER$1} --format='{{.State.Status}}'`, { stdio: "pipe" });
2391
2391
  execSync(`docker exec ${DOCKER_CONTAINER$1} pg_dump -U ${PG_USER$1} ${PG_DATABASE$1} > "${sqlDest}"`, {
@@ -2416,16 +2416,16 @@ function registerBackupCommands(program) {
2416
2416
  label: "Postgres DB",
2417
2417
  path: "-",
2418
2418
  size: "-",
2419
- status: dim("skipped (--no-postgres)")
2419
+ status: dim$1("skipped (--no-postgres)")
2420
2420
  });
2421
- console.log(`\n${bold("Backup complete:")} ${backupDir}\n`);
2421
+ console.log(`\n${bold$1("Backup complete:")} ${backupDir}\n`);
2422
2422
  const labelWidth = Math.max(...results.map((r) => r.label.length)) + 2;
2423
2423
  for (const r of results) {
2424
2424
  const label = r.label.padEnd(labelWidth);
2425
- console.log(` ${bold(label)} ${r.status} ${dim(r.size)}`);
2425
+ console.log(` ${bold$1(label)} ${r.status} ${dim$1(r.size)}`);
2426
2426
  }
2427
- console.log(`\n ${dim("Path:")} ${backupDir}`);
2428
- console.log(` ${dim("To restore:")} pai restore ${backupDir}\n`);
2427
+ console.log(`\n ${dim$1("Path:")} ${backupDir}`);
2428
+ console.log(` ${dim$1("To restore:")} pai restore ${backupDir}\n`);
2429
2429
  });
2430
2430
  }
2431
2431
 
@@ -2481,35 +2481,35 @@ function registerRestoreCommands(program) {
2481
2481
  const backups = listBackups();
2482
2482
  if (backups.length === 0) {
2483
2483
  console.error(err(`No backups found in ${BACKUPS_DIR}`));
2484
- console.log(dim(" Run 'pai backup' to create one."));
2484
+ console.log(dim$1(" Run 'pai backup' to create one."));
2485
2485
  process.exit(1);
2486
2486
  }
2487
- console.log(`\n${bold("Available backups")} (newest first):\n`);
2487
+ console.log(`\n${bold$1("Available backups")} (newest first):\n`);
2488
2488
  backups.forEach((name, i) => {
2489
2489
  const dir = join(BACKUPS_DIR, name);
2490
2490
  const contents = backupContents(dir).join(", ");
2491
2491
  const marker = i === 0 ? ok(" ← latest") : "";
2492
- console.log(` ${bold(String(i + 1).padStart(2))}. ${formatBackupDate(name)}${marker}`);
2493
- console.log(` ${dim(dir)}`);
2494
- console.log(` ${dim("Contents:")} ${contents}`);
2492
+ console.log(` ${bold$1(String(i + 1).padStart(2))}. ${formatBackupDate(name)}${marker}`);
2493
+ console.log(` ${dim$1(dir)}`);
2494
+ console.log(` ${dim$1("Contents:")} ${contents}`);
2495
2495
  });
2496
2496
  console.log();
2497
2497
  resolvedDir = join(BACKUPS_DIR, backups[0]);
2498
- console.log(dim(`Using latest backup: ${resolvedDir}\n`));
2498
+ console.log(dim$1(`Using latest backup: ${resolvedDir}\n`));
2499
2499
  }
2500
2500
  const hasRegistry = existsSync(join(resolvedDir, "registry.db"));
2501
2501
  const hasConfig = existsSync(join(resolvedDir, "config.json"));
2502
2502
  const hasSql = existsSync(join(resolvedDir, "postgres-pai.sql"));
2503
2503
  const hasFed = existsSync(join(resolvedDir, "federation.db"));
2504
- console.log(`${bold("Backup contents:")}`);
2504
+ console.log(`${bold$1("Backup contents:")}`);
2505
2505
  console.log(` registry.db ${hasRegistry ? ok("present") : warn("missing")}`);
2506
2506
  console.log(` config.json ${hasConfig ? ok("present") : warn("missing")}`);
2507
2507
  console.log(` postgres-pai.sql ${hasSql && opts.postgres ? ok("present") : hasSql ? warn("present (skipped via --no-postgres)") : warn("missing")}`);
2508
- if (hasFed) console.log(` federation.db ${ok("present")} ${dim("(legacy)")}`);
2508
+ if (hasFed) console.log(` federation.db ${ok("present")} ${dim$1("(legacy)")}`);
2509
2509
  console.log(`\n${warn("WARNING:")} This will OVERWRITE your current PAI data.`);
2510
2510
  if (hasSql && opts.postgres) console.log(warn(" Postgres database will be dropped and recreated from the backup."));
2511
2511
  if (!(opts.yes || await confirm("Proceed with restore?"))) {
2512
- console.log(dim("Restore cancelled."));
2512
+ console.log(dim$1("Restore cancelled."));
2513
2513
  process.exit(0);
2514
2514
  }
2515
2515
  console.log();
@@ -2561,7 +2561,7 @@ function registerRestoreCommands(program) {
2561
2561
  });
2562
2562
  }
2563
2563
  if (hasSql && opts.postgres) {
2564
- console.log(dim(" Restoring Postgres database (this may take a while)..."));
2564
+ console.log(dim$1(" Restoring Postgres database (this may take a while)..."));
2565
2565
  try {
2566
2566
  execSync(`docker inspect ${DOCKER_CONTAINER} --format='{{.State.Status}}'`, { stdio: "pipe" });
2567
2567
  execSync(`docker exec ${DOCKER_CONTAINER} psql -U ${PG_USER} -c "DROP DATABASE IF EXISTS ${PG_DATABASE}; CREATE DATABASE ${PG_DATABASE} OWNER ${PG_USER};"`, {
@@ -2608,15 +2608,15 @@ function registerRestoreCommands(program) {
2608
2608
  const reason = !hasSql ? "no SQL dump in backup" : "--no-postgres";
2609
2609
  results.push({
2610
2610
  label: "Postgres DB",
2611
- status: dim(`skipped (${reason})`)
2611
+ status: dim$1(`skipped (${reason})`)
2612
2612
  });
2613
2613
  }
2614
- console.log(`\n${bold("Restore complete:")}\n`);
2614
+ console.log(`\n${bold$1("Restore complete:")}\n`);
2615
2615
  const labelWidth = Math.max(...results.map((r) => r.label.length)) + 2;
2616
- for (const r of results) console.log(` ${bold(r.label.padEnd(labelWidth))} ${r.status}`);
2616
+ for (const r of results) console.log(` ${bold$1(r.label.padEnd(labelWidth))} ${r.status}`);
2617
2617
  if (!results.some((r) => r.status.includes("\x1B[31m"))) {
2618
2618
  console.log(`\n ${ok("All done.")} You may need to restart the PAI daemon.\n`);
2619
- console.log(` ${dim("Restart:")} pai daemon restart\n`);
2619
+ console.log(` ${dim$1("Restart:")} pai daemon restart\n`);
2620
2620
  } else {
2621
2621
  console.log(`\n ${warn("Some items failed — check output above.")}\n`);
2622
2622
  process.exit(1);
@@ -3951,6 +3951,355 @@ function stepSummary(configUpdates, claudeMdGenerated, paiSkillInstalled, aiStee
3951
3951
  line$1();
3952
3952
  }
3953
3953
 
3954
+ //#endregion
3955
+ //#region src/tasks/resolver.ts
3956
+ /**
3957
+ * Fold a tracker-supplied string onto its comparable form.
3958
+ *
3959
+ * Container names are written for humans and carry decoration the registry
3960
+ * never sees: "Reading List 📚", "AcmeAPI", "Client Work". Strip the decoration
3961
+ * rather than demanding the user keep two naming schemes in sync.
3962
+ *
3963
+ * Emoji and pictographs are removed, separators collapse to a single hyphen,
3964
+ * and the result is lowercased — so "Client Work" and "client-work" agree.
3965
+ */
3966
+ function normalize$1(raw) {
3967
+ return raw.normalize("NFKD").replace(/[\u{1F000}-\u{1FAFF}\u{2600}-\u{27BF}\u{FE0F}\u{200D}]/gu, "").replace(/[^\p{L}\p{N}]+/gu, "-").replace(/^-+|-+$/g, "").toLowerCase();
3968
+ }
3969
+ /**
3970
+ * Load every alias registered in the PAI registry.
3971
+ *
3972
+ * Reads the curated shortlist only — the `aliases` table — deliberately. The
3973
+ * full registry commonly carries hundreds of projects with no aliases and
3974
+ * genuine ambiguity — separate entries routinely share a display name at
3975
+ * different paths — so widening this query resolves tasks to the wrong
3976
+ * directory. That failure is silent, which makes it worse than no match. Bus
3977
+ * participation is opt-in via `pai project name <identifier> <shortname>`.
3978
+ */
3979
+ function loadAliasMap(db) {
3980
+ const rows = db.prepare(`SELECT a.alias AS alias,
3981
+ p.slug AS slug,
3982
+ p.root_path AS rootPath
3983
+ FROM aliases a
3984
+ JOIN projects p ON p.id = a.project_id
3985
+ WHERE p.status != 'archived'`).all();
3986
+ const map = /* @__PURE__ */ new Map();
3987
+ for (const row of rows) {
3988
+ const target = {
3989
+ alias: row.alias,
3990
+ slug: row.slug,
3991
+ rootPath: row.rootPath
3992
+ };
3993
+ map.set(normalize$1(row.alias), target);
3994
+ const slugKey = normalize$1(row.slug);
3995
+ if (!map.has(slugKey)) map.set(slugKey, target);
3996
+ }
3997
+ return map;
3998
+ }
3999
+ /** Extract the project named by a `pai:<project>` label, if any. */
4000
+ function ownerLabel(labels) {
4001
+ for (const label of labels) {
4002
+ const trimmed = label.trim();
4003
+ if (trimmed.toLowerCase().startsWith(OWNER_LABEL_PREFIX)) {
4004
+ const value = trimmed.slice(OWNER_LABEL_PREFIX.length).trim();
4005
+ if (value) return value;
4006
+ }
4007
+ }
4008
+ return null;
4009
+ }
4010
+ /**
4011
+ * Resolve a task's owner.
4012
+ *
4013
+ * An unresolvable task is never an error: it returns UNROUTED with `rawHint`
4014
+ * set to whatever was tried, so a routine can say *why* it could not route it
4015
+ * rather than dropping it silently. A container like "Reading List 📚" maps to
4016
+ * no PAI project — that is expected, not a fault.
4017
+ */
4018
+ function resolveOwner(input, aliases) {
4019
+ const label = ownerLabel(input.labels);
4020
+ if (label) {
4021
+ const hit = aliases.get(normalize$1(label));
4022
+ if (hit) return {
4023
+ project: hit.alias,
4024
+ rootPath: hit.rootPath,
4025
+ source: "label"
4026
+ };
4027
+ return {
4028
+ ...UNROUTED,
4029
+ rawHint: label
4030
+ };
4031
+ }
4032
+ if (input.container) {
4033
+ const hit = aliases.get(normalize$1(input.container));
4034
+ if (hit) return {
4035
+ project: hit.alias,
4036
+ rootPath: hit.rootPath,
4037
+ source: "container"
4038
+ };
4039
+ return {
4040
+ ...UNROUTED,
4041
+ rawHint: input.container
4042
+ };
4043
+ }
4044
+ return { ...UNROUTED };
4045
+ }
4046
+
4047
+ //#endregion
4048
+ //#region src/tasks/providers/todoist.ts
4049
+ /**
4050
+ * Unified API v1. The older `/rest/v2` endpoints now return 410 Gone — they
4051
+ * were sunset, so anything still targeting them fails outright rather than
4052
+ * degrading.
4053
+ */
4054
+ const API = "https://api.todoist.com/api/v1";
4055
+ async function call(token, path, init) {
4056
+ const res = await fetch(`${API}${path}`, {
4057
+ method: init?.method ?? "GET",
4058
+ headers: {
4059
+ Authorization: `Bearer ${token}`,
4060
+ ...init?.body ? { "Content-Type": "application/json" } : {}
4061
+ },
4062
+ body: init?.body ? JSON.stringify(init.body) : void 0
4063
+ });
4064
+ if (!res.ok) {
4065
+ const hint = res.status === 401 ? " (token rejected — check it was copied in full)" : "";
4066
+ throw new Error(`Todoist ${res.status} ${res.statusText}${hint}`);
4067
+ }
4068
+ if (res.status === 204) return void 0;
4069
+ return await res.json();
4070
+ }
4071
+ /**
4072
+ * Drain a paginated collection.
4073
+ *
4074
+ * Every v1 list endpoint pages. Stopping at the first page would silently
4075
+ * truncate — the account here already exceeds one page of tasks — so follow the
4076
+ * cursor to exhaustion. The guard is a safety stop against a server that keeps
4077
+ * handing back the same cursor, not an intentional cap.
4078
+ */
4079
+ async function collect(token, path) {
4080
+ const out = [];
4081
+ let cursor;
4082
+ let guard = 0;
4083
+ do {
4084
+ const sep = path.includes("?") ? "&" : "?";
4085
+ const page = await call(token, cursor ? `${path}${sep}cursor=${encodeURIComponent(cursor)}` : path);
4086
+ out.push(...page.results ?? []);
4087
+ cursor = page.next_cursor;
4088
+ } while (cursor && ++guard < 100);
4089
+ return out;
4090
+ }
4091
+ /**
4092
+ * List every live project on the account.
4093
+ *
4094
+ * Exported because setup uses it both to let the user pick the bus root and to
4095
+ * validate the token — a bad token fails here, at install time, rather than
4096
+ * silently returning nothing during a routine.
4097
+ */
4098
+ async function listProjects(token) {
4099
+ return (await collect(token, "/projects")).filter((p) => !p.is_archived && !p.is_deleted).map((p) => ({
4100
+ id: p.id,
4101
+ name: p.name
4102
+ }));
4103
+ }
4104
+ /** Todoist priority is inverted: 4 is urgent, 1 is lowest. */
4105
+ function toPriority(wire) {
4106
+ switch (wire) {
4107
+ case 4: return "p1";
4108
+ case 3: return "p2";
4109
+ case 2: return "p3";
4110
+ default: return "p4";
4111
+ }
4112
+ }
4113
+ function fromPriority(p) {
4114
+ switch (p) {
4115
+ case "p1": return 4;
4116
+ case "p2": return 3;
4117
+ case "p3": return 2;
4118
+ default: return 1;
4119
+ }
4120
+ }
4121
+ var TodoistProvider = class {
4122
+ providerId = "todoist";
4123
+ constructor(config, aliases) {
4124
+ this.config = config;
4125
+ this.aliases = aliases;
4126
+ }
4127
+ /**
4128
+ * Resolution order is config → env → unconfigured. A missing token disables
4129
+ * the provider rather than throwing: a user without a tracker still gets a
4130
+ * fully working PAI.
4131
+ */
4132
+ token() {
4133
+ return this.config.apiKey?.trim() || process.env.TODOIST_API_KEY?.trim() || null;
4134
+ }
4135
+ isConfigured() {
4136
+ return Boolean(this.config.enabled && this.token() && this.config.rootProjectId);
4137
+ }
4138
+ async listOpen(opts = {}) {
4139
+ const token = this.token();
4140
+ if (!token || !this.config.rootProjectId) return [];
4141
+ const projects = await collect(token, "/projects");
4142
+ const root = this.config.rootProjectId;
4143
+ const inBus = /* @__PURE__ */ new Map();
4144
+ for (const p of projects) {
4145
+ if (p.is_archived || p.is_deleted) continue;
4146
+ if (p.id === root || p.parent_id === root) inBus.set(p.id, p);
4147
+ }
4148
+ if (inBus.size === 0) return [];
4149
+ const wire = [];
4150
+ for (const id of inBus.keys()) wire.push(...await collect(token, `/tasks?project_id=${encodeURIComponent(id)}`));
4151
+ const out = [];
4152
+ for (const w of wire) {
4153
+ if (w.checked || w.is_deleted) continue;
4154
+ const container = inBus.get(w.project_id);
4155
+ if (!container) continue;
4156
+ const containerName = container.id === root ? null : container.name;
4157
+ const owner = resolveOwner({
4158
+ labels: w.labels ?? [],
4159
+ container: containerName
4160
+ }, this.aliases);
4161
+ if (opts.owner && owner.project !== opts.owner) continue;
4162
+ if (opts.includeUnrouted === false && owner.project === null) continue;
4163
+ const due = w.due?.datetime ?? w.due?.date ?? null;
4164
+ if (opts.dueBefore) {
4165
+ if (!due || due.slice(0, 10) > opts.dueBefore) continue;
4166
+ }
4167
+ out.push({
4168
+ id: w.id,
4169
+ title: w.content,
4170
+ body: w.description ?? "",
4171
+ owner,
4172
+ due,
4173
+ priority: toPriority(w.priority),
4174
+ labels: w.labels ?? [],
4175
+ sourceUrl: `https://app.todoist.com/app/task/${w.id}`
4176
+ });
4177
+ if (opts.limit && out.length >= opts.limit) break;
4178
+ }
4179
+ return out;
4180
+ }
4181
+ async add(task) {
4182
+ const token = this.token();
4183
+ if (!token || !this.config.rootProjectId) throw new Error("Todoist provider is not configured — run `pai setup`.");
4184
+ const labels = [...task.labels ?? []];
4185
+ if (task.owner && !labels.some((l) => l.toLowerCase() === `pai:${task.owner.toLowerCase()}`)) labels.push(`pai:${task.owner}`);
4186
+ const sectionId = task.owner ? void 0 : this.config.findingsSectionId;
4187
+ const created = await call(token, "/tasks", {
4188
+ method: "POST",
4189
+ body: {
4190
+ content: task.title,
4191
+ description: task.body,
4192
+ project_id: this.config.rootProjectId,
4193
+ ...sectionId ? { section_id: sectionId } : {},
4194
+ labels,
4195
+ priority: fromPriority(task.priority),
4196
+ ...task.due ? { due_date: task.due } : {}
4197
+ }
4198
+ });
4199
+ return {
4200
+ id: created.id,
4201
+ title: created.content,
4202
+ body: created.description ?? "",
4203
+ owner: task.owner ? resolveOwner({
4204
+ labels,
4205
+ container: null
4206
+ }, this.aliases) : { ...UNROUTED },
4207
+ due: created.due?.datetime ?? created.due?.date ?? null,
4208
+ priority: toPriority(created.priority),
4209
+ labels: created.labels ?? [],
4210
+ sourceUrl: `https://app.todoist.com/app/task/${created.id}`
4211
+ };
4212
+ }
4213
+ async complete(id) {
4214
+ const token = this.token();
4215
+ if (!token) throw new Error("Todoist provider is not configured — run `pai setup`.");
4216
+ await call(token, `/tasks/${id}/close`, { method: "POST" });
4217
+ }
4218
+ };
4219
+
4220
+ //#endregion
4221
+ //#region src/cli/commands/setup/steps/16-task-bus.ts
4222
+ /** Step 16: Task bus — optional external tracker for cross-session work. */
4223
+ async function stepTaskBus(rl) {
4224
+ section("Step 16: Task Bus (Optional)");
4225
+ const current = readConfigRaw().tasks ?? {};
4226
+ const todoist = current.providers?.todoist;
4227
+ if (todoist?.apiKey && todoist?.rootProjectId) {
4228
+ console.log(c.ok("Task bus already configured. Skipping."));
4229
+ return { tasks: current };
4230
+ }
4231
+ line$1();
4232
+ line$1(" The task bus lets any PAI session file work into a shared tracker, so a");
4233
+ line$1(" routine can pick it up later and hand it to the session that owns it.");
4234
+ line$1();
4235
+ line$1(" PAI works fully without this. Skip it unless you want cross-session tasks.");
4236
+ line$1();
4237
+ if (!await promptYesNo(rl, "Enable the task bus (requires a Todoist account)?", false)) {
4238
+ line$1();
4239
+ console.log(c.ok("Skipping the task bus."));
4240
+ console.log(c.dim(" Add later by re-running `pai setup`."));
4241
+ return { tasks: { enabled: false } };
4242
+ }
4243
+ line$1();
4244
+ line$1(" Get your API token from Todoist:");
4245
+ line$1(c.dim(" Settings -> Integrations -> Developer"));
4246
+ line$1(c.dim(" https://app.todoist.com/app/settings/integrations/developer"));
4247
+ line$1();
4248
+ line$1(" It grants full read/write access to your Todoist. Treat it like a password.");
4249
+ line$1();
4250
+ const envToken = process.env.TODOIST_API_KEY?.trim();
4251
+ let apiKey = todoist?.apiKey ?? "";
4252
+ if (!apiKey && envToken) {
4253
+ if (await promptYesNo(rl, "Found TODOIST_API_KEY in the environment. Use it?", true)) apiKey = envToken;
4254
+ }
4255
+ while (!apiKey) {
4256
+ apiKey = (await prompt(rl, " Paste your Todoist API token: ")).trim();
4257
+ if (!apiKey) console.log(c.warn(" A token is required, or answer no to skip the task bus."));
4258
+ }
4259
+ line$1();
4260
+ line$1(" Fetching your Todoist projects...");
4261
+ let projects;
4262
+ try {
4263
+ projects = await listProjects(apiKey);
4264
+ } catch (e) {
4265
+ line$1();
4266
+ console.log(c.warn(`Could not reach Todoist: ${e instanceof Error ? e.message : String(e)}`));
4267
+ console.log(c.dim(" The token may be wrong, or the network is unavailable."));
4268
+ console.log(c.dim(" Saved nothing. Re-run `pai setup` to try again."));
4269
+ return { tasks: { enabled: false } };
4270
+ }
4271
+ if (projects.length === 0) {
4272
+ console.log(c.warn(" No projects found. Create one in Todoist first, then re-run setup."));
4273
+ return { tasks: { enabled: false } };
4274
+ }
4275
+ line$1();
4276
+ line$1(" Which project should hold the bus? Create a dedicated one if unsure.");
4277
+ line$1();
4278
+ projects.forEach((p, i) => line$1(` ${String(i + 1).padStart(2)}. ${p.name}`));
4279
+ line$1();
4280
+ let rootProjectId = "";
4281
+ while (!rootProjectId) {
4282
+ const answer = (await prompt(rl, ` Project number [1-${projects.length}]: `)).trim();
4283
+ const idx = Number.parseInt(answer, 10) - 1;
4284
+ if (Number.isInteger(idx) && idx >= 0 && idx < projects.length) rootProjectId = projects[idx].id;
4285
+ else console.log(c.warn(` Enter a number between 1 and ${projects.length}.`));
4286
+ }
4287
+ const chosen = projects.find((p) => p.id === rootProjectId);
4288
+ line$1();
4289
+ console.log(c.ok(`Task bus enabled on "${chosen.name}" (id ${rootProjectId}).`));
4290
+ console.log(c.dim(" Label a task `pai:<project>` to assign it to a PAI project."));
4291
+ console.log(c.dim(" Only projects with an alias can be dispatched to — see `pai project name`."));
4292
+ return { tasks: {
4293
+ enabled: true,
4294
+ autoDispatch: await promptYesNo(rl, "Automatically hand tasks to the owning session (needs AIBroker)?", false),
4295
+ providers: { todoist: {
4296
+ enabled: true,
4297
+ apiKey,
4298
+ rootProjectId
4299
+ } }
4300
+ } };
4301
+ }
4302
+
3954
4303
  //#endregion
3955
4304
  //#region src/cli/commands/setup/index.ts
3956
4305
  async function runSetup() {
@@ -3986,9 +4335,11 @@ async function runSetup() {
3986
4335
  const daemonInstalled = await stepDaemon(rl);
3987
4336
  const mcpRegistered = await stepMcp(rl);
3988
4337
  await stepDirectories(rl);
4338
+ const taskConfig = await stepTaskBus(rl);
3989
4339
  const allUpdates = {
3990
4340
  ...storageConfig,
3991
- ...embeddingConfig
4341
+ ...embeddingConfig,
4342
+ ...taskConfig
3992
4343
  };
3993
4344
  mergeConfig(allUpdates);
3994
4345
  line$1();
@@ -4403,8 +4754,23 @@ function removeOldBacklink(filePath) {
4403
4754
  }
4404
4755
  }
4405
4756
  /**
4757
+ * Return true if the note has a meaningful body — i.e. content beyond blank
4758
+ * lines, horizontal rules, and an existing master-note footer.
4759
+ *
4760
+ * Guards against stamping a "← Master" backlink onto an empty note, which
4761
+ * produces a misleading dangling stub (empty body + footer only). Whatever
4762
+ * emptied the note, we must not decorate it as if it were a real session.
4763
+ */
4764
+ function hasMeaningfulBody(content) {
4765
+ return content.replace(/\n---\n\[\[_[^\]]*-master\|[^\]]*\]\]\n?$/, "").split("\n").filter((line) => {
4766
+ const t = line.trim();
4767
+ return t !== "" && t !== "---";
4768
+ }).join("").length > 0;
4769
+ }
4770
+ /**
4406
4771
  * Append the master note backlink footer to a session file, idempotently.
4407
- * Only writes if the sentinel string is not already present.
4772
+ * Only writes if the sentinel string is not already present AND the note has
4773
+ * a meaningful body (never stamp a footer onto an empty note).
4408
4774
  */
4409
4775
  function appendBacklinkIfMissing(filePath, slug, displayName) {
4410
4776
  let content;
@@ -4413,6 +4779,7 @@ function appendBacklinkIfMissing(filePath, slug, displayName) {
4413
4779
  } catch {
4414
4780
  return false;
4415
4781
  }
4782
+ if (!hasMeaningfulBody(content)) return false;
4416
4783
  const masterName = masterFilename(slug).replace(/\.md$/, "");
4417
4784
  const sentinel = `[[${masterName}|`;
4418
4785
  if (content.includes(sentinel)) return false;
@@ -4747,7 +5114,7 @@ function cmdSync(db, opts) {
4747
5114
  if (!q) {
4748
5115
  console.log();
4749
5116
  console.log(header(" PAI Obsidian Sync"));
4750
- console.log(dim(` Vault: ${vaultPath}`));
5117
+ console.log(dim$1(` Vault: ${vaultPath}`));
4751
5118
  console.log();
4752
5119
  }
4753
5120
  if (!q) process.stdout.write(" Syncing symlinks...");
@@ -4783,7 +5150,7 @@ function cmdStatus$1(db, opts) {
4783
5150
  const report = checkHealth(vaultPath, db);
4784
5151
  console.log();
4785
5152
  console.log(header(" PAI Obsidian Vault Status"));
4786
- console.log(dim(` Vault: ${vaultPath}`));
5153
+ console.log(dim$1(` Vault: ${vaultPath}`));
4787
5154
  console.log();
4788
5155
  if (!existsSync(vaultPath)) {
4789
5156
  console.log(warn(" Vault directory does not exist. Run: pai obsidian sync"));
@@ -4797,27 +5164,27 @@ function cmdStatus$1(db, opts) {
4797
5164
  console.log(` ${chalk.green("Healthy:")} ${healthyCount} ${chalk.red("Broken:")} ${brokenCount} ${chalk.yellow("Orphaned:")} ${orphanedCount} ${chalk.cyan("Missing:")} ${missingCount}`);
4798
5165
  console.log();
4799
5166
  if (report.healthy.length && report.healthy.length <= 10) {
4800
- console.log(bold(" Healthy symlinks:"));
4801
- for (const h of report.healthy) console.log(` ${chalk.green("✓")} ${bold(h.slug)} ${dim("→ " + (h.target ?? ""))}`);
5167
+ console.log(bold$1(" Healthy symlinks:"));
5168
+ for (const h of report.healthy) console.log(` ${chalk.green("✓")} ${bold$1(h.slug)} ${dim$1("→ " + (h.target ?? ""))}`);
4802
5169
  console.log();
4803
5170
  } else if (report.healthy.length > 10) {
4804
- console.log(bold(` ${report.healthy.length} healthy symlinks (all good)`));
5171
+ console.log(bold$1(` ${report.healthy.length} healthy symlinks (all good)`));
4805
5172
  console.log();
4806
5173
  }
4807
5174
  if (report.broken.length) {
4808
5175
  console.log(err(" Broken symlinks (target missing):"));
4809
- for (const b of report.broken) console.log(` ${chalk.red("✗")} ${bold(b.slug)} ${dim(b.notes)}`);
4810
- console.log(dim(" Fix: pai obsidian sync"));
5176
+ for (const b of report.broken) console.log(` ${chalk.red("✗")} ${bold$1(b.slug)} ${dim$1(b.notes)}`);
5177
+ console.log(dim$1(" Fix: pai obsidian sync"));
4811
5178
  console.log();
4812
5179
  }
4813
5180
  if (report.orphaned.length) {
4814
5181
  console.log(warn(" Orphaned symlinks (no registry project):"));
4815
- for (const o of report.orphaned) console.log(` ${chalk.yellow("?")} ${bold(o.slug)} ${dim(o.notes)}`);
5182
+ for (const o of report.orphaned) console.log(` ${chalk.yellow("?")} ${bold$1(o.slug)} ${dim$1(o.notes)}`);
4816
5183
  console.log();
4817
5184
  }
4818
5185
  if (report.missing.length) {
4819
5186
  console.log(warn(" Projects with Notes/ but no vault symlink:"));
4820
- for (const m of report.missing) console.log(` ${chalk.cyan("→")} ${bold(m.slug)} ${dim(m.notes)}`);
5187
+ for (const m of report.missing) console.log(` ${chalk.cyan("→")} ${bold$1(m.slug)} ${dim$1(m.notes)}`);
4821
5188
  console.log();
4822
5189
  }
4823
5190
  if (brokenCount === 0 && orphanedCount === 0 && missingCount === 0) {
@@ -4831,17 +5198,17 @@ function cmdOpen(opts) {
4831
5198
  const vaultName = parts[parts.length - 1] ?? "obsidian-vault";
4832
5199
  if (!existsSync(vaultPath)) {
4833
5200
  console.error(err(`Vault not found at: ${vaultPath}`));
4834
- console.error(dim(" Run: pai obsidian sync"));
5201
+ console.error(dim$1(" Run: pai obsidian sync"));
4835
5202
  process.exit(1);
4836
5203
  }
4837
5204
  const url = `obsidian://open?vault=${encodeURIComponent(vaultName)}`;
4838
- console.log(dim(` Opening: ${url}`));
5205
+ console.log(dim$1(` Opening: ${url}`));
4839
5206
  try {
4840
5207
  execSync(`open "${url}"`, { stdio: "ignore" });
4841
5208
  console.log(ok(` Opened vault "${vaultName}" in Obsidian.`));
4842
5209
  } catch (e) {
4843
5210
  console.error(err(` Failed to open Obsidian: ${e}`));
4844
- console.error(dim(" Ensure Obsidian is installed and the vault is registered in Obsidian."));
5211
+ console.error(dim$1(" Ensure Obsidian is installed and the vault is registered in Obsidian."));
4845
5212
  process.exit(1);
4846
5213
  }
4847
5214
  }
@@ -4890,15 +5257,15 @@ async function cmdExplore(note, opts) {
4890
5257
  });
4891
5258
  console.log();
4892
5259
  console.log(header(" PAI Zettel Explore"));
4893
- console.log(dim(` Starting note: ${note}`));
4894
- console.log(dim(` Depth: ${depth} Direction: ${direction} Mode: ${mode}`));
5260
+ console.log(dim$1(` Starting note: ${note}`));
5261
+ console.log(dim$1(` Depth: ${depth} Direction: ${direction} Mode: ${mode}`));
4895
5262
  console.log();
4896
5263
  if (result.nodes.length === 0) {
4897
5264
  console.log(warn(" No connected notes found. Check that the note path exists in the vault index."));
4898
5265
  console.log();
4899
5266
  return;
4900
5267
  }
4901
- console.log(` ${chalk.cyan("●")} ${bold(shortPath(result.root))} ${dim("(root)")}`);
5268
+ console.log(` ${chalk.cyan("●")} ${bold$1(shortPath(result.root))} ${dim$1("(root)")}`);
4902
5269
  const byDepth = /* @__PURE__ */ new Map();
4903
5270
  for (const node of result.nodes) {
4904
5271
  const list = byDepth.get(node.depth) ?? [];
@@ -4909,20 +5276,20 @@ async function cmdExplore(note, opts) {
4909
5276
  const nodes = byDepth.get(d) ?? [];
4910
5277
  if (nodes.length === 0) continue;
4911
5278
  console.log();
4912
- console.log(dim(` ${" ".repeat(d - 1)}Depth ${d}:`));
5279
+ console.log(dim$1(` ${" ".repeat(d - 1)}Depth ${d}:`));
4913
5280
  for (const node of nodes) {
4914
5281
  const indent = " ".repeat(d);
4915
5282
  const isBranching = result.branchingPoints.includes(node.path);
4916
5283
  const typeColor = node.linkType === "sequential" ? chalk.blue : chalk.magenta;
4917
5284
  const branchMark = isBranching ? chalk.yellow(" ⑂ branching") : "";
4918
5285
  const title = node.title ?? shortPath(node.path);
4919
- const stats = dim(`in:${node.inbound} out:${node.outbound}`);
4920
- console.log(` ${indent}${typeColor("→")} ${bold(title)}${branchMark} ${stats} ${dim(typeColor(node.linkType))}`);
5286
+ const stats = dim$1(`in:${node.inbound} out:${node.outbound}`);
5287
+ console.log(` ${indent}${typeColor("→")} ${bold$1(title)}${branchMark} ${stats} ${dim$1(typeColor(node.linkType))}`);
4921
5288
  }
4922
5289
  }
4923
5290
  console.log();
4924
5291
  const edgeSummary = `${result.edges.length} edges (${result.edges.filter((e) => e.type === "sequential").length} sequential, ${result.edges.filter((e) => e.type === "associative").length} associative)`;
4925
- console.log(dim(` ${edgeSummary}`));
5292
+ console.log(dim$1(` ${edgeSummary}`));
4926
5293
  if (result.branchingPoints.length > 0) console.log(ok(` ${result.branchingPoints.length} branching point(s) found`));
4927
5294
  if (result.maxDepthReached) console.log(warn(" Max depth reached — use --depth to explore further"));
4928
5295
  console.log();
@@ -4954,31 +5321,31 @@ async function cmdHealth(opts) {
4954
5321
  });
4955
5322
  console.log();
4956
5323
  console.log(header(" PAI Zettel Health"));
4957
- console.log(dim(` Scope: ${scope}${scope === "project" ? ` Path: ${projectPath ?? "(none)"}` : ""}${scope === "recent" ? ` Days: ${recentDays}` : ""}`));
5324
+ console.log(dim$1(` Scope: ${scope}${scope === "project" ? ` Path: ${projectPath ?? "(none)"}` : ""}${scope === "recent" ? ` Days: ${recentDays}` : ""}`));
4958
5325
  console.log();
4959
5326
  const score = result.healthScore;
4960
5327
  const scoreColor = score >= 80 ? chalk.green : score >= 60 ? chalk.yellow : chalk.red;
4961
5328
  const barWidth = 30;
4962
5329
  const filled = Math.round(score / 100 * barWidth);
4963
- const bar = scoreColor("█".repeat(filled)) + dim("░".repeat(barWidth - filled));
4964
- console.log(` Health Score: ${scoreColor(bold(String(score)))}% [${bar}]`);
5330
+ const bar = scoreColor("█".repeat(filled)) + dim$1("░".repeat(barWidth - filled));
5331
+ console.log(` Health Score: ${scoreColor(bold$1(String(score)))}% [${bar}]`);
4965
5332
  console.log();
4966
- console.log(dim(` Files: ${result.totalFiles} Links: ${result.totalLinks}`));
5333
+ console.log(dim$1(` Files: ${result.totalFiles} Links: ${result.totalLinks}`));
4967
5334
  console.log();
4968
5335
  if (result.deadLinks.length === 0) console.log(ok(" Dead links: none"));
4969
5336
  else {
4970
5337
  console.log(warn(` Dead links: ${result.deadLinks.length}`));
4971
5338
  const preview = result.deadLinks.slice(0, 10);
4972
- for (const dl of preview) console.log(` ${chalk.red("✗")} ${dim(shortPath(dl.sourcePath))} → ${bold(dl.targetRaw)} ${dim(`(line ${dl.lineNumber})`)}`);
4973
- if (result.deadLinks.length > 10) console.log(dim(` ... and ${result.deadLinks.length - 10} more`));
5339
+ for (const dl of preview) console.log(` ${chalk.red("✗")} ${dim$1(shortPath(dl.sourcePath))} → ${bold$1(dl.targetRaw)} ${dim$1(`(line ${dl.lineNumber})`)}`);
5340
+ if (result.deadLinks.length > 10) console.log(dim$1(` ... and ${result.deadLinks.length - 10} more`));
4974
5341
  console.log();
4975
5342
  }
4976
5343
  if (result.orphans.length === 0) console.log(ok(" Orphan notes: none"));
4977
5344
  else {
4978
5345
  console.log(warn(` Orphan notes: ${result.orphans.length}`));
4979
5346
  const preview = result.orphans.slice(0, 10);
4980
- for (const o of preview) console.log(` ${chalk.yellow("○")} ${dim(shortPath(o))}`);
4981
- if (result.orphans.length > 10) console.log(dim(` ... and ${result.orphans.length - 10} more`));
5347
+ for (const o of preview) console.log(` ${chalk.yellow("○")} ${dim$1(shortPath(o))}`);
5348
+ if (result.orphans.length > 10) console.log(dim$1(` ... and ${result.orphans.length - 10} more`));
4982
5349
  console.log();
4983
5350
  }
4984
5351
  if (result.disconnectedClusters <= 1) console.log(ok(" Disconnected clusters: 1 (fully connected)"));
@@ -4987,8 +5354,8 @@ async function cmdHealth(opts) {
4987
5354
  else {
4988
5355
  console.log(warn(` Low-connectivity: ${result.lowConnectivity.length} note(s) with ≤1 link`));
4989
5356
  const preview = result.lowConnectivity.slice(0, 5);
4990
- for (const lc of preview) console.log(` ${chalk.dim("—")} ${dim(shortPath(lc))}`);
4991
- if (result.lowConnectivity.length > 5) console.log(dim(` ... and ${result.lowConnectivity.length - 5} more`));
5357
+ for (const lc of preview) console.log(` ${chalk.dim("—")} ${dim$1(shortPath(lc))}`);
5358
+ if (result.lowConnectivity.length > 5) console.log(dim$1(` ... and ${result.lowConnectivity.length - 5} more`));
4992
5359
  }
4993
5360
  console.log();
4994
5361
  }
@@ -5018,8 +5385,8 @@ async function cmdSurprise(note, opts) {
5018
5385
  const db = getFedDb();
5019
5386
  console.log();
5020
5387
  console.log(header(" PAI Zettel Surprise"));
5021
- console.log(dim(` Reference note: ${note}`));
5022
- process.stdout.write(dim(" Searching for surprising connections...\n"));
5388
+ console.log(dim$1(` Reference note: ${note}`));
5389
+ process.stdout.write(dim$1(" Searching for surprising connections...\n"));
5023
5390
  const results = await zettelSurprise(db, {
5024
5391
  referencePath: note,
5025
5392
  vaultProjectId,
@@ -5033,16 +5400,16 @@ async function cmdSurprise(note, opts) {
5033
5400
  return;
5034
5401
  }
5035
5402
  console.log();
5036
- console.log(bold(` Found ${results.length} surprising connection(s):`));
5403
+ console.log(bold$1(` Found ${results.length} surprising connection(s):`));
5037
5404
  console.log();
5038
5405
  for (let i = 0; i < results.length; i++) {
5039
5406
  const r = results[i];
5040
5407
  const title = r.title ?? shortPath(r.path);
5041
5408
  const surpriseBar = Math.round(r.surpriseScore * 10);
5042
- console.log(` ${chalk.cyan(String(i + 1).padStart(2, " "))}. ${bold(title)}`);
5043
- console.log(` ${dim("Surprise:")} ${chalk.magenta(r.surpriseScore.toFixed(3))} ${"■".repeat(surpriseBar)}${"□".repeat(10 - surpriseBar)}`);
5044
- console.log(` ${dim("Cosine:")} ${r.cosineSimilarity.toFixed(3)} ${dim("Graph distance:")} ${r.graphDistance}`);
5045
- if (r.sharedSnippet) console.log(` ${dim("Context:")} ${r.sharedSnippet.slice(0, 120)}`);
5409
+ console.log(` ${chalk.cyan(String(i + 1).padStart(2, " "))}. ${bold$1(title)}`);
5410
+ console.log(` ${dim$1("Surprise:")} ${chalk.magenta(r.surpriseScore.toFixed(3))} ${"■".repeat(surpriseBar)}${"□".repeat(10 - surpriseBar)}`);
5411
+ console.log(` ${dim$1("Cosine:")} ${r.cosineSimilarity.toFixed(3)} ${dim$1("Graph distance:")} ${r.graphDistance}`);
5412
+ if (r.sharedSnippet) console.log(` ${dim$1("Context:")} ${r.sharedSnippet.slice(0, 120)}`);
5046
5413
  console.log();
5047
5414
  }
5048
5415
  }
@@ -5071,8 +5438,8 @@ async function cmdSuggest(note, opts) {
5071
5438
  const db = getFedDb();
5072
5439
  console.log();
5073
5440
  console.log(header(" PAI Zettel Suggest"));
5074
- console.log(dim(` Note: ${note}`));
5075
- process.stdout.write(dim(" Computing suggestions...\n"));
5441
+ console.log(dim$1(` Note: ${note}`));
5442
+ process.stdout.write(dim$1(" Computing suggestions...\n"));
5076
5443
  const suggestions = await zettelSuggest(db, {
5077
5444
  notePath: note,
5078
5445
  vaultProjectId,
@@ -5085,15 +5452,15 @@ async function cmdSuggest(note, opts) {
5085
5452
  return;
5086
5453
  }
5087
5454
  console.log();
5088
- console.log(bold(` ${suggestions.length} suggested connection(s):`));
5455
+ console.log(bold$1(` ${suggestions.length} suggested connection(s):`));
5089
5456
  console.log();
5090
5457
  for (let i = 0; i < suggestions.length; i++) {
5091
5458
  const s = suggestions[i];
5092
5459
  const title = s.title ?? shortPath(s.path);
5093
- console.log(` ${chalk.green(String(i + 1).padStart(2, " "))}. ${bold(title)}`);
5094
- 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)}`);
5095
- console.log(` ${dim("Reason:")} ${s.reason}`);
5096
- console.log(` ${dim("Wikilink:")} ${chalk.cyan(s.suggestedWikilink)}`);
5460
+ console.log(` ${chalk.green(String(i + 1).padStart(2, " "))}. ${bold$1(title)}`);
5461
+ console.log(` ${dim$1("Score:")} ${chalk.green(s.score.toFixed(3))} ${dim$1("Semantic:")} ${s.semanticScore.toFixed(2)} ${dim$1("Tag:")} ${s.tagScore.toFixed(2)} ${dim$1("Neighbor:")} ${s.neighborScore.toFixed(2)}`);
5462
+ console.log(` ${dim$1("Reason:")} ${s.reason}`);
5463
+ console.log(` ${dim$1("Wikilink:")} ${chalk.cyan(s.suggestedWikilink)}`);
5097
5464
  console.log();
5098
5465
  }
5099
5466
  }
@@ -5122,8 +5489,8 @@ async function cmdConverse(question, opts) {
5122
5489
  const db = getFedDb();
5123
5490
  console.log();
5124
5491
  console.log(header(" PAI Zettel Converse"));
5125
- console.log(dim(` Question: "${question}"`));
5126
- process.stdout.write(dim(" Searching vault for relevant notes...\n"));
5492
+ console.log(dim$1(` Question: "${question}"`));
5493
+ process.stdout.write(dim$1(" Searching vault for relevant notes...\n"));
5127
5494
  const result = await zettelConverse(db, {
5128
5495
  question,
5129
5496
  vaultProjectId,
@@ -5136,24 +5503,24 @@ async function cmdConverse(question, opts) {
5136
5503
  return;
5137
5504
  }
5138
5505
  console.log();
5139
- console.log(bold(` ${result.relevantNotes.length} relevant note(s) from ${result.domains.length} domain(s):`));
5140
- console.log(dim(` Domains: ${result.domains.join(", ")}`));
5506
+ console.log(bold$1(` ${result.relevantNotes.length} relevant note(s) from ${result.domains.length} domain(s):`));
5507
+ console.log(dim$1(` Domains: ${result.domains.join(", ")}`));
5141
5508
  console.log();
5142
5509
  for (const note of result.relevantNotes) {
5143
5510
  const title = note.title ?? shortPath(note.path);
5144
- console.log(` ${chalk.cyan("◆")} ${bold(title)} ${dim(`[${note.domain}] score: ${note.score.toFixed(3)}`)}`);
5145
- if (note.snippet) console.log(` ${dim(note.snippet.slice(0, 200))}`);
5511
+ console.log(` ${chalk.cyan("◆")} ${bold$1(title)} ${dim$1(`[${note.domain}] score: ${note.score.toFixed(3)}`)}`);
5512
+ if (note.snippet) console.log(` ${dim$1(note.snippet.slice(0, 200))}`);
5146
5513
  console.log();
5147
5514
  }
5148
5515
  if (result.connections.length > 0) {
5149
- console.log(bold(" Cross-domain connections:"));
5150
- for (const conn of result.connections.slice(0, 10)) console.log(` ${chalk.magenta("⟷")} ${dim(conn.fromDomain)} ${chalk.dim("→")} ${dim(conn.toDomain)} ${dim(shortPath(conn.fromPath))} → ${dim(shortPath(conn.toPath))} ${dim(`strength: ${conn.strength}`)}`);
5516
+ console.log(bold$1(" Cross-domain connections:"));
5517
+ for (const conn of result.connections.slice(0, 10)) console.log(` ${chalk.magenta("⟷")} ${dim$1(conn.fromDomain)} ${chalk.dim("→")} ${dim$1(conn.toDomain)} ${dim$1(shortPath(conn.fromPath))} → ${dim$1(shortPath(conn.toPath))} ${dim$1(`strength: ${conn.strength}`)}`);
5151
5518
  console.log();
5152
5519
  }
5153
- console.log(bold(" Synthesis prompt (paste into your AI):"));
5520
+ console.log(bold$1(" Synthesis prompt (paste into your AI):"));
5154
5521
  console.log();
5155
5522
  const promptLines = result.synthesisPrompt.split("\n");
5156
- for (const line of promptLines) console.log(` ${dim(line)}`);
5523
+ for (const line of promptLines) console.log(` ${dim$1(line)}`);
5157
5524
  console.log();
5158
5525
  }
5159
5526
  function registerConverseCommand(parent) {
@@ -5183,8 +5550,8 @@ async function cmdThemes(opts) {
5183
5550
  const db = getFedDb();
5184
5551
  console.log();
5185
5552
  console.log(header(" PAI Zettel Themes"));
5186
- console.log(dim(` Lookback: ${lookbackDays}d Min cluster: ${minClusterSize} Threshold: ${similarityThreshold}`));
5187
- process.stdout.write(dim(" Detecting emerging themes...\n"));
5553
+ console.log(dim$1(` Lookback: ${lookbackDays}d Min cluster: ${minClusterSize} Threshold: ${similarityThreshold}`));
5554
+ process.stdout.write(dim$1(" Detecting emerging themes...\n"));
5188
5555
  const result = await zettelThemes(db, {
5189
5556
  vaultProjectId,
5190
5557
  lookbackDays,
@@ -5200,20 +5567,20 @@ async function cmdThemes(opts) {
5200
5567
  const fromDate = new Date(result.timeWindow.from).toISOString().slice(0, 10);
5201
5568
  const toDate = new Date(result.timeWindow.to).toISOString().slice(0, 10);
5202
5569
  console.log();
5203
- console.log(bold(` ${result.themes.length} theme(s) from ${result.totalNotesAnalyzed} notes [${fromDate} → ${toDate}]:`));
5570
+ console.log(bold$1(` ${result.themes.length} theme(s) from ${result.totalNotesAnalyzed} notes [${fromDate} → ${toDate}]:`));
5204
5571
  console.log();
5205
5572
  for (let i = 0; i < result.themes.length; i++) {
5206
5573
  const cluster = result.themes[i];
5207
5574
  const diversityBar = Math.round(cluster.folderDiversity * 10);
5208
5575
  const indexSuggestion = cluster.suggestIndexNote ? chalk.yellow(" ⚑ suggest index note") : "";
5209
- console.log(` ${chalk.cyan(String(i + 1).padStart(2, " "))}. ${bold(cluster.label)}${indexSuggestion}`);
5210
- console.log(` ${dim("Notes:")} ${cluster.size} ${dim("Diversity:")} ${"█".repeat(diversityBar)}${"░".repeat(10 - diversityBar)} ${cluster.folderDiversity.toFixed(2)} ${dim("Linked:")} ${Math.round(cluster.linkedRatio * 100)}%`);
5576
+ console.log(` ${chalk.cyan(String(i + 1).padStart(2, " "))}. ${bold$1(cluster.label)}${indexSuggestion}`);
5577
+ console.log(` ${dim$1("Notes:")} ${cluster.size} ${dim$1("Diversity:")} ${"█".repeat(diversityBar)}${"░".repeat(10 - diversityBar)} ${cluster.folderDiversity.toFixed(2)} ${dim$1("Linked:")} ${Math.round(cluster.linkedRatio * 100)}%`);
5211
5578
  const preview = cluster.notes.slice(0, 5);
5212
5579
  for (const note of preview) {
5213
5580
  const title = note.title ?? shortPath(note.path);
5214
- console.log(` ${dim("•")} ${title}`);
5581
+ console.log(` ${dim$1("•")} ${title}`);
5215
5582
  }
5216
- if (cluster.notes.length > 5) console.log(dim(` ... and ${cluster.notes.length - 5} more`));
5583
+ if (cluster.notes.length > 5) console.log(dim$1(` ... and ${cluster.notes.length - 5} more`));
5217
5584
  console.log();
5218
5585
  }
5219
5586
  }
@@ -5295,12 +5662,12 @@ function typeColor(type) {
5295
5662
  }
5296
5663
  }
5297
5664
  function fmtTs$2(ts) {
5298
- if (!ts) return dim("—");
5665
+ if (!ts) return dim$1("—");
5299
5666
  try {
5300
5667
  const d = new Date(ts);
5301
5668
  return `${d.toISOString().slice(0, 10)} ${d.toISOString().slice(11, 16)}`;
5302
5669
  } catch {
5303
- return dim("—");
5670
+ return dim$1("—");
5304
5671
  }
5305
5672
  }
5306
5673
  function trunc$1(s, maxLen) {
@@ -5319,7 +5686,7 @@ async function cmdList$2(opts) {
5319
5686
  observations = await ipcCall$1("observation_list", params);
5320
5687
  } catch (e) {
5321
5688
  console.error(err(` Failed to reach PAI daemon: ${e}`));
5322
- console.error(dim(" Is the daemon running? Try: pai daemon status"));
5689
+ console.error(dim$1(" Is the daemon running? Try: pai daemon status"));
5323
5690
  process.exit(1);
5324
5691
  }
5325
5692
  console.log();
@@ -5329,7 +5696,7 @@ async function cmdList$2(opts) {
5329
5696
  if (opts.type) filterParts.push(`type: ${opts.type}`);
5330
5697
  if (opts.session) filterParts.push(`session: ${opts.session}`);
5331
5698
  filterParts.push(`limit: ${limit}`);
5332
- console.log(dim(` ${filterParts.join(" | ")}`));
5699
+ console.log(dim$1(` ${filterParts.join(" | ")}`));
5333
5700
  console.log();
5334
5701
  if (!observations || observations.length === 0) {
5335
5702
  console.log(warn(" No observations found."));
@@ -5341,18 +5708,18 @@ async function cmdList$2(opts) {
5341
5708
  const TITLE_W = 42;
5342
5709
  const PROJ_W = 14;
5343
5710
  const TS_W = 16;
5344
- console.log(" " + bold("id".padEnd(ID_W)) + " " + bold("type".padEnd(TYPE_W)) + " " + bold("title".padEnd(TITLE_W)) + " " + bold("project".padEnd(PROJ_W)) + " " + bold("created_at"));
5345
- console.log(dim(" " + "-".repeat(ID_W) + " " + "-".repeat(TYPE_W) + " " + "-".repeat(TITLE_W) + " " + "-".repeat(PROJ_W) + " " + "-".repeat(TS_W)));
5711
+ console.log(" " + bold$1("id".padEnd(ID_W)) + " " + bold$1("type".padEnd(TYPE_W)) + " " + bold$1("title".padEnd(TITLE_W)) + " " + bold$1("project".padEnd(PROJ_W)) + " " + bold$1("created_at"));
5712
+ console.log(dim$1(" " + "-".repeat(ID_W) + " " + "-".repeat(TYPE_W) + " " + "-".repeat(TITLE_W) + " " + "-".repeat(PROJ_W) + " " + "-".repeat(TS_W)));
5346
5713
  for (const obs of observations) {
5347
5714
  const idStr = String(obs.id).padStart(ID_W, " ");
5348
5715
  const typeStr = typeColor(obs.type ?? "").padEnd(TYPE_W + (typeColor(obs.type ?? "").length - (obs.type ?? "").length));
5349
5716
  const titleStr = trunc$1(obs.title ?? "", TITLE_W).padEnd(TITLE_W);
5350
5717
  const projStr = trunc$1(obs.project_slug ?? "—", PROJ_W).padEnd(PROJ_W);
5351
5718
  const tsStr = fmtTs$2(obs.created_at);
5352
- console.log(` ${idStr} ${typeStr} ${titleStr} ${projStr} ${dim(tsStr)}`);
5719
+ console.log(` ${idStr} ${typeStr} ${titleStr} ${projStr} ${dim$1(tsStr)}`);
5353
5720
  }
5354
5721
  console.log();
5355
- console.log(dim(` ${observations.length} observation(s)`));
5722
+ console.log(dim$1(` ${observations.length} observation(s)`));
5356
5723
  console.log();
5357
5724
  }
5358
5725
  async function cmdSearch(query, opts) {
@@ -5372,14 +5739,14 @@ async function cmdSearch(query, opts) {
5372
5739
  });
5373
5740
  } catch (e) {
5374
5741
  console.error(err(` Failed to reach PAI daemon: ${e}`));
5375
- console.error(dim(" Is the daemon running? Try: pai daemon status"));
5742
+ console.error(dim$1(" Is the daemon running? Try: pai daemon status"));
5376
5743
  process.exit(1);
5377
5744
  }
5378
5745
  const q = query.toLowerCase();
5379
5746
  const observations = allObservations.filter((o) => (o.title ?? "").toLowerCase().includes(q) || (o.narrative ?? "").toLowerCase().includes(q) || (o.tool_input_summary ?? "").toLowerCase().includes(q)).slice(0, limit);
5380
5747
  console.log();
5381
5748
  console.log(header(" PAI Observation Search"));
5382
- console.log(dim(` Query: "${query}"${opts.project ? ` project: ${opts.project}` : ""}${opts.type ? ` type: ${opts.type}` : ""} limit: ${limit}`));
5749
+ console.log(dim$1(` Query: "${query}"${opts.project ? ` project: ${opts.project}` : ""}${opts.type ? ` type: ${opts.type}` : ""} limit: ${limit}`));
5383
5750
  console.log();
5384
5751
  if (!observations || observations.length === 0) {
5385
5752
  console.log(warn(` No observations matching "${query}".`));
@@ -5390,19 +5757,19 @@ async function cmdSearch(query, opts) {
5390
5757
  const obs = observations[i];
5391
5758
  const idx = chalk.dim(String(i + 1).padStart(3, " "));
5392
5759
  const type = typeColor(obs.type ?? "");
5393
- const title = bold(obs.title ?? "(untitled)");
5394
- const proj = obs.project_slug ? dim(`[${obs.project_slug}]`) : dim("[—]");
5395
- const ts = dim(fmtTs$2(obs.created_at));
5396
- const id = dim(`#${obs.id}`);
5760
+ const title = bold$1(obs.title ?? "(untitled)");
5761
+ const proj = obs.project_slug ? dim$1(`[${obs.project_slug}]`) : dim$1("[—]");
5762
+ const ts = dim$1(fmtTs$2(obs.created_at));
5763
+ const id = dim$1(`#${obs.id}`);
5397
5764
  console.log(` ${idx} ${type.padEnd(12)} ${title}`);
5398
5765
  console.log(` ${proj} ${ts} ${id}`);
5399
5766
  if (obs.narrative) {
5400
5767
  const snippet = trunc$1(obs.narrative.replace(/\n/g, " "), 160);
5401
- console.log(` ${dim(snippet)}`);
5768
+ console.log(` ${dim$1(snippet)}`);
5402
5769
  }
5403
5770
  console.log();
5404
5771
  }
5405
- console.log(dim(` ${observations.length} result(s)`));
5772
+ console.log(dim$1(` ${observations.length} result(s)`));
5406
5773
  console.log();
5407
5774
  }
5408
5775
  async function cmdStats$1() {
@@ -5411,39 +5778,39 @@ async function cmdStats$1() {
5411
5778
  stats = await ipcCall$1("observation_stats", {});
5412
5779
  } catch (e) {
5413
5780
  console.error(err(` Failed to reach PAI daemon: ${e}`));
5414
- console.error(dim(" Is the daemon running? Try: pai daemon status"));
5781
+ console.error(dim$1(" Is the daemon running? Try: pai daemon status"));
5415
5782
  process.exit(1);
5416
5783
  }
5417
5784
  console.log();
5418
5785
  console.log(header(" PAI Observation Statistics"));
5419
5786
  console.log();
5420
- console.log(` ${bold("Total observations:")} ${chalk.cyan(String(stats.total ?? 0))}`);
5421
- if (stats.most_recent) console.log(` ${bold("Most recent:")} ${dim(fmtTs$2(stats.most_recent))}`);
5787
+ console.log(` ${bold$1("Total observations:")} ${chalk.cyan(String(stats.total ?? 0))}`);
5788
+ if (stats.most_recent) console.log(` ${bold$1("Most recent:")} ${dim$1(fmtTs$2(stats.most_recent))}`);
5422
5789
  console.log();
5423
5790
  if (stats.by_type && stats.by_type.length > 0) {
5424
- console.log(bold(" By type:"));
5791
+ console.log(bold$1(" By type:"));
5425
5792
  const maxCount = Math.max(...stats.by_type.map((r) => r.count));
5426
5793
  for (const row of stats.by_type) {
5427
5794
  const barWidth = 20;
5428
5795
  const filled = Math.round(row.count / maxCount * barWidth);
5429
- const bar = chalk.cyan("█".repeat(filled)) + dim("░".repeat(barWidth - filled));
5796
+ const bar = chalk.cyan("█".repeat(filled)) + dim$1("░".repeat(barWidth - filled));
5430
5797
  const label = typeColor(row.type).padEnd(12 + (typeColor(row.type).length - row.type.length));
5431
5798
  console.log(` ${label} ${bar} ${String(row.count).padStart(5)}`);
5432
5799
  }
5433
5800
  console.log();
5434
5801
  }
5435
5802
  if (stats.by_project && stats.by_project.length > 0) {
5436
- console.log(bold(" By project:"));
5803
+ console.log(bold$1(" By project:"));
5437
5804
  const maxCount = Math.max(...stats.by_project.map((r) => r.count));
5438
5805
  const show = stats.by_project.slice(0, 15);
5439
5806
  for (const row of show) {
5440
5807
  const barWidth = 20;
5441
5808
  const filled = Math.round(row.count / maxCount * barWidth);
5442
- const bar = chalk.green("█".repeat(filled)) + dim("░".repeat(barWidth - filled));
5809
+ const bar = chalk.green("█".repeat(filled)) + dim$1("░".repeat(barWidth - filled));
5443
5810
  const label = (row.project_slug ?? "—").padEnd(20);
5444
- console.log(` ${dim(label)} ${bar} ${String(row.count).padStart(5)}`);
5811
+ console.log(` ${dim$1(label)} ${bar} ${String(row.count).padStart(5)}`);
5445
5812
  }
5446
- if (stats.by_project.length > 15) console.log(dim(` ... and ${stats.by_project.length - 15} more project(s)`));
5813
+ if (stats.by_project.length > 15) console.log(dim$1(` ... and ${stats.by_project.length - 15} more project(s)`));
5447
5814
  console.log();
5448
5815
  }
5449
5816
  if ((!stats.by_type || stats.by_type.length === 0) && (!stats.by_project || stats.by_project.length === 0)) {
@@ -5523,12 +5890,12 @@ function ipcCall(method, params) {
5523
5890
  });
5524
5891
  }
5525
5892
  function fmtTs$1(ts) {
5526
- if (!ts) return dim("—");
5893
+ if (!ts) return dim$1("—");
5527
5894
  try {
5528
5895
  const d = new Date(ts);
5529
5896
  return `${d.toISOString().slice(0, 10)} ${d.toISOString().slice(11, 16)}`;
5530
5897
  } catch {
5531
- return dim("—");
5898
+ return dim$1("—");
5532
5899
  }
5533
5900
  }
5534
5901
  function statusColor$1(status) {
@@ -5554,7 +5921,7 @@ async function cmdTelemetry(opts) {
5554
5921
  rows = await ipcCall("skill_telemetry_query", params);
5555
5922
  } catch (e) {
5556
5923
  console.error(err(` Failed to reach PAI daemon: ${e}`));
5557
- console.error(dim(" Is the daemon running? Try: pai daemon status"));
5924
+ console.error(dim$1(" Is the daemon running? Try: pai daemon status"));
5558
5925
  process.exit(1);
5559
5926
  }
5560
5927
  if (opts.json) {
@@ -5563,11 +5930,11 @@ async function cmdTelemetry(opts) {
5563
5930
  }
5564
5931
  console.log();
5565
5932
  console.log(header(" Skill Telemetry"));
5566
- console.log(dim(` scope: ${params.scope}${opts.status ? ` | status: ${opts.status}` : ""}`));
5933
+ console.log(dim$1(` scope: ${params.scope}${opts.status ? ` | status: ${opts.status}` : ""}`));
5567
5934
  console.log();
5568
5935
  if (!rows || rows.length === 0) {
5569
5936
  console.log(warn(" No skill telemetry yet."));
5570
- console.log(dim(" Invoke a skill (e.g. /name) and it will be recorded."));
5937
+ console.log(dim$1(" Invoke a skill (e.g. /name) and it will be recorded."));
5571
5938
  console.log();
5572
5939
  return;
5573
5940
  }
@@ -5576,8 +5943,8 @@ async function cmdTelemetry(opts) {
5576
5943
  const STAT_W = 9;
5577
5944
  const TRIG_W = 8;
5578
5945
  const SEEN_W = 16;
5579
- console.log(" " + bold("skill".padEnd(NAME_W)) + " " + bold("source".padEnd(SRC_W)) + " " + bold("status".padEnd(STAT_W)) + " " + bold("triggers".padStart(TRIG_W)) + " " + bold("last used".padEnd(SEEN_W)) + " " + bold("projects"));
5580
- console.log(dim(" " + "-".repeat(NAME_W + SRC_W + STAT_W + TRIG_W + SEEN_W + 18)));
5946
+ console.log(" " + bold$1("skill".padEnd(NAME_W)) + " " + bold$1("source".padEnd(SRC_W)) + " " + bold$1("status".padEnd(STAT_W)) + " " + bold$1("triggers".padStart(TRIG_W)) + " " + bold$1("last used".padEnd(SEEN_W)) + " " + bold$1("projects"));
5947
+ console.log(dim$1(" " + "-".repeat(NAME_W + SRC_W + STAT_W + TRIG_W + SEEN_W + 18)));
5581
5948
  for (const r of rows) {
5582
5949
  const name = trunc(r.skill_name ?? "", NAME_W).padEnd(NAME_W);
5583
5950
  const src = trunc(r.source ?? "", SRC_W).padEnd(SRC_W);
@@ -5586,10 +5953,10 @@ async function cmdTelemetry(opts) {
5586
5953
  const trig = String(r.trigger_count ?? 0).padStart(TRIG_W);
5587
5954
  const seen = fmtTs$1(r.last_triggered).padEnd(SEEN_W);
5588
5955
  const projs = Array.isArray(r.context_projects) ? r.context_projects.join(", ") : "";
5589
- console.log(` ${chalk.white(name)} ${dim(src)} ${statPad} ${chalk.cyan(trig)} ${dim(seen)} ${dim(trunc(projs, 30))}`);
5956
+ console.log(` ${chalk.white(name)} ${dim$1(src)} ${statPad} ${chalk.cyan(trig)} ${dim$1(seen)} ${dim$1(trunc(projs, 30))}`);
5590
5957
  }
5591
5958
  console.log();
5592
- console.log(dim(` ${rows.length} skill(s)`));
5959
+ console.log(dim$1(` ${rows.length} skill(s)`));
5593
5960
  console.log();
5594
5961
  }
5595
5962
  function registerSkillCommands(parent) {
@@ -5612,7 +5979,7 @@ function step(msg) {
5612
5979
  console.log(chalk.bold.cyan(`\n ${msg}`));
5613
5980
  }
5614
5981
  function info(msg) {
5615
- console.log(dim(` ${msg}`));
5982
+ console.log(dim$1(` ${msg}`));
5616
5983
  }
5617
5984
  function success(msg) {
5618
5985
  console.log(ok(` ${msg}`));
@@ -5627,7 +5994,7 @@ function error(msg) {
5627
5994
  * Run a shell command, streaming output to stdout.
5628
5995
  * Returns true on success, false on failure.
5629
5996
  */
5630
- function run(cmd, cwd) {
5997
+ function run$1(cmd, cwd) {
5631
5998
  try {
5632
5999
  execSync(cmd, {
5633
6000
  stdio: "inherit",
@@ -5728,7 +6095,7 @@ function stepStash(repoDir) {
5728
6095
  function stepPull(repoDir) {
5729
6096
  step("Pulling latest from origin/main...");
5730
6097
  const headBefore = capture("git rev-parse HEAD", repoDir);
5731
- if (!run("git pull origin main", repoDir)) {
6098
+ if (!run$1("git pull origin main", repoDir)) {
5732
6099
  warning("git pull failed. There may be merge conflicts with your stash.");
5733
6100
  return null;
5734
6101
  }
@@ -5776,12 +6143,12 @@ function stepPopStash(repoDir) {
5776
6143
  function stepBuild(repoDir) {
5777
6144
  step("Rebuilding PAI (bun install && bun run build)...");
5778
6145
  info("Installing dependencies...");
5779
- if (!run("bun install", repoDir)) {
6146
+ if (!run$1("bun install", repoDir)) {
5780
6147
  error("bun install failed.");
5781
6148
  return false;
5782
6149
  }
5783
6150
  info("Building...");
5784
- if (!run("bun run build", repoDir)) {
6151
+ if (!run$1("bun run build", repoDir)) {
5785
6152
  error("bun run build failed.");
5786
6153
  return false;
5787
6154
  }
@@ -5895,7 +6262,7 @@ async function runUpdate() {
5895
6262
  console.log(chalk.bold.cyan(" ─────────────────────────────────────"));
5896
6263
  if (newCommitsLog) success("PAI updated successfully!");
5897
6264
  else success("PAI is up to date and rebuilt successfully!");
5898
- console.log(dim(" Version: ") + chalk.cyan(capture("pai --version", repoDir) ?? "unknown"));
6265
+ console.log(dim$1(" Version: ") + chalk.cyan(capture("pai --version", repoDir) ?? "unknown"));
5899
6266
  line();
5900
6267
  }
5901
6268
  function registerUpdateCommand(program) {
@@ -5912,7 +6279,7 @@ function makeClient$1() {
5912
6279
  function modeColor(mode) {
5913
6280
  switch (mode) {
5914
6281
  case "off": return warn(mode);
5915
- case "voice": return bold(ok(mode));
6282
+ case "voice": return bold$1(ok(mode));
5916
6283
  case "auto": return ok(mode);
5917
6284
  default: return ok(mode);
5918
6285
  }
@@ -5921,23 +6288,23 @@ function printConfig(config, activeChannels) {
5921
6288
  console.log();
5922
6289
  console.log(header(" PAI Notification Config"));
5923
6290
  console.log();
5924
- console.log(` ${bold("Mode:")} ${modeColor(config.mode)}`);
6291
+ console.log(` ${bold$1("Mode:")} ${modeColor(config.mode)}`);
5925
6292
  console.log();
5926
- console.log(` ${bold("Channels:")}`);
6293
+ console.log(` ${bold$1("Channels:")}`);
5927
6294
  for (const [ch, cfg] of Object.entries(config.channels)) {
5928
6295
  const c = cfg;
5929
- const status = c.enabled ? ok("enabled") : dim("disabled");
6296
+ const status = c.enabled ? ok("enabled") : dim$1("disabled");
5930
6297
  let extra = "";
5931
- if (ch === "ntfy" && c.url) extra = dim(` ${c.url}`);
5932
- if (ch === "voice" && c.voiceName) extra = dim(` voice: ${c.voiceName}`);
5933
- console.log(` ${bold(ch.padEnd(12))}${status}${extra}`);
6298
+ if (ch === "ntfy" && c.url) extra = dim$1(` ${c.url}`);
6299
+ if (ch === "voice" && c.voiceName) extra = dim$1(` voice: ${c.voiceName}`);
6300
+ console.log(` ${bold$1(ch.padEnd(12))}${status}${extra}`);
5934
6301
  }
5935
6302
  console.log();
5936
- console.log(` ${bold("Active:")} ${activeChannels.length > 0 ? activeChannels.join(", ") : dim("(none)")}`);
6303
+ console.log(` ${bold$1("Active:")} ${activeChannels.length > 0 ? activeChannels.join(", ") : dim$1("(none)")}`);
5937
6304
  console.log();
5938
- console.log(` ${bold("Routing:")} ${dim("(auto mode only)")}`);
6305
+ console.log(` ${bold$1("Routing:")} ${dim$1("(auto mode only)")}`);
5939
6306
  for (const [event, channels] of Object.entries(config.routing)) {
5940
- const ch = channels.join(", ") || dim("(none)");
6307
+ const ch = channels.join(", ") || dim$1("(none)");
5941
6308
  console.log(` ${event.padEnd(12)}→ ${ch}`);
5942
6309
  }
5943
6310
  console.log();
@@ -5951,8 +6318,8 @@ async function cmdStatus() {
5951
6318
  const msg = e instanceof Error ? e.message : String(e);
5952
6319
  console.log();
5953
6320
  console.log(warn(" Cannot reach PAI daemon."));
5954
- console.log(dim(` ${msg}`));
5955
- console.log(dim(" Start it with: pai daemon serve"));
6321
+ console.log(dim$1(` ${msg}`));
6322
+ console.log(dim$1(" Start it with: pai daemon serve"));
5956
6323
  console.log();
5957
6324
  process.exit(1);
5958
6325
  }
@@ -5994,15 +6361,15 @@ async function cmdSet(opts) {
5994
6361
  if (Object.keys(channels).length > 0) patch.channels = channels;
5995
6362
  if (!patch.mode && !patch.channels) {
5996
6363
  console.error(err("No changes specified. Use --mode, --enable, --disable, etc."));
5997
- console.log(dim(" Example: pai notify set --mode voice"));
5998
- console.log(dim(" Example: pai notify set --mode auto --enable macos --disable ntfy"));
6364
+ console.log(dim$1(" Example: pai notify set --mode voice"));
6365
+ console.log(dim$1(" Example: pai notify set --mode auto --enable macos --disable ntfy"));
5999
6366
  process.exit(1);
6000
6367
  }
6001
6368
  try {
6002
6369
  const { config } = await client.setNotificationConfig(patch);
6003
6370
  console.log();
6004
6371
  console.log(ok(" Notification config updated."));
6005
- console.log(` ${bold("Mode:")} ${modeColor(config.mode)}`);
6372
+ console.log(` ${bold$1("Mode:")} ${modeColor(config.mode)}`);
6006
6373
  console.log();
6007
6374
  } catch (e) {
6008
6375
  const msg = e instanceof Error ? e.message : String(e);
@@ -6014,7 +6381,7 @@ async function cmdTest(opts) {
6014
6381
  const client = makeClient$1();
6015
6382
  const event = opts.event ?? "info";
6016
6383
  console.log();
6017
- console.log(dim(` Sending test ${event} notification...`));
6384
+ console.log(dim$1(` Sending test ${event} notification...`));
6018
6385
  try {
6019
6386
  const result = await client.sendNotification({
6020
6387
  event,
@@ -6025,7 +6392,7 @@ async function cmdTest(opts) {
6025
6392
  else if (result.channelsAttempted.length === 0) console.log(warn(" No channels active for this event. Check mode and channel config."));
6026
6393
  else console.log(ok(` Sent to: ${result.channelsSucceeded.join(", ")}`));
6027
6394
  if (result.channelsFailed.length > 0) console.log(warn(` Failed: ${result.channelsFailed.join(", ")}`));
6028
- console.log(dim(` Mode: ${result.mode}`));
6395
+ console.log(dim$1(` Mode: ${result.mode}`));
6029
6396
  console.log();
6030
6397
  } catch (e) {
6031
6398
  const msg = e instanceof Error ? e.message : String(e);
@@ -6086,6 +6453,292 @@ function registerNotifyCommands(notifyCmd) {
6086
6453
  });
6087
6454
  }
6088
6455
 
6456
+ //#endregion
6457
+ //#region src/tasks/dispatch.ts
6458
+ /**
6459
+ * Render a task as a session-to-session message.
6460
+ *
6461
+ * The body is included in full and deliberately not summarised: the whole point
6462
+ * of the description convention is that a task is actionable months later
6463
+ * without re-deriving anything, and truncating it here would undo that.
6464
+ */
6465
+ function renderTask(task) {
6466
+ const parts = [`Task from the bus: ${task.title}`];
6467
+ if (task.due) parts.push(`Due: ${task.due}`);
6468
+ if (task.priority !== "p4") parts.push(`Priority: ${task.priority}`);
6469
+ if (task.body.trim()) parts.push("", task.body.trim());
6470
+ if (task.sourceUrl) parts.push("", `Source: ${task.sourceUrl}`);
6471
+ parts.push("", `Tracker id: ${task.id}`, `When done, run \`pai task done ${task.id}\` so it is not dispatched again.`, "No reply is expected — the process that sent this has already exited.", "If you are blocked, file it back to the tracker with `pai task add` rather than replying.");
6472
+ return parts.join("\n");
6473
+ }
6474
+ async function dispatchTask(task, opts) {
6475
+ if (task.owner.project === null) return {
6476
+ task,
6477
+ outcome: "unrouted",
6478
+ reason: task.owner.rawHint ? `"${task.owner.rawHint}" matches no PAI project` : "no owner label or container"
6479
+ };
6480
+ const project = task.owner.project;
6481
+ if (!opts.autoDispatch || !opts.transport) return {
6482
+ task,
6483
+ outcome: "skipped",
6484
+ session: project,
6485
+ reason: opts.transport ? "autoDispatch is off" : "no transport available — run `pai <project>` to pick this up"
6486
+ };
6487
+ let result;
6488
+ try {
6489
+ result = await opts.transport.dispatch(project, renderTask(task), { spawnIfAbsent: opts.spawnIfAbsent !== false });
6490
+ } catch (e) {
6491
+ return {
6492
+ task,
6493
+ outcome: "unlaunchable",
6494
+ session: project,
6495
+ reason: e instanceof Error ? e.message : String(e)
6496
+ };
6497
+ }
6498
+ if (result.outcome === "unlaunchable" && !result.reason?.includes("pai project name")) return {
6499
+ task,
6500
+ outcome: "unlaunchable",
6501
+ session: result.session ?? project,
6502
+ reason: `${result.reason ?? "no session could be launched"} — give it an alias with \`pai project name <identifier> ${project}\``
6503
+ };
6504
+ return {
6505
+ task,
6506
+ outcome: result.outcome,
6507
+ session: result.session ?? project,
6508
+ reason: result.reason
6509
+ };
6510
+ }
6511
+ /**
6512
+ * Dispatch a batch, sequentially.
6513
+ *
6514
+ * Deliberately not parallel: dispatching spawns terminal tabs and types into
6515
+ * them. Racing that would interleave input across sessions.
6516
+ */
6517
+ async function dispatchAll(tasks, opts) {
6518
+ const results = [];
6519
+ for (const task of tasks) results.push(await dispatchTask(task, opts));
6520
+ return results;
6521
+ }
6522
+
6523
+ //#endregion
6524
+ //#region src/tasks/transport/aibroker.ts
6525
+ /**
6526
+ * aibroker.ts — AIBroker transport for the task bus
6527
+ *
6528
+ * Shells out to the `aibroker` CLI. PAI's CLI cannot call MCP tools, so the
6529
+ * MCP-only dispatch primitives are unreachable from a shell — this is the
6530
+ * bridge.
6531
+ *
6532
+ * Optional by design: when AIBroker is not installed, `detect()` returns null
6533
+ * and the bus reports ownership instead of delivering. PAI must stay useful on
6534
+ * machines that do not run iTerm2.
6535
+ *
6536
+ * See Notes/docs/task-bus.md.
6537
+ */
6538
+ /** Long enough to cover launching a terminal tab and waiting for it to settle. */
6539
+ const DISPATCH_TIMEOUT_MS = 6e4;
6540
+ const VALID_OUTCOMES = new Set([
6541
+ "delivered",
6542
+ "spawned",
6543
+ "unlaunchable",
6544
+ "unreachable",
6545
+ "skipped"
6546
+ ]);
6547
+ /**
6548
+ * Run `aibroker dispatch`, feeding the message over stdin.
6549
+ *
6550
+ * stdin rather than argv is deliberate: task bodies carry the full procedure
6551
+ * and reasoning, so they are long, multi-line, and full of quotes and
6552
+ * backticks. argv would mangle them or hit length limits.
6553
+ */
6554
+ function run(bin, args, stdin) {
6555
+ return new Promise((resolve, reject) => {
6556
+ execFile(bin, args, {
6557
+ timeout: DISPATCH_TIMEOUT_MS,
6558
+ maxBuffer: 1024 * 1024
6559
+ }, (error, stdout, stderr) => {
6560
+ if (error && !stdout.trim().startsWith("{")) {
6561
+ reject(new Error(stderr.trim() || error.message));
6562
+ return;
6563
+ }
6564
+ resolve(stdout);
6565
+ }).stdin?.end(stdin);
6566
+ });
6567
+ }
6568
+ var AiBrokerTransport = class {
6569
+ constructor(bin = "aibroker") {
6570
+ this.bin = bin;
6571
+ }
6572
+ async dispatch(project, message, opts) {
6573
+ const args = [
6574
+ "dispatch",
6575
+ project,
6576
+ "--stdin",
6577
+ "--json"
6578
+ ];
6579
+ if (!opts.spawnIfAbsent) args.push("--no-spawn");
6580
+ const stdout = await run(this.bin, args, message);
6581
+ const start = stdout.lastIndexOf("{");
6582
+ if (start === -1) throw new Error(`aibroker returned no JSON: ${stdout.trim().slice(0, 200)}`);
6583
+ let wire;
6584
+ try {
6585
+ wire = JSON.parse(stdout.slice(start));
6586
+ } catch {
6587
+ throw new Error(`aibroker returned malformed JSON: ${stdout.trim().slice(0, 200)}`);
6588
+ }
6589
+ if (!wire.outcome || !VALID_OUTCOMES.has(wire.outcome)) return {
6590
+ outcome: "unlaunchable",
6591
+ session: wire.session ?? project,
6592
+ reason: `unexpected outcome from aibroker: ${String(wire.outcome)}`
6593
+ };
6594
+ return {
6595
+ outcome: wire.outcome,
6596
+ session: wire.session ?? wire.project ?? project,
6597
+ reason: wire.reason
6598
+ };
6599
+ }
6600
+ };
6601
+ /**
6602
+ * Return a transport if the `aibroker` CLI is present and supports dispatch.
6603
+ *
6604
+ * Probing for the subcommand rather than just the binary matters: AIBroker
6605
+ * shipped for a long time without `dispatch`, and an older install would
6606
+ * otherwise fail once per task instead of degrading cleanly up front.
6607
+ */
6608
+ async function detectAiBroker(bin = "aibroker") {
6609
+ try {
6610
+ return (await new Promise((resolve, reject) => {
6611
+ execFile(bin, ["help"], { timeout: 1e4 }, (error, stdout, stderr) => {
6612
+ if (error && !stdout) reject(error);
6613
+ else resolve(stdout + stderr);
6614
+ });
6615
+ })).includes("dispatch") ? new AiBrokerTransport(bin) : null;
6616
+ } catch {
6617
+ return null;
6618
+ }
6619
+ }
6620
+
6621
+ //#endregion
6622
+ //#region src/cli/commands/task.ts
6623
+ const dim = chalk.dim;
6624
+ const bold = chalk.bold;
6625
+ function buildProvider() {
6626
+ const tasks = loadConfig().tasks;
6627
+ if (!tasks?.enabled) return null;
6628
+ const aliases = loadAliasMap(openRegistry());
6629
+ const provider = new TodoistProvider(tasks.providers.todoist, aliases);
6630
+ return provider.isConfigured() ? provider : null;
6631
+ }
6632
+ /** Print the one message that actually helps when nothing is configured. */
6633
+ function reportUnconfigured() {
6634
+ console.error(chalk.yellow(" Task bus is not configured."));
6635
+ console.error(dim(" Run `pai setup` and complete the Task Bus step."));
6636
+ }
6637
+ function todayIso() {
6638
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6639
+ }
6640
+ function renderOwner(task) {
6641
+ if (task.owner.project) {
6642
+ const via = task.owner.source === "label" ? "label" : "container";
6643
+ return chalk.cyan(task.owner.project) + dim(` (${via})`);
6644
+ }
6645
+ return task.owner.rawHint ? chalk.yellow("unrouted") + dim(` (${task.owner.rawHint} matches no project)`) : chalk.yellow("unrouted");
6646
+ }
6647
+ function printTasks(tasks) {
6648
+ if (tasks.length === 0) {
6649
+ console.log(dim(" Nothing open."));
6650
+ return;
6651
+ }
6652
+ console.log();
6653
+ for (const t of tasks) {
6654
+ const due = t.due ? dim(` due ${t.due.slice(0, 10)}`) : "";
6655
+ const prio = t.priority !== "p4" ? dim(` ${t.priority}`) : "";
6656
+ console.log(` ${bold(t.title)}${prio}${due}`);
6657
+ console.log(` ${renderOwner(t)} ${dim("· " + t.id)}`);
6658
+ }
6659
+ console.log();
6660
+ }
6661
+ function printResults(results) {
6662
+ const symbol = {
6663
+ delivered: chalk.green("→"),
6664
+ spawned: chalk.green("+"),
6665
+ unrouted: chalk.yellow("?"),
6666
+ unlaunchable: chalk.red("!"),
6667
+ unreachable: chalk.red("✗"),
6668
+ skipped: dim("·")
6669
+ };
6670
+ console.log();
6671
+ for (const r of results) {
6672
+ console.log(` ${symbol[r.outcome]} ${r.task.title}`);
6673
+ const detail = [r.session, r.reason].filter(Boolean).join(" — ");
6674
+ if (detail) console.log(` ${dim(detail)}`);
6675
+ }
6676
+ console.log();
6677
+ const counts = results.reduce((acc, r) => {
6678
+ acc[r.outcome] = (acc[r.outcome] ?? 0) + 1;
6679
+ return acc;
6680
+ }, {});
6681
+ console.log(dim(" " + Object.entries(counts).map(([k, v]) => `${v} ${k}`).join(", ")));
6682
+ console.log();
6683
+ }
6684
+ function registerTaskCommands(taskCmd) {
6685
+ taskCmd.command("list").description("List open tasks on the bus").option("--owner <project>", "Only tasks owned by this PAI project").option("--due <date>", "Only tasks due on or before this ISO date").option("--today", "Shorthand for --due <today>").option("--routed", "Hide tasks with no resolved owner").option("--limit <n>", "Maximum tasks to show", (v) => Number.parseInt(v, 10)).action(async (opts) => {
6686
+ const provider = buildProvider();
6687
+ if (!provider) return reportUnconfigured();
6688
+ printTasks(await provider.listOpen({
6689
+ owner: opts.owner,
6690
+ dueBefore: opts.today ? todayIso() : opts.due,
6691
+ includeUnrouted: !opts.routed,
6692
+ limit: opts.limit
6693
+ }));
6694
+ });
6695
+ taskCmd.command("add <title>").description("File a task onto the bus").option("--owner <project>", "PAI project that owns this (adds a pai: label)").option("--body <text>", "Full procedure and reasoning — not just a restatement of the title").option("--due <date>", "Due date (ISO or natural language)").option("--priority <p>", "p1 (highest) … p4 (default)").option("--url <url>", "Reference — prefer a hook:// URL over a file path").action(async (title, opts) => {
6696
+ const provider = buildProvider();
6697
+ if (!provider) return reportUnconfigured();
6698
+ if (!opts.body) {
6699
+ console.log(dim(" No --body given. Add the procedure and reasoning so this"));
6700
+ console.log(dim(" is still actionable months from now, by you alone."));
6701
+ }
6702
+ const created = await provider.add({
6703
+ title,
6704
+ body: opts.body ?? "",
6705
+ owner: opts.owner ?? null,
6706
+ due: opts.due,
6707
+ priority: opts.priority,
6708
+ sourceUrl: opts.url
6709
+ });
6710
+ console.log(chalk.green(` Filed: ${created.title}`));
6711
+ console.log(` ${renderOwner(created)} ${dim("· " + created.id)}`);
6712
+ });
6713
+ taskCmd.command("dispatch").description("Hand open tasks to the PAI sessions that own them").option("--owner <project>", "Only dispatch tasks for this project").option("--today", "Only tasks due today or overdue").option("--dry-run", "Report what would happen without contacting any session").option("--no-spawn", "Never launch a session; skip owners that are not running").action(async (opts) => {
6714
+ const provider = buildProvider();
6715
+ if (!provider) return reportUnconfigured();
6716
+ const config = loadConfig();
6717
+ const tasks = await provider.listOpen({
6718
+ owner: opts.owner,
6719
+ dueBefore: opts.today ? todayIso() : void 0,
6720
+ includeUnrouted: true
6721
+ });
6722
+ if (tasks.length === 0) {
6723
+ console.log(dim(" Nothing to dispatch."));
6724
+ return;
6725
+ }
6726
+ const transport = opts.dryRun ? null : await detectAiBroker();
6727
+ if (!opts.dryRun && !transport) console.log(dim(" No aibroker CLI with `dispatch` found — reporting ownership only."));
6728
+ printResults(await dispatchAll(tasks, {
6729
+ transport,
6730
+ autoDispatch: opts.dryRun ? false : config.tasks?.autoDispatch ?? false,
6731
+ spawnIfAbsent: opts.spawn !== false
6732
+ }));
6733
+ });
6734
+ taskCmd.command("done <id>").description("Mark a task complete on the tracker").action(async (id) => {
6735
+ const provider = buildProvider();
6736
+ if (!provider) return reportUnconfigured();
6737
+ await provider.complete(id);
6738
+ console.log(chalk.green(` Completed ${id}.`));
6739
+ });
6740
+ }
6741
+
6089
6742
  //#endregion
6090
6743
  //#region src/cli/commands/topic.ts
6091
6744
  function makeClient() {
@@ -6100,32 +6753,32 @@ function printTopicResult(result) {
6100
6753
  console.log();
6101
6754
  console.log(header(" PAI Topic Check"));
6102
6755
  console.log();
6103
- console.log(` ${bold("Current project:")} ${result.currentProject ?? dim("(none)")}`);
6104
- console.log(` ${bold("Suggested project:")} ${result.suggestedProject ?? dim("(none)")}`);
6105
- console.log(` ${bold("Confidence:")} ${confidenceBar(result.confidence)} ${(result.confidence * 100).toFixed(1)}%`);
6106
- console.log(` ${bold("Chunks scored:")} ${result.chunkCount}`);
6756
+ console.log(` ${bold$1("Current project:")} ${result.currentProject ?? dim$1("(none)")}`);
6757
+ console.log(` ${bold$1("Suggested project:")} ${result.suggestedProject ?? dim$1("(none)")}`);
6758
+ console.log(` ${bold$1("Confidence:")} ${confidenceBar(result.confidence)} ${(result.confidence * 100).toFixed(1)}%`);
6759
+ console.log(` ${bold$1("Chunks scored:")} ${result.chunkCount}`);
6107
6760
  if (result.topProjects.length > 0) {
6108
6761
  console.log();
6109
- console.log(` ${bold("Top matches:")}`);
6762
+ console.log(` ${bold$1("Top matches:")}`);
6110
6763
  for (const p of result.topProjects) {
6111
6764
  const bar = confidenceBar(p.score, 15);
6112
- const marker = p.slug === result.currentProject ? dim(" (current)") : "";
6765
+ const marker = p.slug === result.currentProject ? dim$1(" (current)") : "";
6113
6766
  console.log(` ${p.slug.padEnd(30)} ${bar} ${(p.score * 100).toFixed(1)}%${marker}`);
6114
6767
  }
6115
6768
  }
6116
6769
  console.log();
6117
6770
  if (result.shifted) {
6118
- console.log(warn(" TOPIC SHIFT DETECTED") + dim(` — conversation appears to be about "${result.suggestedProject}", not "${result.currentProject}"`));
6771
+ console.log(warn(" TOPIC SHIFT DETECTED") + dim$1(` — conversation appears to be about "${result.suggestedProject}", not "${result.currentProject}"`));
6119
6772
  console.log();
6120
6773
  } else if (result.suggestedProject && result.suggestedProject === result.currentProject) {
6121
- console.log(ok(" No shift detected") + dim(" — context matches current project"));
6774
+ console.log(ok(" No shift detected") + dim$1(" — context matches current project"));
6122
6775
  console.log();
6123
6776
  } else if (!result.currentProject) {
6124
- if (result.suggestedProject) console.log(ok(" Best matching project: ") + bold(result.suggestedProject) + dim(` (confidence: ${(result.confidence * 100).toFixed(0)}%)`));
6125
- else console.log(dim(" No matching project found in memory index."));
6777
+ if (result.suggestedProject) console.log(ok(" Best matching project: ") + bold$1(result.suggestedProject) + dim$1(` (confidence: ${(result.confidence * 100).toFixed(0)}%)`));
6778
+ else console.log(dim$1(" No matching project found in memory index."));
6126
6779
  console.log();
6127
6780
  } else {
6128
- console.log(dim(` No shift detected (top match "${result.suggestedProject}" has confidence ${(result.confidence * 100).toFixed(0)}% — below threshold)`));
6781
+ console.log(dim$1(` No shift detected (top match "${result.suggestedProject}" has confidence ${(result.confidence * 100).toFixed(0)}% — below threshold)`));
6129
6782
  console.log();
6130
6783
  }
6131
6784
  }
@@ -6152,8 +6805,8 @@ async function cmdCheck(context, opts) {
6152
6805
  const msg = e instanceof Error ? e.message : String(e);
6153
6806
  console.log();
6154
6807
  console.log(warn(" Cannot reach PAI daemon."));
6155
- console.log(dim(` ${msg}`));
6156
- console.log(dim(" Start it with: pai daemon serve"));
6808
+ console.log(dim$1(` ${msg}`));
6809
+ console.log(dim$1(" Start it with: pai daemon serve"));
6157
6810
  console.log();
6158
6811
  process.exit(1);
6159
6812
  }
@@ -6326,7 +6979,7 @@ async function getPool$1() {
6326
6979
  const config = loadConfig();
6327
6980
  if (config.storageBackend !== "postgres") {
6328
6981
  console.error(err(" KG commands require Postgres backend."));
6329
- console.error(dim(" Set \"storageBackend\": \"postgres\" in ~/.config/pai/config.json"));
6982
+ console.error(dim$1(" Set \"storageBackend\": \"postgres\" in ~/.config/pai/config.json"));
6330
6983
  process.exit(1);
6331
6984
  }
6332
6985
  const backend = await createStorageBackend(config);
@@ -6351,9 +7004,9 @@ async function cmdBackfill(opts) {
6351
7004
  console.log();
6352
7005
  console.log(header(" PAI KG Backfill"));
6353
7006
  console.log();
6354
- if (opts.project) console.log(` ${bold("Project:")} ${opts.project}`);
6355
- if (limit) console.log(` ${bold("Limit:")} ${limit}`);
6356
- if (opts.dryRun) console.log(` ${bold("Mode:")} ${warn("dry-run")}`);
7007
+ if (opts.project) console.log(` ${bold$1("Project:")} ${opts.project}`);
7008
+ if (limit) console.log(` ${bold$1("Limit:")} ${limit}`);
7009
+ if (opts.dryRun) console.log(` ${bold$1("Mode:")} ${warn("dry-run")}`);
6357
7010
  console.log();
6358
7011
  try {
6359
7012
  const result = await backfillKgFromNotes({
@@ -6362,15 +7015,15 @@ async function cmdBackfill(opts) {
6362
7015
  dryRun: opts.dryRun,
6363
7016
  onProgress: (current, total, note) => {
6364
7017
  const short = shorten$1(note.replace(process.env.HOME ?? "", "~"), 70);
6365
- process.stdout.write(` [${current}/${total}] ${dim(short)}\n`);
7018
+ process.stdout.write(` [${current}/${total}] ${dim$1(short)}\n`);
6366
7019
  }
6367
7020
  });
6368
7021
  console.log();
6369
7022
  console.log(ok(" Backfill complete"));
6370
- console.log(` Notes processed: ${bold(String(result.notes_processed))}`);
6371
- console.log(` Triples extracted: ${bold(String(result.triples_extracted))}`);
6372
- console.log(` Triples added: ${bold(String(result.triples_added))}`);
6373
- console.log(` Triples superseded: ${bold(String(result.triples_superseded))}`);
7023
+ console.log(` Notes processed: ${bold$1(String(result.notes_processed))}`);
7024
+ console.log(` Triples extracted: ${bold$1(String(result.triples_extracted))}`);
7025
+ console.log(` Triples added: ${bold$1(String(result.triples_added))}`);
7026
+ console.log(` Triples superseded: ${bold$1(String(result.triples_superseded))}`);
6374
7027
  if (result.errors > 0) console.log(` ${warn("Errors:")} ${result.errors}`);
6375
7028
  console.log();
6376
7029
  } catch (e) {
@@ -6408,8 +7061,8 @@ async function cmdQuery$1(opts) {
6408
7061
  console.log(header(` ${triples.length} triple(s)`));
6409
7062
  console.log();
6410
7063
  for (const t of triples) {
6411
- const validity = t.valid_to ? dim(`(invalidated ${t.valid_to.toISOString().slice(0, 10)})`) : dim(`(valid since ${t.valid_from.toISOString().slice(0, 10)})`);
6412
- console.log(` ${bold(t.subject)} ${dim("·")} ${t.predicate} ${dim("·")} ${t.object} ${validity}`);
7064
+ const validity = t.valid_to ? dim$1(`(invalidated ${t.valid_to.toISOString().slice(0, 10)})`) : dim$1(`(valid since ${t.valid_from.toISOString().slice(0, 10)})`);
7065
+ console.log(` ${bold$1(t.subject)} ${dim$1("·")} ${t.predicate} ${dim$1("·")} ${t.object} ${validity}`);
6413
7066
  }
6414
7067
  console.log();
6415
7068
  } finally {
@@ -6430,10 +7083,10 @@ async function cmdList$1(opts) {
6430
7083
  console.log();
6431
7084
  console.log(header(` ${slice.length} of ${triples.length} currently-valid triple(s)`));
6432
7085
  console.log();
6433
- for (const t of slice) console.log(` ${bold(t.subject)} ${dim("·")} ${t.predicate} ${dim("·")} ${t.object}`);
7086
+ for (const t of slice) console.log(` ${bold$1(t.subject)} ${dim$1("·")} ${t.predicate} ${dim$1("·")} ${t.object}`);
6434
7087
  if (triples.length > slice.length) {
6435
7088
  console.log();
6436
- console.log(dim(` (${triples.length - slice.length} more — increase --limit)`));
7089
+ console.log(dim$1(` (${triples.length - slice.length} more — increase --limit)`));
6437
7090
  }
6438
7091
  console.log();
6439
7092
  } finally {
@@ -6461,12 +7114,12 @@ async function cmdStats() {
6461
7114
  console.log();
6462
7115
  console.log(header(" PAI KG Stats"));
6463
7116
  console.log();
6464
- console.log(` ${bold("Total triples:")} ${row.total ?? "0"}`);
6465
- console.log(` ${bold("Currently valid:")} ${row.valid ?? "0"}`);
6466
- console.log(` ${bold("Invalidated:")} ${row.invalidated ?? "0"}`);
6467
- console.log(` ${bold("Distinct subjects:")} ${row.subjects ?? "0"}`);
6468
- console.log(` ${bold("Distinct predicates:")} ${row.predicates ?? "0"}`);
6469
- console.log(` ${bold("Contradictions:")} ${contradictions.rows[0]?.count ?? "0"}`);
7117
+ console.log(` ${bold$1("Total triples:")} ${row.total ?? "0"}`);
7118
+ console.log(` ${bold$1("Currently valid:")} ${row.valid ?? "0"}`);
7119
+ console.log(` ${bold$1("Invalidated:")} ${row.invalidated ?? "0"}`);
7120
+ console.log(` ${bold$1("Distinct subjects:")} ${row.subjects ?? "0"}`);
7121
+ console.log(` ${bold$1("Distinct predicates:")} ${row.predicates ?? "0"}`);
7122
+ console.log(` ${bold$1("Contradictions:")} ${contradictions.rows[0]?.count ?? "0"}`);
6470
7123
  console.log();
6471
7124
  } finally {
6472
7125
  await close();
@@ -6626,25 +7279,25 @@ function printResult(result, json) {
6626
7279
  }
6627
7280
  if (result.rows.length === 0) {
6628
7281
  console.log();
6629
- console.log(dim(" (no rows)"));
7282
+ console.log(dim$1(" (no rows)"));
6630
7283
  console.log();
6631
7284
  return;
6632
7285
  }
6633
7286
  const stringRows = result.rows.map((row) => row.map((cell) => {
6634
- if (cell === null || cell === void 0) return dim("NULL");
7287
+ if (cell === null || cell === void 0) return dim$1("NULL");
6635
7288
  if (typeof cell === "object") return JSON.stringify(cell);
6636
7289
  return String(cell);
6637
7290
  }));
6638
7291
  console.log();
6639
7292
  console.log(renderTable(result.columns, stringRows));
6640
7293
  console.log();
6641
- console.log(dim(` ${result.rows.length} row(s)`));
7294
+ console.log(dim$1(` ${result.rows.length} row(s)`));
6642
7295
  console.log();
6643
7296
  }
6644
7297
  async function cmdQuery(sql, opts) {
6645
7298
  const target = opts.db ?? "sqlite";
6646
7299
  console.log();
6647
- console.log(header(` Query [${target}]`) + dim(` ${sql.slice(0, 80)}${sql.length > 80 ? "…" : ""}`));
7300
+ console.log(header(` Query [${target}]`) + dim$1(` ${sql.slice(0, 80)}${sql.length > 80 ? "…" : ""}`));
6648
7301
  try {
6649
7302
  let result;
6650
7303
  if (target === "postgres") result = await postgresQuery(sql);
@@ -6669,10 +7322,10 @@ async function cmdTables(opts) {
6669
7322
  console.log(JSON.stringify(tables, null, 2));
6670
7323
  return;
6671
7324
  }
6672
- if (tables.length === 0) console.log(dim(" (no tables found)"));
6673
- else for (const t of tables) console.log(` ${bold(t)}`);
7325
+ if (tables.length === 0) console.log(dim$1(" (no tables found)"));
7326
+ else for (const t of tables) console.log(` ${bold$1(t)}`);
6674
7327
  console.log();
6675
- console.log(dim(` ${tables.length} table(s)`));
7328
+ console.log(dim$1(` ${tables.length} table(s)`));
6676
7329
  console.log();
6677
7330
  } catch (e) {
6678
7331
  const msg = e instanceof Error ? e.message : String(e);
@@ -6764,11 +7417,11 @@ function renderMarkdown(md) {
6764
7417
  continue;
6765
7418
  }
6766
7419
  if (/^>\s?/.test(line)) {
6767
- out.push(dim(line.replace(/^>\s?/, "")));
7420
+ out.push(dim$1(line.replace(/^>\s?/, "")));
6768
7421
  continue;
6769
7422
  }
6770
7423
  if (/^\|?\s*[-:]+\s*(\|\s*[-:]+\s*)+\|?$/.test(line)) {
6771
- out.push(dim(line));
7424
+ out.push(dim$1(line));
6772
7425
  continue;
6773
7426
  }
6774
7427
  out.push(inlineBold(line));
@@ -6784,7 +7437,7 @@ function registerHelpCommand(program) {
6784
7437
  const docsDir = findDocsDir();
6785
7438
  if (!docsDir) {
6786
7439
  console.error(err(" Command docs not found."));
6787
- console.error(dim(" Run `bun run build` to generate them, or use `pai <area> --help`."));
7440
+ console.error(dim$1(" Run `bun run build` to generate them, or use `pai <area> --help`."));
6788
7441
  process.exitCode = 1;
6789
7442
  return;
6790
7443
  }
@@ -6796,7 +7449,7 @@ function registerHelpCommand(program) {
6796
7449
  } else {
6797
7450
  console.log(chalk.bold("\nPAI command areas\n"));
6798
7451
  for (const a of listAreas(docsDir)) console.log(` ${a}`);
6799
- console.log(dim("\n pai help <area> show the man page for an area"));
7452
+ console.log(dim$1("\n pai help <area> show the man page for an area"));
6800
7453
  }
6801
7454
  return;
6802
7455
  }
@@ -6809,9 +7462,9 @@ function registerHelpCommand(program) {
6809
7462
  console.error(err(` No man page for "${area}".`));
6810
7463
  const areas = listAreas(docsDir);
6811
7464
  const near = areas.filter((a) => a.includes(area) || area.includes(a));
6812
- if (near.length > 0) console.error(dim(` Did you mean: ${near.join(", ")}?`));
6813
- else console.error(dim(` Available: ${areas.join(", ")}`));
6814
- console.error(dim(` Or try: pai ${area} --help`));
7465
+ if (near.length > 0) console.error(dim$1(` Did you mean: ${near.join(", ")}?`));
7466
+ else console.error(dim$1(` Available: ${areas.join(", ")}`));
7467
+ console.error(dim$1(` Or try: pai ${area} --help`));
6815
7468
  process.exitCode = 1;
6816
7469
  });
6817
7470
  }
@@ -7253,7 +7906,7 @@ async function cmdPauseAll(opts) {
7253
7906
  liveSessions = await fetchLiveSessions();
7254
7907
  } catch (e) {
7255
7908
  console.error(err("AIBroker is not running. Cannot list live sessions."));
7256
- console.error(dim(" Start AIBroker or run `pai pause` from each session manually."));
7909
+ console.error(dim$1(" Start AIBroker or run `pai pause` from each session manually."));
7257
7910
  process.exitCode = 1;
7258
7911
  return;
7259
7912
  }
@@ -7268,13 +7921,13 @@ async function cmdPauseAll(opts) {
7268
7921
  console.log("\n" + header("Dry Run — Would Pause These Sessions") + "\n");
7269
7922
  for (const s of claudeSessions) {
7270
7923
  console.log(" " + sessionLabel(s));
7271
- console.log(dim(" → send: \"pause session\""));
7272
- if (opts.exit) console.log(dim(` → wait ${waitMs}ms then send: "/exit"`));
7924
+ console.log(dim$1(" → send: \"pause session\""));
7925
+ if (opts.exit) console.log(dim$1(` → wait ${waitMs}ms then send: "/exit"`));
7273
7926
  }
7274
7927
  console.log();
7275
7928
  return;
7276
7929
  }
7277
- console.log("\n" + header("Pausing All Live Sessions") + "\n" + dim(` ${claudeSessions.length} Claude session(s) via AIBroker`) + "\n");
7930
+ console.log("\n" + header("Pausing All Live Sessions") + "\n" + dim$1(` ${claudeSessions.length} Claude session(s) via AIBroker`) + "\n");
7278
7931
  const results = [];
7279
7932
  for (const s of claudeSessions) {
7280
7933
  process.stdout.write(" " + sessionLabel(s) + " … ");
@@ -7297,7 +7950,7 @@ async function cmdPauseAll(opts) {
7297
7950
  if (opts.exit) {
7298
7951
  const pausedSessions = results.filter((r) => r.pauseOk).map((r) => r.session);
7299
7952
  if (pausedSessions.length > 0) {
7300
- console.log("\n" + dim(` Waiting ${waitMs / 1e3}s for sessions to save state…`));
7953
+ console.log("\n" + dim$1(` Waiting ${waitMs / 1e3}s for sessions to save state…`));
7301
7954
  await sleep(waitMs);
7302
7955
  console.log();
7303
7956
  for (const s of pausedSessions) {
@@ -7324,7 +7977,7 @@ async function cmdPauseAll(opts) {
7324
7977
  }
7325
7978
  if (opts.exit) {
7326
7979
  const exitOk = results.filter((r) => r.exitOk).length;
7327
- console.log(dim(` /exit sent to ${exitOk} session(s).`));
7980
+ console.log(dim$1(` /exit sent to ${exitOk} session(s).`));
7328
7981
  }
7329
7982
  console.log();
7330
7983
  }
@@ -7345,9 +7998,9 @@ async function cmdPauseAll(opts) {
7345
7998
  */
7346
7999
  async function cmdClearNames(opts) {
7347
8000
  if (opts.dryRun) {
7348
- console.log(dim("Dry run — would:"));
7349
- console.log(dim(" 1. Call AIBroker clear_session_names (wipe session-names.json)"));
7350
- console.log(dim(" 2. Call AIBroker clear_pai_names (wipe user.paiName from all iTerm sessions)"));
8001
+ console.log(dim$1("Dry run — would:"));
8002
+ console.log(dim$1(" 1. Call AIBroker clear_session_names (wipe session-names.json)"));
8003
+ console.log(dim$1(" 2. Call AIBroker clear_pai_names (wipe user.paiName from all iTerm sessions)"));
7351
8004
  return;
7352
8005
  }
7353
8006
  let anyFailed = false;
@@ -7447,9 +8100,9 @@ function cmdList(db, projectSlug, opts) {
7447
8100
  ];
7448
8101
  const tableRows = rows.map((r) => {
7449
8102
  const title = r.title.length > 45 ? r.title.slice(0, 42) + "..." : r.title;
7450
- const tokens = r.token_count != null ? dim(r.token_count.toLocaleString()) : dim("—");
8103
+ const tokens = r.token_count != null ? dim$1(r.token_count.toLocaleString()) : dim$1("—");
7451
8104
  if (showProject) return [
7452
- dim(`#${r.number}`),
8105
+ dim$1(`#${r.number}`),
7453
8106
  r.project_slug,
7454
8107
  r.date,
7455
8108
  title,
@@ -7457,7 +8110,7 @@ function cmdList(db, projectSlug, opts) {
7457
8110
  tokens
7458
8111
  ];
7459
8112
  return [
7460
- dim(`#${r.number}`),
8113
+ dim$1(`#${r.number}`),
7461
8114
  r.date,
7462
8115
  title,
7463
8116
  statusColor(r.status),
@@ -7467,12 +8120,12 @@ function cmdList(db, projectSlug, opts) {
7467
8120
  console.log();
7468
8121
  if (projectSlug) {
7469
8122
  const project = getProject(db, projectSlug);
7470
- console.log(` ${bold(project.display_name)} sessions:`);
8123
+ console.log(` ${bold$1(project.display_name)} sessions:`);
7471
8124
  console.log();
7472
8125
  }
7473
8126
  console.log(renderTable(headers, tableRows));
7474
8127
  console.log();
7475
- console.log(dim(` ${rows.length} session(s) shown (limit: ${limit})`));
8128
+ console.log(dim$1(` ${rows.length} session(s) shown (limit: ${limit})`));
7476
8129
  }
7477
8130
 
7478
8131
  //#endregion
@@ -7573,11 +8226,11 @@ async function cmdFind(query, opts) {
7573
8226
  }
7574
8227
  if (matches.length === 0) {
7575
8228
  console.log(warn(`No sessions found matching "${query}" in ~/.claude/history.jsonl.`));
7576
- console.log(dim(` Try a shorter or different search term.`));
8229
+ console.log(dim$1(` Try a shorter or different search term.`));
7577
8230
  return;
7578
8231
  }
7579
8232
  console.log("\n" + header("Session Search Results") + "\n");
7580
- console.log(dim(` Query: "${query}" (${matches.length} session(s) with matching prompts)\n`));
8233
+ console.log(dim$1(` Query: "${query}" (${matches.length} session(s) with matching prompts)\n`));
7581
8234
  const headers = [
7582
8235
  "#",
7583
8236
  "id",
@@ -7587,21 +8240,21 @@ async function cmdFind(query, opts) {
7587
8240
  ];
7588
8241
  const rows = matches.map((m, idx) => {
7589
8242
  const shortId = (m.sessionId ?? "no-session").slice(0, 8);
7590
- const when = m.lastMatchTs > 0 ? fmtTs(m.lastMatchTs) : dim("—");
7591
- const project = shortenProject(m.project || dim("—"));
8243
+ const when = m.lastMatchTs > 0 ? fmtTs(m.lastMatchTs) : dim$1("—");
8244
+ const project = shortenProject(m.project || dim$1("—"));
7592
8245
  const snippet = m.lastMatchDisplay.replace(/\n+/g, " ").trim().slice(0, 48);
7593
8246
  const display = snippet.length < m.lastMatchDisplay.replace(/\n+/g, " ").trim().length ? `"${snippet}…"` : `"${snippet}"`;
7594
8247
  return [
7595
- dim(String(idx + 1)),
8248
+ dim$1(String(idx + 1)),
7596
8249
  chalk.cyan(shortId),
7597
8250
  when,
7598
- dim(project),
8251
+ dim$1(project),
7599
8252
  chalk.dim(display)
7600
8253
  ];
7601
8254
  });
7602
8255
  console.log(renderTable(headers, rows));
7603
8256
  console.log();
7604
- console.log(dim(" Resume: ") + chalk.white("pai resume <id>"));
8257
+ console.log(dim$1(" Resume: ") + chalk.white("pai resume <id>"));
7605
8258
  console.log();
7606
8259
  }
7607
8260
 
@@ -7886,9 +8539,9 @@ function buildFeedFrom(allSessions, projects, liveSessions, allProjects) {
7886
8539
  const kw = dir ? noteKeywords(dir) : "";
7887
8540
  const tag = tagFor(e.status);
7888
8541
  const name = chalk.white(pad(e.name, 22));
7889
- const where = dir ? dim(shorten(dir, 40)) : chalk.green("(running in another tab)");
7890
- const age = e.status === "live" ? chalk.green("now") : e.lastActivity > 0 ? dim(fmtAge(e.lastActivity)) : dim("—");
7891
- const kwDisp = kw ? dim(" " + kw) : "";
8542
+ const where = dir ? dim$1(shorten(dir, 40)) : chalk.green("(running in another tab)");
8543
+ const age = e.status === "live" ? chalk.green("now") : e.lastActivity > 0 ? dim$1(fmtAge(e.lastActivity)) : dim$1("—");
8544
+ const kwDisp = kw ? dim$1(" " + kw) : "";
7892
8545
  const display = `${tag} ${name} ${pad(age, 5)} ${where}${kwDisp}`;
7893
8546
  const search = `${e.name} ${dir ?? ""} ${kw}`.toLowerCase();
7894
8547
  lines.push({
@@ -7946,15 +8599,15 @@ function filterLines(lines, query) {
7946
8599
  /** Cached per-directory context (recent notes) for the detail panel. */
7947
8600
  const ctxCache = /* @__PURE__ */ new Map();
7948
8601
  function contextFor(rec) {
7949
- if (!rec.dir) return rec.live ? [chalk.green("live — running in another tab")] : [dim("no directory on record")];
8602
+ if (!rec.dir) return rec.live ? [chalk.green("live — running in another tab")] : [dim$1("no directory on record")];
7950
8603
  const cached = ctxCache.get(rec.dir);
7951
8604
  if (cached) return cached;
7952
8605
  const lines = [];
7953
8606
  const notes = recentMarkdown(rec.dir, 4);
7954
8607
  if (notes.length) {
7955
- lines.push(dim("recent notes:"));
7956
- for (const n of notes) lines.push(" " + dim(n.rel.slice(0, 60)) + " " + dim(fmtAge(n.mtime)));
7957
- } else lines.push(dim("(no notes here yet)"));
8608
+ lines.push(dim$1("recent notes:"));
8609
+ for (const n of notes) lines.push(" " + dim$1(n.rel.slice(0, 60)) + " " + dim$1(fmtAge(n.mtime)));
8610
+ } else lines.push(dim$1("(no notes here yet)"));
7958
8611
  ctxCache.set(rec.dir, lines);
7959
8612
  return lines;
7960
8613
  }
@@ -7995,43 +8648,43 @@ async function runSelector(lines, onRemove) {
7995
8648
  const rows = out.rows && out.rows > 8 ? out.rows : 24;
7996
8649
  const cols = out.columns && out.columns > 20 ? out.columns : 100;
7997
8650
  const frame = [];
7998
- const title = chalk.bold(" pai") + dim(" — find a project or session");
8651
+ const title = chalk.bold(" pai") + dim$1(" — find a project or session");
7999
8652
  frame.push(title);
8000
8653
  if (mode === "search") frame.push(" " + chalk.cyan("search > ") + query + chalk.inverse(" "));
8001
- else frame.push(" " + dim(`${filtered.length}/${lines.length}`) + (query ? dim(` filter: ${query}`) : ""));
8002
- frame.push(dim(" " + "─".repeat(Math.min(cols - 4, 80))));
8654
+ else frame.push(" " + dim$1(`${filtered.length}/${lines.length}`) + (query ? dim$1(` filter: ${query}`) : ""));
8655
+ frame.push(dim$1(" " + "─".repeat(Math.min(cols - 4, 80))));
8003
8656
  const ctxHeight = 6;
8004
8657
  const listHeight = Math.max(3, rows - 4 - ctxHeight - 2);
8005
8658
  let start = 0;
8006
8659
  if (cursor >= listHeight) start = cursor - listHeight + 1;
8007
8660
  const end = Math.min(filtered.length, start + listHeight);
8008
- if (filtered.length === 0) frame.push(" " + dim("no matches — press s to change the search, esc to quit"));
8661
+ if (filtered.length === 0) frame.push(" " + dim$1("no matches — press s to change the search, esc to quit"));
8009
8662
  for (let i = start; i < end; i++) {
8010
8663
  const row = filtered[i];
8011
8664
  if (i === cursor) frame.push(chalk.inverse(" " + stripAnsi(row.display).slice(0, cols - 2).padEnd(cols - 2)));
8012
8665
  else frame.push(" " + row.display);
8013
8666
  }
8014
- frame.push(dim(" " + "─".repeat(Math.min(cols - 4, 80))));
8667
+ frame.push(dim$1(" " + "─".repeat(Math.min(cols - 4, 80))));
8015
8668
  const cur = filtered[cursor];
8016
8669
  if (cur) {
8017
- frame.push(" " + chalk.bold.white(cur.record.name) + (cur.record.dir ? dim(" " + shorten(cur.record.dir, cols - 30)) : ""));
8670
+ frame.push(" " + chalk.bold.white(cur.record.name) + (cur.record.dir ? dim$1(" " + shorten(cur.record.dir, cols - 30)) : ""));
8018
8671
  for (const l of contextFor(cur.record).slice(0, ctxHeight - 2)) frame.push(l);
8019
8672
  }
8020
- frame.push(dim(" " + "─".repeat(Math.min(cols - 4, 80))));
8021
- if (confirm) frame.push(" " + chalk.yellow(`Remove "${confirm.record.name}" from PAI's list?`) + dim(" files stay on disk ") + chalk.cyan("y") + dim("/") + chalk.cyan("N"));
8022
- else if (mode === "search") frame.push(" " + dim("type to filter · ") + chalk.cyan("Enter") + dim(" done · ") + chalk.cyan("esc") + dim(" cancel"));
8673
+ frame.push(dim$1(" " + "─".repeat(Math.min(cols - 4, 80))));
8674
+ if (confirm) frame.push(" " + chalk.yellow(`Remove "${confirm.record.name}" from PAI's list?`) + dim$1(" files stay on disk ") + chalk.cyan("y") + dim$1("/") + chalk.cyan("N"));
8675
+ else if (mode === "search") frame.push(" " + dim$1("type to filter · ") + chalk.cyan("Enter") + dim$1(" done · ") + chalk.cyan("esc") + dim$1(" cancel"));
8023
8676
  else {
8024
8677
  const parts = [];
8025
- if (cur?.record.live) parts.push(chalk.cyan("g") + dim(" go to tab"));
8026
- if (cur?.record.dir) parts.push(chalk.cyan("n") + dim(" new"));
8027
- if (cur?.record.dir) parts.push(chalk.cyan("c") + dim(" cd"));
8028
- if (cur?.record.dir) parts.push(chalk.cyan("f") + dim(" finder"));
8029
- if (cur?.record.slug) parts.push(chalk.cyan("d") + dim(" remove"));
8030
- parts.push(chalk.cyan("s") + dim(" search"));
8031
- parts.push(chalk.cyan("↑↓") + dim(" move"));
8032
- parts.push(chalk.cyan("q") + dim(" quit"));
8033
- frame.push(" " + parts.join(dim(" · ")));
8034
- if (flash) frame.push(" " + dim(flash));
8678
+ if (cur?.record.live) parts.push(chalk.cyan("g") + dim$1(" go to tab"));
8679
+ if (cur?.record.dir) parts.push(chalk.cyan("n") + dim$1(" new"));
8680
+ if (cur?.record.dir) parts.push(chalk.cyan("c") + dim$1(" cd"));
8681
+ if (cur?.record.dir) parts.push(chalk.cyan("f") + dim$1(" finder"));
8682
+ if (cur?.record.slug) parts.push(chalk.cyan("d") + dim$1(" remove"));
8683
+ parts.push(chalk.cyan("s") + dim$1(" search"));
8684
+ parts.push(chalk.cyan("↑↓") + dim$1(" move"));
8685
+ parts.push(chalk.cyan("q") + dim$1(" quit"));
8686
+ frame.push(" " + parts.join(dim$1(" · ")));
8687
+ if (flash) frame.push(" " + dim$1(flash));
8035
8688
  }
8036
8689
  const painted = frame.map((l) => truncVisible(l, cols)).join("\x1B[K\n");
8037
8690
  out.write("\x1B[H" + painted + "\x1B[K\x1B[J");
@@ -8204,7 +8857,7 @@ async function cmdPick(db, opts = {}) {
8204
8857
  const allProjects = getProjects(db, true);
8205
8858
  const lines = buildFeedFrom(allSessions, projects, live, allProjects);
8206
8859
  if (lines.length === 0) {
8207
- process.stderr.write(dim(" No projects or sessions found.\n"));
8860
+ process.stderr.write(dim$1(" No projects or sessions found.\n"));
8208
8861
  return;
8209
8862
  }
8210
8863
  const onRemove = (rec) => {
@@ -8227,7 +8880,7 @@ async function cmdPick(db, opts = {}) {
8227
8880
  return;
8228
8881
  }
8229
8882
  if (!record.dir) {
8230
- process.stderr.write(dim(` No directory on record for "${record.name}".\n`));
8883
+ process.stderr.write(dim$1(` No directory on record for "${record.name}".\n`));
8231
8884
  return;
8232
8885
  }
8233
8886
  if (action === "cd") {
@@ -8246,5 +8899,5 @@ async function cmdPick(db, opts = {}) {
8246
8899
  }
8247
8900
 
8248
8901
  //#endregion
8249
- export { registerMcpCommands as C, findMovedPath as D, registerProjectsCommands as E, resolveIdentifier as O, registerDaemonCommands as S, registerRegistryCommands as T, registerZettelCommands as _, cmdPauseAll as a, registerRestoreCommands as b, cmdPause as c, registerKgCommands as d, registerTopicCommands as f, registerObservationCommands as g, registerSkillCommands as h, cmdClearNames as i, registerHelpCommand as l, registerUpdateCommand as m, cmdFind as n, cmdGoto as o, registerNotifyCommands as p, cmdList as r, cmdEnd as s, cmdPick as t, registerDbCommands as u, registerObsidianCommands as v, registerMemoryCommands as w, registerBackupCommands as x, registerSetupCommand as y };
8250
- //# sourceMappingURL=pick-7Kg9DX7S.mjs.map
8902
+ export { registerDaemonCommands as C, registerProjectsCommands as D, registerRegistryCommands as E, findMovedPath as O, registerBackupCommands as S, registerMemoryCommands as T, registerObservationCommands as _, cmdPauseAll as a, registerSetupCommand as b, cmdPause as c, registerKgCommands as d, registerTopicCommands as f, registerSkillCommands as g, registerUpdateCommand as h, cmdClearNames as i, resolveIdentifier as k, registerHelpCommand as l, registerNotifyCommands as m, cmdFind as n, cmdGoto as o, registerTaskCommands as p, cmdList as r, cmdEnd as s, cmdPick as t, registerDbCommands as u, registerZettelCommands as v, registerMcpCommands as w, registerRestoreCommands as x, registerObsidianCommands as y };
8903
+ //# sourceMappingURL=pick-CS_TmOqX.mjs.map