@tekmidian/pai 0.14.2 → 0.15.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.
@@ -10,7 +10,7 @@ import { t as createStorageBackend } from "./factory-Q88X1bAN.mjs";
10
10
  import { t as PaiClient } from "./ipc-client-CoyUHPod.mjs";
11
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
- 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";
13
+ import { a as renderDedupedSessions, c as revealItermSession, d as fmtAge, f as resolveSessionByNameOrId, i as normalizeName$1, 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";
15
15
  import { homedir, platform } from "node:os";
16
16
  import { basename, dirname, join, relative, resolve } from "node:path";
@@ -4219,6 +4219,10 @@ async function listProjects(token) {
4219
4219
  name: p.name
4220
4220
  }));
4221
4221
  }
4222
+ /** Strip emoji and case so "Whazaa 🐝" and "whazaa" compare equal. */
4223
+ function normalizeName(raw) {
4224
+ return raw.replace(/[\u{1F000}-\u{1FAFF}\u{2600}-\u{27BF}\u{FE0F}\u{200D}]/gu, "").trim().toLowerCase();
4225
+ }
4222
4226
  /** Todoist priority is inverted: 4 is urgent, 1 is lowest. */
4223
4227
  function toPriority(wire) {
4224
4228
  switch (wire) {
@@ -4301,13 +4305,14 @@ var TodoistProvider = class {
4301
4305
  if (!token || !this.config.rootProjectId) throw new Error("Todoist provider is not configured — run `pai setup`.");
4302
4306
  const labels = [...task.labels ?? []];
4303
4307
  if (task.owner && !labels.some((l) => l.toLowerCase() === `pai:${task.owner.toLowerCase()}`)) labels.push(`pai:${task.owner}`);
4304
- const sectionId = task.owner ? void 0 : this.config.findingsSectionId;
4308
+ const sectionId = task.owner || task.into ? void 0 : this.config.findingsSectionId;
4309
+ const projectId = task.into ? (await this.findOrCreateSubProject(task.into)).id : this.config.rootProjectId;
4305
4310
  const created = await call(token, "/tasks", {
4306
4311
  method: "POST",
4307
4312
  body: {
4308
4313
  content: task.title,
4309
4314
  description: task.body,
4310
- project_id: this.config.rootProjectId,
4315
+ project_id: projectId,
4311
4316
  ...sectionId ? { section_id: sectionId } : {},
4312
4317
  labels,
4313
4318
  priority: fromPriority(task.priority),
@@ -4343,6 +4348,42 @@ var TodoistProvider = class {
4343
4348
  body: { labels }
4344
4349
  });
4345
4350
  }
4351
+ /**
4352
+ * Find the sub-project named `name` under the bus root, creating it if absent.
4353
+ *
4354
+ * Per-project sub-projects are the filing convention: a flat pile in the root
4355
+ * buries findings across projects. This exists so the convention can be
4356
+ * executed rather than re-derived — a session that has to infer structure
4357
+ * from the existing project list will sometimes infer cautiously and file
4358
+ * flat, which is the mess the convention prevents.
4359
+ *
4360
+ * Matching is case-insensitive and ignores decoration, so "Whazaa" finds an
4361
+ * existing "Whazaa 🐝" rather than creating a near-duplicate.
4362
+ */
4363
+ async findOrCreateSubProject(name) {
4364
+ const token = this.token();
4365
+ if (!token || !this.config.rootProjectId) throw new Error("Todoist provider is not configured — run `pai task config`.");
4366
+ const root = this.config.rootProjectId;
4367
+ const projects = await collect(token, "/projects");
4368
+ const want = normalizeName(name);
4369
+ for (const p of projects) {
4370
+ if (p.is_archived || p.is_deleted) continue;
4371
+ if (p.parent_id === root && normalizeName(p.name) === want) return {
4372
+ id: p.id,
4373
+ created: false
4374
+ };
4375
+ }
4376
+ return {
4377
+ id: (await call(token, "/projects", {
4378
+ method: "POST",
4379
+ body: {
4380
+ name,
4381
+ parent_id: root
4382
+ }
4383
+ })).id,
4384
+ created: true
4385
+ };
4386
+ }
4346
4387
  /** Append a comment — used to keep run history on the task itself. */
4347
4388
  async comment(id, content) {
4348
4389
  const token = this.token();
@@ -7387,7 +7428,7 @@ function registerTaskCommands(taskCmd) {
7387
7428
  limit: opts.limit
7388
7429
  }));
7389
7430
  });
7390
- 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) => {
7431
+ 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").option("--into <sub-project>", "File into this sub-project under the bus root, creating it if absent").action(async (title, opts) => {
7391
7432
  const provider = buildProvider();
7392
7433
  if (!provider) return reportUnconfigured();
7393
7434
  if (!opts.body) {
@@ -7400,7 +7441,8 @@ function registerTaskCommands(taskCmd) {
7400
7441
  owner: opts.owner ?? null,
7401
7442
  due: opts.due,
7402
7443
  priority: opts.priority,
7403
- sourceUrl: opts.url
7444
+ sourceUrl: opts.url,
7445
+ into: opts.into
7404
7446
  });
7405
7447
  console.log(chalk.green(` Filed: ${created.title}`));
7406
7448
  console.log(` ${renderOwner(created)} ${dim("· " + created.id)}`);
@@ -9304,7 +9346,7 @@ function buildFeedFrom(allSessions, projects, liveSessions, allProjects) {
9304
9346
  const nameRoot = /* @__PURE__ */ new Map();
9305
9347
  for (const p of allProjects) {
9306
9348
  slugRoot.set(p.slug, p.root_path);
9307
- nameRoot.set(normalizeName(p.display_name ?? p.slug).toLowerCase(), p.root_path);
9349
+ nameRoot.set(normalizeName$1(p.display_name ?? p.slug).toLowerCase(), p.root_path);
9308
9350
  }
9309
9351
  const bestResumable = /* @__PURE__ */ new Map();
9310
9352
  for (const s of allSessions) {
@@ -9707,4 +9749,4 @@ async function cmdPick(db, opts = {}) {
9707
9749
 
9708
9750
  //#endregion
9709
9751
  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 };
9710
- //# sourceMappingURL=pick-gne3kOJG.mjs.map
9752
+ //# sourceMappingURL=pick-r95zvydr.mjs.map