syntaur 0.67.0 → 0.69.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 (73) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dashboard/dist/assets/{_basePickBy-CE2CIvur.js → _basePickBy-DKk6tHtk.js} +1 -1
  3. package/dashboard/dist/assets/{_baseUniq-BznLQqID.js → _baseUniq-DM-f7DWz.js} +1 -1
  4. package/dashboard/dist/assets/{arc-B6JqtWve.js → arc-ZBlA3YdV.js} +1 -1
  5. package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-P8UCT3rj.js → architectureDiagram-2XIMDMQ5-BUmvtGTF.js} +1 -1
  6. package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-C1ATZKSf.js → blockDiagram-WCTKOSBZ-B3qxWK6s.js} +1 -1
  7. package/dashboard/dist/assets/{c4Diagram-IC4MRINW-AvN1yayQ.js → c4Diagram-IC4MRINW-BEq_UJO-.js} +1 -1
  8. package/dashboard/dist/assets/channel-fypxffzQ.js +1 -0
  9. package/dashboard/dist/assets/{chunk-4BX2VUAB-CyYz6mlJ.js → chunk-4BX2VUAB-C-Y9ryMm.js} +1 -1
  10. package/dashboard/dist/assets/{chunk-55IACEB6-QyOF7ox_.js → chunk-55IACEB6-CGdtbsjw.js} +1 -1
  11. package/dashboard/dist/assets/{chunk-FMBD7UC4-DVTHM99U.js → chunk-FMBD7UC4-DllxJhUp.js} +1 -1
  12. package/dashboard/dist/assets/{chunk-JSJVCQXG-DQfxaQtT.js → chunk-JSJVCQXG-jjMM8O5F.js} +1 -1
  13. package/dashboard/dist/assets/{chunk-KX2RTZJC-4R1oobH6.js → chunk-KX2RTZJC-B_6BPltQ.js} +1 -1
  14. package/dashboard/dist/assets/{chunk-NQ4KR5QH-D8H_7yNS.js → chunk-NQ4KR5QH-D0hJiXHp.js} +1 -1
  15. package/dashboard/dist/assets/{chunk-QZHKN3VN-DLxDUSuo.js → chunk-QZHKN3VN-BCWo4hLS.js} +1 -1
  16. package/dashboard/dist/assets/{chunk-WL4C6EOR-CWuFDkVp.js → chunk-WL4C6EOR-DH_jEAwg.js} +1 -1
  17. package/dashboard/dist/assets/classDiagram-VBA2DB6C-1KnjQvtL.js +1 -0
  18. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-1KnjQvtL.js +1 -0
  19. package/dashboard/dist/assets/clone-CKMabBhS.js +1 -0
  20. package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-D23Dy_Za.js → cose-bilkent-S5V4N54A-5ld00TOH.js} +1 -1
  21. package/dashboard/dist/assets/{dagre-KLK3FWXG-CaKBk8eh.js → dagre-KLK3FWXG-Cnu6eQWy.js} +1 -1
  22. package/dashboard/dist/assets/{diagram-E7M64L7V-BAPQPki-.js → diagram-E7M64L7V-_CBBKNP-.js} +1 -1
  23. package/dashboard/dist/assets/{diagram-IFDJBPK2-Cla6qyvn.js → diagram-IFDJBPK2-DE6WjIb1.js} +1 -1
  24. package/dashboard/dist/assets/{diagram-P4PSJMXO-DnTZq_y6.js → diagram-P4PSJMXO-DpW7UzNK.js} +1 -1
  25. package/dashboard/dist/assets/{erDiagram-INFDFZHY-yc1_7ebn.js → erDiagram-INFDFZHY-BD2409fE.js} +1 -1
  26. package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-CMt2tF6O.js → flowDiagram-PKNHOUZH-1p0khhFI.js} +1 -1
  27. package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-CXiK-5Gp.js → ganttDiagram-A5KZAMGK-B2zFyA4s.js} +1 -1
  28. package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-B4PbW06T.js → gitGraphDiagram-K3NZZRJ6-bH-4YH7h.js} +1 -1
  29. package/dashboard/dist/assets/{graph-CjdFy-q-.js → graph-BT24B6iQ.js} +1 -1
  30. package/dashboard/dist/assets/{index-DlUgV5eO.css → index-BZwPAi8K.css} +1 -1
  31. package/dashboard/dist/assets/index-Cxqr3rQB.js +670 -0
  32. package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-BeOWVt7p.js → infoDiagram-LFFYTUFH-CMzP4Hcg.js} +1 -1
  33. package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-LE7swZOH.js → ishikawaDiagram-PHBUUO56-DMEmFC7M.js} +1 -1
  34. package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-DG9-sksf.js → journeyDiagram-4ABVD52K-CAtzYQUm.js} +1 -1
  35. package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-7CKAw6eI.js → kanban-definition-K7BYSVSG-d1JVbtvX.js} +1 -1
  36. package/dashboard/dist/assets/{layout-CHChKPuz.js → layout-BVuI38I_.js} +1 -1
  37. package/dashboard/dist/assets/{linear-BWSj4au4.js → linear-Bc8PGMbp.js} +1 -1
  38. package/dashboard/dist/assets/{mermaid.core-CAwQaPqG.js → mermaid.core-UtwFYLNj.js} +4 -4
  39. package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-CtZWMkVN.js → mindmap-definition-YRQLILUH-DHc5RCDj.js} +1 -1
  40. package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-CstCoaK7.js → pieDiagram-SKSYHLDU-9anIsdIA.js} +1 -1
  41. package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-DD8zRtaB.js → quadrantDiagram-337W2JSQ-FZ0D9HnU.js} +1 -1
  42. package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-BWpKIK3b.js → requirementDiagram-Z7DCOOCP-BkjCvH_u.js} +1 -1
  43. package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-08gqQtFM.js → sankeyDiagram-WA2Y5GQK-DzqwYHDo.js} +1 -1
  44. package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-DsfIDBUH.js → sequenceDiagram-2WXFIKYE-BW4g5Ao-.js} +1 -1
  45. package/dashboard/dist/assets/{stateDiagram-RAJIS63D-Dqzfk_yy.js → stateDiagram-RAJIS63D-D0tKU3Z0.js} +1 -1
  46. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-DxWQhjNO.js +1 -0
  47. package/dashboard/dist/assets/{timeline-definition-YZTLITO2-DgH_p9jR.js → timeline-definition-YZTLITO2-Bu269QDX.js} +1 -1
  48. package/dashboard/dist/assets/{treemap-KZPCXAKY-7XKqMhq9.js → treemap-KZPCXAKY-BGu_rrva.js} +1 -1
  49. package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-BFtNLBWM.js → vennDiagram-LZ73GAT5-Cx_n5FRZ.js} +1 -1
  50. package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-CQPevMNl.js → xychartDiagram-JWTSCODW-BOJsKV_W.js} +1 -1
  51. package/dashboard/dist/index.html +2 -2
  52. package/dist/dashboard/server.js +459 -63
  53. package/dist/dashboard/server.js.map +1 -1
  54. package/dist/index.js +1158 -702
  55. package/dist/index.js.map +1 -1
  56. package/dist/launch/index.d.ts +18 -0
  57. package/dist/launch/index.js +120 -12
  58. package/dist/launch/index.js.map +1 -1
  59. package/package.json +1 -1
  60. package/platforms/claude-code/.claude-plugin/plugin.json +1 -1
  61. package/platforms/claude-code/hooks/hooks.json +1 -1
  62. package/platforms/claude-code/hooks/session-cleanup.sh +21 -0
  63. package/platforms/codex/.codex-plugin/plugin.json +1 -1
  64. package/platforms/codex/hooks.json +1 -1
  65. package/platforms/codex/scripts/session-cleanup.sh +13 -0
  66. package/platforms/hermes/plugins/syntaur/__pycache__/__init__.cpython-312.pyc +0 -0
  67. package/platforms/hermes/plugins/syntaur/__pycache__/boundary.cpython-312.pyc +0 -0
  68. package/dashboard/dist/assets/channel-IZujZyS6.js +0 -1
  69. package/dashboard/dist/assets/classDiagram-VBA2DB6C-ChnJofe3.js +0 -1
  70. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-ChnJofe3.js +0 -1
  71. package/dashboard/dist/assets/clone-JjIbzsqJ.js +0 -1
  72. package/dashboard/dist/assets/index-COOcebN7.js +0 -659
  73. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-D542UPiR.js +0 -1
package/dist/index.js CHANGED
@@ -1591,6 +1591,21 @@ CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);
1591
1591
  });
1592
1592
 
1593
1593
  // src/lifecycle/event-emit.ts
