@danypops/papyrus 0.41.0 → 0.42.1

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.
Files changed (78) hide show
  1. package/README.md +8 -11
  2. package/package.json +2 -2
  3. package/src/adapters/sqlite-artifact-scope-store.ts +18 -9
  4. package/src/adapters/sqlite-artifact-store.ts +7 -5
  5. package/src/adapters/sqlite-discussion-round-store.ts +26 -17
  6. package/src/adapters/sqlite-gate-runner.ts +1 -1
  7. package/src/adapters/sqlite-graph-projection-store.ts +14 -10
  8. package/src/adapters/sqlite-log-store.ts +36 -17
  9. package/src/adapters/sqlite-note-event-store.ts +20 -16
  10. package/src/adapters/sqlite-session-identity-store.ts +13 -7
  11. package/src/adapters/sqlite-task-event-store.ts +29 -21
  12. package/src/adapters/sqlite-task-focus-store.ts +36 -10
  13. package/src/adapters/sqlite-task-lease-store.ts +12 -6
  14. package/src/adapters/sqlite-task-scope-store.ts +25 -14
  15. package/src/artifact-relationship-view.ts +4 -4
  16. package/src/artifact-subtree.ts +4 -2
  17. package/src/authority-registry.ts +2 -1
  18. package/src/cli.ts +794 -354
  19. package/src/client.ts +6 -3
  20. package/src/constants.ts +34 -56
  21. package/src/daemon-state.ts +4 -12
  22. package/src/daemon.ts +31 -9
  23. package/src/db.ts +153 -105
  24. package/src/discussion-service.ts +109 -44
  25. package/src/domain/artifact-event.ts +18 -5
  26. package/src/domain/artifact.ts +3 -1
  27. package/src/domain/blueprint-definition.ts +268 -0
  28. package/src/domain/checklist.ts +20 -17
  29. package/src/domain/discussion.ts +37 -18
  30. package/src/domain/gate.ts +7 -7
  31. package/src/domain/log-entry.ts +1 -1
  32. package/src/domain/note-event.ts +20 -7
  33. package/src/domain/task-event.ts +17 -7
  34. package/src/domain-services.ts +362 -288
  35. package/src/graph-projection-service.ts +34 -8
  36. package/src/id-migration.ts +17 -4
  37. package/src/index.ts +16 -11
  38. package/src/log-service.ts +6 -5
  39. package/src/log.ts +19 -0
  40. package/src/modules/discuss.ts +63 -28
  41. package/src/modules/docs.ts +74 -17
  42. package/src/modules/graph-projection.ts +20 -9
  43. package/src/modules/logs.ts +34 -22
  44. package/src/modules/notes.ts +66 -28
  45. package/src/modules/playbooks.ts +93 -32
  46. package/src/modules/rules.ts +57 -15
  47. package/src/modules/session-identity.ts +6 -2
  48. package/src/modules/tasks.ts +142 -67
  49. package/src/note-service.ts +11 -7
  50. package/src/ops.ts +134 -69
  51. package/src/playbook-definition.ts +124 -39
  52. package/src/playbook-execution.ts +18 -25
  53. package/src/ports/artifact-scope-store.ts +1 -1
  54. package/src/ports/note-event-store.ts +6 -4
  55. package/src/ports/task-event-store.ts +9 -6
  56. package/src/ports/task-focus-store.ts +17 -4
  57. package/src/ports/task-lease-store.ts +9 -4
  58. package/src/ports/task-scope-store.ts +3 -1
  59. package/src/service.ts +148 -110
  60. package/src/session-identity-service.ts +10 -2
  61. package/src/task-context.ts +28 -16
  62. package/src/task-execution.ts +4 -12
  63. package/src/task-graph-view.ts +12 -12
  64. package/src/task-relationship-view.ts +1 -3
  65. package/src/task-service.ts +168 -73
  66. package/src/vehicle/artifact-trash-vehicle.ts +26 -14
  67. package/src/vehicle/artifact-vehicle-shared.ts +32 -13
  68. package/src/vehicle/docs-vehicle.ts +50 -18
  69. package/src/vehicle/notes-vehicle.ts +26 -8
  70. package/src/vehicle/papyrus-vehicle.ts +16 -8
  71. package/src/vehicle/playbooks-vehicle.ts +88 -19
  72. package/src/vehicle/rules-vehicle.ts +58 -21
  73. package/src/vehicle/tasks-vehicle.ts +366 -54
  74. package/src/version.ts +1 -1
  75. package/src/workflow-execution.ts +198 -109
  76. package/src/domain/skill-definition.ts +0 -270
  77. package/src/modules/skills.ts +0 -158
  78. package/src/vehicle/skills-vehicle.ts +0 -194
package/src/cli.ts CHANGED
@@ -6,11 +6,11 @@ import { join } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { createNodeServiceInstallDeps, generateSystemdUnit, installUserService, type ServiceSpec } from "@danypops/vehicle-server/service";
8
8
  import { connectPapyrusClient, type PapyrusClient } from "./client.ts";
9
- import { DAEMON_UNIT_NAME, TASK_EXECUTION_MAX_NODES, dbPath } from "./constants.ts";
9
+ import { DAEMON_UNIT_NAME, dbPath, TASK_EXECUTION_MAX_NODES } from "./constants.ts";
10
10
  import { serveMain } from "./daemon.ts";
11
11
  import { openDb } from "./db.ts";
12
- import { applyIdMigration, mirrorDatabase, planIdMigration, verifyIdMigration, type IdMigrationPlan } from "./id-migration.ts";
13
12
  import type { GateResult } from "./domain/gate.ts";
13
+ import { applyIdMigration, type IdMigrationPlan, mirrorDatabase, planIdMigration, verifyIdMigration } from "./id-migration.ts";
14
14
  import type { TaskExecutionPlan } from "./task-execution.ts";
15
15
  import type { TaskBlockage, TaskCompletion } from "./task-service.ts";
16
16
 
@@ -52,7 +52,7 @@ export function renderSystemdUnit(options: SystemdUnitOptions): string {
52
52
  }
53
53
 
54
54
  function unitPath(): string {
55
- const configHome = process.env["XDG_CONFIG_HOME"] ?? join(homedir(), ".config");
55
+ const configHome = process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config");
56
56
  return join(configHome, "systemd", "user", DAEMON_UNIT_NAME);
57
57
  }
58
58
 
