@tekmidian/pai 0.14.1 → 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.
- package/dist/cli/index.mjs +1 -1
- package/dist/cli/program.mjs +1 -1
- package/dist/config-C8m-tPhP.mjs.map +1 -1
- package/dist/daemon-mcp/index.mjs +22 -0
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/{pick-BPlB38eB.mjs → pick-r95zvydr.mjs} +85 -15
- package/dist/pick-r95zvydr.mjs.map +1 -0
- package/dist/skills/Tasks/SKILL.md +22 -0
- package/docs/commands/task.md +1 -0
- package/package.json +1 -1
- package/dist/pick-BPlB38eB.mjs.map +0 -1
|
@@ -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:
|
|
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();
|
|
@@ -7008,8 +7049,11 @@ const HISTORY_LIMIT = 5;
|
|
|
7008
7049
|
const EMPTY = {
|
|
7009
7050
|
...EMPTY_RUN_STATE,
|
|
7010
7051
|
history: {},
|
|
7011
|
-
lastReported: {}
|
|
7052
|
+
lastReported: {},
|
|
7053
|
+
failedDispatches: {}
|
|
7012
7054
|
};
|
|
7055
|
+
/** Consecutive failed dispatches before a task is reported as needing attention. */
|
|
7056
|
+
const DISPATCH_FAILURES_BEFORE_ALARM = 3;
|
|
7013
7057
|
/**
|
|
7014
7058
|
* Run state is a rebuildable cache, so a damaged file must not block the
|
|
7015
7059
|
* scheduler forever — starting fresh is the correct recovery here, which is
|
|
@@ -7061,10 +7105,15 @@ async function tick(opts) {
|
|
|
7061
7105
|
case "running":
|
|
7062
7106
|
note = `${d.elapsedMinutes}m elapsed`;
|
|
7063
7107
|
break;
|
|
7064
|
-
case "dispatch":
|
|
7065
|
-
|
|
7066
|
-
|
|
7108
|
+
case "dispatch": {
|
|
7109
|
+
const r = await handleDispatch(task, d.overdueMinutes, opts, state, now);
|
|
7110
|
+
note = r.note;
|
|
7111
|
+
if (!opts.dryRun) {
|
|
7112
|
+
report.dispatched++;
|
|
7113
|
+
if (r.alarm) report.stuck++;
|
|
7114
|
+
}
|
|
7067
7115
|
break;
|
|
7116
|
+
}
|
|
7068
7117
|
case "complete":
|
|
7069
7118
|
note = await handleComplete(task, d.durationMinutes, opts, state);
|
|
7070
7119
|
if (!opts.dryRun) report.completed++;
|
|
@@ -7090,8 +7139,14 @@ async function tick(opts) {
|
|
|
7090
7139
|
}
|
|
7091
7140
|
async function handleDispatch(task, overdue, opts, state, now) {
|
|
7092
7141
|
const late = overdue > 5 ? ` (${overdue}m late)` : "";
|
|
7093
|
-
if (opts.dryRun) return
|
|
7094
|
-
|
|
7142
|
+
if (opts.dryRun) return {
|
|
7143
|
+
note: `would dispatch to ${task.owner.project ?? "nobody"}${late}`,
|
|
7144
|
+
alarm: false
|
|
7145
|
+
};
|
|
7146
|
+
if (!task.owner.project) return {
|
|
7147
|
+
note: "unrouted — cannot dispatch",
|
|
7148
|
+
alarm: false
|
|
7149
|
+
};
|
|
7095
7150
|
const result = await dispatchTask(task, {
|
|
7096
7151
|
transport: opts.transport,
|
|
7097
7152
|
autoDispatch: opts.autoDispatch,
|
|
@@ -7101,9 +7156,23 @@ async function handleDispatch(task, overdue, opts, state, now) {
|
|
|
7101
7156
|
await opts.provider.setLabels(task.id, [...task.labels, RUNNING_LABEL]);
|
|
7102
7157
|
state.startedAt[task.id] = now;
|
|
7103
7158
|
delete state.failedProbes[task.id];
|
|
7104
|
-
|
|
7159
|
+
delete state.failedDispatches[task.id];
|
|
7160
|
+
return {
|
|
7161
|
+
note: `${result.outcome} to ${result.session}${late}`,
|
|
7162
|
+
alarm: false
|
|
7163
|
+
};
|
|
7105
7164
|
}
|
|
7106
|
-
|
|
7165
|
+
const fails = (state.failedDispatches[task.id] ?? 0) + 1;
|
|
7166
|
+
state.failedDispatches[task.id] = fails;
|
|
7167
|
+
const detail = `${result.outcome}${result.reason ? " — " + result.reason : ""}`;
|
|
7168
|
+
if (fails >= DISPATCH_FAILURES_BEFORE_ALARM) return {
|
|
7169
|
+
note: `NOT RUNNING — ${fails} failed dispatches: ${detail}`,
|
|
7170
|
+
alarm: true
|
|
7171
|
+
};
|
|
7172
|
+
return {
|
|
7173
|
+
note: `not dispatched (${fails}/${DISPATCH_FAILURES_BEFORE_ALARM}): ${detail}`,
|
|
7174
|
+
alarm: false
|
|
7175
|
+
};
|
|
7107
7176
|
}
|
|
7108
7177
|
async function handleComplete(task, durationMinutes, opts, state) {
|
|
7109
7178
|
if (opts.dryRun) return `would clear ${RUNNING_LABEL}, ${durationMinutes ?? "?"}m`;
|
|
@@ -7359,7 +7428,7 @@ function registerTaskCommands(taskCmd) {
|
|
|
7359
7428
|
limit: opts.limit
|
|
7360
7429
|
}));
|
|
7361
7430
|
});
|
|
7362
|
-
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) => {
|
|
7363
7432
|
const provider = buildProvider();
|
|
7364
7433
|
if (!provider) return reportUnconfigured();
|
|
7365
7434
|
if (!opts.body) {
|
|
@@ -7372,7 +7441,8 @@ function registerTaskCommands(taskCmd) {
|
|
|
7372
7441
|
owner: opts.owner ?? null,
|
|
7373
7442
|
due: opts.due,
|
|
7374
7443
|
priority: opts.priority,
|
|
7375
|
-
sourceUrl: opts.url
|
|
7444
|
+
sourceUrl: opts.url,
|
|
7445
|
+
into: opts.into
|
|
7376
7446
|
});
|
|
7377
7447
|
console.log(chalk.green(` Filed: ${created.title}`));
|
|
7378
7448
|
console.log(` ${renderOwner(created)} ${dim("· " + created.id)}`);
|
|
@@ -9276,7 +9346,7 @@ function buildFeedFrom(allSessions, projects, liveSessions, allProjects) {
|
|
|
9276
9346
|
const nameRoot = /* @__PURE__ */ new Map();
|
|
9277
9347
|
for (const p of allProjects) {
|
|
9278
9348
|
slugRoot.set(p.slug, p.root_path);
|
|
9279
|
-
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);
|
|
9280
9350
|
}
|
|
9281
9351
|
const bestResumable = /* @__PURE__ */ new Map();
|
|
9282
9352
|
for (const s of allSessions) {
|
|
@@ -9679,4 +9749,4 @@ async function cmdPick(db, opts = {}) {
|
|
|
9679
9749
|
|
|
9680
9750
|
//#endregion
|
|
9681
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 };
|
|
9682
|
-
//# sourceMappingURL=pick-
|
|
9752
|
+
//# sourceMappingURL=pick-r95zvydr.mjs.map
|