syntaur 0.24.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -93,9 +93,10 @@ __export(fs_exports, {
93
93
  ensureDir: () => ensureDir,
94
94
  fileExists: () => fileExists,
95
95
  writeFileForce: () => writeFileForce,
96
+ writeFileReport: () => writeFileReport,
96
97
  writeFileSafe: () => writeFileSafe
97
98
  });
98
- import { mkdir, writeFile, access, rename } from "fs/promises";
99
+ import { mkdir, writeFile, readFile, access, rename } from "fs/promises";
99
100
  import { dirname, join } from "path";
100
101
  async function ensureDir(dir) {
101
102
  await mkdir(dir, { recursive: true });
@@ -126,6 +127,22 @@ async function writeFileForce(filePath, content) {
126
127
  await writeFile(tempPath, content, "utf-8");
127
128
  await rename(tempPath, filePath);
128
129
  }
130
+ async function writeFileReport(filePath, content, options = {}) {
131
+ if (!await fileExists(filePath)) {
132
+ await ensureDir(dirname(filePath));
133
+ await writeFile(filePath, content, "utf-8");
134
+ return "written";
135
+ }
136
+ const current = await readFile(filePath, "utf-8").catch(() => null);
137
+ if (current === content) {
138
+ return "already-current";
139
+ }
140
+ if (!options.force) {
141
+ return "differs-preserved";
142
+ }
143
+ await writeFileForce(filePath, content);
144
+ return "overwritten";
145
+ }
129
146
  var init_fs = __esm({
130
147
  "src/utils/fs.ts"() {
131
148
  "use strict";
@@ -471,7 +488,7 @@ var init_timestamp = __esm({
471
488
  });
472
489
 
473
490
  // src/utils/fs-migration.ts
474
- import { readdir, readFile, rename as rename2, writeFile as writeFile2 } from "fs/promises";
491
+ import { readdir, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "fs/promises";
475
492
  import { resolve as resolve2 } from "path";
476
493
  async function migrateLegacyProjectFiles(projectsDir2) {
477
494
  const result = {
@@ -540,7 +557,7 @@ async function migrateLegacyArchivedProjects(projectsDir2) {
540
557
  const projectMd = resolve2(projectsDir2, entry.name, "project.md");
541
558
  try {
542
559
  if (!await fileExists(projectMd)) continue;
543
- const content = await readFile(projectMd, "utf-8");
560
+ const content = await readFile2(projectMd, "utf-8");
544
561
  if (readFrontmatterField(content, "statusOverride") !== "archived") continue;
545
562
  let next = setFrontmatterField(content, "archived", true);
546
563
  if (readFrontmatterField(content, "archivedAt") === null) {
@@ -565,7 +582,7 @@ async function migrateLegacyConfig(configPath) {
565
582
  if (!await fileExists(configPath)) return result;
566
583
  let content;
567
584
  try {
568
- content = await readFile(configPath, "utf-8");
585
+ content = await readFile2(configPath, "utf-8");
569
586
  } catch {
570
587
  return result;
571
588
  }
@@ -822,7 +839,7 @@ __export(config_exports, {
822
839
  writeTerminalConfig: () => writeTerminalConfig,
823
840
  writeThemeConfig: () => writeThemeConfig
824
841
  });
825
- import { readFile as readFile2 } from "fs/promises";
842
+ import { readFile as readFile3 } from "fs/promises";
826
843
  import { spawnSync } from "child_process";
827
844
  import { resolve as resolve3, isAbsolute } from "path";
828
845
  function parseAgentCommand(value, agentId) {
@@ -950,6 +967,18 @@ function parseFrontmatter(content) {
950
967
  }
951
968
  return result;
952
969
  }
970
+ function parseInstalledAgents(fm) {
971
+ const prefix = "integrations.installedAgents.";
972
+ const installedAgents = {};
973
+ for (const [key, value] of Object.entries(fm)) {
974
+ if (!key.startsWith(prefix)) continue;
975
+ const id = key.slice(prefix.length);
976
+ if (!id) continue;
977
+ const scope = value === "project" ? "project" : "global";
978
+ installedAgents[id] = { scope };
979
+ }
980
+ return Object.keys(installedAgents).length > 0 ? { installedAgents } : {};
981
+ }
953
982
  function parseStatusConfig(content) {
954
983
  const match = content.match(/^---\n([\s\S]*?)\n---/);
955
984
  if (!match) return null;
@@ -1079,6 +1108,11 @@ function serializeIntegrationConfig(integrations) {
1079
1108
  if (integrations.codexMarketplacePath) {
1080
1109
  lines.push(` codexMarketplacePath: ${integrations.codexMarketplacePath}`);
1081
1110
  }
1111
+ if (integrations.installedAgents) {
1112
+ for (const [id, rec] of Object.entries(integrations.installedAgents)) {
1113
+ lines.push(` installedAgents.${id}: ${rec.scope}`);
1114
+ }
1115
+ }
1082
1116
  if (lines.length === 0) {
1083
1117
  return null;
1084
1118
  }
@@ -1147,7 +1181,7 @@ async function updatePlaybooksConfig(playbooks) {
1147
1181
  disabled: Array.from(new Set(playbooks.disabled ?? current.disabled))
1148
1182
  };
1149
1183
  const playbooksBlock = serializePlaybooksConfig(nextPlaybooks);
1150
- const existing = await fileExists(configPath) ? await readFile2(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
1184
+ const existing = await fileExists(configPath) ? await readFile3(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
1151
1185
  const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
1152
1186
  if (!fmMatch) {
1153
1187
  const bodyBlock = playbooksBlock ? `${playbooksBlock}
@@ -1199,7 +1233,7 @@ function serializeThemeConfig(theme) {
1199
1233
  async function writeThemeConfig(theme) {
1200
1234
  const configPath = resolve3(syntaurRoot(), "config.md");
1201
1235
  const themeBlock = serializeThemeConfig(theme);
1202
- const existing = await fileExists(configPath) ? await readFile2(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
1236
+ const existing = await fileExists(configPath) ? await readFile3(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
1203
1237
  const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
1204
1238
  if (!fmMatch) {
1205
1239
  const content = `---
@@ -1225,7 +1259,7 @@ ${normalizedFm}
1225
1259
  async function deleteThemeConfig() {
1226
1260
  const configPath = resolve3(syntaurRoot(), "config.md");
1227
1261
  if (!await fileExists(configPath)) return;
1228
- const existing = await readFile2(configPath, "utf-8");
1262
+ const existing = await readFile3(configPath, "utf-8");
1229
1263
  const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
1230
1264
  if (!fmMatch) return;
1231
1265
  const fmBlock = fmMatch[2];
@@ -1245,7 +1279,7 @@ function stripTopLevelScalar(fmBlock, key) {
1245
1279
  async function writeTerminalConfig(terminal) {
1246
1280
  const configPath = resolve3(syntaurRoot(), "config.md");
1247
1281
  const terminalLine = `terminal: ${terminal}`;
1248
- const existing = await fileExists(configPath) ? await readFile2(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
1282
+ const existing = await fileExists(configPath) ? await readFile3(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
1249
1283
  const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
1250
1284
  if (!fmMatch) {
1251
1285
  const content = `---
@@ -1271,7 +1305,7 @@ ${normalizedFm}
1271
1305
  async function deleteTerminalConfig() {
1272
1306
  const configPath = resolve3(syntaurRoot(), "config.md");
1273
1307
  if (!await fileExists(configPath)) return;
1274
- const existing = await readFile2(configPath, "utf-8");
1308
+ const existing = await readFile3(configPath, "utf-8");
1275
1309
  const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
1276
1310
  if (!fmMatch) return;
1277
1311
  const fmBlock = fmMatch[2];
@@ -1340,7 +1374,7 @@ async function writeHotkeyBindingsConfig(cfg) {
1340
1374
  }
1341
1375
  const configPath = resolve3(syntaurRoot(), "config.md");
1342
1376
  const block = serializeHotkeyBindingsConfig({ bindings: cleaned });
1343
- const existing = await fileExists(configPath) ? await readFile2(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
1377
+ const existing = await fileExists(configPath) ? await readFile3(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
1344
1378
  const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
1345
1379
  if (!fmMatch) {
1346
1380
  const content = `---
@@ -1366,7 +1400,7 @@ ${normalizedFm}
1366
1400
  async function deleteHotkeyBindingsConfig() {
1367
1401
  const configPath = resolve3(syntaurRoot(), "config.md");
1368
1402
  if (!await fileExists(configPath)) return;
1369
- const existing = await readFile2(configPath, "utf-8");
1403
+ const existing = await readFile3(configPath, "utf-8");
1370
1404
  const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
1371
1405
  if (!fmMatch) return;
1372
1406
  const fmBlock = fmMatch[2];
@@ -1683,7 +1717,7 @@ async function writeAgentsConfig(agents) {
1683
1717
  validateAgentList(agents);
1684
1718
  const configPath = resolve3(syntaurRoot(), "config.md");
1685
1719
  const agentsBlock = serializeAgentsConfig(agents);
1686
- const existing = await fileExists(configPath) ? await readFile2(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
1720
+ const existing = await fileExists(configPath) ? await readFile3(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
1687
1721
  const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
1688
1722
  if (!fmMatch) {
1689
1723
  const content = `---
@@ -1708,7 +1742,7 @@ ${newFm}
1708
1742
  async function deleteAgentsConfig() {
1709
1743
  const configPath = resolve3(syntaurRoot(), "config.md");
1710
1744
  if (!await fileExists(configPath)) return;
1711
- const existing = await readFile2(configPath, "utf-8");
1745
+ const existing = await readFile3(configPath, "utf-8");
1712
1746
  const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
1713
1747
  if (!fmMatch) return;
1714
1748
  const fmBlock = fmMatch[2];
@@ -1732,7 +1766,7 @@ ${statusBlock}
1732
1766
  await writeFileForce(configPath, content);
1733
1767
  return;
1734
1768
  }
1735
- const existing = await readFile2(configPath, "utf-8");
1769
+ const existing = await readFile3(configPath, "utf-8");
1736
1770
  const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
1737
1771
  if (!fmMatch) {
1738
1772
  const content = `---
@@ -1776,7 +1810,7 @@ ${statusBlock}
1776
1810
  async function deleteStatusConfig() {
1777
1811
  const configPath = resolve3(syntaurRoot(), "config.md");
1778
1812
  if (!await fileExists(configPath)) return;
1779
- const existing = await readFile2(configPath, "utf-8");
1813
+ const existing = await readFile3(configPath, "utf-8");
1780
1814
  const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
1781
1815
  if (!fmMatch) return;
1782
1816
  const fmBlock = fmMatch[2];
@@ -1794,7 +1828,7 @@ async function updateIntegrationConfig(integrations) {
1794
1828
  ...integrations
1795
1829
  };
1796
1830
  const integrationBlock = serializeIntegrationConfig(nextIntegrations);
1797
- const existing = await fileExists(configPath) ? await readFile2(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
1831
+ const existing = await fileExists(configPath) ? await readFile3(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
1798
1832
  const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
1799
1833
  if (!fmMatch) {
1800
1834
  const content = `---
@@ -1824,7 +1858,7 @@ async function updateOnboardingConfig(onboarding) {
1824
1858
  ...onboarding
1825
1859
  };
1826
1860
  const onboardingBlock = serializeOnboardingConfig(nextOnboarding);
1827
- const existing = await fileExists(configPath) ? await readFile2(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
1861
+ const existing = await fileExists(configPath) ? await readFile3(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
1828
1862
  const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
1829
1863
  if (!fmMatch) {
1830
1864
  const content = `---
@@ -1858,7 +1892,7 @@ async function updateBackupConfig(backup) {
1858
1892
  ...backup
1859
1893
  };
1860
1894
  const backupBlock = serializeBackupConfig(nextBackup);
1861
- const existing = await fileExists(configPath) ? await readFile2(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
1895
+ const existing = await fileExists(configPath) ? await readFile3(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
1862
1896
  const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
1863
1897
  if (!fmMatch) {
1864
1898
  const content = `---
@@ -1890,7 +1924,7 @@ async function readConfig() {
1890
1924
  migratedConfigPaths.add(configPath);
1891
1925
  await migrateLegacyConfig(configPath);
1892
1926
  }
1893
- const content = await readFile2(configPath, "utf-8");
1927
+ const content = await readFile3(configPath, "utf-8");
1894
1928
  const fm = parseFrontmatter(content);
1895
1929
  if (Object.keys(fm).length === 0) {
1896
1930
  console.warn("Warning: ~/.syntaur/config.md has malformed frontmatter, using defaults");
@@ -1929,7 +1963,8 @@ async function readConfig() {
1929
1963
  codexMarketplacePath: parseOptionalAbsolutePath(
1930
1964
  fm["integrations.codexMarketplacePath"],
1931
1965
  "integrations.codexMarketplacePath"
1932
- )
1966
+ ),
1967
+ ...parseInstalledAgents(fm)
1933
1968
  },
1934
1969
  backup: fm["backup.repo"] || fm["backup.categories"] ? {
1935
1970
  repo: fm["backup.repo"] && fm["backup.repo"] !== "null" ? fm["backup.repo"] : null,
@@ -2094,7 +2129,7 @@ var init_slug = __esm({
2094
2129
 
2095
2130
  // src/utils/playbooks.ts
2096
2131
  import { resolve as resolve4 } from "path";
2097
- import { readdir as readdir2, readFile as readFile3, unlink } from "fs/promises";
2132
+ import { readdir as readdir2, readFile as readFile4, unlink } from "fs/promises";
2098
2133
  function escapeRegExp(value) {
2099
2134
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2100
2135
  }
@@ -2120,7 +2155,7 @@ async function resolvePlaybookSlug(playbooksDir3, slug) {
2120
2155
  for (const entry of entries) {
2121
2156
  if (!isVisiblePlaybookFile(entry.name, entry.isFile())) continue;
2122
2157
  const filePath = resolve4(playbooksDir3, entry.name);
2123
- const raw = await readFile3(filePath, "utf-8");
2158
+ const raw = await readFile4(filePath, "utf-8");
2124
2159
  const parsed = parsePlaybook(raw);
2125
2160
  const canonical = parsed.slug || entry.name.replace(/\.md$/, "");
2126
2161
  if (canonical === slug) {
@@ -2168,7 +2203,7 @@ async function rebuildPlaybookManifest(playbooksDir3) {
2168
2203
  const rows = [];
2169
2204
  for (const entry of entries) {
2170
2205
  if (!isVisiblePlaybookFile(entry.name, entry.isFile())) continue;
2171
- const raw = await readFile3(resolve4(playbooksDir3, entry.name), "utf-8");
2206
+ const raw = await readFile4(resolve4(playbooksDir3, entry.name), "utf-8");
2172
2207
  const parsed = parsePlaybook(raw);
2173
2208
  const slug = parsed.slug || entry.name.replace(/\.md$/, "");
2174
2209
  if (disabledSet.has(slug)) continue;
@@ -2246,7 +2281,7 @@ async function renamePlaybook(playbooksDir3, oldSlug, newSlug) {
2246
2281
  );
2247
2282
  }
2248
2283
  }
2249
- const raw = await readFile3(oldPath, "utf-8");
2284
+ const raw = await readFile4(oldPath, "utf-8");
2250
2285
  let next = setFrontmatterField2(raw, "slug", newSlug);
2251
2286
  next = setFrontmatterField2(next, "updated", `"${nowTimestamp()}"`);
2252
2287
  await writeFileForce(newPath, next);
@@ -3173,6 +3208,24 @@ var init_opencode_config = __esm({
3173
3208
  }
3174
3209
  });
3175
3210
 
3211
+ // src/templates/hermes-soul.ts
3212
+ function renderHermesSoul(params2) {
3213
+ const body = renderCodexAgents(params2);
3214
+ return `# SOUL -- Syntaur Protocol Operator
3215
+
3216
+ This agent follows the Syntaur protocol for multi-agent project coordination.
3217
+ Hermes loads this file as part of its identity / system context; treat the
3218
+ Write Boundary Rules and Lifecycle sections below as binding.
3219
+
3220
+ ${body}`;
3221
+ }
3222
+ var init_hermes_soul = __esm({
3223
+ "src/templates/hermes-soul.ts"() {
3224
+ "use strict";
3225
+ init_codex_agents();
3226
+ }
3227
+ });
3228
+
3176
3229
  // src/templates/index.ts
3177
3230
  var init_templates = __esm({
3178
3231
  "src/templates/index.ts"() {
@@ -3193,6 +3246,7 @@ var init_templates = __esm({
3193
3246
  init_cursor_rules();
3194
3247
  init_codex_agents();
3195
3248
  init_opencode_config();
3249
+ init_hermes_soul();
3196
3250
  }
3197
3251
  });
3198
3252
 
@@ -3745,7 +3799,7 @@ __export(parser_exports, {
3745
3799
  writeChecklist: () => writeChecklist
3746
3800
  });
3747
3801
  import { randomBytes } from "crypto";
3748
- import { readFile as readFile5 } from "fs/promises";
3802
+ import { readFile as readFile6 } from "fs/promises";
3749
3803
  import { resolve as resolve8 } from "path";
3750
3804
  function generateShortId() {
3751
3805
  return randomBytes(2).toString("hex");
@@ -4042,7 +4096,7 @@ async function readChecklist(todosDir2, workspace) {
4042
4096
  if (!await fileExists(path)) {
4043
4097
  return { workspace, archiveInterval: "weekly", items: [] };
4044
4098
  }
4045
- const content = await readFile5(path, "utf-8");
4099
+ const content = await readFile6(path, "utf-8");
4046
4100
  return parseChecklist(content);
4047
4101
  }
4048
4102
  async function writeChecklist(todosDir2, checklist) {
@@ -4055,7 +4109,7 @@ async function readLog(todosDir2, workspace) {
4055
4109
  if (!await fileExists(path)) {
4056
4110
  return { workspace, entries: [] };
4057
4111
  }
4058
- const content = await readFile5(path, "utf-8");
4112
+ const content = await readFile6(path, "utf-8");
4059
4113
  return parseLog(content);
4060
4114
  }
4061
4115
  async function appendLogEntry(todosDir2, workspace, entry) {
@@ -4063,7 +4117,7 @@ async function appendLogEntry(todosDir2, workspace, entry) {
4063
4117
  const path = logPath(todosDir2, workspace);
4064
4118
  let content;
4065
4119
  if (await fileExists(path)) {
4066
- content = await readFile5(path, "utf-8");
4120
+ content = await readFile6(path, "utf-8");
4067
4121
  content = content.trimEnd() + "\n\n" + serializeLogEntry(entry) + "\n";
4068
4122
  } else {
4069
4123
  const fm = `---
@@ -4231,7 +4285,7 @@ var init_linked_todos = __esm({
4231
4285
 
4232
4286
  // src/lifecycle/transitions.ts
4233
4287
  import { resolve as resolve10 } from "path";
4234
- import { readFile as readFile6 } from "fs/promises";
4288
+ import { readFile as readFile7 } from "fs/promises";
4235
4289
  function linkedAssignmentRef(frontmatter) {
4236
4290
  return frontmatter.project ? `${frontmatter.project}/${frontmatter.slug}` : frontmatter.id;
4237
4291
  }
@@ -4251,7 +4305,7 @@ async function readAssignment(filePath) {
4251
4305
  if (!await fileExists(filePath)) {
4252
4306
  throw new Error(`Assignment file not found: ${filePath}`);
4253
4307
  }
4254
- const content = await readFile6(filePath, "utf-8");
4308
+ const content = await readFile7(filePath, "utf-8");
4255
4309
  const frontmatter = parseAssignmentFrontmatter(content);
4256
4310
  return { content, frontmatter };
4257
4311
  }
@@ -4264,7 +4318,7 @@ async function checkDependencies(projectDir, dependsOn, terminalStatuses3) {
4264
4318
  unmet.push(`${depSlug} (file not found)`);
4265
4319
  continue;
4266
4320
  }
4267
- const depContent = await readFile6(depPath, "utf-8");
4321
+ const depContent = await readFile7(depPath, "utf-8");
4268
4322
  const depFrontmatter = parseAssignmentFrontmatter(depContent);
4269
4323
  if (!terminals.has(depFrontmatter.status)) {
4270
4324
  unmet.push(`${depSlug} (status: ${depFrontmatter.status})`);
@@ -4417,7 +4471,7 @@ var init_lifecycle = __esm({
4417
4471
 
4418
4472
  // src/utils/assignment-resolver.ts
4419
4473
  import { resolve as resolve11 } from "path";
4420
- import { readdir as readdir5, readFile as readFile7 } from "fs/promises";
4474
+ import { readdir as readdir5, readFile as readFile8 } from "fs/promises";
4421
4475
  async function resolveAssignmentById(projectsDir2, assignmentsDir2, id) {
4422
4476
  let standaloneMatch = null;
4423
4477
  let projectMatch = null;
@@ -4426,7 +4480,7 @@ async function resolveAssignmentById(projectsDir2, assignmentsDir2, id) {
4426
4480
  if (await fileExists(standalonePath)) {
4427
4481
  let workspaceGroup = null;
4428
4482
  try {
4429
- const content = await readFile7(standalonePath, "utf-8");
4483
+ const content = await readFile8(standalonePath, "utf-8");
4430
4484
  const [fm] = extractFrontmatter(content);
4431
4485
  workspaceGroup = getField(fm, "workspaceGroup");
4432
4486
  } catch {
@@ -4454,7 +4508,7 @@ async function resolveAssignmentById(projectsDir2, assignmentsDir2, id) {
4454
4508
  const aPath = resolve11(assignmentsPath, a.name, "assignment.md");
4455
4509
  if (!await fileExists(aPath)) continue;
4456
4510
  try {
4457
- const content = await readFile7(aPath, "utf-8");
4511
+ const content = await readFile8(aPath, "utf-8");
4458
4512
  const [fm] = extractFrontmatter(content);
4459
4513
  const fileId = getField(fm, "id");
4460
4514
  if (fileId === id) {
@@ -5106,8 +5160,8 @@ async function migrateFromMarkdown(projectsDir2) {
5106
5160
  return allSessions.length;
5107
5161
  }
5108
5162
  async function parseMarkdownSessionsIndex(filePath, projectSlug) {
5109
- const { readFile: readFile54 } = await import("fs/promises");
5110
- const raw = await readFile54(filePath, "utf-8");
5163
+ const { readFile: readFile56 } = await import("fs/promises");
5164
+ const raw = await readFile56(filePath, "utf-8");
5111
5165
  const sessions = [];
5112
5166
  const lines = raw.split("\n");
5113
5167
  let inTable = false;
@@ -5177,7 +5231,7 @@ CREATE INDEX IF NOT EXISTS idx_sessions_assignment ON sessions(project_slug, ass
5177
5231
  });
5178
5232
 
5179
5233
  // src/dashboard/agent-sessions.ts
5180
- import { readFile as readFile8 } from "fs/promises";
5234
+ import { readFile as readFile9 } from "fs/promises";
5181
5235
  import { resolve as resolve13 } from "path";
5182
5236
  function rowToSession(row) {
5183
5237
  return {
@@ -5265,7 +5319,7 @@ async function deleteSessions(sessionIds) {
5265
5319
  }
5266
5320
  async function readAssignmentStatusFromPath(assignmentMdPath2) {
5267
5321
  if (!await fileExists(assignmentMdPath2)) return null;
5268
- const raw = await readFile8(assignmentMdPath2, "utf-8");
5322
+ const raw = await readFile9(assignmentMdPath2, "utf-8");
5269
5323
  const match = raw.match(/^status:\s*(.+)$/m);
5270
5324
  return match ? match[1].trim() : null;
5271
5325
  }
@@ -5350,7 +5404,7 @@ var init_overviewCopy = __esm({
5350
5404
  });
5351
5405
 
5352
5406
  // src/dashboard/servers.ts
5353
- import { readdir as readdir7, readFile as readFile9, unlink as unlink2 } from "fs/promises";
5407
+ import { readdir as readdir7, readFile as readFile10, unlink as unlink2 } from "fs/promises";
5354
5408
  import { resolve as resolve14 } from "path";
5355
5409
  function sanitizeSessionName(name) {
5356
5410
  return name.replace(/[^a-zA-Z0-9_-]/g, "-");
@@ -5410,7 +5464,7 @@ async function listSessionFiles(dir) {
5410
5464
  async function readSessionFile(dir, name) {
5411
5465
  const filePath = resolve14(dir, `${sanitizeSessionName(name)}.md`);
5412
5466
  if (!await fileExists(filePath)) return null;
5413
- const raw = await readFile9(filePath, "utf-8");
5467
+ const raw = await readFile10(filePath, "utf-8");
5414
5468
  const [frontmatter] = extractFrontmatter(raw);
5415
5469
  if (!frontmatter) return null;
5416
5470
  const session = getField(frontmatter, "session") ?? name;
@@ -5539,8 +5593,8 @@ function scanKey(serversDir2, projectsDir2, assignmentsDir2) {
5539
5593
  return `${serversDir2}\0${projectsDir2}\0${assignmentsDir2 ?? ""}`;
5540
5594
  }
5541
5595
  function delay(ms) {
5542
- return new Promise((resolve79) => {
5543
- const timer2 = setTimeout(resolve79, ms);
5596
+ return new Promise((resolve82) => {
5597
+ const timer2 = setTimeout(resolve82, ms);
5544
5598
  if (typeof timer2.unref === "function") {
5545
5599
  timer2.unref();
5546
5600
  }
@@ -5983,7 +6037,7 @@ var init_scanner = __esm({
5983
6037
  });
5984
6038
 
5985
6039
  // src/dashboard/api.ts
5986
- import { readdir as readdir8, readFile as readFile10, writeFile as writeFile3 } from "fs/promises";
6040
+ import { readdir as readdir8, readFile as readFile11, writeFile as writeFile3 } from "fs/promises";
5987
6041
  import { resolve as resolve16, dirname as dirname3, basename } from "path";
5988
6042
  function clearFrontmatterField(content, key) {
5989
6043
  const fieldRegex = new RegExp(`^(${escapeRegExp2(key)}:)\\s*.*$`, "m");
@@ -6071,7 +6125,7 @@ async function computeStandaloneRecords(assignmentsDir2) {
6071
6125
  const assignmentMdPath2 = resolve16(assignmentDir, "assignment.md");
6072
6126
  if (!await fileExists(assignmentMdPath2)) continue;
6073
6127
  try {
6074
- const content = await readFile10(assignmentMdPath2, "utf-8");
6128
+ const content = await readFile11(assignmentMdPath2, "utf-8");
6075
6129
  const record = parseAssignmentFull(content);
6076
6130
  records.push({ assignmentDir, id: entry.name, record });
6077
6131
  } catch {
@@ -6147,7 +6201,7 @@ async function listProjects(projectsDir2) {
6147
6201
  async function readWorkspaceRegistry(projectsDir2) {
6148
6202
  const registryPath = resolve16(dirname3(projectsDir2), "workspaces.json");
6149
6203
  try {
6150
- const raw = await readFile10(registryPath, "utf-8");
6204
+ const raw = await readFile11(registryPath, "utf-8");
6151
6205
  const parsed = JSON.parse(raw);
6152
6206
  return Array.isArray(parsed) ? parsed.filter((w) => typeof w === "string") : [];
6153
6207
  } catch {
@@ -6236,7 +6290,7 @@ async function deleteWorkspace(projectsDir2, name, opts = {}) {
6236
6290
  const timestamp = nowTimestamp();
6237
6291
  for (const slug of projectsReferencing) {
6238
6292
  const path = resolve16(projectsDir2, slug, "project.md");
6239
- const raw = await readFile10(path, "utf-8");
6293
+ const raw = await readFile11(path, "utf-8");
6240
6294
  let next = clearFrontmatterField(raw, "workspace");
6241
6295
  next = setUpdatedField(next, timestamp);
6242
6296
  await writeFileForce(path, next);
@@ -6245,7 +6299,7 @@ async function deleteWorkspace(projectsDir2, name, opts = {}) {
6245
6299
  for (const id of standalonesReferencing) {
6246
6300
  if (!opts.assignmentsDir) break;
6247
6301
  const path = resolve16(opts.assignmentsDir, id, "assignment.md");
6248
- const raw = await readFile10(path, "utf-8");
6302
+ const raw = await readFile11(path, "utf-8");
6249
6303
  let next = clearFrontmatterField(raw, "workspaceGroup");
6250
6304
  next = setUpdatedField(next, timestamp);
6251
6305
  await writeFileForce(path, next);
@@ -6482,7 +6536,7 @@ async function getEditableDocument(projectsDir2, documentType, projectSlug, assi
6482
6536
  if (!filePath || !await fileExists(filePath)) {
6483
6537
  return null;
6484
6538
  }
6485
- const content = await readFile10(filePath, "utf-8");
6539
+ const content = await readFile11(filePath, "utf-8");
6486
6540
  const title = getEditableDocumentTitle(documentType, projectSlug, assignmentSlug);
6487
6541
  return {
6488
6542
  documentType,
@@ -6508,7 +6562,7 @@ async function getEditableDocumentById(projectsDir2, assignmentsDir2, documentTy
6508
6562
  if (!fileName) return null;
6509
6563
  const filePath = resolve16(resolved.assignmentDir, fileName);
6510
6564
  if (!await fileExists(filePath)) return null;
6511
- const content = await readFile10(filePath, "utf-8");
6565
+ const content = await readFile11(filePath, "utf-8");
6512
6566
  const label = resolved.id;
6513
6567
  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}`;
6514
6568
  return {
@@ -6527,7 +6581,7 @@ async function getProjectDetail(projectsDir2, slug) {
6527
6581
  if (!await fileExists(projectMdPath)) {
6528
6582
  return null;
6529
6583
  }
6530
- const projectContent = await readFile10(projectMdPath, "utf-8");
6584
+ const projectContent = await readFile11(projectMdPath, "utf-8");
6531
6585
  const project = parseProject(projectContent);
6532
6586
  const assignments = await listAssignmentRecords(projectPath);
6533
6587
  const rollup = await buildProjectRollup(projectPath, project, assignments);
@@ -6564,18 +6618,18 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
6564
6618
  if (!await fileExists(assignmentMdPath2)) {
6565
6619
  return null;
6566
6620
  }
6567
- const assignmentContent = await readFile10(assignmentMdPath2, "utf-8");
6621
+ const assignmentContent = await readFile11(assignmentMdPath2, "utf-8");
6568
6622
  const assignment = parseAssignmentFull(assignmentContent);
6569
6623
  let projectWorkspace = null;
6570
6624
  const projectMdPath = resolve16(projectsDir2, projectSlug, "project.md");
6571
6625
  if (await fileExists(projectMdPath)) {
6572
- const projectContent = await readFile10(projectMdPath, "utf-8");
6626
+ const projectContent = await readFile11(projectMdPath, "utf-8");
6573
6627
  projectWorkspace = parseProject(projectContent).workspace;
6574
6628
  }
6575
6629
  let plan = null;
6576
6630
  const planPath = resolve16(assignmentDir, "plan.md");
6577
6631
  if (await fileExists(planPath)) {
6578
- const planContent = await readFile10(planPath, "utf-8");
6632
+ const planContent = await readFile11(planPath, "utf-8");
6579
6633
  const parsed = parsePlan(planContent);
6580
6634
  plan = {
6581
6635
  status: parsed.status,
@@ -6586,7 +6640,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
6586
6640
  let scratchpad = null;
6587
6641
  const scratchpadPath = resolve16(assignmentDir, "scratchpad.md");
6588
6642
  if (await fileExists(scratchpadPath)) {
6589
- const scratchpadContent = await readFile10(scratchpadPath, "utf-8");
6643
+ const scratchpadContent = await readFile11(scratchpadPath, "utf-8");
6590
6644
  const parsed = parseScratchpad(scratchpadContent);
6591
6645
  scratchpad = {
6592
6646
  updated: parsed.updated,
@@ -6596,7 +6650,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
6596
6650
  let handoff = null;
6597
6651
  const handoffPath = resolve16(assignmentDir, "handoff.md");
6598
6652
  if (await fileExists(handoffPath)) {
6599
- const handoffContent = await readFile10(handoffPath, "utf-8");
6653
+ const handoffContent = await readFile11(handoffPath, "utf-8");
6600
6654
  const parsed = parseHandoff(handoffContent);
6601
6655
  handoff = {
6602
6656
  updated: parsed.updated,
@@ -6607,7 +6661,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
6607
6661
  let decisionRecord = null;
6608
6662
  const decisionRecordPath = resolve16(assignmentDir, "decision-record.md");
6609
6663
  if (await fileExists(decisionRecordPath)) {
6610
- const decisionRecordContent = await readFile10(decisionRecordPath, "utf-8");
6664
+ const decisionRecordContent = await readFile11(decisionRecordPath, "utf-8");
6611
6665
  const parsed = parseDecisionRecord(decisionRecordContent);
6612
6666
  decisionRecord = {
6613
6667
  updated: parsed.updated,
@@ -6618,7 +6672,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
6618
6672
  let progress = null;
6619
6673
  const progressPath = resolve16(assignmentDir, "progress.md");
6620
6674
  if (await fileExists(progressPath)) {
6621
- const progressContent = await readFile10(progressPath, "utf-8");
6675
+ const progressContent = await readFile11(progressPath, "utf-8");
6622
6676
  const parsed = parseProgress(progressContent);
6623
6677
  progress = {
6624
6678
  updated: parsed.updated,
@@ -6629,7 +6683,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
6629
6683
  let comments = null;
6630
6684
  const commentsPath = resolve16(assignmentDir, "comments.md");
6631
6685
  if (await fileExists(commentsPath)) {
6632
- const commentsContent = await readFile10(commentsPath, "utf-8");
6686
+ const commentsContent = await readFile11(commentsPath, "utf-8");
6633
6687
  const parsed = parseComments(commentsContent);
6634
6688
  comments = {
6635
6689
  updated: parsed.updated,
@@ -6783,7 +6837,7 @@ async function countMentionsInAssignment(sourceDir, target) {
6783
6837
  const bodies = [];
6784
6838
  const assignmentMd = resolve16(sourceDir, "assignment.md");
6785
6839
  if (await fileExists(assignmentMd)) {
6786
- const content = await readFile10(assignmentMd, "utf-8");
6840
+ const content = await readFile11(assignmentMd, "utf-8");
6787
6841
  const todosMatch = content.match(/^## Todos\s*$([\s\S]*?)(?=^## |$(?![\r\n]))/m);
6788
6842
  if (todosMatch) bodies.push(todosMatch[1]);
6789
6843
  }
@@ -6791,7 +6845,7 @@ async function countMentionsInAssignment(sourceDir, target) {
6791
6845
  const path = resolve16(sourceDir, filename);
6792
6846
  if (await fileExists(path)) {
6793
6847
  try {
6794
- bodies.push(await readFile10(path, "utf-8"));
6848
+ bodies.push(await readFile11(path, "utf-8"));
6795
6849
  } catch {
6796
6850
  }
6797
6851
  }
@@ -6851,42 +6905,42 @@ async function buildStandaloneAssignmentDetail(resolved) {
6851
6905
  const assignmentDir = resolved.assignmentDir;
6852
6906
  const assignmentMdPath2 = resolve16(assignmentDir, "assignment.md");
6853
6907
  if (!await fileExists(assignmentMdPath2)) return null;
6854
- const assignmentContent = await readFile10(assignmentMdPath2, "utf-8");
6908
+ const assignmentContent = await readFile11(assignmentMdPath2, "utf-8");
6855
6909
  const assignment = parseAssignmentFull(assignmentContent);
6856
6910
  let plan = null;
6857
6911
  const planPath = resolve16(assignmentDir, "plan.md");
6858
6912
  if (await fileExists(planPath)) {
6859
- const parsed = parsePlan(await readFile10(planPath, "utf-8"));
6913
+ const parsed = parsePlan(await readFile11(planPath, "utf-8"));
6860
6914
  plan = { status: parsed.status, updated: parsed.updated, body: parsed.body };
6861
6915
  }
6862
6916
  let scratchpad = null;
6863
6917
  const scratchpadPath = resolve16(assignmentDir, "scratchpad.md");
6864
6918
  if (await fileExists(scratchpadPath)) {
6865
- const parsed = parseScratchpad(await readFile10(scratchpadPath, "utf-8"));
6919
+ const parsed = parseScratchpad(await readFile11(scratchpadPath, "utf-8"));
6866
6920
  scratchpad = { updated: parsed.updated, body: parsed.body };
6867
6921
  }
6868
6922
  let handoff = null;
6869
6923
  const handoffPath = resolve16(assignmentDir, "handoff.md");
6870
6924
  if (await fileExists(handoffPath)) {
6871
- const parsed = parseHandoff(await readFile10(handoffPath, "utf-8"));
6925
+ const parsed = parseHandoff(await readFile11(handoffPath, "utf-8"));
6872
6926
  handoff = { updated: parsed.updated, handoffCount: parsed.handoffCount, body: parsed.body };
6873
6927
  }
6874
6928
  let decisionRecord = null;
6875
6929
  const decisionRecordPath = resolve16(assignmentDir, "decision-record.md");
6876
6930
  if (await fileExists(decisionRecordPath)) {
6877
- const parsed = parseDecisionRecord(await readFile10(decisionRecordPath, "utf-8"));
6931
+ const parsed = parseDecisionRecord(await readFile11(decisionRecordPath, "utf-8"));
6878
6932
  decisionRecord = { updated: parsed.updated, decisionCount: parsed.decisionCount, body: parsed.body };
6879
6933
  }
6880
6934
  let progress = null;
6881
6935
  const progressPath = resolve16(assignmentDir, "progress.md");
6882
6936
  if (await fileExists(progressPath)) {
6883
- const parsed = parseProgress(await readFile10(progressPath, "utf-8"));
6937
+ const parsed = parseProgress(await readFile11(progressPath, "utf-8"));
6884
6938
  progress = { updated: parsed.updated, entryCount: parsed.entryCount, entries: parsed.entries };
6885
6939
  }
6886
6940
  let comments = null;
6887
6941
  const commentsPath = resolve16(assignmentDir, "comments.md");
6888
6942
  if (await fileExists(commentsPath)) {
6889
- const parsed = parseComments(await readFile10(commentsPath, "utf-8"));
6943
+ const parsed = parseComments(await readFile11(commentsPath, "utf-8"));
6890
6944
  comments = { updated: parsed.updated, entryCount: parsed.entryCount, entries: parsed.entries };
6891
6945
  }
6892
6946
  const detail = {
@@ -6952,7 +7006,7 @@ async function computeProjectRecords(projectsDir2, traces) {
6952
7006
  return null;
6953
7007
  }
6954
7008
  const t0 = traces ? performance.now() : 0;
6955
- const projectContent = await readFile10(projectMdPath, "utf-8");
7009
+ const projectContent = await readFile11(projectMdPath, "utf-8");
6956
7010
  const project = parseProject(projectContent);
6957
7011
  if (traces) accumulatePhase(traces, "parse-project-md", performance.now() - t0);
6958
7012
  const t1 = traces ? performance.now() : 0;
@@ -7006,7 +7060,7 @@ async function listAssignmentRecords(projectPath, traces) {
7006
7060
  return null;
7007
7061
  }
7008
7062
  const t0 = traces ? performance.now() : 0;
7009
- const content = await readFile10(assignmentMd, "utf-8");
7063
+ const content = await readFile11(assignmentMd, "utf-8");
7010
7064
  const parsed = parseAssignmentFull(content);
7011
7065
  if (traces) accumulatePhase(traces, "read-assignment-md", performance.now() - t0);
7012
7066
  return parsed;
@@ -7028,7 +7082,7 @@ async function listResources(projectPath) {
7028
7082
  continue;
7029
7083
  }
7030
7084
  const filePath = resolve16(resourcesDir, entry.name);
7031
- const content = await readFile10(filePath, "utf-8");
7085
+ const content = await readFile11(filePath, "utf-8");
7032
7086
  const parsed = parseResource(content);
7033
7087
  results.push({
7034
7088
  name: parsed.name,
@@ -7054,7 +7108,7 @@ async function listMemories(projectPath) {
7054
7108
  continue;
7055
7109
  }
7056
7110
  const filePath = resolve16(memoriesDir, entry.name);
7057
- const content = await readFile10(filePath, "utf-8");
7111
+ const content = await readFile11(filePath, "utf-8");
7058
7112
  const parsed = parseMemory(content);
7059
7113
  results.push({
7060
7114
  name: parsed.name,
@@ -7117,7 +7171,7 @@ async function getMemoryDetail(projectsDir2, projectSlug, itemSlug) {
7117
7171
  if (!projectRecord) return null;
7118
7172
  const filePath = resolve16(projectRecord.projectPath, "memories", `${itemSlug}.md`);
7119
7173
  if (!await fileExists(filePath)) return null;
7120
- const content = await readFile10(filePath, "utf-8");
7174
+ const content = await readFile11(filePath, "utf-8");
7121
7175
  const parsed = parseMemory(content);
7122
7176
  return {
7123
7177
  name: parsed.name,
@@ -7143,7 +7197,7 @@ async function getResourceDetail(projectsDir2, projectSlug, itemSlug) {
7143
7197
  if (!projectRecord) return null;
7144
7198
  const filePath = resolve16(projectRecord.projectPath, "resources", `${itemSlug}.md`);
7145
7199
  if (!await fileExists(filePath)) return null;
7146
- const content = await readFile10(filePath, "utf-8");
7200
+ const content = await readFile11(filePath, "utf-8");
7147
7201
  const parsed = parseResource(content);
7148
7202
  return {
7149
7203
  name: parsed.name,
@@ -7161,7 +7215,7 @@ async function getResourceDetail(projectsDir2, projectSlug, itemSlug) {
7161
7215
  async function loadDependencyGraph(projectPath, assignments) {
7162
7216
  const statusPath = resolve16(projectPath, "_status.md");
7163
7217
  if (await fileExists(statusPath)) {
7164
- const statusContent = await readFile10(statusPath, "utf-8");
7218
+ const statusContent = await readFile11(statusPath, "utf-8");
7165
7219
  const parsed = parseStatus(statusContent);
7166
7220
  const derivedGraph = extractMermaidGraph(parsed.body);
7167
7221
  if (derivedGraph) {
@@ -7328,7 +7382,7 @@ async function getUnmetDependencies(projectPath, dependsOn, terminalStatuses3, d
7328
7382
  unmet.push(`${dependency} (missing)`);
7329
7383
  continue;
7330
7384
  }
7331
- const content = await readFile10(dependencyPath, "utf-8");
7385
+ const content = await readFile11(dependencyPath, "utf-8");
7332
7386
  const parsed = parseAssignmentFull(content);
7333
7387
  if (!terminals.has(parsed.status)) {
7334
7388
  unmet.push(`${dependency} (${parsed.status})`);
@@ -7623,7 +7677,7 @@ async function countOpenQuestions(projectPath, assignmentSlug) {
7623
7677
  return 0;
7624
7678
  }
7625
7679
  try {
7626
- const content = await readFile10(commentsPath, "utf-8");
7680
+ const content = await readFile11(commentsPath, "utf-8");
7627
7681
  const parsed = parseComments(content);
7628
7682
  return parsed.entries.filter(
7629
7683
  (e) => e.type === "question" && e.resolved !== true
@@ -7696,7 +7750,7 @@ async function listPlaybooks(playbooksDir3) {
7696
7750
  for (const entry of entries) {
7697
7751
  if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name.startsWith("_") || entry.name === "manifest.md") continue;
7698
7752
  const filePath = resolve16(playbooksDir3, entry.name);
7699
- const raw = await readFile10(filePath, "utf-8");
7753
+ const raw = await readFile11(filePath, "utf-8");
7700
7754
  const parsed = parsePlaybook(raw);
7701
7755
  const slug = parsed.slug || entry.name.replace(/\.md$/, "");
7702
7756
  playbooks.push({
@@ -7881,7 +7935,7 @@ __export(git_worktree_exports, {
7881
7935
  removeWorktree: () => removeWorktree
7882
7936
  });
7883
7937
  import { spawn } from "child_process";
7884
- import { readFile as readFile13 } from "fs/promises";
7938
+ import { readFile as readFile14 } from "fs/promises";
7885
7939
  function run(command, args, cwd) {
7886
7940
  return new Promise((resolvePromise) => {
7887
7941
  const child = spawn(command, args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
@@ -7959,7 +8013,7 @@ async function createWorktreeAndRecord(opts) {
7959
8013
  const { assignmentPath, repository, branch, worktreePath, parentBranch } = opts;
7960
8014
  await createWorktree({ repository, branch, worktreePath, parentBranch });
7961
8015
  try {
7962
- const content = await readFile13(assignmentPath, "utf-8");
8016
+ const content = await readFile14(assignmentPath, "utf-8");
7963
8017
  const updated = updateAssignmentWorkspace(content, {
7964
8018
  repository,
7965
8019
  worktreePath,
@@ -8428,15 +8482,15 @@ import {
8428
8482
  unlinkSync
8429
8483
  } from "fs";
8430
8484
  import { fileURLToPath as fileURLToPath9, pathToFileURL } from "url";
8431
- import { dirname as dirname16, resolve as resolve47, join as join9 } from "path";
8432
- import { homedir as homedir8, tmpdir as tmpdir2 } from "os";
8433
- import { spawnSync as spawnSync6 } from "child_process";
8485
+ import { dirname as dirname16, resolve as resolve49, join as join9 } from "path";
8486
+ import { homedir as homedir9, tmpdir as tmpdir2 } from "os";
8487
+ import { spawnSync as spawnSync7 } from "child_process";
8434
8488
  function syntaurRootMjs() {
8435
8489
  const override = process.env.SYNTAUR_HOME;
8436
8490
  if (override && override.length > 0) {
8437
8491
  return override;
8438
8492
  }
8439
- return join9(homedir8(), ".syntaur");
8493
+ return join9(homedir9(), ".syntaur");
8440
8494
  }
8441
8495
  async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
8442
8496
  const { throwOnFailure } = options;
@@ -8450,7 +8504,7 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
8450
8504
  }
8451
8505
  const stateRoot = syntaurRootMjs();
8452
8506
  mkdirSync2(stateRoot, { recursive: true });
8453
- const lockPath = resolve47(stateRoot, "install-url-handler.lock");
8507
+ const lockPath = resolve49(stateRoot, "install-url-handler.lock");
8454
8508
  let lockFd;
8455
8509
  try {
8456
8510
  lockFd = openSync(lockPath, "wx");
@@ -8466,11 +8520,11 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
8466
8520
  throw err2;
8467
8521
  }
8468
8522
  try {
8469
- const pkgRoot = resolve47(dirname16(fileURLToPath9(import.meta.url)), "..");
8470
- const cliBin = realpathSync3(resolve47(pkgRoot, "bin/syntaur.js"));
8523
+ const pkgRoot = resolve49(dirname16(fileURLToPath9(import.meta.url)), "..");
8524
+ const cliBin = realpathSync3(resolve49(pkgRoot, "bin/syntaur.js"));
8471
8525
  const nodeBin = process.execPath;
8472
8526
  const bundleParent = join9(
8473
- homedir8(),
8527
+ homedir9(),
8474
8528
  "Library",
8475
8529
  "Application Support",
8476
8530
  "Syntaur"
@@ -8490,7 +8544,7 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
8490
8544
  renderAppleScript({ nodeBin, cliBin, installedTerminals }),
8491
8545
  "utf-8"
8492
8546
  );
8493
- const compile = spawnSync6(
8547
+ const compile = spawnSync7(
8494
8548
  OSACOMPILE,
8495
8549
  ["-o", bundlePath, scriptPath],
8496
8550
  { stdio: "pipe", encoding: "utf-8" }
@@ -8534,7 +8588,7 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
8534
8588
  ["Add :CFBundleURLTypes:0:CFBundleURLSchemes array", null],
8535
8589
  [`Add :CFBundleURLTypes:0:CFBundleURLSchemes:0 string ${URL_SCHEME}`, null]
8536
8590
  ]);
8537
- const sign = spawnSync6(
8591
+ const sign = spawnSync7(
8538
8592
  "/usr/bin/codesign",
8539
8593
  ["--force", "--deep", "--sign", "-", bundlePath],
8540
8594
  { stdio: "pipe", encoding: "utf-8" }
@@ -8546,7 +8600,7 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
8546
8600
  }
8547
8601
  console.warn(`syntaur: ${msg} \u2014 macOS may deny Automation permission.`);
8548
8602
  }
8549
- const ls = spawnSync6(LSREGISTER, ["-f", bundlePath], {
8603
+ const ls = spawnSync7(LSREGISTER, ["-f", bundlePath], {
8550
8604
  stdio: "pipe",
8551
8605
  encoding: "utf-8"
8552
8606
  });
@@ -8595,9 +8649,9 @@ function detectInstalledTerminals() {
8595
8649
  ghostty: "Ghostty.app",
8596
8650
  warp: "Warp.app"
8597
8651
  };
8598
- const appDirs = ["/Applications", join9(homedir8(), "Applications")];
8652
+ const appDirs = ["/Applications", join9(homedir9(), "Applications")];
8599
8653
  for (const [id, bundleId] of Object.entries(bundleIds)) {
8600
- const r = spawnSync6(
8654
+ const r = spawnSync7(
8601
8655
  "mdfind",
8602
8656
  [`kMDItemCFBundleIdentifier == '${bundleId}'`],
8603
8657
  { encoding: "utf-8" }
@@ -8757,13 +8811,13 @@ function runPlistBuddy(plistPath, steps) {
8757
8811
  for (const step of steps) {
8758
8812
  if (Array.isArray(step)) {
8759
8813
  const [primary, fallback] = step;
8760
- const a = spawnSync6("/usr/libexec/PlistBuddy", ["-c", primary, plistPath], {
8814
+ const a = spawnSync7("/usr/libexec/PlistBuddy", ["-c", primary, plistPath], {
8761
8815
  stdio: "pipe",
8762
8816
  encoding: "utf-8"
8763
8817
  });
8764
8818
  if (a.status === 0) continue;
8765
8819
  if (fallback === null) continue;
8766
- const b = spawnSync6("/usr/libexec/PlistBuddy", ["-c", fallback, plistPath], {
8820
+ const b = spawnSync7("/usr/libexec/PlistBuddy", ["-c", fallback, plistPath], {
8767
8821
  stdio: "pipe",
8768
8822
  encoding: "utf-8"
8769
8823
  });
@@ -8773,7 +8827,7 @@ function runPlistBuddy(plistPath, steps) {
8773
8827
  );
8774
8828
  }
8775
8829
  } else {
8776
- const r = spawnSync6("/usr/libexec/PlistBuddy", ["-c", step, plistPath], {
8830
+ const r = spawnSync7("/usr/libexec/PlistBuddy", ["-c", step, plistPath], {
8777
8831
  stdio: "pipe",
8778
8832
  encoding: "utf-8"
8779
8833
  });
@@ -9317,7 +9371,7 @@ init_fs();
9317
9371
  init_config();
9318
9372
  init_playbooks();
9319
9373
  import { resolve as resolve5, dirname as dirname2 } from "path";
9320
- import { readdir as readdir3, readFile as readFile4 } from "fs/promises";
9374
+ import { readdir as readdir3, readFile as readFile5 } from "fs/promises";
9321
9375
  import { fileURLToPath } from "url";
9322
9376
  async function initCommand(options) {
9323
9377
  const root = syntaurRoot();
@@ -9369,7 +9423,7 @@ async function seedDefaultPlaybooks(playbooksDir3) {
9369
9423
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
9370
9424
  const targetPath = resolve5(playbooksDir3, entry.name);
9371
9425
  if (await fileExists(targetPath)) continue;
9372
- const content = await readFile4(resolve5(examplesDir, entry.name), "utf-8");
9426
+ const content = await readFile5(resolve5(examplesDir, entry.name), "utf-8");
9373
9427
  await writeFileSafe(targetPath, content);
9374
9428
  count++;
9375
9429
  }
@@ -9931,7 +9985,7 @@ function mergePatch(current, patch) {
9931
9985
  // src/utils/view-prefs.ts
9932
9986
  init_paths();
9933
9987
  init_fs();
9934
- import { readFile as readFile11, rename as rename3, unlink as unlink3 } from "fs/promises";
9988
+ import { readFile as readFile12, rename as rename3, unlink as unlink3 } from "fs/promises";
9935
9989
  import { resolve as resolve17 } from "path";
9936
9990
  function corruptFilePath() {
9937
9991
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
@@ -9954,7 +10008,7 @@ async function readViewPrefsFile() {
9954
10008
  }
9955
10009
  let raw;
9956
10010
  try {
9957
- raw = await readFile11(path, "utf-8");
10011
+ raw = await readFile12(path, "utf-8");
9958
10012
  } catch {
9959
10013
  return { ...DEFAULT_VIEW_PREFS_FILE };
9960
10014
  }
@@ -10137,7 +10191,7 @@ function isDashboardSlot(value) {
10137
10191
  // src/utils/saved-views.ts
10138
10192
  init_paths();
10139
10193
  init_fs();
10140
- import { readFile as readFile12, rename as rename4, unlink as unlink4 } from "fs/promises";
10194
+ import { readFile as readFile13, rename as rename4, unlink as unlink4 } from "fs/promises";
10141
10195
  import { randomUUID as randomUUID2 } from "crypto";
10142
10196
  import { resolve as resolve18 } from "path";
10143
10197
  function corruptFilePath2() {
@@ -10167,7 +10221,7 @@ async function readSavedViewsFile() {
10167
10221
  }
10168
10222
  let raw;
10169
10223
  try {
10170
- raw = await readFile12(path, "utf-8");
10224
+ raw = await readFile13(path, "utf-8");
10171
10225
  } catch {
10172
10226
  return cloneDefault();
10173
10227
  }
@@ -10487,7 +10541,7 @@ init_fs();
10487
10541
  init_git_worktree();
10488
10542
  import { Router as Router2 } from "express";
10489
10543
  import { resolve as resolve21, basename as basename3, isAbsolute as isAbsolute2 } from "path";
10490
- import { rm, readFile as readFile15, open as fsOpen, stat as fsStat, realpath as fsRealpath } from "fs/promises";
10544
+ import { rm, readFile as readFile16, open as fsOpen, stat as fsStat, realpath as fsRealpath } from "fs/promises";
10491
10545
  import { spawnSync as spawnSync3 } from "child_process";
10492
10546
 
10493
10547
  // src/utils/worktree-defaults.ts
@@ -10563,7 +10617,7 @@ function validateBranchName(name) {
10563
10617
  // src/dashboard/repository-candidates.ts
10564
10618
  init_fs();
10565
10619
  init_parser();
10566
- import { readdir as readdir9, readFile as readFile14 } from "fs/promises";
10620
+ import { readdir as readdir9, readFile as readFile15 } from "fs/promises";
10567
10621
  import { resolve as resolve20 } from "path";
10568
10622
  function toSourceAssignment(parsed, fallbackId) {
10569
10623
  const repository = parsed.workspace.repository?.trim();
@@ -10583,7 +10637,7 @@ async function getProjectRepositoryCandidates(projectsDir2, projectSlug) {
10583
10637
  const out = [];
10584
10638
  const projectPath = resolve20(projectsDir2, projectSlug, "project.md");
10585
10639
  if (await fileExists(projectPath)) {
10586
- const project = parseProject(await readFile14(projectPath, "utf-8"));
10640
+ const project = parseProject(await readFile15(projectPath, "utf-8"));
10587
10641
  for (const raw of project.repositories) {
10588
10642
  const path = raw.trim();
10589
10643
  if (!path) continue;
@@ -10600,7 +10654,7 @@ async function getProjectRepositoryCandidates(projectsDir2, projectSlug) {
10600
10654
  if (!entry.isDirectory()) continue;
10601
10655
  const assignmentMd = resolve20(assignmentsDir2, entry.name, "assignment.md");
10602
10656
  if (!await fileExists(assignmentMd)) continue;
10603
- const parsed = parseAssignmentFull(await readFile14(assignmentMd, "utf-8"));
10657
+ const parsed = parseAssignmentFull(await readFile15(assignmentMd, "utf-8"));
10604
10658
  const repo = parsed.workspace.repository?.trim();
10605
10659
  if (!repo) continue;
10606
10660
  const abs = resolve20(repo);
@@ -10623,7 +10677,7 @@ async function getStandaloneRepositoryCandidates(assignmentsDir2, excludeAssignm
10623
10677
  if (entry.name === excludeAssignmentId) continue;
10624
10678
  const assignmentMd = resolve20(assignmentsDir2, entry.name, "assignment.md");
10625
10679
  if (!await fileExists(assignmentMd)) continue;
10626
- const parsed = parseAssignmentFull(await readFile14(assignmentMd, "utf-8"));
10680
+ const parsed = parseAssignmentFull(await readFile15(assignmentMd, "utf-8"));
10627
10681
  const repo = parsed.workspace.repository?.trim();
10628
10682
  if (!repo) continue;
10629
10683
  const abs = resolve20(repo);
@@ -10644,7 +10698,7 @@ async function getProjectSourceAssignments(projectsDir2, projectSlug, excludeSlu
10644
10698
  if (entry.name === excludeSlug) continue;
10645
10699
  const assignmentMd = resolve20(assignmentsDir2, entry.name, "assignment.md");
10646
10700
  if (!await fileExists(assignmentMd)) continue;
10647
- const parsed = parseAssignmentFull(await readFile14(assignmentMd, "utf-8"));
10701
+ const parsed = parseAssignmentFull(await readFile15(assignmentMd, "utf-8"));
10648
10702
  const source = toSourceAssignment(parsed, entry.name);
10649
10703
  if (!source) continue;
10650
10704
  if (seen.has(entry.name)) continue;
@@ -10663,7 +10717,7 @@ async function getStandaloneSourceAssignments(assignmentsDir2, excludeAssignment
10663
10717
  if (entry.name === excludeAssignmentId) continue;
10664
10718
  const assignmentMd = resolve20(assignmentsDir2, entry.name, "assignment.md");
10665
10719
  if (!await fileExists(assignmentMd)) continue;
10666
- const parsed = parseAssignmentFull(await readFile14(assignmentMd, "utf-8"));
10720
+ const parsed = parseAssignmentFull(await readFile15(assignmentMd, "utf-8"));
10667
10721
  const source = toSourceAssignment(parsed, entry.name);
10668
10722
  if (!source) continue;
10669
10723
  if (seen.has(entry.name)) continue;
@@ -10834,7 +10888,7 @@ async function readCurrentDocument(filePath) {
10834
10888
  if (!await fileExists(filePath)) {
10835
10889
  return null;
10836
10890
  }
10837
- return readFile15(filePath, "utf-8");
10891
+ return readFile16(filePath, "utf-8");
10838
10892
  }
10839
10893
  var worktreeInFlight = /* @__PURE__ */ new Set();
10840
10894
  async function assertRepoRoot(repoInput) {
@@ -10888,7 +10942,7 @@ async function handleWorktreeCreate(req, res, ctx) {
10888
10942
  }
10889
10943
  worktreeInFlight.add(ctx.assignmentPath);
10890
10944
  try {
10891
- const parsed = parseAssignmentFull(await readFile15(ctx.assignmentPath, "utf-8"));
10945
+ const parsed = parseAssignmentFull(await readFile16(ctx.assignmentPath, "utf-8"));
10892
10946
  if (parsed.workspace.worktreePath) {
10893
10947
  res.status(409).json({ error: "Worktree already configured for this assignment" });
10894
10948
  return;
@@ -11258,7 +11312,7 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
11258
11312
  }
11259
11313
  const nextContentRaw = requireContent(req, res);
11260
11314
  if (!nextContentRaw) return;
11261
- const currentContent = await readFile15(filePath, "utf-8");
11315
+ const currentContent = await readFile16(filePath, "utf-8");
11262
11316
  const frontmatterBlock = extractFrontmatterBlock(currentContent);
11263
11317
  if (!frontmatterBlock) {
11264
11318
  res.status(500).json({ error: `${kind} file is malformed (no frontmatter)` });
@@ -11709,7 +11763,7 @@ ${nextBody}${nextBody.endsWith("\n") ? "" : "\n"}`;
11709
11763
  let currentContent;
11710
11764
  let currentCount = 0;
11711
11765
  if (await fileExists(commentsPath)) {
11712
- currentContent = await readFile15(commentsPath, "utf-8");
11766
+ currentContent = await readFile16(commentsPath, "utf-8");
11713
11767
  const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
11714
11768
  if (countMatch) currentCount = parseInt(countMatch[1], 10);
11715
11769
  } else {
@@ -11766,7 +11820,7 @@ ${entry}`;
11766
11820
  res.status(400).json({ error: "resolved (boolean) is required" });
11767
11821
  return;
11768
11822
  }
11769
- const content = await readFile15(commentsPath, "utf-8");
11823
+ const content = await readFile16(commentsPath, "utf-8");
11770
11824
  const parsed = parseComments(content);
11771
11825
  const target = parsed.entries.find((e) => e.id === commentId);
11772
11826
  if (!target) {
@@ -11813,7 +11867,7 @@ ${entry}`;
11813
11867
  });
11814
11868
  return;
11815
11869
  }
11816
- let content = await readFile15(projectPath, "utf-8");
11870
+ let content = await readFile16(projectPath, "utf-8");
11817
11871
  content = setTopLevelField(content, "workspace", workspace ?? null);
11818
11872
  content = setTopLevelField(content, "updated", nowTimestamp());
11819
11873
  await writeFileForce(projectPath, content);
@@ -11850,7 +11904,7 @@ ${entry}`;
11850
11904
  return;
11851
11905
  }
11852
11906
  const assignmentPath = resolve21(resolved.assignmentDir, "assignment.md");
11853
- let content = await readFile15(assignmentPath, "utf-8");
11907
+ let content = await readFile16(assignmentPath, "utf-8");
11854
11908
  content = setTopLevelField(content, "workspaceGroup", workspaceGroup ?? null);
11855
11909
  content = setTopLevelField(content, "updated", nowTimestamp());
11856
11910
  await writeFileForce(assignmentPath, content);
@@ -12067,7 +12121,7 @@ ${entry}`;
12067
12121
  return;
12068
12122
  }
12069
12123
  const assignmentPath = resolve21(resolved.assignmentDir, "assignment.md");
12070
- const parsedForSlug = parseAssignmentFull(await readFile15(assignmentPath, "utf-8"));
12124
+ const parsedForSlug = parseAssignmentFull(await readFile16(assignmentPath, "utf-8"));
12071
12125
  const assignmentSlugForBranch = parsedForSlug.slug || resolved.id;
12072
12126
  await handleWorktreeCreate(req, res, {
12073
12127
  assignmentPath,
@@ -12096,7 +12150,7 @@ ${entry}`;
12096
12150
  res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}, or null to clear.` });
12097
12151
  return;
12098
12152
  }
12099
- let content = await readFile15(projectPath, "utf-8");
12153
+ let content = await readFile16(projectPath, "utf-8");
12100
12154
  content = setTopLevelField(content, "statusOverride", status ?? null);
12101
12155
  content = setTopLevelField(content, "updated", nowTimestamp());
12102
12156
  await writeFileForce(projectPath, content);
@@ -12129,7 +12183,7 @@ ${entry}`;
12129
12183
  res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}.` });
12130
12184
  return;
12131
12185
  }
12132
- let content = await readFile15(assignmentPath, "utf-8");
12186
+ let content = await readFile16(assignmentPath, "utf-8");
12133
12187
  content = setTopLevelField(content, "status", status);
12134
12188
  content = setTopLevelField(content, "updated", nowTimestamp());
12135
12189
  if (status !== "blocked") {
@@ -12155,7 +12209,7 @@ ${entry}`;
12155
12209
  res.status(404).json({ error: `Project "${projectSlug}" not found` });
12156
12210
  return;
12157
12211
  }
12158
- const content = await readFile15(projectPath, "utf-8");
12212
+ const content = await readFile16(projectPath, "utf-8");
12159
12213
  await writeFileForce(projectPath, applyArchiveFields(content, true, archiveReason(req.body)));
12160
12214
  const project = await getProjectDetail(projectsDir2, projectSlug);
12161
12215
  res.json({ project });
@@ -12172,7 +12226,7 @@ ${entry}`;
12172
12226
  res.status(404).json({ error: `Project "${projectSlug}" not found` });
12173
12227
  return;
12174
12228
  }
12175
- const content = await readFile15(projectPath, "utf-8");
12229
+ const content = await readFile16(projectPath, "utf-8");
12176
12230
  await writeFileForce(projectPath, applyArchiveFields(content, false, null));
12177
12231
  const project = await getProjectDetail(projectsDir2, projectSlug);
12178
12232
  res.json({ project });
@@ -12189,7 +12243,7 @@ ${entry}`;
12189
12243
  res.status(404).json({ error: "Assignment not found" });
12190
12244
  return;
12191
12245
  }
12192
- const content = await readFile15(assignmentPath, "utf-8");
12246
+ const content = await readFile16(assignmentPath, "utf-8");
12193
12247
  await writeFileForce(assignmentPath, applyArchiveFields(content, archived, archived ? archiveReason(req.body) : null));
12194
12248
  const assignment = await getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug);
12195
12249
  res.json({ assignment });
@@ -12222,7 +12276,7 @@ ${entry}`;
12222
12276
  return;
12223
12277
  }
12224
12278
  const assignmentPath = resolve21(resolved.assignmentDir, "assignment.md");
12225
- const content = await readFile15(assignmentPath, "utf-8");
12279
+ const content = await readFile16(assignmentPath, "utf-8");
12226
12280
  await writeFileForce(assignmentPath, applyArchiveFields(content, archived, archived ? archiveReason(req.body) : null));
12227
12281
  const assignment = await getAssignmentDetailById(projectsDir2, assignmentsDir2, id);
12228
12282
  res.json({ assignment });
@@ -12263,7 +12317,7 @@ ${entry}`;
12263
12317
  res.status(400).json({ error: validation.error });
12264
12318
  return;
12265
12319
  }
12266
- let content = await readFile15(assignmentPath, "utf-8");
12320
+ let content = await readFile16(assignmentPath, "utf-8");
12267
12321
  content = setTopLevelField(content, "assignee", validation.value);
12268
12322
  content = setTopLevelField(content, "updated", nowTimestamp());
12269
12323
  await writeFileForce(assignmentPath, content);
@@ -12294,7 +12348,7 @@ ${entry}`;
12294
12348
  res.status(400).json({ error: validation.error });
12295
12349
  return;
12296
12350
  }
12297
- let content = await readFile15(assignmentPath, "utf-8");
12351
+ let content = await readFile16(assignmentPath, "utf-8");
12298
12352
  content = setTopLevelField(content, "title", validation.value);
12299
12353
  content = setTopLevelField(content, "updated", nowTimestamp());
12300
12354
  await writeFileForce(assignmentPath, content);
@@ -12812,7 +12866,7 @@ ${entry}`;
12812
12866
  res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}.` });
12813
12867
  return;
12814
12868
  }
12815
- let content = await readFile15(assignmentPath, "utf-8");
12869
+ let content = await readFile16(assignmentPath, "utf-8");
12816
12870
  content = setTopLevelField(content, "status", status);
12817
12871
  content = setTopLevelField(content, "updated", nowTimestamp());
12818
12872
  if (status !== "blocked") {
@@ -12848,7 +12902,7 @@ ${entry}`;
12848
12902
  res.status(400).json({ error: validation.error });
12849
12903
  return;
12850
12904
  }
12851
- let content = await readFile15(assignmentPath, "utf-8");
12905
+ let content = await readFile16(assignmentPath, "utf-8");
12852
12906
  content = setTopLevelField(content, "assignee", validation.value);
12853
12907
  content = setTopLevelField(content, "updated", nowTimestamp());
12854
12908
  await writeFileForce(assignmentPath, content);
@@ -12881,7 +12935,7 @@ ${entry}`;
12881
12935
  res.status(400).json({ error: validation.error });
12882
12936
  return;
12883
12937
  }
12884
- let content = await readFile15(assignmentPath, "utf-8");
12938
+ let content = await readFile16(assignmentPath, "utf-8");
12885
12939
  content = setTopLevelField(content, "title", validation.value);
12886
12940
  content = setTopLevelField(content, "updated", nowTimestamp());
12887
12941
  await writeFileForce(assignmentPath, content);
@@ -13018,7 +13072,7 @@ async function appendCommentTo(assignmentDir, assignmentRef, req, res, reloadDet
13018
13072
  let currentContent;
13019
13073
  let currentCount = 0;
13020
13074
  if (await fileExists(commentsPath)) {
13021
- currentContent = await readFile15(commentsPath, "utf-8");
13075
+ currentContent = await readFile16(commentsPath, "utf-8");
13022
13076
  const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
13023
13077
  if (countMatch) currentCount = parseInt(countMatch[1], 10);
13024
13078
  } else {
@@ -13058,7 +13112,7 @@ async function toggleCommentResolvedAt(assignmentDir, commentId, req, res, reloa
13058
13112
  res.status(400).json({ error: "resolved (boolean) is required" });
13059
13113
  return;
13060
13114
  }
13061
- const content = await readFile15(commentsPath, "utf-8");
13115
+ const content = await readFile16(commentsPath, "utf-8");
13062
13116
  const parsed = parseComments(content);
13063
13117
  const target = parsed.entries.find((e) => e.id === commentId);
13064
13118
  if (!target) {
@@ -13844,7 +13898,7 @@ import { Router as Router8 } from "express";
13844
13898
 
13845
13899
  // src/utils/status-config-resolution.ts
13846
13900
  init_frontmatter();
13847
- import { readFile as readFile16, writeFile as writeFile4, rm as rm2 } from "fs/promises";
13901
+ import { readFile as readFile17, writeFile as writeFile4, rm as rm2 } from "fs/promises";
13848
13902
  import { dirname as dirname5 } from "path";
13849
13903
 
13850
13904
  // src/utils/assignment-walk.ts
@@ -13927,7 +13981,7 @@ async function scanAssignmentsByStatus(projectsDir2, standaloneDir, ids) {
13927
13981
  const assignmentPath = `${entry.assignmentDir}/assignment.md`;
13928
13982
  let content;
13929
13983
  try {
13930
- content = await readFile16(assignmentPath, "utf-8");
13984
+ content = await readFile17(assignmentPath, "utf-8");
13931
13985
  } catch (err2) {
13932
13986
  const code = err2?.code;
13933
13987
  if (code === "ENOENT") {
@@ -13990,7 +14044,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
13990
14044
  const list = affected.get(r.id) ?? [];
13991
14045
  for (const a of list) {
13992
14046
  try {
13993
- const content = await readFile16(a.path, "utf-8");
14047
+ const content = await readFile17(a.path, "utf-8");
13994
14048
  buffer.set(a.path, content);
13995
14049
  } catch (err2) {
13996
14050
  const code = err2?.code;
@@ -14018,7 +14072,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
14018
14072
  for (const a of list) {
14019
14073
  let current;
14020
14074
  try {
14021
- current = await readFile16(a.path, "utf-8");
14075
+ current = await readFile17(a.path, "utf-8");
14022
14076
  } catch (err2) {
14023
14077
  const code = err2?.code;
14024
14078
  if (code === "ENOENT") {
@@ -14067,7 +14121,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
14067
14121
  const list = affected.get(r.id) ?? [];
14068
14122
  for (const a of list) {
14069
14123
  try {
14070
- const current = await readFile16(a.path, "utf-8");
14124
+ const current = await readFile17(a.path, "utf-8");
14071
14125
  const fm = parseAssignmentFrontmatter(current);
14072
14126
  if (fm.status !== r.id) {
14073
14127
  console.warn(
@@ -15456,7 +15510,7 @@ init_playbook();
15456
15510
  init_playbooks();
15457
15511
  import { Router as Router11 } from "express";
15458
15512
  import { resolve as resolve26 } from "path";
15459
- import { readFile as readFile17 } from "fs/promises";
15513
+ import { readFile as readFile18 } from "fs/promises";
15460
15514
  function statusForPlaybookError(code) {
15461
15515
  switch (code) {
15462
15516
  case "manifest":
@@ -15538,7 +15592,7 @@ function createPlaybooksRouter(playbooksDir3) {
15538
15592
  return;
15539
15593
  }
15540
15594
  const filePath = resolve26(playbooksDir3, resolved.filename);
15541
- const content = await readFile17(filePath, "utf-8");
15595
+ const content = await readFile18(filePath, "utf-8");
15542
15596
  res.json({
15543
15597
  documentType: "playbook",
15544
15598
  title: `Edit Playbook: ${resolved.slug}`,
@@ -15889,8 +15943,8 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
15889
15943
  router.post("/:workspace/archive", async (req, res) => {
15890
15944
  try {
15891
15945
  const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
15892
- const { resolve: resolve79 } = await import("path");
15893
- const { readFile: readFile54 } = await import("fs/promises");
15946
+ const { resolve: resolve82 } = await import("path");
15947
+ const { readFile: readFile56 } = await import("fs/promises");
15894
15948
  const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
15895
15949
  const workspace = getWorkspaceParam(req.params.workspace);
15896
15950
  const checklist = await readChecklist(todosDir2, workspace);
@@ -15906,10 +15960,10 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
15906
15960
  (e) => e.itemIds.every((id) => completedIds.has(id))
15907
15961
  );
15908
15962
  const archFile = archivePath2(todosDir2, workspace, checklist.archiveInterval);
15909
- await ensureDir(resolve79(todosDir2, "archive"));
15963
+ await ensureDir(resolve82(todosDir2, "archive"));
15910
15964
  let archContent = "";
15911
15965
  if (await fileExists(archFile)) {
15912
- archContent = await readFile54(archFile, "utf-8");
15966
+ archContent = await readFile56(archFile, "utf-8");
15913
15967
  archContent = archContent.trimEnd() + "\n\n";
15914
15968
  } else {
15915
15969
  archContent = `---
@@ -16191,7 +16245,7 @@ workspace: ${workspace}
16191
16245
  const { readConfig: readConfig3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
16192
16246
  const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
16193
16247
  const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
16194
- const { readFile: readFile54 } = await import("fs/promises");
16248
+ const { readFile: readFile56 } = await import("fs/promises");
16195
16249
  const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
16196
16250
  const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
16197
16251
  let assignmentRef;
@@ -16212,7 +16266,7 @@ workspace: ${workspace}
16212
16266
  }
16213
16267
  const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
16214
16268
  if (!await fileExists2(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
16215
- let content = await readFile54(assignmentMdPath2, "utf-8");
16269
+ let content = await readFile56(assignmentMdPath2, "utf-8");
16216
16270
  content = appendTodosToAssignmentBody2(
16217
16271
  content,
16218
16272
  items.map((it) => ({
@@ -16400,7 +16454,7 @@ init_fs();
16400
16454
  init_paths();
16401
16455
  init_slug();
16402
16456
  import { Router as Router13 } from "express";
16403
- import { mkdir as mkdir3, readFile as readFile18, rename as rename6 } from "fs/promises";
16457
+ import { mkdir as mkdir3, readFile as readFile19, rename as rename6 } from "fs/promises";
16404
16458
  import { resolve as resolve27, dirname as dirname7 } from "path";
16405
16459
  init_promote_todos();
16406
16460
  init_api();
@@ -16625,7 +16679,7 @@ function createProjectTodosRouter(projectsDir2, broadcast, workspaceTodosDir) {
16625
16679
  const archFile = archivePath(todosDir2, slug, checklist.archiveInterval);
16626
16680
  let archContent = "";
16627
16681
  if (await fileExists(archFile)) {
16628
- archContent = await readFile18(archFile, "utf-8");
16682
+ archContent = await readFile19(archFile, "utf-8");
16629
16683
  archContent = archContent.trimEnd() + "\n\n";
16630
16684
  } else {
16631
16685
  archContent = `---
@@ -17082,7 +17136,7 @@ workspace: ${slug}
17082
17136
  }
17083
17137
  const assignmentMdPath2 = resolve27(assignmentDir, "assignment.md");
17084
17138
  if (!await fileExists(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
17085
- let content = await readFile18(assignmentMdPath2, "utf-8");
17139
+ let content = await readFile19(assignmentMdPath2, "utf-8");
17086
17140
  content = appendTodosToAssignmentBody2(
17087
17141
  content,
17088
17142
  items.map((it) => ({
@@ -17286,7 +17340,7 @@ init_fs();
17286
17340
  init_paths();
17287
17341
  init_parser2();
17288
17342
  import { randomBytes as randomBytes2 } from "crypto";
17289
- import { readFile as readFile19 } from "fs/promises";
17343
+ import { readFile as readFile20 } from "fs/promises";
17290
17344
  var BUNDLE_ID_REGEX = /^[a-f0-9]{4}$/;
17291
17345
  var SCOPE_VALUES = /* @__PURE__ */ new Set(["workspace", "project", "global"]);
17292
17346
  var SCOPE_ID_REGEX = /^[a-z0-9_][a-z0-9_-]*$/;
@@ -17389,7 +17443,7 @@ ${lines}
17389
17443
  async function readBundles(todosDir2) {
17390
17444
  const path = bundlesPath(todosDir2);
17391
17445
  if (!await fileExists(path)) return [];
17392
- const content = await readFile19(path, "utf-8");
17446
+ const content = await readFile20(path, "utf-8");
17393
17447
  return parseBundles(content).bundles;
17394
17448
  }
17395
17449
  async function writeBundles(todosDir2, bundles) {
@@ -17566,7 +17620,7 @@ init_fs();
17566
17620
  init_config2();
17567
17621
  import { execFile as execFile2 } from "child_process";
17568
17622
  import { promisify as promisify2 } from "util";
17569
- 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";
17623
+ 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";
17570
17624
  import { resolve as resolve29, join as join3 } from "path";
17571
17625
  import { tmpdir } from "os";
17572
17626
  var exec2 = promisify2(execFile2);
@@ -17627,7 +17681,7 @@ async function acquireLock() {
17627
17681
  return lockPath;
17628
17682
  } catch (err2) {
17629
17683
  if (err2.code === "EEXIST") {
17630
- const pid = await readFile20(lockPath, "utf-8").catch(() => "");
17684
+ const pid = await readFile21(lockPath, "utf-8").catch(() => "");
17631
17685
  throw new Error(
17632
17686
  `Backup operation already in progress (lock file at ${lockPath}, pid ${pid.trim() || "unknown"}). If stale, delete the file and retry.`
17633
17687
  );
@@ -17674,7 +17728,7 @@ function resolveCategoriesStrict(csv) {
17674
17728
  return parseCategoriesStrict(parts);
17675
17729
  }
17676
17730
  async function readSanitizedConfig(configPath) {
17677
- const content = await readFile20(configPath, "utf-8");
17731
+ const content = await readFile21(configPath, "utf-8");
17678
17732
  return content.replace(/^(\s*lastBackup:\s*).*$/m, "$1null").replace(/^(\s*lastRestore:\s*).*$/m, "$1null");
17679
17733
  }
17680
17734
  async function backupToGithub(overrides) {
@@ -19066,7 +19120,7 @@ init_frontmatter();
19066
19120
  init_timestamp();
19067
19121
  init_assignment_resolver();
19068
19122
  import { resolve as resolve33 } from "path";
19069
- import { readFile as readFile21, writeFile as writeFile7 } from "fs/promises";
19123
+ import { readFile as readFile22, writeFile as writeFile7 } from "fs/promises";
19070
19124
  async function resolveTarget(target, options) {
19071
19125
  const config = await readConfig();
19072
19126
  const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
@@ -19098,7 +19152,7 @@ async function resolveTarget(target, options) {
19098
19152
  return null;
19099
19153
  }
19100
19154
  async function writeArchiveState(filePath, archived, reason) {
19101
- const content = await readFile21(filePath, "utf-8");
19155
+ const content = await readFile22(filePath, "utf-8");
19102
19156
  const updated = updateAssignmentFile(content, {
19103
19157
  archived,
19104
19158
  archivedAt: archived ? nowTimestamp() : null,
@@ -19167,7 +19221,7 @@ init_config2();
19167
19221
  init_frontmatter();
19168
19222
  init_timestamp();
19169
19223
  import { resolve as resolve34 } from "path";
19170
- import { readdir as readdir13, readFile as readFile22 } from "fs/promises";
19224
+ import { readdir as readdir13, readFile as readFile23 } from "fs/promises";
19171
19225
  var PROMOTABLE_STATUSES = /* @__PURE__ */ new Set(["pending"]);
19172
19226
  function objectiveIsFleshedOut(content) {
19173
19227
  const match = content.match(/##\s+Objective\s*\n([\s\S]*?)(?=\n##\s+|$)/);
@@ -19193,7 +19247,7 @@ async function collectCandidates(baseDirs) {
19193
19247
  if (await fileExists(directAssignmentMd)) {
19194
19248
  const fm = await parseSafe(directAssignmentMd);
19195
19249
  if (fm && PROMOTABLE_STATUSES.has(fm.status)) {
19196
- const content = await readFile22(directAssignmentMd, "utf-8");
19250
+ const content = await readFile23(directAssignmentMd, "utf-8");
19197
19251
  if (objectiveIsFleshedOut(content) && hasAcceptanceCriteria(content)) {
19198
19252
  candidates.push({
19199
19253
  projectSlug: null,
@@ -19216,7 +19270,7 @@ async function collectCandidates(baseDirs) {
19216
19270
  if (!await fileExists(assignmentMd)) continue;
19217
19271
  const fm = await parseSafe(assignmentMd);
19218
19272
  if (!fm || !PROMOTABLE_STATUSES.has(fm.status)) continue;
19219
- const content = await readFile22(assignmentMd, "utf-8");
19273
+ const content = await readFile23(assignmentMd, "utf-8");
19220
19274
  if (!objectiveIsFleshedOut(content)) continue;
19221
19275
  if (!hasAcceptanceCriteria(content)) continue;
19222
19276
  candidates.push({
@@ -19233,7 +19287,7 @@ async function collectCandidates(baseDirs) {
19233
19287
  }
19234
19288
  async function parseSafe(path) {
19235
19289
  try {
19236
- const content = await readFile22(path, "utf-8");
19290
+ const content = await readFile23(path, "utf-8");
19237
19291
  return parseAssignmentFrontmatter(content);
19238
19292
  } catch {
19239
19293
  return null;
@@ -19262,7 +19316,7 @@ async function migrateStatusesCommand(options) {
19262
19316
  const now = nowTimestamp();
19263
19317
  let migrated = 0;
19264
19318
  for (const c2 of candidates) {
19265
- const content = await readFile22(c2.assignmentMd, "utf-8");
19319
+ const content = await readFile23(c2.assignmentMd, "utf-8");
19266
19320
  const updated = updateAssignmentFile(content, {
19267
19321
  status: c2.toStatus,
19268
19322
  updated: now
@@ -19320,7 +19374,7 @@ import {
19320
19374
  readdir as readdir14,
19321
19375
  symlink,
19322
19376
  lstat,
19323
- readFile as readFile23,
19377
+ readFile as readFile24,
19324
19378
  readlink,
19325
19379
  rename as rename8,
19326
19380
  rm as rm4,
@@ -19362,8 +19416,8 @@ function getPluginManifestRelativePath(pluginKind) {
19362
19416
  return pluginKind === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json";
19363
19417
  }
19364
19418
  function getDefaultPluginTargetDir(pluginKind) {
19365
- const home = homedir3();
19366
- return pluginKind === "claude" ? resolve36(home, ".claude", "plugins", "syntaur") : resolve36(home, "plugins", "syntaur");
19419
+ const home2 = homedir3();
19420
+ return pluginKind === "claude" ? resolve36(home2, ".claude", "plugins", "syntaur") : resolve36(home2, "plugins", "syntaur");
19367
19421
  }
19368
19422
  function getDefaultMarketplacePath() {
19369
19423
  return resolve36(homedir3(), ".agents", "plugins", "marketplace.json");
@@ -19381,7 +19435,7 @@ function getInstallMarkerPath(targetDir) {
19381
19435
  return resolve36(targetDir, INSTALL_MARKER_FILENAME);
19382
19436
  }
19383
19437
  async function readPackageManifest(packageRoot) {
19384
- const raw = await readFile23(resolve36(packageRoot, "package.json"), "utf-8");
19438
+ const raw = await readFile24(resolve36(packageRoot, "package.json"), "utf-8");
19385
19439
  return JSON.parse(raw);
19386
19440
  }
19387
19441
  async function readJsonFileIfExists(pathValue) {
@@ -19389,7 +19443,7 @@ async function readJsonFileIfExists(pathValue) {
19389
19443
  return null;
19390
19444
  }
19391
19445
  try {
19392
- const raw = await readFile23(pathValue, "utf-8");
19446
+ const raw = await readFile24(pathValue, "utf-8");
19393
19447
  return JSON.parse(raw);
19394
19448
  } catch {
19395
19449
  return null;
@@ -19405,7 +19459,7 @@ async function readPluginManifestName(targetDir, pluginKind) {
19405
19459
  if (!await fileExists(manifestPath)) {
19406
19460
  return void 0;
19407
19461
  }
19408
- const raw = await readFile23(manifestPath, "utf-8");
19462
+ const raw = await readFile24(manifestPath, "utf-8");
19409
19463
  const parsed = JSON.parse(raw);
19410
19464
  return parsed.name;
19411
19465
  }
@@ -19415,7 +19469,7 @@ async function readInstallMetadata(targetDir) {
19415
19469
  return null;
19416
19470
  }
19417
19471
  try {
19418
- const raw = await readFile23(markerPath, "utf-8");
19472
+ const raw = await readFile24(markerPath, "utf-8");
19419
19473
  return JSON.parse(raw);
19420
19474
  } catch {
19421
19475
  return null;
@@ -19538,7 +19592,7 @@ async function writeClaudeMarketplaceFile(manifestPath, marketplace) {
19538
19592
  }
19539
19593
  if (await fileExists(manifestPath)) {
19540
19594
  try {
19541
- const prev = await readFile23(manifestPath, "utf-8");
19595
+ const prev = await readFile24(manifestPath, "utf-8");
19542
19596
  JSON.parse(prev);
19543
19597
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
19544
19598
  await writeFile8(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
@@ -19686,7 +19740,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
19686
19740
  const manifestPath = getClaudeKnownMarketplacesPath();
19687
19741
  let existing = {};
19688
19742
  if (await fileExists(manifestPath)) {
19689
- const raw = await readFile23(manifestPath, "utf-8");
19743
+ const raw = await readFile24(manifestPath, "utf-8");
19690
19744
  try {
19691
19745
  const parsed = JSON.parse(raw);
19692
19746
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
@@ -19713,7 +19767,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
19713
19767
  existing[name].autoUpdate = true;
19714
19768
  await ensureDir(dirname10(manifestPath));
19715
19769
  if (await fileExists(manifestPath)) {
19716
- const prev = await readFile23(manifestPath, "utf-8");
19770
+ const prev = await readFile24(manifestPath, "utf-8");
19717
19771
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
19718
19772
  await writeFile8(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
19719
19773
  }
@@ -19731,7 +19785,7 @@ async function setSyntaurPluginEnabled(options) {
19731
19785
  const key = `syntaur@${options.marketplaceName}`;
19732
19786
  let parsed = {};
19733
19787
  if (await fileExists(settingsPath)) {
19734
- const raw = await readFile23(settingsPath, "utf-8");
19788
+ const raw = await readFile24(settingsPath, "utf-8");
19735
19789
  try {
19736
19790
  parsed = JSON.parse(raw);
19737
19791
  } catch {
@@ -19750,7 +19804,7 @@ async function setSyntaurPluginEnabled(options) {
19750
19804
  await ensureDir(dirname10(settingsPath));
19751
19805
  if (await fileExists(settingsPath)) {
19752
19806
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
19753
- const prev = await readFile23(settingsPath, "utf-8");
19807
+ const prev = await readFile24(settingsPath, "utf-8");
19754
19808
  await writeFile8(`${settingsPath}.bak-${stamp}`, prev, "utf-8");
19755
19809
  }
19756
19810
  const tmpPath = `${settingsPath}.tmp`;
@@ -19988,7 +20042,7 @@ async function readMarketplaceFile(marketplacePath) {
19988
20042
  plugins: []
19989
20043
  };
19990
20044
  }
19991
- const raw = await readFile23(marketplacePath, "utf-8");
20045
+ const raw = await readFile24(marketplacePath, "utf-8");
19992
20046
  const parsed = JSON.parse(raw);
19993
20047
  return {
19994
20048
  name: parsed.name ?? "local",
@@ -20224,13 +20278,13 @@ async function textPrompt(question, defaultValue) {
20224
20278
 
20225
20279
  // src/utils/install-skills.ts
20226
20280
  init_fs();
20227
- import { readFile as readFile25, readdir as readdir15, mkdir as mkdir4, copyFile, rm as rm5, lstat as lstat2 } from "fs/promises";
20281
+ import { readFile as readFile26, readdir as readdir15, mkdir as mkdir4, copyFile, rm as rm5, lstat as lstat2 } from "fs/promises";
20228
20282
  import { resolve as resolve38, relative as relative3, join as join4 } from "path";
20229
20283
  import { homedir as homedir5 } from "os";
20230
20284
 
20231
20285
  // src/utils/plugin-state.ts
20232
20286
  init_fs();
20233
- import { readFile as readFile24 } from "fs/promises";
20287
+ import { readFile as readFile25 } from "fs/promises";
20234
20288
  import { resolve as resolve37 } from "path";
20235
20289
  import { homedir as homedir4 } from "os";
20236
20290
  function settingsPathFor(agent) {
@@ -20241,7 +20295,7 @@ async function readJsonOrNull(path) {
20241
20295
  if (!path) return null;
20242
20296
  if (!await fileExists(path)) return null;
20243
20297
  try {
20244
- const raw = await readFile24(path, "utf-8");
20298
+ const raw = await readFile25(path, "utf-8");
20245
20299
  return JSON.parse(raw);
20246
20300
  } catch {
20247
20301
  return null;
@@ -20315,7 +20369,7 @@ async function walkFiles(root) {
20315
20369
  }
20316
20370
  async function filesEqual(a, b) {
20317
20371
  try {
20318
- const [ba, bb] = await Promise.all([readFile25(a), readFile25(b)]);
20372
+ const [ba, bb] = await Promise.all([readFile26(a), readFile26(b)]);
20319
20373
  if (ba.length !== bb.length) return false;
20320
20374
  return ba.equals(bb);
20321
20375
  } catch {
@@ -20416,6 +20470,24 @@ async function installSkillsWithReport(options) {
20416
20470
  }
20417
20471
  return { results };
20418
20472
  }
20473
+ async function installSkillsToDir(options) {
20474
+ const source = options.sourceDir ?? await getSkillsDir();
20475
+ if (!await fileExists(source)) {
20476
+ throw new Error(
20477
+ `Syntaur skills not found at ${source}. Reinstall syntaur: npm install -g syntaur@latest`
20478
+ );
20479
+ }
20480
+ const force = options.force ?? false;
20481
+ const skillNames = await discoverSkillNames(source);
20482
+ await mkdir4(options.targetDir, { recursive: true });
20483
+ const results = [];
20484
+ for (const skill of skillNames) {
20485
+ const srcDir = join4(source, skill);
20486
+ const destDir = join4(options.targetDir, skill);
20487
+ results.push(await installSkillDir(srcDir, destDir, skill, force));
20488
+ }
20489
+ return results;
20490
+ }
20419
20491
  async function uninstallSkills(options) {
20420
20492
  const targetRoot = options.targetDir ?? defaultSkillTargetDir(options.target);
20421
20493
  if (!await fileExists(targetRoot)) return [];
@@ -20437,7 +20509,7 @@ async function uninstallSkills(options) {
20437
20509
  if (await isSymlink(destDir)) continue;
20438
20510
  const skillMd = join4(destDir, "SKILL.md");
20439
20511
  if (!await fileExists(skillMd)) continue;
20440
- const content = await readFile25(skillMd, "utf-8").catch(() => "");
20512
+ const content = await readFile26(skillMd, "utf-8").catch(() => "");
20441
20513
  const match = content.match(/^name:\s*(\S+)\s*$/m);
20442
20514
  if (!match || match[1] !== skill) continue;
20443
20515
  await rm5(destDir, { recursive: true, force: true });
@@ -20965,7 +21037,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
20965
21037
  `Spawn failed: ${msg}. Verify the terminal is installed and on PATH.`
20966
21038
  );
20967
21039
  }
20968
- await new Promise((resolve79, reject) => {
21040
+ await new Promise((resolve82, reject) => {
20969
21041
  let settled = false;
20970
21042
  let stderr = "";
20971
21043
  const finishOk = () => {
@@ -20975,7 +21047,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
20975
21047
  child.unref();
20976
21048
  } catch {
20977
21049
  }
20978
- resolve79();
21050
+ resolve82();
20979
21051
  };
20980
21052
  const finishErr = (remediation) => {
20981
21053
  if (settled) return;
@@ -21130,7 +21202,7 @@ function normalizeSlashes(p) {
21130
21202
  }
21131
21203
  function detectInstallKind(scriptUrl, opts = {}) {
21132
21204
  const realpath3 = opts.realpath ?? realpathSync.native;
21133
- const readFile54 = opts.readFile ?? ((p) => readFileSync(p, "utf-8"));
21205
+ const readFile56 = opts.readFile ?? ((p) => readFileSync(p, "utf-8"));
21134
21206
  const ua = opts.envUserAgent !== void 0 ? opts.envUserAgent : process.env.npm_config_user_agent ?? "";
21135
21207
  const resolved = resolveScriptPath(scriptUrl, realpath3);
21136
21208
  if (resolved === null) {
@@ -21151,7 +21223,7 @@ function detectInstallKind(scriptUrl, opts = {}) {
21151
21223
  const pkgJsonPath = join5(dir, "package.json");
21152
21224
  let raw;
21153
21225
  try {
21154
- raw = readFile54(pkgJsonPath);
21226
+ raw = readFile56(pkgJsonPath);
21155
21227
  } catch {
21156
21228
  const parent2 = dirname11(dir);
21157
21229
  if (parent2 === dir) break;
@@ -21236,20 +21308,20 @@ async function maybeNudgeForNpxInstall(scriptUrl) {
21236
21308
  init_paths();
21237
21309
  init_fs();
21238
21310
  import { fileURLToPath as fileURLToPath6 } from "url";
21239
- import { readFile as readFile27 } from "fs/promises";
21311
+ import { readFile as readFile28 } from "fs/promises";
21240
21312
  import { dirname as dirname13, join as join7, resolve as resolve41 } from "path";
21241
21313
  import { spawn as spawn5 } from "child_process";
21242
21314
  import { createInterface as createInterface2 } from "readline/promises";
21243
21315
 
21244
21316
  // src/utils/version.ts
21245
21317
  import { fileURLToPath as fileURLToPath5 } from "url";
21246
- import { readFile as readFile26 } from "fs/promises";
21318
+ import { readFile as readFile27 } from "fs/promises";
21247
21319
  import { dirname as dirname12, join as join6 } from "path";
21248
21320
  async function readPackageVersion(scriptUrl) {
21249
21321
  try {
21250
21322
  const scriptPath = fileURLToPath5(scriptUrl);
21251
21323
  const pkgRoot = dirname12(dirname12(scriptPath));
21252
- const raw = await readFile26(join6(pkgRoot, "package.json"), "utf-8");
21324
+ const raw = await readFile27(join6(pkgRoot, "package.json"), "utf-8");
21253
21325
  const parsed = JSON.parse(raw);
21254
21326
  return typeof parsed.version === "string" ? parsed.version : null;
21255
21327
  } catch {
@@ -21279,7 +21351,7 @@ function isRunningViaNpx(scriptUrl) {
21279
21351
  async function readState() {
21280
21352
  if (!await fileExists(STATE_FILE)) return null;
21281
21353
  try {
21282
- const raw = await readFile27(STATE_FILE, "utf-8");
21354
+ const raw = await readFile28(STATE_FILE, "utf-8");
21283
21355
  return JSON.parse(raw);
21284
21356
  } catch {
21285
21357
  return null;
@@ -21338,7 +21410,7 @@ async function readGlobalVersion() {
21338
21410
  try {
21339
21411
  const manifestPath = join7(rootPath, "syntaur", "package.json");
21340
21412
  if (!await fileExists(manifestPath)) return null;
21341
- const raw = await readFile27(manifestPath, "utf-8");
21413
+ const raw = await readFile28(manifestPath, "utf-8");
21342
21414
  const parsed = JSON.parse(raw);
21343
21415
  return typeof parsed.version === "string" ? parsed.version : null;
21344
21416
  } catch {
@@ -21696,7 +21768,7 @@ async function refreshPluginSkills(options, pm, runner, getManagedDir, resolveFr
21696
21768
  // src/commands/install-statusline.ts
21697
21769
  init_paths();
21698
21770
  init_fs();
21699
- import { readFile as readFile29, writeFile as writeFile11, copyFile as copyFile2, rm as rm6, stat as stat2, symlink as symlink2, unlink as unlink8, lstat as lstat3 } from "fs/promises";
21771
+ import { readFile as readFile30, writeFile as writeFile11, copyFile as copyFile2, rm as rm6, stat as stat2, symlink as symlink2, unlink as unlink8, lstat as lstat3 } from "fs/promises";
21700
21772
  import { resolve as resolve43, dirname as dirname15 } from "path";
21701
21773
  import { homedir as homedir7 } from "os";
21702
21774
  import { fileURLToPath as fileURLToPath8 } from "url";
@@ -21704,7 +21776,7 @@ import { fileURLToPath as fileURLToPath8 } from "url";
21704
21776
  // src/commands/configure-statusline.ts
21705
21777
  init_paths();
21706
21778
  init_fs();
21707
- import { readFile as readFile28, writeFile as writeFile10 } from "fs/promises";
21779
+ import { readFile as readFile29, writeFile as writeFile10 } from "fs/promises";
21708
21780
  import { resolve as resolve42, dirname as dirname14 } from "path";
21709
21781
  import { spawnSync as spawnSync5 } from "child_process";
21710
21782
  import { checkbox, input as input2, confirm } from "@inquirer/prompts";
@@ -21731,7 +21803,7 @@ function getConfigPath(installRoot) {
21731
21803
  async function readConfig2(path) {
21732
21804
  if (!await fileExists(path)) return null;
21733
21805
  try {
21734
- const raw = await readFile28(path, "utf-8");
21806
+ const raw = await readFile29(path, "utf-8");
21735
21807
  const parsed = JSON.parse(raw);
21736
21808
  if (!parsed || typeof parsed !== "object") return null;
21737
21809
  const segments = Array.isArray(parsed.segments) ? parsed.segments.filter(isSegmentName) : [];
@@ -21919,7 +21991,7 @@ function getPackageStatuslineSource() {
21919
21991
  }
21920
21992
  async function readSettingsJson(settingsPath) {
21921
21993
  if (!await fileExists(settingsPath)) return {};
21922
- const raw = await readFile29(settingsPath, "utf-8");
21994
+ const raw = await readFile30(settingsPath, "utf-8");
21923
21995
  if (raw.trim() === "") return {};
21924
21996
  try {
21925
21997
  const parsed = JSON.parse(raw);
@@ -22107,7 +22179,7 @@ async function uninstallStatuslineCommand(options = {}) {
22107
22179
  let restored = null;
22108
22180
  if (await fileExists(backupPath)) {
22109
22181
  try {
22110
- const raw = await readFile29(backupPath, "utf-8");
22182
+ const raw = await readFile30(backupPath, "utf-8");
22111
22183
  const parsed = JSON.parse(raw);
22112
22184
  const prev = parsed?.previousStatusLine;
22113
22185
  if (prev && typeof prev === "object" && typeof prev.command === "string") {
@@ -22284,6 +22356,353 @@ async function uninstallSkillsCommand(options) {
22284
22356
  // src/commands/setup.ts
22285
22357
  import { execSync } from "child_process";
22286
22358
  init_config2();
22359
+
22360
+ // src/commands/cross-agent-install.ts
22361
+ init_fs();
22362
+ import { spawnSync as spawnSync6 } from "child_process";
22363
+ import { resolve as resolve46 } from "path";
22364
+ import { readFile as readFile31 } from "fs/promises";
22365
+
22366
+ // src/commands/setup-adapter.ts
22367
+ init_paths();
22368
+ init_fs();
22369
+ init_config2();
22370
+ init_slug();
22371
+ import { resolve as resolve45 } from "path";
22372
+
22373
+ // src/targets/registry.ts
22374
+ init_fs();
22375
+ import { homedir as homedir8 } from "os";
22376
+ import { resolve as resolve44 } from "path";
22377
+ function home(...segments) {
22378
+ return resolve44(homedir8(), ...segments);
22379
+ }
22380
+ function hermesHome() {
22381
+ const env = process.env.HERMES_HOME;
22382
+ return env && env.length > 0 ? resolve44(env) : home(".hermes");
22383
+ }
22384
+ function hermesSkillsDir() {
22385
+ return resolve44(hermesHome(), "skills");
22386
+ }
22387
+ function isHermesHomeCustom() {
22388
+ return hermesHome() !== home(".hermes");
22389
+ }
22390
+ function codexHome() {
22391
+ const env = process.env.CODEX_HOME;
22392
+ return env && env.length > 0 ? resolve44(env) : home(".codex");
22393
+ }
22394
+ var detectDir = (dir) => () => fileExists(dir);
22395
+ var AGENT_TARGETS = [
22396
+ {
22397
+ id: "cursor",
22398
+ displayName: "Cursor",
22399
+ skillsShAgentId: "cursor",
22400
+ detect: detectDir(home(".cursor")),
22401
+ skillsDir: { global: home(".cursor", "skills") },
22402
+ instructions: {
22403
+ files: [
22404
+ { path: ".cursor/rules/syntaur-protocol.mdc", renderer: "cursorProtocol" },
22405
+ { path: ".cursor/rules/syntaur-assignment.mdc", renderer: "cursorAssignment" }
22406
+ ]
22407
+ }
22408
+ },
22409
+ {
22410
+ // codex is BOTH an adapter (writes AGENTS.md) AND a native plugin.
22411
+ id: "codex",
22412
+ displayName: "Codex",
22413
+ skillsShAgentId: "codex",
22414
+ nativePlugin: "codex",
22415
+ detect: detectDir(codexHome()),
22416
+ skillsDir: { global: resolve44(codexHome(), "skills") },
22417
+ instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] }
22418
+ },
22419
+ {
22420
+ id: "opencode",
22421
+ displayName: "OpenCode",
22422
+ skillsShAgentId: "opencode",
22423
+ detect: detectDir(home(".config", "opencode")),
22424
+ skillsDir: { global: home(".config", "opencode", "skills") },
22425
+ instructions: {
22426
+ files: [
22427
+ { path: "AGENTS.md", renderer: "codexAgents" },
22428
+ { path: "opencode.json", renderer: "openCodeConfig" }
22429
+ ]
22430
+ }
22431
+ },
22432
+ {
22433
+ // claude has NO adapter today (not in the old SUPPORTED_FRAMEWORKS) — the
22434
+ // full plugin path owns its skills/hooks/commands. Native-plugin only.
22435
+ id: "claude",
22436
+ displayName: "Claude Code",
22437
+ skillsShAgentId: "claude-code",
22438
+ nativePlugin: "claude",
22439
+ detect: detectDir(home(".claude")),
22440
+ skillsDir: { global: home(".claude", "skills") }
22441
+ },
22442
+ {
22443
+ id: "pi",
22444
+ displayName: "Pi",
22445
+ skillsShAgentId: "pi",
22446
+ detect: detectDir(home(".pi")),
22447
+ skillsDir: { global: home(".pi", "agent", "skills") },
22448
+ instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] }
22449
+ },
22450
+ {
22451
+ id: "openclaw",
22452
+ displayName: "OpenClaw",
22453
+ skillsShAgentId: "openclaw",
22454
+ detect: detectDir(home(".openclaw")),
22455
+ skillsDir: { global: home(".openclaw", "skills") },
22456
+ instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] }
22457
+ },
22458
+ {
22459
+ id: "hermes",
22460
+ displayName: "Hermes Agent",
22461
+ skillsShAgentId: "hermes-agent",
22462
+ detect: () => fileExists(hermesHome()),
22463
+ skillsDir: { global: hermesSkillsDir() },
22464
+ instructions: { files: [{ path: "SOUL.md", renderer: "hermesSoul" }] }
22465
+ }
22466
+ ];
22467
+ var AGENT_TARGETS_BY_ID = Object.fromEntries(
22468
+ AGENT_TARGETS.map((t) => [t.id, t])
22469
+ );
22470
+ function getAgentTarget(id) {
22471
+ return AGENT_TARGETS_BY_ID[id];
22472
+ }
22473
+ function agentTargetIds() {
22474
+ return AGENT_TARGETS.map((t) => t.id);
22475
+ }
22476
+ function adapterTargets() {
22477
+ return AGENT_TARGETS.filter((t) => t.instructions !== void 0);
22478
+ }
22479
+
22480
+ // src/targets/renderers.ts
22481
+ init_cursor_rules();
22482
+ init_codex_agents();
22483
+ init_opencode_config();
22484
+ init_hermes_soul();
22485
+ var RENDERERS = {
22486
+ codexAgents: (ctx) => renderCodexAgents(ctx),
22487
+ cursorProtocol: () => renderCursorProtocol(),
22488
+ cursorAssignment: (ctx) => renderCursorAssignment(ctx),
22489
+ openCodeConfig: (ctx) => renderOpenCodeConfig({ projectDir: ctx.projectDir }),
22490
+ hermesSoul: (ctx) => renderHermesSoul(ctx)
22491
+ };
22492
+
22493
+ // src/commands/setup-adapter.ts
22494
+ async function setupAdapterCommand(framework, options) {
22495
+ const target = getAgentTarget(framework);
22496
+ if (!target || !target.instructions) {
22497
+ const supported = adapterTargets().map((t) => t.id).join(", ");
22498
+ throw new Error(
22499
+ `Unsupported framework "${framework}". Supported: ${supported}`
22500
+ );
22501
+ }
22502
+ if (!options.project) {
22503
+ throw new Error("--project <slug> is required.");
22504
+ }
22505
+ if (!options.assignment) {
22506
+ throw new Error("--assignment <slug> is required.");
22507
+ }
22508
+ if (!isValidSlug(options.project)) {
22509
+ throw new Error(
22510
+ `Invalid project slug "${options.project}". Slugs must be lowercase, hyphen-separated, with no special characters.`
22511
+ );
22512
+ }
22513
+ if (!isValidSlug(options.assignment)) {
22514
+ throw new Error(
22515
+ `Invalid assignment slug "${options.assignment}". Slugs must be lowercase, hyphen-separated, with no special characters.`
22516
+ );
22517
+ }
22518
+ const config = await readConfig();
22519
+ const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
22520
+ const projectDir = resolve45(baseDir, options.project);
22521
+ const assignmentDir = resolve45(projectDir, "assignments", options.assignment);
22522
+ const projectMdPath = resolve45(projectDir, "project.md");
22523
+ if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
22524
+ throw new Error(`Project "${options.project}" not found at ${projectDir}.`);
22525
+ }
22526
+ const assignmentMdPath2 = resolve45(assignmentDir, "assignment.md");
22527
+ if (!await fileExists(assignmentDir) || !await fileExists(assignmentMdPath2)) {
22528
+ throw new Error(
22529
+ `Assignment "${options.assignment}" not found at ${assignmentDir}.`
22530
+ );
22531
+ }
22532
+ const cwd = process.cwd();
22533
+ const rendererParams = {
22534
+ projectSlug: options.project,
22535
+ assignmentSlug: options.assignment,
22536
+ projectDir,
22537
+ assignmentDir
22538
+ };
22539
+ const writtenFiles = [];
22540
+ const upToDateFiles = [];
22541
+ const skippedFiles = [];
22542
+ for (const file of target.instructions.files) {
22543
+ const filePath = resolve45(cwd, file.path);
22544
+ const content = RENDERERS[file.renderer](rendererParams);
22545
+ const status = await writeFileReport(filePath, content, {
22546
+ force: options.force
22547
+ });
22548
+ if (status === "differs-preserved") {
22549
+ skippedFiles.push(filePath);
22550
+ } else if (status === "already-current") {
22551
+ upToDateFiles.push(filePath);
22552
+ } else {
22553
+ writtenFiles.push(filePath);
22554
+ }
22555
+ }
22556
+ if (writtenFiles.length > 0) {
22557
+ console.log(`Generated ${target.id} adapter files:`);
22558
+ for (const f of writtenFiles) {
22559
+ console.log(` ${f}`);
22560
+ }
22561
+ }
22562
+ if (upToDateFiles.length > 0) {
22563
+ console.log(`Already up-to-date:`);
22564
+ for (const f of upToDateFiles) {
22565
+ console.log(` ${f}`);
22566
+ }
22567
+ }
22568
+ if (skippedFiles.length > 0) {
22569
+ console.log(`Skipped (exists with different content, use --force to overwrite):`);
22570
+ for (const f of skippedFiles) {
22571
+ console.log(` ${f}`);
22572
+ }
22573
+ }
22574
+ if (writtenFiles.length === 0 && skippedFiles.length === 0 && upToDateFiles.length > 0) {
22575
+ console.log(`No changes. All ${target.id} adapter files are already up-to-date.`);
22576
+ }
22577
+ }
22578
+
22579
+ // src/commands/cross-agent-install.ts
22580
+ init_config2();
22581
+ var DEFAULT_SKILLS_SOURCE = "prong-horn/syntaur";
22582
+ function parseTargetIds(options) {
22583
+ const raw = [options.target, options.agent].filter(Boolean).join(",");
22584
+ const ids = raw.split(",").map((s) => s.trim()).filter(Boolean);
22585
+ return [...new Set(ids)];
22586
+ }
22587
+ function isNpxAvailable() {
22588
+ try {
22589
+ const r = spawnSync6("npx", ["--version"], { stdio: "ignore" });
22590
+ return !r.error && r.status === 0;
22591
+ } catch {
22592
+ return false;
22593
+ }
22594
+ }
22595
+ async function readAssignmentContext() {
22596
+ const p = resolve46(process.cwd(), ".syntaur", "context.json");
22597
+ if (!await fileExists(p)) return null;
22598
+ try {
22599
+ return JSON.parse(await readFile31(p, "utf-8"));
22600
+ } catch {
22601
+ return null;
22602
+ }
22603
+ }
22604
+ async function crossAgentInstallCommand(options) {
22605
+ const ids = parseTargetIds(options);
22606
+ if (ids.length === 0) {
22607
+ throw new Error("No agents specified. Use --target <id> or --agent <id>.");
22608
+ }
22609
+ const targets = [];
22610
+ for (const id of ids) {
22611
+ const t = getAgentTarget(id);
22612
+ if (!t) {
22613
+ throw new Error(
22614
+ `Unknown agent "${id}". Known agents: ${agentTargetIds().join(", ")}`
22615
+ );
22616
+ }
22617
+ if (t.nativePlugin) {
22618
+ throw new Error(
22619
+ `"${id}" installs as a native Syntaur plugin, not via cross-agent install. Use \`syntaur setup --${t.nativePlugin}\` (or \`syntaur ${t.nativePlugin === "claude" ? "install-plugin" : "install-codex-plugin"}\`).`
22620
+ );
22621
+ }
22622
+ targets.push(t);
22623
+ }
22624
+ const dryRun = Boolean(options.dryRun);
22625
+ const force = Boolean(options.force);
22626
+ const scope = "global";
22627
+ const prefix = dryRun ? "[dry-run] " : "";
22628
+ if (!dryRun && !await isSyntaurDataInstalled()) {
22629
+ await initCommand({});
22630
+ }
22631
+ const skillsShIds = targets.map((t) => t.skillsShAgentId ?? t.id);
22632
+ const argv = ["skills", "add", DEFAULT_SKILLS_SOURCE, "--agent", ...skillsShIds];
22633
+ console.log(`${prefix}Tier 1 (skills): npx ${argv.join(" ")}`);
22634
+ let tier1Done = false;
22635
+ if (!dryRun) {
22636
+ if (isNpxAvailable()) {
22637
+ const r = spawnSync6("npx", argv, { stdio: "inherit" });
22638
+ tier1Done = !r.error && r.status === 0;
22639
+ if (!tier1Done) {
22640
+ console.log("`npx skills add` failed; falling back to offline copy.");
22641
+ }
22642
+ } else {
22643
+ console.log("npx not available; using offline skill copy.");
22644
+ }
22645
+ }
22646
+ for (const t of targets) {
22647
+ const globalDir = t.id === "hermes" ? hermesSkillsDir() : t.skillsDir?.global;
22648
+ if (!globalDir) continue;
22649
+ const offlineNeeded = !tier1Done;
22650
+ const hermesCustom = t.id === "hermes" && isHermesHomeCustom();
22651
+ if (!offlineNeeded && !hermesCustom) continue;
22652
+ if (dryRun) {
22653
+ const label = hermesCustom ? "offline copy (always \u2014 custom $HERMES_HOME)" : "offline copy (fallback if npx unavailable)";
22654
+ console.log(`${prefix}${label} -> ${globalDir}`);
22655
+ continue;
22656
+ }
22657
+ const reason = hermesCustom && tier1Done ? " ($HERMES_HOME reconcile)" : "";
22658
+ await installSkillsToDir({ targetDir: globalDir, force });
22659
+ console.log(`Copied skills -> ${globalDir}${reason}`);
22660
+ }
22661
+ const adapterTargets2 = targets.filter((t) => t.instructions);
22662
+ if (adapterTargets2.length > 0) {
22663
+ const ctx = await readAssignmentContext();
22664
+ const haveCtx = Boolean(ctx?.projectSlug && ctx?.assignmentSlug);
22665
+ for (const t of adapterTargets2) {
22666
+ if (dryRun) {
22667
+ for (const f of t.instructions.files) {
22668
+ console.log(
22669
+ `${prefix}Tier 2 (${t.id}): ${resolve46(process.cwd(), f.path)}`
22670
+ );
22671
+ }
22672
+ continue;
22673
+ }
22674
+ if (!haveCtx) {
22675
+ console.log(
22676
+ `No project-nested assignment context in cwd; skipping Tier-2 files for ${t.id}.`
22677
+ );
22678
+ continue;
22679
+ }
22680
+ try {
22681
+ await setupAdapterCommand(t.id, {
22682
+ project: ctx.projectSlug,
22683
+ assignment: ctx.assignmentSlug,
22684
+ force
22685
+ });
22686
+ } catch (err2) {
22687
+ const msg = err2 instanceof Error ? err2.message : String(err2);
22688
+ console.log(`Tier 2 for ${t.id} skipped: ${msg}`);
22689
+ }
22690
+ }
22691
+ }
22692
+ if (!dryRun) {
22693
+ const current = (await readConfig()).integrations.installedAgents ?? {};
22694
+ const next = { ...current };
22695
+ for (const t of targets) {
22696
+ next[t.id] = { scope };
22697
+ }
22698
+ await updateIntegrationConfig({ installedAgents: next });
22699
+ }
22700
+ console.log(
22701
+ `${prefix}Done. Agents: ${targets.map((t) => t.displayName).join(", ")}.`
22702
+ );
22703
+ }
22704
+
22705
+ // src/commands/setup.ts
22287
22706
  function isCliInstalled(command) {
22288
22707
  try {
22289
22708
  execSync(`which ${command}`, { stdio: "ignore" });
@@ -22301,6 +22720,15 @@ function printNonInteractiveSetupHelp() {
22301
22720
  console.error(` npx syntaur@latest setup --yes --dashboard`);
22302
22721
  }
22303
22722
  async function setupCommand(options) {
22723
+ if (options.dryRun && !options.target && !options.agent) {
22724
+ throw new Error(
22725
+ "--dry-run only applies to cross-agent install. Pass --target <id> or --agent <id>."
22726
+ );
22727
+ }
22728
+ if (options.target || options.agent) {
22729
+ await crossAgentInstallCommand(options);
22730
+ return;
22731
+ }
22304
22732
  const initialized = await isSyntaurDataInstalled();
22305
22733
  const interactive = isInteractiveTerminal();
22306
22734
  if (!initialized) {
@@ -22385,7 +22813,7 @@ async function setupCommand(options) {
22385
22813
  }
22386
22814
 
22387
22815
  // src/commands/uninstall.ts
22388
- import { resolve as resolve44 } from "path";
22816
+ import { resolve as resolve47 } from "path";
22389
22817
  init_paths();
22390
22818
  function expandTargets(options) {
22391
22819
  if (options.all) {
@@ -22465,7 +22893,7 @@ async function uninstallCommand(options) {
22465
22893
  const configuredProjectDir = await getConfiguredProjectDir();
22466
22894
  await removeSyntaurData();
22467
22895
  console.log(`Removed ${syntaurRoot()}`);
22468
- if (configuredProjectDir && resolve44(configuredProjectDir) !== resolve44(syntaurRoot(), "projects")) {
22896
+ if (configuredProjectDir && resolve47(configuredProjectDir) !== resolve47(syntaurRoot(), "projects")) {
22469
22897
  console.warn(
22470
22898
  `Warning: config.md pointed to an external project directory (${configuredProjectDir}). That directory was not removed automatically.`
22471
22899
  );
@@ -22476,114 +22904,11 @@ async function uninstallCommand(options) {
22476
22904
  }
22477
22905
  }
22478
22906
 
22479
- // src/commands/setup-adapter.ts
22480
- init_paths();
22481
- init_fs();
22482
- init_config2();
22483
- init_slug();
22484
- init_cursor_rules();
22485
- init_codex_agents();
22486
- init_opencode_config();
22487
- import { resolve as resolve45 } from "path";
22488
- var SUPPORTED_FRAMEWORKS = ["cursor", "codex", "opencode"];
22489
- async function setupAdapterCommand(framework, options) {
22490
- if (!SUPPORTED_FRAMEWORKS.includes(framework)) {
22491
- throw new Error(
22492
- `Unsupported framework "${framework}". Supported: ${SUPPORTED_FRAMEWORKS.join(", ")}`
22493
- );
22494
- }
22495
- if (!options.project) {
22496
- throw new Error("--project <slug> is required.");
22497
- }
22498
- if (!options.assignment) {
22499
- throw new Error("--assignment <slug> is required.");
22500
- }
22501
- if (!isValidSlug(options.project)) {
22502
- throw new Error(
22503
- `Invalid project slug "${options.project}". Slugs must be lowercase, hyphen-separated, with no special characters.`
22504
- );
22505
- }
22506
- if (!isValidSlug(options.assignment)) {
22507
- throw new Error(
22508
- `Invalid assignment slug "${options.assignment}". Slugs must be lowercase, hyphen-separated, with no special characters.`
22509
- );
22510
- }
22511
- const config = await readConfig();
22512
- const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
22513
- const projectDir = resolve45(baseDir, options.project);
22514
- const assignmentDir = resolve45(
22515
- projectDir,
22516
- "assignments",
22517
- options.assignment
22518
- );
22519
- const projectMdPath = resolve45(projectDir, "project.md");
22520
- if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
22521
- throw new Error(
22522
- `Project "${options.project}" not found at ${projectDir}.`
22523
- );
22524
- }
22525
- const assignmentMdPath2 = resolve45(assignmentDir, "assignment.md");
22526
- if (!await fileExists(assignmentDir) || !await fileExists(assignmentMdPath2)) {
22527
- throw new Error(
22528
- `Assignment "${options.assignment}" not found at ${assignmentDir}.`
22529
- );
22530
- }
22531
- const cwd = process.cwd();
22532
- const writtenFiles = [];
22533
- const skippedFiles = [];
22534
- const rendererParams = {
22535
- projectSlug: options.project,
22536
- assignmentSlug: options.assignment,
22537
- projectDir,
22538
- assignmentDir
22539
- };
22540
- async function writeAdapterFile(filePath, content) {
22541
- if (options.force) {
22542
- await writeFileForce(filePath, content);
22543
- writtenFiles.push(filePath);
22544
- } else {
22545
- if (await writeFileSafe(filePath, content)) {
22546
- writtenFiles.push(filePath);
22547
- } else {
22548
- skippedFiles.push(filePath);
22549
- }
22550
- }
22551
- }
22552
- if (framework === "cursor") {
22553
- const protocolPath = resolve45(cwd, ".cursor", "rules", "syntaur-protocol.mdc");
22554
- const assignmentPath = resolve45(cwd, ".cursor", "rules", "syntaur-assignment.mdc");
22555
- await writeAdapterFile(protocolPath, renderCursorProtocol());
22556
- await writeAdapterFile(assignmentPath, renderCursorAssignment(rendererParams));
22557
- } else if (framework === "codex" || framework === "opencode") {
22558
- const agentsPath = resolve45(cwd, "AGENTS.md");
22559
- await writeAdapterFile(agentsPath, renderCodexAgents(rendererParams));
22560
- if (framework === "opencode") {
22561
- const configPath = resolve45(cwd, "opencode.json");
22562
- await writeAdapterFile(configPath, renderOpenCodeConfig({ projectDir }));
22563
- }
22564
- }
22565
- if (writtenFiles.length > 0) {
22566
- console.log(`Generated ${framework} adapter files:`);
22567
- for (const f of writtenFiles) {
22568
- console.log(` ${f}`);
22569
- }
22570
- }
22571
- if (skippedFiles.length > 0) {
22572
- console.log(`Skipped (already exist, use --force to overwrite):`);
22573
- for (const f of skippedFiles) {
22574
- console.log(` ${f}`);
22575
- }
22576
- }
22577
- if (writtenFiles.length === 0 && skippedFiles.length > 0) {
22578
- console.log(`No files written. All target files already exist.`);
22579
- }
22580
- }
22581
-
22582
22907
  // src/commands/track-session.ts
22583
22908
  init_paths();
22584
22909
  init_fs();
22585
22910
  init_config2();
22586
- import { resolve as resolve46 } from "path";
22911
+ import { resolve as resolve48 } from "path";
22587
22912
  init_session_db();
22588
22913
  init_agent_sessions();
22589
22914
  async function trackSessionCommand(options) {
@@ -22598,7 +22923,7 @@ async function trackSessionCommand(options) {
22598
22923
  if (options.project) {
22599
22924
  const config = await readConfig();
22600
22925
  const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
22601
- const projectDir = resolve46(baseDir, options.project);
22926
+ const projectDir = resolve48(baseDir, options.project);
22602
22927
  if (!await fileExists(projectDir)) {
22603
22928
  throw new Error(
22604
22929
  `Project "${options.project}" not found at ${projectDir}.`
@@ -22730,8 +23055,8 @@ function formatInstallUrlHandlerError(err2) {
22730
23055
  init_config2();
22731
23056
  init_paths();
22732
23057
  init_fs();
22733
- import { resolve as resolve48, isAbsolute as isAbsolute7 } from "path";
22734
- import { readFile as readFile30 } from "fs/promises";
23058
+ import { resolve as resolve50, isAbsolute as isAbsolute7 } from "path";
23059
+ import { readFile as readFile32 } from "fs/promises";
22735
23060
  import { select, confirm as confirm2, input as input3 } from "@inquirer/prompts";
22736
23061
  async function browseCommand(options) {
22737
23062
  const config = await readConfig();
@@ -22800,7 +23125,7 @@ async function pickAgent2(agents) {
22800
23125
  return picked;
22801
23126
  }
22802
23127
  async function ensureWorktree(opts) {
22803
- const assignmentPath = resolve48(
23128
+ const assignmentPath = resolve50(
22804
23129
  opts.projectsDir,
22805
23130
  opts.projectSlug,
22806
23131
  "assignments",
@@ -22810,7 +23135,7 @@ async function ensureWorktree(opts) {
22810
23135
  if (!await fileExists(assignmentPath)) {
22811
23136
  return void 0;
22812
23137
  }
22813
- const content = await readFile30(assignmentPath, "utf-8");
23138
+ const content = await readFile32(assignmentPath, "utf-8");
22814
23139
  const { parseAssignmentFrontmatter: parseAssignmentFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
22815
23140
  const fm = parseAssignmentFrontmatter2(content);
22816
23141
  const { workspace } = fm;
@@ -22880,7 +23205,7 @@ async function ensureWorktree(opts) {
22880
23205
  async function runCreate(opts) {
22881
23206
  const { createWorktreeAndRecord: createWorktreeAndRecord2, GitWorktreeError: GitWorktreeError2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
22882
23207
  const expandedWorktree = expandHome(opts.worktreePath);
22883
- const absWorktree = isAbsolute7(expandedWorktree) ? expandedWorktree : resolve48(expandedWorktree);
23208
+ const absWorktree = isAbsolute7(expandedWorktree) ? expandedWorktree : resolve50(expandedWorktree);
22884
23209
  try {
22885
23210
  await createWorktreeAndRecord2({
22886
23211
  assignmentPath: opts.assignmentPath,
@@ -22909,7 +23234,7 @@ init_paths();
22909
23234
  init_fs();
22910
23235
  init_playbook();
22911
23236
  init_playbooks();
22912
- import { resolve as resolve49 } from "path";
23237
+ import { resolve as resolve51 } from "path";
22913
23238
  async function createPlaybookCommand(name, options) {
22914
23239
  if (!name.trim()) {
22915
23240
  throw new Error("Playbook name cannot be empty.");
@@ -22922,7 +23247,7 @@ async function createPlaybookCommand(name, options) {
22922
23247
  }
22923
23248
  const dir = playbooksDir();
22924
23249
  await ensureDir(dir);
22925
- const filePath = resolve49(dir, `${slug}.md`);
23250
+ const filePath = resolve51(dir, `${slug}.md`);
22926
23251
  if (await fileExists(filePath)) {
22927
23252
  throw new Error(
22928
23253
  `Playbook "${slug}" already exists at ${filePath}
@@ -22944,8 +23269,8 @@ init_paths();
22944
23269
  init_fs();
22945
23270
  init_parser();
22946
23271
  init_config2();
22947
- import { readdir as readdir16, readFile as readFile31 } from "fs/promises";
22948
- import { resolve as resolve50 } from "path";
23272
+ import { readdir as readdir16, readFile as readFile33 } from "fs/promises";
23273
+ import { resolve as resolve52 } from "path";
22949
23274
  async function listPlaybooksCommand(options = {}) {
22950
23275
  const dir = playbooksDir();
22951
23276
  if (!await fileExists(dir)) {
@@ -22960,8 +23285,8 @@ async function listPlaybooksCommand(options = {}) {
22960
23285
  );
22961
23286
  const rows = [];
22962
23287
  for (const entry of mdFiles) {
22963
- const filePath = resolve50(dir, entry.name);
22964
- const raw = await readFile31(filePath, "utf-8");
23288
+ const filePath = resolve52(dir, entry.name);
23289
+ const raw = await readFile33(filePath, "utf-8");
22965
23290
  const parsed = parsePlaybook(raw);
22966
23291
  const slug = parsed.slug || entry.name.replace(/\.md$/, "");
22967
23292
  const disabled = disabledSet.has(slug);
@@ -23084,14 +23409,14 @@ init_fs();
23084
23409
  init_config2();
23085
23410
  init_slug();
23086
23411
  import { Command as Command2 } from "commander";
23087
- import { readFile as readFile33 } from "fs/promises";
23088
- import { resolve as resolve52 } from "path";
23412
+ import { readFile as readFile35 } from "fs/promises";
23413
+ import { resolve as resolve54 } from "path";
23089
23414
 
23090
23415
  // src/commands/bundle.ts
23091
23416
  init_paths();
23092
23417
  import { Command } from "commander";
23093
- import { mkdir as mkdir7, readFile as readFile32, readdir as readdir17, rm as rm7, writeFile as writeFile12 } from "fs/promises";
23094
- import { resolve as resolve51 } from "path";
23418
+ import { mkdir as mkdir7, readFile as readFile34, readdir as readdir17, rm as rm7, writeFile as writeFile12 } from "fs/promises";
23419
+ import { resolve as resolve53 } from "path";
23095
23420
  init_parser2();
23096
23421
  init_fs();
23097
23422
  init_config2();
@@ -23109,7 +23434,7 @@ async function resolveBundleScope(options) {
23109
23434
  throw new Error(`Invalid project slug: "${options.project}".`);
23110
23435
  }
23111
23436
  const config = await readConfig();
23112
- const projectMd = resolve51(config.defaultProjectDir, options.project, "project.md");
23437
+ const projectMd = resolve53(config.defaultProjectDir, options.project, "project.md");
23113
23438
  if (!await fileExists(projectMd)) {
23114
23439
  throw new Error(`Project "${options.project}" not found.`);
23115
23440
  }
@@ -23179,10 +23504,10 @@ function pickNextPlanFile(planDir, existingFiles) {
23179
23504
  const m = f.match(/^plan-v(\d+)\.md$/);
23180
23505
  if (m) versions.add(parseInt(m[1], 10));
23181
23506
  }
23182
- if (versions.size === 0) return { target: resolve51(planDir, "plan.md"), version: 1 };
23507
+ if (versions.size === 0) return { target: resolve53(planDir, "plan.md"), version: 1 };
23183
23508
  let n = 2;
23184
23509
  while (versions.has(n)) n++;
23185
- return { target: resolve51(planDir, `plan-v${n}.md`), version: n };
23510
+ return { target: resolve53(planDir, `plan-v${n}.md`), version: n };
23186
23511
  }
23187
23512
  function dedupePreserveOrder(ids) {
23188
23513
  const out = [];
@@ -23266,7 +23591,7 @@ bundleCommand.command("new").description("Create a new bundle from 2+ existing t
23266
23591
  if (options.plan) {
23267
23592
  const planDir = bundlePlanDir(sc.todosPath, sc.scopeId, id);
23268
23593
  await ensureDir(planDir);
23269
- const target = resolve51(planDir, "plan.md");
23594
+ const target = resolve53(planDir, "plan.md");
23270
23595
  const memberLines = items.map((it) => `- ${it.description} [t:${it.id}]`).join("\n");
23271
23596
  const stub = [
23272
23597
  "---",
@@ -23442,7 +23767,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
23442
23767
  }
23443
23768
  const repository = options.repository ?? process.cwd();
23444
23769
  const parentBranch = options.parentBranch ?? "main";
23445
- const worktreePath = options.worktreePath ?? resolve51(repository, ".worktrees", options.branch);
23770
+ const worktreePath = options.worktreePath ?? resolve53(repository, ".worktrees", options.branch);
23446
23771
  const checklist = await readChecklist(sc.todosPath, sc.checklistKey);
23447
23772
  for (const memberId of bundle.todoIds) {
23448
23773
  const item = checklist.items.find((i) => i.id === memberId);
@@ -23452,8 +23777,8 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
23452
23777
  }
23453
23778
  const bundlesFilePath = bundlesPath(sc.todosPath);
23454
23779
  const checklistFilePath = checklistPath(sc.todosPath, sc.checklistKey);
23455
- const bundlesSnapshot = await fileExists(bundlesFilePath) ? await readFile32(bundlesFilePath, "utf-8") : null;
23456
- const checklistSnapshot = await fileExists(checklistFilePath) ? await readFile32(checklistFilePath, "utf-8") : null;
23780
+ const bundlesSnapshot = await fileExists(bundlesFilePath) ? await readFile34(bundlesFilePath, "utf-8") : null;
23781
+ const checklistSnapshot = await fileExists(checklistFilePath) ? await readFile34(checklistFilePath, "utf-8") : null;
23457
23782
  const record = async () => {
23458
23783
  bundle.branch = options.branch;
23459
23784
  bundle.worktreePath = worktreePath;
@@ -23469,7 +23794,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
23469
23794
  try {
23470
23795
  await writeBundles(sc.todosPath, bundles);
23471
23796
  await writeChecklist(sc.todosPath, checklist);
23472
- const ctxDir = resolve51(worktreePath, ".syntaur");
23797
+ const ctxDir = resolve53(worktreePath, ".syntaur");
23473
23798
  await mkdir7(ctxDir, { recursive: true });
23474
23799
  const payload = {
23475
23800
  bundleId: bundle.id,
@@ -23483,7 +23808,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
23483
23808
  repository,
23484
23809
  boundAt: nowISO()
23485
23810
  };
23486
- await writeFile12(resolve51(ctxDir, "context.json"), JSON.stringify(payload, null, 2) + "\n");
23811
+ await writeFile12(resolve53(ctxDir, "context.json"), JSON.stringify(payload, null, 2) + "\n");
23487
23812
  } catch (err2) {
23488
23813
  try {
23489
23814
  if (bundlesSnapshot === null) {
@@ -23673,7 +23998,7 @@ async function resolveScope(options) {
23673
23998
  throw new Error(`Invalid project slug: "${options.project}".`);
23674
23999
  }
23675
24000
  const config = await readConfig();
23676
- const projectMd = resolve52(config.defaultProjectDir, options.project, "project.md");
24001
+ const projectMd = resolve54(config.defaultProjectDir, options.project, "project.md");
23677
24002
  if (!await fileExists(projectMd)) {
23678
24003
  throw new Error(`Project "${options.project}" not found.`);
23679
24004
  }
@@ -23996,10 +24321,10 @@ todoCommand.command("archive").description("Archive completed todos and their lo
23996
24321
  (e) => e.itemIds.every((id) => completedIds.has(id))
23997
24322
  );
23998
24323
  const archFile = archivePath(todosPath, workspace, checklist.archiveInterval);
23999
- await ensureDir(resolve52(todosPath, "archive"));
24324
+ await ensureDir(resolve54(todosPath, "archive"));
24000
24325
  let archContent = "";
24001
24326
  if (await fileExists(archFile)) {
24002
- archContent = await readFile33(archFile, "utf-8");
24327
+ archContent = await readFile35(archFile, "utf-8");
24003
24328
  archContent = archContent.trimEnd() + "\n\n";
24004
24329
  } else {
24005
24330
  archContent = `---
@@ -24176,12 +24501,12 @@ function describeScope(scope) {
24176
24501
  }
24177
24502
  async function injectPromotedTodos(assignmentDir, todos, scopeLabel) {
24178
24503
  const { resolve: resolvePath2 } = await import("path");
24179
- const { readFile: readFile54 } = await import("fs/promises");
24504
+ const { readFile: readFile56 } = await import("fs/promises");
24180
24505
  const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
24181
24506
  const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
24182
24507
  const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
24183
24508
  const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
24184
- let content = await readFile54(assignmentMdPath2, "utf-8");
24509
+ let content = await readFile56(assignmentMdPath2, "utf-8");
24185
24510
  content = appendTodosToAssignmentBody2(
24186
24511
  content,
24187
24512
  todos.map((t) => ({
@@ -24232,7 +24557,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
24232
24557
  );
24233
24558
  let target;
24234
24559
  if (existingFiles.length === 0) {
24235
- target = resolve52(planDir, "plan.md");
24560
+ target = resolve54(planDir, "plan.md");
24236
24561
  } else {
24237
24562
  const versions = /* @__PURE__ */ new Set();
24238
24563
  for (const f of existingFiles) {
@@ -24242,7 +24567,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
24242
24567
  }
24243
24568
  let n = 2;
24244
24569
  while (versions.has(n)) n++;
24245
- target = resolve52(planDir, `plan-v${n}.md`);
24570
+ target = resolve54(planDir, `plan-v${n}.md`);
24246
24571
  }
24247
24572
  if (!await fileExists(target)) {
24248
24573
  const stub = `---
@@ -24431,24 +24756,24 @@ backupCommand.command("config").description("Show or update backup configuration
24431
24756
 
24432
24757
  // src/commands/doctor.ts
24433
24758
  import { Command as Command4 } from "commander";
24434
- import { readFile as readFile41 } from "fs/promises";
24435
- import { isAbsolute as isAbsolute10, resolve as resolve64 } from "path";
24759
+ import { readFile as readFile43 } from "fs/promises";
24760
+ import { isAbsolute as isAbsolute10, resolve as resolve67 } from "path";
24436
24761
 
24437
24762
  // src/utils/doctor/index.ts
24438
24763
  import { fileURLToPath as fileURLToPath11 } from "url";
24439
- import { readFile as readFile40 } from "fs/promises";
24440
- import { dirname as dirname19, join as join12 } from "path";
24764
+ import { readFile as readFile42 } from "fs/promises";
24765
+ import { dirname as dirname19, join as join13 } from "path";
24441
24766
 
24442
24767
  // src/utils/doctor/context.ts
24443
24768
  init_config2();
24444
24769
  init_paths();
24445
24770
  init_fs();
24446
24771
  import Database4 from "better-sqlite3";
24447
- import { resolve as resolve53 } from "path";
24772
+ import { resolve as resolve55 } from "path";
24448
24773
  async function buildCheckContext(cwd = process.cwd()) {
24449
24774
  const config = await readConfig();
24450
24775
  const root = syntaurRoot();
24451
- const dbPath = resolve53(root, "syntaur.db");
24776
+ const dbPath = resolve55(root, "syntaur.db");
24452
24777
  let db5 = null;
24453
24778
  let dbError = null;
24454
24779
  if (await fileExists(dbPath)) {
@@ -24482,8 +24807,8 @@ function closeCheckContext(ctx) {
24482
24807
  // src/utils/doctor/checks/env.ts
24483
24808
  init_fs();
24484
24809
  init_paths();
24485
- import { resolve as resolve54, isAbsolute as isAbsolute8 } from "path";
24486
- import { readFile as readFile34, stat as stat3 } from "fs/promises";
24810
+ import { resolve as resolve56, isAbsolute as isAbsolute8 } from "path";
24811
+ import { readFile as readFile36, stat as stat3 } from "fs/promises";
24487
24812
  import { fileURLToPath as fileURLToPath10 } from "url";
24488
24813
  import { dirname as dirname17, join as join10 } from "path";
24489
24814
  var CATEGORY = "env";
@@ -24523,7 +24848,7 @@ var configValid = {
24523
24848
  category: CATEGORY,
24524
24849
  title: "~/.syntaur/config.md is valid",
24525
24850
  async run(ctx) {
24526
- const configPath = resolve54(ctx.syntaurRoot, "config.md");
24851
+ const configPath = resolve56(ctx.syntaurRoot, "config.md");
24527
24852
  if (!await fileExists(configPath)) {
24528
24853
  return {
24529
24854
  id: this.id,
@@ -24540,7 +24865,7 @@ var configValid = {
24540
24865
  autoFixable: false
24541
24866
  };
24542
24867
  }
24543
- const content = await readFile34(configPath, "utf-8");
24868
+ const content = await readFile36(configPath, "utf-8");
24544
24869
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
24545
24870
  if (!fmMatch || fmMatch[1].trim() === "") {
24546
24871
  return {
@@ -24820,7 +25145,7 @@ async function readLocalPkg() {
24820
25145
  for (let i = 0; i < 6; i++) {
24821
25146
  const candidate = join10(dir, "package.json");
24822
25147
  try {
24823
- const text = await readFile34(candidate, "utf-8");
25148
+ const text = await readFile36(candidate, "utf-8");
24824
25149
  return JSON.parse(text);
24825
25150
  } catch {
24826
25151
  dir = dirname17(dir);
@@ -24872,7 +25197,7 @@ function versionGte(a, b) {
24872
25197
 
24873
25198
  // src/utils/doctor/checks/structure.ts
24874
25199
  init_fs();
24875
- import { resolve as resolve55 } from "path";
25200
+ import { resolve as resolve57 } from "path";
24876
25201
  import { readdir as readdir18, stat as stat4 } from "fs/promises";
24877
25202
  var CATEGORY2 = "structure";
24878
25203
  var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
@@ -24892,7 +25217,7 @@ var projectsDir = {
24892
25217
  category: CATEGORY2,
24893
25218
  title: "projects/ directory exists",
24894
25219
  async run(ctx) {
24895
- const p = resolve55(ctx.syntaurRoot, "projects");
25220
+ const p = resolve57(ctx.syntaurRoot, "projects");
24896
25221
  if (!await fileExists(p)) {
24897
25222
  return {
24898
25223
  id: this.id,
@@ -24917,7 +25242,7 @@ var playbooksDir2 = {
24917
25242
  category: CATEGORY2,
24918
25243
  title: "playbooks/ directory exists",
24919
25244
  async run(ctx) {
24920
- const p = resolve55(ctx.syntaurRoot, "playbooks");
25245
+ const p = resolve57(ctx.syntaurRoot, "playbooks");
24921
25246
  if (!await fileExists(p)) {
24922
25247
  return {
24923
25248
  id: this.id,
@@ -24942,7 +25267,7 @@ var todosDirValid = {
24942
25267
  category: CATEGORY2,
24943
25268
  title: "todos/ directory is readable (if present)",
24944
25269
  async run(ctx) {
24945
- const p = resolve55(ctx.syntaurRoot, "todos");
25270
+ const p = resolve57(ctx.syntaurRoot, "todos");
24946
25271
  if (!await fileExists(p)) {
24947
25272
  return {
24948
25273
  id: this.id,
@@ -24973,7 +25298,7 @@ var serversDirValid = {
24973
25298
  category: CATEGORY2,
24974
25299
  title: "servers/ directory is readable (if present)",
24975
25300
  async run(ctx) {
24976
- const p = resolve55(ctx.syntaurRoot, "servers");
25301
+ const p = resolve57(ctx.syntaurRoot, "servers");
24977
25302
  if (!await fileExists(p)) {
24978
25303
  return {
24979
25304
  id: this.id,
@@ -25018,7 +25343,7 @@ var knownFilesRecognized = {
25018
25343
  title: this.title,
25019
25344
  status: "warn",
25020
25345
  detail: `unexpected top-level entries: ${unexpected.join(", ")}`,
25021
- affected: unexpected.map((n) => resolve55(ctx.syntaurRoot, n)),
25346
+ affected: unexpected.map((n) => resolve57(ctx.syntaurRoot, n)),
25022
25347
  remediation: {
25023
25348
  kind: "manual",
25024
25349
  suggestion: "Review these entries \u2014 they may be leftover state from older versions",
@@ -25047,7 +25372,7 @@ function pass2(check) {
25047
25372
 
25048
25373
  // src/utils/doctor/checks/project.ts
25049
25374
  init_fs();
25050
- import { resolve as resolve56 } from "path";
25375
+ import { resolve as resolve58 } from "path";
25051
25376
  import { readdir as readdir19, stat as stat5 } from "fs/promises";
25052
25377
  var CATEGORY3 = "project";
25053
25378
  var REQUIRED_PROJECT_FILES = [
@@ -25077,10 +25402,10 @@ async function listProjects2(ctx) {
25077
25402
  for (const e of entries) {
25078
25403
  if (!e.isDirectory()) continue;
25079
25404
  if (e.name.startsWith(".") || e.name.startsWith("_")) continue;
25080
- const projectDir = resolve56(dir, e.name);
25405
+ const projectDir = resolve58(dir, e.name);
25081
25406
  let looksLikeProject = false;
25082
25407
  for (const marker of PROJECT_MARKERS) {
25083
- if (await fileExists(resolve56(projectDir, marker))) {
25408
+ if (await fileExists(resolve58(projectDir, marker))) {
25084
25409
  looksLikeProject = true;
25085
25410
  break;
25086
25411
  }
@@ -25099,7 +25424,7 @@ var requiredFiles = {
25099
25424
  for (const projectDir of projects) {
25100
25425
  const missing = [];
25101
25426
  for (const rel of REQUIRED_PROJECT_FILES) {
25102
- const p = resolve56(projectDir, rel);
25427
+ const p = resolve58(projectDir, rel);
25103
25428
  if (!await fileExists(p)) missing.push(rel);
25104
25429
  }
25105
25430
  if (missing.length === 0) continue;
@@ -25109,7 +25434,7 @@ var requiredFiles = {
25109
25434
  title: this.title,
25110
25435
  status: "error",
25111
25436
  detail: `project at ${projectDir} is missing: ${missing.join(", ")}`,
25112
- affected: missing.map((m) => resolve56(projectDir, m)),
25437
+ affected: missing.map((m) => resolve58(projectDir, m)),
25113
25438
  remediation: {
25114
25439
  kind: "manual",
25115
25440
  suggestion: "Recreate the missing scaffold files from templates",
@@ -25132,7 +25457,7 @@ var manifestStale = {
25132
25457
  const projects = await listProjects2(ctx);
25133
25458
  const results = [];
25134
25459
  for (const projectDir of projects) {
25135
- const manifestPath = resolve56(projectDir, "manifest.md");
25460
+ const manifestPath = resolve58(projectDir, "manifest.md");
25136
25461
  if (!await fileExists(manifestPath)) continue;
25137
25462
  const manifestMtime = (await stat5(manifestPath)).mtimeMs;
25138
25463
  const newestAssignment = await newestAssignmentMtime(projectDir);
@@ -25181,7 +25506,7 @@ var orphanFiles = {
25181
25506
  title: this.title,
25182
25507
  status: "warn",
25183
25508
  detail: `project at ${projectDir} has unexpected entries: ${orphans.join(", ")}`,
25184
- affected: orphans.map((o) => resolve56(projectDir, o)),
25509
+ affected: orphans.map((o) => resolve58(projectDir, o)),
25185
25510
  autoFixable: false
25186
25511
  });
25187
25512
  }
@@ -25191,7 +25516,7 @@ var orphanFiles = {
25191
25516
  };
25192
25517
  var projectChecks = [requiredFiles, manifestStale, orphanFiles];
25193
25518
  async function newestAssignmentMtime(projectDir) {
25194
- const assignmentsRoot = resolve56(projectDir, "assignments");
25519
+ const assignmentsRoot = resolve58(projectDir, "assignments");
25195
25520
  if (!await fileExists(assignmentsRoot)) return 0;
25196
25521
  let newest = 0;
25197
25522
  let entries;
@@ -25202,7 +25527,7 @@ async function newestAssignmentMtime(projectDir) {
25202
25527
  }
25203
25528
  for (const e of entries) {
25204
25529
  if (!e.isDirectory()) continue;
25205
- const assignmentMd = resolve56(assignmentsRoot, e.name, "assignment.md");
25530
+ const assignmentMd = resolve58(assignmentsRoot, e.name, "assignment.md");
25206
25531
  try {
25207
25532
  const s = await stat5(assignmentMd);
25208
25533
  if (s.mtimeMs > newest) newest = s.mtimeMs;
@@ -25226,8 +25551,8 @@ init_fs();
25226
25551
  init_parser();
25227
25552
  init_types();
25228
25553
  init_paths();
25229
- import { resolve as resolve57 } from "path";
25230
- import { readFile as readFile35, readdir as readdir20 } from "fs/promises";
25554
+ import { resolve as resolve59 } from "path";
25555
+ import { readFile as readFile37, readdir as readdir20 } from "fs/promises";
25231
25556
  var CATEGORY4 = "assignment";
25232
25557
  var STATUSES_REQUIRING_HANDOFF = /* @__PURE__ */ new Set(["review", "completed"]);
25233
25558
  var PRE_WORKSPACE_STATUSES = /* @__PURE__ */ new Set([
@@ -25321,7 +25646,7 @@ var invalidStatus = {
25321
25646
  const allowed = configuredStatuses(ctx);
25322
25647
  const results = [];
25323
25648
  for (const a of withAssignmentMd) {
25324
- const path = resolve57(a.assignmentDir, "assignment.md");
25649
+ const path = resolve59(a.assignmentDir, "assignment.md");
25325
25650
  const parsed = await parseSafe2(path);
25326
25651
  if (!parsed) continue;
25327
25652
  if (!allowed.has(parsed.status)) {
@@ -25354,7 +25679,7 @@ var workspaceMissing = {
25354
25679
  const terminal = terminalStatuses(ctx);
25355
25680
  const results = [];
25356
25681
  for (const a of withAssignmentMd) {
25357
- const path = resolve57(a.assignmentDir, "assignment.md");
25682
+ const path = resolve59(a.assignmentDir, "assignment.md");
25358
25683
  const parsed = await parseSafe2(path);
25359
25684
  if (!parsed) continue;
25360
25685
  if (terminal.has(parsed.status)) continue;
@@ -25401,12 +25726,12 @@ var requiredFilesByStatus = {
25401
25726
  const { withAssignmentMd } = await listAssignments(ctx);
25402
25727
  const results = [];
25403
25728
  for (const a of withAssignmentMd) {
25404
- const assignmentPath = resolve57(a.assignmentDir, "assignment.md");
25729
+ const assignmentPath = resolve59(a.assignmentDir, "assignment.md");
25405
25730
  const parsed = await parseSafe2(assignmentPath);
25406
25731
  if (!parsed) continue;
25407
25732
  const missing = [];
25408
25733
  if (STATUSES_REQUIRING_HANDOFF.has(parsed.status)) {
25409
- const handoffPath = resolve57(a.assignmentDir, "handoff.md");
25734
+ const handoffPath = resolve59(a.assignmentDir, "handoff.md");
25410
25735
  if (!await fileExists(handoffPath)) missing.push("handoff.md");
25411
25736
  }
25412
25737
  if (missing.length === 0) continue;
@@ -25416,7 +25741,7 @@ var requiredFilesByStatus = {
25416
25741
  title: this.title,
25417
25742
  status: "warn",
25418
25743
  detail: `${a.projectSlug}/${a.assignmentSlug} (status: ${parsed.status}) is missing ${missing.join(", ")}`,
25419
- affected: missing.map((m) => resolve57(a.assignmentDir, m)),
25744
+ affected: missing.map((m) => resolve59(a.assignmentDir, m)),
25420
25745
  remediation: {
25421
25746
  kind: "manual",
25422
25747
  suggestion: `Create the missing ${missing.join(" and ")} files for this assignment`,
@@ -25439,7 +25764,7 @@ var companionFilesScaffolded = {
25439
25764
  for (const a of withAssignmentMd) {
25440
25765
  const missing = [];
25441
25766
  for (const filename of ["progress.md", "comments.md"]) {
25442
- if (!await fileExists(resolve57(a.assignmentDir, filename))) {
25767
+ if (!await fileExists(resolve59(a.assignmentDir, filename))) {
25443
25768
  missing.push(filename);
25444
25769
  }
25445
25770
  }
@@ -25451,7 +25776,7 @@ var companionFilesScaffolded = {
25451
25776
  title: this.title,
25452
25777
  status: "warn",
25453
25778
  detail: `${label} is missing ${missing.join(" and ")} (pre-v2.0 assignment \u2014 not required, but scaffolding them keeps the dashboard and CLIs consistent)`,
25454
- affected: missing.map((m) => resolve57(a.assignmentDir, m)),
25779
+ affected: missing.map((m) => resolve59(a.assignmentDir, m)),
25455
25780
  remediation: {
25456
25781
  kind: "manual",
25457
25782
  suggestion: `Create ${missing.join(" and ")} with the renderProgress/renderComments templates, or re-scaffold via the CLI`,
@@ -25484,7 +25809,7 @@ var typeDefinition = {
25484
25809
  const { withAssignmentMd } = await listAssignments(ctx);
25485
25810
  const results = [];
25486
25811
  for (const a of withAssignmentMd) {
25487
- const path = resolve57(a.assignmentDir, "assignment.md");
25812
+ const path = resolve59(a.assignmentDir, "assignment.md");
25488
25813
  const parsed = await parseSafe2(path);
25489
25814
  if (!parsed) continue;
25490
25815
  if (!parsed.type) continue;
@@ -25518,7 +25843,7 @@ var projectFrontmatterMatchesContainer = {
25518
25843
  const { withAssignmentMd } = await listAssignments(ctx);
25519
25844
  const results = [];
25520
25845
  for (const a of withAssignmentMd) {
25521
- const path = resolve57(a.assignmentDir, "assignment.md");
25846
+ const path = resolve59(a.assignmentDir, "assignment.md");
25522
25847
  const parsed = await parseSafe2(path);
25523
25848
  if (!parsed) continue;
25524
25849
  if (a.standalone) {
@@ -25569,13 +25894,13 @@ var draftMissingObjective = {
25569
25894
  const { withAssignmentMd } = await listAssignments(ctx);
25570
25895
  const results = [];
25571
25896
  for (const a of withAssignmentMd) {
25572
- const path = resolve57(a.assignmentDir, "assignment.md");
25897
+ const path = resolve59(a.assignmentDir, "assignment.md");
25573
25898
  const parsed = await parseSafe2(path);
25574
25899
  if (!parsed) continue;
25575
25900
  if (parsed.status !== "draft") continue;
25576
25901
  let raw;
25577
25902
  try {
25578
- raw = await readFile35(path, "utf-8");
25903
+ raw = await readFile37(path, "utf-8");
25579
25904
  } catch {
25580
25905
  continue;
25581
25906
  }
@@ -25608,7 +25933,7 @@ var readyToImplementMissingPlan = {
25608
25933
  const { withAssignmentMd } = await listAssignments(ctx);
25609
25934
  const results = [];
25610
25935
  for (const a of withAssignmentMd) {
25611
- const path = resolve57(a.assignmentDir, "assignment.md");
25936
+ const path = resolve59(a.assignmentDir, "assignment.md");
25612
25937
  const parsed = await parseSafe2(path);
25613
25938
  if (!parsed) continue;
25614
25939
  if (parsed.status !== "ready_to_implement") continue;
@@ -25617,7 +25942,7 @@ var readyToImplementMissingPlan = {
25617
25942
  let hasPlanContent = false;
25618
25943
  for (const f of planFiles) {
25619
25944
  try {
25620
- const c2 = await readFile35(resolve57(a.assignmentDir, f), "utf-8");
25945
+ const c2 = await readFile37(resolve59(a.assignmentDir, f), "utf-8");
25621
25946
  if (c2.trim().length > 0) {
25622
25947
  hasPlanContent = true;
25623
25948
  break;
@@ -25633,7 +25958,7 @@ var readyToImplementMissingPlan = {
25633
25958
  title: this.title,
25634
25959
  status: "warn",
25635
25960
  detail: `${label} (status: ready_to_implement) has no plan.md or plan-v<N>.md`,
25636
- affected: [resolve57(a.assignmentDir, "plan.md")],
25961
+ affected: [resolve59(a.assignmentDir, "plan.md")],
25637
25962
  remediation: {
25638
25963
  kind: "manual",
25639
25964
  suggestion: `Write a plan with '/plan-assignment' (or 'syntaur plan'), then re-mark ready_to_implement`,
@@ -25660,7 +25985,7 @@ var assignmentChecks = [
25660
25985
  ];
25661
25986
  async function parseSafe2(path) {
25662
25987
  try {
25663
- const content = await readFile35(path, "utf-8");
25988
+ const content = await readFile37(path, "utf-8");
25664
25989
  return parseAssignmentFull(content);
25665
25990
  } catch {
25666
25991
  return null;
@@ -25679,7 +26004,7 @@ function pass4(check, detail) {
25679
26004
 
25680
26005
  // src/utils/doctor/checks/dashboard.ts
25681
26006
  init_fs();
25682
- import { resolve as resolve58 } from "path";
26007
+ import { resolve as resolve60 } from "path";
25683
26008
  var CATEGORY5 = "dashboard";
25684
26009
  var dbReachable = {
25685
26010
  id: "dashboard.db-reachable",
@@ -25693,7 +26018,7 @@ var dbReachable = {
25693
26018
  title: this.title,
25694
26019
  status: "error",
25695
26020
  detail: `could not open syntaur.db: ${ctx.dbError ?? "unknown error"}`,
25696
- affected: [resolve58(ctx.syntaurRoot, "syntaur.db")],
26021
+ affected: [resolve60(ctx.syntaurRoot, "syntaur.db")],
25697
26022
  remediation: {
25698
26023
  kind: "manual",
25699
26024
  suggestion: "Start the dashboard once (`syntaur dashboard`) to initialize the DB, or restore it from backup",
@@ -25711,7 +26036,7 @@ var dbReachable = {
25711
26036
  title: this.title,
25712
26037
  status: "error",
25713
26038
  detail: 'syntaur.db is missing the expected "sessions" table',
25714
- affected: [resolve58(ctx.syntaurRoot, "syntaur.db")],
26039
+ affected: [resolve60(ctx.syntaurRoot, "syntaur.db")],
25715
26040
  autoFixable: false
25716
26041
  };
25717
26042
  }
@@ -25723,7 +26048,7 @@ var dbReachable = {
25723
26048
  title: this.title,
25724
26049
  status: "error",
25725
26050
  detail: `syntaur.db query failed: ${err2 instanceof Error ? err2.message : String(err2)}`,
25726
- affected: [resolve58(ctx.syntaurRoot, "syntaur.db")],
26051
+ affected: [resolve60(ctx.syntaurRoot, "syntaur.db")],
25727
26052
  autoFixable: false
25728
26053
  };
25729
26054
  }
@@ -25749,7 +26074,7 @@ var ghostSessions = {
25749
26074
  const results = [];
25750
26075
  for (const row of rows) {
25751
26076
  if (!row.project_slug) continue;
25752
- const projectPath = resolve58(projectsDir2, row.project_slug, "project.md");
26077
+ const projectPath = resolve60(projectsDir2, row.project_slug, "project.md");
25753
26078
  if (!await fileExists(projectPath)) {
25754
26079
  results.push({
25755
26080
  id: this.id,
@@ -25768,7 +26093,7 @@ var ghostSessions = {
25768
26093
  continue;
25769
26094
  }
25770
26095
  if (row.assignment_slug) {
25771
- const assignmentPath = resolve58(
26096
+ const assignmentPath = resolve60(
25772
26097
  projectsDir2,
25773
26098
  row.project_slug,
25774
26099
  "assignments",
@@ -25820,9 +26145,9 @@ function skipped(check, reason) {
25820
26145
 
25821
26146
  // src/utils/doctor/checks/integrations.ts
25822
26147
  init_fs();
25823
- import { resolve as resolve59, dirname as dirname18, basename as basename5 } from "path";
25824
- import { readdir as readdir21, readFile as readFile36 } from "fs/promises";
25825
- import { homedir as homedir9 } from "os";
26148
+ import { resolve as resolve61, dirname as dirname18, basename as basename5 } from "path";
26149
+ import { readdir as readdir21, readFile as readFile38 } from "fs/promises";
26150
+ import { homedir as homedir10 } from "os";
25826
26151
  var CATEGORY6 = "integrations";
25827
26152
  var claudePluginLinked = {
25828
26153
  id: "integrations.claude-plugin-linked",
@@ -25903,10 +26228,10 @@ var backupConfigured = {
25903
26228
  }
25904
26229
  };
25905
26230
  async function readKnownMarketplaces() {
25906
- const path = resolve59(homedir9(), ".claude", "plugins", "known_marketplaces.json");
26231
+ const path = resolve61(homedir10(), ".claude", "plugins", "known_marketplaces.json");
25907
26232
  if (!await fileExists(path)) return {};
25908
26233
  try {
25909
- const raw = await readFile36(path, "utf-8");
26234
+ const raw = await readFile38(path, "utf-8");
25910
26235
  return JSON.parse(raw);
25911
26236
  } catch {
25912
26237
  return {};
@@ -25940,7 +26265,7 @@ var claudeMarketplaceRegistered = {
25940
26265
  };
25941
26266
  }
25942
26267
  const marketplaceRoot = dirname18(pluginsParent);
25943
- const marketplaceManifest = resolve59(marketplaceRoot, ".claude-plugin", "marketplace.json");
26268
+ const marketplaceManifest = resolve61(marketplaceRoot, ".claude-plugin", "marketplace.json");
25944
26269
  if (!await fileExists(marketplaceManifest)) {
25945
26270
  return {
25946
26271
  id: this.id,
@@ -25959,7 +26284,7 @@ var claudeMarketplaceRegistered = {
25959
26284
  }
25960
26285
  let parsed = {};
25961
26286
  try {
25962
- parsed = JSON.parse(await readFile36(marketplaceManifest, "utf-8"));
26287
+ parsed = JSON.parse(await readFile38(marketplaceManifest, "utf-8"));
25963
26288
  } catch {
25964
26289
  return {
25965
26290
  id: this.id,
@@ -25991,7 +26316,7 @@ var claudeMarketplaceRegistered = {
25991
26316
  title: this.title,
25992
26317
  status: "error",
25993
26318
  detail: issues.join("; "),
25994
- affected: [marketplaceManifest, resolve59(homedir9(), ".claude", "plugins", "known_marketplaces.json")],
26319
+ affected: [marketplaceManifest, resolve61(homedir10(), ".claude", "plugins", "known_marketplaces.json")],
25995
26320
  remediation: {
25996
26321
  kind: "manual",
25997
26322
  suggestion: "Re-run install-plugin to ensure both files are in sync.",
@@ -26031,8 +26356,8 @@ function skipped2(check, reason) {
26031
26356
  init_fs();
26032
26357
  init_parser();
26033
26358
  init_types();
26034
- import { resolve as resolve60 } from "path";
26035
- import { readFile as readFile37 } from "fs/promises";
26359
+ import { resolve as resolve62 } from "path";
26360
+ import { readFile as readFile39 } from "fs/promises";
26036
26361
  var CATEGORY7 = "workspace";
26037
26362
  var ASSIGNMENT_FIELDS = ["projectSlug", "assignmentSlug", "projectDir", "assignmentDir"];
26038
26363
  var BUNDLE_FIELDS = ["bundleId", "bundleScope", "bundleScopeId"];
@@ -26053,12 +26378,12 @@ function isStandaloneSession(ctx) {
26053
26378
  return !hasAnyAssignmentField(ctx) && !hasAnyBundleField(ctx) && typeof ctx.sessionId === "string" && ctx.sessionId.length > 0;
26054
26379
  }
26055
26380
  async function loadContext(ctx) {
26056
- const path = resolve60(ctx.cwd, ".syntaur", "context.json");
26381
+ const path = resolve62(ctx.cwd, ".syntaur", "context.json");
26057
26382
  if (!await fileExists(path)) {
26058
26383
  return { data: null, path, exists: false, parseError: null };
26059
26384
  }
26060
26385
  try {
26061
- const raw = await readFile37(path, "utf-8");
26386
+ const raw = await readFile39(path, "utf-8");
26062
26387
  return { data: JSON.parse(raw), path, exists: true, parseError: null };
26063
26388
  } catch (err2) {
26064
26389
  return {
@@ -26152,7 +26477,7 @@ var contextAssignmentResolves = {
26152
26477
  if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to resolve");
26153
26478
  if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to resolve");
26154
26479
  if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
26155
- const assignmentMd = resolve60(data.assignmentDir, "assignment.md");
26480
+ const assignmentMd = resolve62(data.assignmentDir, "assignment.md");
26156
26481
  if (!await fileExists(assignmentMd)) {
26157
26482
  return {
26158
26483
  id: this.id,
@@ -26182,10 +26507,10 @@ var contextTerminal = {
26182
26507
  if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to check");
26183
26508
  if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to check");
26184
26509
  if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
26185
- const assignmentMd = resolve60(data.assignmentDir, "assignment.md");
26510
+ const assignmentMd = resolve62(data.assignmentDir, "assignment.md");
26186
26511
  if (!await fileExists(assignmentMd)) return skipped3(this, "assignment file missing");
26187
26512
  try {
26188
- const content = await readFile37(assignmentMd, "utf-8");
26513
+ const content = await readFile39(assignmentMd, "utf-8");
26189
26514
  const parsed = parseAssignmentFull(content);
26190
26515
  const terminal = terminalStatuses2(ctx);
26191
26516
  if (terminal.has(parsed.status)) {
@@ -26241,7 +26566,7 @@ function skipped3(check, reason) {
26241
26566
  init_config2();
26242
26567
  import { isAbsolute as isAbsolute9 } from "path";
26243
26568
  import { access as access2, constants as fsConstants } from "fs/promises";
26244
- import { spawnSync as spawnSync7 } from "child_process";
26569
+ import { spawnSync as spawnSync8 } from "child_process";
26245
26570
  var CATEGORY8 = "agents";
26246
26571
  var agentsResolvable = {
26247
26572
  id: "agents.commands-resolvable",
@@ -26305,7 +26630,7 @@ async function checkAgent(agent) {
26305
26630
  };
26306
26631
  }
26307
26632
  }
26308
- const result = spawnSync7("which", [agent.command], { encoding: "utf-8" });
26633
+ const result = spawnSync8("which", [agent.command], { encoding: "utf-8" });
26309
26634
  if (result.status === 0 && result.stdout.trim().length > 0) {
26310
26635
  return {
26311
26636
  ...base,
@@ -26330,16 +26655,16 @@ var agentChecks = [agentsResolvable];
26330
26655
 
26331
26656
  // src/utils/doctor/checks/terminal.ts
26332
26657
  init_config2();
26333
- import { spawnSync as spawnSync8 } from "child_process";
26334
- import { readFile as readFile38 } from "fs/promises";
26335
- import { resolve as resolve61 } from "path";
26658
+ import { spawnSync as spawnSync9 } from "child_process";
26659
+ import { readFile as readFile40 } from "fs/promises";
26660
+ import { resolve as resolve63 } from "path";
26336
26661
  init_paths();
26337
26662
  init_fs();
26338
26663
  var CATEGORY9 = "terminal";
26339
26664
  async function readRawTerminalKey() {
26340
- const configPath = resolve61(syntaurRoot(), "config.md");
26665
+ const configPath = resolve63(syntaurRoot(), "config.md");
26341
26666
  if (!await fileExists(configPath)) return null;
26342
- const content = await readFile38(configPath, "utf-8");
26667
+ const content = await readFile40(configPath, "utf-8");
26343
26668
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
26344
26669
  if (!fmMatch) return null;
26345
26670
  const line = fmMatch[1].split("\n").find((l) => /^terminal:\s*/.test(l));
@@ -26452,7 +26777,7 @@ var kittyRemoteControl = {
26452
26777
  autoFixable: false
26453
26778
  };
26454
26779
  }
26455
- const result = spawnSync8("kitty", ["@", "ls"], {
26780
+ const result = spawnSync9("kitty", ["@", "ls"], {
26456
26781
  encoding: "utf-8",
26457
26782
  timeout: 2e3
26458
26783
  });
@@ -26489,13 +26814,13 @@ var terminalChecks = [
26489
26814
 
26490
26815
  // src/utils/doctor/checks/skills.ts
26491
26816
  init_fs();
26492
- import { resolve as resolve62, join as join11 } from "path";
26493
- import { readdir as readdir22, readFile as readFile39, lstat as lstat4 } from "fs/promises";
26494
- import { homedir as homedir10 } from "os";
26817
+ import { resolve as resolve64, join as join11 } from "path";
26818
+ import { readdir as readdir22, readFile as readFile41, lstat as lstat4 } from "fs/promises";
26819
+ import { homedir as homedir11 } from "os";
26495
26820
  var CATEGORY10 = "skills";
26496
26821
  var skillTargets = [
26497
- { agent: "claude", dir: resolve62(homedir10(), ".claude", "skills"), label: "~/.claude/skills" },
26498
- { agent: "codex", dir: resolve62(homedir10(), ".codex", "skills"), label: "~/.codex/skills" }
26822
+ { agent: "claude", dir: resolve64(homedir11(), ".claude", "skills"), label: "~/.claude/skills" },
26823
+ { agent: "codex", dir: resolve64(homedir11(), ".codex", "skills"), label: "~/.codex/skills" }
26499
26824
  ];
26500
26825
  var skillsDedupCheck = {
26501
26826
  id: "skills.dedup",
@@ -26519,7 +26844,7 @@ var skillsDedupCheck = {
26519
26844
  if (!KNOWN_SKILLS.includes(entry.name)) continue;
26520
26845
  const skillMd = join11(dir, entry.name, "SKILL.md");
26521
26846
  if (!await fileExists(skillMd)) continue;
26522
- const content = await readFile39(skillMd, "utf-8").catch(() => "");
26847
+ const content = await readFile41(skillMd, "utf-8").catch(() => "");
26523
26848
  const match = content.match(/^name:\s*(\S+)\s*$/m);
26524
26849
  if (!match || match[1] !== entry.name) continue;
26525
26850
  let isSymlink2 = false;
@@ -26567,14 +26892,101 @@ var skillsDedupCheck = {
26567
26892
  };
26568
26893
  var skillsChecks = [skillsDedupCheck];
26569
26894
 
26895
+ // src/utils/doctor/checks/cross-agent.ts
26896
+ init_fs();
26897
+ import { join as join12, resolve as resolve65 } from "path";
26898
+ var CATEGORY11 = "cross-agent";
26899
+ async function countSyntaurSkills(dir) {
26900
+ if (!await fileExists(dir)) return 0;
26901
+ let n = 0;
26902
+ for (const skill of KNOWN_SKILLS) {
26903
+ if (await fileExists(join12(dir, skill, "SKILL.md"))) n++;
26904
+ }
26905
+ return n;
26906
+ }
26907
+ var crossAgentSkillsCheck = {
26908
+ id: "cross-agent.skills",
26909
+ category: CATEGORY11,
26910
+ title: "Cross-agent targets have Syntaur skills + protocol files",
26911
+ async run(ctx) {
26912
+ const installed = ctx.config.integrations.installedAgents ?? {};
26913
+ const total = KNOWN_SKILLS.length;
26914
+ const lines = [];
26915
+ const problems = [];
26916
+ const affected = [];
26917
+ let considered = 0;
26918
+ for (const t of AGENT_TARGETS) {
26919
+ if (t.nativePlugin) continue;
26920
+ const dir = t.skillsDir?.global;
26921
+ if (!dir) continue;
26922
+ const recorded = Boolean(installed[t.id]);
26923
+ const detected = await t.detect();
26924
+ if (!recorded && !detected) continue;
26925
+ considered++;
26926
+ const present = await countSyntaurSkills(dir);
26927
+ lines.push(`${t.displayName}: ${present}/${total} skills (${dir})`);
26928
+ if (recorded && present < total) {
26929
+ problems.push(
26930
+ `${t.displayName}: ${present === 0 ? "no Syntaur skills" : `incomplete skills (${present}/${total})`}`
26931
+ );
26932
+ affected.push(dir);
26933
+ }
26934
+ if (recorded && t.instructions) {
26935
+ for (const f of t.instructions.files) {
26936
+ const p = resolve65(ctx.cwd, f.path);
26937
+ if (!await fileExists(p)) {
26938
+ problems.push(`${t.displayName}: missing protocol file ${f.path} in cwd`);
26939
+ affected.push(p);
26940
+ }
26941
+ }
26942
+ }
26943
+ }
26944
+ if (considered === 0) {
26945
+ return {
26946
+ id: this.id,
26947
+ category: this.category,
26948
+ title: this.title,
26949
+ status: "skipped",
26950
+ detail: "No cross-agent targets detected or recorded.",
26951
+ autoFixable: false
26952
+ };
26953
+ }
26954
+ if (problems.length > 0) {
26955
+ return {
26956
+ id: this.id,
26957
+ category: this.category,
26958
+ title: this.title,
26959
+ status: "warn",
26960
+ detail: `${problems.join("; ")}. (${lines.join("; ")})`,
26961
+ affected,
26962
+ remediation: {
26963
+ kind: "manual",
26964
+ suggestion: "Re-run `syntaur setup --target <id>` (from the assignment workspace, to also write protocol files) to complete the install.",
26965
+ command: null
26966
+ },
26967
+ autoFixable: false
26968
+ };
26969
+ }
26970
+ return {
26971
+ id: this.id,
26972
+ category: this.category,
26973
+ title: this.title,
26974
+ status: "pass",
26975
+ detail: lines.join("; "),
26976
+ autoFixable: false
26977
+ };
26978
+ }
26979
+ };
26980
+ var crossAgentChecks = [crossAgentSkillsCheck];
26981
+
26570
26982
  // src/utils/doctor/checks/bundles.ts
26571
26983
  init_fs();
26572
26984
  init_paths();
26573
- import { resolve as resolve63 } from "path";
26985
+ import { resolve as resolve66 } from "path";
26574
26986
  import { readdir as readdir23 } from "fs/promises";
26575
- import { spawnSync as spawnSync9 } from "child_process";
26987
+ import { spawnSync as spawnSync10 } from "child_process";
26576
26988
  init_parser2();
26577
- var CATEGORY11 = "bundles";
26989
+ var CATEGORY12 = "bundles";
26578
26990
  async function listScopes(ctx) {
26579
26991
  const out = [];
26580
26992
  const td = todosDir();
@@ -26600,7 +27012,7 @@ async function listScopes(ctx) {
26600
27012
  if (!e.isDirectory()) continue;
26601
27013
  const slug = e.name;
26602
27014
  if (typeof slug !== "string" || slug.startsWith(".")) continue;
26603
- const projectMd = resolve63(ctx.config.defaultProjectDir, slug, "project.md");
27015
+ const projectMd = resolve66(ctx.config.defaultProjectDir, slug, "project.md");
26604
27016
  if (!await fileExists(projectMd)) continue;
26605
27017
  out.push({
26606
27018
  scopeLabel: `project:${slug}`,
@@ -26655,7 +27067,7 @@ async function gatherBundlesByScope(scopes) {
26655
27067
  }
26656
27068
  var orphanBundleId = {
26657
27069
  id: "bundles.orphan-bundleid",
26658
- category: CATEGORY11,
27070
+ category: CATEGORY12,
26659
27071
  title: "Every todo with a bundleId points at an existing bundle in the same scope",
26660
27072
  async run(ctx) {
26661
27073
  const scopes = await listScopes(ctx);
@@ -26678,7 +27090,7 @@ var orphanBundleId = {
26678
27090
  };
26679
27091
  var missingMembers = {
26680
27092
  id: "bundles.missing-members",
26681
- category: CATEGORY11,
27093
+ category: CATEGORY12,
26682
27094
  title: "Every bundle member exists in the bundle's scope checklist",
26683
27095
  async run(ctx) {
26684
27096
  const scopes = await listScopes(ctx);
@@ -26697,7 +27109,7 @@ var missingMembers = {
26697
27109
  };
26698
27110
  var scopeMismatch = {
26699
27111
  id: "bundles.scope-mismatch",
26700
- category: CATEGORY11,
27112
+ category: CATEGORY12,
26701
27113
  title: "Every bundle member's bundleId matches the bundle id",
26702
27114
  async run(ctx) {
26703
27115
  const scopes = await listScopes(ctx);
@@ -26715,7 +27127,7 @@ var scopeMismatch = {
26715
27127
  };
26716
27128
  var minMembers = {
26717
27129
  id: "bundles.min-members",
26718
- category: CATEGORY11,
27130
+ category: CATEGORY12,
26719
27131
  title: "Every bundle has at least 2 members",
26720
27132
  async run(ctx) {
26721
27133
  const scopes = await listScopes(ctx);
@@ -26731,7 +27143,7 @@ var minMembers = {
26731
27143
  };
26732
27144
  var stalePlanDir = {
26733
27145
  id: "bundles.stale-plan-dir",
26734
- category: CATEGORY11,
27146
+ category: CATEGORY12,
26735
27147
  title: "Every bundle's persisted planDir still exists on disk",
26736
27148
  async run(ctx) {
26737
27149
  const scopes = await listScopes(ctx);
@@ -26755,7 +27167,7 @@ var stalePlanDir = {
26755
27167
  };
26756
27168
  var staleWorktree = {
26757
27169
  id: "bundles.stale-worktree",
26758
- category: CATEGORY11,
27170
+ category: CATEGORY12,
26759
27171
  title: "Every bundle's persisted worktree still exists in the repo",
26760
27172
  async run(ctx) {
26761
27173
  const scopes = await listScopes(ctx);
@@ -26765,7 +27177,7 @@ var staleWorktree = {
26765
27177
  if (bs.bundle.worktreePath === null || bs.bundle.repository === null) continue;
26766
27178
  const onDisk = await fileExists(bs.bundle.worktreePath);
26767
27179
  let gitKnowsIt = false;
26768
- const gitOut = spawnSync9("git", ["-C", bs.bundle.repository, "worktree", "list", "--porcelain"], { encoding: "utf-8" });
27180
+ const gitOut = spawnSync10("git", ["-C", bs.bundle.repository, "worktree", "list", "--porcelain"], { encoding: "utf-8" });
26769
27181
  if (gitOut.status === 0) {
26770
27182
  gitKnowsIt = gitOut.stdout.split("\n").some((l) => l.trim() === `worktree ${bs.bundle.worktreePath}`);
26771
27183
  }
@@ -26805,6 +27217,7 @@ function allChecks() {
26805
27217
  ...agentChecks,
26806
27218
  ...terminalChecks,
26807
27219
  ...skillsChecks,
27220
+ ...crossAgentChecks,
26808
27221
  ...bundleChecks
26809
27222
  ];
26810
27223
  }
@@ -26891,7 +27304,7 @@ async function readVersion() {
26891
27304
  let dir = dirname19(here);
26892
27305
  for (let i = 0; i < 6; i++) {
26893
27306
  try {
26894
- const raw = await readFile40(join12(dir, "package.json"), "utf-8");
27307
+ const raw = await readFile42(join13(dir, "package.json"), "utf-8");
26895
27308
  const parsed = JSON.parse(raw);
26896
27309
  return typeof parsed.version === "string" ? parsed.version : null;
26897
27310
  } catch {
@@ -26988,7 +27401,7 @@ var REQUIRED_WORKSPACE_FIELDS = [
26988
27401
  ];
26989
27402
  var ISO_DATE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
26990
27403
  async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
26991
- const absolute = isAbsolute10(inputPath) ? inputPath : resolve64(cwd, inputPath);
27404
+ const absolute = isAbsolute10(inputPath) ? inputPath : resolve67(cwd, inputPath);
26992
27405
  const errors = [];
26993
27406
  const warnings = [];
26994
27407
  if (!await fileExists(absolute)) {
@@ -27001,7 +27414,7 @@ async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
27001
27414
  }
27002
27415
  let content;
27003
27416
  try {
27004
- content = await readFile41(absolute, "utf-8");
27417
+ content = await readFile43(absolute, "utf-8");
27005
27418
  } catch (err2) {
27006
27419
  return {
27007
27420
  ok: false,
@@ -27325,8 +27738,8 @@ init_uuid();
27325
27738
  init_timestamp();
27326
27739
  init_assignment_resolver();
27327
27740
  init_templates();
27328
- import { resolve as resolve65 } from "path";
27329
- import { readFile as readFile42 } from "fs/promises";
27741
+ import { resolve as resolve68 } from "path";
27742
+ import { readFile as readFile44 } from "fs/promises";
27330
27743
  function shortId() {
27331
27744
  return generateId().split("-")[0];
27332
27745
  }
@@ -27356,7 +27769,7 @@ async function commentCommand(target, text, options = {}) {
27356
27769
  if (!isValidSlug(target)) {
27357
27770
  throw new Error(`Invalid assignment slug "${target}".`);
27358
27771
  }
27359
- assignmentDir = resolve65(baseDir, options.project, "assignments", target);
27772
+ assignmentDir = resolve68(baseDir, options.project, "assignments", target);
27360
27773
  assignmentRef = target;
27361
27774
  } else {
27362
27775
  const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
@@ -27366,13 +27779,13 @@ async function commentCommand(target, text, options = {}) {
27366
27779
  assignmentDir = resolved.assignmentDir;
27367
27780
  assignmentRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
27368
27781
  }
27369
- const commentsPath = resolve65(assignmentDir, "comments.md");
27782
+ const commentsPath = resolve68(assignmentDir, "comments.md");
27370
27783
  const timestamp = nowTimestamp();
27371
27784
  const author = options.author ?? process.env.USER ?? "unknown";
27372
27785
  let currentContent;
27373
27786
  let currentCount = 0;
27374
27787
  if (await fileExists(commentsPath)) {
27375
- currentContent = await readFile42(commentsPath, "utf-8");
27788
+ currentContent = await readFile44(commentsPath, "utf-8");
27376
27789
  const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
27377
27790
  if (countMatch) currentCount = parseInt(countMatch[1], 10);
27378
27791
  } else {
@@ -27406,7 +27819,7 @@ ${entry}`;
27406
27819
  }
27407
27820
 
27408
27821
  // src/commands/capture.ts
27409
- import { resolve as resolve69, relative as relative4, dirname as dirname20 } from "path";
27822
+ import { resolve as resolve72, relative as relative4, dirname as dirname20 } from "path";
27410
27823
  import { copyFile as copyFile3, mkdir as mkdir9, realpath as realpath2, rm as rm12, stat as stat8, writeFile as writeFile14 } from "fs/promises";
27411
27824
  import { existsSync as existsSync6 } from "fs";
27412
27825
 
@@ -27417,8 +27830,8 @@ init_config2();
27417
27830
  init_slug();
27418
27831
  init_assignment_resolver();
27419
27832
  init_parser();
27420
- import { resolve as resolve66 } from "path";
27421
- import { readFile as readFile43 } from "fs/promises";
27833
+ import { resolve as resolve69 } from "path";
27834
+ import { readFile as readFile45 } from "fs/promises";
27422
27835
  var AssignmentTargetError = class extends Error {
27423
27836
  };
27424
27837
  function classifyContext(ctx) {
@@ -27430,10 +27843,10 @@ function classifyContext(ctx) {
27430
27843
  return "empty";
27431
27844
  }
27432
27845
  async function readAssignmentFrontmatterId(assignmentDir) {
27433
- const path = resolve66(assignmentDir, "assignment.md");
27846
+ const path = resolve69(assignmentDir, "assignment.md");
27434
27847
  if (!await fileExists(path)) return null;
27435
27848
  try {
27436
- const content = await readFile43(path, "utf-8");
27849
+ const content = await readFile45(path, "utf-8");
27437
27850
  const [fm] = extractFrontmatter(content);
27438
27851
  return getField(fm, "id");
27439
27852
  } catch {
@@ -27441,10 +27854,10 @@ async function readAssignmentFrontmatterId(assignmentDir) {
27441
27854
  }
27442
27855
  }
27443
27856
  async function readContextJson(cwd) {
27444
- const path = resolve66(cwd, ".syntaur", "context.json");
27857
+ const path = resolve69(cwd, ".syntaur", "context.json");
27445
27858
  if (!await fileExists(path)) return null;
27446
27859
  try {
27447
- const raw = await readFile43(path, "utf-8");
27860
+ const raw = await readFile45(path, "utf-8");
27448
27861
  return JSON.parse(raw);
27449
27862
  } catch {
27450
27863
  return null;
@@ -27465,15 +27878,15 @@ async function resolveAssignmentTarget(input4, opts = {}) {
27465
27878
  if (!isValidSlug(input4)) {
27466
27879
  throw new AssignmentTargetError(`Invalid assignment slug "${input4}".`);
27467
27880
  }
27468
- const projectDir = resolve66(baseDir, opts.project);
27469
- const projectMdPath = resolve66(projectDir, "project.md");
27881
+ const projectDir = resolve69(baseDir, opts.project);
27882
+ const projectMdPath = resolve69(projectDir, "project.md");
27470
27883
  if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
27471
27884
  throw new AssignmentTargetError(
27472
27885
  `Project "${opts.project}" not found at ${projectDir}.`
27473
27886
  );
27474
27887
  }
27475
- const assignmentDir = resolve66(projectDir, "assignments", input4);
27476
- const assignmentMdPath2 = resolve66(assignmentDir, "assignment.md");
27888
+ const assignmentDir = resolve69(projectDir, "assignments", input4);
27889
+ const assignmentMdPath2 = resolve69(assignmentDir, "assignment.md");
27477
27890
  if (!await fileExists(assignmentMdPath2)) {
27478
27891
  throw new AssignmentTargetError(
27479
27892
  `Assignment "${input4}" not found in project "${opts.project}".`
@@ -27512,7 +27925,7 @@ async function resolveAssignmentTarget(input4, opts = {}) {
27512
27925
  }
27513
27926
  if (ctx.assignmentDir) {
27514
27927
  const dir = expandHome(ctx.assignmentDir);
27515
- const assignmentMdPath2 = resolve66(dir, "assignment.md");
27928
+ const assignmentMdPath2 = resolve69(dir, "assignment.md");
27516
27929
  if (!await fileExists(assignmentMdPath2)) {
27517
27930
  throw new AssignmentTargetError(
27518
27931
  `.syntaur/context.json points to a missing assignment dir: ${dir}.`
@@ -27541,8 +27954,8 @@ async function resolveAssignmentTarget(input4, opts = {}) {
27541
27954
  `.syntaur/context.json contains invalid slugs: project="${ctx.projectSlug}" assignment="${ctx.assignmentSlug}".`
27542
27955
  );
27543
27956
  }
27544
- const assignmentDir = resolve66(baseDir, ctx.projectSlug, "assignments", ctx.assignmentSlug);
27545
- const assignmentMdPath2 = resolve66(assignmentDir, "assignment.md");
27957
+ const assignmentDir = resolve69(baseDir, ctx.projectSlug, "assignments", ctx.assignmentSlug);
27958
+ const assignmentMdPath2 = resolve69(assignmentDir, "assignment.md");
27546
27959
  if (!await fileExists(assignmentMdPath2)) {
27547
27960
  throw new AssignmentTargetError(
27548
27961
  `.syntaur/context.json points to a missing assignment: ${assignmentDir}.`
@@ -27605,7 +28018,7 @@ init_fs();
27605
28018
  import { spawn as spawn7 } from "child_process";
27606
28019
  import { mkdtemp as mkdtemp2, rm as rm8, stat as stat6 } from "fs/promises";
27607
28020
  import { tmpdir as tmpdir3 } from "os";
27608
- import { join as join13 } from "path";
28021
+ import { join as join14 } from "path";
27609
28022
  function argsFor(mode, pngPath) {
27610
28023
  switch (mode) {
27611
28024
  case "interactive":
@@ -27638,8 +28051,8 @@ async function captureScreenshot(mode) {
27638
28051
  "screencapture is only available on macOS. Use --file <path> to attach an existing image."
27639
28052
  );
27640
28053
  }
27641
- const tmpDir = await mkdtemp2(join13(tmpdir3(), "syntaur-screenshot-"));
27642
- const pngPath = join13(tmpDir, "shot.png");
28054
+ const tmpDir = await mkdtemp2(join14(tmpdir3(), "syntaur-screenshot-"));
28055
+ const pngPath = join14(tmpDir, "shot.png");
27643
28056
  const cleanup = async () => {
27644
28057
  await rm8(tmpDir, { recursive: true, force: true }).catch(() => {
27645
28058
  });
@@ -27677,9 +28090,9 @@ async function captureScreenshot(mode) {
27677
28090
 
27678
28091
  // src/utils/asciinema.ts
27679
28092
  import { spawn as spawn8 } from "child_process";
27680
- import { mkdtemp as mkdtemp3, readFile as readFile44, rm as rm9 } from "fs/promises";
28093
+ import { mkdtemp as mkdtemp3, readFile as readFile46, rm as rm9 } from "fs/promises";
27681
28094
  import { tmpdir as tmpdir4 } from "os";
27682
- import { join as join14 } from "path";
28095
+ import { join as join15 } from "path";
27683
28096
  var SAFE_RE = /^[A-Za-z0-9_@%+=:,./-]+$/;
27684
28097
  function shellQuote2(s) {
27685
28098
  if (s.length === 0) return `''`;
@@ -27716,8 +28129,8 @@ function runAsciinema(args, stdio) {
27716
28129
  });
27717
28130
  }
27718
28131
  async function captureAsciinema(opts) {
27719
- const tmpDir = await mkdtemp3(join14(tmpdir4(), "syntaur-asciinema-"));
27720
- const castPath = join14(tmpDir, "session.cast");
28132
+ const tmpDir = await mkdtemp3(join15(tmpdir4(), "syntaur-asciinema-"));
28133
+ const castPath = join15(tmpDir, "session.cast");
27721
28134
  const cleanup = async () => {
27722
28135
  await rm9(tmpDir, { recursive: true, force: true }).catch(() => {
27723
28136
  });
@@ -27740,7 +28153,7 @@ async function captureAsciinema(opts) {
27740
28153
  }
27741
28154
  throw err2;
27742
28155
  }
27743
- const text = await readFile44(castPath, "utf8").catch(() => null);
28156
+ const text = await readFile46(castPath, "utf8").catch(() => null);
27744
28157
  if (text === null) {
27745
28158
  throw new Error(
27746
28159
  `asciinema produced no cast file at ${castPath} (exit ${exitCode}). Try running 'asciinema rec ${castPath}' directly to diagnose.`
@@ -27768,9 +28181,9 @@ async function captureAsciinema(opts) {
27768
28181
  // src/utils/recording.ts
27769
28182
  init_paths();
27770
28183
  import { spawn as spawn9 } from "child_process";
27771
- import { mkdir as mkdir8, mkdtemp as mkdtemp4, open as open3, readFile as readFile45, rm as rm10, stat as stat7, unlink as unlink9, writeFile as writeFile13 } from "fs/promises";
28184
+ import { mkdir as mkdir8, mkdtemp as mkdtemp4, open as open3, readFile as readFile47, rm as rm10, stat as stat7, unlink as unlink9, writeFile as writeFile13 } from "fs/promises";
27772
28185
  import { tmpdir as tmpdir5 } from "os";
27773
- import { join as join15, resolve as resolve67 } from "path";
28186
+ import { join as join16, resolve as resolve70 } from "path";
27774
28187
  import { setTimeout as sleep } from "timers/promises";
27775
28188
  function sigintPollIntervalMs() {
27776
28189
  const raw = process.env.SYNTAUR_RECORDING_POLL_INTERVAL_MS;
@@ -27791,13 +28204,13 @@ function sigtermWaitMs() {
27791
28204
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : 1e3;
27792
28205
  }
27793
28206
  function pidfilePath() {
27794
- return resolve67(syntaurRoot(), "recording.pid");
28207
+ return resolve70(syntaurRoot(), "recording.pid");
27795
28208
  }
27796
28209
  function logPath2() {
27797
- return resolve67(syntaurRoot(), "recording.log");
28210
+ return resolve70(syntaurRoot(), "recording.log");
27798
28211
  }
27799
28212
  function sidecarPath() {
27800
- return resolve67(syntaurRoot(), "recording.json");
28213
+ return resolve70(syntaurRoot(), "recording.json");
27801
28214
  }
27802
28215
  function ffmpegArgs(device, fps, mp4Path) {
27803
28216
  return [
@@ -27842,7 +28255,7 @@ async function acquirePidfile(pidfile) {
27842
28255
  } catch (err2) {
27843
28256
  if (err2.code !== "EEXIST") throw err2;
27844
28257
  if (attempt === 1) throw err2;
27845
- const existing = (await readFile45(pidfile, "utf-8").catch(() => "")).trim();
28258
+ const existing = (await readFile47(pidfile, "utf-8").catch(() => "")).trim();
27846
28259
  if (existing.startsWith(STARTING_SENTINEL_PREFIX)) {
27847
28260
  const parentPidRaw = existing.slice(STARTING_SENTINEL_PREFIX.length);
27848
28261
  const parentPid = Number.parseInt(parentPidRaw, 10);
@@ -27895,8 +28308,8 @@ async function startRecording(input4) {
27895
28308
  let acquiredPid = null;
27896
28309
  try {
27897
28310
  logHandle = await open3(log, "w");
27898
- tmpDir = await mkdtemp4(join15(tmpdir5(), "syntaur-recording-"));
27899
- const mp4Path = join15(tmpDir, "recording.mp4");
28311
+ tmpDir = await mkdtemp4(join16(tmpdir5(), "syntaur-recording-"));
28312
+ const mp4Path = join16(tmpDir, "recording.mp4");
27900
28313
  let child;
27901
28314
  try {
27902
28315
  child = spawn9("ffmpeg", ffmpegArgs(input4.device, input4.fps, mp4Path), {
@@ -27944,7 +28357,7 @@ async function startRecording(input4) {
27944
28357
  logHandle = null;
27945
28358
  if (warmupMs > 0) await sleep(warmupMs);
27946
28359
  if (!await isProcessAlive(pid)) {
27947
- const tail = await readFile45(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
28360
+ const tail = await readFile47(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
27948
28361
  acquiredPid = null;
27949
28362
  throw new Error(
27950
28363
  `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}
@@ -28001,7 +28414,7 @@ ${tail}`
28001
28414
  async function stopRecording() {
28002
28415
  const pidfile = pidfilePath();
28003
28416
  const sidecar = sidecarPath();
28004
- const pidRaw = await readFile45(pidfile, "utf-8").catch(() => null);
28417
+ const pidRaw = await readFile47(pidfile, "utf-8").catch(() => null);
28005
28418
  if (pidRaw === null) {
28006
28419
  throw new Error(
28007
28420
  `No active recording found (no pidfile at ${pidfile}). Did you run --start?`
@@ -28011,7 +28424,7 @@ async function stopRecording() {
28011
28424
  if (!Number.isInteger(pid) || pid <= 0) {
28012
28425
  throw new Error(`Pidfile at ${pidfile} is corrupt (got "${pidRaw}").`);
28013
28426
  }
28014
- const sidecarRaw = await readFile45(sidecar, "utf-8").catch(() => null);
28427
+ const sidecarRaw = await readFile47(sidecar, "utf-8").catch(() => null);
28015
28428
  if (sidecarRaw === null) {
28016
28429
  throw new Error(
28017
28430
  `No recording sidecar at ${sidecar}. The recording state is inconsistent \u2014 delete ${pidfile} and re-run --start.`
@@ -28076,7 +28489,7 @@ async function stopRecording() {
28076
28489
  // src/db/proof-db.ts
28077
28490
  init_paths();
28078
28491
  import Database5 from "better-sqlite3";
28079
- import { resolve as resolve68 } from "path";
28492
+ import { resolve as resolve71 } from "path";
28080
28493
  var db4 = null;
28081
28494
  var PROOF_SCHEMA_VERSION = "1";
28082
28495
  var SCHEMA_SQL4 = `
@@ -28096,7 +28509,7 @@ CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);
28096
28509
  `;
28097
28510
  function initProofDb(dbPath) {
28098
28511
  if (db4) return db4;
28099
- const finalPath = dbPath ?? resolve68(syntaurRoot(), "syntaur.db");
28512
+ const finalPath = dbPath ?? resolve71(syntaurRoot(), "syntaur.db");
28100
28513
  db4 = new Database5(finalPath);
28101
28514
  db4.pragma("journal_mode = WAL");
28102
28515
  db4.exec(SCHEMA_SQL4);
@@ -28144,9 +28557,9 @@ function listArtifactsByAssignment(assignmentId) {
28144
28557
 
28145
28558
  // src/utils/transcribers/elevenlabs.ts
28146
28559
  import { spawn as spawn10 } from "child_process";
28147
- import { mkdtemp as mkdtemp5, readFile as readFile46, rm as rm11 } from "fs/promises";
28560
+ import { mkdtemp as mkdtemp5, readFile as readFile48, rm as rm11 } from "fs/promises";
28148
28561
  import { tmpdir as tmpdir6 } from "os";
28149
- import { join as join16 } from "path";
28562
+ import { join as join17 } from "path";
28150
28563
  var SCRIBE_URL = "https://api.elevenlabs.io/v1/speech-to-text";
28151
28564
  var NO_AUDIO_MARKERS = [
28152
28565
  "Stream map '0:a:0' matches no streams",
@@ -28208,7 +28621,7 @@ async function extractAudio(videoAbsPath, wavOut) {
28208
28621
  throw new TranscribeFfmpegError(`ffmpeg failed (exit ${result.code}): ${tail}`);
28209
28622
  }
28210
28623
  async function callScribe(wavPath, apiKey, opts) {
28211
- const audio = await readFile46(wavPath);
28624
+ const audio = await readFile48(wavPath);
28212
28625
  const form = new FormData();
28213
28626
  form.set("file", new Blob([new Uint8Array(audio)], { type: "audio/wav" }), "audio.wav");
28214
28627
  form.set("model_id", "scribe_v1");
@@ -28236,8 +28649,8 @@ var elevenLabsScribe = {
28236
28649
  "ELEVENLABS_API_KEY is not set. Export it (e.g. `export ELEVENLABS_API_KEY=\u2026`) and re-run. A config-file slot will land later."
28237
28650
  );
28238
28651
  }
28239
- const tmp = await mkdtemp5(join16(tmpdir6(), "syntaur-transcribe-"));
28240
- const wav = join16(tmp, "audio.wav");
28652
+ const tmp = await mkdtemp5(join17(tmpdir6(), "syntaur-transcribe-"));
28653
+ const wav = join17(tmp, "audio.wav");
28241
28654
  try {
28242
28655
  await extractAudio(videoAbsPath, wav);
28243
28656
  return await callScribe(wav, apiKey, opts);
@@ -28524,7 +28937,7 @@ async function captureCommand(target, options = {}) {
28524
28937
  });
28525
28938
  }
28526
28939
  if (options.file) {
28527
- const expanded = options.file.startsWith("~/") ? resolve69(process.env.HOME ?? "", options.file.slice(2)) : resolve69(options.file);
28940
+ const expanded = options.file.startsWith("~/") ? resolve72(process.env.HOME ?? "", options.file.slice(2)) : resolve72(options.file);
28528
28941
  if (!await fileExists(expanded)) {
28529
28942
  throw new Error(`--file does not exist: ${options.file}`);
28530
28943
  }
@@ -28565,7 +28978,7 @@ async function captureCommand(target, options = {}) {
28565
28978
  }
28566
28979
  initProofDb();
28567
28980
  const subdir = criterionIndex === null ? "untagged" : String(criterionIndex);
28568
- const destDir = resolve69(proofDir(resolved.assignmentDir), subdir);
28981
+ const destDir = resolve72(proofDir(resolved.assignmentDir), subdir);
28569
28982
  if (resolvedSource) await mkdir9(destDir, { recursive: true });
28570
28983
  const ext = resolvedSource ? extensionForKind(kind) : null;
28571
28984
  let id = null;
@@ -28574,7 +28987,7 @@ async function captureCommand(target, options = {}) {
28574
28987
  let lastErr = null;
28575
28988
  for (let attempt = 0; attempt < MAX_ID_RETRIES; attempt += 1) {
28576
28989
  const candidate = generateArtifactId();
28577
- const candidateAbsPath = resolvedSource && ext ? resolve69(destDir, `${candidate}.${ext}`) : null;
28990
+ const candidateAbsPath = resolvedSource && ext ? resolve72(destDir, `${candidate}.${ext}`) : null;
28578
28991
  const candidateRel = candidateAbsPath ? relative4(resolved.assignmentDir, candidateAbsPath) : null;
28579
28992
  try {
28580
28993
  insertArtifact({
@@ -28618,7 +29031,7 @@ async function captureCommand(target, options = {}) {
28618
29031
  }
28619
29032
  }
28620
29033
  if (options.transcribe && kind === "video" && absPath && id) {
28621
- const sidecarPath2 = resolve69(destDir, `${id}.transcript.md`);
29034
+ const sidecarPath2 = resolve72(destDir, `${id}.transcript.md`);
28622
29035
  if (existsSync6(sidecarPath2)) {
28623
29036
  console.warn(
28624
29037
  `transcript: ${sidecarPath2} already exists, skipping (delete to re-transcribe)`
@@ -28650,7 +29063,7 @@ async function captureCommand(target, options = {}) {
28650
29063
  const tagSuffix = criterionIndex === null ? "untagged" : `criterion ${criterionIndex}`;
28651
29064
  console.log(`Captured artifact ${id} (${kind}) for ${ref} \u2014 ${tagSuffix}.`);
28652
29065
  if (relativeFilePath) {
28653
- console.log(` file: ${resolve69(resolved.assignmentDir, relativeFilePath)}`);
29066
+ console.log(` file: ${resolve72(resolved.assignmentDir, relativeFilePath)}`);
28654
29067
  }
28655
29068
  } catch (err2) {
28656
29069
  if (options.stop && kind === "video" && shelloutCleanup && resolvedSource) {
@@ -28673,8 +29086,8 @@ async function captureCommand(target, options = {}) {
28673
29086
 
28674
29087
  // src/commands/proof.ts
28675
29088
  import { Command as Command6 } from "commander";
28676
- import { readFile as readFile47, writeFile as writeFile15, rename as rename9, stat as stat9 } from "fs/promises";
28677
- import { resolve as resolve70, relative as relative5, isAbsolute as isAbsolute11, dirname as dirname21 } from "path";
29089
+ import { readFile as readFile49, writeFile as writeFile15, rename as rename9, stat as stat9 } from "fs/promises";
29090
+ import { resolve as resolve73, relative as relative5, isAbsolute as isAbsolute11, dirname as dirname21 } from "path";
28678
29091
  import { randomBytes as randomBytes4 } from "crypto";
28679
29092
 
28680
29093
  // src/utils/acceptance-criteria-parse.ts
@@ -28914,11 +29327,11 @@ function renderProofHtml(params2, inlineFiles = /* @__PURE__ */ new Map(), trans
28914
29327
 
28915
29328
  // src/commands/proof.ts
28916
29329
  async function readAssignmentMeta(assignmentDir) {
28917
- const path = resolve70(assignmentDir, "assignment.md");
29330
+ const path = resolve73(assignmentDir, "assignment.md");
28918
29331
  if (!await fileExists(path)) {
28919
29332
  return { title: "", body: "" };
28920
29333
  }
28921
- const content = await readFile47(path, "utf-8");
29334
+ const content = await readFile49(path, "utf-8");
28922
29335
  const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
28923
29336
  let title = "";
28924
29337
  if (fmMatch) {
@@ -28965,7 +29378,7 @@ async function loadInlineFiles(rows, assignmentDir) {
28965
29378
  for (const r of rows) {
28966
29379
  if (!r.file_path) continue;
28967
29380
  if (r.kind !== "http" && r.kind !== "text") continue;
28968
- const abs = resolve70(assignmentDir, r.file_path);
29381
+ const abs = resolve73(assignmentDir, r.file_path);
28969
29382
  if (!isWithin(proofRoot, abs)) {
28970
29383
  out.set(r.file_path, null);
28971
29384
  continue;
@@ -28980,7 +29393,7 @@ async function loadInlineFiles(rows, assignmentDir) {
28980
29393
  continue;
28981
29394
  }
28982
29395
  try {
28983
- out.set(r.file_path, await readFile47(abs, "utf-8"));
29396
+ out.set(r.file_path, await readFile49(abs, "utf-8"));
28984
29397
  } catch {
28985
29398
  out.set(r.file_path, null);
28986
29399
  }
@@ -28992,14 +29405,14 @@ async function loadTranscriptSidecars(rows, assignmentDir) {
28992
29405
  const proofRoot = proofDir(assignmentDir);
28993
29406
  for (const r of rows) {
28994
29407
  if (r.kind !== "video" || !r.file_path) continue;
28995
- const videoAbs = resolve70(assignmentDir, r.file_path);
28996
- const sidecar = resolve70(dirname21(videoAbs), `${r.id}.transcript.md`);
29408
+ const videoAbs = resolve73(assignmentDir, r.file_path);
29409
+ const sidecar = resolve73(dirname21(videoAbs), `${r.id}.transcript.md`);
28997
29410
  if (!isWithin(proofRoot, sidecar)) continue;
28998
29411
  if (!await fileExists(sidecar)) continue;
28999
29412
  const st = await stat9(sidecar);
29000
29413
  if (st.size > INLINE_TEXT_LIMIT_BYTES) continue;
29001
29414
  try {
29002
- out.set(r.id, await readFile47(sidecar, "utf-8"));
29415
+ out.set(r.id, await readFile49(sidecar, "utf-8"));
29003
29416
  } catch {
29004
29417
  }
29005
29418
  }
@@ -29033,8 +29446,8 @@ async function proofBuildCommand(target, options = {}) {
29033
29446
  };
29034
29447
  const md = renderProofMarkdown(renderParams);
29035
29448
  const html = renderProofHtml(renderParams, inlineFiles, transcriptSidecars);
29036
- const mdPath = resolve70(resolved.assignmentDir, "proof.md");
29037
- const htmlPath = resolve70(resolved.assignmentDir, "proof.html");
29449
+ const mdPath = resolve73(resolved.assignmentDir, "proof.md");
29450
+ const htmlPath = resolve73(resolved.assignmentDir, "proof.html");
29038
29451
  await atomicWrite(mdPath, md);
29039
29452
  await atomicWrite(htmlPath, html);
29040
29453
  console.log(`Wrote ${htmlPath}`);
@@ -29699,7 +30112,7 @@ async function runCcusage(opts = {}) {
29699
30112
  };
29700
30113
  }
29701
30114
  function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
29702
- return new Promise((resolve79) => {
30115
+ return new Promise((resolve82) => {
29703
30116
  const child = spawn11(binary, args, {
29704
30117
  env: env ?? process.env,
29705
30118
  stdio: ["ignore", "pipe", "pipe"]
@@ -29713,7 +30126,7 @@ function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
29713
30126
  if (settled) return;
29714
30127
  settled = true;
29715
30128
  clearTimeout(timer2);
29716
- resolve79(result);
30129
+ resolve82(result);
29717
30130
  };
29718
30131
  const timer2 = setTimeout(() => {
29719
30132
  timedOut = true;
@@ -29758,8 +30171,8 @@ function isoToCcusageDate(iso) {
29758
30171
  // src/usage/cwd-extractor.ts
29759
30172
  init_paths();
29760
30173
  import { open as open4, readdir as readdir24, stat as stat10 } from "fs/promises";
29761
- import { join as join17 } from "path";
29762
- import { homedir as homedir11 } from "os";
30174
+ import { join as join18 } from "path";
30175
+ import { homedir as homedir12 } from "os";
29763
30176
  var SCAN_LINE_CAP = 50;
29764
30177
  var TAIL_READ_BYTES = 8 * 1024;
29765
30178
  var TAIL_READ_BYTES_MAX = 64 * 1024;
@@ -29833,12 +30246,12 @@ async function* walkClaudeProjects(opts = {}) {
29833
30246
  const dirs = await listDirSafe(root);
29834
30247
  for (const dirent of dirs) {
29835
30248
  if (!dirent.isDirectory) continue;
29836
- const dirPath = join17(root, dirent.name);
30249
+ const dirPath = join18(root, dirent.name);
29837
30250
  const files = await listDirSafe(dirPath);
29838
30251
  let cachedCwd = null;
29839
30252
  for (const f of files) {
29840
30253
  if (!f.isFile || !f.name.endsWith(".jsonl")) continue;
29841
- const filePath = join17(dirPath, f.name);
30254
+ const filePath = join18(dirPath, f.name);
29842
30255
  if (opts.sinceMtimeMs !== void 0) {
29843
30256
  const mtime = await mtimeMs(filePath);
29844
30257
  if (mtime !== null && mtime < opts.sinceMtimeMs) continue;
@@ -29875,8 +30288,8 @@ function resolveCodexSessionsRoot(override) {
29875
30288
  const fromSessionsEnv = process.env.CODEX_SESSIONS_DIR;
29876
30289
  if (fromSessionsEnv && fromSessionsEnv.length > 0) return expandHome(fromSessionsEnv);
29877
30290
  const fromHomeEnv = process.env.CODEX_HOME;
29878
- if (fromHomeEnv && fromHomeEnv.length > 0) return join17(expandHome(fromHomeEnv), "sessions");
29879
- return join17(homedir11(), ".codex", "sessions");
30291
+ if (fromHomeEnv && fromHomeEnv.length > 0) return join18(expandHome(fromHomeEnv), "sessions");
30292
+ return join18(homedir12(), ".codex", "sessions");
29880
30293
  }
29881
30294
  async function listDirSafe(path) {
29882
30295
  try {
@@ -29896,7 +30309,7 @@ async function* walkJsonlRecursive(root) {
29896
30309
  const current = stack.pop();
29897
30310
  const entries = await listDirSafe(current);
29898
30311
  for (const e of entries) {
29899
- const full = join17(current, e.name);
30312
+ const full = join18(current, e.name);
29900
30313
  if (e.isDirectory) {
29901
30314
  stack.push(full);
29902
30315
  } else if (e.isFile && e.name.endsWith(".jsonl")) {
@@ -30235,8 +30648,8 @@ init_slug();
30235
30648
  init_timestamp();
30236
30649
  init_assignment_resolver();
30237
30650
  init_assignment_todos();
30238
- import { resolve as resolve71 } from "path";
30239
- import { readFile as readFile48 } from "fs/promises";
30651
+ import { resolve as resolve74 } from "path";
30652
+ import { readFile as readFile50 } from "fs/promises";
30240
30653
  async function requestCommand(target, text, options = {}) {
30241
30654
  if (!text || !text.trim()) {
30242
30655
  throw new Error("Request text cannot be empty.");
@@ -30252,7 +30665,7 @@ async function requestCommand(target, text, options = {}) {
30252
30665
  if (!isValidSlug(target)) {
30253
30666
  throw new Error(`Invalid assignment slug "${target}".`);
30254
30667
  }
30255
- assignmentDir = resolve71(baseDir, options.project, "assignments", target);
30668
+ assignmentDir = resolve74(baseDir, options.project, "assignments", target);
30256
30669
  targetRef = target;
30257
30670
  } else {
30258
30671
  const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
@@ -30262,12 +30675,12 @@ async function requestCommand(target, text, options = {}) {
30262
30675
  assignmentDir = resolved.assignmentDir;
30263
30676
  targetRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
30264
30677
  }
30265
- const assignmentMdPath2 = resolve71(assignmentDir, "assignment.md");
30678
+ const assignmentMdPath2 = resolve74(assignmentDir, "assignment.md");
30266
30679
  if (!await fileExists(assignmentMdPath2)) {
30267
30680
  throw new Error(`assignment.md not found at ${assignmentMdPath2}`);
30268
30681
  }
30269
30682
  const source = options.from ?? process.env.SYNTAUR_ASSIGNMENT ?? "unknown";
30270
- let content = await readFile48(assignmentMdPath2, "utf-8");
30683
+ let content = await readFile50(assignmentMdPath2, "utf-8");
30271
30684
  content = appendTodosToAssignmentBody(content, [
30272
30685
  { description: `${text.trim()} (from: ${source})` }
30273
30686
  ]);
@@ -30280,13 +30693,13 @@ async function requestCommand(target, text, options = {}) {
30280
30693
  init_fs();
30281
30694
  init_paths();
30282
30695
  import { Command as Command9 } from "commander";
30283
- import { readFile as readFile49, readdir as readdir25 } from "fs/promises";
30284
- import { resolve as resolve72 } from "path";
30696
+ import { readFile as readFile51, readdir as readdir25 } from "fs/promises";
30697
+ import { resolve as resolve75 } from "path";
30285
30698
  async function readContextAssignmentDir(cwd) {
30286
- const path = resolve72(cwd, ".syntaur", "context.json");
30699
+ const path = resolve75(cwd, ".syntaur", "context.json");
30287
30700
  if (!await fileExists(path)) return null;
30288
30701
  try {
30289
- const raw = await readFile49(path, "utf-8");
30702
+ const raw = await readFile51(path, "utf-8");
30290
30703
  const ctx = JSON.parse(raw);
30291
30704
  if (typeof ctx.assignmentDir === "string" && ctx.assignmentDir.length > 0) {
30292
30705
  return ctx.assignmentDir;
@@ -30300,9 +30713,9 @@ async function resolveAssignmentDir(opts) {
30300
30713
  const cwd = opts.cwd ?? process.cwd();
30301
30714
  if (opts.assignment) {
30302
30715
  if (opts.project) {
30303
- return resolve72(defaultProjectDir(), opts.project, "assignments", opts.assignment);
30716
+ return resolve75(defaultProjectDir(), opts.project, "assignments", opts.assignment);
30304
30717
  }
30305
- return resolve72(assignmentsDir(), opts.assignment);
30718
+ return resolve75(assignmentsDir(), opts.assignment);
30306
30719
  }
30307
30720
  const fromCtx = await readContextAssignmentDir(cwd);
30308
30721
  if (fromCtx) return fromCtx;
@@ -30445,7 +30858,7 @@ async function runPlanVersion(options) {
30445
30858
  if (!await fileExists(assignmentDir)) {
30446
30859
  throw new Error(`Assignment directory does not exist: ${assignmentDir}`);
30447
30860
  }
30448
- const assignmentMdPath2 = resolve72(assignmentDir, "assignment.md");
30861
+ const assignmentMdPath2 = resolve75(assignmentDir, "assignment.md");
30449
30862
  if (!await fileExists(assignmentMdPath2)) {
30450
30863
  throw new Error(`Missing assignment.md at: ${assignmentMdPath2}`);
30451
30864
  }
@@ -30457,15 +30870,15 @@ async function runPlanVersion(options) {
30457
30870
  }
30458
30871
  const current = planFiles[planFiles.length - 1];
30459
30872
  const next = nextPlanFileName(current.version);
30460
- const newPath = resolve72(assignmentDir, next.fileName);
30873
+ const newPath = resolve75(assignmentDir, next.fileName);
30461
30874
  if (await fileExists(newPath) && !options.force) {
30462
30875
  throw new Error(`${next.fileName} already exists. Use --force to overwrite.`);
30463
30876
  }
30464
- const assignmentMd = await readFile49(assignmentMdPath2, "utf-8");
30877
+ const assignmentMd = await readFile51(assignmentMdPath2, "utf-8");
30465
30878
  const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
30466
30879
  const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
30467
- const oldPlanPath = resolve72(assignmentDir, current.fileName);
30468
- const oldPlanContent = await readFile49(oldPlanPath, "utf-8");
30880
+ const oldPlanPath = resolve75(assignmentDir, current.fileName);
30881
+ const oldPlanContent = await readFile51(oldPlanPath, "utf-8");
30469
30882
  const oldBody = oldPlanContent.replace(/^---[\s\S]*?\n---\n?/, "");
30470
30883
  const carriedTodos = extractUncheckedTodos(oldBody);
30471
30884
  const stub = buildNewPlanStub({
@@ -30502,26 +30915,26 @@ planCommand.command("version").description(
30502
30915
  // src/commands/session.ts
30503
30916
  init_fs();
30504
30917
  import { Command as Command10 } from "commander";
30505
- import { readFile as readFile50, readdir as readdir26, stat as stat11 } from "fs/promises";
30506
- import { resolve as resolve73 } from "path";
30918
+ import { readFile as readFile52, readdir as readdir26, stat as stat11 } from "fs/promises";
30919
+ import { resolve as resolve76 } from "path";
30507
30920
  async function readContext(cwd) {
30508
- const path = resolve73(cwd, ".syntaur", "context.json");
30921
+ const path = resolve76(cwd, ".syntaur", "context.json");
30509
30922
  if (!await fileExists(path)) return null;
30510
30923
  try {
30511
- const raw = await readFile50(path, "utf-8");
30924
+ const raw = await readFile52(path, "utf-8");
30512
30925
  return JSON.parse(raw);
30513
30926
  } catch {
30514
30927
  return null;
30515
30928
  }
30516
30929
  }
30517
30930
  async function findLatestSessionSummary(assignmentDir) {
30518
- const sessionsRoot = resolve73(assignmentDir, "sessions");
30931
+ const sessionsRoot = resolve76(assignmentDir, "sessions");
30519
30932
  if (!await fileExists(sessionsRoot)) return null;
30520
30933
  const entries = await readdir26(sessionsRoot, { withFileTypes: true });
30521
30934
  let best = null;
30522
30935
  for (const entry of entries) {
30523
30936
  if (!entry.isDirectory()) continue;
30524
- const summaryPath = resolve73(sessionsRoot, entry.name, "summary.md");
30937
+ const summaryPath = resolve76(sessionsRoot, entry.name, "summary.md");
30525
30938
  if (!await fileExists(summaryPath)) continue;
30526
30939
  const st = await stat11(summaryPath);
30527
30940
  if (best === null || st.mtime.getTime() > best.mtime.getTime()) {
@@ -30531,9 +30944,9 @@ async function findLatestSessionSummary(assignmentDir) {
30531
30944
  return best;
30532
30945
  }
30533
30946
  async function findOpenHandoff(assignmentDir) {
30534
- const handoffPath = resolve73(assignmentDir, "handoff.md");
30947
+ const handoffPath = resolve76(assignmentDir, "handoff.md");
30535
30948
  if (!await fileExists(handoffPath)) return null;
30536
- const content = await readFile50(handoffPath, "utf-8");
30949
+ const content = await readFile52(handoffPath, "utf-8");
30537
30950
  const body = content.replace(/^---[\s\S]*?\n---\n?/, "").trim();
30538
30951
  if (body.length === 0) return null;
30539
30952
  if (/^<!--[\s\S]*-->$/.test(body)) return null;
@@ -30641,13 +31054,13 @@ init_git_worktree();
30641
31054
  init_fs();
30642
31055
  init_paths();
30643
31056
  import { Command as Command11 } from "commander";
30644
- import { readFile as readFile51 } from "fs/promises";
30645
- import { resolve as resolve74 } from "path";
31057
+ import { readFile as readFile53 } from "fs/promises";
31058
+ import { resolve as resolve77 } from "path";
30646
31059
  async function readContext2(cwd) {
30647
- const path = resolve74(cwd, ".syntaur", "context.json");
31060
+ const path = resolve77(cwd, ".syntaur", "context.json");
30648
31061
  if (!await fileExists(path)) return null;
30649
31062
  try {
30650
- return JSON.parse(await readFile51(path, "utf-8"));
31063
+ return JSON.parse(await readFile53(path, "utf-8"));
30651
31064
  } catch {
30652
31065
  return null;
30653
31066
  }
@@ -30655,7 +31068,7 @@ async function readContext2(cwd) {
30655
31068
  async function resolveAssignmentPath2(opts) {
30656
31069
  if (opts.assignment) {
30657
31070
  if (opts.project) {
30658
- return resolve74(
31071
+ return resolve77(
30659
31072
  defaultProjectDir(),
30660
31073
  opts.project,
30661
31074
  "assignments",
@@ -30663,10 +31076,10 @@ async function resolveAssignmentPath2(opts) {
30663
31076
  "assignment.md"
30664
31077
  );
30665
31078
  }
30666
- return resolve74(assignmentsDir(), opts.assignment, "assignment.md");
31079
+ return resolve77(assignmentsDir(), opts.assignment, "assignment.md");
30667
31080
  }
30668
31081
  const ctx = await readContext2(opts.cwd);
30669
- if (ctx?.assignmentDir) return resolve74(ctx.assignmentDir, "assignment.md");
31082
+ if (ctx?.assignmentDir) return resolve77(ctx.assignmentDir, "assignment.md");
30670
31083
  throw new Error(
30671
31084
  "No active assignment. Pass --assignment <slug> [--project <slug>] or run from a workspace with .syntaur/context.json."
30672
31085
  );
@@ -30677,7 +31090,7 @@ async function runWorktreeCreate(options, cwd = process.cwd()) {
30677
31090
  }
30678
31091
  const repository = options.repository ?? cwd;
30679
31092
  const parentBranch = options.parentBranch ?? "main";
30680
- const worktreePath = options.worktreePath ?? resolve74(repository, ".worktrees", options.branch);
31093
+ const worktreePath = options.worktreePath ?? resolve77(repository, ".worktrees", options.branch);
30681
31094
  const assignmentPath = await resolveAssignmentPath2({
30682
31095
  assignment: options.assignment,
30683
31096
  project: options.project,
@@ -30714,13 +31127,13 @@ init_paths();
30714
31127
  init_fs();
30715
31128
  init_slug();
30716
31129
  import { Command as Command12 } from "commander";
30717
- import { resolve as resolve76 } from "path";
31130
+ import { resolve as resolve79 } from "path";
30718
31131
 
30719
31132
  // src/utils/project-indexes.ts
30720
31133
  init_parser();
30721
31134
  init_fs();
30722
- import { readdir as readdir27, readFile as readFile52 } from "fs/promises";
30723
- import { resolve as resolve75 } from "path";
31135
+ import { readdir as readdir27, readFile as readFile54 } from "fs/promises";
31136
+ import { resolve as resolve78 } from "path";
30724
31137
  function nowIso3() {
30725
31138
  return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
30726
31139
  }
@@ -30739,7 +31152,7 @@ function joinList(items) {
30739
31152
  return items.length === 0 ? "\u2014" : items.map(escapeCell).join(", ");
30740
31153
  }
30741
31154
  async function rebuildResourcesIndex(projectDir) {
30742
- const dir = resolve75(projectDir, "resources");
31155
+ const dir = resolve78(projectDir, "resources");
30743
31156
  await ensureDir(dir);
30744
31157
  const files = await listSlugFiles(dir);
30745
31158
  const slug = readProjectSlug(projectDir);
@@ -30755,7 +31168,7 @@ async function rebuildResourcesIndex(projectDir) {
30755
31168
  lines.push("| Name | Category | Source | Related Assignments | Updated |");
30756
31169
  lines.push("|------|----------|--------|---------------------|---------|");
30757
31170
  for (const fileName of files) {
30758
- const content = await readFile52(resolve75(dir, fileName), "utf-8");
31171
+ const content = await readFile54(resolve78(dir, fileName), "utf-8");
30759
31172
  const parsed = parseResource(content);
30760
31173
  const slugBase = fileName.replace(/\.md$/, "");
30761
31174
  const name = parsed.name || slugBase;
@@ -30765,12 +31178,12 @@ async function rebuildResourcesIndex(projectDir) {
30765
31178
  );
30766
31179
  }
30767
31180
  lines.push("");
30768
- const indexPath = resolve75(dir, "_index.md");
31181
+ const indexPath = resolve78(dir, "_index.md");
30769
31182
  await writeFileForce(indexPath, lines.join("\n"));
30770
31183
  return { total: files.length, path: indexPath };
30771
31184
  }
30772
31185
  async function rebuildMemoriesIndex(projectDir) {
30773
- const dir = resolve75(projectDir, "memories");
31186
+ const dir = resolve78(projectDir, "memories");
30774
31187
  await ensureDir(dir);
30775
31188
  const files = await listSlugFiles(dir);
30776
31189
  const slug = readProjectSlug(projectDir);
@@ -30786,7 +31199,7 @@ async function rebuildMemoriesIndex(projectDir) {
30786
31199
  lines.push("| Name | Source | Scope | Source Assignment | Updated |");
30787
31200
  lines.push("|------|--------|-------|-------------------|---------|");
30788
31201
  for (const fileName of files) {
30789
- const content = await readFile52(resolve75(dir, fileName), "utf-8");
31202
+ const content = await readFile54(resolve78(dir, fileName), "utf-8");
30790
31203
  const parsed = parseMemory(content);
30791
31204
  const slugBase = fileName.replace(/\.md$/, "");
30792
31205
  const name = parsed.name || slugBase;
@@ -30796,7 +31209,7 @@ async function rebuildMemoriesIndex(projectDir) {
30796
31209
  );
30797
31210
  }
30798
31211
  lines.push("");
30799
- const indexPath = resolve75(dir, "_index.md");
31212
+ const indexPath = resolve78(dir, "_index.md");
30800
31213
  await writeFileForce(indexPath, lines.join("\n"));
30801
31214
  return { total: files.length, path: indexPath };
30802
31215
  }
@@ -30834,8 +31247,8 @@ async function runResourceAdd(options) {
30834
31247
  if (!isValidSlug(options.project)) {
30835
31248
  throw new Error(`Invalid project slug: "${options.project}".`);
30836
31249
  }
30837
- const projectDir = resolve76(defaultProjectDir(), options.project);
30838
- if (!await fileExists(resolve76(projectDir, "project.md"))) {
31250
+ const projectDir = resolve79(defaultProjectDir(), options.project);
31251
+ if (!await fileExists(resolve79(projectDir, "project.md"))) {
30839
31252
  throw new Error(`Project "${options.project}" not found at ${projectDir}.`);
30840
31253
  }
30841
31254
  if (!options.name) throw new Error("--name is required.");
@@ -30844,7 +31257,7 @@ async function runResourceAdd(options) {
30844
31257
  if (!isValidSlug(slug)) {
30845
31258
  throw new Error(`Invalid resource slug: "${slug}".`);
30846
31259
  }
30847
- const filePath = resolve76(projectDir, "resources", `${slug}.md`);
31260
+ const filePath = resolve79(projectDir, "resources", `${slug}.md`);
30848
31261
  if (await fileExists(filePath) && !options.force) {
30849
31262
  throw new Error(
30850
31263
  `Resource "${slug}" already exists at ${filePath}. Use --force to overwrite.`
@@ -30879,7 +31292,7 @@ init_paths();
30879
31292
  init_fs();
30880
31293
  init_slug();
30881
31294
  import { Command as Command13 } from "commander";
30882
- import { resolve as resolve77 } from "path";
31295
+ import { resolve as resolve80 } from "path";
30883
31296
  function nowIso5() {
30884
31297
  return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
30885
31298
  }
@@ -30914,8 +31327,8 @@ async function runMemoryAdd(options) {
30914
31327
  if (!isValidSlug(options.project)) {
30915
31328
  throw new Error(`Invalid project slug: "${options.project}".`);
30916
31329
  }
30917
- const projectDir = resolve77(defaultProjectDir(), options.project);
30918
- if (!await fileExists(resolve77(projectDir, "project.md"))) {
31330
+ const projectDir = resolve80(defaultProjectDir(), options.project);
31331
+ if (!await fileExists(resolve80(projectDir, "project.md"))) {
30919
31332
  throw new Error(`Project "${options.project}" not found at ${projectDir}.`);
30920
31333
  }
30921
31334
  if (!options.name) throw new Error("--name is required.");
@@ -30924,7 +31337,7 @@ async function runMemoryAdd(options) {
30924
31337
  if (!isValidSlug(slug)) {
30925
31338
  throw new Error(`Invalid memory slug: "${slug}".`);
30926
31339
  }
30927
- const filePath = resolve77(projectDir, "memories", `${slug}.md`);
31340
+ const filePath = resolve80(projectDir, "memories", `${slug}.md`);
30928
31341
  if (await fileExists(filePath) && !options.force) {
30929
31342
  throw new Error(
30930
31343
  `Memory "${slug}" already exists at ${filePath}. Use --force to overwrite.`
@@ -30961,8 +31374,8 @@ init_paths();
30961
31374
  init_fs();
30962
31375
  init_frontmatter();
30963
31376
  import { Command as Command14 } from "commander";
30964
- import { readFile as readFile53 } from "fs/promises";
30965
- import { resolve as resolve78 } from "path";
31377
+ import { readFile as readFile55 } from "fs/promises";
31378
+ import { resolve as resolve81 } from "path";
30966
31379
  var AGE_PATTERN = /^(\d+)([dhwm])$/i;
30967
31380
  function parseAgeToCutoff(age) {
30968
31381
  const match = age.match(AGE_PATTERN);
@@ -30981,7 +31394,7 @@ function parseAgeToCutoff(age) {
30981
31394
  }
30982
31395
  function assignmentMdPath(item) {
30983
31396
  if (item.projectSlug) {
30984
- return resolve78(
31397
+ return resolve81(
30985
31398
  defaultProjectDir(),
30986
31399
  item.projectSlug,
30987
31400
  "assignments",
@@ -30989,13 +31402,13 @@ function assignmentMdPath(item) {
30989
31402
  "assignment.md"
30990
31403
  );
30991
31404
  }
30992
- return resolve78(assignmentsDir(), item.id, "assignment.md");
31405
+ return resolve81(assignmentsDir(), item.id, "assignment.md");
30993
31406
  }
30994
31407
  async function loadTags(item) {
30995
31408
  const path = assignmentMdPath(item);
30996
31409
  if (!await fileExists(path)) return [];
30997
31410
  try {
30998
- const content = await readFile53(path, "utf-8");
31411
+ const content = await readFile55(path, "utf-8");
30999
31412
  return parseAssignmentFrontmatter(content).tags;
31000
31413
  } catch {
31001
31414
  return [];
@@ -31467,7 +31880,8 @@ function spliceDashDashFromArgv(argv) {
31467
31880
  // src/index.ts
31468
31881
  {
31469
31882
  const sub = process.argv[2];
31470
- if (sub !== "update" && sub !== "upgrade") {
31883
+ const isDryRunSetup = sub === "setup" && process.argv.slice(3).includes("--dry-run");
31884
+ if (sub !== "update" && sub !== "upgrade" && !isDryRunSetup) {
31471
31885
  await maybePromptInstall(import.meta.url);
31472
31886
  await maybeNudgeForNpxInstall(import.meta.url);
31473
31887
  }
@@ -31712,7 +32126,7 @@ program.command("reopen").description("Reopen a completed or failed assignment")
31712
32126
  process.exit(1);
31713
32127
  }
31714
32128
  });
31715
- program.command("setup").description("Initialize Syntaur and optionally install plugins or launch the dashboard").option("--yes", "Skip interactive prompts and perform only the requested flags").option("--claude", "Install the Claude Code plugin").option("--codex", "Install the Codex plugin").option("--claude-dir <path>", "Install the Claude Code plugin at a specific path").option("--codex-dir <path>", "Install the Codex plugin at a specific path").option("--codex-marketplace-path <path>", "Write the Codex marketplace entry to a specific file").option("--dashboard", "Launch the dashboard after setup").action(async (options) => {
32129
+ program.command("setup").description("Initialize Syntaur and optionally install plugins or launch the dashboard").option("--yes", "Skip interactive prompts and perform only the requested flags").option("--claude", "Install the Claude Code plugin").option("--codex", "Install the Codex plugin").option("--claude-dir <path>", "Install the Claude Code plugin at a specific path").option("--codex-dir <path>", "Install the Codex plugin at a specific path").option("--codex-marketplace-path <path>", "Write the Codex marketplace entry to a specific file").option("--dashboard", "Launch the dashboard after setup").option("--target <id>", "Install Syntaur into a cross-agent target (pi, hermes, openclaw, cursor, opencode); comma-separated for several").option("--agent <id>", "Alias for --target; cross-agent target id(s) to install into").option("--force", "Overwrite existing cross-agent protocol files / skills").option("--dry-run", "Print the cross-agent install actions without writing anything").action(async (options) => {
31716
32130
  try {
31717
32131
  await setupCommand(options);
31718
32132
  } catch (error) {