syntaur 0.17.1 → 0.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/.claude-plugin/plugin.json +2 -1
  2. package/dashboard/dist/assets/{_basePickBy-Dek5-ACb.js → _basePickBy-DiPrdxAJ.js} +1 -1
  3. package/dashboard/dist/assets/{_baseUniq-BeLddDKU.js → _baseUniq-CChFfAnf.js} +1 -1
  4. package/dashboard/dist/assets/{arc-kotdj4Uh.js → arc-CfkGktFw.js} +1 -1
  5. package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-BaqFXQ-q.js → architectureDiagram-2XIMDMQ5-hFcMu5eO.js} +1 -1
  6. package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-BSyGne6h.js → blockDiagram-WCTKOSBZ-GZDth9sZ.js} +1 -1
  7. package/dashboard/dist/assets/{c4Diagram-IC4MRINW-iYjZFKOW.js → c4Diagram-IC4MRINW-BVajqiYz.js} +1 -1
  8. package/dashboard/dist/assets/channel-C3CI1Xiu.js +1 -0
  9. package/dashboard/dist/assets/{chunk-4BX2VUAB-DvY9Y7jB.js → chunk-4BX2VUAB-L7ajwHyw.js} +1 -1
  10. package/dashboard/dist/assets/{chunk-55IACEB6-2vCDm2cJ.js → chunk-55IACEB6-BACmeqT4.js} +1 -1
  11. package/dashboard/dist/assets/{chunk-FMBD7UC4-BVPaoRtV.js → chunk-FMBD7UC4-BYQ2kuuH.js} +1 -1
  12. package/dashboard/dist/assets/{chunk-JSJVCQXG-B9uJpMzd.js → chunk-JSJVCQXG-akulVsXk.js} +1 -1
  13. package/dashboard/dist/assets/{chunk-KX2RTZJC-CHcjjGaq.js → chunk-KX2RTZJC-4ibFxLB_.js} +1 -1
  14. package/dashboard/dist/assets/{chunk-NQ4KR5QH-B8n19pSs.js → chunk-NQ4KR5QH-DU4iJf0Y.js} +1 -1
  15. package/dashboard/dist/assets/{chunk-QZHKN3VN-CF9ZYFGK.js → chunk-QZHKN3VN-ouyGBhQB.js} +1 -1
  16. package/dashboard/dist/assets/{chunk-WL4C6EOR-mY_1Vmdf.js → chunk-WL4C6EOR-Pq-ySwIY.js} +1 -1
  17. package/dashboard/dist/assets/classDiagram-VBA2DB6C-Ck2tXWRf.js +1 -0
  18. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-Ck2tXWRf.js +1 -0
  19. package/dashboard/dist/assets/clone-BEaO4u3F.js +1 -0
  20. package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-DOYoG3gB.js → cose-bilkent-S5V4N54A-BfkMWgQb.js} +1 -1
  21. package/dashboard/dist/assets/{dagre-KLK3FWXG-CZFrp-Yq.js → dagre-KLK3FWXG-0yhY8ZsO.js} +1 -1
  22. package/dashboard/dist/assets/{diagram-E7M64L7V-F3rF5YwM.js → diagram-E7M64L7V-DBkanpFi.js} +1 -1
  23. package/dashboard/dist/assets/{diagram-IFDJBPK2-BynWv-Vo.js → diagram-IFDJBPK2-CrucCpxV.js} +1 -1
  24. package/dashboard/dist/assets/{diagram-P4PSJMXO-NIyzhm_M.js → diagram-P4PSJMXO-BJrQ6lK3.js} +1 -1
  25. package/dashboard/dist/assets/{erDiagram-INFDFZHY-Ca9NbihE.js → erDiagram-INFDFZHY--joDpxAm.js} +1 -1
  26. package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-YhS0MOu4.js → flowDiagram-PKNHOUZH-BUXiVwju.js} +1 -1
  27. package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-G7P3qq_2.js → ganttDiagram-A5KZAMGK-u2XuIMRA.js} +1 -1
  28. package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-BJAK7M7E.js → gitGraphDiagram-K3NZZRJ6-Ayf0-gwT.js} +1 -1
  29. package/dashboard/dist/assets/{graph-11geMy3f.js → graph-BUGVXq3c.js} +1 -1
  30. package/dashboard/dist/assets/index-Bv6vn4_x.js +535 -0
  31. package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-COcQYcLf.js → infoDiagram-LFFYTUFH-DYehx6g7.js} +1 -1
  32. package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-B7Jjyw8J.js → ishikawaDiagram-PHBUUO56-Bo8-BClf.js} +1 -1
  33. package/dashboard/dist/assets/{journeyDiagram-4ABVD52K--snU-QAX.js → journeyDiagram-4ABVD52K-DLm-bRYd.js} +1 -1
  34. package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-BucLoc89.js → kanban-definition-K7BYSVSG-stPdEvds.js} +1 -1
  35. package/dashboard/dist/assets/{layout-Bcgxdz8A.js → layout-B7aPbDvN.js} +1 -1
  36. package/dashboard/dist/assets/{linear-CRF8xwvk.js → linear-CtQ_RMAp.js} +1 -1
  37. package/dashboard/dist/assets/{mermaid.core-D5JD5uYg.js → mermaid.core-CcJoQ5At.js} +4 -4
  38. package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-BqGcTcbk.js → mindmap-definition-YRQLILUH-DahXO6Ui.js} +1 -1
  39. package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-CnQ2EkyX.js → pieDiagram-SKSYHLDU-DXv973_0.js} +1 -1
  40. package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-CKCfmTRc.js → quadrantDiagram-337W2JSQ-5U0ryvjr.js} +1 -1
  41. package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-B9J4bvUK.js → requirementDiagram-Z7DCOOCP-CvGflSyl.js} +1 -1
  42. package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-CMSzI5rg.js → sankeyDiagram-WA2Y5GQK-AeetdpCi.js} +1 -1
  43. package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-BFQDtxd0.js → sequenceDiagram-2WXFIKYE-BhRR4qzn.js} +1 -1
  44. package/dashboard/dist/assets/{stateDiagram-RAJIS63D-DgWYKT02.js → stateDiagram-RAJIS63D-CxxF58nH.js} +1 -1
  45. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-DHCNHbs7.js +1 -0
  46. package/dashboard/dist/assets/{timeline-definition-YZTLITO2-jANu0e7M.js → timeline-definition-YZTLITO2-BEQIeygp.js} +1 -1
  47. package/dashboard/dist/assets/{treemap-KZPCXAKY-BFjWMkyM.js → treemap-KZPCXAKY-BCWNTFfj.js} +1 -1
  48. package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-DGwrAJYj.js → vennDiagram-LZ73GAT5-BcuC_UIz.js} +1 -1
  49. package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-BLCPfoFM.js → xychartDiagram-JWTSCODW-CWGez3_f.js} +1 -1
  50. package/dashboard/dist/index.html +1 -1
  51. package/dist/dashboard/server.js +198 -160
  52. package/dist/dashboard/server.js.map +1 -1
  53. package/dist/index.js +329 -290
  54. package/dist/index.js.map +1 -1
  55. package/dist/launch/index.js +20 -1
  56. package/dist/launch/index.js.map +1 -1
  57. package/package.json +1 -1
  58. package/platforms/claude-code/.claude-plugin/plugin.json +2 -1
  59. package/platforms/claude-code/commands/run-playbook/run-playbook.md +16 -0
  60. package/platforms/claude-code/skills/run-playbook/SKILL.md +87 -0
  61. package/platforms/codex/skills/run-playbook/SKILL.md +87 -0
  62. package/skills/run-playbook/SKILL.md +87 -0
  63. package/dashboard/dist/assets/channel-eXdj7ayV.js +0 -1
  64. package/dashboard/dist/assets/classDiagram-VBA2DB6C-B-bBQACr.js +0 -1
  65. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-B-bBQACr.js +0 -1
  66. package/dashboard/dist/assets/clone-CL04pv9J.js +0 -1
  67. package/dashboard/dist/assets/index--qwybK3W.js +0 -535
  68. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-Nmwft4cR.js +0 -1
package/dist/index.js CHANGED
@@ -5042,8 +5042,8 @@ async function migrateFromMarkdown(projectsDir2) {
5042
5042
  return allSessions.length;
5043
5043
  }
