syntaur 0.66.1 → 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;
@@ -13368,7 +13667,7 @@ async function resolveAgentTargets() {
13368
13667
  });
13369
13668
  return { targets: [...AGENT_TARGETS, ...user], warnings };
13370
13669
  }
13371
- var detectDir, claudeSessions, codexSessions, AGENT_TARGETS, AGENT_TARGETS_BY_ID;
13670
+ var detectDir, claudeSessions, codexSessions, piSessions, AGENT_TARGETS, AGENT_TARGETS_BY_ID;
13372
13671
  var init_registry = __esm({
13373
13672
  "src/targets/registry.ts"() {
13374
13673
  "use strict";
@@ -13396,6 +13695,16 @@ var init_registry = __esm({
13396
13695
  }
13397
13696
  }
13398
13697
  };
13698
+ piSessions = {
13699
+ globs: (root) => [join10(root ?? resolvePiSessionsRoot(), "*", "*.jsonl")],
13700
+ parse: async (file) => toDiscovered(await extractPiSessionMeta(file)),
13701
+ walk: async function* (opts = {}) {
13702
+ for await (const meta of walkPiSessions({ root: opts.root, sinceMtimeMs: opts.sinceMtimeMs })) {
13703
+ const d = toDiscovered(meta);
13704
+ if (d) yield d;
13705
+ }
13706
+ }
13707
+ };
13399
13708
  AGENT_TARGETS = [
13400
13709
  {
13401
13710
  id: "cursor",
@@ -13452,6 +13761,7 @@ var init_registry = __esm({
13452
13761
  detect: detectDir(home(".pi")),
13453
13762
  skillsDir: { global: home(".pi", "agent", "skills") },
13454
13763
  instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] },
13764
+ sessions: piSessions,
13455
13765
  tier3: {
13456
13766
  kind: "pi-extension",
13457
13767
  source: "platforms/pi/extensions/syntaur",
@@ -13722,6 +14032,40 @@ var init_scanner2 = __esm({
13722
14032
  }
13723
14033
  });
13724
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
+
13725
14069
  // scripts/install-macos-url-handler.mjs
13726
14070
  var install_macos_url_handler_exports = {};
13727
14071
  __export(install_macos_url_handler_exports, {
@@ -13738,7 +14082,7 @@ import {
13738
14082
  unlinkSync
13739
14083
  } from "fs";
13740
14084
  import { fileURLToPath as fileURLToPath9, pathToFileURL } from "url";
13741
- 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";
13742
14086
  import { homedir as homedir13, tmpdir as tmpdir2 } from "os";
13743
14087
  import { spawnSync as spawnSync7 } from "child_process";
13744
14088
  function syntaurRootMjs() {
@@ -13760,7 +14104,7 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
13760
14104
  }
13761
14105
  const stateRoot = syntaurRootMjs();
13762
14106
  mkdirSync3(stateRoot, { recursive: true });
13763
- const lockPath = resolve71(stateRoot, "install-url-handler.lock");
14107
+ const lockPath = resolve72(stateRoot, "install-url-handler.lock");
13764
14108
  let lockFd;
13765
14109
  try {
13766
14110
  lockFd = openSync(lockPath, "wx");
@@ -13776,8 +14120,8 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
13776
14120
  throw err2;
13777
14121
  }
13778
14122
  try {
13779
- const pkgRoot = resolve71(dirname21(fileURLToPath9(import.meta.url)), "..");
13780
- 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"));
13781
14125
  const nodeBin = process.execPath;
13782
14126
  const bundleParent = join16(
13783
14127
  homedir13(),
@@ -15956,7 +16300,7 @@ init_timestamp();
15956
16300
  init_fs();
15957
16301
  init_git_worktree();
15958
16302
  import { Router as Router2 } from "express";
15959
- 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";
15960
16304
  import { rm, readFile as readFile19, open as fsOpen, stat as fsStat, realpath as fsRealpath } from "fs/promises";
15961
16305
  import { spawnSync as spawnSync3 } from "child_process";
15962
16306
 
@@ -16760,7 +17104,7 @@ function createWriteRouter(projectsDir2, assignmentsDir2, todosDir2) {
16760
17104
  res.status(404).json({ error: `Project "${slug}" not found` });
16761
17105
  return;
16762
17106
  }
16763
- const document = await getEditableDocument(projectsDir2, "memory", basename3(projectDir), itemSlug);
17107
+ const document = await getEditableDocument(projectsDir2, "memory", basename4(projectDir), itemSlug);
16764
17108
  if (!document) {
16765
17109
  res.status(404).json({ error: "Memory not found" });
16766
17110
  return;
@@ -16779,7 +17123,7 @@ function createWriteRouter(projectsDir2, assignmentsDir2, todosDir2) {
16779
17123
  res.status(404).json({ error: `Project "${slug}" not found` });
16780
17124
  return;
16781
17125
  }
16782
- const document = await getEditableDocument(projectsDir2, "resource", basename3(projectDir), itemSlug);
17126
+ const document = await getEditableDocument(projectsDir2, "resource", basename4(projectDir), itemSlug);
16783
17127
  if (!document) {
16784
17128
  res.status(404).json({ error: "Resource not found" });
16785
17129
  return;
@@ -16864,7 +17208,7 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
16864
17208
  let content = renderItemStub(kind, {
16865
17209
  slug: requestedSlug,
16866
17210
  name,
16867
- projectSlug: basename3(projectDir),
17211
+ projectSlug: basename4(projectDir),
16868
17212
  timestamp
16869
17213
  });
16870
17214
  const customBody = typeof body.body === "string" ? body.body : "";
@@ -16881,13 +17225,13 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
16881
17225
  } catch (err2) {
16882
17226
  if (err2.code === "EEXIST") {
16883
17227
  res.status(409).json({
16884
- 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)}".`
16885
17229
  });
16886
17230
  return;
16887
17231
  }
16888
17232
  throw err2;
16889
17233
  }
16890
- res.status(201).json({ slug: requestedSlug, projectSlug: basename3(projectDir), content });
17234
+ res.status(201).json({ slug: requestedSlug, projectSlug: basename4(projectDir), content });
16891
17235
  } catch (error) {
16892
17236
  console.error(`Error creating ${kind}:`, error);
16893
17237
  res.status(500).json({ error: `Failed to create ${kind}: ${error.message}` });
@@ -16922,7 +17266,7 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
16922
17266
  ${nextBody}${nextBody.endsWith("\n") ? "" : "\n"}`;
16923
17267
  merged = setTopLevelField(merged, "updated", nowTimestamp());
16924
17268
  await writeFileForce(filePath, merged);
16925
- const detail = await getItemDetail(kind, basename3(projectDir), itemSlug);
17269
+ const detail = await getItemDetail(kind, basename4(projectDir), itemSlug);
16926
17270
  res.json({ [kind]: detail, content: merged });
16927
17271
  } catch (error) {
16928
17272
  console.error(`Error updating ${kind}:`, error);
@@ -20189,7 +20533,7 @@ async function resolveSessionPlan(input4, terminal) {
20189
20533
  init_launch();
20190
20534
  import { spawn as spawn3 } from "child_process";
20191
20535
  import { homedir as homedir5 } from "os";
20192
- 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";
20193
20537
  init_fs();
20194
20538
  init_config2();
20195
20539
  init_session_id();
@@ -20209,7 +20553,7 @@ var TerminalNotFoundError = class extends Error {
20209
20553
  var realSpawn = (command, args, options) => spawn3(command, args, options);
20210
20554
  var WRAPPER_COMMANDS = /* @__PURE__ */ new Set(["osascript", "open", "sh"]);
20211
20555
  function isWrapperCommand(command) {
20212
- return WRAPPER_COMMANDS.has(basename4(command));
20556
+ return WRAPPER_COMMANDS.has(basename5(command));
20213
20557
  }
20214
20558
  var WRAPPER_EXIT_TIMEOUT_MS = 1500;
20215
20559
  async function executeLaunchPlan(plan, spawnFn = realSpawn) {
@@ -20237,7 +20581,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
20237
20581
  `Spawn failed: ${msg2}. Verify the terminal is installed and on PATH.`
20238
20582
  );
20239
20583
  }
20240
- await new Promise((resolve110, reject) => {
20584
+ await new Promise((resolve111, reject) => {
20241
20585
  let settled = false;
20242
20586
  let stderr = "";
20243
20587
  const finishOk = () => {
@@ -20247,7 +20591,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
20247
20591
  child.unref();
20248
20592
  } catch {
20249
20593
  }
20250
- resolve110();
20594
+ resolve111();
20251
20595
  };
20252
20596
  const finishErr = (remediation) => {
20253
20597
  if (settled) return;
@@ -20513,7 +20857,7 @@ function normalizeSlashes(p) {
20513
20857
  }
20514
20858
  function detectInstallKind(scriptUrl, opts = {}) {
20515
20859
  const realpath3 = opts.realpath ?? realpathSync.native;
20516
- const readFile81 = opts.readFile ?? ((p) => readFileSync2(p, "utf-8"));
20860
+ const readFile82 = opts.readFile ?? ((p) => readFileSync2(p, "utf-8"));
20517
20861
  const ua = opts.envUserAgent !== void 0 ? opts.envUserAgent : process.env.npm_config_user_agent ?? "";
20518
20862
  const resolved = resolveScriptPath(scriptUrl, realpath3);
20519
20863
  if (resolved === null) {
@@ -20534,7 +20878,7 @@ function detectInstallKind(scriptUrl, opts = {}) {
20534
20878
  const pkgJsonPath = join7(dir, "package.json");
20535
20879
  let raw2;
20536
20880
  try {
20537
- raw2 = readFile81(pkgJsonPath);
20881
+ raw2 = readFile82(pkgJsonPath);
20538
20882
  } catch {
20539
20883
  const parent2 = dirname7(dir);
20540
20884
  if (parent2 === dir) break;
@@ -22599,7 +22943,7 @@ async function readEvents(jobId) {
22599
22943
 
22600
22944
  // src/schedules/attempt.ts
22601
22945
  import { createHash as createHash3 } from "crypto";
22602
- 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";
22603
22947
  import { resolve as resolve34 } from "path";
22604
22948
 
22605
22949
  // src/schedules/triggers.ts
@@ -22796,7 +23140,7 @@ async function acquireJobLock(id) {
22796
23140
  const code = err2.code;
22797
23141
  if (code !== "EEXIST") throw err2;
22798
23142
  try {
22799
- const info = await stat4(lockPath);
23143
+ const info = await stat5(lockPath);
22800
23144
  if (Date.now() - info.mtimeMs > LOCK_STALE_MS2) {
22801
23145
  await unlink6(lockPath).catch(() => {
22802
23146
  });
@@ -24463,8 +24807,8 @@ init_api();
24463
24807
  import { raw } from "express";
24464
24808
 
24465
24809
  // src/todos/attachments.ts
24466
- 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";
24467
- 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";
24468
24812
 
24469
24813
  // src/utils/proof-artifact-id.ts
24470
24814
  import { randomBytes as randomBytes2 } from "crypto";
@@ -24561,7 +24905,7 @@ function isSafeInlineMime(mime) {
24561
24905
  return SAFE_INLINE_MIME.has(mime);
24562
24906
  }
24563
24907
  function sanitizeAttachmentName(name) {
24564
- let n = basename5(name || "").replace(/["'\\/]/g, "_");
24908
+ let n = basename6(name || "").replace(/["'\\/]/g, "_");
24565
24909
  n = Array.from(n, (ch) => {
24566
24910
  const code = ch.charCodeAt(0);
24567
24911
  return code < 32 || code === 127 ? "_" : ch;
@@ -24584,7 +24928,7 @@ function attachmentDirFor(todosDir2, scopeId, todoId) {
24584
24928
  }
24585
24929
  async function dirExists(p) {
24586
24930
  try {
24587
- return (await stat5(p)).isDirectory();
24931
+ return (await stat6(p)).isDirectory();
24588
24932
  } catch {
24589
24933
  return false;
24590
24934
  }
@@ -24619,7 +24963,7 @@ async function listAttachments(todosDir2, scopeId, todoId) {
24619
24963
  if (!ATTACHMENT_ID_RE.test(id)) continue;
24620
24964
  const filename = stored.slice(sep2 + 2);
24621
24965
  try {
24622
- const st = await stat5(resolve41(dir, stored));
24966
+ const st = await stat6(resolve41(dir, stored));
24623
24967
  if (!st.isFile()) continue;
24624
24968
  out.push({ id, filename, mime: mimeForName(filename), size: st.size, createdAt: st.mtime.toISOString() });
24625
24969
  } catch {
@@ -25074,8 +25418,8 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
25074
25418
  router.post("/:workspace/archive", async (req2, res) => {
25075
25419
  try {
25076
25420
  const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
25077
- const { resolve: resolve110 } = await import("path");
25078
- const { readFile: readFile81 } = await import("fs/promises");
25421
+ const { resolve: resolve111 } = await import("path");
25422
+ const { readFile: readFile82 } = await import("fs/promises");
25079
25423
  const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
25080
25424
  const workspace = getWorkspaceParam(req2.params.workspace);
25081
25425
  const outcome = await wsLock(workspace, async () => {
@@ -25091,10 +25435,10 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
25091
25435
  (e) => e.itemIds.every((id) => completedIds.has(id))
25092
25436
  );
25093
25437
  const archFile = archivePath2(todosDir2, workspace, checklist.archiveInterval);
25094
- await ensureDir(resolve110(todosDir2, "archive"));
25438
+ await ensureDir(resolve111(todosDir2, "archive"));
25095
25439
  let archContent = "";
25096
25440
  if (await fileExists(archFile)) {
25097
- archContent = await readFile81(archFile, "utf-8");
25441
+ archContent = await readFile82(archFile, "utf-8");
25098
25442
  archContent = archContent.trimEnd() + "\n\n";
25099
25443
  } else {
25100
25444
  archContent = `---
@@ -25395,7 +25739,7 @@ workspace: ${workspace}
25395
25739
  const { readConfig: readConfig3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
25396
25740
  const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
25397
25741
  const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
25398
- const { readFile: readFile81 } = await import("fs/promises");
25742
+ const { readFile: readFile82 } = await import("fs/promises");
25399
25743
  const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
25400
25744
  const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
25401
25745
  let assignmentRef;
@@ -25416,7 +25760,7 @@ workspace: ${workspace}
25416
25760
  }
25417
25761
  const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
25418
25762
  if (!await fileExists2(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
25419
- let content = await readFile81(assignmentMdPath2, "utf-8");
25763
+ let content = await readFile82(assignmentMdPath2, "utf-8");
25420
25764
  content = appendTodosToAssignmentBody2(
25421
25765
  content,
25422
25766
  items.map((it) => ({
@@ -26826,7 +27170,7 @@ init_fs();
26826
27170
  init_config2();
26827
27171
  import { execFile as execFile2 } from "child_process";
26828
27172
  import { promisify as promisify2 } from "util";
26829
- 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";
26830
27174
  import { resolve as resolve44, join as join9 } from "path";
26831
27175
  import { tmpdir } from "os";
26832
27176
  var exec2 = promisify2(execFile2);
@@ -26920,7 +27264,7 @@ async function cloneOrInit(repoUrl, destDir) {
26920
27264
  }
26921
27265
  async function copyRecursive(src, dest) {
26922
27266
  if (!await fileExists(src)) return;
26923
- const s = await stat6(src);
27267
+ const s = await stat7(src);
26924
27268
  if (s.isDirectory()) {
26925
27269
  await ensureDir(dest);
26926
27270
  await cp2(src, dest, { recursive: true, force: true });
@@ -27611,7 +27955,7 @@ async function runCcusage(opts = {}) {
27611
27955
  };
27612
27956
  }
27613
27957
  function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
27614
- return new Promise((resolve110) => {
27958
+ return new Promise((resolve111) => {
27615
27959
  const child = spawn4(binary, args, {
27616
27960
  env: env ?? process.env,
27617
27961
  stdio: ["ignore", "pipe", "pipe"]
@@ -27625,7 +27969,7 @@ function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
27625
27969
  if (settled) return;
27626
27970
  settled = true;
27627
27971
  clearTimeout(timer3);
27628
- resolve110(result);
27972
+ resolve111(result);
27629
27973
  };
27630
27974
  const timer3 = setTimeout(() => {
27631
27975
  timedOut = true;
@@ -27776,6 +28120,41 @@ function runRollup() {
27776
28120
  return { daysComputed, rowsWritten };
27777
28121
  }
27778
28122
 
28123
+ // src/usage/pricing.ts
28124
+ var PER_MILLION = 1e6;
28125
+ var MODEL_PRICING = {
28126
+ // Moonshot Kimi K2.6 — the model pi emits today. Official Moonshot list price.
28127
+ // source: https://platform.moonshot.ai/ (official) — cross-checked
28128
+ // https://openrouter.ai/moonshotai/kimi-k2.6 (retrieved 2026-06-17)
28129
+ "moonshotai/kimi-k2.6": { input: 0.95, output: 4, cacheRead: 0.16, cacheWrite: 0.95 },
28130
+ // Moonshot Kimi K2.5 — prior Kimi model. Official Moonshot list price.
28131
+ // source: https://platform.moonshot.ai/ — cross-checked
28132
+ // https://openrouter.ai/moonshotai/kimi-k2.5 (retrieved 2026-06-17)
28133
+ "moonshotai/kimi-k2.5": { input: 0.6, output: 3, cacheRead: 0.1, cacheWrite: 0.6 },
28134
+ // Z.ai (Zhipu) GLM-5.2 — official z.ai platform list price.
28135
+ // source: https://docs.z.ai/guides/overview/pricing — cross-checked
28136
+ // https://openrouter.ai/z-ai/glm-5 (retrieved 2026-06-18)
28137
+ "zai-org/glm-5.2": { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 1.4 },
28138
+ // MiniMax M2.5 — official MiniMax platform list price. No separately-pinned
28139
+ // cached-read rate is published, so cacheRead is set conservatively to the
28140
+ // input rate (upper bound); revise if MiniMax publishes a cache rate.
28141
+ // source: https://platform.minimax.io/docs/guides/pricing-token-plan
28142
+ // — cross-checked https://openrouter.ai/minimax/minimax-m2.5 (retrieved 2026-06-18)
28143
+ "minimaxai/minimax-m2.5": { input: 0.15, output: 0.9, cacheRead: 0.15, cacheWrite: 0.15 }
28144
+ // NOTE: opaque Synthetic tier aliases like `syn:large:text` have no public
28145
+ // per-token rate (they route to whatever Synthetic assigns), so they remain
28146
+ // unpriced (→ $0). Reseller discounts (e.g. DeepInfra K2.6 0.75/3.50/0.15) are
28147
+ // rejected by the canonical-source rule and are NOT used here.
28148
+ };
28149
+ function normalizeModelKey(model) {
28150
+ return model.replace(/^\s*\[[^\]]*\]\s*/, "").replace(/^hf:/i, "").trim().toLowerCase();
28151
+ }
28152
+ function priceForModel(model, tokens) {
28153
+ const rate = MODEL_PRICING[normalizeModelKey(model)];
28154
+ if (!rate) return null;
28155
+ return (tokens.inputTokens * rate.input + tokens.outputTokens * rate.output + tokens.cacheReadTokens * rate.cacheRead + tokens.cacheCreationTokens * rate.cacheWrite) / PER_MILLION;
28156
+ }
28157
+
27779
28158
  // src/usage/collect.ts
27780
28159
  var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
27781
28160
  function formatDayFromCcusageDate(yyyymmdd) {
@@ -27809,6 +28188,12 @@ async function collectAndPersist() {
27809
28188
  cwd,
27810
28189
  eventTs: row.eventTs
27811
28190
  });
28191
+ const totalCost = row.totalCost > 0 ? row.totalCost : priceForModel(row.model, {
28192
+ inputTokens: row.inputTokens,
28193
+ outputTokens: row.outputTokens,
28194
+ cacheCreationTokens: row.cacheCreationTokens,
28195
+ cacheReadTokens: row.cacheReadTokens
28196
+ }) ?? 0;
27812
28197
  return {
27813
28198
  sessionId: row.sessionId,
27814
28199
  model: row.model,
@@ -27819,7 +28204,7 @@ async function collectAndPersist() {
27819
28204
  cacheCreationTokens: row.cacheCreationTokens,
27820
28205
  cacheReadTokens: row.cacheReadTokens,
27821
28206
  totalTokens: row.totalTokens,
27822
- totalCost: row.totalCost,
28207
+ totalCost,
27823
28208
  cwd,
27824
28209
  projectSlug: attr.projectSlug ?? "",
27825
28210
  assignmentSlug: attr.assignmentSlug ?? "",
@@ -27836,8 +28221,72 @@ async function collectAndPersist() {
27836
28221
  tx.immediate();
27837
28222
  return { isFirstRun, rowsIngested: enriched.length };
27838
28223
  }
28224
+ function backfillZeroCostEvents() {
28225
+ const db6 = getUsageDb();
28226
+ const select2 = db6.prepare(
28227
+ `SELECT session_id, model, input_tokens, output_tokens,
28228
+ cache_creation_tokens, cache_read_tokens
28229
+ FROM usage_events
28230
+ WHERE total_cost = 0`
28231
+ );
28232
+ const update = db6.prepare(
28233
+ `UPDATE usage_events SET total_cost = @cost, updated_at = @updatedAt
28234
+ WHERE session_id = @sessionId AND model = @model AND total_cost = 0`
28235
+ );
28236
+ let updated = 0;
28237
+ const tx = db6.transaction(() => {
28238
+ const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
28239
+ for (const r of select2.all()) {
28240
+ const cost = priceForModel(r.model, {
28241
+ inputTokens: r.input_tokens,
28242
+ outputTokens: r.output_tokens,
28243
+ cacheCreationTokens: r.cache_creation_tokens,
28244
+ cacheReadTokens: r.cache_read_tokens
28245
+ });
28246
+ if (cost !== null && cost > 0) {
28247
+ updated += update.run({ cost, updatedAt, sessionId: r.session_id, model: r.model }).changes;
28248
+ }
28249
+ }
28250
+ });
28251
+ tx.immediate();
28252
+ return updated;
28253
+ }
28254
+ function reattributeOrphanEvents() {
28255
+ const db6 = getUsageDb();
28256
+ const select2 = db6.prepare(
28257
+ `SELECT session_id, model, cwd, event_ts
28258
+ FROM usage_events
28259
+ WHERE project_slug = '' AND assignment_slug = ''`
28260
+ );
28261
+ const update = db6.prepare(
28262
+ `UPDATE usage_events
28263
+ SET project_slug = @projectSlug, assignment_slug = @assignmentSlug, updated_at = @updatedAt
28264
+ WHERE session_id = @sessionId AND model = @model
28265
+ AND project_slug = '' AND assignment_slug = ''`
28266
+ );
28267
+ let updated = 0;
28268
+ const tx = db6.transaction(() => {
28269
+ const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
28270
+ for (const r of select2.all()) {
28271
+ const attr = resolveAttribution({
28272
+ sessionId: r.session_id,
28273
+ cwd: r.cwd,
28274
+ eventTs: r.event_ts
28275
+ });
28276
+ const projectSlug = attr.projectSlug ?? "";
28277
+ const assignmentSlug = attr.assignmentSlug ?? "";
28278
+ if (projectSlug !== "" || assignmentSlug !== "") {
28279
+ updated += update.run({ projectSlug, assignmentSlug, updatedAt, sessionId: r.session_id, model: r.model }).changes;
28280
+ }
28281
+ }
28282
+ });
28283
+ tx.immediate();
28284
+ return updated;
28285
+ }
27839
28286
  async function collectUsage() {
27840
28287
  const info = await collectAndPersist();
28288
+ backfillZeroCostEvents();
28289
+ reattributeOrphanEvents();
27841
28290
  runRollup();
27842
28291
  advanceMetaIso("usage_collector_heartbeat", (/* @__PURE__ */ new Date()).toISOString());
27843
28292
  return info;
@@ -28470,6 +28919,8 @@ function createDashboardServer(options) {
28470
28919
  });
28471
28920
  }
28472
28921
  let watcherHandle = null;
28922
+ const STALENESS_WATCHDOG_INTERVAL_MS = 5 * 60 * 1e3;
28923
+ let stalenessWatchdogTimer = null;
28473
28924
  return {
28474
28925
  async start() {
28475
28926
  const { recomputeAndWrite: recomputeAndWrite2, recomputeAll: recomputeAll2, resolveDeriveContext: resolveDeriveContext2, isDeriveMigrated: isDeriveMigrated2 } = await Promise.resolve().then(() => (init_recompute(), recompute_exports));
@@ -28566,6 +29017,38 @@ function createDashboardServer(options) {
28566
29017
  onAgentSessionsChanged: () => broadcast({ type: "agent-sessions-updated", timestamp: (/* @__PURE__ */ new Date()).toISOString() })
28567
29018
  });
28568
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
+ }
28569
29052
  return new Promise((resolvePromise, reject) => {
28570
29053
  server.on("error", (err2) => {
28571
29054
  if (err2.code === "EADDRINUSE") {
@@ -28587,6 +29070,10 @@ function createDashboardServer(options) {
28587
29070
  });
28588
29071
  },
28589
29072
  async stop() {
29073
+ if (stalenessWatchdogTimer) {
29074
+ clearInterval(stalenessWatchdogTimer);
29075
+ stalenessWatchdogTimer = null;
29076
+ }
28590
29077
  await stopAutodiscovery();
28591
29078
  await stopUsageCollector();
28592
29079
  if (watcherHandle) {
@@ -28953,17 +29440,173 @@ init_facts();
28953
29440
  init_git_worktree();
28954
29441
  init_recompute();
28955
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";
28956
29455
  import { readFile as readFile33 } from "fs/promises";
28957
- 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
28958
29601
  async function resolveTarget(assignment, options) {
28959
29602
  const config = await readConfig();
28960
29603
  const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
28961
29604
  if (options.project) {
28962
29605
  if (!isValidSlug(options.project)) throw new Error(`Invalid project slug "${options.project}".`);
28963
29606
  if (!isValidSlug(assignment)) throw new Error(`Invalid assignment slug "${assignment}".`);
28964
- const projectDir = resolve51(baseDir, options.project);
28965
- const assignmentDir = resolve51(projectDir, "assignments", assignment);
28966
- 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");
28967
29610
  if (!await fileExists(assignmentPath)) {
28968
29611
  throw new Error(`Assignment "${assignment}" not found at ${assignmentPath}.`);
28969
29612
  }
@@ -28975,14 +29618,14 @@ async function resolveTarget(assignment, options) {
28975
29618
  }
28976
29619
  return {
28977
29620
  assignmentDir: resolved.assignmentDir,
28978
- assignmentPath: resolve51(resolved.assignmentDir, "assignment.md"),
28979
- projectDir: resolved.standalone ? null : resolve51(resolved.assignmentDir, "..", "..")
29621
+ assignmentPath: resolve52(resolved.assignmentDir, "assignment.md"),
29622
+ projectDir: resolved.standalone ? null : resolve52(resolved.assignmentDir, "..", "..")
28980
29623
  };
28981
29624
  }
28982
29625
  async function inferActor(options) {
28983
29626
  if (options.agent) return `agent:${options.agent}`;
28984
29627
  try {
28985
- 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");
28986
29629
  const ctx = JSON.parse(raw2);
28987
29630
  if (ctx.sessionId) return `agent:${ctx.sessionId.slice(0, 8)}`;
28988
29631
  } catch {
@@ -28991,10 +29634,10 @@ async function inferActor(options) {
28991
29634
  }
28992
29635
  async function emitDeriveEvent(target, type, actor, details) {
28993
29636
  try {
28994
- const fm = parseAssignmentFrontmatter(await readFile33(target.assignmentPath, "utf-8"));
29637
+ const fm = parseAssignmentFrontmatter(await readFile34(target.assignmentPath, "utf-8"));
28995
29638
  emitEvent({
28996
29639
  assignmentId: fm.id,
28997
- projectSlug: target.projectDir ? basename6(target.projectDir) : null,
29640
+ projectSlug: target.projectDir ? basename7(target.projectDir) : null,
28998
29641
  type,
28999
29642
  actor,
29000
29643
  details
@@ -29052,7 +29695,7 @@ async function planApproveCommand(assignment, options) {
29052
29695
  throw new Error("No plan file found (plan.md / plan-v*.md). Write a plan before approving.");
29053
29696
  }
29054
29697
  approvedFile = planFile;
29055
- const planContent = await readFile33(resolve51(target2.assignmentDir, planFile), "utf-8");
29698
+ const planContent = await readFile34(resolve52(target2.assignmentDir, planFile), "utf-8");
29056
29699
  return updatePlanApproval(content, {
29057
29700
  file: planFile,
29058
29701
  digest: planDigest(planContent),
@@ -29125,7 +29768,7 @@ async function attestCommand(assignment, fact, options) {
29125
29768
  "No plan file found (plan.md / plan-v*.md). Write a plan before attesting a binds:plan fact."
29126
29769
  );
29127
29770
  }
29128
- const planContent = await readFile33(resolve51(target2.assignmentDir, planFile), "utf-8");
29771
+ const planContent = await readFile34(resolve52(target2.assignmentDir, planFile), "utf-8");
29129
29772
  record.file = planFile;
29130
29773
  record.digest = planDigest(planContent);
29131
29774
  } else if (binds === "commit") {
@@ -29175,6 +29818,15 @@ async function requestReviewCommand(assignment, options) {
29175
29818
  );
29176
29819
  }
29177
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
+ }
29178
29830
  await assertFact(
29179
29831
  assignment,
29180
29832
  options,
@@ -29182,14 +29834,15 @@ async function implementStartedCommand(assignment, options) {
29182
29834
  (content) => {
29183
29835
  let next = updateAssignmentFile(content, { implementationStarted: true });
29184
29836
  if (options.agent) {
29185
- const fm = parseAssignmentFrontmatter(next);
29186
- if (fm.assignee === null) {
29837
+ const innerFm = parseAssignmentFrontmatter(next);
29838
+ if (innerFm.assignee === null) {
29187
29839
  next = updateAssignmentFile(next, { assignee: options.agent });
29188
29840
  }
29189
29841
  }
29190
29842
  return next;
29191
29843
  },
29192
- "Implementation started"
29844
+ "Implementation started",
29845
+ { context }
29193
29846
  );
29194
29847
  }
29195
29848
  async function blockFactCommand(assignment, options) {
@@ -29237,6 +29890,7 @@ async function statusUnpinCommand(assignment, options) {
29237
29890
  await assertFact(assignment, options, "unpin", (content) => updateOverride(content, null), "Unpinned");
29238
29891
  }
29239
29892
  async function recomputeCommand(assignment, options) {
29893
+ if (options.ifMigrated && !await isDeriveMigrated()) return;
29240
29894
  const context = await resolveDeriveContext();
29241
29895
  if (options.all) {
29242
29896
  const { recomputeAll: recomputeAll2 } = await Promise.resolve().then(() => (init_recompute(), recompute_exports));
@@ -29252,12 +29906,15 @@ async function recomputeCommand(assignment, options) {
29252
29906
  for (const w of summary.warnings) console.warn(`Warning: ${w}`);
29253
29907
  return;
29254
29908
  }
29255
- if (!assignment) throw new Error("Provide an assignment or --all.");
29256
- const target = await resolveTarget(assignment, options);
29257
- 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"), {
29258
29915
  cause: "recompute",
29259
29916
  by: await inferActor(options),
29260
- projectDir: target.projectDir,
29917
+ projectDir,
29261
29918
  context
29262
29919
  });
29263
29920
  reportDerived(result.changed ? "Recomputed" : "Recomputed (no change)", result);
@@ -29277,8 +29934,8 @@ init_frontmatter();
29277
29934
  init_timestamp();
29278
29935
  init_assignment_resolver();
29279
29936
  init_event_emit();
29280
- import { resolve as resolve52 } from "path";
29281
- 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";
29282
29939
  async function resolveTarget2(target, options) {
29283
29940
  const config = await readConfig();
29284
29941
  const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
@@ -29289,7 +29946,7 @@ async function resolveTarget2(target, options) {
29289
29946
  if (!isValidSlug(target)) {
29290
29947
  throw new Error(`Invalid assignment slug "${target}".`);
29291
29948
  }
29292
- const assignmentMd = resolve52(baseDir, options.project, "assignments", target, "assignment.md");
29949
+ const assignmentMd = resolve53(baseDir, options.project, "assignments", target, "assignment.md");
29293
29950
  if (!await fileExists(assignmentMd)) {
29294
29951
  throw new Error(`Assignment "${target}" not found in project "${options.project}".`);
29295
29952
  }
@@ -29299,18 +29956,18 @@ async function resolveTarget2(target, options) {
29299
29956
  if (resolved) {
29300
29957
  return {
29301
29958
  kind: "assignment",
29302
- filePath: resolve52(resolved.assignmentDir, "assignment.md"),
29959
+ filePath: resolve53(resolved.assignmentDir, "assignment.md"),
29303
29960
  label: resolved.projectSlug ? `assignment "${resolved.projectSlug}/${resolved.assignmentSlug}"` : `assignment "${target}"`
29304
29961
  };
29305
29962
  }
29306
- const projectMd = resolve52(baseDir, target, "project.md");
29963
+ const projectMd = resolve53(baseDir, target, "project.md");
29307
29964
  if (await fileExists(projectMd)) {
29308
29965
  return { kind: "project", filePath: projectMd, label: `project "${target}"` };
29309
29966
  }
29310
29967
  return null;
29311
29968
  }
29312
29969
  async function writeArchiveState(filePath, archived, reason) {
29313
- const content = await readFile34(filePath, "utf-8");
29970
+ const content = await readFile35(filePath, "utf-8");
29314
29971
  const updated = updateAssignmentFile(content, {
29315
29972
  archived,
29316
29973
  archivedAt: archived ? nowTimestamp() : null,
@@ -29322,7 +29979,7 @@ async function writeArchiveState(filePath, archived, reason) {
29322
29979
  async function emitArchiveEvent(resolved, type, reason) {
29323
29980
  if (resolved.kind !== "assignment") return;
29324
29981
  try {
29325
- const fm = parseAssignmentFrontmatter(await readFile34(resolved.filePath, "utf-8"));
29982
+ const fm = parseAssignmentFrontmatter(await readFile35(resolved.filePath, "utf-8"));
29326
29983
  emitEvent({
29327
29984
  assignmentId: fm.id,
29328
29985
  projectSlug: fm.project,
@@ -29392,8 +30049,8 @@ init_config2();
29392
30049
  init_frontmatter();
29393
30050
  init_timestamp();
29394
30051
  init_event_emit();
29395
- import { resolve as resolve53 } from "path";
29396
- 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";
29397
30054
  var PROMOTABLE_STATUSES = /* @__PURE__ */ new Set(["pending"]);
29398
30055
  function objectiveIsFleshedOut(content) {
29399
30056
  const match = content.match(/##\s+Objective\s*\n([\s\S]*?)(?=\n##\s+|$)/);
@@ -29415,11 +30072,11 @@ async function collectCandidates(baseDirs) {
29415
30072
  for (const m of projects) {
29416
30073
  if (!m.isDirectory()) continue;
29417
30074
  if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
29418
- const directAssignmentMd = resolve53(baseDir, m.name, "assignment.md");
30075
+ const directAssignmentMd = resolve54(baseDir, m.name, "assignment.md");
29419
30076
  if (await fileExists(directAssignmentMd)) {
29420
30077
  const fm = await parseSafe(directAssignmentMd);
29421
30078
  if (fm && PROMOTABLE_STATUSES.has(fm.status)) {
29422
- const content = await readFile35(directAssignmentMd, "utf-8");
30079
+ const content = await readFile36(directAssignmentMd, "utf-8");
29423
30080
  if (objectiveIsFleshedOut(content) && hasAcceptanceCriteria(content)) {
29424
30081
  candidates.push({
29425
30082
  projectSlug: null,
@@ -29432,17 +30089,17 @@ async function collectCandidates(baseDirs) {
29432
30089
  }
29433
30090
  continue;
29434
30091
  }
29435
- const assignmentsDir2 = resolve53(baseDir, m.name, "assignments");
30092
+ const assignmentsDir2 = resolve54(baseDir, m.name, "assignments");
29436
30093
  if (!await fileExists(assignmentsDir2)) continue;
29437
30094
  const entries = await readdir20(assignmentsDir2, { withFileTypes: true });
29438
30095
  for (const a of entries) {
29439
30096
  if (!a.isDirectory()) continue;
29440
30097
  if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
29441
- const assignmentMd = resolve53(assignmentsDir2, a.name, "assignment.md");
30098
+ const assignmentMd = resolve54(assignmentsDir2, a.name, "assignment.md");
29442
30099
  if (!await fileExists(assignmentMd)) continue;
29443
30100
  const fm = await parseSafe(assignmentMd);
29444
30101
  if (!fm || !PROMOTABLE_STATUSES.has(fm.status)) continue;
29445
- const content = await readFile35(assignmentMd, "utf-8");
30102
+ const content = await readFile36(assignmentMd, "utf-8");
29446
30103
  if (!objectiveIsFleshedOut(content)) continue;
29447
30104
  if (!hasAcceptanceCriteria(content)) continue;
29448
30105
  candidates.push({
@@ -29459,7 +30116,7 @@ async function collectCandidates(baseDirs) {
29459
30116
  }
29460
30117
  async function parseSafe(path) {
29461
30118
  try {
29462
- const content = await readFile35(path, "utf-8");
30119
+ const content = await readFile36(path, "utf-8");
29463
30120
  return parseAssignmentFrontmatter(content);
29464
30121
  } catch {
29465
30122
  return null;
@@ -29489,7 +30146,7 @@ async function migrateStatusesCommand(options) {
29489
30146
  let migrated = 0;
29490
30147
  await withSuppressedEvents(async () => {
29491
30148
  for (const c2 of candidates) {
29492
- const content = await readFile35(c2.assignmentMd, "utf-8");
30149
+ const content = await readFile36(c2.assignmentMd, "utf-8");
29493
30150
  const updated = appendStatusHistoryEntry(
29494
30151
  updateAssignmentFile(content, {
29495
30152
  status: c2.toStatus,
@@ -29511,11 +30168,11 @@ init_config2();
29511
30168
  init_frontmatter();
29512
30169
  init_event_emit();
29513
30170
  init_types();
29514
- import { resolve as resolve54 } from "path";
29515
- 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";
29516
30173
  async function parseSafe2(path) {
29517
30174
  try {
29518
- return parseAssignmentFrontmatter(await readFile36(path, "utf-8"));
30175
+ return parseAssignmentFrontmatter(await readFile37(path, "utf-8"));
29519
30176
  } catch {
29520
30177
  return null;
29521
30178
  }
@@ -29542,7 +30199,7 @@ async function collectTargets(baseDirs, terminalStatuses3) {
29542
30199
  for (const m of entries) {
29543
30200
  if (!m.isDirectory()) continue;
29544
30201
  if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
29545
- const directAssignmentMd = resolve54(baseDir, m.name, "assignment.md");
30202
+ const directAssignmentMd = resolve55(baseDir, m.name, "assignment.md");
29546
30203
  if (await fileExists(directAssignmentMd)) {
29547
30204
  if (seen.has(directAssignmentMd)) continue;
29548
30205
  const fm = await parseSafe2(directAssignmentMd);
@@ -29557,13 +30214,13 @@ async function collectTargets(baseDirs, terminalStatuses3) {
29557
30214
  }
29558
30215
  continue;
29559
30216
  }
29560
- const assignmentsBase = resolve54(baseDir, m.name, "assignments");
30217
+ const assignmentsBase = resolve55(baseDir, m.name, "assignments");
29561
30218
  if (!await fileExists(assignmentsBase)) continue;
29562
30219
  const slugs = await readdir21(assignmentsBase, { withFileTypes: true });
29563
30220
  for (const a of slugs) {
29564
30221
  if (!a.isDirectory()) continue;
29565
30222
  if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
29566
- const assignmentMd = resolve54(assignmentsBase, a.name, "assignment.md");
30223
+ const assignmentMd = resolve55(assignmentsBase, a.name, "assignment.md");
29567
30224
  if (!await fileExists(assignmentMd)) continue;
29568
30225
  if (seen.has(assignmentMd)) continue;
29569
30226
  const fm = await parseSafe2(assignmentMd);
@@ -29607,7 +30264,7 @@ async function migrateStatusHistoryCommand(options) {
29607
30264
  await withSuppressedEvents(async () => {
29608
30265
  for (const t of targets) {
29609
30266
  try {
29610
- const content = await readFile36(t.assignmentMd, "utf-8");
30267
+ const content = await readFile37(t.assignmentMd, "utf-8");
29611
30268
  if (parseAssignmentFrontmatter(content).statusHistory.length > 0) continue;
29612
30269
  const seededContent = appendStatusHistoryEntry(content, {
29613
30270
  at: t.seedAt,
@@ -29635,11 +30292,11 @@ init_fs();
29635
30292
  init_config2();
29636
30293
  init_parser();
29637
30294
  init_events_db();
29638
- import { resolve as resolve55 } from "path";
29639
- 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";
29640
30297
  async function parseSafe3(path) {
29641
30298
  try {
29642
- return parseAssignmentFull(await readFile37(path, "utf-8"));
30299
+ return parseAssignmentFull(await readFile38(path, "utf-8"));
29643
30300
  } catch {
29644
30301
  return null;
29645
30302
  }
@@ -29676,7 +30333,7 @@ async function collectTargets2(baseDirs) {
29676
30333
  for (const m of entries) {
29677
30334
  if (!m.isDirectory()) continue;
29678
30335
  if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
29679
- const directAssignmentMd = resolve55(baseDir, m.name, "assignment.md");
30336
+ const directAssignmentMd = resolve56(baseDir, m.name, "assignment.md");
29680
30337
  if (await fileExists(directAssignmentMd)) {
29681
30338
  if (seen.has(directAssignmentMd)) continue;
29682
30339
  seen.add(directAssignmentMd);
@@ -29694,13 +30351,13 @@ async function collectTargets2(baseDirs) {
29694
30351
  }
29695
30352
  continue;
29696
30353
  }
29697
- const assignmentsBase = resolve55(baseDir, m.name, "assignments");
30354
+ const assignmentsBase = resolve56(baseDir, m.name, "assignments");
29698
30355
  if (!await fileExists(assignmentsBase)) continue;
29699
30356
  const slugs = await readdir22(assignmentsBase, { withFileTypes: true });
29700
30357
  for (const a of slugs) {
29701
30358
  if (!a.isDirectory()) continue;
29702
30359
  if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
29703
- const assignmentMd = resolve55(assignmentsBase, a.name, "assignment.md");
30360
+ const assignmentMd = resolve56(assignmentsBase, a.name, "assignment.md");
29704
30361
  if (!await fileExists(assignmentMd)) continue;
29705
30362
  if (seen.has(assignmentMd)) continue;
29706
30363
  seen.add(assignmentMd);
@@ -29781,8 +30438,8 @@ init_frontmatter();
29781
30438
  init_facts();
29782
30439
  init_derive();
29783
30440
  init_recompute();
29784
- import { readdir as readdir23, readFile as readFile38 } from "fs/promises";
29785
- import { resolve as resolve56 } from "path";
30441
+ import { readdir as readdir23, readFile as readFile39 } from "fs/promises";
30442
+ import { resolve as resolve57 } from "path";
29786
30443
  var IMPLEMENTATION_STATUSES = /* @__PURE__ */ new Set(["in_progress", "review", "code_review"]);
29787
30444
  var REVIEW_STATUSES = /* @__PURE__ */ new Set(["review", "code_review"]);
29788
30445
  async function listTargets(projectsDir2, standaloneDir) {
@@ -29793,15 +30450,15 @@ async function listTargets(projectsDir2, standaloneDir) {
29793
30450
  } catch {
29794
30451
  }
29795
30452
  for (const project of projects) {
29796
- const projectDir = resolve56(projectsDir2, project);
30453
+ const projectDir = resolve57(projectsDir2, project);
29797
30454
  let slugs = [];
29798
30455
  try {
29799
- slugs = await readdir23(resolve56(projectDir, "assignments"));
30456
+ slugs = await readdir23(resolve57(projectDir, "assignments"));
29800
30457
  } catch {
29801
30458
  continue;
29802
30459
  }
29803
30460
  for (const slug of slugs) {
29804
- const path = resolve56(projectDir, "assignments", slug, "assignment.md");
30461
+ const path = resolve57(projectDir, "assignments", slug, "assignment.md");
29805
30462
  if (await fileExists(path)) targets.push({ path, projectDir, ref: `${project}/${slug}` });
29806
30463
  }
29807
30464
  }
@@ -29811,7 +30468,7 @@ async function listTargets(projectsDir2, standaloneDir) {
29811
30468
  } catch {
29812
30469
  }
29813
30470
  for (const id of ids) {
29814
- const path = resolve56(standaloneDir, id, "assignment.md");
30471
+ const path = resolve57(standaloneDir, id, "assignment.md");
29815
30472
  if (await fileExists(path)) targets.push({ path, projectDir: null, ref: id });
29816
30473
  }
29817
30474
  return targets;
@@ -29866,7 +30523,7 @@ async function migrateDeriveCommand(options) {
29866
30523
  for (const target of targets) {
29867
30524
  let content;
29868
30525
  try {
29869
- content = await readFile38(target.path, "utf-8");
30526
+ content = await readFile39(target.path, "utf-8");
29870
30527
  } catch {
29871
30528
  continue;
29872
30529
  }
@@ -29881,7 +30538,7 @@ async function migrateDeriveCommand(options) {
29881
30538
  const seededFm = parseAssignmentFrontmatter(seededContent);
29882
30539
  const body = seededContent.replace(/^---\n[\s\S]*?\n---/, "");
29883
30540
  const facts = await computeFacts({
29884
- assignmentDir: resolve56(target.path, ".."),
30541
+ assignmentDir: resolve57(target.path, ".."),
29885
30542
  frontmatter: seededFm,
29886
30543
  body,
29887
30544
  projectDir: target.projectDir,
@@ -29941,7 +30598,7 @@ async function migrateDeriveCommand(options) {
29941
30598
  }
29942
30599
 
29943
30600
  // src/commands/complete.ts
29944
- import { resolve as resolve57 } from "path";
30601
+ import { resolve as resolve58 } from "path";
29945
30602
  init_config2();
29946
30603
  init_paths();
29947
30604
  init_assignment_resolver();
@@ -29955,12 +30612,12 @@ async function completeCommand(assignment, options) {
29955
30612
  let projectDir;
29956
30613
  let changedSlug;
29957
30614
  if (options.project) {
29958
- projectDir = resolve57(baseDir, options.project);
30615
+ projectDir = resolve58(baseDir, options.project);
29959
30616
  changedSlug = assignment;
29960
30617
  } else {
29961
30618
  const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), assignment);
29962
30619
  if (!resolved) return;
29963
- projectDir = resolved.standalone ? null : resolve57(resolved.assignmentDir, "..", "..");
30620
+ projectDir = resolved.standalone ? null : resolve58(resolved.assignmentDir, "..", "..");
29964
30621
  changedSlug = resolved.assignmentSlug;
29965
30622
  }
29966
30623
  if (projectDir) {
@@ -29991,7 +30648,7 @@ async function reviewCommand(assignment, options) {
29991
30648
  }
29992
30649
 
29993
30650
  // src/commands/fail.ts
29994
- import { resolve as resolve58 } from "path";
30651
+ import { resolve as resolve59 } from "path";
29995
30652
  init_config2();
29996
30653
  init_paths();
29997
30654
  init_assignment_resolver();
@@ -30005,12 +30662,12 @@ async function failCommand(assignment, options) {
30005
30662
  let projectDir;
30006
30663
  let changedSlug;
30007
30664
  if (options.project) {
30008
- projectDir = resolve58(baseDir, options.project);
30665
+ projectDir = resolve59(baseDir, options.project);
30009
30666
  changedSlug = assignment;
30010
30667
  } else {
30011
30668
  const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), assignment);
30012
30669
  if (!resolved) return;
30013
- projectDir = resolved.standalone ? null : resolve58(resolved.assignmentDir, "..", "..");
30670
+ projectDir = resolved.standalone ? null : resolve59(resolved.assignmentDir, "..", "..");
30014
30671
  changedSlug = resolved.assignmentSlug;
30015
30672
  }
30016
30673
  if (projectDir) {
@@ -30026,7 +30683,7 @@ async function failCommand(assignment, options) {
30026
30683
  }
30027
30684
 
30028
30685
  // src/commands/reopen.ts
30029
- import { resolve as resolve59 } from "path";
30686
+ import { resolve as resolve60 } from "path";
30030
30687
  init_config2();
30031
30688
  init_paths();
30032
30689
  init_assignment_resolver();
@@ -30042,14 +30699,14 @@ async function reopenCommand(assignment, options) {
30042
30699
  let projectDir;
30043
30700
  let changedSlug;
30044
30701
  if (options.project) {
30045
- projectDir = resolve59(baseDir, options.project);
30046
- assignmentPath = resolve59(projectDir, "assignments", assignment, "assignment.md");
30702
+ projectDir = resolve60(baseDir, options.project);
30703
+ assignmentPath = resolve60(projectDir, "assignments", assignment, "assignment.md");
30047
30704
  changedSlug = assignment;
30048
30705
  } else {
30049
30706
  const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), assignment);
30050
30707
  if (!resolved) return;
30051
- assignmentPath = resolve59(resolved.assignmentDir, "assignment.md");
30052
- projectDir = resolved.standalone ? null : resolve59(resolved.assignmentDir, "..", "..");
30708
+ assignmentPath = resolve60(resolved.assignmentDir, "assignment.md");
30709
+ projectDir = resolved.standalone ? null : resolve60(resolved.assignmentDir, "..", "..");
30053
30710
  changedSlug = resolved.assignmentSlug;
30054
30711
  }
30055
30712
  const derived = await recomputeAndWrite(assignmentPath, {
@@ -30083,7 +30740,7 @@ import {
30083
30740
  readdir as readdir24,
30084
30741
  symlink,
30085
30742
  lstat,
30086
- readFile as readFile39,
30743
+ readFile as readFile40,
30087
30744
  readlink,
30088
30745
  rename as rename9,
30089
30746
  rm as rm6,
@@ -30092,20 +30749,20 @@ import {
30092
30749
  } from "fs/promises";
30093
30750
  import { existsSync as existsSync4 } from "fs";
30094
30751
  import { homedir as homedir8 } from "os";
30095
- 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";
30096
30753
 
30097
30754
  // src/utils/package-root.ts
30098
30755
  init_fs();
30099
- import { dirname as dirname14, resolve as resolve60 } from "path";
30756
+ import { dirname as dirname14, resolve as resolve61 } from "path";
30100
30757
  import { fileURLToPath as fileURLToPath4 } from "url";
30101
30758
  async function findPackageRoot(expectedRelativePath) {
30102
30759
  let currentDir = dirname14(fileURLToPath4(import.meta.url));
30103
30760
  while (true) {
30104
- const candidate = resolve60(currentDir, expectedRelativePath);
30761
+ const candidate = resolve61(currentDir, expectedRelativePath);
30105
30762
  if (await fileExists(candidate)) {
30106
30763
  return currentDir;
30107
30764
  }
30108
- const parentDir = resolve60(currentDir, "..");
30765
+ const parentDir = resolve61(currentDir, "..");
30109
30766
  if (parentDir === currentDir) {
30110
30767
  throw new Error(
30111
30768
  `Could not locate package root containing ${expectedRelativePath}.`
@@ -30126,25 +30783,25 @@ function getPluginManifestRelativePath(pluginKind) {
30126
30783
  }
30127
30784
  function getDefaultPluginTargetDir(pluginKind) {
30128
30785
  const home2 = homedir8();
30129
- 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");
30130
30787
  }
30131
30788
  function getDefaultMarketplacePath() {
30132
- return resolve61(homedir8(), ".agents", "plugins", "marketplace.json");
30789
+ return resolve62(homedir8(), ".agents", "plugins", "marketplace.json");
30133
30790
  }
30134
30791
  function getClaudeMarketplacesRoot() {
30135
- return resolve61(homedir8(), ".claude", "plugins", "marketplaces");
30792
+ return resolve62(homedir8(), ".claude", "plugins", "marketplaces");
30136
30793
  }
30137
30794
  function getClaudeKnownMarketplacesPath() {
30138
- return resolve61(homedir8(), ".claude", "plugins", "known_marketplaces.json");
30795
+ return resolve62(homedir8(), ".claude", "plugins", "known_marketplaces.json");
30139
30796
  }
30140
30797
  function getClaudeInstalledPluginsPath() {
30141
- return resolve61(homedir8(), ".claude", "plugins", "installed_plugins.json");
30798
+ return resolve62(homedir8(), ".claude", "plugins", "installed_plugins.json");
30142
30799
  }
30143
30800
  function getInstallMarkerPath(targetDir) {
30144
- return resolve61(targetDir, INSTALL_MARKER_FILENAME);
30801
+ return resolve62(targetDir, INSTALL_MARKER_FILENAME);
30145
30802
  }
30146
30803
  async function readPackageManifest(packageRoot) {
30147
- const raw2 = await readFile39(resolve61(packageRoot, "package.json"), "utf-8");
30804
+ const raw2 = await readFile40(resolve62(packageRoot, "package.json"), "utf-8");
30148
30805
  return JSON.parse(raw2);
30149
30806
  }
30150
30807
  async function readJsonFileIfExists(pathValue) {
@@ -30152,7 +30809,7 @@ async function readJsonFileIfExists(pathValue) {
30152
30809
  return null;
30153
30810
  }
30154
30811
  try {
30155
- const raw2 = await readFile39(pathValue, "utf-8");
30812
+ const raw2 = await readFile40(pathValue, "utf-8");
30156
30813
  return JSON.parse(raw2);
30157
30814
  } catch {
30158
30815
  return null;
@@ -30160,15 +30817,15 @@ async function readJsonFileIfExists(pathValue) {
30160
30817
  }
30161
30818
  async function readClaudePluginManifest(pluginDir) {
30162
30819
  return await readJsonFileIfExists(
30163
- resolve61(pluginDir, ".claude-plugin", "plugin.json")
30820
+ resolve62(pluginDir, ".claude-plugin", "plugin.json")
30164
30821
  ) ?? {};
30165
30822
  }
30166
30823
  async function readPluginManifestName(targetDir, pluginKind) {
30167
- const manifestPath = resolve61(targetDir, getPluginManifestRelativePath(pluginKind));
30824
+ const manifestPath = resolve62(targetDir, getPluginManifestRelativePath(pluginKind));
30168
30825
  if (!await fileExists(manifestPath)) {
30169
30826
  return void 0;
30170
30827
  }
30171
- const raw2 = await readFile39(manifestPath, "utf-8");
30828
+ const raw2 = await readFile40(manifestPath, "utf-8");
30172
30829
  const parsed = JSON.parse(raw2);
30173
30830
  return parsed.name;
30174
30831
  }
@@ -30178,7 +30835,7 @@ async function readInstallMetadata(targetDir) {
30178
30835
  return null;
30179
30836
  }
30180
30837
  try {
30181
- const raw2 = await readFile39(markerPath, "utf-8");
30838
+ const raw2 = await readFile40(markerPath, "utf-8");
30182
30839
  return JSON.parse(raw2);
30183
30840
  } catch {
30184
30841
  return null;
@@ -30191,7 +30848,7 @@ async function getInstallStatus(targetDir, pluginKind) {
30191
30848
  const info = await lstat(targetDir);
30192
30849
  if (info.isSymbolicLink()) {
30193
30850
  const symlinkTarget = await readlink(targetDir);
30194
- const resolvedTarget = resolve61(dirname15(targetDir), symlinkTarget);
30851
+ const resolvedTarget = resolve62(dirname15(targetDir), symlinkTarget);
30195
30852
  const manifestName2 = await readPluginManifestName(resolvedTarget, pluginKind);
30196
30853
  return {
30197
30854
  exists: true,
@@ -30237,7 +30894,7 @@ async function installLink(paths) {
30237
30894
  await ensureDir(dirname15(paths.targetDir));
30238
30895
  await rm6(paths.targetDir, { recursive: true, force: true });
30239
30896
  await ensureDir(dirname15(paths.targetDir));
30240
- await symlink(resolve61(paths.sourceDir), paths.targetDir, "dir");
30897
+ await symlink(resolve62(paths.sourceDir), paths.targetDir, "dir");
30241
30898
  }
30242
30899
  async function removeInstallMarker(targetDir) {
30243
30900
  const markerPath = getInstallMarkerPath(targetDir);
@@ -30251,13 +30908,13 @@ function normalizeAbsoluteInstallPath(pathValue, label) {
30251
30908
  if (!isAbsolute7(expanded)) {
30252
30909
  throw new Error(`${label} must be an absolute path.`);
30253
30910
  }
30254
- return resolve61(expanded);
30911
+ return resolve62(expanded);
30255
30912
  }
30256
30913
  async function resolvePluginPaths(pluginKind, targetDir) {
30257
30914
  const packageRoot = await findPackageRoot(getPluginRelativePath(pluginKind));
30258
30915
  return {
30259
30916
  packageRoot,
30260
- sourceDir: resolve61(packageRoot, getPluginRelativePath(pluginKind)),
30917
+ sourceDir: resolve62(packageRoot, getPluginRelativePath(pluginKind)),
30261
30918
  targetDir: targetDir ?? getDefaultPluginTargetDir(pluginKind)
30262
30919
  };
30263
30920
  }
@@ -30301,7 +30958,7 @@ async function writeClaudeMarketplaceFile(manifestPath, marketplace) {
30301
30958
  }
30302
30959
  if (await fileExists(manifestPath)) {
30303
30960
  try {
30304
- const prev = await readFile39(manifestPath, "utf-8");
30961
+ const prev = await readFile40(manifestPath, "utf-8");
30305
30962
  JSON.parse(prev);
30306
30963
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
30307
30964
  await writeFile10(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
@@ -30382,8 +31039,8 @@ async function listClaudeMarketplaceCandidates() {
30382
31039
  if (!entry.isDirectory()) {
30383
31040
  continue;
30384
31041
  }
30385
- const candidateRoot = resolve61(rootDir, entry.name);
30386
- const manifestPath = resolve61(candidateRoot, ".claude-plugin", "marketplace.json");
31042
+ const candidateRoot = resolve62(rootDir, entry.name);
31043
+ const manifestPath = resolve62(candidateRoot, ".claude-plugin", "marketplace.json");
30387
31044
  if (!await fileExists(manifestPath)) {
30388
31045
  continue;
30389
31046
  }
@@ -30410,11 +31067,11 @@ async function listClaudeMarketplaceCandidates() {
30410
31067
  if (!installLocation) {
30411
31068
  continue;
30412
31069
  }
30413
- const candidateRoot = resolve61(expandHome(installLocation));
31070
+ const candidateRoot = resolve62(expandHome(installLocation));
30414
31071
  if (seen.has(candidateRoot)) {
30415
31072
  continue;
30416
31073
  }
30417
- const manifestPath = resolve61(candidateRoot, ".claude-plugin", "marketplace.json");
31074
+ const manifestPath = resolve62(candidateRoot, ".claude-plugin", "marketplace.json");
30418
31075
  if (!await fileExists(manifestPath)) {
30419
31076
  continue;
30420
31077
  }
@@ -30442,14 +31099,14 @@ async function getPreferredClaudeMarketplace() {
30442
31099
  name: candidate.name,
30443
31100
  rootDir: candidate.rootDir,
30444
31101
  manifestPath: candidate.manifestPath,
30445
- targetDir: resolve61(candidate.rootDir, "plugins", "syntaur")
31102
+ targetDir: resolve62(candidate.rootDir, "plugins", "syntaur")
30446
31103
  };
30447
31104
  }
30448
31105
  async function registerKnownClaudeMarketplace(name, rootDir) {
30449
31106
  const manifestPath = getClaudeKnownMarketplacesPath();
30450
31107
  let existing = {};
30451
31108
  if (await fileExists(manifestPath)) {
30452
- const raw2 = await readFile39(manifestPath, "utf-8");
31109
+ const raw2 = await readFile40(manifestPath, "utf-8");
30453
31110
  try {
30454
31111
  const parsed = JSON.parse(raw2);
30455
31112
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
@@ -30476,7 +31133,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
30476
31133
  existing[name].autoUpdate = true;
30477
31134
  await ensureDir(dirname15(manifestPath));
30478
31135
  if (await fileExists(manifestPath)) {
30479
- const prev = await readFile39(manifestPath, "utf-8");
31136
+ const prev = await readFile40(manifestPath, "utf-8");
30480
31137
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
30481
31138
  await writeFile10(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
30482
31139
  }
@@ -30490,11 +31147,11 @@ async function ensureKnownClaudeMarketplaceForRoot(options) {
30490
31147
  return registerKnownClaudeMarketplace(options.name, options.rootDir);
30491
31148
  }
30492
31149
  async function setSyntaurPluginEnabled(options) {
30493
- const settingsPath = resolve61(homedir8(), ".claude", "settings.json");
31150
+ const settingsPath = resolve62(homedir8(), ".claude", "settings.json");
30494
31151
  const key = `syntaur@${options.marketplaceName}`;
30495
31152
  let parsed = {};
30496
31153
  if (await fileExists(settingsPath)) {
30497
- const raw2 = await readFile39(settingsPath, "utf-8");
31154
+ const raw2 = await readFile40(settingsPath, "utf-8");
30498
31155
  try {
30499
31156
  parsed = JSON.parse(raw2);
30500
31157
  } catch {
@@ -30513,7 +31170,7 @@ async function setSyntaurPluginEnabled(options) {
30513
31170
  await ensureDir(dirname15(settingsPath));
30514
31171
  if (await fileExists(settingsPath)) {
30515
31172
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
30516
- const prev = await readFile39(settingsPath, "utf-8");
31173
+ const prev = await readFile40(settingsPath, "utf-8");
30517
31174
  await writeFile10(`${settingsPath}.bak-${stamp}`, prev, "utf-8");
30518
31175
  }
30519
31176
  const tmpPath = `${settingsPath}.tmp`;
@@ -30527,9 +31184,9 @@ async function ensureClaudeUserMarketplace() {
30527
31184
  if (existing) {
30528
31185
  return existing;
30529
31186
  }
30530
- const rootDir = resolve61(getClaudeMarketplacesRoot(), "user-plugins");
30531
- const manifestPath = resolve61(rootDir, ".claude-plugin", "marketplace.json");
30532
- 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"));
30533
31190
  if (!await fileExists(manifestPath)) {
30534
31191
  const scaffold = {
30535
31192
  plugins: []
@@ -30548,22 +31205,22 @@ async function ensureClaudeUserMarketplace() {
30548
31205
  name: "user-plugins",
30549
31206
  rootDir,
30550
31207
  manifestPath,
30551
- targetDir: resolve61(rootDir, "plugins", "syntaur")
31208
+ targetDir: resolve62(rootDir, "plugins", "syntaur")
30552
31209
  };
30553
31210
  }
30554
31211
  async function detectClaudeMarketplaceForTarget(targetDir) {
30555
31212
  const normalizedTargetDir = normalizeAbsoluteInstallPath(targetDir, "Claude plugin target");
30556
31213
  const pluginsDir = dirname15(normalizedTargetDir);
30557
- if (basename7(pluginsDir) !== "plugins") {
31214
+ if (basename8(pluginsDir) !== "plugins") {
30558
31215
  return null;
30559
31216
  }
30560
31217
  const rootDir = dirname15(pluginsDir);
30561
- const manifestPath = resolve61(rootDir, ".claude-plugin", "marketplace.json");
31218
+ const manifestPath = resolve62(rootDir, ".claude-plugin", "marketplace.json");
30562
31219
  if (!await fileExists(manifestPath)) {
30563
31220
  return null;
30564
31221
  }
30565
31222
  const marketplace = await readClaudeMarketplaceFile(manifestPath);
30566
- 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);
30567
31224
  return {
30568
31225
  name,
30569
31226
  rootDir,
@@ -30574,7 +31231,7 @@ async function detectClaudeMarketplaceForTarget(targetDir) {
30574
31231
  async function findManagedClaudeMarketplacePluginDir() {
30575
31232
  const marketplaces = await listClaudeMarketplaceCandidates();
30576
31233
  for (const marketplace of marketplaces) {
30577
- const targetDir = resolve61(marketplace.rootDir, "plugins", "syntaur");
31234
+ const targetDir = resolve62(marketplace.rootDir, "plugins", "syntaur");
30578
31235
  const status = await getInstallStatus(targetDir, "claude");
30579
31236
  if (status.exists && status.managed) {
30580
31237
  return targetDir;
@@ -30699,7 +31356,7 @@ async function installManagedPlugin(options) {
30699
31356
  `${paths.targetDir} already exists and is not a Syntaur-managed install. Remove it manually before installing Syntaur there.`
30700
31357
  );
30701
31358
  }
30702
- 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) {
30703
31360
  return {
30704
31361
  targetDir: paths.targetDir,
30705
31362
  sourceDir: paths.sourceDir,
@@ -30751,7 +31408,7 @@ async function readMarketplaceFile(marketplacePath) {
30751
31408
  plugins: []
30752
31409
  };
30753
31410
  }
30754
- const raw2 = await readFile39(marketplacePath, "utf-8");
31411
+ const raw2 = await readFile40(marketplacePath, "utf-8");
30755
31412
  const parsed = JSON.parse(raw2);
30756
31413
  return {
30757
31414
  name: parsed.name ?? "local",
@@ -30918,13 +31575,13 @@ async function recommendMarketplacePath() {
30918
31575
  return configuredOrManaged ?? getDefaultMarketplacePath();
30919
31576
  }
30920
31577
  async function isSyntaurDataInstalled() {
30921
- return fileExists(resolve61(syntaurRoot(), "config.md"));
31578
+ return fileExists(resolve62(syntaurRoot(), "config.md"));
30922
31579
  }
30923
31580
  async function removeSyntaurData() {
30924
31581
  await rm6(syntaurRoot(), { recursive: true, force: true });
30925
31582
  }
30926
31583
  async function getConfiguredProjectDir() {
30927
- if (!await fileExists(resolve61(syntaurRoot(), "config.md"))) {
31584
+ if (!await fileExists(resolve62(syntaurRoot(), "config.md"))) {
30928
31585
  return null;
30929
31586
  }
30930
31587
  return (await readConfig()).defaultProjectDir;
@@ -30993,24 +31650,24 @@ async function textPrompt(question, defaultValue) {
30993
31650
 
30994
31651
  // src/utils/install-skills.ts
30995
31652
  init_fs();
30996
- import { readFile as readFile41, readdir as readdir25, mkdir as mkdir7, copyFile, rm as rm7, lstat as lstat2 } from "fs/promises";
30997
- 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";
30998
31655
  import { homedir as homedir10 } from "os";
30999
31656
 
31000
31657
  // src/utils/plugin-state.ts
31001
31658
  init_fs();
31002
- import { readFile as readFile40 } from "fs/promises";
31003
- import { resolve as resolve62 } from "path";
31659
+ import { readFile as readFile41 } from "fs/promises";
31660
+ import { resolve as resolve63 } from "path";
31004
31661
  import { homedir as homedir9 } from "os";
31005
31662
  function settingsPathFor(agent) {
31006
- if (agent === "claude") return resolve62(homedir9(), ".claude", "settings.json");
31663
+ if (agent === "claude") return resolve63(homedir9(), ".claude", "settings.json");
31007
31664
  return null;
31008
31665
  }
31009
31666
  async function readJsonOrNull(path) {
31010
31667
  if (!path) return null;
31011
31668
  if (!await fileExists(path)) return null;
31012
31669
  try {
31013
- const raw2 = await readFile40(path, "utf-8");
31670
+ const raw2 = await readFile41(path, "utf-8");
31014
31671
  return JSON.parse(raw2);
31015
31672
  } catch {
31016
31673
  return null;
@@ -31060,11 +31717,11 @@ var KNOWN_SKILL_NAMES = [
31060
31717
  var KNOWN_SKILLS = KNOWN_SKILL_NAMES;
31061
31718
  async function getSkillsDir() {
31062
31719
  const packageRoot = await findPackageRoot("skills");
31063
- return resolve63(packageRoot, "skills");
31720
+ return resolve64(packageRoot, "skills");
31064
31721
  }
31065
31722
  function defaultSkillTargetDir(target) {
31066
- if (target === "claude") return resolve63(homedir10(), ".claude", "skills");
31067
- return resolve63(homedir10(), ".codex", "skills");
31723
+ if (target === "claude") return resolve64(homedir10(), ".claude", "skills");
31724
+ return resolve64(homedir10(), ".codex", "skills");
31068
31725
  }
31069
31726
  async function walkFiles(root) {
31070
31727
  const out = [];
@@ -31084,7 +31741,7 @@ async function walkFiles(root) {
31084
31741
  }
31085
31742
  async function filesEqual(a, b) {
31086
31743
  try {
31087
- const [ba, bb] = await Promise.all([readFile41(a), readFile41(b)]);
31744
+ const [ba, bb] = await Promise.all([readFile42(a), readFile42(b)]);
31088
31745
  if (ba.length !== bb.length) return false;
31089
31746
  return ba.equals(bb);
31090
31747
  } catch {
@@ -31118,8 +31775,8 @@ async function skillMatches(srcDir, destDir) {
31118
31775
  }
31119
31776
  async function isSymlink(path) {
31120
31777
  try {
31121
- const stat16 = await lstat2(path);
31122
- return stat16.isSymbolicLink();
31778
+ const stat17 = await lstat2(path);
31779
+ return stat17.isSymbolicLink();
31123
31780
  } catch {
31124
31781
  return false;
31125
31782
  }
@@ -31224,7 +31881,7 @@ async function uninstallSkills(options) {
31224
31881
  if (await isSymlink(destDir)) continue;
31225
31882
  const skillMd = join11(destDir, "SKILL.md");
31226
31883
  if (!await fileExists(skillMd)) continue;
31227
- const content = await readFile41(skillMd, "utf-8").catch(() => "");
31884
+ const content = await readFile42(skillMd, "utf-8").catch(() => "");
31228
31885
  const match = content.match(/^name:\s*(\S+)\s*$/m);
31229
31886
  if (!match || match[1] !== skill) continue;
31230
31887
  await rm7(destDir, { recursive: true, force: true });
@@ -31419,20 +32076,20 @@ import { realpathSync as realpathSync2 } from "fs";
31419
32076
  init_paths();
31420
32077
  init_fs();
31421
32078
  import { fileURLToPath as fileURLToPath6 } from "url";
31422
- import { readFile as readFile43 } from "fs/promises";
31423
- 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";
31424
32081
  import { spawn as spawn6 } from "child_process";
31425
32082
  import { createInterface as createInterface2 } from "readline/promises";
31426
32083
 
31427
32084
  // src/utils/version.ts
31428
32085
  import { fileURLToPath as fileURLToPath5 } from "url";
31429
- import { readFile as readFile42 } from "fs/promises";
32086
+ import { readFile as readFile43 } from "fs/promises";
31430
32087
  import { dirname as dirname16, join as join12 } from "path";
31431
32088
  async function readPackageVersion(scriptUrl) {
31432
32089
  try {
31433
32090
  const scriptPath = fileURLToPath5(scriptUrl);
31434
32091
  const pkgRoot = dirname16(dirname16(scriptPath));
31435
- const raw2 = await readFile42(join12(pkgRoot, "package.json"), "utf-8");
32092
+ const raw2 = await readFile43(join12(pkgRoot, "package.json"), "utf-8");
31436
32093
  const parsed = JSON.parse(raw2);
31437
32094
  return typeof parsed.version === "string" ? parsed.version : null;
31438
32095
  } catch {
@@ -31441,7 +32098,7 @@ async function readPackageVersion(scriptUrl) {
31441
32098
  }
31442
32099
 
31443
32100
  // src/utils/npx-prompt.ts
31444
- var STATE_FILE = resolve64(syntaurRoot(), "npx-install.json");
32101
+ var STATE_FILE = resolve65(syntaurRoot(), "npx-install.json");
31445
32102
  var META_ARGS2 = /* @__PURE__ */ new Set(["-h", "--help", "-V", "--version", "help"]);
31446
32103
  var GLOBAL_VERSION_TIMEOUT_MS = 2e3;
31447
32104
  function isRunningViaNpx(scriptUrl) {
@@ -31462,7 +32119,7 @@ function isRunningViaNpx(scriptUrl) {
31462
32119
  async function readState() {
31463
32120
  if (!await fileExists(STATE_FILE)) return null;
31464
32121
  try {
31465
- const raw2 = await readFile43(STATE_FILE, "utf-8");
32122
+ const raw2 = await readFile44(STATE_FILE, "utf-8");
31466
32123
  return JSON.parse(raw2);
31467
32124
  } catch {
31468
32125
  return null;
@@ -31521,7 +32178,7 @@ async function readGlobalVersion() {
31521
32178
  try {
31522
32179
  const manifestPath = join13(rootPath, "syntaur", "package.json");
31523
32180
  if (!await fileExists(manifestPath)) return null;
31524
- const raw2 = await readFile43(manifestPath, "utf-8");
32181
+ const raw2 = await readFile44(manifestPath, "utf-8");
31525
32182
  const parsed = JSON.parse(raw2);
31526
32183
  return typeof parsed.version === "string" ? parsed.version : null;
31527
32184
  } catch {
@@ -31879,16 +32536,16 @@ async function refreshPluginSkills(options, pm, runner, getManagedDir, resolveFr
31879
32536
  // src/commands/install-statusline.ts
31880
32537
  init_paths();
31881
32538
  init_fs();
31882
- 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";
31883
- 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";
31884
32541
  import { homedir as homedir12 } from "os";
31885
32542
  import { fileURLToPath as fileURLToPath8 } from "url";
31886
32543
 
31887
32544
  // src/commands/configure-statusline.ts
31888
32545
  init_paths();
31889
32546
  init_fs();
31890
- import { readFile as readFile44, writeFile as writeFile11 } from "fs/promises";
31891
- 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";
31892
32549
  import { spawnSync as spawnSync5 } from "child_process";
31893
32550
  import { checkbox, input as input2, confirm } from "@inquirer/prompts";
31894
32551
  var AVAILABLE_SEGMENTS = [
@@ -31909,12 +32566,12 @@ var PRESETS = {
31909
32566
  tracker: { segments: ["git", "assignment", "external", "session"], separator: " \xB7 " }
31910
32567
  };
31911
32568
  function getConfigPath(installRoot) {
31912
- return resolve65(installRoot, "statusline.config.json");
32569
+ return resolve66(installRoot, "statusline.config.json");
31913
32570
  }
31914
32571
  async function readConfig2(path) {
31915
32572
  if (!await fileExists(path)) return null;
31916
32573
  try {
31917
- const raw2 = await readFile44(path, "utf-8");
32574
+ const raw2 = await readFile45(path, "utf-8");
31918
32575
  const parsed = JSON.parse(raw2);
31919
32576
  if (!parsed || typeof parsed !== "object") return null;
31920
32577
  const segments = Array.isArray(parsed.segments) ? parsed.segments.filter(isSegmentName) : [];
@@ -32067,7 +32724,7 @@ async function configureStatuslineCommand(options = {}) {
32067
32724
  console.log(` segments: ${config.segments.join(", ")}`);
32068
32725
  console.log(` separator: ${JSON.stringify(config.separator)}`);
32069
32726
  if (config.wrap) console.log(` wrap: ${config.wrap}`);
32070
- const script = options.statuslineScript ?? resolve65(installRoot, "statusline.sh");
32727
+ const script = options.statuslineScript ?? resolve66(installRoot, "statusline.sh");
32071
32728
  if (await fileExists(script)) {
32072
32729
  console.log("");
32073
32730
  console.log("Live preview:");
@@ -32098,11 +32755,11 @@ async function writeDefaultConfigIfMissing(installRoot) {
32098
32755
  // src/commands/install-statusline.ts
32099
32756
  function getPackageStatuslineSource() {
32100
32757
  const here = dirname19(fileURLToPath8(import.meta.url));
32101
- return resolve66(here, "..", "statusline", "statusline.sh");
32758
+ return resolve67(here, "..", "statusline", "statusline.sh");
32102
32759
  }
32103
32760
  async function readSettingsJson(settingsPath) {
32104
32761
  if (!await fileExists(settingsPath)) return {};
32105
- const raw2 = await readFile45(settingsPath, "utf-8");
32762
+ const raw2 = await readFile46(settingsPath, "utf-8");
32106
32763
  if (raw2.trim() === "") return {};
32107
32764
  try {
32108
32765
  const parsed = JSON.parse(raw2);
@@ -32182,12 +32839,12 @@ async function installScript(sourceScript, destScript, link) {
32182
32839
  }
32183
32840
  async function installStatuslineCommand(options = {}) {
32184
32841
  const mode = options.mode ?? "ask";
32185
- const settingsPath = options.settingsPath ?? resolve66(homedir12(), ".claude", "settings.json");
32842
+ const settingsPath = options.settingsPath ?? resolve67(homedir12(), ".claude", "settings.json");
32186
32843
  const installRoot = options.installRoot ?? syntaurRoot();
32187
32844
  const sourceScript = options.sourceScript ?? getPackageStatuslineSource();
32188
- const destScript = resolve66(installRoot, "statusline.sh");
32189
- const confPath = resolve66(installRoot, "statusline.conf");
32190
- 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");
32191
32848
  if (!await fileExists(sourceScript)) {
32192
32849
  throw new Error(
32193
32850
  `Statusline source script not found at ${sourceScript}. Try re-installing syntaur (npm install -g syntaur) or pass --source-script explicitly.`
@@ -32223,7 +32880,7 @@ async function installStatuslineCommand(options = {}) {
32223
32880
  if (parsed) {
32224
32881
  wrapTarget = parsed;
32225
32882
  } else {
32226
- const wrapperPath = resolve66(installRoot, "statusline-wrapped.sh");
32883
+ const wrapperPath = resolve67(installRoot, "statusline-wrapped.sh");
32227
32884
  const wrapperBody = `#!/usr/bin/env bash
32228
32885
  # Auto-generated by syntaur install-statusline.
32229
32886
  # Executes the previously configured statusLine command.
@@ -32272,25 +32929,25 @@ function parseWrapCommand(command) {
32272
32929
  async function chmodExec(path) {
32273
32930
  const fs = await import("fs/promises");
32274
32931
  try {
32275
- const s = await stat7(path);
32932
+ const s = await stat8(path);
32276
32933
  await fs.chmod(path, s.mode | 73);
32277
32934
  } catch {
32278
32935
  }
32279
32936
  }
32280
32937
  async function uninstallStatuslineCommand(options = {}) {
32281
- const settingsPath = options.settingsPath ?? resolve66(homedir12(), ".claude", "settings.json");
32938
+ const settingsPath = options.settingsPath ?? resolve67(homedir12(), ".claude", "settings.json");
32282
32939
  const installRoot = options.installRoot ?? syntaurRoot();
32283
- const destScript = resolve66(installRoot, "statusline.sh");
32284
- const confPath = resolve66(installRoot, "statusline.conf");
32285
- const backupPath = resolve66(installRoot, "statusline.backup.json");
32286
- 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");
32287
32944
  const settings = await readSettingsJson(settingsPath);
32288
32945
  const existing = extractExistingCommand(settings);
32289
32946
  const ourCommand = `bash ${destScript}`;
32290
32947
  let restored = null;
32291
32948
  if (await fileExists(backupPath)) {
32292
32949
  try {
32293
- const raw2 = await readFile45(backupPath, "utf-8");
32950
+ const raw2 = await readFile46(backupPath, "utf-8");
32294
32951
  const parsed = JSON.parse(raw2);
32295
32952
  const prev = parsed?.previousStatusLine;
32296
32953
  if (prev && typeof prev === "object" && typeof prev.command === "string") {
@@ -32311,7 +32968,7 @@ async function uninstallStatuslineCommand(options = {}) {
32311
32968
  await writeSettingsJson(settingsPath, settings);
32312
32969
  }
32313
32970
  if (!options.keepScript) {
32314
- const configPath2 = resolve66(installRoot, "statusline.config.json");
32971
+ const configPath2 = resolve67(installRoot, "statusline.config.json");
32315
32972
  for (const path of [destScript, confPath, backupPath, wrapperPath, configPath2]) {
32316
32973
  try {
32317
32974
  await rm8(path, { force: true });
@@ -32471,8 +33128,8 @@ init_config2();
32471
33128
  // src/commands/cross-agent-install.ts
32472
33129
  init_fs();
32473
33130
  import { spawnSync as spawnSync6 } from "child_process";
32474
- import { dirname as dirname20, join as join15, resolve as resolve68 } from "path";
32475
- 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";
32476
33133
 
32477
33134
  // src/commands/setup-adapter.ts
32478
33135
  init_paths();
@@ -32481,7 +33138,7 @@ init_config2();
32481
33138
  init_slug();
32482
33139
  init_registry();
32483
33140
  init_renderers();
32484
- import { resolve as resolve67 } from "path";
33141
+ import { resolve as resolve68 } from "path";
32485
33142
  async function setupAdapterCommand(framework, options) {
32486
33143
  const { targets: known, warnings } = await resolveAgentTargets();
32487
33144
  const target = known.find((t) => t.id === framework);
@@ -32510,13 +33167,13 @@ async function setupAdapterCommand(framework, options) {
32510
33167
  }
32511
33168
  const config = await readConfig();
32512
33169
  const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
32513
- const projectDir = resolve67(baseDir, options.project);
32514
- const assignmentDir = resolve67(projectDir, "assignments", options.assignment);
32515
- 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");
32516
33173
  if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
32517
33174
  throw new Error(`Project "${options.project}" not found at ${projectDir}.`);
32518
33175
  }
32519
- const assignmentMdPath2 = resolve67(assignmentDir, "assignment.md");
33176
+ const assignmentMdPath2 = resolve68(assignmentDir, "assignment.md");
32520
33177
  if (!await fileExists(assignmentDir) || !await fileExists(assignmentMdPath2)) {
32521
33178
  throw new Error(
32522
33179
  `Assignment "${options.assignment}" not found at ${assignmentDir}.`
@@ -32533,7 +33190,7 @@ async function setupAdapterCommand(framework, options) {
32533
33190
  const upToDateFiles = [];
32534
33191
  const skippedFiles = [];
32535
33192
  for (const file of target.instructions.files) {
32536
- const filePath = resolve67(cwd, file.path);
33193
+ const filePath = resolve68(cwd, file.path);
32537
33194
  const content = RENDERERS[file.renderer](rendererParams);
32538
33195
  const status = await writeFileReport(filePath, content, {
32539
33196
  force: options.force
@@ -32589,7 +33246,7 @@ async function installTier3Plugin(t, opts = {}) {
32589
33246
  return "already-present";
32590
33247
  }
32591
33248
  try {
32592
- const sourceDir = resolve68(await findPackageRoot(plugin.source), plugin.source);
33249
+ const sourceDir = resolve69(await findPackageRoot(plugin.source), plugin.source);
32593
33250
  await mkdir9(dirname20(installDir), { recursive: true });
32594
33251
  await cp4(sourceDir, installDir, { recursive: true, force: true });
32595
33252
  console.log(`Tier 3 (${t.id}): installed ${plugin.kind} -> ${installDir}`);
@@ -32614,10 +33271,10 @@ function isNpxAvailable() {
32614
33271
  }
32615
33272
  }
32616
33273
  async function readAssignmentContext() {
32617
- const p = resolve68(process.cwd(), ".syntaur", "context.json");
33274
+ const p = resolve69(process.cwd(), ".syntaur", "context.json");
32618
33275
  if (!await fileExists(p)) return null;
32619
33276
  try {
32620
- return JSON.parse(await readFile46(p, "utf-8"));
33277
+ return JSON.parse(await readFile47(p, "utf-8"));
32621
33278
  } catch {
32622
33279
  return null;
32623
33280
  }
@@ -32697,7 +33354,7 @@ async function crossAgentInstallCommand(options) {
32697
33354
  if (dryRun) {
32698
33355
  for (const f of t.instructions.files) {
32699
33356
  console.log(
32700
- `${prefix}Tier 2 (${t.id}): ${resolve68(process.cwd(), f.path)}`
33357
+ `${prefix}Tier 2 (${t.id}): ${resolve69(process.cwd(), f.path)}`
32701
33358
  );
32702
33359
  }
32703
33360
  continue;
@@ -32854,7 +33511,7 @@ async function setupCommand(options) {
32854
33511
  }
32855
33512
 
32856
33513
  // src/commands/uninstall.ts
32857
- import { resolve as resolve69 } from "path";
33514
+ import { resolve as resolve70 } from "path";
32858
33515
  init_paths();
32859
33516
  function expandTargets(options) {
32860
33517
  if (options.all) {
@@ -32934,7 +33591,7 @@ async function uninstallCommand(options) {
32934
33591
  const configuredProjectDir = await getConfiguredProjectDir();
32935
33592
  await removeSyntaurData();
32936
33593
  console.log(`Removed ${syntaurRoot()}`);
32937
- if (configuredProjectDir && resolve69(configuredProjectDir) !== resolve69(syntaurRoot(), "projects")) {
33594
+ if (configuredProjectDir && resolve70(configuredProjectDir) !== resolve70(syntaurRoot(), "projects")) {
32938
33595
  console.warn(
32939
33596
  `Warning: config.md pointed to an external project directory (${configuredProjectDir}). That directory was not removed automatically.`
32940
33597
  );
@@ -32956,8 +33613,8 @@ init_session_id();
32956
33613
  init_cwd();
32957
33614
  init_session_db();
32958
33615
  init_agent_sessions();
32959
- import { resolve as resolve70 } from "path";
32960
- import { readFile as readFile47 } from "fs/promises";
33616
+ import { resolve as resolve71 } from "path";
33617
+ import { readFile as readFile48 } from "fs/promises";
32961
33618
  async function trackSessionCommand(options, deps = {}) {
32962
33619
  if (!options.agent) {
32963
33620
  throw new Error("--agent <name> is required.");
@@ -32967,7 +33624,7 @@ async function trackSessionCommand(options, deps = {}) {
32967
33624
  const cwd = process.cwd();
32968
33625
  let legacyHint;
32969
33626
  try {
32970
- const raw2 = await readFile47(resolve70(cwd, ".syntaur", "context.json"), "utf-8");
33627
+ const raw2 = await readFile48(resolve71(cwd, ".syntaur", "context.json"), "utf-8");
32971
33628
  const parsed = JSON.parse(raw2);
32972
33629
  if (typeof parsed.sessionId === "string") legacyHint = parsed.sessionId;
32973
33630
  } catch {
@@ -32982,7 +33639,7 @@ async function trackSessionCommand(options, deps = {}) {
32982
33639
  if (options.project) {
32983
33640
  const config = await readConfig();
32984
33641
  const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
32985
- const projectDir = resolve70(baseDir, options.project);
33642
+ const projectDir = resolve71(baseDir, options.project);
32986
33643
  if (!await fileExists(projectDir)) {
32987
33644
  throw new Error(
32988
33645
  `Project "${options.project}" not found at ${projectDir}.`
@@ -33115,8 +33772,8 @@ function formatInstallUrlHandlerError(err2) {
33115
33772
  init_config2();
33116
33773
  init_paths();
33117
33774
  init_fs();
33118
- import { resolve as resolve72, isAbsolute as isAbsolute8 } from "path";
33119
- 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";
33120
33777
  import { select, confirm as confirm2, input as input3 } from "@inquirer/prompts";
33121
33778
  async function browseCommand(options) {
33122
33779
  const config = await readConfig();
@@ -33185,7 +33842,7 @@ async function pickAgent2(agents) {
33185
33842
  return picked;
33186
33843
  }
33187
33844
  async function ensureWorktree(opts) {
33188
- const assignmentPath = resolve72(
33845
+ const assignmentPath = resolve73(
33189
33846
  opts.projectsDir,
33190
33847
  opts.projectSlug,
33191
33848
  "assignments",
@@ -33195,7 +33852,7 @@ async function ensureWorktree(opts) {
33195
33852
  if (!await fileExists(assignmentPath)) {
33196
33853
  return void 0;
33197
33854
  }
33198
- const content = await readFile48(assignmentPath, "utf-8");
33855
+ const content = await readFile49(assignmentPath, "utf-8");
33199
33856
  const { parseAssignmentFrontmatter: parseAssignmentFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
33200
33857
  const fm = parseAssignmentFrontmatter2(content);
33201
33858
  const { workspace } = fm;
@@ -33265,7 +33922,7 @@ async function ensureWorktree(opts) {
33265
33922
  async function runCreate(opts) {
33266
33923
  const { createWorktreeAndRecord: createWorktreeAndRecord2, GitWorktreeError: GitWorktreeError2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
33267
33924
  const expandedWorktree = expandHome(opts.worktreePath);
33268
- const absWorktree = isAbsolute8(expandedWorktree) ? expandedWorktree : resolve72(expandedWorktree);
33925
+ const absWorktree = isAbsolute8(expandedWorktree) ? expandedWorktree : resolve73(expandedWorktree);
33269
33926
  try {
33270
33927
  await createWorktreeAndRecord2({
33271
33928
  assignmentPath: opts.assignmentPath,
@@ -33294,7 +33951,7 @@ init_paths();
33294
33951
  init_fs();
33295
33952
  init_playbook();
33296
33953
  init_playbooks();
33297
- import { resolve as resolve73 } from "path";
33954
+ import { resolve as resolve74 } from "path";
33298
33955
  async function createPlaybookCommand(name, options) {
33299
33956
  if (!name.trim()) {
33300
33957
  throw new Error("Playbook name cannot be empty.");
@@ -33307,7 +33964,7 @@ async function createPlaybookCommand(name, options) {
33307
33964
  }
33308
33965
  const dir = playbooksDir();
33309
33966
  await ensureDir(dir);
33310
- const filePath = resolve73(dir, `${slug}.md`);
33967
+ const filePath = resolve74(dir, `${slug}.md`);
33311
33968
  if (await fileExists(filePath)) {
33312
33969
  throw new Error(
33313
33970
  `Playbook "${slug}" already exists at ${filePath}
@@ -33329,8 +33986,8 @@ init_paths();
33329
33986
  init_fs();
33330
33987
  init_parser();
33331
33988
  init_config2();
33332
- import { readdir as readdir26, readFile as readFile49 } from "fs/promises";
33333
- import { resolve as resolve74 } from "path";
33989
+ import { readdir as readdir26, readFile as readFile50 } from "fs/promises";
33990
+ import { resolve as resolve75 } from "path";
33334
33991
  async function listPlaybooksCommand(options = {}) {
33335
33992
  const dir = playbooksDir();
33336
33993
  if (!await fileExists(dir)) {
@@ -33345,8 +34002,8 @@ async function listPlaybooksCommand(options = {}) {
33345
34002
  );
33346
34003
  const rows = [];
33347
34004
  for (const entry of mdFiles) {
33348
- const filePath = resolve74(dir, entry.name);
33349
- const raw2 = await readFile49(filePath, "utf-8");
34005
+ const filePath = resolve75(dir, entry.name);
34006
+ const raw2 = await readFile50(filePath, "utf-8");
33350
34007
  const parsed = parsePlaybook(raw2);
33351
34008
  const slug = parsed.slug || entry.name.replace(/\.md$/, "");
33352
34009
  const disabled = disabledSet.has(slug);
@@ -33469,14 +34126,14 @@ init_fs();
33469
34126
  init_config2();
33470
34127
  init_slug();
33471
34128
  import { Command as Command2 } from "commander";
33472
- import { readFile as readFile51 } from "fs/promises";
33473
- import { resolve as resolve76 } from "path";
34129
+ import { readFile as readFile52 } from "fs/promises";
34130
+ import { resolve as resolve77 } from "path";
33474
34131
 
33475
34132
  // src/commands/bundle.ts
33476
34133
  init_paths();
33477
34134
  import { Command } from "commander";
33478
- import { mkdir as mkdir10, readFile as readFile50, readdir as readdir27, rm as rm9, writeFile as writeFile13 } from "fs/promises";
33479
- 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";
33480
34137
  init_parser2();
33481
34138
  init_fs();
33482
34139
  init_config2();
@@ -33494,7 +34151,7 @@ async function resolveBundleScope(options) {
33494
34151
  throw new Error(`Invalid project slug: "${options.project}".`);
33495
34152
  }
33496
34153
  const config = await readConfig();
33497
- const projectMd = resolve75(config.defaultProjectDir, options.project, "project.md");
34154
+ const projectMd = resolve76(config.defaultProjectDir, options.project, "project.md");
33498
34155
  if (!await fileExists(projectMd)) {
33499
34156
  throw new Error(`Project "${options.project}" not found.`);
33500
34157
  }
@@ -33564,10 +34221,10 @@ function pickNextPlanFile(planDir, existingFiles) {
33564
34221
  const m = f.match(/^plan-v(\d+)\.md$/);
33565
34222
  if (m) versions.add(parseInt(m[1], 10));
33566
34223
  }
33567
- 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 };
33568
34225
  let n = 2;
33569
34226
  while (versions.has(n)) n++;
33570
- return { target: resolve75(planDir, `plan-v${n}.md`), version: n };
34227
+ return { target: resolve76(planDir, `plan-v${n}.md`), version: n };
33571
34228
  }
33572
34229
  function dedupePreserveOrder(ids) {
33573
34230
  const out = [];
@@ -33651,7 +34308,7 @@ bundleCommand.command("new").description("Create a new bundle from 2+ existing t
33651
34308
  if (options.plan) {
33652
34309
  const planDir = bundlePlanDir(sc.todosPath, sc.scopeId, id);
33653
34310
  await ensureDir(planDir);
33654
- const target = resolve75(planDir, "plan.md");
34311
+ const target = resolve76(planDir, "plan.md");
33655
34312
  const memberLines = items.map((it) => `- ${it.description} [t:${it.id}]`).join("\n");
33656
34313
  const stub = [
33657
34314
  "---",
@@ -33827,7 +34484,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
33827
34484
  }
33828
34485
  const repository = options.repository ?? process.cwd();
33829
34486
  const parentBranch = options.parentBranch ?? "main";
33830
- const worktreePath = options.worktreePath ?? resolve75(repository, ".worktrees", options.branch);
34487
+ const worktreePath = options.worktreePath ?? resolve76(repository, ".worktrees", options.branch);
33831
34488
  const checklist = await readChecklist(sc.todosPath, sc.checklistKey);
33832
34489
  for (const memberId of bundle.todoIds) {
33833
34490
  const item = checklist.items.find((i) => i.id === memberId);
@@ -33837,8 +34494,8 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
33837
34494
  }
33838
34495
  const bundlesFilePath = bundlesPath(sc.todosPath);
33839
34496
  const checklistFilePath = checklistPath(sc.todosPath, sc.checklistKey);
33840
- const bundlesSnapshot = await fileExists(bundlesFilePath) ? await readFile50(bundlesFilePath, "utf-8") : null;
33841
- 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;
33842
34499
  const record = async () => {
33843
34500
  bundle.branch = options.branch;
33844
34501
  bundle.worktreePath = worktreePath;
@@ -33854,7 +34511,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
33854
34511
  try {
33855
34512
  await writeBundles(sc.todosPath, bundles);
33856
34513
  await writeChecklist(sc.todosPath, checklist);
33857
- const ctxDir = resolve75(worktreePath, ".syntaur");
34514
+ const ctxDir = resolve76(worktreePath, ".syntaur");
33858
34515
  await mkdir10(ctxDir, { recursive: true });
33859
34516
  const payload = {
33860
34517
  bundleId: bundle.id,
@@ -33868,7 +34525,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
33868
34525
  repository,
33869
34526
  boundAt: nowISO()
33870
34527
  };
33871
- 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");
33872
34529
  } catch (err2) {
33873
34530
  try {
33874
34531
  if (bundlesSnapshot === null) {
@@ -34058,7 +34715,7 @@ async function resolveScope(options) {
34058
34715
  throw new Error(`Invalid project slug: "${options.project}".`);
34059
34716
  }
34060
34717
  const config = await readConfig();
34061
- const projectMd = resolve76(config.defaultProjectDir, options.project, "project.md");
34718
+ const projectMd = resolve77(config.defaultProjectDir, options.project, "project.md");
34062
34719
  if (!await fileExists(projectMd)) {
34063
34720
  throw new Error(`Project "${options.project}" not found.`);
34064
34721
  }
@@ -34391,10 +35048,10 @@ todoCommand.command("archive").description("Archive completed todos and their lo
34391
35048
  (e) => e.itemIds.every((id) => completedIds.has(id))
34392
35049
  );
34393
35050
  const archFile = archivePath(todosPath, workspace, checklist.archiveInterval);
34394
- await ensureDir(resolve76(todosPath, "archive"));
35051
+ await ensureDir(resolve77(todosPath, "archive"));
34395
35052
  let archContent = "";
34396
35053
  if (await fileExists(archFile)) {
34397
- archContent = await readFile51(archFile, "utf-8");
35054
+ archContent = await readFile52(archFile, "utf-8");
34398
35055
  archContent = archContent.trimEnd() + "\n\n";
34399
35056
  } else {
34400
35057
  archContent = `---
@@ -34572,12 +35229,12 @@ function describeScope(scope) {
34572
35229
  }
34573
35230
  async function injectPromotedTodos(assignmentDir, todos, scopeLabel) {
34574
35231
  const { resolve: resolvePath2 } = await import("path");
34575
- const { readFile: readFile81 } = await import("fs/promises");
35232
+ const { readFile: readFile82 } = await import("fs/promises");
34576
35233
  const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
34577
35234
  const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
34578
35235
  const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
34579
35236
  const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
34580
- let content = await readFile81(assignmentMdPath2, "utf-8");
35237
+ let content = await readFile82(assignmentMdPath2, "utf-8");
34581
35238
  content = appendTodosToAssignmentBody2(
34582
35239
  content,
34583
35240
  todos.map((t) => ({
@@ -34628,7 +35285,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
34628
35285
  );
34629
35286
  let target;
34630
35287
  if (existingFiles.length === 0) {
34631
- target = resolve76(planDir, "plan.md");
35288
+ target = resolve77(planDir, "plan.md");
34632
35289
  } else {
34633
35290
  const versions = /* @__PURE__ */ new Set();
34634
35291
  for (const f of existingFiles) {
@@ -34638,7 +35295,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
34638
35295
  }
34639
35296
  let n = 2;
34640
35297
  while (versions.has(n)) n++;
34641
- target = resolve76(planDir, `plan-v${n}.md`);
35298
+ target = resolve77(planDir, `plan-v${n}.md`);
34642
35299
  }
34643
35300
  if (!await fileExists(target)) {
34644
35301
  const stub = `---
@@ -34832,12 +35489,12 @@ backupCommand.command("config").description("Show or update backup configuration
34832
35489
 
34833
35490
  // src/commands/doctor.ts
34834
35491
  import { Command as Command4 } from "commander";
34835
- import { readFile as readFile62 } from "fs/promises";
34836
- 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";
34837
35494
 
34838
35495
  // src/utils/doctor/index.ts
34839
35496
  import { fileURLToPath as fileURLToPath12 } from "url";
34840
- import { readFile as readFile61 } from "fs/promises";
35497
+ import { readFile as readFile63 } from "fs/promises";
34841
35498
  import { dirname as dirname25, join as join21 } from "path";
34842
35499
 
34843
35500
  // src/utils/doctor/context.ts
@@ -34845,11 +35502,11 @@ init_config2();
34845
35502
  init_paths();
34846
35503
  init_fs();
34847
35504
  import Database5 from "better-sqlite3";
34848
- import { resolve as resolve77 } from "path";
35505
+ import { resolve as resolve78 } from "path";
34849
35506
  async function buildCheckContext(cwd = process.cwd()) {
34850
35507
  const config = await readConfig();
34851
35508
  const root = syntaurRoot();
34852
- const dbPath = resolve77(root, "syntaur.db");
35509
+ const dbPath = resolve78(root, "syntaur.db");
34853
35510
  let db6 = null;
34854
35511
  let dbError = null;
34855
35512
  if (await fileExists(dbPath)) {
@@ -34883,8 +35540,8 @@ function closeCheckContext(ctx) {
34883
35540
  // src/utils/doctor/checks/env.ts
34884
35541
  init_fs();
34885
35542
  init_paths();
34886
- import { resolve as resolve78, isAbsolute as isAbsolute9 } from "path";
34887
- 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";
34888
35545
  import { fileURLToPath as fileURLToPath10 } from "url";
34889
35546
  import { dirname as dirname22, join as join17 } from "path";
34890
35547
  var CATEGORY = "env";
@@ -34894,7 +35551,7 @@ var syntaurRootExists = {
34894
35551
  title: "~/.syntaur/ directory exists",
34895
35552
  async run(ctx) {
34896
35553
  try {
34897
- const s = await stat8(ctx.syntaurRoot);
35554
+ const s = await stat9(ctx.syntaurRoot);
34898
35555
  if (!s.isDirectory()) {
34899
35556
  return err(this, `${ctx.syntaurRoot} exists but is not a directory`, [
34900
35557
  ctx.syntaurRoot
@@ -34924,7 +35581,7 @@ var configValid = {
34924
35581
  category: CATEGORY,
34925
35582
  title: "~/.syntaur/config.md is valid",
34926
35583
  async run(ctx) {
34927
- const configPath2 = resolve78(ctx.syntaurRoot, "config.md");
35584
+ const configPath2 = resolve79(ctx.syntaurRoot, "config.md");
34928
35585
  if (!await fileExists(configPath2)) {
34929
35586
  return {
34930
35587
  id: this.id,
@@ -34941,7 +35598,7 @@ var configValid = {
34941
35598
  autoFixable: false
34942
35599
  };
34943
35600
  }
34944
- const content = await readFile52(configPath2, "utf-8");
35601
+ const content = await readFile53(configPath2, "utf-8");
34945
35602
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
34946
35603
  if (!fmMatch || fmMatch[1].trim() === "") {
34947
35604
  return {
@@ -35221,7 +35878,7 @@ async function readLocalPkg() {
35221
35878
  for (let i = 0; i < 6; i++) {
35222
35879
  const candidate = join17(dir, "package.json");
35223
35880
  try {
35224
- const text = await readFile52(candidate, "utf-8");
35881
+ const text = await readFile53(candidate, "utf-8");
35225
35882
  return JSON.parse(text);
35226
35883
  } catch {
35227
35884
  dir = dirname22(dir);
@@ -35273,8 +35930,8 @@ function versionGte(a, b) {
35273
35930
 
35274
35931
  // src/utils/doctor/checks/structure.ts
35275
35932
  init_fs();
35276
- import { resolve as resolve79 } from "path";
35277
- 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";
35278
35935
  var CATEGORY2 = "structure";
35279
35936
  var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
35280
35937
  "projects",
@@ -35293,7 +35950,7 @@ var projectsDir = {
35293
35950
  category: CATEGORY2,
35294
35951
  title: "projects/ directory exists",
35295
35952
  async run(ctx) {
35296
- const p = resolve79(ctx.syntaurRoot, "projects");
35953
+ const p = resolve80(ctx.syntaurRoot, "projects");
35297
35954
  if (!await fileExists(p)) {
35298
35955
  return {
35299
35956
  id: this.id,
@@ -35318,7 +35975,7 @@ var playbooksDir2 = {
35318
35975
  category: CATEGORY2,
35319
35976
  title: "playbooks/ directory exists",
35320
35977
  async run(ctx) {
35321
- const p = resolve79(ctx.syntaurRoot, "playbooks");
35978
+ const p = resolve80(ctx.syntaurRoot, "playbooks");
35322
35979
  if (!await fileExists(p)) {
35323
35980
  return {
35324
35981
  id: this.id,
@@ -35343,7 +36000,7 @@ var todosDirValid = {
35343
36000
  category: CATEGORY2,
35344
36001
  title: "todos/ directory is readable (if present)",
35345
36002
  async run(ctx) {
35346
- const p = resolve79(ctx.syntaurRoot, "todos");
36003
+ const p = resolve80(ctx.syntaurRoot, "todos");
35347
36004
  if (!await fileExists(p)) {
35348
36005
  return {
35349
36006
  id: this.id,
@@ -35354,7 +36011,7 @@ var todosDirValid = {
35354
36011
  autoFixable: false
35355
36012
  };
35356
36013
  }
35357
- const s = await stat9(p);
36014
+ const s = await stat10(p);
35358
36015
  if (!s.isDirectory()) {
35359
36016
  return {
35360
36017
  id: this.id,
@@ -35374,7 +36031,7 @@ var serversDirValid = {
35374
36031
  category: CATEGORY2,
35375
36032
  title: "servers/ directory is readable (if present)",
35376
36033
  async run(ctx) {
35377
- const p = resolve79(ctx.syntaurRoot, "servers");
36034
+ const p = resolve80(ctx.syntaurRoot, "servers");
35378
36035
  if (!await fileExists(p)) {
35379
36036
  return {
35380
36037
  id: this.id,
@@ -35385,7 +36042,7 @@ var serversDirValid = {
35385
36042
  autoFixable: false
35386
36043
  };
35387
36044
  }
35388
- const s = await stat9(p);
36045
+ const s = await stat10(p);
35389
36046
  if (!s.isDirectory()) {
35390
36047
  return {
35391
36048
  id: this.id,
@@ -35419,7 +36076,7 @@ var knownFilesRecognized = {
35419
36076
  title: this.title,
35420
36077
  status: "warn",
35421
36078
  detail: `unexpected top-level entries: ${unexpected.join(", ")}`,
35422
- affected: unexpected.map((n) => resolve79(ctx.syntaurRoot, n)),
36079
+ affected: unexpected.map((n) => resolve80(ctx.syntaurRoot, n)),
35423
36080
  remediation: {
35424
36081
  kind: "manual",
35425
36082
  suggestion: "Review these entries \u2014 they may be leftover state from older versions",
@@ -35448,8 +36105,8 @@ function pass2(check) {
35448
36105
 
35449
36106
  // src/utils/doctor/checks/project.ts
35450
36107
  init_fs();
35451
- import { resolve as resolve80 } from "path";
35452
- 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";
35453
36110
  var CATEGORY3 = "project";
35454
36111
  var REQUIRED_PROJECT_FILES = [
35455
36112
  "project.md",
@@ -35478,10 +36135,10 @@ async function listProjects2(ctx) {
35478
36135
  for (const e of entries) {
35479
36136
  if (!e.isDirectory()) continue;
35480
36137
  if (e.name.startsWith(".") || e.name.startsWith("_")) continue;
35481
- const projectDir = resolve80(dir, e.name);
36138
+ const projectDir = resolve81(dir, e.name);
35482
36139
  let looksLikeProject = false;
35483
36140
  for (const marker of PROJECT_MARKERS) {
35484
- if (await fileExists(resolve80(projectDir, marker))) {
36141
+ if (await fileExists(resolve81(projectDir, marker))) {
35485
36142
  looksLikeProject = true;
35486
36143
  break;
35487
36144
  }
@@ -35500,7 +36157,7 @@ var requiredFiles = {
35500
36157
  for (const projectDir of projects) {
35501
36158
  const missing = [];
35502
36159
  for (const rel of REQUIRED_PROJECT_FILES) {
35503
- const p = resolve80(projectDir, rel);
36160
+ const p = resolve81(projectDir, rel);
35504
36161
  if (!await fileExists(p)) missing.push(rel);
35505
36162
  }
35506
36163
  if (missing.length === 0) continue;
@@ -35510,7 +36167,7 @@ var requiredFiles = {
35510
36167
  title: this.title,
35511
36168
  status: "error",
35512
36169
  detail: `project at ${projectDir} is missing: ${missing.join(", ")}`,
35513
- affected: missing.map((m) => resolve80(projectDir, m)),
36170
+ affected: missing.map((m) => resolve81(projectDir, m)),
35514
36171
  remediation: {
35515
36172
  kind: "manual",
35516
36173
  suggestion: "Recreate the missing scaffold files from templates",
@@ -35533,9 +36190,9 @@ var manifestStale = {
35533
36190
  const projects = await listProjects2(ctx);
35534
36191
  const results = [];
35535
36192
  for (const projectDir of projects) {
35536
- const manifestPath = resolve80(projectDir, "manifest.md");
36193
+ const manifestPath = resolve81(projectDir, "manifest.md");
35537
36194
  if (!await fileExists(manifestPath)) continue;
35538
- const manifestMtime = (await stat10(manifestPath)).mtimeMs;
36195
+ const manifestMtime = (await stat11(manifestPath)).mtimeMs;
35539
36196
  const newestAssignment = await newestAssignmentMtime(projectDir);
35540
36197
  if (newestAssignment === 0) continue;
35541
36198
  if (newestAssignment > manifestMtime) {
@@ -35582,7 +36239,7 @@ var orphanFiles = {
35582
36239
  title: this.title,
35583
36240
  status: "warn",
35584
36241
  detail: `project at ${projectDir} has unexpected entries: ${orphans.join(", ")}`,
35585
- affected: orphans.map((o) => resolve80(projectDir, o)),
36242
+ affected: orphans.map((o) => resolve81(projectDir, o)),
35586
36243
  autoFixable: false
35587
36244
  });
35588
36245
  }
@@ -35592,7 +36249,7 @@ var orphanFiles = {
35592
36249
  };
35593
36250
  var projectChecks = [requiredFiles, manifestStale, orphanFiles];
35594
36251
  async function newestAssignmentMtime(projectDir) {
35595
- const assignmentsRoot = resolve80(projectDir, "assignments");
36252
+ const assignmentsRoot = resolve81(projectDir, "assignments");
35596
36253
  if (!await fileExists(assignmentsRoot)) return 0;
35597
36254
  let newest = 0;
35598
36255
  let entries;
@@ -35603,9 +36260,9 @@ async function newestAssignmentMtime(projectDir) {
35603
36260
  }
35604
36261
  for (const e of entries) {
35605
36262
  if (!e.isDirectory()) continue;
35606
- const assignmentMd = resolve80(assignmentsRoot, e.name, "assignment.md");
36263
+ const assignmentMd = resolve81(assignmentsRoot, e.name, "assignment.md");
35607
36264
  try {
35608
- const s = await stat10(assignmentMd);
36265
+ const s = await stat11(assignmentMd);
35609
36266
  if (s.mtimeMs > newest) newest = s.mtimeMs;
35610
36267
  } catch {
35611
36268
  }
@@ -35628,8 +36285,8 @@ init_parser();
35628
36285
  init_types();
35629
36286
  init_paths();
35630
36287
  init_assignment_walk();
35631
- import { resolve as resolve81 } from "path";
35632
- 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";
35633
36290
  var CATEGORY4 = "assignment";
35634
36291
  var STATUSES_REQUIRING_HANDOFF = /* @__PURE__ */ new Set(["review", "completed"]);
35635
36292
  var PRE_WORKSPACE_STATUSES = /* @__PURE__ */ new Set([
@@ -35723,7 +36380,7 @@ var invalidStatus = {
35723
36380
  const allowed = configuredStatuses(ctx);
35724
36381
  const results = [];
35725
36382
  for (const a of withAssignmentMd) {
35726
- const path = resolve81(a.assignmentDir, "assignment.md");
36383
+ const path = resolve82(a.assignmentDir, "assignment.md");
35727
36384
  const parsed = await parseSafe4(path);
35728
36385
  if (!parsed) continue;
35729
36386
  if (!allowed.has(parsed.status)) {
@@ -35756,7 +36413,7 @@ var workspaceMissing = {
35756
36413
  const terminal = terminalStatuses(ctx);
35757
36414
  const results = [];
35758
36415
  for (const a of withAssignmentMd) {
35759
- const path = resolve81(a.assignmentDir, "assignment.md");
36416
+ const path = resolve82(a.assignmentDir, "assignment.md");
35760
36417
  const parsed = await parseSafe4(path);
35761
36418
  if (!parsed) continue;
35762
36419
  if (terminal.has(parsed.status)) continue;
@@ -35803,12 +36460,12 @@ var requiredFilesByStatus = {
35803
36460
  const { withAssignmentMd } = await listAssignments(ctx);
35804
36461
  const results = [];
35805
36462
  for (const a of withAssignmentMd) {
35806
- const assignmentPath = resolve81(a.assignmentDir, "assignment.md");
36463
+ const assignmentPath = resolve82(a.assignmentDir, "assignment.md");
35807
36464
  const parsed = await parseSafe4(assignmentPath);
35808
36465
  if (!parsed) continue;
35809
36466
  const missing = [];
35810
36467
  if (STATUSES_REQUIRING_HANDOFF.has(parsed.status)) {
35811
- const handoffPath = resolve81(a.assignmentDir, "handoff.md");
36468
+ const handoffPath = resolve82(a.assignmentDir, "handoff.md");
35812
36469
  if (!await fileExists(handoffPath)) missing.push("handoff.md");
35813
36470
  }
35814
36471
  if (missing.length === 0) continue;
@@ -35818,7 +36475,7 @@ var requiredFilesByStatus = {
35818
36475
  title: this.title,
35819
36476
  status: "warn",
35820
36477
  detail: `${a.projectSlug}/${a.assignmentSlug} (status: ${parsed.status}) is missing ${missing.join(", ")}`,
35821
- affected: missing.map((m) => resolve81(a.assignmentDir, m)),
36478
+ affected: missing.map((m) => resolve82(a.assignmentDir, m)),
35822
36479
  remediation: {
35823
36480
  kind: "manual",
35824
36481
  suggestion: `Create the missing ${missing.join(" and ")} files for this assignment`,
@@ -35841,7 +36498,7 @@ var companionFilesScaffolded = {
35841
36498
  for (const a of withAssignmentMd) {
35842
36499
  const missing = [];
35843
36500
  for (const filename of ["progress.md", "comments.md"]) {
35844
- if (!await fileExists(resolve81(a.assignmentDir, filename))) {
36501
+ if (!await fileExists(resolve82(a.assignmentDir, filename))) {
35845
36502
  missing.push(filename);
35846
36503
  }
35847
36504
  }
@@ -35853,7 +36510,7 @@ var companionFilesScaffolded = {
35853
36510
  title: this.title,
35854
36511
  status: "warn",
35855
36512
  detail: `${label} is missing ${missing.join(" and ")} (pre-v2.0 assignment \u2014 not required, but scaffolding them keeps the dashboard and CLIs consistent)`,
35856
- affected: missing.map((m) => resolve81(a.assignmentDir, m)),
36513
+ affected: missing.map((m) => resolve82(a.assignmentDir, m)),
35857
36514
  remediation: {
35858
36515
  kind: "manual",
35859
36516
  suggestion: `Create ${missing.join(" and ")} with the renderProgress/renderComments templates, or re-scaffold via the CLI`,
@@ -35886,7 +36543,7 @@ var typeDefinition = {
35886
36543
  const { withAssignmentMd } = await listAssignments(ctx);
35887
36544
  const results = [];
35888
36545
  for (const a of withAssignmentMd) {
35889
- const path = resolve81(a.assignmentDir, "assignment.md");
36546
+ const path = resolve82(a.assignmentDir, "assignment.md");
35890
36547
  const parsed = await parseSafe4(path);
35891
36548
  if (!parsed) continue;
35892
36549
  if (!parsed.type) continue;
@@ -35920,7 +36577,7 @@ var projectFrontmatterMatchesContainer = {
35920
36577
  const { withAssignmentMd } = await listAssignments(ctx);
35921
36578
  const results = [];
35922
36579
  for (const a of withAssignmentMd) {
35923
- const path = resolve81(a.assignmentDir, "assignment.md");
36580
+ const path = resolve82(a.assignmentDir, "assignment.md");
35924
36581
  const parsed = await parseSafe4(path);
35925
36582
  if (!parsed) continue;
35926
36583
  if (a.standalone) {
@@ -35971,13 +36628,13 @@ var draftMissingObjective = {
35971
36628
  const { withAssignmentMd } = await listAssignments(ctx);
35972
36629
  const results = [];
35973
36630
  for (const a of withAssignmentMd) {
35974
- const path = resolve81(a.assignmentDir, "assignment.md");
36631
+ const path = resolve82(a.assignmentDir, "assignment.md");
35975
36632
  const parsed = await parseSafe4(path);
35976
36633
  if (!parsed) continue;
35977
36634
  if (parsed.status !== "draft") continue;
35978
36635
  let raw2;
35979
36636
  try {
35980
- raw2 = await readFile53(path, "utf-8");
36637
+ raw2 = await readFile54(path, "utf-8");
35981
36638
  } catch {
35982
36639
  continue;
35983
36640
  }
@@ -36010,7 +36667,7 @@ var readyToImplementMissingPlan = {
36010
36667
  const { withAssignmentMd } = await listAssignments(ctx);
36011
36668
  const results = [];
36012
36669
  for (const a of withAssignmentMd) {
36013
- const path = resolve81(a.assignmentDir, "assignment.md");
36670
+ const path = resolve82(a.assignmentDir, "assignment.md");
36014
36671
  const parsed = await parseSafe4(path);
36015
36672
  if (!parsed) continue;
36016
36673
  if (parsed.status !== "ready_to_implement") continue;
@@ -36019,7 +36676,7 @@ var readyToImplementMissingPlan = {
36019
36676
  let hasPlanContent = false;
36020
36677
  for (const f of planFiles) {
36021
36678
  try {
36022
- const c2 = await readFile53(resolve81(a.assignmentDir, f), "utf-8");
36679
+ const c2 = await readFile54(resolve82(a.assignmentDir, f), "utf-8");
36023
36680
  if (c2.trim().length > 0) {
36024
36681
  hasPlanContent = true;
36025
36682
  break;
@@ -36035,7 +36692,7 @@ var readyToImplementMissingPlan = {
36035
36692
  title: this.title,
36036
36693
  status: "warn",
36037
36694
  detail: `${label} (status: ready_to_implement) has no plan.md or plan-v<N>.md`,
36038
- affected: [resolve81(a.assignmentDir, "plan.md")],
36695
+ affected: [resolve82(a.assignmentDir, "plan.md")],
36039
36696
  remediation: {
36040
36697
  kind: "manual",
36041
36698
  suggestion: `Write a plan with '/plan-assignment' (or 'syntaur plan'), then re-mark ready_to_implement`,
@@ -36062,7 +36719,7 @@ var assignmentChecks = [
36062
36719
  ];
36063
36720
  async function parseSafe4(path) {
36064
36721
  try {
36065
- const content = await readFile53(path, "utf-8");
36722
+ const content = await readFile54(path, "utf-8");
36066
36723
  return parseAssignmentFull(content);
36067
36724
  } catch {
36068
36725
  return null;
@@ -36081,7 +36738,7 @@ function pass4(check, detail) {
36081
36738
 
36082
36739
  // src/utils/doctor/checks/dashboard.ts
36083
36740
  init_fs();
36084
- import { resolve as resolve82 } from "path";
36741
+ import { resolve as resolve83 } from "path";
36085
36742
  var CATEGORY5 = "dashboard";
36086
36743
  var dbReachable = {
36087
36744
  id: "dashboard.db-reachable",
@@ -36095,7 +36752,7 @@ var dbReachable = {
36095
36752
  title: this.title,
36096
36753
  status: "error",
36097
36754
  detail: `could not open syntaur.db: ${ctx.dbError ?? "unknown error"}`,
36098
- affected: [resolve82(ctx.syntaurRoot, "syntaur.db")],
36755
+ affected: [resolve83(ctx.syntaurRoot, "syntaur.db")],
36099
36756
  remediation: {
36100
36757
  kind: "manual",
36101
36758
  suggestion: "Start the dashboard once (`syntaur dashboard`) to initialize the DB, or restore it from backup",
@@ -36113,7 +36770,7 @@ var dbReachable = {
36113
36770
  title: this.title,
36114
36771
  status: "error",
36115
36772
  detail: 'syntaur.db is missing the expected "sessions" table',
36116
- affected: [resolve82(ctx.syntaurRoot, "syntaur.db")],
36773
+ affected: [resolve83(ctx.syntaurRoot, "syntaur.db")],
36117
36774
  autoFixable: false
36118
36775
  };
36119
36776
  }
@@ -36125,7 +36782,7 @@ var dbReachable = {
36125
36782
  title: this.title,
36126
36783
  status: "error",
36127
36784
  detail: `syntaur.db query failed: ${err2 instanceof Error ? err2.message : String(err2)}`,
36128
- affected: [resolve82(ctx.syntaurRoot, "syntaur.db")],
36785
+ affected: [resolve83(ctx.syntaurRoot, "syntaur.db")],
36129
36786
  autoFixable: false
36130
36787
  };
36131
36788
  }
@@ -36151,7 +36808,7 @@ var ghostSessions = {
36151
36808
  const results = [];
36152
36809
  for (const row of rows) {
36153
36810
  if (!row.project_slug) continue;
36154
- const projectPath = resolve82(projectsDir2, row.project_slug, "project.md");
36811
+ const projectPath = resolve83(projectsDir2, row.project_slug, "project.md");
36155
36812
  if (!await fileExists(projectPath)) {
36156
36813
  results.push({
36157
36814
  id: this.id,
@@ -36170,7 +36827,7 @@ var ghostSessions = {
36170
36827
  continue;
36171
36828
  }
36172
36829
  if (row.assignment_slug) {
36173
- const assignmentPath = resolve82(
36830
+ const assignmentPath = resolve83(
36174
36831
  projectsDir2,
36175
36832
  row.project_slug,
36176
36833
  "assignments",
@@ -36222,8 +36879,8 @@ function skipped(check, reason) {
36222
36879
 
36223
36880
  // src/utils/doctor/checks/integrations.ts
36224
36881
  init_fs();
36225
- import { resolve as resolve83, dirname as dirname23, basename as basename8 } from "path";
36226
- 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";
36227
36884
  import { homedir as homedir14 } from "os";
36228
36885
  var CATEGORY6 = "integrations";
36229
36886
  var claudePluginLinked = {
@@ -36305,10 +36962,10 @@ var backupConfigured = {
36305
36962
  }
36306
36963
  };
36307
36964
  async function readKnownMarketplaces() {
36308
- const path = resolve83(homedir14(), ".claude", "plugins", "known_marketplaces.json");
36965
+ const path = resolve84(homedir14(), ".claude", "plugins", "known_marketplaces.json");
36309
36966
  if (!await fileExists(path)) return {};
36310
36967
  try {
36311
- const raw2 = await readFile54(path, "utf-8");
36968
+ const raw2 = await readFile55(path, "utf-8");
36312
36969
  return JSON.parse(raw2);
36313
36970
  } catch {
36314
36971
  return {};
@@ -36325,7 +36982,7 @@ var claudeMarketplaceRegistered = {
36325
36982
  return skipped2(this, "claudePluginDir does not exist (run install-plugin)");
36326
36983
  }
36327
36984
  const pluginsParent = dirname23(dir);
36328
- if (basename8(pluginsParent) !== "plugins") {
36985
+ if (basename9(pluginsParent) !== "plugins") {
36329
36986
  return {
36330
36987
  id: this.id,
36331
36988
  category: this.category,
@@ -36342,7 +36999,7 @@ var claudeMarketplaceRegistered = {
36342
36999
  };
36343
37000
  }
36344
37001
  const marketplaceRoot = dirname23(pluginsParent);
36345
- const marketplaceManifest = resolve83(marketplaceRoot, ".claude-plugin", "marketplace.json");
37002
+ const marketplaceManifest = resolve84(marketplaceRoot, ".claude-plugin", "marketplace.json");
36346
37003
  if (!await fileExists(marketplaceManifest)) {
36347
37004
  return {
36348
37005
  id: this.id,
@@ -36361,7 +37018,7 @@ var claudeMarketplaceRegistered = {
36361
37018
  }
36362
37019
  let parsed = {};
36363
37020
  try {
36364
- parsed = JSON.parse(await readFile54(marketplaceManifest, "utf-8"));
37021
+ parsed = JSON.parse(await readFile55(marketplaceManifest, "utf-8"));
36365
37022
  } catch {
36366
37023
  return {
36367
37024
  id: this.id,
@@ -36373,7 +37030,7 @@ var claudeMarketplaceRegistered = {
36373
37030
  autoFixable: false
36374
37031
  };
36375
37032
  }
36376
- const marketplaceName = parsed.name ?? basename8(marketplaceRoot);
37033
+ const marketplaceName = parsed.name ?? basename9(marketplaceRoot);
36377
37034
  const hasSyntaurEntry = (parsed.plugins ?? []).some((p) => p?.name === "syntaur");
36378
37035
  const known = await readKnownMarketplaces();
36379
37036
  const registered = known[marketplaceName]?.installLocation === marketplaceRoot || Object.values(known).some((v) => v.installLocation === marketplaceRoot);
@@ -36393,7 +37050,7 @@ var claudeMarketplaceRegistered = {
36393
37050
  title: this.title,
36394
37051
  status: "error",
36395
37052
  detail: issues.join("; "),
36396
- affected: [marketplaceManifest, resolve83(homedir14(), ".claude", "plugins", "known_marketplaces.json")],
37053
+ affected: [marketplaceManifest, resolve84(homedir14(), ".claude", "plugins", "known_marketplaces.json")],
36397
37054
  remediation: {
36398
37055
  kind: "manual",
36399
37056
  suggestion: "Re-run install-plugin to ensure both files are in sync.",
@@ -36433,8 +37090,8 @@ function skipped2(check, reason) {
36433
37090
  init_fs();
36434
37091
  init_parser();
36435
37092
  init_types();
36436
- import { resolve as resolve84 } from "path";
36437
- import { readFile as readFile55 } from "fs/promises";
37093
+ import { resolve as resolve85 } from "path";
37094
+ import { readFile as readFile56 } from "fs/promises";
36438
37095
  var CATEGORY7 = "workspace";
36439
37096
  var ASSIGNMENT_FIELDS2 = ["projectSlug", "assignmentSlug", "projectDir", "assignmentDir"];
36440
37097
  var BUNDLE_FIELDS = ["bundleId", "bundleScope", "bundleScopeId"];
@@ -36456,12 +37113,12 @@ function isStandaloneSession(ctx) {
36456
37113
  return !hasAnyAssignmentField(ctx) && !hasAnyBundleField(ctx) && hasSessionMeta;
36457
37114
  }
36458
37115
  async function loadContext(ctx) {
36459
- const path = resolve84(ctx.cwd, ".syntaur", "context.json");
37116
+ const path = resolve85(ctx.cwd, ".syntaur", "context.json");
36460
37117
  if (!await fileExists(path)) {
36461
37118
  return { data: null, path, exists: false, parseError: null };
36462
37119
  }
36463
37120
  try {
36464
- const raw2 = await readFile55(path, "utf-8");
37121
+ const raw2 = await readFile56(path, "utf-8");
36465
37122
  return { data: JSON.parse(raw2), path, exists: true, parseError: null };
36466
37123
  } catch (err2) {
36467
37124
  return {
@@ -36555,7 +37212,7 @@ var contextAssignmentResolves = {
36555
37212
  if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to resolve");
36556
37213
  if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to resolve");
36557
37214
  if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
36558
- const assignmentMd = resolve84(data.assignmentDir, "assignment.md");
37215
+ const assignmentMd = resolve85(data.assignmentDir, "assignment.md");
36559
37216
  if (!await fileExists(assignmentMd)) {
36560
37217
  return {
36561
37218
  id: this.id,
@@ -36585,10 +37242,10 @@ var contextTerminal = {
36585
37242
  if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to check");
36586
37243
  if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to check");
36587
37244
  if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
36588
- const assignmentMd = resolve84(data.assignmentDir, "assignment.md");
37245
+ const assignmentMd = resolve85(data.assignmentDir, "assignment.md");
36589
37246
  if (!await fileExists(assignmentMd)) return skipped3(this, "assignment file missing");
36590
37247
  try {
36591
- const content = await readFile55(assignmentMd, "utf-8");
37248
+ const content = await readFile56(assignmentMd, "utf-8");
36592
37249
  const parsed = parseAssignmentFull(content);
36593
37250
  const terminal = terminalStatuses2(ctx);
36594
37251
  if (terminal.has(parsed.status)) {
@@ -36734,15 +37391,15 @@ var agentChecks = [agentsResolvable];
36734
37391
  // src/utils/doctor/checks/terminal.ts
36735
37392
  init_config2();
36736
37393
  import { spawnSync as spawnSync9 } from "child_process";
36737
- import { readFile as readFile56 } from "fs/promises";
36738
- import { resolve as resolve85 } from "path";
37394
+ import { readFile as readFile57 } from "fs/promises";
37395
+ import { resolve as resolve86 } from "path";
36739
37396
  init_paths();
36740
37397
  init_fs();
36741
37398
  var CATEGORY9 = "terminal";
36742
37399
  async function readRawTerminalKey() {
36743
- const configPath2 = resolve85(syntaurRoot(), "config.md");
37400
+ const configPath2 = resolve86(syntaurRoot(), "config.md");
36744
37401
  if (!await fileExists(configPath2)) return null;
36745
- const content = await readFile56(configPath2, "utf-8");
37402
+ const content = await readFile57(configPath2, "utf-8");
36746
37403
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
36747
37404
  if (!fmMatch) return null;
36748
37405
  const line = fmMatch[1].split("\n").find((l) => /^terminal:\s*/.test(l));
@@ -36892,13 +37549,13 @@ var terminalChecks = [
36892
37549
 
36893
37550
  // src/utils/doctor/checks/skills.ts
36894
37551
  init_fs();
36895
- import { resolve as resolve86, join as join18 } from "path";
36896
- 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";
36897
37554
  import { homedir as homedir15 } from "os";
36898
37555
  var CATEGORY10 = "skills";
36899
37556
  var skillTargets = [
36900
- { agent: "claude", dir: resolve86(homedir15(), ".claude", "skills"), label: "~/.claude/skills" },
36901
- { 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" }
36902
37559
  ];
36903
37560
  var skillsDedupCheck = {
36904
37561
  id: "skills.dedup",
@@ -36922,7 +37579,7 @@ var skillsDedupCheck = {
36922
37579
  if (!KNOWN_SKILLS.includes(entry.name)) continue;
36923
37580
  const skillMd = join18(dir, entry.name, "SKILL.md");
36924
37581
  if (!await fileExists(skillMd)) continue;
36925
- const content = await readFile57(skillMd, "utf-8").catch(() => "");
37582
+ const content = await readFile58(skillMd, "utf-8").catch(() => "");
36926
37583
  const match = content.match(/^name:\s*(\S+)\s*$/m);
36927
37584
  if (!match || match[1] !== entry.name) continue;
36928
37585
  let isSymlink2 = false;
@@ -36972,12 +37629,12 @@ var skillsChecks = [skillsDedupCheck];
36972
37629
 
36973
37630
  // src/utils/doctor/checks/cross-agent.ts
36974
37631
  init_fs();
36975
- import { join as join19, resolve as resolve87 } from "path";
36976
- 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";
36977
37634
 
36978
37635
  // src/utils/skill-frontmatter.ts
36979
37636
  import { createHash as createHash4 } from "crypto";
36980
- import { readFile as readFile58 } from "fs/promises";
37637
+ import { readFile as readFile59 } from "fs/promises";
36981
37638
  function stripQuotes(raw2) {
36982
37639
  const t = raw2.trim();
36983
37640
  if (t.length >= 2 && (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'"))) {
@@ -37021,7 +37678,7 @@ function readSkillIdentity(skillMdText) {
37021
37678
  return { name, hasDescription };
37022
37679
  }
37023
37680
  async function sha256File(path) {
37024
- return createHash4("sha256").update(await readFile58(path)).digest("hex");
37681
+ return createHash4("sha256").update(await readFile59(path)).digest("hex");
37025
37682
  }
37026
37683
 
37027
37684
  // src/utils/doctor/checks/cross-agent.ts
@@ -37038,7 +37695,7 @@ async function checkTargetSkillsIntegrity(installedDir, canonicalSkillsDir, know
37038
37695
  }
37039
37696
  let text;
37040
37697
  try {
37041
- text = await readFile59(installedPath, "utf-8");
37698
+ text = await readFile60(installedPath, "utf-8");
37042
37699
  } catch {
37043
37700
  problems.push({ skill, kind: "invalid-frontmatter" });
37044
37701
  continue;
@@ -37115,7 +37772,7 @@ var crossAgentSkillsCheck = {
37115
37772
  }
37116
37773
  if (recorded && t.instructions) {
37117
37774
  for (const f of t.instructions.files) {
37118
- const p = resolve87(ctx.cwd, f.path);
37775
+ const p = resolve88(ctx.cwd, f.path);
37119
37776
  if (!await fileExists(p)) {
37120
37777
  problems.push(`${t.displayName}: missing protocol file ${f.path} in cwd`);
37121
37778
  affected.push(p);
@@ -37191,7 +37848,7 @@ var crossAgentChecks = [crossAgentSkillsCheck];
37191
37848
  // src/utils/doctor/checks/bundles.ts
37192
37849
  init_fs();
37193
37850
  init_paths();
37194
- import { resolve as resolve88 } from "path";
37851
+ import { resolve as resolve89 } from "path";
37195
37852
  import { readdir as readdir33 } from "fs/promises";
37196
37853
  import { spawnSync as spawnSync10 } from "child_process";
37197
37854
  init_parser2();
@@ -37221,7 +37878,7 @@ async function listScopes(ctx) {
37221
37878
  if (!e.isDirectory()) continue;
37222
37879
  const slug = e.name;
37223
37880
  if (typeof slug !== "string" || slug.startsWith(".")) continue;
37224
- const projectMd = resolve88(ctx.config.defaultProjectDir, slug, "project.md");
37881
+ const projectMd = resolve89(ctx.config.defaultProjectDir, slug, "project.md");
37225
37882
  if (!await fileExists(projectMd)) continue;
37226
37883
  out.push({
37227
37884
  scopeLabel: `project:${slug}`,
@@ -37414,7 +38071,7 @@ var bundleChecks = [
37414
38071
  ];
37415
38072
 
37416
38073
  // src/utils/doctor/checks/plugin.ts
37417
- import { readFile as readFile60 } from "fs/promises";
38074
+ import { readFile as readFile61 } from "fs/promises";
37418
38075
  import { dirname as dirname24, join as join20 } from "path";
37419
38076
  import { fileURLToPath as fileURLToPath11 } from "url";
37420
38077
  var CATEGORY13 = "plugin";
@@ -37424,7 +38081,7 @@ async function readCliVersion() {
37424
38081
  let dir = dirname24(fileURLToPath11(import.meta.url));
37425
38082
  for (let i = 0; i < 8; i += 1) {
37426
38083
  try {
37427
- 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"));
37428
38085
  if (typeof parsed.version === "string" && parsed.version.length > 0) return parsed.version;
37429
38086
  } catch {
37430
38087
  }
@@ -37532,6 +38189,68 @@ var deriveConfigValid = {
37532
38189
  };
37533
38190
  var deriveConfigChecks = [deriveConfigValid];
37534
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
+
37535
38254
  // src/utils/doctor/registry.ts
37536
38255
  function allChecks() {
37537
38256
  return [
@@ -37548,7 +38267,8 @@ function allChecks() {
37548
38267
  ...crossAgentChecks,
37549
38268
  ...bundleChecks,
37550
38269
  ...pluginChecks,
37551
- ...deriveConfigChecks
38270
+ ...deriveConfigChecks,
38271
+ ...stalenessChecks
37552
38272
  ];
37553
38273
  }
37554
38274
 
@@ -37634,7 +38354,7 @@ async function readVersion() {
37634
38354
  let dir = dirname25(here);
37635
38355
  for (let i = 0; i < 6; i++) {
37636
38356
  try {
37637
- const raw2 = await readFile61(join21(dir, "package.json"), "utf-8");
38357
+ const raw2 = await readFile63(join21(dir, "package.json"), "utf-8");
37638
38358
  const parsed = JSON.parse(raw2);
37639
38359
  return typeof parsed.version === "string" ? parsed.version : null;
37640
38360
  } catch {
@@ -37731,7 +38451,7 @@ var REQUIRED_WORKSPACE_FIELDS = [
37731
38451
  ];
37732
38452
  var ISO_DATE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
37733
38453
  async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
37734
- const absolute = isAbsolute11(inputPath) ? inputPath : resolve89(cwd, inputPath);
38454
+ const absolute = isAbsolute11(inputPath) ? inputPath : resolve91(cwd, inputPath);
37735
38455
  const errors = [];
37736
38456
  const warnings = [];
37737
38457
  if (!await fileExists(absolute)) {
@@ -37744,7 +38464,7 @@ async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
37744
38464
  }
37745
38465
  let content;
37746
38466
  try {
37747
- content = await readFile62(absolute, "utf-8");
38467
+ content = await readFile64(absolute, "utf-8");
37748
38468
  } catch (err2) {
37749
38469
  return {
37750
38470
  ok: false,
@@ -38111,8 +38831,8 @@ init_assignment_resolver();
38111
38831
  init_frontmatter();
38112
38832
  init_event_emit();
38113
38833
  init_templates();
38114
- import { resolve as resolve90 } from "path";
38115
- import { readFile as readFile63 } from "fs/promises";
38834
+ import { resolve as resolve92 } from "path";
38835
+ import { readFile as readFile65 } from "fs/promises";
38116
38836
  function shortId() {
38117
38837
  return generateId().split("-")[0];
38118
38838
  }
@@ -38143,7 +38863,7 @@ async function commentCommand(target, text, options = {}) {
38143
38863
  if (!isValidSlug(target)) {
38144
38864
  throw new Error(`Invalid assignment slug "${target}".`);
38145
38865
  }
38146
- assignmentDir = resolve90(baseDir, options.project, "assignments", target);
38866
+ assignmentDir = resolve92(baseDir, options.project, "assignments", target);
38147
38867
  assignmentRef = target;
38148
38868
  projectSlug = options.project;
38149
38869
  } else {
@@ -38155,13 +38875,13 @@ async function commentCommand(target, text, options = {}) {
38155
38875
  assignmentRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
38156
38876
  projectSlug = resolved.projectSlug;
38157
38877
  }
38158
- const commentsPath = resolve90(assignmentDir, "comments.md");
38878
+ const commentsPath = resolve92(assignmentDir, "comments.md");
38159
38879
  const timestamp = nowTimestamp();
38160
38880
  const author = options.author ?? process.env.USER ?? "unknown";
38161
38881
  let currentContent;
38162
38882
  let currentCount = 0;
38163
38883
  if (await fileExists(commentsPath)) {
38164
- currentContent = await readFile63(commentsPath, "utf-8");
38884
+ currentContent = await readFile65(commentsPath, "utf-8");
38165
38885
  const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
38166
38886
  if (countMatch) currentCount = parseInt(countMatch[1], 10);
38167
38887
  } else {
@@ -38189,9 +38909,9 @@ ${entry}`;
38189
38909
  }
38190
38910
  await writeFileForce(commentsPath, next);
38191
38911
  try {
38192
- const assignmentMd = resolve90(assignmentDir, "assignment.md");
38912
+ const assignmentMd = resolve92(assignmentDir, "assignment.md");
38193
38913
  if (await fileExists(assignmentMd)) {
38194
- const fm = parseAssignmentFrontmatter(await readFile63(assignmentMd, "utf-8"));
38914
+ const fm = parseAssignmentFrontmatter(await readFile65(assignmentMd, "utf-8"));
38195
38915
  emitEvent({
38196
38916
  assignmentId: fm.id,
38197
38917
  projectSlug,
@@ -38215,170 +38935,15 @@ ${entry}`;
38215
38935
  }
38216
38936
 
38217
38937
  // src/commands/capture.ts
38218
- import { resolve as resolve94, relative as relative4, dirname as dirname26 } from "path";
38219
- 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";
38220
38940
  import { existsSync as existsSync7 } from "fs";
38221
-
38222
- // src/utils/assignment-target.ts
38223
- init_paths();
38224
- init_fs();
38225
- init_config2();
38226
- init_slug();
38227
- init_assignment_resolver();
38228
- init_parser();
38229
- import { resolve as resolve91 } from "path";
38230
- import { readFile as readFile64 } from "fs/promises";
38231
- var AssignmentTargetError = class extends Error {
38232
- };
38233
- function classifyContext(ctx) {
38234
- if (!ctx) return "empty";
38235
- const hasAssignment = Boolean(ctx.assignmentDir) || Boolean(ctx.assignmentSlug) || Boolean(ctx.projectSlug);
38236
- if (hasAssignment) return "assignment";
38237
- if (ctx.bundleId) return "bundle";
38238
- if (ctx.sessionId || ctx.transcriptPath) return "standalone";
38239
- return "empty";
38240
- }
38241
- async function readAssignmentFrontmatterId(assignmentDir) {
38242
- const path = resolve91(assignmentDir, "assignment.md");
38243
- if (!await fileExists(path)) return null;
38244
- try {
38245
- const content = await readFile64(path, "utf-8");
38246
- const [fm] = extractFrontmatter(content);
38247
- return getField(fm, "id");
38248
- } catch {
38249
- return null;
38250
- }
38251
- }
38252
- async function readContextJson(cwd) {
38253
- const path = resolve91(cwd, ".syntaur", "context.json");
38254
- if (!await fileExists(path)) return null;
38255
- try {
38256
- const raw2 = await readFile64(path, "utf-8");
38257
- return JSON.parse(raw2);
38258
- } catch {
38259
- return null;
38260
- }
38261
- }
38262
- async function resolveAssignmentTarget(input4, opts = {}) {
38263
- const config = await readConfig();
38264
- const baseDir = opts.dir ? expandHome(opts.dir) : config.defaultProjectDir;
38265
- if (opts.project) {
38266
- if (!input4) {
38267
- throw new AssignmentTargetError(
38268
- "--project requires an assignment slug as a positional argument."
38269
- );
38270
- }
38271
- if (!isValidSlug(opts.project)) {
38272
- throw new AssignmentTargetError(`Invalid project slug "${opts.project}".`);
38273
- }
38274
- if (!isValidSlug(input4)) {
38275
- throw new AssignmentTargetError(`Invalid assignment slug "${input4}".`);
38276
- }
38277
- const projectDir = resolve91(baseDir, opts.project);
38278
- const projectMdPath = resolve91(projectDir, "project.md");
38279
- if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
38280
- throw new AssignmentTargetError(
38281
- `Project "${opts.project}" not found at ${projectDir}.`
38282
- );
38283
- }
38284
- const assignmentDir = resolve91(projectDir, "assignments", input4);
38285
- const assignmentMdPath2 = resolve91(assignmentDir, "assignment.md");
38286
- if (!await fileExists(assignmentMdPath2)) {
38287
- throw new AssignmentTargetError(
38288
- `Assignment "${input4}" not found in project "${opts.project}".`
38289
- );
38290
- }
38291
- const id = await readAssignmentFrontmatterId(assignmentDir) ?? input4;
38292
- return {
38293
- assignmentDir,
38294
- projectSlug: opts.project,
38295
- assignmentSlug: input4,
38296
- id,
38297
- standalone: false,
38298
- workspaceGroup: null
38299
- };
38300
- }
38301
- if (input4) {
38302
- const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), input4);
38303
- if (!resolved) {
38304
- throw new AssignmentTargetError(
38305
- `Assignment "${input4}" not found. Provide --project <slug> + <slug> or a valid standalone UUID.`
38306
- );
38307
- }
38308
- return resolved;
38309
- }
38310
- const cwd = opts.cwd ?? process.cwd();
38311
- const ctx = await readContextJson(cwd);
38312
- if (!ctx) {
38313
- throw new AssignmentTargetError(
38314
- "No assignment specified. Provide an argument, --project + slug, or run from a directory with .syntaur/context.json."
38315
- );
38316
- }
38317
- if (classifyContext(ctx) === "bundle" && ctx.bundleId) {
38318
- throw new AssignmentTargetError(
38319
- `Context is bound to bundle b:${ctx.bundleId}, not an assignment. Use \`syntaur todo bundle show ${ctx.bundleId}\` or the complete-bundle skill.`
38320
- );
38321
- }
38322
- if (ctx.assignmentDir) {
38323
- const dir = expandHome(ctx.assignmentDir);
38324
- const assignmentMdPath2 = resolve91(dir, "assignment.md");
38325
- if (!await fileExists(assignmentMdPath2)) {
38326
- throw new AssignmentTargetError(
38327
- `.syntaur/context.json points to a missing assignment dir: ${dir}.`
38328
- );
38329
- }
38330
- const id = await readAssignmentFrontmatterId(dir);
38331
- if (!id || id.trim() === "") {
38332
- throw new AssignmentTargetError(
38333
- `.syntaur/context.json points to an assignment with no frontmatter \`id\`: ${dir}.`
38334
- );
38335
- }
38336
- const assignmentSlug = ctx.assignmentSlug ?? dir.split("/").pop() ?? "";
38337
- const projectSlug = ctx.projectSlug ?? null;
38338
- return {
38339
- assignmentDir: dir,
38340
- projectSlug,
38341
- assignmentSlug,
38342
- id,
38343
- standalone: projectSlug === null,
38344
- workspaceGroup: null
38345
- };
38346
- }
38347
- if (ctx.projectSlug && ctx.assignmentSlug) {
38348
- if (!isValidSlug(ctx.projectSlug) || !isValidSlug(ctx.assignmentSlug)) {
38349
- throw new AssignmentTargetError(
38350
- `.syntaur/context.json contains invalid slugs: project="${ctx.projectSlug}" assignment="${ctx.assignmentSlug}".`
38351
- );
38352
- }
38353
- const assignmentDir = resolve91(baseDir, ctx.projectSlug, "assignments", ctx.assignmentSlug);
38354
- const assignmentMdPath2 = resolve91(assignmentDir, "assignment.md");
38355
- if (!await fileExists(assignmentMdPath2)) {
38356
- throw new AssignmentTargetError(
38357
- `.syntaur/context.json points to a missing assignment: ${assignmentDir}.`
38358
- );
38359
- }
38360
- const id = await readAssignmentFrontmatterId(assignmentDir) ?? ctx.assignmentSlug;
38361
- return {
38362
- assignmentDir,
38363
- projectSlug: ctx.projectSlug,
38364
- assignmentSlug: ctx.assignmentSlug,
38365
- id,
38366
- standalone: false,
38367
- workspaceGroup: null
38368
- };
38369
- }
38370
- throw new AssignmentTargetError(
38371
- ".syntaur/context.json exists but contains neither assignmentDir nor projectSlug+assignmentSlug."
38372
- );
38373
- }
38374
-
38375
- // src/commands/capture.ts
38376
38941
  init_paths();
38377
38942
  init_fs();
38378
38943
 
38379
38944
  // src/utils/screencapture.ts
38380
38945
  import { spawn as spawn8 } from "child_process";
38381
- 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";
38382
38947
  import { tmpdir as tmpdir3 } from "os";
38383
38948
  import { join as join22 } from "path";
38384
38949
  function argsFor(mode, pngPath) {
@@ -38436,7 +39001,7 @@ async function captureScreenshot(mode) {
38436
39001
  }
38437
39002
  let size = 0;
38438
39003
  try {
38439
- size = (await stat11(pngPath)).size;
39004
+ size = (await stat12(pngPath)).size;
38440
39005
  } catch {
38441
39006
  throw new Error("screencapture exited 0 but produced no image.");
38442
39007
  }
@@ -38452,7 +39017,7 @@ async function captureScreenshot(mode) {
38452
39017
 
38453
39018
  // src/utils/asciinema.ts
38454
39019
  import { spawn as spawn9 } from "child_process";
38455
- 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";
38456
39021
  import { tmpdir as tmpdir4 } from "os";
38457
39022
  import { join as join23 } from "path";
38458
39023
  var SAFE_RE = /^[A-Za-z0-9_@%+=:,./-]+$/;
@@ -38515,7 +39080,7 @@ async function captureAsciinema(opts) {
38515
39080
  }
38516
39081
  throw err2;
38517
39082
  }
38518
- const text = await readFile65(castPath, "utf8").catch(() => null);
39083
+ const text = await readFile66(castPath, "utf8").catch(() => null);
38519
39084
  if (text === null) {
38520
39085
  throw new Error(
38521
39086
  `asciinema produced no cast file at ${castPath} (exit ${exitCode}). Try running 'asciinema rec ${castPath}' directly to diagnose.`
@@ -38543,9 +39108,9 @@ async function captureAsciinema(opts) {
38543
39108
  // src/utils/recording.ts
38544
39109
  init_paths();
38545
39110
  import { spawn as spawn10 } from "child_process";
38546
- 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";
38547
39112
  import { tmpdir as tmpdir5 } from "os";
38548
- import { join as join24, resolve as resolve92 } from "path";
39113
+ import { join as join24, resolve as resolve93 } from "path";
38549
39114
  import { setTimeout as sleep } from "timers/promises";
38550
39115
  function sigintPollIntervalMs() {
38551
39116
  const raw2 = process.env.SYNTAUR_RECORDING_POLL_INTERVAL_MS;
@@ -38566,13 +39131,13 @@ function sigtermWaitMs() {
38566
39131
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : 1e3;
38567
39132
  }
38568
39133
  function pidfilePath() {
38569
- return resolve92(syntaurRoot(), "recording.pid");
39134
+ return resolve93(syntaurRoot(), "recording.pid");
38570
39135
  }
38571
39136
  function logPath2() {
38572
- return resolve92(syntaurRoot(), "recording.log");
39137
+ return resolve93(syntaurRoot(), "recording.log");
38573
39138
  }
38574
39139
  function sidecarPath() {
38575
- return resolve92(syntaurRoot(), "recording.json");
39140
+ return resolve93(syntaurRoot(), "recording.json");
38576
39141
  }
38577
39142
  function ffmpegArgs(device, fps, mp4Path) {
38578
39143
  return [
@@ -38617,7 +39182,7 @@ async function acquirePidfile(pidfile) {
38617
39182
  } catch (err2) {
38618
39183
  if (err2.code !== "EEXIST") throw err2;
38619
39184
  if (attempt === 1) throw err2;
38620
- const existing = (await readFile66(pidfile, "utf-8").catch(() => "")).trim();
39185
+ const existing = (await readFile67(pidfile, "utf-8").catch(() => "")).trim();
38621
39186
  if (existing.startsWith(STARTING_SENTINEL_PREFIX)) {
38622
39187
  const parentPidRaw = existing.slice(STARTING_SENTINEL_PREFIX.length);
38623
39188
  const parentPid = Number.parseInt(parentPidRaw, 10);
@@ -38719,7 +39284,7 @@ async function startRecording(input4) {
38719
39284
  logHandle = null;
38720
39285
  if (warmupMs > 0) await sleep(warmupMs);
38721
39286
  if (!await isProcessAlive(pid)) {
38722
- 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(() => "");
38723
39288
  acquiredPid = null;
38724
39289
  throw new Error(
38725
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}
@@ -38776,7 +39341,7 @@ ${tail}`
38776
39341
  async function stopRecording() {
38777
39342
  const pidfile = pidfilePath();
38778
39343
  const sidecar = sidecarPath();
38779
- const pidRaw = await readFile66(pidfile, "utf-8").catch(() => null);
39344
+ const pidRaw = await readFile67(pidfile, "utf-8").catch(() => null);
38780
39345
  if (pidRaw === null) {
38781
39346
  throw new Error(
38782
39347
  `No active recording found (no pidfile at ${pidfile}). Did you run --start?`
@@ -38786,7 +39351,7 @@ async function stopRecording() {
38786
39351
  if (!Number.isInteger(pid) || pid <= 0) {
38787
39352
  throw new Error(`Pidfile at ${pidfile} is corrupt (got "${pidRaw}").`);
38788
39353
  }
38789
- const sidecarRaw = await readFile66(sidecar, "utf-8").catch(() => null);
39354
+ const sidecarRaw = await readFile67(sidecar, "utf-8").catch(() => null);
38790
39355
  if (sidecarRaw === null) {
38791
39356
  throw new Error(
38792
39357
  `No recording sidecar at ${sidecar}. The recording state is inconsistent \u2014 delete ${pidfile} and re-run --start.`
@@ -38831,7 +39396,7 @@ async function stopRecording() {
38831
39396
  if (alive) {
38832
39397
  throw new Error(`ffmpeg (PID ${pid}) refused to die after SIGKILL`);
38833
39398
  }
38834
- const st = await stat12(sidecarData.mp4Path).catch(() => null);
39399
+ const st = await stat13(sidecarData.mp4Path).catch(() => null);
38835
39400
  if (st === null || st.size === 0) {
38836
39401
  await unlink12(pidfile).catch(() => {
38837
39402
  });
@@ -38851,7 +39416,7 @@ async function stopRecording() {
38851
39416
  // src/db/proof-db.ts
38852
39417
  init_paths();
38853
39418
  import Database6 from "better-sqlite3";
38854
- import { resolve as resolve93 } from "path";
39419
+ import { resolve as resolve94 } from "path";
38855
39420
  var db5 = null;
38856
39421
  var PROOF_SCHEMA_VERSION = "1";
38857
39422
  var SCHEMA_SQL5 = `
@@ -38871,7 +39436,7 @@ CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);
38871
39436
  `;
38872
39437
  function initProofDb(dbPath) {
38873
39438
  if (db5) return db5;
38874
- const finalPath = dbPath ?? resolve93(syntaurRoot(), "syntaur.db");
39439
+ const finalPath = dbPath ?? resolve94(syntaurRoot(), "syntaur.db");
38875
39440
  db5 = new Database6(finalPath);
38876
39441
  db5.pragma("journal_mode = WAL");
38877
39442
  db5.exec(SCHEMA_SQL5);
@@ -38919,7 +39484,7 @@ function listArtifactsByAssignment(assignmentId) {
38919
39484
 
38920
39485
  // src/utils/transcribers/elevenlabs.ts
38921
39486
  import { spawn as spawn11 } from "child_process";
38922
- 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";
38923
39488
  import { tmpdir as tmpdir6 } from "os";
38924
39489
  import { join as join25 } from "path";
38925
39490
  var SCRIBE_URL = "https://api.elevenlabs.io/v1/speech-to-text";
@@ -38983,7 +39548,7 @@ async function extractAudio(videoAbsPath, wavOut) {
38983
39548
  throw new TranscribeFfmpegError(`ffmpeg failed (exit ${result.code}): ${tail}`);
38984
39549
  }
38985
39550
  async function callScribe(wavPath, apiKey, opts) {
38986
- const audio = await readFile67(wavPath);
39551
+ const audio = await readFile68(wavPath);
38987
39552
  const form = new FormData();
38988
39553
  form.set("file", new Blob([new Uint8Array(audio)], { type: "audio/wav" }), "audio.wav");
38989
39554
  form.set("model_id", "scribe_v1");
@@ -39299,7 +39864,7 @@ async function captureCommand(target, options = {}) {
39299
39864
  });
39300
39865
  }
39301
39866
  if (options.file) {
39302
- 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);
39303
39868
  if (!await fileExists(expanded)) {
39304
39869
  throw new Error(`--file does not exist: ${options.file}`);
39305
39870
  }
@@ -39311,7 +39876,7 @@ async function captureCommand(target, options = {}) {
39311
39876
  `--file is unreadable: ${options.file} (${e instanceof Error ? e.message : String(e)})`
39312
39877
  );
39313
39878
  }
39314
- const st = await stat13(real);
39879
+ const st = await stat14(real);
39315
39880
  if (!st.isFile()) {
39316
39881
  throw new Error(`--file is not a regular file: ${options.file}`);
39317
39882
  }
@@ -39340,7 +39905,7 @@ async function captureCommand(target, options = {}) {
39340
39905
  }
39341
39906
  initProofDb();
39342
39907
  const subdir = criterionIndex === null ? "untagged" : String(criterionIndex);
39343
- const destDir = resolve94(proofDir(resolved.assignmentDir), subdir);
39908
+ const destDir = resolve95(proofDir(resolved.assignmentDir), subdir);
39344
39909
  if (resolvedSource) await mkdir12(destDir, { recursive: true });
39345
39910
  const ext = resolvedSource ? extensionForKind(kind) : null;
39346
39911
  let id = null;
@@ -39349,7 +39914,7 @@ async function captureCommand(target, options = {}) {
39349
39914
  let lastErr = null;
39350
39915
  for (let attempt = 0; attempt < MAX_ID_RETRIES; attempt += 1) {
39351
39916
  const candidate = generateArtifactId();
39352
- const candidateAbsPath = resolvedSource && ext ? resolve94(destDir, `${candidate}.${ext}`) : null;
39917
+ const candidateAbsPath = resolvedSource && ext ? resolve95(destDir, `${candidate}.${ext}`) : null;
39353
39918
  const candidateRel = candidateAbsPath ? relative4(resolved.assignmentDir, candidateAbsPath) : null;
39354
39919
  try {
39355
39920
  insertArtifact({
@@ -39393,7 +39958,7 @@ async function captureCommand(target, options = {}) {
39393
39958
  }
39394
39959
  }
39395
39960
  if (options.transcribe && kind === "video" && absPath && id) {
39396
- const sidecarPath2 = resolve94(destDir, `${id}.transcript.md`);
39961
+ const sidecarPath2 = resolve95(destDir, `${id}.transcript.md`);
39397
39962
  if (existsSync7(sidecarPath2)) {
39398
39963
  console.warn(
39399
39964
  `transcript: ${sidecarPath2} already exists, skipping (delete to re-transcribe)`
@@ -39425,7 +39990,7 @@ async function captureCommand(target, options = {}) {
39425
39990
  const tagSuffix = criterionIndex === null ? "untagged" : `criterion ${criterionIndex}`;
39426
39991
  console.log(`Captured artifact ${id} (${kind}) for ${ref} \u2014 ${tagSuffix}.`);
39427
39992
  if (relativeFilePath) {
39428
- console.log(` file: ${resolve94(resolved.assignmentDir, relativeFilePath)}`);
39993
+ console.log(` file: ${resolve95(resolved.assignmentDir, relativeFilePath)}`);
39429
39994
  }
39430
39995
  } catch (err2) {
39431
39996
  if (options.stop && kind === "video" && shelloutCleanup && resolvedSource) {
@@ -39448,8 +40013,8 @@ async function captureCommand(target, options = {}) {
39448
40013
 
39449
40014
  // src/commands/proof.ts
39450
40015
  import { Command as Command6 } from "commander";
39451
- import { readFile as readFile68, writeFile as writeFile16, rename as rename10, stat as stat14 } from "fs/promises";
39452
- 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";
39453
40018
  import { randomBytes as randomBytes4 } from "crypto";
39454
40019
 
39455
40020
  // src/utils/acceptance-criteria-parse.ts
@@ -39689,11 +40254,11 @@ function renderProofHtml(params2, inlineFiles = /* @__PURE__ */ new Map(), trans
39689
40254
 
39690
40255
  // src/commands/proof.ts
39691
40256
  async function readAssignmentMeta(assignmentDir) {
39692
- const path = resolve95(assignmentDir, "assignment.md");
40257
+ const path = resolve96(assignmentDir, "assignment.md");
39693
40258
  if (!await fileExists(path)) {
39694
40259
  return { title: "", body: "" };
39695
40260
  }
39696
- const content = await readFile68(path, "utf-8");
40261
+ const content = await readFile69(path, "utf-8");
39697
40262
  const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
39698
40263
  let title = "";
39699
40264
  if (fmMatch) {
@@ -39740,7 +40305,7 @@ async function loadInlineFiles(rows, assignmentDir) {
39740
40305
  for (const r of rows) {
39741
40306
  if (!r.file_path) continue;
39742
40307
  if (r.kind !== "http" && r.kind !== "text") continue;
39743
- const abs = resolve95(assignmentDir, r.file_path);
40308
+ const abs = resolve96(assignmentDir, r.file_path);
39744
40309
  if (!isWithin(proofRoot, abs)) {
39745
40310
  out.set(r.file_path, null);
39746
40311
  continue;
@@ -39749,13 +40314,13 @@ async function loadInlineFiles(rows, assignmentDir) {
39749
40314
  out.set(r.file_path, null);
39750
40315
  continue;
39751
40316
  }
39752
- const st = await stat14(abs);
40317
+ const st = await stat15(abs);
39753
40318
  if (st.size > INLINE_TEXT_LIMIT_BYTES) {
39754
40319
  out.set(r.file_path, null);
39755
40320
  continue;
39756
40321
  }
39757
40322
  try {
39758
- out.set(r.file_path, await readFile68(abs, "utf-8"));
40323
+ out.set(r.file_path, await readFile69(abs, "utf-8"));
39759
40324
  } catch {
39760
40325
  out.set(r.file_path, null);
39761
40326
  }
@@ -39767,14 +40332,14 @@ async function loadTranscriptSidecars(rows, assignmentDir) {
39767
40332
  const proofRoot = proofDir(assignmentDir);
39768
40333
  for (const r of rows) {
39769
40334
  if (r.kind !== "video" || !r.file_path) continue;
39770
- const videoAbs = resolve95(assignmentDir, r.file_path);
39771
- 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`);
39772
40337
  if (!isWithin(proofRoot, sidecar)) continue;
39773
40338
  if (!await fileExists(sidecar)) continue;
39774
- const st = await stat14(sidecar);
40339
+ const st = await stat15(sidecar);
39775
40340
  if (st.size > INLINE_TEXT_LIMIT_BYTES) continue;
39776
40341
  try {
39777
- out.set(r.id, await readFile68(sidecar, "utf-8"));
40342
+ out.set(r.id, await readFile69(sidecar, "utf-8"));
39778
40343
  } catch {
39779
40344
  }
39780
40345
  }
@@ -39808,8 +40373,8 @@ async function proofBuildCommand(target, options = {}) {
39808
40373
  };
39809
40374
  const md = renderProofMarkdown(renderParams);
39810
40375
  const html = renderProofHtml(renderParams, inlineFiles, transcriptSidecars);
39811
- const mdPath = resolve95(resolved.assignmentDir, "proof.md");
39812
- const htmlPath = resolve95(resolved.assignmentDir, "proof.html");
40376
+ const mdPath = resolve96(resolved.assignmentDir, "proof.md");
40377
+ const htmlPath = resolve96(resolved.assignmentDir, "proof.html");
39813
40378
  await atomicWrite(mdPath, md);
39814
40379
  await atomicWrite(htmlPath, html);
39815
40380
  console.log(`Wrote ${htmlPath}`);
@@ -40352,7 +40917,7 @@ function isScheduledSessionLive(sessionId, launchPid) {
40352
40917
  import { execFileSync as execFileSync5 } from "child_process";
40353
40918
  import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, rmSync as rmSync2, realpathSync as realpathSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
40354
40919
  import { homedir as homedir16, userInfo } from "os";
40355
- 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";
40356
40921
  var LAUNCH_AGENT_LABEL = "com.syntaur.schedule.tick";
40357
40922
  var LaunchAgentRefusalError = class extends Error {
40358
40923
  constructor(message) {
@@ -40406,7 +40971,7 @@ function absolutize(p) {
40406
40971
  try {
40407
40972
  return realpathSync4(p);
40408
40973
  } catch {
40409
- return resolve96(p);
40974
+ return resolve97(p);
40410
40975
  }
40411
40976
  }
40412
40977
  function defaultRun(command, args) {
@@ -40467,7 +41032,7 @@ function uninstallLaunchAgent(deps = {}) {
40467
41032
  // src/commands/schedule.ts
40468
41033
  init_timestamp();
40469
41034
  var DURATION_REGEX2 = /^(\d+)\s*(s|m|h|d)?$/i;
40470
- function parseDurationMs(input4) {
41035
+ function parseDurationMs2(input4) {
40471
41036
  const m = DURATION_REGEX2.exec(input4.trim());
40472
41037
  if (!m) throw new Error(`invalid duration "${input4}" \u2014 use e.g. 30s, 5m, 2h, 1d`);
40473
41038
  const n = Number.parseInt(m[1], 10);
@@ -40488,7 +41053,7 @@ function validateTerminalChoice(value) {
40488
41053
  function buildTrigger(opts) {
40489
41054
  const chosen = [];
40490
41055
  if (opts.at) chosen.push({ kind: "at", at: opts.at });
40491
- 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() });
40492
41057
  if (opts.cron) {
40493
41058
  try {
40494
41059
  new Cron2(opts.cron).nextRun();
@@ -40556,7 +41121,7 @@ scheduleCommand.command("create").description("Create a scheduled job").required
40556
41121
  const unattended = !opts.interactive;
40557
41122
  if (unattended) assertUnattendedTerminalSupported(terminal);
40558
41123
  const limits = defaultLimits();
40559
- if (opts.maxRuntime) limits.maxRuntimeMs = parseDurationMs(opts.maxRuntime);
41124
+ if (opts.maxRuntime) limits.maxRuntimeMs = parseDurationMs2(opts.maxRuntime);
40560
41125
  if (opts.maxLaunchesPerDay) {
40561
41126
  const n = Number(opts.maxLaunchesPerDay);
40562
41127
  if (!Number.isInteger(n) || n <= 0) {
@@ -40564,7 +41129,7 @@ scheduleCommand.command("create").description("Create a scheduled job").required
40564
41129
  }
40565
41130
  limits.maxLaunchesPerDay = n;
40566
41131
  }
40567
- if (opts.cooldown) limits.cooldownMs = parseDurationMs(opts.cooldown);
41132
+ if (opts.cooldown) limits.cooldownMs = parseDurationMs2(opts.cooldown);
40568
41133
  const now = nowTimestamp();
40569
41134
  const job = {
40570
41135
  id: newJobId(),
@@ -40782,8 +41347,8 @@ init_slug();
40782
41347
  init_timestamp();
40783
41348
  init_assignment_resolver();
40784
41349
  init_assignment_todos();
40785
- import { resolve as resolve97 } from "path";
40786
- import { readFile as readFile69 } from "fs/promises";
41350
+ import { resolve as resolve98 } from "path";
41351
+ import { readFile as readFile70 } from "fs/promises";
40787
41352
  async function requestCommand(target, text, options = {}) {
40788
41353
  if (!text || !text.trim()) {
40789
41354
  throw new Error("Request text cannot be empty.");
@@ -40799,7 +41364,7 @@ async function requestCommand(target, text, options = {}) {
40799
41364
  if (!isValidSlug(target)) {
40800
41365
  throw new Error(`Invalid assignment slug "${target}".`);
40801
41366
  }
40802
- assignmentDir = resolve97(baseDir, options.project, "assignments", target);
41367
+ assignmentDir = resolve98(baseDir, options.project, "assignments", target);
40803
41368
  targetRef = target;
40804
41369
  } else {
40805
41370
  const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
@@ -40809,12 +41374,12 @@ async function requestCommand(target, text, options = {}) {
40809
41374
  assignmentDir = resolved.assignmentDir;
40810
41375
  targetRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
40811
41376
  }
40812
- const assignmentMdPath2 = resolve97(assignmentDir, "assignment.md");
41377
+ const assignmentMdPath2 = resolve98(assignmentDir, "assignment.md");
40813
41378
  if (!await fileExists(assignmentMdPath2)) {
40814
41379
  throw new Error(`assignment.md not found at ${assignmentMdPath2}`);
40815
41380
  }
40816
41381
  const source = options.from ?? process.env.SYNTAUR_ASSIGNMENT ?? "unknown";
40817
- let content = await readFile69(assignmentMdPath2, "utf-8");
41382
+ let content = await readFile70(assignmentMdPath2, "utf-8");
40818
41383
  content = appendTodosToAssignmentBody(content, [
40819
41384
  { description: `${text.trim()} (from: ${source})` }
40820
41385
  ]);
@@ -40827,14 +41392,15 @@ async function requestCommand(target, text, options = {}) {
40827
41392
  init_fs();
40828
41393
  init_paths();
40829
41394
  init_config2();
41395
+ init_recompute();
40830
41396
  import { Command as Command10 } from "commander";
40831
- import { readFile as readFile70, readdir as readdir34 } from "fs/promises";
40832
- import { resolve as resolve98 } from "path";
41397
+ import { readFile as readFile71, readdir as readdir34 } from "fs/promises";
41398
+ import { resolve as resolve99 } from "path";
40833
41399
  async function readContextAssignmentDir(cwd) {
40834
- const path = resolve98(cwd, ".syntaur", "context.json");
41400
+ const path = resolve99(cwd, ".syntaur", "context.json");
40835
41401
  if (!await fileExists(path)) return null;
40836
41402
  try {
40837
- const raw2 = await readFile70(path, "utf-8");
41403
+ const raw2 = await readFile71(path, "utf-8");
40838
41404
  const ctx = JSON.parse(raw2);
40839
41405
  if (typeof ctx.assignmentDir === "string" && ctx.assignmentDir.length > 0) {
40840
41406
  return ctx.assignmentDir;
@@ -40848,9 +41414,9 @@ async function resolveAssignmentDir(opts) {
40848
41414
  const cwd = opts.cwd ?? process.cwd();
40849
41415
  if (opts.assignment) {
40850
41416
  if (opts.project) {
40851
- return resolve98((await readConfig()).defaultProjectDir, opts.project, "assignments", opts.assignment);
41417
+ return resolve99((await readConfig()).defaultProjectDir, opts.project, "assignments", opts.assignment);
40852
41418
  }
40853
- return resolve98(assignmentsDir(), opts.assignment);
41419
+ return resolve99(assignmentsDir(), opts.assignment);
40854
41420
  }
40855
41421
  const fromCtx = await readContextAssignmentDir(cwd);
40856
41422
  if (fromCtx) return fromCtx;
@@ -41046,17 +41612,17 @@ async function runPlanCreate(options) {
41046
41612
  if (!await fileExists(assignmentDir)) {
41047
41613
  throw new Error(`Assignment directory does not exist: ${assignmentDir}`);
41048
41614
  }
41049
- const assignmentMdPath2 = resolve98(assignmentDir, "assignment.md");
41615
+ const assignmentMdPath2 = resolve99(assignmentDir, "assignment.md");
41050
41616
  if (!await fileExists(assignmentMdPath2)) {
41051
41617
  throw new Error(`Missing assignment.md at: ${assignmentMdPath2}`);
41052
41618
  }
41053
- const planPath = resolve98(assignmentDir, "plan.md");
41619
+ const planPath = resolve99(assignmentDir, "plan.md");
41054
41620
  if (await fileExists(planPath) && !options.force) {
41055
41621
  throw new Error(
41056
41622
  "plan.md already exists. Use --force to overwrite, or `syntaur plan version` to create the next version."
41057
41623
  );
41058
41624
  }
41059
- const assignmentMd = await readFile70(assignmentMdPath2, "utf-8");
41625
+ const assignmentMd = await readFile71(assignmentMdPath2, "utf-8");
41060
41626
  const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
41061
41627
  const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
41062
41628
  await writeFileForce(planPath, buildInitialPlanStub(slug));
@@ -41070,13 +41636,14 @@ async function runPlanCreate(options) {
41070
41636
  }
41071
41637
  console.log(`Created ${planPath}`);
41072
41638
  if (appended > 0) console.log(`Appended ${appended} plan todo(s) to assignment.md.`);
41639
+ await recomputeAssignmentDir(assignmentDir, "plan-create", null);
41073
41640
  }
41074
41641
  async function runPlanVersion(options) {
41075
41642
  const assignmentDir = await resolveAssignmentDir(options);
41076
41643
  if (!await fileExists(assignmentDir)) {
41077
41644
  throw new Error(`Assignment directory does not exist: ${assignmentDir}`);
41078
41645
  }
41079
- const assignmentMdPath2 = resolve98(assignmentDir, "assignment.md");
41646
+ const assignmentMdPath2 = resolve99(assignmentDir, "assignment.md");
41080
41647
  if (!await fileExists(assignmentMdPath2)) {
41081
41648
  throw new Error(`Missing assignment.md at: ${assignmentMdPath2}`);
41082
41649
  }
@@ -41088,15 +41655,15 @@ async function runPlanVersion(options) {
41088
41655
  }
41089
41656
  const current = planFiles[planFiles.length - 1];
41090
41657
  const next = nextPlanFileName(current.version);
41091
- const newPath = resolve98(assignmentDir, next.fileName);
41658
+ const newPath = resolve99(assignmentDir, next.fileName);
41092
41659
  if (await fileExists(newPath) && !options.force) {
41093
41660
  throw new Error(`${next.fileName} already exists. Use --force to overwrite.`);
41094
41661
  }
41095
- const assignmentMd = await readFile70(assignmentMdPath2, "utf-8");
41662
+ const assignmentMd = await readFile71(assignmentMdPath2, "utf-8");
41096
41663
  const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
41097
41664
  const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
41098
- const oldPlanPath = resolve98(assignmentDir, current.fileName);
41099
- const oldPlanContent = await readFile70(oldPlanPath, "utf-8");
41665
+ const oldPlanPath = resolve99(assignmentDir, current.fileName);
41666
+ const oldPlanContent = await readFile71(oldPlanPath, "utf-8");
41100
41667
  const oldBody = oldPlanContent.replace(/^---[\s\S]*?\n---\n?/, "");
41101
41668
  const carriedTodos = extractUncheckedTodos(oldBody);
41102
41669
  const stub = buildNewPlanStub({
@@ -41117,6 +41684,7 @@ async function runPlanVersion(options) {
41117
41684
  );
41118
41685
  console.log(`Path: ${newPath}`);
41119
41686
  console.log(`Carried forward: ${carriedTodos.length} unchecked task(s).`);
41687
+ await recomputeAssignmentDir(assignmentDir, "plan-version", null);
41120
41688
  }
41121
41689
  var planCommand = new Command10("plan").description("Manage plan files for the active assignment");
41122
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) => {
@@ -41166,28 +41734,28 @@ init_cwd();
41166
41734
  init_session_db();
41167
41735
  init_agent_sessions();
41168
41736
  import { Command as Command11 } from "commander";
41169
- import { readFile as readFile71, readdir as readdir35, stat as stat15 } from "fs/promises";
41170
- 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";
41171
41739
  async function readContext(cwd) {
41172
- const path = resolve99(cwd, ".syntaur", "context.json");
41740
+ const path = resolve100(cwd, ".syntaur", "context.json");
41173
41741
  if (!await fileExists(path)) return null;
41174
41742
  try {
41175
- const raw2 = await readFile71(path, "utf-8");
41743
+ const raw2 = await readFile72(path, "utf-8");
41176
41744
  return JSON.parse(raw2);
41177
41745
  } catch {
41178
41746
  return null;
41179
41747
  }
41180
41748
  }
41181
41749
  async function findLatestSessionSummary(assignmentDir) {
41182
- const sessionsRoot = resolve99(assignmentDir, "sessions");
41750
+ const sessionsRoot = resolve100(assignmentDir, "sessions");
41183
41751
  if (!await fileExists(sessionsRoot)) return null;
41184
41752
  const entries = await readdir35(sessionsRoot, { withFileTypes: true });
41185
41753
  let best = null;
41186
41754
  for (const entry of entries) {
41187
41755
  if (!entry.isDirectory()) continue;
41188
- const summaryPath = resolve99(sessionsRoot, entry.name, "summary.md");
41756
+ const summaryPath = resolve100(sessionsRoot, entry.name, "summary.md");
41189
41757
  if (!await fileExists(summaryPath)) continue;
41190
- const st = await stat15(summaryPath);
41758
+ const st = await stat16(summaryPath);
41191
41759
  if (best === null || st.mtime.getTime() > best.mtime.getTime()) {
41192
41760
  best = { sessionId: entry.name, path: summaryPath, mtime: st.mtime };
41193
41761
  }
@@ -41195,9 +41763,9 @@ async function findLatestSessionSummary(assignmentDir) {
41195
41763
  return best;
41196
41764
  }
41197
41765
  async function findOpenHandoff(assignmentDir) {
41198
- const handoffPath = resolve99(assignmentDir, "handoff.md");
41766
+ const handoffPath = resolve100(assignmentDir, "handoff.md");
41199
41767
  if (!await fileExists(handoffPath)) return null;
41200
- const content = await readFile71(handoffPath, "utf-8");
41768
+ const content = await readFile72(handoffPath, "utf-8");
41201
41769
  const body = content.replace(/^---[\s\S]*?\n---\n?/, "").trim();
41202
41770
  if (body.length === 0) return null;
41203
41771
  if (/^<!--[\s\S]*-->$/.test(body)) return null;
@@ -41292,7 +41860,7 @@ async function resolveSaveTarget(options, cwd) {
41292
41860
  let slug;
41293
41861
  const ctx = await readContext(cwd);
41294
41862
  if (options.assignment) {
41295
- 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);
41296
41864
  slug = options.assignment;
41297
41865
  } else {
41298
41866
  if (!ctx?.assignmentDir) {
@@ -41349,21 +41917,21 @@ function extractCreated(content) {
41349
41917
  }
41350
41918
  async function runSessionSave(options, cwd = process.cwd(), body) {
41351
41919
  const { assignmentDir, slug, sessionId } = await resolveSaveTarget(options, cwd);
41352
- if (!await fileExists(resolve99(assignmentDir, "assignment.md"))) {
41920
+ if (!await fileExists(resolve100(assignmentDir, "assignment.md"))) {
41353
41921
  throw new Error(`No assignment found at ${assignmentDir} (missing assignment.md).`);
41354
41922
  }
41355
- const sessionDir = resolve99(assignmentDir, "sessions", sessionId);
41356
- const summaryPath = resolve99(sessionDir, "summary.md");
41923
+ const sessionDir = resolve100(assignmentDir, "sessions", sessionId);
41924
+ const summaryPath = resolve100(sessionDir, "summary.md");
41357
41925
  const now = nowTimestamp();
41358
41926
  let created = now;
41359
41927
  if (await fileExists(summaryPath)) {
41360
- const existing = await readFile71(summaryPath, "utf-8");
41928
+ const existing = await readFile72(summaryPath, "utf-8");
41361
41929
  created = extractCreated(existing) ?? now;
41362
41930
  }
41363
41931
  let sectionBody2 = body;
41364
41932
  if (sectionBody2 === void 0) {
41365
41933
  if (options.fromFile) {
41366
- sectionBody2 = await readFile71(resolve99(cwd, options.fromFile), "utf-8");
41934
+ sectionBody2 = await readFile72(resolve100(cwd, options.fromFile), "utf-8");
41367
41935
  } else {
41368
41936
  sectionBody2 = await readStdin();
41369
41937
  }
@@ -41399,7 +41967,7 @@ async function runSessionRegister(rawStdin, options = {}, deps = {}) {
41399
41967
  if (!isSafeSessionId(sessionId) || !cwd) return result;
41400
41968
  result.sessionId = sessionId;
41401
41969
  const transcriptPath = payload.transcript_path ?? "";
41402
- const contextPath = resolve99(cwd, ".syntaur", "context.json");
41970
+ const contextPath = resolve100(cwd, ".syntaur", "context.json");
41403
41971
  const hasContextFile = await fileExists(contextPath);
41404
41972
  const ctx = hasContextFile ? await readContext(cwd) : null;
41405
41973
  if (ctx) {
@@ -41545,8 +42113,8 @@ sessionCommand.command("resolve-id").description(
41545
42113
  // src/commands/worktree.ts
41546
42114
  init_git_worktree();
41547
42115
  import { Command as Command12 } from "commander";
41548
- import { readFile as readFile72 } from "fs/promises";
41549
- import { resolve as resolve101 } from "path";
42116
+ import { readFile as readFile73 } from "fs/promises";
42117
+ import { resolve as resolve102 } from "path";
41550
42118
  init_fs();
41551
42119
  init_paths();
41552
42120
  init_config2();
@@ -41555,12 +42123,12 @@ init_state_machine();
41555
42123
 
41556
42124
  // src/utils/path-canon.ts
41557
42125
  import { realpathSync as realpathSync5 } from "fs";
41558
- import { resolve as resolve100 } from "path";
42126
+ import { resolve as resolve101 } from "path";
41559
42127
  function canonicalPath(p) {
41560
42128
  try {
41561
- return realpathSync5(resolve100(p));
42129
+ return realpathSync5(resolve101(p));
41562
42130
  } catch {
41563
- return resolve100(p).replace(/\/+$/, "");
42131
+ return resolve101(p).replace(/\/+$/, "");
41564
42132
  }
41565
42133
  }
41566
42134
 
@@ -41588,10 +42156,10 @@ function countSessionsByPath(dbPath, paths) {
41588
42156
  init_timestamp();
41589
42157
  init_frontmatter();
41590
42158
  async function readContext2(cwd) {
41591
- const path = resolve101(cwd, ".syntaur", "context.json");
42159
+ const path = resolve102(cwd, ".syntaur", "context.json");
41592
42160
  if (!await fileExists(path)) return null;
41593
42161
  try {
41594
- return JSON.parse(await readFile72(path, "utf-8"));
42162
+ return JSON.parse(await readFile73(path, "utf-8"));
41595
42163
  } catch {
41596
42164
  return null;
41597
42165
  }
@@ -41600,12 +42168,12 @@ async function resolveAssignmentPath2(opts) {
41600
42168
  if (opts.assignment) {
41601
42169
  if (opts.project) {
41602
42170
  const projectsDir2 = (await readConfig()).defaultProjectDir;
41603
- return resolve101(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
42171
+ return resolve102(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
41604
42172
  }
41605
- return resolve101(assignmentsDir(), opts.assignment, "assignment.md");
42173
+ return resolve102(assignmentsDir(), opts.assignment, "assignment.md");
41606
42174
  }
41607
42175
  const ctx = await readContext2(opts.cwd);
41608
- if (ctx?.assignmentDir) return resolve101(ctx.assignmentDir, "assignment.md");
42176
+ if (ctx?.assignmentDir) return resolve102(ctx.assignmentDir, "assignment.md");
41609
42177
  throw new Error(
41610
42178
  "No active assignment. Pass --assignment <slug> [--project <slug>] or run from a workspace with .syntaur/context.json."
41611
42179
  );
@@ -41616,7 +42184,7 @@ async function runWorktreeCreate(options, cwd = process.cwd()) {
41616
42184
  }
41617
42185
  const repository = options.repository ?? cwd;
41618
42186
  const parentBranch = options.parentBranch ?? "main";
41619
- const worktreePath = options.worktreePath ?? resolve101(repository, ".worktrees", options.branch);
42187
+ const worktreePath = options.worktreePath ?? resolve102(repository, ".worktrees", options.branch);
41620
42188
  const assignmentPath = await resolveAssignmentPath2({
41621
42189
  assignment: options.assignment,
41622
42190
  project: options.project,
@@ -41646,7 +42214,7 @@ async function runWorktreeRemove(options, cwd = process.cwd()) {
41646
42214
  if (!await fileExists(assignmentPath)) {
41647
42215
  throw new Error(`Assignment file not found: ${assignmentPath}`);
41648
42216
  }
41649
- const original = await readFile72(assignmentPath, "utf-8");
42217
+ const original = await readFile73(assignmentPath, "utf-8");
41650
42218
  const fm = parseAssignmentFrontmatter(original);
41651
42219
  const repository = options.repository ?? fm.workspace.repository ?? void 0;
41652
42220
  const worktreePath = fm.workspace.worktreePath ?? void 0;
@@ -41712,7 +42280,7 @@ async function runWorktreeGc(options, cwd = process.cwd()) {
41712
42280
  const owners = /* @__PURE__ */ new Map();
41713
42281
  for (const entry of walk.withAssignmentMd) {
41714
42282
  try {
41715
- const content = await readFile72(resolve101(entry.assignmentDir, "assignment.md"), "utf-8");
42283
+ const content = await readFile73(resolve102(entry.assignmentDir, "assignment.md"), "utf-8");
41716
42284
  const fm = parseAssignmentFrontmatter(content);
41717
42285
  const wp = fm.workspace?.worktreePath;
41718
42286
  if (!wp) continue;
@@ -41734,7 +42302,7 @@ async function runWorktreeGc(options, cwd = process.cwd()) {
41734
42302
  const repoTop = rt ? canonicalPath(rt) : null;
41735
42303
  const cwdTop = ct ? canonicalPath(ct) : null;
41736
42304
  const mainPath = entries.length > 0 ? canonicalPath(entries[0].worktreePath) : null;
41737
- const dbPath = resolve101(syntaurRoot(), "syntaur.db");
42305
+ const dbPath = resolve102(syntaurRoot(), "syntaur.db");
41738
42306
  const candidates = [];
41739
42307
  for (const entry of entries) {
41740
42308
  const canon = canonicalPath(entry.worktreePath);
@@ -41953,8 +42521,8 @@ worktreeCommand.command("gc").description(
41953
42521
  // src/commands/open.ts
41954
42522
  init_fs();
41955
42523
  import { Command as Command13 } from "commander";
41956
- import { readFile as readFile73 } from "fs/promises";
41957
- import { resolve as resolve102 } from "path";
42524
+ import { readFile as readFile74 } from "fs/promises";
42525
+ import { resolve as resolve103 } from "path";
41958
42526
  init_frontmatter();
41959
42527
  init_config2();
41960
42528
  init_paths();
@@ -42008,13 +42576,13 @@ function openInTerminal(path, config) {
42008
42576
  // src/commands/open.ts
42009
42577
  async function runOpen(assignmentArg, options) {
42010
42578
  const resolved = options.id ? await resolveAssignmentTarget(options.id, {}) : await resolveAssignmentTarget(assignmentArg, { project: options.project });
42011
- const assignmentPath = resolve102(resolved.assignmentDir, "assignment.md");
42579
+ const assignmentPath = resolve103(resolved.assignmentDir, "assignment.md");
42012
42580
  if (!await fileExists(assignmentPath)) {
42013
42581
  throw new SyntaurError(`Assignment file not found: ${assignmentPath}`, {
42014
42582
  remediation: "check the assignment slug or --id"
42015
42583
  });
42016
42584
  }
42017
- const fm = parseAssignmentFrontmatter(await readFile73(assignmentPath, "utf-8"));
42585
+ const fm = parseAssignmentFrontmatter(await readFile74(assignmentPath, "utf-8"));
42018
42586
  const worktreePath = fm.workspace?.worktreePath;
42019
42587
  if (!worktreePath) {
42020
42588
  throw new SyntaurError("No worktree recorded for this assignment.", {
@@ -42079,14 +42647,14 @@ init_config2();
42079
42647
  init_fs();
42080
42648
  init_slug();
42081
42649
  import { Command as Command14 } from "commander";
42082
- import { resolve as resolve104 } from "path";
42083
- 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";
42084
42652
 
42085
42653
  // src/utils/project-indexes.ts
42086
42654
  init_parser();
42087
42655
  init_fs();
42088
- import { readdir as readdir36, readFile as readFile74 } from "fs/promises";
42089
- import { resolve as resolve103 } from "path";
42656
+ import { readdir as readdir36, readFile as readFile75 } from "fs/promises";
42657
+ import { resolve as resolve104 } from "path";
42090
42658
  function nowIso3() {
42091
42659
  return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
42092
42660
  }
@@ -42105,7 +42673,7 @@ function joinList(items) {
42105
42673
  return items.length === 0 ? "\u2014" : items.map(escapeCell).join(", ");
42106
42674
  }
42107
42675
  async function rebuildResourcesIndex(projectDir) {
42108
- const dir = resolve103(projectDir, "resources");
42676
+ const dir = resolve104(projectDir, "resources");
42109
42677
  await ensureDir(dir);
42110
42678
  const files = await listSlugFiles(dir);
42111
42679
  const slug = readProjectSlug(projectDir);
@@ -42121,7 +42689,7 @@ async function rebuildResourcesIndex(projectDir) {
42121
42689
  lines.push("| Name | Category | Source | Related Assignments | Updated |");
42122
42690
  lines.push("|------|----------|--------|---------------------|---------|");
42123
42691
  for (const fileName of files) {
42124
- const content = await readFile74(resolve103(dir, fileName), "utf-8");
42692
+ const content = await readFile75(resolve104(dir, fileName), "utf-8");
42125
42693
  const parsed = parseResource(content);
42126
42694
  const slugBase = fileName.replace(/\.md$/, "");
42127
42695
  const name = parsed.name || slugBase;
@@ -42131,12 +42699,12 @@ async function rebuildResourcesIndex(projectDir) {
42131
42699
  );
42132
42700
  }
42133
42701
  lines.push("");
42134
- const indexPath = resolve103(dir, "_index.md");
42702
+ const indexPath = resolve104(dir, "_index.md");
42135
42703
  await writeFileForce(indexPath, lines.join("\n"));
42136
42704
  return { total: files.length, path: indexPath };
42137
42705
  }
42138
42706
  async function rebuildMemoriesIndex(projectDir) {
42139
- const dir = resolve103(projectDir, "memories");
42707
+ const dir = resolve104(projectDir, "memories");
42140
42708
  await ensureDir(dir);
42141
42709
  const files = await listSlugFiles(dir);
42142
42710
  const slug = readProjectSlug(projectDir);
@@ -42152,7 +42720,7 @@ async function rebuildMemoriesIndex(projectDir) {
42152
42720
  lines.push("| Name | Source | Scope | Source Assignment | Updated |");
42153
42721
  lines.push("|------|--------|-------|-------------------|---------|");
42154
42722
  for (const fileName of files) {
42155
- const content = await readFile74(resolve103(dir, fileName), "utf-8");
42723
+ const content = await readFile75(resolve104(dir, fileName), "utf-8");
42156
42724
  const parsed = parseMemory(content);
42157
42725
  const slugBase = fileName.replace(/\.md$/, "");
42158
42726
  const name = parsed.name || slugBase;
@@ -42162,7 +42730,7 @@ async function rebuildMemoriesIndex(projectDir) {
42162
42730
  );
42163
42731
  }
42164
42732
  lines.push("");
42165
- const indexPath = resolve103(dir, "_index.md");
42733
+ const indexPath = resolve104(dir, "_index.md");
42166
42734
  await writeFileForce(indexPath, lines.join("\n"));
42167
42735
  return { total: files.length, path: indexPath };
42168
42736
  }
@@ -42226,8 +42794,8 @@ ${opts.body ?? "<!-- Add notes about this resource here. -->"}
42226
42794
  }
42227
42795
  async function resolveProjectDir(project) {
42228
42796
  if (!isValidSlug(project)) throw new Error(`Invalid project slug: "${project}".`);
42229
- const projectDir = resolve104((await readConfig()).defaultProjectDir, project);
42230
- if (!await fileExists(resolve104(projectDir, "project.md"))) {
42797
+ const projectDir = resolve105((await readConfig()).defaultProjectDir, project);
42798
+ if (!await fileExists(resolve105(projectDir, "project.md"))) {
42231
42799
  throw new Error(`Project "${project}" not found at ${projectDir}.`);
42232
42800
  }
42233
42801
  return projectDir;
@@ -42240,7 +42808,7 @@ async function runResourceAdd(options) {
42240
42808
  if (!isValidSlug(slug)) {
42241
42809
  throw new Error(`Invalid resource slug: "${slug}".`);
42242
42810
  }
42243
- const filePath = resolve104(projectDir, "resources", `${slug}.md`);
42811
+ const filePath = resolve105(projectDir, "resources", `${slug}.md`);
42244
42812
  if (await fileExists(filePath) && !options.force) {
42245
42813
  throw new Error(
42246
42814
  `Resource "${slug}" already exists at ${filePath}. Use --force to overwrite.`
@@ -42257,7 +42825,7 @@ async function runResourceAdd(options) {
42257
42825
  return { filePath, indexPath, total };
42258
42826
  }
42259
42827
  async function listResourceSlugs(projectDir) {
42260
- const dir = resolve104(projectDir, "resources");
42828
+ const dir = resolve105(projectDir, "resources");
42261
42829
  if (!await fileExists(dir)) return [];
42262
42830
  const entries = await readdir37(dir, { withFileTypes: true });
42263
42831
  return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "_index.md").map((e) => e.name.slice(0, -3)).sort();
@@ -42267,16 +42835,16 @@ async function runResourceList(project) {
42267
42835
  const slugs = await listResourceSlugs(projectDir);
42268
42836
  const out = [];
42269
42837
  for (const slug of slugs) {
42270
- 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"));
42271
42839
  out.push({ slug, name: parsed.name, category: parsed.category, source: parsed.source, updated: parsed.updated });
42272
42840
  }
42273
42841
  return out;
42274
42842
  }
42275
42843
  async function runResourceShow(project, slug) {
42276
42844
  const projectDir = await resolveProjectDir(project);
42277
- const filePath = resolve104(projectDir, "resources", `${slug}.md`);
42845
+ const filePath = resolve105(projectDir, "resources", `${slug}.md`);
42278
42846
  if (!await fileExists(filePath)) throw new Error(`Resource "${slug}" not found in project "${project}".`);
42279
- const parsed = parseResource(await readFile75(filePath, "utf-8"));
42847
+ const parsed = parseResource(await readFile76(filePath, "utf-8"));
42280
42848
  return {
42281
42849
  slug,
42282
42850
  name: parsed.name,
@@ -42290,14 +42858,14 @@ async function runResourceShow(project, slug) {
42290
42858
  }
42291
42859
  async function runResourceUpdate(slug, options) {
42292
42860
  const projectDir = await resolveProjectDir(options.project);
42293
- const filePath = resolve104(projectDir, "resources", `${slug}.md`);
42861
+ const filePath = resolve105(projectDir, "resources", `${slug}.md`);
42294
42862
  if (!await fileExists(filePath)) {
42295
42863
  throw new Error(`Resource "${slug}" not found in project "${options.project}".`);
42296
42864
  }
42297
42865
  if (options.name === void 0 && options.source === void 0 && options.category === void 0 && options.relatedAssignments === void 0) {
42298
42866
  throw new Error("Provide at least one of --name, --source, --category, --related-assignments.");
42299
42867
  }
42300
- const original = await readFile75(filePath, "utf-8");
42868
+ const original = await readFile76(filePath, "utf-8");
42301
42869
  const content = editResourceFrontmatter(original, {
42302
42870
  name: options.name,
42303
42871
  category: options.category,
@@ -42310,7 +42878,7 @@ async function runResourceUpdate(slug, options) {
42310
42878
  }
42311
42879
  async function runResourceRemove(slug, options) {
42312
42880
  const projectDir = await resolveProjectDir(options.project);
42313
- const filePath = resolve104(projectDir, "resources", `${slug}.md`);
42881
+ const filePath = resolve105(projectDir, "resources", `${slug}.md`);
42314
42882
  if (!await fileExists(filePath)) {
42315
42883
  throw new Error(`Resource "${slug}" not found in project "${options.project}".`);
42316
42884
  }
@@ -42393,8 +42961,8 @@ init_config2();
42393
42961
  init_fs();
42394
42962
  init_slug();
42395
42963
  import { Command as Command15 } from "commander";
42396
- import { resolve as resolve105 } from "path";
42397
- 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";
42398
42966
  init_parser();
42399
42967
  function nowIso5() {
42400
42968
  return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
@@ -42458,8 +43026,8 @@ ${opts.body ?? "<!-- Capture the load-bearing context for this memory here. -->"
42458
43026
  }
42459
43027
  async function resolveProjectDir2(project) {
42460
43028
  if (!isValidSlug(project)) throw new Error(`Invalid project slug: "${project}".`);
42461
- const projectDir = resolve105((await readConfig()).defaultProjectDir, project);
42462
- if (!await fileExists(resolve105(projectDir, "project.md"))) {
43029
+ const projectDir = resolve106((await readConfig()).defaultProjectDir, project);
43030
+ if (!await fileExists(resolve106(projectDir, "project.md"))) {
42463
43031
  throw new Error(`Project "${project}" not found at ${projectDir}.`);
42464
43032
  }
42465
43033
  return projectDir;
@@ -42472,7 +43040,7 @@ async function runMemoryAdd(options) {
42472
43040
  if (!isValidSlug(slug)) {
42473
43041
  throw new Error(`Invalid memory slug: "${slug}".`);
42474
43042
  }
42475
- const filePath = resolve105(projectDir, "memories", `${slug}.md`);
43043
+ const filePath = resolve106(projectDir, "memories", `${slug}.md`);
42476
43044
  if (await fileExists(filePath) && !options.force) {
42477
43045
  throw new Error(
42478
43046
  `Memory "${slug}" already exists at ${filePath}. Use --force to overwrite.`
@@ -42490,7 +43058,7 @@ async function runMemoryAdd(options) {
42490
43058
  return { filePath, indexPath, total };
42491
43059
  }
42492
43060
  async function listMemorySlugs(projectDir) {
42493
- const dir = resolve105(projectDir, "memories");
43061
+ const dir = resolve106(projectDir, "memories");
42494
43062
  if (!await fileExists(dir)) return [];
42495
43063
  const entries = await readdir38(dir, { withFileTypes: true });
42496
43064
  return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "_index.md").map((e) => e.name.slice(0, -3)).sort();
@@ -42500,16 +43068,16 @@ async function runMemoryList(project) {
42500
43068
  const slugs = await listMemorySlugs(projectDir);
42501
43069
  const out = [];
42502
43070
  for (const slug of slugs) {
42503
- 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"));
42504
43072
  out.push({ slug, name: parsed.name, scope: parsed.scope, source: parsed.source, updated: parsed.updated });
42505
43073
  }
42506
43074
  return out;
42507
43075
  }
42508
43076
  async function runMemoryShow(project, slug) {
42509
43077
  const projectDir = await resolveProjectDir2(project);
42510
- const filePath = resolve105(projectDir, "memories", `${slug}.md`);
43078
+ const filePath = resolve106(projectDir, "memories", `${slug}.md`);
42511
43079
  if (!await fileExists(filePath)) throw new Error(`Memory "${slug}" not found in project "${project}".`);
42512
- const parsed = parseMemory(await readFile76(filePath, "utf-8"));
43080
+ const parsed = parseMemory(await readFile77(filePath, "utf-8"));
42513
43081
  return {
42514
43082
  slug,
42515
43083
  name: parsed.name,
@@ -42524,7 +43092,7 @@ async function runMemoryShow(project, slug) {
42524
43092
  }
42525
43093
  async function runMemoryUpdate(slug, options) {
42526
43094
  const projectDir = await resolveProjectDir2(options.project);
42527
- const filePath = resolve105(projectDir, "memories", `${slug}.md`);
43095
+ const filePath = resolve106(projectDir, "memories", `${slug}.md`);
42528
43096
  if (!await fileExists(filePath)) {
42529
43097
  throw new Error(`Memory "${slug}" not found in project "${options.project}".`);
42530
43098
  }
@@ -42533,7 +43101,7 @@ async function runMemoryUpdate(slug, options) {
42533
43101
  "Provide at least one of --name, --source, --scope, --source-assignment, --related-assignments."
42534
43102
  );
42535
43103
  }
42536
- const original = await readFile76(filePath, "utf-8");
43104
+ const original = await readFile77(filePath, "utf-8");
42537
43105
  const content = editMemoryFrontmatter(original, {
42538
43106
  name: options.name,
42539
43107
  source: options.source,
@@ -42547,7 +43115,7 @@ async function runMemoryUpdate(slug, options) {
42547
43115
  }
42548
43116
  async function runMemoryRemove(slug, options) {
42549
43117
  const projectDir = await resolveProjectDir2(options.project);
42550
- const filePath = resolve105(projectDir, "memories", `${slug}.md`);
43118
+ const filePath = resolve106(projectDir, "memories", `${slug}.md`);
42551
43119
  if (!await fileExists(filePath)) {
42552
43120
  throw new Error(`Memory "${slug}" not found in project "${options.project}".`);
42553
43121
  }
@@ -42636,8 +43204,8 @@ init_derive();
42636
43204
  init_recompute();
42637
43205
  init_query();
42638
43206
  import { Command as Command16 } from "commander";
42639
- import { readFile as readFile77 } from "fs/promises";
42640
- 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";
42641
43209
  var AGE_PATTERN = /^(\d+)([dhwm])$/i;
42642
43210
  function parseAgeToCutoff(age) {
42643
43211
  const match = age.match(AGE_PATTERN);
@@ -42656,7 +43224,7 @@ function parseAgeToCutoff(age) {
42656
43224
  }
42657
43225
  function assignmentMdPath(item) {
42658
43226
  if (item.projectSlug) {
42659
- return resolve106(
43227
+ return resolve107(
42660
43228
  defaultProjectDir(),
42661
43229
  item.projectSlug,
42662
43230
  "assignments",
@@ -42664,13 +43232,13 @@ function assignmentMdPath(item) {
42664
43232
  "assignment.md"
42665
43233
  );
42666
43234
  }
42667
- return resolve106(assignmentsDir(), item.id, "assignment.md");
43235
+ return resolve107(assignmentsDir(), item.id, "assignment.md");
42668
43236
  }
42669
43237
  async function loadTags(item) {
42670
43238
  const path = assignmentMdPath(item);
42671
43239
  if (!await fileExists(path)) return [];
42672
43240
  try {
42673
- const content = await readFile77(path, "utf-8");
43241
+ const content = await readFile78(path, "utf-8");
42674
43242
  return parseAssignmentFrontmatter(content).tags;
42675
43243
  } catch {
42676
43244
  return [];
@@ -42734,11 +43302,11 @@ async function loadQueryItem(item, terminalStatuses3, now, declarations) {
42734
43302
  const path = assignmentMdPath(item);
42735
43303
  if (!await fileExists(path)) return null;
42736
43304
  try {
42737
- const content = await readFile77(path, "utf-8");
43305
+ const content = await readFile78(path, "utf-8");
42738
43306
  const fm = parseAssignmentFrontmatter(content);
42739
43307
  const body = content.replace(/^---\n[\s\S]*?\n---/, "");
42740
43308
  const assignmentDir = dirname29(path);
42741
- const projectDir = item.projectSlug ? resolve106(defaultProjectDir(), item.projectSlug) : null;
43309
+ const projectDir = item.projectSlug ? resolve107(defaultProjectDir(), item.projectSlug) : null;
42742
43310
  const facts = await computeFacts({ assignmentDir, frontmatter: fm, body, projectDir, terminalStatuses: terminalStatuses3, declarations });
42743
43311
  const history = fm.statusHistory;
42744
43312
  const lastHeadlineChange = [...history].reverse().find((e) => e.from !== e.to || e.from === null);
@@ -43508,8 +44076,8 @@ init_fs();
43508
44076
  init_timestamp();
43509
44077
  init_frontmatter();
43510
44078
  import { Command as Command21 } from "commander";
43511
- import { readFile as readFile78 } from "fs/promises";
43512
- import { resolve as resolve107 } from "path";
44079
+ import { readFile as readFile79 } from "fs/promises";
44080
+ import { resolve as resolve108 } from "path";
43513
44081
  function renameAssignmentStatusRefs(content, id, newId, now) {
43514
44082
  const fm = parseAssignmentFrontmatter(content);
43515
44083
  const updates = { updated: now };
@@ -43530,12 +44098,12 @@ function fail3(error) {
43530
44098
  process.exit(1);
43531
44099
  }
43532
44100
  function configPath() {
43533
- return resolve107(syntaurRoot(), "config.md");
44101
+ return resolve108(syntaurRoot(), "config.md");
43534
44102
  }
43535
44103
  async function readStatusBlock() {
43536
44104
  const p = configPath();
43537
44105
  if (!await fileExists(p)) return null;
43538
- const content = await readFile78(p, "utf-8");
44106
+ const content = await readFile79(p, "utf-8");
43539
44107
  return parseStatusConfig(content);
43540
44108
  }
43541
44109
  async function requireStatusBlock() {
@@ -43819,7 +44387,7 @@ async function runStatusRename(id, opts) {
43819
44387
  printBlockDiff(before, after);
43820
44388
  const now2 = nowTimestamp();
43821
44389
  for (const a of affected) {
43822
- const original = await readFile78(a.path, "utf-8");
44390
+ const original = await readFile79(a.path, "utf-8");
43823
44391
  const rewritten = renameAssignmentStatusRefs(original, id, newId, now2);
43824
44392
  console.log(`
43825
44393
  --- ${a.display}/assignment.md`);
@@ -43830,9 +44398,9 @@ async function runStatusRename(id, opts) {
43830
44398
  }
43831
44399
  const cfgPath = configPath();
43832
44400
  const buffers = /* @__PURE__ */ new Map();
43833
- buffers.set(cfgPath, await fileExists(cfgPath) ? await readFile78(cfgPath, "utf-8") : "");
44401
+ buffers.set(cfgPath, await fileExists(cfgPath) ? await readFile79(cfgPath, "utf-8") : "");
43834
44402
  for (const a of affected) {
43835
- buffers.set(a.path, await readFile78(a.path, "utf-8"));
44403
+ buffers.set(a.path, await readFile79(a.path, "utf-8"));
43836
44404
  }
43837
44405
  const now = nowTimestamp();
43838
44406
  try {
@@ -44034,13 +44602,13 @@ init_config2();
44034
44602
  init_timestamp();
44035
44603
  init_frontmatter();
44036
44604
  import { Command as Command22 } from "commander";
44037
- import { readFile as readFile79 } from "fs/promises";
44038
- import { resolve as resolve108 } from "path";
44605
+ import { readFile as readFile80 } from "fs/promises";
44606
+ import { resolve as resolve109 } from "path";
44039
44607
  async function readContext3(cwd) {
44040
- const path = resolve108(cwd, ".syntaur", "context.json");
44608
+ const path = resolve109(cwd, ".syntaur", "context.json");
44041
44609
  if (!await fileExists(path)) return null;
44042
44610
  try {
44043
- return JSON.parse(await readFile79(path, "utf-8"));
44611
+ return JSON.parse(await readFile80(path, "utf-8"));
44044
44612
  } catch {
44045
44613
  return null;
44046
44614
  }
@@ -44049,12 +44617,12 @@ async function resolveAssignmentPath3(opts) {
44049
44617
  if (opts.assignment) {
44050
44618
  if (opts.project) {
44051
44619
  const projectsDir2 = (await readConfig()).defaultProjectDir;
44052
- return resolve108(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
44620
+ return resolve109(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
44053
44621
  }
44054
- return resolve108(assignmentsDir(), opts.assignment, "assignment.md");
44622
+ return resolve109(assignmentsDir(), opts.assignment, "assignment.md");
44055
44623
  }
44056
44624
  const ctx = await readContext3(opts.cwd);
44057
- if (ctx?.assignmentDir) return resolve108(ctx.assignmentDir, "assignment.md");
44625
+ if (ctx?.assignmentDir) return resolve109(ctx.assignmentDir, "assignment.md");
44058
44626
  throw new Error(
44059
44627
  "No active assignment. Pass --assignment <slug> [--project <slug>] or run from a workspace with .syntaur/context.json."
44060
44628
  );
@@ -44091,7 +44659,7 @@ async function runWorkspaceSet(options, cwd = process.cwd()) {
44091
44659
  ${pre.errors.map((e) => ` - ${e}`).join("\n")}`
44092
44660
  );
44093
44661
  }
44094
- const original = await readFile79(path, "utf-8");
44662
+ const original = await readFile80(path, "utf-8");
44095
44663
  let next = updateAssignmentWorkspace(original, partial);
44096
44664
  next = updateAssignmentFile(next, { updated: nowTimestamp() });
44097
44665
  await writeFileForce(path, next);
@@ -44128,13 +44696,13 @@ init_config2();
44128
44696
  init_timestamp();
44129
44697
  init_templates();
44130
44698
  import { Command as Command23 } from "commander";
44131
- import { readFile as readFile80 } from "fs/promises";
44132
- import { resolve as resolve109 } from "path";
44699
+ import { readFile as readFile81 } from "fs/promises";
44700
+ import { resolve as resolve110 } from "path";
44133
44701
  async function readContext4(cwd) {
44134
- const path = resolve109(cwd, ".syntaur", "context.json");
44702
+ const path = resolve110(cwd, ".syntaur", "context.json");
44135
44703
  if (!await fileExists(path)) return null;
44136
44704
  try {
44137
- return JSON.parse(await readFile80(path, "utf-8"));
44705
+ return JSON.parse(await readFile81(path, "utf-8"));
44138
44706
  } catch {
44139
44707
  return null;
44140
44708
  }
@@ -44144,11 +44712,11 @@ async function resolveAssignmentDir2(opts) {
44144
44712
  if (opts.project) {
44145
44713
  const projectsDir2 = (await readConfig()).defaultProjectDir;
44146
44714
  return {
44147
- dir: resolve109(projectsDir2, opts.project, "assignments", opts.assignment),
44715
+ dir: resolve110(projectsDir2, opts.project, "assignments", opts.assignment),
44148
44716
  slug: opts.assignment
44149
44717
  };
44150
44718
  }
44151
- return { dir: resolve109(assignmentsDir(), opts.assignment), slug: opts.assignment };
44719
+ return { dir: resolve110(assignmentsDir(), opts.assignment), slug: opts.assignment };
44152
44720
  }
44153
44721
  const ctx = await readContext4(opts.cwd);
44154
44722
  if (ctx?.assignmentDir) {
@@ -44210,12 +44778,12 @@ async function runProgressLog(text, options, cwd = process.cwd()) {
44210
44778
  project: options.project,
44211
44779
  cwd
44212
44780
  });
44213
- if (!await fileExists(resolve109(dir, "assignment.md"))) {
44781
+ if (!await fileExists(resolve110(dir, "assignment.md"))) {
44214
44782
  throw new Error(`No assignment found at ${dir} (missing assignment.md).`);
44215
44783
  }
44216
- const path = resolve109(dir, "progress.md");
44784
+ const path = resolve110(dir, "progress.md");
44217
44785
  const now = nowTimestamp();
44218
- 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 });
44219
44787
  const next = appendProgressEntry(content, text, now);
44220
44788
  await writeFileForce(path, next);
44221
44789
  return path;
@@ -44420,7 +44988,7 @@ program.command("attest").description("Record an attestation (agent reviewed a r
44420
44988
  await attestCommand(assignment, fact, options);
44421
44989
  })
44422
44990
  );
44423
- 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(
44424
44992
  runCommand(async (assignment, options) => {
44425
44993
  await recomputeCommand(assignment, options);
44426
44994
  })