syntaur 0.67.0 → 0.68.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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);
@@ -20200,7 +20533,7 @@ async function resolveSessionPlan(input4, terminal) {
20200
20533
  init_launch();
20201
20534
  import { spawn as spawn3 } from "child_process";
20202
20535
  import { homedir as homedir5 } from "os";
20203
- import { basename as basename4, join as join6, resolve as resolve29 } from "path";
20536
+ import { basename as basename5, join as join6, resolve as resolve29 } from "path";
20204
20537
  init_fs();
20205
20538
  init_config2();
20206
20539
  init_session_id();
@@ -20220,7 +20553,7 @@ var TerminalNotFoundError = class extends Error {
20220
20553
  var realSpawn = (command, args, options) => spawn3(command, args, options);
20221
20554
  var WRAPPER_COMMANDS = /* @__PURE__ */ new Set(["osascript", "open", "sh"]);
20222
20555
  function isWrapperCommand(command) {
20223
- return WRAPPER_COMMANDS.has(basename4(command));
20556
+ return WRAPPER_COMMANDS.has(basename5(command));
20224
20557
  }
20225
20558
  var WRAPPER_EXIT_TIMEOUT_MS = 1500;
20226
20559
  async function executeLaunchPlan(plan, spawnFn = realSpawn) {
@@ -20248,7 +20581,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
20248
20581
  `Spawn failed: ${msg2}. Verify the terminal is installed and on PATH.`
20249
20582
  );
20250
20583
  }
20251
- await new Promise((resolve110, reject) => {
20584
+ await new Promise((resolve111, reject) => {
20252
20585
  let settled = false;
20253
20586
  let stderr = "";
20254
20587
  const finishOk = () => {
@@ -20258,7 +20591,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
20258
20591
  child.unref();
20259
20592
  } catch {
20260
20593
  }
20261
- resolve110();
20594
+ resolve111();
20262
20595
  };
20263
20596
  const finishErr = (remediation) => {
20264
20597
  if (settled) return;
@@ -20524,7 +20857,7 @@ function normalizeSlashes(p) {
20524
20857
  }
20525
20858
  function detectInstallKind(scriptUrl, opts = {}) {
20526
20859
  const realpath3 = opts.realpath ?? realpathSync.native;
20527
- const readFile81 = opts.readFile ?? ((p) => readFileSync2(p, "utf-8"));
20860
+ const readFile82 = opts.readFile ?? ((p) => readFileSync2(p, "utf-8"));
20528
20861
  const ua = opts.envUserAgent !== void 0 ? opts.envUserAgent : process.env.npm_config_user_agent ?? "";
20529
20862
  const resolved = resolveScriptPath(scriptUrl, realpath3);
20530
20863
  if (resolved === null) {
@@ -20545,7 +20878,7 @@ function detectInstallKind(scriptUrl, opts = {}) {
20545
20878
  const pkgJsonPath = join7(dir, "package.json");
20546
20879
  let raw2;
20547
20880
  try {
20548
- raw2 = readFile81(pkgJsonPath);
20881
+ raw2 = readFile82(pkgJsonPath);
20549
20882
  } catch {
20550
20883
  const parent2 = dirname7(dir);
20551
20884
  if (parent2 === dir) break;
@@ -22610,7 +22943,7 @@ async function readEvents(jobId) {
22610
22943
 
22611
22944
  // src/schedules/attempt.ts
22612
22945
  import { createHash as createHash3 } from "crypto";
22613
- import { open as open4, readFile as readFile23, stat as stat4, unlink as unlink6 } from "fs/promises";
22946
+ import { open as open4, readFile as readFile23, stat as stat5, unlink as unlink6 } from "fs/promises";
22614
22947
  import { resolve as resolve34 } from "path";
22615
22948
 
22616
22949
  // src/schedules/triggers.ts
@@ -22807,7 +23140,7 @@ async function acquireJobLock(id) {
22807
23140
  const code = err2.code;
22808
23141
  if (code !== "EEXIST") throw err2;
22809
23142
  try {
22810
- const info = await stat4(lockPath);
23143
+ const info = await stat5(lockPath);
22811
23144
  if (Date.now() - info.mtimeMs > LOCK_STALE_MS2) {
22812
23145
  await unlink6(lockPath).catch(() => {
22813
23146
  });
@@ -24474,8 +24807,8 @@ init_api();
24474
24807
  import { raw } from "express";
24475
24808
 
24476
24809
  // 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";
24810
+ 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";
24811
+ import { resolve as resolve41, basename as basename6, dirname as dirname10, extname } from "path";
24479
24812
 
24480
24813
  // src/utils/proof-artifact-id.ts
24481
24814
  import { randomBytes as randomBytes2 } from "crypto";
@@ -24572,7 +24905,7 @@ function isSafeInlineMime(mime) {
24572
24905
  return SAFE_INLINE_MIME.has(mime);
24573
24906
  }
24574
24907
  function sanitizeAttachmentName(name) {
24575
- let n = basename5(name || "").replace(/["'\\/]/g, "_");
24908
+ let n = basename6(name || "").replace(/["'\\/]/g, "_");
24576
24909
  n = Array.from(n, (ch) => {
24577
24910
  const code = ch.charCodeAt(0);
24578
24911
  return code < 32 || code === 127 ? "_" : ch;
@@ -24595,7 +24928,7 @@ function attachmentDirFor(todosDir2, scopeId, todoId) {
24595
24928
  }
24596
24929
  async function dirExists(p) {
24597
24930
  try {
24598
- return (await stat5(p)).isDirectory();
24931
+ return (await stat6(p)).isDirectory();
24599
24932
  } catch {
24600
24933
  return false;
24601
24934
  }
@@ -24630,7 +24963,7 @@ async function listAttachments(todosDir2, scopeId, todoId) {
24630
24963
  if (!ATTACHMENT_ID_RE.test(id)) continue;
24631
24964
  const filename = stored.slice(sep2 + 2);
24632
24965
  try {
24633
- const st = await stat5(resolve41(dir, stored));
24966
+ const st = await stat6(resolve41(dir, stored));
24634
24967
  if (!st.isFile()) continue;
24635
24968
  out.push({ id, filename, mime: mimeForName(filename), size: st.size, createdAt: st.mtime.toISOString() });
24636
24969
  } catch {
@@ -25085,8 +25418,8 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
25085
25418
  router.post("/:workspace/archive", async (req2, res) => {
25086
25419
  try {
25087
25420
  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");
25421
+ const { resolve: resolve111 } = await import("path");
25422
+ const { readFile: readFile82 } = await import("fs/promises");
25090
25423
  const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
25091
25424
  const workspace = getWorkspaceParam(req2.params.workspace);
25092
25425
  const outcome = await wsLock(workspace, async () => {
@@ -25102,10 +25435,10 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
25102
25435
  (e) => e.itemIds.every((id) => completedIds.has(id))
25103
25436
  );
25104
25437
  const archFile = archivePath2(todosDir2, workspace, checklist.archiveInterval);
25105
- await ensureDir(resolve110(todosDir2, "archive"));
25438
+ await ensureDir(resolve111(todosDir2, "archive"));
25106
25439
  let archContent = "";
25107
25440
  if (await fileExists(archFile)) {
25108
- archContent = await readFile81(archFile, "utf-8");
25441
+ archContent = await readFile82(archFile, "utf-8");
25109
25442
  archContent = archContent.trimEnd() + "\n\n";
25110
25443
  } else {
25111
25444
  archContent = `---
@@ -25406,7 +25739,7 @@ workspace: ${workspace}
25406
25739
  const { readConfig: readConfig3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
25407
25740
  const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
25408
25741
  const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
25409
- const { readFile: readFile81 } = await import("fs/promises");
25742
+ const { readFile: readFile82 } = await import("fs/promises");
25410
25743
  const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
25411
25744
  const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
25412
25745
  let assignmentRef;
@@ -25427,7 +25760,7 @@ workspace: ${workspace}
25427
25760
  }
25428
25761
  const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
25429
25762
  if (!await fileExists2(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
25430
- let content = await readFile81(assignmentMdPath2, "utf-8");
25763
+ let content = await readFile82(assignmentMdPath2, "utf-8");
25431
25764
  content = appendTodosToAssignmentBody2(
25432
25765
  content,
25433
25766
  items.map((it) => ({
@@ -26837,7 +27170,7 @@ init_fs();
26837
27170
  init_config2();
26838
27171
  import { execFile as execFile2 } from "child_process";
26839
27172
  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";
27173
+ 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
27174
  import { resolve as resolve44, join as join9 } from "path";
26842
27175
  import { tmpdir } from "os";
26843
27176
  var exec2 = promisify2(execFile2);
@@ -26931,7 +27264,7 @@ async function cloneOrInit(repoUrl, destDir) {
26931
27264
  }
26932
27265
  async function copyRecursive(src, dest) {
26933
27266
  if (!await fileExists(src)) return;
26934
- const s = await stat6(src);
27267
+ const s = await stat7(src);
26935
27268
  if (s.isDirectory()) {
26936
27269
  await ensureDir(dest);
26937
27270
  await cp2(src, dest, { recursive: true, force: true });
@@ -27622,7 +27955,7 @@ async function runCcusage(opts = {}) {
27622
27955
  };
27623
27956
  }
27624
27957
  function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
27625
- return new Promise((resolve110) => {
27958
+ return new Promise((resolve111) => {
27626
27959
  const child = spawn4(binary, args, {
27627
27960
  env: env ?? process.env,
27628
27961
  stdio: ["ignore", "pipe", "pipe"]
@@ -27636,7 +27969,7 @@ function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
27636
27969
  if (settled) return;
27637
27970
  settled = true;
27638
27971
  clearTimeout(timer3);
27639
- resolve110(result);
27972
+ resolve111(result);
27640
27973
  };
27641
27974
  const timer3 = setTimeout(() => {
27642
27975
  timedOut = true;
@@ -28586,6 +28919,8 @@ function createDashboardServer(options) {
28586
28919
  });
28587
28920
  }
28588
28921
  let watcherHandle = null;
28922
+ const STALENESS_WATCHDOG_INTERVAL_MS = 5 * 60 * 1e3;
28923
+ let stalenessWatchdogTimer = null;
28589
28924
  return {
28590
28925
  async start() {
28591
28926
  const { recomputeAndWrite: recomputeAndWrite2, recomputeAll: recomputeAll2, resolveDeriveContext: resolveDeriveContext2, isDeriveMigrated: isDeriveMigrated2 } = await Promise.resolve().then(() => (init_recompute(), recompute_exports));
@@ -28682,6 +29017,38 @@ function createDashboardServer(options) {
28682
29017
  onAgentSessionsChanged: () => broadcast({ type: "agent-sessions-updated", timestamp: (/* @__PURE__ */ new Date()).toISOString() })
28683
29018
  });
28684
29019
  startUsageCollector();
29020
+ const startupConfig = await readConfig();
29021
+ if (startupConfig.stalenessWatchdog) {
29022
+ const { collectStaleCandidates: collectStaleCandidates2 } = await Promise.resolve().then(() => (init_api(), api_exports));
29023
+ const { runStalenessWatchdogTick: runStalenessWatchdogTick2 } = await Promise.resolve().then(() => (init_watchdog(), watchdog_exports));
29024
+ const { emitEvent: emitEvent2 } = await Promise.resolve().then(() => (init_event_emit(), event_emit_exports));
29025
+ const stalenessSeen = /* @__PURE__ */ new Set();
29026
+ const watchdogTick = async () => {
29027
+ if (!await migrationGate()) return;
29028
+ try {
29029
+ const candidates = await collectStaleCandidates2(projectsDir2, assignmentsDir2);
29030
+ const summary = runStalenessWatchdogTick2(candidates, stalenessSeen, (e) => {
29031
+ emitEvent2({
29032
+ assignmentId: e.assignmentId,
29033
+ projectSlug: e.projectSlug,
29034
+ type: e.type,
29035
+ actor: "system",
29036
+ details: { reasons: e.reasons.map((r) => r.kind) }
29037
+ });
29038
+ });
29039
+ if (summary.newlyStale > 0 || summary.cleared > 0) {
29040
+ console.log(
29041
+ `staleness watchdog: ${summary.newlyStale} newly stale, ${summary.cleared} cleared (${summary.stale}/${summary.scanned} stale).`
29042
+ );
29043
+ }
29044
+ } catch (err2) {
29045
+ console.error("staleness watchdog tick failed:", err2);
29046
+ }
29047
+ };
29048
+ void watchdogTick();
29049
+ stalenessWatchdogTimer = setInterval(() => void watchdogTick(), STALENESS_WATCHDOG_INTERVAL_MS);
29050
+ stalenessWatchdogTimer.unref?.();
29051
+ }
28685
29052
  return new Promise((resolvePromise, reject) => {
28686
29053
  server.on("error", (err2) => {
28687
29054
  if (err2.code === "EADDRINUSE") {
@@ -28703,6 +29070,10 @@ function createDashboardServer(options) {
28703
29070
  });
28704
29071
  },
28705
29072
  async stop() {
29073
+ if (stalenessWatchdogTimer) {
29074
+ clearInterval(stalenessWatchdogTimer);
29075
+ stalenessWatchdogTimer = null;
29076
+ }
28706
29077
  await stopAutodiscovery();
28707
29078
  await stopUsageCollector();
28708
29079
  if (watcherHandle) {
@@ -29069,17 +29440,173 @@ init_facts();
29069
29440
  init_git_worktree();
29070
29441
  init_recompute();
29071
29442
  init_event_emit();
29443
+ init_transitions();
29444
+ import { readFile as readFile34 } from "fs/promises";
29445
+ import { resolve as resolve52, basename as basename7 } from "path";
29446
+
29447
+ // src/utils/assignment-target.ts
29448
+ init_paths();
29449
+ init_fs();
29450
+ init_config2();
29451
+ init_slug();
29452
+ init_assignment_resolver();
29453
+ init_parser();
29454
+ import { resolve as resolve51 } from "path";
29072
29455
  import { readFile as readFile33 } from "fs/promises";
29073
- import { resolve as resolve51, basename as basename6 } from "path";
29456
+ var AssignmentTargetError = class extends Error {
29457
+ };
29458
+ function classifyContext(ctx) {
29459
+ if (!ctx) return "empty";
29460
+ const hasAssignment = Boolean(ctx.assignmentDir) || Boolean(ctx.assignmentSlug) || Boolean(ctx.projectSlug);
29461
+ if (hasAssignment) return "assignment";
29462
+ if (ctx.bundleId) return "bundle";
29463
+ if (ctx.sessionId || ctx.transcriptPath) return "standalone";
29464
+ return "empty";
29465
+ }
29466
+ async function readAssignmentFrontmatterId(assignmentDir) {
29467
+ const path = resolve51(assignmentDir, "assignment.md");
29468
+ if (!await fileExists(path)) return null;
29469
+ try {
29470
+ const content = await readFile33(path, "utf-8");
29471
+ const [fm] = extractFrontmatter(content);
29472
+ return getField(fm, "id");
29473
+ } catch {
29474
+ return null;
29475
+ }
29476
+ }
29477
+ async function readContextJson(cwd) {
29478
+ const path = resolve51(cwd, ".syntaur", "context.json");
29479
+ if (!await fileExists(path)) return null;
29480
+ try {
29481
+ const raw2 = await readFile33(path, "utf-8");
29482
+ return JSON.parse(raw2);
29483
+ } catch {
29484
+ return null;
29485
+ }
29486
+ }
29487
+ async function resolveAssignmentTarget(input4, opts = {}) {
29488
+ const config = await readConfig();
29489
+ const baseDir = opts.dir ? expandHome(opts.dir) : config.defaultProjectDir;
29490
+ if (opts.project) {
29491
+ if (!input4) {
29492
+ throw new AssignmentTargetError(
29493
+ "--project requires an assignment slug as a positional argument."
29494
+ );
29495
+ }
29496
+ if (!isValidSlug(opts.project)) {
29497
+ throw new AssignmentTargetError(`Invalid project slug "${opts.project}".`);
29498
+ }
29499
+ if (!isValidSlug(input4)) {
29500
+ throw new AssignmentTargetError(`Invalid assignment slug "${input4}".`);
29501
+ }
29502
+ const projectDir = resolve51(baseDir, opts.project);
29503
+ const projectMdPath = resolve51(projectDir, "project.md");
29504
+ if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
29505
+ throw new AssignmentTargetError(
29506
+ `Project "${opts.project}" not found at ${projectDir}.`
29507
+ );
29508
+ }
29509
+ const assignmentDir = resolve51(projectDir, "assignments", input4);
29510
+ const assignmentMdPath2 = resolve51(assignmentDir, "assignment.md");
29511
+ if (!await fileExists(assignmentMdPath2)) {
29512
+ throw new AssignmentTargetError(
29513
+ `Assignment "${input4}" not found in project "${opts.project}".`
29514
+ );
29515
+ }
29516
+ const id = await readAssignmentFrontmatterId(assignmentDir) ?? input4;
29517
+ return {
29518
+ assignmentDir,
29519
+ projectSlug: opts.project,
29520
+ assignmentSlug: input4,
29521
+ id,
29522
+ standalone: false,
29523
+ workspaceGroup: null
29524
+ };
29525
+ }
29526
+ if (input4) {
29527
+ const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), input4);
29528
+ if (!resolved) {
29529
+ throw new AssignmentTargetError(
29530
+ `Assignment "${input4}" not found. Provide --project <slug> + <slug> or a valid standalone UUID.`
29531
+ );
29532
+ }
29533
+ return resolved;
29534
+ }
29535
+ const cwd = opts.cwd ?? process.cwd();
29536
+ const ctx = await readContextJson(cwd);
29537
+ if (!ctx) {
29538
+ throw new AssignmentTargetError(
29539
+ "No assignment specified. Provide an argument, --project + slug, or run from a directory with .syntaur/context.json."
29540
+ );
29541
+ }
29542
+ if (classifyContext(ctx) === "bundle" && ctx.bundleId) {
29543
+ throw new AssignmentTargetError(
29544
+ `Context is bound to bundle b:${ctx.bundleId}, not an assignment. Use \`syntaur todo bundle show ${ctx.bundleId}\` or the complete-bundle skill.`
29545
+ );
29546
+ }
29547
+ if (ctx.assignmentDir) {
29548
+ const dir = expandHome(ctx.assignmentDir);
29549
+ const assignmentMdPath2 = resolve51(dir, "assignment.md");
29550
+ if (!await fileExists(assignmentMdPath2)) {
29551
+ throw new AssignmentTargetError(
29552
+ `.syntaur/context.json points to a missing assignment dir: ${dir}.`
29553
+ );
29554
+ }
29555
+ const id = await readAssignmentFrontmatterId(dir);
29556
+ if (!id || id.trim() === "") {
29557
+ throw new AssignmentTargetError(
29558
+ `.syntaur/context.json points to an assignment with no frontmatter \`id\`: ${dir}.`
29559
+ );
29560
+ }
29561
+ const assignmentSlug = ctx.assignmentSlug ?? dir.split("/").pop() ?? "";
29562
+ const projectSlug = ctx.projectSlug ?? null;
29563
+ return {
29564
+ assignmentDir: dir,
29565
+ projectSlug,
29566
+ assignmentSlug,
29567
+ id,
29568
+ standalone: projectSlug === null,
29569
+ workspaceGroup: null
29570
+ };
29571
+ }
29572
+ if (ctx.projectSlug && ctx.assignmentSlug) {
29573
+ if (!isValidSlug(ctx.projectSlug) || !isValidSlug(ctx.assignmentSlug)) {
29574
+ throw new AssignmentTargetError(
29575
+ `.syntaur/context.json contains invalid slugs: project="${ctx.projectSlug}" assignment="${ctx.assignmentSlug}".`
29576
+ );
29577
+ }
29578
+ const assignmentDir = resolve51(baseDir, ctx.projectSlug, "assignments", ctx.assignmentSlug);
29579
+ const assignmentMdPath2 = resolve51(assignmentDir, "assignment.md");
29580
+ if (!await fileExists(assignmentMdPath2)) {
29581
+ throw new AssignmentTargetError(
29582
+ `.syntaur/context.json points to a missing assignment: ${assignmentDir}.`
29583
+ );
29584
+ }
29585
+ const id = await readAssignmentFrontmatterId(assignmentDir) ?? ctx.assignmentSlug;
29586
+ return {
29587
+ assignmentDir,
29588
+ projectSlug: ctx.projectSlug,
29589
+ assignmentSlug: ctx.assignmentSlug,
29590
+ id,
29591
+ standalone: false,
29592
+ workspaceGroup: null
29593
+ };
29594
+ }
29595
+ throw new AssignmentTargetError(
29596
+ ".syntaur/context.json exists but contains neither assignmentDir nor projectSlug+assignmentSlug."
29597
+ );
29598
+ }
29599
+
29600
+ // src/commands/derive-verbs.ts
29074
29601
  async function resolveTarget(assignment, options) {
29075
29602
  const config = await readConfig();
29076
29603
  const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
29077
29604
  if (options.project) {
29078
29605
  if (!isValidSlug(options.project)) throw new Error(`Invalid project slug "${options.project}".`);
29079
29606
  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");
29607
+ const projectDir = resolve52(baseDir, options.project);
29608
+ const assignmentDir = resolve52(projectDir, "assignments", assignment);
29609
+ const assignmentPath = resolve52(assignmentDir, "assignment.md");
29083
29610
  if (!await fileExists(assignmentPath)) {
29084
29611
  throw new Error(`Assignment "${assignment}" not found at ${assignmentPath}.`);
29085
29612
  }
@@ -29091,14 +29618,14 @@ async function resolveTarget(assignment, options) {
29091
29618
  }
29092
29619
  return {
29093
29620
  assignmentDir: resolved.assignmentDir,
29094
- assignmentPath: resolve51(resolved.assignmentDir, "assignment.md"),
29095
- projectDir: resolved.standalone ? null : resolve51(resolved.assignmentDir, "..", "..")
29621
+ assignmentPath: resolve52(resolved.assignmentDir, "assignment.md"),
29622
+ projectDir: resolved.standalone ? null : resolve52(resolved.assignmentDir, "..", "..")
29096
29623
  };
29097
29624
  }
29098
29625
  async function inferActor(options) {
29099
29626
  if (options.agent) return `agent:${options.agent}`;
29100
29627
  try {
29101
- const raw2 = await readFile33(resolve51(process.cwd(), ".syntaur", "context.json"), "utf-8");
29628
+ const raw2 = await readFile34(resolve52(process.cwd(), ".syntaur", "context.json"), "utf-8");
29102
29629
  const ctx = JSON.parse(raw2);
29103
29630
  if (ctx.sessionId) return `agent:${ctx.sessionId.slice(0, 8)}`;
29104
29631
  } catch {
@@ -29107,10 +29634,10 @@ async function inferActor(options) {
29107
29634
  }
29108
29635
  async function emitDeriveEvent(target, type, actor, details) {
29109
29636
  try {
29110
- const fm = parseAssignmentFrontmatter(await readFile33(target.assignmentPath, "utf-8"));
29637
+ const fm = parseAssignmentFrontmatter(await readFile34(target.assignmentPath, "utf-8"));
29111
29638
  emitEvent({
29112
29639
  assignmentId: fm.id,
29113
- projectSlug: target.projectDir ? basename6(target.projectDir) : null,
29640
+ projectSlug: target.projectDir ? basename7(target.projectDir) : null,
29114
29641
  type,
29115
29642
  actor,
29116
29643
  details
@@ -29168,7 +29695,7 @@ async function planApproveCommand(assignment, options) {
29168
29695
  throw new Error("No plan file found (plan.md / plan-v*.md). Write a plan before approving.");
29169
29696
  }
29170
29697
  approvedFile = planFile;
29171
- const planContent = await readFile33(resolve51(target2.assignmentDir, planFile), "utf-8");
29698
+ const planContent = await readFile34(resolve52(target2.assignmentDir, planFile), "utf-8");
29172
29699
  return updatePlanApproval(content, {
29173
29700
  file: planFile,
29174
29701
  digest: planDigest(planContent),
@@ -29241,7 +29768,7 @@ async function attestCommand(assignment, fact, options) {
29241
29768
  "No plan file found (plan.md / plan-v*.md). Write a plan before attesting a binds:plan fact."
29242
29769
  );
29243
29770
  }
29244
- const planContent = await readFile33(resolve51(target2.assignmentDir, planFile), "utf-8");
29771
+ const planContent = await readFile34(resolve52(target2.assignmentDir, planFile), "utf-8");
29245
29772
  record.file = planFile;
29246
29773
  record.digest = planDigest(planContent);
29247
29774
  } else if (binds === "commit") {
@@ -29291,6 +29818,15 @@ async function requestReviewCommand(assignment, options) {
29291
29818
  );
29292
29819
  }
29293
29820
  async function implementStartedCommand(assignment, options) {
29821
+ const target = await resolveTarget(assignment, options);
29822
+ const context = await resolveDeriveContext();
29823
+ const fm = parseAssignmentFrontmatter(await readFile34(target.assignmentPath, "utf-8"));
29824
+ if (fm.dependsOn.length > 0 && target.projectDir) {
29825
+ const dep = await checkDependencies(target.projectDir, fm.dependsOn, context.terminalStatuses);
29826
+ if (!dep.satisfied) {
29827
+ console.warn(`Warning: starting with unmet dependencies: ${dep.unmet.join(", ")}`);
29828
+ }
29829
+ }
29294
29830
  await assertFact(
29295
29831
  assignment,
29296
29832
  options,
@@ -29298,14 +29834,15 @@ async function implementStartedCommand(assignment, options) {
29298
29834
  (content) => {
29299
29835
  let next = updateAssignmentFile(content, { implementationStarted: true });
29300
29836
  if (options.agent) {
29301
- const fm = parseAssignmentFrontmatter(next);
29302
- if (fm.assignee === null) {
29837
+ const innerFm = parseAssignmentFrontmatter(next);
29838
+ if (innerFm.assignee === null) {
29303
29839
  next = updateAssignmentFile(next, { assignee: options.agent });
29304
29840
  }
29305
29841
  }
29306
29842
  return next;
29307
29843
  },
29308
- "Implementation started"
29844
+ "Implementation started",
29845
+ { context }
29309
29846
  );
29310
29847
  }
29311
29848
  async function blockFactCommand(assignment, options) {
@@ -29353,6 +29890,7 @@ async function statusUnpinCommand(assignment, options) {
29353
29890
  await assertFact(assignment, options, "unpin", (content) => updateOverride(content, null), "Unpinned");
29354
29891
  }
29355
29892
  async function recomputeCommand(assignment, options) {
29893
+ if (options.ifMigrated && !await isDeriveMigrated()) return;
29356
29894
  const context = await resolveDeriveContext();
29357
29895
  if (options.all) {
29358
29896
  const { recomputeAll: recomputeAll2 } = await Promise.resolve().then(() => (init_recompute(), recompute_exports));
@@ -29368,12 +29906,15 @@ async function recomputeCommand(assignment, options) {
29368
29906
  for (const w of summary.warnings) console.warn(`Warning: ${w}`);
29369
29907
  return;
29370
29908
  }
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, {
29909
+ const resolved = await resolveAssignmentTarget(assignment, {
29910
+ project: options.project,
29911
+ dir: options.dir
29912
+ });
29913
+ const projectDir = resolved.standalone ? null : resolve52(resolved.assignmentDir, "..", "..");
29914
+ const result = await recomputeAndWrite(resolve52(resolved.assignmentDir, "assignment.md"), {
29374
29915
  cause: "recompute",
29375
29916
  by: await inferActor(options),
29376
- projectDir: target.projectDir,
29917
+ projectDir,
29377
29918
  context
29378
29919
  });
29379
29920
  reportDerived(result.changed ? "Recomputed" : "Recomputed (no change)", result);
@@ -29393,8 +29934,8 @@ init_frontmatter();
29393
29934
  init_timestamp();
29394
29935
  init_assignment_resolver();
29395
29936
  init_event_emit();
29396
- import { resolve as resolve52 } from "path";
29397
- import { readFile as readFile34, writeFile as writeFile9 } from "fs/promises";
29937
+ import { resolve as resolve53 } from "path";
29938
+ import { readFile as readFile35, writeFile as writeFile9 } from "fs/promises";
29398
29939
  async function resolveTarget2(target, options) {
29399
29940
  const config = await readConfig();
29400
29941
  const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
@@ -29405,7 +29946,7 @@ async function resolveTarget2(target, options) {
29405
29946
  if (!isValidSlug(target)) {
29406
29947
  throw new Error(`Invalid assignment slug "${target}".`);
29407
29948
  }
29408
- const assignmentMd = resolve52(baseDir, options.project, "assignments", target, "assignment.md");
29949
+ const assignmentMd = resolve53(baseDir, options.project, "assignments", target, "assignment.md");
29409
29950
  if (!await fileExists(assignmentMd)) {
29410
29951
  throw new Error(`Assignment "${target}" not found in project "${options.project}".`);
29411
29952
  }
@@ -29415,18 +29956,18 @@ async function resolveTarget2(target, options) {
29415
29956
  if (resolved) {
29416
29957
  return {
29417
29958
  kind: "assignment",
29418
- filePath: resolve52(resolved.assignmentDir, "assignment.md"),
29959
+ filePath: resolve53(resolved.assignmentDir, "assignment.md"),
29419
29960
  label: resolved.projectSlug ? `assignment "${resolved.projectSlug}/${resolved.assignmentSlug}"` : `assignment "${target}"`
29420
29961
  };
29421
29962
  }
29422
- const projectMd = resolve52(baseDir, target, "project.md");
29963
+ const projectMd = resolve53(baseDir, target, "project.md");
29423
29964
  if (await fileExists(projectMd)) {
29424
29965
  return { kind: "project", filePath: projectMd, label: `project "${target}"` };
29425
29966
  }
29426
29967
  return null;
29427
29968
  }
29428
29969
  async function writeArchiveState(filePath, archived, reason) {
29429
- const content = await readFile34(filePath, "utf-8");
29970
+ const content = await readFile35(filePath, "utf-8");
29430
29971
  const updated = updateAssignmentFile(content, {
29431
29972
  archived,
29432
29973
  archivedAt: archived ? nowTimestamp() : null,
@@ -29438,7 +29979,7 @@ async function writeArchiveState(filePath, archived, reason) {
29438
29979
  async function emitArchiveEvent(resolved, type, reason) {
29439
29980
  if (resolved.kind !== "assignment") return;
29440
29981
  try {
29441
- const fm = parseAssignmentFrontmatter(await readFile34(resolved.filePath, "utf-8"));
29982
+ const fm = parseAssignmentFrontmatter(await readFile35(resolved.filePath, "utf-8"));
29442
29983
  emitEvent({
29443
29984
  assignmentId: fm.id,
29444
29985
  projectSlug: fm.project,
@@ -29508,8 +30049,8 @@ init_config2();
29508
30049
  init_frontmatter();
29509
30050
  init_timestamp();
29510
30051
  init_event_emit();
29511
- import { resolve as resolve53 } from "path";
29512
- import { readdir as readdir20, readFile as readFile35 } from "fs/promises";
30052
+ import { resolve as resolve54 } from "path";
30053
+ import { readdir as readdir20, readFile as readFile36 } from "fs/promises";
29513
30054
  var PROMOTABLE_STATUSES = /* @__PURE__ */ new Set(["pending"]);
29514
30055
  function objectiveIsFleshedOut(content) {
29515
30056
  const match = content.match(/##\s+Objective\s*\n([\s\S]*?)(?=\n##\s+|$)/);
@@ -29531,11 +30072,11 @@ async function collectCandidates(baseDirs) {
29531
30072
  for (const m of projects) {
29532
30073
  if (!m.isDirectory()) continue;
29533
30074
  if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
29534
- const directAssignmentMd = resolve53(baseDir, m.name, "assignment.md");
30075
+ const directAssignmentMd = resolve54(baseDir, m.name, "assignment.md");
29535
30076
  if (await fileExists(directAssignmentMd)) {
29536
30077
  const fm = await parseSafe(directAssignmentMd);
29537
30078
  if (fm && PROMOTABLE_STATUSES.has(fm.status)) {
29538
- const content = await readFile35(directAssignmentMd, "utf-8");
30079
+ const content = await readFile36(directAssignmentMd, "utf-8");
29539
30080
  if (objectiveIsFleshedOut(content) && hasAcceptanceCriteria(content)) {
29540
30081
  candidates.push({
29541
30082
  projectSlug: null,
@@ -29548,17 +30089,17 @@ async function collectCandidates(baseDirs) {
29548
30089
  }
29549
30090
  continue;
29550
30091
  }
29551
- const assignmentsDir2 = resolve53(baseDir, m.name, "assignments");
30092
+ const assignmentsDir2 = resolve54(baseDir, m.name, "assignments");
29552
30093
  if (!await fileExists(assignmentsDir2)) continue;
29553
30094
  const entries = await readdir20(assignmentsDir2, { withFileTypes: true });
29554
30095
  for (const a of entries) {
29555
30096
  if (!a.isDirectory()) continue;
29556
30097
  if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
29557
- const assignmentMd = resolve53(assignmentsDir2, a.name, "assignment.md");
30098
+ const assignmentMd = resolve54(assignmentsDir2, a.name, "assignment.md");
29558
30099
  if (!await fileExists(assignmentMd)) continue;
29559
30100
  const fm = await parseSafe(assignmentMd);
29560
30101
  if (!fm || !PROMOTABLE_STATUSES.has(fm.status)) continue;
29561
- const content = await readFile35(assignmentMd, "utf-8");
30102
+ const content = await readFile36(assignmentMd, "utf-8");
29562
30103
  if (!objectiveIsFleshedOut(content)) continue;
29563
30104
  if (!hasAcceptanceCriteria(content)) continue;
29564
30105
  candidates.push({
@@ -29575,7 +30116,7 @@ async function collectCandidates(baseDirs) {
29575
30116
  }
29576
30117
  async function parseSafe(path) {
29577
30118
  try {
29578
- const content = await readFile35(path, "utf-8");
30119
+ const content = await readFile36(path, "utf-8");
29579
30120
  return parseAssignmentFrontmatter(content);
29580
30121
  } catch {
29581
30122
  return null;
@@ -29605,7 +30146,7 @@ async function migrateStatusesCommand(options) {
29605
30146
  let migrated = 0;
29606
30147
  await withSuppressedEvents(async () => {
29607
30148
  for (const c2 of candidates) {
29608
- const content = await readFile35(c2.assignmentMd, "utf-8");
30149
+ const content = await readFile36(c2.assignmentMd, "utf-8");
29609
30150
  const updated = appendStatusHistoryEntry(
29610
30151
  updateAssignmentFile(content, {
29611
30152
  status: c2.toStatus,
@@ -29627,11 +30168,11 @@ init_config2();
29627
30168
  init_frontmatter();
29628
30169
  init_event_emit();
29629
30170
  init_types();
29630
- import { resolve as resolve54 } from "path";
29631
- import { readdir as readdir21, readFile as readFile36 } from "fs/promises";
30171
+ import { resolve as resolve55 } from "path";
30172
+ import { readdir as readdir21, readFile as readFile37 } from "fs/promises";
29632
30173
  async function parseSafe2(path) {
29633
30174
  try {
29634
- return parseAssignmentFrontmatter(await readFile36(path, "utf-8"));
30175
+ return parseAssignmentFrontmatter(await readFile37(path, "utf-8"));
29635
30176
  } catch {
29636
30177
  return null;
29637
30178
  }
@@ -29658,7 +30199,7 @@ async function collectTargets(baseDirs, terminalStatuses3) {
29658
30199
  for (const m of entries) {
29659
30200
  if (!m.isDirectory()) continue;
29660
30201
  if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
29661
- const directAssignmentMd = resolve54(baseDir, m.name, "assignment.md");
30202
+ const directAssignmentMd = resolve55(baseDir, m.name, "assignment.md");
29662
30203
  if (await fileExists(directAssignmentMd)) {
29663
30204
  if (seen.has(directAssignmentMd)) continue;
29664
30205
  const fm = await parseSafe2(directAssignmentMd);
@@ -29673,13 +30214,13 @@ async function collectTargets(baseDirs, terminalStatuses3) {
29673
30214
  }
29674
30215
  continue;
29675
30216
  }
29676
- const assignmentsBase = resolve54(baseDir, m.name, "assignments");
30217
+ const assignmentsBase = resolve55(baseDir, m.name, "assignments");
29677
30218
  if (!await fileExists(assignmentsBase)) continue;
29678
30219
  const slugs = await readdir21(assignmentsBase, { withFileTypes: true });
29679
30220
  for (const a of slugs) {
29680
30221
  if (!a.isDirectory()) continue;
29681
30222
  if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
29682
- const assignmentMd = resolve54(assignmentsBase, a.name, "assignment.md");
30223
+ const assignmentMd = resolve55(assignmentsBase, a.name, "assignment.md");
29683
30224
  if (!await fileExists(assignmentMd)) continue;
29684
30225
  if (seen.has(assignmentMd)) continue;
29685
30226
  const fm = await parseSafe2(assignmentMd);
@@ -29723,7 +30264,7 @@ async function migrateStatusHistoryCommand(options) {
29723
30264
  await withSuppressedEvents(async () => {
29724
30265
  for (const t of targets) {
29725
30266
  try {
29726
- const content = await readFile36(t.assignmentMd, "utf-8");
30267
+ const content = await readFile37(t.assignmentMd, "utf-8");
29727
30268
  if (parseAssignmentFrontmatter(content).statusHistory.length > 0) continue;
29728
30269
  const seededContent = appendStatusHistoryEntry(content, {
29729
30270
  at: t.seedAt,
@@ -29751,11 +30292,11 @@ init_fs();
29751
30292
  init_config2();
29752
30293
  init_parser();
29753
30294
  init_events_db();
29754
- import { resolve as resolve55 } from "path";
29755
- import { readdir as readdir22, readFile as readFile37 } from "fs/promises";
30295
+ import { resolve as resolve56 } from "path";
30296
+ import { readdir as readdir22, readFile as readFile38 } from "fs/promises";
29756
30297
  async function parseSafe3(path) {
29757
30298
  try {
29758
- return parseAssignmentFull(await readFile37(path, "utf-8"));
30299
+ return parseAssignmentFull(await readFile38(path, "utf-8"));
29759
30300
  } catch {
29760
30301
  return null;
29761
30302
  }
@@ -29792,7 +30333,7 @@ async function collectTargets2(baseDirs) {
29792
30333
  for (const m of entries) {
29793
30334
  if (!m.isDirectory()) continue;
29794
30335
  if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
29795
- const directAssignmentMd = resolve55(baseDir, m.name, "assignment.md");
30336
+ const directAssignmentMd = resolve56(baseDir, m.name, "assignment.md");
29796
30337
  if (await fileExists(directAssignmentMd)) {
29797
30338
  if (seen.has(directAssignmentMd)) continue;
29798
30339
  seen.add(directAssignmentMd);
@@ -29810,13 +30351,13 @@ async function collectTargets2(baseDirs) {
29810
30351
  }
29811
30352
  continue;
29812
30353
  }
29813
- const assignmentsBase = resolve55(baseDir, m.name, "assignments");
30354
+ const assignmentsBase = resolve56(baseDir, m.name, "assignments");
29814
30355
  if (!await fileExists(assignmentsBase)) continue;
29815
30356
  const slugs = await readdir22(assignmentsBase, { withFileTypes: true });
29816
30357
  for (const a of slugs) {
29817
30358
  if (!a.isDirectory()) continue;
29818
30359
  if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
29819
- const assignmentMd = resolve55(assignmentsBase, a.name, "assignment.md");
30360
+ const assignmentMd = resolve56(assignmentsBase, a.name, "assignment.md");
29820
30361
  if (!await fileExists(assignmentMd)) continue;
29821
30362
  if (seen.has(assignmentMd)) continue;
29822
30363
  seen.add(assignmentMd);
@@ -29897,8 +30438,8 @@ init_frontmatter();
29897
30438
  init_facts();
29898
30439
  init_derive();
29899
30440
  init_recompute();
29900
- import { readdir as readdir23, readFile as readFile38 } from "fs/promises";
29901
- import { resolve as resolve56 } from "path";
30441
+ import { readdir as readdir23, readFile as readFile39 } from "fs/promises";
30442
+ import { resolve as resolve57 } from "path";
29902
30443
  var IMPLEMENTATION_STATUSES = /* @__PURE__ */ new Set(["in_progress", "review", "code_review"]);
29903
30444
  var REVIEW_STATUSES = /* @__PURE__ */ new Set(["review", "code_review"]);
29904
30445
  async function listTargets(projectsDir2, standaloneDir) {
@@ -29909,15 +30450,15 @@ async function listTargets(projectsDir2, standaloneDir) {
29909
30450
  } catch {
29910
30451
  }
29911
30452
  for (const project of projects) {
29912
- const projectDir = resolve56(projectsDir2, project);
30453
+ const projectDir = resolve57(projectsDir2, project);
29913
30454
  let slugs = [];
29914
30455
  try {
29915
- slugs = await readdir23(resolve56(projectDir, "assignments"));
30456
+ slugs = await readdir23(resolve57(projectDir, "assignments"));
29916
30457
  } catch {
29917
30458
  continue;
29918
30459
  }
29919
30460
  for (const slug of slugs) {
29920
- const path = resolve56(projectDir, "assignments", slug, "assignment.md");
30461
+ const path = resolve57(projectDir, "assignments", slug, "assignment.md");
29921
30462
  if (await fileExists(path)) targets.push({ path, projectDir, ref: `${project}/${slug}` });
29922
30463
  }
29923
30464
  }
@@ -29927,7 +30468,7 @@ async function listTargets(projectsDir2, standaloneDir) {
29927
30468
  } catch {
29928
30469
  }
29929
30470
  for (const id of ids) {
29930
- const path = resolve56(standaloneDir, id, "assignment.md");
30471
+ const path = resolve57(standaloneDir, id, "assignment.md");
29931
30472
  if (await fileExists(path)) targets.push({ path, projectDir: null, ref: id });
29932
30473
  }
29933
30474
  return targets;
@@ -29982,7 +30523,7 @@ async function migrateDeriveCommand(options) {
29982
30523
  for (const target of targets) {
29983
30524
  let content;
29984
30525
  try {
29985
- content = await readFile38(target.path, "utf-8");
30526
+ content = await readFile39(target.path, "utf-8");
29986
30527
  } catch {
29987
30528
  continue;
29988
30529
  }
@@ -29997,7 +30538,7 @@ async function migrateDeriveCommand(options) {
29997
30538
  const seededFm = parseAssignmentFrontmatter(seededContent);
29998
30539
  const body = seededContent.replace(/^---\n[\s\S]*?\n---/, "");
29999
30540
  const facts = await computeFacts({
30000
- assignmentDir: resolve56(target.path, ".."),
30541
+ assignmentDir: resolve57(target.path, ".."),
30001
30542
  frontmatter: seededFm,
30002
30543
  body,
30003
30544
  projectDir: target.projectDir,
@@ -30057,7 +30598,7 @@ async function migrateDeriveCommand(options) {
30057
30598
  }
30058
30599
 
30059
30600
  // src/commands/complete.ts
30060
- import { resolve as resolve57 } from "path";
30601
+ import { resolve as resolve58 } from "path";
30061
30602
  init_config2();
30062
30603
  init_paths();
30063
30604
  init_assignment_resolver();
@@ -30071,12 +30612,12 @@ async function completeCommand(assignment, options) {
30071
30612
  let projectDir;
30072
30613
  let changedSlug;
30073
30614
  if (options.project) {
30074
- projectDir = resolve57(baseDir, options.project);
30615
+ projectDir = resolve58(baseDir, options.project);
30075
30616
  changedSlug = assignment;
30076
30617
  } else {
30077
30618
  const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), assignment);
30078
30619
  if (!resolved) return;
30079
- projectDir = resolved.standalone ? null : resolve57(resolved.assignmentDir, "..", "..");
30620
+ projectDir = resolved.standalone ? null : resolve58(resolved.assignmentDir, "..", "..");
30080
30621
  changedSlug = resolved.assignmentSlug;
30081
30622
  }
30082
30623
  if (projectDir) {
@@ -30107,7 +30648,7 @@ async function reviewCommand(assignment, options) {
30107
30648
  }
30108
30649
 
30109
30650
  // src/commands/fail.ts
30110
- import { resolve as resolve58 } from "path";
30651
+ import { resolve as resolve59 } from "path";
30111
30652
  init_config2();
30112
30653
  init_paths();
30113
30654
  init_assignment_resolver();
@@ -30121,12 +30662,12 @@ async function failCommand(assignment, options) {
30121
30662
  let projectDir;
30122
30663
  let changedSlug;
30123
30664
  if (options.project) {
30124
- projectDir = resolve58(baseDir, options.project);
30665
+ projectDir = resolve59(baseDir, options.project);
30125
30666
  changedSlug = assignment;
30126
30667
  } else {
30127
30668
  const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), assignment);
30128
30669
  if (!resolved) return;
30129
- projectDir = resolved.standalone ? null : resolve58(resolved.assignmentDir, "..", "..");
30670
+ projectDir = resolved.standalone ? null : resolve59(resolved.assignmentDir, "..", "..");
30130
30671
  changedSlug = resolved.assignmentSlug;
30131
30672
  }
30132
30673
  if (projectDir) {
@@ -30142,7 +30683,7 @@ async function failCommand(assignment, options) {
30142
30683
  }
30143
30684
 
30144
30685
  // src/commands/reopen.ts
30145
- import { resolve as resolve59 } from "path";
30686
+ import { resolve as resolve60 } from "path";
30146
30687
  init_config2();
30147
30688
  init_paths();
30148
30689
  init_assignment_resolver();
@@ -30158,14 +30699,14 @@ async function reopenCommand(assignment, options) {
30158
30699
  let projectDir;
30159
30700
  let changedSlug;
30160
30701
  if (options.project) {
30161
- projectDir = resolve59(baseDir, options.project);
30162
- assignmentPath = resolve59(projectDir, "assignments", assignment, "assignment.md");
30702
+ projectDir = resolve60(baseDir, options.project);
30703
+ assignmentPath = resolve60(projectDir, "assignments", assignment, "assignment.md");
30163
30704
  changedSlug = assignment;
30164
30705
  } else {
30165
30706
  const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), assignment);
30166
30707
  if (!resolved) return;
30167
- assignmentPath = resolve59(resolved.assignmentDir, "assignment.md");
30168
- projectDir = resolved.standalone ? null : resolve59(resolved.assignmentDir, "..", "..");
30708
+ assignmentPath = resolve60(resolved.assignmentDir, "assignment.md");
30709
+ projectDir = resolved.standalone ? null : resolve60(resolved.assignmentDir, "..", "..");
30169
30710
  changedSlug = resolved.assignmentSlug;
30170
30711
  }
30171
30712
  const derived = await recomputeAndWrite(assignmentPath, {
@@ -30199,7 +30740,7 @@ import {
30199
30740
  readdir as readdir24,
30200
30741
  symlink,
30201
30742
  lstat,
30202
- readFile as readFile39,
30743
+ readFile as readFile40,
30203
30744
  readlink,
30204
30745
  rename as rename9,
30205
30746
  rm as rm6,
@@ -30208,20 +30749,20 @@ import {
30208
30749
  } from "fs/promises";
30209
30750
  import { existsSync as existsSync4 } from "fs";
30210
30751
  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";
30752
+ import { basename as basename8, dirname as dirname15, isAbsolute as isAbsolute7, relative as relative2, resolve as resolve62 } from "path";
30212
30753
 
30213
30754
  // src/utils/package-root.ts
30214
30755
  init_fs();
30215
- import { dirname as dirname14, resolve as resolve60 } from "path";
30756
+ import { dirname as dirname14, resolve as resolve61 } from "path";
30216
30757
  import { fileURLToPath as fileURLToPath4 } from "url";
30217
30758
  async function findPackageRoot(expectedRelativePath) {
30218
30759
  let currentDir = dirname14(fileURLToPath4(import.meta.url));
30219
30760
  while (true) {
30220
- const candidate = resolve60(currentDir, expectedRelativePath);
30761
+ const candidate = resolve61(currentDir, expectedRelativePath);
30221
30762
  if (await fileExists(candidate)) {
30222
30763
  return currentDir;
30223
30764
  }
30224
- const parentDir = resolve60(currentDir, "..");
30765
+ const parentDir = resolve61(currentDir, "..");
30225
30766
  if (parentDir === currentDir) {
30226
30767
  throw new Error(
30227
30768
  `Could not locate package root containing ${expectedRelativePath}.`
@@ -30242,25 +30783,25 @@ function getPluginManifestRelativePath(pluginKind) {
30242
30783
  }
30243
30784
  function getDefaultPluginTargetDir(pluginKind) {
30244
30785
  const home2 = homedir8();
30245
- return pluginKind === "claude" ? resolve61(home2, ".claude", "plugins", "syntaur") : resolve61(home2, "plugins", "syntaur");
30786
+ return pluginKind === "claude" ? resolve62(home2, ".claude", "plugins", "syntaur") : resolve62(home2, "plugins", "syntaur");
30246
30787
  }
30247
30788
  function getDefaultMarketplacePath() {
30248
- return resolve61(homedir8(), ".agents", "plugins", "marketplace.json");
30789
+ return resolve62(homedir8(), ".agents", "plugins", "marketplace.json");
30249
30790
  }
30250
30791
  function getClaudeMarketplacesRoot() {
30251
- return resolve61(homedir8(), ".claude", "plugins", "marketplaces");
30792
+ return resolve62(homedir8(), ".claude", "plugins", "marketplaces");
30252
30793
  }
30253
30794
  function getClaudeKnownMarketplacesPath() {
30254
- return resolve61(homedir8(), ".claude", "plugins", "known_marketplaces.json");
30795
+ return resolve62(homedir8(), ".claude", "plugins", "known_marketplaces.json");
30255
30796
  }
30256
30797
  function getClaudeInstalledPluginsPath() {
30257
- return resolve61(homedir8(), ".claude", "plugins", "installed_plugins.json");
30798
+ return resolve62(homedir8(), ".claude", "plugins", "installed_plugins.json");
30258
30799
  }
30259
30800
  function getInstallMarkerPath(targetDir) {
30260
- return resolve61(targetDir, INSTALL_MARKER_FILENAME);
30801
+ return resolve62(targetDir, INSTALL_MARKER_FILENAME);
30261
30802
  }
30262
30803
  async function readPackageManifest(packageRoot) {
30263
- const raw2 = await readFile39(resolve61(packageRoot, "package.json"), "utf-8");
30804
+ const raw2 = await readFile40(resolve62(packageRoot, "package.json"), "utf-8");
30264
30805
  return JSON.parse(raw2);
30265
30806
  }
30266
30807
  async function readJsonFileIfExists(pathValue) {
@@ -30268,7 +30809,7 @@ async function readJsonFileIfExists(pathValue) {
30268
30809
  return null;
30269
30810
  }
30270
30811
  try {
30271
- const raw2 = await readFile39(pathValue, "utf-8");
30812
+ const raw2 = await readFile40(pathValue, "utf-8");
30272
30813
  return JSON.parse(raw2);
30273
30814
  } catch {
30274
30815
  return null;
@@ -30276,15 +30817,15 @@ async function readJsonFileIfExists(pathValue) {
30276
30817
  }
30277
30818
  async function readClaudePluginManifest(pluginDir) {
30278
30819
  return await readJsonFileIfExists(
30279
- resolve61(pluginDir, ".claude-plugin", "plugin.json")
30820
+ resolve62(pluginDir, ".claude-plugin", "plugin.json")
30280
30821
  ) ?? {};
30281
30822
  }
30282
30823
  async function readPluginManifestName(targetDir, pluginKind) {
30283
- const manifestPath = resolve61(targetDir, getPluginManifestRelativePath(pluginKind));
30824
+ const manifestPath = resolve62(targetDir, getPluginManifestRelativePath(pluginKind));
30284
30825
  if (!await fileExists(manifestPath)) {
30285
30826
  return void 0;
30286
30827
  }
30287
- const raw2 = await readFile39(manifestPath, "utf-8");
30828
+ const raw2 = await readFile40(manifestPath, "utf-8");
30288
30829
  const parsed = JSON.parse(raw2);
30289
30830
  return parsed.name;
30290
30831
  }
@@ -30294,7 +30835,7 @@ async function readInstallMetadata(targetDir) {
30294
30835
  return null;
30295
30836
  }
30296
30837
  try {
30297
- const raw2 = await readFile39(markerPath, "utf-8");
30838
+ const raw2 = await readFile40(markerPath, "utf-8");
30298
30839
  return JSON.parse(raw2);
30299
30840
  } catch {
30300
30841
  return null;
@@ -30307,7 +30848,7 @@ async function getInstallStatus(targetDir, pluginKind) {
30307
30848
  const info = await lstat(targetDir);
30308
30849
  if (info.isSymbolicLink()) {
30309
30850
  const symlinkTarget = await readlink(targetDir);
30310
- const resolvedTarget = resolve61(dirname15(targetDir), symlinkTarget);
30851
+ const resolvedTarget = resolve62(dirname15(targetDir), symlinkTarget);
30311
30852
  const manifestName2 = await readPluginManifestName(resolvedTarget, pluginKind);
30312
30853
  return {
30313
30854
  exists: true,
@@ -30353,7 +30894,7 @@ async function installLink(paths) {
30353
30894
  await ensureDir(dirname15(paths.targetDir));
30354
30895
  await rm6(paths.targetDir, { recursive: true, force: true });
30355
30896
  await ensureDir(dirname15(paths.targetDir));
30356
- await symlink(resolve61(paths.sourceDir), paths.targetDir, "dir");
30897
+ await symlink(resolve62(paths.sourceDir), paths.targetDir, "dir");
30357
30898
  }
30358
30899
  async function removeInstallMarker(targetDir) {
30359
30900
  const markerPath = getInstallMarkerPath(targetDir);
@@ -30367,13 +30908,13 @@ function normalizeAbsoluteInstallPath(pathValue, label) {
30367
30908
  if (!isAbsolute7(expanded)) {
30368
30909
  throw new Error(`${label} must be an absolute path.`);
30369
30910
  }
30370
- return resolve61(expanded);
30911
+ return resolve62(expanded);
30371
30912
  }
30372
30913
  async function resolvePluginPaths(pluginKind, targetDir) {
30373
30914
  const packageRoot = await findPackageRoot(getPluginRelativePath(pluginKind));
30374
30915
  return {
30375
30916
  packageRoot,
30376
- sourceDir: resolve61(packageRoot, getPluginRelativePath(pluginKind)),
30917
+ sourceDir: resolve62(packageRoot, getPluginRelativePath(pluginKind)),
30377
30918
  targetDir: targetDir ?? getDefaultPluginTargetDir(pluginKind)
30378
30919
  };
30379
30920
  }
@@ -30417,7 +30958,7 @@ async function writeClaudeMarketplaceFile(manifestPath, marketplace) {
30417
30958
  }
30418
30959
  if (await fileExists(manifestPath)) {
30419
30960
  try {
30420
- const prev = await readFile39(manifestPath, "utf-8");
30961
+ const prev = await readFile40(manifestPath, "utf-8");
30421
30962
  JSON.parse(prev);
30422
30963
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
30423
30964
  await writeFile10(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
@@ -30498,8 +31039,8 @@ async function listClaudeMarketplaceCandidates() {
30498
31039
  if (!entry.isDirectory()) {
30499
31040
  continue;
30500
31041
  }
30501
- const candidateRoot = resolve61(rootDir, entry.name);
30502
- const manifestPath = resolve61(candidateRoot, ".claude-plugin", "marketplace.json");
31042
+ const candidateRoot = resolve62(rootDir, entry.name);
31043
+ const manifestPath = resolve62(candidateRoot, ".claude-plugin", "marketplace.json");
30503
31044
  if (!await fileExists(manifestPath)) {
30504
31045
  continue;
30505
31046
  }
@@ -30526,11 +31067,11 @@ async function listClaudeMarketplaceCandidates() {
30526
31067
  if (!installLocation) {
30527
31068
  continue;
30528
31069
  }
30529
- const candidateRoot = resolve61(expandHome(installLocation));
31070
+ const candidateRoot = resolve62(expandHome(installLocation));
30530
31071
  if (seen.has(candidateRoot)) {
30531
31072
  continue;
30532
31073
  }
30533
- const manifestPath = resolve61(candidateRoot, ".claude-plugin", "marketplace.json");
31074
+ const manifestPath = resolve62(candidateRoot, ".claude-plugin", "marketplace.json");
30534
31075
  if (!await fileExists(manifestPath)) {
30535
31076
  continue;
30536
31077
  }
@@ -30558,14 +31099,14 @@ async function getPreferredClaudeMarketplace() {
30558
31099
  name: candidate.name,
30559
31100
  rootDir: candidate.rootDir,
30560
31101
  manifestPath: candidate.manifestPath,
30561
- targetDir: resolve61(candidate.rootDir, "plugins", "syntaur")
31102
+ targetDir: resolve62(candidate.rootDir, "plugins", "syntaur")
30562
31103
  };
30563
31104
  }
30564
31105
  async function registerKnownClaudeMarketplace(name, rootDir) {
30565
31106
  const manifestPath = getClaudeKnownMarketplacesPath();
30566
31107
  let existing = {};
30567
31108
  if (await fileExists(manifestPath)) {
30568
- const raw2 = await readFile39(manifestPath, "utf-8");
31109
+ const raw2 = await readFile40(manifestPath, "utf-8");
30569
31110
  try {
30570
31111
  const parsed = JSON.parse(raw2);
30571
31112
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
@@ -30592,7 +31133,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
30592
31133
  existing[name].autoUpdate = true;
30593
31134
  await ensureDir(dirname15(manifestPath));
30594
31135
  if (await fileExists(manifestPath)) {
30595
- const prev = await readFile39(manifestPath, "utf-8");
31136
+ const prev = await readFile40(manifestPath, "utf-8");
30596
31137
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
30597
31138
  await writeFile10(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
30598
31139
  }
@@ -30606,11 +31147,11 @@ async function ensureKnownClaudeMarketplaceForRoot(options) {
30606
31147
  return registerKnownClaudeMarketplace(options.name, options.rootDir);
30607
31148
  }
30608
31149
  async function setSyntaurPluginEnabled(options) {
30609
- const settingsPath = resolve61(homedir8(), ".claude", "settings.json");
31150
+ const settingsPath = resolve62(homedir8(), ".claude", "settings.json");
30610
31151
  const key = `syntaur@${options.marketplaceName}`;
30611
31152
  let parsed = {};
30612
31153
  if (await fileExists(settingsPath)) {
30613
- const raw2 = await readFile39(settingsPath, "utf-8");
31154
+ const raw2 = await readFile40(settingsPath, "utf-8");
30614
31155
  try {
30615
31156
  parsed = JSON.parse(raw2);
30616
31157
  } catch {
@@ -30629,7 +31170,7 @@ async function setSyntaurPluginEnabled(options) {
30629
31170
  await ensureDir(dirname15(settingsPath));
30630
31171
  if (await fileExists(settingsPath)) {
30631
31172
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
30632
- const prev = await readFile39(settingsPath, "utf-8");
31173
+ const prev = await readFile40(settingsPath, "utf-8");
30633
31174
  await writeFile10(`${settingsPath}.bak-${stamp}`, prev, "utf-8");
30634
31175
  }
30635
31176
  const tmpPath = `${settingsPath}.tmp`;
@@ -30643,9 +31184,9 @@ async function ensureClaudeUserMarketplace() {
30643
31184
  if (existing) {
30644
31185
  return existing;
30645
31186
  }
30646
- const rootDir = resolve61(getClaudeMarketplacesRoot(), "user-plugins");
30647
- const manifestPath = resolve61(rootDir, ".claude-plugin", "marketplace.json");
30648
- await ensureDir(resolve61(rootDir, "plugins"));
31187
+ const rootDir = resolve62(getClaudeMarketplacesRoot(), "user-plugins");
31188
+ const manifestPath = resolve62(rootDir, ".claude-plugin", "marketplace.json");
31189
+ await ensureDir(resolve62(rootDir, "plugins"));
30649
31190
  if (!await fileExists(manifestPath)) {
30650
31191
  const scaffold = {
30651
31192
  plugins: []
@@ -30664,22 +31205,22 @@ async function ensureClaudeUserMarketplace() {
30664
31205
  name: "user-plugins",
30665
31206
  rootDir,
30666
31207
  manifestPath,
30667
- targetDir: resolve61(rootDir, "plugins", "syntaur")
31208
+ targetDir: resolve62(rootDir, "plugins", "syntaur")
30668
31209
  };
30669
31210
  }
30670
31211
  async function detectClaudeMarketplaceForTarget(targetDir) {
30671
31212
  const normalizedTargetDir = normalizeAbsoluteInstallPath(targetDir, "Claude plugin target");
30672
31213
  const pluginsDir = dirname15(normalizedTargetDir);
30673
- if (basename7(pluginsDir) !== "plugins") {
31214
+ if (basename8(pluginsDir) !== "plugins") {
30674
31215
  return null;
30675
31216
  }
30676
31217
  const rootDir = dirname15(pluginsDir);
30677
- const manifestPath = resolve61(rootDir, ".claude-plugin", "marketplace.json");
31218
+ const manifestPath = resolve62(rootDir, ".claude-plugin", "marketplace.json");
30678
31219
  if (!await fileExists(manifestPath)) {
30679
31220
  return null;
30680
31221
  }
30681
31222
  const marketplace = await readClaudeMarketplaceFile(manifestPath);
30682
- const name = typeof marketplace.name === "string" && marketplace.name.trim() !== "" ? marketplace.name : basename7(rootDir);
31223
+ const name = typeof marketplace.name === "string" && marketplace.name.trim() !== "" ? marketplace.name : basename8(rootDir);
30683
31224
  return {
30684
31225
  name,
30685
31226
  rootDir,
@@ -30690,7 +31231,7 @@ async function detectClaudeMarketplaceForTarget(targetDir) {
30690
31231
  async function findManagedClaudeMarketplacePluginDir() {
30691
31232
  const marketplaces = await listClaudeMarketplaceCandidates();
30692
31233
  for (const marketplace of marketplaces) {
30693
- const targetDir = resolve61(marketplace.rootDir, "plugins", "syntaur");
31234
+ const targetDir = resolve62(marketplace.rootDir, "plugins", "syntaur");
30694
31235
  const status = await getInstallStatus(targetDir, "claude");
30695
31236
  if (status.exists && status.managed) {
30696
31237
  return targetDir;
@@ -30815,7 +31356,7 @@ async function installManagedPlugin(options) {
30815
31356
  `${paths.targetDir} already exists and is not a Syntaur-managed install. Remove it manually before installing Syntaur there.`
30816
31357
  );
30817
31358
  }
30818
- if (desiredMode === "link" && existing.exists && existing.installMode === "link" && existing.symlinkTarget === resolve61(paths.sourceDir) && !force) {
31359
+ if (desiredMode === "link" && existing.exists && existing.installMode === "link" && existing.symlinkTarget === resolve62(paths.sourceDir) && !force) {
30819
31360
  return {
30820
31361
  targetDir: paths.targetDir,
30821
31362
  sourceDir: paths.sourceDir,
@@ -30867,7 +31408,7 @@ async function readMarketplaceFile(marketplacePath) {
30867
31408
  plugins: []
30868
31409
  };
30869
31410
  }
30870
- const raw2 = await readFile39(marketplacePath, "utf-8");
31411
+ const raw2 = await readFile40(marketplacePath, "utf-8");
30871
31412
  const parsed = JSON.parse(raw2);
30872
31413
  return {
30873
31414
  name: parsed.name ?? "local",
@@ -31034,13 +31575,13 @@ async function recommendMarketplacePath() {
31034
31575
  return configuredOrManaged ?? getDefaultMarketplacePath();
31035
31576
  }
31036
31577
  async function isSyntaurDataInstalled() {
31037
- return fileExists(resolve61(syntaurRoot(), "config.md"));
31578
+ return fileExists(resolve62(syntaurRoot(), "config.md"));
31038
31579
  }
31039
31580
  async function removeSyntaurData() {
31040
31581
  await rm6(syntaurRoot(), { recursive: true, force: true });
31041
31582
  }
31042
31583
  async function getConfiguredProjectDir() {
31043
- if (!await fileExists(resolve61(syntaurRoot(), "config.md"))) {
31584
+ if (!await fileExists(resolve62(syntaurRoot(), "config.md"))) {
31044
31585
  return null;
31045
31586
  }
31046
31587
  return (await readConfig()).defaultProjectDir;
@@ -31109,24 +31650,24 @@ async function textPrompt(question, defaultValue) {
31109
31650
 
31110
31651
  // src/utils/install-skills.ts
31111
31652
  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";
31653
+ import { readFile as readFile42, readdir as readdir25, mkdir as mkdir7, copyFile, rm as rm7, lstat as lstat2 } from "fs/promises";
31654
+ import { resolve as resolve64, relative as relative3, join as join11 } from "path";
31114
31655
  import { homedir as homedir10 } from "os";
31115
31656
 
31116
31657
  // src/utils/plugin-state.ts
31117
31658
  init_fs();
31118
- import { readFile as readFile40 } from "fs/promises";
31119
- import { resolve as resolve62 } from "path";
31659
+ import { readFile as readFile41 } from "fs/promises";
31660
+ import { resolve as resolve63 } from "path";
31120
31661
  import { homedir as homedir9 } from "os";
31121
31662
  function settingsPathFor(agent) {
31122
- if (agent === "claude") return resolve62(homedir9(), ".claude", "settings.json");
31663
+ if (agent === "claude") return resolve63(homedir9(), ".claude", "settings.json");
31123
31664
  return null;
31124
31665
  }
31125
31666
  async function readJsonOrNull(path) {
31126
31667
  if (!path) return null;
31127
31668
  if (!await fileExists(path)) return null;
31128
31669
  try {
31129
- const raw2 = await readFile40(path, "utf-8");
31670
+ const raw2 = await readFile41(path, "utf-8");
31130
31671
  return JSON.parse(raw2);
31131
31672
  } catch {
31132
31673
  return null;
@@ -31176,11 +31717,11 @@ var KNOWN_SKILL_NAMES = [
31176
31717
  var KNOWN_SKILLS = KNOWN_SKILL_NAMES;
31177
31718
  async function getSkillsDir() {
31178
31719
  const packageRoot = await findPackageRoot("skills");
31179
- return resolve63(packageRoot, "skills");
31720
+ return resolve64(packageRoot, "skills");
31180
31721
  }
31181
31722
  function defaultSkillTargetDir(target) {
31182
- if (target === "claude") return resolve63(homedir10(), ".claude", "skills");
31183
- return resolve63(homedir10(), ".codex", "skills");
31723
+ if (target === "claude") return resolve64(homedir10(), ".claude", "skills");
31724
+ return resolve64(homedir10(), ".codex", "skills");
31184
31725
  }
31185
31726
  async function walkFiles(root) {
31186
31727
  const out = [];
@@ -31200,7 +31741,7 @@ async function walkFiles(root) {
31200
31741
  }
31201
31742
  async function filesEqual(a, b) {
31202
31743
  try {
31203
- const [ba, bb] = await Promise.all([readFile41(a), readFile41(b)]);
31744
+ const [ba, bb] = await Promise.all([readFile42(a), readFile42(b)]);
31204
31745
  if (ba.length !== bb.length) return false;
31205
31746
  return ba.equals(bb);
31206
31747
  } catch {
@@ -31234,8 +31775,8 @@ async function skillMatches(srcDir, destDir) {
31234
31775
  }
31235
31776
  async function isSymlink(path) {
31236
31777
  try {
31237
- const stat16 = await lstat2(path);
31238
- return stat16.isSymbolicLink();
31778
+ const stat17 = await lstat2(path);
31779
+ return stat17.isSymbolicLink();
31239
31780
  } catch {
31240
31781
  return false;
31241
31782
  }
@@ -31340,7 +31881,7 @@ async function uninstallSkills(options) {
31340
31881
  if (await isSymlink(destDir)) continue;
31341
31882
  const skillMd = join11(destDir, "SKILL.md");
31342
31883
  if (!await fileExists(skillMd)) continue;
31343
- const content = await readFile41(skillMd, "utf-8").catch(() => "");
31884
+ const content = await readFile42(skillMd, "utf-8").catch(() => "");
31344
31885
  const match = content.match(/^name:\s*(\S+)\s*$/m);
31345
31886
  if (!match || match[1] !== skill) continue;
31346
31887
  await rm7(destDir, { recursive: true, force: true });
@@ -31535,20 +32076,20 @@ import { realpathSync as realpathSync2 } from "fs";
31535
32076
  init_paths();
31536
32077
  init_fs();
31537
32078
  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";
32079
+ import { readFile as readFile44 } from "fs/promises";
32080
+ import { dirname as dirname17, join as join13, resolve as resolve65 } from "path";
31540
32081
  import { spawn as spawn6 } from "child_process";
31541
32082
  import { createInterface as createInterface2 } from "readline/promises";
31542
32083
 
31543
32084
  // src/utils/version.ts
31544
32085
  import { fileURLToPath as fileURLToPath5 } from "url";
31545
- import { readFile as readFile42 } from "fs/promises";
32086
+ import { readFile as readFile43 } from "fs/promises";
31546
32087
  import { dirname as dirname16, join as join12 } from "path";
31547
32088
  async function readPackageVersion(scriptUrl) {
31548
32089
  try {
31549
32090
  const scriptPath = fileURLToPath5(scriptUrl);
31550
32091
  const pkgRoot = dirname16(dirname16(scriptPath));
31551
- const raw2 = await readFile42(join12(pkgRoot, "package.json"), "utf-8");
32092
+ const raw2 = await readFile43(join12(pkgRoot, "package.json"), "utf-8");
31552
32093
  const parsed = JSON.parse(raw2);
31553
32094
  return typeof parsed.version === "string" ? parsed.version : null;
31554
32095
  } catch {
@@ -31557,7 +32098,7 @@ async function readPackageVersion(scriptUrl) {
31557
32098
  }
31558
32099
 
31559
32100
  // src/utils/npx-prompt.ts
31560
- var STATE_FILE = resolve64(syntaurRoot(), "npx-install.json");
32101
+ var STATE_FILE = resolve65(syntaurRoot(), "npx-install.json");
31561
32102
  var META_ARGS2 = /* @__PURE__ */ new Set(["-h", "--help", "-V", "--version", "help"]);
31562
32103
  var GLOBAL_VERSION_TIMEOUT_MS = 2e3;
31563
32104
  function isRunningViaNpx(scriptUrl) {
@@ -31578,7 +32119,7 @@ function isRunningViaNpx(scriptUrl) {
31578
32119
  async function readState() {
31579
32120
  if (!await fileExists(STATE_FILE)) return null;
31580
32121
  try {
31581
- const raw2 = await readFile43(STATE_FILE, "utf-8");
32122
+ const raw2 = await readFile44(STATE_FILE, "utf-8");
31582
32123
  return JSON.parse(raw2);
31583
32124
  } catch {
31584
32125
  return null;
@@ -31637,7 +32178,7 @@ async function readGlobalVersion() {
31637
32178
  try {
31638
32179
  const manifestPath = join13(rootPath, "syntaur", "package.json");
31639
32180
  if (!await fileExists(manifestPath)) return null;
31640
- const raw2 = await readFile43(manifestPath, "utf-8");
32181
+ const raw2 = await readFile44(manifestPath, "utf-8");
31641
32182
  const parsed = JSON.parse(raw2);
31642
32183
  return typeof parsed.version === "string" ? parsed.version : null;
31643
32184
  } catch {
@@ -31995,16 +32536,16 @@ async function refreshPluginSkills(options, pm, runner, getManagedDir, resolveFr
31995
32536
  // src/commands/install-statusline.ts
31996
32537
  init_paths();
31997
32538
  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";
32539
+ 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";
32540
+ import { resolve as resolve67, dirname as dirname19 } from "path";
32000
32541
  import { homedir as homedir12 } from "os";
32001
32542
  import { fileURLToPath as fileURLToPath8 } from "url";
32002
32543
 
32003
32544
  // src/commands/configure-statusline.ts
32004
32545
  init_paths();
32005
32546
  init_fs();
32006
- import { readFile as readFile44, writeFile as writeFile11 } from "fs/promises";
32007
- import { resolve as resolve65, dirname as dirname18 } from "path";
32547
+ import { readFile as readFile45, writeFile as writeFile11 } from "fs/promises";
32548
+ import { resolve as resolve66, dirname as dirname18 } from "path";
32008
32549
  import { spawnSync as spawnSync5 } from "child_process";
32009
32550
  import { checkbox, input as input2, confirm } from "@inquirer/prompts";
32010
32551
  var AVAILABLE_SEGMENTS = [
@@ -32025,12 +32566,12 @@ var PRESETS = {
32025
32566
  tracker: { segments: ["git", "assignment", "external", "session"], separator: " \xB7 " }
32026
32567
  };
32027
32568
  function getConfigPath(installRoot) {
32028
- return resolve65(installRoot, "statusline.config.json");
32569
+ return resolve66(installRoot, "statusline.config.json");
32029
32570
  }
32030
32571
  async function readConfig2(path) {
32031
32572
  if (!await fileExists(path)) return null;
32032
32573
  try {
32033
- const raw2 = await readFile44(path, "utf-8");
32574
+ const raw2 = await readFile45(path, "utf-8");
32034
32575
  const parsed = JSON.parse(raw2);
32035
32576
  if (!parsed || typeof parsed !== "object") return null;
32036
32577
  const segments = Array.isArray(parsed.segments) ? parsed.segments.filter(isSegmentName) : [];
@@ -32183,7 +32724,7 @@ async function configureStatuslineCommand(options = {}) {
32183
32724
  console.log(` segments: ${config.segments.join(", ")}`);
32184
32725
  console.log(` separator: ${JSON.stringify(config.separator)}`);
32185
32726
  if (config.wrap) console.log(` wrap: ${config.wrap}`);
32186
- const script = options.statuslineScript ?? resolve65(installRoot, "statusline.sh");
32727
+ const script = options.statuslineScript ?? resolve66(installRoot, "statusline.sh");
32187
32728
  if (await fileExists(script)) {
32188
32729
  console.log("");
32189
32730
  console.log("Live preview:");
@@ -32214,11 +32755,11 @@ async function writeDefaultConfigIfMissing(installRoot) {
32214
32755
  // src/commands/install-statusline.ts
32215
32756
  function getPackageStatuslineSource() {
32216
32757
  const here = dirname19(fileURLToPath8(import.meta.url));
32217
- return resolve66(here, "..", "statusline", "statusline.sh");
32758
+ return resolve67(here, "..", "statusline", "statusline.sh");
32218
32759
  }
32219
32760
  async function readSettingsJson(settingsPath) {
32220
32761
  if (!await fileExists(settingsPath)) return {};
32221
- const raw2 = await readFile45(settingsPath, "utf-8");
32762
+ const raw2 = await readFile46(settingsPath, "utf-8");
32222
32763
  if (raw2.trim() === "") return {};
32223
32764
  try {
32224
32765
  const parsed = JSON.parse(raw2);
@@ -32298,12 +32839,12 @@ async function installScript(sourceScript, destScript, link) {
32298
32839
  }
32299
32840
  async function installStatuslineCommand(options = {}) {
32300
32841
  const mode = options.mode ?? "ask";
32301
- const settingsPath = options.settingsPath ?? resolve66(homedir12(), ".claude", "settings.json");
32842
+ const settingsPath = options.settingsPath ?? resolve67(homedir12(), ".claude", "settings.json");
32302
32843
  const installRoot = options.installRoot ?? syntaurRoot();
32303
32844
  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");
32845
+ const destScript = resolve67(installRoot, "statusline.sh");
32846
+ const confPath = resolve67(installRoot, "statusline.conf");
32847
+ const backupPath = resolve67(installRoot, "statusline.backup.json");
32307
32848
  if (!await fileExists(sourceScript)) {
32308
32849
  throw new Error(
32309
32850
  `Statusline source script not found at ${sourceScript}. Try re-installing syntaur (npm install -g syntaur) or pass --source-script explicitly.`
@@ -32339,7 +32880,7 @@ async function installStatuslineCommand(options = {}) {
32339
32880
  if (parsed) {
32340
32881
  wrapTarget = parsed;
32341
32882
  } else {
32342
- const wrapperPath = resolve66(installRoot, "statusline-wrapped.sh");
32883
+ const wrapperPath = resolve67(installRoot, "statusline-wrapped.sh");
32343
32884
  const wrapperBody = `#!/usr/bin/env bash
32344
32885
  # Auto-generated by syntaur install-statusline.
32345
32886
  # Executes the previously configured statusLine command.
@@ -32388,25 +32929,25 @@ function parseWrapCommand(command) {
32388
32929
  async function chmodExec(path) {
32389
32930
  const fs = await import("fs/promises");
32390
32931
  try {
32391
- const s = await stat7(path);
32932
+ const s = await stat8(path);
32392
32933
  await fs.chmod(path, s.mode | 73);
32393
32934
  } catch {
32394
32935
  }
32395
32936
  }
32396
32937
  async function uninstallStatuslineCommand(options = {}) {
32397
- const settingsPath = options.settingsPath ?? resolve66(homedir12(), ".claude", "settings.json");
32938
+ const settingsPath = options.settingsPath ?? resolve67(homedir12(), ".claude", "settings.json");
32398
32939
  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");
32940
+ const destScript = resolve67(installRoot, "statusline.sh");
32941
+ const confPath = resolve67(installRoot, "statusline.conf");
32942
+ const backupPath = resolve67(installRoot, "statusline.backup.json");
32943
+ const wrapperPath = resolve67(installRoot, "statusline-wrapped.sh");
32403
32944
  const settings = await readSettingsJson(settingsPath);
32404
32945
  const existing = extractExistingCommand(settings);
32405
32946
  const ourCommand = `bash ${destScript}`;
32406
32947
  let restored = null;
32407
32948
  if (await fileExists(backupPath)) {
32408
32949
  try {
32409
- const raw2 = await readFile45(backupPath, "utf-8");
32950
+ const raw2 = await readFile46(backupPath, "utf-8");
32410
32951
  const parsed = JSON.parse(raw2);
32411
32952
  const prev = parsed?.previousStatusLine;
32412
32953
  if (prev && typeof prev === "object" && typeof prev.command === "string") {
@@ -32427,7 +32968,7 @@ async function uninstallStatuslineCommand(options = {}) {
32427
32968
  await writeSettingsJson(settingsPath, settings);
32428
32969
  }
32429
32970
  if (!options.keepScript) {
32430
- const configPath2 = resolve66(installRoot, "statusline.config.json");
32971
+ const configPath2 = resolve67(installRoot, "statusline.config.json");
32431
32972
  for (const path of [destScript, confPath, backupPath, wrapperPath, configPath2]) {
32432
32973
  try {
32433
32974
  await rm8(path, { force: true });
@@ -32587,8 +33128,8 @@ init_config2();
32587
33128
  // src/commands/cross-agent-install.ts
32588
33129
  init_fs();
32589
33130
  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";
33131
+ import { dirname as dirname20, join as join15, resolve as resolve69 } from "path";
33132
+ import { cp as cp4, mkdir as mkdir9, readFile as readFile47 } from "fs/promises";
32592
33133
 
32593
33134
  // src/commands/setup-adapter.ts
32594
33135
  init_paths();
@@ -32597,7 +33138,7 @@ init_config2();
32597
33138
  init_slug();
32598
33139
  init_registry();
32599
33140
  init_renderers();
32600
- import { resolve as resolve67 } from "path";
33141
+ import { resolve as resolve68 } from "path";
32601
33142
  async function setupAdapterCommand(framework, options) {
32602
33143
  const { targets: known, warnings } = await resolveAgentTargets();
32603
33144
  const target = known.find((t) => t.id === framework);
@@ -32626,13 +33167,13 @@ async function setupAdapterCommand(framework, options) {
32626
33167
  }
32627
33168
  const config = await readConfig();
32628
33169
  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");
33170
+ const projectDir = resolve68(baseDir, options.project);
33171
+ const assignmentDir = resolve68(projectDir, "assignments", options.assignment);
33172
+ const projectMdPath = resolve68(projectDir, "project.md");
32632
33173
  if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
32633
33174
  throw new Error(`Project "${options.project}" not found at ${projectDir}.`);
32634
33175
  }
32635
- const assignmentMdPath2 = resolve67(assignmentDir, "assignment.md");
33176
+ const assignmentMdPath2 = resolve68(assignmentDir, "assignment.md");
32636
33177
  if (!await fileExists(assignmentDir) || !await fileExists(assignmentMdPath2)) {
32637
33178
  throw new Error(
32638
33179
  `Assignment "${options.assignment}" not found at ${assignmentDir}.`
@@ -32649,7 +33190,7 @@ async function setupAdapterCommand(framework, options) {
32649
33190
  const upToDateFiles = [];
32650
33191
  const skippedFiles = [];
32651
33192
  for (const file of target.instructions.files) {
32652
- const filePath = resolve67(cwd, file.path);
33193
+ const filePath = resolve68(cwd, file.path);
32653
33194
  const content = RENDERERS[file.renderer](rendererParams);
32654
33195
  const status = await writeFileReport(filePath, content, {
32655
33196
  force: options.force
@@ -32705,7 +33246,7 @@ async function installTier3Plugin(t, opts = {}) {
32705
33246
  return "already-present";
32706
33247
  }
32707
33248
  try {
32708
- const sourceDir = resolve68(await findPackageRoot(plugin.source), plugin.source);
33249
+ const sourceDir = resolve69(await findPackageRoot(plugin.source), plugin.source);
32709
33250
  await mkdir9(dirname20(installDir), { recursive: true });
32710
33251
  await cp4(sourceDir, installDir, { recursive: true, force: true });
32711
33252
  console.log(`Tier 3 (${t.id}): installed ${plugin.kind} -> ${installDir}`);
@@ -32730,10 +33271,10 @@ function isNpxAvailable() {
32730
33271
  }
32731
33272
  }
32732
33273
  async function readAssignmentContext() {
32733
- const p = resolve68(process.cwd(), ".syntaur", "context.json");
33274
+ const p = resolve69(process.cwd(), ".syntaur", "context.json");
32734
33275
  if (!await fileExists(p)) return null;
32735
33276
  try {
32736
- return JSON.parse(await readFile46(p, "utf-8"));
33277
+ return JSON.parse(await readFile47(p, "utf-8"));
32737
33278
  } catch {
32738
33279
  return null;
32739
33280
  }
@@ -32813,7 +33354,7 @@ async function crossAgentInstallCommand(options) {
32813
33354
  if (dryRun) {
32814
33355
  for (const f of t.instructions.files) {
32815
33356
  console.log(
32816
- `${prefix}Tier 2 (${t.id}): ${resolve68(process.cwd(), f.path)}`
33357
+ `${prefix}Tier 2 (${t.id}): ${resolve69(process.cwd(), f.path)}`
32817
33358
  );
32818
33359
  }
32819
33360
  continue;
@@ -32970,7 +33511,7 @@ async function setupCommand(options) {
32970
33511
  }
32971
33512
 
32972
33513
  // src/commands/uninstall.ts
32973
- import { resolve as resolve69 } from "path";
33514
+ import { resolve as resolve70 } from "path";
32974
33515
  init_paths();
32975
33516
  function expandTargets(options) {
32976
33517
  if (options.all) {
@@ -33050,7 +33591,7 @@ async function uninstallCommand(options) {
33050
33591
  const configuredProjectDir = await getConfiguredProjectDir();
33051
33592
  await removeSyntaurData();
33052
33593
  console.log(`Removed ${syntaurRoot()}`);
33053
- if (configuredProjectDir && resolve69(configuredProjectDir) !== resolve69(syntaurRoot(), "projects")) {
33594
+ if (configuredProjectDir && resolve70(configuredProjectDir) !== resolve70(syntaurRoot(), "projects")) {
33054
33595
  console.warn(
33055
33596
  `Warning: config.md pointed to an external project directory (${configuredProjectDir}). That directory was not removed automatically.`
33056
33597
  );
@@ -33072,8 +33613,8 @@ init_session_id();
33072
33613
  init_cwd();
33073
33614
  init_session_db();
33074
33615
  init_agent_sessions();
33075
- import { resolve as resolve70 } from "path";
33076
- import { readFile as readFile47 } from "fs/promises";
33616
+ import { resolve as resolve71 } from "path";
33617
+ import { readFile as readFile48 } from "fs/promises";
33077
33618
  async function trackSessionCommand(options, deps = {}) {
33078
33619
  if (!options.agent) {
33079
33620
  throw new Error("--agent <name> is required.");
@@ -33083,7 +33624,7 @@ async function trackSessionCommand(options, deps = {}) {
33083
33624
  const cwd = process.cwd();
33084
33625
  let legacyHint;
33085
33626
  try {
33086
- const raw2 = await readFile47(resolve70(cwd, ".syntaur", "context.json"), "utf-8");
33627
+ const raw2 = await readFile48(resolve71(cwd, ".syntaur", "context.json"), "utf-8");
33087
33628
  const parsed = JSON.parse(raw2);
33088
33629
  if (typeof parsed.sessionId === "string") legacyHint = parsed.sessionId;
33089
33630
  } catch {
@@ -33098,7 +33639,7 @@ async function trackSessionCommand(options, deps = {}) {
33098
33639
  if (options.project) {
33099
33640
  const config = await readConfig();
33100
33641
  const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
33101
- const projectDir = resolve70(baseDir, options.project);
33642
+ const projectDir = resolve71(baseDir, options.project);
33102
33643
  if (!await fileExists(projectDir)) {
33103
33644
  throw new Error(
33104
33645
  `Project "${options.project}" not found at ${projectDir}.`
@@ -33231,8 +33772,8 @@ function formatInstallUrlHandlerError(err2) {
33231
33772
  init_config2();
33232
33773
  init_paths();
33233
33774
  init_fs();
33234
- import { resolve as resolve72, isAbsolute as isAbsolute8 } from "path";
33235
- import { readFile as readFile48 } from "fs/promises";
33775
+ import { resolve as resolve73, isAbsolute as isAbsolute8 } from "path";
33776
+ import { readFile as readFile49 } from "fs/promises";
33236
33777
  import { select, confirm as confirm2, input as input3 } from "@inquirer/prompts";
33237
33778
  async function browseCommand(options) {
33238
33779
  const config = await readConfig();
@@ -33301,7 +33842,7 @@ async function pickAgent2(agents) {
33301
33842
  return picked;
33302
33843
  }
33303
33844
  async function ensureWorktree(opts) {
33304
- const assignmentPath = resolve72(
33845
+ const assignmentPath = resolve73(
33305
33846
  opts.projectsDir,
33306
33847
  opts.projectSlug,
33307
33848
  "assignments",
@@ -33311,7 +33852,7 @@ async function ensureWorktree(opts) {
33311
33852
  if (!await fileExists(assignmentPath)) {
33312
33853
  return void 0;
33313
33854
  }
33314
- const content = await readFile48(assignmentPath, "utf-8");
33855
+ const content = await readFile49(assignmentPath, "utf-8");
33315
33856
  const { parseAssignmentFrontmatter: parseAssignmentFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
33316
33857
  const fm = parseAssignmentFrontmatter2(content);
33317
33858
  const { workspace } = fm;
@@ -33381,7 +33922,7 @@ async function ensureWorktree(opts) {
33381
33922
  async function runCreate(opts) {
33382
33923
  const { createWorktreeAndRecord: createWorktreeAndRecord2, GitWorktreeError: GitWorktreeError2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
33383
33924
  const expandedWorktree = expandHome(opts.worktreePath);
33384
- const absWorktree = isAbsolute8(expandedWorktree) ? expandedWorktree : resolve72(expandedWorktree);
33925
+ const absWorktree = isAbsolute8(expandedWorktree) ? expandedWorktree : resolve73(expandedWorktree);
33385
33926
  try {
33386
33927
  await createWorktreeAndRecord2({
33387
33928
  assignmentPath: opts.assignmentPath,
@@ -33410,7 +33951,7 @@ init_paths();
33410
33951
  init_fs();
33411
33952
  init_playbook();
33412
33953
  init_playbooks();
33413
- import { resolve as resolve73 } from "path";
33954
+ import { resolve as resolve74 } from "path";
33414
33955
  async function createPlaybookCommand(name, options) {
33415
33956
  if (!name.trim()) {
33416
33957
  throw new Error("Playbook name cannot be empty.");
@@ -33423,7 +33964,7 @@ async function createPlaybookCommand(name, options) {
33423
33964
  }
33424
33965
  const dir = playbooksDir();
33425
33966
  await ensureDir(dir);
33426
- const filePath = resolve73(dir, `${slug}.md`);
33967
+ const filePath = resolve74(dir, `${slug}.md`);
33427
33968
  if (await fileExists(filePath)) {
33428
33969
  throw new Error(
33429
33970
  `Playbook "${slug}" already exists at ${filePath}
@@ -33445,8 +33986,8 @@ init_paths();
33445
33986
  init_fs();
33446
33987
  init_parser();
33447
33988
  init_config2();
33448
- import { readdir as readdir26, readFile as readFile49 } from "fs/promises";
33449
- import { resolve as resolve74 } from "path";
33989
+ import { readdir as readdir26, readFile as readFile50 } from "fs/promises";
33990
+ import { resolve as resolve75 } from "path";
33450
33991
  async function listPlaybooksCommand(options = {}) {
33451
33992
  const dir = playbooksDir();
33452
33993
  if (!await fileExists(dir)) {
@@ -33461,8 +34002,8 @@ async function listPlaybooksCommand(options = {}) {
33461
34002
  );
33462
34003
  const rows = [];
33463
34004
  for (const entry of mdFiles) {
33464
- const filePath = resolve74(dir, entry.name);
33465
- const raw2 = await readFile49(filePath, "utf-8");
34005
+ const filePath = resolve75(dir, entry.name);
34006
+ const raw2 = await readFile50(filePath, "utf-8");
33466
34007
  const parsed = parsePlaybook(raw2);
33467
34008
  const slug = parsed.slug || entry.name.replace(/\.md$/, "");
33468
34009
  const disabled = disabledSet.has(slug);
@@ -33585,14 +34126,14 @@ init_fs();
33585
34126
  init_config2();
33586
34127
  init_slug();
33587
34128
  import { Command as Command2 } from "commander";
33588
- import { readFile as readFile51 } from "fs/promises";
33589
- import { resolve as resolve76 } from "path";
34129
+ import { readFile as readFile52 } from "fs/promises";
34130
+ import { resolve as resolve77 } from "path";
33590
34131
 
33591
34132
  // src/commands/bundle.ts
33592
34133
  init_paths();
33593
34134
  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";
34135
+ import { mkdir as mkdir10, readFile as readFile51, readdir as readdir27, rm as rm9, writeFile as writeFile13 } from "fs/promises";
34136
+ import { resolve as resolve76 } from "path";
33596
34137
  init_parser2();
33597
34138
  init_fs();
33598
34139
  init_config2();
@@ -33610,7 +34151,7 @@ async function resolveBundleScope(options) {
33610
34151
  throw new Error(`Invalid project slug: "${options.project}".`);
33611
34152
  }
33612
34153
  const config = await readConfig();
33613
- const projectMd = resolve75(config.defaultProjectDir, options.project, "project.md");
34154
+ const projectMd = resolve76(config.defaultProjectDir, options.project, "project.md");
33614
34155
  if (!await fileExists(projectMd)) {
33615
34156
  throw new Error(`Project "${options.project}" not found.`);
33616
34157
  }
@@ -33680,10 +34221,10 @@ function pickNextPlanFile(planDir, existingFiles) {
33680
34221
  const m = f.match(/^plan-v(\d+)\.md$/);
33681
34222
  if (m) versions.add(parseInt(m[1], 10));
33682
34223
  }
33683
- if (versions.size === 0) return { target: resolve75(planDir, "plan.md"), version: 1 };
34224
+ if (versions.size === 0) return { target: resolve76(planDir, "plan.md"), version: 1 };
33684
34225
  let n = 2;
33685
34226
  while (versions.has(n)) n++;
33686
- return { target: resolve75(planDir, `plan-v${n}.md`), version: n };
34227
+ return { target: resolve76(planDir, `plan-v${n}.md`), version: n };
33687
34228
  }
33688
34229
  function dedupePreserveOrder(ids) {
33689
34230
  const out = [];
@@ -33767,7 +34308,7 @@ bundleCommand.command("new").description("Create a new bundle from 2+ existing t
33767
34308
  if (options.plan) {
33768
34309
  const planDir = bundlePlanDir(sc.todosPath, sc.scopeId, id);
33769
34310
  await ensureDir(planDir);
33770
- const target = resolve75(planDir, "plan.md");
34311
+ const target = resolve76(planDir, "plan.md");
33771
34312
  const memberLines = items.map((it) => `- ${it.description} [t:${it.id}]`).join("\n");
33772
34313
  const stub = [
33773
34314
  "---",
@@ -33943,7 +34484,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
33943
34484
  }
33944
34485
  const repository = options.repository ?? process.cwd();
33945
34486
  const parentBranch = options.parentBranch ?? "main";
33946
- const worktreePath = options.worktreePath ?? resolve75(repository, ".worktrees", options.branch);
34487
+ const worktreePath = options.worktreePath ?? resolve76(repository, ".worktrees", options.branch);
33947
34488
  const checklist = await readChecklist(sc.todosPath, sc.checklistKey);
33948
34489
  for (const memberId of bundle.todoIds) {
33949
34490
  const item = checklist.items.find((i) => i.id === memberId);
@@ -33953,8 +34494,8 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
33953
34494
  }
33954
34495
  const bundlesFilePath = bundlesPath(sc.todosPath);
33955
34496
  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;
34497
+ const bundlesSnapshot = await fileExists(bundlesFilePath) ? await readFile51(bundlesFilePath, "utf-8") : null;
34498
+ const checklistSnapshot = await fileExists(checklistFilePath) ? await readFile51(checklistFilePath, "utf-8") : null;
33958
34499
  const record = async () => {
33959
34500
  bundle.branch = options.branch;
33960
34501
  bundle.worktreePath = worktreePath;
@@ -33970,7 +34511,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
33970
34511
  try {
33971
34512
  await writeBundles(sc.todosPath, bundles);
33972
34513
  await writeChecklist(sc.todosPath, checklist);
33973
- const ctxDir = resolve75(worktreePath, ".syntaur");
34514
+ const ctxDir = resolve76(worktreePath, ".syntaur");
33974
34515
  await mkdir10(ctxDir, { recursive: true });
33975
34516
  const payload = {
33976
34517
  bundleId: bundle.id,
@@ -33984,7 +34525,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
33984
34525
  repository,
33985
34526
  boundAt: nowISO()
33986
34527
  };
33987
- await writeFile13(resolve75(ctxDir, "context.json"), JSON.stringify(payload, null, 2) + "\n");
34528
+ await writeFile13(resolve76(ctxDir, "context.json"), JSON.stringify(payload, null, 2) + "\n");
33988
34529
  } catch (err2) {
33989
34530
  try {
33990
34531
  if (bundlesSnapshot === null) {
@@ -34174,7 +34715,7 @@ async function resolveScope(options) {
34174
34715
  throw new Error(`Invalid project slug: "${options.project}".`);
34175
34716
  }
34176
34717
  const config = await readConfig();
34177
- const projectMd = resolve76(config.defaultProjectDir, options.project, "project.md");
34718
+ const projectMd = resolve77(config.defaultProjectDir, options.project, "project.md");
34178
34719
  if (!await fileExists(projectMd)) {
34179
34720
  throw new Error(`Project "${options.project}" not found.`);
34180
34721
  }
@@ -34507,10 +35048,10 @@ todoCommand.command("archive").description("Archive completed todos and their lo
34507
35048
  (e) => e.itemIds.every((id) => completedIds.has(id))
34508
35049
  );
34509
35050
  const archFile = archivePath(todosPath, workspace, checklist.archiveInterval);
34510
- await ensureDir(resolve76(todosPath, "archive"));
35051
+ await ensureDir(resolve77(todosPath, "archive"));
34511
35052
  let archContent = "";
34512
35053
  if (await fileExists(archFile)) {
34513
- archContent = await readFile51(archFile, "utf-8");
35054
+ archContent = await readFile52(archFile, "utf-8");
34514
35055
  archContent = archContent.trimEnd() + "\n\n";
34515
35056
  } else {
34516
35057
  archContent = `---
@@ -34688,12 +35229,12 @@ function describeScope(scope) {
34688
35229
  }
34689
35230
  async function injectPromotedTodos(assignmentDir, todos, scopeLabel) {
34690
35231
  const { resolve: resolvePath2 } = await import("path");
34691
- const { readFile: readFile81 } = await import("fs/promises");
35232
+ const { readFile: readFile82 } = await import("fs/promises");
34692
35233
  const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
34693
35234
  const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
34694
35235
  const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
34695
35236
  const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
34696
- let content = await readFile81(assignmentMdPath2, "utf-8");
35237
+ let content = await readFile82(assignmentMdPath2, "utf-8");
34697
35238
  content = appendTodosToAssignmentBody2(
34698
35239
  content,
34699
35240
  todos.map((t) => ({
@@ -34744,7 +35285,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
34744
35285
  );
34745
35286
  let target;
34746
35287
  if (existingFiles.length === 0) {
34747
- target = resolve76(planDir, "plan.md");
35288
+ target = resolve77(planDir, "plan.md");
34748
35289
  } else {
34749
35290
  const versions = /* @__PURE__ */ new Set();
34750
35291
  for (const f of existingFiles) {
@@ -34754,7 +35295,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
34754
35295
  }
34755
35296
  let n = 2;
34756
35297
  while (versions.has(n)) n++;
34757
- target = resolve76(planDir, `plan-v${n}.md`);
35298
+ target = resolve77(planDir, `plan-v${n}.md`);
34758
35299
  }
34759
35300
  if (!await fileExists(target)) {
34760
35301
  const stub = `---
@@ -34948,12 +35489,12 @@ backupCommand.command("config").description("Show or update backup configuration
34948
35489
 
34949
35490
  // src/commands/doctor.ts
34950
35491
  import { Command as Command4 } from "commander";
34951
- import { readFile as readFile62 } from "fs/promises";
34952
- import { isAbsolute as isAbsolute11, resolve as resolve89 } from "path";
35492
+ import { readFile as readFile64 } from "fs/promises";
35493
+ import { isAbsolute as isAbsolute11, resolve as resolve91 } from "path";
34953
35494
 
34954
35495
  // src/utils/doctor/index.ts
34955
35496
  import { fileURLToPath as fileURLToPath12 } from "url";
34956
- import { readFile as readFile61 } from "fs/promises";
35497
+ import { readFile as readFile63 } from "fs/promises";
34957
35498
  import { dirname as dirname25, join as join21 } from "path";
34958
35499
 
34959
35500
  // src/utils/doctor/context.ts
@@ -34961,11 +35502,11 @@ init_config2();
34961
35502
  init_paths();
34962
35503
  init_fs();
34963
35504
  import Database5 from "better-sqlite3";
34964
- import { resolve as resolve77 } from "path";
35505
+ import { resolve as resolve78 } from "path";
34965
35506
  async function buildCheckContext(cwd = process.cwd()) {
34966
35507
  const config = await readConfig();
34967
35508
  const root = syntaurRoot();
34968
- const dbPath = resolve77(root, "syntaur.db");
35509
+ const dbPath = resolve78(root, "syntaur.db");
34969
35510
  let db6 = null;
34970
35511
  let dbError = null;
34971
35512
  if (await fileExists(dbPath)) {
@@ -34999,8 +35540,8 @@ function closeCheckContext(ctx) {
34999
35540
  // src/utils/doctor/checks/env.ts
35000
35541
  init_fs();
35001
35542
  init_paths();
35002
- import { resolve as resolve78, isAbsolute as isAbsolute9 } from "path";
35003
- import { readFile as readFile52, stat as stat8 } from "fs/promises";
35543
+ import { resolve as resolve79, isAbsolute as isAbsolute9 } from "path";
35544
+ import { readFile as readFile53, stat as stat9 } from "fs/promises";
35004
35545
  import { fileURLToPath as fileURLToPath10 } from "url";
35005
35546
  import { dirname as dirname22, join as join17 } from "path";
35006
35547
  var CATEGORY = "env";
@@ -35010,7 +35551,7 @@ var syntaurRootExists = {
35010
35551
  title: "~/.syntaur/ directory exists",
35011
35552
  async run(ctx) {
35012
35553
  try {
35013
- const s = await stat8(ctx.syntaurRoot);
35554
+ const s = await stat9(ctx.syntaurRoot);
35014
35555
  if (!s.isDirectory()) {
35015
35556
  return err(this, `${ctx.syntaurRoot} exists but is not a directory`, [
35016
35557
  ctx.syntaurRoot
@@ -35040,7 +35581,7 @@ var configValid = {
35040
35581
  category: CATEGORY,
35041
35582
  title: "~/.syntaur/config.md is valid",
35042
35583
  async run(ctx) {
35043
- const configPath2 = resolve78(ctx.syntaurRoot, "config.md");
35584
+ const configPath2 = resolve79(ctx.syntaurRoot, "config.md");
35044
35585
  if (!await fileExists(configPath2)) {
35045
35586
  return {
35046
35587
  id: this.id,
@@ -35057,7 +35598,7 @@ var configValid = {
35057
35598
  autoFixable: false
35058
35599
  };
35059
35600
  }
35060
- const content = await readFile52(configPath2, "utf-8");
35601
+ const content = await readFile53(configPath2, "utf-8");
35061
35602
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
35062
35603
  if (!fmMatch || fmMatch[1].trim() === "") {
35063
35604
  return {
@@ -35337,7 +35878,7 @@ async function readLocalPkg() {
35337
35878
  for (let i = 0; i < 6; i++) {
35338
35879
  const candidate = join17(dir, "package.json");
35339
35880
  try {
35340
- const text = await readFile52(candidate, "utf-8");
35881
+ const text = await readFile53(candidate, "utf-8");
35341
35882
  return JSON.parse(text);
35342
35883
  } catch {
35343
35884
  dir = dirname22(dir);
@@ -35389,8 +35930,8 @@ function versionGte(a, b) {
35389
35930
 
35390
35931
  // src/utils/doctor/checks/structure.ts
35391
35932
  init_fs();
35392
- import { resolve as resolve79 } from "path";
35393
- import { readdir as readdir28, stat as stat9 } from "fs/promises";
35933
+ import { resolve as resolve80 } from "path";
35934
+ import { readdir as readdir28, stat as stat10 } from "fs/promises";
35394
35935
  var CATEGORY2 = "structure";
35395
35936
  var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
35396
35937
  "projects",
@@ -35409,7 +35950,7 @@ var projectsDir = {
35409
35950
  category: CATEGORY2,
35410
35951
  title: "projects/ directory exists",
35411
35952
  async run(ctx) {
35412
- const p = resolve79(ctx.syntaurRoot, "projects");
35953
+ const p = resolve80(ctx.syntaurRoot, "projects");
35413
35954
  if (!await fileExists(p)) {
35414
35955
  return {
35415
35956
  id: this.id,
@@ -35434,7 +35975,7 @@ var playbooksDir2 = {
35434
35975
  category: CATEGORY2,
35435
35976
  title: "playbooks/ directory exists",
35436
35977
  async run(ctx) {
35437
- const p = resolve79(ctx.syntaurRoot, "playbooks");
35978
+ const p = resolve80(ctx.syntaurRoot, "playbooks");
35438
35979
  if (!await fileExists(p)) {
35439
35980
  return {
35440
35981
  id: this.id,
@@ -35459,7 +36000,7 @@ var todosDirValid = {
35459
36000
  category: CATEGORY2,
35460
36001
  title: "todos/ directory is readable (if present)",
35461
36002
  async run(ctx) {
35462
- const p = resolve79(ctx.syntaurRoot, "todos");
36003
+ const p = resolve80(ctx.syntaurRoot, "todos");
35463
36004
  if (!await fileExists(p)) {
35464
36005
  return {
35465
36006
  id: this.id,
@@ -35470,7 +36011,7 @@ var todosDirValid = {
35470
36011
  autoFixable: false
35471
36012
  };
35472
36013
  }
35473
- const s = await stat9(p);
36014
+ const s = await stat10(p);
35474
36015
  if (!s.isDirectory()) {
35475
36016
  return {
35476
36017
  id: this.id,
@@ -35490,7 +36031,7 @@ var serversDirValid = {
35490
36031
  category: CATEGORY2,
35491
36032
  title: "servers/ directory is readable (if present)",
35492
36033
  async run(ctx) {
35493
- const p = resolve79(ctx.syntaurRoot, "servers");
36034
+ const p = resolve80(ctx.syntaurRoot, "servers");
35494
36035
  if (!await fileExists(p)) {
35495
36036
  return {
35496
36037
  id: this.id,
@@ -35501,7 +36042,7 @@ var serversDirValid = {
35501
36042
  autoFixable: false
35502
36043
  };
35503
36044
  }
35504
- const s = await stat9(p);
36045
+ const s = await stat10(p);
35505
36046
  if (!s.isDirectory()) {
35506
36047
  return {
35507
36048
  id: this.id,
@@ -35535,7 +36076,7 @@ var knownFilesRecognized = {
35535
36076
  title: this.title,
35536
36077
  status: "warn",
35537
36078
  detail: `unexpected top-level entries: ${unexpected.join(", ")}`,
35538
- affected: unexpected.map((n) => resolve79(ctx.syntaurRoot, n)),
36079
+ affected: unexpected.map((n) => resolve80(ctx.syntaurRoot, n)),
35539
36080
  remediation: {
35540
36081
  kind: "manual",
35541
36082
  suggestion: "Review these entries \u2014 they may be leftover state from older versions",
@@ -35564,8 +36105,8 @@ function pass2(check) {
35564
36105
 
35565
36106
  // src/utils/doctor/checks/project.ts
35566
36107
  init_fs();
35567
- import { resolve as resolve80 } from "path";
35568
- import { readdir as readdir29, stat as stat10 } from "fs/promises";
36108
+ import { resolve as resolve81 } from "path";
36109
+ import { readdir as readdir29, stat as stat11 } from "fs/promises";
35569
36110
  var CATEGORY3 = "project";
35570
36111
  var REQUIRED_PROJECT_FILES = [
35571
36112
  "project.md",
@@ -35594,10 +36135,10 @@ async function listProjects2(ctx) {
35594
36135
  for (const e of entries) {
35595
36136
  if (!e.isDirectory()) continue;
35596
36137
  if (e.name.startsWith(".") || e.name.startsWith("_")) continue;
35597
- const projectDir = resolve80(dir, e.name);
36138
+ const projectDir = resolve81(dir, e.name);
35598
36139
  let looksLikeProject = false;
35599
36140
  for (const marker of PROJECT_MARKERS) {
35600
- if (await fileExists(resolve80(projectDir, marker))) {
36141
+ if (await fileExists(resolve81(projectDir, marker))) {
35601
36142
  looksLikeProject = true;
35602
36143
  break;
35603
36144
  }
@@ -35616,7 +36157,7 @@ var requiredFiles = {
35616
36157
  for (const projectDir of projects) {
35617
36158
  const missing = [];
35618
36159
  for (const rel of REQUIRED_PROJECT_FILES) {
35619
- const p = resolve80(projectDir, rel);
36160
+ const p = resolve81(projectDir, rel);
35620
36161
  if (!await fileExists(p)) missing.push(rel);
35621
36162
  }
35622
36163
  if (missing.length === 0) continue;
@@ -35626,7 +36167,7 @@ var requiredFiles = {
35626
36167
  title: this.title,
35627
36168
  status: "error",
35628
36169
  detail: `project at ${projectDir} is missing: ${missing.join(", ")}`,
35629
- affected: missing.map((m) => resolve80(projectDir, m)),
36170
+ affected: missing.map((m) => resolve81(projectDir, m)),
35630
36171
  remediation: {
35631
36172
  kind: "manual",
35632
36173
  suggestion: "Recreate the missing scaffold files from templates",
@@ -35649,9 +36190,9 @@ var manifestStale = {
35649
36190
  const projects = await listProjects2(ctx);
35650
36191
  const results = [];
35651
36192
  for (const projectDir of projects) {
35652
- const manifestPath = resolve80(projectDir, "manifest.md");
36193
+ const manifestPath = resolve81(projectDir, "manifest.md");
35653
36194
  if (!await fileExists(manifestPath)) continue;
35654
- const manifestMtime = (await stat10(manifestPath)).mtimeMs;
36195
+ const manifestMtime = (await stat11(manifestPath)).mtimeMs;
35655
36196
  const newestAssignment = await newestAssignmentMtime(projectDir);
35656
36197
  if (newestAssignment === 0) continue;
35657
36198
  if (newestAssignment > manifestMtime) {
@@ -35698,7 +36239,7 @@ var orphanFiles = {
35698
36239
  title: this.title,
35699
36240
  status: "warn",
35700
36241
  detail: `project at ${projectDir} has unexpected entries: ${orphans.join(", ")}`,
35701
- affected: orphans.map((o) => resolve80(projectDir, o)),
36242
+ affected: orphans.map((o) => resolve81(projectDir, o)),
35702
36243
  autoFixable: false
35703
36244
  });
35704
36245
  }
@@ -35708,7 +36249,7 @@ var orphanFiles = {
35708
36249
  };
35709
36250
  var projectChecks = [requiredFiles, manifestStale, orphanFiles];
35710
36251
  async function newestAssignmentMtime(projectDir) {
35711
- const assignmentsRoot = resolve80(projectDir, "assignments");
36252
+ const assignmentsRoot = resolve81(projectDir, "assignments");
35712
36253
  if (!await fileExists(assignmentsRoot)) return 0;
35713
36254
  let newest = 0;
35714
36255
  let entries;
@@ -35719,9 +36260,9 @@ async function newestAssignmentMtime(projectDir) {
35719
36260
  }
35720
36261
  for (const e of entries) {
35721
36262
  if (!e.isDirectory()) continue;
35722
- const assignmentMd = resolve80(assignmentsRoot, e.name, "assignment.md");
36263
+ const assignmentMd = resolve81(assignmentsRoot, e.name, "assignment.md");
35723
36264
  try {
35724
- const s = await stat10(assignmentMd);
36265
+ const s = await stat11(assignmentMd);
35725
36266
  if (s.mtimeMs > newest) newest = s.mtimeMs;
35726
36267
  } catch {
35727
36268
  }
@@ -35744,8 +36285,8 @@ init_parser();
35744
36285
  init_types();
35745
36286
  init_paths();
35746
36287
  init_assignment_walk();
35747
- import { resolve as resolve81 } from "path";
35748
- import { readFile as readFile53, readdir as readdir30 } from "fs/promises";
36288
+ import { resolve as resolve82 } from "path";
36289
+ import { readFile as readFile54, readdir as readdir30 } from "fs/promises";
35749
36290
  var CATEGORY4 = "assignment";
35750
36291
  var STATUSES_REQUIRING_HANDOFF = /* @__PURE__ */ new Set(["review", "completed"]);
35751
36292
  var PRE_WORKSPACE_STATUSES = /* @__PURE__ */ new Set([
@@ -35839,7 +36380,7 @@ var invalidStatus = {
35839
36380
  const allowed = configuredStatuses(ctx);
35840
36381
  const results = [];
35841
36382
  for (const a of withAssignmentMd) {
35842
- const path = resolve81(a.assignmentDir, "assignment.md");
36383
+ const path = resolve82(a.assignmentDir, "assignment.md");
35843
36384
  const parsed = await parseSafe4(path);
35844
36385
  if (!parsed) continue;
35845
36386
  if (!allowed.has(parsed.status)) {
@@ -35872,7 +36413,7 @@ var workspaceMissing = {
35872
36413
  const terminal = terminalStatuses(ctx);
35873
36414
  const results = [];
35874
36415
  for (const a of withAssignmentMd) {
35875
- const path = resolve81(a.assignmentDir, "assignment.md");
36416
+ const path = resolve82(a.assignmentDir, "assignment.md");
35876
36417
  const parsed = await parseSafe4(path);
35877
36418
  if (!parsed) continue;
35878
36419
  if (terminal.has(parsed.status)) continue;
@@ -35919,12 +36460,12 @@ var requiredFilesByStatus = {
35919
36460
  const { withAssignmentMd } = await listAssignments(ctx);
35920
36461
  const results = [];
35921
36462
  for (const a of withAssignmentMd) {
35922
- const assignmentPath = resolve81(a.assignmentDir, "assignment.md");
36463
+ const assignmentPath = resolve82(a.assignmentDir, "assignment.md");
35923
36464
  const parsed = await parseSafe4(assignmentPath);
35924
36465
  if (!parsed) continue;
35925
36466
  const missing = [];
35926
36467
  if (STATUSES_REQUIRING_HANDOFF.has(parsed.status)) {
35927
- const handoffPath = resolve81(a.assignmentDir, "handoff.md");
36468
+ const handoffPath = resolve82(a.assignmentDir, "handoff.md");
35928
36469
  if (!await fileExists(handoffPath)) missing.push("handoff.md");
35929
36470
  }
35930
36471
  if (missing.length === 0) continue;
@@ -35934,7 +36475,7 @@ var requiredFilesByStatus = {
35934
36475
  title: this.title,
35935
36476
  status: "warn",
35936
36477
  detail: `${a.projectSlug}/${a.assignmentSlug} (status: ${parsed.status}) is missing ${missing.join(", ")}`,
35937
- affected: missing.map((m) => resolve81(a.assignmentDir, m)),
36478
+ affected: missing.map((m) => resolve82(a.assignmentDir, m)),
35938
36479
  remediation: {
35939
36480
  kind: "manual",
35940
36481
  suggestion: `Create the missing ${missing.join(" and ")} files for this assignment`,
@@ -35957,7 +36498,7 @@ var companionFilesScaffolded = {
35957
36498
  for (const a of withAssignmentMd) {
35958
36499
  const missing = [];
35959
36500
  for (const filename of ["progress.md", "comments.md"]) {
35960
- if (!await fileExists(resolve81(a.assignmentDir, filename))) {
36501
+ if (!await fileExists(resolve82(a.assignmentDir, filename))) {
35961
36502
  missing.push(filename);
35962
36503
  }
35963
36504
  }
@@ -35969,7 +36510,7 @@ var companionFilesScaffolded = {
35969
36510
  title: this.title,
35970
36511
  status: "warn",
35971
36512
  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)),
36513
+ affected: missing.map((m) => resolve82(a.assignmentDir, m)),
35973
36514
  remediation: {
35974
36515
  kind: "manual",
35975
36516
  suggestion: `Create ${missing.join(" and ")} with the renderProgress/renderComments templates, or re-scaffold via the CLI`,
@@ -36002,7 +36543,7 @@ var typeDefinition = {
36002
36543
  const { withAssignmentMd } = await listAssignments(ctx);
36003
36544
  const results = [];
36004
36545
  for (const a of withAssignmentMd) {
36005
- const path = resolve81(a.assignmentDir, "assignment.md");
36546
+ const path = resolve82(a.assignmentDir, "assignment.md");
36006
36547
  const parsed = await parseSafe4(path);
36007
36548
  if (!parsed) continue;
36008
36549
  if (!parsed.type) continue;
@@ -36036,7 +36577,7 @@ var projectFrontmatterMatchesContainer = {
36036
36577
  const { withAssignmentMd } = await listAssignments(ctx);
36037
36578
  const results = [];
36038
36579
  for (const a of withAssignmentMd) {
36039
- const path = resolve81(a.assignmentDir, "assignment.md");
36580
+ const path = resolve82(a.assignmentDir, "assignment.md");
36040
36581
  const parsed = await parseSafe4(path);
36041
36582
  if (!parsed) continue;
36042
36583
  if (a.standalone) {
@@ -36087,13 +36628,13 @@ var draftMissingObjective = {
36087
36628
  const { withAssignmentMd } = await listAssignments(ctx);
36088
36629
  const results = [];
36089
36630
  for (const a of withAssignmentMd) {
36090
- const path = resolve81(a.assignmentDir, "assignment.md");
36631
+ const path = resolve82(a.assignmentDir, "assignment.md");
36091
36632
  const parsed = await parseSafe4(path);
36092
36633
  if (!parsed) continue;
36093
36634
  if (parsed.status !== "draft") continue;
36094
36635
  let raw2;
36095
36636
  try {
36096
- raw2 = await readFile53(path, "utf-8");
36637
+ raw2 = await readFile54(path, "utf-8");
36097
36638
  } catch {
36098
36639
  continue;
36099
36640
  }
@@ -36126,7 +36667,7 @@ var readyToImplementMissingPlan = {
36126
36667
  const { withAssignmentMd } = await listAssignments(ctx);
36127
36668
  const results = [];
36128
36669
  for (const a of withAssignmentMd) {
36129
- const path = resolve81(a.assignmentDir, "assignment.md");
36670
+ const path = resolve82(a.assignmentDir, "assignment.md");
36130
36671
  const parsed = await parseSafe4(path);
36131
36672
  if (!parsed) continue;
36132
36673
  if (parsed.status !== "ready_to_implement") continue;
@@ -36135,7 +36676,7 @@ var readyToImplementMissingPlan = {
36135
36676
  let hasPlanContent = false;
36136
36677
  for (const f of planFiles) {
36137
36678
  try {
36138
- const c2 = await readFile53(resolve81(a.assignmentDir, f), "utf-8");
36679
+ const c2 = await readFile54(resolve82(a.assignmentDir, f), "utf-8");
36139
36680
  if (c2.trim().length > 0) {
36140
36681
  hasPlanContent = true;
36141
36682
  break;
@@ -36151,7 +36692,7 @@ var readyToImplementMissingPlan = {
36151
36692
  title: this.title,
36152
36693
  status: "warn",
36153
36694
  detail: `${label} (status: ready_to_implement) has no plan.md or plan-v<N>.md`,
36154
- affected: [resolve81(a.assignmentDir, "plan.md")],
36695
+ affected: [resolve82(a.assignmentDir, "plan.md")],
36155
36696
  remediation: {
36156
36697
  kind: "manual",
36157
36698
  suggestion: `Write a plan with '/plan-assignment' (or 'syntaur plan'), then re-mark ready_to_implement`,
@@ -36178,7 +36719,7 @@ var assignmentChecks = [
36178
36719
  ];
36179
36720
  async function parseSafe4(path) {
36180
36721
  try {
36181
- const content = await readFile53(path, "utf-8");
36722
+ const content = await readFile54(path, "utf-8");
36182
36723
  return parseAssignmentFull(content);
36183
36724
  } catch {
36184
36725
  return null;
@@ -36197,7 +36738,7 @@ function pass4(check, detail) {
36197
36738
 
36198
36739
  // src/utils/doctor/checks/dashboard.ts
36199
36740
  init_fs();
36200
- import { resolve as resolve82 } from "path";
36741
+ import { resolve as resolve83 } from "path";
36201
36742
  var CATEGORY5 = "dashboard";
36202
36743
  var dbReachable = {
36203
36744
  id: "dashboard.db-reachable",
@@ -36211,7 +36752,7 @@ var dbReachable = {
36211
36752
  title: this.title,
36212
36753
  status: "error",
36213
36754
  detail: `could not open syntaur.db: ${ctx.dbError ?? "unknown error"}`,
36214
- affected: [resolve82(ctx.syntaurRoot, "syntaur.db")],
36755
+ affected: [resolve83(ctx.syntaurRoot, "syntaur.db")],
36215
36756
  remediation: {
36216
36757
  kind: "manual",
36217
36758
  suggestion: "Start the dashboard once (`syntaur dashboard`) to initialize the DB, or restore it from backup",
@@ -36229,7 +36770,7 @@ var dbReachable = {
36229
36770
  title: this.title,
36230
36771
  status: "error",
36231
36772
  detail: 'syntaur.db is missing the expected "sessions" table',
36232
- affected: [resolve82(ctx.syntaurRoot, "syntaur.db")],
36773
+ affected: [resolve83(ctx.syntaurRoot, "syntaur.db")],
36233
36774
  autoFixable: false
36234
36775
  };
36235
36776
  }
@@ -36241,7 +36782,7 @@ var dbReachable = {
36241
36782
  title: this.title,
36242
36783
  status: "error",
36243
36784
  detail: `syntaur.db query failed: ${err2 instanceof Error ? err2.message : String(err2)}`,
36244
- affected: [resolve82(ctx.syntaurRoot, "syntaur.db")],
36785
+ affected: [resolve83(ctx.syntaurRoot, "syntaur.db")],
36245
36786
  autoFixable: false
36246
36787
  };
36247
36788
  }
@@ -36267,7 +36808,7 @@ var ghostSessions = {
36267
36808
  const results = [];
36268
36809
  for (const row of rows) {
36269
36810
  if (!row.project_slug) continue;
36270
- const projectPath = resolve82(projectsDir2, row.project_slug, "project.md");
36811
+ const projectPath = resolve83(projectsDir2, row.project_slug, "project.md");
36271
36812
  if (!await fileExists(projectPath)) {
36272
36813
  results.push({
36273
36814
  id: this.id,
@@ -36286,7 +36827,7 @@ var ghostSessions = {
36286
36827
  continue;
36287
36828
  }
36288
36829
  if (row.assignment_slug) {
36289
- const assignmentPath = resolve82(
36830
+ const assignmentPath = resolve83(
36290
36831
  projectsDir2,
36291
36832
  row.project_slug,
36292
36833
  "assignments",
@@ -36338,8 +36879,8 @@ function skipped(check, reason) {
36338
36879
 
36339
36880
  // src/utils/doctor/checks/integrations.ts
36340
36881
  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";
36882
+ import { resolve as resolve84, dirname as dirname23, basename as basename9 } from "path";
36883
+ import { readdir as readdir31, readFile as readFile55 } from "fs/promises";
36343
36884
  import { homedir as homedir14 } from "os";
36344
36885
  var CATEGORY6 = "integrations";
36345
36886
  var claudePluginLinked = {
@@ -36421,10 +36962,10 @@ var backupConfigured = {
36421
36962
  }
36422
36963
  };
36423
36964
  async function readKnownMarketplaces() {
36424
- const path = resolve83(homedir14(), ".claude", "plugins", "known_marketplaces.json");
36965
+ const path = resolve84(homedir14(), ".claude", "plugins", "known_marketplaces.json");
36425
36966
  if (!await fileExists(path)) return {};
36426
36967
  try {
36427
- const raw2 = await readFile54(path, "utf-8");
36968
+ const raw2 = await readFile55(path, "utf-8");
36428
36969
  return JSON.parse(raw2);
36429
36970
  } catch {
36430
36971
  return {};
@@ -36441,7 +36982,7 @@ var claudeMarketplaceRegistered = {
36441
36982
  return skipped2(this, "claudePluginDir does not exist (run install-plugin)");
36442
36983
  }
36443
36984
  const pluginsParent = dirname23(dir);
36444
- if (basename8(pluginsParent) !== "plugins") {
36985
+ if (basename9(pluginsParent) !== "plugins") {
36445
36986
  return {
36446
36987
  id: this.id,
36447
36988
  category: this.category,
@@ -36458,7 +36999,7 @@ var claudeMarketplaceRegistered = {
36458
36999
  };
36459
37000
  }
36460
37001
  const marketplaceRoot = dirname23(pluginsParent);
36461
- const marketplaceManifest = resolve83(marketplaceRoot, ".claude-plugin", "marketplace.json");
37002
+ const marketplaceManifest = resolve84(marketplaceRoot, ".claude-plugin", "marketplace.json");
36462
37003
  if (!await fileExists(marketplaceManifest)) {
36463
37004
  return {
36464
37005
  id: this.id,
@@ -36477,7 +37018,7 @@ var claudeMarketplaceRegistered = {
36477
37018
  }
36478
37019
  let parsed = {};
36479
37020
  try {
36480
- parsed = JSON.parse(await readFile54(marketplaceManifest, "utf-8"));
37021
+ parsed = JSON.parse(await readFile55(marketplaceManifest, "utf-8"));
36481
37022
  } catch {
36482
37023
  return {
36483
37024
  id: this.id,
@@ -36489,7 +37030,7 @@ var claudeMarketplaceRegistered = {
36489
37030
  autoFixable: false
36490
37031
  };
36491
37032
  }
36492
- const marketplaceName = parsed.name ?? basename8(marketplaceRoot);
37033
+ const marketplaceName = parsed.name ?? basename9(marketplaceRoot);
36493
37034
  const hasSyntaurEntry = (parsed.plugins ?? []).some((p) => p?.name === "syntaur");
36494
37035
  const known = await readKnownMarketplaces();
36495
37036
  const registered = known[marketplaceName]?.installLocation === marketplaceRoot || Object.values(known).some((v) => v.installLocation === marketplaceRoot);
@@ -36509,7 +37050,7 @@ var claudeMarketplaceRegistered = {
36509
37050
  title: this.title,
36510
37051
  status: "error",
36511
37052
  detail: issues.join("; "),
36512
- affected: [marketplaceManifest, resolve83(homedir14(), ".claude", "plugins", "known_marketplaces.json")],
37053
+ affected: [marketplaceManifest, resolve84(homedir14(), ".claude", "plugins", "known_marketplaces.json")],
36513
37054
  remediation: {
36514
37055
  kind: "manual",
36515
37056
  suggestion: "Re-run install-plugin to ensure both files are in sync.",
@@ -36549,8 +37090,8 @@ function skipped2(check, reason) {
36549
37090
  init_fs();
36550
37091
  init_parser();
36551
37092
  init_types();
36552
- import { resolve as resolve84 } from "path";
36553
- import { readFile as readFile55 } from "fs/promises";
37093
+ import { resolve as resolve85 } from "path";
37094
+ import { readFile as readFile56 } from "fs/promises";
36554
37095
  var CATEGORY7 = "workspace";
36555
37096
  var ASSIGNMENT_FIELDS2 = ["projectSlug", "assignmentSlug", "projectDir", "assignmentDir"];
36556
37097
  var BUNDLE_FIELDS = ["bundleId", "bundleScope", "bundleScopeId"];
@@ -36572,12 +37113,12 @@ function isStandaloneSession(ctx) {
36572
37113
  return !hasAnyAssignmentField(ctx) && !hasAnyBundleField(ctx) && hasSessionMeta;
36573
37114
  }
36574
37115
  async function loadContext(ctx) {
36575
- const path = resolve84(ctx.cwd, ".syntaur", "context.json");
37116
+ const path = resolve85(ctx.cwd, ".syntaur", "context.json");
36576
37117
  if (!await fileExists(path)) {
36577
37118
  return { data: null, path, exists: false, parseError: null };
36578
37119
  }
36579
37120
  try {
36580
- const raw2 = await readFile55(path, "utf-8");
37121
+ const raw2 = await readFile56(path, "utf-8");
36581
37122
  return { data: JSON.parse(raw2), path, exists: true, parseError: null };
36582
37123
  } catch (err2) {
36583
37124
  return {
@@ -36671,7 +37212,7 @@ var contextAssignmentResolves = {
36671
37212
  if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to resolve");
36672
37213
  if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to resolve");
36673
37214
  if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
36674
- const assignmentMd = resolve84(data.assignmentDir, "assignment.md");
37215
+ const assignmentMd = resolve85(data.assignmentDir, "assignment.md");
36675
37216
  if (!await fileExists(assignmentMd)) {
36676
37217
  return {
36677
37218
  id: this.id,
@@ -36701,10 +37242,10 @@ var contextTerminal = {
36701
37242
  if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to check");
36702
37243
  if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to check");
36703
37244
  if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
36704
- const assignmentMd = resolve84(data.assignmentDir, "assignment.md");
37245
+ const assignmentMd = resolve85(data.assignmentDir, "assignment.md");
36705
37246
  if (!await fileExists(assignmentMd)) return skipped3(this, "assignment file missing");
36706
37247
  try {
36707
- const content = await readFile55(assignmentMd, "utf-8");
37248
+ const content = await readFile56(assignmentMd, "utf-8");
36708
37249
  const parsed = parseAssignmentFull(content);
36709
37250
  const terminal = terminalStatuses2(ctx);
36710
37251
  if (terminal.has(parsed.status)) {
@@ -36850,15 +37391,15 @@ var agentChecks = [agentsResolvable];
36850
37391
  // src/utils/doctor/checks/terminal.ts
36851
37392
  init_config2();
36852
37393
  import { spawnSync as spawnSync9 } from "child_process";
36853
- import { readFile as readFile56 } from "fs/promises";
36854
- import { resolve as resolve85 } from "path";
37394
+ import { readFile as readFile57 } from "fs/promises";
37395
+ import { resolve as resolve86 } from "path";
36855
37396
  init_paths();
36856
37397
  init_fs();
36857
37398
  var CATEGORY9 = "terminal";
36858
37399
  async function readRawTerminalKey() {
36859
- const configPath2 = resolve85(syntaurRoot(), "config.md");
37400
+ const configPath2 = resolve86(syntaurRoot(), "config.md");
36860
37401
  if (!await fileExists(configPath2)) return null;
36861
- const content = await readFile56(configPath2, "utf-8");
37402
+ const content = await readFile57(configPath2, "utf-8");
36862
37403
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
36863
37404
  if (!fmMatch) return null;
36864
37405
  const line = fmMatch[1].split("\n").find((l) => /^terminal:\s*/.test(l));
@@ -37008,13 +37549,13 @@ var terminalChecks = [
37008
37549
 
37009
37550
  // src/utils/doctor/checks/skills.ts
37010
37551
  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";
37552
+ import { resolve as resolve87, join as join18 } from "path";
37553
+ import { readdir as readdir32, readFile as readFile58, lstat as lstat4 } from "fs/promises";
37013
37554
  import { homedir as homedir15 } from "os";
37014
37555
  var CATEGORY10 = "skills";
37015
37556
  var skillTargets = [
37016
- { agent: "claude", dir: resolve86(homedir15(), ".claude", "skills"), label: "~/.claude/skills" },
37017
- { agent: "codex", dir: resolve86(homedir15(), ".codex", "skills"), label: "~/.codex/skills" }
37557
+ { agent: "claude", dir: resolve87(homedir15(), ".claude", "skills"), label: "~/.claude/skills" },
37558
+ { agent: "codex", dir: resolve87(homedir15(), ".codex", "skills"), label: "~/.codex/skills" }
37018
37559
  ];
37019
37560
  var skillsDedupCheck = {
37020
37561
  id: "skills.dedup",
@@ -37038,7 +37579,7 @@ var skillsDedupCheck = {
37038
37579
  if (!KNOWN_SKILLS.includes(entry.name)) continue;
37039
37580
  const skillMd = join18(dir, entry.name, "SKILL.md");
37040
37581
  if (!await fileExists(skillMd)) continue;
37041
- const content = await readFile57(skillMd, "utf-8").catch(() => "");
37582
+ const content = await readFile58(skillMd, "utf-8").catch(() => "");
37042
37583
  const match = content.match(/^name:\s*(\S+)\s*$/m);
37043
37584
  if (!match || match[1] !== entry.name) continue;
37044
37585
  let isSymlink2 = false;
@@ -37088,12 +37629,12 @@ var skillsChecks = [skillsDedupCheck];
37088
37629
 
37089
37630
  // src/utils/doctor/checks/cross-agent.ts
37090
37631
  init_fs();
37091
- import { join as join19, resolve as resolve87 } from "path";
37092
- import { readFile as readFile59 } from "fs/promises";
37632
+ import { join as join19, resolve as resolve88 } from "path";
37633
+ import { readFile as readFile60 } from "fs/promises";
37093
37634
 
37094
37635
  // src/utils/skill-frontmatter.ts
37095
37636
  import { createHash as createHash4 } from "crypto";
37096
- import { readFile as readFile58 } from "fs/promises";
37637
+ import { readFile as readFile59 } from "fs/promises";
37097
37638
  function stripQuotes(raw2) {
37098
37639
  const t = raw2.trim();
37099
37640
  if (t.length >= 2 && (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'"))) {
@@ -37137,7 +37678,7 @@ function readSkillIdentity(skillMdText) {
37137
37678
  return { name, hasDescription };
37138
37679
  }
37139
37680
  async function sha256File(path) {
37140
- return createHash4("sha256").update(await readFile58(path)).digest("hex");
37681
+ return createHash4("sha256").update(await readFile59(path)).digest("hex");
37141
37682
  }
37142
37683
 
37143
37684
  // src/utils/doctor/checks/cross-agent.ts
@@ -37154,7 +37695,7 @@ async function checkTargetSkillsIntegrity(installedDir, canonicalSkillsDir, know
37154
37695
  }
37155
37696
  let text;
37156
37697
  try {
37157
- text = await readFile59(installedPath, "utf-8");
37698
+ text = await readFile60(installedPath, "utf-8");
37158
37699
  } catch {
37159
37700
  problems.push({ skill, kind: "invalid-frontmatter" });
37160
37701
  continue;
@@ -37231,7 +37772,7 @@ var crossAgentSkillsCheck = {
37231
37772
  }
37232
37773
  if (recorded && t.instructions) {
37233
37774
  for (const f of t.instructions.files) {
37234
- const p = resolve87(ctx.cwd, f.path);
37775
+ const p = resolve88(ctx.cwd, f.path);
37235
37776
  if (!await fileExists(p)) {
37236
37777
  problems.push(`${t.displayName}: missing protocol file ${f.path} in cwd`);
37237
37778
  affected.push(p);
@@ -37307,7 +37848,7 @@ var crossAgentChecks = [crossAgentSkillsCheck];
37307
37848
  // src/utils/doctor/checks/bundles.ts
37308
37849
  init_fs();
37309
37850
  init_paths();
37310
- import { resolve as resolve88 } from "path";
37851
+ import { resolve as resolve89 } from "path";
37311
37852
  import { readdir as readdir33 } from "fs/promises";
37312
37853
  import { spawnSync as spawnSync10 } from "child_process";
37313
37854
  init_parser2();
@@ -37337,7 +37878,7 @@ async function listScopes(ctx) {
37337
37878
  if (!e.isDirectory()) continue;
37338
37879
  const slug = e.name;
37339
37880
  if (typeof slug !== "string" || slug.startsWith(".")) continue;
37340
- const projectMd = resolve88(ctx.config.defaultProjectDir, slug, "project.md");
37881
+ const projectMd = resolve89(ctx.config.defaultProjectDir, slug, "project.md");
37341
37882
  if (!await fileExists(projectMd)) continue;
37342
37883
  out.push({
37343
37884
  scopeLabel: `project:${slug}`,
@@ -37530,7 +38071,7 @@ var bundleChecks = [
37530
38071
  ];
37531
38072
 
37532
38073
  // src/utils/doctor/checks/plugin.ts
37533
- import { readFile as readFile60 } from "fs/promises";
38074
+ import { readFile as readFile61 } from "fs/promises";
37534
38075
  import { dirname as dirname24, join as join20 } from "path";
37535
38076
  import { fileURLToPath as fileURLToPath11 } from "url";
37536
38077
  var CATEGORY13 = "plugin";
@@ -37540,7 +38081,7 @@ async function readCliVersion() {
37540
38081
  let dir = dirname24(fileURLToPath11(import.meta.url));
37541
38082
  for (let i = 0; i < 8; i += 1) {
37542
38083
  try {
37543
- const parsed = JSON.parse(await readFile60(join20(dir, "package.json"), "utf-8"));
38084
+ const parsed = JSON.parse(await readFile61(join20(dir, "package.json"), "utf-8"));
37544
38085
  if (typeof parsed.version === "string" && parsed.version.length > 0) return parsed.version;
37545
38086
  } catch {
37546
38087
  }
@@ -37648,6 +38189,68 @@ var deriveConfigValid = {
37648
38189
  };
37649
38190
  var deriveConfigChecks = [deriveConfigValid];
37650
38191
 
38192
+ // src/utils/doctor/checks/staleness.ts
38193
+ init_config2();
38194
+ import { resolve as resolve90 } from "path";
38195
+ import { readFile as readFile62 } from "fs/promises";
38196
+ var CATEGORY15 = "staleness";
38197
+ var stalenessConfigValid = {
38198
+ id: "staleness.valid",
38199
+ category: CATEGORY15,
38200
+ title: "Staleness threshold overrides are valid",
38201
+ async run(ctx) {
38202
+ let content;
38203
+ try {
38204
+ content = await readFile62(resolve90(ctx.syntaurRoot, "config.md"), "utf-8");
38205
+ } catch {
38206
+ return {
38207
+ id: this.id,
38208
+ category: this.category,
38209
+ title: this.title,
38210
+ status: "skipped",
38211
+ detail: "no config.md",
38212
+ autoFixable: false
38213
+ };
38214
+ }
38215
+ if (!/^\s*staleness:\s*$/m.test(content)) {
38216
+ return {
38217
+ id: this.id,
38218
+ category: this.category,
38219
+ title: this.title,
38220
+ status: "skipped",
38221
+ detail: "no staleness: block configured (using defaults)",
38222
+ autoFixable: false
38223
+ };
38224
+ }
38225
+ const problems = validateStalenessConfig(content);
38226
+ if (problems.length === 0) {
38227
+ return {
38228
+ id: this.id,
38229
+ category: this.category,
38230
+ title: this.title,
38231
+ status: "pass",
38232
+ detail: "staleness threshold overrides are valid",
38233
+ autoFixable: false
38234
+ };
38235
+ }
38236
+ return {
38237
+ id: this.id,
38238
+ category: this.category,
38239
+ title: this.title,
38240
+ status: "warn",
38241
+ detail: problems.join("; "),
38242
+ affected: problems,
38243
+ remediation: {
38244
+ kind: "manual",
38245
+ 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.",
38246
+ command: null
38247
+ },
38248
+ autoFixable: false
38249
+ };
38250
+ }
38251
+ };
38252
+ var stalenessChecks = [stalenessConfigValid];
38253
+
37651
38254
  // src/utils/doctor/registry.ts
37652
38255
  function allChecks() {
37653
38256
  return [
@@ -37664,7 +38267,8 @@ function allChecks() {
37664
38267
  ...crossAgentChecks,
37665
38268
  ...bundleChecks,
37666
38269
  ...pluginChecks,
37667
- ...deriveConfigChecks
38270
+ ...deriveConfigChecks,
38271
+ ...stalenessChecks
37668
38272
  ];
37669
38273
  }
37670
38274
 
@@ -37750,7 +38354,7 @@ async function readVersion() {
37750
38354
  let dir = dirname25(here);
37751
38355
  for (let i = 0; i < 6; i++) {
37752
38356
  try {
37753
- const raw2 = await readFile61(join21(dir, "package.json"), "utf-8");
38357
+ const raw2 = await readFile63(join21(dir, "package.json"), "utf-8");
37754
38358
  const parsed = JSON.parse(raw2);
37755
38359
  return typeof parsed.version === "string" ? parsed.version : null;
37756
38360
  } catch {
@@ -37847,7 +38451,7 @@ var REQUIRED_WORKSPACE_FIELDS = [
37847
38451
  ];
37848
38452
  var ISO_DATE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
37849
38453
  async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
37850
- const absolute = isAbsolute11(inputPath) ? inputPath : resolve89(cwd, inputPath);
38454
+ const absolute = isAbsolute11(inputPath) ? inputPath : resolve91(cwd, inputPath);
37851
38455
  const errors = [];
37852
38456
  const warnings = [];
37853
38457
  if (!await fileExists(absolute)) {
@@ -37860,7 +38464,7 @@ async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
37860
38464
  }
37861
38465
  let content;
37862
38466
  try {
37863
- content = await readFile62(absolute, "utf-8");
38467
+ content = await readFile64(absolute, "utf-8");
37864
38468
  } catch (err2) {
37865
38469
  return {
37866
38470
  ok: false,
@@ -38227,8 +38831,8 @@ init_assignment_resolver();
38227
38831
  init_frontmatter();
38228
38832
  init_event_emit();
38229
38833
  init_templates();
38230
- import { resolve as resolve90 } from "path";
38231
- import { readFile as readFile63 } from "fs/promises";
38834
+ import { resolve as resolve92 } from "path";
38835
+ import { readFile as readFile65 } from "fs/promises";
38232
38836
  function shortId() {
38233
38837
  return generateId().split("-")[0];
38234
38838
  }
@@ -38259,7 +38863,7 @@ async function commentCommand(target, text, options = {}) {
38259
38863
  if (!isValidSlug(target)) {
38260
38864
  throw new Error(`Invalid assignment slug "${target}".`);
38261
38865
  }
38262
- assignmentDir = resolve90(baseDir, options.project, "assignments", target);
38866
+ assignmentDir = resolve92(baseDir, options.project, "assignments", target);
38263
38867
  assignmentRef = target;
38264
38868
  projectSlug = options.project;
38265
38869
  } else {
@@ -38271,13 +38875,13 @@ async function commentCommand(target, text, options = {}) {
38271
38875
  assignmentRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
38272
38876
  projectSlug = resolved.projectSlug;
38273
38877
  }
38274
- const commentsPath = resolve90(assignmentDir, "comments.md");
38878
+ const commentsPath = resolve92(assignmentDir, "comments.md");
38275
38879
  const timestamp = nowTimestamp();
38276
38880
  const author = options.author ?? process.env.USER ?? "unknown";
38277
38881
  let currentContent;
38278
38882
  let currentCount = 0;
38279
38883
  if (await fileExists(commentsPath)) {
38280
- currentContent = await readFile63(commentsPath, "utf-8");
38884
+ currentContent = await readFile65(commentsPath, "utf-8");
38281
38885
  const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
38282
38886
  if (countMatch) currentCount = parseInt(countMatch[1], 10);
38283
38887
  } else {
@@ -38305,9 +38909,9 @@ ${entry}`;
38305
38909
  }
38306
38910
  await writeFileForce(commentsPath, next);
38307
38911
  try {
38308
- const assignmentMd = resolve90(assignmentDir, "assignment.md");
38912
+ const assignmentMd = resolve92(assignmentDir, "assignment.md");
38309
38913
  if (await fileExists(assignmentMd)) {
38310
- const fm = parseAssignmentFrontmatter(await readFile63(assignmentMd, "utf-8"));
38914
+ const fm = parseAssignmentFrontmatter(await readFile65(assignmentMd, "utf-8"));
38311
38915
  emitEvent({
38312
38916
  assignmentId: fm.id,
38313
38917
  projectSlug,
@@ -38331,170 +38935,15 @@ ${entry}`;
38331
38935
  }
38332
38936
 
38333
38937
  // 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";
38938
+ import { resolve as resolve95, relative as relative4, dirname as dirname26 } from "path";
38939
+ import { copyFile as copyFile3, mkdir as mkdir12, realpath as realpath2, rm as rm14, stat as stat14, writeFile as writeFile15 } from "fs/promises";
38336
38940
  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
38941
  init_paths();
38493
38942
  init_fs();
38494
38943
 
38495
38944
  // src/utils/screencapture.ts
38496
38945
  import { spawn as spawn8 } from "child_process";
38497
- import { mkdtemp as mkdtemp2, rm as rm10, stat as stat11 } from "fs/promises";
38946
+ import { mkdtemp as mkdtemp2, rm as rm10, stat as stat12 } from "fs/promises";
38498
38947
  import { tmpdir as tmpdir3 } from "os";
38499
38948
  import { join as join22 } from "path";
38500
38949
  function argsFor(mode, pngPath) {
@@ -38552,7 +39001,7 @@ async function captureScreenshot(mode) {
38552
39001
  }
38553
39002
  let size = 0;
38554
39003
  try {
38555
- size = (await stat11(pngPath)).size;
39004
+ size = (await stat12(pngPath)).size;
38556
39005
  } catch {
38557
39006
  throw new Error("screencapture exited 0 but produced no image.");
38558
39007
  }
@@ -38568,7 +39017,7 @@ async function captureScreenshot(mode) {
38568
39017
 
38569
39018
  // src/utils/asciinema.ts
38570
39019
  import { spawn as spawn9 } from "child_process";
38571
- import { mkdtemp as mkdtemp3, readFile as readFile65, rm as rm11 } from "fs/promises";
39020
+ import { mkdtemp as mkdtemp3, readFile as readFile66, rm as rm11 } from "fs/promises";
38572
39021
  import { tmpdir as tmpdir4 } from "os";
38573
39022
  import { join as join23 } from "path";
38574
39023
  var SAFE_RE = /^[A-Za-z0-9_@%+=:,./-]+$/;
@@ -38631,7 +39080,7 @@ async function captureAsciinema(opts) {
38631
39080
  }
38632
39081
  throw err2;
38633
39082
  }
38634
- const text = await readFile65(castPath, "utf8").catch(() => null);
39083
+ const text = await readFile66(castPath, "utf8").catch(() => null);
38635
39084
  if (text === null) {
38636
39085
  throw new Error(
38637
39086
  `asciinema produced no cast file at ${castPath} (exit ${exitCode}). Try running 'asciinema rec ${castPath}' directly to diagnose.`
@@ -38659,9 +39108,9 @@ async function captureAsciinema(opts) {
38659
39108
  // src/utils/recording.ts
38660
39109
  init_paths();
38661
39110
  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";
39111
+ 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
39112
  import { tmpdir as tmpdir5 } from "os";
38664
- import { join as join24, resolve as resolve92 } from "path";
39113
+ import { join as join24, resolve as resolve93 } from "path";
38665
39114
  import { setTimeout as sleep } from "timers/promises";
38666
39115
  function sigintPollIntervalMs() {
38667
39116
  const raw2 = process.env.SYNTAUR_RECORDING_POLL_INTERVAL_MS;
@@ -38682,13 +39131,13 @@ function sigtermWaitMs() {
38682
39131
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : 1e3;
38683
39132
  }
38684
39133
  function pidfilePath() {
38685
- return resolve92(syntaurRoot(), "recording.pid");
39134
+ return resolve93(syntaurRoot(), "recording.pid");
38686
39135
  }
38687
39136
  function logPath2() {
38688
- return resolve92(syntaurRoot(), "recording.log");
39137
+ return resolve93(syntaurRoot(), "recording.log");
38689
39138
  }
38690
39139
  function sidecarPath() {
38691
- return resolve92(syntaurRoot(), "recording.json");
39140
+ return resolve93(syntaurRoot(), "recording.json");
38692
39141
  }
38693
39142
  function ffmpegArgs(device, fps, mp4Path) {
38694
39143
  return [
@@ -38733,7 +39182,7 @@ async function acquirePidfile(pidfile) {
38733
39182
  } catch (err2) {
38734
39183
  if (err2.code !== "EEXIST") throw err2;
38735
39184
  if (attempt === 1) throw err2;
38736
- const existing = (await readFile66(pidfile, "utf-8").catch(() => "")).trim();
39185
+ const existing = (await readFile67(pidfile, "utf-8").catch(() => "")).trim();
38737
39186
  if (existing.startsWith(STARTING_SENTINEL_PREFIX)) {
38738
39187
  const parentPidRaw = existing.slice(STARTING_SENTINEL_PREFIX.length);
38739
39188
  const parentPid = Number.parseInt(parentPidRaw, 10);
@@ -38835,7 +39284,7 @@ async function startRecording(input4) {
38835
39284
  logHandle = null;
38836
39285
  if (warmupMs > 0) await sleep(warmupMs);
38837
39286
  if (!await isProcessAlive(pid)) {
38838
- const tail = await readFile66(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
39287
+ const tail = await readFile67(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
38839
39288
  acquiredPid = null;
38840
39289
  throw new Error(
38841
39290
  `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 +39341,7 @@ ${tail}`
38892
39341
  async function stopRecording() {
38893
39342
  const pidfile = pidfilePath();
38894
39343
  const sidecar = sidecarPath();
38895
- const pidRaw = await readFile66(pidfile, "utf-8").catch(() => null);
39344
+ const pidRaw = await readFile67(pidfile, "utf-8").catch(() => null);
38896
39345
  if (pidRaw === null) {
38897
39346
  throw new Error(
38898
39347
  `No active recording found (no pidfile at ${pidfile}). Did you run --start?`
@@ -38902,7 +39351,7 @@ async function stopRecording() {
38902
39351
  if (!Number.isInteger(pid) || pid <= 0) {
38903
39352
  throw new Error(`Pidfile at ${pidfile} is corrupt (got "${pidRaw}").`);
38904
39353
  }
38905
- const sidecarRaw = await readFile66(sidecar, "utf-8").catch(() => null);
39354
+ const sidecarRaw = await readFile67(sidecar, "utf-8").catch(() => null);
38906
39355
  if (sidecarRaw === null) {
38907
39356
  throw new Error(
38908
39357
  `No recording sidecar at ${sidecar}. The recording state is inconsistent \u2014 delete ${pidfile} and re-run --start.`
@@ -38947,7 +39396,7 @@ async function stopRecording() {
38947
39396
  if (alive) {
38948
39397
  throw new Error(`ffmpeg (PID ${pid}) refused to die after SIGKILL`);
38949
39398
  }
38950
- const st = await stat12(sidecarData.mp4Path).catch(() => null);
39399
+ const st = await stat13(sidecarData.mp4Path).catch(() => null);
38951
39400
  if (st === null || st.size === 0) {
38952
39401
  await unlink12(pidfile).catch(() => {
38953
39402
  });
@@ -38967,7 +39416,7 @@ async function stopRecording() {
38967
39416
  // src/db/proof-db.ts
38968
39417
  init_paths();
38969
39418
  import Database6 from "better-sqlite3";
38970
- import { resolve as resolve93 } from "path";
39419
+ import { resolve as resolve94 } from "path";
38971
39420
  var db5 = null;
38972
39421
  var PROOF_SCHEMA_VERSION = "1";
38973
39422
  var SCHEMA_SQL5 = `
@@ -38987,7 +39436,7 @@ CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);
38987
39436
  `;
38988
39437
  function initProofDb(dbPath) {
38989
39438
  if (db5) return db5;
38990
- const finalPath = dbPath ?? resolve93(syntaurRoot(), "syntaur.db");
39439
+ const finalPath = dbPath ?? resolve94(syntaurRoot(), "syntaur.db");
38991
39440
  db5 = new Database6(finalPath);
38992
39441
  db5.pragma("journal_mode = WAL");
38993
39442
  db5.exec(SCHEMA_SQL5);
@@ -39035,7 +39484,7 @@ function listArtifactsByAssignment(assignmentId) {
39035
39484
 
39036
39485
  // src/utils/transcribers/elevenlabs.ts
39037
39486
  import { spawn as spawn11 } from "child_process";
39038
- import { mkdtemp as mkdtemp5, readFile as readFile67, rm as rm13 } from "fs/promises";
39487
+ import { mkdtemp as mkdtemp5, readFile as readFile68, rm as rm13 } from "fs/promises";
39039
39488
  import { tmpdir as tmpdir6 } from "os";
39040
39489
  import { join as join25 } from "path";
39041
39490
  var SCRIBE_URL = "https://api.elevenlabs.io/v1/speech-to-text";
@@ -39099,7 +39548,7 @@ async function extractAudio(videoAbsPath, wavOut) {
39099
39548
  throw new TranscribeFfmpegError(`ffmpeg failed (exit ${result.code}): ${tail}`);
39100
39549
  }
39101
39550
  async function callScribe(wavPath, apiKey, opts) {
39102
- const audio = await readFile67(wavPath);
39551
+ const audio = await readFile68(wavPath);
39103
39552
  const form = new FormData();
39104
39553
  form.set("file", new Blob([new Uint8Array(audio)], { type: "audio/wav" }), "audio.wav");
39105
39554
  form.set("model_id", "scribe_v1");
@@ -39415,7 +39864,7 @@ async function captureCommand(target, options = {}) {
39415
39864
  });
39416
39865
  }
39417
39866
  if (options.file) {
39418
- const expanded = options.file.startsWith("~/") ? resolve94(process.env.HOME ?? "", options.file.slice(2)) : resolve94(options.file);
39867
+ const expanded = options.file.startsWith("~/") ? resolve95(process.env.HOME ?? "", options.file.slice(2)) : resolve95(options.file);
39419
39868
  if (!await fileExists(expanded)) {
39420
39869
  throw new Error(`--file does not exist: ${options.file}`);
39421
39870
  }
@@ -39427,7 +39876,7 @@ async function captureCommand(target, options = {}) {
39427
39876
  `--file is unreadable: ${options.file} (${e instanceof Error ? e.message : String(e)})`
39428
39877
  );
39429
39878
  }
39430
- const st = await stat13(real);
39879
+ const st = await stat14(real);
39431
39880
  if (!st.isFile()) {
39432
39881
  throw new Error(`--file is not a regular file: ${options.file}`);
39433
39882
  }
@@ -39456,7 +39905,7 @@ async function captureCommand(target, options = {}) {
39456
39905
  }
39457
39906
  initProofDb();
39458
39907
  const subdir = criterionIndex === null ? "untagged" : String(criterionIndex);
39459
- const destDir = resolve94(proofDir(resolved.assignmentDir), subdir);
39908
+ const destDir = resolve95(proofDir(resolved.assignmentDir), subdir);
39460
39909
  if (resolvedSource) await mkdir12(destDir, { recursive: true });
39461
39910
  const ext = resolvedSource ? extensionForKind(kind) : null;
39462
39911
  let id = null;
@@ -39465,7 +39914,7 @@ async function captureCommand(target, options = {}) {
39465
39914
  let lastErr = null;
39466
39915
  for (let attempt = 0; attempt < MAX_ID_RETRIES; attempt += 1) {
39467
39916
  const candidate = generateArtifactId();
39468
- const candidateAbsPath = resolvedSource && ext ? resolve94(destDir, `${candidate}.${ext}`) : null;
39917
+ const candidateAbsPath = resolvedSource && ext ? resolve95(destDir, `${candidate}.${ext}`) : null;
39469
39918
  const candidateRel = candidateAbsPath ? relative4(resolved.assignmentDir, candidateAbsPath) : null;
39470
39919
  try {
39471
39920
  insertArtifact({
@@ -39509,7 +39958,7 @@ async function captureCommand(target, options = {}) {
39509
39958
  }
39510
39959
  }
39511
39960
  if (options.transcribe && kind === "video" && absPath && id) {
39512
- const sidecarPath2 = resolve94(destDir, `${id}.transcript.md`);
39961
+ const sidecarPath2 = resolve95(destDir, `${id}.transcript.md`);
39513
39962
  if (existsSync7(sidecarPath2)) {
39514
39963
  console.warn(
39515
39964
  `transcript: ${sidecarPath2} already exists, skipping (delete to re-transcribe)`
@@ -39541,7 +39990,7 @@ async function captureCommand(target, options = {}) {
39541
39990
  const tagSuffix = criterionIndex === null ? "untagged" : `criterion ${criterionIndex}`;
39542
39991
  console.log(`Captured artifact ${id} (${kind}) for ${ref} \u2014 ${tagSuffix}.`);
39543
39992
  if (relativeFilePath) {
39544
- console.log(` file: ${resolve94(resolved.assignmentDir, relativeFilePath)}`);
39993
+ console.log(` file: ${resolve95(resolved.assignmentDir, relativeFilePath)}`);
39545
39994
  }
39546
39995
  } catch (err2) {
39547
39996
  if (options.stop && kind === "video" && shelloutCleanup && resolvedSource) {
@@ -39564,8 +40013,8 @@ async function captureCommand(target, options = {}) {
39564
40013
 
39565
40014
  // src/commands/proof.ts
39566
40015
  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";
40016
+ import { readFile as readFile69, writeFile as writeFile16, rename as rename10, stat as stat15 } from "fs/promises";
40017
+ import { resolve as resolve96, relative as relative5, isAbsolute as isAbsolute12, dirname as dirname27 } from "path";
39569
40018
  import { randomBytes as randomBytes4 } from "crypto";
39570
40019
 
39571
40020
  // src/utils/acceptance-criteria-parse.ts
@@ -39805,11 +40254,11 @@ function renderProofHtml(params2, inlineFiles = /* @__PURE__ */ new Map(), trans
39805
40254
 
39806
40255
  // src/commands/proof.ts
39807
40256
  async function readAssignmentMeta(assignmentDir) {
39808
- const path = resolve95(assignmentDir, "assignment.md");
40257
+ const path = resolve96(assignmentDir, "assignment.md");
39809
40258
  if (!await fileExists(path)) {
39810
40259
  return { title: "", body: "" };
39811
40260
  }
39812
- const content = await readFile68(path, "utf-8");
40261
+ const content = await readFile69(path, "utf-8");
39813
40262
  const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
39814
40263
  let title = "";
39815
40264
  if (fmMatch) {
@@ -39856,7 +40305,7 @@ async function loadInlineFiles(rows, assignmentDir) {
39856
40305
  for (const r of rows) {
39857
40306
  if (!r.file_path) continue;
39858
40307
  if (r.kind !== "http" && r.kind !== "text") continue;
39859
- const abs = resolve95(assignmentDir, r.file_path);
40308
+ const abs = resolve96(assignmentDir, r.file_path);
39860
40309
  if (!isWithin(proofRoot, abs)) {
39861
40310
  out.set(r.file_path, null);
39862
40311
  continue;
@@ -39865,13 +40314,13 @@ async function loadInlineFiles(rows, assignmentDir) {
39865
40314
  out.set(r.file_path, null);
39866
40315
  continue;
39867
40316
  }
39868
- const st = await stat14(abs);
40317
+ const st = await stat15(abs);
39869
40318
  if (st.size > INLINE_TEXT_LIMIT_BYTES) {
39870
40319
  out.set(r.file_path, null);
39871
40320
  continue;
39872
40321
  }
39873
40322
  try {
39874
- out.set(r.file_path, await readFile68(abs, "utf-8"));
40323
+ out.set(r.file_path, await readFile69(abs, "utf-8"));
39875
40324
  } catch {
39876
40325
  out.set(r.file_path, null);
39877
40326
  }
@@ -39883,14 +40332,14 @@ async function loadTranscriptSidecars(rows, assignmentDir) {
39883
40332
  const proofRoot = proofDir(assignmentDir);
39884
40333
  for (const r of rows) {
39885
40334
  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`);
40335
+ const videoAbs = resolve96(assignmentDir, r.file_path);
40336
+ const sidecar = resolve96(dirname27(videoAbs), `${r.id}.transcript.md`);
39888
40337
  if (!isWithin(proofRoot, sidecar)) continue;
39889
40338
  if (!await fileExists(sidecar)) continue;
39890
- const st = await stat14(sidecar);
40339
+ const st = await stat15(sidecar);
39891
40340
  if (st.size > INLINE_TEXT_LIMIT_BYTES) continue;
39892
40341
  try {
39893
- out.set(r.id, await readFile68(sidecar, "utf-8"));
40342
+ out.set(r.id, await readFile69(sidecar, "utf-8"));
39894
40343
  } catch {
39895
40344
  }
39896
40345
  }
@@ -39924,8 +40373,8 @@ async function proofBuildCommand(target, options = {}) {
39924
40373
  };
39925
40374
  const md = renderProofMarkdown(renderParams);
39926
40375
  const html = renderProofHtml(renderParams, inlineFiles, transcriptSidecars);
39927
- const mdPath = resolve95(resolved.assignmentDir, "proof.md");
39928
- const htmlPath = resolve95(resolved.assignmentDir, "proof.html");
40376
+ const mdPath = resolve96(resolved.assignmentDir, "proof.md");
40377
+ const htmlPath = resolve96(resolved.assignmentDir, "proof.html");
39929
40378
  await atomicWrite(mdPath, md);
39930
40379
  await atomicWrite(htmlPath, html);
39931
40380
  console.log(`Wrote ${htmlPath}`);
@@ -40468,7 +40917,7 @@ function isScheduledSessionLive(sessionId, launchPid) {
40468
40917
  import { execFileSync as execFileSync5 } from "child_process";
40469
40918
  import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, rmSync as rmSync2, realpathSync as realpathSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
40470
40919
  import { homedir as homedir16, userInfo } from "os";
40471
- import { dirname as dirname28, join as join26, resolve as resolve96 } from "path";
40920
+ import { dirname as dirname28, join as join26, resolve as resolve97 } from "path";
40472
40921
  var LAUNCH_AGENT_LABEL = "com.syntaur.schedule.tick";
40473
40922
  var LaunchAgentRefusalError = class extends Error {
40474
40923
  constructor(message) {
@@ -40522,7 +40971,7 @@ function absolutize(p) {
40522
40971
  try {
40523
40972
  return realpathSync4(p);
40524
40973
  } catch {
40525
- return resolve96(p);
40974
+ return resolve97(p);
40526
40975
  }
40527
40976
  }
40528
40977
  function defaultRun(command, args) {
@@ -40583,7 +41032,7 @@ function uninstallLaunchAgent(deps = {}) {
40583
41032
  // src/commands/schedule.ts
40584
41033
  init_timestamp();
40585
41034
  var DURATION_REGEX2 = /^(\d+)\s*(s|m|h|d)?$/i;
40586
- function parseDurationMs(input4) {
41035
+ function parseDurationMs2(input4) {
40587
41036
  const m = DURATION_REGEX2.exec(input4.trim());
40588
41037
  if (!m) throw new Error(`invalid duration "${input4}" \u2014 use e.g. 30s, 5m, 2h, 1d`);
40589
41038
  const n = Number.parseInt(m[1], 10);
@@ -40604,7 +41053,7 @@ function validateTerminalChoice(value) {
40604
41053
  function buildTrigger(opts) {
40605
41054
  const chosen = [];
40606
41055
  if (opts.at) chosen.push({ kind: "at", at: opts.at });
40607
- if (opts.in) chosen.push({ kind: "in", durationMs: parseDurationMs(opts.in), anchorIso: nowTimestamp() });
41056
+ if (opts.in) chosen.push({ kind: "in", durationMs: parseDurationMs2(opts.in), anchorIso: nowTimestamp() });
40608
41057
  if (opts.cron) {
40609
41058
  try {
40610
41059
  new Cron2(opts.cron).nextRun();
@@ -40672,7 +41121,7 @@ scheduleCommand.command("create").description("Create a scheduled job").required
40672
41121
  const unattended = !opts.interactive;
40673
41122
  if (unattended) assertUnattendedTerminalSupported(terminal);
40674
41123
  const limits = defaultLimits();
40675
- if (opts.maxRuntime) limits.maxRuntimeMs = parseDurationMs(opts.maxRuntime);
41124
+ if (opts.maxRuntime) limits.maxRuntimeMs = parseDurationMs2(opts.maxRuntime);
40676
41125
  if (opts.maxLaunchesPerDay) {
40677
41126
  const n = Number(opts.maxLaunchesPerDay);
40678
41127
  if (!Number.isInteger(n) || n <= 0) {
@@ -40680,7 +41129,7 @@ scheduleCommand.command("create").description("Create a scheduled job").required
40680
41129
  }
40681
41130
  limits.maxLaunchesPerDay = n;
40682
41131
  }
40683
- if (opts.cooldown) limits.cooldownMs = parseDurationMs(opts.cooldown);
41132
+ if (opts.cooldown) limits.cooldownMs = parseDurationMs2(opts.cooldown);
40684
41133
  const now = nowTimestamp();
40685
41134
  const job = {
40686
41135
  id: newJobId(),
@@ -40898,8 +41347,8 @@ init_slug();
40898
41347
  init_timestamp();
40899
41348
  init_assignment_resolver();
40900
41349
  init_assignment_todos();
40901
- import { resolve as resolve97 } from "path";
40902
- import { readFile as readFile69 } from "fs/promises";
41350
+ import { resolve as resolve98 } from "path";
41351
+ import { readFile as readFile70 } from "fs/promises";
40903
41352
  async function requestCommand(target, text, options = {}) {
40904
41353
  if (!text || !text.trim()) {
40905
41354
  throw new Error("Request text cannot be empty.");
@@ -40915,7 +41364,7 @@ async function requestCommand(target, text, options = {}) {
40915
41364
  if (!isValidSlug(target)) {
40916
41365
  throw new Error(`Invalid assignment slug "${target}".`);
40917
41366
  }
40918
- assignmentDir = resolve97(baseDir, options.project, "assignments", target);
41367
+ assignmentDir = resolve98(baseDir, options.project, "assignments", target);
40919
41368
  targetRef = target;
40920
41369
  } else {
40921
41370
  const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
@@ -40925,12 +41374,12 @@ async function requestCommand(target, text, options = {}) {
40925
41374
  assignmentDir = resolved.assignmentDir;
40926
41375
  targetRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
40927
41376
  }
40928
- const assignmentMdPath2 = resolve97(assignmentDir, "assignment.md");
41377
+ const assignmentMdPath2 = resolve98(assignmentDir, "assignment.md");
40929
41378
  if (!await fileExists(assignmentMdPath2)) {
40930
41379
  throw new Error(`assignment.md not found at ${assignmentMdPath2}`);
40931
41380
  }
40932
41381
  const source = options.from ?? process.env.SYNTAUR_ASSIGNMENT ?? "unknown";
40933
- let content = await readFile69(assignmentMdPath2, "utf-8");
41382
+ let content = await readFile70(assignmentMdPath2, "utf-8");
40934
41383
  content = appendTodosToAssignmentBody(content, [
40935
41384
  { description: `${text.trim()} (from: ${source})` }
40936
41385
  ]);
@@ -40943,14 +41392,15 @@ async function requestCommand(target, text, options = {}) {
40943
41392
  init_fs();
40944
41393
  init_paths();
40945
41394
  init_config2();
41395
+ init_recompute();
40946
41396
  import { Command as Command10 } from "commander";
40947
- import { readFile as readFile70, readdir as readdir34 } from "fs/promises";
40948
- import { resolve as resolve98 } from "path";
41397
+ import { readFile as readFile71, readdir as readdir34 } from "fs/promises";
41398
+ import { resolve as resolve99 } from "path";
40949
41399
  async function readContextAssignmentDir(cwd) {
40950
- const path = resolve98(cwd, ".syntaur", "context.json");
41400
+ const path = resolve99(cwd, ".syntaur", "context.json");
40951
41401
  if (!await fileExists(path)) return null;
40952
41402
  try {
40953
- const raw2 = await readFile70(path, "utf-8");
41403
+ const raw2 = await readFile71(path, "utf-8");
40954
41404
  const ctx = JSON.parse(raw2);
40955
41405
  if (typeof ctx.assignmentDir === "string" && ctx.assignmentDir.length > 0) {
40956
41406
  return ctx.assignmentDir;
@@ -40964,9 +41414,9 @@ async function resolveAssignmentDir(opts) {
40964
41414
  const cwd = opts.cwd ?? process.cwd();
40965
41415
  if (opts.assignment) {
40966
41416
  if (opts.project) {
40967
- return resolve98((await readConfig()).defaultProjectDir, opts.project, "assignments", opts.assignment);
41417
+ return resolve99((await readConfig()).defaultProjectDir, opts.project, "assignments", opts.assignment);
40968
41418
  }
40969
- return resolve98(assignmentsDir(), opts.assignment);
41419
+ return resolve99(assignmentsDir(), opts.assignment);
40970
41420
  }
40971
41421
  const fromCtx = await readContextAssignmentDir(cwd);
40972
41422
  if (fromCtx) return fromCtx;
@@ -41162,17 +41612,17 @@ async function runPlanCreate(options) {
41162
41612
  if (!await fileExists(assignmentDir)) {
41163
41613
  throw new Error(`Assignment directory does not exist: ${assignmentDir}`);
41164
41614
  }
41165
- const assignmentMdPath2 = resolve98(assignmentDir, "assignment.md");
41615
+ const assignmentMdPath2 = resolve99(assignmentDir, "assignment.md");
41166
41616
  if (!await fileExists(assignmentMdPath2)) {
41167
41617
  throw new Error(`Missing assignment.md at: ${assignmentMdPath2}`);
41168
41618
  }
41169
- const planPath = resolve98(assignmentDir, "plan.md");
41619
+ const planPath = resolve99(assignmentDir, "plan.md");
41170
41620
  if (await fileExists(planPath) && !options.force) {
41171
41621
  throw new Error(
41172
41622
  "plan.md already exists. Use --force to overwrite, or `syntaur plan version` to create the next version."
41173
41623
  );
41174
41624
  }
41175
- const assignmentMd = await readFile70(assignmentMdPath2, "utf-8");
41625
+ const assignmentMd = await readFile71(assignmentMdPath2, "utf-8");
41176
41626
  const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
41177
41627
  const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
41178
41628
  await writeFileForce(planPath, buildInitialPlanStub(slug));
@@ -41186,13 +41636,14 @@ async function runPlanCreate(options) {
41186
41636
  }
41187
41637
  console.log(`Created ${planPath}`);
41188
41638
  if (appended > 0) console.log(`Appended ${appended} plan todo(s) to assignment.md.`);
41639
+ await recomputeAssignmentDir(assignmentDir, "plan-create", null);
41189
41640
  }
41190
41641
  async function runPlanVersion(options) {
41191
41642
  const assignmentDir = await resolveAssignmentDir(options);
41192
41643
  if (!await fileExists(assignmentDir)) {
41193
41644
  throw new Error(`Assignment directory does not exist: ${assignmentDir}`);
41194
41645
  }
41195
- const assignmentMdPath2 = resolve98(assignmentDir, "assignment.md");
41646
+ const assignmentMdPath2 = resolve99(assignmentDir, "assignment.md");
41196
41647
  if (!await fileExists(assignmentMdPath2)) {
41197
41648
  throw new Error(`Missing assignment.md at: ${assignmentMdPath2}`);
41198
41649
  }
@@ -41204,15 +41655,15 @@ async function runPlanVersion(options) {
41204
41655
  }
41205
41656
  const current = planFiles[planFiles.length - 1];
41206
41657
  const next = nextPlanFileName(current.version);
41207
- const newPath = resolve98(assignmentDir, next.fileName);
41658
+ const newPath = resolve99(assignmentDir, next.fileName);
41208
41659
  if (await fileExists(newPath) && !options.force) {
41209
41660
  throw new Error(`${next.fileName} already exists. Use --force to overwrite.`);
41210
41661
  }
41211
- const assignmentMd = await readFile70(assignmentMdPath2, "utf-8");
41662
+ const assignmentMd = await readFile71(assignmentMdPath2, "utf-8");
41212
41663
  const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
41213
41664
  const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
41214
- const oldPlanPath = resolve98(assignmentDir, current.fileName);
41215
- const oldPlanContent = await readFile70(oldPlanPath, "utf-8");
41665
+ const oldPlanPath = resolve99(assignmentDir, current.fileName);
41666
+ const oldPlanContent = await readFile71(oldPlanPath, "utf-8");
41216
41667
  const oldBody = oldPlanContent.replace(/^---[\s\S]*?\n---\n?/, "");
41217
41668
  const carriedTodos = extractUncheckedTodos(oldBody);
41218
41669
  const stub = buildNewPlanStub({
@@ -41233,6 +41684,7 @@ async function runPlanVersion(options) {
41233
41684
  );
41234
41685
  console.log(`Path: ${newPath}`);
41235
41686
  console.log(`Carried forward: ${carriedTodos.length} unchecked task(s).`);
41687
+ await recomputeAssignmentDir(assignmentDir, "plan-version", null);
41236
41688
  }
41237
41689
  var planCommand = new Command10("plan").description("Manage plan files for the active assignment");
41238
41690
  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 +41734,28 @@ init_cwd();
41282
41734
  init_session_db();
41283
41735
  init_agent_sessions();
41284
41736
  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";
41737
+ import { readFile as readFile72, readdir as readdir35, stat as stat16 } from "fs/promises";
41738
+ import { resolve as resolve100 } from "path";
41287
41739
  async function readContext(cwd) {
41288
- const path = resolve99(cwd, ".syntaur", "context.json");
41740
+ const path = resolve100(cwd, ".syntaur", "context.json");
41289
41741
  if (!await fileExists(path)) return null;
41290
41742
  try {
41291
- const raw2 = await readFile71(path, "utf-8");
41743
+ const raw2 = await readFile72(path, "utf-8");
41292
41744
  return JSON.parse(raw2);
41293
41745
  } catch {
41294
41746
  return null;
41295
41747
  }
41296
41748
  }
41297
41749
  async function findLatestSessionSummary(assignmentDir) {
41298
- const sessionsRoot = resolve99(assignmentDir, "sessions");
41750
+ const sessionsRoot = resolve100(assignmentDir, "sessions");
41299
41751
  if (!await fileExists(sessionsRoot)) return null;
41300
41752
  const entries = await readdir35(sessionsRoot, { withFileTypes: true });
41301
41753
  let best = null;
41302
41754
  for (const entry of entries) {
41303
41755
  if (!entry.isDirectory()) continue;
41304
- const summaryPath = resolve99(sessionsRoot, entry.name, "summary.md");
41756
+ const summaryPath = resolve100(sessionsRoot, entry.name, "summary.md");
41305
41757
  if (!await fileExists(summaryPath)) continue;
41306
- const st = await stat15(summaryPath);
41758
+ const st = await stat16(summaryPath);
41307
41759
  if (best === null || st.mtime.getTime() > best.mtime.getTime()) {
41308
41760
  best = { sessionId: entry.name, path: summaryPath, mtime: st.mtime };
41309
41761
  }
@@ -41311,9 +41763,9 @@ async function findLatestSessionSummary(assignmentDir) {
41311
41763
  return best;
41312
41764
  }
41313
41765
  async function findOpenHandoff(assignmentDir) {
41314
- const handoffPath = resolve99(assignmentDir, "handoff.md");
41766
+ const handoffPath = resolve100(assignmentDir, "handoff.md");
41315
41767
  if (!await fileExists(handoffPath)) return null;
41316
- const content = await readFile71(handoffPath, "utf-8");
41768
+ const content = await readFile72(handoffPath, "utf-8");
41317
41769
  const body = content.replace(/^---[\s\S]*?\n---\n?/, "").trim();
41318
41770
  if (body.length === 0) return null;
41319
41771
  if (/^<!--[\s\S]*-->$/.test(body)) return null;
@@ -41408,7 +41860,7 @@ async function resolveSaveTarget(options, cwd) {
41408
41860
  let slug;
41409
41861
  const ctx = await readContext(cwd);
41410
41862
  if (options.assignment) {
41411
- assignmentDir = options.project ? resolve99((await readConfig()).defaultProjectDir, options.project, "assignments", options.assignment) : resolve99(assignmentsDir(), options.assignment);
41863
+ assignmentDir = options.project ? resolve100((await readConfig()).defaultProjectDir, options.project, "assignments", options.assignment) : resolve100(assignmentsDir(), options.assignment);
41412
41864
  slug = options.assignment;
41413
41865
  } else {
41414
41866
  if (!ctx?.assignmentDir) {
@@ -41465,21 +41917,21 @@ function extractCreated(content) {
41465
41917
  }
41466
41918
  async function runSessionSave(options, cwd = process.cwd(), body) {
41467
41919
  const { assignmentDir, slug, sessionId } = await resolveSaveTarget(options, cwd);
41468
- if (!await fileExists(resolve99(assignmentDir, "assignment.md"))) {
41920
+ if (!await fileExists(resolve100(assignmentDir, "assignment.md"))) {
41469
41921
  throw new Error(`No assignment found at ${assignmentDir} (missing assignment.md).`);
41470
41922
  }
41471
- const sessionDir = resolve99(assignmentDir, "sessions", sessionId);
41472
- const summaryPath = resolve99(sessionDir, "summary.md");
41923
+ const sessionDir = resolve100(assignmentDir, "sessions", sessionId);
41924
+ const summaryPath = resolve100(sessionDir, "summary.md");
41473
41925
  const now = nowTimestamp();
41474
41926
  let created = now;
41475
41927
  if (await fileExists(summaryPath)) {
41476
- const existing = await readFile71(summaryPath, "utf-8");
41928
+ const existing = await readFile72(summaryPath, "utf-8");
41477
41929
  created = extractCreated(existing) ?? now;
41478
41930
  }
41479
41931
  let sectionBody2 = body;
41480
41932
  if (sectionBody2 === void 0) {
41481
41933
  if (options.fromFile) {
41482
- sectionBody2 = await readFile71(resolve99(cwd, options.fromFile), "utf-8");
41934
+ sectionBody2 = await readFile72(resolve100(cwd, options.fromFile), "utf-8");
41483
41935
  } else {
41484
41936
  sectionBody2 = await readStdin();
41485
41937
  }
@@ -41515,7 +41967,7 @@ async function runSessionRegister(rawStdin, options = {}, deps = {}) {
41515
41967
  if (!isSafeSessionId(sessionId) || !cwd) return result;
41516
41968
  result.sessionId = sessionId;
41517
41969
  const transcriptPath = payload.transcript_path ?? "";
41518
- const contextPath = resolve99(cwd, ".syntaur", "context.json");
41970
+ const contextPath = resolve100(cwd, ".syntaur", "context.json");
41519
41971
  const hasContextFile = await fileExists(contextPath);
41520
41972
  const ctx = hasContextFile ? await readContext(cwd) : null;
41521
41973
  if (ctx) {
@@ -41661,8 +42113,8 @@ sessionCommand.command("resolve-id").description(
41661
42113
  // src/commands/worktree.ts
41662
42114
  init_git_worktree();
41663
42115
  import { Command as Command12 } from "commander";
41664
- import { readFile as readFile72 } from "fs/promises";
41665
- import { resolve as resolve101 } from "path";
42116
+ import { readFile as readFile73 } from "fs/promises";
42117
+ import { resolve as resolve102 } from "path";
41666
42118
  init_fs();
41667
42119
  init_paths();
41668
42120
  init_config2();
@@ -41671,12 +42123,12 @@ init_state_machine();
41671
42123
 
41672
42124
  // src/utils/path-canon.ts
41673
42125
  import { realpathSync as realpathSync5 } from "fs";
41674
- import { resolve as resolve100 } from "path";
42126
+ import { resolve as resolve101 } from "path";
41675
42127
  function canonicalPath(p) {
41676
42128
  try {
41677
- return realpathSync5(resolve100(p));
42129
+ return realpathSync5(resolve101(p));
41678
42130
  } catch {
41679
- return resolve100(p).replace(/\/+$/, "");
42131
+ return resolve101(p).replace(/\/+$/, "");
41680
42132
  }
41681
42133
  }
41682
42134
 
@@ -41704,10 +42156,10 @@ function countSessionsByPath(dbPath, paths) {
41704
42156
  init_timestamp();
41705
42157
  init_frontmatter();
41706
42158
  async function readContext2(cwd) {
41707
- const path = resolve101(cwd, ".syntaur", "context.json");
42159
+ const path = resolve102(cwd, ".syntaur", "context.json");
41708
42160
  if (!await fileExists(path)) return null;
41709
42161
  try {
41710
- return JSON.parse(await readFile72(path, "utf-8"));
42162
+ return JSON.parse(await readFile73(path, "utf-8"));
41711
42163
  } catch {
41712
42164
  return null;
41713
42165
  }
@@ -41716,12 +42168,12 @@ async function resolveAssignmentPath2(opts) {
41716
42168
  if (opts.assignment) {
41717
42169
  if (opts.project) {
41718
42170
  const projectsDir2 = (await readConfig()).defaultProjectDir;
41719
- return resolve101(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
42171
+ return resolve102(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
41720
42172
  }
41721
- return resolve101(assignmentsDir(), opts.assignment, "assignment.md");
42173
+ return resolve102(assignmentsDir(), opts.assignment, "assignment.md");
41722
42174
  }
41723
42175
  const ctx = await readContext2(opts.cwd);
41724
- if (ctx?.assignmentDir) return resolve101(ctx.assignmentDir, "assignment.md");
42176
+ if (ctx?.assignmentDir) return resolve102(ctx.assignmentDir, "assignment.md");
41725
42177
  throw new Error(
41726
42178
  "No active assignment. Pass --assignment <slug> [--project <slug>] or run from a workspace with .syntaur/context.json."
41727
42179
  );
@@ -41732,7 +42184,7 @@ async function runWorktreeCreate(options, cwd = process.cwd()) {
41732
42184
  }
41733
42185
  const repository = options.repository ?? cwd;
41734
42186
  const parentBranch = options.parentBranch ?? "main";
41735
- const worktreePath = options.worktreePath ?? resolve101(repository, ".worktrees", options.branch);
42187
+ const worktreePath = options.worktreePath ?? resolve102(repository, ".worktrees", options.branch);
41736
42188
  const assignmentPath = await resolveAssignmentPath2({
41737
42189
  assignment: options.assignment,
41738
42190
  project: options.project,
@@ -41762,7 +42214,7 @@ async function runWorktreeRemove(options, cwd = process.cwd()) {
41762
42214
  if (!await fileExists(assignmentPath)) {
41763
42215
  throw new Error(`Assignment file not found: ${assignmentPath}`);
41764
42216
  }
41765
- const original = await readFile72(assignmentPath, "utf-8");
42217
+ const original = await readFile73(assignmentPath, "utf-8");
41766
42218
  const fm = parseAssignmentFrontmatter(original);
41767
42219
  const repository = options.repository ?? fm.workspace.repository ?? void 0;
41768
42220
  const worktreePath = fm.workspace.worktreePath ?? void 0;
@@ -41828,7 +42280,7 @@ async function runWorktreeGc(options, cwd = process.cwd()) {
41828
42280
  const owners = /* @__PURE__ */ new Map();
41829
42281
  for (const entry of walk.withAssignmentMd) {
41830
42282
  try {
41831
- const content = await readFile72(resolve101(entry.assignmentDir, "assignment.md"), "utf-8");
42283
+ const content = await readFile73(resolve102(entry.assignmentDir, "assignment.md"), "utf-8");
41832
42284
  const fm = parseAssignmentFrontmatter(content);
41833
42285
  const wp = fm.workspace?.worktreePath;
41834
42286
  if (!wp) continue;
@@ -41850,7 +42302,7 @@ async function runWorktreeGc(options, cwd = process.cwd()) {
41850
42302
  const repoTop = rt ? canonicalPath(rt) : null;
41851
42303
  const cwdTop = ct ? canonicalPath(ct) : null;
41852
42304
  const mainPath = entries.length > 0 ? canonicalPath(entries[0].worktreePath) : null;
41853
- const dbPath = resolve101(syntaurRoot(), "syntaur.db");
42305
+ const dbPath = resolve102(syntaurRoot(), "syntaur.db");
41854
42306
  const candidates = [];
41855
42307
  for (const entry of entries) {
41856
42308
  const canon = canonicalPath(entry.worktreePath);
@@ -42069,8 +42521,8 @@ worktreeCommand.command("gc").description(
42069
42521
  // src/commands/open.ts
42070
42522
  init_fs();
42071
42523
  import { Command as Command13 } from "commander";
42072
- import { readFile as readFile73 } from "fs/promises";
42073
- import { resolve as resolve102 } from "path";
42524
+ import { readFile as readFile74 } from "fs/promises";
42525
+ import { resolve as resolve103 } from "path";
42074
42526
  init_frontmatter();
42075
42527
  init_config2();
42076
42528
  init_paths();
@@ -42124,13 +42576,13 @@ function openInTerminal(path, config) {
42124
42576
  // src/commands/open.ts
42125
42577
  async function runOpen(assignmentArg, options) {
42126
42578
  const resolved = options.id ? await resolveAssignmentTarget(options.id, {}) : await resolveAssignmentTarget(assignmentArg, { project: options.project });
42127
- const assignmentPath = resolve102(resolved.assignmentDir, "assignment.md");
42579
+ const assignmentPath = resolve103(resolved.assignmentDir, "assignment.md");
42128
42580
  if (!await fileExists(assignmentPath)) {
42129
42581
  throw new SyntaurError(`Assignment file not found: ${assignmentPath}`, {
42130
42582
  remediation: "check the assignment slug or --id"
42131
42583
  });
42132
42584
  }
42133
- const fm = parseAssignmentFrontmatter(await readFile73(assignmentPath, "utf-8"));
42585
+ const fm = parseAssignmentFrontmatter(await readFile74(assignmentPath, "utf-8"));
42134
42586
  const worktreePath = fm.workspace?.worktreePath;
42135
42587
  if (!worktreePath) {
42136
42588
  throw new SyntaurError("No worktree recorded for this assignment.", {
@@ -42195,14 +42647,14 @@ init_config2();
42195
42647
  init_fs();
42196
42648
  init_slug();
42197
42649
  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";
42650
+ import { resolve as resolve105 } from "path";
42651
+ import { readFile as readFile76, readdir as readdir37, rm as rm15 } from "fs/promises";
42200
42652
 
42201
42653
  // src/utils/project-indexes.ts
42202
42654
  init_parser();
42203
42655
  init_fs();
42204
- import { readdir as readdir36, readFile as readFile74 } from "fs/promises";
42205
- import { resolve as resolve103 } from "path";
42656
+ import { readdir as readdir36, readFile as readFile75 } from "fs/promises";
42657
+ import { resolve as resolve104 } from "path";
42206
42658
  function nowIso3() {
42207
42659
  return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
42208
42660
  }
@@ -42221,7 +42673,7 @@ function joinList(items) {
42221
42673
  return items.length === 0 ? "\u2014" : items.map(escapeCell).join(", ");
42222
42674
  }
42223
42675
  async function rebuildResourcesIndex(projectDir) {
42224
- const dir = resolve103(projectDir, "resources");
42676
+ const dir = resolve104(projectDir, "resources");
42225
42677
  await ensureDir(dir);
42226
42678
  const files = await listSlugFiles(dir);
42227
42679
  const slug = readProjectSlug(projectDir);
@@ -42237,7 +42689,7 @@ async function rebuildResourcesIndex(projectDir) {
42237
42689
  lines.push("| Name | Category | Source | Related Assignments | Updated |");
42238
42690
  lines.push("|------|----------|--------|---------------------|---------|");
42239
42691
  for (const fileName of files) {
42240
- const content = await readFile74(resolve103(dir, fileName), "utf-8");
42692
+ const content = await readFile75(resolve104(dir, fileName), "utf-8");
42241
42693
  const parsed = parseResource(content);
42242
42694
  const slugBase = fileName.replace(/\.md$/, "");
42243
42695
  const name = parsed.name || slugBase;
@@ -42247,12 +42699,12 @@ async function rebuildResourcesIndex(projectDir) {
42247
42699
  );
42248
42700
  }
42249
42701
  lines.push("");
42250
- const indexPath = resolve103(dir, "_index.md");
42702
+ const indexPath = resolve104(dir, "_index.md");
42251
42703
  await writeFileForce(indexPath, lines.join("\n"));
42252
42704
  return { total: files.length, path: indexPath };
42253
42705
  }
42254
42706
  async function rebuildMemoriesIndex(projectDir) {
42255
- const dir = resolve103(projectDir, "memories");
42707
+ const dir = resolve104(projectDir, "memories");
42256
42708
  await ensureDir(dir);
42257
42709
  const files = await listSlugFiles(dir);
42258
42710
  const slug = readProjectSlug(projectDir);
@@ -42268,7 +42720,7 @@ async function rebuildMemoriesIndex(projectDir) {
42268
42720
  lines.push("| Name | Source | Scope | Source Assignment | Updated |");
42269
42721
  lines.push("|------|--------|-------|-------------------|---------|");
42270
42722
  for (const fileName of files) {
42271
- const content = await readFile74(resolve103(dir, fileName), "utf-8");
42723
+ const content = await readFile75(resolve104(dir, fileName), "utf-8");
42272
42724
  const parsed = parseMemory(content);
42273
42725
  const slugBase = fileName.replace(/\.md$/, "");
42274
42726
  const name = parsed.name || slugBase;
@@ -42278,7 +42730,7 @@ async function rebuildMemoriesIndex(projectDir) {
42278
42730
  );
42279
42731
  }
42280
42732
  lines.push("");
42281
- const indexPath = resolve103(dir, "_index.md");
42733
+ const indexPath = resolve104(dir, "_index.md");
42282
42734
  await writeFileForce(indexPath, lines.join("\n"));
42283
42735
  return { total: files.length, path: indexPath };
42284
42736
  }
@@ -42342,8 +42794,8 @@ ${opts.body ?? "<!-- Add notes about this resource here. -->"}
42342
42794
  }
42343
42795
  async function resolveProjectDir(project) {
42344
42796
  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"))) {
42797
+ const projectDir = resolve105((await readConfig()).defaultProjectDir, project);
42798
+ if (!await fileExists(resolve105(projectDir, "project.md"))) {
42347
42799
  throw new Error(`Project "${project}" not found at ${projectDir}.`);
42348
42800
  }
42349
42801
  return projectDir;
@@ -42356,7 +42808,7 @@ async function runResourceAdd(options) {
42356
42808
  if (!isValidSlug(slug)) {
42357
42809
  throw new Error(`Invalid resource slug: "${slug}".`);
42358
42810
  }
42359
- const filePath = resolve104(projectDir, "resources", `${slug}.md`);
42811
+ const filePath = resolve105(projectDir, "resources", `${slug}.md`);
42360
42812
  if (await fileExists(filePath) && !options.force) {
42361
42813
  throw new Error(
42362
42814
  `Resource "${slug}" already exists at ${filePath}. Use --force to overwrite.`
@@ -42373,7 +42825,7 @@ async function runResourceAdd(options) {
42373
42825
  return { filePath, indexPath, total };
42374
42826
  }
42375
42827
  async function listResourceSlugs(projectDir) {
42376
- const dir = resolve104(projectDir, "resources");
42828
+ const dir = resolve105(projectDir, "resources");
42377
42829
  if (!await fileExists(dir)) return [];
42378
42830
  const entries = await readdir37(dir, { withFileTypes: true });
42379
42831
  return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "_index.md").map((e) => e.name.slice(0, -3)).sort();
@@ -42383,16 +42835,16 @@ async function runResourceList(project) {
42383
42835
  const slugs = await listResourceSlugs(projectDir);
42384
42836
  const out = [];
42385
42837
  for (const slug of slugs) {
42386
- const parsed = parseResource(await readFile75(resolve104(projectDir, "resources", `${slug}.md`), "utf-8"));
42838
+ const parsed = parseResource(await readFile76(resolve105(projectDir, "resources", `${slug}.md`), "utf-8"));
42387
42839
  out.push({ slug, name: parsed.name, category: parsed.category, source: parsed.source, updated: parsed.updated });
42388
42840
  }
42389
42841
  return out;
42390
42842
  }
42391
42843
  async function runResourceShow(project, slug) {
42392
42844
  const projectDir = await resolveProjectDir(project);
42393
- const filePath = resolve104(projectDir, "resources", `${slug}.md`);
42845
+ const filePath = resolve105(projectDir, "resources", `${slug}.md`);
42394
42846
  if (!await fileExists(filePath)) throw new Error(`Resource "${slug}" not found in project "${project}".`);
42395
- const parsed = parseResource(await readFile75(filePath, "utf-8"));
42847
+ const parsed = parseResource(await readFile76(filePath, "utf-8"));
42396
42848
  return {
42397
42849
  slug,
42398
42850
  name: parsed.name,
@@ -42406,14 +42858,14 @@ async function runResourceShow(project, slug) {
42406
42858
  }
42407
42859
  async function runResourceUpdate(slug, options) {
42408
42860
  const projectDir = await resolveProjectDir(options.project);
42409
- const filePath = resolve104(projectDir, "resources", `${slug}.md`);
42861
+ const filePath = resolve105(projectDir, "resources", `${slug}.md`);
42410
42862
  if (!await fileExists(filePath)) {
42411
42863
  throw new Error(`Resource "${slug}" not found in project "${options.project}".`);
42412
42864
  }
42413
42865
  if (options.name === void 0 && options.source === void 0 && options.category === void 0 && options.relatedAssignments === void 0) {
42414
42866
  throw new Error("Provide at least one of --name, --source, --category, --related-assignments.");
42415
42867
  }
42416
- const original = await readFile75(filePath, "utf-8");
42868
+ const original = await readFile76(filePath, "utf-8");
42417
42869
  const content = editResourceFrontmatter(original, {
42418
42870
  name: options.name,
42419
42871
  category: options.category,
@@ -42426,7 +42878,7 @@ async function runResourceUpdate(slug, options) {
42426
42878
  }
42427
42879
  async function runResourceRemove(slug, options) {
42428
42880
  const projectDir = await resolveProjectDir(options.project);
42429
- const filePath = resolve104(projectDir, "resources", `${slug}.md`);
42881
+ const filePath = resolve105(projectDir, "resources", `${slug}.md`);
42430
42882
  if (!await fileExists(filePath)) {
42431
42883
  throw new Error(`Resource "${slug}" not found in project "${options.project}".`);
42432
42884
  }
@@ -42509,8 +42961,8 @@ init_config2();
42509
42961
  init_fs();
42510
42962
  init_slug();
42511
42963
  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";
42964
+ import { resolve as resolve106 } from "path";
42965
+ import { readFile as readFile77, readdir as readdir38, rm as rm16 } from "fs/promises";
42514
42966
  init_parser();
42515
42967
  function nowIso5() {
42516
42968
  return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
@@ -42574,8 +43026,8 @@ ${opts.body ?? "<!-- Capture the load-bearing context for this memory here. -->"
42574
43026
  }
42575
43027
  async function resolveProjectDir2(project) {
42576
43028
  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"))) {
43029
+ const projectDir = resolve106((await readConfig()).defaultProjectDir, project);
43030
+ if (!await fileExists(resolve106(projectDir, "project.md"))) {
42579
43031
  throw new Error(`Project "${project}" not found at ${projectDir}.`);
42580
43032
  }
42581
43033
  return projectDir;
@@ -42588,7 +43040,7 @@ async function runMemoryAdd(options) {
42588
43040
  if (!isValidSlug(slug)) {
42589
43041
  throw new Error(`Invalid memory slug: "${slug}".`);
42590
43042
  }
42591
- const filePath = resolve105(projectDir, "memories", `${slug}.md`);
43043
+ const filePath = resolve106(projectDir, "memories", `${slug}.md`);
42592
43044
  if (await fileExists(filePath) && !options.force) {
42593
43045
  throw new Error(
42594
43046
  `Memory "${slug}" already exists at ${filePath}. Use --force to overwrite.`
@@ -42606,7 +43058,7 @@ async function runMemoryAdd(options) {
42606
43058
  return { filePath, indexPath, total };
42607
43059
  }
42608
43060
  async function listMemorySlugs(projectDir) {
42609
- const dir = resolve105(projectDir, "memories");
43061
+ const dir = resolve106(projectDir, "memories");
42610
43062
  if (!await fileExists(dir)) return [];
42611
43063
  const entries = await readdir38(dir, { withFileTypes: true });
42612
43064
  return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "_index.md").map((e) => e.name.slice(0, -3)).sort();
@@ -42616,16 +43068,16 @@ async function runMemoryList(project) {
42616
43068
  const slugs = await listMemorySlugs(projectDir);
42617
43069
  const out = [];
42618
43070
  for (const slug of slugs) {
42619
- const parsed = parseMemory(await readFile76(resolve105(projectDir, "memories", `${slug}.md`), "utf-8"));
43071
+ const parsed = parseMemory(await readFile77(resolve106(projectDir, "memories", `${slug}.md`), "utf-8"));
42620
43072
  out.push({ slug, name: parsed.name, scope: parsed.scope, source: parsed.source, updated: parsed.updated });
42621
43073
  }
42622
43074
  return out;
42623
43075
  }
42624
43076
  async function runMemoryShow(project, slug) {
42625
43077
  const projectDir = await resolveProjectDir2(project);
42626
- const filePath = resolve105(projectDir, "memories", `${slug}.md`);
43078
+ const filePath = resolve106(projectDir, "memories", `${slug}.md`);
42627
43079
  if (!await fileExists(filePath)) throw new Error(`Memory "${slug}" not found in project "${project}".`);
42628
- const parsed = parseMemory(await readFile76(filePath, "utf-8"));
43080
+ const parsed = parseMemory(await readFile77(filePath, "utf-8"));
42629
43081
  return {
42630
43082
  slug,
42631
43083
  name: parsed.name,
@@ -42640,7 +43092,7 @@ async function runMemoryShow(project, slug) {
42640
43092
  }
42641
43093
  async function runMemoryUpdate(slug, options) {
42642
43094
  const projectDir = await resolveProjectDir2(options.project);
42643
- const filePath = resolve105(projectDir, "memories", `${slug}.md`);
43095
+ const filePath = resolve106(projectDir, "memories", `${slug}.md`);
42644
43096
  if (!await fileExists(filePath)) {
42645
43097
  throw new Error(`Memory "${slug}" not found in project "${options.project}".`);
42646
43098
  }
@@ -42649,7 +43101,7 @@ async function runMemoryUpdate(slug, options) {
42649
43101
  "Provide at least one of --name, --source, --scope, --source-assignment, --related-assignments."
42650
43102
  );
42651
43103
  }
42652
- const original = await readFile76(filePath, "utf-8");
43104
+ const original = await readFile77(filePath, "utf-8");
42653
43105
  const content = editMemoryFrontmatter(original, {
42654
43106
  name: options.name,
42655
43107
  source: options.source,
@@ -42663,7 +43115,7 @@ async function runMemoryUpdate(slug, options) {
42663
43115
  }
42664
43116
  async function runMemoryRemove(slug, options) {
42665
43117
  const projectDir = await resolveProjectDir2(options.project);
42666
- const filePath = resolve105(projectDir, "memories", `${slug}.md`);
43118
+ const filePath = resolve106(projectDir, "memories", `${slug}.md`);
42667
43119
  if (!await fileExists(filePath)) {
42668
43120
  throw new Error(`Memory "${slug}" not found in project "${options.project}".`);
42669
43121
  }
@@ -42752,8 +43204,8 @@ init_derive();
42752
43204
  init_recompute();
42753
43205
  init_query();
42754
43206
  import { Command as Command16 } from "commander";
42755
- import { readFile as readFile77 } from "fs/promises";
42756
- import { dirname as dirname29, resolve as resolve106 } from "path";
43207
+ import { readFile as readFile78 } from "fs/promises";
43208
+ import { dirname as dirname29, resolve as resolve107 } from "path";
42757
43209
  var AGE_PATTERN = /^(\d+)([dhwm])$/i;
42758
43210
  function parseAgeToCutoff(age) {
42759
43211
  const match = age.match(AGE_PATTERN);
@@ -42772,7 +43224,7 @@ function parseAgeToCutoff(age) {
42772
43224
  }
42773
43225
  function assignmentMdPath(item) {
42774
43226
  if (item.projectSlug) {
42775
- return resolve106(
43227
+ return resolve107(
42776
43228
  defaultProjectDir(),
42777
43229
  item.projectSlug,
42778
43230
  "assignments",
@@ -42780,13 +43232,13 @@ function assignmentMdPath(item) {
42780
43232
  "assignment.md"
42781
43233
  );
42782
43234
  }
42783
- return resolve106(assignmentsDir(), item.id, "assignment.md");
43235
+ return resolve107(assignmentsDir(), item.id, "assignment.md");
42784
43236
  }
42785
43237
  async function loadTags(item) {
42786
43238
  const path = assignmentMdPath(item);
42787
43239
  if (!await fileExists(path)) return [];
42788
43240
  try {
42789
- const content = await readFile77(path, "utf-8");
43241
+ const content = await readFile78(path, "utf-8");
42790
43242
  return parseAssignmentFrontmatter(content).tags;
42791
43243
  } catch {
42792
43244
  return [];
@@ -42850,11 +43302,11 @@ async function loadQueryItem(item, terminalStatuses3, now, declarations) {
42850
43302
  const path = assignmentMdPath(item);
42851
43303
  if (!await fileExists(path)) return null;
42852
43304
  try {
42853
- const content = await readFile77(path, "utf-8");
43305
+ const content = await readFile78(path, "utf-8");
42854
43306
  const fm = parseAssignmentFrontmatter(content);
42855
43307
  const body = content.replace(/^---\n[\s\S]*?\n---/, "");
42856
43308
  const assignmentDir = dirname29(path);
42857
- const projectDir = item.projectSlug ? resolve106(defaultProjectDir(), item.projectSlug) : null;
43309
+ const projectDir = item.projectSlug ? resolve107(defaultProjectDir(), item.projectSlug) : null;
42858
43310
  const facts = await computeFacts({ assignmentDir, frontmatter: fm, body, projectDir, terminalStatuses: terminalStatuses3, declarations });
42859
43311
  const history = fm.statusHistory;
42860
43312
  const lastHeadlineChange = [...history].reverse().find((e) => e.from !== e.to || e.from === null);
@@ -43624,8 +44076,8 @@ init_fs();
43624
44076
  init_timestamp();
43625
44077
  init_frontmatter();
43626
44078
  import { Command as Command21 } from "commander";
43627
- import { readFile as readFile78 } from "fs/promises";
43628
- import { resolve as resolve107 } from "path";
44079
+ import { readFile as readFile79 } from "fs/promises";
44080
+ import { resolve as resolve108 } from "path";
43629
44081
  function renameAssignmentStatusRefs(content, id, newId, now) {
43630
44082
  const fm = parseAssignmentFrontmatter(content);
43631
44083
  const updates = { updated: now };
@@ -43646,12 +44098,12 @@ function fail3(error) {
43646
44098
  process.exit(1);
43647
44099
  }
43648
44100
  function configPath() {
43649
- return resolve107(syntaurRoot(), "config.md");
44101
+ return resolve108(syntaurRoot(), "config.md");
43650
44102
  }
43651
44103
  async function readStatusBlock() {
43652
44104
  const p = configPath();
43653
44105
  if (!await fileExists(p)) return null;
43654
- const content = await readFile78(p, "utf-8");
44106
+ const content = await readFile79(p, "utf-8");
43655
44107
  return parseStatusConfig(content);
43656
44108
  }
43657
44109
  async function requireStatusBlock() {
@@ -43935,7 +44387,7 @@ async function runStatusRename(id, opts) {
43935
44387
  printBlockDiff(before, after);
43936
44388
  const now2 = nowTimestamp();
43937
44389
  for (const a of affected) {
43938
- const original = await readFile78(a.path, "utf-8");
44390
+ const original = await readFile79(a.path, "utf-8");
43939
44391
  const rewritten = renameAssignmentStatusRefs(original, id, newId, now2);
43940
44392
  console.log(`
43941
44393
  --- ${a.display}/assignment.md`);
@@ -43946,9 +44398,9 @@ async function runStatusRename(id, opts) {
43946
44398
  }
43947
44399
  const cfgPath = configPath();
43948
44400
  const buffers = /* @__PURE__ */ new Map();
43949
- buffers.set(cfgPath, await fileExists(cfgPath) ? await readFile78(cfgPath, "utf-8") : "");
44401
+ buffers.set(cfgPath, await fileExists(cfgPath) ? await readFile79(cfgPath, "utf-8") : "");
43950
44402
  for (const a of affected) {
43951
- buffers.set(a.path, await readFile78(a.path, "utf-8"));
44403
+ buffers.set(a.path, await readFile79(a.path, "utf-8"));
43952
44404
  }
43953
44405
  const now = nowTimestamp();
43954
44406
  try {
@@ -44150,13 +44602,13 @@ init_config2();
44150
44602
  init_timestamp();
44151
44603
  init_frontmatter();
44152
44604
  import { Command as Command22 } from "commander";
44153
- import { readFile as readFile79 } from "fs/promises";
44154
- import { resolve as resolve108 } from "path";
44605
+ import { readFile as readFile80 } from "fs/promises";
44606
+ import { resolve as resolve109 } from "path";
44155
44607
  async function readContext3(cwd) {
44156
- const path = resolve108(cwd, ".syntaur", "context.json");
44608
+ const path = resolve109(cwd, ".syntaur", "context.json");
44157
44609
  if (!await fileExists(path)) return null;
44158
44610
  try {
44159
- return JSON.parse(await readFile79(path, "utf-8"));
44611
+ return JSON.parse(await readFile80(path, "utf-8"));
44160
44612
  } catch {
44161
44613
  return null;
44162
44614
  }
@@ -44165,12 +44617,12 @@ async function resolveAssignmentPath3(opts) {
44165
44617
  if (opts.assignment) {
44166
44618
  if (opts.project) {
44167
44619
  const projectsDir2 = (await readConfig()).defaultProjectDir;
44168
- return resolve108(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
44620
+ return resolve109(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
44169
44621
  }
44170
- return resolve108(assignmentsDir(), opts.assignment, "assignment.md");
44622
+ return resolve109(assignmentsDir(), opts.assignment, "assignment.md");
44171
44623
  }
44172
44624
  const ctx = await readContext3(opts.cwd);
44173
- if (ctx?.assignmentDir) return resolve108(ctx.assignmentDir, "assignment.md");
44625
+ if (ctx?.assignmentDir) return resolve109(ctx.assignmentDir, "assignment.md");
44174
44626
  throw new Error(
44175
44627
  "No active assignment. Pass --assignment <slug> [--project <slug>] or run from a workspace with .syntaur/context.json."
44176
44628
  );
@@ -44207,7 +44659,7 @@ async function runWorkspaceSet(options, cwd = process.cwd()) {
44207
44659
  ${pre.errors.map((e) => ` - ${e}`).join("\n")}`
44208
44660
  );
44209
44661
  }
44210
- const original = await readFile79(path, "utf-8");
44662
+ const original = await readFile80(path, "utf-8");
44211
44663
  let next = updateAssignmentWorkspace(original, partial);
44212
44664
  next = updateAssignmentFile(next, { updated: nowTimestamp() });
44213
44665
  await writeFileForce(path, next);
@@ -44244,13 +44696,13 @@ init_config2();
44244
44696
  init_timestamp();
44245
44697
  init_templates();
44246
44698
  import { Command as Command23 } from "commander";
44247
- import { readFile as readFile80 } from "fs/promises";
44248
- import { resolve as resolve109 } from "path";
44699
+ import { readFile as readFile81 } from "fs/promises";
44700
+ import { resolve as resolve110 } from "path";
44249
44701
  async function readContext4(cwd) {
44250
- const path = resolve109(cwd, ".syntaur", "context.json");
44702
+ const path = resolve110(cwd, ".syntaur", "context.json");
44251
44703
  if (!await fileExists(path)) return null;
44252
44704
  try {
44253
- return JSON.parse(await readFile80(path, "utf-8"));
44705
+ return JSON.parse(await readFile81(path, "utf-8"));
44254
44706
  } catch {
44255
44707
  return null;
44256
44708
  }
@@ -44260,11 +44712,11 @@ async function resolveAssignmentDir2(opts) {
44260
44712
  if (opts.project) {
44261
44713
  const projectsDir2 = (await readConfig()).defaultProjectDir;
44262
44714
  return {
44263
- dir: resolve109(projectsDir2, opts.project, "assignments", opts.assignment),
44715
+ dir: resolve110(projectsDir2, opts.project, "assignments", opts.assignment),
44264
44716
  slug: opts.assignment
44265
44717
  };
44266
44718
  }
44267
- return { dir: resolve109(assignmentsDir(), opts.assignment), slug: opts.assignment };
44719
+ return { dir: resolve110(assignmentsDir(), opts.assignment), slug: opts.assignment };
44268
44720
  }
44269
44721
  const ctx = await readContext4(opts.cwd);
44270
44722
  if (ctx?.assignmentDir) {
@@ -44326,12 +44778,12 @@ async function runProgressLog(text, options, cwd = process.cwd()) {
44326
44778
  project: options.project,
44327
44779
  cwd
44328
44780
  });
44329
- if (!await fileExists(resolve109(dir, "assignment.md"))) {
44781
+ if (!await fileExists(resolve110(dir, "assignment.md"))) {
44330
44782
  throw new Error(`No assignment found at ${dir} (missing assignment.md).`);
44331
44783
  }
44332
- const path = resolve109(dir, "progress.md");
44784
+ const path = resolve110(dir, "progress.md");
44333
44785
  const now = nowTimestamp();
44334
- const content = await fileExists(path) ? await readFile80(path, "utf-8") : renderProgress({ assignment: slug, timestamp: now });
44786
+ const content = await fileExists(path) ? await readFile81(path, "utf-8") : renderProgress({ assignment: slug, timestamp: now });
44335
44787
  const next = appendProgressEntry(content, text, now);
44336
44788
  await writeFileForce(path, next);
44337
44789
  return path;
@@ -44536,7 +44988,7 @@ program.command("attest").description("Record an attestation (agent reviewed a r
44536
44988
  await attestCommand(assignment, fact, options);
44537
44989
  })
44538
44990
  );
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(
44991
+ 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
44992
  runCommand(async (assignment, options) => {
44541
44993
  await recomputeCommand(assignment, options);
44542
44994
  })