syntaur 0.56.0 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dashboard/dist/assets/_basePickBy-BSxL8bBc.js +1 -0
  3. package/dashboard/dist/assets/{_baseUniq-C6ARYdEJ.js → _baseUniq-1f9ohhNp.js} +1 -1
  4. package/dashboard/dist/assets/arc-DlPK9j9g.js +1 -0
  5. package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-eoW8_dbN.js → architectureDiagram-2XIMDMQ5-CcV5llaa.js} +3 -3
  6. package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ--XxaNbBK.js → blockDiagram-WCTKOSBZ-DGkYzINJ.js} +1 -1
  7. package/dashboard/dist/assets/{c4Diagram-IC4MRINW-D2B-NJq6.js → c4Diagram-IC4MRINW-CI4Z5c6N.js} +1 -1
  8. package/dashboard/dist/assets/channel-Qr2eMXkR.js +1 -0
  9. package/dashboard/dist/assets/{chunk-4BX2VUAB-CY34rkdT.js → chunk-4BX2VUAB-C8KasqJp.js} +1 -1
  10. package/dashboard/dist/assets/chunk-55IACEB6-CgAEilyk.js +1 -0
  11. package/dashboard/dist/assets/{chunk-FMBD7UC4-D00VtmIk.js → chunk-FMBD7UC4-DsNiby6p.js} +1 -1
  12. package/dashboard/dist/assets/chunk-JSJVCQXG-4eCDFRIx.js +1 -0
  13. package/dashboard/dist/assets/chunk-KX2RTZJC-DHia_zig.js +1 -0
  14. package/dashboard/dist/assets/{chunk-NQ4KR5QH-DnYhUmZY.js → chunk-NQ4KR5QH-V-IiQwer.js} +2 -2
  15. package/dashboard/dist/assets/{chunk-QZHKN3VN-CqCcFoFI.js → chunk-QZHKN3VN-BXTS8E6J.js} +1 -1
  16. package/dashboard/dist/assets/{chunk-WL4C6EOR-Bwx5h46l.js → chunk-WL4C6EOR-BuLlUWn0.js} +4 -4
  17. package/dashboard/dist/assets/classDiagram-VBA2DB6C-DHkm1twH.js +1 -0
  18. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-DHkm1twH.js +1 -0
  19. package/dashboard/dist/assets/clone-ChZeNkMI.js +1 -0
  20. package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-QUZroYuH.js → cose-bilkent-S5V4N54A-B1M1ENTo.js} +1 -1
  21. package/dashboard/dist/assets/{dagre-KLK3FWXG-D8hYiZ7i.js → dagre-KLK3FWXG-3pyNBzWY.js} +2 -2
  22. package/dashboard/dist/assets/diagram-E7M64L7V-B5-5pbKh.js +24 -0
  23. package/dashboard/dist/assets/diagram-IFDJBPK2-CTx5NFFi.js +43 -0
  24. package/dashboard/dist/assets/{diagram-P4PSJMXO-GcqkAPB2.js → diagram-P4PSJMXO-B5KRvZqb.js} +2 -2
  25. package/dashboard/dist/assets/{erDiagram-INFDFZHY-DQnXbhhn.js → erDiagram-INFDFZHY-BhVWsRrc.js} +3 -3
  26. package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-DSmXtiZK.js → flowDiagram-PKNHOUZH-CuoB0thO.js} +1 -1
  27. package/dashboard/dist/assets/ganttDiagram-A5KZAMGK-yNNalQY9.js +292 -0
  28. package/dashboard/dist/assets/gitGraphDiagram-K3NZZRJ6-DfEhHzOH.js +65 -0
  29. package/dashboard/dist/assets/{graph-Czw8ZNXY.js → graph-NaP7lLuY.js} +1 -1
  30. package/dashboard/dist/assets/index-BG_Ed2-k.js +644 -0
  31. package/dashboard/dist/assets/index-BeL-DOH-.css +1 -0
  32. package/dashboard/dist/assets/infoDiagram-LFFYTUFH-CDAMAxDE.js +2 -0
  33. package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-39XaFufR.js → ishikawaDiagram-PHBUUO56-DMRl5xeW.js} +5 -5
  34. package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-DCMzi84v.js → journeyDiagram-4ABVD52K-DtuQQjW_.js} +1 -1
  35. package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-RSNF4GmM.js → kanban-definition-K7BYSVSG-DYGt44vU.js} +7 -7
  36. package/dashboard/dist/assets/{layout-AOYFASCy.js → layout-B9sS0Gu9.js} +1 -1
  37. package/dashboard/dist/assets/linear-DQcNFWB2.js +1 -0
  38. package/dashboard/dist/assets/mermaid.core-CzhsZ1QZ.js +255 -0
  39. package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-Ck4L1K7y.js → mindmap-definition-YRQLILUH-CL8mKZYJ.js} +3 -3
  40. package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-BHEH_9Yh.js → pieDiagram-SKSYHLDU-BULUJrhI.js} +3 -3
  41. package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-DScQHhsU.js → quadrantDiagram-337W2JSQ-CWQC3mf-.js} +4 -4
  42. package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-DLmxwUdt.js → requirementDiagram-Z7DCOOCP-CYadvMn7.js} +3 -3
  43. package/dashboard/dist/assets/sankeyDiagram-WA2Y5GQK-e2AhGJiF.js +10 -0
  44. package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-woFPqQIY.js → sequenceDiagram-2WXFIKYE-CeWKKFdg.js} +3 -3
  45. package/dashboard/dist/assets/stateDiagram-RAJIS63D-BvN-wCps.js +1 -0
  46. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-BV7kxiIj.js +1 -0
  47. package/dashboard/dist/assets/{timeline-definition-YZTLITO2-CGVOTXAD.js → timeline-definition-YZTLITO2-DOLRfgbL.js} +4 -4
  48. package/dashboard/dist/assets/{treemap-KZPCXAKY-CGmqtjaL.js → treemap-KZPCXAKY-VqhsqWzF.js} +1 -1
  49. package/dashboard/dist/assets/vennDiagram-LZ73GAT5-Dt-knC6b.js +34 -0
  50. package/dashboard/dist/assets/{xychartDiagram-JWTSCODW--uOFgr6-.js → xychartDiagram-JWTSCODW-A3enOtVP.js} +3 -3
  51. package/dashboard/dist/index.html +2 -2
  52. package/dist/dashboard/server.js +208 -131
  53. package/dist/dashboard/server.js.map +1 -1
  54. package/dist/index.js +584 -591
  55. package/dist/index.js.map +1 -1
  56. package/dist/launch/index.js.map +1 -1
  57. package/package.json +1 -1
  58. package/platforms/claude-code/.claude-plugin/plugin.json +1 -1
  59. package/platforms/codex/.codex-plugin/plugin.json +1 -1
  60. package/platforms/hermes/plugins/syntaur/__pycache__/__init__.cpython-312.pyc +0 -0
  61. package/platforms/hermes/plugins/syntaur/__pycache__/boundary.cpython-312.pyc +0 -0
  62. package/dashboard/dist/assets/_basePickBy-BboQhTYn.js +0 -1
  63. package/dashboard/dist/assets/arc-D76wUYJ-.js +0 -1
  64. package/dashboard/dist/assets/channel-BPykMjmL.js +0 -1
  65. package/dashboard/dist/assets/chunk-55IACEB6-CLXE1Ejn.js +0 -1
  66. package/dashboard/dist/assets/chunk-JSJVCQXG-_EITJTaI.js +0 -1
  67. package/dashboard/dist/assets/chunk-KX2RTZJC-DrqZI_kX.js +0 -1
  68. package/dashboard/dist/assets/classDiagram-VBA2DB6C-z8L393gl.js +0 -1
  69. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-z8L393gl.js +0 -1
  70. package/dashboard/dist/assets/clone--rMJOGpf.js +0 -1
  71. package/dashboard/dist/assets/diagram-E7M64L7V-Cl_n980P.js +0 -24
  72. package/dashboard/dist/assets/diagram-IFDJBPK2-01DdmzmS.js +0 -43
  73. package/dashboard/dist/assets/ganttDiagram-A5KZAMGK-BLyt9LDp.js +0 -292
  74. package/dashboard/dist/assets/gitGraphDiagram-K3NZZRJ6-FWlr0I_j.js +0 -65
  75. package/dashboard/dist/assets/index-EntCfH3I.css +0 -1
  76. package/dashboard/dist/assets/index-zHNBGpC7.js +0 -602
  77. package/dashboard/dist/assets/infoDiagram-LFFYTUFH-CygGWMzd.js +0 -2
  78. package/dashboard/dist/assets/linear-Bf-cRRMe.js +0 -1
  79. package/dashboard/dist/assets/mermaid.core-7QJzPWTu.js +0 -255
  80. package/dashboard/dist/assets/sankeyDiagram-WA2Y5GQK-B6OZtKmg.js +0 -10
  81. package/dashboard/dist/assets/stateDiagram-RAJIS63D-Db9HV_HG.js +0 -1
  82. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-DZsj07hg.js +0 -1
  83. package/dashboard/dist/assets/vennDiagram-LZ73GAT5-DAuyqqKn.js +0 -34