5044
5044
  async function parseMarkdownSessionsIndex(filePath, projectSlug) {
5045
- const { readFile: readFile54 } = await import("fs/promises");
5046
- const raw = await readFile54(filePath, "utf-8");
5045
+ const { readFile: readFile53 } = await import("fs/promises");
5046
+ const raw = await readFile53(filePath, "utf-8");
5047
5047
  const sessions = [];
5048
5048
  const lines = raw.split("\n");
5049
5049
  let inTable = false;
@@ -5460,12 +5460,13 @@ __export(scanner_exports, {
5460
5460
  import { execFile } from "child_process";
5461
5461
  import { promisify } from "util";
5462
5462
  import { resolve as resolve15 } from "path";
5463
- import { realpath, readdir as readdir8, readFile as readFile10 } from "fs/promises";
5463
+ import { realpath } from "fs/promises";
5464
5464
  function clearScanCache() {
5465
5465
  cache = null;
5466
5466
  forceFreshNext = true;
5467
5467
  scanEpoch++;
5468
5468
  inFlight = null;
5469
+ tmuxAvailableCache = null;
5469
5470
  }
5470
5471
  function emptyScan() {
5471
5472
  return { sessions: [], tmuxAvailable: false };
@@ -5523,8 +5524,10 @@ async function execQuiet(cmd, args) {
5523
5524
  }
5524
5525
  }
5525
5526
  async function checkTmuxAvailable() {
5526
- const result = await execQuiet("which", ["tmux"]);
5527
- return result.length > 0;
5527
+ if (!tmuxAvailableCache) {
5528
+ tmuxAvailableCache = execQuiet("which", ["tmux"]).then((result) => result.length > 0);
5529
+ }
5530
+ return tmuxAvailableCache;
5528
5531
  }
5529
5532
  async function sessionAlive(name) {
5530
5533
  try {
@@ -5619,62 +5622,11 @@ async function getGitInfo(cwd) {
5619
5622
  return { branch: branch || null, worktree: isWorktree };
5620
5623
  }
5621
5624
  async function loadWorkspaceRecords(projectsDir2, assignmentsDir2) {
5622
- const records = [];
5623
5625
  try {
5624
- const projects = await listProjects(projectsDir2);
5625
- for (const project of projects) {
5626
- const projectAssignmentsDir = resolve15(projectsDir2, project.slug, "assignments");
5627
- let slugs;
5628
- try {
5629
- slugs = await readdir8(projectAssignmentsDir);
5630
- } catch {
5631
- continue;
5632
- }
5633
- for (const aslug of slugs) {
5634
- const aFile = resolve15(projectAssignmentsDir, aslug, "assignment.md");
5635
- try {
5636
- const raw = await readFile10(aFile, "utf-8");
5637
- const [fm] = extractFrontmatter(raw);
5638
- if (!fm) continue;
5639
- records.push({
5640
- projectSlug: project.slug,
5641
- assignmentSlug: aslug,
5642
- assignmentTitle: getField(fm, "title") ?? aslug,
5643
- worktreePath: getNestedField(fm, "workspace", "worktreePath") ?? null,
5644
- branch: getNestedField(fm, "workspace", "branch") ?? null
5645
- });
5646
- } catch {
5647
- continue;
5648
- }
5649
- }
5650
- }
5626
+ return await listWorkspaceRecords(projectsDir2, assignmentsDir2);
5651
5627
  } catch {
5628
+ return [];
5652
5629
  }
5653
- if (assignmentsDir2) {
5654
- try {
5655
- const entries = await readdir8(assignmentsDir2);
5656
- for (const id of entries) {
5657
- if (id.startsWith(".") || id.startsWith("_")) continue;
5658
- const aFile = resolve15(assignmentsDir2, id, "assignment.md");
5659
- try {
5660
- const raw = await readFile10(aFile, "utf-8");
5661
- const [fm] = extractFrontmatter(raw);
5662
- if (!fm) continue;
5663
- records.push({
5664
- projectSlug: null,
5665
- assignmentSlug: id,
5666
- assignmentTitle: getField(fm, "title") ?? id,
5667
- worktreePath: getNestedField(fm, "workspace", "worktreePath") ?? null,
5668
- branch: getNestedField(fm, "workspace", "branch") ?? null
5669
- });
5670
- } catch {
5671
- continue;
5672
- }
5673
- }
5674
- } catch {
5675
- }
5676
- }
5677
- return records;
5678
5630
  }
5679
5631
  async function resolveAndNormalize(p) {
5680
5632
  try {
@@ -5945,13 +5897,12 @@ async function scanSingleSession(serversDir2, projectsDir2, name, options) {
5945
5897
  }
5946
5898
  return scanSession(data, lsofOutput, workspaceRecords, procSnapshot);
5947
5899
  }
5948
- var exec, cache, lastKnown, inFlight, forceFreshNext, scanEpoch, cacheKey, CACHE_TTL_MS, COLD_WAIT_BUDGET_MS, PROBE_TIMEOUT_MS, PROBE_MAX_BUFFER;
5900
+ var exec, cache, lastKnown, inFlight, forceFreshNext, scanEpoch, cacheKey, CACHE_TTL_MS, COLD_WAIT_BUDGET_MS, PROBE_TIMEOUT_MS, PROBE_MAX_BUFFER, tmuxAvailableCache;
5949
5901
  var init_scanner = __esm({
5950
5902
  "src/dashboard/scanner.ts"() {
5951
5903
  "use strict";
5952
5904
  init_api();
5953
5905
  init_servers();
5954
- init_parser();
5955
5906
  exec = promisify(execFile);
5956
5907
  cache = null;
5957
5908
  lastKnown = null;
@@ -5963,11 +5914,12 @@ var init_scanner = __esm({
5963
5914
  COLD_WAIT_BUDGET_MS = 2500;
5964
5915
  PROBE_TIMEOUT_MS = 15e3;
5965
5916
  PROBE_MAX_BUFFER = 32 * 1024 * 1024;
5917
+ tmuxAvailableCache = null;
5966
5918
  }
5967
5919
  });
5968
5920
 
5969
5921
  // src/dashboard/api.ts
5970
- import { readdir as readdir9, readFile as readFile11, writeFile as writeFile3 } from "fs/promises";
5922
+ import { readdir as readdir8, readFile as readFile10, writeFile as writeFile3 } from "fs/promises";
5971
5923
  import { resolve as resolve16, dirname as dirname3, basename } from "path";
5972
5924
  function clearFrontmatterField(content, key) {
5973
5925
  const fieldRegex = new RegExp(`^(${escapeRegExp2(key)}:)\\s*.*$`, "m");
@@ -6007,10 +5959,41 @@ function emitTrace(traces, meta) {
6007
5959
  JSON.stringify({ kind: "overview-trace", totalMs, phases: traces.entries, subPhases, ...meta })
6008
5960
  );
6009
5961
  }
5962
+ function invalidateRecordsCache() {
5963
+ projectRecordsCache.clear();
5964
+ standaloneRecordsCache.clear();
5965
+ }
5966
+ function installRecordsInvalidation(router) {
5967
+ for (const method of ["post", "put", "patch", "delete"]) {
5968
+ const original = router[method].bind(router);
5969
+ router[method] = (path, ...handlers) => {
5970
+ if (handlers.length > 0) {
5971
+ const last = handlers[handlers.length - 1];
5972
+ handlers[handlers.length - 1] = async (req, res, next) => {
5973
+ try {
5974
+ return await last(req, res, next);
5975
+ } finally {
5976
+ invalidateRecordsCache();
5977
+ }
5978
+ };
5979
+ }
5980
+ return original(path, ...handlers);
5981
+ };
5982
+ }
5983
+ }
6010
5984
  async function listStandaloneRecords(assignmentsDir2) {
5985
+ const key = assignmentsDir2 ?? "";
5986
+ const cached = standaloneRecordsCache.get(key);
5987
+ if (cached) return cached;
5988
+ const promise = computeStandaloneRecords(assignmentsDir2);
5989
+ standaloneRecordsCache.set(key, promise);
5990
+ promise.catch(() => standaloneRecordsCache.delete(key));
5991
+ return promise;
5992
+ }
5993
+ async function computeStandaloneRecords(assignmentsDir2) {
6011
5994
  if (!assignmentsDir2) return [];
6012
5995
  if (!await fileExists(assignmentsDir2)) return [];
6013
- const entries = await readdir9(assignmentsDir2, { withFileTypes: true });
5996
+ const entries = await readdir8(assignmentsDir2, { withFileTypes: true });
6014
5997
  const records = [];
6015
5998
  for (const entry of entries) {
6016
5999
  if (!entry.isDirectory() || entry.name.startsWith(".") || entry.name.startsWith("_")) continue;
@@ -6018,7 +6001,7 @@ async function listStandaloneRecords(assignmentsDir2) {
6018
6001
  const assignmentMdPath2 = resolve16(assignmentDir, "assignment.md");
6019
6002
  if (!await fileExists(assignmentMdPath2)) continue;
6020
6003
  try {
6021
- const content = await readFile11(assignmentMdPath2, "utf-8");
6004
+ const content = await readFile10(assignmentMdPath2, "utf-8");
6022
6005
  const record = parseAssignmentFull(content);
6023
6006
  records.push({ assignmentDir, id: entry.name, record });
6024
6007
  } catch {
@@ -6094,7 +6077,7 @@ async function listProjects(projectsDir2) {
6094
6077
  async function readWorkspaceRegistry(projectsDir2) {
6095
6078
  const registryPath = resolve16(dirname3(projectsDir2), "workspaces.json");
6096
6079
  try {
6097
- const raw = await readFile11(registryPath, "utf-8");
6080
+ const raw = await readFile10(registryPath, "utf-8");
6098
6081
  const parsed = JSON.parse(raw);
6099
6082
  return Array.isArray(parsed) ? parsed.filter((w) => typeof w === "string") : [];
6100
6083
  } catch {
@@ -6130,6 +6113,34 @@ async function listWorkspaces(projectsDir2, assignmentsDir2) {
6130
6113
  const workspaces = Array.from(workspaceSet).sort();
6131
6114
  return { workspaces, hasUngrouped };
6132
6115
  }
6116
+ async function listWorkspaceRecords(projectsDir2, assignmentsDir2) {
6117
+ const [projectRecords, standaloneRecords] = await Promise.all([
6118
+ listProjectRecords(projectsDir2),
6119
+ listStandaloneRecords(assignmentsDir2)
6120
+ ]);
6121
+ const records = [];
6122
+ for (const project of projectRecords) {
6123
+ for (const assignment of project.assignments) {
6124
+ records.push({
6125
+ projectSlug: project.summary.slug,
6126
+ assignmentSlug: assignment.slug,
6127
+ assignmentTitle: assignment.title || assignment.slug,
6128
+ worktreePath: assignment.workspace.worktreePath ?? null,
6129
+ branch: assignment.workspace.branch ?? null
6130
+ });
6131
+ }
6132
+ }
6133
+ for (const standalone of standaloneRecords) {
6134
+ records.push({
6135
+ projectSlug: null,
6136
+ assignmentSlug: standalone.id,
6137
+ assignmentTitle: standalone.record.title || standalone.id,
6138
+ worktreePath: standalone.record.workspace.worktreePath ?? null,
6139
+ branch: standalone.record.workspace.branch ?? null
6140
+ });
6141
+ }
6142
+ return records;
6143
+ }
6133
6144
  async function createWorkspace(projectsDir2, name) {
6134
6145
  const registered = await readWorkspaceRegistry(projectsDir2);
6135
6146
  if (!registered.includes(name)) {
@@ -6155,7 +6166,7 @@ async function deleteWorkspace(projectsDir2, name, opts = {}) {
6155
6166
  const timestamp = nowTimestamp();
6156
6167
  for (const slug of projectsReferencing) {
6157
6168
  const path = resolve16(projectsDir2, slug, "project.md");
6158
- const raw = await readFile11(path, "utf-8");
6169
+ const raw = await readFile10(path, "utf-8");
6159
6170
  let next = clearFrontmatterField(raw, "workspace");
6160
6171
  next = setUpdatedField(next, timestamp);
6161
6172
  await writeFileForce(path, next);
@@ -6164,7 +6175,7 @@ async function deleteWorkspace(projectsDir2, name, opts = {}) {
6164
6175
  for (const id of standalonesReferencing) {
6165
6176
  if (!opts.assignmentsDir) break;
6166
6177
  const path = resolve16(opts.assignmentsDir, id, "assignment.md");
6167
- const raw = await readFile11(path, "utf-8");
6178
+ const raw = await readFile10(path, "utf-8");
6168
6179
  let next = clearFrontmatterField(raw, "workspaceGroup");
6169
6180
  next = setUpdatedField(next, timestamp);
6170
6181
  await writeFileForce(path, next);
@@ -6174,6 +6185,9 @@ async function deleteWorkspace(projectsDir2, name, opts = {}) {
6174
6185
  const registered = await readWorkspaceRegistry(projectsDir2);
6175
6186
  const filtered = registered.filter((w) => w !== name);
6176
6187
  await writeWorkspaceRegistry(projectsDir2, filtered);
6188
+ if (rewroteFiles) {
6189
+ invalidateRecordsCache();
6190
+ }
6177
6191
  return { rewroteFiles };
6178
6192
  }
6179
6193
  async function getOverview(projectsDir2, serversDir2, assignmentsDir2, options = {}) {
@@ -6341,7 +6355,7 @@ async function getEditableDocument(projectsDir2, documentType, projectSlug, assi
6341
6355
  if (!filePath || !await fileExists(filePath)) {
6342
6356
  return null;
6343
6357
  }
6344
- const content = await readFile11(filePath, "utf-8");
6358
+ const content = await readFile10(filePath, "utf-8");
6345
6359
  const title = getEditableDocumentTitle(documentType, projectSlug, assignmentSlug);
6346
6360
  return {
6347
6361
  documentType,
@@ -6367,7 +6381,7 @@ async function getEditableDocumentById(projectsDir2, assignmentsDir2, documentTy
6367
6381
  if (!fileName) return null;
6368
6382
  const filePath = resolve16(resolved.assignmentDir, fileName);
6369
6383
  if (!await fileExists(filePath)) return null;
6370
- const content = await readFile11(filePath, "utf-8");
6384
+ const content = await readFile10(filePath, "utf-8");
6371
6385
  const label = resolved.id;
6372
6386
  const title = documentType === "assignment" ? `Edit Assignment: ${label}` : documentType === "plan" ? `Edit Plan: ${label}` : documentType === "scratchpad" ? `Edit Scratchpad: ${label}` : documentType === "handoff" ? `Append Handoff: ${label}` : `Append Decision: ${label}`;
6373
6387
  return {
@@ -6386,7 +6400,7 @@ async function getProjectDetail(projectsDir2, slug) {
6386
6400
  if (!await fileExists(projectMdPath)) {
6387
6401
  return null;
6388
6402
  }
6389
- const projectContent = await readFile11(projectMdPath, "utf-8");
6403
+ const projectContent = await readFile10(projectMdPath, "utf-8");
6390
6404
  const project = parseProject(projectContent);
6391
6405
  const assignments = await listAssignmentRecords(projectPath);
6392
6406
  const rollup = await buildProjectRollup(projectPath, project, assignments);
@@ -6423,18 +6437,18 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
6423
6437
  if (!await fileExists(assignmentMdPath2)) {
6424
6438
  return null;
6425
6439
  }
6426
- const assignmentContent = await readFile11(assignmentMdPath2, "utf-8");
6440
+ const assignmentContent = await readFile10(assignmentMdPath2, "utf-8");
6427
6441
  const assignment = parseAssignmentFull(assignmentContent);
6428
6442
  let projectWorkspace = null;
6429
6443
  const projectMdPath = resolve16(projectsDir2, projectSlug, "project.md");
6430
6444
  if (await fileExists(projectMdPath)) {
6431
- const projectContent = await readFile11(projectMdPath, "utf-8");
6445
+ const projectContent = await readFile10(projectMdPath, "utf-8");
6432
6446
  projectWorkspace = parseProject(projectContent).workspace;
6433
6447
  }
6434
6448
  let plan = null;
6435
6449
  const planPath = resolve16(assignmentDir, "plan.md");
6436
6450
  if (await fileExists(planPath)) {
6437
- const planContent = await readFile11(planPath, "utf-8");
6451
+ const planContent = await readFile10(planPath, "utf-8");
6438
6452
  const parsed = parsePlan(planContent);
6439
6453
  plan = {
6440
6454
  status: parsed.status,
@@ -6445,7 +6459,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
6445
6459
  let scratchpad = null;
6446
6460
  const scratchpadPath = resolve16(assignmentDir, "scratchpad.md");
6447
6461
  if (await fileExists(scratchpadPath)) {
6448
- const scratchpadContent = await readFile11(scratchpadPath, "utf-8");
6462
+ const scratchpadContent = await readFile10(scratchpadPath, "utf-8");
6449
6463
  const parsed = parseScratchpad(scratchpadContent);
6450
6464
  scratchpad = {
6451
6465
  updated: parsed.updated,
@@ -6455,7 +6469,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
6455
6469
  let handoff = null;
6456
6470
  const handoffPath = resolve16(assignmentDir, "handoff.md");
6457
6471
  if (await fileExists(handoffPath)) {
6458
- const handoffContent = await readFile11(handoffPath, "utf-8");
6472
+ const handoffContent = await readFile10(handoffPath, "utf-8");
6459
6473
  const parsed = parseHandoff(handoffContent);
6460
6474
  handoff = {
6461
6475
  updated: parsed.updated,
@@ -6466,7 +6480,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
6466
6480
  let decisionRecord = null;
6467
6481
  const decisionRecordPath = resolve16(assignmentDir, "decision-record.md");
6468
6482
  if (await fileExists(decisionRecordPath)) {
6469
- const decisionRecordContent = await readFile11(decisionRecordPath, "utf-8");
6483
+ const decisionRecordContent = await readFile10(decisionRecordPath, "utf-8");
6470
6484
  const parsed = parseDecisionRecord(decisionRecordContent);
6471
6485
  decisionRecord = {
6472
6486
  updated: parsed.updated,
@@ -6477,7 +6491,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
6477
6491
  let progress = null;
6478
6492
  const progressPath = resolve16(assignmentDir, "progress.md");
6479
6493
  if (await fileExists(progressPath)) {
6480
- const progressContent = await readFile11(progressPath, "utf-8");
6494
+ const progressContent = await readFile10(progressPath, "utf-8");
6481
6495
  const parsed = parseProgress(progressContent);
6482
6496
  progress = {
6483
6497
  updated: parsed.updated,
@@ -6488,7 +6502,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
6488
6502
  let comments = null;
6489
6503
  const commentsPath = resolve16(assignmentDir, "comments.md");
6490
6504
  if (await fileExists(commentsPath)) {
6491
- const commentsContent = await readFile11(commentsPath, "utf-8");
6505
+ const commentsContent = await readFile10(commentsPath, "utf-8");
6492
6506
  const parsed = parseComments(commentsContent);
6493
6507
  comments = {
6494
6508
  updated: parsed.updated,
@@ -6639,7 +6653,7 @@ async function countMentionsInAssignment(sourceDir, target) {
6639
6653
  const bodies = [];
6640
6654
  const assignmentMd = resolve16(sourceDir, "assignment.md");
6641
6655
  if (await fileExists(assignmentMd)) {
6642
- const content = await readFile11(assignmentMd, "utf-8");
6656
+ const content = await readFile10(assignmentMd, "utf-8");
6643
6657
  const todosMatch = content.match(/^## Todos\s*$([\s\S]*?)(?=^## |$(?![\r\n]))/m);
6644
6658
  if (todosMatch) bodies.push(todosMatch[1]);
6645
6659
  }
@@ -6647,7 +6661,7 @@ async function countMentionsInAssignment(sourceDir, target) {
6647
6661
  const path = resolve16(sourceDir, filename);
6648
6662
  if (await fileExists(path)) {
6649
6663
  try {
6650
- bodies.push(await readFile11(path, "utf-8"));
6664
+ bodies.push(await readFile10(path, "utf-8"));
6651
6665
  } catch {
6652
6666
  }
6653
6667
  }
@@ -6707,42 +6721,42 @@ async function buildStandaloneAssignmentDetail(resolved) {
6707
6721
  const assignmentDir = resolved.assignmentDir;
6708
6722
  const assignmentMdPath2 = resolve16(assignmentDir, "assignment.md");
6709
6723
  if (!await fileExists(assignmentMdPath2)) return null;
6710
- const assignmentContent = await readFile11(assignmentMdPath2, "utf-8");
6724
+ const assignmentContent = await readFile10(assignmentMdPath2, "utf-8");
6711
6725
  const assignment = parseAssignmentFull(assignmentContent);
6712
6726
  let plan = null;
6713
6727
  const planPath = resolve16(assignmentDir, "plan.md");
6714
6728
  if (await fileExists(planPath)) {
6715
- const parsed = parsePlan(await readFile11(planPath, "utf-8"));
6729
+ const parsed = parsePlan(await readFile10(planPath, "utf-8"));
6716
6730
  plan = { status: parsed.status, updated: parsed.updated, body: parsed.body };
6717
6731
  }
6718
6732
  let scratchpad = null;
6719
6733
  const scratchpadPath = resolve16(assignmentDir, "scratchpad.md");
6720
6734
  if (await fileExists(scratchpadPath)) {
6721
- const parsed = parseScratchpad(await readFile11(scratchpadPath, "utf-8"));
6735
+ const parsed = parseScratchpad(await readFile10(scratchpadPath, "utf-8"));
6722
6736
  scratchpad = { updated: parsed.updated, body: parsed.body };
6723
6737
  }
6724
6738
  let handoff = null;
6725
6739
  const handoffPath = resolve16(assignmentDir, "handoff.md");
6726
6740
  if (await fileExists(handoffPath)) {
6727
- const parsed = parseHandoff(await readFile11(handoffPath, "utf-8"));
6741
+ const parsed = parseHandoff(await readFile10(handoffPath, "utf-8"));
6728
6742
  handoff = { updated: parsed.updated, handoffCount: parsed.handoffCount, body: parsed.body };
6729
6743
  }
6730
6744
  let decisionRecord = null;
6731
6745
  const decisionRecordPath = resolve16(assignmentDir, "decision-record.md");
6732
6746
  if (await fileExists(decisionRecordPath)) {
6733
- const parsed = parseDecisionRecord(await readFile11(decisionRecordPath, "utf-8"));
6747
+ const parsed = parseDecisionRecord(await readFile10(decisionRecordPath, "utf-8"));
6734
6748
  decisionRecord = { updated: parsed.updated, decisionCount: parsed.decisionCount, body: parsed.body };
6735
6749
  }
6736
6750
  let progress = null;
6737
6751
  const progressPath = resolve16(assignmentDir, "progress.md");
6738
6752
  if (await fileExists(progressPath)) {
6739
- const parsed = parseProgress(await readFile11(progressPath, "utf-8"));
6753
+ const parsed = parseProgress(await readFile10(progressPath, "utf-8"));
6740
6754
  progress = { updated: parsed.updated, entryCount: parsed.entryCount, entries: parsed.entries };
6741
6755
  }
6742
6756
  let comments = null;
6743
6757
  const commentsPath = resolve16(assignmentDir, "comments.md");
6744
6758
  if (await fileExists(commentsPath)) {
6745
- const parsed = parseComments(await readFile11(commentsPath, "utf-8"));
6759
+ const parsed = parseComments(await readFile10(commentsPath, "utf-8"));
6746
6760
  comments = { updated: parsed.updated, entryCount: parsed.entryCount, entries: parsed.entries };
6747
6761
  }
6748
6762
  const detail = {
@@ -6779,6 +6793,14 @@ async function buildStandaloneAssignmentDetail(resolved) {
6779
6793
  return detail;
6780
6794
  }
6781
6795
  async function listProjectRecords(projectsDir2, traces) {
6796
+ const cached = projectRecordsCache.get(projectsDir2);
6797
+ if (cached) return cached;
6798
+ const promise = computeProjectRecords(projectsDir2, traces);
6799
+ projectRecordsCache.set(projectsDir2, promise);
6800
+ promise.catch(() => projectRecordsCache.delete(projectsDir2));
6801
+ return promise;
6802
+ }
6803
+ async function computeProjectRecords(projectsDir2, traces) {
6782
6804
  if (!await fileExists(projectsDir2)) {
6783
6805
  return [];
6784
6806
  }
@@ -6786,7 +6808,7 @@ async function listProjectRecords(projectsDir2, traces) {
6786
6808
  migratedProjectsDirs.add(projectsDir2);
6787
6809
  await migrateLegacyProjectFiles(projectsDir2);
6788
6810
  }
6789
- const entries = await readdir9(projectsDir2, { withFileTypes: true });
6811
+ const entries = await readdir8(projectsDir2, { withFileTypes: true });
6790
6812
  const projectDirs = entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
6791
6813
  const maybeRecords = await Promise.all(
6792
6814
  projectDirs.map(async (entry) => {
@@ -6796,7 +6818,7 @@ async function listProjectRecords(projectsDir2, traces) {
6796
6818
  return null;
6797
6819
  }
6798
6820
  const t0 = traces ? performance.now() : 0;
6799
- const projectContent = await readFile11(projectMdPath, "utf-8");
6821
+ const projectContent = await readFile10(projectMdPath, "utf-8");
6800
6822
  const project = parseProject(projectContent);
6801
6823
  if (traces) accumulatePhase(traces, "parse-project-md", performance.now() - t0);
6802
6824
  const t1 = traces ? performance.now() : 0;
@@ -6841,7 +6863,7 @@ async function listAssignmentRecords(projectPath, traces) {
6841
6863
  if (!await fileExists(assignmentsDir2)) {
6842
6864
  return [];
6843
6865
  }
6844
- const entries = await readdir9(assignmentsDir2, { withFileTypes: true });
6866
+ const entries = await readdir8(assignmentsDir2, { withFileTypes: true });
6845
6867
  const dirEntries = entries.filter((entry) => entry.isDirectory());
6846
6868
  const maybeRecords = await Promise.all(
6847
6869
  dirEntries.map(async (entry) => {
@@ -6850,7 +6872,7 @@ async function listAssignmentRecords(projectPath, traces) {
6850
6872
  return null;
6851
6873
  }
6852
6874
  const t0 = traces ? performance.now() : 0;
6853
- const content = await readFile11(assignmentMd, "utf-8");
6875
+ const content = await readFile10(assignmentMd, "utf-8");
6854
6876
  const parsed = parseAssignmentFull(content);
6855
6877
  if (traces) accumulatePhase(traces, "read-assignment-md", performance.now() - t0);
6856
6878
  return parsed;
@@ -6865,14 +6887,14 @@ async function listResources(projectPath) {
6865
6887
  if (!await fileExists(resourcesDir)) {
6866
6888
  return [];
6867
6889
  }
6868
- const entries = await readdir9(resourcesDir, { withFileTypes: true });
6890
+ const entries = await readdir8(resourcesDir, { withFileTypes: true });
6869
6891
  const results = [];
6870
6892
  for (const entry of entries) {
6871
6893
  if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name.startsWith("_")) {
6872
6894
  continue;
6873
6895
  }
6874
6896
  const filePath = resolve16(resourcesDir, entry.name);
6875
- const content = await readFile11(filePath, "utf-8");
6897
+ const content = await readFile10(filePath, "utf-8");
6876
6898
  const parsed = parseResource(content);
6877
6899
  results.push({
6878
6900
  name: parsed.name,
@@ -6891,14 +6913,14 @@ async function listMemories(projectPath) {
6891
6913
  if (!await fileExists(memoriesDir)) {
6892
6914
  return [];
6893
6915
  }
6894
- const entries = await readdir9(memoriesDir, { withFileTypes: true });
6916
+ const entries = await readdir8(memoriesDir, { withFileTypes: true });
6895
6917
  const results = [];
6896
6918
  for (const entry of entries) {
6897
6919
  if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name.startsWith("_")) {
6898
6920
  continue;
6899
6921
  }
6900
6922
  const filePath = resolve16(memoriesDir, entry.name);
6901
- const content = await readFile11(filePath, "utf-8");
6923
+ const content = await readFile10(filePath, "utf-8");
6902
6924
  const parsed = parseMemory(content);
6903
6925
  results.push({
6904
6926
  name: parsed.name,
@@ -6961,7 +6983,7 @@ async function getMemoryDetail(projectsDir2, projectSlug, itemSlug) {
6961
6983
  if (!projectRecord) return null;
6962
6984
  const filePath = resolve16(projectRecord.projectPath, "memories", `${itemSlug}.md`);
6963
6985
  if (!await fileExists(filePath)) return null;
6964
- const content = await readFile11(filePath, "utf-8");
6986
+ const content = await readFile10(filePath, "utf-8");
6965
6987
  const parsed = parseMemory(content);
6966
6988
  return {
6967
6989
  name: parsed.name,
@@ -6987,7 +7009,7 @@ async function getResourceDetail(projectsDir2, projectSlug, itemSlug) {
6987
7009
  if (!projectRecord) return null;
6988
7010
  const filePath = resolve16(projectRecord.projectPath, "resources", `${itemSlug}.md`);
6989
7011
  if (!await fileExists(filePath)) return null;
6990
- const content = await readFile11(filePath, "utf-8");
7012
+ const content = await readFile10(filePath, "utf-8");
6991
7013
  const parsed = parseResource(content);
6992
7014
  return {
6993
7015
  name: parsed.name,
@@ -7005,7 +7027,7 @@ async function getResourceDetail(projectsDir2, projectSlug, itemSlug) {
7005
7027
  async function loadDependencyGraph(projectPath, assignments) {
7006
7028
  const statusPath = resolve16(projectPath, "_status.md");
7007
7029
  if (await fileExists(statusPath)) {
7008
- const statusContent = await readFile11(statusPath, "utf-8");
7030
+ const statusContent = await readFile10(statusPath, "utf-8");
7009
7031
  const parsed = parseStatus(statusContent);
7010
7032
  const derivedGraph = extractMermaidGraph(parsed.body);
7011
7033
  if (derivedGraph) {
@@ -7166,7 +7188,7 @@ async function getUnmetDependencies(projectPath, dependsOn, terminalStatuses3, d
7166
7188
  unmet.push(`${dependency} (missing)`);
7167
7189
  continue;
7168
7190
  }
7169
- const content = await readFile11(dependencyPath, "utf-8");
7191
+ const content = await readFile10(dependencyPath, "utf-8");
7170
7192
  const parsed = parseAssignmentFull(content);
7171
7193
  if (!terminals.has(parsed.status)) {
7172
7194
  unmet.push(`${dependency} (${parsed.status})`);
@@ -7459,7 +7481,7 @@ async function countOpenQuestions(projectPath, assignmentSlug) {
7459
7481
  return 0;
7460
7482
  }
7461
7483
  try {
7462
- const content = await readFile11(commentsPath, "utf-8");
7484
+ const content = await readFile10(commentsPath, "utf-8");
7463
7485
  const parsed = parseComments(content);
7464
7486
  return parsed.entries.filter(
7465
7487
  (e) => e.type === "question" && e.resolved !== true
@@ -7527,12 +7549,12 @@ async function listPlaybooks(playbooksDir3) {
7527
7549
  if (!await fileExists(playbooksDir3)) return [];
7528
7550
  const config = await readConfig();
7529
7551
  const disabledSet = new Set(config.playbooks.disabled);
7530
- const entries = await readdir9(playbooksDir3, { withFileTypes: true });
7552
+ const entries = await readdir8(playbooksDir3, { withFileTypes: true });
7531
7553
  const playbooks = [];
7532
7554
  for (const entry of entries) {
7533
7555
  if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name.startsWith("_") || entry.name === "manifest.md") continue;
7534
7556
  const filePath = resolve16(playbooksDir3, entry.name);
7535
- const raw = await readFile11(filePath, "utf-8");
7557
+ const raw = await readFile10(filePath, "utf-8");
7536
7558
  const parsed = parsePlaybook(raw);
7537
7559
  const slug = parsed.slug || entry.name.replace(/\.md$/, "");
7538
7560
  playbooks.push({
@@ -7566,7 +7588,7 @@ async function getPlaybookDetail(playbooksDir3, slug) {
7566
7588
  enabled
7567
7589
  };
7568
7590
  }
7569
- 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, DEFAULT_TRANSITION_DEFINITIONS, DEFAULT_STATUS_COLORS, _cachedConfig, REFERENCED_BY_LIMIT, migratedProjectsDirs, DEFAULT_GRAPH_COLORS;
7591
+ 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, DEFAULT_STATUS_COLORS, _cachedConfig, REFERENCED_BY_LIMIT, migratedProjectsDirs, DEFAULT_GRAPH_COLORS;
7570
7592
  var init_api = __esm({
7571
7593
  "src/dashboard/api.ts"() {
7572
7594
  "use strict";
@@ -7617,6 +7639,8 @@ var init_api = __esm({
7617
7639
  ["blocked", "blocked"],
7618
7640
  ["stale", "stale"]
7619
7641
  ];
7642
+ projectRecordsCache = /* @__PURE__ */ new Map();
7643
+ standaloneRecordsCache = /* @__PURE__ */ new Map();
7620
7644
  DEFAULT_TRANSITION_DEFINITIONS = [
7621
7645
  {
7622
7646
  command: "start",
@@ -7713,7 +7737,7 @@ __export(git_worktree_exports, {
7713
7737
  removeWorktree: () => removeWorktree
7714
7738
  });
7715
7739
  import { spawn } from "child_process";
7716
- import { readFile as readFile14 } from "fs/promises";
7740
+ import { readFile as readFile13 } from "fs/promises";
7717
7741
  function run(command, args, cwd) {
7718
7742
  return new Promise((resolvePromise) => {
7719
7743
  const child = spawn(command, args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
@@ -7757,7 +7781,7 @@ async function createWorktreeAndRecord(opts) {
7757
7781
  const { assignmentPath, repository, branch, worktreePath, parentBranch } = opts;
7758
7782
  await createWorktree({ repository, branch, worktreePath, parentBranch });
7759
7783
  try {
7760
- const content = await readFile14(assignmentPath, "utf-8");
7784
+ const content = await readFile13(assignmentPath, "utf-8");
7761
7785
  const updated = updateAssignmentWorkspace(content, {
7762
7786
  repository,
7763
7787
  worktreePath,
@@ -9604,7 +9628,7 @@ function mergePatch(current, patch) {
9604
9628
  // src/utils/view-prefs.ts
9605
9629
  init_paths();
9606
9630
  init_fs();
9607
- import { readFile as readFile12, rename as rename3, unlink as unlink3 } from "fs/promises";
9631
+ import { readFile as readFile11, rename as rename3, unlink as unlink3 } from "fs/promises";
9608
9632
  import { resolve as resolve17 } from "path";
9609
9633
  function corruptFilePath() {
9610
9634
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
@@ -9627,7 +9651,7 @@ async function readViewPrefsFile() {
9627
9651
  }
9628
9652
  let raw;
9629
9653
  try {
9630
- raw = await readFile12(path, "utf-8");
9654
+ raw = await readFile11(path, "utf-8");
9631
9655
  } catch {
9632
9656
  return { ...DEFAULT_VIEW_PREFS_FILE };
9633
9657
  }
@@ -9810,7 +9834,7 @@ function isDashboardSlot(value) {
9810
9834
  // src/utils/saved-views.ts
9811
9835
  init_paths();
9812
9836
  init_fs();
9813
- import { readFile as readFile13, rename as rename4, unlink as unlink4 } from "fs/promises";
9837
+ import { readFile as readFile12, rename as rename4, unlink as unlink4 } from "fs/promises";
9814
9838
  import { randomUUID as randomUUID2 } from "crypto";
9815
9839
  import { resolve as resolve18 } from "path";
9816
9840
  function corruptFilePath2() {
@@ -9840,7 +9864,7 @@ async function readSavedViewsFile() {
9840
9864
  }
9841
9865
  let raw;
9842
9866
  try {
9843
- raw = await readFile13(path, "utf-8");
9867
+ raw = await readFile12(path, "utf-8");
9844
9868
  } catch {
9845
9869
  return cloneDefault();
9846
9870
  }
@@ -10160,7 +10184,7 @@ init_fs();
10160
10184
  init_git_worktree();
10161
10185
  import { Router as Router2 } from "express";
10162
10186
  import { resolve as resolve21, basename as basename3, isAbsolute as isAbsolute2 } from "path";
10163
- import { rm, readFile as readFile16, open as fsOpen, stat as fsStat, realpath as fsRealpath } from "fs/promises";
10187
+ import { rm, readFile as readFile15, open as fsOpen, stat as fsStat, realpath as fsRealpath } from "fs/promises";
10164
10188
  import { spawnSync as spawnSync3 } from "child_process";
10165
10189
 
10166
10190
  // src/utils/worktree-defaults.ts
@@ -10207,14 +10231,14 @@ function detectCurrentBranch(cwd) {
10207
10231
  // src/dashboard/repository-candidates.ts
10208
10232
  init_fs();
10209
10233
  init_parser();
10210
- import { readdir as readdir10, readFile as readFile15 } from "fs/promises";
10234
+ import { readdir as readdir9, readFile as readFile14 } from "fs/promises";
10211
10235
  import { resolve as resolve20 } from "path";
10212
10236
  async function getProjectRepositoryCandidates(projectsDir2, projectSlug) {
10213
10237
  const seen = /* @__PURE__ */ new Set();
10214
10238
  const out = [];
10215
10239
  const projectPath = resolve20(projectsDir2, projectSlug, "project.md");
10216
10240
  if (await fileExists(projectPath)) {
10217
- const project = parseProject(await readFile15(projectPath, "utf-8"));
10241
+ const project = parseProject(await readFile14(projectPath, "utf-8"));
10218
10242
  for (const raw of project.repositories) {
10219
10243
  const path = raw.trim();
10220
10244
  if (!path) continue;
@@ -10226,12 +10250,12 @@ async function getProjectRepositoryCandidates(projectsDir2, projectSlug) {
10226
10250
  }
10227
10251
  const assignmentsDir2 = resolve20(projectsDir2, projectSlug, "assignments");
10228
10252
  if (await fileExists(assignmentsDir2)) {
10229
- const entries = await readdir10(assignmentsDir2, { withFileTypes: true });
10253
+ const entries = await readdir9(assignmentsDir2, { withFileTypes: true });
10230
10254
  for (const entry of entries) {
10231
10255
  if (!entry.isDirectory()) continue;
10232
10256
  const assignmentMd = resolve20(assignmentsDir2, entry.name, "assignment.md");
10233
10257
  if (!await fileExists(assignmentMd)) continue;
10234
- const parsed = parseAssignmentFull(await readFile15(assignmentMd, "utf-8"));
10258
+ const parsed = parseAssignmentFull(await readFile14(assignmentMd, "utf-8"));
10235
10259
  const repo = parsed.workspace.repository?.trim();
10236
10260
  if (!repo) continue;
10237
10261
  const abs = resolve20(repo);
@@ -10248,13 +10272,13 @@ async function getStandaloneRepositoryCandidates(assignmentsDir2, excludeAssignm
10248
10272
  }
10249
10273
  const seen = /* @__PURE__ */ new Set();
10250
10274
  const out = [];
10251
- const entries = await readdir10(assignmentsDir2, { withFileTypes: true });
10275
+ const entries = await readdir9(assignmentsDir2, { withFileTypes: true });
10252
10276
  for (const entry of entries) {
10253
10277
  if (!entry.isDirectory()) continue;
10254
10278
  if (entry.name === excludeAssignmentId) continue;
10255
10279
  const assignmentMd = resolve20(assignmentsDir2, entry.name, "assignment.md");
10256
10280
  if (!await fileExists(assignmentMd)) continue;
10257
- const parsed = parseAssignmentFull(await readFile15(assignmentMd, "utf-8"));
10281
+ const parsed = parseAssignmentFull(await readFile14(assignmentMd, "utf-8"));
10258
10282
  const repo = parsed.workspace.repository?.trim();
10259
10283
  if (!repo) continue;
10260
10284
  const abs = resolve20(repo);
@@ -10419,14 +10443,14 @@ async function readCurrentDocument(filePath) {
10419
10443
  if (!await fileExists(filePath)) {
10420
10444
  return null;
10421
10445
  }
10422
- return readFile16(filePath, "utf-8");
10446
+ return readFile15(filePath, "utf-8");
10423
10447
  }
10424
10448
  async function handleWorktreeCreate(req, res, ctx) {
10425
10449
  if (!await fileExists(ctx.assignmentPath)) {
10426
10450
  res.status(404).json({ error: "Assignment not found" });
10427
10451
  return;
10428
10452
  }
10429
- const parsed = parseAssignmentFull(await readFile16(ctx.assignmentPath, "utf-8"));
10453
+ const parsed = parseAssignmentFull(await readFile15(ctx.assignmentPath, "utf-8"));
10430
10454
  if (parsed.workspace.worktreePath) {
10431
10455
  res.status(409).json({ error: "Worktree already configured for this assignment" });
10432
10456
  return;
@@ -10518,6 +10542,7 @@ async function handleWorktreeCreate(req, res, ctx) {
10518
10542
  function createWriteRouter(projectsDir2, assignmentsDir2, todosDir2) {
10519
10543
  const linkedTodosLookup = todosDir2 ? { todosDir: todosDir2, projectsDir: projectsDir2 } : void 0;
10520
10544
  const router = Router2();
10545
+ installRecordsInvalidation(router);
10521
10546
  router.get("/api/templates/project", (_req, res) => {
10522
10547
  const content = renderProject({
10523
10548
  id: generateId(),
@@ -10795,7 +10820,7 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
10795
10820
  }
10796
10821
  const nextContentRaw = requireContent(req, res);
10797
10822
  if (!nextContentRaw) return;
10798
- const currentContent = await readFile16(filePath, "utf-8");
10823
+ const currentContent = await readFile15(filePath, "utf-8");
10799
10824
  const frontmatterBlock = extractFrontmatterBlock(currentContent);
10800
10825
  if (!frontmatterBlock) {
10801
10826
  res.status(500).json({ error: `${kind} file is malformed (no frontmatter)` });
@@ -11246,7 +11271,7 @@ ${nextBody}${nextBody.endsWith("\n") ? "" : "\n"}`;
11246
11271
  let currentContent;
11247
11272
  let currentCount = 0;
11248
11273
  if (await fileExists(commentsPath)) {
11249
- currentContent = await readFile16(commentsPath, "utf-8");
11274
+ currentContent = await readFile15(commentsPath, "utf-8");
11250
11275
  const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
11251
11276
  if (countMatch) currentCount = parseInt(countMatch[1], 10);
11252
11277
  } else {
@@ -11303,7 +11328,7 @@ ${entry}`;
11303
11328
  res.status(400).json({ error: "resolved (boolean) is required" });
11304
11329
  return;
11305
11330
  }
11306
- const content = await readFile16(commentsPath, "utf-8");
11331
+ const content = await readFile15(commentsPath, "utf-8");
11307
11332
  const parsed = parseComments(content);
11308
11333
  const target = parsed.entries.find((e) => e.id === commentId);
11309
11334
  if (!target) {
@@ -11350,7 +11375,7 @@ ${entry}`;
11350
11375
  });
11351
11376
  return;
11352
11377
  }
11353
- let content = await readFile16(projectPath, "utf-8");
11378
+ let content = await readFile15(projectPath, "utf-8");
11354
11379
  content = setTopLevelField(content, "workspace", workspace ?? null);
11355
11380
  content = setTopLevelField(content, "updated", nowTimestamp());
11356
11381
  await writeFileForce(projectPath, content);
@@ -11387,7 +11412,7 @@ ${entry}`;
11387
11412
  return;
11388
11413
  }
11389
11414
  const assignmentPath = resolve21(resolved.assignmentDir, "assignment.md");
11390
- let content = await readFile16(assignmentPath, "utf-8");
11415
+ let content = await readFile15(assignmentPath, "utf-8");
11391
11416
  content = setTopLevelField(content, "workspaceGroup", workspaceGroup ?? null);
11392
11417
  content = setTopLevelField(content, "updated", nowTimestamp());
11393
11418
  await writeFileForce(assignmentPath, content);
@@ -11482,7 +11507,7 @@ ${entry}`;
11482
11507
  return;
11483
11508
  }
11484
11509
  const assignmentPath = resolve21(resolved.assignmentDir, "assignment.md");
11485
- const parsedForSlug = parseAssignmentFull(await readFile16(assignmentPath, "utf-8"));
11510
+ const parsedForSlug = parseAssignmentFull(await readFile15(assignmentPath, "utf-8"));
11486
11511
  const assignmentSlugForBranch = parsedForSlug.slug || resolved.id;
11487
11512
  await handleWorktreeCreate(req, res, {
11488
11513
  assignmentPath,
@@ -11511,7 +11536,7 @@ ${entry}`;
11511
11536
  res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}, or null to clear.` });
11512
11537
  return;
11513
11538
  }
11514
- let content = await readFile16(projectPath, "utf-8");
11539
+ let content = await readFile15(projectPath, "utf-8");
11515
11540
  content = setTopLevelField(content, "statusOverride", status ?? null);
11516
11541
  content = setTopLevelField(content, "updated", nowTimestamp());
11517
11542
  await writeFileForce(projectPath, content);
@@ -11544,7 +11569,7 @@ ${entry}`;
11544
11569
  res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}.` });
11545
11570
  return;
11546
11571
  }
11547
- let content = await readFile16(assignmentPath, "utf-8");
11572
+ let content = await readFile15(assignmentPath, "utf-8");
11548
11573
  content = setTopLevelField(content, "status", status);
11549
11574
  content = setTopLevelField(content, "updated", nowTimestamp());
11550
11575
  if (status !== "blocked") {
@@ -11578,7 +11603,7 @@ ${entry}`;
11578
11603
  res.status(400).json({ error: validation.error });
11579
11604
  return;
11580
11605
  }
11581
- let content = await readFile16(assignmentPath, "utf-8");
11606
+ let content = await readFile15(assignmentPath, "utf-8");
11582
11607
  content = setTopLevelField(content, "assignee", validation.value);
11583
11608
  content = setTopLevelField(content, "updated", nowTimestamp());
11584
11609
  await writeFileForce(assignmentPath, content);
@@ -11609,7 +11634,7 @@ ${entry}`;
11609
11634
  res.status(400).json({ error: validation.error });
11610
11635
  return;
11611
11636
  }
11612
- let content = await readFile16(assignmentPath, "utf-8");
11637
+ let content = await readFile15(assignmentPath, "utf-8");
11613
11638
  content = setTopLevelField(content, "title", validation.value);
11614
11639
  content = setTopLevelField(content, "updated", nowTimestamp());
11615
11640
  await writeFileForce(assignmentPath, content);
@@ -12127,7 +12152,7 @@ ${entry}`;
12127
12152
  res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}.` });
12128
12153
  return;
12129
12154
  }
12130
- let content = await readFile16(assignmentPath, "utf-8");
12155
+ let content = await readFile15(assignmentPath, "utf-8");
12131
12156
  content = setTopLevelField(content, "status", status);
12132
12157
  content = setTopLevelField(content, "updated", nowTimestamp());
12133
12158
  if (status !== "blocked") {
@@ -12163,7 +12188,7 @@ ${entry}`;
12163
12188
  res.status(400).json({ error: validation.error });
12164
12189
  return;
12165
12190
  }
12166
- let content = await readFile16(assignmentPath, "utf-8");
12191
+ let content = await readFile15(assignmentPath, "utf-8");
12167
12192
  content = setTopLevelField(content, "assignee", validation.value);
12168
12193
  content = setTopLevelField(content, "updated", nowTimestamp());
12169
12194
  await writeFileForce(assignmentPath, content);
@@ -12196,7 +12221,7 @@ ${entry}`;
12196
12221
  res.status(400).json({ error: validation.error });
12197
12222
  return;
12198
12223
  }
12199
- let content = await readFile16(assignmentPath, "utf-8");
12224
+ let content = await readFile15(assignmentPath, "utf-8");
12200
12225
  content = setTopLevelField(content, "title", validation.value);
12201
12226
  content = setTopLevelField(content, "updated", nowTimestamp());
12202
12227
  await writeFileForce(assignmentPath, content);
@@ -12265,7 +12290,7 @@ ${entry}`;
12265
12290
  if (!await fileExists(assignmentPath)) {
12266
12291
  throw new Error("assignment file not found");
12267
12292
  }
12268
- let content = await readFile16(assignmentPath, "utf-8");
12293
+ let content = await readFile15(assignmentPath, "utf-8");
12269
12294
  content = setTopLevelField(content, "status", status);
12270
12295
  content = setTopLevelField(content, "updated", timestamp);
12271
12296
  if (status !== "blocked") {
@@ -12421,7 +12446,7 @@ async function appendCommentTo(assignmentDir, assignmentRef, req, res, reloadDet
12421
12446
  let currentContent;
12422
12447
  let currentCount = 0;
12423
12448
  if (await fileExists(commentsPath)) {
12424
- currentContent = await readFile16(commentsPath, "utf-8");
12449
+ currentContent = await readFile15(commentsPath, "utf-8");
12425
12450
  const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
12426
12451
  if (countMatch) currentCount = parseInt(countMatch[1], 10);
12427
12452
  } else {
@@ -12461,7 +12486,7 @@ async function toggleCommentResolvedAt(assignmentDir, commentId, req, res, reloa
12461
12486
  res.status(400).json({ error: "resolved (boolean) is required" });
12462
12487
  return;
12463
12488
  }
12464
- const content = await readFile16(commentsPath, "utf-8");
12489
+ const content = await readFile15(commentsPath, "utf-8");
12465
12490
  const parsed = parseComments(content);
12466
12491
  const target = parsed.entries.find((e) => e.id === commentId);
12467
12492
  if (!target) {
@@ -13189,26 +13214,26 @@ import { Router as Router8 } from "express";
13189
13214
 
13190
13215
  // src/utils/status-config-resolution.ts
13191
13216
  init_frontmatter();
13192
- import { readFile as readFile17, writeFile as writeFile4, rm as rm2 } from "fs/promises";
13217
+ import { readFile as readFile16, writeFile as writeFile4, rm as rm2 } from "fs/promises";
13193
13218
  import { dirname as dirname5 } from "path";
13194
13219
 
13195
13220
  // src/utils/assignment-walk.ts
13196
13221
  init_fs();
13197
13222
  import { resolve as resolve23 } from "path";
13198
- import { readdir as readdir11 } from "fs/promises";
13223
+ import { readdir as readdir10 } from "fs/promises";
13199
13224
  async function listAssignmentsByProject(projectsDir2, standaloneDir) {
13200
13225
  const result = {
13201
13226
  withAssignmentMd: [],
13202
13227
  orphanFolders: []
13203
13228
  };
13204
13229
  if (await fileExists(projectsDir2)) {
13205
- const projects = await readdir11(projectsDir2, { withFileTypes: true });
13230
+ const projects = await readdir10(projectsDir2, { withFileTypes: true });
13206
13231
  for (const m of projects) {
13207
13232
  if (!m.isDirectory()) continue;
13208
13233
  if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
13209
13234
  const assignmentsDir2 = resolve23(projectsDir2, m.name, "assignments");
13210
13235
  if (!await fileExists(assignmentsDir2)) continue;
13211
- const entries = await readdir11(assignmentsDir2, { withFileTypes: true });
13236
+ const entries = await readdir10(assignmentsDir2, { withFileTypes: true });
13212
13237
  for (const a of entries) {
13213
13238
  if (!a.isDirectory()) continue;
13214
13239
  if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
@@ -13230,7 +13255,7 @@ async function listAssignmentsByProject(projectsDir2, standaloneDir) {
13230
13255
  }
13231
13256
  }
13232
13257
  if (standaloneDir !== null && await fileExists(standaloneDir)) {
13233
- const entries = await readdir11(standaloneDir, { withFileTypes: true });
13258
+ const entries = await readdir10(standaloneDir, { withFileTypes: true });
13234
13259
  for (const a of entries) {
13235
13260
  if (!a.isDirectory()) continue;
13236
13261
  if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
@@ -13272,7 +13297,7 @@ async function scanAssignmentsByStatus(projectsDir2, standaloneDir, ids) {
13272
13297
  const assignmentPath = `${entry.assignmentDir}/assignment.md`;
13273
13298
  let content;
13274
13299
  try {
13275
- content = await readFile17(assignmentPath, "utf-8");
13300
+ content = await readFile16(assignmentPath, "utf-8");
13276
13301
  } catch (err2) {
13277
13302
  const code = err2?.code;
13278
13303
  if (code === "ENOENT") {
@@ -13335,7 +13360,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
13335
13360
  const list = affected.get(r.id) ?? [];
13336
13361
  for (const a of list) {
13337
13362
  try {
13338
- const content = await readFile17(a.path, "utf-8");
13363
+ const content = await readFile16(a.path, "utf-8");
13339
13364
  buffer.set(a.path, content);
13340
13365
  } catch (err2) {
13341
13366
  const code = err2?.code;
@@ -13363,7 +13388,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
13363
13388
  for (const a of list) {
13364
13389
  let current;
13365
13390
  try {
13366
- current = await readFile17(a.path, "utf-8");
13391
+ current = await readFile16(a.path, "utf-8");
13367
13392
  } catch (err2) {
13368
13393
  const code = err2?.code;
13369
13394
  if (code === "ENOENT") {
@@ -13412,7 +13437,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
13412
13437
  const list = affected.get(r.id) ?? [];
13413
13438
  for (const a of list) {
13414
13439
  try {
13415
- const current = await readFile17(a.path, "utf-8");
13440
+ const current = await readFile16(a.path, "utf-8");
13416
13441
  const fm = parseAssignmentFrontmatter(current);
13417
13442
  if (fm.status !== r.id) {
13418
13443
  console.warn(
@@ -13548,6 +13573,7 @@ function mapResolutionErrorToHttp(err2, applied) {
13548
13573
  }
13549
13574
  function createStatusConfigRouter(projectsDir2, assignmentsDir2) {
13550
13575
  const router = Router8();
13576
+ installRecordsInvalidation(router);
13551
13577
  router.get("/", async (_req, res) => {
13552
13578
  try {
13553
13579
  const config = await getStatusConfig();
@@ -14772,7 +14798,7 @@ init_playbook();
14772
14798
  init_playbooks();
14773
14799
  import { Router as Router11 } from "express";
14774
14800
  import { resolve as resolve26 } from "path";
14775
- import { readFile as readFile18 } from "fs/promises";
14801
+ import { readFile as readFile17 } from "fs/promises";
14776
14802
  function statusForPlaybookError(code) {
14777
14803
  switch (code) {
14778
14804
  case "manifest":
@@ -14854,7 +14880,7 @@ function createPlaybooksRouter(playbooksDir3) {
14854
14880
  return;
14855
14881
  }
14856
14882
  const filePath = resolve26(playbooksDir3, resolved.filename);
14857
- const content = await readFile18(filePath, "utf-8");
14883
+ const content = await readFile17(filePath, "utf-8");
14858
14884
  res.json({
14859
14885
  documentType: "playbook",
14860
14886
  title: `Edit Playbook: ${resolved.slug}`,
@@ -14951,11 +14977,12 @@ init_parser2();
14951
14977
  init_fs();
14952
14978
  init_paths();
14953
14979
  import { Router as Router12 } from "express";
14954
- import { readdir as readdir12 } from "fs/promises";
14980
+ import { readdir as readdir11 } from "fs/promises";
14955
14981
  import { resolve as resolvePath, dirname as dirname6 } from "path";
14956
14982
  import { rename as rename5, mkdir as mkdir2 } from "fs/promises";
14957
14983
  init_slug();
14958
14984
  init_promote_todos();
14985
+ init_api();
14959
14986
  var WORKSPACE_REGEX = /^[a-z0-9_][a-z0-9-]*$/;
14960
14987
  function getWorkspaceParam(value) {
14961
14988
  if (Array.isArray(value)) {
@@ -14970,6 +14997,7 @@ function touchItem3(item) {
14970
14997
  }
14971
14998
  function createTodosRouter(todosDir2, broadcast, projectsDir2) {
14972
14999
  const router = Router12();
15000
+ installRecordsInvalidation(router);
14973
15001
  function broadcastUpdate() {
14974
15002
  broadcast({ type: "todos-updated", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
14975
15003
  }
@@ -15089,7 +15117,7 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
15089
15117
  router.get("/", async (_req, res) => {
15090
15118
  try {
15091
15119
  await ensureDir(todosDir2);
15092
- const files = await readdir12(todosDir2).catch(() => []);
15120
+ const files = await readdir11(todosDir2).catch(() => []);
15093
15121
  const workspaces = [];
15094
15122
  for (const file of files) {
15095
15123
  if (typeof file !== "string") continue;
@@ -15204,7 +15232,7 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
15204
15232
  try {
15205
15233
  const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
15206
15234
  const { resolve: resolve78 } = await import("path");
15207
- const { readFile: readFile54 } = await import("fs/promises");
15235
+ const { readFile: readFile53 } = await import("fs/promises");
15208
15236
  const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
15209
15237
  const workspace = getWorkspaceParam(req.params.workspace);
15210
15238
  const checklist = await readChecklist(todosDir2, workspace);
@@ -15223,7 +15251,7 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
15223
15251
  await ensureDir(resolve78(todosDir2, "archive"));
15224
15252
  let archContent = "";
15225
15253
  if (await fileExists(archFile)) {
15226
- archContent = await readFile54(archFile, "utf-8");
15254
+ archContent = await readFile53(archFile, "utf-8");
15227
15255
  archContent = archContent.trimEnd() + "\n\n";
15228
15256
  } else {
15229
15257
  archContent = `---
@@ -15505,7 +15533,7 @@ workspace: ${workspace}
15505
15533
  const { readConfig: readConfig3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
15506
15534
  const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
15507
15535
  const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
15508
- const { readFile: readFile54 } = await import("fs/promises");
15536
+ const { readFile: readFile53 } = await import("fs/promises");
15509
15537
  const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
15510
15538
  const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
15511
15539
  let assignmentRef;
@@ -15526,7 +15554,7 @@ workspace: ${workspace}
15526
15554
  }
15527
15555
  const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
15528
15556
  if (!await fileExists2(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
15529
- let content = await readFile54(assignmentMdPath2, "utf-8");
15557
+ let content = await readFile53(assignmentMdPath2, "utf-8");
15530
15558
  content = appendTodosToAssignmentBody2(
15531
15559
  content,
15532
15560
  items.map((it) => ({
@@ -15714,9 +15742,10 @@ init_fs();
15714
15742
  init_paths();
15715
15743
  init_slug();
15716
15744
  import { Router as Router13 } from "express";
15717
- import { mkdir as mkdir3, readFile as readFile19, rename as rename6 } from "fs/promises";
15745
+ import { mkdir as mkdir3, readFile as readFile18, rename as rename6 } from "fs/promises";
15718
15746
  import { resolve as resolve27, dirname as dirname7 } from "path";
15719
15747
  init_promote_todos();
15748
+ init_api();
15720
15749
  var WORKSPACE_REGEX2 = /^[a-z0-9_][a-z0-9-]*$/;
15721
15750
  function touchItem4(item) {
15722
15751
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -15765,6 +15794,7 @@ function notFound(res, slug) {
15765
15794
  }
15766
15795
  function createProjectTodosRouter(projectsDir2, broadcast, workspaceTodosDir) {
15767
15796
  const router = Router13({ mergeParams: true });
15797
+ installRecordsInvalidation(router);
15768
15798
  function broadcastUpdate(projectSlug) {
15769
15799
  broadcast({ type: "todos-updated", projectSlug, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
15770
15800
  }
@@ -15937,7 +15967,7 @@ function createProjectTodosRouter(projectsDir2, broadcast, workspaceTodosDir) {
15937
15967
  const archFile = archivePath(todosDir2, slug, checklist.archiveInterval);
15938
15968
  let archContent = "";
15939
15969
  if (await fileExists(archFile)) {
15940
- archContent = await readFile19(archFile, "utf-8");
15970
+ archContent = await readFile18(archFile, "utf-8");
15941
15971
  archContent = archContent.trimEnd() + "\n\n";
15942
15972
  } else {
15943
15973
  archContent = `---
@@ -16394,7 +16424,7 @@ workspace: ${slug}
16394
16424
  }
16395
16425
  const assignmentMdPath2 = resolve27(assignmentDir, "assignment.md");
16396
16426
  if (!await fileExists(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
16397
- let content = await readFile19(assignmentMdPath2, "utf-8");
16427
+ let content = await readFile18(assignmentMdPath2, "utf-8");
16398
16428
  content = appendTodosToAssignmentBody2(
16399
16429
  content,
16400
16430
  items.map((it) => ({
@@ -16590,7 +16620,7 @@ workspace: ${slug}
16590
16620
 
16591
16621
  // src/dashboard/api-bundles.ts
16592
16622
  import { Router as Router14 } from "express";
16593
- import { readdir as readdir13 } from "fs/promises";
16623
+ import { readdir as readdir12 } from "fs/promises";
16594
16624
 
16595
16625
  // src/todos/bundle-parser.ts
16596
16626
  init_parser();
@@ -16598,7 +16628,7 @@ init_fs();
16598
16628
  init_paths();
16599
16629
  init_parser2();
16600
16630
  import { randomBytes as randomBytes2 } from "crypto";
16601
- import { readFile as readFile20 } from "fs/promises";
16631
+ import { readFile as readFile19 } from "fs/promises";
16602
16632
  var BUNDLE_ID_REGEX = /^[a-f0-9]{4}$/;
16603
16633
  var SCOPE_VALUES = /* @__PURE__ */ new Set(["workspace", "project", "global"]);
16604
16634
  var SCOPE_ID_REGEX = /^[a-z0-9_][a-z0-9_-]*$/;
@@ -16701,7 +16731,7 @@ ${lines}
16701
16731
  async function readBundles(todosDir2) {
16702
16732
  const path = bundlesPath(todosDir2);
16703
16733
  if (!await fileExists(path)) return [];
16704
- const content = await readFile20(path, "utf-8");
16734
+ const content = await readFile19(path, "utf-8");
16705
16735
  return parseBundles(content).bundles;
16706
16736
  }
16707
16737
  async function writeBundles(todosDir2, bundles) {
@@ -16754,7 +16784,7 @@ function createBundlesRouter(todosDir2, broadcast) {
16754
16784
  try {
16755
16785
  await ensureDir(todosDir2);
16756
16786
  const bundles = await readBundles(todosDir2);
16757
- const workspaceFiles = await readdir13(todosDir2).catch(() => []);
16787
+ const workspaceFiles = await readdir12(todosDir2).catch(() => []);
16758
16788
  const itemsByKey = /* @__PURE__ */ new Map();
16759
16789
  for (const f of workspaceFiles) {
16760
16790
  if (typeof f !== "string") continue;
@@ -16868,6 +16898,8 @@ function createProjectBundlesRouter(projectsDir2, broadcast) {
16868
16898
 
16869
16899
  // src/dashboard/api-backup.ts
16870
16900
  init_config2();
16901
+ init_api();
16902
+ init_scanner();
16871
16903
  import { Router as Router16 } from "express";
16872
16904
 
16873
16905
  // src/utils/github-backup.ts
@@ -16876,7 +16908,7 @@ init_fs();
16876
16908
  init_config2();
16877
16909
  import { execFile as execFile2 } from "child_process";
16878
16910
  import { promisify as promisify2 } from "util";
16879
- import { cp, mkdtemp, rm as rm3, readFile as readFile21, writeFile as writeFile5, unlink as unlink5, stat, open as open2, rename as rename7 } from "fs/promises";
16911
+ import { cp, mkdtemp, rm as rm3, readFile as readFile20, writeFile as writeFile5, unlink as unlink5, stat, open as open2, rename as rename7 } from "fs/promises";
16880
16912
  import { resolve as resolve29, join as join2 } from "path";
16881
16913
  import { tmpdir } from "os";
16882
16914
  var exec2 = promisify2(execFile2);
@@ -16937,7 +16969,7 @@ async function acquireLock() {
16937
16969
  return lockPath;
16938
16970
  } catch (err2) {
16939
16971
  if (err2.code === "EEXIST") {
16940
- const pid = await readFile21(lockPath, "utf-8").catch(() => "");
16972
+ const pid = await readFile20(lockPath, "utf-8").catch(() => "");
16941
16973
  throw new Error(
16942
16974
  `Backup operation already in progress (lock file at ${lockPath}, pid ${pid.trim() || "unknown"}). If stale, delete the file and retry.`
16943
16975
  );
@@ -16984,7 +17016,7 @@ function resolveCategoriesStrict(csv) {
16984
17016
  return parseCategoriesStrict(parts);
16985
17017
  }
16986
17018
  async function readSanitizedConfig(configPath) {
16987
- const content = await readFile21(configPath, "utf-8");
17019
+ const content = await readFile20(configPath, "utf-8");
16988
17020
  return content.replace(/^(\s*lastBackup:\s*).*$/m, "$1null").replace(/^(\s*lastRestore:\s*).*$/m, "$1null");
16989
17021
  }
16990
17022
  async function backupToGithub(overrides) {
@@ -17266,6 +17298,9 @@ function createBackupRouter() {
17266
17298
  success: false,
17267
17299
  error: error instanceof Error ? error.message : String(error)
17268
17300
  });
17301
+ } finally {
17302
+ invalidateRecordsCache();
17303
+ clearScanCache();
17269
17304
  }
17270
17305
  });
17271
17306
  return router;
@@ -17504,6 +17539,9 @@ function createDashboardServer(options) {
17504
17539
  });
17505
17540
  });
17506
17541
  function broadcast(message) {
17542
+ if (message.type === "project-updated" || message.type === "assignment-updated") {
17543
+ invalidateRecordsCache();
17544
+ }
17507
17545
  const data = JSON.stringify(message);
17508
17546
  for (const client of clients) {
17509
17547
  if (client.readyState === WebSocket.OPEN) {
@@ -18372,7 +18410,7 @@ init_config2();
18372
18410
  init_frontmatter();
18373
18411
  init_timestamp();
18374
18412
  import { resolve as resolve33 } from "path";
18375
- import { readdir as readdir14, readFile as readFile22 } from "fs/promises";
18413
+ import { readdir as readdir13, readFile as readFile21 } from "fs/promises";
18376
18414
  var PROMOTABLE_STATUSES = /* @__PURE__ */ new Set(["pending"]);
18377
18415
  function objectiveIsFleshedOut(content) {
18378
18416
  const match = content.match(/##\s+Objective\s*\n([\s\S]*?)(?=\n##\s+|$)/);
@@ -18390,7 +18428,7 @@ async function collectCandidates(baseDirs) {
18390
18428
  const candidates = [];
18391
18429
  for (const baseDir of baseDirs) {
18392
18430
  if (!await fileExists(baseDir)) continue;
18393
- const projects = await readdir14(baseDir, { withFileTypes: true });
18431
+ const projects = await readdir13(baseDir, { withFileTypes: true });
18394
18432
  for (const m of projects) {
18395
18433
  if (!m.isDirectory()) continue;
18396
18434
  if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
@@ -18398,7 +18436,7 @@ async function collectCandidates(baseDirs) {
18398
18436
  if (await fileExists(directAssignmentMd)) {
18399
18437
  const fm = await parseSafe(directAssignmentMd);
18400
18438
  if (fm && PROMOTABLE_STATUSES.has(fm.status)) {
18401
- const content = await readFile22(directAssignmentMd, "utf-8");
18439
+ const content = await readFile21(directAssignmentMd, "utf-8");
18402
18440
  if (objectiveIsFleshedOut(content) && hasAcceptanceCriteria(content)) {
18403
18441
  candidates.push({
18404
18442
  projectSlug: null,
@@ -18413,7 +18451,7 @@ async function collectCandidates(baseDirs) {
18413
18451
  }
18414
18452
  const assignmentsDir2 = resolve33(baseDir, m.name, "assignments");
18415
18453
  if (!await fileExists(assignmentsDir2)) continue;
18416
- const entries = await readdir14(assignmentsDir2, { withFileTypes: true });
18454
+ const entries = await readdir13(assignmentsDir2, { withFileTypes: true });
18417
18455
  for (const a of entries) {
18418
18456
  if (!a.isDirectory()) continue;
18419
18457
  if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
@@ -18421,7 +18459,7 @@ async function collectCandidates(baseDirs) {
18421
18459
  if (!await fileExists(assignmentMd)) continue;
18422
18460
  const fm = await parseSafe(assignmentMd);
18423
18461
  if (!fm || !PROMOTABLE_STATUSES.has(fm.status)) continue;
18424
- const content = await readFile22(assignmentMd, "utf-8");
18462
+ const content = await readFile21(assignmentMd, "utf-8");
18425
18463
  if (!objectiveIsFleshedOut(content)) continue;
18426
18464
  if (!hasAcceptanceCriteria(content)) continue;
18427
18465
  candidates.push({
@@ -18438,7 +18476,7 @@ async function collectCandidates(baseDirs) {
18438
18476
  }
18439
18477
  async function parseSafe(path) {
18440
18478
  try {
18441
- const content = await readFile22(path, "utf-8");
18479
+ const content = await readFile21(path, "utf-8");
18442
18480
  return parseAssignmentFrontmatter(content);
18443
18481
  } catch {
18444
18482
  return null;
@@ -18467,7 +18505,7 @@ async function migrateStatusesCommand(options) {
18467
18505
  const now = nowTimestamp();
18468
18506
  let migrated = 0;
18469
18507
  for (const c2 of candidates) {
18470
- const content = await readFile22(c2.assignmentMd, "utf-8");
18508
+ const content = await readFile21(c2.assignmentMd, "utf-8");
18471
18509
  const updated = updateAssignmentFile(content, {
18472
18510
  status: c2.toStatus,
18473
18511
  updated: now
@@ -18522,10 +18560,10 @@ init_config2();
18522
18560
  init_fs();
18523
18561
  import {
18524
18562
  cp as cp2,
18525
- readdir as readdir15,
18563
+ readdir as readdir14,
18526
18564
  symlink,
18527
18565
  lstat,
18528
- readFile as readFile23,
18566
+ readFile as readFile22,
18529
18567
  readlink,
18530
18568
  rename as rename8,
18531
18569
  rm as rm4,
@@ -18586,7 +18624,7 @@ function getInstallMarkerPath(targetDir) {
18586
18624
  return resolve35(targetDir, INSTALL_MARKER_FILENAME);
18587
18625
  }
18588
18626
  async function readPackageManifest(packageRoot) {
18589
- const raw = await readFile23(resolve35(packageRoot, "package.json"), "utf-8");
18627
+ const raw = await readFile22(resolve35(packageRoot, "package.json"), "utf-8");
18590
18628
  return JSON.parse(raw);
18591
18629
  }
18592
18630
  async function readJsonFileIfExists(pathValue) {
@@ -18594,7 +18632,7 @@ async function readJsonFileIfExists(pathValue) {
18594
18632
  return null;
18595
18633
  }
18596
18634
  try {
18597
- const raw = await readFile23(pathValue, "utf-8");
18635
+ const raw = await readFile22(pathValue, "utf-8");
18598
18636
  return JSON.parse(raw);
18599
18637
  } catch {
18600
18638
  return null;
@@ -18610,7 +18648,7 @@ async function readPluginManifestName(targetDir, pluginKind) {
18610
18648
  if (!await fileExists(manifestPath)) {
18611
18649
  return void 0;
18612
18650
  }
18613
- const raw = await readFile23(manifestPath, "utf-8");
18651
+ const raw = await readFile22(manifestPath, "utf-8");
18614
18652
  const parsed = JSON.parse(raw);
18615
18653
  return parsed.name;
18616
18654
  }
@@ -18620,7 +18658,7 @@ async function readInstallMetadata(targetDir) {
18620
18658
  return null;
18621
18659
  }
18622
18660
  try {
18623
- const raw = await readFile23(markerPath, "utf-8");
18661
+ const raw = await readFile22(markerPath, "utf-8");
18624
18662
  return JSON.parse(raw);
18625
18663
  } catch {
18626
18664
  return null;
@@ -18743,7 +18781,7 @@ async function writeClaudeMarketplaceFile(manifestPath, marketplace) {
18743
18781
  }
18744
18782
  if (await fileExists(manifestPath)) {
18745
18783
  try {
18746
- const prev = await readFile23(manifestPath, "utf-8");
18784
+ const prev = await readFile22(manifestPath, "utf-8");
18747
18785
  JSON.parse(prev);
18748
18786
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
18749
18787
  await writeFile7(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
@@ -18816,7 +18854,7 @@ async function listClaudeMarketplaceCandidates() {
18816
18854
  const [knownMarketplaces, activeMarketplaceNames, entries] = await Promise.all([
18817
18855
  readKnownClaudeMarketplaceRecords(),
18818
18856
  readInstalledClaudeMarketplaceNames(),
18819
- readdir15(rootDir, { withFileTypes: true })
18857
+ readdir14(rootDir, { withFileTypes: true })
18820
18858
  ]);
18821
18859
  const candidates = [];
18822
18860
  const seen = /* @__PURE__ */ new Set();
@@ -18891,7 +18929,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
18891
18929
  const manifestPath = getClaudeKnownMarketplacesPath();
18892
18930
  let existing = {};
18893
18931
  if (await fileExists(manifestPath)) {
18894
- const raw = await readFile23(manifestPath, "utf-8");
18932
+ const raw = await readFile22(manifestPath, "utf-8");
18895
18933
  try {
18896
18934
  const parsed = JSON.parse(raw);
18897
18935
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
@@ -18918,7 +18956,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
18918
18956
  existing[name].autoUpdate = true;
18919
18957
  await ensureDir(dirname10(manifestPath));
18920
18958
  if (await fileExists(manifestPath)) {
18921
- const prev = await readFile23(manifestPath, "utf-8");
18959
+ const prev = await readFile22(manifestPath, "utf-8");
18922
18960
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
18923
18961
  await writeFile7(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
18924
18962
  }
@@ -18936,7 +18974,7 @@ async function setSyntaurPluginEnabled(options) {
18936
18974
  const key = `syntaur@${options.marketplaceName}`;
18937
18975
  let parsed = {};
18938
18976
  if (await fileExists(settingsPath)) {
18939
- const raw = await readFile23(settingsPath, "utf-8");
18977
+ const raw = await readFile22(settingsPath, "utf-8");
18940
18978
  try {
18941
18979
  parsed = JSON.parse(raw);
18942
18980
  } catch {
@@ -18955,7 +18993,7 @@ async function setSyntaurPluginEnabled(options) {
18955
18993
  await ensureDir(dirname10(settingsPath));
18956
18994
  if (await fileExists(settingsPath)) {
18957
18995
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
18958
- const prev = await readFile23(settingsPath, "utf-8");
18996
+ const prev = await readFile22(settingsPath, "utf-8");
18959
18997
  await writeFile7(`${settingsPath}.bak-${stamp}`, prev, "utf-8");
18960
18998
  }
18961
18999
  const tmpPath = `${settingsPath}.tmp`;
@@ -19193,7 +19231,7 @@ async function readMarketplaceFile(marketplacePath) {
19193
19231
  plugins: []
19194
19232
  };
19195
19233
  }
19196
- const raw = await readFile23(marketplacePath, "utf-8");
19234
+ const raw = await readFile22(marketplacePath, "utf-8");
19197
19235
  const parsed = JSON.parse(raw);
19198
19236
  return {
19199
19237
  name: parsed.name ?? "local",
@@ -19429,13 +19467,13 @@ async function textPrompt(question, defaultValue) {
19429
19467
 
19430
19468
  // src/utils/install-skills.ts
19431
19469
  init_fs();
19432
- import { readFile as readFile25, readdir as readdir16, mkdir as mkdir4, copyFile, rm as rm5, lstat as lstat2 } from "fs/promises";
19470
+ import { readFile as readFile24, readdir as readdir15, mkdir as mkdir4, copyFile, rm as rm5, lstat as lstat2 } from "fs/promises";
19433
19471
  import { resolve as resolve37, relative as relative3, join as join3 } from "path";
19434
19472
  import { homedir as homedir4 } from "os";
19435
19473
 
19436
19474
  // src/utils/plugin-state.ts
19437
19475
  init_fs();
19438
- import { readFile as readFile24 } from "fs/promises";
19476
+ import { readFile as readFile23 } from "fs/promises";
19439
19477
  import { resolve as resolve36 } from "path";
19440
19478
  import { homedir as homedir3 } from "os";
19441
19479
  function settingsPathFor(agent) {
@@ -19446,7 +19484,7 @@ async function readJsonOrNull(path) {
19446
19484
  if (!path) return null;
19447
19485
  if (!await fileExists(path)) return null;
19448
19486
  try {
19449
- const raw = await readFile24(path, "utf-8");
19487
+ const raw = await readFile23(path, "utf-8");
19450
19488
  return JSON.parse(raw);
19451
19489
  } catch {
19452
19490
  return null;
@@ -19489,7 +19527,8 @@ var KNOWN_SKILL_NAMES = [
19489
19527
  "claim-resource",
19490
19528
  "release-resource",
19491
19529
  "extend-resource",
19492
- "list-resources"
19530
+ "list-resources",
19531
+ "run-playbook"
19493
19532
  ];
19494
19533
  var KNOWN_SKILLS = KNOWN_SKILL_NAMES;
19495
19534
  async function getSkillsDir() {
@@ -19503,7 +19542,7 @@ function defaultSkillTargetDir(target) {
19503
19542
  async function walkFiles(root) {
19504
19543
  const out = [];
19505
19544
  async function walk(dir) {
19506
- const entries = await readdir16(dir, { withFileTypes: true });
19545
+ const entries = await readdir15(dir, { withFileTypes: true });
19507
19546
  for (const entry of entries) {
19508
19547
  const full = join3(dir, entry.name);
19509
19548
  if (entry.isDirectory()) {
@@ -19518,7 +19557,7 @@ async function walkFiles(root) {
19518
19557
  }
19519
19558
  async function filesEqual(a, b) {
19520
19559
  try {
19521
- const [ba, bb] = await Promise.all([readFile25(a), readFile25(b)]);
19560
+ const [ba, bb] = await Promise.all([readFile24(a), readFile24(b)]);
19522
19561
  if (ba.length !== bb.length) return false;
19523
19562
  return ba.equals(bb);
19524
19563
  } catch {
@@ -19527,7 +19566,7 @@ async function filesEqual(a, b) {
19527
19566
  }
19528
19567
  async function copyDir(srcDir, destDir) {
19529
19568
  await mkdir4(destDir, { recursive: true });
19530
- const entries = await readdir16(srcDir, { withFileTypes: true });
19569
+ const entries = await readdir15(srcDir, { withFileTypes: true });
19531
19570
  for (const entry of entries) {
19532
19571
  const src = join3(srcDir, entry.name);
19533
19572
  const dest = join3(destDir, entry.name);
@@ -19581,7 +19620,7 @@ async function installSkillDir(srcDir, destDir, skillName, force) {
19581
19620
  return { skill: skillName, status: "differs-preserved", targetPath: destDir };
19582
19621
  }
19583
19622
  async function discoverSkillNames(sourceDir) {
19584
- const entries = await readdir16(sourceDir, { withFileTypes: true });
19623
+ const entries = await readdir15(sourceDir, { withFileTypes: true });
19585
19624
  const names = [];
19586
19625
  for (const entry of entries) {
19587
19626
  if (!entry.isDirectory()) continue;
@@ -19640,7 +19679,7 @@ async function uninstallSkills(options) {
19640
19679
  if (await isSymlink(destDir)) continue;
19641
19680
  const skillMd = join3(destDir, "SKILL.md");
19642
19681
  if (!await fileExists(skillMd)) continue;
19643
- const content = await readFile25(skillMd, "utf-8").catch(() => "");
19682
+ const content = await readFile24(skillMd, "utf-8").catch(() => "");
19644
19683
  const match = content.match(/^name:\s*(\S+)\s*$/m);
19645
19684
  if (!match || match[1] !== skill) continue;
19646
19685
  await rm5(destDir, { recursive: true, force: true });
@@ -19829,7 +19868,7 @@ async function installPluginCommand(options) {
19829
19868
  // src/commands/install-statusline.ts
19830
19869
  init_paths();
19831
19870
  init_fs();
19832
- import { readFile as readFile27, writeFile as writeFile9, copyFile as copyFile2, rm as rm6, stat as stat2, symlink as symlink2, unlink as unlink8, lstat as lstat3 } from "fs/promises";
19871
+ import { readFile as readFile26, writeFile as writeFile9, copyFile as copyFile2, rm as rm6, stat as stat2, symlink as symlink2, unlink as unlink8, lstat as lstat3 } from "fs/promises";
19833
19872
  import { resolve as resolve39, dirname as dirname12 } from "path";
19834
19873
  import { homedir as homedir5 } from "os";
19835
19874
  import { fileURLToPath as fileURLToPath4 } from "url";
@@ -19837,7 +19876,7 @@ import { fileURLToPath as fileURLToPath4 } from "url";
19837
19876
  // src/commands/configure-statusline.ts
19838
19877
  init_paths();
19839
19878
  init_fs();
19840
- import { readFile as readFile26, writeFile as writeFile8 } from "fs/promises";
19879
+ import { readFile as readFile25, writeFile as writeFile8 } from "fs/promises";
19841
19880
  import { resolve as resolve38, dirname as dirname11 } from "path";
19842
19881
  import { spawnSync as spawnSync5 } from "child_process";
19843
19882
  import { checkbox, input as input2, confirm } from "@inquirer/prompts";
@@ -19864,7 +19903,7 @@ function getConfigPath(installRoot) {
19864
19903
  async function readConfig2(path) {
19865
19904
  if (!await fileExists(path)) return null;
19866
19905
  try {
19867
- const raw = await readFile26(path, "utf-8");
19906
+ const raw = await readFile25(path, "utf-8");
19868
19907
  const parsed = JSON.parse(raw);
19869
19908
  if (!parsed || typeof parsed !== "object") return null;
19870
19909
  const segments = Array.isArray(parsed.segments) ? parsed.segments.filter(isSegmentName) : [];
@@ -20052,7 +20091,7 @@ function getPackageStatuslineSource() {
20052
20091
  }
20053
20092
  async function readSettingsJson(settingsPath) {
20054
20093
  if (!await fileExists(settingsPath)) return {};
20055
- const raw = await readFile27(settingsPath, "utf-8");
20094
+ const raw = await readFile26(settingsPath, "utf-8");
20056
20095
  if (raw.trim() === "") return {};
20057
20096
  try {
20058
20097
  const parsed = JSON.parse(raw);
@@ -20240,7 +20279,7 @@ async function uninstallStatuslineCommand(options = {}) {
20240
20279
  let restored = null;
20241
20280
  if (await fileExists(backupPath)) {
20242
20281
  try {
20243
- const raw = await readFile27(backupPath, "utf-8");
20282
+ const raw = await readFile26(backupPath, "utf-8");
20244
20283
  const parsed = JSON.parse(raw);
20245
20284
  const prev = parsed?.previousStatusLine;
20246
20285
  if (prev && typeof prev === "object" && typeof prev.command === "string") {
@@ -21273,7 +21312,7 @@ function normalizeSlashes(p) {
21273
21312
  }
21274
21313
  function detectInstallKind(scriptUrl, opts = {}) {
21275
21314
  const realpath3 = opts.realpath ?? realpathSync.native;
21276
- const readFile54 = opts.readFile ?? ((p) => readFileSync(p, "utf-8"));
21315
+ const readFile53 = opts.readFile ?? ((p) => readFileSync(p, "utf-8"));
21277
21316
  const ua = opts.envUserAgent !== void 0 ? opts.envUserAgent : process.env.npm_config_user_agent ?? "";
21278
21317
  const resolved = resolveScriptPath(scriptUrl, realpath3);
21279
21318
  if (resolved === null) {
@@ -21294,7 +21333,7 @@ function detectInstallKind(scriptUrl, opts = {}) {
21294
21333
  const pkgJsonPath = join4(dir, "package.json");
21295
21334
  let raw;
21296
21335
  try {
21297
- raw = readFile54(pkgJsonPath);
21336
+ raw = readFile53(pkgJsonPath);
21298
21337
  } catch {
21299
21338
  const parent2 = dirname13(dir);
21300
21339
  if (parent2 === dir) break;
@@ -21468,7 +21507,7 @@ init_config2();
21468
21507
  init_paths();
21469
21508
  init_fs();
21470
21509
  import { resolve as resolve46, isAbsolute as isAbsolute7 } from "path";
21471
- import { readFile as readFile28 } from "fs/promises";
21510
+ import { readFile as readFile27 } from "fs/promises";
21472
21511
  import { select, confirm as confirm2, input as input3 } from "@inquirer/prompts";
21473
21512
  async function browseCommand(options) {
21474
21513
  const config = await readConfig();
@@ -21547,7 +21586,7 @@ async function ensureWorktree(opts) {
21547
21586
  if (!await fileExists(assignmentPath)) {
21548
21587
  return void 0;
21549
21588
  }
21550
- const content = await readFile28(assignmentPath, "utf-8");
21589
+ const content = await readFile27(assignmentPath, "utf-8");
21551
21590
  const { parseAssignmentFrontmatter: parseAssignmentFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
21552
21591
  const fm = parseAssignmentFrontmatter2(content);
21553
21592
  const { workspace } = fm;
@@ -21681,7 +21720,7 @@ init_paths();
21681
21720
  init_fs();
21682
21721
  init_parser();
21683
21722
  init_config2();
21684
- import { readdir as readdir17, readFile as readFile29 } from "fs/promises";
21723
+ import { readdir as readdir16, readFile as readFile28 } from "fs/promises";
21685
21724
  import { resolve as resolve48 } from "path";
21686
21725
  async function listPlaybooksCommand(options = {}) {
21687
21726
  const dir = playbooksDir();
@@ -21691,14 +21730,14 @@ async function listPlaybooksCommand(options = {}) {
21691
21730
  }
21692
21731
  const config = await readConfig();
21693
21732
  const disabledSet = new Set(config.playbooks.disabled);
21694
- const entries = await readdir17(dir, { withFileTypes: true });
21733
+ const entries = await readdir16(dir, { withFileTypes: true });
21695
21734
  const mdFiles = entries.filter(
21696
21735
  (e) => e.isFile() && e.name.endsWith(".md") && !e.name.startsWith("_") && e.name !== "manifest.md"
21697
21736
  );
21698
21737
  const rows = [];
21699
21738
  for (const entry of mdFiles) {
21700
21739
  const filePath = resolve48(dir, entry.name);
21701
- const raw = await readFile29(filePath, "utf-8");
21740
+ const raw = await readFile28(filePath, "utf-8");
21702
21741
  const parsed = parsePlaybook(raw);
21703
21742
  const slug = parsed.slug || entry.name.replace(/\.md$/, "");
21704
21743
  const disabled = disabledSet.has(slug);
@@ -21821,13 +21860,13 @@ init_fs();
21821
21860
  init_config2();
21822
21861
  init_slug();
21823
21862
  import { Command as Command2 } from "commander";
21824
- import { readFile as readFile31 } from "fs/promises";
21863
+ import { readFile as readFile30 } from "fs/promises";
21825
21864
  import { resolve as resolve50 } from "path";
21826
21865
 
21827
21866
  // src/commands/bundle.ts
21828
21867
  init_paths();
21829
21868
  import { Command } from "commander";
21830
- import { mkdir as mkdir7, readFile as readFile30, readdir as readdir18, rm as rm7, writeFile as writeFile11 } from "fs/promises";
21869
+ import { mkdir as mkdir7, readFile as readFile29, readdir as readdir17, rm as rm7, writeFile as writeFile11 } from "fs/promises";
21831
21870
  import { resolve as resolve49 } from "path";
21832
21871
  init_parser2();
21833
21872
  init_fs();
@@ -22120,7 +22159,7 @@ bundleCommand.command("plan").description("Create or open the bundle's shared pl
22120
22159
  }
22121
22160
  const planDir = bundlePlanDir(sc.todosPath, sc.scopeId, id);
22122
22161
  await ensureDir(planDir);
22123
- const existing = (await readdir18(planDir).catch(() => [])).filter((f) => /^plan(?:-v\d+)?\.md$/.test(f));
22162
+ const existing = (await readdir17(planDir).catch(() => [])).filter((f) => /^plan(?:-v\d+)?\.md$/.test(f));
22124
22163
  const { target } = pickNextPlanFile(planDir, existing);
22125
22164
  const checklist = await readChecklist(sc.todosPath, sc.checklistKey);
22126
22165
  if (!await fileExists(target)) {
@@ -22189,8 +22228,8 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
22189
22228
  }
22190
22229
  const bundlesFilePath = bundlesPath(sc.todosPath);
22191
22230
  const checklistFilePath = checklistPath(sc.todosPath, sc.checklistKey);
22192
- const bundlesSnapshot = await fileExists(bundlesFilePath) ? await readFile30(bundlesFilePath, "utf-8") : null;
22193
- const checklistSnapshot = await fileExists(checklistFilePath) ? await readFile30(checklistFilePath, "utf-8") : null;
22231
+ const bundlesSnapshot = await fileExists(bundlesFilePath) ? await readFile29(bundlesFilePath, "utf-8") : null;
22232
+ const checklistSnapshot = await fileExists(checklistFilePath) ? await readFile29(checklistFilePath, "utf-8") : null;
22194
22233
  const record = async () => {
22195
22234
  bundle.branch = options.branch;
22196
22235
  bundle.worktreePath = worktreePath;
@@ -22736,7 +22775,7 @@ todoCommand.command("archive").description("Archive completed todos and their lo
22736
22775
  await ensureDir(resolve50(todosPath, "archive"));
22737
22776
  let archContent = "";
22738
22777
  if (await fileExists(archFile)) {
22739
- archContent = await readFile31(archFile, "utf-8");
22778
+ archContent = await readFile30(archFile, "utf-8");
22740
22779
  archContent = archContent.trimEnd() + "\n\n";
22741
22780
  } else {
22742
22781
  archContent = `---
@@ -22913,12 +22952,12 @@ function describeScope(scope) {
22913
22952
  }
22914
22953
  async function injectPromotedTodos(assignmentDir, todos, scopeLabel) {
22915
22954
  const { resolve: resolvePath2 } = await import("path");
22916
- const { readFile: readFile54 } = await import("fs/promises");
22955
+ const { readFile: readFile53 } = await import("fs/promises");
22917
22956
  const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
22918
22957
  const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
22919
22958
  const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
22920
22959
  const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
22921
- let content = await readFile54(assignmentMdPath2, "utf-8");
22960
+ let content = await readFile53(assignmentMdPath2, "utf-8");
22922
22961
  content = appendTodosToAssignmentBody2(
22923
22962
  content,
22924
22963
  todos.map((t) => ({
@@ -22963,8 +23002,8 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
22963
23002
  }
22964
23003
  const planDir = todoPlanDir(todosPath, workspace, id);
22965
23004
  await ensureDir(planDir);
22966
- const { readdir: readdir30 } = await import("fs/promises");
22967
- const existingFiles = (await readdir30(planDir).catch(() => [])).filter(
23005
+ const { readdir: readdir29 } = await import("fs/promises");
23006
+ const existingFiles = (await readdir29(planDir).catch(() => [])).filter(
22968
23007
  (f) => /^plan(?:-v\d+)?\.md$/.test(f)
22969
23008
  );
22970
23009
  let target;
@@ -23168,12 +23207,12 @@ backupCommand.command("config").description("Show or update backup configuration
23168
23207
 
23169
23208
  // src/commands/doctor.ts
23170
23209
  import { Command as Command4 } from "commander";
23171
- import { readFile as readFile39 } from "fs/promises";
23210
+ import { readFile as readFile38 } from "fs/promises";
23172
23211
  import { isAbsolute as isAbsolute10, resolve as resolve62 } from "path";
23173
23212
 
23174
23213
  // src/utils/doctor/index.ts
23175
23214
  import { fileURLToPath as fileURLToPath8 } from "url";
23176
- import { readFile as readFile38 } from "fs/promises";
23215
+ import { readFile as readFile37 } from "fs/promises";
23177
23216
  import { dirname as dirname17, join as join8 } from "path";
23178
23217
 
23179
23218
  // src/utils/doctor/context.ts
@@ -23220,7 +23259,7 @@ function closeCheckContext(ctx) {
23220
23259
  init_fs();
23221
23260
  init_paths();
23222
23261
  import { resolve as resolve52, isAbsolute as isAbsolute8 } from "path";
23223
- import { readFile as readFile32, stat as stat3 } from "fs/promises";
23262
+ import { readFile as readFile31, stat as stat3 } from "fs/promises";
23224
23263
  import { fileURLToPath as fileURLToPath7 } from "url";
23225
23264
  import { dirname as dirname15, join as join6 } from "path";
23226
23265
  var CATEGORY = "env";
@@ -23277,7 +23316,7 @@ var configValid = {
23277
23316
  autoFixable: false
23278
23317
  };
23279
23318
  }
23280
- const content = await readFile32(configPath, "utf-8");
23319
+ const content = await readFile31(configPath, "utf-8");
23281
23320
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
23282
23321
  if (!fmMatch || fmMatch[1].trim() === "") {
23283
23322
  return {
@@ -23557,7 +23596,7 @@ async function readLocalPkg() {
23557
23596
  for (let i = 0; i < 6; i++) {
23558
23597
  const candidate = join6(dir, "package.json");
23559
23598
  try {
23560
- const text = await readFile32(candidate, "utf-8");
23599
+ const text = await readFile31(candidate, "utf-8");
23561
23600
  return JSON.parse(text);
23562
23601
  } catch {
23563
23602
  dir = dirname15(dir);
@@ -23610,7 +23649,7 @@ function versionGte(a, b) {
23610
23649
  // src/utils/doctor/checks/structure.ts
23611
23650
  init_fs();
23612
23651
  import { resolve as resolve53 } from "path";
23613
- import { readdir as readdir19, stat as stat4 } from "fs/promises";
23652
+ import { readdir as readdir18, stat as stat4 } from "fs/promises";
23614
23653
  var CATEGORY2 = "structure";
23615
23654
  var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
23616
23655
  "projects",
@@ -23741,7 +23780,7 @@ var knownFilesRecognized = {
23741
23780
  category: CATEGORY2,
23742
23781
  title: "No unexpected top-level entries under ~/.syntaur/",
23743
23782
  async run(ctx) {
23744
- const entries = await readdir19(ctx.syntaurRoot, { withFileTypes: true });
23783
+ const entries = await readdir18(ctx.syntaurRoot, { withFileTypes: true });
23745
23784
  const unexpected = [];
23746
23785
  for (const e of entries) {
23747
23786
  if (e.name.startsWith(".")) continue;
@@ -23785,7 +23824,7 @@ function pass2(check) {
23785
23824
  // src/utils/doctor/checks/project.ts
23786
23825
  init_fs();
23787
23826
  import { resolve as resolve54 } from "path";
23788
- import { readdir as readdir20, stat as stat5 } from "fs/promises";
23827
+ import { readdir as readdir19, stat as stat5 } from "fs/promises";
23789
23828
  var CATEGORY3 = "project";
23790
23829
  var REQUIRED_PROJECT_FILES = [
23791
23830
  "project.md",
@@ -23809,7 +23848,7 @@ var PROJECT_MARKERS = ["project.md", "manifest.md", "assignments"];
23809
23848
  async function listProjects2(ctx) {
23810
23849
  const dir = ctx.config.defaultProjectDir;
23811
23850
  if (!await fileExists(dir)) return [];
23812
- const entries = await readdir20(dir, { withFileTypes: true });
23851
+ const entries = await readdir19(dir, { withFileTypes: true });
23813
23852
  const result = [];
23814
23853
  for (const e of entries) {
23815
23854
  if (!e.isDirectory()) continue;
@@ -23903,7 +23942,7 @@ var orphanFiles = {
23903
23942
  const projects = await listProjects2(ctx);
23904
23943
  const results = [];
23905
23944
  for (const projectDir of projects) {
23906
- const entries = await readdir20(projectDir, { withFileTypes: true });
23945
+ const entries = await readdir19(projectDir, { withFileTypes: true });
23907
23946
  const orphans = [];
23908
23947
  for (const e of entries) {
23909
23948
  if (e.name.startsWith(".")) continue;
@@ -23933,7 +23972,7 @@ async function newestAssignmentMtime(projectDir) {
23933
23972
  let newest = 0;
23934
23973
  let entries;
23935
23974
  try {
23936
- entries = await readdir20(assignmentsRoot, { withFileTypes: true });
23975
+ entries = await readdir19(assignmentsRoot, { withFileTypes: true });
23937
23976
  } catch {
23938
23977
  return 0;
23939
23978
  }
@@ -23964,7 +24003,7 @@ init_parser();
23964
24003
  init_types();
23965
24004
  init_paths();
23966
24005
  import { resolve as resolve55 } from "path";
23967
- import { readFile as readFile33, readdir as readdir21 } from "fs/promises";
24006
+ import { readFile as readFile32, readdir as readdir20 } from "fs/promises";
23968
24007
  var CATEGORY4 = "assignment";
23969
24008
  var STATUSES_REQUIRING_HANDOFF = /* @__PURE__ */ new Set(["review", "completed"]);
23970
24009
  var PRE_WORKSPACE_STATUSES = /* @__PURE__ */ new Set([
@@ -24312,7 +24351,7 @@ var draftMissingObjective = {
24312
24351
  if (parsed.status !== "draft") continue;
24313
24352
  let raw;
24314
24353
  try {
24315
- raw = await readFile33(path, "utf-8");
24354
+ raw = await readFile32(path, "utf-8");
24316
24355
  } catch {
24317
24356
  continue;
24318
24357
  }
@@ -24349,12 +24388,12 @@ var readyToImplementMissingPlan = {
24349
24388
  const parsed = await parseSafe2(path);
24350
24389
  if (!parsed) continue;
24351
24390
  if (parsed.status !== "ready_to_implement") continue;
24352
- const entries = await readdir21(a.assignmentDir).catch(() => []);
24391
+ const entries = await readdir20(a.assignmentDir).catch(() => []);
24353
24392
  const planFiles = entries.filter((f) => /^plan(?:-v\d+)?\.md$/i.test(f));
24354
24393
  let hasPlanContent = false;
24355
24394
  for (const f of planFiles) {
24356
24395
  try {
24357
- const c2 = await readFile33(resolve55(a.assignmentDir, f), "utf-8");
24396
+ const c2 = await readFile32(resolve55(a.assignmentDir, f), "utf-8");
24358
24397
  if (c2.trim().length > 0) {
24359
24398
  hasPlanContent = true;
24360
24399
  break;
@@ -24397,7 +24436,7 @@ var assignmentChecks = [
24397
24436
  ];
24398
24437
  async function parseSafe2(path) {
24399
24438
  try {
24400
- const content = await readFile33(path, "utf-8");
24439
+ const content = await readFile32(path, "utf-8");
24401
24440
  return parseAssignmentFull(content);
24402
24441
  } catch {
24403
24442
  return null;
@@ -24558,7 +24597,7 @@ function skipped(check, reason) {
24558
24597
  // src/utils/doctor/checks/integrations.ts
24559
24598
  init_fs();
24560
24599
  import { resolve as resolve57, dirname as dirname16, basename as basename5 } from "path";
24561
- import { readdir as readdir22, readFile as readFile34 } from "fs/promises";
24600
+ import { readdir as readdir21, readFile as readFile33 } from "fs/promises";
24562
24601
  import { homedir as homedir7 } from "os";
24563
24602
  var CATEGORY6 = "integrations";
24564
24603
  var claudePluginLinked = {
@@ -24621,7 +24660,7 @@ var backupConfigured = {
24621
24660
  if (ctx.config.backup?.repo) return pass6(this);
24622
24661
  const projectsDir2 = ctx.config.defaultProjectDir;
24623
24662
  if (!await fileExists(projectsDir2)) return skipped2(this, "no projects dir");
24624
- const entries = await readdir22(projectsDir2, { withFileTypes: true });
24663
+ const entries = await readdir21(projectsDir2, { withFileTypes: true });
24625
24664
  const hasProjects = entries.some((e) => e.isDirectory() && !e.name.startsWith(".") && !e.name.startsWith("_"));
24626
24665
  if (!hasProjects) return skipped2(this, "no projects yet");
24627
24666
  return {
@@ -24643,7 +24682,7 @@ async function readKnownMarketplaces() {
24643
24682
  const path = resolve57(homedir7(), ".claude", "plugins", "known_marketplaces.json");
24644
24683
  if (!await fileExists(path)) return {};
24645
24684
  try {
24646
- const raw = await readFile34(path, "utf-8");
24685
+ const raw = await readFile33(path, "utf-8");
24647
24686
  return JSON.parse(raw);
24648
24687
  } catch {
24649
24688
  return {};
@@ -24696,7 +24735,7 @@ var claudeMarketplaceRegistered = {
24696
24735
  }
24697
24736
  let parsed = {};
24698
24737
  try {
24699
- parsed = JSON.parse(await readFile34(marketplaceManifest, "utf-8"));
24738
+ parsed = JSON.parse(await readFile33(marketplaceManifest, "utf-8"));
24700
24739
  } catch {
24701
24740
  return {
24702
24741
  id: this.id,
@@ -24769,7 +24808,7 @@ init_fs();
24769
24808
  init_parser();
24770
24809
  init_types();
24771
24810
  import { resolve as resolve58 } from "path";
24772
- import { readFile as readFile35 } from "fs/promises";
24811
+ import { readFile as readFile34 } from "fs/promises";
24773
24812
  var CATEGORY7 = "workspace";
24774
24813
  var ASSIGNMENT_FIELDS = ["projectSlug", "assignmentSlug", "projectDir", "assignmentDir"];
24775
24814
  var BUNDLE_FIELDS = ["bundleId", "bundleScope", "bundleScopeId"];
@@ -24795,7 +24834,7 @@ async function loadContext(ctx) {
24795
24834
  return { data: null, path, exists: false, parseError: null };
24796
24835
  }
24797
24836
  try {
24798
- const raw = await readFile35(path, "utf-8");
24837
+ const raw = await readFile34(path, "utf-8");
24799
24838
  return { data: JSON.parse(raw), path, exists: true, parseError: null };
24800
24839
  } catch (err2) {
24801
24840
  return {
@@ -24922,7 +24961,7 @@ var contextTerminal = {
24922
24961
  const assignmentMd = resolve58(data.assignmentDir, "assignment.md");
24923
24962
  if (!await fileExists(assignmentMd)) return skipped3(this, "assignment file missing");
24924
24963
  try {
24925
- const content = await readFile35(assignmentMd, "utf-8");
24964
+ const content = await readFile34(assignmentMd, "utf-8");
24926
24965
  const parsed = parseAssignmentFull(content);
24927
24966
  const terminal = terminalStatuses2(ctx);
24928
24967
  if (terminal.has(parsed.status)) {
@@ -25068,7 +25107,7 @@ var agentChecks = [agentsResolvable];
25068
25107
  // src/utils/doctor/checks/terminal.ts
25069
25108
  init_config2();
25070
25109
  import { spawnSync as spawnSync8 } from "child_process";
25071
- import { readFile as readFile36 } from "fs/promises";
25110
+ import { readFile as readFile35 } from "fs/promises";
25072
25111
  import { resolve as resolve59 } from "path";
25073
25112
  init_paths();
25074
25113
  init_fs();
@@ -25076,7 +25115,7 @@ var CATEGORY9 = "terminal";
25076
25115
  async function readRawTerminalKey() {
25077
25116
  const configPath = resolve59(syntaurRoot(), "config.md");
25078
25117
  if (!await fileExists(configPath)) return null;
25079
- const content = await readFile36(configPath, "utf-8");
25118
+ const content = await readFile35(configPath, "utf-8");
25080
25119
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
25081
25120
  if (!fmMatch) return null;
25082
25121
  const line = fmMatch[1].split("\n").find((l) => /^terminal:\s*/.test(l));
@@ -25227,7 +25266,7 @@ var terminalChecks = [
25227
25266
  // src/utils/doctor/checks/skills.ts
25228
25267
  init_fs();
25229
25268
  import { resolve as resolve60, join as join7 } from "path";
25230
- import { readdir as readdir23, readFile as readFile37, lstat as lstat4 } from "fs/promises";
25269
+ import { readdir as readdir22, readFile as readFile36, lstat as lstat4 } from "fs/promises";
25231
25270
  import { homedir as homedir8 } from "os";
25232
25271
  var CATEGORY10 = "skills";
25233
25272
  var skillTargets = [
@@ -25247,7 +25286,7 @@ var skillsDedupCheck = {
25247
25286
  const present = [];
25248
25287
  let entries;
25249
25288
  try {
25250
- entries = await readdir23(dir, { withFileTypes: true });
25289
+ entries = await readdir22(dir, { withFileTypes: true });
25251
25290
  } catch {
25252
25291
  continue;
25253
25292
  }
@@ -25256,7 +25295,7 @@ var skillsDedupCheck = {
25256
25295
  if (!KNOWN_SKILLS.includes(entry.name)) continue;
25257
25296
  const skillMd = join7(dir, entry.name, "SKILL.md");
25258
25297
  if (!await fileExists(skillMd)) continue;
25259
- const content = await readFile37(skillMd, "utf-8").catch(() => "");
25298
+ const content = await readFile36(skillMd, "utf-8").catch(() => "");
25260
25299
  const match = content.match(/^name:\s*(\S+)\s*$/m);
25261
25300
  if (!match || match[1] !== entry.name) continue;
25262
25301
  let isSymlink2 = false;
@@ -25308,7 +25347,7 @@ var skillsChecks = [skillsDedupCheck];
25308
25347
  init_fs();
25309
25348
  init_paths();
25310
25349
  import { resolve as resolve61 } from "path";
25311
- import { readdir as readdir24 } from "fs/promises";
25350
+ import { readdir as readdir23 } from "fs/promises";
25312
25351
  import { spawnSync as spawnSync9 } from "child_process";
25313
25352
  init_parser2();
25314
25353
  var CATEGORY11 = "bundles";
@@ -25316,7 +25355,7 @@ async function listScopes(ctx) {
25316
25355
  const out = [];
25317
25356
  const td = todosDir();
25318
25357
  if (await fileExists(td)) {
25319
- const entries = await readdir24(td).catch(() => []);
25358
+ const entries = await readdir23(td).catch(() => []);
25320
25359
  for (const f of entries) {
25321
25360
  if (typeof f !== "string") continue;
25322
25361
  if (!f.endsWith(".md") || f.endsWith("-log.md")) continue;
@@ -25332,7 +25371,7 @@ async function listScopes(ctx) {
25332
25371
  }
25333
25372
  }
25334
25373
  if (await fileExists(ctx.config.defaultProjectDir)) {
25335
- const projectEntries = await readdir24(ctx.config.defaultProjectDir, { withFileTypes: true }).catch(() => []);
25374
+ const projectEntries = await readdir23(ctx.config.defaultProjectDir, { withFileTypes: true }).catch(() => []);
25336
25375
  for (const e of projectEntries) {
25337
25376
  if (!e.isDirectory()) continue;
25338
25377
  const slug = e.name;
@@ -25628,7 +25667,7 @@ async function readVersion() {
25628
25667
  let dir = dirname17(here);
25629
25668
  for (let i = 0; i < 6; i++) {
25630
25669
  try {
25631
- const raw = await readFile38(join8(dir, "package.json"), "utf-8");
25670
+ const raw = await readFile37(join8(dir, "package.json"), "utf-8");
25632
25671
  const parsed = JSON.parse(raw);
25633
25672
  return typeof parsed.version === "string" ? parsed.version : null;
25634
25673
  } catch {
@@ -25738,7 +25777,7 @@ async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
25738
25777
  }
25739
25778
  let content;
25740
25779
  try {
25741
- content = await readFile39(absolute, "utf-8");
25780
+ content = await readFile38(absolute, "utf-8");
25742
25781
  } catch (err2) {
25743
25782
  return {
25744
25783
  ok: false,
@@ -26063,7 +26102,7 @@ init_timestamp();
26063
26102
  init_assignment_resolver();
26064
26103
  init_templates();
26065
26104
  import { resolve as resolve63 } from "path";
26066
- import { readFile as readFile40 } from "fs/promises";
26105
+ import { readFile as readFile39 } from "fs/promises";
26067
26106
  function shortId() {
26068
26107
  return generateId().split("-")[0];
26069
26108
  }
@@ -26109,7 +26148,7 @@ async function commentCommand(target, text, options = {}) {
26109
26148
  let currentContent;
26110
26149
  let currentCount = 0;
26111
26150
  if (await fileExists(commentsPath)) {
26112
- currentContent = await readFile40(commentsPath, "utf-8");
26151
+ currentContent = await readFile39(commentsPath, "utf-8");
26113
26152
  const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
26114
26153
  if (countMatch) currentCount = parseInt(countMatch[1], 10);
26115
26154
  } else {
@@ -26155,7 +26194,7 @@ init_slug();
26155
26194
  init_assignment_resolver();
26156
26195
  init_parser();
26157
26196
  import { resolve as resolve64 } from "path";
26158
- import { readFile as readFile41 } from "fs/promises";
26197
+ import { readFile as readFile40 } from "fs/promises";
26159
26198
  var AssignmentTargetError = class extends Error {
26160
26199
  };
26161
26200
  function classifyContext(ctx) {
@@ -26170,7 +26209,7 @@ async function readAssignmentFrontmatterId(assignmentDir) {
26170
26209
  const path = resolve64(assignmentDir, "assignment.md");
26171
26210
  if (!await fileExists(path)) return null;
26172
26211
  try {
26173
- const content = await readFile41(path, "utf-8");
26212
+ const content = await readFile40(path, "utf-8");
26174
26213
  const [fm] = extractFrontmatter(content);
26175
26214
  return getField(fm, "id");
26176
26215
  } catch {
@@ -26181,7 +26220,7 @@ async function readContextJson(cwd) {
26181
26220
  const path = resolve64(cwd, ".syntaur", "context.json");
26182
26221
  if (!await fileExists(path)) return null;
26183
26222
  try {
26184
- const raw = await readFile41(path, "utf-8");
26223
+ const raw = await readFile40(path, "utf-8");
26185
26224
  return JSON.parse(raw);
26186
26225
  } catch {
26187
26226
  return null;
@@ -26414,7 +26453,7 @@ async function captureScreenshot(mode) {
26414
26453
 
26415
26454
  // src/utils/asciinema.ts
26416
26455
  import { spawn as spawn6 } from "child_process";
26417
- import { mkdtemp as mkdtemp3, readFile as readFile42, rm as rm9 } from "fs/promises";
26456
+ import { mkdtemp as mkdtemp3, readFile as readFile41, rm as rm9 } from "fs/promises";
26418
26457
  import { tmpdir as tmpdir4 } from "os";
26419
26458
  import { join as join10 } from "path";
26420
26459
  var SAFE_RE = /^[A-Za-z0-9_@%+=:,./-]+$/;
@@ -26477,7 +26516,7 @@ async function captureAsciinema(opts) {
26477
26516
  }
26478
26517
  throw err2;
26479
26518
  }
26480
- const text = await readFile42(castPath, "utf8").catch(() => null);
26519
+ const text = await readFile41(castPath, "utf8").catch(() => null);
26481
26520
  if (text === null) {
26482
26521
  throw new Error(
26483
26522
  `asciinema produced no cast file at ${castPath} (exit ${exitCode}). Try running 'asciinema rec ${castPath}' directly to diagnose.`
@@ -26505,7 +26544,7 @@ async function captureAsciinema(opts) {
26505
26544
  // src/utils/recording.ts
26506
26545
  init_paths();
26507
26546
  import { spawn as spawn7 } from "child_process";
26508
- import { mkdir as mkdir8, mkdtemp as mkdtemp4, open as open3, readFile as readFile43, rm as rm10, stat as stat7, unlink as unlink9, writeFile as writeFile12 } from "fs/promises";
26547
+ import { mkdir as mkdir8, mkdtemp as mkdtemp4, open as open3, readFile as readFile42, rm as rm10, stat as stat7, unlink as unlink9, writeFile as writeFile12 } from "fs/promises";
26509
26548
  import { tmpdir as tmpdir5 } from "os";
26510
26549
  import { join as join11, resolve as resolve65 } from "path";
26511
26550
  import { setTimeout as sleep } from "timers/promises";
@@ -26579,7 +26618,7 @@ async function acquirePidfile(pidfile) {
26579
26618
  } catch (err2) {
26580
26619
  if (err2.code !== "EEXIST") throw err2;
26581
26620
  if (attempt === 1) throw err2;
26582
- const existing = (await readFile43(pidfile, "utf-8").catch(() => "")).trim();
26621
+ const existing = (await readFile42(pidfile, "utf-8").catch(() => "")).trim();
26583
26622
  if (existing.startsWith(STARTING_SENTINEL_PREFIX)) {
26584
26623
  const parentPidRaw = existing.slice(STARTING_SENTINEL_PREFIX.length);
26585
26624
  const parentPid = Number.parseInt(parentPidRaw, 10);
@@ -26681,7 +26720,7 @@ async function startRecording(input4) {
26681
26720
  logHandle = null;
26682
26721
  if (warmupMs > 0) await sleep(warmupMs);
26683
26722
  if (!await isProcessAlive(pid)) {
26684
- const tail = await readFile43(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
26723
+ const tail = await readFile42(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
26685
26724
  acquiredPid = null;
26686
26725
  throw new Error(
26687
26726
  `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}
@@ -26738,7 +26777,7 @@ ${tail}`
26738
26777
  async function stopRecording() {
26739
26778
  const pidfile = pidfilePath();
26740
26779
  const sidecar = sidecarPath();
26741
- const pidRaw = await readFile43(pidfile, "utf-8").catch(() => null);
26780
+ const pidRaw = await readFile42(pidfile, "utf-8").catch(() => null);
26742
26781
  if (pidRaw === null) {
26743
26782
  throw new Error(
26744
26783
  `No active recording found (no pidfile at ${pidfile}). Did you run --start?`
@@ -26748,7 +26787,7 @@ async function stopRecording() {
26748
26787
  if (!Number.isInteger(pid) || pid <= 0) {
26749
26788
  throw new Error(`Pidfile at ${pidfile} is corrupt (got "${pidRaw}").`);
26750
26789
  }
26751
- const sidecarRaw = await readFile43(sidecar, "utf-8").catch(() => null);
26790
+ const sidecarRaw = await readFile42(sidecar, "utf-8").catch(() => null);
26752
26791
  if (sidecarRaw === null) {
26753
26792
  throw new Error(
26754
26793
  `No recording sidecar at ${sidecar}. The recording state is inconsistent \u2014 delete ${pidfile} and re-run --start.`
@@ -26881,7 +26920,7 @@ function listArtifactsByAssignment(assignmentId) {
26881
26920
 
26882
26921
  // src/utils/transcribers/elevenlabs.ts
26883
26922
  import { spawn as spawn8 } from "child_process";
26884
- import { mkdtemp as mkdtemp5, readFile as readFile44, rm as rm11 } from "fs/promises";
26923
+ import { mkdtemp as mkdtemp5, readFile as readFile43, rm as rm11 } from "fs/promises";
26885
26924
  import { tmpdir as tmpdir6 } from "os";
26886
26925
  import { join as join12 } from "path";
26887
26926
  var SCRIBE_URL = "https://api.elevenlabs.io/v1/speech-to-text";
@@ -26945,7 +26984,7 @@ async function extractAudio(videoAbsPath, wavOut) {
26945
26984
  throw new TranscribeFfmpegError(`ffmpeg failed (exit ${result.code}): ${tail}`);
26946
26985
  }
26947
26986
  async function callScribe(wavPath, apiKey, opts) {
26948
- const audio = await readFile44(wavPath);
26987
+ const audio = await readFile43(wavPath);
26949
26988
  const form = new FormData();
26950
26989
  form.set("file", new Blob([new Uint8Array(audio)], { type: "audio/wav" }), "audio.wav");
26951
26990
  form.set("model_id", "scribe_v1");
@@ -27410,7 +27449,7 @@ async function captureCommand(target, options = {}) {
27410
27449
 
27411
27450
  // src/commands/proof.ts
27412
27451
  import { Command as Command6 } from "commander";
27413
- import { readFile as readFile45, writeFile as writeFile14, rename as rename9, stat as stat9 } from "fs/promises";
27452
+ import { readFile as readFile44, writeFile as writeFile14, rename as rename9, stat as stat9 } from "fs/promises";
27414
27453
  import { resolve as resolve68, relative as relative5, isAbsolute as isAbsolute11, dirname as dirname19 } from "path";
27415
27454
  import { randomBytes as randomBytes4 } from "crypto";
27416
27455
 
@@ -27655,7 +27694,7 @@ async function readAssignmentMeta(assignmentDir) {
27655
27694
  if (!await fileExists(path)) {
27656
27695
  return { title: "", body: "" };
27657
27696
  }
27658
- const content = await readFile45(path, "utf-8");
27697
+ const content = await readFile44(path, "utf-8");
27659
27698
  const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
27660
27699
  let title = "";
27661
27700
  if (fmMatch) {
@@ -27717,7 +27756,7 @@ async function loadInlineFiles(rows, assignmentDir) {
27717
27756
  continue;
27718
27757
  }
27719
27758
  try {
27720
- out.set(r.file_path, await readFile45(abs, "utf-8"));
27759
+ out.set(r.file_path, await readFile44(abs, "utf-8"));
27721
27760
  } catch {
27722
27761
  out.set(r.file_path, null);
27723
27762
  }
@@ -27736,7 +27775,7 @@ async function loadTranscriptSidecars(rows, assignmentDir) {
27736
27775
  const st = await stat9(sidecar);
27737
27776
  if (st.size > INLINE_TEXT_LIMIT_BYTES) continue;
27738
27777
  try {
27739
- out.set(r.id, await readFile45(sidecar, "utf-8"));
27778
+ out.set(r.id, await readFile44(sidecar, "utf-8"));
27740
27779
  } catch {
27741
27780
  }
27742
27781
  }
@@ -28494,7 +28533,7 @@ function isoToCcusageDate(iso) {
28494
28533
 
28495
28534
  // src/usage/cwd-extractor.ts
28496
28535
  init_paths();
28497
- import { open as open4, readdir as readdir25, stat as stat10 } from "fs/promises";
28536
+ import { open as open4, readdir as readdir24, stat as stat10 } from "fs/promises";
28498
28537
  import { join as join13 } from "path";
28499
28538
  import { homedir as homedir9 } from "os";
28500
28539
  var SCAN_LINE_CAP = 50;
@@ -28617,7 +28656,7 @@ function resolveCodexSessionsRoot(override) {
28617
28656
  }
28618
28657
  async function listDirSafe(path) {
28619
28658
  try {
28620
- const entries = await readdir25(path, { withFileTypes: true });
28659
+ const entries = await readdir24(path, { withFileTypes: true });
28621
28660
  return entries.map((e) => ({
28622
28661
  name: e.name,
28623
28662
  isFile: e.isFile(),
@@ -28973,7 +29012,7 @@ init_timestamp();
28973
29012
  init_assignment_resolver();
28974
29013
  init_assignment_todos();
28975
29014
  import { resolve as resolve69 } from "path";
28976
- import { readFile as readFile46 } from "fs/promises";
29015
+ import { readFile as readFile45 } from "fs/promises";
28977
29016
  async function requestCommand(target, text, options = {}) {
28978
29017
  if (!text || !text.trim()) {
28979
29018
  throw new Error("Request text cannot be empty.");
@@ -29004,7 +29043,7 @@ async function requestCommand(target, text, options = {}) {
29004
29043
  throw new Error(`assignment.md not found at ${assignmentMdPath2}`);
29005
29044
  }
29006
29045
  const source = options.from ?? process.env.SYNTAUR_ASSIGNMENT ?? "unknown";
29007
- let content = await readFile46(assignmentMdPath2, "utf-8");
29046
+ let content = await readFile45(assignmentMdPath2, "utf-8");
29008
29047
  content = appendTodosToAssignmentBody(content, [
29009
29048
  { description: `${text.trim()} (from: ${source})` }
29010
29049
  ]);
@@ -29017,13 +29056,13 @@ async function requestCommand(target, text, options = {}) {
29017
29056
  init_fs();
29018
29057
  init_paths();
29019
29058
  import { Command as Command9 } from "commander";
29020
- import { readFile as readFile47, readdir as readdir26 } from "fs/promises";
29059
+ import { readFile as readFile46, readdir as readdir25 } from "fs/promises";
29021
29060
  import { resolve as resolve70 } from "path";
29022
29061
  async function readContextAssignmentDir(cwd) {
29023
29062
  const path = resolve70(cwd, ".syntaur", "context.json");
29024
29063
  if (!await fileExists(path)) return null;
29025
29064
  try {
29026
- const raw = await readFile47(path, "utf-8");
29065
+ const raw = await readFile46(path, "utf-8");
29027
29066
  const ctx = JSON.parse(raw);
29028
29067
  if (typeof ctx.assignmentDir === "string" && ctx.assignmentDir.length > 0) {
29029
29068
  return ctx.assignmentDir;
@@ -29050,7 +29089,7 @@ async function resolveAssignmentDir(opts) {
29050
29089
  var PLAN_PATTERN = /^plan(?:-v(\d+))?\.md$/;
29051
29090
  async function listPlanFiles(assignmentDir) {
29052
29091
  if (!await fileExists(assignmentDir)) return [];
29053
- const entries = await readdir26(assignmentDir, { withFileTypes: true });
29092
+ const entries = await readdir25(assignmentDir, { withFileTypes: true });
29054
29093
  const out = [];
29055
29094
  for (const e of entries) {
29056
29095
  if (!e.isFile()) continue;
@@ -29198,11 +29237,11 @@ async function runPlanVersion(options) {
29198
29237
  if (await fileExists(newPath) && !options.force) {
29199
29238
  throw new Error(`${next.fileName} already exists. Use --force to overwrite.`);
29200
29239
  }
29201
- const assignmentMd = await readFile47(assignmentMdPath2, "utf-8");
29240
+ const assignmentMd = await readFile46(assignmentMdPath2, "utf-8");
29202
29241
  const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
29203
29242
  const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
29204
29243
  const oldPlanPath = resolve70(assignmentDir, current.fileName);
29205
- const oldPlanContent = await readFile47(oldPlanPath, "utf-8");
29244
+ const oldPlanContent = await readFile46(oldPlanPath, "utf-8");
29206
29245
  const oldBody = oldPlanContent.replace(/^---[\s\S]*?\n---\n?/, "");
29207
29246
  const carriedTodos = extractUncheckedTodos(oldBody);
29208
29247
  const stub = buildNewPlanStub({
@@ -29239,13 +29278,13 @@ planCommand.command("version").description(
29239
29278
  // src/commands/session.ts
29240
29279
  init_fs();
29241
29280
  import { Command as Command10 } from "commander";
29242
- import { readFile as readFile48, readdir as readdir27, stat as stat11 } from "fs/promises";
29281
+ import { readFile as readFile47, readdir as readdir26, stat as stat11 } from "fs/promises";
29243
29282
  import { resolve as resolve71 } from "path";
29244
29283
  async function readContext(cwd) {
29245
29284
  const path = resolve71(cwd, ".syntaur", "context.json");
29246
29285
  if (!await fileExists(path)) return null;
29247
29286
  try {
29248
- const raw = await readFile48(path, "utf-8");
29287
+ const raw = await readFile47(path, "utf-8");
29249
29288
  return JSON.parse(raw);
29250
29289
  } catch {
29251
29290
  return null;
@@ -29254,7 +29293,7 @@ async function readContext(cwd) {
29254
29293
  async function findLatestSessionSummary(assignmentDir) {
29255
29294
  const sessionsRoot = resolve71(assignmentDir, "sessions");
29256
29295
  if (!await fileExists(sessionsRoot)) return null;
29257
- const entries = await readdir27(sessionsRoot, { withFileTypes: true });
29296
+ const entries = await readdir26(sessionsRoot, { withFileTypes: true });
29258
29297
  let best = null;
29259
29298
  for (const entry of entries) {
29260
29299
  if (!entry.isDirectory()) continue;
@@ -29270,7 +29309,7 @@ async function findLatestSessionSummary(assignmentDir) {
29270
29309
  async function findOpenHandoff(assignmentDir) {
29271
29310
  const handoffPath = resolve71(assignmentDir, "handoff.md");
29272
29311
  if (!await fileExists(handoffPath)) return null;
29273
- const content = await readFile48(handoffPath, "utf-8");
29312
+ const content = await readFile47(handoffPath, "utf-8");
29274
29313
  const body = content.replace(/^---[\s\S]*?\n---\n?/, "").trim();
29275
29314
  if (body.length === 0) return null;
29276
29315
  if (/^<!--[\s\S]*-->$/.test(body)) return null;
@@ -29378,13 +29417,13 @@ init_git_worktree();
29378
29417
  init_fs();
29379
29418
  init_paths();
29380
29419
  import { Command as Command11 } from "commander";
29381
- import { readFile as readFile49 } from "fs/promises";
29420
+ import { readFile as readFile48 } from "fs/promises";
29382
29421
  import { resolve as resolve72 } from "path";
29383
29422
  async function readContext2(cwd) {
29384
29423
  const path = resolve72(cwd, ".syntaur", "context.json");
29385
29424
  if (!await fileExists(path)) return null;
29386
29425
  try {
29387
- return JSON.parse(await readFile49(path, "utf-8"));
29426
+ return JSON.parse(await readFile48(path, "utf-8"));
29388
29427
  } catch {
29389
29428
  return null;
29390
29429
  }
@@ -29456,7 +29495,7 @@ import { resolve as resolve74 } from "path";
29456
29495
  // src/utils/project-indexes.ts
29457
29496
  init_parser();
29458
29497
  init_fs();
29459
- import { readdir as readdir28, readFile as readFile50 } from "fs/promises";
29498
+ import { readdir as readdir27, readFile as readFile49 } from "fs/promises";
29460
29499
  import { resolve as resolve73 } from "path";
29461
29500
  function nowIso3() {
29462
29501
  return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
@@ -29466,7 +29505,7 @@ function readProjectSlug(projectDir) {
29466
29505
  }
29467
29506
  async function listSlugFiles(dir) {
29468
29507
  if (!await fileExists(dir)) return [];
29469
- const entries = await readdir28(dir, { withFileTypes: true });
29508
+ const entries = await readdir27(dir, { withFileTypes: true });
29470
29509
  return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && !e.name.startsWith("_")).map((e) => e.name).sort();
29471
29510
  }
29472
29511
  function escapeCell(value) {
@@ -29492,7 +29531,7 @@ async function rebuildResourcesIndex(projectDir) {
29492
29531
  lines.push("| Name | Category | Source | Related Assignments | Updated |");
29493
29532
  lines.push("|------|----------|--------|---------------------|---------|");
29494
29533
  for (const fileName of files) {
29495
- const content = await readFile50(resolve73(dir, fileName), "utf-8");
29534
+ const content = await readFile49(resolve73(dir, fileName), "utf-8");
29496
29535
  const parsed = parseResource(content);
29497
29536
  const slugBase = fileName.replace(/\.md$/, "");
29498
29537
  const name = parsed.name || slugBase;
@@ -29523,7 +29562,7 @@ async function rebuildMemoriesIndex(projectDir) {
29523
29562
  lines.push("| Name | Source | Scope | Source Assignment | Updated |");
29524
29563
  lines.push("|------|--------|-------|-------------------|---------|");
29525
29564
  for (const fileName of files) {
29526
- const content = await readFile50(resolve73(dir, fileName), "utf-8");
29565
+ const content = await readFile49(resolve73(dir, fileName), "utf-8");
29527
29566
  const parsed = parseMemory(content);
29528
29567
  const slugBase = fileName.replace(/\.md$/, "");
29529
29568
  const name = parsed.name || slugBase;
@@ -29698,7 +29737,7 @@ init_paths();
29698
29737
  init_fs();
29699
29738
  init_frontmatter();
29700
29739
  import { Command as Command14 } from "commander";
29701
- import { readFile as readFile51 } from "fs/promises";
29740
+ import { readFile as readFile50 } from "fs/promises";
29702
29741
  import { resolve as resolve76 } from "path";
29703
29742
  var AGE_PATTERN = /^(\d+)([dhwm])$/i;
29704
29743
  function parseAgeToCutoff(age) {
@@ -29732,7 +29771,7 @@ async function loadTags(item) {
29732
29771
  const path = assignmentMdPath(item);
29733
29772
  if (!await fileExists(path)) return [];
29734
29773
  try {
29735
- const content = await readFile51(path, "utf-8");
29774
+ const content = await readFile50(path, "utf-8");
29736
29775
  return parseAssignmentFrontmatter(content).tags;
29737
29776
  } catch {
29738
29777
  return [];
@@ -29810,10 +29849,10 @@ var lsCommand = new Command14("ls").description(
29810
29849
 
29811
29850
  // src/cli-default-command.ts
29812
29851
  init_config2();
29813
- import { readdir as readdir29 } from "fs/promises";
29852
+ import { readdir as readdir28 } from "fs/promises";
29814
29853
  async function hasAnyProjectContent(projectsDir2) {
29815
29854
  try {
29816
- const entries = await readdir29(projectsDir2, { withFileTypes: true });
29855
+ const entries = await readdir28(projectsDir2, { withFileTypes: true });
29817
29856
  return entries.some((entry) => entry.isDirectory());
29818
29857
  } catch {
29819
29858
  return false;
@@ -29850,20 +29889,20 @@ async function getDefaultCommandName() {
29850
29889
  init_paths();
29851
29890
  init_fs();
29852
29891
  import { fileURLToPath as fileURLToPath10 } from "url";
29853
- import { readFile as readFile53 } from "fs/promises";
29892
+ import { readFile as readFile52 } from "fs/promises";
29854
29893
  import { dirname as dirname21, join as join15, resolve as resolve77 } from "path";
29855
29894
  import { spawn as spawn10 } from "child_process";
29856
29895
  import { createInterface as createInterface2 } from "readline/promises";
29857
29896
 
29858
29897
  // src/utils/version.ts
29859
29898
  import { fileURLToPath as fileURLToPath9 } from "url";
29860
- import { readFile as readFile52 } from "fs/promises";
29899
+ import { readFile as readFile51 } from "fs/promises";
29861
29900
  import { dirname as dirname20, join as join14 } from "path";
29862
29901
  async function readPackageVersion(scriptUrl) {
29863
29902
  try {
29864
29903
  const scriptPath = fileURLToPath9(scriptUrl);
29865
29904
  const pkgRoot = dirname20(dirname20(scriptPath));
29866
- const raw = await readFile52(join14(pkgRoot, "package.json"), "utf-8");
29905
+ const raw = await readFile51(join14(pkgRoot, "package.json"), "utf-8");
29867
29906
  const parsed = JSON.parse(raw);
29868
29907
  return typeof parsed.version === "string" ? parsed.version : null;
29869
29908
  } catch {
@@ -29893,7 +29932,7 @@ function isRunningViaNpx(scriptUrl) {
29893
29932
  async function readState() {
29894
29933
  if (!await fileExists(STATE_FILE)) return null;
29895
29934
  try {
29896
- const raw = await readFile53(STATE_FILE, "utf-8");
29935
+ const raw = await readFile52(STATE_FILE, "utf-8");
29897
29936
  return JSON.parse(raw);
29898
29937
  } catch {
29899
29938
  return null;
@@ -29952,7 +29991,7 @@ async function readGlobalVersion() {
29952
29991
  try {
29953
29992
  const manifestPath = join15(rootPath, "syntaur", "package.json");
29954
29993
  if (!await fileExists(manifestPath)) return null;
29955
- const raw = await readFile53(manifestPath, "utf-8");
29994
+ const raw = await readFile52(manifestPath, "utf-8");
29956
29995
  const parsed = JSON.parse(raw);
29957
29996
  return typeof parsed.version === "string" ? parsed.version : null;
29958
29997
  } catch {