@tekmidian/pai 0.12.2 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.mjs +2 -2
- package/dist/cli/program.d.mts.map +1 -1
- package/dist/cli/program.mjs +3 -2
- package/dist/cli/program.mjs.map +1 -1
- package/dist/{config-BuhHWyOK.mjs → config-C8m-tPhP.mjs} +22 -2
- package/dist/config-C8m-tPhP.mjs.map +1 -0
- package/dist/daemon/index.mjs +2 -2
- package/dist/{daemon-Ca7YXHUW.mjs → daemon-D2aKb4mJ.mjs} +2 -2
- package/dist/{daemon-Ca7YXHUW.mjs.map → daemon-D2aKb4mJ.mjs.map} +1 -1
- package/dist/daemon-mcp/index.mjs +581 -2
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/{pick-C5sUQ5g_.mjs → pick-CS_TmOqX.mjs} +1082 -445
- package/dist/pick-CS_TmOqX.mjs.map +1 -0
- package/dist/skills/Tasks/SKILL.md +101 -0
- package/docs/commands/README.md +6 -0
- package/docs/commands/backup.md +1 -1
- package/docs/commands/clear-names.md +1 -1
- package/docs/commands/daemon.md +1 -1
- package/docs/commands/db.md +1 -1
- package/docs/commands/end.md +1 -1
- package/docs/commands/help.md +1 -1
- package/docs/commands/kg.md +1 -1
- package/docs/commands/mcp.md +1 -1
- package/docs/commands/memory.md +1 -1
- package/docs/commands/notify.md +1 -1
- package/docs/commands/observation.md +1 -1
- package/docs/commands/obsidian.md +1 -1
- package/docs/commands/pause.md +1 -1
- package/docs/commands/project.md +1 -1
- package/docs/commands/projects.md +1 -1
- package/docs/commands/registry.md +1 -1
- package/docs/commands/restore.md +1 -1
- package/docs/commands/sessions.md +1 -1
- package/docs/commands/setup.md +1 -1
- package/docs/commands/shell-init.md +1 -1
- package/docs/commands/skill.md +1 -1
- package/docs/commands/task.md +90 -0
- package/docs/commands/topic.md +1 -1
- package/docs/commands/update.md +1 -1
- package/docs/commands/zettel.md +1 -1
- package/package.json +1 -1
- package/plugins/productivity/plugin.json +2 -2
- package/plugins/productivity/skills/Tasks/SKILL.md +126 -0
- package/dist/config-BuhHWyOK.mjs.map +0 -1
- package/dist/pick-C5sUQ5g_.mjs.map +0 -1
|
@@ -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-
|
|
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(`
|
|
@@ -934,12 +934,12 @@ function cmdNames(db, opts) {
|
|
|
934
934
|
if (seen.has(row.id)) continue;
|
|
935
935
|
seen.add(row.id);
|
|
936
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("default");
|
|
937
|
+
const perm = (row.session_config ? JSON.parse(row.session_config) : null)?.permission ?? dim$1("default");
|
|
938
938
|
tableRows.push([
|
|
939
|
-
bold(allNames.length ? allNames.join(", ") : row.slug),
|
|
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) => {
|
|
@@ -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-
|
|
2263
|
-
const { loadConfig: lc, ensureConfigDir } = await import("./config-
|
|
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();
|
|
@@ -4763,7 +5114,7 @@ function cmdSync(db, opts) {
|
|
|
4763
5114
|
if (!q) {
|
|
4764
5115
|
console.log();
|
|
4765
5116
|
console.log(header(" PAI Obsidian Sync"));
|
|
4766
|
-
console.log(dim(` Vault: ${vaultPath}`));
|
|
5117
|
+
console.log(dim$1(` Vault: ${vaultPath}`));
|
|
4767
5118
|
console.log();
|
|
4768
5119
|
}
|
|
4769
5120
|
if (!q) process.stdout.write(" Syncing symlinks...");
|
|
@@ -4799,7 +5150,7 @@ function cmdStatus$1(db, opts) {
|
|
|
4799
5150
|
const report = checkHealth(vaultPath, db);
|
|
4800
5151
|
console.log();
|
|
4801
5152
|
console.log(header(" PAI Obsidian Vault Status"));
|
|
4802
|
-
console.log(dim(` Vault: ${vaultPath}`));
|
|
5153
|
+
console.log(dim$1(` Vault: ${vaultPath}`));
|
|
4803
5154
|
console.log();
|
|
4804
5155
|
if (!existsSync(vaultPath)) {
|
|
4805
5156
|
console.log(warn(" Vault directory does not exist. Run: pai obsidian sync"));
|
|
@@ -4813,27 +5164,27 @@ function cmdStatus$1(db, opts) {
|
|
|
4813
5164
|
console.log(` ${chalk.green("Healthy:")} ${healthyCount} ${chalk.red("Broken:")} ${brokenCount} ${chalk.yellow("Orphaned:")} ${orphanedCount} ${chalk.cyan("Missing:")} ${missingCount}`);
|
|
4814
5165
|
console.log();
|
|
4815
5166
|
if (report.healthy.length && report.healthy.length <= 10) {
|
|
4816
|
-
console.log(bold(" Healthy symlinks:"));
|
|
4817
|
-
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 ?? ""))}`);
|
|
4818
5169
|
console.log();
|
|
4819
5170
|
} else if (report.healthy.length > 10) {
|
|
4820
|
-
console.log(bold(` ${report.healthy.length} healthy symlinks (all good)`));
|
|
5171
|
+
console.log(bold$1(` ${report.healthy.length} healthy symlinks (all good)`));
|
|
4821
5172
|
console.log();
|
|
4822
5173
|
}
|
|
4823
5174
|
if (report.broken.length) {
|
|
4824
5175
|
console.log(err(" Broken symlinks (target missing):"));
|
|
4825
|
-
for (const b of report.broken) console.log(` ${chalk.red("✗")} ${bold(b.slug)} ${dim(b.notes)}`);
|
|
4826
|
-
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"));
|
|
4827
5178
|
console.log();
|
|
4828
5179
|
}
|
|
4829
5180
|
if (report.orphaned.length) {
|
|
4830
5181
|
console.log(warn(" Orphaned symlinks (no registry project):"));
|
|
4831
|
-
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)}`);
|
|
4832
5183
|
console.log();
|
|
4833
5184
|
}
|
|
4834
5185
|
if (report.missing.length) {
|
|
4835
5186
|
console.log(warn(" Projects with Notes/ but no vault symlink:"));
|
|
4836
|
-
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)}`);
|
|
4837
5188
|
console.log();
|
|
4838
5189
|
}
|
|
4839
5190
|
if (brokenCount === 0 && orphanedCount === 0 && missingCount === 0) {
|
|
@@ -4847,17 +5198,17 @@ function cmdOpen(opts) {
|
|
|
4847
5198
|
const vaultName = parts[parts.length - 1] ?? "obsidian-vault";
|
|
4848
5199
|
if (!existsSync(vaultPath)) {
|
|
4849
5200
|
console.error(err(`Vault not found at: ${vaultPath}`));
|
|
4850
|
-
console.error(dim(" Run: pai obsidian sync"));
|
|
5201
|
+
console.error(dim$1(" Run: pai obsidian sync"));
|
|
4851
5202
|
process.exit(1);
|
|
4852
5203
|
}
|
|
4853
5204
|
const url = `obsidian://open?vault=${encodeURIComponent(vaultName)}`;
|
|
4854
|
-
console.log(dim(` Opening: ${url}`));
|
|
5205
|
+
console.log(dim$1(` Opening: ${url}`));
|
|
4855
5206
|
try {
|
|
4856
5207
|
execSync(`open "${url}"`, { stdio: "ignore" });
|
|
4857
5208
|
console.log(ok(` Opened vault "${vaultName}" in Obsidian.`));
|
|
4858
5209
|
} catch (e) {
|
|
4859
5210
|
console.error(err(` Failed to open Obsidian: ${e}`));
|
|
4860
|
-
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."));
|
|
4861
5212
|
process.exit(1);
|
|
4862
5213
|
}
|
|
4863
5214
|
}
|
|
@@ -4906,15 +5257,15 @@ async function cmdExplore(note, opts) {
|
|
|
4906
5257
|
});
|
|
4907
5258
|
console.log();
|
|
4908
5259
|
console.log(header(" PAI Zettel Explore"));
|
|
4909
|
-
console.log(dim(` Starting note: ${note}`));
|
|
4910
|
-
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}`));
|
|
4911
5262
|
console.log();
|
|
4912
5263
|
if (result.nodes.length === 0) {
|
|
4913
5264
|
console.log(warn(" No connected notes found. Check that the note path exists in the vault index."));
|
|
4914
5265
|
console.log();
|
|
4915
5266
|
return;
|
|
4916
5267
|
}
|
|
4917
|
-
console.log(` ${chalk.cyan("●")} ${bold(shortPath(result.root))} ${dim("(root)")}`);
|
|
5268
|
+
console.log(` ${chalk.cyan("●")} ${bold$1(shortPath(result.root))} ${dim$1("(root)")}`);
|
|
4918
5269
|
const byDepth = /* @__PURE__ */ new Map();
|
|
4919
5270
|
for (const node of result.nodes) {
|
|
4920
5271
|
const list = byDepth.get(node.depth) ?? [];
|
|
@@ -4925,20 +5276,20 @@ async function cmdExplore(note, opts) {
|
|
|
4925
5276
|
const nodes = byDepth.get(d) ?? [];
|
|
4926
5277
|
if (nodes.length === 0) continue;
|
|
4927
5278
|
console.log();
|
|
4928
|
-
console.log(dim(` ${" ".repeat(d - 1)}Depth ${d}:`));
|
|
5279
|
+
console.log(dim$1(` ${" ".repeat(d - 1)}Depth ${d}:`));
|
|
4929
5280
|
for (const node of nodes) {
|
|
4930
5281
|
const indent = " ".repeat(d);
|
|
4931
5282
|
const isBranching = result.branchingPoints.includes(node.path);
|
|
4932
5283
|
const typeColor = node.linkType === "sequential" ? chalk.blue : chalk.magenta;
|
|
4933
5284
|
const branchMark = isBranching ? chalk.yellow(" ⑂ branching") : "";
|
|
4934
5285
|
const title = node.title ?? shortPath(node.path);
|
|
4935
|
-
const stats = dim(`in:${node.inbound} out:${node.outbound}`);
|
|
4936
|
-
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))}`);
|
|
4937
5288
|
}
|
|
4938
5289
|
}
|
|
4939
5290
|
console.log();
|
|
4940
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)`;
|
|
4941
|
-
console.log(dim(` ${edgeSummary}`));
|
|
5292
|
+
console.log(dim$1(` ${edgeSummary}`));
|
|
4942
5293
|
if (result.branchingPoints.length > 0) console.log(ok(` ${result.branchingPoints.length} branching point(s) found`));
|
|
4943
5294
|
if (result.maxDepthReached) console.log(warn(" Max depth reached — use --depth to explore further"));
|
|
4944
5295
|
console.log();
|
|
@@ -4970,31 +5321,31 @@ async function cmdHealth(opts) {
|
|
|
4970
5321
|
});
|
|
4971
5322
|
console.log();
|
|
4972
5323
|
console.log(header(" PAI Zettel Health"));
|
|
4973
|
-
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}` : ""}`));
|
|
4974
5325
|
console.log();
|
|
4975
5326
|
const score = result.healthScore;
|
|
4976
5327
|
const scoreColor = score >= 80 ? chalk.green : score >= 60 ? chalk.yellow : chalk.red;
|
|
4977
5328
|
const barWidth = 30;
|
|
4978
5329
|
const filled = Math.round(score / 100 * barWidth);
|
|
4979
|
-
const bar = scoreColor("█".repeat(filled)) + dim("░".repeat(barWidth - filled));
|
|
4980
|
-
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}]`);
|
|
4981
5332
|
console.log();
|
|
4982
|
-
console.log(dim(` Files: ${result.totalFiles} Links: ${result.totalLinks}`));
|
|
5333
|
+
console.log(dim$1(` Files: ${result.totalFiles} Links: ${result.totalLinks}`));
|
|
4983
5334
|
console.log();
|
|
4984
5335
|
if (result.deadLinks.length === 0) console.log(ok(" Dead links: none"));
|
|
4985
5336
|
else {
|
|
4986
5337
|
console.log(warn(` Dead links: ${result.deadLinks.length}`));
|
|
4987
5338
|
const preview = result.deadLinks.slice(0, 10);
|
|
4988
|
-
for (const dl of preview) console.log(` ${chalk.red("✗")} ${dim(shortPath(dl.sourcePath))} → ${bold(dl.targetRaw)} ${dim(`(line ${dl.lineNumber})`)}`);
|
|
4989
|
-
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`));
|
|
4990
5341
|
console.log();
|
|
4991
5342
|
}
|
|
4992
5343
|
if (result.orphans.length === 0) console.log(ok(" Orphan notes: none"));
|
|
4993
5344
|
else {
|
|
4994
5345
|
console.log(warn(` Orphan notes: ${result.orphans.length}`));
|
|
4995
5346
|
const preview = result.orphans.slice(0, 10);
|
|
4996
|
-
for (const o of preview) console.log(` ${chalk.yellow("○")} ${dim(shortPath(o))}`);
|
|
4997
|
-
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`));
|
|
4998
5349
|
console.log();
|
|
4999
5350
|
}
|
|
5000
5351
|
if (result.disconnectedClusters <= 1) console.log(ok(" Disconnected clusters: 1 (fully connected)"));
|
|
@@ -5003,8 +5354,8 @@ async function cmdHealth(opts) {
|
|
|
5003
5354
|
else {
|
|
5004
5355
|
console.log(warn(` Low-connectivity: ${result.lowConnectivity.length} note(s) with ≤1 link`));
|
|
5005
5356
|
const preview = result.lowConnectivity.slice(0, 5);
|
|
5006
|
-
for (const lc of preview) console.log(` ${chalk.dim("—")} ${dim(shortPath(lc))}`);
|
|
5007
|
-
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`));
|
|
5008
5359
|
}
|
|
5009
5360
|
console.log();
|
|
5010
5361
|
}
|
|
@@ -5034,8 +5385,8 @@ async function cmdSurprise(note, opts) {
|
|
|
5034
5385
|
const db = getFedDb();
|
|
5035
5386
|
console.log();
|
|
5036
5387
|
console.log(header(" PAI Zettel Surprise"));
|
|
5037
|
-
console.log(dim(` Reference note: ${note}`));
|
|
5038
|
-
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"));
|
|
5039
5390
|
const results = await zettelSurprise(db, {
|
|
5040
5391
|
referencePath: note,
|
|
5041
5392
|
vaultProjectId,
|
|
@@ -5049,16 +5400,16 @@ async function cmdSurprise(note, opts) {
|
|
|
5049
5400
|
return;
|
|
5050
5401
|
}
|
|
5051
5402
|
console.log();
|
|
5052
|
-
console.log(bold(` Found ${results.length} surprising connection(s):`));
|
|
5403
|
+
console.log(bold$1(` Found ${results.length} surprising connection(s):`));
|
|
5053
5404
|
console.log();
|
|
5054
5405
|
for (let i = 0; i < results.length; i++) {
|
|
5055
5406
|
const r = results[i];
|
|
5056
5407
|
const title = r.title ?? shortPath(r.path);
|
|
5057
5408
|
const surpriseBar = Math.round(r.surpriseScore * 10);
|
|
5058
|
-
console.log(` ${chalk.cyan(String(i + 1).padStart(2, " "))}. ${bold(title)}`);
|
|
5059
|
-
console.log(` ${dim("Surprise:")} ${chalk.magenta(r.surpriseScore.toFixed(3))} ${"■".repeat(surpriseBar)}${"□".repeat(10 - surpriseBar)}`);
|
|
5060
|
-
console.log(` ${dim("Cosine:")} ${r.cosineSimilarity.toFixed(3)} ${dim("Graph distance:")} ${r.graphDistance}`);
|
|
5061
|
-
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)}`);
|
|
5062
5413
|
console.log();
|
|
5063
5414
|
}
|
|
5064
5415
|
}
|
|
@@ -5087,8 +5438,8 @@ async function cmdSuggest(note, opts) {
|
|
|
5087
5438
|
const db = getFedDb();
|
|
5088
5439
|
console.log();
|
|
5089
5440
|
console.log(header(" PAI Zettel Suggest"));
|
|
5090
|
-
console.log(dim(` Note: ${note}`));
|
|
5091
|
-
process.stdout.write(dim(" Computing suggestions...\n"));
|
|
5441
|
+
console.log(dim$1(` Note: ${note}`));
|
|
5442
|
+
process.stdout.write(dim$1(" Computing suggestions...\n"));
|
|
5092
5443
|
const suggestions = await zettelSuggest(db, {
|
|
5093
5444
|
notePath: note,
|
|
5094
5445
|
vaultProjectId,
|
|
@@ -5101,15 +5452,15 @@ async function cmdSuggest(note, opts) {
|
|
|
5101
5452
|
return;
|
|
5102
5453
|
}
|
|
5103
5454
|
console.log();
|
|
5104
|
-
console.log(bold(` ${suggestions.length} suggested connection(s):`));
|
|
5455
|
+
console.log(bold$1(` ${suggestions.length} suggested connection(s):`));
|
|
5105
5456
|
console.log();
|
|
5106
5457
|
for (let i = 0; i < suggestions.length; i++) {
|
|
5107
5458
|
const s = suggestions[i];
|
|
5108
5459
|
const title = s.title ?? shortPath(s.path);
|
|
5109
|
-
console.log(` ${chalk.green(String(i + 1).padStart(2, " "))}. ${bold(title)}`);
|
|
5110
|
-
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)}`);
|
|
5111
|
-
console.log(` ${dim("Reason:")} ${s.reason}`);
|
|
5112
|
-
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)}`);
|
|
5113
5464
|
console.log();
|
|
5114
5465
|
}
|
|
5115
5466
|
}
|
|
@@ -5138,8 +5489,8 @@ async function cmdConverse(question, opts) {
|
|
|
5138
5489
|
const db = getFedDb();
|
|
5139
5490
|
console.log();
|
|
5140
5491
|
console.log(header(" PAI Zettel Converse"));
|
|
5141
|
-
console.log(dim(` Question: "${question}"`));
|
|
5142
|
-
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"));
|
|
5143
5494
|
const result = await zettelConverse(db, {
|
|
5144
5495
|
question,
|
|
5145
5496
|
vaultProjectId,
|
|
@@ -5152,24 +5503,24 @@ async function cmdConverse(question, opts) {
|
|
|
5152
5503
|
return;
|
|
5153
5504
|
}
|
|
5154
5505
|
console.log();
|
|
5155
|
-
console.log(bold(` ${result.relevantNotes.length} relevant note(s) from ${result.domains.length} domain(s):`));
|
|
5156
|
-
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(", ")}`));
|
|
5157
5508
|
console.log();
|
|
5158
5509
|
for (const note of result.relevantNotes) {
|
|
5159
5510
|
const title = note.title ?? shortPath(note.path);
|
|
5160
|
-
console.log(` ${chalk.cyan("◆")} ${bold(title)} ${dim(`[${note.domain}] score: ${note.score.toFixed(3)}`)}`);
|
|
5161
|
-
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))}`);
|
|
5162
5513
|
console.log();
|
|
5163
5514
|
}
|
|
5164
5515
|
if (result.connections.length > 0) {
|
|
5165
|
-
console.log(bold(" Cross-domain connections:"));
|
|
5166
|
-
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}`)}`);
|
|
5167
5518
|
console.log();
|
|
5168
5519
|
}
|
|
5169
|
-
console.log(bold(" Synthesis prompt (paste into your AI):"));
|
|
5520
|
+
console.log(bold$1(" Synthesis prompt (paste into your AI):"));
|
|
5170
5521
|
console.log();
|
|
5171
5522
|
const promptLines = result.synthesisPrompt.split("\n");
|
|
5172
|
-
for (const line of promptLines) console.log(` ${dim(line)}`);
|
|
5523
|
+
for (const line of promptLines) console.log(` ${dim$1(line)}`);
|
|
5173
5524
|
console.log();
|
|
5174
5525
|
}
|
|
5175
5526
|
function registerConverseCommand(parent) {
|
|
@@ -5199,8 +5550,8 @@ async function cmdThemes(opts) {
|
|
|
5199
5550
|
const db = getFedDb();
|
|
5200
5551
|
console.log();
|
|
5201
5552
|
console.log(header(" PAI Zettel Themes"));
|
|
5202
|
-
console.log(dim(` Lookback: ${lookbackDays}d Min cluster: ${minClusterSize} Threshold: ${similarityThreshold}`));
|
|
5203
|
-
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"));
|
|
5204
5555
|
const result = await zettelThemes(db, {
|
|
5205
5556
|
vaultProjectId,
|
|
5206
5557
|
lookbackDays,
|
|
@@ -5216,20 +5567,20 @@ async function cmdThemes(opts) {
|
|
|
5216
5567
|
const fromDate = new Date(result.timeWindow.from).toISOString().slice(0, 10);
|
|
5217
5568
|
const toDate = new Date(result.timeWindow.to).toISOString().slice(0, 10);
|
|
5218
5569
|
console.log();
|
|
5219
|
-
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}]:`));
|
|
5220
5571
|
console.log();
|
|
5221
5572
|
for (let i = 0; i < result.themes.length; i++) {
|
|
5222
5573
|
const cluster = result.themes[i];
|
|
5223
5574
|
const diversityBar = Math.round(cluster.folderDiversity * 10);
|
|
5224
5575
|
const indexSuggestion = cluster.suggestIndexNote ? chalk.yellow(" ⚑ suggest index note") : "";
|
|
5225
|
-
console.log(` ${chalk.cyan(String(i + 1).padStart(2, " "))}. ${bold(cluster.label)}${indexSuggestion}`);
|
|
5226
|
-
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)}%`);
|
|
5227
5578
|
const preview = cluster.notes.slice(0, 5);
|
|
5228
5579
|
for (const note of preview) {
|
|
5229
5580
|
const title = note.title ?? shortPath(note.path);
|
|
5230
|
-
console.log(` ${dim("•")} ${title}`);
|
|
5581
|
+
console.log(` ${dim$1("•")} ${title}`);
|
|
5231
5582
|
}
|
|
5232
|
-
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`));
|
|
5233
5584
|
console.log();
|
|
5234
5585
|
}
|
|
5235
5586
|
}
|
|
@@ -5311,12 +5662,12 @@ function typeColor(type) {
|
|
|
5311
5662
|
}
|
|
5312
5663
|
}
|
|
5313
5664
|
function fmtTs$2(ts) {
|
|
5314
|
-
if (!ts) return dim("—");
|
|
5665
|
+
if (!ts) return dim$1("—");
|
|
5315
5666
|
try {
|
|
5316
5667
|
const d = new Date(ts);
|
|
5317
5668
|
return `${d.toISOString().slice(0, 10)} ${d.toISOString().slice(11, 16)}`;
|
|
5318
5669
|
} catch {
|
|
5319
|
-
return dim("—");
|
|
5670
|
+
return dim$1("—");
|
|
5320
5671
|
}
|
|
5321
5672
|
}
|
|
5322
5673
|
function trunc$1(s, maxLen) {
|
|
@@ -5335,7 +5686,7 @@ async function cmdList$2(opts) {
|
|
|
5335
5686
|
observations = await ipcCall$1("observation_list", params);
|
|
5336
5687
|
} catch (e) {
|
|
5337
5688
|
console.error(err(` Failed to reach PAI daemon: ${e}`));
|
|
5338
|
-
console.error(dim(" Is the daemon running? Try: pai daemon status"));
|
|
5689
|
+
console.error(dim$1(" Is the daemon running? Try: pai daemon status"));
|
|
5339
5690
|
process.exit(1);
|
|
5340
5691
|
}
|
|
5341
5692
|
console.log();
|
|
@@ -5345,7 +5696,7 @@ async function cmdList$2(opts) {
|
|
|
5345
5696
|
if (opts.type) filterParts.push(`type: ${opts.type}`);
|
|
5346
5697
|
if (opts.session) filterParts.push(`session: ${opts.session}`);
|
|
5347
5698
|
filterParts.push(`limit: ${limit}`);
|
|
5348
|
-
console.log(dim(` ${filterParts.join(" | ")}`));
|
|
5699
|
+
console.log(dim$1(` ${filterParts.join(" | ")}`));
|
|
5349
5700
|
console.log();
|
|
5350
5701
|
if (!observations || observations.length === 0) {
|
|
5351
5702
|
console.log(warn(" No observations found."));
|
|
@@ -5357,18 +5708,18 @@ async function cmdList$2(opts) {
|
|
|
5357
5708
|
const TITLE_W = 42;
|
|
5358
5709
|
const PROJ_W = 14;
|
|
5359
5710
|
const TS_W = 16;
|
|
5360
|
-
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"));
|
|
5361
|
-
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)));
|
|
5362
5713
|
for (const obs of observations) {
|
|
5363
5714
|
const idStr = String(obs.id).padStart(ID_W, " ");
|
|
5364
5715
|
const typeStr = typeColor(obs.type ?? "").padEnd(TYPE_W + (typeColor(obs.type ?? "").length - (obs.type ?? "").length));
|
|
5365
5716
|
const titleStr = trunc$1(obs.title ?? "", TITLE_W).padEnd(TITLE_W);
|
|
5366
5717
|
const projStr = trunc$1(obs.project_slug ?? "—", PROJ_W).padEnd(PROJ_W);
|
|
5367
5718
|
const tsStr = fmtTs$2(obs.created_at);
|
|
5368
|
-
console.log(` ${idStr} ${typeStr} ${titleStr} ${projStr} ${dim(tsStr)}`);
|
|
5719
|
+
console.log(` ${idStr} ${typeStr} ${titleStr} ${projStr} ${dim$1(tsStr)}`);
|
|
5369
5720
|
}
|
|
5370
5721
|
console.log();
|
|
5371
|
-
console.log(dim(` ${observations.length} observation(s)`));
|
|
5722
|
+
console.log(dim$1(` ${observations.length} observation(s)`));
|
|
5372
5723
|
console.log();
|
|
5373
5724
|
}
|
|
5374
5725
|
async function cmdSearch(query, opts) {
|
|
@@ -5388,14 +5739,14 @@ async function cmdSearch(query, opts) {
|
|
|
5388
5739
|
});
|
|
5389
5740
|
} catch (e) {
|
|
5390
5741
|
console.error(err(` Failed to reach PAI daemon: ${e}`));
|
|
5391
|
-
console.error(dim(" Is the daemon running? Try: pai daemon status"));
|
|
5742
|
+
console.error(dim$1(" Is the daemon running? Try: pai daemon status"));
|
|
5392
5743
|
process.exit(1);
|
|
5393
5744
|
}
|
|
5394
5745
|
const q = query.toLowerCase();
|
|
5395
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);
|
|
5396
5747
|
console.log();
|
|
5397
5748
|
console.log(header(" PAI Observation Search"));
|
|
5398
|
-
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}`));
|
|
5399
5750
|
console.log();
|
|
5400
5751
|
if (!observations || observations.length === 0) {
|
|
5401
5752
|
console.log(warn(` No observations matching "${query}".`));
|
|
@@ -5406,19 +5757,19 @@ async function cmdSearch(query, opts) {
|
|
|
5406
5757
|
const obs = observations[i];
|
|
5407
5758
|
const idx = chalk.dim(String(i + 1).padStart(3, " "));
|
|
5408
5759
|
const type = typeColor(obs.type ?? "");
|
|
5409
|
-
const title = bold(obs.title ?? "(untitled)");
|
|
5410
|
-
const proj = obs.project_slug ? dim(`[${obs.project_slug}]`) : dim("[—]");
|
|
5411
|
-
const ts = dim(fmtTs$2(obs.created_at));
|
|
5412
|
-
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}`);
|
|
5413
5764
|
console.log(` ${idx} ${type.padEnd(12)} ${title}`);
|
|
5414
5765
|
console.log(` ${proj} ${ts} ${id}`);
|
|
5415
5766
|
if (obs.narrative) {
|
|
5416
5767
|
const snippet = trunc$1(obs.narrative.replace(/\n/g, " "), 160);
|
|
5417
|
-
console.log(` ${dim(snippet)}`);
|
|
5768
|
+
console.log(` ${dim$1(snippet)}`);
|
|
5418
5769
|
}
|
|
5419
5770
|
console.log();
|
|
5420
5771
|
}
|
|
5421
|
-
console.log(dim(` ${observations.length} result(s)`));
|
|
5772
|
+
console.log(dim$1(` ${observations.length} result(s)`));
|
|
5422
5773
|
console.log();
|
|
5423
5774
|
}
|
|
5424
5775
|
async function cmdStats$1() {
|
|
@@ -5427,39 +5778,39 @@ async function cmdStats$1() {
|
|
|
5427
5778
|
stats = await ipcCall$1("observation_stats", {});
|
|
5428
5779
|
} catch (e) {
|
|
5429
5780
|
console.error(err(` Failed to reach PAI daemon: ${e}`));
|
|
5430
|
-
console.error(dim(" Is the daemon running? Try: pai daemon status"));
|
|
5781
|
+
console.error(dim$1(" Is the daemon running? Try: pai daemon status"));
|
|
5431
5782
|
process.exit(1);
|
|
5432
5783
|
}
|
|
5433
5784
|
console.log();
|
|
5434
5785
|
console.log(header(" PAI Observation Statistics"));
|
|
5435
5786
|
console.log();
|
|
5436
|
-
console.log(` ${bold("Total observations:")} ${chalk.cyan(String(stats.total ?? 0))}`);
|
|
5437
|
-
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))}`);
|
|
5438
5789
|
console.log();
|
|
5439
5790
|
if (stats.by_type && stats.by_type.length > 0) {
|
|
5440
|
-
console.log(bold(" By type:"));
|
|
5791
|
+
console.log(bold$1(" By type:"));
|
|
5441
5792
|
const maxCount = Math.max(...stats.by_type.map((r) => r.count));
|
|
5442
5793
|
for (const row of stats.by_type) {
|
|
5443
5794
|
const barWidth = 20;
|
|
5444
5795
|
const filled = Math.round(row.count / maxCount * barWidth);
|
|
5445
|
-
const bar = chalk.cyan("█".repeat(filled)) + dim("░".repeat(barWidth - filled));
|
|
5796
|
+
const bar = chalk.cyan("█".repeat(filled)) + dim$1("░".repeat(barWidth - filled));
|
|
5446
5797
|
const label = typeColor(row.type).padEnd(12 + (typeColor(row.type).length - row.type.length));
|
|
5447
5798
|
console.log(` ${label} ${bar} ${String(row.count).padStart(5)}`);
|
|
5448
5799
|
}
|
|
5449
5800
|
console.log();
|
|
5450
5801
|
}
|
|
5451
5802
|
if (stats.by_project && stats.by_project.length > 0) {
|
|
5452
|
-
console.log(bold(" By project:"));
|
|
5803
|
+
console.log(bold$1(" By project:"));
|
|
5453
5804
|
const maxCount = Math.max(...stats.by_project.map((r) => r.count));
|
|
5454
5805
|
const show = stats.by_project.slice(0, 15);
|
|
5455
5806
|
for (const row of show) {
|
|
5456
5807
|
const barWidth = 20;
|
|
5457
5808
|
const filled = Math.round(row.count / maxCount * barWidth);
|
|
5458
|
-
const bar = chalk.green("█".repeat(filled)) + dim("░".repeat(barWidth - filled));
|
|
5809
|
+
const bar = chalk.green("█".repeat(filled)) + dim$1("░".repeat(barWidth - filled));
|
|
5459
5810
|
const label = (row.project_slug ?? "—").padEnd(20);
|
|
5460
|
-
console.log(` ${dim(label)} ${bar} ${String(row.count).padStart(5)}`);
|
|
5811
|
+
console.log(` ${dim$1(label)} ${bar} ${String(row.count).padStart(5)}`);
|
|
5461
5812
|
}
|
|
5462
|
-
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)`));
|
|
5463
5814
|
console.log();
|
|
5464
5815
|
}
|
|
5465
5816
|
if ((!stats.by_type || stats.by_type.length === 0) && (!stats.by_project || stats.by_project.length === 0)) {
|
|
@@ -5539,12 +5890,12 @@ function ipcCall(method, params) {
|
|
|
5539
5890
|
});
|
|
5540
5891
|
}
|
|
5541
5892
|
function fmtTs$1(ts) {
|
|
5542
|
-
if (!ts) return dim("—");
|
|
5893
|
+
if (!ts) return dim$1("—");
|
|
5543
5894
|
try {
|
|
5544
5895
|
const d = new Date(ts);
|
|
5545
5896
|
return `${d.toISOString().slice(0, 10)} ${d.toISOString().slice(11, 16)}`;
|
|
5546
5897
|
} catch {
|
|
5547
|
-
return dim("—");
|
|
5898
|
+
return dim$1("—");
|
|
5548
5899
|
}
|
|
5549
5900
|
}
|
|
5550
5901
|
function statusColor$1(status) {
|
|
@@ -5570,7 +5921,7 @@ async function cmdTelemetry(opts) {
|
|
|
5570
5921
|
rows = await ipcCall("skill_telemetry_query", params);
|
|
5571
5922
|
} catch (e) {
|
|
5572
5923
|
console.error(err(` Failed to reach PAI daemon: ${e}`));
|
|
5573
|
-
console.error(dim(" Is the daemon running? Try: pai daemon status"));
|
|
5924
|
+
console.error(dim$1(" Is the daemon running? Try: pai daemon status"));
|
|
5574
5925
|
process.exit(1);
|
|
5575
5926
|
}
|
|
5576
5927
|
if (opts.json) {
|
|
@@ -5579,11 +5930,11 @@ async function cmdTelemetry(opts) {
|
|
|
5579
5930
|
}
|
|
5580
5931
|
console.log();
|
|
5581
5932
|
console.log(header(" Skill Telemetry"));
|
|
5582
|
-
console.log(dim(` scope: ${params.scope}${opts.status ? ` | status: ${opts.status}` : ""}`));
|
|
5933
|
+
console.log(dim$1(` scope: ${params.scope}${opts.status ? ` | status: ${opts.status}` : ""}`));
|
|
5583
5934
|
console.log();
|
|
5584
5935
|
if (!rows || rows.length === 0) {
|
|
5585
5936
|
console.log(warn(" No skill telemetry yet."));
|
|
5586
|
-
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."));
|
|
5587
5938
|
console.log();
|
|
5588
5939
|
return;
|
|
5589
5940
|
}
|
|
@@ -5592,8 +5943,8 @@ async function cmdTelemetry(opts) {
|
|
|
5592
5943
|
const STAT_W = 9;
|
|
5593
5944
|
const TRIG_W = 8;
|
|
5594
5945
|
const SEEN_W = 16;
|
|
5595
|
-
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"));
|
|
5596
|
-
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)));
|
|
5597
5948
|
for (const r of rows) {
|
|
5598
5949
|
const name = trunc(r.skill_name ?? "", NAME_W).padEnd(NAME_W);
|
|
5599
5950
|
const src = trunc(r.source ?? "", SRC_W).padEnd(SRC_W);
|
|
@@ -5602,10 +5953,10 @@ async function cmdTelemetry(opts) {
|
|
|
5602
5953
|
const trig = String(r.trigger_count ?? 0).padStart(TRIG_W);
|
|
5603
5954
|
const seen = fmtTs$1(r.last_triggered).padEnd(SEEN_W);
|
|
5604
5955
|
const projs = Array.isArray(r.context_projects) ? r.context_projects.join(", ") : "";
|
|
5605
|
-
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))}`);
|
|
5606
5957
|
}
|
|
5607
5958
|
console.log();
|
|
5608
|
-
console.log(dim(` ${rows.length} skill(s)`));
|
|
5959
|
+
console.log(dim$1(` ${rows.length} skill(s)`));
|
|
5609
5960
|
console.log();
|
|
5610
5961
|
}
|
|
5611
5962
|
function registerSkillCommands(parent) {
|
|
@@ -5628,7 +5979,7 @@ function step(msg) {
|
|
|
5628
5979
|
console.log(chalk.bold.cyan(`\n ${msg}`));
|
|
5629
5980
|
}
|
|
5630
5981
|
function info(msg) {
|
|
5631
|
-
console.log(dim(` ${msg}`));
|
|
5982
|
+
console.log(dim$1(` ${msg}`));
|
|
5632
5983
|
}
|
|
5633
5984
|
function success(msg) {
|
|
5634
5985
|
console.log(ok(` ${msg}`));
|
|
@@ -5643,7 +5994,7 @@ function error(msg) {
|
|
|
5643
5994
|
* Run a shell command, streaming output to stdout.
|
|
5644
5995
|
* Returns true on success, false on failure.
|
|
5645
5996
|
*/
|
|
5646
|
-
function run(cmd, cwd) {
|
|
5997
|
+
function run$1(cmd, cwd) {
|
|
5647
5998
|
try {
|
|
5648
5999
|
execSync(cmd, {
|
|
5649
6000
|
stdio: "inherit",
|
|
@@ -5744,7 +6095,7 @@ function stepStash(repoDir) {
|
|
|
5744
6095
|
function stepPull(repoDir) {
|
|
5745
6096
|
step("Pulling latest from origin/main...");
|
|
5746
6097
|
const headBefore = capture("git rev-parse HEAD", repoDir);
|
|
5747
|
-
if (!run("git pull origin main", repoDir)) {
|
|
6098
|
+
if (!run$1("git pull origin main", repoDir)) {
|
|
5748
6099
|
warning("git pull failed. There may be merge conflicts with your stash.");
|
|
5749
6100
|
return null;
|
|
5750
6101
|
}
|
|
@@ -5792,12 +6143,12 @@ function stepPopStash(repoDir) {
|
|
|
5792
6143
|
function stepBuild(repoDir) {
|
|
5793
6144
|
step("Rebuilding PAI (bun install && bun run build)...");
|
|
5794
6145
|
info("Installing dependencies...");
|
|
5795
|
-
if (!run("bun install", repoDir)) {
|
|
6146
|
+
if (!run$1("bun install", repoDir)) {
|
|
5796
6147
|
error("bun install failed.");
|
|
5797
6148
|
return false;
|
|
5798
6149
|
}
|
|
5799
6150
|
info("Building...");
|
|
5800
|
-
if (!run("bun run build", repoDir)) {
|
|
6151
|
+
if (!run$1("bun run build", repoDir)) {
|
|
5801
6152
|
error("bun run build failed.");
|
|
5802
6153
|
return false;
|
|
5803
6154
|
}
|
|
@@ -5911,7 +6262,7 @@ async function runUpdate() {
|
|
|
5911
6262
|
console.log(chalk.bold.cyan(" ─────────────────────────────────────"));
|
|
5912
6263
|
if (newCommitsLog) success("PAI updated successfully!");
|
|
5913
6264
|
else success("PAI is up to date and rebuilt successfully!");
|
|
5914
|
-
console.log(dim(" Version: ") + chalk.cyan(capture("pai --version", repoDir) ?? "unknown"));
|
|
6265
|
+
console.log(dim$1(" Version: ") + chalk.cyan(capture("pai --version", repoDir) ?? "unknown"));
|
|
5915
6266
|
line();
|
|
5916
6267
|
}
|
|
5917
6268
|
function registerUpdateCommand(program) {
|
|
@@ -5928,7 +6279,7 @@ function makeClient$1() {
|
|
|
5928
6279
|
function modeColor(mode) {
|
|
5929
6280
|
switch (mode) {
|
|
5930
6281
|
case "off": return warn(mode);
|
|
5931
|
-
case "voice": return bold(ok(mode));
|
|
6282
|
+
case "voice": return bold$1(ok(mode));
|
|
5932
6283
|
case "auto": return ok(mode);
|
|
5933
6284
|
default: return ok(mode);
|
|
5934
6285
|
}
|
|
@@ -5937,23 +6288,23 @@ function printConfig(config, activeChannels) {
|
|
|
5937
6288
|
console.log();
|
|
5938
6289
|
console.log(header(" PAI Notification Config"));
|
|
5939
6290
|
console.log();
|
|
5940
|
-
console.log(` ${bold("Mode:")} ${modeColor(config.mode)}`);
|
|
6291
|
+
console.log(` ${bold$1("Mode:")} ${modeColor(config.mode)}`);
|
|
5941
6292
|
console.log();
|
|
5942
|
-
console.log(` ${bold("Channels:")}`);
|
|
6293
|
+
console.log(` ${bold$1("Channels:")}`);
|
|
5943
6294
|
for (const [ch, cfg] of Object.entries(config.channels)) {
|
|
5944
6295
|
const c = cfg;
|
|
5945
|
-
const status = c.enabled ? ok("enabled") : dim("disabled");
|
|
6296
|
+
const status = c.enabled ? ok("enabled") : dim$1("disabled");
|
|
5946
6297
|
let extra = "";
|
|
5947
|
-
if (ch === "ntfy" && c.url) extra = dim(` ${c.url}`);
|
|
5948
|
-
if (ch === "voice" && c.voiceName) extra = dim(` voice: ${c.voiceName}`);
|
|
5949
|
-
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}`);
|
|
5950
6301
|
}
|
|
5951
6302
|
console.log();
|
|
5952
|
-
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)")}`);
|
|
5953
6304
|
console.log();
|
|
5954
|
-
console.log(` ${bold("Routing:")} ${dim("(auto mode only)")}`);
|
|
6305
|
+
console.log(` ${bold$1("Routing:")} ${dim$1("(auto mode only)")}`);
|
|
5955
6306
|
for (const [event, channels] of Object.entries(config.routing)) {
|
|
5956
|
-
const ch = channels.join(", ") || dim("(none)");
|
|
6307
|
+
const ch = channels.join(", ") || dim$1("(none)");
|
|
5957
6308
|
console.log(` ${event.padEnd(12)}→ ${ch}`);
|
|
5958
6309
|
}
|
|
5959
6310
|
console.log();
|
|
@@ -5967,8 +6318,8 @@ async function cmdStatus() {
|
|
|
5967
6318
|
const msg = e instanceof Error ? e.message : String(e);
|
|
5968
6319
|
console.log();
|
|
5969
6320
|
console.log(warn(" Cannot reach PAI daemon."));
|
|
5970
|
-
console.log(dim(` ${msg}`));
|
|
5971
|
-
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"));
|
|
5972
6323
|
console.log();
|
|
5973
6324
|
process.exit(1);
|
|
5974
6325
|
}
|
|
@@ -6010,15 +6361,15 @@ async function cmdSet(opts) {
|
|
|
6010
6361
|
if (Object.keys(channels).length > 0) patch.channels = channels;
|
|
6011
6362
|
if (!patch.mode && !patch.channels) {
|
|
6012
6363
|
console.error(err("No changes specified. Use --mode, --enable, --disable, etc."));
|
|
6013
|
-
console.log(dim(" Example: pai notify set --mode voice"));
|
|
6014
|
-
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"));
|
|
6015
6366
|
process.exit(1);
|
|
6016
6367
|
}
|
|
6017
6368
|
try {
|
|
6018
6369
|
const { config } = await client.setNotificationConfig(patch);
|
|
6019
6370
|
console.log();
|
|
6020
6371
|
console.log(ok(" Notification config updated."));
|
|
6021
|
-
console.log(` ${bold("Mode:")} ${modeColor(config.mode)}`);
|
|
6372
|
+
console.log(` ${bold$1("Mode:")} ${modeColor(config.mode)}`);
|
|
6022
6373
|
console.log();
|
|
6023
6374
|
} catch (e) {
|
|
6024
6375
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -6030,7 +6381,7 @@ async function cmdTest(opts) {
|
|
|
6030
6381
|
const client = makeClient$1();
|
|
6031
6382
|
const event = opts.event ?? "info";
|
|
6032
6383
|
console.log();
|
|
6033
|
-
console.log(dim(` Sending test ${event} notification...`));
|
|
6384
|
+
console.log(dim$1(` Sending test ${event} notification...`));
|
|
6034
6385
|
try {
|
|
6035
6386
|
const result = await client.sendNotification({
|
|
6036
6387
|
event,
|
|
@@ -6041,7 +6392,7 @@ async function cmdTest(opts) {
|
|
|
6041
6392
|
else if (result.channelsAttempted.length === 0) console.log(warn(" No channels active for this event. Check mode and channel config."));
|
|
6042
6393
|
else console.log(ok(` Sent to: ${result.channelsSucceeded.join(", ")}`));
|
|
6043
6394
|
if (result.channelsFailed.length > 0) console.log(warn(` Failed: ${result.channelsFailed.join(", ")}`));
|
|
6044
|
-
console.log(dim(` Mode: ${result.mode}`));
|
|
6395
|
+
console.log(dim$1(` Mode: ${result.mode}`));
|
|
6045
6396
|
console.log();
|
|
6046
6397
|
} catch (e) {
|
|
6047
6398
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -6102,6 +6453,292 @@ function registerNotifyCommands(notifyCmd) {
|
|
|
6102
6453
|
});
|
|
6103
6454
|
}
|
|
6104
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
|
+
|
|
6105
6742
|
//#endregion
|
|
6106
6743
|
//#region src/cli/commands/topic.ts
|
|
6107
6744
|
function makeClient() {
|
|
@@ -6116,32 +6753,32 @@ function printTopicResult(result) {
|
|
|
6116
6753
|
console.log();
|
|
6117
6754
|
console.log(header(" PAI Topic Check"));
|
|
6118
6755
|
console.log();
|
|
6119
|
-
console.log(` ${bold("Current project:")} ${result.currentProject ?? dim("(none)")}`);
|
|
6120
|
-
console.log(` ${bold("Suggested project:")} ${result.suggestedProject ?? dim("(none)")}`);
|
|
6121
|
-
console.log(` ${bold("Confidence:")} ${confidenceBar(result.confidence)} ${(result.confidence * 100).toFixed(1)}%`);
|
|
6122
|
-
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}`);
|
|
6123
6760
|
if (result.topProjects.length > 0) {
|
|
6124
6761
|
console.log();
|
|
6125
|
-
console.log(` ${bold("Top matches:")}`);
|
|
6762
|
+
console.log(` ${bold$1("Top matches:")}`);
|
|
6126
6763
|
for (const p of result.topProjects) {
|
|
6127
6764
|
const bar = confidenceBar(p.score, 15);
|
|
6128
|
-
const marker = p.slug === result.currentProject ? dim(" (current)") : "";
|
|
6765
|
+
const marker = p.slug === result.currentProject ? dim$1(" (current)") : "";
|
|
6129
6766
|
console.log(` ${p.slug.padEnd(30)} ${bar} ${(p.score * 100).toFixed(1)}%${marker}`);
|
|
6130
6767
|
}
|
|
6131
6768
|
}
|
|
6132
6769
|
console.log();
|
|
6133
6770
|
if (result.shifted) {
|
|
6134
|
-
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}"`));
|
|
6135
6772
|
console.log();
|
|
6136
6773
|
} else if (result.suggestedProject && result.suggestedProject === result.currentProject) {
|
|
6137
|
-
console.log(ok(" No shift detected") + dim(" — context matches current project"));
|
|
6774
|
+
console.log(ok(" No shift detected") + dim$1(" — context matches current project"));
|
|
6138
6775
|
console.log();
|
|
6139
6776
|
} else if (!result.currentProject) {
|
|
6140
|
-
if (result.suggestedProject) console.log(ok(" Best matching project: ") + bold(result.suggestedProject) + dim(` (confidence: ${(result.confidence * 100).toFixed(0)}%)`));
|
|
6141
|
-
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."));
|
|
6142
6779
|
console.log();
|
|
6143
6780
|
} else {
|
|
6144
|
-
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)`));
|
|
6145
6782
|
console.log();
|
|
6146
6783
|
}
|
|
6147
6784
|
}
|
|
@@ -6168,8 +6805,8 @@ async function cmdCheck(context, opts) {
|
|
|
6168
6805
|
const msg = e instanceof Error ? e.message : String(e);
|
|
6169
6806
|
console.log();
|
|
6170
6807
|
console.log(warn(" Cannot reach PAI daemon."));
|
|
6171
|
-
console.log(dim(` ${msg}`));
|
|
6172
|
-
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"));
|
|
6173
6810
|
console.log();
|
|
6174
6811
|
process.exit(1);
|
|
6175
6812
|
}
|
|
@@ -6342,7 +6979,7 @@ async function getPool$1() {
|
|
|
6342
6979
|
const config = loadConfig();
|
|
6343
6980
|
if (config.storageBackend !== "postgres") {
|
|
6344
6981
|
console.error(err(" KG commands require Postgres backend."));
|
|
6345
|
-
console.error(dim(" Set \"storageBackend\": \"postgres\" in ~/.config/pai/config.json"));
|
|
6982
|
+
console.error(dim$1(" Set \"storageBackend\": \"postgres\" in ~/.config/pai/config.json"));
|
|
6346
6983
|
process.exit(1);
|
|
6347
6984
|
}
|
|
6348
6985
|
const backend = await createStorageBackend(config);
|
|
@@ -6367,9 +7004,9 @@ async function cmdBackfill(opts) {
|
|
|
6367
7004
|
console.log();
|
|
6368
7005
|
console.log(header(" PAI KG Backfill"));
|
|
6369
7006
|
console.log();
|
|
6370
|
-
if (opts.project) console.log(` ${bold("Project:")} ${opts.project}`);
|
|
6371
|
-
if (limit) console.log(` ${bold("Limit:")} ${limit}`);
|
|
6372
|
-
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")}`);
|
|
6373
7010
|
console.log();
|
|
6374
7011
|
try {
|
|
6375
7012
|
const result = await backfillKgFromNotes({
|
|
@@ -6378,15 +7015,15 @@ async function cmdBackfill(opts) {
|
|
|
6378
7015
|
dryRun: opts.dryRun,
|
|
6379
7016
|
onProgress: (current, total, note) => {
|
|
6380
7017
|
const short = shorten$1(note.replace(process.env.HOME ?? "", "~"), 70);
|
|
6381
|
-
process.stdout.write(` [${current}/${total}] ${dim(short)}\n`);
|
|
7018
|
+
process.stdout.write(` [${current}/${total}] ${dim$1(short)}\n`);
|
|
6382
7019
|
}
|
|
6383
7020
|
});
|
|
6384
7021
|
console.log();
|
|
6385
7022
|
console.log(ok(" Backfill complete"));
|
|
6386
|
-
console.log(` Notes processed: ${bold(String(result.notes_processed))}`);
|
|
6387
|
-
console.log(` Triples extracted: ${bold(String(result.triples_extracted))}`);
|
|
6388
|
-
console.log(` Triples added: ${bold(String(result.triples_added))}`);
|
|
6389
|
-
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))}`);
|
|
6390
7027
|
if (result.errors > 0) console.log(` ${warn("Errors:")} ${result.errors}`);
|
|
6391
7028
|
console.log();
|
|
6392
7029
|
} catch (e) {
|
|
@@ -6424,8 +7061,8 @@ async function cmdQuery$1(opts) {
|
|
|
6424
7061
|
console.log(header(` ${triples.length} triple(s)`));
|
|
6425
7062
|
console.log();
|
|
6426
7063
|
for (const t of triples) {
|
|
6427
|
-
const validity = t.valid_to ? dim(`(invalidated ${t.valid_to.toISOString().slice(0, 10)})`) : dim(`(valid since ${t.valid_from.toISOString().slice(0, 10)})`);
|
|
6428
|
-
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}`);
|
|
6429
7066
|
}
|
|
6430
7067
|
console.log();
|
|
6431
7068
|
} finally {
|
|
@@ -6446,10 +7083,10 @@ async function cmdList$1(opts) {
|
|
|
6446
7083
|
console.log();
|
|
6447
7084
|
console.log(header(` ${slice.length} of ${triples.length} currently-valid triple(s)`));
|
|
6448
7085
|
console.log();
|
|
6449
|
-
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}`);
|
|
6450
7087
|
if (triples.length > slice.length) {
|
|
6451
7088
|
console.log();
|
|
6452
|
-
console.log(dim(` (${triples.length - slice.length} more — increase --limit)`));
|
|
7089
|
+
console.log(dim$1(` (${triples.length - slice.length} more — increase --limit)`));
|
|
6453
7090
|
}
|
|
6454
7091
|
console.log();
|
|
6455
7092
|
} finally {
|
|
@@ -6477,12 +7114,12 @@ async function cmdStats() {
|
|
|
6477
7114
|
console.log();
|
|
6478
7115
|
console.log(header(" PAI KG Stats"));
|
|
6479
7116
|
console.log();
|
|
6480
|
-
console.log(` ${bold("Total triples:")} ${row.total ?? "0"}`);
|
|
6481
|
-
console.log(` ${bold("Currently valid:")} ${row.valid ?? "0"}`);
|
|
6482
|
-
console.log(` ${bold("Invalidated:")} ${row.invalidated ?? "0"}`);
|
|
6483
|
-
console.log(` ${bold("Distinct subjects:")} ${row.subjects ?? "0"}`);
|
|
6484
|
-
console.log(` ${bold("Distinct predicates:")} ${row.predicates ?? "0"}`);
|
|
6485
|
-
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"}`);
|
|
6486
7123
|
console.log();
|
|
6487
7124
|
} finally {
|
|
6488
7125
|
await close();
|
|
@@ -6642,25 +7279,25 @@ function printResult(result, json) {
|
|
|
6642
7279
|
}
|
|
6643
7280
|
if (result.rows.length === 0) {
|
|
6644
7281
|
console.log();
|
|
6645
|
-
console.log(dim(" (no rows)"));
|
|
7282
|
+
console.log(dim$1(" (no rows)"));
|
|
6646
7283
|
console.log();
|
|
6647
7284
|
return;
|
|
6648
7285
|
}
|
|
6649
7286
|
const stringRows = result.rows.map((row) => row.map((cell) => {
|
|
6650
|
-
if (cell === null || cell === void 0) return dim("NULL");
|
|
7287
|
+
if (cell === null || cell === void 0) return dim$1("NULL");
|
|
6651
7288
|
if (typeof cell === "object") return JSON.stringify(cell);
|
|
6652
7289
|
return String(cell);
|
|
6653
7290
|
}));
|
|
6654
7291
|
console.log();
|
|
6655
7292
|
console.log(renderTable(result.columns, stringRows));
|
|
6656
7293
|
console.log();
|
|
6657
|
-
console.log(dim(` ${result.rows.length} row(s)`));
|
|
7294
|
+
console.log(dim$1(` ${result.rows.length} row(s)`));
|
|
6658
7295
|
console.log();
|
|
6659
7296
|
}
|
|
6660
7297
|
async function cmdQuery(sql, opts) {
|
|
6661
7298
|
const target = opts.db ?? "sqlite";
|
|
6662
7299
|
console.log();
|
|
6663
|
-
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 ? "…" : ""}`));
|
|
6664
7301
|
try {
|
|
6665
7302
|
let result;
|
|
6666
7303
|
if (target === "postgres") result = await postgresQuery(sql);
|
|
@@ -6685,10 +7322,10 @@ async function cmdTables(opts) {
|
|
|
6685
7322
|
console.log(JSON.stringify(tables, null, 2));
|
|
6686
7323
|
return;
|
|
6687
7324
|
}
|
|
6688
|
-
if (tables.length === 0) console.log(dim(" (no tables found)"));
|
|
6689
|
-
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)}`);
|
|
6690
7327
|
console.log();
|
|
6691
|
-
console.log(dim(` ${tables.length} table(s)`));
|
|
7328
|
+
console.log(dim$1(` ${tables.length} table(s)`));
|
|
6692
7329
|
console.log();
|
|
6693
7330
|
} catch (e) {
|
|
6694
7331
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -6780,11 +7417,11 @@ function renderMarkdown(md) {
|
|
|
6780
7417
|
continue;
|
|
6781
7418
|
}
|
|
6782
7419
|
if (/^>\s?/.test(line)) {
|
|
6783
|
-
out.push(dim(line.replace(/^>\s?/, "")));
|
|
7420
|
+
out.push(dim$1(line.replace(/^>\s?/, "")));
|
|
6784
7421
|
continue;
|
|
6785
7422
|
}
|
|
6786
7423
|
if (/^\|?\s*[-:]+\s*(\|\s*[-:]+\s*)+\|?$/.test(line)) {
|
|
6787
|
-
out.push(dim(line));
|
|
7424
|
+
out.push(dim$1(line));
|
|
6788
7425
|
continue;
|
|
6789
7426
|
}
|
|
6790
7427
|
out.push(inlineBold(line));
|
|
@@ -6800,7 +7437,7 @@ function registerHelpCommand(program) {
|
|
|
6800
7437
|
const docsDir = findDocsDir();
|
|
6801
7438
|
if (!docsDir) {
|
|
6802
7439
|
console.error(err(" Command docs not found."));
|
|
6803
|
-
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`."));
|
|
6804
7441
|
process.exitCode = 1;
|
|
6805
7442
|
return;
|
|
6806
7443
|
}
|
|
@@ -6812,7 +7449,7 @@ function registerHelpCommand(program) {
|
|
|
6812
7449
|
} else {
|
|
6813
7450
|
console.log(chalk.bold("\nPAI command areas\n"));
|
|
6814
7451
|
for (const a of listAreas(docsDir)) console.log(` ${a}`);
|
|
6815
|
-
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"));
|
|
6816
7453
|
}
|
|
6817
7454
|
return;
|
|
6818
7455
|
}
|
|
@@ -6825,9 +7462,9 @@ function registerHelpCommand(program) {
|
|
|
6825
7462
|
console.error(err(` No man page for "${area}".`));
|
|
6826
7463
|
const areas = listAreas(docsDir);
|
|
6827
7464
|
const near = areas.filter((a) => a.includes(area) || area.includes(a));
|
|
6828
|
-
if (near.length > 0) console.error(dim(` Did you mean: ${near.join(", ")}?`));
|
|
6829
|
-
else console.error(dim(` Available: ${areas.join(", ")}`));
|
|
6830
|
-
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`));
|
|
6831
7468
|
process.exitCode = 1;
|
|
6832
7469
|
});
|
|
6833
7470
|
}
|
|
@@ -7269,7 +7906,7 @@ async function cmdPauseAll(opts) {
|
|
|
7269
7906
|
liveSessions = await fetchLiveSessions();
|
|
7270
7907
|
} catch (e) {
|
|
7271
7908
|
console.error(err("AIBroker is not running. Cannot list live sessions."));
|
|
7272
|
-
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."));
|
|
7273
7910
|
process.exitCode = 1;
|
|
7274
7911
|
return;
|
|
7275
7912
|
}
|
|
@@ -7284,13 +7921,13 @@ async function cmdPauseAll(opts) {
|
|
|
7284
7921
|
console.log("\n" + header("Dry Run — Would Pause These Sessions") + "\n");
|
|
7285
7922
|
for (const s of claudeSessions) {
|
|
7286
7923
|
console.log(" " + sessionLabel(s));
|
|
7287
|
-
console.log(dim(" → send: \"pause session\""));
|
|
7288
|
-
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"`));
|
|
7289
7926
|
}
|
|
7290
7927
|
console.log();
|
|
7291
7928
|
return;
|
|
7292
7929
|
}
|
|
7293
|
-
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");
|
|
7294
7931
|
const results = [];
|
|
7295
7932
|
for (const s of claudeSessions) {
|
|
7296
7933
|
process.stdout.write(" " + sessionLabel(s) + " … ");
|
|
@@ -7313,7 +7950,7 @@ async function cmdPauseAll(opts) {
|
|
|
7313
7950
|
if (opts.exit) {
|
|
7314
7951
|
const pausedSessions = results.filter((r) => r.pauseOk).map((r) => r.session);
|
|
7315
7952
|
if (pausedSessions.length > 0) {
|
|
7316
|
-
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…`));
|
|
7317
7954
|
await sleep(waitMs);
|
|
7318
7955
|
console.log();
|
|
7319
7956
|
for (const s of pausedSessions) {
|
|
@@ -7340,7 +7977,7 @@ async function cmdPauseAll(opts) {
|
|
|
7340
7977
|
}
|
|
7341
7978
|
if (opts.exit) {
|
|
7342
7979
|
const exitOk = results.filter((r) => r.exitOk).length;
|
|
7343
|
-
console.log(dim(` /exit sent to ${exitOk} session(s).`));
|
|
7980
|
+
console.log(dim$1(` /exit sent to ${exitOk} session(s).`));
|
|
7344
7981
|
}
|
|
7345
7982
|
console.log();
|
|
7346
7983
|
}
|
|
@@ -7361,9 +7998,9 @@ async function cmdPauseAll(opts) {
|
|
|
7361
7998
|
*/
|
|
7362
7999
|
async function cmdClearNames(opts) {
|
|
7363
8000
|
if (opts.dryRun) {
|
|
7364
|
-
console.log(dim("Dry run — would:"));
|
|
7365
|
-
console.log(dim(" 1. Call AIBroker clear_session_names (wipe session-names.json)"));
|
|
7366
|
-
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)"));
|
|
7367
8004
|
return;
|
|
7368
8005
|
}
|
|
7369
8006
|
let anyFailed = false;
|
|
@@ -7463,9 +8100,9 @@ function cmdList(db, projectSlug, opts) {
|
|
|
7463
8100
|
];
|
|
7464
8101
|
const tableRows = rows.map((r) => {
|
|
7465
8102
|
const title = r.title.length > 45 ? r.title.slice(0, 42) + "..." : r.title;
|
|
7466
|
-
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("—");
|
|
7467
8104
|
if (showProject) return [
|
|
7468
|
-
dim(`#${r.number}`),
|
|
8105
|
+
dim$1(`#${r.number}`),
|
|
7469
8106
|
r.project_slug,
|
|
7470
8107
|
r.date,
|
|
7471
8108
|
title,
|
|
@@ -7473,7 +8110,7 @@ function cmdList(db, projectSlug, opts) {
|
|
|
7473
8110
|
tokens
|
|
7474
8111
|
];
|
|
7475
8112
|
return [
|
|
7476
|
-
dim(`#${r.number}`),
|
|
8113
|
+
dim$1(`#${r.number}`),
|
|
7477
8114
|
r.date,
|
|
7478
8115
|
title,
|
|
7479
8116
|
statusColor(r.status),
|
|
@@ -7483,12 +8120,12 @@ function cmdList(db, projectSlug, opts) {
|
|
|
7483
8120
|
console.log();
|
|
7484
8121
|
if (projectSlug) {
|
|
7485
8122
|
const project = getProject(db, projectSlug);
|
|
7486
|
-
console.log(` ${bold(project.display_name)} sessions:`);
|
|
8123
|
+
console.log(` ${bold$1(project.display_name)} sessions:`);
|
|
7487
8124
|
console.log();
|
|
7488
8125
|
}
|
|
7489
8126
|
console.log(renderTable(headers, tableRows));
|
|
7490
8127
|
console.log();
|
|
7491
|
-
console.log(dim(` ${rows.length} session(s) shown (limit: ${limit})`));
|
|
8128
|
+
console.log(dim$1(` ${rows.length} session(s) shown (limit: ${limit})`));
|
|
7492
8129
|
}
|
|
7493
8130
|
|
|
7494
8131
|
//#endregion
|
|
@@ -7589,11 +8226,11 @@ async function cmdFind(query, opts) {
|
|
|
7589
8226
|
}
|
|
7590
8227
|
if (matches.length === 0) {
|
|
7591
8228
|
console.log(warn(`No sessions found matching "${query}" in ~/.claude/history.jsonl.`));
|
|
7592
|
-
console.log(dim(` Try a shorter or different search term.`));
|
|
8229
|
+
console.log(dim$1(` Try a shorter or different search term.`));
|
|
7593
8230
|
return;
|
|
7594
8231
|
}
|
|
7595
8232
|
console.log("\n" + header("Session Search Results") + "\n");
|
|
7596
|
-
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`));
|
|
7597
8234
|
const headers = [
|
|
7598
8235
|
"#",
|
|
7599
8236
|
"id",
|
|
@@ -7603,21 +8240,21 @@ async function cmdFind(query, opts) {
|
|
|
7603
8240
|
];
|
|
7604
8241
|
const rows = matches.map((m, idx) => {
|
|
7605
8242
|
const shortId = (m.sessionId ?? "no-session").slice(0, 8);
|
|
7606
|
-
const when = m.lastMatchTs > 0 ? fmtTs(m.lastMatchTs) : dim("—");
|
|
7607
|
-
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("—"));
|
|
7608
8245
|
const snippet = m.lastMatchDisplay.replace(/\n+/g, " ").trim().slice(0, 48);
|
|
7609
8246
|
const display = snippet.length < m.lastMatchDisplay.replace(/\n+/g, " ").trim().length ? `"${snippet}…"` : `"${snippet}"`;
|
|
7610
8247
|
return [
|
|
7611
|
-
dim(String(idx + 1)),
|
|
8248
|
+
dim$1(String(idx + 1)),
|
|
7612
8249
|
chalk.cyan(shortId),
|
|
7613
8250
|
when,
|
|
7614
|
-
dim(project),
|
|
8251
|
+
dim$1(project),
|
|
7615
8252
|
chalk.dim(display)
|
|
7616
8253
|
];
|
|
7617
8254
|
});
|
|
7618
8255
|
console.log(renderTable(headers, rows));
|
|
7619
8256
|
console.log();
|
|
7620
|
-
console.log(dim(" Resume: ") + chalk.white("pai resume <id>"));
|
|
8257
|
+
console.log(dim$1(" Resume: ") + chalk.white("pai resume <id>"));
|
|
7621
8258
|
console.log();
|
|
7622
8259
|
}
|
|
7623
8260
|
|
|
@@ -7902,9 +8539,9 @@ function buildFeedFrom(allSessions, projects, liveSessions, allProjects) {
|
|
|
7902
8539
|
const kw = dir ? noteKeywords(dir) : "";
|
|
7903
8540
|
const tag = tagFor(e.status);
|
|
7904
8541
|
const name = chalk.white(pad(e.name, 22));
|
|
7905
|
-
const where = dir ? dim(shorten(dir, 40)) : chalk.green("(running in another tab)");
|
|
7906
|
-
const age = e.status === "live" ? chalk.green("now") : e.lastActivity > 0 ? dim(fmtAge(e.lastActivity)) : dim("—");
|
|
7907
|
-
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) : "";
|
|
7908
8545
|
const display = `${tag} ${name} ${pad(age, 5)} ${where}${kwDisp}`;
|
|
7909
8546
|
const search = `${e.name} ${dir ?? ""} ${kw}`.toLowerCase();
|
|
7910
8547
|
lines.push({
|
|
@@ -7962,15 +8599,15 @@ function filterLines(lines, query) {
|
|
|
7962
8599
|
/** Cached per-directory context (recent notes) for the detail panel. */
|
|
7963
8600
|
const ctxCache = /* @__PURE__ */ new Map();
|
|
7964
8601
|
function contextFor(rec) {
|
|
7965
|
-
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")];
|
|
7966
8603
|
const cached = ctxCache.get(rec.dir);
|
|
7967
8604
|
if (cached) return cached;
|
|
7968
8605
|
const lines = [];
|
|
7969
8606
|
const notes = recentMarkdown(rec.dir, 4);
|
|
7970
8607
|
if (notes.length) {
|
|
7971
|
-
lines.push(dim("recent notes:"));
|
|
7972
|
-
for (const n of notes) lines.push(" " + dim(n.rel.slice(0, 60)) + " " + dim(fmtAge(n.mtime)));
|
|
7973
|
-
} 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)"));
|
|
7974
8611
|
ctxCache.set(rec.dir, lines);
|
|
7975
8612
|
return lines;
|
|
7976
8613
|
}
|
|
@@ -8011,43 +8648,43 @@ async function runSelector(lines, onRemove) {
|
|
|
8011
8648
|
const rows = out.rows && out.rows > 8 ? out.rows : 24;
|
|
8012
8649
|
const cols = out.columns && out.columns > 20 ? out.columns : 100;
|
|
8013
8650
|
const frame = [];
|
|
8014
|
-
const title = chalk.bold(" pai") + dim(" — find a project or session");
|
|
8651
|
+
const title = chalk.bold(" pai") + dim$1(" — find a project or session");
|
|
8015
8652
|
frame.push(title);
|
|
8016
8653
|
if (mode === "search") frame.push(" " + chalk.cyan("search > ") + query + chalk.inverse(" "));
|
|
8017
|
-
else frame.push(" " + dim(`${filtered.length}/${lines.length}`) + (query ? dim(` filter: ${query}`) : ""));
|
|
8018
|
-
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))));
|
|
8019
8656
|
const ctxHeight = 6;
|
|
8020
8657
|
const listHeight = Math.max(3, rows - 4 - ctxHeight - 2);
|
|
8021
8658
|
let start = 0;
|
|
8022
8659
|
if (cursor >= listHeight) start = cursor - listHeight + 1;
|
|
8023
8660
|
const end = Math.min(filtered.length, start + listHeight);
|
|
8024
|
-
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"));
|
|
8025
8662
|
for (let i = start; i < end; i++) {
|
|
8026
8663
|
const row = filtered[i];
|
|
8027
8664
|
if (i === cursor) frame.push(chalk.inverse(" " + stripAnsi(row.display).slice(0, cols - 2).padEnd(cols - 2)));
|
|
8028
8665
|
else frame.push(" " + row.display);
|
|
8029
8666
|
}
|
|
8030
|
-
frame.push(dim(" " + "─".repeat(Math.min(cols - 4, 80))));
|
|
8667
|
+
frame.push(dim$1(" " + "─".repeat(Math.min(cols - 4, 80))));
|
|
8031
8668
|
const cur = filtered[cursor];
|
|
8032
8669
|
if (cur) {
|
|
8033
|
-
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)) : ""));
|
|
8034
8671
|
for (const l of contextFor(cur.record).slice(0, ctxHeight - 2)) frame.push(l);
|
|
8035
8672
|
}
|
|
8036
|
-
frame.push(dim(" " + "─".repeat(Math.min(cols - 4, 80))));
|
|
8037
|
-
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"));
|
|
8038
|
-
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"));
|
|
8039
8676
|
else {
|
|
8040
8677
|
const parts = [];
|
|
8041
|
-
if (cur?.record.live) parts.push(chalk.cyan("g") + dim(" go to tab"));
|
|
8042
|
-
if (cur?.record.dir) parts.push(chalk.cyan("n") + dim(" new"));
|
|
8043
|
-
if (cur?.record.dir) parts.push(chalk.cyan("c") + dim(" cd"));
|
|
8044
|
-
if (cur?.record.dir) parts.push(chalk.cyan("f") + dim(" finder"));
|
|
8045
|
-
if (cur?.record.slug) parts.push(chalk.cyan("d") + dim(" remove"));
|
|
8046
|
-
parts.push(chalk.cyan("s") + dim(" search"));
|
|
8047
|
-
parts.push(chalk.cyan("↑↓") + dim(" move"));
|
|
8048
|
-
parts.push(chalk.cyan("q") + dim(" quit"));
|
|
8049
|
-
frame.push(" " + parts.join(dim(" · ")));
|
|
8050
|
-
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));
|
|
8051
8688
|
}
|
|
8052
8689
|
const painted = frame.map((l) => truncVisible(l, cols)).join("\x1B[K\n");
|
|
8053
8690
|
out.write("\x1B[H" + painted + "\x1B[K\x1B[J");
|
|
@@ -8220,7 +8857,7 @@ async function cmdPick(db, opts = {}) {
|
|
|
8220
8857
|
const allProjects = getProjects(db, true);
|
|
8221
8858
|
const lines = buildFeedFrom(allSessions, projects, live, allProjects);
|
|
8222
8859
|
if (lines.length === 0) {
|
|
8223
|
-
process.stderr.write(dim(" No projects or sessions found.\n"));
|
|
8860
|
+
process.stderr.write(dim$1(" No projects or sessions found.\n"));
|
|
8224
8861
|
return;
|
|
8225
8862
|
}
|
|
8226
8863
|
const onRemove = (rec) => {
|
|
@@ -8243,7 +8880,7 @@ async function cmdPick(db, opts = {}) {
|
|
|
8243
8880
|
return;
|
|
8244
8881
|
}
|
|
8245
8882
|
if (!record.dir) {
|
|
8246
|
-
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`));
|
|
8247
8884
|
return;
|
|
8248
8885
|
}
|
|
8249
8886
|
if (action === "cd") {
|
|
@@ -8262,5 +8899,5 @@ async function cmdPick(db, opts = {}) {
|
|
|
8262
8899
|
}
|
|
8263
8900
|
|
|
8264
8901
|
//#endregion
|
|
8265
|
-
export {
|
|
8266
|
-
//# sourceMappingURL=pick-
|
|
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
|