package/dist/index.js CHANGED
@@ -1483,6 +1483,7 @@ __export(parser_exports, {
1483
1483
  readLog: () => readLog,
1484
1484
  serializeChecklist: () => serializeChecklist,
1485
1485
  serializeChecklistItem: () => serializeChecklistItem,
1486
+ serializeLog: () => serializeLog,
1486
1487
  serializeLogEntry: () => serializeLogEntry,
1487
1488
  serializeMetaToken: () => serializeMetaToken,
1488
1489
  writeChecklist: () => writeChecklist
@@ -1610,27 +1611,60 @@ function statusToMarker(item) {
1610
1611
  return item.session ? `>:${sanitizeSession(item.session)}` : ">";
1611
1612
  }
1612
1613
  }
1614
+ function escapeDescription(description) {
1615
+ return description.replace(/\\/g, "\\\\").replace(/#/g, "\\#").replace(/\[/g, "\\[");
1616
+ }
1617
+ function unescapeDescription(escaped) {
1618
+ let out = "";
1619
+ for (let i = 0; i < escaped.length; i++) {
1620
+ if (escaped[i] === "\\" && i + 1 < escaped.length) {
1621
+ const next = escaped[i + 1];
1622
+ if (next === "\\" || next === "#" || next === "[") {
1623
+ out += next;
1624
+ i++;
1625
+ continue;
1626
+ }
1627
+ }
1628
+ out += escaped[i];
1629
+ }
1630
+ return out;
1631
+ }
1632
+ function findStructuralCut(rest) {
1633
+ for (let i = 0; i < rest.length; i++) {
1634
+ const ch = rest[i];
1635
+ if (ch !== "#" && ch !== "[") continue;
1636
+ let backslashes = 0;
1637
+ let j = i - 1;
1638
+ while (j >= 0 && rest[j] === "\\") {
1639
+ backslashes++;
1640
+ j--;
1641
+ }
1642
+ if (backslashes % 2 === 1) continue;
1643
+ if (ch === "#") {
1644
+ if (/[a-zA-Z0-9_-]/.test(rest[i + 1] ?? "")) return i;
1645
+ } else {
1646
+ return i;
1647
+ }
1648
+ }
1649
+ return rest.length;
1650
+ }
1613
1651
  function parseChecklistItem(line) {
1614
1652
  const match = line.match(ITEM_REGEX);
1615
1653
  if (!match) return null;
1616
1654
  const marker = match[1];
1617
1655
  const rest = match[2];
1618
1656
  const { status, session } = parseStatus2(marker);
1619
- const idMatch = rest.match(ID_REGEX);
1657
+ const cut = findStructuralCut(rest);
1658
+ const description = unescapeDescription(rest.slice(0, cut).trim());
1659
+ const tail = rest.slice(cut);
1660
+ const idMatch = tail.match(ID_REGEX);
1620
1661
  const id = idMatch ? idMatch[1] : "";
1621
1662
  const tags = [];
1622
1663
  let tagMatch;
1623
1664
  const tagRegex = new RegExp(TAG_REGEX.source, "g");
1624
- while ((tagMatch = tagRegex.exec(rest)) !== null) {
1665
+ while ((tagMatch = tagRegex.exec(tail)) !== null) {
1625
1666
  tags.push(tagMatch[1]);
1626
1667
  }
1627
- let description = rest;
1628
- const firstTagIdx = rest.search(/#[a-zA-Z0-9_-]/);
1629
- const firstIdIdx = rest.search(/\[t:[a-f0-9]{4}\]/);
1630
- const cutPoints = [firstTagIdx, firstIdIdx].filter((i) => i >= 0);
1631
- if (cutPoints.length > 0) {
1632
- description = rest.slice(0, Math.min(...cutPoints)).trim();
1633
- }
1634
1668
  const meta = parseMetaToken(line);
1635
1669
  return {
1636
1670
  id,
@@ -1651,7 +1685,7 @@ function parseChecklistItem(line) {
1651
1685
  function serializeChecklistItem(item) {
1652
1686
  const marker = statusToMarker(item);
1653
1687
  const tagStr = item.tags.map((t) => `#${t}`).join(" ");
1654
- const parts = [`- [${marker}] ${item.description}`];
1688
+ const parts = [`- [${marker}] ${escapeDescription(item.description)}`];
1655
1689
  if (tagStr) parts.push(tagStr);
1656
1690
  parts.push(`[t:${item.id}]`);
1657
1691
  const meta = serializeMetaToken(item);
@@ -1750,6 +1784,18 @@ function serializeLogEntry(entry) {
1750
1784
  if (entry.status) lines.push(`**Status:** ${entry.status}`);
1751
1785
  return lines.join("\n");
1752
1786
  }
1787
+ function serializeLog(log) {
1788
+ const header = `---
1789
+ workspace: ${log.workspace}
1790
+ ---
1791
+
1792
+ # Todo Log
1793
+ `;
1794
+ if (log.entries.length === 0) {
1795
+ return header;
1796
+ }
1797
+ return header + "\n" + log.entries.map(serializeLogEntry).join("\n\n") + "\n";
1798
+ }
1753
1799
  function checklistPath(todosDir2, workspace) {
1754
1800
  return resolve3(todosDir2, `${workspace}.md`);
1755
1801
  }
@@ -8435,7 +8481,8 @@ __export(git_worktree_exports, {
8435
8481
  listBranches: () => listBranches,
8436
8482
  listWorktrees: () => listWorktrees,
8437
8483
  recreateWorktree: () => recreateWorktree,
8438
- removeWorktree: () => removeWorktree
8484
+ removeWorktree: () => removeWorktree,
8485
+ resolveBranchSha: () => resolveBranchSha
8439
8486
  });
8440
8487
  import { spawn } from "child_process";
8441
8488
  import { readFile as readFile11 } from "fs/promises";
@@ -8550,6 +8597,18 @@ async function captureHeadSha(dir) {
8550
8597
  const sha = result.stdout.trim();
8551
8598
  return sha.length > 0 ? sha : null;
8552
8599
  }
8600
+ async function resolveBranchSha(repository, branch) {
8601
+ const result = await run("git", [
8602
+ "-C",
8603
+ repository,
8604
+ "rev-parse",
8605
+ "--short",
8606
+ branch
8607
+ ]);
8608
+ if (result.code !== 0) return null;
8609
+ const sha = result.stdout.trim();
8610
+ return sha.length > 0 ? sha : null;
8611
+ }
8553
8612
  async function branchCheckedOutAt(repository, branch) {
8554
8613
  const result = await run("git", ["-C", repository, "worktree", "list", "--porcelain"]);
8555
8614
  if (result.code !== 0) return null;
@@ -12688,7 +12747,7 @@ async function scanSessions(opts = {}, deps = {}) {
12688
12747
  const now = deps.now ?? (() => Date.now());
12689
12748
  const statMtimeMs = deps.statMtimeMs ?? defaultStatMtimeMs2;
12690
12749
  const openFiles = deps.openFiles ?? defaultOpenFiles;
12691
- const isPidAlive = deps.isPidAlive ?? defaultIsPidAlive;
12750
+ const isPidAlive2 = deps.isPidAlive ?? defaultIsPidAlive;
12692
12751
  const pidStartedAt = deps.pidStartedAt ?? defaultPidStartedAt;
12693
12752
  const targets = (deps.targets ?? AGENT_TARGETS).filter((t) => t.sessions !== void 0);
12694
12753
  const scanStartMs = now();
@@ -12767,7 +12826,7 @@ async function scanSessions(opts = {}, deps = {}) {
12767
12826
  const sweepCandidates = [];
12768
12827
  for (const row of activeRows) {
12769
12828
  if (row.pid !== null) {
12770
- const alive = isPidAlive(row.pid) && (!row.pid_started_at || (pidStartedAt(row.pid) ?? row.pid_started_at) === row.pid_started_at);
12829
+ const alive = isPidAlive2(row.pid) && (!row.pid_started_at || (pidStartedAt(row.pid) ?? row.pid_started_at) === row.pid_started_at);
12771
12830
  if (alive) continue;
12772
12831
  }
12773
12832
  if (row.transcript_path) {
@@ -16465,7 +16524,7 @@ ${nextBody}${nextBody.endsWith("\n") ? "" : "\n"}`;
16465
16524
  };
16466
16525
  const entry = formatCommentEntry(comment);
16467
16526
  let next = setTopLevelField(currentContent, "entryCount", String(currentCount + 1));
16468
- next = setTopLevelField(next, "updated", `"${timestamp}"`);
16527
+ next = setTopLevelField(next, "updated", timestamp);
16469
16528
  if (next.includes("No comments yet.")) {
16470
16529
  next = next.replace("No comments yet.", entry.trimEnd());
16471
16530
  } else {
@@ -16525,7 +16584,7 @@ ${entry}`;
16525
16584
  res.status(500).json({ error: "Failed to update resolved flag" });
16526
16585
  return;
16527
16586
  }
16528
- const withUpdated = setTopLevelField(next, "updated", `"${nowTimestamp()}"`);
16587
+ const withUpdated = setTopLevelField(next, "updated", nowTimestamp());
16529
16588
  await writeFileForce(commentsPath, withUpdated);
16530
16589
  const assignment = await getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug);
16531
16590
  res.json({ assignment });
@@ -17994,7 +18053,7 @@ async function appendCommentTo(assignmentDir, assignmentRef, req2, res, reloadDe
17994
18053
  };
17995
18054
  const entry = formatCommentEntry(comment);
17996
18055
  let next = setTopLevelField(currentContent, "entryCount", String(currentCount + 1));
17997
- next = setTopLevelField(next, "updated", `"${timestamp}"`);
18056
+ next = setTopLevelField(next, "updated", timestamp);
17998
18057
  if (next.includes("No comments yet.")) {
17999
18058
  next = next.replace("No comments yet.", entry.trimEnd());
18000
18059
  } else {
@@ -18037,7 +18096,7 @@ async function toggleCommentResolvedAt(assignmentDir, commentId, req2, res, relo
18037
18096
  res.status(500).json({ error: "Failed to update resolved flag" });
18038
18097
  return;
18039
18098
  }
18040
- const withUpdated = setTopLevelField(next, "updated", `"${nowTimestamp()}"`);
18099
+ const withUpdated = setTopLevelField(next, "updated", nowTimestamp());
18041
18100
  await writeFileForce(commentsPath, withUpdated);
18042
18101
  const assignment = await reloadDetail();
18043
18102
  res.json({ assignment });
@@ -21505,6 +21564,119 @@ async function readEvents(jobId) {
21505
21564
  import { createHash as createHash3 } from "crypto";
21506
21565
  import { open as open4, readFile as readFile22, stat as stat3, unlink as unlink6 } from "fs/promises";
21507
21566
  import { resolve as resolve32 } from "path";
21567
+
21568
+ // src/schedules/triggers.ts
21569
+ import { Cron } from "croner";
21570
+
21571
+ // src/schedules/reset-window.ts
21572
+ var FIVE_HOURS_MS = 5 * 60 * 60 * 1e3;
21573
+ var ONE_WEEK_MS = 7 * 24 * 60 * 60 * 1e3;
21574
+ function windowLengthMs(anchor) {
21575
+ return anchor.windowKind === "weekly" ? ONE_WEEK_MS : FIVE_HOURS_MS;
21576
+ }
21577
+ function predictReset(_provider, anchor) {
21578
+ const start = Date.parse(anchor.windowStartIso);
21579
+ if (Number.isNaN(start)) {
21580
+ throw new Error(`Invalid reset anchor windowStartIso: ${anchor.windowStartIso}`);
21581
+ }
21582
+ return new Date(start + windowLengthMs(anchor)).toISOString().replace(/\.\d{3}Z$/, "Z");
21583
+ }
21584
+ function verifyReset(provider, anchor, now) {
21585
+ const reset = predictReset(provider, anchor);
21586
+ if (now.getTime() >= Date.parse(reset)) return { eligible: true };
21587
+ return { eligible: false, rescheduleToIso: reset };
21588
+ }
21589
+
21590
+ // src/schedules/triggers.ts
21591
+ function iso(d) {
21592
+ return d.toISOString().replace(/\.\d{3}Z$/, "Z");
21593
+ }
21594
+ function isRecurring(trigger) {
21595
+ return trigger.kind === "cron";
21596
+ }
21597
+ var notDue = { due: false };
21598
+ function evaluateTrigger(job, ctx) {
21599
+ const consumed = new Set(job.attempt.consumedEdges);
21600
+ const result = evaluateKind(job.trigger, job, ctx);
21601
+ if (result.due && result.dedupeKey && consumed.has(result.dedupeKey)) {
21602
+ return { ...result, due: false };
21603
+ }
21604
+ return result;
21605
+ }
21606
+ function evaluateKind(trigger, job, ctx) {
21607
+ const createdAtMs = Date.parse(job.createdAt);
21608
+ switch (trigger.kind) {
21609
+ case "at": {
21610
+ const at = Date.parse(trigger.at);
21611
+ const due = !Number.isNaN(at) && ctx.now.getTime() >= at;
21612
+ return { due, dedupeKey: `at:${trigger.at}`, nextFireIso: trigger.at };
21613
+ }
21614
+ case "in": {
21615
+ const anchor = Date.parse(trigger.anchorIso);
21616
+ const fireAt = anchor + trigger.durationMs;
21617
+ const due = !Number.isNaN(anchor) && ctx.now.getTime() >= fireAt;
21618
+ const fireIso = Number.isNaN(anchor) ? null : iso(new Date(fireAt));
21619
+ return { due, dedupeKey: `in:${fireAt}`, nextFireIso: fireIso };
21620
+ }
21621
+ case "cron":
21622
+ return evaluateCron(trigger, ctx.now, createdAtMs);
21623
+ case "after-reset":
21624
+ return evaluateAfterReset(trigger, ctx.now);
21625
+ case "when-status":
21626
+ return evaluateWhenStatus(trigger, job, ctx, createdAtMs);
21627
+ case "when-plan-lands":
21628
+ return evaluateWhenPlanLands(ctx, createdAtMs);
21629
+ }
21630
+ }
21631
+ function evaluateCron(trigger, now, createdAtMs) {
21632
+ let cron;
21633
+ try {
21634
+ cron = new Cron(trigger.expr, trigger.tz ? { timezone: trigger.tz } : {});
21635
+ } catch {
21636
+ return notDue;
21637
+ }
21638
+ const prevs = cron.previousRuns(1, new Date(now.getTime() + 1e3));
21639
+ let prev = prevs.length > 0 ? prevs[0] : null;
21640
+ if (prev && prev.getTime() > now.getTime()) prev = null;
21641
+ if (prev && !Number.isNaN(createdAtMs) && prev.getTime() < createdAtMs) prev = null;
21642
+ const next = cron.nextRun(now);
21643
+ if (!prev) return { due: false, nextFireIso: next ? iso(next) : null };
21644
+ return { due: true, dedupeKey: `cron:${iso(prev)}`, nextFireIso: next ? iso(next) : null };
21645
+ }
21646
+ function evaluateAfterReset(trigger, now) {
21647
+ const v = verifyReset(trigger.provider, trigger.anchor, now);
21648
+ const predicted = predictReset(trigger.provider, trigger.anchor);
21649
+ if (v.eligible) {
21650
+ return { due: true, dedupeKey: `after-reset:${trigger.anchor.windowStartIso}`, nextFireIso: predicted };
21651
+ }
21652
+ return { due: false, nextFireIso: predicted, rescheduleToIso: v.rescheduleToIso };
21653
+ }
21654
+ function evaluateWhenStatus(trigger, job, ctx, createdAtMs) {
21655
+ const fm = ctx.assignment;
21656
+ if (!fm) return notDue;
21657
+ const history = fm.statusHistory ?? [];
21658
+ for (let i = job.attempt.cursor; i < history.length; i++) {
21659
+ const atMs = Date.parse(history[i].at);
21660
+ const afterCreation = Number.isNaN(createdAtMs) || Number.isNaN(atMs) || atMs >= createdAtMs;
21661
+ if (history[i].to === trigger.status && afterCreation) {
21662
+ return { due: true, dedupeKey: `status:${i}:${history[i].at}`, nextCursor: i + 1 };
21663
+ }
21664
+ }
21665
+ return { due: false, nextCursor: history.length };
21666
+ }
21667
+ function evaluateWhenPlanLands(ctx, createdAtMs) {
21668
+ const fm = ctx.assignment;
21669
+ if (!fm) return notDue;
21670
+ const approval = fm.planApproval;
21671
+ if (!approval) return notDue;
21672
+ const approvedMs = Date.parse(approval.at);
21673
+ if (!Number.isNaN(createdAtMs) && !Number.isNaN(approvedMs) && approvedMs < createdAtMs) {
21674
+ return notDue;
21675
+ }
21676
+ return { due: true, dedupeKey: `plan:${approval.file}:${approval.digest}` };
21677
+ }
21678
+
21679
+ // src/schedules/attempt.ts
21508
21680
  init_timestamp();
21509
21681
 
21510
21682
  // src/schedules/types.ts
@@ -21703,6 +21875,15 @@ async function markLaunchFailed(job, reason) {
21703
21875
  await appendEvent(job.id, "launch_failed", { reason });
21704
21876
  return written;
21705
21877
  }
21878
+ async function markCompleted(job) {
21879
+ const next = {
21880
+ ...job,
21881
+ attempt: { ...job.attempt, state: "completed", claim: null }
21882
+ };
21883
+ const written = await writeJob(next);
21884
+ await appendEvent(job.id, "completed", { reason: "one-shot session ended" });
21885
+ return written;
21886
+ }
21706
21887
  var TransitionError = class extends Error {
21707
21888
  constructor(message) {
21708
21889
  super(message);
@@ -21790,10 +21971,11 @@ function retryJob(id) {
21790
21971
  () => ({ claim: null, lastError: null })
21791
21972
  );
21792
21973
  }
21974
+ var ONE_SHOT_COMPLETE_GRACE_MS = 6e4;
21793
21975
  async function reapStale(jobs, deps) {
21794
21976
  const now = deps.now();
21795
21977
  const isLive = deps.isSessionLive ?? (() => true);
21796
- const out = { reaped: [], stuck: [] };
21978
+ const out = { reaped: [], stuck: [], completed: [] };
21797
21979
  for (const job of jobs) {
21798
21980
  const a = job.attempt;
21799
21981
  if ((a.state === "claimed" || a.state === "launching") && a.claim && now.getTime() > a.claim.expiresAt) {
@@ -21802,7 +21984,17 @@ async function reapStale(jobs, deps) {
21802
21984
  out.reaped.push(job.id);
21803
21985
  continue;
21804
21986
  }
21805
- if (a.state === "running" && job.limits.maxRuntimeMs && a.runningSince) {
21987
+ if (a.state !== "running") continue;
21988
+ if (!isRecurring(job.trigger) && a.sessionId && a.runningSince) {
21989
+ const graceMs = job.limits.maxRuntimeMs ?? ONE_SHOT_COMPLETE_GRACE_MS;
21990
+ const pastGrace = now.getTime() - Date.parse(a.runningSince) > graceMs;
21991
+ if (pastGrace && !isLive(a.sessionId, a.launchPid)) {
21992
+ await markCompleted(job);
21993
+ out.completed.push(job.id);
21994
+ continue;
21995
+ }
21996
+ }
21997
+ if (job.limits.maxRuntimeMs && a.runningSince) {
21806
21998
  const overrun = now.getTime() - Date.parse(a.runningSince) > job.limits.maxRuntimeMs;
21807
21999
  if (overrun && !isLive(a.sessionId, a.launchPid) && a.lastError !== "stuck:max-runtime") {
21808
22000
  await writeJob({ ...job, attempt: { ...a, lastError: "stuck:max-runtime" } });
@@ -21986,117 +22178,6 @@ init_frontmatter();
21986
22178
  import { readFile as readFile23 } from "fs/promises";
21987
22179
  import { resolve as resolve34 } from "path";
21988
22180
 
21989
- // src/schedules/triggers.ts
21990
- import { Cron } from "croner";
21991
-
21992
- // src/schedules/reset-window.ts
21993
- var FIVE_HOURS_MS = 5 * 60 * 60 * 1e3;
21994
- var ONE_WEEK_MS = 7 * 24 * 60 * 60 * 1e3;
21995
- function windowLengthMs(anchor) {
21996
- return anchor.windowKind === "weekly" ? ONE_WEEK_MS : FIVE_HOURS_MS;
21997
- }
21998
- function predictReset(_provider, anchor) {
21999
- const start = Date.parse(anchor.windowStartIso);
22000
- if (Number.isNaN(start)) {
22001
- throw new Error(`Invalid reset anchor windowStartIso: ${anchor.windowStartIso}`);
22002
- }
22003
- return new Date(start + windowLengthMs(anchor)).toISOString().replace(/\.\d{3}Z$/, "Z");
22004
- }
22005
- function verifyReset(provider, anchor, now) {
22006
- const reset = predictReset(provider, anchor);
22007
- if (now.getTime() >= Date.parse(reset)) return { eligible: true };
22008
- return { eligible: false, rescheduleToIso: reset };
22009
- }
22010
-
22011
- // src/schedules/triggers.ts
22012
- function iso(d) {
22013
- return d.toISOString().replace(/\.\d{3}Z$/, "Z");
22014
- }
22015
- function isRecurring(trigger) {
22016
- return trigger.kind === "cron";
22017
- }
22018
- var notDue = { due: false };
22019
- function evaluateTrigger(job, ctx) {
22020
- const consumed = new Set(job.attempt.consumedEdges);
22021
- const result = evaluateKind(job.trigger, job, ctx);
22022
- if (result.due && result.dedupeKey && consumed.has(result.dedupeKey)) {
22023
- return { ...result, due: false };
22024
- }
22025
- return result;
22026
- }
22027
- function evaluateKind(trigger, job, ctx) {
22028
- const createdAtMs = Date.parse(job.createdAt);
22029
- switch (trigger.kind) {
22030
- case "at": {
22031
- const at = Date.parse(trigger.at);
22032
- const due = !Number.isNaN(at) && ctx.now.getTime() >= at;
22033
- return { due, dedupeKey: `at:${trigger.at}`, nextFireIso: trigger.at };
22034
- }
22035
- case "in": {
22036
- const anchor = Date.parse(trigger.anchorIso);
22037
- const fireAt = anchor + trigger.durationMs;
22038
- const due = !Number.isNaN(anchor) && ctx.now.getTime() >= fireAt;
22039
- const fireIso = Number.isNaN(anchor) ? null : iso(new Date(fireAt));
22040
- return { due, dedupeKey: `in:${fireAt}`, nextFireIso: fireIso };
22041
- }
22042
- case "cron":
22043
- return evaluateCron(trigger, ctx.now, createdAtMs);
22044
- case "after-reset":
22045
- return evaluateAfterReset(trigger, ctx.now);
22046
- case "when-status":
22047
- return evaluateWhenStatus(trigger, job, ctx, createdAtMs);
22048
- case "when-plan-lands":
22049
- return evaluateWhenPlanLands(ctx, createdAtMs);
22050
- }
22051
- }
22052
- function evaluateCron(trigger, now, createdAtMs) {
22053
- let cron;
22054
- try {
22055
- cron = new Cron(trigger.expr, trigger.tz ? { timezone: trigger.tz } : {});
22056
- } catch {
22057
- return notDue;
22058
- }
22059
- const prevs = cron.previousRuns(1, new Date(now.getTime() + 1e3));
22060
- let prev = prevs.length > 0 ? prevs[0] : null;
22061
- if (prev && prev.getTime() > now.getTime()) prev = null;
22062
- if (prev && !Number.isNaN(createdAtMs) && prev.getTime() < createdAtMs) prev = null;
22063
- const next = cron.nextRun(now);
22064
- if (!prev) return { due: false, nextFireIso: next ? iso(next) : null };
22065
- return { due: true, dedupeKey: `cron:${iso(prev)}`, nextFireIso: next ? iso(next) : null };
22066
- }
22067
- function evaluateAfterReset(trigger, now) {
22068
- const v = verifyReset(trigger.provider, trigger.anchor, now);
22069
- const predicted = predictReset(trigger.provider, trigger.anchor);
22070
- if (v.eligible) {
22071
- return { due: true, dedupeKey: `after-reset:${trigger.anchor.windowStartIso}`, nextFireIso: predicted };
22072
- }
22073
- return { due: false, nextFireIso: predicted, rescheduleToIso: v.rescheduleToIso };
22074
- }
22075
- function evaluateWhenStatus(trigger, job, ctx, createdAtMs) {
22076
- const fm = ctx.assignment;
22077
- if (!fm) return notDue;
22078
- const history = fm.statusHistory ?? [];
22079
- for (let i = job.attempt.cursor; i < history.length; i++) {
22080
- const atMs = Date.parse(history[i].at);
22081
- const afterCreation = Number.isNaN(createdAtMs) || Number.isNaN(atMs) || atMs >= createdAtMs;
22082
- if (history[i].to === trigger.status && afterCreation) {
22083
- return { due: true, dedupeKey: `status:${i}:${history[i].at}`, nextCursor: i + 1 };
22084
- }
22085
- }
22086
- return { due: false, nextCursor: history.length };
22087
- }
22088
- function evaluateWhenPlanLands(ctx, createdAtMs) {
22089
- const fm = ctx.assignment;
22090
- if (!fm) return notDue;
22091
- const approval = fm.planApproval;
22092
- if (!approval) return notDue;
22093
- const approvedMs = Date.parse(approval.at);
22094
- if (!Number.isNaN(createdAtMs) && !Number.isNaN(approvedMs) && approvedMs < createdAtMs) {
22095
- return notDue;
22096
- }
22097
- return { due: true, dedupeKey: `plan:${approval.file}:${approval.digest}` };
22098
- }
22099
-
22100
22181
  // src/schedules/launch-ack.ts
22101
22182
  init_session_id();
22102
22183
  import { readdirSync } from "fs";
@@ -22178,11 +22259,12 @@ async function defaultResolvePlan(job) {
22178
22259
  async function runTick(deps = {}) {
22179
22260
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
22180
22261
  const attemptDeps = { now, isSessionLive: deps.isSessionLive };
22181
- const result = { evaluated: 0, fired: [], failed: [], skipped: 0, reaped: [], stuck: [] };
22262
+ const result = { evaluated: 0, fired: [], failed: [], skipped: 0, reaped: [], stuck: [], completed: [] };
22182
22263
  if (deps.reap !== false) {
22183
22264
  const reap = await reapStale(await listJobs(), attemptDeps);
22184
22265
  result.reaped = reap.reaped;
22185
22266
  result.stuck = reap.stuck;
22267
+ result.completed = reap.completed;
22186
22268
  }
22187
22269
  const jobs = await listJobs();
22188
22270
  for (const job of jobs) {
@@ -23537,6 +23619,9 @@ workspace: ${workspace}
23537
23619
  await writeFileForce2(archFile, archContent);
23538
23620
  checklist.items = checklist.items.filter((i) => !completedIds.has(i.id));
23539
23621
  await writeChecklist(todosDir2, checklist);
23622
+ const archivedEntries = new Set(toArchive);
23623
+ log.entries = log.entries.filter((e) => !archivedEntries.has(e));
23624
+ await writeFileForce2(logPath(todosDir2, workspace), serializeLog(log));
23540
23625
  for (const id of completedIds) {
23541
23626
  await deleteAllAttachments(todosDir2, workspace, id);
23542
23627
  }
@@ -24282,6 +24367,9 @@ workspace: ${slug}
24282
24367
  checklist.workspace = slug;
24283
24368
  checklist.items = checklist.items.filter((i) => !completedIds.has(i.id));
24284
24369
  await writeChecklist(todosDir2, checklist);
24370
+ const archivedEntries = new Set(toArchive);
24371
+ log.entries = log.entries.filter((e) => !archivedEntries.has(e));
24372
+ await writeFileForce(logPath(todosDir2, slug), serializeLog(log));
24285
24373
  for (const id of completedIds) {
24286
24374
  await deleteAllAttachments(todosDir2, slug, id);
24287
24375
  }
@@ -26934,9 +27022,11 @@ function createDashboardServer(options) {
26934
27022
  return new Promise((resolvePromise, reject) => {
26935
27023
  server.on("error", (err2) => {
26936
27024
  if (err2.code === "EADDRINUSE") {
26937
- reject(new Error(
26938
- `Port ${port} is already in use. Use --port <number> to specify a different port.`
26939
- ));
27025
+ const inUse = new Error(
27026
+ `Port ${port} is already in use.`
27027
+ );
27028
+ inUse.code = "EADDRINUSE";
27029
+ reject(inUse);
26940
27030
  } else {
26941
27031
  reject(err2);
26942
27032
  }
@@ -26979,6 +27069,54 @@ function createDashboardServer(options) {
26979
27069
  // src/commands/dashboard.ts
26980
27070
  init_paths();
26981
27071
  init_fs();
27072
+
27073
+ // src/errors.ts
27074
+ var SyntaurError = class extends Error {
27075
+ /** Actionable next step printed as `→ try: <remediation>` on stderr. */
27076
+ remediation;
27077
+ /** Optional non-1 exit code (e.g. doctor's exit 2). Defaults to 1. */
27078
+ code;
27079
+ constructor(message, options = {
27080
+ remediation: ""
27081
+ }) {
27082
+ super(message);
27083
+ this.name = "SyntaurError";
27084
+ this.remediation = options.remediation;
27085
+ this.code = options.code ?? 1;
27086
+ if (options.cause !== void 0) {
27087
+ this.cause = options.cause;
27088
+ }
27089
+ }
27090
+ };
27091
+ function isSyntaurError(error) {
27092
+ return error instanceof SyntaurError;
27093
+ }
27094
+ function formatCliError(error) {
27095
+ const message = error instanceof Error ? error.message : String(error);
27096
+ const lines = [`Error: ${message}`];
27097
+ if (isSyntaurError(error) && error.remediation) {
27098
+ lines.push(`\u2192 try: ${error.remediation}`);
27099
+ }
27100
+ return lines.join("\n");
27101
+ }
27102
+ function exitCodeFor(error) {
27103
+ if (isSyntaurError(error) && Number.isInteger(error.code) && error.code !== 0) {
27104
+ return error.code;
27105
+ }
27106
+ return 1;
27107
+ }
27108
+ function runCommand(fn) {
27109
+ return async (...args) => {
27110
+ try {
27111
+ await fn(...args);
27112
+ } catch (error) {
27113
+ console.error(formatCliError(error));
27114
+ process.exit(exitCodeFor(error));
27115
+ }
27116
+ };
27117
+ }
27118
+
27119
+ // src/commands/dashboard.ts
26982
27120
  function didUserSpecifyDashboardPort(argv = process.argv) {
26983
27121
  return argv.some((arg) => arg === "--port" || arg.startsWith("--port="));
26984
27122
  }
@@ -27054,7 +27192,17 @@ async function dashboardCommand(options) {
27054
27192
  serveStaticUi: mode === "static",
27055
27193
  dashboardDistPath: dashboardDist
27056
27194
  });
27057
- await server.start();
27195
+ try {
27196
+ await server.start();
27197
+ } catch (err2) {
27198
+ if (err2?.code === "EADDRINUSE") {
27199
+ throw new SyntaurError(`Port ${port} is already in use.`, {
27200
+ remediation: `drop --port to let Syntaur auto-pick a free port, or run "lsof -i :${port}" to find what is holding it`,
27201
+ cause: err2
27202
+ });
27203
+ }
27204
+ throw err2;
27205
+ }
27058
27206
  let viteProcess = null;
27059
27207
  if (mode === "dev") {
27060
27208
  const dashboardDir = resolve45(packageRoot, "dashboard");
@@ -35901,15 +36049,30 @@ var doctorCommand = new Command4("doctor").description("Diagnose Syntaur state a
35901
36049
  }
35902
36050
  try {
35903
36051
  const report = await runChecks({ only: options.only });
36052
+ const FIX_NOOP_NOTE = "--fix is reserved but has no auto-fixable remediations in v1; nothing was changed.";
35904
36053
  if (options.json) {
35905
- process.stdout.write(renderJson(report) + "\n");
36054
+ if (options.fix) {
36055
+ process.stdout.write(
36056
+ JSON.stringify(
36057
+ { ...report, fix: { fixApplied: false, note: FIX_NOOP_NOTE } },
36058
+ null,
36059
+ 2
36060
+ ) + "\n"
36061
+ );
36062
+ } else {
36063
+ process.stdout.write(renderJson(report) + "\n");
36064
+ }
35906
36065
  } else {
35907
36066
  process.stdout.write(renderHuman(report, { verbose: options.verbose ?? false }) + "\n");
35908
36067
  }
35909
- if (options.fix && !options.json) {
35910
- process.stdout.write(
35911
- "\nnote: --fix is reserved but has no auto-fixable remediations in v1.\n"
35912
- );
36068
+ if (options.fix) {
36069
+ if (!options.json) {
36070
+ process.stdout.write(`
36071
+ note: ${FIX_NOOP_NOTE}
36072
+ `);
36073
+ }
36074
+ process.stderr.write(`note: ${FIX_NOOP_NOTE}
36075
+ `);
35913
36076
  }
35914
36077
  const hasError = report.summary.error > 0;
35915
36078
  process.exit(hasError ? 1 : 0);
@@ -38335,6 +38498,37 @@ init_config2();
38335
38498
  import { Command as Command8 } from "commander";
38336
38499
  init_agent_sessions();
38337
38500
  init_session_db();
38501
+ init_terminal_schema();
38502
+
38503
+ // src/schedules/liveness.ts
38504
+ init_agent_sessions();
38505
+ init_session_db();
38506
+ function isPidAlive(pid) {
38507
+ if (!Number.isFinite(pid) || pid <= 0) return false;
38508
+ try {
38509
+ process.kill(pid, 0);
38510
+ return true;
38511
+ } catch (err2) {
38512
+ const code = err2.code;
38513
+ if (code === "EPERM") return true;
38514
+ return false;
38515
+ }
38516
+ }
38517
+ function isScheduledSessionLive(sessionId, launchPid) {
38518
+ if (sessionId) {
38519
+ let row = null;
38520
+ try {
38521
+ initSessionDb();
38522
+ row = getSessionById(sessionId);
38523
+ } catch {
38524
+ return true;
38525
+ }
38526
+ if (!row) return true;
38527
+ return computeIsLive(row);
38528
+ }
38529
+ if (launchPid == null) return true;
38530
+ return isPidAlive(launchPid);
38531
+ }
38338
38532
 
38339
38533
  // src/schedules/launchd.ts
38340
38534
  import { execFileSync as execFileSync5 } from "child_process";
@@ -38462,6 +38656,17 @@ function parseDurationMs(input4) {
38462
38656
  const mult = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
38463
38657
  return n * mult[(m[2] ?? "s").toLowerCase()];
38464
38658
  }
38659
+ function validateTerminalChoice(value) {
38660
+ if (value == null) return null;
38661
+ const trimmed = value.trim();
38662
+ if (trimmed === "") return null;
38663
+ if (!TERMINAL_CHOICES.includes(trimmed)) {
38664
+ throw new Error(
38665
+ `--terminal "${trimmed}" is not a known choice \u2014 expected one of ${TERMINAL_CHOICES.join("|")}`
38666
+ );
38667
+ }
38668
+ return trimmed;
38669
+ }
38465
38670
  function buildTrigger(opts) {
38466
38671
  const chosen = [];
38467
38672
  if (opts.at) chosen.push({ kind: "at", at: opts.at });
@@ -38515,7 +38720,7 @@ scheduleCommand.command("create").description("Create a scheduled job").required
38515
38720
  wrap(async (opts) => {
38516
38721
  const config = await readConfig();
38517
38722
  const agentId = opts.agent ?? pickAgent(config).id;
38518
- const terminal = opts.terminal ?? null;
38723
+ const terminal = validateTerminalChoice(opts.terminal);
38519
38724
  const unattended = !opts.interactive;
38520
38725
  if (unattended) assertUnattendedTerminalSupported(terminal);
38521
38726
  const limits = defaultLimits();
@@ -38617,15 +38822,15 @@ scheduleCommand.command("reschedule").description("Change a job's trigger and re
38617
38822
  );
38618
38823
  scheduleCommand.command("tick").description("Run one scheduler tick (the one authority): evaluate, fire due, reap").action(
38619
38824
  wrap(async () => {
38620
- const r = await runTick({ log: (m) => console.error(m) });
38825
+ const r = await runTick({ log: (m) => console.error(m), isSessionLive: isScheduledSessionLive });
38621
38826
  console.log(
38622
- `tick: evaluated ${r.evaluated}, fired ${r.fired.length}, failed ${r.failed.length}, reaped ${r.reaped.length}, stuck ${r.stuck.length}`
38827
+ `tick: evaluated ${r.evaluated}, fired ${r.fired.length}, failed ${r.failed.length}, reaped ${r.reaped.length}, stuck ${r.stuck.length}, completed ${r.completed.length}`
38623
38828
  );
38624
38829
  })
38625
38830
  );
38626
38831
  scheduleCommand.command("fire-due").description("Internal: fire currently-due jobs without reaping (accelerator path)").action(
38627
38832
  wrap(async () => {
38628
- const r = await runTick({ reap: false });
38833
+ const r = await runTick({ reap: false, isSessionLive: isScheduledSessionLive });
38629
38834
  console.log(`fire-due: fired ${r.fired.length}, failed ${r.failed.length}`);
38630
38835
  })
38631
38836
  );
@@ -39501,14 +39706,14 @@ sessionCommand.command("resolve-id").description(
39501
39706
 
39502
39707
  // src/commands/worktree.ts
39503
39708
  init_git_worktree();
39709
+ import { Command as Command12 } from "commander";
39710
+ import { readFile as readFile68 } from "fs/promises";
39711
+ import { resolve as resolve95 } from "path";
39504
39712
  init_fs();
39505
39713
  init_paths();
39506
39714
  init_config2();
39507
39715
  init_timestamp();
39508
39716
  init_frontmatter();
39509
- import { Command as Command12 } from "commander";
39510
- import { readFile as readFile68 } from "fs/promises";
39511
- import { resolve as resolve95 } from "path";
39512
39717
  async function readContext2(cwd) {
39513
39718
  const path = resolve95(cwd, ".syntaur", "context.json");
39514
39719
  if (!await fileExists(path)) return null;
@@ -39591,6 +39796,12 @@ async function runWorktreeRemove(options, cwd = process.cwd()) {
39591
39796
  }
39592
39797
  let branchDeleted = false;
39593
39798
  if (options.deleteBranch && branch) {
39799
+ const sha = await resolveBranchSha(repository, branch);
39800
+ if (sha) {
39801
+ console.log(
39802
+ `Branch "${branch}" was at ${sha}. To recover it: git -C ${repository} branch ${branch} ${sha}`
39803
+ );
39804
+ }
39594
39805
  const del = await deleteBranch(repository, branch);
39595
39806
  if (!del.ok) {
39596
39807
  throw new Error(
@@ -39650,15 +39861,31 @@ worktreeCommand.command("list").description("List the git worktrees of a reposit
39650
39861
  });
39651
39862
  worktreeCommand.command("remove").alias("prune").description(
39652
39863
  "Remove an assignment's git worktree and clear its workspace.* fields. Branch deletion is opt-in."
39653
- ).option("--assignment <slug>", "Assignment slug (UUID for standalone). Defaults to .syntaur/context.json").option("--project <slug>", "Project slug. Required when --assignment is given for a project-nested assignment").option("--repository <path>", "Repository root (defaults to the recorded workspace.repository)").option("--delete-branch", "Also delete the branch after removing the worktree").option("--force", "Discard a dirty/locked worktree (passes --force to git)").action(async (options) => {
39864
+ ).option("--assignment <slug>", "Assignment slug (UUID for standalone). Defaults to .syntaur/context.json").option("--project <slug>", "Project slug. Required when --assignment is given for a project-nested assignment").option("--repository <path>", "Repository root (defaults to the recorded workspace.repository)").option("--delete-branch", "Also delete the branch after removing the worktree").option("--force", "Discard a dirty/locked worktree (passes --force to git)").option("--yes", "Skip the confirmation prompt for a destructive --force removal (required for non-TTY)").action(async (options) => {
39654
39865
  try {
39866
+ if (options.force && !options.yes) {
39867
+ if (!isInteractiveTerminal()) {
39868
+ throw new SyntaurError(
39869
+ "--force discards uncommitted work in the worktree, but there is no TTY to confirm.",
39870
+ { remediation: "re-run with --yes to confirm the destructive removal" }
39871
+ );
39872
+ }
39873
+ const confirmed = await confirmPrompt(
39874
+ "--force will discard any uncommitted work in the worktree. Continue?",
39875
+ false
39876
+ );
39877
+ if (!confirmed) {
39878
+ console.log("Aborted. Nothing was removed.");
39879
+ return;
39880
+ }
39881
+ }
39655
39882
  const { worktreePath, branchDeleted, workspaceCleared } = await runWorktreeRemove(options);
39656
39883
  console.log(`Removed worktree at ${worktreePath}`);
39657
39884
  if (branchDeleted) console.log("Deleted the branch.");
39658
39885
  if (workspaceCleared) console.log("Cleared the assignment workspace fields.");
39659
39886
  } catch (error) {
39660
- console.error("Error:", error instanceof Error ? error.message : String(error));
39661
- process.exit(1);
39887
+ console.error(formatCliError(error));
39888
+ process.exit(exitCodeFor(error));
39662
39889
  }
39663
39890
  });
39664
39891
 
@@ -41568,358 +41795,187 @@ var captureDashDashArgv = [];
41568
41795
  var program = new Command20();
41569
41796
  var version = await readPackageVersion(import.meta.url) ?? "0.0.0";
41570
41797
  program.name("syntaur").description("CLI scaffolding tool for the Syntaur protocol").version(version);
41571
- program.command("init").description("Initialize ~/.syntaur/ directory structure and config").option("--force", "Overwrite existing config file").action(async (options) => {
41572
- try {
41798
+ program.command("init").description("Initialize ~/.syntaur/ directory structure and config").option("--force", "Overwrite existing config file").action(
41799
+ runCommand(async (options) => {
41573
41800
  await initCommand(options);
41574
- } catch (error) {
41575
- console.error(
41576
- "Error:",
41577
- error instanceof Error ? error.message : String(error)
41578
- );
41579
- process.exit(1);
41580
- }
41581
- });
41582
- program.command("create-project").description("Create a new project with all required files").argument("<title>", "Project title").option("--slug <slug>", "Override auto-generated slug").option("--dir <path>", "Override default project directory").option("--workspace <workspace>", "Workspace for organizational grouping").action(async (title, options) => {
41583
- try {
41801
+ })
41802
+ );
41803
+ program.command("create-project").description("Create a new project with all required files").argument("<title>", "Project title").option("--slug <slug>", "Override auto-generated slug").option("--dir <path>", "Override default project directory").option("--workspace <workspace>", "Workspace for organizational grouping").action(
41804
+ runCommand(async (title, options) => {
41584
41805
  await createProjectCommand(title, options);
41585
- } catch (error) {
41586
- console.error(
41587
- "Error:",
41588
- error instanceof Error ? error.message : String(error)
41589
- );
41590
- process.exit(1);
41591
- }
41592
- });
41806
+ })
41807
+ );
41593
41808
  program.command("create-assignment").description("Create a new assignment within a project").argument("<title>", "Assignment title").option("--project <slug>", "Target project slug (required unless --one-off)").option("--one-off", "Create a standalone assignment at ~/.syntaur/assignments/<uuid>/").option("--slug <slug>", "Override auto-generated slug (display only for standalone; folder name for project-nested)").option(
41594
41809
  "--priority <level>",
41595
41810
  "Priority level (low|medium|high|critical)",
41596
41811
  "medium"
41597
- ).option("--type <type>", "Assignment type (e.g. feature, bug, refactor)").option("--depends-on <slugs>", "Comma-separated dependency slugs (not allowed with --one-off)").option("--links <slugs>", "Comma-separated linked assignment slugs (projectSlug/assignmentSlug format)").option("--dir <path>", "Override default project directory (ignored for --one-off)").option("--with-todos", "Scaffold a ## Todos section in assignment.md (omitted by default; typically populated by /plan-assignment)").option("--workspace <slug>", "Workspace group slug (only valid with --one-off; mutually exclusive with --project)").option("--ready", "Create the assignment directly as ready_for_planning (skips the draft phase)").action(async (title, options) => {
41598
- try {
41812
+ ).option("--type <type>", "Assignment type (e.g. feature, bug, refactor)").option("--depends-on <slugs>", "Comma-separated dependency slugs (not allowed with --one-off)").option("--links <slugs>", "Comma-separated linked assignment slugs (projectSlug/assignmentSlug format)").option("--dir <path>", "Override default project directory (ignored for --one-off)").option("--with-todos", "Scaffold a ## Todos section in assignment.md (omitted by default; typically populated by /plan-assignment)").option("--workspace <slug>", "Workspace group slug (only valid with --one-off; mutually exclusive with --project)").option("--ready", "Create the assignment directly as ready_for_planning (skips the draft phase)").action(
41813
+ runCommand(async (title, options) => {
41599
41814
  await createAssignmentCommand(title, options);
41600
- } catch (error) {
41601
- console.error(
41602
- "Error:",
41603
- error instanceof Error ? error.message : String(error)
41604
- );
41605
- process.exit(1);
41606
- }
41607
- });
41608
- program.command("comment").description("Add a comment to an assignment (CLI-mediated, append-only)").argument("<assignment>", "Target assignment slug (with --project) or UUID (standalone)").argument("<text>", "Comment body").option("--project <slug>", "Project slug if the target is project-nested").option("--reply-to <id>", "ID of the comment this replies to").option("--type <type>", "Comment type: question | note | feedback", "note").option("--author <name>", 'Override author (default: $USER or "unknown")').option("--dir <path>", "Override default project directory").action(async (assignment, text, options) => {
41609
- try {
41815
+ })
41816
+ );
41817
+ program.command("comment").description("Add a comment to an assignment (CLI-mediated, append-only)").argument("<assignment>", "Target assignment slug (with --project) or UUID (standalone)").argument("<text>", "Comment body").option("--project <slug>", "Project slug if the target is project-nested").option("--reply-to <id>", "ID of the comment this replies to").option("--type <type>", "Comment type: question | note | feedback", "note").option("--author <name>", 'Override author (default: $USER or "unknown")').option("--dir <path>", "Override default project directory").action(
41818
+ runCommand(async (assignment, text, options) => {
41610
41819
  await commentCommand(assignment, text, options);
41611
- } catch (error) {
41612
- console.error(
41613
- "Error:",
41614
- error instanceof Error ? error.message : String(error)
41615
- );
41616
- process.exit(1);
41617
- }
41618
- });
41619
- program.command("capture").description("Capture a typed proof artifact for an assignment").argument("[target]", "Assignment slug (with --project) or UUID; falls back to .syntaur/context.json").option("--kind <type>", "Artifact kind: screenshot | video | asciinema | http | text").option("--file <path>", "Source file to ingest (forbidden for --kind=text)").option("--criterion <index>", "Optional 0-based criterion index to tag").option("--note <text>", "Optional note (required for --kind=text)").option("--project <slug>", "Project slug if the target is project-nested").option("--dir <path>", "Override default project directory").option("--interactive", "Interactive mode: drag a region (--kind=screenshot) or record a TTY (--kind=asciinema)").option("--window", "Screenshot mode: window picker (macOS only)").option("--fullscreen", "Screenshot mode: silent full-screen capture (macOS only)").option("--start", "Start ffmpeg screen recording in the background (macOS only). Stop with --stop.").option("--stop", "Stop the running ffmpeg recording and attach the mp4.").option("--device <index>", 'AVFoundation video device index for --start (default: 1). List devices: ffmpeg -f avfoundation -list_devices true -i ""').option("--fps <n>", "Frame rate for --start (default: 30)").option("--transcribe", "Auto-transcribe captured video to a <id>.transcript.md sidecar (kind=video only; requires ELEVENLABS_API_KEY + ffmpeg)").action(async (target, options) => {
41620
- try {
41820
+ })
41821
+ );
41822
+ program.command("capture").description("Capture a typed proof artifact for an assignment").argument("[target]", "Assignment slug (with --project) or UUID; falls back to .syntaur/context.json").option("--kind <type>", "Artifact kind: screenshot | video | asciinema | http | text").option("--file <path>", "Source file to ingest (forbidden for --kind=text)").option("--criterion <index>", "Optional 0-based criterion index to tag").option("--note <text>", "Optional note (required for --kind=text)").option("--project <slug>", "Project slug if the target is project-nested").option("--dir <path>", "Override default project directory").option("--interactive", "Interactive mode: drag a region (--kind=screenshot) or record a TTY (--kind=asciinema)").option("--window", "Screenshot mode: window picker (macOS only)").option("--fullscreen", "Screenshot mode: silent full-screen capture (macOS only)").option("--start", "Start ffmpeg screen recording in the background (macOS only). Stop with --stop.").option("--stop", "Stop the running ffmpeg recording and attach the mp4.").option("--device <index>", 'AVFoundation video device index for --start (default: 1). List devices: ffmpeg -f avfoundation -list_devices true -i ""').option("--fps <n>", "Frame rate for --start (default: 30)").option("--transcribe", "Auto-transcribe captured video to a <id>.transcript.md sidecar (kind=video only; requires ELEVENLABS_API_KEY + ffmpeg)").action(
41823
+ runCommand(async (target, options) => {
41621
41824
  await captureCommand(target, {
41622
41825
  ...options,
41623
41826
  commandArgv: captureDashDashArgv
41624
41827
  });
41625
- } catch (error) {
41626
- console.error(
41627
- "Error:",
41628
- error instanceof Error ? error.message : String(error)
41629
- );
41630
- process.exit(1);
41631
- }
41632
- });
41633
- program.command("request").description("Append a todo to another assignment (cross-assignment work request)").argument("<target>", "Target assignment slug (with --project) or UUID (standalone)").argument("<text>", "Todo text").option("--project <slug>", "Project slug if the target is project-nested").option("--from <source>", "Source assignment (default: $SYNTAUR_ASSIGNMENT)").option("--dir <path>", "Override default project directory").action(async (target, text, options) => {
41634
- try {
41828
+ })
41829
+ );
41830
+ program.command("request").description("Append a todo to another assignment (cross-assignment work request)").argument("<target>", "Target assignment slug (with --project) or UUID (standalone)").argument("<text>", "Todo text").option("--project <slug>", "Project slug if the target is project-nested").option("--from <source>", "Source assignment (default: $SYNTAUR_ASSIGNMENT)").option("--dir <path>", "Override default project directory").action(
41831
+ runCommand(async (target, text, options) => {
41635
41832
  await requestCommand(target, text, options);
41636
- } catch (error) {
41637
- console.error(
41638
- "Error:",
41639
- error instanceof Error ? error.message : String(error)
41640
- );
41641
- process.exit(1);
41642
- }
41643
- });
41644
- program.command("dashboard").description("Start the local Syntaur dashboard web UI").option("--port <number>", "Port to run the dashboard on", "4800").option("--dev", "Run the dashboard with the Vite dev server", false).option("--server-only", "Run only the API server without any UI", false).option("--api-only", "Deprecated alias for --server-only", false).option("--no-open", "Do not automatically open the browser").action(async (options) => {
41645
- try {
41833
+ })
41834
+ );
41835
+ program.command("dashboard").description("Start the local Syntaur dashboard web UI").option("--port <number>", "Port to run the dashboard on", "4800").option("--dev", "Run the dashboard with the Vite dev server", false).option("--server-only", "Run only the API server without any UI", false).option("--api-only", "Deprecated alias for --server-only", false).option("--no-open", "Do not automatically open the browser").action(
41836
+ runCommand(async (options) => {
41646
41837
  const autoPort = !didUserSpecifyDashboardPort();
41647
41838
  await dashboardCommand({
41648
41839
  ...options,
41649
41840
  autoPort
41650
41841
  });
41651
- } catch (error) {
41652
- console.error(
41653
- "Error:",
41654
- error instanceof Error ? error.message : String(error)
41655
- );
41656
- process.exit(1);
41657
- }
41658
- });
41659
- program.command("assign").description("Set the assignee on an assignment").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--agent <name>", "Agent name to assign").option("--dir <path>", "Override default project directory").action(async (assignment, options) => {
41660
- try {
41842
+ })
41843
+ );
41844
+ program.command("assign").description("Set the assignee on an assignment").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--agent <name>", "Agent name to assign").option("--dir <path>", "Override default project directory").action(
41845
+ runCommand(async (assignment, options) => {
41661
41846
  await assignCommand(assignment, options);
41662
- } catch (error) {
41663
- console.error(
41664
- "Error:",
41665
- error instanceof Error ? error.message : String(error)
41666
- );
41667
- process.exit(1);
41668
- }
41669
- });
41670
- program.command("unassign").description("Clear the assignee on an assignment (inverse of assign)").argument("<assignment>", "Assignment slug (UUID for standalone)").option("--project <slug>", "Target project slug").option("--dir <path>", "Override default project directory").action(async (assignment, options) => {
41671
- try {
41847
+ })
41848
+ );
41849
+ program.command("unassign").description("Clear the assignee on an assignment (inverse of assign)").argument("<assignment>", "Assignment slug (UUID for standalone)").option("--project <slug>", "Target project slug").option("--dir <path>", "Override default project directory").action(
41850
+ runCommand(async (assignment, options) => {
41672
41851
  await unassignCommand(assignment, options);
41673
- } catch (error) {
41674
- console.error(
41675
- "Error:",
41676
- error instanceof Error ? error.message : String(error)
41677
- );
41678
- process.exit(1);
41679
- }
41680
- });
41681
- program.command("start").description("Assert implementation has started (alias of implement under derived status)").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--agent <name>", "Agent name (sets assignee if not already set)").option("--dir <path>", "Override default project directory").action(async (assignment, options) => {
41682
- try {
41852
+ })
41853
+ );
41854
+ program.command("start").description("Assert implementation has started (alias of implement under derived status)").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--agent <name>", "Agent name (sets assignee if not already set)").option("--dir <path>", "Override default project directory").action(
41855
+ runCommand(async (assignment, options) => {
41683
41856
  await startCommand(assignment, options);
41684
- } catch (error) {
41685
- console.error(
41686
- "Error:",
41687
- error instanceof Error ? error.message : String(error)
41688
- );
41689
- process.exit(1);
41690
- }
41691
- });
41692
- program.command("archive").description("Archive an assignment or a project (hidden from normal views; restorable)").argument("<target>", "Assignment slug/UUID, or a project slug").option("--project <slug>", "Resolve <target> as an assignment within this project").option("--reason <text>", "Optional reason recorded with the archive").option("--dir <path>", "Override default project directory").action(async (target, options) => {
41693
- try {
41857
+ })
41858
+ );
41859
+ program.command("archive").description("Archive an assignment or a project (hidden from normal views; restorable)").argument("<target>", "Assignment slug/UUID, or a project slug").option("--project <slug>", "Resolve <target> as an assignment within this project").option("--reason <text>", "Optional reason recorded with the archive").option("--dir <path>", "Override default project directory").action(
41860
+ runCommand(async (target, options) => {
41694
41861
  await archiveCommand(target, options);
41695
- } catch (error) {
41696
- console.error("Error:", error instanceof Error ? error.message : String(error));
41697
- process.exit(1);
41698
- }
41699
- });
41700
- program.command("restore").description("Restore an archived assignment or project (preserves prior status)").argument("<target>", "Assignment slug/UUID, or a project slug").option("--project <slug>", "Resolve <target> as an assignment within this project").option("--dir <path>", "Override default project directory").action(async (target, options) => {
41701
- try {
41862
+ })
41863
+ );
41864
+ program.command("restore").description("Restore an archived assignment or project (preserves prior status)").argument("<target>", "Assignment slug/UUID, or a project slug").option("--project <slug>", "Resolve <target> as an assignment within this project").option("--dir <path>", "Override default project directory").action(
41865
+ runCommand(async (target, options) => {
41702
41866
  await restoreCommand(target, options);
41703
- } catch (error) {
41704
- console.error("Error:", error instanceof Error ? error.message : String(error));
41705
- process.exit(1);
41706
- }
41707
- });
41708
- program.command("shape").description("Recompute derived status; ready_for_planning follows once objective + ACs are real").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--agent <name>", "Agent name (sets assignee if not already set)").option("--dir <path>", "Override default project directory").action(async (assignment, options) => {
41709
- try {
41867
+ })
41868
+ );
41869
+ program.command("shape").description("Recompute derived status; ready_for_planning follows once objective + ACs are real").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--agent <name>", "Agent name (sets assignee if not already set)").option("--dir <path>", "Override default project directory").action(
41870
+ runCommand(async (assignment, options) => {
41710
41871
  await shapeCommand(assignment, options);
41711
- } catch (error) {
41712
- console.error(
41713
- "Error:",
41714
- error instanceof Error ? error.message : String(error)
41715
- );
41716
- process.exit(1);
41717
- }
41718
- });
41719
- program.command("plan-ready").description("Approve the latest plan revision (file+digest bound); ready_to_implement derives from it").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--agent <name>", "Agent name (sets assignee if not already set)").option("--dir <path>", "Override default project directory").action(async (assignment, options) => {
41720
- try {
41872
+ })
41873
+ );
41874
+ program.command("plan-ready").description("Approve the latest plan revision (file+digest bound); ready_to_implement derives from it").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--agent <name>", "Agent name (sets assignee if not already set)").option("--dir <path>", "Override default project directory").action(
41875
+ runCommand(async (assignment, options) => {
41721
41876
  await planReadyCommand(assignment, options);
41722
- } catch (error) {
41723
- console.error(
41724
- "Error:",
41725
- error instanceof Error ? error.message : String(error)
41726
- );
41727
- process.exit(1);
41728
- }
41729
- });
41730
- program.command("migrate-statuses").description("Suggest pending -> ready_for_planning promotions for fleshed-out assignments (use --apply to write)").option("--dir <path>", "Override default project directory").option("--apply", "Apply the migration (default: dry-run)").action(async (options) => {
41731
- try {
41877
+ })
41878
+ );
41879
+ program.command("migrate-statuses").description("Suggest pending -> ready_for_planning promotions for fleshed-out assignments (use --apply to write)").option("--dir <path>", "Override default project directory").option("--apply", "Apply the migration (default: dry-run)").action(
41880
+ runCommand(async (options) => {
41732
41881
  await migrateStatusesCommand(options);
41733
- } catch (error) {
41734
- console.error(
41735
- "Error:",
41736
- error instanceof Error ? error.message : String(error)
41737
- );
41738
- process.exit(1);
41739
- }
41740
- });
41741
- program.command("migrate-status-history").description("Seed a synthetic statusHistory entry on assignments that lack one (use --apply to write)").option("--dir <path>", "Override default project directory").option("--apply", "Apply the migration (default: dry-run)").action(async (options) => {
41742
- try {
41882
+ })
41883
+ );
41884
+ program.command("migrate-status-history").description("Seed a synthetic statusHistory entry on assignments that lack one (use --apply to write)").option("--dir <path>", "Override default project directory").option("--apply", "Apply the migration (default: dry-run)").action(
41885
+ runCommand(async (options) => {
41743
41886
  await migrateStatusHistoryCommand(options);
41744
- } catch (error) {
41745
- console.error(
41746
- "Error:",
41747
- error instanceof Error ? error.message : String(error)
41748
- );
41749
- process.exit(1);
41750
- }
41751
- });
41752
- program.command("migrate-derive").description("One-time migration to derived status: seed facts from current statuses, re-derive all, print a divergence report").option("--dir <path>", "Override default project directory").option("--dry-run", "Report what would change without writing").action(async (options) => {
41753
- try {
41887
+ })
41888
+ );
41889
+ program.command("migrate-derive").description("One-time migration to derived status: seed facts from current statuses, re-derive all, print a divergence report").option("--dir <path>", "Override default project directory").option("--dry-run", "Report what would change without writing").action(
41890
+ runCommand(async (options) => {
41754
41891
  await migrateDeriveCommand(options);
41755
- } catch (error) {
41756
- console.error("Error:", error instanceof Error ? error.message : String(error));
41757
- process.exit(1);
41758
- }
41759
- });
41760
- program.command("park").description("Park an assignment (intentional withhold); disposition derives to parked").argument("<assignment>", "Assignment slug or standalone UUID").option("--project <slug>", "Target project slug").option("--reason <text>", "Why it is parked (recorded in history)").option("--agent <name>", "Acting agent id").option("--dir <path>", "Override default project directory").action(async (assignment, options) => {
41761
- try {
41892
+ })
41893
+ );
41894
+ program.command("park").description("Park an assignment (intentional withhold); disposition derives to parked").argument("<assignment>", "Assignment slug or standalone UUID").option("--project <slug>", "Target project slug").option("--reason <text>", "Why it is parked (recorded in history)").option("--agent <name>", "Acting agent id").option("--dir <path>", "Override default project directory").action(
41895
+ runCommand(async (assignment, options) => {
41762
41896
  await parkCommand(assignment, options);
41763
- } catch (error) {
41764
- console.error("Error:", error instanceof Error ? error.message : String(error));
41765
- process.exit(1);
41766
- }
41767
- });
41768
- program.command("unpark").description("Unpark an assignment; status re-derives from facts").argument("<assignment>", "Assignment slug or standalone UUID").option("--project <slug>", "Target project slug").option("--agent <name>", "Acting agent id").option("--dir <path>", "Override default project directory").action(async (assignment, options) => {
41769
- try {
41897
+ })
41898
+ );
41899
+ program.command("unpark").description("Unpark an assignment; status re-derives from facts").argument("<assignment>", "Assignment slug or standalone UUID").option("--project <slug>", "Target project slug").option("--agent <name>", "Acting agent id").option("--dir <path>", "Override default project directory").action(
41900
+ runCommand(async (assignment, options) => {
41770
41901
  await unparkCommand(assignment, options);
41771
- } catch (error) {
41772
- console.error("Error:", error instanceof Error ? error.message : String(error));
41773
- process.exit(1);
41774
- }
41775
- });
41776
- program.command("request-review").description("Request review (sets reviewRequested); the review phase derives from it").argument("<assignment>", "Assignment slug or standalone UUID").option("--project <slug>", "Target project slug").option("--clear", "Clear the review request instead").option("--agent <name>", "Acting agent id").option("--dir <path>", "Override default project directory").action(async (assignment, options) => {
41777
- try {
41902
+ })
41903
+ );
41904
+ program.command("request-review").description("Request review (sets reviewRequested); the review phase derives from it").argument("<assignment>", "Assignment slug or standalone UUID").option("--project <slug>", "Target project slug").option("--clear", "Clear the review request instead").option("--agent <name>", "Acting agent id").option("--dir <path>", "Override default project directory").action(
41905
+ runCommand(async (assignment, options) => {
41778
41906
  await requestReviewCommand(assignment, options);
41779
- } catch (error) {
41780
- console.error("Error:", error instanceof Error ? error.message : String(error));
41781
- process.exit(1);
41782
- }
41783
- });
41907
+ })
41908
+ );
41784
41909
  var factCommand = program.command("fact").description("Manage custom asserted facts declared under statuses.facts");
41785
- factCommand.command("set").description("Set a declared custom fact (bool/number); status re-derives from it").argument("<assignment>", "Assignment slug or standalone UUID").argument("<name>", "Declared fact name (statuses.facts)").argument("<value>", "Value (bool: true/false; number: any finite number)").option("--project <slug>", "Target project slug").option("--agent <name>", "Acting agent id").option("--dir <path>", "Override default project directory").action(async (assignment, name, value, options) => {
41786
- try {
41910
+ factCommand.command("set").description("Set a declared custom fact (bool/number); status re-derives from it").argument("<assignment>", "Assignment slug or standalone UUID").argument("<name>", "Declared fact name (statuses.facts)").argument("<value>", "Value (bool: true/false; number: any finite number)").option("--project <slug>", "Target project slug").option("--agent <name>", "Acting agent id").option("--dir <path>", "Override default project directory").action(
41911
+ runCommand(async (assignment, name, value, options) => {
41787
41912
  await factSetCommand(assignment, name, value, options);
41788
- } catch (error) {
41789
- console.error("Error:", error instanceof Error ? error.message : String(error));
41790
- process.exit(1);
41791
- }
41792
- });
41793
- program.command("attest").description("Record an attestation (agent reviewed a revision with a verdict); revision-bound").argument("<assignment>", "Assignment slug or standalone UUID").argument("<fact>", "Declared attestation fact name (statuses.facts)").option("--verdict <verdict>", "approved | changes-requested", "approved").option("--note <text>", "Optional note recorded on the attestation").option("--agent <id>", "Acting agent id (else the bound session, else human)").option("--project <slug>", "Target project slug").option("--dir <path>", "Override default project directory").action(async (assignment, fact, options) => {
41794
- try {
41913
+ })
41914
+ );
41915
+ program.command("attest").description("Record an attestation (agent reviewed a revision with a verdict); revision-bound").argument("<assignment>", "Assignment slug or standalone UUID").argument("<fact>", "Declared attestation fact name (statuses.facts)").option("--verdict <verdict>", "approved | changes-requested", "approved").option("--note <text>", "Optional note recorded on the attestation").option("--agent <id>", "Acting agent id (else the bound session, else human)").option("--project <slug>", "Target project slug").option("--dir <path>", "Override default project directory").action(
41916
+ runCommand(async (assignment, fact, options) => {
41795
41917
  await attestCommand(assignment, fact, options);
41796
- } catch (error) {
41797
- console.error("Error:", error instanceof Error ? error.message : String(error));
41798
- process.exit(1);
41799
- }
41800
- });
41801
- program.command("recompute").description("Recompute derived status for one assignment or --all (headless reconcile)").argument("[assignment]", "Assignment slug or standalone UUID").option("--all", "Recompute every assignment (projects + standalone)").option("--project <slug>", "Target project slug").option("--agent <name>", "Acting agent id").option("--dir <path>", "Override default project directory").action(async (assignment, options) => {
41802
- try {
41918
+ })
41919
+ );
41920
+ program.command("recompute").description("Recompute derived status for one assignment or --all (headless reconcile)").argument("[assignment]", "Assignment slug or standalone UUID").option("--all", "Recompute every assignment (projects + standalone)").option("--project <slug>", "Target project slug").option("--agent <name>", "Acting agent id").option("--dir <path>", "Override default project directory").action(
41921
+ runCommand(async (assignment, options) => {
41803
41922
  await recomputeCommand(assignment, options);
41804
- } catch (error) {
41805
- console.error("Error:", error instanceof Error ? error.message : String(error));
41806
- process.exit(1);
41807
- }
41808
- });
41809
- program.command("implement").description("Assert implementation has started; status derives to in_progress when the plan is approved").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--agent <name>", "Agent name (sets assignee if not already set)").option("--dir <path>", "Override default project directory").action(async (assignment, options) => {
41810
- try {
41923
+ })
41924
+ );
41925
+ program.command("implement").description("Assert implementation has started; status derives to in_progress when the plan is approved").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--agent <name>", "Agent name (sets assignee if not already set)").option("--dir <path>", "Override default project directory").action(
41926
+ runCommand(async (assignment, options) => {
41811
41927
  await implementCommand(assignment, options);
41812
- } catch (error) {
41813
- console.error(
41814
- "Error:",
41815
- error instanceof Error ? error.message : String(error)
41816
- );
41817
- process.exit(1);
41818
- }
41819
- });
41820
- program.command("complete").description("Transition an assignment to completed").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--dir <path>", "Override default project directory").action(async (assignment, options) => {
41821
- try {
41928
+ })
41929
+ );
41930
+ program.command("complete").description("Transition an assignment to completed").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--dir <path>", "Override default project directory").action(
41931
+ runCommand(async (assignment, options) => {
41822
41932
  await completeCommand(assignment, options);
41823
- } catch (error) {
41824
- console.error(
41825
- "Error:",
41826
- error instanceof Error ? error.message : String(error)
41827
- );
41828
- process.exit(1);
41829
- }
41830
- });
41831
- program.command("block").description("Assert a blocker (sets blockedReason); disposition derives to blocked").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--reason <text>", "Reason for blocking").option("--dir <path>", "Override default project directory").action(async (assignment, options) => {
41832
- try {
41933
+ })
41934
+ );
41935
+ program.command("block").description("Assert a blocker (sets blockedReason); disposition derives to blocked").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--reason <text>", "Reason for blocking").option("--dir <path>", "Override default project directory").action(
41936
+ runCommand(async (assignment, options) => {
41833
41937
  await blockCommand(assignment, options);
41834
- } catch (error) {
41835
- console.error(
41836
- "Error:",
41837
- error instanceof Error ? error.message : String(error)
41838
- );
41839
- process.exit(1);
41840
- }
41841
- });
41842
- program.command("unblock").description("Clear the blocker; status re-derives from facts").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--dir <path>", "Override default project directory").action(async (assignment, options) => {
41843
- try {
41938
+ })
41939
+ );
41940
+ program.command("unblock").description("Clear the blocker; status re-derives from facts").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--dir <path>", "Override default project directory").action(
41941
+ runCommand(async (assignment, options) => {
41844
41942
  await unblockCommand(assignment, options);
41845
- } catch (error) {
41846
- console.error(
41847
- "Error:",
41848
- error instanceof Error ? error.message : String(error)
41849
- );
41850
- process.exit(1);
41851
- }
41852
- });
41853
- program.command("review").description("Request review; the review phase derives from it (or from all ACs checked)").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--dir <path>", "Override default project directory").action(async (assignment, options) => {
41854
- try {
41943
+ })
41944
+ );
41945
+ program.command("review").description("Request review; the review phase derives from it (or from all ACs checked)").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--dir <path>", "Override default project directory").action(
41946
+ runCommand(async (assignment, options) => {
41855
41947
  await reviewCommand(assignment, options);
41856
- } catch (error) {
41857
- console.error(
41858
- "Error:",
41859
- error instanceof Error ? error.message : String(error)
41860
- );
41861
- process.exit(1);
41862
- }
41863
- });
41864
- program.command("fail").description("Transition an assignment to failed").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--dir <path>", "Override default project directory").action(async (assignment, options) => {
41865
- try {
41948
+ })
41949
+ );
41950
+ program.command("fail").description("Transition an assignment to failed").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--dir <path>", "Override default project directory").action(
41951
+ runCommand(async (assignment, options) => {
41866
41952
  await failCommand(assignment, options);
41867
- } catch (error) {
41868
- console.error(
41869
- "Error:",
41870
- error instanceof Error ? error.message : String(error)
41871
- );
41872
- process.exit(1);
41873
- }
41874
- });
41875
- program.command("reopen").description("Reopen a completed or failed assignment").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--dir <path>", "Override default project directory").action(async (assignment, options) => {
41876
- try {
41953
+ })
41954
+ );
41955
+ program.command("reopen").description("Reopen a completed or failed assignment").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--dir <path>", "Override default project directory").action(
41956
+ runCommand(async (assignment, options) => {
41877
41957
  await reopenCommand(assignment, options);
41878
- } catch (error) {
41879
- console.error(
41880
- "Error:",
41881
- error instanceof Error ? error.message : String(error)
41882
- );
41883
- process.exit(1);
41884
- }
41885
- });
41886
- program.command("setup").description("Initialize Syntaur and optionally install plugins or launch the dashboard").option("--yes", "Skip interactive prompts and perform only the requested flags").option("--claude", "Install the Claude Code plugin").option("--codex", "Install the Codex plugin").option("--claude-dir <path>", "Install the Claude Code plugin at a specific path").option("--codex-dir <path>", "Install the Codex plugin at a specific path").option("--codex-marketplace-path <path>", "Write the Codex marketplace entry to a specific file").option("--dashboard", "Launch the dashboard after setup").option("--target <id>", "Install Syntaur into a cross-agent target. Built-in ids: pi, hermes, openclaw, cursor, opencode (plus any user descriptors in ~/.syntaur/targets/). Comma-separated for several").option("--agent <id>", "Alias for --target; cross-agent target id(s) to install into").option("--force", "Overwrite existing cross-agent protocol files / skills").option("--dry-run", "Print the cross-agent install actions without writing anything").action(async (options) => {
41887
- try {
41958
+ })
41959
+ );
41960
+ program.command("setup").description("Initialize Syntaur and optionally install plugins or launch the dashboard").option("--yes", "Skip interactive prompts and perform only the requested flags").option("--claude", "Install the Claude Code plugin").option("--codex", "Install the Codex plugin").option("--claude-dir <path>", "Install the Claude Code plugin at a specific path").option("--codex-dir <path>", "Install the Codex plugin at a specific path").option("--codex-marketplace-path <path>", "Write the Codex marketplace entry to a specific file").option("--dashboard", "Launch the dashboard after setup").option("--target <id>", "Install Syntaur into a cross-agent target. Built-in ids: pi, hermes, openclaw, cursor, opencode (plus any user descriptors in ~/.syntaur/targets/). Comma-separated for several").option("--agent <id>", "Alias for --target; cross-agent target id(s) to install into").option("--force", "Overwrite existing cross-agent protocol files / skills").option("--dry-run", "Print the cross-agent install actions without writing anything").action(
41961
+ runCommand(async (options) => {
41888
41962
  await setupCommand(options);
41889
- } catch (error) {
41890
- console.error(
41891
- "Error:",
41892
- error instanceof Error ? error.message : String(error)
41893
- );
41894
- process.exit(1);
41895
- }
41896
- });
41897
- program.command("install-plugin").description("Install the Syntaur Claude Code plugin").option("--force", "Overwrite an existing Syntaur-managed install").option("--target-dir <path>", "Install the plugin at a specific directory").option("--link", "Use a symlink instead of copying files (repo-local dev only)").option("--force-skills", "Overwrite user-edited skills in ~/.claude/skills").option("--skip-skills", "Do not install protocol skills into ~/.claude/skills").option("--enable", "Enable the plugin in ~/.claude/settings.json after install").action(async (options) => {
41898
- try {
41963
+ })
41964
+ );
41965
+ program.command("install-plugin").description("Install the Syntaur Claude Code plugin").option("--force", "Overwrite an existing Syntaur-managed install").option("--target-dir <path>", "Install the plugin at a specific directory").option("--link", "Use a symlink instead of copying files (repo-local dev only)").option("--force-skills", "Overwrite user-edited skills in ~/.claude/skills").option("--skip-skills", "Do not install protocol skills into ~/.claude/skills").option("--enable", "Enable the plugin in ~/.claude/settings.json after install").action(
41966
+ runCommand(async (options) => {
41899
41967
  await installPluginCommand({ ...options, promptForTarget: true });
41900
- } catch (error) {
41901
- console.error(
41902
- "Error:",
41903
- error instanceof Error ? error.message : String(error)
41904
- );
41905
- process.exit(1);
41906
- }
41907
- });
41908
- program.command("update").alias("upgrade").description("Self-update the global syntaur package and refresh the plugin/skills").option("--version <v>", "Update to a specific version instead of latest").option("--check", "Report whether an update is available; apply nothing").option("--dry-run", "Print what would happen without changing anything").option("--skip-refresh", "Update the package only; do not refresh the plugin/skills").option("--force-skills", "Overwrite user-edited skills during the refresh").option("--enable", "Enable the plugin in settings.json during the refresh").option("--pm <name>", "Override package-manager detection (npm|pnpm|yarn|bun)").option("--yes", "Assume yes for any confirmation (non-interactive)").action(async (options) => {
41909
- try {
41968
+ })
41969
+ );
41970
+ program.command("update").alias("upgrade").description("Self-update the global syntaur package and refresh the plugin/skills").option("--version <v>", "Update to a specific version instead of latest").option("--check", "Report whether an update is available; apply nothing").option("--dry-run", "Print what would happen without changing anything").option("--skip-refresh", "Update the package only; do not refresh the plugin/skills").option("--force-skills", "Overwrite user-edited skills during the refresh").option("--enable", "Enable the plugin in settings.json during the refresh").option("--pm <name>", "Override package-manager detection (npm|pnpm|yarn|bun)").option("--yes", "Assume yes for any confirmation (non-interactive)").action(
41971
+ runCommand(async (options) => {
41910
41972
  await updateCommand({ ...options, scriptUrl: import.meta.url });
41911
- } catch (error) {
41912
- console.error(
41913
- "Error:",
41914
- error instanceof Error ? error.message : String(error)
41915
- );
41916
- process.exit(1);
41917
- }
41918
- });
41973
+ })
41974
+ );
41919
41975
  program.command("install-statusline").description(
41920
41976
  "Install the syntaur statusLine for Claude Code. Augments ~/.claude/settings.json; wraps any existing script by default."
41921
- ).option("--mode <mode>", "replace | wrap | skip | ask (default: ask, wrap in non-TTY)", "ask").option("--link", "Symlink the installed script to the package source (dev mode)").action(async (options) => {
41922
- try {
41977
+ ).option("--mode <mode>", "replace | wrap | skip | ask (default: ask, wrap in non-TTY)", "ask").option("--link", "Symlink the installed script to the package source (dev mode)").action(
41978
+ runCommand(async (options) => {
41923
41979
  const rawMode = (options.mode ?? "ask").toLowerCase();
41924
41980
  const valid = ["replace", "wrap", "skip", "ask"];
41925
41981
  if (!valid.includes(rawMode)) {
@@ -41931,21 +41987,15 @@ program.command("install-statusline").description(
41931
41987
  mode: rawMode,
41932
41988
  link: options.link
41933
41989
  });
41934
- } catch (error) {
41935
- console.error("Error:", error instanceof Error ? error.message : String(error));
41936
- process.exit(1);
41937
- }
41938
- });
41990
+ })
41991
+ );
41939
41992
  program.command("uninstall-statusline").description(
41940
41993
  "Remove the syntaur statusLine. Restores the previously configured command from backup if present."
41941
- ).option("--keep-script", "Leave ~/.syntaur/statusline.sh on disk (only edit settings.json)").action(async (options) => {
41942
- try {
41994
+ ).option("--keep-script", "Leave ~/.syntaur/statusline.sh on disk (only edit settings.json)").action(
41995
+ runCommand(async (options) => {
41943
41996
  await uninstallStatuslineCommand({ keepScript: options.keepScript });
41944
- } catch (error) {
41945
- console.error("Error:", error instanceof Error ? error.message : String(error));
41946
- process.exit(1);
41947
- }
41948
- });
41997
+ })
41998
+ );
41949
41999
  program.command("configure-statusline").description(
41950
42000
  "Configure which segments (git, assignment, session, model, ctx, cwd, wrap) appear in the syntaur statusLine and in what order."
41951
42001
  ).option(
@@ -41954,55 +42004,31 @@ program.command("configure-statusline").description(
41954
42004
  ).option(
41955
42005
  "--segments <list>",
41956
42006
  'Comma-separated segment list, e.g. "git,assignment,session,model,ctx".'
41957
- ).option("--separator <string>", 'Segment separator (default " \xB7 ")').option("--wrap <path>", 'Path to an external statusline script to compose as a "wrap" segment').option("--preview", "Print the resolved config and a preview line without writing").action(async (options) => {
41958
- try {
42007
+ ).option("--separator <string>", 'Segment separator (default " \xB7 ")').option("--wrap <path>", 'Path to an external statusline script to compose as a "wrap" segment').option("--preview", "Print the resolved config and a preview line without writing").action(
42008
+ runCommand(async (options) => {
41959
42009
  await configureStatuslineCommand(options);
41960
- } catch (error) {
41961
- console.error("Error:", error instanceof Error ? error.message : String(error));
41962
- process.exit(1);
41963
- }
41964
- });
41965
- program.command("uninstall-skills").description("Remove Syntaur protocol skills from ~/.claude/skills and/or ~/.codex/skills").option("--claude", "Remove from ~/.claude/skills").option("--codex", "Remove from ~/.codex/skills").option("--all", "Remove from both").action(async (options) => {
41966
- try {
42010
+ })
42011
+ );
42012
+ program.command("uninstall-skills").description("Remove Syntaur protocol skills from ~/.claude/skills and/or ~/.codex/skills").option("--claude", "Remove from ~/.claude/skills").option("--codex", "Remove from ~/.codex/skills").option("--all", "Remove from both").action(
42013
+ runCommand(async (options) => {
41967
42014
  await uninstallSkillsCommand(options);
41968
- } catch (error) {
41969
- console.error("Error:", error instanceof Error ? error.message : String(error));
41970
- process.exit(1);
41971
- }
41972
- });
41973
- program.command("install-codex-plugin").description("Install the Syntaur Codex plugin and marketplace entry").option("--force", "Overwrite an existing Syntaur-managed install").option("--target-dir <path>", "Install the plugin at a specific directory").option("--marketplace-path <path>", "Write the marketplace entry to a specific file").option("--link", "Use a symlink instead of copying files (repo-local dev only)").option("--force-skills", "Overwrite user-edited skills in ~/.codex/skills").option("--skip-skills", "Do not install protocol skills into ~/.codex/skills").action(async (options) => {
41974
- try {
42015
+ })
42016
+ );
42017
+ program.command("install-codex-plugin").description("Install the Syntaur Codex plugin and marketplace entry").option("--force", "Overwrite an existing Syntaur-managed install").option("--target-dir <path>", "Install the plugin at a specific directory").option("--marketplace-path <path>", "Write the marketplace entry to a specific file").option("--link", "Use a symlink instead of copying files (repo-local dev only)").option("--force-skills", "Overwrite user-edited skills in ~/.codex/skills").option("--skip-skills", "Do not install protocol skills into ~/.codex/skills").action(
42018
+ runCommand(async (options) => {
41975
42019
  await installCodexPluginCommand({ ...options, promptForTarget: true });
41976
- } catch (error) {
41977
- console.error(
41978
- "Error:",
41979
- error instanceof Error ? error.message : String(error)
41980
- );
41981
- process.exit(1);
41982
- }
41983
- });
41984
- program.command("uninstall").description("Remove Syntaur integrations and optionally local data").option("--claude", "Remove only the Claude Code plugin").option("--codex", "Remove only the Codex plugin and marketplace entry").option("--data", "Remove ~/.syntaur data").option("--all", "Remove plugins and ~/.syntaur data").option("--yes", "Skip confirmation prompts").action(async (options) => {
41985
- try {
42020
+ })
42021
+ );
42022
+ program.command("uninstall").description("Remove Syntaur integrations and optionally local data").option("--claude", "Remove only the Claude Code plugin").option("--codex", "Remove only the Codex plugin and marketplace entry").option("--data", "Remove ~/.syntaur data").option("--all", "Remove plugins and ~/.syntaur data").option("--yes", "Skip confirmation prompts").action(
42023
+ runCommand(async (options) => {
41986
42024
  await uninstallCommand(options);
41987
- } catch (error) {
41988
- console.error(
41989
- "Error:",
41990
- error instanceof Error ? error.message : String(error)
41991
- );
41992
- process.exit(1);
41993
- }
41994
- });
41995
- program.command("setup-adapter").description("Generate adapter instruction files for a framework in the current directory").argument("<framework>", "Target framework: built-in ids cursor, codex, opencode, pi, openclaw, hermes (plus any user descriptor with an instructions adapter in ~/.syntaur/targets/)").option("--project <slug>", "Target project slug (required)").option("--assignment <slug>", "Target assignment slug (required)").option("--force", "Overwrite existing adapter files").option("--dir <path>", "Override default project directory").action(async (framework, options) => {
41996
- try {
42025
+ })
42026
+ );
42027
+ program.command("setup-adapter").description("Generate adapter instruction files for a framework in the current directory").argument("<framework>", "Target framework: built-in ids cursor, codex, opencode, pi, openclaw, hermes (plus any user descriptor with an instructions adapter in ~/.syntaur/targets/)").option("--project <slug>", "Target project slug (required)").option("--assignment <slug>", "Target assignment slug (required)").option("--force", "Overwrite existing adapter files").option("--dir <path>", "Override default project directory").action(
42028
+ runCommand(async (framework, options) => {
41997
42029
  await setupAdapterCommand(framework, options);
41998
- } catch (error) {
41999
- console.error(
42000
- "Error:",
42001
- error instanceof Error ? error.message : String(error)
42002
- );
42003
- process.exit(1);
42004
- }
42005
- });
42030
+ })
42031
+ );
42006
42032
  program.command("track-session").description("Register an agent session (optionally linked to a project/assignment)").option("--project <slug>", "Target project slug").option("--assignment <slug>", "Assignment slug").option("--agent <name>", "Agent name, e.g. claude, codex, cursor (required)").option(
42007
42033
  "--session-id <id>",
42008
42034
  "Session id from the agent runtime (real, not generated). Defaults to self-resolution via env / process-tree markers / transcript scan \u2014 pass explicitly only when registering a session other than the calling one."
@@ -42019,17 +42045,11 @@ program.command("track-session").description("Register an agent session (optiona
42019
42045
  }
42020
42046
  return n;
42021
42047
  }
42022
- ).action(async (options) => {
42023
- try {
42048
+ ).action(
42049
+ runCommand(async (options) => {
42024
42050
  await trackSessionCommand(options);
42025
- } catch (error) {
42026
- console.error(
42027
- "Error:",
42028
- error instanceof Error ? error.message : String(error)
42029
- );
42030
- process.exit(1);
42031
- }
42032
- });
42051
+ })
42052
+ );
42033
42053
  program.command("url <url>").description(
42034
42054
  "Open an assignment or session in the configured terminal + agent (handles syntaur:// deep links)"
42035
42055
  ).option(
@@ -42053,83 +42073,41 @@ program.command("install-url-handler").description(
42053
42073
  process.exit(1);
42054
42074
  }
42055
42075
  });
