@tekmidian/pai 0.32.2 → 0.33.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/{checkpoint-block-DYq9RmMU.mjs → checkpoint-block-D3rm4dAJ.mjs} +4 -4
- package/dist/checkpoint-block-D3rm4dAJ.mjs.map +1 -0
- package/dist/cli/index.mjs +4 -4
- package/dist/cli/program.mjs +4 -4
- package/dist/config-CcdkNSWa.mjs.map +1 -1
- package/dist/daemon/index.mjs +4 -4
- package/dist/{daemon-BhbX7XHt.mjs → daemon-BlaOnldQ.mjs} +5 -5
- package/dist/{daemon-BhbX7XHt.mjs.map → daemon-BlaOnldQ.mjs.map} +1 -1
- package/dist/{factory-hmDHxTaJ.mjs → factory-Cd94gzNz.mjs} +2 -2
- package/dist/{factory-hmDHxTaJ.mjs.map → factory-Cd94gzNz.mjs.map} +1 -1
- package/dist/hooks/load-project-context.mjs.map +1 -1
- package/dist/{main-resolver-DjyUDJrv.mjs → main-resolver-qIjvdJsb.mjs} +27 -12
- package/dist/main-resolver-qIjvdJsb.mjs.map +1 -0
- package/dist/{pick-BBb6PiKa.mjs → pick-CX1A7ffC.mjs} +116 -17
- package/dist/pick-CX1A7ffC.mjs.map +1 -0
- package/dist/{postgres-5IpjMwo_.mjs → postgres-L6TIAIGV.mjs} +2 -2
- package/dist/postgres-L6TIAIGV.mjs.map +1 -0
- package/dist/search-C32zQ0V0.mjs.map +1 -1
- package/dist/{work-queue-worker-CGMXpO_4.mjs → work-queue-worker-DgoTjWfd.mjs} +2 -2
- package/dist/{work-queue-worker-CGMXpO_4.mjs.map → work-queue-worker-DgoTjWfd.mjs.map} +1 -1
- package/docs/commands/README.md +2 -0
- package/docs/commands/project.md +22 -0
- package/docs/commands/projects.md +22 -0
- package/package.json +1 -1
- package/src/hooks/session-stop.sh +47 -9
- package/src/hooks/ts/session-start/load-project-context.ts +2 -2
- package/dist/checkpoint-block-DYq9RmMU.mjs.map +0 -1
- package/dist/main-resolver-DjyUDJrv.mjs.map +0 -1
- package/dist/pick-BBb6PiKa.mjs.map +0 -1
- package/dist/postgres-5IpjMwo_.mjs.map +0 -1
|
@@ -7,13 +7,13 @@ import { i as indexProject, n as indexAll, t as embedChunks } from "./sync--BoxB
|
|
|
7
7
|
import { t as STOP_WORDS } from "./stop-words-BaMEGVeY.mjs";
|
|
8
8
|
import { n as populateSlugs, r as searchMemory } from "./search-C32zQ0V0.mjs";
|
|
9
9
|
import { n as formatDetection, r as formatDetectionJson, t as detectProject } from "./detect-Bf2z-oKB.mjs";
|
|
10
|
-
import { _ as transcriptFiles, a as readBodyFile, c as loadScanConfig, d as saveScanConfig, f as upsertProject, g as scanTranscriptFolders, h as findMovedProjects, i as findNotesDir$1, m as claudeProjectsDir, n as applyContinue, o as extractAndStoreTriples, p as upsertSession, r as findLatestNote, s as cmdScan, t as appendCheckpointToNote, u as resolveHome } from "./checkpoint-block-
|
|
10
|
+
import { _ as transcriptFiles, a as readBodyFile, c as loadScanConfig, d as saveScanConfig, f as upsertProject, g as scanTranscriptFolders, h as findMovedProjects, i as findNotesDir$1, m as claudeProjectsDir, n as applyContinue, o as extractAndStoreTriples, p as upsertSession, r as findLatestNote, s as cmdScan, t as appendCheckpointToNote, u as resolveHome } from "./checkpoint-block-D3rm4dAJ.mjs";
|
|
11
11
|
import { a as schedulerLogPath, i as paiSocketPath, n as daemonLogPath, r as daemonPidPath } from "./runtime-paths-B0P1TvUr.mjs";
|
|
12
12
|
import { t as PaiClient } from "./ipc-client-aVKVERjJ.mjs";
|
|
13
13
|
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-CcdkNSWa.mjs";
|
|
14
|
-
import { i as humanDuration, t as createStorageBackend } from "./factory-
|
|
14
|
+
import { i as humanDuration, t as createStorageBackend } from "./factory-Cd94gzNz.mjs";
|
|
15
15
|
import { s as kgQuery } from "./kg-entity-r8duqhi9.mjs";
|
|
16
|
-
import { _ as scanSessions, a as renderDedupedSessions, c as probeResume, d as callAiBroker, f as fetchLiveSessions, g as resolveSessionByNameOrId, h as fmtAge, i as normalizeName$2, l as restoreTopLevel, m as sendToSession, o as hasConversation, p as revealItermSession, r as buildDeduped, s as launchInDir, u as printExitDir } from "./main-resolver-
|
|
16
|
+
import { _ as scanSessions, a as renderDedupedSessions, c as probeResume, d as callAiBroker, f as fetchLiveSessions, g as resolveSessionByNameOrId, h as fmtAge, i as normalizeName$2, l as restoreTopLevel, m as sendToSession, o as hasConversation, p as revealItermSession, r as buildDeduped, s as launchInDir, u as printExitDir } from "./main-resolver-qIjvdJsb.mjs";
|
|
17
17
|
import { appendFileSync, chmodSync, copyFileSync, createReadStream, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
|
|
18
18
|
import { homedir, platform, tmpdir } from "node:os";
|
|
19
19
|
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
@@ -1277,7 +1277,7 @@ function cmdConfig(db, identifier, opts) {
|
|
|
1277
1277
|
* `my project`. That looseness is the point, and the uniqueness rule below is
|
|
1278
1278
|
* what keeps it safe.
|
|
1279
1279
|
*/
|
|
1280
|
-
function norm(s) {
|
|
1280
|
+
function norm$1(s) {
|
|
1281
1281
|
return s.normalize("NFC").toLowerCase().replace(/[^\p{Letter}\p{Number}]+/gu, "");
|
|
1282
1282
|
}
|
|
1283
1283
|
/** Hidden by Unix convention — tool state rather than a project directory. */
|
|
@@ -1322,9 +1322,9 @@ function stripOrderingPrefix(name) {
|
|
|
1322
1322
|
*/
|
|
1323
1323
|
function matchKeys(name) {
|
|
1324
1324
|
const keys = /* @__PURE__ */ new Set();
|
|
1325
|
-
const bare = norm(name);
|
|
1325
|
+
const bare = norm$1(name);
|
|
1326
1326
|
if (bare) keys.add(bare);
|
|
1327
|
-
const stripped = norm(stripOrderingPrefix(name));
|
|
1327
|
+
const stripped = norm$1(stripOrderingPrefix(name));
|
|
1328
1328
|
if (stripped) keys.add(stripped);
|
|
1329
1329
|
return keys;
|
|
1330
1330
|
}
|
|
@@ -1421,7 +1421,7 @@ function suggestByBasename(rootPath) {
|
|
|
1421
1421
|
* merged out of this registry earlier the same day.
|
|
1422
1422
|
*
|
|
1423
1423
|
* The comparison must be by realpath, not string. String equality is precisely
|
|
1424
|
-
* what missed it: the two paths share not one character after `/Users/
|
|
1424
|
+
* what missed it: the two paths share not one character after `/Users/owner/`.
|
|
1425
1425
|
*/
|
|
1426
1426
|
function suggestMovedPath(rootPath, otherRoots = []) {
|
|
1427
1427
|
const candidate = relocateRenamedAncestor(rootPath) ?? suggestByBasename(rootPath);
|
|
@@ -1730,6 +1730,102 @@ function cmdMerge(db, fromSlug, intoSlug, opts = {}) {
|
|
|
1730
1730
|
console.log();
|
|
1731
1731
|
}
|
|
1732
1732
|
|
|
1733
|
+
//#endregion
|
|
1734
|
+
//#region src/cli/commands/project/here.ts
|
|
1735
|
+
/** Normalise for name comparison: lowercase, collapse whitespace and hyphens. */
|
|
1736
|
+
function norm(s) {
|
|
1737
|
+
return s.toLowerCase().replace(/[-_]+/g, " ").replace(/\s+/g, " ").trim();
|
|
1738
|
+
}
|
|
1739
|
+
/**
|
|
1740
|
+
* Find the project a human means by `name`.
|
|
1741
|
+
*
|
|
1742
|
+
* Exact display-name or slug first. Only if that finds nothing do we fall back
|
|
1743
|
+
* to requiring every query word to appear, which is deliberately narrower than
|
|
1744
|
+
* a substring match: it reaches "Jobs Search Beta" from "jobs beta" without
|
|
1745
|
+
* also reaching a sibling project that merely shares the first word.
|
|
1746
|
+
*/
|
|
1747
|
+
function findProjectsByName(db, name) {
|
|
1748
|
+
const rows = db.prepare("SELECT id, slug, display_name, root_path, status FROM projects").all();
|
|
1749
|
+
const q = norm(name);
|
|
1750
|
+
const exact = rows.filter((r) => norm(r.display_name) === q || norm(r.slug) === q);
|
|
1751
|
+
if (exact.length > 0) return exact;
|
|
1752
|
+
const words = q.split(" ").filter(Boolean);
|
|
1753
|
+
if (words.length === 0) return [];
|
|
1754
|
+
return rows.filter((r) => {
|
|
1755
|
+
const hay = `${norm(r.display_name)} ${norm(r.slug)}`;
|
|
1756
|
+
return words.every((w) => hay.includes(w));
|
|
1757
|
+
});
|
|
1758
|
+
}
|
|
1759
|
+
/** Slug from a display name: lowercase, non-alphanumerics to hyphens. */
|
|
1760
|
+
function slugFromName(name) {
|
|
1761
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
|
|
1762
|
+
}
|
|
1763
|
+
function cmdHere(db, name, opts = {}) {
|
|
1764
|
+
let target;
|
|
1765
|
+
try {
|
|
1766
|
+
target = realpathSync(opts.cwd ?? process.cwd());
|
|
1767
|
+
} catch {
|
|
1768
|
+
console.error(err(`Cannot resolve the current directory.`));
|
|
1769
|
+
process.exit(1);
|
|
1770
|
+
return;
|
|
1771
|
+
}
|
|
1772
|
+
const encoded = encodeDir(target);
|
|
1773
|
+
const matches = findProjectsByName(db, name);
|
|
1774
|
+
if (matches.length > 1) {
|
|
1775
|
+
console.error(err(`"${name}" matches ${matches.length} projects — name one exactly:\n`) + matches.map((m) => dim$1(` ${m.slug} (${m.display_name})\n`)).join(""));
|
|
1776
|
+
process.exit(1);
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
const owner = db.prepare("SELECT id, slug, display_name FROM projects WHERE encoded_dir = ?").get(encoded);
|
|
1780
|
+
if (matches.length === 1) {
|
|
1781
|
+
const p = matches[0];
|
|
1782
|
+
if (owner && owner.id !== p.id) {
|
|
1783
|
+
console.error(err(`This directory is already registered to ${bold$1(owner.slug)}.\n`) + dim$1(` ${target}\n`) + dim$1(` Merge them: pai project merge ${owner.slug} ${p.slug} --execute`));
|
|
1784
|
+
process.exit(1);
|
|
1785
|
+
return;
|
|
1786
|
+
}
|
|
1787
|
+
let sameAlready = false;
|
|
1788
|
+
try {
|
|
1789
|
+
sameAlready = realpathSync(p.root_path) === target;
|
|
1790
|
+
} catch {
|
|
1791
|
+
sameAlready = false;
|
|
1792
|
+
}
|
|
1793
|
+
if (sameAlready) {
|
|
1794
|
+
console.log(ok$1(`Already set: ${bold$1(p.display_name)}`));
|
|
1795
|
+
console.log(dim$1(` ${target}`));
|
|
1796
|
+
return;
|
|
1797
|
+
}
|
|
1798
|
+
if (opts.dryRun) {
|
|
1799
|
+
console.log(dim$1(`Would repoint ${p.slug}:`));
|
|
1800
|
+
console.log(dim$1(` from ${p.root_path}`));
|
|
1801
|
+
console.log(dim$1(` to ${target}`));
|
|
1802
|
+
return;
|
|
1803
|
+
}
|
|
1804
|
+
db.prepare("UPDATE projects SET root_path = ?, encoded_dir = ?, updated_at = ? WHERE id = ?").run(target, encoded, now(), p.id);
|
|
1805
|
+
console.log(ok$1(`${bold$1(p.display_name)} is now here`));
|
|
1806
|
+
console.log(dim$1(` was: ${p.root_path}`));
|
|
1807
|
+
console.log(dim$1(` now: ${target}`));
|
|
1808
|
+
return;
|
|
1809
|
+
}
|
|
1810
|
+
if (owner) {
|
|
1811
|
+
console.error(err(`This directory is already registered as ${bold$1(owner.slug)} (${owner.display_name}).\n`) + dim$1(` Rename it instead: pai project edit ${owner.slug} --display-name "${name}"`));
|
|
1812
|
+
process.exit(1);
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
const slug = slugFromName(name);
|
|
1816
|
+
const finalSlug = db.prepare("SELECT 1 FROM projects WHERE slug = ?").get(slug) ? `${slug}-${encoded.slice(-6)}` : slug;
|
|
1817
|
+
if (opts.dryRun) {
|
|
1818
|
+
console.log(dim$1(`Would create ${finalSlug} (${name}) at ${target}`));
|
|
1819
|
+
return;
|
|
1820
|
+
}
|
|
1821
|
+
const ts = now();
|
|
1822
|
+
db.prepare(`INSERT INTO projects (slug, display_name, root_path, encoded_dir, type, status, created_at, updated_at)
|
|
1823
|
+
VALUES (?, ?, ?, ?, 'local', 'active', ?, ?)`).run(finalSlug, name, target, encoded, ts, ts);
|
|
1824
|
+
console.log(ok$1(`Created ${bold$1(name)}`));
|
|
1825
|
+
console.log(dim$1(` slug: ${finalSlug}`));
|
|
1826
|
+
console.log(dim$1(` path: ${target}`));
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1733
1829
|
//#endregion
|
|
1734
1830
|
//#region src/cli/commands/project/unregister.ts
|
|
1735
1831
|
function cmdUnregister(db, slug, opts = {}) {
|
|
@@ -1777,7 +1873,7 @@ function cmdUnregister(db, slug, opts = {}) {
|
|
|
1777
1873
|
//#region src/cli/commands/project/projects-index.ts
|
|
1778
1874
|
function registerProjectsCommands(projectsCmd, getDb) {
|
|
1779
1875
|
projectsCmd.command("list", { isDefault: true }).description("Show the unified deduped listing (same as `pai`).\nUse --all to include cold / zero-session / archived projects.").option("--all", "Show all entries including cold / zero-session / archived projects").option("-n, --n <count>", "Max rows to show", "20").action(async (opts) => {
|
|
1780
|
-
const { cmdMain } = await import("./main-resolver-
|
|
1876
|
+
const { cmdMain } = await import("./main-resolver-qIjvdJsb.mjs").then((n) => n.n);
|
|
1781
1877
|
await cmdMain(getDb(), void 0, void 0, opts);
|
|
1782
1878
|
});
|
|
1783
1879
|
projectsCmd.command("list-raw").description("Raw registry listing with slug, path, status, last_active columns").option("--all", "Include archived projects (default: active only)").option("--status <status>", "Filter by status: active | archived").option("--tag <tag>", "Filter by tag").option("--type <type>", "Filter by type").action((opts) => {
|
|
@@ -1840,6 +1936,9 @@ function registerProjectsCommands(projectsCmd, getDb) {
|
|
|
1840
1936
|
projectsCmd.command("rebind <slug> <new-path>").description("Manually update the root_path for a project (for when auto-detect found multiple matches).\nValidates the new path exists and is a directory, then updates the registry.").action((slug, newPath) => {
|
|
1841
1937
|
cmdRebind(getDb(), slug, newPath);
|
|
1842
1938
|
});
|
|
1939
|
+
projectsCmd.command("here <name>").description("Declare that the CURRENT directory is this project — no slug, no path.\nRepoints an existing project of that name, or creates it. Stores the\ncanonical (realpath) location, so a symlinked parent cannot register the\nsame directory twice.").option("--dry-run", "Show what would change without writing").action((name, opts) => {
|
|
1940
|
+
cmdHere(getDb(), name, { dryRun: opts.dryRun });
|
|
1941
|
+
});
|
|
1843
1942
|
projectsCmd.command("tag <slug> <tags...>").description("Add one or more tags to a project").action((slug, tags) => {
|
|
1844
1943
|
cmdTag$1(getDb(), slug, tags);
|
|
1845
1944
|
});
|
|
@@ -2848,7 +2947,7 @@ function registerMemoryCommands(memoryCmd, getDb) {
|
|
|
2848
2947
|
* self-addressed, and no pattern is ever inferred.
|
|
2849
2948
|
*
|
|
2850
2949
|
* Aliases are NOT matched by pattern, deliberately. `matthias.nott+x@gmail.com`
|
|
2851
|
-
* and `
|
|
2950
|
+
* and `owner.name@example.com` are the same mailbox, and Gmail also ignores
|
|
2852
2951
|
* dots — but the general forms of those rules ("same local part before a plus",
|
|
2853
2952
|
* "same domain") also match addresses belonging to other people. Gmail's own
|
|
2854
2953
|
* normalisation is Gmail's; other providers treat `+` and `.` as ordinary
|
|
@@ -3484,7 +3583,7 @@ function cmdLogs(opts) {
|
|
|
3484
3583
|
}
|
|
3485
3584
|
function registerDaemonCommands(daemonCmd) {
|
|
3486
3585
|
daemonCmd.command("serve").description("Start the PAI daemon in the foreground").action(async () => {
|
|
3487
|
-
const { serve } = await import("./daemon-
|
|
3586
|
+
const { serve } = await import("./daemon-BlaOnldQ.mjs").then((n) => n.t);
|
|
3488
3587
|
const { loadConfig: lc, ensureConfigDir } = await import("./config-CcdkNSWa.mjs").then((n) => n.r);
|
|
3489
3588
|
ensureConfigDir();
|
|
3490
3589
|
await serve(lc());
|
|
@@ -5437,8 +5536,8 @@ const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
|
5437
5536
|
*
|
|
5438
5537
|
* Ownership belongs to the sub-projects directly under the root — they are the
|
|
5439
5538
|
* ones mirroring PAI projects. Anything deeper is a FOLDER: a way to group work
|
|
5440
|
-
* inside one owner's inbox, not a new owner. So `Claude / Jobs
|
|
5441
|
-
* Executive Search` is eighteen tasks belonging to Jobs
|
|
5539
|
+
* inside one owner's inbox, not a new owner. So `Claude / Jobs Alpha /
|
|
5540
|
+
* Executive Search` is eighteen tasks belonging to Jobs Alpha, filed under a
|
|
5442
5541
|
* heading.
|
|
5443
5542
|
*
|
|
5444
5543
|
* This used to take the root and its direct children only. A grandchild was not
|
|
@@ -9519,7 +9618,7 @@ function reconcile(sessions, projects) {
|
|
|
9519
9618
|
*
|
|
9520
9619
|
* Ownership is decided by matching the container name against a curated alias.
|
|
9521
9620
|
* A session's display name is almost never its PAI slug — "SL" is
|
|
9522
|
-
* `seriousletter`, "Home" is `
|
|
9621
|
+
* `seriousletter`, "Home" is `owner` — so a freshly created sub-project
|
|
9523
9622
|
* routes nowhere until someone connects the two. That gap bit twice on
|
|
9524
9623
|
* 2026-08-02: the project existed, looked addressable in every listing, and
|
|
9525
9624
|
* tasks filed into it came back unrouted.
|
|
@@ -11248,7 +11347,7 @@ function cmdActive(db, opts) {
|
|
|
11248
11347
|
async function cmdAutoRoute(opts) {
|
|
11249
11348
|
const { autoRoute, formatAutoRoute, formatAutoRouteJson } = await import("./auto-route-C8xAfsds.mjs");
|
|
11250
11349
|
const { openRegistry } = await import("./db-BtuN768f.mjs").then((n) => n.t);
|
|
11251
|
-
const { createStorageBackend } = await import("./factory-
|
|
11350
|
+
const { createStorageBackend } = await import("./factory-Cd94gzNz.mjs").then((n) => n.n);
|
|
11252
11351
|
const { loadConfig } = await import("./config-CcdkNSWa.mjs").then((n) => n.r);
|
|
11253
11352
|
const config = loadConfig();
|
|
11254
11353
|
const registryDb = openRegistry();
|
|
@@ -12757,7 +12856,7 @@ async function countVectorDbPaths(oldPaths) {
|
|
|
12757
12856
|
if (oldPaths.length === 0) return 0;
|
|
12758
12857
|
try {
|
|
12759
12858
|
const { loadConfig } = await import("./config-CcdkNSWa.mjs").then((n) => n.r);
|
|
12760
|
-
const { PostgresBackend } = await import("./postgres-
|
|
12859
|
+
const { PostgresBackend } = await import("./postgres-L6TIAIGV.mjs");
|
|
12761
12860
|
const config = loadConfig();
|
|
12762
12861
|
if (config.storageBackend !== "postgres") return 0;
|
|
12763
12862
|
const pgBackend = new PostgresBackend(config.postgres ?? {});
|
|
@@ -12778,7 +12877,7 @@ async function updateVectorDbPaths(moves) {
|
|
|
12778
12877
|
if (moves.length === 0) return 0;
|
|
12779
12878
|
try {
|
|
12780
12879
|
const { loadConfig } = await import("./config-CcdkNSWa.mjs").then((n) => n.r);
|
|
12781
|
-
const { PostgresBackend } = await import("./postgres-
|
|
12880
|
+
const { PostgresBackend } = await import("./postgres-L6TIAIGV.mjs");
|
|
12782
12881
|
const config = loadConfig();
|
|
12783
12882
|
if (config.storageBackend !== "postgres") return 0;
|
|
12784
12883
|
const pgBackend = new PostgresBackend(config.postgres ?? {});
|
|
@@ -14061,4 +14160,4 @@ async function cmdPick(db, opts = {}) {
|
|
|
14061
14160
|
|
|
14062
14161
|
//#endregion
|
|
14063
14162
|
export { registerProjectsCommands as A, registerRestoreCommands as C, registerIdentityCommands as D, registerMcpCommands as E, resolveIdentifier as M, registerMemoryCommands as O, registerSetupCommand as S, registerDaemonCommands as T, registerUpdateCommand as _, cmdEnd as a, registerZettelCommands as b, cmdGoto as c, registerHelpCommand as d, registerDbCommands as f, registerNotifyCommands as g, registerTaskCommands as h, cmdPauseAll as i, findMovedPath as j, registerRegistryCommands as k, cmdPause as l, registerTopicCommands as m, cmdFind as n, registerSessionCleanupCommand as o, registerKgCommands as p, cmdClearNames as r, registerSessionCommands as s, cmdPick as t, cmdList as u, registerSkillCommands as v, registerBackupCommands as w, registerObsidianCommands as x, registerObservationCommands as y };
|
|
14064
|
-
//# sourceMappingURL=pick-
|
|
14163
|
+
//# sourceMappingURL=pick-CX1A7ffC.mjs.map
|