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