1594
+ var event_emit_exports = {};
1595
+ __export(event_emit_exports, {
1596
+ emitEvent: () => emitEvent,
1597
+ isSuppressingEvents: () => isSuppressingEvents,
1598
+ recordStatusEvent: () => recordStatusEvent,
1599
+ resolveActor: () => resolveActor,
1600
+ setSuppressEvents: () => setSuppressEvents,
1601
+ withSuppressedEvents: () => withSuppressedEvents
1602
+ });
1603
+ function setSuppressEvents(value) {
1604
+ suppressEvents = value;
1605
+ }
1606
+ function isSuppressingEvents() {
1607
+ return suppressEvents;
1608
+ }
1594
1609
  function withSuppressedEvents(fn) {
1595
1610
  const prior = suppressEvents;
1596
1611
  suppressEvents = true;
@@ -3885,7 +3900,9 @@ __export(config_exports, {
3885
3900
  getTerminal: () => getTerminal,
3886
3901
  normalizeFactDeclarations: () => normalizeFactDeclarations,
3887
3902
  parseAgentCommand: () => parseAgentCommand,
3903
+ parseDurationMs: () => parseDurationMs,
3888
3904
  parseSearchConfig: () => parseSearchConfig,
3905
+ parseStalenessConfig: () => parseStalenessConfig,
3889
3906
  parseStatusConfig: () => parseStatusConfig,
3890
3907
  parseTerminalConfig: () => parseTerminalConfig,
3891
3908
  readConfig: () => readConfig,
@@ -3901,6 +3918,7 @@ __export(config_exports, {
3901
3918
  validateDeriveConfig: () => validateDeriveConfig,
3902
3919
  validateDeriveShape: () => validateDeriveShape,
3903
3920
  validateFactDeclarations: () => validateFactDeclarations,
3921
+ validateStalenessConfig: () => validateStalenessConfig,
3904
3922
  writeAgentsConfig: () => writeAgentsConfig,
3905
3923
  writeHotkeyBindingsConfig: () => writeHotkeyBindingsConfig,
3906
3924
  writeSearchConfig: () => writeSearchConfig,
@@ -3912,6 +3930,13 @@ __export(config_exports, {
3912
3930
  import { readFile as readFile5 } from "fs/promises";
3913
3931
  import { spawnSync } from "child_process";
3914
3932
  import { resolve as resolve7, isAbsolute } from "path";
3933
+ function parseDurationMs(raw2) {
3934
+ const m = raw2.trim().match(DURATION_RE);
3935
+ if (!m) return null;
3936
+ const n = Number(m[1]);
3937
+ if (!Number.isFinite(n) || n <= 0) return null;
3938
+ return n * DURATION_UNIT_MS[m[2] ?? "ms"];
3939
+ }
3915
3940
  function parseAgentCommand(value, agentId) {
3916
3941
  if (typeof value !== "string" || value.trim() === "") {
3917
3942
  throw new AgentConfigError(
@@ -5148,6 +5173,65 @@ ${cleanedFm}
5148
5173
  ---${afterFrontmatter}`;
5149
5174
  await writeFileForce(configPath2, newContent);
5150
5175
  }
5176
+ function parseStalenessConfig(content) {
5177
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
5178
+ if (!match) return null;
5179
+ const fmBlock = match[1];
5180
+ const blockStart = fmBlock.match(/^staleness:\s*$/m);
5181
+ if (!blockStart) return null;
5182
+ const startIdx = (blockStart.index ?? 0) + blockStart[0].length;
5183
+ const lines = fmBlock.slice(startIdx).split("\n");
5184
+ const out = {};
5185
+ for (const line of lines) {
5186
+ if (line.trim() === "") continue;
5187
+ const trimmed = line.trimStart();
5188
+ const indent = line.length - trimmed.length;
5189
+ if (indent === 0) break;
5190
+ const ci = trimmed.indexOf(":");
5191
+ if (ci <= 0) continue;
5192
+ const key = trimmed.slice(0, ci).trim();
5193
+ const field = STALENESS_KEY_TO_FIELD[key];
5194
+ if (!field) continue;
5195
+ let value = trimmed.slice(ci + 1).trim();
5196
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
5197
+ value = value.slice(1, -1);
5198
+ }
5199
+ const ms = parseDurationMs(value);
5200
+ if (ms !== null) out[field] = ms;
5201
+ }
5202
+ return Object.keys(out).length > 0 ? out : null;
5203
+ }
5204
+ function validateStalenessConfig(content) {
5205
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
5206
+ if (!match) return [];
5207
+ const fmBlock = match[1];
5208
+ const blockStart = fmBlock.match(/^staleness:\s*$/m);
5209
+ if (!blockStart) return [];
5210
+ const startIdx = (blockStart.index ?? 0) + blockStart[0].length;
5211
+ const lines = fmBlock.slice(startIdx).split("\n");
5212
+ const problems = [];
5213
+ for (const line of lines) {
5214
+ if (line.trim() === "") continue;
5215
+ const trimmed = line.trimStart();
5216
+ const indent = line.length - trimmed.length;
5217
+ if (indent === 0) break;
5218
+ const ci = trimmed.indexOf(":");
5219
+ if (ci <= 0) continue;
5220
+ const key = trimmed.slice(0, ci).trim();
5221
+ let value = trimmed.slice(ci + 1).trim();
5222
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
5223
+ value = value.slice(1, -1);
5224
+ }
5225
+ if (!(key in STALENESS_KEY_TO_FIELD)) {
5226
+ problems.push(`staleness.${key}: unknown key (expected one of ${Object.keys(STALENESS_KEY_TO_FIELD).join(", ")})`);
5227
+ continue;
5228
+ }
5229
+ if (parseDurationMs(value) === null) {
5230
+ problems.push(`staleness.${key}: "${value}" is not a positive duration (e.g. 7d, 12h, 30m, 90s, 500ms)`);
5231
+ }
5232
+ }
5233
+ return problems;
5234
+ }
5151
5235
  function parseSearchConfig(content) {
5152
5236
  const match = content.match(/^---\n([\s\S]*?)\n---/);
5153
5237
  if (!match) return null;
@@ -5430,7 +5514,9 @@ async function readConfig() {
5430
5514
  }
5431
5515
  })(),
5432
5516
  searchConfig: parseSearchConfig(content),
5433
- workspaceVisibility: parseWorkspaceVisibilityConfig(fmBlock)
5517
+ workspaceVisibility: parseWorkspaceVisibilityConfig(fmBlock),
5518
+ staleness: parseStalenessConfig(content),
5519
+ stalenessWatchdog: String(fm["stalenessWatchdog"]).toLowerCase() === "true"
5434
5520
  };
5435
5521
  }
5436
5522
  function getAssignmentTypes(config) {
@@ -5493,7 +5579,7 @@ async function updateAgentsConfig(mutation, options = {}) {
5493
5579
  await writeAgentsConfig(next);
5494
5580
  return { previous, next, written: true };
5495
5581
  }
5496
- var DEFAULT_ASSIGNMENT_TYPES, DEFAULT_CONFIG, AUTO_CREATE_WORKTREE_VALUES, SESSION_AUTO_TRACK_VALUES, AgentConfigError, DEFAULT_STATUS_COLORS, KNOWN_AGENT_SCALAR_FIELDS, migratedConfigPaths, TerminalConfigError;
5582
+ var STALENESS_KEY_TO_FIELD, DURATION_RE, DURATION_UNIT_MS, DEFAULT_ASSIGNMENT_TYPES, DEFAULT_CONFIG, AUTO_CREATE_WORKTREE_VALUES, SESSION_AUTO_TRACK_VALUES, AgentConfigError, DEFAULT_STATUS_COLORS, KNOWN_AGENT_SCALAR_FIELDS, migratedConfigPaths, TerminalConfigError;
5497
5583
  var init_config2 = __esm({
5498
5584
  "src/utils/config.ts"() {
5499
5585
  "use strict";
@@ -5510,6 +5596,21 @@ var init_config2 = __esm({
5510
5596
  init_terminal_schema();
5511
5597
  init_search_schema();
5512
5598
  init_workspace_visibility_schema();
5599
+ STALENESS_KEY_TO_FIELD = {
5600
+ inProgressNoActivity: "inProgressNoActivityMs",
5601
+ readyUnclaimed: "readyUnclaimedMs",
5602
+ reviewAging: "reviewAgingMs",
5603
+ blockedAging: "blockedAgingMs",
5604
+ planApprovalAging: "planApprovalAgingMs"
5605
+ };
5606
+ DURATION_RE = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/;
5607
+ DURATION_UNIT_MS = {
5608
+ ms: 1,
5609
+ s: 1e3,
5610
+ m: 6e4,
5611
+ h: 36e5,
5612
+ d: 864e5
5613
+ };
5513
5614
  DEFAULT_ASSIGNMENT_TYPES = {
5514
5615
  definitions: [
5515
5616
  { id: "feature", label: "Feature" },
@@ -5552,7 +5653,9 @@ var init_config2 = __esm({
5552
5653
  searchConfig: null,
5553
5654
  workspaceVisibility: {
5554
5655
  hidden: []
5555
- }
5656
+ },
5657
+ staleness: null,
5658
+ stalenessWatchdog: false
5556
5659
  };
5557
5660
  AUTO_CREATE_WORKTREE_VALUES = ["skip", "ask", "always"];
5558
5661
  SESSION_AUTO_TRACK_VALUES = ["all", "workspaces-only", "off"];
@@ -8907,8 +9010,8 @@ async function migrateFromMarkdown(projectsDir2) {
8907
9010
  return allSessions.length;
8908
9011
  }
8909
9012
  async function parseMarkdownSessionsIndex(filePath, projectSlug) {
8910
- const { readFile: readFile81 } = await import("fs/promises");
8911
- const raw2 = await readFile81(filePath, "utf-8");
9013
+ const { readFile: readFile82 } = await import("fs/promises");
9014
+ const raw2 = await readFile82(filePath, "utf-8");
8912
9015
  const sessions = [];
8913
9016
  const lines = raw2.split("\n");
8914
9017
  let inTable = false;
@@ -9177,6 +9280,74 @@ var init_overviewCopy = __esm({
9177
9280
  }
9178
9281
  });
9179
9282
 
9283
+ // src/staleness/classify.ts
9284
+ function resolveStaleThresholds(overrides) {
9285
+ const merged = { ...DEFAULT_STALE_THRESHOLDS };
9286
+ if (overrides) {
9287
+ for (const key of Object.keys(merged)) {
9288
+ const v = overrides[key];
9289
+ if (typeof v === "number" && Number.isFinite(v) && v > 0) merged[key] = v;
9290
+ }
9291
+ }
9292
+ return merged;
9293
+ }
9294
+ function classifyNeedsAttention(input4, thresholds = DEFAULT_STALE_THRESHOLDS) {
9295
+ if (input4.isTerminal) return [];
9296
+ const reasons = [];
9297
+ const age = input4.statusAgeMs;
9298
+ const aged = (gate) => age !== null && age >= gate;
9299
+ const blocked = input4.disposition === "blocked" || input4.blockedReason !== null;
9300
+ if (input4.phase === IN_PROGRESS_PHASE && !blocked && aged(thresholds.inProgressNoActivityMs) && input4.lastActivityMs !== null && input4.lastActivityMs >= thresholds.inProgressNoActivityMs) {
9301
+ reasons.push({
9302
+ kind: "in_progress_no_activity",
9303
+ label: "In progress, but no recent activity",
9304
+ severity: "medium"
9305
+ });
9306
+ }
9307
+ if (input4.phase === READY_PHASE && input4.assignee === null && aged(thresholds.readyUnclaimedMs)) {
9308
+ reasons.push({
9309
+ kind: "ready_unclaimed",
9310
+ label: "Ready to implement, unclaimed",
9311
+ severity: "medium"
9312
+ });
9313
+ }
9314
+ if (input4.phase === REVIEW_PHASE && aged(thresholds.reviewAgingMs)) {
9315
+ reasons.push({ kind: "review_aging", label: "Awaiting review", severity: "high" });
9316
+ }
9317
+ if (blocked && aged(thresholds.blockedAgingMs)) {
9318
+ reasons.push({ kind: "blocked_aging", label: "Blocked and aging", severity: "high" });
9319
+ }
9320
+ if (input4.phase === PLANNING_PHASE && input4.planExists && !input4.planApproved && aged(thresholds.planApprovalAgingMs)) {
9321
+ reasons.push({
9322
+ kind: "plan_awaiting_approval",
9323
+ label: "Plan awaiting approval",
9324
+ severity: "medium"
9325
+ });
9326
+ }
9327
+ if (input4.depsSatisfied === false && (input4.phase === READY_PHASE || input4.phase === IN_PROGRESS_PHASE)) {
9328
+ reasons.push({ kind: "deps_unsatisfied", label: "Unmet dependencies", severity: "high" });
9329
+ }
9330
+ return reasons;
9331
+ }
9332
+ var DAY, DEFAULT_STALE_THRESHOLDS, PLANNING_PHASE, READY_PHASE, IN_PROGRESS_PHASE, REVIEW_PHASE;
9333
+ var init_classify = __esm({
9334
+ "src/staleness/classify.ts"() {
9335
+ "use strict";
9336
+ DAY = 24 * 60 * 60 * 1e3;
9337
+ DEFAULT_STALE_THRESHOLDS = {
9338
+ inProgressNoActivityMs: 7 * DAY,
9339
+ readyUnclaimedMs: 3 * DAY,
9340
+ reviewAgingMs: 3 * DAY,
9341
+ blockedAgingMs: 3 * DAY,
9342
+ planApprovalAgingMs: 3 * DAY
9343
+ };
9344
+ PLANNING_PHASE = "ready_for_planning";
9345
+ READY_PHASE = "ready_to_implement";
9346
+ IN_PROGRESS_PHASE = "in_progress";
9347
+ REVIEW_PHASE = "review";
9348
+ }
9349
+ });
9350
+
9180
9351
  // src/dashboard/servers.ts
9181
9352
  import { readdir as readdir10, readFile as readFile13, unlink as unlink2 } from "fs/promises";
9182
9353
  import { resolve as resolve18 } from "path";
@@ -9367,8 +9538,8 @@ function scanKey(serversDir2, projectsDir2, assignmentsDir2) {
9367
9538
  return `${serversDir2}\0${projectsDir2}\0${assignmentsDir2 ?? ""}`;
9368
9539
  }
9369
9540
  function delay(ms) {
9370
- return new Promise((resolve110) => {
9371
- const timer3 = setTimeout(resolve110, ms);
9541
+ return new Promise((resolve111) => {
9542
+ const timer3 = setTimeout(resolve111, ms);
9372
9543
  if (typeof timer3.unref === "function") {
9373
9544
  timer3.unref();
9374
9545
  }
@@ -9811,7 +9982,38 @@ var init_scanner = __esm({
9811
9982
  });
9812
9983
 
9813
9984
  // src/dashboard/api.ts
9814
- import { readdir as readdir11, readFile as readFile14, writeFile as writeFile3 } from "fs/promises";
9985
+ var api_exports = {};
9986
+ __export(api_exports, {
9987
+ WorkspaceBlockedError: () => WorkspaceBlockedError,
9988
+ clearStatusConfigCache: () => clearStatusConfigCache,
9989
+ collectStaleCandidates: () => collectStaleCandidates,
9990
+ createWorkspace: () => createWorkspace,
9991
+ deleteWorkspace: () => deleteWorkspace,
9992
+ getAssignmentDetail: () => getAssignmentDetail,
9993
+ getAssignmentDetailById: () => getAssignmentDetailById,
9994
+ getEditableDocument: () => getEditableDocument,
9995
+ getEditableDocumentById: () => getEditableDocumentById,
9996
+ getHelp: () => getHelp,
9997
+ getMemoryDetail: () => getMemoryDetail,
9998
+ getOverview: () => getOverview,
9999
+ getPlaybookDetail: () => getPlaybookDetail,
10000
+ getProjectDetail: () => getProjectDetail,
10001
+ getResourceDetail: () => getResourceDetail,
10002
+ getStatusConfig: () => getStatusConfig,
10003
+ installRecordsInvalidation: () => installRecordsInvalidation,
10004
+ invalidateRecordsCache: () => invalidateRecordsCache,
10005
+ listAllMemories: () => listAllMemories,
10006
+ listAllResources: () => listAllResources,
10007
+ listArchived: () => listArchived,
10008
+ listAssignmentsBoard: () => listAssignmentsBoard,
10009
+ listPlaybooks: () => listPlaybooks,
10010
+ listProjects: () => listProjects,
10011
+ listWorkspaceRecords: () => listWorkspaceRecords,
10012
+ listWorkspaces: () => listWorkspaces,
10013
+ resolveProjectPath: () => resolveProjectPath,
10014
+ resolveWorkspaceMembers: () => resolveWorkspaceMembers
10015
+ });
10016
+ import { readdir as readdir11, readFile as readFile14, writeFile as writeFile3, stat as stat2 } from "fs/promises";
9815
10017
  import { resolve as resolve20, dirname as dirname3, basename } from "path";
9816
10018
  function clearFrontmatterField(content, key) {
9817
10019
  const fieldRegex = new RegExp(`^(${escapeRegExp2(key)}:)\\s*.*$`, "m");
@@ -10202,10 +10404,10 @@ async function getOverview(projectsDir2, serversDir2, assignmentsDir2, options =
10202
10404
  (total, record) => total + (record.summary.progress["failed"] ?? 0),
10203
10405
  0
10204
10406
  ),
10205
- staleAssignments: activeProjectRecords.reduce(
10206
- (total, record) => total + activeAssignments(record.assignments).filter((assignment) => isStale(assignment.updated)).length,
10207
- 0
10208
- ) + activeStandaloneRecords.filter((sr) => isStale(sr.record.updated)).length
10407
+ // Derived from the SAME classifier verdict as the stale segment (via the
10408
+ // pre-cap segment total) so the badge count can never diverge from the
10409
+ // listed rows.
10410
+ staleAssignments: segments.stale.total
10209
10411
  },
10210
10412
  hero,
10211
10413
  segments,
@@ -11357,9 +11559,77 @@ function segmentSeverity(segment) {
11357
11559
  return "medium";
11358
11560
  }
11359
11561
  }
11562
+ function topStaleReason(reasons) {
11563
+ if (reasons.length === 0) return null;
11564
+ return reasons.slice().sort((a, b) => STALE_SEVERITY_RANK[b.severity] - STALE_SEVERITY_RANK[a.severity])[0];
11565
+ }
11566
+ async function readProgressActivityMs(progressPath, now) {
11567
+ try {
11568
+ const s = await stat2(progressPath);
11569
+ return Math.max(0, now - s.mtimeMs);
11570
+ } catch {
11571
+ return null;
11572
+ }
11573
+ }
11574
+ function classifyAssignmentRecord(assignment, terminalStatuses3, depsSatisfied, lastActivityMs, thresholds) {
11575
+ const virtuals = deriveStatusVirtuals(assignment, terminalStatuses3);
11576
+ return classifyNeedsAttention(
11577
+ {
11578
+ phase: virtuals.phase,
11579
+ disposition: virtuals.disposition,
11580
+ isTerminal: terminalStatuses3.has(assignment.status),
11581
+ assignee: assignment.assignee ?? null,
11582
+ blockedReason: assignment.blockedReason,
11583
+ depsSatisfied,
11584
+ // plan_awaiting_approval is deferred to the decision inbox's plan-approval
11585
+ // category for now; pass values that keep that reason dormant.
11586
+ planExists: false,
11587
+ planApproved: true,
11588
+ statusAgeMs: virtuals.statusAge,
11589
+ lastActivityMs
11590
+ },
11591
+ thresholds
11592
+ );
11593
+ }
11594
+ async function collectStaleCandidates(projectsDir2, assignmentsDir2) {
11595
+ const [projectRecords, standaloneRecords] = await Promise.all([
11596
+ listProjectRecords(projectsDir2),
11597
+ listStandaloneRecords(assignmentsDir2)
11598
+ ]);
11599
+ const { terminalStatuses: terminalStatuses3 } = await getStatusConfig();
11600
+ const thresholds = resolveStaleThresholds((await readConfig()).staleness);
11601
+ const now = Date.now();
11602
+ const out = [];
11603
+ for (const record of projectRecords) {
11604
+ if (isProjectArchived(record.summary)) continue;
11605
+ const projectPath = resolve20(projectsDir2, record.summary.slug);
11606
+ const depMap = /* @__PURE__ */ new Map();
11607
+ for (const a of record.assignments) depMap.set(a.slug, a.status);
11608
+ for (const assignment of activeAssignments(record.assignments)) {
11609
+ const depsSatisfied = assignment.dependsOn.length === 0 ? true : (await getUnmetDependencies(projectPath, assignment.dependsOn, terminalStatuses3, depMap)).length === 0;
11610
+ const lastActivityMs = await readProgressActivityMs(
11611
+ resolve20(projectPath, "assignments", assignment.slug, "progress.md"),
11612
+ now
11613
+ );
11614
+ const reasons = classifyAssignmentRecord(assignment, terminalStatuses3, depsSatisfied, lastActivityMs, thresholds);
11615
+ if (reasons.length > 0) {
11616
+ out.push({ assignmentId: assignment.id, projectSlug: record.summary.slug, reasons });
11617
+ }
11618
+ }
11619
+ }
11620
+ for (const sr of standaloneRecords) {
11621
+ if (sr.record.archived === true) continue;
11622
+ const lastActivityMs = await readProgressActivityMs(resolve20(sr.assignmentDir, "progress.md"), now);
11623
+ const reasons = classifyAssignmentRecord(sr.record, terminalStatuses3, true, lastActivityMs, thresholds);
11624
+ if (reasons.length > 0) out.push({ assignmentId: sr.record.id, projectSlug: null, reasons });
11625
+ }
11626
+ return out;
11627
+ }
11360
11628
  async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standaloneRecords, traces) {
11361
11629
  const now = Date.now();
11362
11630
  const buckets = emptyBuckets();
11631
+ const { terminalStatuses: terminalStatuses3 } = await getStatusConfig();
11632
+ const staleThresholds = resolveStaleThresholds((await readConfig()).staleness);
11363
11633
  const newestPool = [];
11364
11634
  for (const record of projectRecords) {
11365
11635
  if (isProjectArchived(record.summary)) continue;
@@ -11368,6 +11638,7 @@ async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standal
11368
11638
  depMap.set(a.slug, a.status);
11369
11639
  }
11370
11640
  const visibleAssignments = activeAssignments(record.assignments);
11641
+ const projectPath = resolve20(projectsDir2, record.summary.slug);
11371
11642
  const resolvedTransitions = await Promise.all(
11372
11643
  visibleAssignments.map(async (assignment) => {
11373
11644
  const t0 = traces ? performance.now() : 0;
@@ -11379,13 +11650,25 @@ async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standal
11379
11650
  { traces, dependencyStatusMap: depMap }
11380
11651
  );
11381
11652
  if (traces) accumulatePhase(traces, "get-available-transitions", performance.now() - t0);
11382
- return { assignment, availableTransitions };
11653
+ const depsSatisfied = assignment.dependsOn.length === 0 ? true : (await getUnmetDependencies(projectPath, assignment.dependsOn, terminalStatuses3, depMap)).length === 0;
11654
+ const lastActivityMs = await readProgressActivityMs(
11655
+ resolve20(projectPath, "assignments", assignment.slug, "progress.md"),
11656
+ now
11657
+ );
11658
+ return { assignment, availableTransitions, depsSatisfied, lastActivityMs };
11383
11659
  })
11384
11660
  );
11385
- for (const { assignment, availableTransitions } of resolvedTransitions) {
11661
+ for (const { assignment, availableTransitions, depsSatisfied, lastActivityMs } of resolvedTransitions) {
11386
11662
  const segmentId = STATUS_TO_SEGMENT[assignment.status];
11387
- const stale = isStale(assignment.updated);
11388
- const isTerminal = TERMINAL_STATUSES2.has(assignment.status);
11663
+ const isTerminal = terminalStatuses3.has(assignment.status);
11664
+ const staleReasons = classifyAssignmentRecord(
11665
+ assignment,
11666
+ terminalStatuses3,
11667
+ depsSatisfied,
11668
+ lastActivityMs,
11669
+ staleThresholds
11670
+ );
11671
+ const stale = staleReasons.length > 0;
11389
11672
  const agingMs = Math.max(0, now - parseTimestamp(assignment.updated));
11390
11673
  const baseId = `${record.summary.slug}:${assignment.slug}`;
11391
11674
  const shared = {
@@ -11414,11 +11697,12 @@ async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standal
11414
11697
  buckets[segmentId].push(primary);
11415
11698
  }
11416
11699
  if (stale && !isTerminal) {
11700
+ const top = topStaleReason(staleReasons);
11417
11701
  const staleItem = {
11418
11702
  ...shared,
11419
11703
  id: `${baseId}:stale`,
11420
11704
  severity: "low",
11421
- reason: SEGMENT_REASON.stale,
11705
+ reason: top?.label ?? SEGMENT_REASON.stale,
11422
11706
  segment: "stale"
11423
11707
  };
11424
11708
  buckets.stale.push(staleItem);
@@ -11442,14 +11726,22 @@ async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standal
11442
11726
  const t0 = traces ? performance.now() : 0;
11443
11727
  const availableTransitions = await getStandaloneAvailableTransitions(sr.record);
11444
11728
  if (traces) accumulatePhase(traces, "get-available-transitions", performance.now() - t0);
11445
- return { sr, availableTransitions };
11729
+ const lastActivityMs = await readProgressActivityMs(resolve20(sr.assignmentDir, "progress.md"), now);
11730
+ return { sr, availableTransitions, lastActivityMs };
11446
11731
  })
11447
11732
  );
11448
- for (const { sr, availableTransitions } of resolvedStandaloneTransitions) {
11733
+ for (const { sr, availableTransitions, lastActivityMs } of resolvedStandaloneTransitions) {
11449
11734
  const assignment = sr.record;
11450
11735
  const segmentId = STATUS_TO_SEGMENT[assignment.status];
11451
- const stale = isStale(assignment.updated);
11452
- const isTerminal = TERMINAL_STATUSES2.has(assignment.status);
11736
+ const isTerminal = terminalStatuses3.has(assignment.status);
11737
+ const staleReasons = classifyAssignmentRecord(
11738
+ assignment,
11739
+ terminalStatuses3,
11740
+ true,
11741
+ lastActivityMs,
11742
+ staleThresholds
11743
+ );
11744
+ const stale = staleReasons.length > 0;
11453
11745
  const agingMs = Math.max(0, now - parseTimestamp(assignment.updated));
11454
11746
  const baseId = `standalone:${sr.id}`;
11455
11747
  const shared = {
@@ -11477,11 +11769,12 @@ async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standal
11477
11769
  });
11478
11770
  }
11479
11771
  if (stale && !isTerminal) {
11772
+ const top = topStaleReason(staleReasons);
11480
11773
  buckets.stale.push({
11481
11774
  ...shared,
11482
11775
  id: `${baseId}:stale`,
11483
11776
  severity: "low",
11484
- reason: SEGMENT_REASON.stale,
11777
+ reason: top?.label ?? SEGMENT_REASON.stale,
11485
11778
  segment: "stale"
11486
11779
  });
11487
11780
  }
@@ -11602,13 +11895,6 @@ function parseTimestamp(timestamp) {
11602
11895
  const parsed = Date.parse(timestamp);
11603
11896
  return Number.isFinite(parsed) ? parsed : 0;
11604
11897
  }
11605
- function isStale(updated) {
11606
- const timestamp = parseTimestamp(updated);
11607
- if (timestamp === 0) {
11608
- return false;
11609
- }
11610
- return Date.now() - timestamp > STALE_ASSIGNMENT_MS;
11611
- }
11612
11898
  async function countOpenQuestions(projectPath, assignmentSlug) {
11613
11899
  const commentsPath = resolve20(
11614
11900
  projectPath,
@@ -11727,7 +12013,7 @@ async function getPlaybookDetail(playbooksDir3, slug) {
11727
12013
  enabled
11728
12014
  };
11729
12015
  }
11730
- var WorkspaceBlockedError, STALE_ASSIGNMENT_MS, RECENT_PROJECTS_LIMIT, RECENT_ACTIVITY_LIMIT, RECENT_SESSIONS_LIMIT, NEWEST_CREATED_LIMIT, SEGMENT_DISPLAY_CAP, STALE_LIMIT_DEFAULT, STALE_LIMIT_MAX, TERMINAL_STATUSES2, STATUS_TO_SEGMENT, HERO_PRIORITY, projectRecordsCache, standaloneRecordsCache, DEFAULT_TRANSITION_DEFINITIONS, _cachedConfig, REFERENCED_BY_LIMIT, migratedProjectsDirs, DEFAULT_GRAPH_COLORS;
12016
+ var WorkspaceBlockedError, RECENT_PROJECTS_LIMIT, RECENT_ACTIVITY_LIMIT, RECENT_SESSIONS_LIMIT, NEWEST_CREATED_LIMIT, SEGMENT_DISPLAY_CAP, STALE_LIMIT_DEFAULT, STALE_LIMIT_MAX, STATUS_TO_SEGMENT, HERO_PRIORITY, projectRecordsCache, standaloneRecordsCache, DEFAULT_TRANSITION_DEFINITIONS, _cachedConfig, REFERENCED_BY_LIMIT, migratedProjectsDirs, DEFAULT_GRAPH_COLORS, STALE_SEVERITY_RANK;
11731
12017
  var init_api = __esm({
11732
12018
  "src/dashboard/api.ts"() {
11733
12019
  "use strict";
@@ -11745,6 +12031,7 @@ var init_api = __esm({
11745
12031
  init_help();
11746
12032
  init_agent_sessions();
11747
12033
  init_overviewCopy();
12034
+ init_classify();
11748
12035
  WorkspaceBlockedError = class extends Error {
11749
12036
  blockedBy;
11750
12037
  constructor(blockedBy) {
@@ -11755,7 +12042,6 @@ var init_api = __esm({
11755
12042
  this.blockedBy = blockedBy;
11756
12043
  }
11757
12044
  };
11758
- STALE_ASSIGNMENT_MS = 7 * 24 * 60 * 60 * 1e3;
11759
12045
  RECENT_PROJECTS_LIMIT = 6;
11760
12046
  RECENT_ACTIVITY_LIMIT = 12;
11761
12047
  RECENT_SESSIONS_LIMIT = 10;
@@ -11763,7 +12049,6 @@ var init_api = __esm({
11763
12049
  SEGMENT_DISPLAY_CAP = 5;
11764
12050
  STALE_LIMIT_DEFAULT = 50;
11765
12051
  STALE_LIMIT_MAX = 200;
11766
- TERMINAL_STATUSES2 = /* @__PURE__ */ new Set(["completed", "failed", "archived"]);
11767
12052
  STATUS_TO_SEGMENT = {
11768
12053
  review: "readyForReview",
11769
12054
  ready_to_implement: "readyToImplement",
@@ -11856,6 +12141,7 @@ var init_api = __esm({
11856
12141
  failed: "fill:#9f2d2d,stroke:#651616,color:#ffffff",
11857
12142
  review: "fill:#c6911e,stroke:#7a5a10,color:#ffffff"
11858
12143
  };
12144
+ STALE_SEVERITY_RANK = { high: 3, medium: 2, low: 1 };
11859
12145
  }
11860
12146
  });
11861
12147
 
@@ -11866,12 +12152,13 @@ __export(recompute_exports, {
11866
12152
  markDeriveMigrated: () => markDeriveMigrated,
11867
12153
  recomputeAll: () => recomputeAll,
11868
12154
  recomputeAndWrite: () => recomputeAndWrite,
12155
+ recomputeAssignmentDir: () => recomputeAssignmentDir,
11869
12156
  recomputeDependents: () => recomputeDependents,
11870
12157
  resolveDeriveContext: () => resolveDeriveContext
11871
12158
  });
11872
12159
  import { createHash as createHash2 } from "crypto";
11873
- import { open, readdir as readdir13, readFile as readFile18, unlink as unlink5, stat as stat2 } from "fs/promises";
11874
- import { dirname as dirname5, resolve as resolve25 } from "path";
12160
+ import { open, readdir as readdir13, readFile as readFile18, unlink as unlink5, stat as stat3 } from "fs/promises";
12161
+ import { basename as basename3, dirname as dirname5, resolve as resolve25 } from "path";
11875
12162
  async function isDeriveMigrated() {
11876
12163
  return fileExists(resolve25(syntaurRoot(), MIGRATION_MARKER));
11877
12164
  }
@@ -11911,7 +12198,7 @@ async function acquireLock(assignmentDir) {
11911
12198
  const code = err2.code;
11912
12199
  if (code !== "EEXIST") throw err2;
11913
12200
  try {
11914
- const info = await stat2(lockPath);
12201
+ const info = await stat3(lockPath);
11915
12202
  if (Date.now() - info.mtimeMs > LOCK_STALE_MS) {
11916
12203
  await unlink5(lockPath).catch(() => {
11917
12204
  });
@@ -12103,6 +12390,18 @@ async function recomputeAll(projectsDir2, standaloneDir, opts) {
12103
12390
  }
12104
12391
  return summary;
12105
12392
  }
12393
+ async function recomputeAssignmentDir(assignmentDir, cause, by) {
12394
+ try {
12395
+ const assignmentPath = resolve25(assignmentDir, "assignment.md");
12396
+ if (!await fileExists(assignmentPath)) return null;
12397
+ const parent = dirname5(assignmentDir);
12398
+ const projectDir = basename3(parent) === "assignments" ? dirname5(parent) : null;
12399
+ const context = await resolveDeriveContext();
12400
+ return await recomputeAndWrite(assignmentPath, { cause, by, projectDir, context });
12401
+ } catch {
12402
+ return null;
12403
+ }
12404
+ }
12106
12405
  var LOCK_FILE, LOCK_STALE_MS, LOCK_WAIT_MS, LOCK_MAX_WAITS, CAS_RETRIES, MIGRATION_MARKER;
12107
12406
  var init_recompute = __esm({
12108
12407
  "src/lifecycle/recompute.ts"() {
@@ -12447,14 +12746,14 @@ var init_launch = __esm({
12447
12746
  });
12448
12747
 
12449
12748
  // src/usage/cwd-extractor.ts
12450
- import { open as open3, readdir as readdir14, stat as stat3 } from "fs/promises";
12749
+ import { open as open3, readdir as readdir14, stat as stat4 } from "fs/promises";
12451
12750
  import { join as join4 } from "path";
12452
12751
  import { homedir as homedir3 } from "os";
12453
12752
  async function extractClaudeSessionMeta(jsonlPath) {
12454
12753
  const cwd = await derivePathFromTranscript(jsonlPath);
12455
12754
  if (!cwd) return null;
12456
- const basename9 = jsonlPath.split("/").pop() ?? "";
12457
- const sessionId = basename9.replace(/\.jsonl$/, "");
12755
+ const basename10 = jsonlPath.split("/").pop() ?? "";
12756
+ const sessionId = basename10.replace(/\.jsonl$/, "");
12458
12757
  if (!sessionId) return null;
12459
12758
  const startTs = await readFirstTimestamp(jsonlPath);
12460
12759
  const endTs = await readLastTimestamp(jsonlPath);
@@ -12549,8 +12848,8 @@ async function* walkClaudeProjects(opts = {}) {
12549
12848
  async function* walkCodexSessions(opts = {}) {
12550
12849
  const root = resolveCodexSessionsRoot(opts.root);
12551
12850
  for await (const filePath of walkJsonlRecursive(root)) {
12552
- const basename9 = filePath.split("/").pop() ?? "";
12553
- if (!basename9.endsWith(".jsonl")) continue;
12851
+ const basename10 = filePath.split("/").pop() ?? "";
12852
+ if (!basename10.endsWith(".jsonl")) continue;
12554
12853
  if (opts.sinceMtimeMs !== void 0) {
12555
12854
  const mtime = await mtimeMs(filePath);
12556
12855
  if (mtime !== null && mtime < opts.sinceMtimeMs) continue;
@@ -12568,10 +12867,10 @@ function resolveCodexSessionsRoot(override) {
12568
12867
  return join4(homedir3(), ".codex", "sessions");
12569
12868
  }
12570
12869
  async function extractPiSessionMeta(jsonlPath) {
12571
- const basename9 = jsonlPath.split("/").pop() ?? "";
12572
- const underscoreIdx = basename9.lastIndexOf("_");
12870
+ const basename10 = jsonlPath.split("/").pop() ?? "";
12871
+ const underscoreIdx = basename10.lastIndexOf("_");
12573
12872
  if (underscoreIdx === -1) return null;
12574
- const sessionId = basename9.slice(underscoreIdx + 1).replace(/\.jsonl$/, "");
12873
+ const sessionId = basename10.slice(underscoreIdx + 1).replace(/\.jsonl$/, "");
12575
12874
  if (!sessionId) return null;
12576
12875
  let handle;
12577
12876
  try {
@@ -12686,7 +12985,7 @@ async function* walkJsonlRecursive(root) {
12686
12985
  }
12687
12986
  async function mtimeMs(path) {
12688
12987
  try {
12689
- const s = await stat3(path);
12988
+ const s = await stat4(path);
12690
12989
  return s.mtimeMs;
12691
12990
  } catch {
12692
12991
  return null;
@@ -13733,6 +14032,40 @@ var init_scanner2 = __esm({
13733
14032
  }
13734
14033
  });
13735
14034
 
14035
+ // src/staleness/watchdog.ts
14036
+ var watchdog_exports = {};
14037
+ __export(watchdog_exports, {
14038
+ runStalenessWatchdogTick: () => runStalenessWatchdogTick
14039
+ });
14040
+ function runStalenessWatchdogTick(candidates, seen, emit) {
14041
+ const staleNow = /* @__PURE__ */ new Map();
14042
+ for (const c2 of candidates) {
14043
+ if (c2.reasons.length > 0) staleNow.set(c2.assignmentId, c2);
14044
+ }
14045
+ let newlyStale = 0;
14046
+ for (const [id, c2] of staleNow) {
14047
+ if (!seen.has(id)) {
14048
+ seen.add(id);
14049
+ newlyStale++;
14050
+ emit({ assignmentId: id, projectSlug: c2.projectSlug, type: "staleness-detected", reasons: c2.reasons });
14051
+ }
14052
+ }
14053
+ let cleared = 0;
14054
+ for (const id of [...seen]) {
14055
+ if (!staleNow.has(id)) {
14056
+ seen.delete(id);
14057
+ cleared++;
14058
+ emit({ assignmentId: id, projectSlug: null, type: "staleness-cleared", reasons: [] });
14059
+ }
14060
+ }
14061
+ return { scanned: candidates.length, stale: staleNow.size, newlyStale, cleared };
14062
+ }
14063
+ var init_watchdog = __esm({
14064
+ "src/staleness/watchdog.ts"() {
14065
+ "use strict";
14066
+ }
14067
+ });
14068
+
13736
14069
  // scripts/install-macos-url-handler.mjs
13737
14070
  var install_macos_url_handler_exports = {};
13738
14071
  __export(install_macos_url_handler_exports, {
@@ -13749,7 +14082,7 @@ import {
13749
14082
  unlinkSync
13750
14083
  } from "fs";
13751
14084
  import { fileURLToPath as fileURLToPath9, pathToFileURL } from "url";
13752
- import { dirname as dirname21, resolve as resolve71, join as join16 } from "path";
14085
+ import { dirname as dirname21, resolve as resolve72, join as join16 } from "path";
13753
14086
  import { homedir as homedir13, tmpdir as tmpdir2 } from "os";
13754
14087
  import { spawnSync as spawnSync7 } from "child_process";
13755
14088
  function syntaurRootMjs() {
@@ -13771,7 +14104,7 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
13771
14104
  }
13772
14105
  const stateRoot = syntaurRootMjs();
13773
14106
  mkdirSync3(stateRoot, { recursive: true });
13774
- const lockPath = resolve71(stateRoot, "install-url-handler.lock");
14107
+ const lockPath = resolve72(stateRoot, "install-url-handler.lock");
13775
14108
  let lockFd;
13776
14109
  try {
13777
14110
  lockFd = openSync(lockPath, "wx");
@@ -13787,8 +14120,8 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
13787
14120
  throw err2;
13788
14121
  }
13789
14122
  try {
13790
- const pkgRoot = resolve71(dirname21(fileURLToPath9(import.meta.url)), "..");
13791
- const cliBin = realpathSync3(resolve71(pkgRoot, "bin/syntaur.js"));
14123
+ const pkgRoot = resolve72(dirname21(fileURLToPath9(import.meta.url)), "..");
14124
+ const cliBin = realpathSync3(resolve72(pkgRoot, "bin/syntaur.js"));
13792
14125
  const nodeBin = process.execPath;
13793
14126
  const bundleParent = join16(
13794
14127
  homedir13(),
@@ -15967,7 +16300,7 @@ init_timestamp();
15967
16300
  init_fs();
15968
16301
  init_git_worktree();
15969
16302
  import { Router as Router2 } from "express";
15970
- import { resolve as resolve26, basename as basename3, isAbsolute as isAbsolute4 } from "path";
16303
+ import { resolve as resolve26, basename as basename4, isAbsolute as isAbsolute4 } from "path";
15971
16304
  import { rm, readFile as readFile19, open as fsOpen, stat as fsStat, realpath as fsRealpath } from "fs/promises";
15972
16305
  import { spawnSync as spawnSync3 } from "child_process";
15973
16306
 
@@ -16771,7 +17104,7 @@ function createWriteRouter(projectsDir2, assignmentsDir2, todosDir2) {
16771
17104
  res.status(404).json({ error: `Project "${slug}" not found` });
16772
17105
  return;
16773
17106
  }
16774
- const document = await getEditableDocument(projectsDir2, "memory", basename3(projectDir), itemSlug);
17107
+ const document = await getEditableDocument(projectsDir2, "memory", basename4(projectDir), itemSlug);
16775
17108
  if (!document) {
16776
17109
  res.status(404).json({ error: "Memory not found" });
16777
17110
  return;
@@ -16790,7 +17123,7 @@ function createWriteRouter(projectsDir2, assignmentsDir2, todosDir2) {
16790
17123
  res.status(404).json({ error: `Project "${slug}" not found` });
16791
17124
  return;
16792
17125
  }
16793
- const document = await getEditableDocument(projectsDir2, "resource", basename3(projectDir), itemSlug);
17126
+ const document = await getEditableDocument(projectsDir2, "resource", basename4(projectDir), itemSlug);
16794
17127
  if (!document) {
16795
17128
  res.status(404).json({ error: "Resource not found" });
16796
17129
  return;
@@ -16875,7 +17208,7 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
16875
17208
  let content = renderItemStub(kind, {
16876
17209
  slug: requestedSlug,
16877
17210
  name,
16878
- projectSlug: basename3(projectDir),
17211
+ projectSlug: basename4(projectDir),
16879
17212
  timestamp
16880
17213
  });
16881
17214
  const customBody = typeof body.body === "string" ? body.body : "";
@@ -16892,13 +17225,13 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
16892
17225
  } catch (err2) {
16893
17226
  if (err2.code === "EEXIST") {
16894
17227
  res.status(409).json({
16895
- error: `${kind === "memory" ? "Memory" : "Resource"} with slug "${requestedSlug}" already exists in project "${basename3(projectDir)}".`
17228
+ error: `${kind === "memory" ? "Memory" : "Resource"} with slug "${requestedSlug}" already exists in project "${basename4(projectDir)}".`
16896
17229
  });
16897
17230
  return;
16898
17231
  }
16899
17232
  throw err2;
16900
17233
  }
16901
- res.status(201).json({ slug: requestedSlug, projectSlug: basename3(projectDir), content });
17234
+ res.status(201).json({ slug: requestedSlug, projectSlug: basename4(projectDir), content });
16902
17235
  } catch (error) {
16903
17236
  console.error(`Error creating ${kind}:`, error);
16904
17237
  res.status(500).json({ error: `Failed to create ${kind}: ${error.message}` });
@@ -16933,7 +17266,7 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
16933
17266
  ${nextBody}${nextBody.endsWith("\n") ? "" : "\n"}`;
16934
17267
  merged = setTopLevelField(merged, "updated", nowTimestamp());
16935
17268
  await writeFileForce(filePath, merged);
16936
- const detail = await getItemDetail(kind, basename3(projectDir), itemSlug);
17269
+ const detail = await getItemDetail(kind, basename4(projectDir), itemSlug);
16937
17270
  res.json({ [kind]: detail, content: merged });
16938
17271
  } catch (error) {
16939
17272
  console.error(`Error updating ${kind}:`, error);
@@ -20145,25 +20478,29 @@ async function resolveSessionPlan(input4, terminal) {
20145
20478
  }
20146
20479
  let cwd = session.path;
20147
20480
  let fallbackWarning = null;
20148
- if (session.projectSlug && session.assignmentSlug) {
20149
- const detail = await getAssignmentDetail(
20481
+ if (!isExistingDir(session.path)) {
20482
+ const detail = session.projectSlug && session.assignmentSlug ? await getAssignmentDetail(
20150
20483
  input4.projectsDir,
20151
20484
  session.projectSlug,
20152
20485
  session.assignmentSlug
20153
- );
20486
+ ) : session.assignmentSlug ? await getAssignmentDetailById(
20487
+ input4.projectsDir,
20488
+ input4.assignmentsDir,
20489
+ session.assignmentSlug
20490
+ ) : null;
20154
20491
  if (detail) {
20155
20492
  const picked = resolveWorkspaceCwd({
20156
20493
  worktreePath: detail.workspace.worktreePath,
20157
20494
  repository: detail.workspace.repository,
20158
20495
  branch: detail.workspace.branch,
20159
- assignmentSlug: session.assignmentSlug
20496
+ assignmentSlug: detail.slug
20160
20497
  });
20161
20498
  if (picked.cwd !== null) {
20162
20499
  cwd = picked.cwd;
20163
20500
  fallbackWarning = picked.fallbackWarning;
20164
20501
  } else {
20165
20502
  fallbackWarning = formatFallbackCwdWarning({
20166
- assignmentSlug: session.assignmentSlug,
20503
+ assignmentSlug: detail.slug,
20167
20504
  workspaceDir: session.path,
20168
20505
  worktreePath: detail.workspace.worktreePath,
20169
20506
  branch: detail.workspace.branch
@@ -20200,7 +20537,7 @@ async function resolveSessionPlan(input4, terminal) {
20200
20537
  init_launch();
20201
20538
  import { spawn as spawn3 } from "child_process";
20202
20539
  import { homedir as homedir5 } from "os";
20203
- import { basename as basename4, join as join6, resolve as resolve29 } from "path";
20540
+ import { basename as basename5, join as join6, resolve as resolve29 } from "path";
20204
20541
  init_fs();
20205
20542
  init_config2();
20206
20543
  init_session_id();
@@ -20220,7 +20557,7 @@ var TerminalNotFoundError = class extends Error {
20220
20557
  var realSpawn = (command, args, options) => spawn3(command, args, options);
20221
20558
  var WRAPPER_COMMANDS = /* @__PURE__ */ new Set(["osascript", "open", "sh"]);
20222
20559
  function isWrapperCommand(command) {
20223
- return WRAPPER_COMMANDS.has(basename4(command));
20560
+ return WRAPPER_COMMANDS.has(basename5(command));
20224
20561
  }
20225
20562
  var WRAPPER_EXIT_TIMEOUT_MS = 1500;
20226
20563
  async function executeLaunchPlan(plan, spawnFn = realSpawn) {
@@ -20248,7 +20585,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
20248
20585
  `Spawn failed: ${msg2}. Verify the terminal is installed and on PATH.`
20249
20586
  );
20250
20587
  }
20251
- await new Promise((resolve110, reject) => {
20588
+ await new Promise((resolve111, reject) => {
20252
20589
  let settled = false;
20253
20590
  let stderr = "";
20254
20591
  const finishOk = () => {
@@ -20258,7 +20595,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
20258
20595
  child.unref();
20259
20596
  } catch {
20260
20597
  }
20261
- resolve110();
20598
+ resolve111();
20262
20599
  };
20263
20600
  const finishErr = (remediation) => {
20264
20601
  if (settled) return;
@@ -20524,7 +20861,7 @@ function normalizeSlashes(p) {
20524
20861
  }
20525
20862
  function detectInstallKind(scriptUrl, opts = {}) {
20526
20863
  const realpath3 = opts.realpath ?? realpathSync.native;
20527
- const readFile81 = opts.readFile ?? ((p) => readFileSync2(p, "utf-8"));
20864
+ const readFile82 = opts.readFile ?? ((p) => readFileSync2(p, "utf-8"));
20528
20865
  const ua = opts.envUserAgent !== void 0 ? opts.envUserAgent : process.env.npm_config_user_agent ?? "";
20529
20866
  const resolved = resolveScriptPath(scriptUrl, realpath3);
20530
20867
  if (resolved === null) {
@@ -20545,7 +20882,7 @@ function detectInstallKind(scriptUrl, opts = {}) {
20545
20882
  const pkgJsonPath = join7(dir, "package.json");
20546
20883
  let raw2;
20547
20884
  try {
20548
- raw2 = readFile81(pkgJsonPath);
20885
+ raw2 = readFile82(pkgJsonPath);
20549
20886
  } catch {
20550
20887
  const parent2 = dirname7(dir);
20551
20888
  if (parent2 === dir) break;
@@ -22610,7 +22947,7 @@ async function readEvents(jobId) {
22610
22947
 
22611
22948
  // src/schedules/attempt.ts
22612
22949
  import { createHash as createHash3 } from "crypto";
22613
- import { open as open4, readFile as readFile23, stat as stat4, unlink as unlink6 } from "fs/promises";
22950
+ import { open as open4, readFile as readFile23, stat as stat5, unlink as unlink6 } from "fs/promises";
22614
22951
  import { resolve as resolve34 } from "path";
22615
22952
 
22616
22953
  // src/schedules/triggers.ts
@@ -22807,7 +23144,7 @@ async function acquireJobLock(id) {
22807
23144
  const code = err2.code;
22808
23145
  if (code !== "EEXIST") throw err2;
22809
23146
  try {
22810
- const info = await stat4(lockPath);
23147
+ const info = await stat5(lockPath);
22811
23148
  if (Date.now() - info.mtimeMs > LOCK_STALE_MS2) {
22812
23149
  await unlink6(lockPath).catch(() => {
22813
23150
  });
@@ -24474,8 +24811,8 @@ init_api();
24474
24811
  import { raw } from "express";
24475
24812
 
24476
24813
  // src/todos/attachments.ts
24477
- import { mkdir as mkdir4, readdir as readdir16, stat as stat5, rename as rename5, rm as rm4, unlink as unlink7, writeFile as writeFile6, cp } from "fs/promises";
24478
- import { resolve as resolve41, basename as basename5, dirname as dirname10, extname } from "path";
24814
+ import { mkdir as mkdir4, readdir as readdir16, stat as stat6, rename as rename5, rm as rm4, unlink as unlink7, writeFile as writeFile6, cp } from "fs/promises";
24815
+ import { resolve as resolve41, basename as basename6, dirname as dirname10, extname } from "path";
24479
24816
 
24480
24817
  // src/utils/proof-artifact-id.ts
24481
24818
  import { randomBytes as randomBytes2 } from "crypto";
@@ -24572,7 +24909,7 @@ function isSafeInlineMime(mime) {
24572
24909
  return SAFE_INLINE_MIME.has(mime);
24573
24910
  }
24574
24911
  function sanitizeAttachmentName(name) {
24575
- let n = basename5(name || "").replace(/["'\\/]/g, "_");
24912
+ let n = basename6(name || "").replace(/["'\\/]/g, "_");
24576
24913
  n = Array.from(n, (ch) => {
24577
24914
  const code = ch.charCodeAt(0);
24578
24915
  return code < 32 || code === 127 ? "_" : ch;
@@ -24595,7 +24932,7 @@ function attachmentDirFor(todosDir2, scopeId, todoId) {
24595
24932
  }
24596
24933
  async function dirExists(p) {
24597
24934
  try {
24598
- return (await stat5(p)).isDirectory();
24935
+ return (await stat6(p)).isDirectory();
24599
24936
  } catch {
24600
24937
  return false;
24601
24938
  }
@@ -24630,7 +24967,7 @@ async function listAttachments(todosDir2, scopeId, todoId) {
24630
24967
  if (!ATTACHMENT_ID_RE.test(id)) continue;
24631
24968
  const filename = stored.slice(sep2 + 2);
24632
24969
  try {
24633
- const st = await stat5(resolve41(dir, stored));
24970
+ const st = await stat6(resolve41(dir, stored));
24634
24971
  if (!st.isFile()) continue;
24635
24972
  out.push({ id, filename, mime: mimeForName(filename), size: st.size, createdAt: st.mtime.toISOString() });
24636
24973
  } catch {
@@ -25085,8 +25422,8 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
25085
25422
  router.post("/:workspace/archive", async (req2, res) => {
25086
25423
  try {
25087
25424
  const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
25088
- const { resolve: resolve110 } = await import("path");
25089
- const { readFile: readFile81 } = await import("fs/promises");
25425
+ const { resolve: resolve111 } = await import("path");
25426
+ const { readFile: readFile82 } = await import("fs/promises");
25090
25427
  const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
25091
25428
  const workspace = getWorkspaceParam(req2.params.workspace);
25092
25429
  const outcome = await wsLock(workspace, async () => {
@@ -25102,10 +25439,10 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
25102
25439
  (e) => e.itemIds.every((id) => completedIds.has(id))
25103
25440
  );
25104
25441
  const archFile = archivePath2(todosDir2, workspace, checklist.archiveInterval);
25105
- await ensureDir(resolve110(todosDir2, "archive"));
25442
+ await ensureDir(resolve111(todosDir2, "archive"));
25106
25443
  let archContent = "";
25107
25444
  if (await fileExists(archFile)) {
25108
- archContent = await readFile81(archFile, "utf-8");
25445
+ archContent = await readFile82(archFile, "utf-8");
25109
25446
  archContent = archContent.trimEnd() + "\n\n";
25110
25447
  } else {
25111
25448
  archContent = `---
@@ -25406,7 +25743,7 @@ workspace: ${workspace}
25406
25743
  const { readConfig: readConfig3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
25407
25744
  const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
25408
25745
  const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
25409
- const { readFile: readFile81 } = await import("fs/promises");
25746
+ const { readFile: readFile82 } = await import("fs/promises");
25410
25747
  const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
25411
25748
  const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
25412
25749
  let assignmentRef;
@@ -25427,7 +25764,7 @@ workspace: ${workspace}
25427
25764
  }
25428
25765
  const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
25429
25766
  if (!await fileExists2(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
25430
- let content = await readFile81(assignmentMdPath2, "utf-8");
25767
+ let content = await readFile82(assignmentMdPath2, "utf-8");
25431
25768
  content = appendTodosToAssignmentBody2(
25432
25769
  content,
25433
25770
  items.map((it) => ({
@@ -26837,7 +27174,7 @@ init_fs();
26837
27174
  init_config2();
26838
27175
  import { execFile as execFile2 } from "child_process";
26839
27176
  import { promisify as promisify2 } from "util";
26840
- import { cp as cp2, mkdtemp, rm as rm5, readFile as readFile30, writeFile as writeFile7, unlink as unlink8, stat as stat6, open as open5, rename as rename8 } from "fs/promises";
27177
+ import { cp as cp2, mkdtemp, rm as rm5, readFile as readFile30, writeFile as writeFile7, unlink as unlink8, stat as stat7, open as open5, rename as rename8 } from "fs/promises";
26841
27178
  import { resolve as resolve44, join as join9 } from "path";
26842
27179
  import { tmpdir } from "os";
26843
27180
  var exec2 = promisify2(execFile2);
@@ -26931,7 +27268,7 @@ async function cloneOrInit(repoUrl, destDir) {
26931
27268
  }
26932
27269
  async function copyRecursive(src, dest) {
26933
27270
  if (!await fileExists(src)) return;
26934
- const s = await stat6(src);
27271
+ const s = await stat7(src);
26935
27272
  if (s.isDirectory()) {
26936
27273
  await ensureDir(dest);
26937
27274
  await cp2(src, dest, { recursive: true, force: true });
@@ -27622,7 +27959,7 @@ async function runCcusage(opts = {}) {
27622
27959
  };
27623
27960
  }
27624
27961
  function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
27625
- return new Promise((resolve110) => {
27962
+ return new Promise((resolve111) => {
27626
27963
  const child = spawn4(binary, args, {
27627
27964
  env: env ?? process.env,
27628
27965
  stdio: ["ignore", "pipe", "pipe"]
@@ -27636,7 +27973,7 @@ function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
27636
27973
  if (settled) return;
27637
27974
  settled = true;
27638
27975
  clearTimeout(timer3);
27639
- resolve110(result);
27976
+ resolve111(result);
27640
27977
  };
27641
27978
  const timer3 = setTimeout(() => {
27642
27979
  timedOut = true;
@@ -28586,6 +28923,8 @@ function createDashboardServer(options) {
28586
28923
  });
28587
28924
  }
28588
28925
  let watcherHandle = null;
28926
+ const STALENESS_WATCHDOG_INTERVAL_MS = 5 * 60 * 1e3;
28927
+ let stalenessWatchdogTimer = null;
28589
28928
  return {
28590
28929
  async start() {
28591
28930
  const { recomputeAndWrite: recomputeAndWrite2, recomputeAll: recomputeAll2, resolveDeriveContext: resolveDeriveContext2, isDeriveMigrated: isDeriveMigrated2 } = await Promise.resolve().then(() => (init_recompute(), recompute_exports));
@@ -28682,6 +29021,38 @@ function createDashboardServer(options) {
28682
29021
  onAgentSessionsChanged: () => broadcast({ type: "agent-sessions-updated", timestamp: (/* @__PURE__ */ new Date()).toISOString() })
28683
29022
  });
28684
29023
  startUsageCollector();
29024
+ const startupConfig = await readConfig();
29025
+ if (startupConfig.stalenessWatchdog) {
29026
+ const { collectStaleCandidates: collectStaleCandidates2 } = await Promise.resolve().then(() => (init_api(), api_exports));
29027
+ const { runStalenessWatchdogTick: runStalenessWatchdogTick2 } = await Promise.resolve().then(() => (init_watchdog(), watchdog_exports));
29028
+ const { emitEvent: emitEvent2 } = await Promise.resolve().then(() => (init_event_emit(), event_emit_exports));
29029
+ const stalenessSeen = /* @__PURE__ */ new Set();
29030
+ const watchdogTick = async () => {
29031
+ if (!await migrationGate()) return;
29032
+ try {
29033
+ const candidates = await collectStaleCandidates2(projectsDir2, assignmentsDir2);
29034
+ const summary = runStalenessWatchdogTick2(candidates, stalenessSeen, (e) => {
29035
+ emitEvent2({
29036
+ assignmentId: e.assignmentId,
29037
+ projectSlug: e.projectSlug,
29038
+ type: e.type,
29039
+ actor: "system",
29040
+ details: { reasons: e.reasons.map((r) => r.kind) }
29041
+ });
29042
+ });
29043
+ if (summary.newlyStale > 0 || summary.cleared > 0) {
29044
+ console.log(
29045
+ `staleness watchdog: ${summary.newlyStale} newly stale, ${summary.cleared} cleared (${summary.stale}/${summary.scanned} stale).`
29046
+ );
29047
+ }
29048
+ } catch (err2) {
29049
+ console.error("staleness watchdog tick failed:", err2);
29050
+ }
29051
+ };
29052
+ void watchdogTick();
29053
+ stalenessWatchdogTimer = setInterval(() => void watchdogTick(), STALENESS_WATCHDOG_INTERVAL_MS);
29054
+ stalenessWatchdogTimer.unref?.();
29055
+ }
28685
29056
  return new Promise((resolvePromise, reject) => {
28686
29057
  server.on("error", (err2) => {
28687
29058
  if (err2.code === "EADDRINUSE") {
@@ -28703,6 +29074,10 @@ function createDashboardServer(options) {
28703
29074
  });
28704
29075
  },
28705
29076
  async stop() {
29077
+ if (stalenessWatchdogTimer) {
29078
+ clearInterval(stalenessWatchdogTimer);
29079
+ stalenessWatchdogTimer = null;
29080
+ }
28706
29081
  await stopAutodiscovery();
28707
29082
  await stopUsageCollector();
28708
29083
  if (watcherHandle) {
@@ -29069,17 +29444,173 @@ init_facts();
29069
29444
  init_git_worktree();
29070
29445
  init_recompute();
29071
29446
  init_event_emit();
29447
+ init_transitions();
29448
+ import { readFile as readFile34 } from "fs/promises";
29449
+ import { resolve as resolve52, basename as basename7 } from "path";
29450
+
29451
+ // src/utils/assignment-target.ts
29452
+ init_paths();
29453
+ init_fs();
29454
+ init_config2();
29455
+ init_slug();
29456
+ init_assignment_resolver();
29457
+ init_parser();
29458
+ import { resolve as resolve51 } from "path";
29072
29459
  import { readFile as readFile33 } from "fs/promises";
29073
- import { resolve as resolve51, basename as basename6 } from "path";
29460
+ var AssignmentTargetError = class extends Error {
29461
+ };
29462
+ function classifyContext(ctx) {
29463
+ if (!ctx) return "empty";
29464
+ const hasAssignment = Boolean(ctx.assignmentDir) || Boolean(ctx.assignmentSlug) || Boolean(ctx.projectSlug);
29465
+ if (hasAssignment) return "assignment";
29466
+ if (ctx.bundleId) return "bundle";
29467
+ if (ctx.sessionId || ctx.transcriptPath) return "standalone";
29468
+ return "empty";
29469
+ }
29470
+ async function readAssignmentFrontmatterId(assignmentDir) {
29471
+ const path = resolve51(assignmentDir, "assignment.md");
29472
+ if (!await fileExists(path)) return null;
29473
+ try {
29474
+ const content = await readFile33(path, "utf-8");
29475
+ const [fm] = extractFrontmatter(content);
29476
+ return getField(fm, "id");
29477
+ } catch {
29478
+ return null;
29479
+ }
29480
+ }
29481
+ async function readContextJson(cwd) {
29482
+ const path = resolve51(cwd, ".syntaur", "context.json");
29483
+ if (!await fileExists(path)) return null;
29484
+ try {
29485
+ const raw2 = await readFile33(path, "utf-8");
29486
+ return JSON.parse(raw2);
29487
+ } catch {
29488
+ return null;
29489
+ }
29490
+ }
29491
+ async function resolveAssignmentTarget(input4, opts = {}) {
29492
+ const config = await readConfig();
29493
+ const baseDir = opts.dir ? expandHome(opts.dir) : config.defaultProjectDir;
29494
+ if (opts.project) {
29495
+ if (!input4) {
29496
+ throw new AssignmentTargetError(
29497
+ "--project requires an assignment slug as a positional argument."
29498
+ );
29499
+ }
29500
+ if (!isValidSlug(opts.project)) {
29501
+ throw new AssignmentTargetError(`Invalid project slug "${opts.project}".`);
29502
+ }
29503
+ if (!isValidSlug(input4)) {
29504
+ throw new AssignmentTargetError(`Invalid assignment slug "${input4}".`);
29505
+ }
29506
+ const projectDir = resolve51(baseDir, opts.project);
29507
+ const projectMdPath = resolve51(projectDir, "project.md");
29508
+ if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
29509
+ throw new AssignmentTargetError(
29510
+ `Project "${opts.project}" not found at ${projectDir}.`
29511
+ );
29512
+ }
29513
+ const assignmentDir = resolve51(projectDir, "assignments", input4);
29514
+ const assignmentMdPath2 = resolve51(assignmentDir, "assignment.md");
29515
+ if (!await fileExists(assignmentMdPath2)) {
29516
+ throw new AssignmentTargetError(
29517
+ `Assignment "${input4}" not found in project "${opts.project}".`
29518
+ );
29519
+ }
29520
+ const id = await readAssignmentFrontmatterId(assignmentDir) ?? input4;
29521
+ return {
29522
+ assignmentDir,
29523
+ projectSlug: opts.project,
29524
+ assignmentSlug: input4,
29525
+ id,
29526
+ standalone: false,
29527
+ workspaceGroup: null
29528
+ };
29529
+ }
29530
+ if (input4) {
29531
+ const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), input4);
29532
+ if (!resolved) {
29533
+ throw new AssignmentTargetError(
29534
+ `Assignment "${input4}" not found. Provide --project <slug> + <slug> or a valid standalone UUID.`
29535
+ );
29536
+ }
29537
+ return resolved;
29538
+ }
29539
+ const cwd = opts.cwd ?? process.cwd();
29540
+ const ctx = await readContextJson(cwd);
29541
+ if (!ctx) {
29542
+ throw new AssignmentTargetError(
29543
+ "No assignment specified. Provide an argument, --project + slug, or run from a directory with .syntaur/context.json."
29544
+ );
29545
+ }
29546
+ if (classifyContext(ctx) === "bundle" && ctx.bundleId) {
29547
+ throw new AssignmentTargetError(
29548
+ `Context is bound to bundle b:${ctx.bundleId}, not an assignment. Use \`syntaur todo bundle show ${ctx.bundleId}\` or the complete-bundle skill.`
29549
+ );
29550
+ }
29551
+ if (ctx.assignmentDir) {
29552
+ const dir = expandHome(ctx.assignmentDir);
29553
+ const assignmentMdPath2 = resolve51(dir, "assignment.md");
29554
+ if (!await fileExists(assignmentMdPath2)) {
29555
+ throw new AssignmentTargetError(
29556
+ `.syntaur/context.json points to a missing assignment dir: ${dir}.`
29557
+ );
29558
+ }
29559
+ const id = await readAssignmentFrontmatterId(dir);
29560
+ if (!id || id.trim() === "") {
29561
+ throw new AssignmentTargetError(
29562
+ `.syntaur/context.json points to an assignment with no frontmatter \`id\`: ${dir}.`
29563
+ );
29564
+ }
29565
+ const assignmentSlug = ctx.assignmentSlug ?? dir.split("/").pop() ?? "";
29566
+ const projectSlug = ctx.projectSlug ?? null;
29567
+ return {
29568
+ assignmentDir: dir,
29569
+ projectSlug,
29570
+ assignmentSlug,
29571
+ id,
29572
+ standalone: projectSlug === null,
29573
+ workspaceGroup: null
29574
+ };
29575
+ }
29576
+ if (ctx.projectSlug && ctx.assignmentSlug) {
29577
+ if (!isValidSlug(ctx.projectSlug) || !isValidSlug(ctx.assignmentSlug)) {
29578
+ throw new AssignmentTargetError(
29579
+ `.syntaur/context.json contains invalid slugs: project="${ctx.projectSlug}" assignment="${ctx.assignmentSlug}".`
29580
+ );
29581
+ }
29582
+ const assignmentDir = resolve51(baseDir, ctx.projectSlug, "assignments", ctx.assignmentSlug);
29583
+ const assignmentMdPath2 = resolve51(assignmentDir, "assignment.md");
29584
+ if (!await fileExists(assignmentMdPath2)) {
29585
+ throw new AssignmentTargetError(
29586
+ `.syntaur/context.json points to a missing assignment: ${assignmentDir}.`
29587
+ );
29588
+ }
29589
+ const id = await readAssignmentFrontmatterId(assignmentDir) ?? ctx.assignmentSlug;
29590
+ return {
29591
+ assignmentDir,
29592
+ projectSlug: ctx.projectSlug,
29593
+ assignmentSlug: ctx.assignmentSlug,
29594
+ id,
29595
+ standalone: false,
29596
+ workspaceGroup: null
29597
+ };
29598
+ }
29599
+ throw new AssignmentTargetError(
29600
+ ".syntaur/context.json exists but contains neither assignmentDir nor projectSlug+assignmentSlug."
29601
+ );
29602
+ }
29603
+
29604
+ // src/commands/derive-verbs.ts
29074
29605
  async function resolveTarget(assignment, options) {
29075
29606
  const config = await readConfig();
29076
29607
  const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
29077
29608
  if (options.project) {
29078
29609
  if (!isValidSlug(options.project)) throw new Error(`Invalid project slug "${options.project}".`);
29079
29610
  if (!isValidSlug(assignment)) throw new Error(`Invalid assignment slug "${assignment}".`);
29080
- const projectDir = resolve51(baseDir, options.project);
29081
- const assignmentDir = resolve51(projectDir, "assignments", assignment);
29082
- const assignmentPath = resolve51(assignmentDir, "assignment.md");
29611
+ const projectDir = resolve52(baseDir, options.project);
29612
+ const assignmentDir = resolve52(projectDir, "assignments", assignment);
29613
+ const assignmentPath = resolve52(assignmentDir, "assignment.md");
29083
29614
  if (!await fileExists(assignmentPath)) {
29084
29615
  throw new Error(`Assignment "${assignment}" not found at ${assignmentPath}.`);
29085
29616
  }
@@ -29091,14 +29622,14 @@ async function resolveTarget(assignment, options) {
29091
29622
  }
29092
29623
  return {
29093
29624
  assignmentDir: resolved.assignmentDir,
29094
- assignmentPath: resolve51(resolved.assignmentDir, "assignment.md"),
29095
- projectDir: resolved.standalone ? null : resolve51(resolved.assignmentDir, "..", "..")
29625
+ assignmentPath: resolve52(resolved.assignmentDir, "assignment.md"),
29626
+ projectDir: resolved.standalone ? null : resolve52(resolved.assignmentDir, "..", "..")
29096
29627
  };
29097
29628
  }
29098
29629
  async function inferActor(options) {
29099
29630
  if (options.agent) return `agent:${options.agent}`;
29100
29631
  try {
29101
- const raw2 = await readFile33(resolve51(process.cwd(), ".syntaur", "context.json"), "utf-8");
29632
+ const raw2 = await readFile34(resolve52(process.cwd(), ".syntaur", "context.json"), "utf-8");
29102
29633
  const ctx = JSON.parse(raw2);
29103
29634
  if (ctx.sessionId) return `agent:${ctx.sessionId.slice(0, 8)}`;
29104
29635
  } catch {
@@ -29107,10 +29638,10 @@ async function inferActor(options) {
29107
29638
  }
29108
29639
  async function emitDeriveEvent(target, type, actor, details) {
29109
29640
  try {
29110
- const fm = parseAssignmentFrontmatter(await readFile33(target.assignmentPath, "utf-8"));
29641
+ const fm = parseAssignmentFrontmatter(await readFile34(target.assignmentPath, "utf-8"));
29111
29642
  emitEvent({
29112
29643
  assignmentId: fm.id,
29113
- projectSlug: target.projectDir ? basename6(target.projectDir) : null,
29644
+ projectSlug: target.projectDir ? basename7(target.projectDir) : null,
29114
29645
  type,
29115
29646
  actor,
29116
29647
  details
@@ -29168,7 +29699,7 @@ async function planApproveCommand(assignment, options) {
29168
29699
  throw new Error("No plan file found (plan.md / plan-v*.md). Write a plan before approving.");
29169
29700
  }
29170
29701
  approvedFile = planFile;
29171
- const planContent = await readFile33(resolve51(target2.assignmentDir, planFile), "utf-8");
29702
+ const planContent = await readFile34(resolve52(target2.assignmentDir, planFile), "utf-8");
29172
29703
  return updatePlanApproval(content, {
29173
29704
  file: planFile,
29174
29705
  digest: planDigest(planContent),
@@ -29241,7 +29772,7 @@ async function attestCommand(assignment, fact, options) {
29241
29772
  "No plan file found (plan.md / plan-v*.md). Write a plan before attesting a binds:plan fact."
29242
29773
  );
29243
29774
  }
29244
- const planContent = await readFile33(resolve51(target2.assignmentDir, planFile), "utf-8");
29775
+ const planContent = await readFile34(resolve52(target2.assignmentDir, planFile), "utf-8");
29245
29776
  record.file = planFile;
29246
29777
  record.digest = planDigest(planContent);
29247
29778
  } else if (binds === "commit") {
@@ -29291,6 +29822,15 @@ async function requestReviewCommand(assignment, options) {
29291
29822
  );
29292
29823
  }
29293
29824
  async function implementStartedCommand(assignment, options) {
29825
+ const target = await resolveTarget(assignment, options);
29826
+ const context = await resolveDeriveContext();
29827
+ const fm = parseAssignmentFrontmatter(await readFile34(target.assignmentPath, "utf-8"));
29828
+ if (fm.dependsOn.length > 0 && target.projectDir) {
29829
+ const dep = await checkDependencies(target.projectDir, fm.dependsOn, context.terminalStatuses);
29830
+ if (!dep.satisfied) {
29831
+ console.warn(`Warning: starting with unmet dependencies: ${dep.unmet.join(", ")}`);
29832
+ }
29833
+ }
29294
29834
  await assertFact(
29295
29835
  assignment,
29296
29836
  options,
@@ -29298,14 +29838,15 @@ async function implementStartedCommand(assignment, options) {
29298
29838
  (content) => {
29299
29839
  let next = updateAssignmentFile(content, { implementationStarted: true });
29300
29840
  if (options.agent) {
29301
- const fm = parseAssignmentFrontmatter(next);
29302
- if (fm.assignee === null) {
29841
+ const innerFm = parseAssignmentFrontmatter(next);
29842
+ if (innerFm.assignee === null) {
29303
29843
  next = updateAssignmentFile(next, { assignee: options.agent });
29304
29844
  }
29305
29845
  }
29306
29846
  return next;
29307
29847
  },
29308
- "Implementation started"
29848
+ "Implementation started",
29849
+ { context }
29309
29850
  );
29310
29851
  }
29311
29852
  async function blockFactCommand(assignment, options) {
@@ -29353,6 +29894,7 @@ async function statusUnpinCommand(assignment, options) {
29353
29894
  await assertFact(assignment, options, "unpin", (content) => updateOverride(content, null), "Unpinned");
29354
29895
  }
29355
29896
  async function recomputeCommand(assignment, options) {
29897
+ if (options.ifMigrated && !await isDeriveMigrated()) return;
29356
29898
  const context = await resolveDeriveContext();
29357
29899
  if (options.all) {
29358
29900
  const { recomputeAll: recomputeAll2 } = await Promise.resolve().then(() => (init_recompute(), recompute_exports));
@@ -29368,12 +29910,15 @@ async function recomputeCommand(assignment, options) {
29368
29910
  for (const w of summary.warnings) console.warn(`Warning: ${w}`);
29369
29911
  return;
29370
29912
  }
29371
- if (!assignment) throw new Error("Provide an assignment or --all.");
29372
- const target = await resolveTarget(assignment, options);
29373
- const result = await recomputeAndWrite(target.assignmentPath, {
29913
+ const resolved = await resolveAssignmentTarget(assignment, {
29914
+ project: options.project,
29915
+ dir: options.dir
29916
+ });
29917
+ const projectDir = resolved.standalone ? null : resolve52(resolved.assignmentDir, "..", "..");
29918
+ const result = await recomputeAndWrite(resolve52(resolved.assignmentDir, "assignment.md"), {
29374
29919
  cause: "recompute",
29375
29920
  by: await inferActor(options),
29376
- projectDir: target.projectDir,
29921
+ projectDir,
29377
29922
  context
29378
29923
  });
29379
29924
  reportDerived(result.changed ? "Recomputed" : "Recomputed (no change)", result);
@@ -29393,8 +29938,8 @@ init_frontmatter();
29393
29938
  init_timestamp();
29394
29939
  init_assignment_resolver();
29395
29940
  init_event_emit();
29396
- import { resolve as resolve52 } from "path";
29397
- import { readFile as readFile34, writeFile as writeFile9 } from "fs/promises";
29941
+ import { resolve as resolve53 } from "path";
29942
+ import { readFile as readFile35, writeFile as writeFile9 } from "fs/promises";
29398
29943
  async function resolveTarget2(target, options) {
29399
29944
  const config = await readConfig();
29400
29945
  const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
@@ -29405,7 +29950,7 @@ async function resolveTarget2(target, options) {
29405
29950
  if (!isValidSlug(target)) {
29406
29951
  throw new Error(`Invalid assignment slug "${target}".`);
29407
29952
  }
29408
- const assignmentMd = resolve52(baseDir, options.project, "assignments", target, "assignment.md");
29953
+ const assignmentMd = resolve53(baseDir, options.project, "assignments", target, "assignment.md");
29409
29954
  if (!await fileExists(assignmentMd)) {
29410
29955
  throw new Error(`Assignment "${target}" not found in project "${options.project}".`);
29411
29956
  }
@@ -29415,18 +29960,18 @@ async function resolveTarget2(target, options) {
29415
29960
  if (resolved) {
29416
29961
  return {
29417
29962
  kind: "assignment",
29418
- filePath: resolve52(resolved.assignmentDir, "assignment.md"),
29963
+ filePath: resolve53(resolved.assignmentDir, "assignment.md"),
29419
29964
  label: resolved.projectSlug ? `assignment "${resolved.projectSlug}/${resolved.assignmentSlug}"` : `assignment "${target}"`
29420
29965
  };
29421
29966
  }
29422
- const projectMd = resolve52(baseDir, target, "project.md");
29967
+ const projectMd = resolve53(baseDir, target, "project.md");
29423
29968
  if (await fileExists(projectMd)) {
29424
29969
  return { kind: "project", filePath: projectMd, label: `project "${target}"` };
29425
29970
  }
29426
29971
  return null;
29427
29972
  }
29428
29973
  async function writeArchiveState(filePath, archived, reason) {
29429
- const content = await readFile34(filePath, "utf-8");
29974
+ const content = await readFile35(filePath, "utf-8");
29430
29975
  const updated = updateAssignmentFile(content, {
29431
29976
  archived,
29432
29977
  archivedAt: archived ? nowTimestamp() : null,
@@ -29438,7 +29983,7 @@ async function writeArchiveState(filePath, archived, reason) {
29438
29983
  async function emitArchiveEvent(resolved, type, reason) {
29439
29984
  if (resolved.kind !== "assignment") return;
29440
29985
  try {
29441
- const fm = parseAssignmentFrontmatter(await readFile34(resolved.filePath, "utf-8"));
29986
+ const fm = parseAssignmentFrontmatter(await readFile35(resolved.filePath, "utf-8"));
29442
29987
  emitEvent({
29443
29988
  assignmentId: fm.id,
29444
29989
  projectSlug: fm.project,
@@ -29508,8 +30053,8 @@ init_config2();
29508
30053
  init_frontmatter();
29509
30054
  init_timestamp();
29510
30055
  init_event_emit();
29511
- import { resolve as resolve53 } from "path";
29512
- import { readdir as readdir20, readFile as readFile35 } from "fs/promises";
30056
+ import { resolve as resolve54 } from "path";
30057
+ import { readdir as readdir20, readFile as readFile36 } from "fs/promises";
29513
30058
  var PROMOTABLE_STATUSES = /* @__PURE__ */ new Set(["pending"]);
29514
30059
  function objectiveIsFleshedOut(content) {
29515
30060
  const match = content.match(/##\s+Objective\s*\n([\s\S]*?)(?=\n##\s+|$)/);
@@ -29531,11 +30076,11 @@ async function collectCandidates(baseDirs) {
29531
30076
  for (const m of projects) {
29532
30077
  if (!m.isDirectory()) continue;
29533
30078
  if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
29534
- const directAssignmentMd = resolve53(baseDir, m.name, "assignment.md");
30079
+ const directAssignmentMd = resolve54(baseDir, m.name, "assignment.md");
29535
30080
  if (await fileExists(directAssignmentMd)) {
29536
30081
  const fm = await parseSafe(directAssignmentMd);
29537
30082
  if (fm && PROMOTABLE_STATUSES.has(fm.status)) {
29538
- const content = await readFile35(directAssignmentMd, "utf-8");
30083
+ const content = await readFile36(directAssignmentMd, "utf-8");
29539
30084
  if (objectiveIsFleshedOut(content) && hasAcceptanceCriteria(content)) {
29540
30085
  candidates.push({
29541
30086
  projectSlug: null,
@@ -29548,17 +30093,17 @@ async function collectCandidates(baseDirs) {
29548
30093
  }
29549
30094
  continue;
29550
30095
  }
29551
- const assignmentsDir2 = resolve53(baseDir, m.name, "assignments");
30096
+ const assignmentsDir2 = resolve54(baseDir, m.name, "assignments");
29552
30097
  if (!await fileExists(assignmentsDir2)) continue;
29553
30098
  const entries = await readdir20(assignmentsDir2, { withFileTypes: true });
29554
30099
  for (const a of entries) {
29555
30100
  if (!a.isDirectory()) continue;
29556
30101
  if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
29557
- const assignmentMd = resolve53(assignmentsDir2, a.name, "assignment.md");
30102
+ const assignmentMd = resolve54(assignmentsDir2, a.name, "assignment.md");
29558
30103
  if (!await fileExists(assignmentMd)) continue;
29559
30104
  const fm = await parseSafe(assignmentMd);
29560
30105
  if (!fm || !PROMOTABLE_STATUSES.has(fm.status)) continue;
29561
- const content = await readFile35(assignmentMd, "utf-8");
30106
+ const content = await readFile36(assignmentMd, "utf-8");
29562
30107
  if (!objectiveIsFleshedOut(content)) continue;
29563
30108
  if (!hasAcceptanceCriteria(content)) continue;
29564
30109
  candidates.push({
@@ -29575,7 +30120,7 @@ async function collectCandidates(baseDirs) {
29575
30120
  }
29576
30121
  async function parseSafe(path) {
29577
30122
  try {
29578
- const content = await readFile35(path, "utf-8");
30123
+ const content = await readFile36(path, "utf-8");
29579
30124
  return parseAssignmentFrontmatter(content);
29580
30125
  } catch {
29581
30126
  return null;
@@ -29605,7 +30150,7 @@ async function migrateStatusesCommand(options) {
29605
30150
  let migrated = 0;
29606
30151
  await withSuppressedEvents(async () => {
29607
30152
  for (const c2 of candidates) {
29608
- const content = await readFile35(c2.assignmentMd, "utf-8");
30153
+ const content = await readFile36(c2.assignmentMd, "utf-8");
29609
30154
  const updated = appendStatusHistoryEntry(
29610
30155
  updateAssignmentFile(content, {
29611
30156
  status: c2.toStatus,
@@ -29627,11 +30172,11 @@ init_config2();
29627
30172
  init_frontmatter();
29628
30173
  init_event_emit();
29629
30174
  init_types();
29630
- import { resolve as resolve54 } from "path";
29631
- import { readdir as readdir21, readFile as readFile36 } from "fs/promises";
30175
+ import { resolve as resolve55 } from "path";
30176
+ import { readdir as readdir21, readFile as readFile37 } from "fs/promises";
29632
30177
  async function parseSafe2(path) {
29633
30178
  try {
29634
- return parseAssignmentFrontmatter(await readFile36(path, "utf-8"));
30179
+ return parseAssignmentFrontmatter(await readFile37(path, "utf-8"));
29635
30180
  } catch {
29636
30181
  return null;
29637
30182
  }
@@ -29658,7 +30203,7 @@ async function collectTargets(baseDirs, terminalStatuses3) {
29658
30203
  for (const m of entries) {
29659
30204
  if (!m.isDirectory()) continue;
29660
30205
  if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
29661
- const directAssignmentMd = resolve54(baseDir, m.name, "assignment.md");
30206
+ const directAssignmentMd = resolve55(baseDir, m.name, "assignment.md");
29662
30207
  if (await fileExists(directAssignmentMd)) {
29663
30208
  if (seen.has(directAssignmentMd)) continue;
29664
30209
  const fm = await parseSafe2(directAssignmentMd);
@@ -29673,13 +30218,13 @@ async function collectTargets(baseDirs, terminalStatuses3) {
29673
30218
  }
29674
30219
  continue;
29675
30220
  }
29676
- const assignmentsBase = resolve54(baseDir, m.name, "assignments");
30221
+ const assignmentsBase = resolve55(baseDir, m.name, "assignments");
29677
30222
  if (!await fileExists(assignmentsBase)) continue;
29678
30223
  const slugs = await readdir21(assignmentsBase, { withFileTypes: true });
29679
30224
  for (const a of slugs) {
29680
30225
  if (!a.isDirectory()) continue;
29681
30226
  if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
29682
- const assignmentMd = resolve54(assignmentsBase, a.name, "assignment.md");
30227
+ const assignmentMd = resolve55(assignmentsBase, a.name, "assignment.md");
29683
30228
  if (!await fileExists(assignmentMd)) continue;
29684
30229
  if (seen.has(assignmentMd)) continue;
29685
30230
  const fm = await parseSafe2(assignmentMd);
@@ -29723,7 +30268,7 @@ async function migrateStatusHistoryCommand(options) {
29723
30268
  await withSuppressedEvents(async () => {
29724
30269
  for (const t of targets) {
29725
30270
  try {
29726
- const content = await readFile36(t.assignmentMd, "utf-8");
30271
+ const content = await readFile37(t.assignmentMd, "utf-8");
29727
30272
  if (parseAssignmentFrontmatter(content).statusHistory.length > 0) continue;
29728
30273
  const seededContent = appendStatusHistoryEntry(content, {
29729
30274
  at: t.seedAt,
@@ -29751,11 +30296,11 @@ init_fs();
29751
30296
  init_config2();
29752
30297
  init_parser();
29753
30298
  init_events_db();
29754
- import { resolve as resolve55 } from "path";
29755
- import { readdir as readdir22, readFile as readFile37 } from "fs/promises";
30299
+ import { resolve as resolve56 } from "path";
30300
+ import { readdir as readdir22, readFile as readFile38 } from "fs/promises";
29756
30301
  async function parseSafe3(path) {
29757
30302
  try {
29758
- return parseAssignmentFull(await readFile37(path, "utf-8"));
30303
+ return parseAssignmentFull(await readFile38(path, "utf-8"));
29759
30304
  } catch {
29760
30305
  return null;
29761
30306
  }
@@ -29792,7 +30337,7 @@ async function collectTargets2(baseDirs) {
29792
30337
  for (const m of entries) {
29793
30338
  if (!m.isDirectory()) continue;
29794
30339
  if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
29795
- const directAssignmentMd = resolve55(baseDir, m.name, "assignment.md");
30340
+ const directAssignmentMd = resolve56(baseDir, m.name, "assignment.md");
29796
30341
  if (await fileExists(directAssignmentMd)) {
29797
30342
  if (seen.has(directAssignmentMd)) continue;
29798
30343
  seen.add(directAssignmentMd);
@@ -29810,13 +30355,13 @@ async function collectTargets2(baseDirs) {
29810
30355
  }
29811
30356
  continue;
29812
30357
  }
29813
- const assignmentsBase = resolve55(baseDir, m.name, "assignments");
30358
+ const assignmentsBase = resolve56(baseDir, m.name, "assignments");
29814
30359
  if (!await fileExists(assignmentsBase)) continue;
29815
30360
  const slugs = await readdir22(assignmentsBase, { withFileTypes: true });
29816
30361
  for (const a of slugs) {
29817
30362
  if (!a.isDirectory()) continue;
29818
30363
  if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
29819
- const assignmentMd = resolve55(assignmentsBase, a.name, "assignment.md");
30364
+ const assignmentMd = resolve56(assignmentsBase, a.name, "assignment.md");
29820
30365
  if (!await fileExists(assignmentMd)) continue;
29821
30366
  if (seen.has(assignmentMd)) continue;
29822
30367
  seen.add(assignmentMd);
@@ -29897,8 +30442,8 @@ init_frontmatter();
29897
30442
  init_facts();
29898
30443
  init_derive();
29899
30444
  init_recompute();
29900
- import { readdir as readdir23, readFile as readFile38 } from "fs/promises";
29901
- import { resolve as resolve56 } from "path";
30445
+ import { readdir as readdir23, readFile as readFile39 } from "fs/promises";
30446
+ import { resolve as resolve57 } from "path";
29902
30447
  var IMPLEMENTATION_STATUSES = /* @__PURE__ */ new Set(["in_progress", "review", "code_review"]);
29903
30448
  var REVIEW_STATUSES = /* @__PURE__ */ new Set(["review", "code_review"]);
29904
30449
  async function listTargets(projectsDir2, standaloneDir) {
@@ -29909,15 +30454,15 @@ async function listTargets(projectsDir2, standaloneDir) {
29909
30454
  } catch {
29910
30455
  }
29911
30456
  for (const project of projects) {
29912
- const projectDir = resolve56(projectsDir2, project);
30457
+ const projectDir = resolve57(projectsDir2, project);
29913
30458
  let slugs = [];
29914
30459
  try {
29915
- slugs = await readdir23(resolve56(projectDir, "assignments"));
30460
+ slugs = await readdir23(resolve57(projectDir, "assignments"));
29916
30461
  } catch {
29917
30462
  continue;
29918
30463
  }
29919
30464
  for (const slug of slugs) {
29920
- const path = resolve56(projectDir, "assignments", slug, "assignment.md");
30465
+ const path = resolve57(projectDir, "assignments", slug, "assignment.md");
29921
30466
  if (await fileExists(path)) targets.push({ path, projectDir, ref: `${project}/${slug}` });
29922
30467
  }
29923
30468
  }
@@ -29927,7 +30472,7 @@ async function listTargets(projectsDir2, standaloneDir) {
29927
30472
  } catch {
29928
30473
  }
29929
30474
  for (const id of ids) {
29930
- const path = resolve56(standaloneDir, id, "assignment.md");
30475
+ const path = resolve57(standaloneDir, id, "assignment.md");
29931
30476
  if (await fileExists(path)) targets.push({ path, projectDir: null, ref: id });
29932
30477
  }
29933
30478
  return targets;
@@ -29982,7 +30527,7 @@ async function migrateDeriveCommand(options) {
29982
30527
  for (const target of targets) {
29983
30528
  let content;
29984
30529
  try {
29985
- content = await readFile38(target.path, "utf-8");
30530
+ content = await readFile39(target.path, "utf-8");
29986
30531
  } catch {
29987
30532
  continue;
29988
30533
  }
@@ -29997,7 +30542,7 @@ async function migrateDeriveCommand(options) {
29997
30542
  const seededFm = parseAssignmentFrontmatter(seededContent);
29998
30543
  const body = seededContent.replace(/^---\n[\s\S]*?\n---/, "");
29999
30544
  const facts = await computeFacts({
30000
- assignmentDir: resolve56(target.path, ".."),
30545
+ assignmentDir: resolve57(target.path, ".."),
30001
30546
  frontmatter: seededFm,
30002
30547
  body,
30003
30548
  projectDir: target.projectDir,
@@ -30057,7 +30602,7 @@ async function migrateDeriveCommand(options) {
30057
30602
  }
30058
30603
 
30059
30604
  // src/commands/complete.ts
30060
- import { resolve as resolve57 } from "path";
30605
+ import { resolve as resolve58 } from "path";
30061
30606
  init_config2();
30062
30607
  init_paths();
30063
30608
  init_assignment_resolver();
@@ -30071,12 +30616,12 @@ async function completeCommand(assignment, options) {
30071
30616
  let projectDir;
30072
30617
  let changedSlug;
30073
30618
  if (options.project) {
30074
- projectDir = resolve57(baseDir, options.project);
30619
+ projectDir = resolve58(baseDir, options.project);
30075
30620
  changedSlug = assignment;
30076
30621
  } else {
30077
30622
  const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), assignment);
30078
30623
  if (!resolved) return;
30079
- projectDir = resolved.standalone ? null : resolve57(resolved.assignmentDir, "..", "..");
30624
+ projectDir = resolved.standalone ? null : resolve58(resolved.assignmentDir, "..", "..");
30080
30625
  changedSlug = resolved.assignmentSlug;
30081
30626
  }
30082
30627
  if (projectDir) {
@@ -30107,7 +30652,7 @@ async function reviewCommand(assignment, options) {
30107
30652
  }
30108
30653
 
30109
30654
  // src/commands/fail.ts
30110
- import { resolve as resolve58 } from "path";
30655
+ import { resolve as resolve59 } from "path";
30111
30656
  init_config2();
30112
30657
  init_paths();
30113
30658
  init_assignment_resolver();
@@ -30121,12 +30666,12 @@ async function failCommand(assignment, options) {
30121
30666
  let projectDir;
30122
30667
  let changedSlug;
30123
30668
  if (options.project) {
30124
- projectDir = resolve58(baseDir, options.project);
30669
+ projectDir = resolve59(baseDir, options.project);
30125
30670
  changedSlug = assignment;
30126
30671
  } else {
30127
30672
  const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), assignment);
30128
30673
  if (!resolved) return;
30129
- projectDir = resolved.standalone ? null : resolve58(resolved.assignmentDir, "..", "..");
30674
+ projectDir = resolved.standalone ? null : resolve59(resolved.assignmentDir, "..", "..");
30130
30675
  changedSlug = resolved.assignmentSlug;
30131
30676
  }
30132
30677
  if (projectDir) {
@@ -30142,7 +30687,7 @@ async function failCommand(assignment, options) {
30142
30687
  }
30143
30688
 
30144
30689
  // src/commands/reopen.ts
30145
- import { resolve as resolve59 } from "path";
30690
+ import { resolve as resolve60 } from "path";
30146
30691
  init_config2();
30147
30692
  init_paths();
30148
30693
  init_assignment_resolver();
@@ -30158,14 +30703,14 @@ async function reopenCommand(assignment, options) {
30158
30703
  let projectDir;
30159
30704
  let changedSlug;
30160
30705
  if (options.project) {
30161
- projectDir = resolve59(baseDir, options.project);
30162
- assignmentPath = resolve59(projectDir, "assignments", assignment, "assignment.md");
30706
+ projectDir = resolve60(baseDir, options.project);
30707
+ assignmentPath = resolve60(projectDir, "assignments", assignment, "assignment.md");
30163
30708
  changedSlug = assignment;
30164
30709
  } else {
30165
30710
  const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), assignment);
30166
30711
  if (!resolved) return;
30167
- assignmentPath = resolve59(resolved.assignmentDir, "assignment.md");
30168
- projectDir = resolved.standalone ? null : resolve59(resolved.assignmentDir, "..", "..");
30712
+ assignmentPath = resolve60(resolved.assignmentDir, "assignment.md");
30713
+ projectDir = resolved.standalone ? null : resolve60(resolved.assignmentDir, "..", "..");
30169
30714
  changedSlug = resolved.assignmentSlug;
30170
30715
  }
30171
30716
  const derived = await recomputeAndWrite(assignmentPath, {
@@ -30199,7 +30744,7 @@ import {
30199
30744
  readdir as readdir24,
30200
30745
  symlink,
30201
30746
  lstat,
30202
- readFile as readFile39,
30747
+ readFile as readFile40,
30203
30748
  readlink,
30204
30749
  rename as rename9,
30205
30750
  rm as rm6,
@@ -30208,20 +30753,20 @@ import {
30208
30753
  } from "fs/promises";
30209
30754
  import { existsSync as existsSync4 } from "fs";
30210
30755
  import { homedir as homedir8 } from "os";
30211
- import { basename as basename7, dirname as dirname15, isAbsolute as isAbsolute7, relative as relative2, resolve as resolve61 } from "path";
30756
+ import { basename as basename8, dirname as dirname15, isAbsolute as isAbsolute7, relative as relative2, resolve as resolve62 } from "path";
30212
30757
 
30213
30758
  // src/utils/package-root.ts
30214
30759
  init_fs();
30215
- import { dirname as dirname14, resolve as resolve60 } from "path";
30760
+ import { dirname as dirname14, resolve as resolve61 } from "path";
30216
30761
  import { fileURLToPath as fileURLToPath4 } from "url";
30217
30762
  async function findPackageRoot(expectedRelativePath) {
30218
30763
  let currentDir = dirname14(fileURLToPath4(import.meta.url));
30219
30764
  while (true) {
30220
- const candidate = resolve60(currentDir, expectedRelativePath);
30765
+ const candidate = resolve61(currentDir, expectedRelativePath);
30221
30766
  if (await fileExists(candidate)) {
30222
30767
  return currentDir;
30223
30768
  }
30224
- const parentDir = resolve60(currentDir, "..");
30769
+ const parentDir = resolve61(currentDir, "..");
30225
30770
  if (parentDir === currentDir) {
30226
30771
  throw new Error(
30227
30772
  `Could not locate package root containing ${expectedRelativePath}.`
@@ -30242,25 +30787,25 @@ function getPluginManifestRelativePath(pluginKind) {
30242
30787
  }
30243
30788
  function getDefaultPluginTargetDir(pluginKind) {
30244
30789
  const home2 = homedir8();
30245
- return pluginKind === "claude" ? resolve61(home2, ".claude", "plugins", "syntaur") : resolve61(home2, "plugins", "syntaur");
30790
+ return pluginKind === "claude" ? resolve62(home2, ".claude", "plugins", "syntaur") : resolve62(home2, "plugins", "syntaur");
30246
30791
  }
30247
30792
  function getDefaultMarketplacePath() {
30248
- return resolve61(homedir8(), ".agents", "plugins", "marketplace.json");
30793
+ return resolve62(homedir8(), ".agents", "plugins", "marketplace.json");
30249
30794
  }
30250
30795
  function getClaudeMarketplacesRoot() {
30251
- return resolve61(homedir8(), ".claude", "plugins", "marketplaces");
30796
+ return resolve62(homedir8(), ".claude", "plugins", "marketplaces");
30252
30797
  }
30253
30798
  function getClaudeKnownMarketplacesPath() {
30254
- return resolve61(homedir8(), ".claude", "plugins", "known_marketplaces.json");
30799
+ return resolve62(homedir8(), ".claude", "plugins", "known_marketplaces.json");
30255
30800
  }
30256
30801
  function getClaudeInstalledPluginsPath() {
30257
- return resolve61(homedir8(), ".claude", "plugins", "installed_plugins.json");
30802
+ return resolve62(homedir8(), ".claude", "plugins", "installed_plugins.json");
30258
30803
  }
30259
30804
  function getInstallMarkerPath(targetDir) {
30260
- return resolve61(targetDir, INSTALL_MARKER_FILENAME);
30805
+ return resolve62(targetDir, INSTALL_MARKER_FILENAME);
30261
30806
  }
30262
30807
  async function readPackageManifest(packageRoot) {
30263
- const raw2 = await readFile39(resolve61(packageRoot, "package.json"), "utf-8");
30808
+ const raw2 = await readFile40(resolve62(packageRoot, "package.json"), "utf-8");
30264
30809
  return JSON.parse(raw2);
30265
30810
  }
30266
30811
  async function readJsonFileIfExists(pathValue) {
@@ -30268,7 +30813,7 @@ async function readJsonFileIfExists(pathValue) {
30268
30813
  return null;
30269
30814
  }
30270
30815
  try {
30271
- const raw2 = await readFile39(pathValue, "utf-8");
30816
+ const raw2 = await readFile40(pathValue, "utf-8");
30272
30817
  return JSON.parse(raw2);
30273
30818
  } catch {
30274
30819
  return null;
@@ -30276,15 +30821,15 @@ async function readJsonFileIfExists(pathValue) {
30276
30821
  }
30277
30822
  async function readClaudePluginManifest(pluginDir) {
30278
30823
  return await readJsonFileIfExists(
30279
- resolve61(pluginDir, ".claude-plugin", "plugin.json")
30824
+ resolve62(pluginDir, ".claude-plugin", "plugin.json")
30280
30825
  ) ?? {};
30281
30826
  }
30282
30827
  async function readPluginManifestName(targetDir, pluginKind) {
30283
- const manifestPath = resolve61(targetDir, getPluginManifestRelativePath(pluginKind));
30828
+ const manifestPath = resolve62(targetDir, getPluginManifestRelativePath(pluginKind));
30284
30829
  if (!await fileExists(manifestPath)) {
30285
30830
  return void 0;
30286
30831
  }
30287
- const raw2 = await readFile39(manifestPath, "utf-8");
30832
+ const raw2 = await readFile40(manifestPath, "utf-8");
30288
30833
  const parsed = JSON.parse(raw2);
30289
30834
  return parsed.name;
30290
30835
  }
@@ -30294,7 +30839,7 @@ async function readInstallMetadata(targetDir) {
30294
30839
  return null;
30295
30840
  }
30296
30841
  try {
30297
- const raw2 = await readFile39(markerPath, "utf-8");
30842
+ const raw2 = await readFile40(markerPath, "utf-8");
30298
30843
  return JSON.parse(raw2);
30299
30844
  } catch {
30300
30845
  return null;
@@ -30307,7 +30852,7 @@ async function getInstallStatus(targetDir, pluginKind) {
30307
30852
  const info = await lstat(targetDir);
30308
30853
  if (info.isSymbolicLink()) {
30309
30854
  const symlinkTarget = await readlink(targetDir);
30310
- const resolvedTarget = resolve61(dirname15(targetDir), symlinkTarget);
30855
+ const resolvedTarget = resolve62(dirname15(targetDir), symlinkTarget);
30311
30856
  const manifestName2 = await readPluginManifestName(resolvedTarget, pluginKind);
30312
30857
  return {
30313
30858
  exists: true,
@@ -30353,7 +30898,7 @@ async function installLink(paths) {
30353
30898
  await ensureDir(dirname15(paths.targetDir));
30354
30899
  await rm6(paths.targetDir, { recursive: true, force: true });
30355
30900
  await ensureDir(dirname15(paths.targetDir));
30356
- await symlink(resolve61(paths.sourceDir), paths.targetDir, "dir");
30901
+ await symlink(resolve62(paths.sourceDir), paths.targetDir, "dir");
30357
30902
  }
30358
30903
  async function removeInstallMarker(targetDir) {
30359
30904
  const markerPath = getInstallMarkerPath(targetDir);
@@ -30367,13 +30912,13 @@ function normalizeAbsoluteInstallPath(pathValue, label) {
30367
30912
  if (!isAbsolute7(expanded)) {
30368
30913
  throw new Error(`${label} must be an absolute path.`);
30369
30914
  }
30370
- return resolve61(expanded);
30915
+ return resolve62(expanded);
30371
30916
  }
30372
30917
  async function resolvePluginPaths(pluginKind, targetDir) {
30373
30918
  const packageRoot = await findPackageRoot(getPluginRelativePath(pluginKind));
30374
30919
  return {
30375
30920
  packageRoot,
30376
- sourceDir: resolve61(packageRoot, getPluginRelativePath(pluginKind)),
30921
+ sourceDir: resolve62(packageRoot, getPluginRelativePath(pluginKind)),
30377
30922
  targetDir: targetDir ?? getDefaultPluginTargetDir(pluginKind)
30378
30923
  };
30379
30924
  }
@@ -30417,7 +30962,7 @@ async function writeClaudeMarketplaceFile(manifestPath, marketplace) {
30417
30962
  }
30418
30963
  if (await fileExists(manifestPath)) {
30419
30964
  try {
30420
- const prev = await readFile39(manifestPath, "utf-8");
30965
+ const prev = await readFile40(manifestPath, "utf-8");
30421
30966
  JSON.parse(prev);
30422
30967
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
30423
30968
  await writeFile10(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
@@ -30498,8 +31043,8 @@ async function listClaudeMarketplaceCandidates() {
30498
31043
  if (!entry.isDirectory()) {
30499
31044
  continue;
30500
31045
  }
30501
- const candidateRoot = resolve61(rootDir, entry.name);
30502
- const manifestPath = resolve61(candidateRoot, ".claude-plugin", "marketplace.json");
31046
+ const candidateRoot = resolve62(rootDir, entry.name);
31047
+ const manifestPath = resolve62(candidateRoot, ".claude-plugin", "marketplace.json");
30503
31048
  if (!await fileExists(manifestPath)) {
30504
31049
  continue;
30505
31050
  }
@@ -30526,11 +31071,11 @@ async function listClaudeMarketplaceCandidates() {
30526
31071
  if (!installLocation) {
30527
31072
  continue;
30528
31073
  }
30529
- const candidateRoot = resolve61(expandHome(installLocation));
31074
+ const candidateRoot = resolve62(expandHome(installLocation));
30530
31075
  if (seen.has(candidateRoot)) {
30531
31076
  continue;
30532
31077
  }
30533
- const manifestPath = resolve61(candidateRoot, ".claude-plugin", "marketplace.json");
31078
+ const manifestPath = resolve62(candidateRoot, ".claude-plugin", "marketplace.json");
30534
31079
  if (!await fileExists(manifestPath)) {
30535
31080
  continue;
30536
31081
  }
@@ -30558,14 +31103,14 @@ async function getPreferredClaudeMarketplace() {
30558
31103
  name: candidate.name,
30559
31104
  rootDir: candidate.rootDir,
30560
31105
  manifestPath: candidate.manifestPath,
30561
- targetDir: resolve61(candidate.rootDir, "plugins", "syntaur")
31106
+ targetDir: resolve62(candidate.rootDir, "plugins", "syntaur")
30562
31107
  };
30563
31108
  }
30564
31109
  async function registerKnownClaudeMarketplace(name, rootDir) {
30565
31110
  const manifestPath = getClaudeKnownMarketplacesPath();
30566
31111
  let existing = {};
30567
31112
  if (await fileExists(manifestPath)) {
30568
- const raw2 = await readFile39(manifestPath, "utf-8");
31113
+ const raw2 = await readFile40(manifestPath, "utf-8");
30569
31114
  try {
30570
31115
  const parsed = JSON.parse(raw2);
30571
31116
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
@@ -30592,7 +31137,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
30592
31137
  existing[name].autoUpdate = true;
30593
31138
  await ensureDir(dirname15(manifestPath));
30594
31139
  if (await fileExists(manifestPath)) {
30595
- const prev = await readFile39(manifestPath, "utf-8");
31140
+ const prev = await readFile40(manifestPath, "utf-8");
30596
31141
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
30597
31142
  await writeFile10(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
30598
31143
  }
@@ -30606,11 +31151,11 @@ async function ensureKnownClaudeMarketplaceForRoot(options) {
30606
31151
  return registerKnownClaudeMarketplace(options.name, options.rootDir);
30607
31152
  }
30608
31153
  async function setSyntaurPluginEnabled(options) {
30609
- const settingsPath = resolve61(homedir8(), ".claude", "settings.json");
31154
+ const settingsPath = resolve62(homedir8(), ".claude", "settings.json");
30610
31155
  const key = `syntaur@${options.marketplaceName}`;
30611
31156
  let parsed = {};
30612
31157
  if (await fileExists(settingsPath)) {
30613
- const raw2 = await readFile39(settingsPath, "utf-8");
31158
+ const raw2 = await readFile40(settingsPath, "utf-8");
30614
31159
  try {
30615
31160
  parsed = JSON.parse(raw2);
30616
31161
  } catch {
@@ -30629,7 +31174,7 @@ async function setSyntaurPluginEnabled(options) {
30629
31174
  await ensureDir(dirname15(settingsPath));
30630
31175
  if (await fileExists(settingsPath)) {
30631
31176
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
30632
- const prev = await readFile39(settingsPath, "utf-8");
31177
+ const prev = await readFile40(settingsPath, "utf-8");
30633
31178
  await writeFile10(`${settingsPath}.bak-${stamp}`, prev, "utf-8");
30634
31179
  }
30635
31180
  const tmpPath = `${settingsPath}.tmp`;
@@ -30643,9 +31188,9 @@ async function ensureClaudeUserMarketplace() {
30643
31188
  if (existing) {
30644
31189
  return existing;
30645
31190
  }
30646
- const rootDir = resolve61(getClaudeMarketplacesRoot(), "user-plugins");
30647
- const manifestPath = resolve61(rootDir, ".claude-plugin", "marketplace.json");
30648
- await ensureDir(resolve61(rootDir, "plugins"));
31191
+ const rootDir = resolve62(getClaudeMarketplacesRoot(), "user-plugins");
31192
+ const manifestPath = resolve62(rootDir, ".claude-plugin", "marketplace.json");
31193
+ await ensureDir(resolve62(rootDir, "plugins"));
30649
31194
  if (!await fileExists(manifestPath)) {
30650
31195
  const scaffold = {
30651
31196
  plugins: []
@@ -30664,22 +31209,22 @@ async function ensureClaudeUserMarketplace() {
30664
31209
  name: "user-plugins",
30665
31210
  rootDir,
30666
31211
  manifestPath,
30667
- targetDir: resolve61(rootDir, "plugins", "syntaur")
31212
+ targetDir: resolve62(rootDir, "plugins", "syntaur")
30668
31213
  };
30669
31214
  }
30670
31215
  async function detectClaudeMarketplaceForTarget(targetDir) {
30671
31216
  const normalizedTargetDir = normalizeAbsoluteInstallPath(targetDir, "Claude plugin target");
30672
31217
  const pluginsDir = dirname15(normalizedTargetDir);
30673
- if (basename7(pluginsDir) !== "plugins") {
31218
+ if (basename8(pluginsDir) !== "plugins") {
30674
31219
  return null;
30675
31220
  }
30676
31221
  const rootDir = dirname15(pluginsDir);
30677
- const manifestPath = resolve61(rootDir, ".claude-plugin", "marketplace.json");
31222
+ const manifestPath = resolve62(rootDir, ".claude-plugin", "marketplace.json");
30678
31223
  if (!await fileExists(manifestPath)) {
30679
31224
  return null;
30680
31225
  }
30681
31226
  const marketplace = await readClaudeMarketplaceFile(manifestPath);
30682
- const name = typeof marketplace.name === "string" && marketplace.name.trim() !== "" ? marketplace.name : basename7(rootDir);
31227
+ const name = typeof marketplace.name === "string" && marketplace.name.trim() !== "" ? marketplace.name : basename8(rootDir);
30683
31228
  return {
30684
31229
  name,
30685
31230
  rootDir,
@@ -30690,7 +31235,7 @@ async function detectClaudeMarketplaceForTarget(targetDir) {
30690
31235
  async function findManagedClaudeMarketplacePluginDir() {
30691
31236
  const marketplaces = await listClaudeMarketplaceCandidates();
30692
31237
  for (const marketplace of marketplaces) {
30693
- const targetDir = resolve61(marketplace.rootDir, "plugins", "syntaur");
31238
+ const targetDir = resolve62(marketplace.rootDir, "plugins", "syntaur");
30694
31239
  const status = await getInstallStatus(targetDir, "claude");
30695
31240
  if (status.exists && status.managed) {
30696
31241
  return targetDir;
@@ -30815,7 +31360,7 @@ async function installManagedPlugin(options) {
30815
31360
  `${paths.targetDir} already exists and is not a Syntaur-managed install. Remove it manually before installing Syntaur there.`
30816
31361
  );
30817
31362
  }
30818
- if (desiredMode === "link" && existing.exists && existing.installMode === "link" && existing.symlinkTarget === resolve61(paths.sourceDir) && !force) {
31363
+ if (desiredMode === "link" && existing.exists && existing.installMode === "link" && existing.symlinkTarget === resolve62(paths.sourceDir) && !force) {
30819
31364
  return {
30820
31365
  targetDir: paths.targetDir,
30821
31366
  sourceDir: paths.sourceDir,
@@ -30867,7 +31412,7 @@ async function readMarketplaceFile(marketplacePath) {
30867
31412
  plugins: []
30868
31413
  };
30869
31414
  }
30870
- const raw2 = await readFile39(marketplacePath, "utf-8");
31415
+ const raw2 = await readFile40(marketplacePath, "utf-8");
30871
31416
  const parsed = JSON.parse(raw2);
30872
31417
  return {
30873
31418
  name: parsed.name ?? "local",
@@ -31034,13 +31579,13 @@ async function recommendMarketplacePath() {
31034
31579
  return configuredOrManaged ?? getDefaultMarketplacePath();
31035
31580
  }
31036
31581
  async function isSyntaurDataInstalled() {
31037
- return fileExists(resolve61(syntaurRoot(), "config.md"));
31582
+ return fileExists(resolve62(syntaurRoot(), "config.md"));
31038
31583
  }
31039
31584
  async function removeSyntaurData() {
31040
31585
  await rm6(syntaurRoot(), { recursive: true, force: true });
31041
31586
  }
31042
31587
  async function getConfiguredProjectDir() {
31043
- if (!await fileExists(resolve61(syntaurRoot(), "config.md"))) {
31588
+ if (!await fileExists(resolve62(syntaurRoot(), "config.md"))) {
31044
31589
  return null;
31045
31590
  }
31046
31591
  return (await readConfig()).defaultProjectDir;
@@ -31109,24 +31654,24 @@ async function textPrompt(question, defaultValue) {
31109
31654
 
31110
31655
  // src/utils/install-skills.ts
31111
31656
  init_fs();
31112
- import { readFile as readFile41, readdir as readdir25, mkdir as mkdir7, copyFile, rm as rm7, lstat as lstat2 } from "fs/promises";
31113
- import { resolve as resolve63, relative as relative3, join as join11 } from "path";
31657
+ import { readFile as readFile42, readdir as readdir25, mkdir as mkdir7, copyFile, rm as rm7, lstat as lstat2 } from "fs/promises";
31658
+ import { resolve as resolve64, relative as relative3, join as join11 } from "path";
31114
31659
  import { homedir as homedir10 } from "os";
31115
31660
 
31116
31661
  // src/utils/plugin-state.ts
31117
31662
  init_fs();
31118
- import { readFile as readFile40 } from "fs/promises";
31119
- import { resolve as resolve62 } from "path";
31663
+ import { readFile as readFile41 } from "fs/promises";
31664
+ import { resolve as resolve63 } from "path";
31120
31665
  import { homedir as homedir9 } from "os";
31121
31666
  function settingsPathFor(agent) {
31122
- if (agent === "claude") return resolve62(homedir9(), ".claude", "settings.json");
31667
+ if (agent === "claude") return resolve63(homedir9(), ".claude", "settings.json");
31123
31668
  return null;
31124
31669
  }
31125
31670
  async function readJsonOrNull(path) {
31126
31671
  if (!path) return null;
31127
31672
  if (!await fileExists(path)) return null;
31128
31673
  try {
31129
- const raw2 = await readFile40(path, "utf-8");
31674
+ const raw2 = await readFile41(path, "utf-8");
31130
31675
  return JSON.parse(raw2);
31131
31676
  } catch {
31132
31677
  return null;
@@ -31176,11 +31721,11 @@ var KNOWN_SKILL_NAMES = [
31176
31721
  var KNOWN_SKILLS = KNOWN_SKILL_NAMES;
31177
31722
  async function getSkillsDir() {
31178
31723
  const packageRoot = await findPackageRoot("skills");
31179
- return resolve63(packageRoot, "skills");
31724
+ return resolve64(packageRoot, "skills");
31180
31725
  }
31181
31726
  function defaultSkillTargetDir(target) {
31182
- if (target === "claude") return resolve63(homedir10(), ".claude", "skills");
31183
- return resolve63(homedir10(), ".codex", "skills");
31727
+ if (target === "claude") return resolve64(homedir10(), ".claude", "skills");
31728
+ return resolve64(homedir10(), ".codex", "skills");
31184
31729
  }
31185
31730
  async function walkFiles(root) {
31186
31731
  const out = [];
@@ -31200,7 +31745,7 @@ async function walkFiles(root) {
31200
31745
  }
31201
31746
  async function filesEqual(a, b) {
31202
31747
  try {
31203
- const [ba, bb] = await Promise.all([readFile41(a), readFile41(b)]);
31748
+ const [ba, bb] = await Promise.all([readFile42(a), readFile42(b)]);
31204
31749
  if (ba.length !== bb.length) return false;
31205
31750
  return ba.equals(bb);
31206
31751
  } catch {
@@ -31234,8 +31779,8 @@ async function skillMatches(srcDir, destDir) {
31234
31779
  }
31235
31780
  async function isSymlink(path) {
31236
31781
  try {
31237
- const stat16 = await lstat2(path);
31238
- return stat16.isSymbolicLink();
31782
+ const stat17 = await lstat2(path);
31783
+ return stat17.isSymbolicLink();
31239
31784
  } catch {
31240
31785
  return false;
31241
31786
  }
@@ -31340,7 +31885,7 @@ async function uninstallSkills(options) {
31340
31885
  if (await isSymlink(destDir)) continue;
31341
31886
  const skillMd = join11(destDir, "SKILL.md");
31342
31887
  if (!await fileExists(skillMd)) continue;
31343
- const content = await readFile41(skillMd, "utf-8").catch(() => "");
31888
+ const content = await readFile42(skillMd, "utf-8").catch(() => "");
31344
31889
  const match = content.match(/^name:\s*(\S+)\s*$/m);
31345
31890
  if (!match || match[1] !== skill) continue;
31346
31891
  await rm7(destDir, { recursive: true, force: true });
@@ -31535,20 +32080,20 @@ import { realpathSync as realpathSync2 } from "fs";
31535
32080
  init_paths();
31536
32081
  init_fs();
31537
32082
  import { fileURLToPath as fileURLToPath6 } from "url";
31538
- import { readFile as readFile43 } from "fs/promises";
31539
- import { dirname as dirname17, join as join13, resolve as resolve64 } from "path";
32083
+ import { readFile as readFile44 } from "fs/promises";
32084
+ import { dirname as dirname17, join as join13, resolve as resolve65 } from "path";
31540
32085
  import { spawn as spawn6 } from "child_process";
31541
32086
  import { createInterface as createInterface2 } from "readline/promises";
31542
32087
 
31543
32088
  // src/utils/version.ts
31544
32089
  import { fileURLToPath as fileURLToPath5 } from "url";
31545
- import { readFile as readFile42 } from "fs/promises";
32090
+ import { readFile as readFile43 } from "fs/promises";
31546
32091
  import { dirname as dirname16, join as join12 } from "path";
31547
32092
  async function readPackageVersion(scriptUrl) {
31548
32093
  try {
31549
32094
  const scriptPath = fileURLToPath5(scriptUrl);
31550
32095
  const pkgRoot = dirname16(dirname16(scriptPath));
31551
- const raw2 = await readFile42(join12(pkgRoot, "package.json"), "utf-8");
32096
+ const raw2 = await readFile43(join12(pkgRoot, "package.json"), "utf-8");
31552
32097
  const parsed = JSON.parse(raw2);
31553
32098
  return typeof parsed.version === "string" ? parsed.version : null;
31554
32099
  } catch {
@@ -31557,7 +32102,7 @@ async function readPackageVersion(scriptUrl) {
31557
32102
  }
31558
32103
 
31559
32104
  // src/utils/npx-prompt.ts
31560
- var STATE_FILE = resolve64(syntaurRoot(), "npx-install.json");
32105
+ var STATE_FILE = resolve65(syntaurRoot(), "npx-install.json");
31561
32106
  var META_ARGS2 = /* @__PURE__ */ new Set(["-h", "--help", "-V", "--version", "help"]);
31562
32107
  var GLOBAL_VERSION_TIMEOUT_MS = 2e3;
31563
32108
  function isRunningViaNpx(scriptUrl) {
@@ -31578,7 +32123,7 @@ function isRunningViaNpx(scriptUrl) {
31578
32123
  async function readState() {
31579
32124
  if (!await fileExists(STATE_FILE)) return null;
31580
32125
  try {
31581
- const raw2 = await readFile43(STATE_FILE, "utf-8");
32126
+ const raw2 = await readFile44(STATE_FILE, "utf-8");
31582
32127
  return JSON.parse(raw2);
31583
32128
  } catch {
31584
32129
  return null;
@@ -31637,7 +32182,7 @@ async function readGlobalVersion() {
31637
32182
  try {
31638
32183
  const manifestPath = join13(rootPath, "syntaur", "package.json");
31639
32184
  if (!await fileExists(manifestPath)) return null;
31640
- const raw2 = await readFile43(manifestPath, "utf-8");
32185
+ const raw2 = await readFile44(manifestPath, "utf-8");
31641
32186
  const parsed = JSON.parse(raw2);
31642
32187
  return typeof parsed.version === "string" ? parsed.version : null;
31643
32188
  } catch {
@@ -31995,16 +32540,16 @@ async function refreshPluginSkills(options, pm, runner, getManagedDir, resolveFr
31995
32540
  // src/commands/install-statusline.ts
31996
32541
  init_paths();
31997
32542
  init_fs();
31998
- import { readFile as readFile45, writeFile as writeFile12, copyFile as copyFile2, rm as rm8, stat as stat7, symlink as symlink2, unlink as unlink11, lstat as lstat3 } from "fs/promises";
31999
- import { resolve as resolve66, dirname as dirname19 } from "path";
32543
+ import { readFile as readFile46, writeFile as writeFile12, copyFile as copyFile2, rm as rm8, stat as stat8, symlink as symlink2, unlink as unlink11, lstat as lstat3 } from "fs/promises";
32544
+ import { resolve as resolve67, dirname as dirname19 } from "path";
32000
32545
  import { homedir as homedir12 } from "os";
32001
32546
  import { fileURLToPath as fileURLToPath8 } from "url";
32002
32547
 
32003
32548
  // src/commands/configure-statusline.ts
32004
32549
  init_paths();
32005
32550
  init_fs();
32006
- import { readFile as readFile44, writeFile as writeFile11 } from "fs/promises";
32007
- import { resolve as resolve65, dirname as dirname18 } from "path";
32551
+ import { readFile as readFile45, writeFile as writeFile11 } from "fs/promises";
32552
+ import { resolve as resolve66, dirname as dirname18 } from "path";
32008
32553
  import { spawnSync as spawnSync5 } from "child_process";
32009
32554
  import { checkbox, input as input2, confirm } from "@inquirer/prompts";
32010
32555
  var AVAILABLE_SEGMENTS = [
@@ -32025,12 +32570,12 @@ var PRESETS = {
32025
32570
  tracker: { segments: ["git", "assignment", "external", "session"], separator: " \xB7 " }
32026
32571
  };
32027
32572
  function getConfigPath(installRoot) {
32028
- return resolve65(installRoot, "statusline.config.json");
32573
+ return resolve66(installRoot, "statusline.config.json");
32029
32574
  }
32030
32575
  async function readConfig2(path) {
32031
32576
  if (!await fileExists(path)) return null;
32032
32577
  try {
32033
- const raw2 = await readFile44(path, "utf-8");
32578
+ const raw2 = await readFile45(path, "utf-8");
32034
32579
  const parsed = JSON.parse(raw2);
32035
32580
  if (!parsed || typeof parsed !== "object") return null;
32036
32581
  const segments = Array.isArray(parsed.segments) ? parsed.segments.filter(isSegmentName) : [];
@@ -32183,7 +32728,7 @@ async function configureStatuslineCommand(options = {}) {
32183
32728
  console.log(` segments: ${config.segments.join(", ")}`);
32184
32729
  console.log(` separator: ${JSON.stringify(config.separator)}`);
32185
32730
  if (config.wrap) console.log(` wrap: ${config.wrap}`);
32186
- const script = options.statuslineScript ?? resolve65(installRoot, "statusline.sh");
32731
+ const script = options.statuslineScript ?? resolve66(installRoot, "statusline.sh");
32187
32732
  if (await fileExists(script)) {
32188
32733
  console.log("");
32189
32734
  console.log("Live preview:");
@@ -32214,11 +32759,11 @@ async function writeDefaultConfigIfMissing(installRoot) {
32214
32759
  // src/commands/install-statusline.ts
32215
32760
  function getPackageStatuslineSource() {
32216
32761
  const here = dirname19(fileURLToPath8(import.meta.url));
32217
- return resolve66(here, "..", "statusline", "statusline.sh");
32762
+ return resolve67(here, "..", "statusline", "statusline.sh");
32218
32763
  }
32219
32764
  async function readSettingsJson(settingsPath) {
32220
32765
  if (!await fileExists(settingsPath)) return {};
32221
- const raw2 = await readFile45(settingsPath, "utf-8");
32766
+ const raw2 = await readFile46(settingsPath, "utf-8");
32222
32767
  if (raw2.trim() === "") return {};
32223
32768
  try {
32224
32769
  const parsed = JSON.parse(raw2);
@@ -32298,12 +32843,12 @@ async function installScript(sourceScript, destScript, link) {
32298
32843
  }
32299
32844
  async function installStatuslineCommand(options = {}) {
32300
32845
  const mode = options.mode ?? "ask";
32301
- const settingsPath = options.settingsPath ?? resolve66(homedir12(), ".claude", "settings.json");
32846
+ const settingsPath = options.settingsPath ?? resolve67(homedir12(), ".claude", "settings.json");
32302
32847
  const installRoot = options.installRoot ?? syntaurRoot();
32303
32848
  const sourceScript = options.sourceScript ?? getPackageStatuslineSource();
32304
- const destScript = resolve66(installRoot, "statusline.sh");
32305
- const confPath = resolve66(installRoot, "statusline.conf");
32306
- const backupPath = resolve66(installRoot, "statusline.backup.json");
32849
+ const destScript = resolve67(installRoot, "statusline.sh");
32850
+ const confPath = resolve67(installRoot, "statusline.conf");
32851
+ const backupPath = resolve67(installRoot, "statusline.backup.json");
32307
32852
  if (!await fileExists(sourceScript)) {
32308
32853
  throw new Error(
32309
32854
  `Statusline source script not found at ${sourceScript}. Try re-installing syntaur (npm install -g syntaur) or pass --source-script explicitly.`
@@ -32339,7 +32884,7 @@ async function installStatuslineCommand(options = {}) {
32339
32884
  if (parsed) {
32340
32885
  wrapTarget = parsed;
32341
32886
  } else {
32342
- const wrapperPath = resolve66(installRoot, "statusline-wrapped.sh");
32887
+ const wrapperPath = resolve67(installRoot, "statusline-wrapped.sh");
32343
32888
  const wrapperBody = `#!/usr/bin/env bash
32344
32889
  # Auto-generated by syntaur install-statusline.
32345
32890
  # Executes the previously configured statusLine command.
@@ -32388,25 +32933,25 @@ function parseWrapCommand(command) {
32388
32933
  async function chmodExec(path) {
32389
32934
  const fs = await import("fs/promises");
32390
32935
  try {
32391
- const s = await stat7(path);
32936
+ const s = await stat8(path);
32392
32937
  await fs.chmod(path, s.mode | 73);
32393
32938
  } catch {
32394
32939
  }
32395
32940
  }
32396
32941
  async function uninstallStatuslineCommand(options = {}) {
32397
- const settingsPath = options.settingsPath ?? resolve66(homedir12(), ".claude", "settings.json");
32942
+ const settingsPath = options.settingsPath ?? resolve67(homedir12(), ".claude", "settings.json");
32398
32943
  const installRoot = options.installRoot ?? syntaurRoot();
32399
- const destScript = resolve66(installRoot, "statusline.sh");
32400
- const confPath = resolve66(installRoot, "statusline.conf");
32401
- const backupPath = resolve66(installRoot, "statusline.backup.json");
32402
- const wrapperPath = resolve66(installRoot, "statusline-wrapped.sh");
32944
+ const destScript = resolve67(installRoot, "statusline.sh");
32945
+ const confPath = resolve67(installRoot, "statusline.conf");
32946
+ const backupPath = resolve67(installRoot, "statusline.backup.json");
32947
+ const wrapperPath = resolve67(installRoot, "statusline-wrapped.sh");
32403
32948
  const settings = await readSettingsJson(settingsPath);
32404
32949
  const existing = extractExistingCommand(settings);
32405
32950
  const ourCommand = `bash ${destScript}`;
32406
32951
  let restored = null;
32407
32952
  if (await fileExists(backupPath)) {
32408
32953
  try {
32409
- const raw2 = await readFile45(backupPath, "utf-8");
32954
+ const raw2 = await readFile46(backupPath, "utf-8");
32410
32955
  const parsed = JSON.parse(raw2);
32411
32956
  const prev = parsed?.previousStatusLine;
32412
32957
  if (prev && typeof prev === "object" && typeof prev.command === "string") {
@@ -32427,7 +32972,7 @@ async function uninstallStatuslineCommand(options = {}) {
32427
32972
  await writeSettingsJson(settingsPath, settings);
32428
32973
  }
32429
32974
  if (!options.keepScript) {
32430
- const configPath2 = resolve66(installRoot, "statusline.config.json");
32975
+ const configPath2 = resolve67(installRoot, "statusline.config.json");
32431
32976
  for (const path of [destScript, confPath, backupPath, wrapperPath, configPath2]) {
32432
32977
  try {
32433
32978
  await rm8(path, { force: true });
@@ -32587,8 +33132,8 @@ init_config2();
32587
33132
  // src/commands/cross-agent-install.ts
32588
33133
  init_fs();
32589
33134
  import { spawnSync as spawnSync6 } from "child_process";
32590
- import { dirname as dirname20, join as join15, resolve as resolve68 } from "path";
32591
- import { cp as cp4, mkdir as mkdir9, readFile as readFile46 } from "fs/promises";
33135
+ import { dirname as dirname20, join as join15, resolve as resolve69 } from "path";
33136
+ import { cp as cp4, mkdir as mkdir9, readFile as readFile47 } from "fs/promises";
32592
33137
 
32593
33138
  // src/commands/setup-adapter.ts
32594
33139
  init_paths();
@@ -32597,7 +33142,7 @@ init_config2();
32597
33142
  init_slug();
32598
33143
  init_registry();
32599
33144
  init_renderers();
32600
- import { resolve as resolve67 } from "path";
33145
+ import { resolve as resolve68 } from "path";
32601
33146
  async function setupAdapterCommand(framework, options) {
32602
33147
  const { targets: known, warnings } = await resolveAgentTargets();
32603
33148
  const target = known.find((t) => t.id === framework);
@@ -32626,13 +33171,13 @@ async function setupAdapterCommand(framework, options) {
32626
33171
  }
32627
33172
  const config = await readConfig();
32628
33173
  const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
32629
- const projectDir = resolve67(baseDir, options.project);
32630
- const assignmentDir = resolve67(projectDir, "assignments", options.assignment);
32631
- const projectMdPath = resolve67(projectDir, "project.md");
33174
+ const projectDir = resolve68(baseDir, options.project);
33175
+ const assignmentDir = resolve68(projectDir, "assignments", options.assignment);
33176
+ const projectMdPath = resolve68(projectDir, "project.md");
32632
33177
  if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
32633
33178
  throw new Error(`Project "${options.project}" not found at ${projectDir}.`);
32634
33179
  }
32635
- const assignmentMdPath2 = resolve67(assignmentDir, "assignment.md");
33180
+ const assignmentMdPath2 = resolve68(assignmentDir, "assignment.md");
32636
33181
  if (!await fileExists(assignmentDir) || !await fileExists(assignmentMdPath2)) {
32637
33182
  throw new Error(
32638
33183
  `Assignment "${options.assignment}" not found at ${assignmentDir}.`
@@ -32649,7 +33194,7 @@ async function setupAdapterCommand(framework, options) {
32649
33194
  const upToDateFiles = [];
32650
33195
  const skippedFiles = [];
32651
33196
  for (const file of target.instructions.files) {
32652
- const filePath = resolve67(cwd, file.path);
33197
+ const filePath = resolve68(cwd, file.path);
32653
33198
  const content = RENDERERS[file.renderer](rendererParams);
32654
33199
  const status = await writeFileReport(filePath, content, {
32655
33200
  force: options.force
@@ -32705,7 +33250,7 @@ async function installTier3Plugin(t, opts = {}) {
32705
33250
  return "already-present";
32706
33251
  }
32707
33252
  try {
32708
- const sourceDir = resolve68(await findPackageRoot(plugin.source), plugin.source);
33253
+ const sourceDir = resolve69(await findPackageRoot(plugin.source), plugin.source);
32709
33254
  await mkdir9(dirname20(installDir), { recursive: true });
32710
33255
  await cp4(sourceDir, installDir, { recursive: true, force: true });
32711
33256
  console.log(`Tier 3 (${t.id}): installed ${plugin.kind} -> ${installDir}`);
@@ -32730,10 +33275,10 @@ function isNpxAvailable() {
32730
33275
  }
32731
33276
  }
32732
33277
  async function readAssignmentContext() {
32733
- const p = resolve68(process.cwd(), ".syntaur", "context.json");
33278
+ const p = resolve69(process.cwd(), ".syntaur", "context.json");
32734
33279
  if (!await fileExists(p)) return null;
32735
33280
  try {
32736
- return JSON.parse(await readFile46(p, "utf-8"));
33281
+ return JSON.parse(await readFile47(p, "utf-8"));
32737
33282
  } catch {
32738
33283
  return null;
32739
33284
  }
@@ -32813,7 +33358,7 @@ async function crossAgentInstallCommand(options) {
32813
33358
  if (dryRun) {
32814
33359
  for (const f of t.instructions.files) {
32815
33360
  console.log(
32816
- `${prefix}Tier 2 (${t.id}): ${resolve68(process.cwd(), f.path)}`
33361
+ `${prefix}Tier 2 (${t.id}): ${resolve69(process.cwd(), f.path)}`
32817
33362
  );
32818
33363
  }
32819
33364
  continue;
@@ -32970,7 +33515,7 @@ async function setupCommand(options) {
32970
33515
  }
32971
33516
 
32972
33517
  // src/commands/uninstall.ts
32973
- import { resolve as resolve69 } from "path";
33518
+ import { resolve as resolve70 } from "path";
32974
33519
  init_paths();
32975
33520
  function expandTargets(options) {
32976
33521
  if (options.all) {
@@ -33050,7 +33595,7 @@ async function uninstallCommand(options) {
33050
33595
  const configuredProjectDir = await getConfiguredProjectDir();
33051
33596
  await removeSyntaurData();
33052
33597
  console.log(`Removed ${syntaurRoot()}`);
33053
- if (configuredProjectDir && resolve69(configuredProjectDir) !== resolve69(syntaurRoot(), "projects")) {
33598
+ if (configuredProjectDir && resolve70(configuredProjectDir) !== resolve70(syntaurRoot(), "projects")) {
33054
33599
  console.warn(
33055
33600
  `Warning: config.md pointed to an external project directory (${configuredProjectDir}). That directory was not removed automatically.`
33056
33601
  );
@@ -33072,8 +33617,8 @@ init_session_id();
33072
33617
  init_cwd();
33073
33618
  init_session_db();
33074
33619
  init_agent_sessions();
33075
- import { resolve as resolve70 } from "path";
33076
- import { readFile as readFile47 } from "fs/promises";
33620
+ import { resolve as resolve71 } from "path";
33621
+ import { readFile as readFile48 } from "fs/promises";
33077
33622
  async function trackSessionCommand(options, deps = {}) {
33078
33623
  if (!options.agent) {
33079
33624
  throw new Error("--agent <name> is required.");
@@ -33083,7 +33628,7 @@ async function trackSessionCommand(options, deps = {}) {
33083
33628
  const cwd = process.cwd();
33084
33629
  let legacyHint;
33085
33630
  try {
33086
- const raw2 = await readFile47(resolve70(cwd, ".syntaur", "context.json"), "utf-8");
33631
+ const raw2 = await readFile48(resolve71(cwd, ".syntaur", "context.json"), "utf-8");
33087
33632
  const parsed = JSON.parse(raw2);
33088
33633
  if (typeof parsed.sessionId === "string") legacyHint = parsed.sessionId;
33089
33634
  } catch {
@@ -33098,7 +33643,7 @@ async function trackSessionCommand(options, deps = {}) {
33098
33643
  if (options.project) {
33099
33644
  const config = await readConfig();
33100
33645
  const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
33101
- const projectDir = resolve70(baseDir, options.project);
33646
+ const projectDir = resolve71(baseDir, options.project);
33102
33647
  if (!await fileExists(projectDir)) {
33103
33648
  throw new Error(
33104
33649
  `Project "${options.project}" not found at ${projectDir}.`
@@ -33231,8 +33776,8 @@ function formatInstallUrlHandlerError(err2) {
33231
33776
  init_config2();
33232
33777
  init_paths();
33233
33778
  init_fs();
33234
- import { resolve as resolve72, isAbsolute as isAbsolute8 } from "path";
33235
- import { readFile as readFile48 } from "fs/promises";
33779
+ import { resolve as resolve73, isAbsolute as isAbsolute8 } from "path";
33780
+ import { readFile as readFile49 } from "fs/promises";
33236
33781
  import { select, confirm as confirm2, input as input3 } from "@inquirer/prompts";
33237
33782
  async function browseCommand(options) {
33238
33783
  const config = await readConfig();
@@ -33301,7 +33846,7 @@ async function pickAgent2(agents) {
33301
33846
  return picked;
33302
33847
  }
33303
33848
  async function ensureWorktree(opts) {
33304
- const assignmentPath = resolve72(
33849
+ const assignmentPath = resolve73(
33305
33850
  opts.projectsDir,
33306
33851
  opts.projectSlug,
33307
33852
  "assignments",
@@ -33311,7 +33856,7 @@ async function ensureWorktree(opts) {
33311
33856
  if (!await fileExists(assignmentPath)) {
33312
33857
  return void 0;
33313
33858
  }
33314
- const content = await readFile48(assignmentPath, "utf-8");
33859
+ const content = await readFile49(assignmentPath, "utf-8");
33315
33860
  const { parseAssignmentFrontmatter: parseAssignmentFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
33316
33861
  const fm = parseAssignmentFrontmatter2(content);
33317
33862
  const { workspace } = fm;
@@ -33381,7 +33926,7 @@ async function ensureWorktree(opts) {
33381
33926
  async function runCreate(opts) {
33382
33927
  const { createWorktreeAndRecord: createWorktreeAndRecord2, GitWorktreeError: GitWorktreeError2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
33383
33928
  const expandedWorktree = expandHome(opts.worktreePath);
33384
- const absWorktree = isAbsolute8(expandedWorktree) ? expandedWorktree : resolve72(expandedWorktree);
33929
+ const absWorktree = isAbsolute8(expandedWorktree) ? expandedWorktree : resolve73(expandedWorktree);
33385
33930
  try {
33386
33931
  await createWorktreeAndRecord2({
33387
33932
  assignmentPath: opts.assignmentPath,
@@ -33410,7 +33955,7 @@ init_paths();
33410
33955
  init_fs();
33411
33956
  init_playbook();
33412
33957
  init_playbooks();
33413
- import { resolve as resolve73 } from "path";
33958
+ import { resolve as resolve74 } from "path";
33414
33959
  async function createPlaybookCommand(name, options) {
33415
33960
  if (!name.trim()) {
33416
33961
  throw new Error("Playbook name cannot be empty.");
@@ -33423,7 +33968,7 @@ async function createPlaybookCommand(name, options) {
33423
33968
  }
33424
33969
  const dir = playbooksDir();
33425
33970
  await ensureDir(dir);
33426
- const filePath = resolve73(dir, `${slug}.md`);
33971
+ const filePath = resolve74(dir, `${slug}.md`);
33427
33972
  if (await fileExists(filePath)) {
33428
33973
  throw new Error(
33429
33974
  `Playbook "${slug}" already exists at ${filePath}
@@ -33445,8 +33990,8 @@ init_paths();
33445
33990
  init_fs();
33446
33991
  init_parser();
33447
33992
  init_config2();
33448
- import { readdir as readdir26, readFile as readFile49 } from "fs/promises";
33449
- import { resolve as resolve74 } from "path";
33993
+ import { readdir as readdir26, readFile as readFile50 } from "fs/promises";
33994
+ import { resolve as resolve75 } from "path";
33450
33995
  async function listPlaybooksCommand(options = {}) {
33451
33996
  const dir = playbooksDir();
33452
33997
  if (!await fileExists(dir)) {
@@ -33461,8 +34006,8 @@ async function listPlaybooksCommand(options = {}) {
33461
34006
  );
33462
34007
  const rows = [];
33463
34008
  for (const entry of mdFiles) {
33464
- const filePath = resolve74(dir, entry.name);
33465
- const raw2 = await readFile49(filePath, "utf-8");
34009
+ const filePath = resolve75(dir, entry.name);
34010
+ const raw2 = await readFile50(filePath, "utf-8");
33466
34011
  const parsed = parsePlaybook(raw2);
33467
34012
  const slug = parsed.slug || entry.name.replace(/\.md$/, "");
33468
34013
  const disabled = disabledSet.has(slug);
@@ -33585,14 +34130,14 @@ init_fs();
33585
34130
  init_config2();
33586
34131
  init_slug();
33587
34132
  import { Command as Command2 } from "commander";
33588
- import { readFile as readFile51 } from "fs/promises";
33589
- import { resolve as resolve76 } from "path";
34133
+ import { readFile as readFile52 } from "fs/promises";
34134
+ import { resolve as resolve77 } from "path";
33590
34135
 
33591
34136
  // src/commands/bundle.ts
33592
34137
  init_paths();
33593
34138
  import { Command } from "commander";
33594
- import { mkdir as mkdir10, readFile as readFile50, readdir as readdir27, rm as rm9, writeFile as writeFile13 } from "fs/promises";
33595
- import { resolve as resolve75 } from "path";
34139
+ import { mkdir as mkdir10, readFile as readFile51, readdir as readdir27, rm as rm9, writeFile as writeFile13 } from "fs/promises";
34140
+ import { resolve as resolve76 } from "path";
33596
34141
  init_parser2();
33597
34142
  init_fs();
33598
34143
  init_config2();
@@ -33610,7 +34155,7 @@ async function resolveBundleScope(options) {
33610
34155
  throw new Error(`Invalid project slug: "${options.project}".`);
33611
34156
  }
33612
34157
  const config = await readConfig();
33613
- const projectMd = resolve75(config.defaultProjectDir, options.project, "project.md");
34158
+ const projectMd = resolve76(config.defaultProjectDir, options.project, "project.md");
33614
34159
  if (!await fileExists(projectMd)) {
33615
34160
  throw new Error(`Project "${options.project}" not found.`);
33616
34161
  }
@@ -33680,10 +34225,10 @@ function pickNextPlanFile(planDir, existingFiles) {
33680
34225
  const m = f.match(/^plan-v(\d+)\.md$/);
33681
34226
  if (m) versions.add(parseInt(m[1], 10));
33682
34227
  }
33683
- if (versions.size === 0) return { target: resolve75(planDir, "plan.md"), version: 1 };
34228
+ if (versions.size === 0) return { target: resolve76(planDir, "plan.md"), version: 1 };
33684
34229
  let n = 2;
33685
34230
  while (versions.has(n)) n++;
33686
- return { target: resolve75(planDir, `plan-v${n}.md`), version: n };
34231
+ return { target: resolve76(planDir, `plan-v${n}.md`), version: n };
33687
34232
  }
33688
34233
  function dedupePreserveOrder(ids) {
33689
34234
  const out = [];
@@ -33767,7 +34312,7 @@ bundleCommand.command("new").description("Create a new bundle from 2+ existing t
33767
34312
  if (options.plan) {
33768
34313
  const planDir = bundlePlanDir(sc.todosPath, sc.scopeId, id);
33769
34314
  await ensureDir(planDir);
33770
- const target = resolve75(planDir, "plan.md");
34315
+ const target = resolve76(planDir, "plan.md");
33771
34316
  const memberLines = items.map((it) => `- ${it.description} [t:${it.id}]`).join("\n");
33772
34317
  const stub = [
33773
34318
  "---",
@@ -33943,7 +34488,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
33943
34488
  }
33944
34489
  const repository = options.repository ?? process.cwd();
33945
34490
  const parentBranch = options.parentBranch ?? "main";
33946
- const worktreePath = options.worktreePath ?? resolve75(repository, ".worktrees", options.branch);
34491
+ const worktreePath = options.worktreePath ?? resolve76(repository, ".worktrees", options.branch);
33947
34492
  const checklist = await readChecklist(sc.todosPath, sc.checklistKey);
33948
34493
  for (const memberId of bundle.todoIds) {
33949
34494
  const item = checklist.items.find((i) => i.id === memberId);
@@ -33953,8 +34498,8 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
33953
34498
  }
33954
34499
  const bundlesFilePath = bundlesPath(sc.todosPath);
33955
34500
  const checklistFilePath = checklistPath(sc.todosPath, sc.checklistKey);
33956
- const bundlesSnapshot = await fileExists(bundlesFilePath) ? await readFile50(bundlesFilePath, "utf-8") : null;
33957
- const checklistSnapshot = await fileExists(checklistFilePath) ? await readFile50(checklistFilePath, "utf-8") : null;
34501
+ const bundlesSnapshot = await fileExists(bundlesFilePath) ? await readFile51(bundlesFilePath, "utf-8") : null;
34502
+ const checklistSnapshot = await fileExists(checklistFilePath) ? await readFile51(checklistFilePath, "utf-8") : null;
33958
34503
  const record = async () => {
33959
34504
  bundle.branch = options.branch;
33960
34505
  bundle.worktreePath = worktreePath;
@@ -33970,7 +34515,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
33970
34515
  try {
33971
34516
  await writeBundles(sc.todosPath, bundles);
33972
34517
  await writeChecklist(sc.todosPath, checklist);
33973
- const ctxDir = resolve75(worktreePath, ".syntaur");
34518
+ const ctxDir = resolve76(worktreePath, ".syntaur");
33974
34519
  await mkdir10(ctxDir, { recursive: true });
33975
34520
  const payload = {
33976
34521
  bundleId: bundle.id,
@@ -33984,7 +34529,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
33984
34529
  repository,
33985
34530
  boundAt: nowISO()
33986
34531
  };
33987
- await writeFile13(resolve75(ctxDir, "context.json"), JSON.stringify(payload, null, 2) + "\n");
34532
+ await writeFile13(resolve76(ctxDir, "context.json"), JSON.stringify(payload, null, 2) + "\n");
33988
34533
  } catch (err2) {
33989
34534
  try {
33990
34535
  if (bundlesSnapshot === null) {
@@ -34174,7 +34719,7 @@ async function resolveScope(options) {
34174
34719
  throw new Error(`Invalid project slug: "${options.project}".`);
34175
34720
  }
34176
34721
  const config = await readConfig();
34177
- const projectMd = resolve76(config.defaultProjectDir, options.project, "project.md");
34722
+ const projectMd = resolve77(config.defaultProjectDir, options.project, "project.md");
34178
34723
  if (!await fileExists(projectMd)) {
34179
34724
  throw new Error(`Project "${options.project}" not found.`);
34180
34725
  }
@@ -34507,10 +35052,10 @@ todoCommand.command("archive").description("Archive completed todos and their lo
34507
35052
  (e) => e.itemIds.every((id) => completedIds.has(id))
34508
35053
  );
34509
35054
  const archFile = archivePath(todosPath, workspace, checklist.archiveInterval);
34510
- await ensureDir(resolve76(todosPath, "archive"));
35055
+ await ensureDir(resolve77(todosPath, "archive"));
34511
35056
  let archContent = "";
34512
35057
  if (await fileExists(archFile)) {
34513
- archContent = await readFile51(archFile, "utf-8");
35058
+ archContent = await readFile52(archFile, "utf-8");
34514
35059
  archContent = archContent.trimEnd() + "\n\n";
34515
35060
  } else {
34516
35061
  archContent = `---
@@ -34688,12 +35233,12 @@ function describeScope(scope) {
34688
35233
  }
34689
35234
  async function injectPromotedTodos(assignmentDir, todos, scopeLabel) {
34690
35235
  const { resolve: resolvePath2 } = await import("path");
34691
- const { readFile: readFile81 } = await import("fs/promises");
35236
+ const { readFile: readFile82 } = await import("fs/promises");
34692
35237
  const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
34693
35238
  const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
34694
35239
  const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
34695
35240
  const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
34696
- let content = await readFile81(assignmentMdPath2, "utf-8");
35241
+ let content = await readFile82(assignmentMdPath2, "utf-8");
34697
35242
  content = appendTodosToAssignmentBody2(
34698
35243
  content,
34699
35244
  todos.map((t) => ({
@@ -34744,7 +35289,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
34744
35289
  );
34745
35290
  let target;
34746
35291
  if (existingFiles.length === 0) {
34747
- target = resolve76(planDir, "plan.md");
35292
+ target = resolve77(planDir, "plan.md");
34748
35293
  } else {
34749
35294
  const versions = /* @__PURE__ */ new Set();
34750
35295
  for (const f of existingFiles) {
@@ -34754,7 +35299,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
34754
35299
  }
34755
35300
  let n = 2;
34756
35301
  while (versions.has(n)) n++;
34757
- target = resolve76(planDir, `plan-v${n}.md`);
35302
+ target = resolve77(planDir, `plan-v${n}.md`);
34758
35303
  }
34759
35304
  if (!await fileExists(target)) {
34760
35305
  const stub = `---
@@ -34948,12 +35493,12 @@ backupCommand.command("config").description("Show or update backup configuration
34948
35493
 
34949
35494
  // src/commands/doctor.ts
34950
35495
  import { Command as Command4 } from "commander";
34951
- import { readFile as readFile62 } from "fs/promises";
34952
- import { isAbsolute as isAbsolute11, resolve as resolve89 } from "path";
35496
+ import { readFile as readFile64 } from "fs/promises";
35497
+ import { isAbsolute as isAbsolute11, resolve as resolve91 } from "path";
34953
35498
 
34954
35499
  // src/utils/doctor/index.ts
34955
35500
  import { fileURLToPath as fileURLToPath12 } from "url";
34956
- import { readFile as readFile61 } from "fs/promises";
35501
+ import { readFile as readFile63 } from "fs/promises";
34957
35502
  import { dirname as dirname25, join as join21 } from "path";
34958
35503
 
34959
35504
  // src/utils/doctor/context.ts
@@ -34961,11 +35506,11 @@ init_config2();
34961
35506
  init_paths();
34962
35507
  init_fs();
34963
35508
  import Database5 from "better-sqlite3";
34964
- import { resolve as resolve77 } from "path";
35509
+ import { resolve as resolve78 } from "path";
34965
35510
  async function buildCheckContext(cwd = process.cwd()) {
34966
35511
  const config = await readConfig();
34967
35512
  const root = syntaurRoot();
34968
- const dbPath = resolve77(root, "syntaur.db");
35513
+ const dbPath = resolve78(root, "syntaur.db");
34969
35514
  let db6 = null;
34970
35515
  let dbError = null;
34971
35516
  if (await fileExists(dbPath)) {
@@ -34999,8 +35544,8 @@ function closeCheckContext(ctx) {
34999
35544
  // src/utils/doctor/checks/env.ts
35000
35545
  init_fs();
35001
35546
  init_paths();
35002
- import { resolve as resolve78, isAbsolute as isAbsolute9 } from "path";
35003
- import { readFile as readFile52, stat as stat8 } from "fs/promises";
35547
+ import { resolve as resolve79, isAbsolute as isAbsolute9 } from "path";
35548
+ import { readFile as readFile53, stat as stat9 } from "fs/promises";
35004
35549
  import { fileURLToPath as fileURLToPath10 } from "url";
35005
35550
  import { dirname as dirname22, join as join17 } from "path";
35006
35551
  var CATEGORY = "env";
@@ -35010,7 +35555,7 @@ var syntaurRootExists = {
35010
35555
  title: "~/.syntaur/ directory exists",
35011
35556
  async run(ctx) {
35012
35557
  try {
35013
- const s = await stat8(ctx.syntaurRoot);
35558
+ const s = await stat9(ctx.syntaurRoot);
35014
35559
  if (!s.isDirectory()) {
35015
35560
  return err(this, `${ctx.syntaurRoot} exists but is not a directory`, [
35016
35561
  ctx.syntaurRoot
@@ -35040,7 +35585,7 @@ var configValid = {
35040
35585
  category: CATEGORY,
35041
35586
  title: "~/.syntaur/config.md is valid",
35042
35587
  async run(ctx) {
35043
- const configPath2 = resolve78(ctx.syntaurRoot, "config.md");
35588
+ const configPath2 = resolve79(ctx.syntaurRoot, "config.md");
35044
35589
  if (!await fileExists(configPath2)) {
35045
35590
  return {
35046
35591
  id: this.id,
@@ -35057,7 +35602,7 @@ var configValid = {
35057
35602
  autoFixable: false
35058
35603
  };
35059
35604
  }
35060
- const content = await readFile52(configPath2, "utf-8");
35605
+ const content = await readFile53(configPath2, "utf-8");
35061
35606
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
35062
35607
  if (!fmMatch || fmMatch[1].trim() === "") {
35063
35608
  return {
@@ -35337,7 +35882,7 @@ async function readLocalPkg() {
35337
35882
  for (let i = 0; i < 6; i++) {
35338
35883
  const candidate = join17(dir, "package.json");
35339
35884
  try {
35340
- const text = await readFile52(candidate, "utf-8");
35885
+ const text = await readFile53(candidate, "utf-8");
35341
35886
  return JSON.parse(text);
35342
35887
  } catch {
35343
35888
  dir = dirname22(dir);
@@ -35389,8 +35934,8 @@ function versionGte(a, b) {
35389
35934
 
35390
35935
  // src/utils/doctor/checks/structure.ts
35391
35936
  init_fs();
35392
- import { resolve as resolve79 } from "path";
35393
- import { readdir as readdir28, stat as stat9 } from "fs/promises";
35937
+ import { resolve as resolve80 } from "path";
35938
+ import { readdir as readdir28, stat as stat10 } from "fs/promises";
35394
35939
  var CATEGORY2 = "structure";
35395
35940
  var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
35396
35941
  "projects",
@@ -35409,7 +35954,7 @@ var projectsDir = {
35409
35954
  category: CATEGORY2,
35410
35955
  title: "projects/ directory exists",
35411
35956
  async run(ctx) {
35412
- const p = resolve79(ctx.syntaurRoot, "projects");
35957
+ const p = resolve80(ctx.syntaurRoot, "projects");
35413
35958
  if (!await fileExists(p)) {
35414
35959
  return {
35415
35960
  id: this.id,
@@ -35434,7 +35979,7 @@ var playbooksDir2 = {
35434
35979
  category: CATEGORY2,
35435
35980
  title: "playbooks/ directory exists",
35436
35981
  async run(ctx) {
35437
- const p = resolve79(ctx.syntaurRoot, "playbooks");
35982
+ const p = resolve80(ctx.syntaurRoot, "playbooks");
35438
35983
  if (!await fileExists(p)) {
35439
35984
  return {
35440
35985
  id: this.id,
@@ -35459,7 +36004,7 @@ var todosDirValid = {
35459
36004
  category: CATEGORY2,
35460
36005
  title: "todos/ directory is readable (if present)",
35461
36006
  async run(ctx) {
35462
- const p = resolve79(ctx.syntaurRoot, "todos");
36007
+ const p = resolve80(ctx.syntaurRoot, "todos");
35463
36008
  if (!await fileExists(p)) {
35464
36009
  return {
35465
36010
  id: this.id,
@@ -35470,7 +36015,7 @@ var todosDirValid = {
35470
36015
  autoFixable: false
35471
36016
  };
35472
36017
  }
35473
- const s = await stat9(p);
36018
+ const s = await stat10(p);
35474
36019
  if (!s.isDirectory()) {
35475
36020
  return {
35476
36021
  id: this.id,
@@ -35490,7 +36035,7 @@ var serversDirValid = {
35490
36035
  category: CATEGORY2,
35491
36036
  title: "servers/ directory is readable (if present)",
35492
36037
  async run(ctx) {
35493
- const p = resolve79(ctx.syntaurRoot, "servers");
36038
+ const p = resolve80(ctx.syntaurRoot, "servers");
35494
36039
  if (!await fileExists(p)) {
35495
36040
  return {
35496
36041
  id: this.id,
@@ -35501,7 +36046,7 @@ var serversDirValid = {
35501
36046
  autoFixable: false
35502
36047
  };
35503
36048
  }
35504
- const s = await stat9(p);
36049
+ const s = await stat10(p);
35505
36050
  if (!s.isDirectory()) {
35506
36051
  return {
35507
36052
  id: this.id,
@@ -35535,7 +36080,7 @@ var knownFilesRecognized = {
35535
36080
  title: this.title,
35536
36081
  status: "warn",
35537
36082
  detail: `unexpected top-level entries: ${unexpected.join(", ")}`,
35538
- affected: unexpected.map((n) => resolve79(ctx.syntaurRoot, n)),
36083
+ affected: unexpected.map((n) => resolve80(ctx.syntaurRoot, n)),
35539
36084
  remediation: {
35540
36085
  kind: "manual",
35541
36086
  suggestion: "Review these entries \u2014 they may be leftover state from older versions",
@@ -35564,8 +36109,8 @@ function pass2(check) {
35564
36109
 
35565
36110
  // src/utils/doctor/checks/project.ts
35566
36111
  init_fs();
35567
- import { resolve as resolve80 } from "path";
35568
- import { readdir as readdir29, stat as stat10 } from "fs/promises";
36112
+ import { resolve as resolve81 } from "path";
36113
+ import { readdir as readdir29, stat as stat11 } from "fs/promises";
35569
36114
  var CATEGORY3 = "project";
35570
36115
  var REQUIRED_PROJECT_FILES = [
35571
36116
  "project.md",
@@ -35594,10 +36139,10 @@ async function listProjects2(ctx) {
35594
36139
  for (const e of entries) {
35595
36140
  if (!e.isDirectory()) continue;
35596
36141
  if (e.name.startsWith(".") || e.name.startsWith("_")) continue;
35597
- const projectDir = resolve80(dir, e.name);
36142
+ const projectDir = resolve81(dir, e.name);
35598
36143
  let looksLikeProject = false;
35599
36144
  for (const marker of PROJECT_MARKERS) {
35600
- if (await fileExists(resolve80(projectDir, marker))) {
36145
+ if (await fileExists(resolve81(projectDir, marker))) {
35601
36146
  looksLikeProject = true;
35602
36147
  break;
35603
36148
  }
@@ -35616,7 +36161,7 @@ var requiredFiles = {
35616
36161
  for (const projectDir of projects) {
35617
36162
  const missing = [];
35618
36163
  for (const rel of REQUIRED_PROJECT_FILES) {
35619
- const p = resolve80(projectDir, rel);
36164
+ const p = resolve81(projectDir, rel);
35620
36165
  if (!await fileExists(p)) missing.push(rel);
35621
36166
  }
35622
36167
  if (missing.length === 0) continue;
@@ -35626,7 +36171,7 @@ var requiredFiles = {
35626
36171
  title: this.title,
35627
36172
  status: "error",
35628
36173
  detail: `project at ${projectDir} is missing: ${missing.join(", ")}`,
35629
- affected: missing.map((m) => resolve80(projectDir, m)),
36174
+ affected: missing.map((m) => resolve81(projectDir, m)),
35630
36175
  remediation: {
35631
36176
  kind: "manual",
35632
36177
  suggestion: "Recreate the missing scaffold files from templates",
@@ -35649,9 +36194,9 @@ var manifestStale = {
35649
36194
  const projects = await listProjects2(ctx);
35650
36195
  const results = [];
35651
36196
  for (const projectDir of projects) {
35652
- const manifestPath = resolve80(projectDir, "manifest.md");
36197
+ const manifestPath = resolve81(projectDir, "manifest.md");
35653
36198
  if (!await fileExists(manifestPath)) continue;
35654
- const manifestMtime = (await stat10(manifestPath)).mtimeMs;
36199
+ const manifestMtime = (await stat11(manifestPath)).mtimeMs;
35655
36200
  const newestAssignment = await newestAssignmentMtime(projectDir);
35656
36201
  if (newestAssignment === 0) continue;
35657
36202
  if (newestAssignment > manifestMtime) {
@@ -35698,7 +36243,7 @@ var orphanFiles = {
35698
36243
  title: this.title,
35699
36244
  status: "warn",
35700
36245
  detail: `project at ${projectDir} has unexpected entries: ${orphans.join(", ")}`,
35701
- affected: orphans.map((o) => resolve80(projectDir, o)),
36246
+ affected: orphans.map((o) => resolve81(projectDir, o)),
35702
36247
  autoFixable: false
35703
36248
  });
35704
36249
  }
@@ -35708,7 +36253,7 @@ var orphanFiles = {
35708
36253
  };
35709
36254
  var projectChecks = [requiredFiles, manifestStale, orphanFiles];
35710
36255
  async function newestAssignmentMtime(projectDir) {
35711
- const assignmentsRoot = resolve80(projectDir, "assignments");
36256
+ const assignmentsRoot = resolve81(projectDir, "assignments");
35712
36257
  if (!await fileExists(assignmentsRoot)) return 0;
35713
36258
  let newest = 0;
35714
36259
  let entries;
@@ -35719,9 +36264,9 @@ async function newestAssignmentMtime(projectDir) {
35719
36264
  }
35720
36265
  for (const e of entries) {
35721
36266
  if (!e.isDirectory()) continue;
35722
- const assignmentMd = resolve80(assignmentsRoot, e.name, "assignment.md");
36267
+ const assignmentMd = resolve81(assignmentsRoot, e.name, "assignment.md");
35723
36268
  try {
35724
- const s = await stat10(assignmentMd);
36269
+ const s = await stat11(assignmentMd);
35725
36270
  if (s.mtimeMs > newest) newest = s.mtimeMs;
35726
36271
  } catch {
35727
36272
  }
@@ -35744,8 +36289,8 @@ init_parser();
35744
36289
  init_types();
35745
36290
  init_paths();
35746
36291
  init_assignment_walk();
35747
- import { resolve as resolve81 } from "path";
35748
- import { readFile as readFile53, readdir as readdir30 } from "fs/promises";
36292
+ import { resolve as resolve82 } from "path";
36293
+ import { readFile as readFile54, readdir as readdir30 } from "fs/promises";
35749
36294
  var CATEGORY4 = "assignment";
35750
36295
  var STATUSES_REQUIRING_HANDOFF = /* @__PURE__ */ new Set(["review", "completed"]);
35751
36296
  var PRE_WORKSPACE_STATUSES = /* @__PURE__ */ new Set([
@@ -35839,7 +36384,7 @@ var invalidStatus = {
35839
36384
  const allowed = configuredStatuses(ctx);
35840
36385
  const results = [];
35841
36386
  for (const a of withAssignmentMd) {
35842
- const path = resolve81(a.assignmentDir, "assignment.md");
36387
+ const path = resolve82(a.assignmentDir, "assignment.md");
35843
36388
  const parsed = await parseSafe4(path);
35844
36389
  if (!parsed) continue;
35845
36390
  if (!allowed.has(parsed.status)) {
@@ -35872,7 +36417,7 @@ var workspaceMissing = {
35872
36417
  const terminal = terminalStatuses(ctx);
35873
36418
  const results = [];
35874
36419
  for (const a of withAssignmentMd) {
35875
- const path = resolve81(a.assignmentDir, "assignment.md");
36420
+ const path = resolve82(a.assignmentDir, "assignment.md");
35876
36421
  const parsed = await parseSafe4(path);
35877
36422
  if (!parsed) continue;
35878
36423
  if (terminal.has(parsed.status)) continue;
@@ -35919,12 +36464,12 @@ var requiredFilesByStatus = {
35919
36464
  const { withAssignmentMd } = await listAssignments(ctx);
35920
36465
  const results = [];
35921
36466
  for (const a of withAssignmentMd) {
35922
- const assignmentPath = resolve81(a.assignmentDir, "assignment.md");
36467
+ const assignmentPath = resolve82(a.assignmentDir, "assignment.md");
35923
36468
  const parsed = await parseSafe4(assignmentPath);
35924
36469
  if (!parsed) continue;
35925
36470
  const missing = [];
35926
36471
  if (STATUSES_REQUIRING_HANDOFF.has(parsed.status)) {
35927
- const handoffPath = resolve81(a.assignmentDir, "handoff.md");
36472
+ const handoffPath = resolve82(a.assignmentDir, "handoff.md");
35928
36473
  if (!await fileExists(handoffPath)) missing.push("handoff.md");
35929
36474
  }
35930
36475
  if (missing.length === 0) continue;
@@ -35934,7 +36479,7 @@ var requiredFilesByStatus = {
35934
36479
  title: this.title,
35935
36480
  status: "warn",
35936
36481
  detail: `${a.projectSlug}/${a.assignmentSlug} (status: ${parsed.status}) is missing ${missing.join(", ")}`,
35937
- affected: missing.map((m) => resolve81(a.assignmentDir, m)),
36482
+ affected: missing.map((m) => resolve82(a.assignmentDir, m)),
35938
36483
  remediation: {
35939
36484
  kind: "manual",
35940
36485
  suggestion: `Create the missing ${missing.join(" and ")} files for this assignment`,
@@ -35957,7 +36502,7 @@ var companionFilesScaffolded = {
35957
36502
  for (const a of withAssignmentMd) {
35958
36503
  const missing = [];
35959
36504
  for (const filename of ["progress.md", "comments.md"]) {
35960
- if (!await fileExists(resolve81(a.assignmentDir, filename))) {
36505
+ if (!await fileExists(resolve82(a.assignmentDir, filename))) {
35961
36506
  missing.push(filename);
35962
36507
  }
35963
36508
  }
@@ -35969,7 +36514,7 @@ var companionFilesScaffolded = {
35969
36514
  title: this.title,
35970
36515
  status: "warn",
35971
36516
  detail: `${label} is missing ${missing.join(" and ")} (pre-v2.0 assignment \u2014 not required, but scaffolding them keeps the dashboard and CLIs consistent)`,
35972
- affected: missing.map((m) => resolve81(a.assignmentDir, m)),
36517
+ affected: missing.map((m) => resolve82(a.assignmentDir, m)),
35973
36518
  remediation: {
35974
36519
  kind: "manual",
35975
36520
  suggestion: `Create ${missing.join(" and ")} with the renderProgress/renderComments templates, or re-scaffold via the CLI`,
@@ -36002,7 +36547,7 @@ var typeDefinition = {
36002
36547
  const { withAssignmentMd } = await listAssignments(ctx);
36003
36548
  const results = [];
36004
36549
  for (const a of withAssignmentMd) {
36005
- const path = resolve81(a.assignmentDir, "assignment.md");
36550
+ const path = resolve82(a.assignmentDir, "assignment.md");
36006
36551
  const parsed = await parseSafe4(path);
36007
36552
  if (!parsed) continue;
36008
36553
  if (!parsed.type) continue;
@@ -36036,7 +36581,7 @@ var projectFrontmatterMatchesContainer = {
36036
36581
  const { withAssignmentMd } = await listAssignments(ctx);
36037
36582
  const results = [];
36038
36583
  for (const a of withAssignmentMd) {
36039
- const path = resolve81(a.assignmentDir, "assignment.md");
36584
+ const path = resolve82(a.assignmentDir, "assignment.md");
36040
36585
  const parsed = await parseSafe4(path);
36041
36586
  if (!parsed) continue;
36042
36587
  if (a.standalone) {
@@ -36087,13 +36632,13 @@ var draftMissingObjective = {
36087
36632
  const { withAssignmentMd } = await listAssignments(ctx);
36088
36633
  const results = [];
36089
36634
  for (const a of withAssignmentMd) {
36090
- const path = resolve81(a.assignmentDir, "assignment.md");
36635
+ const path = resolve82(a.assignmentDir, "assignment.md");
36091
36636
  const parsed = await parseSafe4(path);
36092
36637
  if (!parsed) continue;
36093
36638
  if (parsed.status !== "draft") continue;
36094
36639
  let raw2;
36095
36640
  try {
36096
- raw2 = await readFile53(path, "utf-8");
36641
+ raw2 = await readFile54(path, "utf-8");
36097
36642
  } catch {
36098
36643
  continue;
36099
36644
  }
@@ -36126,7 +36671,7 @@ var readyToImplementMissingPlan = {
36126
36671
  const { withAssignmentMd } = await listAssignments(ctx);
36127
36672
  const results = [];
36128
36673
  for (const a of withAssignmentMd) {
36129
- const path = resolve81(a.assignmentDir, "assignment.md");
36674
+ const path = resolve82(a.assignmentDir, "assignment.md");
36130
36675
  const parsed = await parseSafe4(path);
36131
36676
  if (!parsed) continue;
36132
36677
  if (parsed.status !== "ready_to_implement") continue;
@@ -36135,7 +36680,7 @@ var readyToImplementMissingPlan = {
36135
36680
  let hasPlanContent = false;
36136
36681
  for (const f of planFiles) {
36137
36682
  try {
36138
- const c2 = await readFile53(resolve81(a.assignmentDir, f), "utf-8");
36683
+ const c2 = await readFile54(resolve82(a.assignmentDir, f), "utf-8");
36139
36684
  if (c2.trim().length > 0) {
36140
36685
  hasPlanContent = true;
36141
36686
  break;
@@ -36151,7 +36696,7 @@ var readyToImplementMissingPlan = {
36151
36696
  title: this.title,
36152
36697
  status: "warn",
36153
36698
  detail: `${label} (status: ready_to_implement) has no plan.md or plan-v<N>.md`,
36154
- affected: [resolve81(a.assignmentDir, "plan.md")],
36699
+ affected: [resolve82(a.assignmentDir, "plan.md")],
36155
36700
  remediation: {
36156
36701
  kind: "manual",
36157
36702
  suggestion: `Write a plan with '/plan-assignment' (or 'syntaur plan'), then re-mark ready_to_implement`,
@@ -36178,7 +36723,7 @@ var assignmentChecks = [
36178
36723
  ];
36179
36724
  async function parseSafe4(path) {
36180
36725
  try {
36181
- const content = await readFile53(path, "utf-8");
36726
+ const content = await readFile54(path, "utf-8");
36182
36727
  return parseAssignmentFull(content);
36183
36728
  } catch {
36184
36729
  return null;
@@ -36197,7 +36742,7 @@ function pass4(check, detail) {
36197
36742
 
36198
36743
  // src/utils/doctor/checks/dashboard.ts
36199
36744
  init_fs();
36200
- import { resolve as resolve82 } from "path";
36745
+ import { resolve as resolve83 } from "path";
36201
36746
  var CATEGORY5 = "dashboard";
36202
36747
  var dbReachable = {
36203
36748
  id: "dashboard.db-reachable",
@@ -36211,7 +36756,7 @@ var dbReachable = {
36211
36756
  title: this.title,
36212
36757
  status: "error",
36213
36758
  detail: `could not open syntaur.db: ${ctx.dbError ?? "unknown error"}`,
36214
- affected: [resolve82(ctx.syntaurRoot, "syntaur.db")],
36759
+ affected: [resolve83(ctx.syntaurRoot, "syntaur.db")],
36215
36760
  remediation: {
36216
36761
  kind: "manual",
36217
36762
  suggestion: "Start the dashboard once (`syntaur dashboard`) to initialize the DB, or restore it from backup",
@@ -36229,7 +36774,7 @@ var dbReachable = {
36229
36774
  title: this.title,
36230
36775
  status: "error",
36231
36776
  detail: 'syntaur.db is missing the expected "sessions" table',
36232
- affected: [resolve82(ctx.syntaurRoot, "syntaur.db")],
36777
+ affected: [resolve83(ctx.syntaurRoot, "syntaur.db")],
36233
36778
  autoFixable: false
36234
36779
  };
36235
36780
  }
@@ -36241,7 +36786,7 @@ var dbReachable = {
36241
36786
  title: this.title,
36242
36787
  status: "error",
36243
36788
  detail: `syntaur.db query failed: ${err2 instanceof Error ? err2.message : String(err2)}`,
36244
- affected: [resolve82(ctx.syntaurRoot, "syntaur.db")],
36789
+ affected: [resolve83(ctx.syntaurRoot, "syntaur.db")],
36245
36790
  autoFixable: false
36246
36791
  };
36247
36792
  }
@@ -36267,7 +36812,7 @@ var ghostSessions = {
36267
36812
  const results = [];
36268
36813
  for (const row of rows) {
36269
36814
  if (!row.project_slug) continue;
36270
- const projectPath = resolve82(projectsDir2, row.project_slug, "project.md");
36815
+ const projectPath = resolve83(projectsDir2, row.project_slug, "project.md");
36271
36816
  if (!await fileExists(projectPath)) {
36272
36817
  results.push({
36273
36818
  id: this.id,
@@ -36286,7 +36831,7 @@ var ghostSessions = {
36286
36831
  continue;
36287
36832
  }
36288
36833
  if (row.assignment_slug) {
36289
- const assignmentPath = resolve82(
36834
+ const assignmentPath = resolve83(
36290
36835
  projectsDir2,
36291
36836
  row.project_slug,
36292
36837
  "assignments",
@@ -36338,8 +36883,8 @@ function skipped(check, reason) {
36338
36883
 
36339
36884
  // src/utils/doctor/checks/integrations.ts
36340
36885
  init_fs();
36341
- import { resolve as resolve83, dirname as dirname23, basename as basename8 } from "path";
36342
- import { readdir as readdir31, readFile as readFile54 } from "fs/promises";
36886
+ import { resolve as resolve84, dirname as dirname23, basename as basename9 } from "path";
36887
+ import { readdir as readdir31, readFile as readFile55 } from "fs/promises";
36343
36888
  import { homedir as homedir14 } from "os";
36344
36889
  var CATEGORY6 = "integrations";
36345
36890
  var claudePluginLinked = {
@@ -36421,10 +36966,10 @@ var backupConfigured = {
36421
36966
  }
36422
36967
  };
36423
36968
  async function readKnownMarketplaces() {
36424
- const path = resolve83(homedir14(), ".claude", "plugins", "known_marketplaces.json");
36969
+ const path = resolve84(homedir14(), ".claude", "plugins", "known_marketplaces.json");
36425
36970
  if (!await fileExists(path)) return {};
36426
36971
  try {
36427
- const raw2 = await readFile54(path, "utf-8");
36972
+ const raw2 = await readFile55(path, "utf-8");
36428
36973
  return JSON.parse(raw2);
36429
36974
  } catch {
36430
36975
  return {};
@@ -36441,7 +36986,7 @@ var claudeMarketplaceRegistered = {
36441
36986
  return skipped2(this, "claudePluginDir does not exist (run install-plugin)");
36442
36987
  }
36443
36988
  const pluginsParent = dirname23(dir);
36444
- if (basename8(pluginsParent) !== "plugins") {
36989
+ if (basename9(pluginsParent) !== "plugins") {
36445
36990
  return {
36446
36991
  id: this.id,
36447
36992
  category: this.category,
@@ -36458,7 +37003,7 @@ var claudeMarketplaceRegistered = {
36458
37003
  };
36459
37004
  }
36460
37005
  const marketplaceRoot = dirname23(pluginsParent);
36461
- const marketplaceManifest = resolve83(marketplaceRoot, ".claude-plugin", "marketplace.json");
37006
+ const marketplaceManifest = resolve84(marketplaceRoot, ".claude-plugin", "marketplace.json");
36462
37007
  if (!await fileExists(marketplaceManifest)) {
36463
37008
  return {
36464
37009
  id: this.id,
@@ -36477,7 +37022,7 @@ var claudeMarketplaceRegistered = {
36477
37022
  }
36478
37023
  let parsed = {};
36479
37024
  try {
36480
- parsed = JSON.parse(await readFile54(marketplaceManifest, "utf-8"));
37025
+ parsed = JSON.parse(await readFile55(marketplaceManifest, "utf-8"));
36481
37026
  } catch {
36482
37027
  return {
36483
37028
  id: this.id,
@@ -36489,7 +37034,7 @@ var claudeMarketplaceRegistered = {
36489
37034
  autoFixable: false
36490
37035
  };
36491
37036
  }
36492
- const marketplaceName = parsed.name ?? basename8(marketplaceRoot);
37037
+ const marketplaceName = parsed.name ?? basename9(marketplaceRoot);
36493
37038
  const hasSyntaurEntry = (parsed.plugins ?? []).some((p) => p?.name === "syntaur");
36494
37039
  const known = await readKnownMarketplaces();
36495
37040
  const registered = known[marketplaceName]?.installLocation === marketplaceRoot || Object.values(known).some((v) => v.installLocation === marketplaceRoot);
@@ -36509,7 +37054,7 @@ var claudeMarketplaceRegistered = {
36509
37054
  title: this.title,
36510
37055
  status: "error",
36511
37056
  detail: issues.join("; "),
36512
- affected: [marketplaceManifest, resolve83(homedir14(), ".claude", "plugins", "known_marketplaces.json")],
37057
+ affected: [marketplaceManifest, resolve84(homedir14(), ".claude", "plugins", "known_marketplaces.json")],
36513
37058
  remediation: {
36514
37059
  kind: "manual",
36515
37060
  suggestion: "Re-run install-plugin to ensure both files are in sync.",
@@ -36549,8 +37094,8 @@ function skipped2(check, reason) {
36549
37094
  init_fs();
36550
37095
  init_parser();
36551
37096
  init_types();
36552
- import { resolve as resolve84 } from "path";
36553
- import { readFile as readFile55 } from "fs/promises";
37097
+ import { resolve as resolve85 } from "path";
37098
+ import { readFile as readFile56 } from "fs/promises";
36554
37099
  var CATEGORY7 = "workspace";
36555
37100
  var ASSIGNMENT_FIELDS2 = ["projectSlug", "assignmentSlug", "projectDir", "assignmentDir"];
36556
37101
  var BUNDLE_FIELDS = ["bundleId", "bundleScope", "bundleScopeId"];
@@ -36572,12 +37117,12 @@ function isStandaloneSession(ctx) {
36572
37117
  return !hasAnyAssignmentField(ctx) && !hasAnyBundleField(ctx) && hasSessionMeta;
36573
37118
  }
36574
37119
  async function loadContext(ctx) {
36575
- const path = resolve84(ctx.cwd, ".syntaur", "context.json");
37120
+ const path = resolve85(ctx.cwd, ".syntaur", "context.json");
36576
37121
  if (!await fileExists(path)) {
36577
37122
  return { data: null, path, exists: false, parseError: null };
36578
37123
  }
36579
37124
  try {
36580
- const raw2 = await readFile55(path, "utf-8");
37125
+ const raw2 = await readFile56(path, "utf-8");
36581
37126
  return { data: JSON.parse(raw2), path, exists: true, parseError: null };
36582
37127
  } catch (err2) {
36583
37128
  return {
@@ -36671,7 +37216,7 @@ var contextAssignmentResolves = {
36671
37216
  if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to resolve");
36672
37217
  if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to resolve");
36673
37218
  if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
36674
- const assignmentMd = resolve84(data.assignmentDir, "assignment.md");
37219
+ const assignmentMd = resolve85(data.assignmentDir, "assignment.md");
36675
37220
  if (!await fileExists(assignmentMd)) {
36676
37221
  return {
36677
37222
  id: this.id,
@@ -36701,10 +37246,10 @@ var contextTerminal = {
36701
37246
  if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to check");
36702
37247
  if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to check");
36703
37248
  if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
36704
- const assignmentMd = resolve84(data.assignmentDir, "assignment.md");
37249
+ const assignmentMd = resolve85(data.assignmentDir, "assignment.md");
36705
37250
  if (!await fileExists(assignmentMd)) return skipped3(this, "assignment file missing");
36706
37251
  try {
36707
- const content = await readFile55(assignmentMd, "utf-8");
37252
+ const content = await readFile56(assignmentMd, "utf-8");
36708
37253
  const parsed = parseAssignmentFull(content);
36709
37254
  const terminal = terminalStatuses2(ctx);
36710
37255
  if (terminal.has(parsed.status)) {
@@ -36850,15 +37395,15 @@ var agentChecks = [agentsResolvable];
36850
37395
  // src/utils/doctor/checks/terminal.ts
36851
37396
  init_config2();
36852
37397
  import { spawnSync as spawnSync9 } from "child_process";
36853
- import { readFile as readFile56 } from "fs/promises";
36854
- import { resolve as resolve85 } from "path";
37398
+ import { readFile as readFile57 } from "fs/promises";
37399
+ import { resolve as resolve86 } from "path";
36855
37400
  init_paths();
36856
37401
  init_fs();
36857
37402
  var CATEGORY9 = "terminal";
36858
37403
  async function readRawTerminalKey() {
36859
- const configPath2 = resolve85(syntaurRoot(), "config.md");
37404
+ const configPath2 = resolve86(syntaurRoot(), "config.md");
36860
37405
  if (!await fileExists(configPath2)) return null;
36861
- const content = await readFile56(configPath2, "utf-8");
37406
+ const content = await readFile57(configPath2, "utf-8");
36862
37407
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
36863
37408
  if (!fmMatch) return null;
36864
37409
  const line = fmMatch[1].split("\n").find((l) => /^terminal:\s*/.test(l));
@@ -37008,13 +37553,13 @@ var terminalChecks = [
37008
37553
 
37009
37554
  // src/utils/doctor/checks/skills.ts
37010
37555
  init_fs();
37011
- import { resolve as resolve86, join as join18 } from "path";
37012
- import { readdir as readdir32, readFile as readFile57, lstat as lstat4 } from "fs/promises";
37556
+ import { resolve as resolve87, join as join18 } from "path";
37557
+ import { readdir as readdir32, readFile as readFile58, lstat as lstat4 } from "fs/promises";
37013
37558
  import { homedir as homedir15 } from "os";
37014
37559
  var CATEGORY10 = "skills";
37015
37560
  var skillTargets = [
37016
- { agent: "claude", dir: resolve86(homedir15(), ".claude", "skills"), label: "~/.claude/skills" },
37017
- { agent: "codex", dir: resolve86(homedir15(), ".codex", "skills"), label: "~/.codex/skills" }
37561
+ { agent: "claude", dir: resolve87(homedir15(), ".claude", "skills"), label: "~/.claude/skills" },
37562
+ { agent: "codex", dir: resolve87(homedir15(), ".codex", "skills"), label: "~/.codex/skills" }
37018
37563
  ];
37019
37564
  var skillsDedupCheck = {
37020
37565
  id: "skills.dedup",
@@ -37038,7 +37583,7 @@ var skillsDedupCheck = {
37038
37583
  if (!KNOWN_SKILLS.includes(entry.name)) continue;
37039
37584
  const skillMd = join18(dir, entry.name, "SKILL.md");
37040
37585
  if (!await fileExists(skillMd)) continue;
37041
- const content = await readFile57(skillMd, "utf-8").catch(() => "");
37586
+ const content = await readFile58(skillMd, "utf-8").catch(() => "");
37042
37587
  const match = content.match(/^name:\s*(\S+)\s*$/m);
37043
37588
  if (!match || match[1] !== entry.name) continue;
37044
37589
  let isSymlink2 = false;
@@ -37088,12 +37633,12 @@ var skillsChecks = [skillsDedupCheck];
37088
37633
 
37089
37634
  // src/utils/doctor/checks/cross-agent.ts
37090
37635
  init_fs();
37091
- import { join as join19, resolve as resolve87 } from "path";
37092
- import { readFile as readFile59 } from "fs/promises";
37636
+ import { join as join19, resolve as resolve88 } from "path";
37637
+ import { readFile as readFile60 } from "fs/promises";
37093
37638
 
37094
37639
  // src/utils/skill-frontmatter.ts
37095
37640
  import { createHash as createHash4 } from "crypto";
37096
- import { readFile as readFile58 } from "fs/promises";
37641
+ import { readFile as readFile59 } from "fs/promises";
37097
37642
  function stripQuotes(raw2) {
37098
37643
  const t = raw2.trim();
37099
37644
  if (t.length >= 2 && (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'"))) {
@@ -37137,7 +37682,7 @@ function readSkillIdentity(skillMdText) {
37137
37682
  return { name, hasDescription };
37138
37683
  }
37139
37684
  async function sha256File(path) {
37140
- return createHash4("sha256").update(await readFile58(path)).digest("hex");
37685
+ return createHash4("sha256").update(await readFile59(path)).digest("hex");
37141
37686
  }
37142
37687
 
37143
37688
  // src/utils/doctor/checks/cross-agent.ts
@@ -37154,7 +37699,7 @@ async function checkTargetSkillsIntegrity(installedDir, canonicalSkillsDir, know
37154
37699
  }
37155
37700
  let text;
37156
37701
  try {
37157
- text = await readFile59(installedPath, "utf-8");
37702
+ text = await readFile60(installedPath, "utf-8");
37158
37703
  } catch {
37159
37704
  problems.push({ skill, kind: "invalid-frontmatter" });
37160
37705
  continue;
@@ -37231,7 +37776,7 @@ var crossAgentSkillsCheck = {
37231
37776
  }
37232
37777
  if (recorded && t.instructions) {
37233
37778
  for (const f of t.instructions.files) {
37234
- const p = resolve87(ctx.cwd, f.path);
37779
+ const p = resolve88(ctx.cwd, f.path);
37235
37780
  if (!await fileExists(p)) {
37236
37781
  problems.push(`${t.displayName}: missing protocol file ${f.path} in cwd`);
37237
37782
  affected.push(p);
@@ -37307,7 +37852,7 @@ var crossAgentChecks = [crossAgentSkillsCheck];
37307
37852
  // src/utils/doctor/checks/bundles.ts
37308
37853
  init_fs();
37309
37854
  init_paths();
37310
- import { resolve as resolve88 } from "path";
37855
+ import { resolve as resolve89 } from "path";
37311
37856
  import { readdir as readdir33 } from "fs/promises";
37312
37857
  import { spawnSync as spawnSync10 } from "child_process";
37313
37858
  init_parser2();
@@ -37337,7 +37882,7 @@ async function listScopes(ctx) {
37337
37882
  if (!e.isDirectory()) continue;
37338
37883
  const slug = e.name;
37339
37884
  if (typeof slug !== "string" || slug.startsWith(".")) continue;
37340
- const projectMd = resolve88(ctx.config.defaultProjectDir, slug, "project.md");
37885
+ const projectMd = resolve89(ctx.config.defaultProjectDir, slug, "project.md");
37341
37886
  if (!await fileExists(projectMd)) continue;
37342
37887
  out.push({
37343
37888
  scopeLabel: `project:${slug}`,
@@ -37530,7 +38075,7 @@ var bundleChecks = [
37530
38075
  ];
37531
38076
 
37532
38077
  // src/utils/doctor/checks/plugin.ts
37533
- import { readFile as readFile60 } from "fs/promises";
38078
+ import { readFile as readFile61 } from "fs/promises";
37534
38079
  import { dirname as dirname24, join as join20 } from "path";
37535
38080
  import { fileURLToPath as fileURLToPath11 } from "url";
37536
38081
  var CATEGORY13 = "plugin";
@@ -37540,7 +38085,7 @@ async function readCliVersion() {
37540
38085
  let dir = dirname24(fileURLToPath11(import.meta.url));
37541
38086
  for (let i = 0; i < 8; i += 1) {
37542
38087
  try {
37543
- const parsed = JSON.parse(await readFile60(join20(dir, "package.json"), "utf-8"));
38088
+ const parsed = JSON.parse(await readFile61(join20(dir, "package.json"), "utf-8"));
37544
38089
  if (typeof parsed.version === "string" && parsed.version.length > 0) return parsed.version;
37545
38090
  } catch {
37546
38091
  }
@@ -37648,6 +38193,68 @@ var deriveConfigValid = {
37648
38193
  };
37649
38194
  var deriveConfigChecks = [deriveConfigValid];
37650
38195
 
38196
+ // src/utils/doctor/checks/staleness.ts
38197
+ init_config2();
38198
+ import { resolve as resolve90 } from "path";
38199
+ import { readFile as readFile62 } from "fs/promises";
38200
+ var CATEGORY15 = "staleness";
38201
+ var stalenessConfigValid = {
38202
+ id: "staleness.valid",
38203
+ category: CATEGORY15,
38204
+ title: "Staleness threshold overrides are valid",
38205
+ async run(ctx) {
38206
+ let content;
38207
+ try {
38208
+ content = await readFile62(resolve90(ctx.syntaurRoot, "config.md"), "utf-8");
38209
+ } catch {
38210
+ return {
38211
+ id: this.id,
38212
+ category: this.category,
38213
+ title: this.title,
38214
+ status: "skipped",
38215
+ detail: "no config.md",
38216
+ autoFixable: false
38217
+ };
38218
+ }
38219
+ if (!/^\s*staleness:\s*$/m.test(content)) {
38220
+ return {
38221
+ id: this.id,
38222
+ category: this.category,
38223
+ title: this.title,
38224
+ status: "skipped",
38225
+ detail: "no staleness: block configured (using defaults)",
38226
+ autoFixable: false
38227
+ };
38228
+ }
38229
+ const problems = validateStalenessConfig(content);
38230
+ if (problems.length === 0) {
38231
+ return {
38232
+ id: this.id,
38233
+ category: this.category,
38234
+ title: this.title,
38235
+ status: "pass",
38236
+ detail: "staleness threshold overrides are valid",
38237
+ autoFixable: false
38238
+ };
38239
+ }
38240
+ return {
38241
+ id: this.id,
38242
+ category: this.category,
38243
+ title: this.title,
38244
+ status: "warn",
38245
+ detail: problems.join("; "),
38246
+ affected: problems,
38247
+ remediation: {
38248
+ kind: "manual",
38249
+ suggestion: "Fix the flagged staleness: entries in ~/.syntaur/config.md. Each value must be a positive duration (7d, 12h, 30m, 90s, 500ms). Invalid entries are ignored and fall back to the default gate.",
38250
+ command: null
38251
+ },
38252
+ autoFixable: false
38253
+ };
38254
+ }
38255
+ };
38256
+ var stalenessChecks = [stalenessConfigValid];
38257
+
37651
38258
  // src/utils/doctor/registry.ts
37652
38259
  function allChecks() {
37653
38260
  return [
@@ -37664,7 +38271,8 @@ function allChecks() {
37664
38271
  ...crossAgentChecks,
37665
38272
  ...bundleChecks,
37666
38273
  ...pluginChecks,
37667
- ...deriveConfigChecks
38274
+ ...deriveConfigChecks,
38275
+ ...stalenessChecks
37668
38276
  ];
37669
38277
  }
37670
38278
 
@@ -37750,7 +38358,7 @@ async function readVersion() {
37750
38358
  let dir = dirname25(here);
37751
38359
  for (let i = 0; i < 6; i++) {
37752
38360
  try {
37753
- const raw2 = await readFile61(join21(dir, "package.json"), "utf-8");
38361
+ const raw2 = await readFile63(join21(dir, "package.json"), "utf-8");
37754
38362
  const parsed = JSON.parse(raw2);
37755
38363
  return typeof parsed.version === "string" ? parsed.version : null;
37756
38364
  } catch {
@@ -37847,7 +38455,7 @@ var REQUIRED_WORKSPACE_FIELDS = [
37847
38455
  ];
37848
38456
  var ISO_DATE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
37849
38457
  async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
37850
- const absolute = isAbsolute11(inputPath) ? inputPath : resolve89(cwd, inputPath);
38458
+ const absolute = isAbsolute11(inputPath) ? inputPath : resolve91(cwd, inputPath);
37851
38459
  const errors = [];
37852
38460
  const warnings = [];
37853
38461
  if (!await fileExists(absolute)) {
@@ -37860,7 +38468,7 @@ async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
37860
38468
  }
37861
38469
  let content;
37862
38470
  try {
37863
- content = await readFile62(absolute, "utf-8");
38471
+ content = await readFile64(absolute, "utf-8");
37864
38472
  } catch (err2) {
37865
38473
  return {
37866
38474
  ok: false,
@@ -38227,8 +38835,8 @@ init_assignment_resolver();
38227
38835
  init_frontmatter();
38228
38836
  init_event_emit();
38229
38837
  init_templates();
38230
- import { resolve as resolve90 } from "path";
38231
- import { readFile as readFile63 } from "fs/promises";
38838
+ import { resolve as resolve92 } from "path";
38839
+ import { readFile as readFile65 } from "fs/promises";
38232
38840
  function shortId() {
38233
38841
  return generateId().split("-")[0];
38234
38842
  }
@@ -38259,7 +38867,7 @@ async function commentCommand(target, text, options = {}) {
38259
38867
  if (!isValidSlug(target)) {
38260
38868
  throw new Error(`Invalid assignment slug "${target}".`);
38261
38869
  }
38262
- assignmentDir = resolve90(baseDir, options.project, "assignments", target);
38870
+ assignmentDir = resolve92(baseDir, options.project, "assignments", target);
38263
38871
  assignmentRef = target;
38264
38872
  projectSlug = options.project;
38265
38873
  } else {
@@ -38271,13 +38879,13 @@ async function commentCommand(target, text, options = {}) {
38271
38879
  assignmentRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
38272
38880
  projectSlug = resolved.projectSlug;
38273
38881
  }
38274
- const commentsPath = resolve90(assignmentDir, "comments.md");
38882
+ const commentsPath = resolve92(assignmentDir, "comments.md");
38275
38883
  const timestamp = nowTimestamp();
38276
38884
  const author = options.author ?? process.env.USER ?? "unknown";
38277
38885
  let currentContent;
38278
38886
  let currentCount = 0;
38279
38887
  if (await fileExists(commentsPath)) {
38280
- currentContent = await readFile63(commentsPath, "utf-8");
38888
+ currentContent = await readFile65(commentsPath, "utf-8");
38281
38889
  const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
38282
38890
  if (countMatch) currentCount = parseInt(countMatch[1], 10);
38283
38891
  } else {
@@ -38305,9 +38913,9 @@ ${entry}`;
38305
38913
  }
38306
38914
  await writeFileForce(commentsPath, next);
38307
38915
  try {
38308
- const assignmentMd = resolve90(assignmentDir, "assignment.md");
38916
+ const assignmentMd = resolve92(assignmentDir, "assignment.md");
38309
38917
  if (await fileExists(assignmentMd)) {
38310
- const fm = parseAssignmentFrontmatter(await readFile63(assignmentMd, "utf-8"));
38918
+ const fm = parseAssignmentFrontmatter(await readFile65(assignmentMd, "utf-8"));
38311
38919
  emitEvent({
38312
38920
  assignmentId: fm.id,
38313
38921
  projectSlug,
@@ -38331,170 +38939,15 @@ ${entry}`;
38331
38939
  }
38332
38940
 
38333
38941
  // src/commands/capture.ts
38334
- import { resolve as resolve94, relative as relative4, dirname as dirname26 } from "path";
38335
- import { copyFile as copyFile3, mkdir as mkdir12, realpath as realpath2, rm as rm14, stat as stat13, writeFile as writeFile15 } from "fs/promises";
38942
+ import { resolve as resolve95, relative as relative4, dirname as dirname26 } from "path";
38943
+ import { copyFile as copyFile3, mkdir as mkdir12, realpath as realpath2, rm as rm14, stat as stat14, writeFile as writeFile15 } from "fs/promises";
38336
38944
  import { existsSync as existsSync7 } from "fs";
38337
-
38338
- // src/utils/assignment-target.ts
38339
- init_paths();
38340
- init_fs();
38341
- init_config2();
38342
- init_slug();
38343
- init_assignment_resolver();
38344
- init_parser();
38345
- import { resolve as resolve91 } from "path";
38346
- import { readFile as readFile64 } from "fs/promises";
38347
- var AssignmentTargetError = class extends Error {
38348
- };
38349
- function classifyContext(ctx) {
38350
- if (!ctx) return "empty";
38351
- const hasAssignment = Boolean(ctx.assignmentDir) || Boolean(ctx.assignmentSlug) || Boolean(ctx.projectSlug);
38352
- if (hasAssignment) return "assignment";
38353
- if (ctx.bundleId) return "bundle";
38354
- if (ctx.sessionId || ctx.transcriptPath) return "standalone";
38355
- return "empty";
38356
- }
38357
- async function readAssignmentFrontmatterId(assignmentDir) {
38358
- const path = resolve91(assignmentDir, "assignment.md");
38359
- if (!await fileExists(path)) return null;
38360
- try {
38361
- const content = await readFile64(path, "utf-8");
38362
- const [fm] = extractFrontmatter(content);
38363
- return getField(fm, "id");
38364
- } catch {
38365
- return null;
38366
- }
38367
- }
38368
- async function readContextJson(cwd) {
38369
- const path = resolve91(cwd, ".syntaur", "context.json");
38370
- if (!await fileExists(path)) return null;
38371
- try {
38372
- const raw2 = await readFile64(path, "utf-8");
38373
- return JSON.parse(raw2);
38374
- } catch {
38375
- return null;
38376
- }
38377
- }
38378
- async function resolveAssignmentTarget(input4, opts = {}) {
38379
- const config = await readConfig();
38380
- const baseDir = opts.dir ? expandHome(opts.dir) : config.defaultProjectDir;
38381
- if (opts.project) {
38382
- if (!input4) {
38383
- throw new AssignmentTargetError(
38384
- "--project requires an assignment slug as a positional argument."
38385
- );
38386
- }
38387
- if (!isValidSlug(opts.project)) {
38388
- throw new AssignmentTargetError(`Invalid project slug "${opts.project}".`);
38389
- }
38390
- if (!isValidSlug(input4)) {
38391
- throw new AssignmentTargetError(`Invalid assignment slug "${input4}".`);
38392
- }
38393
- const projectDir = resolve91(baseDir, opts.project);
38394
- const projectMdPath = resolve91(projectDir, "project.md");
38395
- if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
38396
- throw new AssignmentTargetError(
38397
- `Project "${opts.project}" not found at ${projectDir}.`
38398
- );
38399
- }
38400
- const assignmentDir = resolve91(projectDir, "assignments", input4);
38401
- const assignmentMdPath2 = resolve91(assignmentDir, "assignment.md");
38402
- if (!await fileExists(assignmentMdPath2)) {
38403
- throw new AssignmentTargetError(
38404
- `Assignment "${input4}" not found in project "${opts.project}".`
38405
- );
38406
- }
38407
- const id = await readAssignmentFrontmatterId(assignmentDir) ?? input4;
38408
- return {
38409
- assignmentDir,
38410
- projectSlug: opts.project,
38411
- assignmentSlug: input4,
38412
- id,
38413
- standalone: false,
38414
- workspaceGroup: null
38415
- };
38416
- }
38417
- if (input4) {
38418
- const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), input4);
38419
- if (!resolved) {
38420
- throw new AssignmentTargetError(
38421
- `Assignment "${input4}" not found. Provide --project <slug> + <slug> or a valid standalone UUID.`
38422
- );
38423
- }
38424
- return resolved;
38425
- }
38426
- const cwd = opts.cwd ?? process.cwd();
38427
- const ctx = await readContextJson(cwd);
38428
- if (!ctx) {
38429
- throw new AssignmentTargetError(
38430
- "No assignment specified. Provide an argument, --project + slug, or run from a directory with .syntaur/context.json."
38431
- );
38432
- }
38433
- if (classifyContext(ctx) === "bundle" && ctx.bundleId) {
38434
- throw new AssignmentTargetError(
38435
- `Context is bound to bundle b:${ctx.bundleId}, not an assignment. Use \`syntaur todo bundle show ${ctx.bundleId}\` or the complete-bundle skill.`
38436
- );
38437
- }
38438
- if (ctx.assignmentDir) {
38439
- const dir = expandHome(ctx.assignmentDir);
38440
- const assignmentMdPath2 = resolve91(dir, "assignment.md");
38441
- if (!await fileExists(assignmentMdPath2)) {
38442
- throw new AssignmentTargetError(
38443
- `.syntaur/context.json points to a missing assignment dir: ${dir}.`
38444
- );
38445
- }
38446
- const id = await readAssignmentFrontmatterId(dir);
38447
- if (!id || id.trim() === "") {
38448
- throw new AssignmentTargetError(
38449
- `.syntaur/context.json points to an assignment with no frontmatter \`id\`: ${dir}.`
38450
- );
38451
- }
38452
- const assignmentSlug = ctx.assignmentSlug ?? dir.split("/").pop() ?? "";
38453
- const projectSlug = ctx.projectSlug ?? null;
38454
- return {
38455
- assignmentDir: dir,
38456
- projectSlug,
38457
- assignmentSlug,
38458
- id,
38459
- standalone: projectSlug === null,
38460
- workspaceGroup: null
38461
- };
38462
- }
38463
- if (ctx.projectSlug && ctx.assignmentSlug) {
38464
- if (!isValidSlug(ctx.projectSlug) || !isValidSlug(ctx.assignmentSlug)) {
38465
- throw new AssignmentTargetError(
38466
- `.syntaur/context.json contains invalid slugs: project="${ctx.projectSlug}" assignment="${ctx.assignmentSlug}".`
38467
- );
38468
- }
38469
- const assignmentDir = resolve91(baseDir, ctx.projectSlug, "assignments", ctx.assignmentSlug);
38470
- const assignmentMdPath2 = resolve91(assignmentDir, "assignment.md");
38471
- if (!await fileExists(assignmentMdPath2)) {
38472
- throw new AssignmentTargetError(
38473
- `.syntaur/context.json points to a missing assignment: ${assignmentDir}.`
38474
- );
38475
- }
38476
- const id = await readAssignmentFrontmatterId(assignmentDir) ?? ctx.assignmentSlug;
38477
- return {
38478
- assignmentDir,
38479
- projectSlug: ctx.projectSlug,
38480
- assignmentSlug: ctx.assignmentSlug,
38481
- id,
38482
- standalone: false,
38483
- workspaceGroup: null
38484
- };
38485
- }
38486
- throw new AssignmentTargetError(
38487
- ".syntaur/context.json exists but contains neither assignmentDir nor projectSlug+assignmentSlug."
38488
- );
38489
- }
38490
-
38491
- // src/commands/capture.ts
38492
38945
  init_paths();
38493
38946
  init_fs();
38494
38947
 
38495
38948
  // src/utils/screencapture.ts
38496
38949
  import { spawn as spawn8 } from "child_process";
38497
- import { mkdtemp as mkdtemp2, rm as rm10, stat as stat11 } from "fs/promises";
38950
+ import { mkdtemp as mkdtemp2, rm as rm10, stat as stat12 } from "fs/promises";
38498
38951
  import { tmpdir as tmpdir3 } from "os";
38499
38952
  import { join as join22 } from "path";
38500
38953
  function argsFor(mode, pngPath) {
@@ -38552,7 +39005,7 @@ async function captureScreenshot(mode) {
38552
39005
  }
38553
39006
  let size = 0;
38554
39007
  try {
38555
- size = (await stat11(pngPath)).size;
39008
+ size = (await stat12(pngPath)).size;
38556
39009
  } catch {
38557
39010
  throw new Error("screencapture exited 0 but produced no image.");
38558
39011
  }
@@ -38568,7 +39021,7 @@ async function captureScreenshot(mode) {
38568
39021
 
38569
39022
  // src/utils/asciinema.ts
38570
39023
  import { spawn as spawn9 } from "child_process";
38571
- import { mkdtemp as mkdtemp3, readFile as readFile65, rm as rm11 } from "fs/promises";
39024
+ import { mkdtemp as mkdtemp3, readFile as readFile66, rm as rm11 } from "fs/promises";
38572
39025
  import { tmpdir as tmpdir4 } from "os";
38573
39026
  import { join as join23 } from "path";
38574
39027
  var SAFE_RE = /^[A-Za-z0-9_@%+=:,./-]+$/;
@@ -38631,7 +39084,7 @@ async function captureAsciinema(opts) {
38631
39084
  }
38632
39085
  throw err2;
38633
39086
  }
38634
- const text = await readFile65(castPath, "utf8").catch(() => null);
39087
+ const text = await readFile66(castPath, "utf8").catch(() => null);
38635
39088
  if (text === null) {
38636
39089
  throw new Error(
38637
39090
  `asciinema produced no cast file at ${castPath} (exit ${exitCode}). Try running 'asciinema rec ${castPath}' directly to diagnose.`
@@ -38659,9 +39112,9 @@ async function captureAsciinema(opts) {
38659
39112
  // src/utils/recording.ts
38660
39113
  init_paths();
38661
39114
  import { spawn as spawn10 } from "child_process";
38662
- import { mkdir as mkdir11, mkdtemp as mkdtemp4, open as open6, readFile as readFile66, rm as rm12, stat as stat12, unlink as unlink12, writeFile as writeFile14 } from "fs/promises";
39115
+ import { mkdir as mkdir11, mkdtemp as mkdtemp4, open as open6, readFile as readFile67, rm as rm12, stat as stat13, unlink as unlink12, writeFile as writeFile14 } from "fs/promises";
38663
39116
  import { tmpdir as tmpdir5 } from "os";
38664
- import { join as join24, resolve as resolve92 } from "path";
39117
+ import { join as join24, resolve as resolve93 } from "path";
38665
39118
  import { setTimeout as sleep } from "timers/promises";
38666
39119
  function sigintPollIntervalMs() {
38667
39120
  const raw2 = process.env.SYNTAUR_RECORDING_POLL_INTERVAL_MS;
@@ -38682,13 +39135,13 @@ function sigtermWaitMs() {
38682
39135
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : 1e3;
38683
39136
  }
38684
39137
  function pidfilePath() {
38685
- return resolve92(syntaurRoot(), "recording.pid");
39138
+ return resolve93(syntaurRoot(), "recording.pid");
38686
39139
  }
38687
39140
  function logPath2() {
38688
- return resolve92(syntaurRoot(), "recording.log");
39141
+ return resolve93(syntaurRoot(), "recording.log");
38689
39142
  }
38690
39143
  function sidecarPath() {
38691
- return resolve92(syntaurRoot(), "recording.json");
39144
+ return resolve93(syntaurRoot(), "recording.json");
38692
39145
  }
38693
39146
  function ffmpegArgs(device, fps, mp4Path) {
38694
39147
  return [
@@ -38733,7 +39186,7 @@ async function acquirePidfile(pidfile) {
38733
39186
  } catch (err2) {
38734
39187
  if (err2.code !== "EEXIST") throw err2;
38735
39188
  if (attempt === 1) throw err2;
38736
- const existing = (await readFile66(pidfile, "utf-8").catch(() => "")).trim();
39189
+ const existing = (await readFile67(pidfile, "utf-8").catch(() => "")).trim();
38737
39190
  if (existing.startsWith(STARTING_SENTINEL_PREFIX)) {
38738
39191
  const parentPidRaw = existing.slice(STARTING_SENTINEL_PREFIX.length);
38739
39192
  const parentPid = Number.parseInt(parentPidRaw, 10);
@@ -38835,7 +39288,7 @@ async function startRecording(input4) {
38835
39288
  logHandle = null;
38836
39289
  if (warmupMs > 0) await sleep(warmupMs);
38837
39290
  if (!await isProcessAlive(pid)) {
38838
- const tail = await readFile66(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
39291
+ const tail = await readFile67(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
38839
39292
  acquiredPid = null;
38840
39293
  throw new Error(
38841
39294
  `ffmpeg exited during startup \u2014 likely macOS Screen Recording permission missing. Grant access to your terminal in System Settings \u2192 Privacy & Security \u2192 Screen Recording, then retry. Log: ${log}
@@ -38892,7 +39345,7 @@ ${tail}`
38892
39345
  async function stopRecording() {
38893
39346
  const pidfile = pidfilePath();
38894
39347
  const sidecar = sidecarPath();
38895
- const pidRaw = await readFile66(pidfile, "utf-8").catch(() => null);
39348
+ const pidRaw = await readFile67(pidfile, "utf-8").catch(() => null);
38896
39349
  if (pidRaw === null) {
38897
39350
  throw new Error(
38898
39351
  `No active recording found (no pidfile at ${pidfile}). Did you run --start?`
@@ -38902,7 +39355,7 @@ async function stopRecording() {
38902
39355
  if (!Number.isInteger(pid) || pid <= 0) {
38903
39356
  throw new Error(`Pidfile at ${pidfile} is corrupt (got "${pidRaw}").`);
38904
39357
  }
38905
- const sidecarRaw = await readFile66(sidecar, "utf-8").catch(() => null);
39358
+ const sidecarRaw = await readFile67(sidecar, "utf-8").catch(() => null);
38906
39359
  if (sidecarRaw === null) {
38907
39360
  throw new Error(
38908
39361
  `No recording sidecar at ${sidecar}. The recording state is inconsistent \u2014 delete ${pidfile} and re-run --start.`
@@ -38947,7 +39400,7 @@ async function stopRecording() {
38947
39400
  if (alive) {
38948
39401
  throw new Error(`ffmpeg (PID ${pid}) refused to die after SIGKILL`);
38949
39402
  }
38950
- const st = await stat12(sidecarData.mp4Path).catch(() => null);
39403
+ const st = await stat13(sidecarData.mp4Path).catch(() => null);
38951
39404
  if (st === null || st.size === 0) {
38952
39405
  await unlink12(pidfile).catch(() => {
38953
39406
  });
@@ -38967,7 +39420,7 @@ async function stopRecording() {
38967
39420
  // src/db/proof-db.ts
38968
39421
  init_paths();
38969
39422
  import Database6 from "better-sqlite3";
38970
- import { resolve as resolve93 } from "path";
39423
+ import { resolve as resolve94 } from "path";
38971
39424
  var db5 = null;
38972
39425
  var PROOF_SCHEMA_VERSION = "1";
38973
39426
  var SCHEMA_SQL5 = `
@@ -38987,7 +39440,7 @@ CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);
38987
39440
  `;
38988
39441
  function initProofDb(dbPath) {
38989
39442
  if (db5) return db5;
38990
- const finalPath = dbPath ?? resolve93(syntaurRoot(), "syntaur.db");
39443
+ const finalPath = dbPath ?? resolve94(syntaurRoot(), "syntaur.db");
38991
39444
  db5 = new Database6(finalPath);
38992
39445
  db5.pragma("journal_mode = WAL");
38993
39446
  db5.exec(SCHEMA_SQL5);
@@ -39035,7 +39488,7 @@ function listArtifactsByAssignment(assignmentId) {
39035
39488
 
39036
39489
  // src/utils/transcribers/elevenlabs.ts
39037
39490
  import { spawn as spawn11 } from "child_process";
39038
- import { mkdtemp as mkdtemp5, readFile as readFile67, rm as rm13 } from "fs/promises";
39491
+ import { mkdtemp as mkdtemp5, readFile as readFile68, rm as rm13 } from "fs/promises";
39039
39492
  import { tmpdir as tmpdir6 } from "os";
39040
39493
  import { join as join25 } from "path";
39041
39494
  var SCRIBE_URL = "https://api.elevenlabs.io/v1/speech-to-text";
@@ -39099,7 +39552,7 @@ async function extractAudio(videoAbsPath, wavOut) {
39099
39552
  throw new TranscribeFfmpegError(`ffmpeg failed (exit ${result.code}): ${tail}`);
39100
39553
  }
39101
39554
  async function callScribe(wavPath, apiKey, opts) {
39102
- const audio = await readFile67(wavPath);
39555
+ const audio = await readFile68(wavPath);
39103
39556
  const form = new FormData();
39104
39557
  form.set("file", new Blob([new Uint8Array(audio)], { type: "audio/wav" }), "audio.wav");
39105
39558
  form.set("model_id", "scribe_v1");
@@ -39415,7 +39868,7 @@ async function captureCommand(target, options = {}) {
39415
39868
  });
39416
39869
  }
39417
39870
  if (options.file) {
39418
- const expanded = options.file.startsWith("~/") ? resolve94(process.env.HOME ?? "", options.file.slice(2)) : resolve94(options.file);
39871
+ const expanded = options.file.startsWith("~/") ? resolve95(process.env.HOME ?? "", options.file.slice(2)) : resolve95(options.file);
39419
39872
  if (!await fileExists(expanded)) {
39420
39873
  throw new Error(`--file does not exist: ${options.file}`);
39421
39874
  }
@@ -39427,7 +39880,7 @@ async function captureCommand(target, options = {}) {
39427
39880
  `--file is unreadable: ${options.file} (${e instanceof Error ? e.message : String(e)})`
39428
39881
  );
39429
39882
  }
39430
- const st = await stat13(real);
39883
+ const st = await stat14(real);
39431
39884
  if (!st.isFile()) {
39432
39885
  throw new Error(`--file is not a regular file: ${options.file}`);
39433
39886
  }
@@ -39456,7 +39909,7 @@ async function captureCommand(target, options = {}) {
39456
39909
  }
39457
39910
  initProofDb();
39458
39911
  const subdir = criterionIndex === null ? "untagged" : String(criterionIndex);
39459
- const destDir = resolve94(proofDir(resolved.assignmentDir), subdir);
39912
+ const destDir = resolve95(proofDir(resolved.assignmentDir), subdir);
39460
39913
  if (resolvedSource) await mkdir12(destDir, { recursive: true });
39461
39914
  const ext = resolvedSource ? extensionForKind(kind) : null;
39462
39915
  let id = null;
@@ -39465,7 +39918,7 @@ async function captureCommand(target, options = {}) {
39465
39918
  let lastErr = null;
39466
39919
  for (let attempt = 0; attempt < MAX_ID_RETRIES; attempt += 1) {
39467
39920
  const candidate = generateArtifactId();
39468
- const candidateAbsPath = resolvedSource && ext ? resolve94(destDir, `${candidate}.${ext}`) : null;
39921
+ const candidateAbsPath = resolvedSource && ext ? resolve95(destDir, `${candidate}.${ext}`) : null;
39469
39922
  const candidateRel = candidateAbsPath ? relative4(resolved.assignmentDir, candidateAbsPath) : null;
39470
39923
  try {
39471
39924
  insertArtifact({
@@ -39509,7 +39962,7 @@ async function captureCommand(target, options = {}) {
39509
39962
  }
39510
39963
  }
39511
39964
  if (options.transcribe && kind === "video" && absPath && id) {
39512
- const sidecarPath2 = resolve94(destDir, `${id}.transcript.md`);
39965
+ const sidecarPath2 = resolve95(destDir, `${id}.transcript.md`);
39513
39966
  if (existsSync7(sidecarPath2)) {
39514
39967
  console.warn(
39515
39968
  `transcript: ${sidecarPath2} already exists, skipping (delete to re-transcribe)`
@@ -39541,7 +39994,7 @@ async function captureCommand(target, options = {}) {
39541
39994
  const tagSuffix = criterionIndex === null ? "untagged" : `criterion ${criterionIndex}`;
39542
39995
  console.log(`Captured artifact ${id} (${kind}) for ${ref} \u2014 ${tagSuffix}.`);
39543
39996
  if (relativeFilePath) {
39544
- console.log(` file: ${resolve94(resolved.assignmentDir, relativeFilePath)}`);
39997
+ console.log(` file: ${resolve95(resolved.assignmentDir, relativeFilePath)}`);
39545
39998
  }
39546
39999
  } catch (err2) {
39547
40000
  if (options.stop && kind === "video" && shelloutCleanup && resolvedSource) {
@@ -39564,8 +40017,8 @@ async function captureCommand(target, options = {}) {
39564
40017
 
39565
40018
  // src/commands/proof.ts
39566
40019
  import { Command as Command6 } from "commander";
39567
- import { readFile as readFile68, writeFile as writeFile16, rename as rename10, stat as stat14 } from "fs/promises";
39568
- import { resolve as resolve95, relative as relative5, isAbsolute as isAbsolute12, dirname as dirname27 } from "path";
40020
+ import { readFile as readFile69, writeFile as writeFile16, rename as rename10, stat as stat15 } from "fs/promises";
40021
+ import { resolve as resolve96, relative as relative5, isAbsolute as isAbsolute12, dirname as dirname27 } from "path";
39569
40022
  import { randomBytes as randomBytes4 } from "crypto";
39570
40023
 
39571
40024
  // src/utils/acceptance-criteria-parse.ts
@@ -39805,11 +40258,11 @@ function renderProofHtml(params2, inlineFiles = /* @__PURE__ */ new Map(), trans
39805
40258
 
39806
40259
  // src/commands/proof.ts
39807
40260
  async function readAssignmentMeta(assignmentDir) {
39808
- const path = resolve95(assignmentDir, "assignment.md");
40261
+ const path = resolve96(assignmentDir, "assignment.md");
39809
40262
  if (!await fileExists(path)) {
39810
40263
  return { title: "", body: "" };
39811
40264
  }
39812
- const content = await readFile68(path, "utf-8");
40265
+ const content = await readFile69(path, "utf-8");
39813
40266
  const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
39814
40267
  let title = "";
39815
40268
  if (fmMatch) {
@@ -39856,7 +40309,7 @@ async function loadInlineFiles(rows, assignmentDir) {
39856
40309
  for (const r of rows) {
39857
40310
  if (!r.file_path) continue;
39858
40311
  if (r.kind !== "http" && r.kind !== "text") continue;
39859
- const abs = resolve95(assignmentDir, r.file_path);
40312
+ const abs = resolve96(assignmentDir, r.file_path);
39860
40313
  if (!isWithin(proofRoot, abs)) {
39861
40314
  out.set(r.file_path, null);
39862
40315
  continue;
@@ -39865,13 +40318,13 @@ async function loadInlineFiles(rows, assignmentDir) {
39865
40318
  out.set(r.file_path, null);
39866
40319
  continue;
39867
40320
  }
39868
- const st = await stat14(abs);
40321
+ const st = await stat15(abs);
39869
40322
  if (st.size > INLINE_TEXT_LIMIT_BYTES) {
39870
40323
  out.set(r.file_path, null);
39871
40324
  continue;
39872
40325
  }
39873
40326
  try {
39874
- out.set(r.file_path, await readFile68(abs, "utf-8"));
40327
+ out.set(r.file_path, await readFile69(abs, "utf-8"));
39875
40328
  } catch {
39876
40329
  out.set(r.file_path, null);
39877
40330
  }
@@ -39883,14 +40336,14 @@ async function loadTranscriptSidecars(rows, assignmentDir) {
39883
40336
  const proofRoot = proofDir(assignmentDir);
39884
40337
  for (const r of rows) {
39885
40338
  if (r.kind !== "video" || !r.file_path) continue;
39886
- const videoAbs = resolve95(assignmentDir, r.file_path);
39887
- const sidecar = resolve95(dirname27(videoAbs), `${r.id}.transcript.md`);
40339
+ const videoAbs = resolve96(assignmentDir, r.file_path);
40340
+ const sidecar = resolve96(dirname27(videoAbs), `${r.id}.transcript.md`);
39888
40341
  if (!isWithin(proofRoot, sidecar)) continue;
39889
40342
  if (!await fileExists(sidecar)) continue;
39890
- const st = await stat14(sidecar);
40343
+ const st = await stat15(sidecar);
39891
40344
  if (st.size > INLINE_TEXT_LIMIT_BYTES) continue;
39892
40345
  try {
39893
- out.set(r.id, await readFile68(sidecar, "utf-8"));
40346
+ out.set(r.id, await readFile69(sidecar, "utf-8"));
39894
40347
  } catch {
39895
40348
  }
39896
40349
  }
@@ -39924,8 +40377,8 @@ async function proofBuildCommand(target, options = {}) {
39924
40377
  };
39925
40378
  const md = renderProofMarkdown(renderParams);
39926
40379
  const html = renderProofHtml(renderParams, inlineFiles, transcriptSidecars);
39927
- const mdPath = resolve95(resolved.assignmentDir, "proof.md");
39928
- const htmlPath = resolve95(resolved.assignmentDir, "proof.html");
40380
+ const mdPath = resolve96(resolved.assignmentDir, "proof.md");
40381
+ const htmlPath = resolve96(resolved.assignmentDir, "proof.html");
39929
40382
  await atomicWrite(mdPath, md);
39930
40383
  await atomicWrite(htmlPath, html);
39931
40384
  console.log(`Wrote ${htmlPath}`);
@@ -40468,7 +40921,7 @@ function isScheduledSessionLive(sessionId, launchPid) {
40468
40921
  import { execFileSync as execFileSync5 } from "child_process";
40469
40922
  import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, rmSync as rmSync2, realpathSync as realpathSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
40470
40923
  import { homedir as homedir16, userInfo } from "os";
40471
- import { dirname as dirname28, join as join26, resolve as resolve96 } from "path";
40924
+ import { dirname as dirname28, join as join26, resolve as resolve97 } from "path";
40472
40925
  var LAUNCH_AGENT_LABEL = "com.syntaur.schedule.tick";
40473
40926
  var LaunchAgentRefusalError = class extends Error {
40474
40927
  constructor(message) {
@@ -40522,7 +40975,7 @@ function absolutize(p) {
40522
40975
  try {
40523
40976
  return realpathSync4(p);
40524
40977
  } catch {
40525
- return resolve96(p);
40978
+ return resolve97(p);
40526
40979
  }
40527
40980
  }
40528
40981
  function defaultRun(command, args) {
@@ -40583,7 +41036,7 @@ function uninstallLaunchAgent(deps = {}) {
40583
41036
  // src/commands/schedule.ts
40584
41037
  init_timestamp();
40585
41038
  var DURATION_REGEX2 = /^(\d+)\s*(s|m|h|d)?$/i;
40586
- function parseDurationMs(input4) {
41039
+ function parseDurationMs2(input4) {
40587
41040
  const m = DURATION_REGEX2.exec(input4.trim());
40588
41041
  if (!m) throw new Error(`invalid duration "${input4}" \u2014 use e.g. 30s, 5m, 2h, 1d`);
40589
41042
  const n = Number.parseInt(m[1], 10);
@@ -40604,7 +41057,7 @@ function validateTerminalChoice(value) {
40604
41057
  function buildTrigger(opts) {
40605
41058
  const chosen = [];
40606
41059
  if (opts.at) chosen.push({ kind: "at", at: opts.at });
40607
- if (opts.in) chosen.push({ kind: "in", durationMs: parseDurationMs(opts.in), anchorIso: nowTimestamp() });
41060
+ if (opts.in) chosen.push({ kind: "in", durationMs: parseDurationMs2(opts.in), anchorIso: nowTimestamp() });
40608
41061
  if (opts.cron) {
40609
41062
  try {
40610
41063
  new Cron2(opts.cron).nextRun();
@@ -40672,7 +41125,7 @@ scheduleCommand.command("create").description("Create a scheduled job").required
40672
41125
  const unattended = !opts.interactive;
40673
41126
  if (unattended) assertUnattendedTerminalSupported(terminal);
40674
41127
  const limits = defaultLimits();
40675
- if (opts.maxRuntime) limits.maxRuntimeMs = parseDurationMs(opts.maxRuntime);
41128
+ if (opts.maxRuntime) limits.maxRuntimeMs = parseDurationMs2(opts.maxRuntime);
40676
41129
  if (opts.maxLaunchesPerDay) {
40677
41130
  const n = Number(opts.maxLaunchesPerDay);
40678
41131
  if (!Number.isInteger(n) || n <= 0) {
@@ -40680,7 +41133,7 @@ scheduleCommand.command("create").description("Create a scheduled job").required
40680
41133
  }
40681
41134
  limits.maxLaunchesPerDay = n;
40682
41135
  }
40683
- if (opts.cooldown) limits.cooldownMs = parseDurationMs(opts.cooldown);
41136
+ if (opts.cooldown) limits.cooldownMs = parseDurationMs2(opts.cooldown);
40684
41137
  const now = nowTimestamp();
40685
41138
  const job = {
40686
41139
  id: newJobId(),
@@ -40898,8 +41351,8 @@ init_slug();
40898
41351
  init_timestamp();
40899
41352
  init_assignment_resolver();
40900
41353
  init_assignment_todos();
40901
- import { resolve as resolve97 } from "path";
40902
- import { readFile as readFile69 } from "fs/promises";
41354
+ import { resolve as resolve98 } from "path";
41355
+ import { readFile as readFile70 } from "fs/promises";
40903
41356
  async function requestCommand(target, text, options = {}) {
40904
41357
  if (!text || !text.trim()) {
40905
41358
  throw new Error("Request text cannot be empty.");
@@ -40915,7 +41368,7 @@ async function requestCommand(target, text, options = {}) {
40915
41368
  if (!isValidSlug(target)) {
40916
41369
  throw new Error(`Invalid assignment slug "${target}".`);
40917
41370
  }
40918
- assignmentDir = resolve97(baseDir, options.project, "assignments", target);
41371
+ assignmentDir = resolve98(baseDir, options.project, "assignments", target);
40919
41372
  targetRef = target;
40920
41373
  } else {
40921
41374
  const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
@@ -40925,12 +41378,12 @@ async function requestCommand(target, text, options = {}) {
40925
41378
  assignmentDir = resolved.assignmentDir;
40926
41379
  targetRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
40927
41380
  }
40928
- const assignmentMdPath2 = resolve97(assignmentDir, "assignment.md");
41381
+ const assignmentMdPath2 = resolve98(assignmentDir, "assignment.md");
40929
41382
  if (!await fileExists(assignmentMdPath2)) {
40930
41383
  throw new Error(`assignment.md not found at ${assignmentMdPath2}`);
40931
41384
  }
40932
41385
  const source = options.from ?? process.env.SYNTAUR_ASSIGNMENT ?? "unknown";
40933
- let content = await readFile69(assignmentMdPath2, "utf-8");
41386
+ let content = await readFile70(assignmentMdPath2, "utf-8");
40934
41387
  content = appendTodosToAssignmentBody(content, [
40935
41388
  { description: `${text.trim()} (from: ${source})` }
40936
41389
  ]);
@@ -40943,14 +41396,15 @@ async function requestCommand(target, text, options = {}) {
40943
41396
  init_fs();
40944
41397
  init_paths();
40945
41398
  init_config2();
41399
+ init_recompute();
40946
41400
  import { Command as Command10 } from "commander";
40947
- import { readFile as readFile70, readdir as readdir34 } from "fs/promises";
40948
- import { resolve as resolve98 } from "path";
41401
+ import { readFile as readFile71, readdir as readdir34 } from "fs/promises";
41402
+ import { resolve as resolve99 } from "path";
40949
41403
  async function readContextAssignmentDir(cwd) {
40950
- const path = resolve98(cwd, ".syntaur", "context.json");
41404
+ const path = resolve99(cwd, ".syntaur", "context.json");
40951
41405
  if (!await fileExists(path)) return null;
40952
41406
  try {
40953
- const raw2 = await readFile70(path, "utf-8");
41407
+ const raw2 = await readFile71(path, "utf-8");
40954
41408
  const ctx = JSON.parse(raw2);
40955
41409
  if (typeof ctx.assignmentDir === "string" && ctx.assignmentDir.length > 0) {
40956
41410
  return ctx.assignmentDir;
@@ -40964,9 +41418,9 @@ async function resolveAssignmentDir(opts) {
40964
41418
  const cwd = opts.cwd ?? process.cwd();
40965
41419
  if (opts.assignment) {
40966
41420
  if (opts.project) {
40967
- return resolve98((await readConfig()).defaultProjectDir, opts.project, "assignments", opts.assignment);
41421
+ return resolve99((await readConfig()).defaultProjectDir, opts.project, "assignments", opts.assignment);
40968
41422
  }
40969
- return resolve98(assignmentsDir(), opts.assignment);
41423
+ return resolve99(assignmentsDir(), opts.assignment);
40970
41424
  }
40971
41425
  const fromCtx = await readContextAssignmentDir(cwd);
40972
41426
  if (fromCtx) return fromCtx;
@@ -41162,17 +41616,17 @@ async function runPlanCreate(options) {
41162
41616
  if (!await fileExists(assignmentDir)) {
41163
41617
  throw new Error(`Assignment directory does not exist: ${assignmentDir}`);
41164
41618
  }
41165
- const assignmentMdPath2 = resolve98(assignmentDir, "assignment.md");
41619
+ const assignmentMdPath2 = resolve99(assignmentDir, "assignment.md");
41166
41620
  if (!await fileExists(assignmentMdPath2)) {
41167
41621
  throw new Error(`Missing assignment.md at: ${assignmentMdPath2}`);
41168
41622
  }
41169
- const planPath = resolve98(assignmentDir, "plan.md");
41623
+ const planPath = resolve99(assignmentDir, "plan.md");
41170
41624
  if (await fileExists(planPath) && !options.force) {
41171
41625
  throw new Error(
41172
41626
  "plan.md already exists. Use --force to overwrite, or `syntaur plan version` to create the next version."
41173
41627
  );
41174
41628
  }
41175
- const assignmentMd = await readFile70(assignmentMdPath2, "utf-8");
41629
+ const assignmentMd = await readFile71(assignmentMdPath2, "utf-8");
41176
41630
  const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
41177
41631
  const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
41178
41632
  await writeFileForce(planPath, buildInitialPlanStub(slug));
@@ -41186,13 +41640,14 @@ async function runPlanCreate(options) {
41186
41640
  }
41187
41641
  console.log(`Created ${planPath}`);
41188
41642
  if (appended > 0) console.log(`Appended ${appended} plan todo(s) to assignment.md.`);
41643
+ await recomputeAssignmentDir(assignmentDir, "plan-create", null);
41189
41644
  }
41190
41645
  async function runPlanVersion(options) {
41191
41646
  const assignmentDir = await resolveAssignmentDir(options);
41192
41647
  if (!await fileExists(assignmentDir)) {
41193
41648
  throw new Error(`Assignment directory does not exist: ${assignmentDir}`);
41194
41649
  }
41195
- const assignmentMdPath2 = resolve98(assignmentDir, "assignment.md");
41650
+ const assignmentMdPath2 = resolve99(assignmentDir, "assignment.md");
41196
41651
  if (!await fileExists(assignmentMdPath2)) {
41197
41652
  throw new Error(`Missing assignment.md at: ${assignmentMdPath2}`);
41198
41653
  }
@@ -41204,15 +41659,15 @@ async function runPlanVersion(options) {
41204
41659
  }
41205
41660
  const current = planFiles[planFiles.length - 1];
41206
41661
  const next = nextPlanFileName(current.version);
41207
- const newPath = resolve98(assignmentDir, next.fileName);
41662
+ const newPath = resolve99(assignmentDir, next.fileName);
41208
41663
  if (await fileExists(newPath) && !options.force) {
41209
41664
  throw new Error(`${next.fileName} already exists. Use --force to overwrite.`);
41210
41665
  }
41211
- const assignmentMd = await readFile70(assignmentMdPath2, "utf-8");
41666
+ const assignmentMd = await readFile71(assignmentMdPath2, "utf-8");
41212
41667
  const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
41213
41668
  const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
41214
- const oldPlanPath = resolve98(assignmentDir, current.fileName);
41215
- const oldPlanContent = await readFile70(oldPlanPath, "utf-8");
41669
+ const oldPlanPath = resolve99(assignmentDir, current.fileName);
41670
+ const oldPlanContent = await readFile71(oldPlanPath, "utf-8");
41216
41671
  const oldBody = oldPlanContent.replace(/^---[\s\S]*?\n---\n?/, "");
41217
41672
  const carriedTodos = extractUncheckedTodos(oldBody);
41218
41673
  const stub = buildNewPlanStub({
@@ -41233,6 +41688,7 @@ async function runPlanVersion(options) {
41233
41688
  );
41234
41689
  console.log(`Path: ${newPath}`);
41235
41690
  console.log(`Carried forward: ${carriedTodos.length} unchecked task(s).`);
41691
+ await recomputeAssignmentDir(assignmentDir, "plan-version", null);
41236
41692
  }
41237
41693
  var planCommand = new Command10("plan").description("Manage plan files for the active assignment");
41238
41694
  planCommand.command("create").description("Create the initial plan.md and append the plan todo-cycle to assignment.md ## Todos").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("--force", "Overwrite an existing plan.md").action(async (options) => {
@@ -41282,28 +41738,28 @@ init_cwd();
41282
41738
  init_session_db();
41283
41739
  init_agent_sessions();
41284
41740
  import { Command as Command11 } from "commander";
41285
- import { readFile as readFile71, readdir as readdir35, stat as stat15 } from "fs/promises";
41286
- import { resolve as resolve99 } from "path";
41741
+ import { readFile as readFile72, readdir as readdir35, stat as stat16 } from "fs/promises";
41742
+ import { resolve as resolve100 } from "path";
41287
41743
  async function readContext(cwd) {
41288
- const path = resolve99(cwd, ".syntaur", "context.json");
41744
+ const path = resolve100(cwd, ".syntaur", "context.json");
41289
41745
  if (!await fileExists(path)) return null;
41290
41746
  try {
41291
- const raw2 = await readFile71(path, "utf-8");
41747
+ const raw2 = await readFile72(path, "utf-8");
41292
41748
  return JSON.parse(raw2);
41293
41749
  } catch {
41294
41750
  return null;
41295
41751
  }
41296
41752
  }
41297
41753
  async function findLatestSessionSummary(assignmentDir) {
41298
- const sessionsRoot = resolve99(assignmentDir, "sessions");
41754
+ const sessionsRoot = resolve100(assignmentDir, "sessions");
41299
41755
  if (!await fileExists(sessionsRoot)) return null;
41300
41756
  const entries = await readdir35(sessionsRoot, { withFileTypes: true });
41301
41757
  let best = null;
41302
41758
  for (const entry of entries) {
41303
41759
  if (!entry.isDirectory()) continue;
41304
- const summaryPath = resolve99(sessionsRoot, entry.name, "summary.md");
41760
+ const summaryPath = resolve100(sessionsRoot, entry.name, "summary.md");
41305
41761
  if (!await fileExists(summaryPath)) continue;
41306
- const st = await stat15(summaryPath);
41762
+ const st = await stat16(summaryPath);
41307
41763
  if (best === null || st.mtime.getTime() > best.mtime.getTime()) {
41308
41764
  best = { sessionId: entry.name, path: summaryPath, mtime: st.mtime };
41309
41765
  }
@@ -41311,9 +41767,9 @@ async function findLatestSessionSummary(assignmentDir) {
41311
41767
  return best;
41312
41768
  }
41313
41769
  async function findOpenHandoff(assignmentDir) {
41314
- const handoffPath = resolve99(assignmentDir, "handoff.md");
41770
+ const handoffPath = resolve100(assignmentDir, "handoff.md");
41315
41771
  if (!await fileExists(handoffPath)) return null;
41316
- const content = await readFile71(handoffPath, "utf-8");
41772
+ const content = await readFile72(handoffPath, "utf-8");
41317
41773
  const body = content.replace(/^---[\s\S]*?\n---\n?/, "").trim();
41318
41774
  if (body.length === 0) return null;
41319
41775
  if (/^<!--[\s\S]*-->$/.test(body)) return null;
@@ -41408,7 +41864,7 @@ async function resolveSaveTarget(options, cwd) {
41408
41864
  let slug;
41409
41865
  const ctx = await readContext(cwd);
41410
41866
  if (options.assignment) {
41411
- assignmentDir = options.project ? resolve99((await readConfig()).defaultProjectDir, options.project, "assignments", options.assignment) : resolve99(assignmentsDir(), options.assignment);
41867
+ assignmentDir = options.project ? resolve100((await readConfig()).defaultProjectDir, options.project, "assignments", options.assignment) : resolve100(assignmentsDir(), options.assignment);
41412
41868
  slug = options.assignment;
41413
41869
  } else {
41414
41870
  if (!ctx?.assignmentDir) {
@@ -41465,21 +41921,21 @@ function extractCreated(content) {
41465
41921
  }
41466
41922
  async function runSessionSave(options, cwd = process.cwd(), body) {
41467
41923
  const { assignmentDir, slug, sessionId } = await resolveSaveTarget(options, cwd);
41468
- if (!await fileExists(resolve99(assignmentDir, "assignment.md"))) {
41924
+ if (!await fileExists(resolve100(assignmentDir, "assignment.md"))) {
41469
41925
  throw new Error(`No assignment found at ${assignmentDir} (missing assignment.md).`);
41470
41926
  }
41471
- const sessionDir = resolve99(assignmentDir, "sessions", sessionId);
41472
- const summaryPath = resolve99(sessionDir, "summary.md");
41927
+ const sessionDir = resolve100(assignmentDir, "sessions", sessionId);
41928
+ const summaryPath = resolve100(sessionDir, "summary.md");
41473
41929
  const now = nowTimestamp();
41474
41930
  let created = now;
41475
41931
  if (await fileExists(summaryPath)) {
41476
- const existing = await readFile71(summaryPath, "utf-8");
41932
+ const existing = await readFile72(summaryPath, "utf-8");
41477
41933
  created = extractCreated(existing) ?? now;
41478
41934
  }
41479
41935
  let sectionBody2 = body;
41480
41936
  if (sectionBody2 === void 0) {
41481
41937
  if (options.fromFile) {
41482
- sectionBody2 = await readFile71(resolve99(cwd, options.fromFile), "utf-8");
41938
+ sectionBody2 = await readFile72(resolve100(cwd, options.fromFile), "utf-8");
41483
41939
  } else {
41484
41940
  sectionBody2 = await readStdin();
41485
41941
  }
@@ -41515,7 +41971,7 @@ async function runSessionRegister(rawStdin, options = {}, deps = {}) {
41515
41971
  if (!isSafeSessionId(sessionId) || !cwd) return result;
41516
41972
  result.sessionId = sessionId;
41517
41973
  const transcriptPath = payload.transcript_path ?? "";
41518
- const contextPath = resolve99(cwd, ".syntaur", "context.json");
41974
+ const contextPath = resolve100(cwd, ".syntaur", "context.json");
41519
41975
  const hasContextFile = await fileExists(contextPath);
41520
41976
  const ctx = hasContextFile ? await readContext(cwd) : null;
41521
41977
  if (ctx) {
@@ -41661,8 +42117,8 @@ sessionCommand.command("resolve-id").description(
41661
42117
  // src/commands/worktree.ts
41662
42118
  init_git_worktree();
41663
42119
  import { Command as Command12 } from "commander";
41664
- import { readFile as readFile72 } from "fs/promises";
41665
- import { resolve as resolve101 } from "path";
42120
+ import { readFile as readFile73 } from "fs/promises";
42121
+ import { resolve as resolve102 } from "path";
41666
42122
  init_fs();
41667
42123
  init_paths();
41668
42124
  init_config2();
@@ -41671,12 +42127,12 @@ init_state_machine();
41671
42127
 
41672
42128
  // src/utils/path-canon.ts
41673
42129
  import { realpathSync as realpathSync5 } from "fs";
41674
- import { resolve as resolve100 } from "path";
42130
+ import { resolve as resolve101 } from "path";
41675
42131
  function canonicalPath(p) {
41676
42132
  try {
41677
- return realpathSync5(resolve100(p));
42133
+ return realpathSync5(resolve101(p));
41678
42134
  } catch {
41679
- return resolve100(p).replace(/\/+$/, "");
42135
+ return resolve101(p).replace(/\/+$/, "");
41680
42136
  }
41681
42137
  }
41682
42138
 
@@ -41704,10 +42160,10 @@ function countSessionsByPath(dbPath, paths) {
41704
42160
  init_timestamp();
41705
42161
  init_frontmatter();
41706
42162
  async function readContext2(cwd) {
41707
- const path = resolve101(cwd, ".syntaur", "context.json");
42163
+ const path = resolve102(cwd, ".syntaur", "context.json");
41708
42164
  if (!await fileExists(path)) return null;
41709
42165
  try {
41710
- return JSON.parse(await readFile72(path, "utf-8"));
42166
+ return JSON.parse(await readFile73(path, "utf-8"));
41711
42167
  } catch {
41712
42168
  return null;
41713
42169
  }
@@ -41716,12 +42172,12 @@ async function resolveAssignmentPath2(opts) {
41716
42172
  if (opts.assignment) {
41717
42173
  if (opts.project) {
41718
42174
  const projectsDir2 = (await readConfig()).defaultProjectDir;
41719
- return resolve101(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
42175
+ return resolve102(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
41720
42176
  }
41721
- return resolve101(assignmentsDir(), opts.assignment, "assignment.md");
42177
+ return resolve102(assignmentsDir(), opts.assignment, "assignment.md");
41722
42178
  }
41723
42179
  const ctx = await readContext2(opts.cwd);
41724
- if (ctx?.assignmentDir) return resolve101(ctx.assignmentDir, "assignment.md");
42180
+ if (ctx?.assignmentDir) return resolve102(ctx.assignmentDir, "assignment.md");
41725
42181
  throw new Error(
41726
42182
  "No active assignment. Pass --assignment <slug> [--project <slug>] or run from a workspace with .syntaur/context.json."
41727
42183
  );
@@ -41732,7 +42188,7 @@ async function runWorktreeCreate(options, cwd = process.cwd()) {
41732
42188
  }
41733
42189
  const repository = options.repository ?? cwd;
41734
42190
  const parentBranch = options.parentBranch ?? "main";
41735
- const worktreePath = options.worktreePath ?? resolve101(repository, ".worktrees", options.branch);
42191
+ const worktreePath = options.worktreePath ?? resolve102(repository, ".worktrees", options.branch);
41736
42192
  const assignmentPath = await resolveAssignmentPath2({
41737
42193
  assignment: options.assignment,
41738
42194
  project: options.project,
@@ -41762,7 +42218,7 @@ async function runWorktreeRemove(options, cwd = process.cwd()) {
41762
42218
  if (!await fileExists(assignmentPath)) {
41763
42219
  throw new Error(`Assignment file not found: ${assignmentPath}`);
41764
42220
  }
41765
- const original = await readFile72(assignmentPath, "utf-8");
42221
+ const original = await readFile73(assignmentPath, "utf-8");
41766
42222
  const fm = parseAssignmentFrontmatter(original);
41767
42223
  const repository = options.repository ?? fm.workspace.repository ?? void 0;
41768
42224
  const worktreePath = fm.workspace.worktreePath ?? void 0;
@@ -41828,7 +42284,7 @@ async function runWorktreeGc(options, cwd = process.cwd()) {
41828
42284
  const owners = /* @__PURE__ */ new Map();
41829
42285
  for (const entry of walk.withAssignmentMd) {
41830
42286
  try {
41831
- const content = await readFile72(resolve101(entry.assignmentDir, "assignment.md"), "utf-8");
42287
+ const content = await readFile73(resolve102(entry.assignmentDir, "assignment.md"), "utf-8");
41832
42288
  const fm = parseAssignmentFrontmatter(content);
41833
42289
  const wp = fm.workspace?.worktreePath;
41834
42290
  if (!wp) continue;
@@ -41850,7 +42306,7 @@ async function runWorktreeGc(options, cwd = process.cwd()) {
41850
42306
  const repoTop = rt ? canonicalPath(rt) : null;
41851
42307
  const cwdTop = ct ? canonicalPath(ct) : null;
41852
42308
  const mainPath = entries.length > 0 ? canonicalPath(entries[0].worktreePath) : null;
41853
- const dbPath = resolve101(syntaurRoot(), "syntaur.db");
42309
+ const dbPath = resolve102(syntaurRoot(), "syntaur.db");
41854
42310
  const candidates = [];
41855
42311
  for (const entry of entries) {
41856
42312
  const canon = canonicalPath(entry.worktreePath);
@@ -42069,8 +42525,8 @@ worktreeCommand.command("gc").description(
42069
42525
  // src/commands/open.ts
42070
42526
  init_fs();
42071
42527
  import { Command as Command13 } from "commander";
42072
- import { readFile as readFile73 } from "fs/promises";
42073
- import { resolve as resolve102 } from "path";
42528
+ import { readFile as readFile74 } from "fs/promises";
42529
+ import { resolve as resolve103 } from "path";
42074
42530
  init_frontmatter();
42075
42531
  init_config2();
42076
42532
  init_paths();
@@ -42124,13 +42580,13 @@ function openInTerminal(path, config) {
42124
42580
  // src/commands/open.ts
42125
42581
  async function runOpen(assignmentArg, options) {
42126
42582
  const resolved = options.id ? await resolveAssignmentTarget(options.id, {}) : await resolveAssignmentTarget(assignmentArg, { project: options.project });
42127
- const assignmentPath = resolve102(resolved.assignmentDir, "assignment.md");
42583
+ const assignmentPath = resolve103(resolved.assignmentDir, "assignment.md");
42128
42584
  if (!await fileExists(assignmentPath)) {
42129
42585
  throw new SyntaurError(`Assignment file not found: ${assignmentPath}`, {
42130
42586
  remediation: "check the assignment slug or --id"
42131
42587
  });
42132
42588
  }
42133
- const fm = parseAssignmentFrontmatter(await readFile73(assignmentPath, "utf-8"));
42589
+ const fm = parseAssignmentFrontmatter(await readFile74(assignmentPath, "utf-8"));
42134
42590
  const worktreePath = fm.workspace?.worktreePath;
42135
42591
  if (!worktreePath) {
42136
42592
  throw new SyntaurError("No worktree recorded for this assignment.", {
@@ -42195,14 +42651,14 @@ init_config2();
42195
42651
  init_fs();
42196
42652
  init_slug();
42197
42653
  import { Command as Command14 } from "commander";
42198
- import { resolve as resolve104 } from "path";
42199
- import { readFile as readFile75, readdir as readdir37, rm as rm15 } from "fs/promises";
42654
+ import { resolve as resolve105 } from "path";
42655
+ import { readFile as readFile76, readdir as readdir37, rm as rm15 } from "fs/promises";
42200
42656
 
42201
42657
  // src/utils/project-indexes.ts
42202
42658
  init_parser();
42203
42659
  init_fs();
42204
- import { readdir as readdir36, readFile as readFile74 } from "fs/promises";
42205
- import { resolve as resolve103 } from "path";
42660
+ import { readdir as readdir36, readFile as readFile75 } from "fs/promises";
42661
+ import { resolve as resolve104 } from "path";
42206
42662
  function nowIso3() {
42207
42663
  return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
42208
42664
  }
@@ -42221,7 +42677,7 @@ function joinList(items) {
42221
42677
  return items.length === 0 ? "\u2014" : items.map(escapeCell).join(", ");
42222
42678
  }
42223
42679
  async function rebuildResourcesIndex(projectDir) {
42224
- const dir = resolve103(projectDir, "resources");
42680
+ const dir = resolve104(projectDir, "resources");
42225
42681
  await ensureDir(dir);
42226
42682
  const files = await listSlugFiles(dir);
42227
42683
  const slug = readProjectSlug(projectDir);
@@ -42237,7 +42693,7 @@ async function rebuildResourcesIndex(projectDir) {
42237
42693
  lines.push("| Name | Category | Source | Related Assignments | Updated |");
42238
42694
  lines.push("|------|----------|--------|---------------------|---------|");
42239
42695
  for (const fileName of files) {
42240
- const content = await readFile74(resolve103(dir, fileName), "utf-8");
42696
+ const content = await readFile75(resolve104(dir, fileName), "utf-8");
42241
42697
  const parsed = parseResource(content);
42242
42698
  const slugBase = fileName.replace(/\.md$/, "");
42243
42699
  const name = parsed.name || slugBase;
@@ -42247,12 +42703,12 @@ async function rebuildResourcesIndex(projectDir) {
42247
42703
  );
42248
42704
  }
42249
42705
  lines.push("");
42250
- const indexPath = resolve103(dir, "_index.md");
42706
+ const indexPath = resolve104(dir, "_index.md");
42251
42707
  await writeFileForce(indexPath, lines.join("\n"));
42252
42708
  return { total: files.length, path: indexPath };
42253
42709
  }
42254
42710
  async function rebuildMemoriesIndex(projectDir) {
42255
- const dir = resolve103(projectDir, "memories");
42711
+ const dir = resolve104(projectDir, "memories");
42256
42712
  await ensureDir(dir);
42257
42713
  const files = await listSlugFiles(dir);
42258
42714
  const slug = readProjectSlug(projectDir);
@@ -42268,7 +42724,7 @@ async function rebuildMemoriesIndex(projectDir) {
42268
42724
  lines.push("| Name | Source | Scope | Source Assignment | Updated |");
42269
42725
  lines.push("|------|--------|-------|-------------------|---------|");
42270
42726
  for (const fileName of files) {
42271
- const content = await readFile74(resolve103(dir, fileName), "utf-8");
42727
+ const content = await readFile75(resolve104(dir, fileName), "utf-8");
42272
42728
  const parsed = parseMemory(content);
42273
42729
  const slugBase = fileName.replace(/\.md$/, "");
42274
42730
  const name = parsed.name || slugBase;
@@ -42278,7 +42734,7 @@ async function rebuildMemoriesIndex(projectDir) {
42278
42734
  );
42279
42735
  }
42280
42736
  lines.push("");
42281
- const indexPath = resolve103(dir, "_index.md");
42737
+ const indexPath = resolve104(dir, "_index.md");
42282
42738
  await writeFileForce(indexPath, lines.join("\n"));
42283
42739
  return { total: files.length, path: indexPath };
42284
42740
  }
@@ -42342,8 +42798,8 @@ ${opts.body ?? "<!-- Add notes about this resource here. -->"}
42342
42798
  }
42343
42799
  async function resolveProjectDir(project) {
42344
42800
  if (!isValidSlug(project)) throw new Error(`Invalid project slug: "${project}".`);
42345
- const projectDir = resolve104((await readConfig()).defaultProjectDir, project);
42346
- if (!await fileExists(resolve104(projectDir, "project.md"))) {
42801
+ const projectDir = resolve105((await readConfig()).defaultProjectDir, project);
42802
+ if (!await fileExists(resolve105(projectDir, "project.md"))) {
42347
42803
  throw new Error(`Project "${project}" not found at ${projectDir}.`);
42348
42804
  }
42349
42805
  return projectDir;
@@ -42356,7 +42812,7 @@ async function runResourceAdd(options) {
42356
42812
  if (!isValidSlug(slug)) {
42357
42813
  throw new Error(`Invalid resource slug: "${slug}".`);
42358
42814
  }
42359
- const filePath = resolve104(projectDir, "resources", `${slug}.md`);
42815
+ const filePath = resolve105(projectDir, "resources", `${slug}.md`);
42360
42816
  if (await fileExists(filePath) && !options.force) {
42361
42817
  throw new Error(
42362
42818
  `Resource "${slug}" already exists at ${filePath}. Use --force to overwrite.`
@@ -42373,7 +42829,7 @@ async function runResourceAdd(options) {
42373
42829
  return { filePath, indexPath, total };
42374
42830
  }
42375
42831
  async function listResourceSlugs(projectDir) {
42376
- const dir = resolve104(projectDir, "resources");
42832
+ const dir = resolve105(projectDir, "resources");
42377
42833
  if (!await fileExists(dir)) return [];
42378
42834
  const entries = await readdir37(dir, { withFileTypes: true });
42379
42835
  return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "_index.md").map((e) => e.name.slice(0, -3)).sort();
@@ -42383,16 +42839,16 @@ async function runResourceList(project) {
42383
42839
  const slugs = await listResourceSlugs(projectDir);
42384
42840
  const out = [];
42385
42841
  for (const slug of slugs) {
42386
- const parsed = parseResource(await readFile75(resolve104(projectDir, "resources", `${slug}.md`), "utf-8"));
42842
+ const parsed = parseResource(await readFile76(resolve105(projectDir, "resources", `${slug}.md`), "utf-8"));
42387
42843
  out.push({ slug, name: parsed.name, category: parsed.category, source: parsed.source, updated: parsed.updated });
42388
42844
  }
42389
42845
  return out;
42390
42846
  }
42391
42847
  async function runResourceShow(project, slug) {
42392
42848
  const projectDir = await resolveProjectDir(project);
42393
- const filePath = resolve104(projectDir, "resources", `${slug}.md`);
42849
+ const filePath = resolve105(projectDir, "resources", `${slug}.md`);
42394
42850
  if (!await fileExists(filePath)) throw new Error(`Resource "${slug}" not found in project "${project}".`);
42395
- const parsed = parseResource(await readFile75(filePath, "utf-8"));
42851
+ const parsed = parseResource(await readFile76(filePath, "utf-8"));
42396
42852
  return {
42397
42853
  slug,
42398
42854
  name: parsed.name,
@@ -42406,14 +42862,14 @@ async function runResourceShow(project, slug) {
42406
42862
  }
42407
42863
  async function runResourceUpdate(slug, options) {
42408
42864
  const projectDir = await resolveProjectDir(options.project);
42409
- const filePath = resolve104(projectDir, "resources", `${slug}.md`);
42865
+ const filePath = resolve105(projectDir, "resources", `${slug}.md`);
42410
42866
  if (!await fileExists(filePath)) {
42411
42867
  throw new Error(`Resource "${slug}" not found in project "${options.project}".`);
42412
42868
  }
42413
42869
  if (options.name === void 0 && options.source === void 0 && options.category === void 0 && options.relatedAssignments === void 0) {
42414
42870
  throw new Error("Provide at least one of --name, --source, --category, --related-assignments.");
42415
42871
  }
42416
- const original = await readFile75(filePath, "utf-8");
42872
+ const original = await readFile76(filePath, "utf-8");
42417
42873
  const content = editResourceFrontmatter(original, {
42418
42874
  name: options.name,
42419
42875
  category: options.category,
@@ -42426,7 +42882,7 @@ async function runResourceUpdate(slug, options) {
42426
42882
  }
42427
42883
  async function runResourceRemove(slug, options) {
42428
42884
  const projectDir = await resolveProjectDir(options.project);
42429
- const filePath = resolve104(projectDir, "resources", `${slug}.md`);
42885
+ const filePath = resolve105(projectDir, "resources", `${slug}.md`);
42430
42886
  if (!await fileExists(filePath)) {
42431
42887
  throw new Error(`Resource "${slug}" not found in project "${options.project}".`);
42432
42888
  }
@@ -42509,8 +42965,8 @@ init_config2();
42509
42965
  init_fs();
42510
42966
  init_slug();
42511
42967
  import { Command as Command15 } from "commander";
42512
- import { resolve as resolve105 } from "path";
42513
- import { readFile as readFile76, readdir as readdir38, rm as rm16 } from "fs/promises";
42968
+ import { resolve as resolve106 } from "path";
42969
+ import { readFile as readFile77, readdir as readdir38, rm as rm16 } from "fs/promises";
42514
42970
  init_parser();
42515
42971
  function nowIso5() {
42516
42972
  return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
@@ -42574,8 +43030,8 @@ ${opts.body ?? "<!-- Capture the load-bearing context for this memory here. -->"
42574
43030
  }
42575
43031
  async function resolveProjectDir2(project) {
42576
43032
  if (!isValidSlug(project)) throw new Error(`Invalid project slug: "${project}".`);
42577
- const projectDir = resolve105((await readConfig()).defaultProjectDir, project);
42578
- if (!await fileExists(resolve105(projectDir, "project.md"))) {
43033
+ const projectDir = resolve106((await readConfig()).defaultProjectDir, project);
43034
+ if (!await fileExists(resolve106(projectDir, "project.md"))) {
42579
43035
  throw new Error(`Project "${project}" not found at ${projectDir}.`);
42580
43036
  }
42581
43037
  return projectDir;
@@ -42588,7 +43044,7 @@ async function runMemoryAdd(options) {
42588
43044
  if (!isValidSlug(slug)) {
42589
43045
  throw new Error(`Invalid memory slug: "${slug}".`);
42590
43046
  }
42591
- const filePath = resolve105(projectDir, "memories", `${slug}.md`);
43047
+ const filePath = resolve106(projectDir, "memories", `${slug}.md`);
42592
43048
  if (await fileExists(filePath) && !options.force) {
42593
43049
  throw new Error(
42594
43050
  `Memory "${slug}" already exists at ${filePath}. Use --force to overwrite.`
@@ -42606,7 +43062,7 @@ async function runMemoryAdd(options) {
42606
43062
  return { filePath, indexPath, total };
42607
43063
  }
42608
43064
  async function listMemorySlugs(projectDir) {
42609
- const dir = resolve105(projectDir, "memories");
43065
+ const dir = resolve106(projectDir, "memories");
42610
43066
  if (!await fileExists(dir)) return [];
42611
43067
  const entries = await readdir38(dir, { withFileTypes: true });
42612
43068
  return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "_index.md").map((e) => e.name.slice(0, -3)).sort();
@@ -42616,16 +43072,16 @@ async function runMemoryList(project) {
42616
43072
  const slugs = await listMemorySlugs(projectDir);
42617
43073
  const out = [];
42618
43074
  for (const slug of slugs) {
42619
- const parsed = parseMemory(await readFile76(resolve105(projectDir, "memories", `${slug}.md`), "utf-8"));
43075
+ const parsed = parseMemory(await readFile77(resolve106(projectDir, "memories", `${slug}.md`), "utf-8"));
42620
43076
  out.push({ slug, name: parsed.name, scope: parsed.scope, source: parsed.source, updated: parsed.updated });
42621
43077
  }
42622
43078
  return out;
42623
43079
  }
42624
43080
  async function runMemoryShow(project, slug) {
42625
43081
  const projectDir = await resolveProjectDir2(project);
42626
- const filePath = resolve105(projectDir, "memories", `${slug}.md`);
43082
+ const filePath = resolve106(projectDir, "memories", `${slug}.md`);
42627
43083
  if (!await fileExists(filePath)) throw new Error(`Memory "${slug}" not found in project "${project}".`);
42628
- const parsed = parseMemory(await readFile76(filePath, "utf-8"));
43084
+ const parsed = parseMemory(await readFile77(filePath, "utf-8"));
42629
43085
  return {
42630
43086
  slug,
42631
43087
  name: parsed.name,
@@ -42640,7 +43096,7 @@ async function runMemoryShow(project, slug) {
42640
43096
  }
42641
43097
  async function runMemoryUpdate(slug, options) {
42642
43098
  const projectDir = await resolveProjectDir2(options.project);
42643
- const filePath = resolve105(projectDir, "memories", `${slug}.md`);
43099
+ const filePath = resolve106(projectDir, "memories", `${slug}.md`);
42644
43100
  if (!await fileExists(filePath)) {
42645
43101
  throw new Error(`Memory "${slug}" not found in project "${options.project}".`);
42646
43102
  }
@@ -42649,7 +43105,7 @@ async function runMemoryUpdate(slug, options) {
42649
43105
  "Provide at least one of --name, --source, --scope, --source-assignment, --related-assignments."
42650
43106
  );
42651
43107
  }
42652
- const original = await readFile76(filePath, "utf-8");
43108
+ const original = await readFile77(filePath, "utf-8");
42653
43109
  const content = editMemoryFrontmatter(original, {
42654
43110
  name: options.name,
42655
43111
  source: options.source,
@@ -42663,7 +43119,7 @@ async function runMemoryUpdate(slug, options) {
42663
43119
  }
42664
43120
  async function runMemoryRemove(slug, options) {
42665
43121
  const projectDir = await resolveProjectDir2(options.project);
42666
- const filePath = resolve105(projectDir, "memories", `${slug}.md`);
43122
+ const filePath = resolve106(projectDir, "memories", `${slug}.md`);
42667
43123
  if (!await fileExists(filePath)) {
42668
43124
  throw new Error(`Memory "${slug}" not found in project "${options.project}".`);
42669
43125
  }
@@ -42752,8 +43208,8 @@ init_derive();
42752
43208
  init_recompute();
42753
43209
  init_query();
42754
43210
  import { Command as Command16 } from "commander";
42755
- import { readFile as readFile77 } from "fs/promises";
42756
- import { dirname as dirname29, resolve as resolve106 } from "path";
43211
+ import { readFile as readFile78 } from "fs/promises";
43212
+ import { dirname as dirname29, resolve as resolve107 } from "path";
42757
43213
  var AGE_PATTERN = /^(\d+)([dhwm])$/i;
42758
43214
  function parseAgeToCutoff(age) {
42759
43215
  const match = age.match(AGE_PATTERN);
@@ -42772,7 +43228,7 @@ function parseAgeToCutoff(age) {
42772
43228
  }
42773
43229
  function assignmentMdPath(item) {
42774
43230
  if (item.projectSlug) {
42775
- return resolve106(
43231
+ return resolve107(
42776
43232
  defaultProjectDir(),
42777
43233
  item.projectSlug,
42778
43234
  "assignments",
@@ -42780,13 +43236,13 @@ function assignmentMdPath(item) {
42780
43236
  "assignment.md"
42781
43237
  );
42782
43238
  }
42783
- return resolve106(assignmentsDir(), item.id, "assignment.md");
43239
+ return resolve107(assignmentsDir(), item.id, "assignment.md");
42784
43240
  }
42785
43241
  async function loadTags(item) {
42786
43242
  const path = assignmentMdPath(item);
42787
43243
  if (!await fileExists(path)) return [];
42788
43244
  try {
42789
- const content = await readFile77(path, "utf-8");
43245
+ const content = await readFile78(path, "utf-8");
42790
43246
  return parseAssignmentFrontmatter(content).tags;
42791
43247
  } catch {
42792
43248
  return [];
@@ -42850,11 +43306,11 @@ async function loadQueryItem(item, terminalStatuses3, now, declarations) {
42850
43306
  const path = assignmentMdPath(item);
42851
43307
  if (!await fileExists(path)) return null;
42852
43308
  try {
42853
- const content = await readFile77(path, "utf-8");
43309
+ const content = await readFile78(path, "utf-8");
42854
43310
  const fm = parseAssignmentFrontmatter(content);
42855
43311
  const body = content.replace(/^---\n[\s\S]*?\n---/, "");
42856
43312
  const assignmentDir = dirname29(path);
42857
- const projectDir = item.projectSlug ? resolve106(defaultProjectDir(), item.projectSlug) : null;
43313
+ const projectDir = item.projectSlug ? resolve107(defaultProjectDir(), item.projectSlug) : null;
42858
43314
  const facts = await computeFacts({ assignmentDir, frontmatter: fm, body, projectDir, terminalStatuses: terminalStatuses3, declarations });
42859
43315
  const history = fm.statusHistory;
42860
43316
  const lastHeadlineChange = [...history].reverse().find((e) => e.from !== e.to || e.from === null);
@@ -43624,8 +44080,8 @@ init_fs();
43624
44080
  init_timestamp();
43625
44081
  init_frontmatter();
43626
44082
  import { Command as Command21 } from "commander";
43627
- import { readFile as readFile78 } from "fs/promises";
43628
- import { resolve as resolve107 } from "path";
44083
+ import { readFile as readFile79 } from "fs/promises";
44084
+ import { resolve as resolve108 } from "path";
43629
44085
  function renameAssignmentStatusRefs(content, id, newId, now) {
43630
44086
  const fm = parseAssignmentFrontmatter(content);
43631
44087
  const updates = { updated: now };
@@ -43646,12 +44102,12 @@ function fail3(error) {
43646
44102
  process.exit(1);
43647
44103
  }
43648
44104
  function configPath() {
43649
- return resolve107(syntaurRoot(), "config.md");
44105
+ return resolve108(syntaurRoot(), "config.md");
43650
44106
  }
43651
44107
  async function readStatusBlock() {
43652
44108
  const p = configPath();
43653
44109
  if (!await fileExists(p)) return null;
43654
- const content = await readFile78(p, "utf-8");
44110
+ const content = await readFile79(p, "utf-8");
43655
44111
  return parseStatusConfig(content);
43656
44112
  }
43657
44113
  async function requireStatusBlock() {
@@ -43935,7 +44391,7 @@ async function runStatusRename(id, opts) {
43935
44391
  printBlockDiff(before, after);
43936
44392
  const now2 = nowTimestamp();
43937
44393
  for (const a of affected) {
43938
- const original = await readFile78(a.path, "utf-8");
44394
+ const original = await readFile79(a.path, "utf-8");
43939
44395
  const rewritten = renameAssignmentStatusRefs(original, id, newId, now2);
43940
44396
  console.log(`
43941
44397
  --- ${a.display}/assignment.md`);
@@ -43946,9 +44402,9 @@ async function runStatusRename(id, opts) {
43946
44402
  }
43947
44403
  const cfgPath = configPath();
43948
44404
  const buffers = /* @__PURE__ */ new Map();
43949
- buffers.set(cfgPath, await fileExists(cfgPath) ? await readFile78(cfgPath, "utf-8") : "");
44405
+ buffers.set(cfgPath, await fileExists(cfgPath) ? await readFile79(cfgPath, "utf-8") : "");
43950
44406
  for (const a of affected) {
43951
- buffers.set(a.path, await readFile78(a.path, "utf-8"));
44407
+ buffers.set(a.path, await readFile79(a.path, "utf-8"));
43952
44408
  }
43953
44409
  const now = nowTimestamp();
43954
44410
  try {
@@ -44150,13 +44606,13 @@ init_config2();
44150
44606
  init_timestamp();
44151
44607
  init_frontmatter();
44152
44608
  import { Command as Command22 } from "commander";
44153
- import { readFile as readFile79 } from "fs/promises";
44154
- import { resolve as resolve108 } from "path";
44609
+ import { readFile as readFile80 } from "fs/promises";
44610
+ import { resolve as resolve109 } from "path";
44155
44611
  async function readContext3(cwd) {
44156
- const path = resolve108(cwd, ".syntaur", "context.json");
44612
+ const path = resolve109(cwd, ".syntaur", "context.json");
44157
44613
  if (!await fileExists(path)) return null;
44158
44614
  try {
44159
- return JSON.parse(await readFile79(path, "utf-8"));
44615
+ return JSON.parse(await readFile80(path, "utf-8"));
44160
44616
  } catch {
44161
44617
  return null;
44162
44618
  }
@@ -44165,12 +44621,12 @@ async function resolveAssignmentPath3(opts) {
44165
44621
  if (opts.assignment) {
44166
44622
  if (opts.project) {
44167
44623
  const projectsDir2 = (await readConfig()).defaultProjectDir;
44168
- return resolve108(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
44624
+ return resolve109(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
44169
44625
  }
44170
- return resolve108(assignmentsDir(), opts.assignment, "assignment.md");
44626
+ return resolve109(assignmentsDir(), opts.assignment, "assignment.md");
44171
44627
  }
44172
44628
  const ctx = await readContext3(opts.cwd);
44173
- if (ctx?.assignmentDir) return resolve108(ctx.assignmentDir, "assignment.md");
44629
+ if (ctx?.assignmentDir) return resolve109(ctx.assignmentDir, "assignment.md");
44174
44630
  throw new Error(
44175
44631
  "No active assignment. Pass --assignment <slug> [--project <slug>] or run from a workspace with .syntaur/context.json."
44176
44632
  );
@@ -44207,7 +44663,7 @@ async function runWorkspaceSet(options, cwd = process.cwd()) {
44207
44663
  ${pre.errors.map((e) => ` - ${e}`).join("\n")}`
44208
44664
  );
44209
44665
  }
44210
- const original = await readFile79(path, "utf-8");
44666
+ const original = await readFile80(path, "utf-8");
44211
44667
  let next = updateAssignmentWorkspace(original, partial);
44212
44668
  next = updateAssignmentFile(next, { updated: nowTimestamp() });
44213
44669
  await writeFileForce(path, next);
@@ -44244,13 +44700,13 @@ init_config2();
44244
44700
  init_timestamp();
44245
44701
  init_templates();
44246
44702
  import { Command as Command23 } from "commander";
44247
- import { readFile as readFile80 } from "fs/promises";
44248
- import { resolve as resolve109 } from "path";
44703
+ import { readFile as readFile81 } from "fs/promises";
44704
+ import { resolve as resolve110 } from "path";
44249
44705
  async function readContext4(cwd) {
44250
- const path = resolve109(cwd, ".syntaur", "context.json");
44706
+ const path = resolve110(cwd, ".syntaur", "context.json");
44251
44707
  if (!await fileExists(path)) return null;
44252
44708
  try {
44253
- return JSON.parse(await readFile80(path, "utf-8"));
44709
+ return JSON.parse(await readFile81(path, "utf-8"));
44254
44710
  } catch {
44255
44711
  return null;
44256
44712
  }
@@ -44260,11 +44716,11 @@ async function resolveAssignmentDir2(opts) {
44260
44716
  if (opts.project) {
44261
44717
  const projectsDir2 = (await readConfig()).defaultProjectDir;
44262
44718
  return {
44263
- dir: resolve109(projectsDir2, opts.project, "assignments", opts.assignment),
44719
+ dir: resolve110(projectsDir2, opts.project, "assignments", opts.assignment),
44264
44720
  slug: opts.assignment
44265
44721
  };
44266
44722
  }
44267
- return { dir: resolve109(assignmentsDir(), opts.assignment), slug: opts.assignment };
44723
+ return { dir: resolve110(assignmentsDir(), opts.assignment), slug: opts.assignment };
44268
44724
  }
44269
44725
  const ctx = await readContext4(opts.cwd);
44270
44726
  if (ctx?.assignmentDir) {
@@ -44326,12 +44782,12 @@ async function runProgressLog(text, options, cwd = process.cwd()) {
44326
44782
  project: options.project,
44327
44783
  cwd
44328
44784
  });
44329
- if (!await fileExists(resolve109(dir, "assignment.md"))) {
44785
+ if (!await fileExists(resolve110(dir, "assignment.md"))) {
44330
44786
  throw new Error(`No assignment found at ${dir} (missing assignment.md).`);
44331
44787
  }
44332
- const path = resolve109(dir, "progress.md");
44788
+ const path = resolve110(dir, "progress.md");
44333
44789
  const now = nowTimestamp();
44334
- const content = await fileExists(path) ? await readFile80(path, "utf-8") : renderProgress({ assignment: slug, timestamp: now });
44790
+ const content = await fileExists(path) ? await readFile81(path, "utf-8") : renderProgress({ assignment: slug, timestamp: now });
44335
44791
  const next = appendProgressEntry(content, text, now);
44336
44792
  await writeFileForce(path, next);
44337
44793
  return path;
@@ -44536,7 +44992,7 @@ program.command("attest").description("Record an attestation (agent reviewed a r
44536
44992
  await attestCommand(assignment, fact, options);
44537
44993
  })
44538
44994
  );
44539
- 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(
44995
+ 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").option("--if-migrated", "No-op unless derive migration has run (for implicit triggers like session-end hooks)").action(
44540
44996
  runCommand(async (assignment, options) => {
44541
44997
  await recomputeCommand(assignment, options);
44542
44998
  })