@@ -118,18 +118,8 @@ const USAGE = `Usage:
118
118
  papyrus rules injectable [--json]
119
119
  papyrus rules assign-project <id> [project-root] [--json]
120
120
  papyrus rules update <id> [--title <title>] [--body <body>] [--labels-json <json>] [--json]
121
- papyrus skills run <id> [--arguments-json <json>] [--run-id <id>] [--json]
122
- papyrus skills create --title <title> [--body <body>] [--trigger <text>] [--steps-json <json>] [--tools-json <json>] [--definition-json <json>] [--labels-json <json>] [--extra-json <json>] [--project-root <path>] [--json]
123
- papyrus skills create-template --title <title> --target-kind <kind> [--defaults-json <json>] [--required-json <json>] [--body <body>] [--labels-json <json>] [--project-root <path>] [--json]
124
- papyrus skills list [--status <status>] [--text <query>] [--limit <count>] [--project-root <path>] [--json]
125
- papyrus skills show <id> [--json]
126
- papyrus skills invoke <id> [--json]
127
- papyrus skills enable|disable <id> [--json]
128
- papyrus skills instantiate <template-id> [--title <title>] [--body <body>] [--status <status>] [--labels-json <json>] [--extra-json <json>] [--json]
129
- papyrus skills assign-project <id> [project-root] [--json]
130
- papyrus skills update <id> [--title <title>] [--body <body>] [--labels-json <json>] [--json]
131
- papyrus playbooks create --title <title> [--body <body>] [--trigger <text>] [--steps-json <json>] [--tools-json <json>] [--labels-json <json>] [--extra-json <json>] [--arguments-json <json array>] [--project-root <path>] [--json]
132
- papyrus playbooks invoke <id> [--arguments-json <json object>] [--project-root <path>] [--json] # materializes real tasks (contains/depends_on-wired) and focuses the entry task
121
+ papyrus playbooks create --title <title> [--body <body>] [--trigger <text>] [--steps-json <json array of strings and/or {kind:'doc'|'rule'|'call'|'task',...} objects>] [--tools-json <json>] [--labels-json <json>] [--extra-json <json>] [--arguments-json <json array, each optionally {type,enum,default}>] [--project-root <path>] [--json]
122
+ papyrus playbooks invoke <id> [--arguments-json <json object>] [--run-id <id>] [--project-root <path>] [--json] # materializes real tasks (contains/depends_on-wired) and focuses the entry task
133
123
  papyrus playbooks preview <id> [--arguments-json <json object>] [--json] # renders text only, creates nothing
134
124
  papyrus playbooks list [--status <status>] [--text <query>] [--limit <count>] [--project-root <path>] [--json]
135
125
  papyrus playbooks show <id> [--json]
@@ -209,7 +199,15 @@ function usage(): never {
209
199
  type TaskCliClient = Pick<PapyrusClient, "call">;
210
200
  type MigrationResult = { from: number; to: number; applied: string[] };
211
201
  type CliArtifact = { id: string; title: string; status: string; body?: string };
212
- type CliTaskLease = { taskId: string; owner: string; token: string; claimedAt: string; leaseExpiresAt: string; heartbeatAt?: string; note?: string };
202
+ type CliTaskLease = {
203
+ taskId: string;
204
+ owner: string;
205
+ token: string;
206
+ claimedAt: string;
207
+ leaseExpiresAt: string;
208
+ heartbeatAt?: string;
209
+ note?: string;
210
+ };
213
211
  type CliCompletion = Omit<TaskCompletion, "artifact" | "blocked"> & {
214
212
  artifact: CliArtifact;
215
213
  blocked: Array<Omit<TaskBlockage, "artifact"> & { artifact: CliArtifact }>;
@@ -231,10 +229,11 @@ function parseJsonStringArrayFlag(value: string | undefined, flag: string): stri
231
229
  }
232
230
 
233
231
  /**
234
- * No shape assertion here -- unlike every other JSON flag, playbooks --arguments-json is genuinely
235
- * polymorphic (an array on create, a {name: value} map on invoke), and the two actions share one
236
- * flag-parsing pass in runPlaybooksCli. The service validates the shape for whichever operation
237
- * actually receives it.
232
+ * No shape assertion here -- playbooks --arguments-json is genuinely polymorphic (an array on
233
+ * create, a {name: value} map on invoke, sharing one flag-parsing pass in runPlaybooksCli), and
234
+ * --steps-json accepts a mix of plain prose strings and structured step objects (doc/rule/call/
235
+ * task) that a single string-array assertion would wrongly reject. The service validates the
236
+ * real shape for whichever operation actually receives it.
238
237
  */
239
238
  function parseJsonAnyFlag(value: string | undefined, flag: string): unknown {
240
239
  if (value === undefined) throw new Error(`${flag} requires a value`);
@@ -297,7 +296,11 @@ export function runIdMigrationCli(args: string[]): string {
297
296
  const mirrorPath = flag("out");
298
297
  if (!mirrorPath) throw new Error("migrate-ids mirror requires --out <path>");
299
298
  const sourceDb = openDb(source);
300
- try { mirrorDatabase(sourceDb, mirrorPath); } finally { sourceDb.close(); }
299
+ try {
300
+ mirrorDatabase(sourceDb, mirrorPath);
301
+ } finally {
302
+ sourceDb.close();
303
+ }
301
304
 
302
305
  const mirror = openDb(mirrorPath);
303
306
  let plan: IdMigrationPlan;
@@ -327,9 +330,14 @@ export function runIdMigrationCli(args: string[]): string {
327
330
  const plan = readIdMap(flag("idmap") ?? `${mirrorPath}.idmap.json`);
328
331
  const mirror = openDb(mirrorPath);
329
332
  let result: ReturnType<typeof verifyIdMigration>;
330
- try { result = verifyIdMigration(mirror, plan); } finally { mirror.close(); }
333
+ try {
334
+ result = verifyIdMigration(mirror, plan);
335
+ } finally {
336
+ mirror.close();
337
+ }
331
338
  if (json) return JSON.stringify(result);
332
- if (result.ok) return `Validation passed: ${plan.idMap.size} artifact id(s) correctly migrated.\nNext: papyrus migrate-ids promote --mirror ${mirrorPath}`;
339
+ if (result.ok)
340
+ return `Validation passed: ${plan.idMap.size} artifact id(s) correctly migrated.\nNext: papyrus migrate-ids promote --mirror ${mirrorPath}`;
333
341
  return `Validation FAILED:\n${result.problems.map((problem) => ` - ${problem}`).join("\n")}\nDo not promote this mirror.`;
334
342
  }
335
343
 
@@ -339,14 +347,22 @@ export function runIdMigrationCli(args: string[]): string {
339
347
  const target = flag("db") ?? dbPath();
340
348
  const force = rest.includes("--force");
341
349
  if (isDaemonActive() && !force) {
342
- throw new Error(`refusing to promote while ${DAEMON_UNIT_NAME} is active -- stop it first (papyrus service stop), or pass --force if you have already verified it is safe`);
350
+ throw new Error(
351
+ `refusing to promote while ${DAEMON_UNIT_NAME} is active -- stop it first (papyrus service stop), or pass --force if you have already verified it is safe`,
352
+ );
343
353
  }
344
354
  const plan = readIdMap(flag("idmap") ?? `${mirrorPath}.idmap.json`);
345
355
  const mirror = openDb(mirrorPath);
346
356
  let result: ReturnType<typeof verifyIdMigration>;
347
- try { result = verifyIdMigration(mirror, plan); } finally { mirror.close(); }
357
+ try {
358
+ result = verifyIdMigration(mirror, plan);
359
+ } finally {
360
+ mirror.close();
361
+ }
348
362
  if (!result.ok) {
349
- throw new Error(`refusing to promote: mirror failed validation (${result.problems.length} problem(s)) -- run migrate-ids validate for details`);
363
+ throw new Error(
364
+ `refusing to promote: mirror failed validation (${result.problems.length} problem(s)) -- run migrate-ids validate for details`,
365
+ );
350
366
  }
351
367
  let backupPath: string | undefined;
352
368
  if (existsSync(target)) {
@@ -376,160 +392,6 @@ export function runIdMigrationCli(args: string[]): string {
376
392
  throw new Error("migrate-ids requires one of: mirror, validate, promote");
377
393
  }
378
394
 
379
- export async function runSkillCli(args: string[], client: TaskCliClient, projectRoot: string = process.cwd()): Promise<string> {
380
- const json = args.includes("--json");
381
- const positional: string[] = [];
382
- let runId: string | undefined;
383
- let arguments_: Record<string, unknown> = {};
384
- let title: string | undefined;
385
- let body: string | undefined;
386
- let trigger: string | undefined;
387
- let steps: string[] | undefined;
388
- let tools: string[] | undefined;
389
- let definition: unknown;
390
- let labels: string[] | undefined;
391
- let extra: Record<string, unknown> | undefined;
392
- let targetKind: string | undefined;
393
- let defaults: Record<string, unknown> | undefined;
394
- let required: string[] | undefined;
395
- let status: string | undefined;
396
- let text: string | undefined;
397
- let limit: number | undefined;
398
- let skillProjectRoot: string | undefined;
399
- for (let index = 0; index < args.length; index++) {
400
- const argument = args[index]!;
401
- if (argument === "--json") continue;
402
- if (argument === "--run-id") { runId = args[++index]; if (!runId) throw new Error("--run-id requires a value"); continue; }
403
- if (argument === "--arguments-json") { arguments_ = parseJsonObjectFlag(args[++index], "--arguments-json"); continue; }
404
- if (argument === "--title") { title = args[++index]; if (title === undefined) throw new Error("--title requires a value"); continue; }
405
- if (argument === "--body") { body = args[++index]; if (body === undefined) throw new Error("--body requires a value"); continue; }
406
- if (argument === "--trigger") { trigger = args[++index]; if (trigger === undefined) throw new Error("--trigger requires a value"); continue; }
407
- if (argument === "--steps-json") { steps = parseJsonStringArrayFlag(args[++index], "--steps-json"); continue; }
408
- if (argument === "--tools-json") { tools = parseJsonStringArrayFlag(args[++index], "--tools-json"); continue; }
409
- if (argument === "--definition-json") {
410
- const value = args[++index];
411
- if (!value) throw new Error("--definition-json requires a value");
412
- definition = JSON.parse(value);
413
- continue;
414
- }
415
- if (argument === "--labels-json") { labels = parseJsonStringArrayFlag(args[++index], "--labels-json"); continue; }
416
- if (argument === "--extra-json") { extra = parseJsonObjectFlag(args[++index], "--extra-json"); continue; }
417
- if (argument === "--target-kind") { targetKind = args[++index]; if (!targetKind) throw new Error("--target-kind requires a value"); continue; }
418
- if (argument === "--defaults-json") { defaults = parseJsonObjectFlag(args[++index], "--defaults-json"); continue; }
419
- if (argument === "--required-json") { required = parseJsonStringArrayFlag(args[++index], "--required-json"); continue; }
420
- if (argument === "--status") { status = args[++index]; if (!status) throw new Error("--status requires a value"); continue; }
421
- if (argument === "--text") { text = args[++index]; if (text === undefined) throw new Error("--text requires a value"); continue; }
422
- if (argument === "--project-root") { skillProjectRoot = args[++index]; if (!skillProjectRoot) throw new Error("--project-root requires a value"); continue; }
423
- if (argument === "--limit") {
424
- const value = args[++index];
425
- if (!value || Number.isNaN(Number(value))) throw new Error("--limit requires a numeric value");
426
- limit = Number(value);
427
- continue;
428
- }
429
- if (argument.startsWith("--")) throw new Error(`unknown skills option ${argument}`);
430
- positional.push(argument);
431
- }
432
- const [action, id, second] = positional;
433
- if (action === "run") {
434
- if (positional.length !== 2) throw new Error("skills requires `run <id>`");
435
- const input: Record<string, unknown> = { id, arguments: arguments_, project_root: projectRoot };
436
- if (runId) input["run_id"] = runId;
437
- const result = await client.call<Record<string, unknown>, {
438
- runId: string;
439
- created: { tasks: string[]; rules: string[]; docs: string[] };
440
- rootTaskIds: string[];
441
- execution: TaskExecutionPlan;
442
- }>("skills.run", input);
443
- if (json) return JSON.stringify(result);
444
- return [
445
- `Created Skill run ${result.runId}: ${result.created.tasks.length} tasks, ${result.created.rules.length} rules, ${result.created.docs.length} docs`,
446
- `Ready roots: ${result.rootTaskIds.join(", ") || "none"}`,
447
- `Context docs: ${result.created.docs.join(", ") || "none"}`,
448
- `Scoped rules: ${result.created.rules.join(", ") || "none"}`,
449
- ...result.execution.nodes.map((node) => `[${node.state}] ${node.id} ${node.title}`),
450
- ].join("\n");
451
- }
452
- let result: unknown;
453
- let human: string;
454
- switch (action) {
455
- case "create": {
456
- if (id) throw new Error("skills create accepts no positional arguments");
457
- if (!title) throw new Error("skills create requires --title");
458
- const artifact = await client.call<Record<string, unknown>, CliArtifact>("skills.create", { title, body, trigger, steps, tools, definition, labels, extra, project_root: skillProjectRoot });
459
- result = artifact;
460
- human = `Created skill: ${artifactLabel(artifact)}`;
461
- break;
462
- }
463
- case "create-template": {
464
- if (id) throw new Error("skills create-template accepts no positional arguments");
465
- if (!title || !targetKind) throw new Error("skills create-template requires --title and --target-kind");
466
- const artifact = await client.call<Record<string, unknown>, CliArtifact>("skills.create_template", {
467
- title, target_kind: targetKind, defaults, required, body, labels, project_root: skillProjectRoot,
468
- });
469
- result = artifact;
470
- human = `Created template: ${artifactLabel(artifact)}`;
471
- break;
472
- }
473
- case "list": {
474
- if (id) throw new Error("skills list accepts no positional arguments");
475
- const rows = await client.call<Record<string, unknown>, CliArtifact[]>("skills.list", { status, text, limit, project_root: skillProjectRoot });
476
- result = rows;
477
- human = rows.length === 0 ? "No skills found." : rows.map((row) => artifactLabel(row)).join("\n");
478
- break;
479
- }
480
- case "assign-project": {
481
- if (!id) throw new Error("skills assign-project requires <id> [project-root]");
482
- const artifact = await client.call<Record<string, unknown>, CliArtifact>("skills.assign_project", { id, project_root: second });
483
- result = artifact;
484
- human = second ? `Assigned ${id} to ${second}` : `Unscoped ${id}`;
485
- break;
486
- }
487
- case "show": {
488
- if (!id) throw new Error("skills show requires exactly one skill id");
489
- const artifact = await client.call<Record<string, unknown>, CliArtifact>("skills.show", { id });
490
- result = artifact;
491
- human = `${artifactLabel(artifact)}\n\n${artifact.body ?? ""}`;
492
- break;
493
- }
494
- case "invoke": {
495
- if (!id) throw new Error("skills invoke requires exactly one skill id");
496
- const invocation = await client.call<Record<string, unknown>, string>("skills.invoke", { id });
497
- result = invocation;
498
- human = invocation;
499
- break;
500
- }
501
- case "enable":
502
- case "disable": {
503
- if (!id) throw new Error(`skills ${action} requires exactly one skill id`);
504
- const operation = action === "enable" ? "skills.enable" : "skills.disable";
505
- const artifact = await client.call<Record<string, unknown>, CliArtifact>(operation, { id });
506
- result = artifact;
507
- human = `${artifactLabel(artifact)}`;
508
- break;
509
- }
510
- case "instantiate": {
511
- if (!id) throw new Error("skills instantiate requires exactly one template id");
512
- const artifact = await client.call<Record<string, unknown>, CliArtifact>("skills.instantiate", {
513
- template_id: id, title, body, status, labels, extra, project_root: projectRoot,
514
- });
515
- result = artifact;
516
- human = `Created: ${artifactLabel(artifact)}`;
517
- break;
518
- }
519
- case "update": {
520
- if (!id || second) throw new Error("skills update requires exactly one skill id");
521
- if (title === undefined && body === undefined && labels === undefined) throw new Error("skills update requires --title, --body, or --labels-json");
522
- const artifact = await client.call<Record<string, unknown>, CliArtifact>("skills.update", { id, title, body, labels });
523
- result = artifact;
524
- human = `${artifactLabel(artifact)}`;
525
- break;
526
- }
527
- default:
528
- throw new Error("skills action must be run, create, create-template, list, show, invoke, enable, disable, instantiate, assign-project, or update");
529
- }
530
- return json ? JSON.stringify(result) : human;
531
- }
532
-
533
395
  export async function runGraphCli(args: string[], client: TaskCliClient): Promise<string> {
534
396
  const json = args.includes("--json");
535
397
  const positional: string[] = [];
@@ -540,7 +402,10 @@ export async function runGraphCli(args: string[], client: TaskCliClient): Promis
540
402
  const argument = args[index]!;
541
403
  if (argument === "--json") continue;
542
404
  const flags: Record<string, string> = {
543
- "--id": "id", "--actor": "actor", "--session-id": "session_id", "--since": "since",
405
+ "--id": "id",
406
+ "--actor": "actor",
407
+ "--session-id": "session_id",
408
+ "--since": "since",
544
409
  "--direction": "direction",
545
410
  };
546
411
  if (argument in flags) {
@@ -578,13 +443,20 @@ export async function runGraphCli(args: string[], client: TaskCliClient): Promis
578
443
  }
579
444
  if (action === "unlink") {
580
445
  if (positional.length !== 4) throw new Error("graph unlink requires <from> <relation> <to>");
581
- const result = await client.call<Record<string, unknown>, { removed: boolean }>("graph.unlink", { from: first, relation: second, to: third });
446
+ const result = await client.call<Record<string, unknown>, { removed: boolean }>("graph.unlink", {
447
+ from: first,
448
+ relation: second,
449
+ to: third,
450
+ });
582
451
  if (json) return JSON.stringify(result);
583
452
  return result.removed ? `Unlinked ${first} --${second}--> ${third}` : `No such relationship: ${first} --${second}--> ${third}`;
584
453
  }
585
454
  if (action === "tree") {
586
455
  if (positional.length !== 2) throw new Error("graph tree requires exactly one artifact id");
587
- const artifact = await client.call<Record<string, unknown>, CliArtifact & { edges?: Array<{ from: string; relation: string; to: string }> }>("graph.tree", { id: first, depth, max_nodes: maxNodes });
456
+ const artifact = await client.call<
457
+ Record<string, unknown>,
458
+ CliArtifact & { edges?: Array<{ from: string; relation: string; to: string }> }
459
+ >("graph.tree", { id: first, depth, max_nodes: maxNodes });
588
460
  if (json) return JSON.stringify(artifact);
589
461
  const edges = artifact.edges ?? [];
590
462
  return edges.length === 0
@@ -600,11 +472,13 @@ export async function runGraphCli(args: string[], client: TaskCliClient): Promis
600
472
  const page = await client.call<Record<string, unknown>, import("./domain/artifact-event.ts").ArtifactEventPage>("graph.history", input);
601
473
  if (json) return JSON.stringify(page);
602
474
  if (page.events.length === 0) return "No recorded events.";
603
- return page.events.map((event) => {
604
- const transition = event.fromStatus || event.toStatus ? ` ${event.fromStatus ?? "\u2205"} \u2192 ${event.toStatus ?? "\u2205"}` : "";
605
- const relation = event.relation ? ` ${event.relation} \u2192 ${event.relatedId}` : "";
606
- return `${event.occurredAt} ${event.artifactId} ${event.type}${transition}${relation} \u00b7 ${event.actor}/${event.source}${event.sessionId ? ` \u00b7 ${event.sessionId}` : ""}`;
607
- }).join("\n");
475
+ return page.events
476
+ .map((event) => {
477
+ const transition = event.fromStatus || event.toStatus ? ` ${event.fromStatus ?? "\u2205"} \u2192 ${event.toStatus ?? "\u2205"}` : "";
478
+ const relation = event.relation ? ` ${event.relation} \u2192 ${event.relatedId}` : "";
479
+ return `${event.occurredAt} ${event.artifactId} ${event.type}${transition}${relation} \u00b7 ${event.actor}/${event.source}${event.sessionId ? ` \u00b7 ${event.sessionId}` : ""}`;
480
+ })
481
+ .join("\n");
608
482
  }
609
483
 
610
484
  export async function runDocsCli(args: string[], client: TaskCliClient): Promise<string> {
@@ -623,15 +497,49 @@ export async function runDocsCli(args: string[], client: TaskCliClient): Promise
623
497
  for (let index = 0; index < args.length; index++) {
624
498
  const argument = args[index]!;
625
499
  if (argument === "--json") continue;
626
- if (argument === "--title") { title = args[++index]; if (title === undefined) throw new Error("--title requires a value"); continue; }
627
- if (argument === "--body") { body = args[++index]; if (body === undefined) throw new Error("--body requires a value"); continue; }
628
- if (argument === "--subtype") { subtype = args[++index]; if (!subtype) throw new Error("--subtype requires a value"); continue; }
629
- if (argument === "--labels-json") { labels = parseJsonStringArrayFlag(args[++index], "--labels-json"); continue; }
630
- if (argument === "--extra-json") { extra = parseJsonObjectFlag(args[++index], "--extra-json"); continue; }
631
- if (argument === "--template-id") { templateId = args[++index]; if (!templateId) throw new Error("--template-id requires a value"); continue; }
632
- if (argument === "--status") { status = args[++index]; if (!status) throw new Error("--status requires a value"); continue; }
633
- if (argument === "--text") { text = args[++index]; if (text === undefined) throw new Error("--text requires a value"); continue; }
634
- if (argument === "--project-root") { projectRoot = args[++index]; if (!projectRoot) throw new Error("--project-root requires a value"); continue; }
500
+ if (argument === "--title") {
501
+ title = args[++index];
502
+ if (title === undefined) throw new Error("--title requires a value");
503
+ continue;
504
+ }
505
+ if (argument === "--body") {
506
+ body = args[++index];
507
+ if (body === undefined) throw new Error("--body requires a value");
508
+ continue;
509
+ }
510
+ if (argument === "--subtype") {
511
+ subtype = args[++index];
512
+ if (!subtype) throw new Error("--subtype requires a value");
513
+ continue;
514
+ }
515
+ if (argument === "--labels-json") {
516
+ labels = parseJsonStringArrayFlag(args[++index], "--labels-json");
517
+ continue;
518
+ }
519
+ if (argument === "--extra-json") {
520
+ extra = parseJsonObjectFlag(args[++index], "--extra-json");
521
+ continue;
522
+ }
523
+ if (argument === "--template-id") {
524
+ templateId = args[++index];
525
+ if (!templateId) throw new Error("--template-id requires a value");
526
+ continue;
527
+ }
528
+ if (argument === "--status") {
529
+ status = args[++index];
530
+ if (!status) throw new Error("--status requires a value");
531
+ continue;
532
+ }
533
+ if (argument === "--text") {
534
+ text = args[++index];
535
+ if (text === undefined) throw new Error("--text requires a value");
536
+ continue;
537
+ }
538
+ if (argument === "--project-root") {
539
+ projectRoot = args[++index];
540
+ if (!projectRoot) throw new Error("--project-root requires a value");
541
+ continue;
542
+ }
635
543
  if (argument === "--limit") {
636
544
  const value = args[++index];
637
545
  if (!value || Number.isNaN(Number(value))) throw new Error("--limit requires a numeric value");
@@ -648,14 +556,27 @@ export async function runDocsCli(args: string[], client: TaskCliClient): Promise
648
556
  case "create": {
649
557
  if (id) throw new Error("docs create accepts no positional arguments");
650
558
  if (!title) throw new Error("docs create requires --title");
651
- const artifact = await client.call<Record<string, unknown>, CliArtifact>("docs.create", { title, body, subtype, labels, extra, template_id: templateId, project_root: projectRoot });
559
+ const artifact = await client.call<Record<string, unknown>, CliArtifact>("docs.create", {
560
+ title,
561
+ body,
562
+ subtype,
563
+ labels,
564
+ extra,
565
+ template_id: templateId,
566
+ project_root: projectRoot,
567
+ });
652
568
  result = artifact;
653
569
  human = `Created document: ${artifactLabel(artifact)}`;
654
570
  break;
655
571
  }
656
572
  case "list": {
657
573
  if (id) throw new Error("docs list accepts no positional arguments");
658
- const rows = await client.call<Record<string, unknown>, CliArtifact[]>("docs.list", { status, text, limit, project_root: projectRoot });
574
+ const rows = await client.call<Record<string, unknown>, CliArtifact[]>("docs.list", {
575
+ status,
576
+ text,
577
+ limit,
578
+ project_root: projectRoot,
579
+ });
659
580
  result = rows;
660
581
  human = rows.length === 0 ? "No documents found." : rows.map((row) => artifactLabel(row)).join("\n");
661
582
  break;
@@ -692,7 +613,8 @@ export async function runDocsCli(args: string[], client: TaskCliClient): Promise
692
613
  }
693
614
  case "update": {
694
615
  if (!id || second) throw new Error("docs update requires exactly one document id");
695
- if (title === undefined && body === undefined && labels === undefined) throw new Error("docs update requires --title, --body, or --labels-json");
616
+ if (title === undefined && body === undefined && labels === undefined)
617
+ throw new Error("docs update requires --title, --body, or --labels-json");
696
618
  const artifact = await client.call<Record<string, unknown>, CliArtifact>("docs.update", { id, title, body, labels });
697
619
  result = artifact;
698
620
  human = `${artifactLabel(artifact)}`;
@@ -721,16 +643,54 @@ export async function runRulesCli(args: string[], client: TaskCliClient, project
721
643
  for (let index = 0; index < args.length; index++) {
722
644
  const argument = args[index]!;
723
645
  if (argument === "--json") continue;
724
- if (argument === "--title") { title = args[++index]; if (title === undefined) throw new Error("--title requires a value"); continue; }
725
- if (argument === "--body") { body = args[++index]; if (body === undefined) throw new Error("--body requires a value"); continue; }
726
- if (argument === "--condition") { condition = args[++index]; if (condition === undefined) throw new Error("--condition requires a value"); continue; }
727
- if (argument === "--rule-action") { ruleAction = args[++index]; if (ruleAction === undefined) throw new Error("--rule-action requires a value"); continue; }
728
- if (argument === "--severity") { severity = args[++index]; if (!severity) throw new Error("--severity requires a value"); continue; }
729
- if (argument === "--labels-json") { labels = parseJsonStringArrayFlag(args[++index], "--labels-json"); continue; }
730
- if (argument === "--extra-json") { extra = parseJsonObjectFlag(args[++index], "--extra-json"); continue; }
731
- if (argument === "--status") { status = args[++index]; if (!status) throw new Error("--status requires a value"); continue; }
732
- if (argument === "--text") { text = args[++index]; if (text === undefined) throw new Error("--text requires a value"); continue; }
733
- if (argument === "--project-root") { ruleProjectRoot = args[++index]; if (!ruleProjectRoot) throw new Error("--project-root requires a value"); continue; }
646
+ if (argument === "--title") {
647
+ title = args[++index];
648
+ if (title === undefined) throw new Error("--title requires a value");
649
+ continue;
650
+ }
651
+ if (argument === "--body") {
652
+ body = args[++index];
653
+ if (body === undefined) throw new Error("--body requires a value");
654
+ continue;
655
+ }
656
+ if (argument === "--condition") {
657
+ condition = args[++index];
658
+ if (condition === undefined) throw new Error("--condition requires a value");
659
+ continue;
660
+ }
661
+ if (argument === "--rule-action") {
662
+ ruleAction = args[++index];
663
+ if (ruleAction === undefined) throw new Error("--rule-action requires a value");
664
+ continue;
665
+ }
666
+ if (argument === "--severity") {
667
+ severity = args[++index];
668
+ if (!severity) throw new Error("--severity requires a value");
669
+ continue;
670
+ }
671
+ if (argument === "--labels-json") {
672
+ labels = parseJsonStringArrayFlag(args[++index], "--labels-json");
673
+ continue;
674
+ }
675
+ if (argument === "--extra-json") {
676
+ extra = parseJsonObjectFlag(args[++index], "--extra-json");
677
+ continue;
678
+ }
679
+ if (argument === "--status") {
680
+ status = args[++index];
681
+ if (!status) throw new Error("--status requires a value");
682
+ continue;
683
+ }
684
+ if (argument === "--text") {
685
+ text = args[++index];
686
+ if (text === undefined) throw new Error("--text requires a value");
687
+ continue;
688
+ }
689
+ if (argument === "--project-root") {
690
+ ruleProjectRoot = args[++index];
691
+ if (!ruleProjectRoot) throw new Error("--project-root requires a value");
692
+ continue;
693
+ }
734
694
  if (argument === "--limit") {
735
695
  const value = args[++index];
736
696
  if (!value || Number.isNaN(Number(value))) throw new Error("--limit requires a numeric value");
@@ -747,14 +707,28 @@ export async function runRulesCli(args: string[], client: TaskCliClient, project
747
707
  case "create": {
748
708
  if (id) throw new Error("rules create accepts no positional arguments");
749
709
  if (!title) throw new Error("rules create requires --title");
750
- const artifact = await client.call<Record<string, unknown>, CliArtifact>("rules.create", { title, body, condition, rule_action: ruleAction, severity, labels, extra, project_root: ruleProjectRoot });
710
+ const artifact = await client.call<Record<string, unknown>, CliArtifact>("rules.create", {
711
+ title,
712
+ body,
713
+ condition,
714
+ rule_action: ruleAction,
715
+ severity,
716
+ labels,
717
+ extra,
718
+ project_root: ruleProjectRoot,
719
+ });
751
720
  result = artifact;
752
721
  human = `Created rule: ${artifactLabel(artifact)}`;
753
722
  break;
754
723
  }
755
724
  case "list": {
756
725
  if (id) throw new Error("rules list accepts no positional arguments");
757
- const rows = await client.call<Record<string, unknown>, CliArtifact[]>("rules.list", { status, text, limit, project_root: ruleProjectRoot });
726
+ const rows = await client.call<Record<string, unknown>, CliArtifact[]>("rules.list", {
727
+ status,
728
+ text,
729
+ limit,
730
+ project_root: ruleProjectRoot,
731
+ });
758
732
  result = rows;
759
733
  human = rows.length === 0 ? "No rules found." : rows.map((row) => artifactLabel(row)).join("\n");
760
734
  break;
@@ -804,7 +778,8 @@ export async function runRulesCli(args: string[], client: TaskCliClient, project
804
778
  }
805
779
  case "update": {
806
780
  if (!id || second) throw new Error("rules update requires exactly one rule id");
807
- if (title === undefined && body === undefined && labels === undefined) throw new Error("rules update requires --title, --body, or --labels-json");
781
+ if (title === undefined && body === undefined && labels === undefined)
782
+ throw new Error("rules update requires --title, --body, or --labels-json");
808
783
  const artifact = await client.call<Record<string, unknown>, CliArtifact>("rules.update", { id, title, body, labels });
809
784
  result = artifact;
810
785
  human = `${artifactLabel(artifact)}`;
@@ -822,11 +797,12 @@ export async function runPlaybooksCli(args: string[], client: TaskCliClient): Pr
822
797
  let title: string | undefined;
823
798
  let body: string | undefined;
824
799
  let trigger: string | undefined;
825
- let steps: string[] | undefined;
800
+ let steps: unknown;
826
801
  let tools: string[] | undefined;
827
802
  let labels: string[] | undefined;
828
803
  let extra: Record<string, unknown> | undefined;
829
804
  let playbookArguments: unknown;
805
+ let runId: string | undefined;
830
806
  let status: string | undefined;
831
807
  let text: string | undefined;
832
808
  let limit: number | undefined;
@@ -834,17 +810,61 @@ export async function runPlaybooksCli(args: string[], client: TaskCliClient): Pr
834
810
  for (let index = 0; index < args.length; index++) {
835
811
  const argument = args[index]!;
836
812
  if (argument === "--json") continue;
837
- if (argument === "--title") { title = args[++index]; if (title === undefined) throw new Error("--title requires a value"); continue; }
838
- if (argument === "--body") { body = args[++index]; if (body === undefined) throw new Error("--body requires a value"); continue; }
839
- if (argument === "--trigger") { trigger = args[++index]; if (trigger === undefined) throw new Error("--trigger requires a value"); continue; }
840
- if (argument === "--steps-json") { steps = parseJsonStringArrayFlag(args[++index], "--steps-json"); continue; }
841
- if (argument === "--tools-json") { tools = parseJsonStringArrayFlag(args[++index], "--tools-json"); continue; }
842
- if (argument === "--labels-json") { labels = parseJsonStringArrayFlag(args[++index], "--labels-json"); continue; }
843
- if (argument === "--extra-json") { extra = parseJsonObjectFlag(args[++index], "--extra-json"); continue; }
844
- if (argument === "--arguments-json") { playbookArguments = parseJsonAnyFlag(args[++index], "--arguments-json"); continue; }
845
- if (argument === "--status") { status = args[++index]; if (!status) throw new Error("--status requires a value"); continue; }
846
- if (argument === "--text") { text = args[++index]; if (text === undefined) throw new Error("--text requires a value"); continue; }
847
- if (argument === "--project-root") { playbookProjectRoot = args[++index]; if (!playbookProjectRoot) throw new Error("--project-root requires a value"); continue; }
813
+ if (argument === "--title") {
814
+ title = args[++index];
815
+ if (title === undefined) throw new Error("--title requires a value");
816
+ continue;
817
+ }
818
+ if (argument === "--body") {
819
+ body = args[++index];
820
+ if (body === undefined) throw new Error("--body requires a value");
821
+ continue;
822
+ }
823
+ if (argument === "--trigger") {
824
+ trigger = args[++index];
825
+ if (trigger === undefined) throw new Error("--trigger requires a value");
826
+ continue;
827
+ }
828
+ if (argument === "--steps-json") {
829
+ steps = parseJsonAnyFlag(args[++index], "--steps-json");
830
+ continue;
831
+ }
832
+ if (argument === "--tools-json") {
833
+ tools = parseJsonStringArrayFlag(args[++index], "--tools-json");
834
+ continue;
835
+ }
836
+ if (argument === "--labels-json") {
837
+ labels = parseJsonStringArrayFlag(args[++index], "--labels-json");
838
+ continue;
839
+ }
840
+ if (argument === "--extra-json") {
841
+ extra = parseJsonObjectFlag(args[++index], "--extra-json");
842
+ continue;
843
+ }
844
+ if (argument === "--arguments-json") {
845
+ playbookArguments = parseJsonAnyFlag(args[++index], "--arguments-json");
846
+ continue;
847
+ }
848
+ if (argument === "--run-id") {
849
+ runId = args[++index];
850
+ if (!runId) throw new Error("--run-id requires a value");
851
+ continue;
852
+ }
853
+ if (argument === "--status") {
854
+ status = args[++index];
855
+ if (!status) throw new Error("--status requires a value");
856
+ continue;
857
+ }
858
+ if (argument === "--text") {
859
+ text = args[++index];
860
+ if (text === undefined) throw new Error("--text requires a value");
861
+ continue;
862
+ }
863
+ if (argument === "--project-root") {
864
+ playbookProjectRoot = args[++index];
865
+ if (!playbookProjectRoot) throw new Error("--project-root requires a value");
866
+ continue;
867
+ }
848
868
  if (argument === "--limit") {
849
869
  const value = args[++index];
850
870
  if (!value || Number.isNaN(Number(value))) throw new Error("--limit requires a numeric value");
@@ -861,14 +881,29 @@ export async function runPlaybooksCli(args: string[], client: TaskCliClient): Pr
861
881
  case "create": {
862
882
  if (id) throw new Error("playbooks create accepts no positional arguments");
863
883
  if (!title) throw new Error("playbooks create requires --title");
864
- const artifact = await client.call<Record<string, unknown>, CliArtifact>("playbooks.create", { title, body, trigger, steps, tools, labels, extra, arguments: playbookArguments, project_root: playbookProjectRoot });
884
+ const artifact = await client.call<Record<string, unknown>, CliArtifact>("playbooks.create", {
885
+ title,
886
+ body,
887
+ trigger,
888
+ steps,
889
+ tools,
890
+ labels,
891
+ extra,
892
+ arguments: playbookArguments,
893
+ project_root: playbookProjectRoot,
894
+ });
865
895
  result = artifact;
866
896
  human = `Created playbook: ${artifactLabel(artifact)}`;
867
897
  break;
868
898
  }
869
899
  case "list": {
870
900
  if (id) throw new Error("playbooks list accepts no positional arguments");
871
- const rows = await client.call<Record<string, unknown>, CliArtifact[]>("playbooks.list", { status, text, limit, project_root: playbookProjectRoot });
901
+ const rows = await client.call<Record<string, unknown>, CliArtifact[]>("playbooks.list", {
902
+ status,
903
+ text,
904
+ limit,
905
+ project_root: playbookProjectRoot,
906
+ });
872
907
  result = rows;
873
908
  human = rows.length === 0 ? "No playbooks found." : rows.map((row) => artifactLabel(row)).join("\n");
874
909
  break;
@@ -889,7 +924,10 @@ export async function runPlaybooksCli(args: string[], client: TaskCliClient): Pr
889
924
  }
890
925
  case "invoke": {
891
926
  if (!id || second) throw new Error("playbooks invoke requires exactly one playbook id");
892
- const invocation = await client.call<Record<string, unknown>, { entryTaskId: string; missingArguments?: string[] }>("playbooks.invoke", { id, arguments: playbookArguments, project_root: playbookProjectRoot });
927
+ const invocation = await client.call<Record<string, unknown>, { entryTaskId: string; missingArguments?: string[] }>(
928
+ "playbooks.invoke",
929
+ { id, arguments: playbookArguments, run_id: runId, project_root: playbookProjectRoot },
930
+ );
893
931
  result = invocation;
894
932
  human = invocation.missingArguments
895
933
  ? `Missing required argument(s): ${invocation.missingArguments.join(", ")}.`
@@ -905,7 +943,7 @@ export async function runPlaybooksCli(args: string[], client: TaskCliClient): Pr
905
943
  break;
906
944
  }
907
945
  case "assign-project": {
908
- if (!id || second === undefined && positional.length > 2) throw new Error("playbooks assign-project requires <id> [project-root]");
946
+ if (!id || (second === undefined && positional.length > 2)) throw new Error("playbooks assign-project requires <id> [project-root]");
909
947
  const artifact = await client.call<Record<string, unknown>, CliArtifact>("playbooks.assign_project", { id, project_root: second });
910
948
  result = artifact;
911
949
  human = second ? `Assigned ${id} to ${second}` : `Unscoped ${id}`;
@@ -913,7 +951,8 @@ export async function runPlaybooksCli(args: string[], client: TaskCliClient): Pr
913
951
  }
914
952
  case "update": {
915
953
  if (!id || second) throw new Error("playbooks update requires exactly one playbook id");
916
- if (title === undefined && body === undefined && labels === undefined) throw new Error("playbooks update requires --title, --body, or --labels-json");
954
+ if (title === undefined && body === undefined && labels === undefined)
955
+ throw new Error("playbooks update requires --title, --body, or --labels-json");
917
956
  const artifact = await client.call<Record<string, unknown>, CliArtifact>("playbooks.update", { id, title, body, labels });
918
957
  result = artifact;
919
958
  human = `${artifactLabel(artifact)}`;
@@ -948,7 +987,9 @@ export async function runPlaybooksCli(args: string[], client: TaskCliClient): Pr
948
987
  break;
949
988
  }
950
989
  default:
951
- throw new Error("playbooks action must be create, list, show, invoke, preview, enable, disable, assign-project, update, contain, uncontain, depend, or undepend");
990
+ throw new Error(
991
+ "playbooks action must be create, list, show, invoke, preview, enable, disable, assign-project, update, contain, uncontain, depend, or undepend",
992
+ );
952
993
  }
953
994
  return json ? JSON.stringify(result) : human;
954
995
  }
@@ -972,16 +1013,54 @@ export async function runArtifactCli(args: string[], client: TaskCliClient, proj
972
1013
  for (let index = 0; index < args.length; index++) {
973
1014
  const argument = args[index]!;
974
1015
  if (argument === "--json") continue;
975
- if (argument === "--kind") { kind = args[++index]; if (!kind) throw new Error("--kind requires a value"); continue; }
976
- if (argument === "--title") { title = args[++index]; if (title === undefined) throw new Error("--title requires a value"); continue; }
977
- if (argument === "--body") { body = args[++index]; if (body === undefined) throw new Error("--body requires a value"); continue; }
978
- if (argument === "--status") { status = args[++index]; if (!status) throw new Error("--status requires a value"); continue; }
979
- if (argument === "--subtype") { subtype = args[++index]; if (!subtype) throw new Error("--subtype requires a value"); continue; }
980
- if (argument === "--labels-json") { labels = parseJsonStringArrayFlag(args[++index], "--labels-json"); continue; }
981
- if (argument === "--extra-json") { extra = parseJsonObjectFlag(args[++index], "--extra-json"); continue; }
982
- if (argument === "--template-id") { templateId = args[++index]; if (!templateId) throw new Error("--template-id requires a value"); continue; }
983
- if (argument === "--reason") { reason = args[++index]; if (reason === undefined) throw new Error("--reason requires a value"); continue; }
984
- if (argument === "--text") { text = args[++index]; if (text === undefined) throw new Error("--text requires a value"); continue; }
1016
+ if (argument === "--kind") {
1017
+ kind = args[++index];
1018
+ if (!kind) throw new Error("--kind requires a value");
1019
+ continue;
1020
+ }
1021
+ if (argument === "--title") {
1022
+ title = args[++index];
1023
+ if (title === undefined) throw new Error("--title requires a value");
1024
+ continue;
1025
+ }
1026
+ if (argument === "--body") {
1027
+ body = args[++index];
1028
+ if (body === undefined) throw new Error("--body requires a value");
1029
+ continue;
1030
+ }
1031
+ if (argument === "--status") {
1032
+ status = args[++index];
1033
+ if (!status) throw new Error("--status requires a value");
1034
+ continue;
1035
+ }
1036
+ if (argument === "--subtype") {
1037
+ subtype = args[++index];
1038
+ if (!subtype) throw new Error("--subtype requires a value");
1039
+ continue;
1040
+ }
1041
+ if (argument === "--labels-json") {
1042
+ labels = parseJsonStringArrayFlag(args[++index], "--labels-json");
1043
+ continue;
1044
+ }
1045
+ if (argument === "--extra-json") {
1046
+ extra = parseJsonObjectFlag(args[++index], "--extra-json");
1047
+ continue;
1048
+ }
1049
+ if (argument === "--template-id") {
1050
+ templateId = args[++index];
1051
+ if (!templateId) throw new Error("--template-id requires a value");
1052
+ continue;
1053
+ }
1054
+ if (argument === "--reason") {
1055
+ reason = args[++index];
1056
+ if (reason === undefined) throw new Error("--reason requires a value");
1057
+ continue;
1058
+ }
1059
+ if (argument === "--text") {
1060
+ text = args[++index];
1061
+ if (text === undefined) throw new Error("--text requires a value");
1062
+ continue;
1063
+ }
985
1064
  if (argument === "--limit") {
986
1065
  const value = args[++index];
987
1066
  if (!value || Number.isNaN(Number(value))) throw new Error("--limit requires a numeric value");
@@ -1011,7 +1090,14 @@ export async function runArtifactCli(args: string[], client: TaskCliClient, proj
1011
1090
  if (id) throw new Error("artifact create accepts no positional arguments");
1012
1091
  if (!kind && !templateId) throw new Error("artifact create requires --kind (or --template-id)");
1013
1092
  const artifact = await client.call<Record<string, unknown>, CliArtifact>("artifact.create", {
1014
- kind, title, body, status, subtype, labels, extra, template_id: templateId,
1093
+ kind,
1094
+ title,
1095
+ body,
1096
+ status,
1097
+ subtype,
1098
+ labels,
1099
+ extra,
1100
+ template_id: templateId,
1015
1101
  ...(kind === "task" ? { project_root: projectRoot } : {}),
1016
1102
  });
1017
1103
  result = artifact;
@@ -1034,14 +1120,20 @@ export async function runArtifactCli(args: string[], client: TaskCliClient, proj
1034
1120
  }
1035
1121
  case "remove": {
1036
1122
  if (!id) throw new Error("artifact remove requires exactly one artifact id");
1037
- const record = await client.call<Record<string, unknown>, { artifactId: string; trashedAt: string; purgeAfter: string; reason?: string }>("artifact.remove", { id, reason });
1123
+ const record = await client.call<
1124
+ Record<string, unknown>,
1125
+ { artifactId: string; trashedAt: string; purgeAfter: string; reason?: string }
1126
+ >("artifact.remove", { id, reason });
1038
1127
  result = record;
1039
1128
  human = `Trashed ${record.artifactId}: eligible for purge at ${record.purgeAfter}`;
1040
1129
  break;
1041
1130
  }
1042
1131
  case "remove-subtree": {
1043
1132
  if (!id) throw new Error("artifact remove-subtree requires exactly one artifact id");
1044
- const outcome = await client.call<Record<string, unknown>, { removed: string[]; skipped: string[] }>("artifact.remove_subtree", { id, reason });
1133
+ const outcome = await client.call<Record<string, unknown>, { removed: string[]; skipped: string[] }>("artifact.remove_subtree", {
1134
+ id,
1135
+ reason,
1136
+ });
1045
1137
  result = outcome;
1046
1138
  human = `Trashed ${outcome.removed.length} artifact(s)${outcome.skipped.length > 0 ? `, skipped ${outcome.skipped.length} already-trashed` : ""}.`;
1047
1139
  break;
@@ -1055,16 +1147,25 @@ export async function runArtifactCli(args: string[], client: TaskCliClient, proj
1055
1147
  }
1056
1148
  case "trash-status": {
1057
1149
  if (!id) throw new Error("artifact trash-status requires exactly one artifact id");
1058
- const record = await client.call<Record<string, unknown>, { artifactId: string; trashedAt: string; purgeAfter: string; reason?: string } | null>("artifact.trash_status", { id });
1150
+ const record = await client.call<
1151
+ Record<string, unknown>,
1152
+ { artifactId: string; trashedAt: string; purgeAfter: string; reason?: string } | null
1153
+ >("artifact.trash_status", { id });
1059
1154
  result = record;
1060
- human = record ? `${record.artifactId}: trashed at ${record.trashedAt}, purge eligible at ${record.purgeAfter}` : `${id} is not trashed`;
1155
+ human = record
1156
+ ? `${record.artifactId}: trashed at ${record.trashedAt}, purge eligible at ${record.purgeAfter}`
1157
+ : `${id} is not trashed`;
1061
1158
  break;
1062
1159
  }
1063
1160
  case "trash-list": {
1064
1161
  if (id) throw new Error("artifact trash-list accepts no positional arguments");
1065
- const rows = await client.call<Record<string, unknown>, Array<{ artifactId: string; trashedAt: string; purgeAfter: string; reason?: string }>>("artifact.trash_list", {});
1162
+ const rows = await client.call<
1163
+ Record<string, unknown>,
1164
+ Array<{ artifactId: string; trashedAt: string; purgeAfter: string; reason?: string }>
1165
+ >("artifact.trash_list", {});
1066
1166
  result = rows;
1067
- human = rows.length === 0 ? "Trash is empty." : rows.map((row) => `${row.artifactId}: purge eligible at ${row.purgeAfter}`).join("\n");
1167
+ human =
1168
+ rows.length === 0 ? "Trash is empty." : rows.map((row) => `${row.artifactId}: purge eligible at ${row.purgeAfter}`).join("\n");
1068
1169
  break;
1069
1170
  }
1070
1171
  default:
@@ -1092,8 +1193,15 @@ export async function runGraphProjectionCli(args: string[], client: TaskCliClien
1092
1193
  for (let index = 0; index < args.length; index++) {
1093
1194
  const argument = args[index]!;
1094
1195
  if (argument === "--json") continue;
1095
- if (argument === "--batch-json") { batch = parseJsonObjectFlag(args[++index], "--batch-json"); continue; }
1096
- if (argument === "--producer-id") { producerId = args[++index]; if (!producerId) throw new Error("--producer-id requires a value"); continue; }
1196
+ if (argument === "--batch-json") {
1197
+ batch = parseJsonObjectFlag(args[++index], "--batch-json");
1198
+ continue;
1199
+ }
1200
+ if (argument === "--producer-id") {
1201
+ producerId = args[++index];
1202
+ if (!producerId) throw new Error("--producer-id requires a value");
1203
+ continue;
1204
+ }
1097
1205
  positional.push(argument);
1098
1206
  }
1099
1207
  const [action] = positional;
@@ -1128,9 +1236,27 @@ export async function runLogCli(args: string[], client: TaskCliClient, projectRo
1128
1236
  for (let index = 0; index < args.length; index++) {
1129
1237
  const argument = args[index]!;
1130
1238
  if (argument === "--json") continue;
1131
- if (argument === "--global") { global = true; continue; }
1132
- if (argument === "--fields-json") { fields = parseJsonObjectFlag(args[++index], "--fields-json"); continue; }
1133
- if (["--source", "--source-label", "--level", "--message", "--operation-id", "--session-id", "--occurred-at", "--since", "--limit"].includes(argument)) {
1239
+ if (argument === "--global") {
1240
+ global = true;
1241
+ continue;
1242
+ }
1243
+ if (argument === "--fields-json") {
1244
+ fields = parseJsonObjectFlag(args[++index], "--fields-json");
1245
+ continue;
1246
+ }
1247
+ if (
1248
+ [
1249
+ "--source",
1250
+ "--source-label",
1251
+ "--level",
1252
+ "--message",
1253
+ "--operation-id",
1254
+ "--session-id",
1255
+ "--occurred-at",
1256
+ "--since",
1257
+ "--limit",
1258
+ ].includes(argument)
1259
+ ) {
1134
1260
  const value = args[++index];
1135
1261
  if (value === undefined) throw new Error(`${argument} requires a value`);
1136
1262
  if (argument === "--source") source = value;
@@ -1157,17 +1283,25 @@ export async function runLogCli(args: string[], client: TaskCliClient, projectRo
1157
1283
  if (!message) throw new Error("log append requires --message");
1158
1284
  if (!operationId) throw new Error("log append requires --operation-id");
1159
1285
  const result = await client.call("logs.append", {
1160
- source_id: source, ...(sourceLabel ? { source_label: sourceLabel } : {}),
1286
+ source_id: source,
1287
+ ...(sourceLabel ? { source_label: sourceLabel } : {}),
1161
1288
  ...(global ? {} : { project_root: projectRoot }),
1162
- level, message, operation_id: operationId,
1163
- ...(fields ? { fields } : {}), ...(sessionId ? { session_id: sessionId } : {}), ...(occurredAt ? { occurred_at: occurredAt } : {}),
1289
+ level,
1290
+ message,
1291
+ operation_id: operationId,
1292
+ ...(fields ? { fields } : {}),
1293
+ ...(sessionId ? { session_id: sessionId } : {}),
1294
+ ...(occurredAt ? { occurred_at: occurredAt } : {}),
1164
1295
  });
1165
1296
  return json ? JSON.stringify(result) : JSON.stringify(result, null, 2);
1166
1297
  }
1167
1298
  if (action === "query") {
1168
1299
  if (!source) throw new Error("log query requires --source");
1169
1300
  const result = await client.call<Record<string, unknown>, { entries: unknown[]; truncated: boolean }>("logs.query", {
1170
- source_id: source, ...(since ? { since } : {}), ...(level ? { level } : {}), ...(limit === undefined ? {} : { limit }),
1301
+ source_id: source,
1302
+ ...(since ? { since } : {}),
1303
+ ...(level ? { level } : {}),
1304
+ ...(limit === undefined ? {} : { limit }),
1171
1305
  });
1172
1306
  if (json) return JSON.stringify(result);
1173
1307
  const lines = result.entries.map((entry) => JSON.stringify(entry));
@@ -1184,20 +1318,31 @@ export async function runSessionIdentityCli(args: string[], client: TaskCliClien
1184
1318
  for (let index = 0; index < args.length; index++) {
1185
1319
  const argument = args[index]!;
1186
1320
  if (argument === "--json") continue;
1187
- if (argument === "--session-id") { sessionId = args[++index]; continue; }
1188
- if (argument === "--session-secret") { secret = args[++index]; continue; }
1321
+ if (argument === "--session-id") {
1322
+ sessionId = args[++index];
1323
+ continue;
1324
+ }
1325
+ if (argument === "--session-secret") {
1326
+ secret = args[++index];
1327
+ continue;
1328
+ }
1189
1329
  if (argument.startsWith("--")) throw new Error(`unknown session option ${argument}`);
1190
1330
  positional.push(argument);
1191
1331
  }
1192
1332
  const [action] = positional;
1193
1333
  if (action === "register") {
1194
1334
  if (!sessionId) throw new Error("session register requires --session-id");
1195
- const result = await client.call<Record<string, unknown>, { sessionId: string; secret: string }>("session.register", { session_id: sessionId });
1335
+ const result = await client.call<Record<string, unknown>, { sessionId: string; secret: string }>("session.register", {
1336
+ session_id: sessionId,
1337
+ });
1196
1338
  return json ? JSON.stringify(result) : JSON.stringify(result, null, 2);
1197
1339
  }
1198
1340
  if (action === "release") {
1199
1341
  if (!sessionId) throw new Error("session release requires --session-id");
1200
- const result = await client.call<Record<string, unknown>, { released: boolean }>("session.release", { session_id: sessionId, ...(secret ? { session_secret: secret } : {}) });
1342
+ const result = await client.call<Record<string, unknown>, { released: boolean }>("session.release", {
1343
+ session_id: sessionId,
1344
+ ...(secret ? { session_secret: secret } : {}),
1345
+ });
1201
1346
  return json ? JSON.stringify(result) : JSON.stringify(result, null, 2);
1202
1347
  }
1203
1348
  throw new Error("session action must be register or release");
@@ -1225,20 +1370,71 @@ export async function runDiscussCli(args: string[], client: TaskCliClient): Prom
1225
1370
  for (let index = 0; index < args.length; index++) {
1226
1371
  const argument = args[index]!;
1227
1372
  if (argument === "--json") continue;
1228
- if (argument === "--title") { title = args[++index]; if (!title) throw new Error("--title requires a value"); continue; }
1229
- if (argument === "--actor") { actor = args[++index]; if (!actor) throw new Error("--actor requires a value"); continue; }
1230
- if (argument === "--content") { content = args[++index]; if (content === undefined) throw new Error("--content requires a value"); continue; }
1231
- if (argument === "--body") { body = args[++index]; if (body === undefined) throw new Error("--body requires a value"); continue; }
1232
- if (argument === "--labels-json") { labels = parseJsonStringArrayFlag(args[++index], "--labels-json"); continue; }
1233
- if (argument === "--blocks-json") { blocksTaskIds = parseJsonStringArrayFlag(args[++index], "--blocks-json"); continue; }
1234
- if (argument === "--task-id") { taskId = args[++index]; if (!taskId) throw new Error("--task-id requires a value"); continue; }
1235
- if (argument === "--options-json") { options = parseJsonStringArrayFlag(args[++index], "--options-json"); continue; }
1236
- if (argument === "--options-mode") { optionsMode = args[++index]; if (!optionsMode) throw new Error("--options-mode requires a value"); continue; }
1237
- if (argument === "--option-descriptions-json") { optionDescriptions = parseJsonStringArrayFlag(args[++index], "--option-descriptions-json"); continue; }
1238
- if (argument === "--selected-json") { selected = parseJsonStringArrayFlag(args[++index], "--selected-json"); continue; }
1239
- if (argument === "--reason") { reason = args[++index]; if (reason === undefined) throw new Error("--reason requires a value"); continue; }
1240
- if (argument === "--settlement") { settlement = args[++index]; if (!settlement) throw new Error("--settlement requires a value"); continue; }
1241
- if (argument === "--state") { state = args[++index]; if (!state) throw new Error("--state requires a value"); continue; }
1373
+ if (argument === "--title") {
1374
+ title = args[++index];
1375
+ if (!title) throw new Error("--title requires a value");
1376
+ continue;
1377
+ }
1378
+ if (argument === "--actor") {
1379
+ actor = args[++index];
1380
+ if (!actor) throw new Error("--actor requires a value");
1381
+ continue;
1382
+ }
1383
+ if (argument === "--content") {
1384
+ content = args[++index];
1385
+ if (content === undefined) throw new Error("--content requires a value");
1386
+ continue;
1387
+ }
1388
+ if (argument === "--body") {
1389
+ body = args[++index];
1390
+ if (body === undefined) throw new Error("--body requires a value");
1391
+ continue;
1392
+ }
1393
+ if (argument === "--labels-json") {
1394
+ labels = parseJsonStringArrayFlag(args[++index], "--labels-json");
1395
+ continue;
1396
+ }
1397
+ if (argument === "--blocks-json") {
1398
+ blocksTaskIds = parseJsonStringArrayFlag(args[++index], "--blocks-json");
1399
+ continue;
1400
+ }
1401
+ if (argument === "--task-id") {
1402
+ taskId = args[++index];
1403
+ if (!taskId) throw new Error("--task-id requires a value");
1404
+ continue;
1405
+ }
1406
+ if (argument === "--options-json") {
1407
+ options = parseJsonStringArrayFlag(args[++index], "--options-json");
1408
+ continue;
1409
+ }
1410
+ if (argument === "--options-mode") {
1411
+ optionsMode = args[++index];
1412
+ if (!optionsMode) throw new Error("--options-mode requires a value");
1413
+ continue;
1414
+ }
1415
+ if (argument === "--option-descriptions-json") {
1416
+ optionDescriptions = parseJsonStringArrayFlag(args[++index], "--option-descriptions-json");
1417
+ continue;
1418
+ }
1419
+ if (argument === "--selected-json") {
1420
+ selected = parseJsonStringArrayFlag(args[++index], "--selected-json");
1421
+ continue;
1422
+ }
1423
+ if (argument === "--reason") {
1424
+ reason = args[++index];
1425
+ if (reason === undefined) throw new Error("--reason requires a value");
1426
+ continue;
1427
+ }
1428
+ if (argument === "--settlement") {
1429
+ settlement = args[++index];
1430
+ if (!settlement) throw new Error("--settlement requires a value");
1431
+ continue;
1432
+ }
1433
+ if (argument === "--state") {
1434
+ state = args[++index];
1435
+ if (!state) throw new Error("--state requires a value");
1436
+ continue;
1437
+ }
1242
1438
  if (argument === "--after-round") {
1243
1439
  const value = args[++index];
1244
1440
  if (!value || Number.isNaN(Number(value))) throw new Error("--after-round requires a numeric value");
@@ -1258,12 +1454,30 @@ export async function runDiscussCli(args: string[], client: TaskCliClient): Prom
1258
1454
  switch (action) {
1259
1455
  case "open": {
1260
1456
  if (id) throw new Error("discuss open accepts no positional arguments");
1261
- const result = await client.call<Record<string, unknown>, unknown>("discuss.open", { title, actor, content, body, labels, blocks_task_ids: blocksTaskIds, options, options_mode: optionsMode, option_descriptions: optionDescriptions });
1457
+ const result = await client.call<Record<string, unknown>, unknown>("discuss.open", {
1458
+ title,
1459
+ actor,
1460
+ content,
1461
+ body,
1462
+ labels,
1463
+ blocks_task_ids: blocksTaskIds,
1464
+ options,
1465
+ options_mode: optionsMode,
1466
+ option_descriptions: optionDescriptions,
1467
+ });
1262
1468
  return json ? JSON.stringify(result) : JSON.stringify(result, null, 2);
1263
1469
  }
1264
1470
  case "reply": {
1265
1471
  if (!id) throw new Error("discuss reply requires exactly one discussion id");
1266
- const result = await client.call<Record<string, unknown>, unknown>("discuss.reply", { id, actor, content, selected, options, options_mode: optionsMode, option_descriptions: optionDescriptions });
1472
+ const result = await client.call<Record<string, unknown>, unknown>("discuss.reply", {
1473
+ id,
1474
+ actor,
1475
+ content,
1476
+ selected,
1477
+ options,
1478
+ options_mode: optionsMode,
1479
+ option_descriptions: optionDescriptions,
1480
+ });
1267
1481
  return json ? JSON.stringify(result) : JSON.stringify(result, null, 2);
1268
1482
  }
1269
1483
  case "defer": {
@@ -1343,34 +1557,77 @@ export async function runNoteCli(args: string[], client: TaskCliClient, projectR
1343
1557
  let human: string;
1344
1558
  if (action === "capture") {
1345
1559
  if (!id || target) throw new Error("notes capture requires exactly one request argument");
1346
- result = await client.call("notes.capture", { body: id, ...(title ? { title } : {}), project_root: projectRoot, actor: "human", source: "cli" }) as CliArtifact;
1560
+ result = (await client.call("notes.capture", {
1561
+ body: id,
1562
+ ...(title ? { title } : {}),
1563
+ project_root: projectRoot,
1564
+ actor: "human",
1565
+ source: "cli",
1566
+ })) as CliArtifact;
1347
1567
  human = `Captured: ${artifactLabel(result)}`;
1348
1568
  } else if (action === "list") {
1349
1569
  if (id) throw new Error("notes list accepts no positional arguments");
1350
- result = await client.call("notes.list", { project_root: projectRoot, ...(status ? { status } : {}), ...(text ? { text } : {}), ...(limit === undefined ? {} : { limit }) }) as CliArtifact[];
1570
+ result = (await client.call("notes.list", {
1571
+ project_root: projectRoot,
1572
+ ...(status ? { status } : {}),
1573
+ ...(text ? { text } : {}),
1574
+ ...(limit === undefined ? {} : { limit }),
1575
+ })) as CliArtifact[];
1351
1576
  human = result.length > 0 ? result.map((note) => `[${note.status}] ${artifactLabel(note)}`).join("\n") : "No open notes.";
1352
1577
  } else if (action === "show") {
1353
1578
  if (!id || target) throw new Error("notes show requires exactly one note id");
1354
- result = await client.call("notes.show", { id, project_root: projectRoot }) as CliArtifact;
1579
+ result = (await client.call("notes.show", { id, project_root: projectRoot })) as CliArtifact;
1355
1580
  human = `${artifactLabel(result)}\n\n${result.body ?? ""}`.trimEnd();
1356
1581
  } else if (action === "history") {
1357
1582
  if (!id || target) throw new Error("notes history requires exactly one note id");
1358
- const page = await client.call<Record<string, unknown>, import("./domain/note-event.ts").NoteHistoryPage>("notes.history", { id, project_root: projectRoot, direction: "desc", ...(limit === undefined ? {} : { limit }) });
1583
+ const page = await client.call<Record<string, unknown>, import("./domain/note-event.ts").NoteHistoryPage>("notes.history", {
1584
+ id,
1585
+ project_root: projectRoot,
1586
+ direction: "desc",
1587
+ ...(limit === undefined ? {} : { limit }),
1588
+ });
1359
1589
  result = page;
1360
- human = page.events.length === 0
1361
- ? `No recorded history for ${id}.`
1362
- : [...page.events].reverse().map((event) => `${event.occurredAt} ${event.type} · ${event.actor}/${event.source}${event.relatedId ? ` · ${event.relatedId}` : ""}${event.disposition ? ` · ${event.disposition}` : ""}${event.reason ? ` · ${event.reason}` : ""}`).join("\n");
1590
+ human =
1591
+ page.events.length === 0
1592
+ ? `No recorded history for ${id}.`
1593
+ : [...page.events]
1594
+ .reverse()
1595
+ .map(
1596
+ (event) =>
1597
+ `${event.occurredAt} ${event.type} · ${event.actor}/${event.source}${event.relatedId ? ` · ${event.relatedId}` : ""}${event.disposition ? ` · ${event.disposition}` : ""}${event.reason ? ` · ${event.reason}` : ""}`,
1598
+ )
1599
+ .join("\n");
1363
1600
  } else if (action === "consume") {
1364
1601
  if (!id || target) throw new Error("notes consume requires exactly one note id");
1365
- result = await client.call("notes.consume", { id, project_root: projectRoot, actor: "agent", source: "cli", ...(reason ? { reason } : {}) }) as CliArtifact;
1602
+ result = (await client.call("notes.consume", {
1603
+ id,
1604
+ project_root: projectRoot,
1605
+ actor: "agent",
1606
+ source: "cli",
1607
+ ...(reason ? { reason } : {}),
1608
+ })) as CliArtifact;
1366
1609
  human = `Consumed: ${artifactLabel(result)}`;
1367
1610
  } else if (action === "promote") {
1368
1611
  if (!id || !target || positional.length !== 3) throw new Error("notes promote requires a note id and target artifact id");
1369
- result = await client.call("notes.promote", { id, target_id: target, project_root: projectRoot, actor: "agent", source: "cli", ...(reason ? { reason } : {}) }) as CliArtifact;
1612
+ result = (await client.call("notes.promote", {
1613
+ id,
1614
+ target_id: target,
1615
+ project_root: projectRoot,
1616
+ actor: "agent",
1617
+ source: "cli",
1618
+ ...(reason ? { reason } : {}),
1619
+ })) as CliArtifact;
1370
1620
  human = `Promoted: ${artifactLabel(result)} → ${target}`;
1371
1621
  } else if (action === "archive") {
1372
1622
  if (!id || !target || positional.length !== 3) throw new Error("notes archive requires a note id and disposition");
1373
- result = await client.call("notes.archive", { id, disposition: target, project_root: projectRoot, actor: "human", source: "cli", ...(reason ? { reason } : {}) }) as CliArtifact;
1623
+ result = (await client.call("notes.archive", {
1624
+ id,
1625
+ disposition: target,
1626
+ project_root: projectRoot,
1627
+ actor: "human",
1628
+ source: "cli",
1629
+ ...(reason ? { reason } : {}),
1630
+ })) as CliArtifact;
1374
1631
  human = `Archived: ${artifactLabel(result)} · ${target}`;
1375
1632
  } else {
1376
1633
  throw new Error("notes action must be capture, list, show, history, consume, promote, or archive");
@@ -1418,12 +1675,22 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1418
1675
  if (!sessionSecret) throw new Error("--session-secret requires a value");
1419
1676
  continue;
1420
1677
  }
1421
- if (argument === "--title" || argument === "--body" || argument === "--labels-json" || argument === "--status" || argument === "--reason") {
1678
+ if (
1679
+ argument === "--title" ||
1680
+ argument === "--body" ||
1681
+ argument === "--labels-json" ||
1682
+ argument === "--status" ||
1683
+ argument === "--reason"
1684
+ ) {
1422
1685
  const value = args[++index];
1423
1686
  if (value === undefined) throw new Error(`${argument} requires a value`);
1424
- if (argument === "--title") { updateInput.title = value; title = value; }
1425
- else if (argument === "--body") { updateInput.body = value; body = value; }
1426
- else if (argument === "--reason") reason = value;
1687
+ if (argument === "--title") {
1688
+ updateInput.title = value;
1689
+ title = value;
1690
+ } else if (argument === "--body") {
1691
+ updateInput.body = value;
1692
+ body = value;
1693
+ } else if (argument === "--reason") reason = value;
1427
1694
  else if (argument === "--status") {
1428
1695
  status = value;
1429
1696
  if (value === "todo") updateInput.status = value;
@@ -1433,7 +1700,10 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1433
1700
  }
1434
1701
  continue;
1435
1702
  }
1436
- if (argument === "--extra-json") { extra = parseJsonObjectFlag(args[++index]!, "--extra-json"); continue; }
1703
+ if (argument === "--extra-json") {
1704
+ extra = parseJsonObjectFlag(args[++index]!, "--extra-json");
1705
+ continue;
1706
+ }
1437
1707
  if (argument === "--gates-json") {
1438
1708
  const value = args[++index];
1439
1709
  if (!value) throw new Error("--gates-json requires a value");
@@ -1442,11 +1712,29 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1442
1712
  gates = parsed;
1443
1713
  continue;
1444
1714
  }
1445
- if (argument === "--checklist-json") { checklist = parseJsonObjectFlag(args[++index]!, "--checklist-json"); continue; }
1446
- if (argument === "--template-id") { templateId = args[++index]; if (!templateId) throw new Error("--template-id requires a value"); continue; }
1447
- if (argument === "--parent-id") { parentId = args[++index]; if (!parentId) throw new Error("--parent-id requires a value"); continue; }
1448
- if (argument === "--depends-on-json") { dependsOn = parseJsonStringArrayFlag(args[++index]!, "--depends-on-json"); continue; }
1449
- if (argument === "--text") { text = args[++index]; if (text === undefined) throw new Error("--text requires a value"); continue; }
1715
+ if (argument === "--checklist-json") {
1716
+ checklist = parseJsonObjectFlag(args[++index]!, "--checklist-json");
1717
+ continue;
1718
+ }
1719
+ if (argument === "--template-id") {
1720
+ templateId = args[++index];
1721
+ if (!templateId) throw new Error("--template-id requires a value");
1722
+ continue;
1723
+ }
1724
+ if (argument === "--parent-id") {
1725
+ parentId = args[++index];
1726
+ if (!parentId) throw new Error("--parent-id requires a value");
1727
+ continue;
1728
+ }
1729
+ if (argument === "--depends-on-json") {
1730
+ dependsOn = parseJsonStringArrayFlag(args[++index]!, "--depends-on-json");
1731
+ continue;
1732
+ }
1733
+ if (argument === "--text") {
1734
+ text = args[++index];
1735
+ if (text === undefined) throw new Error("--text requires a value");
1736
+ continue;
1737
+ }
1450
1738
  if (argument === "--limit") {
1451
1739
  const value = args[++index];
1452
1740
  if (!value || Number.isNaN(Number(value))) throw new Error("--limit requires a numeric value");
@@ -1459,10 +1747,26 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1459
1747
  listScope = value;
1460
1748
  continue;
1461
1749
  }
1462
- if (argument === "--root-task-id") { rootTaskId = args[++index]; if (!rootTaskId) throw new Error("--root-task-id requires a value"); continue; }
1463
- if (argument === "--owner") { owner = args[++index]; if (!owner) throw new Error("--owner requires a value"); continue; }
1464
- if (argument === "--token") { token = args[++index]; if (!token) throw new Error("--token requires a value"); continue; }
1465
- if (argument === "--note") { note = args[++index]; if (note === undefined) throw new Error("--note requires a value"); continue; }
1750
+ if (argument === "--root-task-id") {
1751
+ rootTaskId = args[++index];
1752
+ if (!rootTaskId) throw new Error("--root-task-id requires a value");
1753
+ continue;
1754
+ }
1755
+ if (argument === "--owner") {
1756
+ owner = args[++index];
1757
+ if (!owner) throw new Error("--owner requires a value");
1758
+ continue;
1759
+ }
1760
+ if (argument === "--token") {
1761
+ token = args[++index];
1762
+ if (!token) throw new Error("--token requires a value");
1763
+ continue;
1764
+ }
1765
+ if (argument === "--note") {
1766
+ note = args[++index];
1767
+ if (note === undefined) throw new Error("--note requires a value");
1768
+ continue;
1769
+ }
1466
1770
  if (argument === "--ttl-ms") {
1467
1771
  const value = args[++index];
1468
1772
  if (!value || Number.isNaN(Number(value))) throw new Error("--ttl-ms requires a numeric value");
@@ -1475,13 +1779,17 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1475
1779
  cursor = Number(value);
1476
1780
  continue;
1477
1781
  }
1478
- if (argument === "--event-types-json") { eventTypes = parseJsonStringArrayFlag(args[++index]!, "--event-types-json"); continue; }
1782
+ if (argument === "--event-types-json") {
1783
+ eventTypes = parseJsonStringArrayFlag(args[++index]!, "--event-types-json");
1784
+ continue;
1785
+ }
1479
1786
  if (argument.startsWith("--")) throw new Error(`unknown tasks option ${argument}`);
1480
1787
  positional.push(argument);
1481
1788
  }
1482
1789
  const [action, id, dependencyId] = positional;
1483
1790
  const reasonSupportedActions = new Set(["update", "depend", "undepend", "contain", "uncontain"]);
1484
- if (reason !== undefined && !reasonSupportedActions.has(action ?? "")) throw new Error("--reason is only supported by tasks update, depend, undepend, contain, and uncontain");
1791
+ if (reason !== undefined && !reasonSupportedActions.has(action ?? ""))
1792
+ throw new Error("--reason is only supported by tasks update, depend, undepend, contain, and uncontain");
1485
1793
  const sessionScope = sessionId ? { session_id: sessionId } : {};
1486
1794
  // Only meaningful alongside a registered session_id (see session.register); required by the
1487
1795
  // daemon only for the specific Focus-mutating operations below (focus/pause/unpause/clear_focus)
@@ -1492,14 +1800,20 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1492
1800
  switch (action) {
1493
1801
  case "active": {
1494
1802
  if (id) throw new Error("tasks active accepts no positional arguments");
1495
- const active = await client.call<Record<string, unknown>, CliArtifact | null>("tasks.active", { project_root: projectRoot, ...sessionScope });
1803
+ const active = await client.call<Record<string, unknown>, CliArtifact | null>("tasks.active", {
1804
+ project_root: projectRoot,
1805
+ ...sessionScope,
1806
+ });
1496
1807
  result = active;
1497
1808
  human = active ? `Active: ${artifactLabel(active)}` : "No active task.";
1498
1809
  break;
1499
1810
  }
1500
1811
  case "focused": {
1501
1812
  if (id) throw new Error("tasks focused accepts no positional arguments");
1502
- const focus = await client.call<Record<string, unknown>, { artifact: CliArtifact; status: "active" | "paused"; updatedAt: string } | null>("tasks.focused", { project_root: projectRoot, ...sessionScope });
1813
+ const focus = await client.call<
1814
+ Record<string, unknown>,
1815
+ { artifact: CliArtifact; status: "active" | "paused"; updatedAt: string } | null
1816
+ >("tasks.focused", { project_root: projectRoot, ...sessionScope });
1503
1817
  result = focus;
1504
1818
  human = focus ? `Focused (${focus.status}): ${artifactLabel(focus.artifact)}` : "No focused task.";
1505
1819
  break;
@@ -1508,14 +1822,24 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1508
1822
  case "unpause": {
1509
1823
  if (id) throw new Error(`tasks ${action} accepts no positional arguments`);
1510
1824
  const operation = action === "pause" ? "tasks.pause" : "tasks.unpause";
1511
- const focus = await client.call<Record<string, unknown>, { artifact: CliArtifact; status: string }>(operation, { actor: "user", source: "cli", ...sessionScope, ...sessionSecretField });
1825
+ const focus = await client.call<Record<string, unknown>, { artifact: CliArtifact; status: string }>(operation, {
1826
+ actor: "user",
1827
+ source: "cli",
1828
+ ...sessionScope,
1829
+ ...sessionSecretField,
1830
+ });
1512
1831
  result = focus;
1513
1832
  human = `Focused (${focus.status}): ${artifactLabel(focus.artifact)}`;
1514
1833
  break;
1515
1834
  }
1516
1835
  case "clear-focus": {
1517
1836
  if (id) throw new Error("tasks clear-focus accepts no positional arguments");
1518
- const cleared = await client.call<Record<string, unknown>, { cleared: boolean }>("tasks.clear_focus", { actor: "user", source: "cli", ...sessionScope, ...sessionSecretField });
1837
+ const cleared = await client.call<Record<string, unknown>, { cleared: boolean }>("tasks.clear_focus", {
1838
+ actor: "user",
1839
+ source: "cli",
1840
+ ...sessionScope,
1841
+ ...sessionSecretField,
1842
+ });
1519
1843
  result = cleared;
1520
1844
  human = cleared.cleared ? "Task focus cleared." : "No focused task.";
1521
1845
  break;
@@ -1567,20 +1891,36 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1567
1891
  }
1568
1892
  case "event-feed": {
1569
1893
  if (id) throw new Error("tasks event-feed accepts no positional arguments");
1570
- const page = await client.call<Record<string, unknown>, import("./domain/task-event.ts").TaskEventFeedPage>("tasks.event_feed", { cursor, limit, event_types: eventTypes });
1894
+ const page = await client.call<Record<string, unknown>, import("./domain/task-event.ts").TaskEventFeedPage>("tasks.event_feed", {
1895
+ cursor,
1896
+ limit,
1897
+ event_types: eventTypes,
1898
+ });
1571
1899
  result = page;
1572
- human = page.events.length === 0
1573
- ? "No events."
1574
- : page.events.map((event) => `${event.id} ${event.occurredAt} ${event.taskId} ${event.type}`).join("\n");
1900
+ human =
1901
+ page.events.length === 0
1902
+ ? "No events."
1903
+ : page.events.map((event) => `${event.id} ${event.occurredAt} ${event.taskId} ${event.type}`).join("\n");
1575
1904
  break;
1576
1905
  }
1577
1906
  case "create": {
1578
1907
  if (id) throw new Error("tasks create accepts no positional arguments");
1579
1908
  if (!title) throw new Error("tasks create requires --title");
1580
1909
  const artifact = await client.call<Record<string, unknown>, CliArtifact>("tasks.create", {
1581
- title, body, status, labels, extra, gates, checklist,
1582
- template_id: templateId, parent_id: parentId, depends_on: dependsOn,
1583
- project_root: projectRoot, actor: "user", source: "cli", ...sessionScope,
1910
+ title,
1911
+ body,
1912
+ status,
1913
+ labels,
1914
+ extra,
1915
+ gates,
1916
+ checklist,
1917
+ template_id: templateId,
1918
+ parent_id: parentId,
1919
+ depends_on: dependsOn,
1920
+ project_root: projectRoot,
1921
+ actor: "user",
1922
+ source: "cli",
1923
+ ...sessionScope,
1584
1924
  });
1585
1925
  result = artifact;
1586
1926
  human = `Created task: ${artifactLabel(artifact)}`;
@@ -1589,7 +1929,14 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1589
1929
  case "list": {
1590
1930
  if (id) throw new Error("tasks list accepts no positional arguments");
1591
1931
  const rows = await client.call<Record<string, unknown>, CliArtifact[]>("tasks.list", {
1592
- status, text, limit, labels, project_root: projectRoot, scope: listScope, root_task_id: rootTaskId, ...sessionScope,
1932
+ status,
1933
+ text,
1934
+ limit,
1935
+ labels,
1936
+ project_root: projectRoot,
1937
+ scope: listScope,
1938
+ root_task_id: rootTaskId,
1939
+ ...sessionScope,
1593
1940
  });
1594
1941
  result = rows;
1595
1942
  human = rows.length === 0 ? "No tasks found." : rows.map((row) => artifactLabel(row)).join("\n");
@@ -1606,9 +1953,10 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1606
1953
  if (!id || dependencyId) throw new Error("tasks run-gates requires exactly one task id");
1607
1954
  const results = await client.call<Record<string, unknown>, GateResult[]>("tasks.run_gates", { id, actor: "user", source: "cli" });
1608
1955
  result = results;
1609
- human = results.length === 0
1610
- ? "No gates configured."
1611
- : results.map((gate) => `${gate.passed ? "✓" : "✗"} ${gate.gate.type}: ${gate.gate.target} — ${gate.output}`).join("\n");
1956
+ human =
1957
+ results.length === 0
1958
+ ? "No gates configured."
1959
+ : results.map((gate) => `${gate.passed ? "✓" : "✗"} ${gate.gate.type}: ${gate.gate.target} — ${gate.output}`).join("\n");
1612
1960
  break;
1613
1961
  }
1614
1962
  case "set-checklist": {
@@ -1630,7 +1978,10 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1630
1978
  case "context": {
1631
1979
  if (id) throw new Error("tasks context accepts no positional arguments");
1632
1980
  const summary = await client.call<Record<string, unknown>, string | null>("tasks.context", {
1633
- project_root: projectRoot, scope: listScope, root_task_id: rootTaskId, ...sessionScope,
1981
+ project_root: projectRoot,
1982
+ scope: listScope,
1983
+ root_task_id: rootTaskId,
1984
+ ...sessionScope,
1634
1985
  });
1635
1986
  result = summary;
1636
1987
  human = summary ?? "No open tasks.";
@@ -1639,7 +1990,12 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1639
1990
  case "contain": {
1640
1991
  if (!id || !dependencyId || positional.length !== 3) throw new Error("tasks contain requires a parent id and child id");
1641
1992
  const artifact = await client.call<Record<string, unknown>, CliArtifact>("tasks.contain", {
1642
- parent_id: id, child_id: dependencyId, actor: "user", source: "cli", ...(reason ? { reason } : {}), ...sessionScope,
1993
+ parent_id: id,
1994
+ child_id: dependencyId,
1995
+ actor: "user",
1996
+ source: "cli",
1997
+ ...(reason ? { reason } : {}),
1998
+ ...sessionScope,
1643
1999
  });
1644
2000
  result = artifact;
1645
2001
  human = `Contained: ${dependencyId} → ${artifactLabel(artifact)}`;
@@ -1648,7 +2004,12 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1648
2004
  case "uncontain": {
1649
2005
  if (!id || !dependencyId || positional.length !== 3) throw new Error("tasks uncontain requires a parent id and child id");
1650
2006
  const artifact = await client.call<Record<string, unknown>, CliArtifact>("tasks.uncontain", {
1651
- parent_id: id, child_id: dependencyId, actor: "user", source: "cli", ...(reason ? { reason } : {}), ...sessionScope,
2007
+ parent_id: id,
2008
+ child_id: dependencyId,
2009
+ actor: "user",
2010
+ source: "cli",
2011
+ ...(reason ? { reason } : {}),
2012
+ ...sessionScope,
1652
2013
  });
1653
2014
  result = artifact;
1654
2015
  human = `Removed ${dependencyId} from ${artifactLabel(artifact)}`;
@@ -1656,12 +2017,17 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1656
2017
  }
1657
2018
  case "update": {
1658
2019
  if (!id || dependencyId) throw new Error("tasks update requires exactly one task id");
1659
- if (status !== undefined && status !== "todo") throw new Error("tasks update --status only supports todo for accidental creation recovery");
2020
+ if (status !== undefined && status !== "todo")
2021
+ throw new Error("tasks update --status only supports todo for accidental creation recovery");
1660
2022
  if (Object.keys(updateInput).length === 0) throw new Error("tasks update requires --title, --body, --labels-json, or --status todo");
1661
2023
  if (updateInput.status !== undefined && !reason?.trim()) throw new Error("tasks update --status requires --reason");
1662
2024
  if (reason !== undefined && updateInput.status === undefined) throw new Error("tasks update --reason requires --status todo");
1663
2025
  const artifact = await client.call<Record<string, unknown>, CliArtifact>("tasks.update", {
1664
- id, ...updateInput, ...(reason ? { reason } : {}), actor: "user", source: "cli",
2026
+ id,
2027
+ ...updateInput,
2028
+ ...(reason ? { reason } : {}),
2029
+ actor: "user",
2030
+ source: "cli",
1665
2031
  });
1666
2032
  result = artifact;
1667
2033
  human = `Updated: ${artifactLabel(artifact)}`;
@@ -1669,16 +2035,28 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1669
2035
  }
1670
2036
  case "history": {
1671
2037
  if (!id || dependencyId) throw new Error("tasks history requires exactly one task id");
1672
- const page = await client.call<{ id: string; direction: "desc" }, import("./domain/task-event.ts").TaskHistoryPage>("tasks.history", { id, direction: "desc" });
2038
+ const page = await client.call<{ id: string; direction: "desc" }, import("./domain/task-event.ts").TaskHistoryPage>("tasks.history", {
2039
+ id,
2040
+ direction: "desc",
2041
+ });
1673
2042
  result = page;
1674
- human = page.events.length === 0
1675
- ? `No recorded history for ${id}.`
1676
- : [...page.events].reverse().map((event) => `${event.occurredAt} ${event.type} ${event.fromStatus ?? "∅"} → ${event.toStatus ?? "∅"} · ${event.actor}/${event.source}${event.reason ? ` · ${event.reason}` : ""}`).join("\n");
2043
+ human =
2044
+ page.events.length === 0
2045
+ ? `No recorded history for ${id}.`
2046
+ : [...page.events]
2047
+ .reverse()
2048
+ .map(
2049
+ (event) =>
2050
+ `${event.occurredAt} ${event.type} ${event.fromStatus ?? "∅"} → ${event.toStatus ?? "∅"} · ${event.actor}/${event.source}${event.reason ? ` · ${event.reason}` : ""}`,
2051
+ )
2052
+ .join("\n");
1677
2053
  break;
1678
2054
  }
1679
2055
  case "scope": {
1680
2056
  if (!id) {
1681
- const selection = await client.call<Record<string, string>, import("./domain/task-scope.ts").TaskViewSelection>("tasks.scope", { project_root: projectRoot });
2057
+ const selection = await client.call<Record<string, string>, import("./domain/task-scope.ts").TaskViewSelection>("tasks.scope", {
2058
+ project_root: projectRoot,
2059
+ });
1682
2060
  result = selection;
1683
2061
  human = `Task scope: ${selection.label}`;
1684
2062
  break;
@@ -1709,17 +2087,33 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1709
2087
  }
1710
2088
  case "focus": {
1711
2089
  if (!id || dependencyId) throw new Error("tasks focus requires exactly one task id");
1712
- const active = await client.call<Record<string, unknown>, CliArtifact>("tasks.focus", { id, actor: "user", source: "cli", ...sessionScope, ...sessionSecretField });
2090
+ const active = await client.call<Record<string, unknown>, CliArtifact>("tasks.focus", {
2091
+ id,
2092
+ actor: "user",
2093
+ source: "cli",
2094
+ ...sessionScope,
2095
+ ...sessionSecretField,
2096
+ });
1713
2097
  result = active;
1714
2098
  human = `Active: ${artifactLabel(active)}`;
1715
2099
  break;
1716
2100
  }
1717
2101
  case "graph": {
1718
2102
  if (id) throw new Error("tasks graph accepts no positional arguments");
1719
- const graph = await client.call<Record<string, unknown>, {
1720
- nodes: Array<{ dependencyIds: string[]; childIds: string[] }>;
1721
- rootIds: string[];
1722
- }>("tasks.graph", { limit: TASK_EXECUTION_MAX_NODES + 1, labels, project_root: projectRoot, scope: listScope, root_task_id: rootTaskId, ...sessionScope });
2103
+ const graph = await client.call<
2104
+ Record<string, unknown>,
2105
+ {
2106
+ nodes: Array<{ dependencyIds: string[]; childIds: string[] }>;
2107
+ rootIds: string[];
2108
+ }
2109
+ >("tasks.graph", {
2110
+ limit: TASK_EXECUTION_MAX_NODES + 1,
2111
+ labels,
2112
+ project_root: projectRoot,
2113
+ scope: listScope,
2114
+ root_task_id: rootTaskId,
2115
+ ...sessionScope,
2116
+ });
1723
2117
  result = graph;
1724
2118
  const dependencies = graph.nodes.reduce((count, node) => count + node.dependencyIds.length, 0);
1725
2119
  const children = graph.nodes.reduce((count, node) => count + node.childIds.length, 0);
@@ -1728,19 +2122,29 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1728
2122
  }
1729
2123
  case "plan": {
1730
2124
  if (id) throw new Error("tasks plan accepts no positional arguments");
1731
- const plan = await client.call<Record<string, unknown>, TaskExecutionPlan>("tasks.plan", { project_root: projectRoot, ...sessionScope });
2125
+ const plan = await client.call<Record<string, unknown>, TaskExecutionPlan>("tasks.plan", {
2126
+ project_root: projectRoot,
2127
+ ...sessionScope,
2128
+ });
1732
2129
  result = plan;
1733
2130
  human = planText(plan);
1734
2131
  break;
1735
2132
  }
1736
2133
  case "complete": {
1737
2134
  if (!id || dependencyId) throw new Error("tasks complete requires exactly one task id");
1738
- const completion = await client.call<Record<string, unknown>, CliCompletion>("tasks.complete", { id, actor: "user", source: "cli", ...sessionScope });
2135
+ const completion = await client.call<Record<string, unknown>, CliCompletion>("tasks.complete", {
2136
+ id,
2137
+ actor: "user",
2138
+ source: "cli",
2139
+ ...sessionScope,
2140
+ });
1739
2141
  result = completion;
1740
2142
  const lines = [`${completion.completed ? "Completed" : "Rejected"}: ${artifactLabel(completion.artifact)}`];
1741
2143
  if (completion.focused) lines.push(`Active: ${artifactLabel(completion.focused)}`);
1742
2144
  if (completion.blocked.length > 0) {
1743
- lines.push(`Blocked: ${completion.blocked.map((entry) => `${artifactLabel(entry.artifact)} waits for ${entry.dependencyIds.join(", ")}`).join("; ")}`);
2145
+ lines.push(
2146
+ `Blocked: ${completion.blocked.map((entry) => `${artifactLabel(entry.artifact)} waits for ${entry.dependencyIds.join(", ")}`).join("; ")}`,
2147
+ );
1744
2148
  }
1745
2149
  for (const gate of completion.gates) lines.push(`${gate.passed ? "✓" : "✗"} ${gate.gate.type}: ${gate.gate.target} — ${gate.output}`);
1746
2150
  human = lines.join("\n");
@@ -1748,7 +2152,12 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1748
2152
  }
1749
2153
  case "start": {
1750
2154
  if (!id || dependencyId) throw new Error("tasks start requires exactly one task id");
1751
- const artifact = await client.call<Record<string, unknown>, CliArtifact>("tasks.start", { id, actor: "user", source: "cli", ...sessionScope });
2155
+ const artifact = await client.call<Record<string, unknown>, CliArtifact>("tasks.start", {
2156
+ id,
2157
+ actor: "user",
2158
+ source: "cli",
2159
+ ...sessionScope,
2160
+ });
1752
2161
  result = artifact;
1753
2162
  human = `Started: ${artifactLabel(artifact)}`;
1754
2163
  break;
@@ -1759,14 +2168,24 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1759
2168
  case "cancel": {
1760
2169
  if (!id || dependencyId) throw new Error(`tasks ${action} requires exactly one task id`);
1761
2170
  const operation = `tasks.${action}` as "tasks.submit" | "tasks.reject" | "tasks.retry" | "tasks.cancel";
1762
- const artifact = await client.call<Record<string, unknown>, CliArtifact>(operation, { id, actor: "user", source: "cli", ...sessionScope });
2171
+ const artifact = await client.call<Record<string, unknown>, CliArtifact>(operation, {
2172
+ id,
2173
+ actor: "user",
2174
+ source: "cli",
2175
+ ...sessionScope,
2176
+ });
1763
2177
  result = artifact;
1764
2178
  human = `${action[0]!.toUpperCase()}${action.slice(1)}: ${artifactLabel(artifact)}`;
1765
2179
  break;
1766
2180
  }
1767
2181
  case "cancel-subtree": {
1768
2182
  if (!id || dependencyId) throw new Error("tasks cancel-subtree requires exactly one task id");
1769
- const outcome = await client.call<Record<string, unknown>, { canceled: string[]; skipped: string[] }>("tasks.cancel_subtree", { id, actor: "user", source: "cli", ...sessionScope });
2183
+ const outcome = await client.call<Record<string, unknown>, { canceled: string[]; skipped: string[] }>("tasks.cancel_subtree", {
2184
+ id,
2185
+ actor: "user",
2186
+ source: "cli",
2187
+ ...sessionScope,
2188
+ });
1770
2189
  result = outcome;
1771
2190
  human = `Canceled ${outcome.canceled.length} task(s)${outcome.skipped.length > 0 ? `, skipped ${outcome.skipped.length} already-terminal` : ""}.`;
1772
2191
  break;
@@ -1774,7 +2193,12 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1774
2193
  case "depend": {
1775
2194
  if (!id || !dependencyId || positional.length !== 3) throw new Error("tasks depend requires a task id and prerequisite id");
1776
2195
  const artifact = await client.call<Record<string, unknown>, CliArtifact>("tasks.depend", {
1777
- id, dependency_id: dependencyId, actor: "user", source: "cli", ...(reason ? { reason } : {}), ...sessionScope,
2196
+ id,
2197
+ dependency_id: dependencyId,
2198
+ actor: "user",
2199
+ source: "cli",
2200
+ ...(reason ? { reason } : {}),
2201
+ ...sessionScope,
1778
2202
  });
1779
2203
  result = artifact;
1780
2204
  human = `Dependency added: ${artifactLabel(artifact)} waits for ${dependencyId}`;
@@ -1783,29 +2207,34 @@ export async function runTaskCli(args: string[], client: TaskCliClient, projectR
1783
2207
  case "undepend": {
1784
2208
  if (!id || !dependencyId || positional.length !== 3) throw new Error("tasks undepend requires a task id and prerequisite id");
1785
2209
  const artifact = await client.call<Record<string, unknown>, CliArtifact>("tasks.undepend", {
1786
- id, dependency_id: dependencyId, actor: "user", source: "cli", ...(reason ? { reason } : {}), ...sessionScope,
2210
+ id,
2211
+ dependency_id: dependencyId,
2212
+ actor: "user",
2213
+ source: "cli",
2214
+ ...(reason ? { reason } : {}),
2215
+ ...sessionScope,
1787
2216
  });
1788
2217
  result = artifact;
1789
2218
  human = `Dependency removed: ${artifactLabel(artifact)} no longer waits for ${dependencyId}`;
1790
2219
  break;
1791
2220
  }
1792
2221
  default:
1793
- throw new Error("tasks action must be create, list, show, active, focused, focus, pause, unpause, clear-focus, update, graph, plan, context, history, scope, assign-project, complete, start, submit, reject, retry, cancel, cancel-subtree, depend, undepend, contain, uncontain, run-gates, set-checklist, or set-gates");
2222
+ throw new Error(
2223
+ "tasks action must be create, list, show, active, focused, focus, pause, unpause, clear-focus, update, graph, plan, context, history, scope, assign-project, complete, start, submit, reject, retry, cancel, cancel-subtree, depend, undepend, contain, uncontain, run-gates, set-checklist, or set-gates",
2224
+ );
1794
2225
  }
1795
2226
  return json ? JSON.stringify(result) : human;
1796
2227
  }
1797
2228
 
1798
2229
  export async function main(args: string[] = process.argv.slice(2)): Promise<void> {
1799
2230
  const [command, action] = args;
1800
- if (command === "serve") { serveMain(); return; }
1801
- if (command === "tasks") {
1802
- const client = await connectPapyrusClient();
1803
- console.log(await runTaskCli(args.slice(1), client));
2231
+ if (command === "serve") {
2232
+ serveMain();
1804
2233
  return;
1805
2234
  }
1806
- if (command === "skills") {
2235
+ if (command === "tasks") {
1807
2236
  const client = await connectPapyrusClient();
1808
- console.log(await runSkillCli(args.slice(1), client));
2237
+ console.log(await runTaskCli(args.slice(1), client));
1809
2238
  return;
1810
2239
  }
1811
2240
  if (command === "playbooks") {
@@ -1874,12 +2303,23 @@ export async function main(args: string[] = process.argv.slice(2)): Promise<void
1874
2303
  }
1875
2304
  if (command !== "service") usage();
1876
2305
  switch (action) {
1877
- case "install": installService(); break;
1878
- case "start": systemctl("start", DAEMON_UNIT_NAME); break;
1879
- case "stop": systemctl("stop", DAEMON_UNIT_NAME); break;
1880
- case "restart": systemctl("restart", DAEMON_UNIT_NAME); break;
1881
- case "status": systemctl("status", DAEMON_UNIT_NAME); break;
1882
- default: usage();
2306
+ case "install":
2307
+ installService();
2308
+ break;
2309
+ case "start":
2310
+ systemctl("start", DAEMON_UNIT_NAME);
2311
+ break;
2312
+ case "stop":
2313
+ systemctl("stop", DAEMON_UNIT_NAME);
2314
+ break;
2315
+ case "restart":
2316
+ systemctl("restart", DAEMON_UNIT_NAME);
2317
+ break;
2318
+ case "status":
2319
+ systemctl("status", DAEMON_UNIT_NAME);
2320
+ break;
2321
+ default:
2322
+ usage();
1883
2323
  }
1884
2324
  }
1885
2325