42056
- program.command("browse").description("Interactive TUI browser for projects and assignments").option("--agent <id>", "Bypass the agent picker and launch the given configured agent id").option("--no-worktree-prompt", "Skip the prompt to create a worktree when one is missing").action(async (options) => {
42057
- try {
42076
+ program.command("browse").description("Interactive TUI browser for projects and assignments").option("--agent <id>", "Bypass the agent picker and launch the given configured agent id").option("--no-worktree-prompt", "Skip the prompt to create a worktree when one is missing").action(
42077
+ runCommand(async (options) => {
42058
42078
  await browseCommand(options);
42059
- } catch (error) {
42060
- console.error(
42061
- "Error:",
42062
- error instanceof Error ? error.message : String(error)
42063
- );
42064
- process.exit(1);
42065
- }
42066
- });
42067
- program.command("create-playbook").description("Create a new playbook").argument("<name>", "Playbook name").option("--slug <slug>", "Override auto-generated slug").option("--description <desc>", "Playbook description").action(async (name, options) => {
42068
- try {
42079
+ })
42080
+ );
42081
+ program.command("create-playbook").description("Create a new playbook").argument("<name>", "Playbook name").option("--slug <slug>", "Override auto-generated slug").option("--description <desc>", "Playbook description").action(
42082
+ runCommand(async (name, options) => {
42069
42083
  await createPlaybookCommand(name, options);
42070
- } catch (error) {
42071
- console.error(
42072
- "Error:",
42073
- error instanceof Error ? error.message : String(error)
42074
- );
42075
- process.exit(1);
42076
- }
42077
- });
42078
- program.command("list-playbooks").description("List playbooks (disabled playbooks are excluded unless --all is passed)").option("--all", "Include disabled playbooks").action(async (options) => {
42079
- try {
42084
+ })
42085
+ );
42086
+ program.command("list-playbooks").description("List playbooks (disabled playbooks are excluded unless --all is passed)").option("--all", "Include disabled playbooks").action(
42087
+ runCommand(async (options) => {
42080
42088
  await listPlaybooksCommand({ all: Boolean(options?.all) });
42081
- } catch (error) {
42082
- console.error(
42083
- "Error:",
42084
- error instanceof Error ? error.message : String(error)
42085
- );
42086
- process.exit(1);
42087
- }
42088
- });
42089
- program.command("enable-playbook").description("Enable a previously-disabled playbook").argument("<slug>", "Playbook slug").action(async (slug) => {
42090
- try {
42089
+ })
42090
+ );
42091
+ program.command("enable-playbook").description("Enable a previously-disabled playbook").argument("<slug>", "Playbook slug").action(
42092
+ runCommand(async (slug) => {
42091
42093
  await enablePlaybookCommand(slug);
42092
- } catch (error) {
42093
- console.error(
42094
- "Error:",
42095
- error instanceof Error ? error.message : String(error)
42096
- );
42097
- process.exit(1);
42098
- }
42099
- });
42100
- program.command("disable-playbook").description("Disable a playbook so agents no longer load it").argument("<slug>", "Playbook slug").action(async (slug) => {
42101
- try {
42094
+ })
42095
+ );
42096
+ program.command("disable-playbook").description("Disable a playbook so agents no longer load it").argument("<slug>", "Playbook slug").action(
42097
+ runCommand(async (slug) => {
42102
42098
  await disablePlaybookCommand(slug);
42103
- } catch (error) {
42104
- console.error(
42105
- "Error:",
42106
- error instanceof Error ? error.message : String(error)
42107
- );
42108
- process.exit(1);
42109
- }
42110
- });
42111
- program.command("delete-playbook").description("Delete a playbook from disk and regenerate the manifest").argument("<slug>", "Playbook slug").action(async (slug) => {
42112
- try {
42099
+ })
42100
+ );
42101
+ program.command("delete-playbook").description("Delete a playbook from disk and regenerate the manifest").argument("<slug>", "Playbook slug").action(
42102
+ runCommand(async (slug) => {
42113
42103
  await deletePlaybookCommand(slug);
42114
- } catch (error) {
42115
- console.error(
42116
- "Error:",
42117
- error instanceof Error ? error.message : String(error)
42118
- );
42119
- process.exit(1);
42120
- }
42121
- });
42122
- program.command("regen-playbook-manifest").description("Rebuild ~/.syntaur/playbooks/manifest.md from current playbook files").action(async () => {
42123
- try {
42104
+ })
42105
+ );
42106
+ program.command("regen-playbook-manifest").description("Rebuild ~/.syntaur/playbooks/manifest.md from current playbook files").action(
42107
+ runCommand(async () => {
42124
42108
  await regenPlaybookManifestCommand();
42125
- } catch (error) {
42126
- console.error(
42127
- "Error:",
42128
- error instanceof Error ? error.message : String(error)
42129
- );
42130
- process.exit(1);
42131
- }
42132
- });
42109
+ })
42110
+ );
42133
42111
  program.addCommand(todoCommand);
42134
42112
  program.addCommand(backupCommand);
42135
42113
  program.addCommand(doctorCommand);
@@ -42148,6 +42126,21 @@ program.addCommand(progressCommand);
42148
42126
  program.addCommand(leaseCommand);
42149
42127
  program.addCommand(scheduleCommand);
42150
42128
  program.addCommand(usageCommand);
42129
+ program.addHelpText(
42130
+ "after",
42131
+ `
42132
+ Common workflow:
42133
+ $ syntaur setup Initialize Syntaur (plugins, dashboard)
42134
+ $ syntaur create-project "My App" Start a new project
42135
+ $ syntaur create-assignment --project my-app "Add login" Add a task to a project
42136
+ $ syntaur browse Interactive TUI: pick & launch work
42137
+ $ syntaur dashboard Open the local web dashboard
42138
+ $ syntaur doctor Diagnose Syntaur state & suggested fixes
42139
+
42140
+ Run 'syntaur <command> --help' for command-specific options.
42141
+ Migration/internal commands (migrate-*, regen-playbook-manifest) are advanced; most
42142
+ workflows never need them.`
42143
+ );
42151
42144
  if (process.argv.length <= 2) {
42152
42145
  process.argv.push(await getDefaultCommandName());
42153
42146
  }