syntaur 0.24.0 → 0.25.1

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" }
@@ -8619,9 +8673,17 @@ function buildTerminalDispatch(installedTerminals) {
8619
8673
  branches.push({
8620
8674
  id: "terminal-app",
8621
8675
  block: [
8676
+ ' set wasRunning to application "Terminal" is running',
8622
8677
  ' tell application "Terminal"',
8623
8678
  " activate",
8624
- " do script shellCmd",
8679
+ " if wasRunning then",
8680
+ " do script shellCmd",
8681
+ " else",
8682
+ " repeat until (count of windows) > 0",
8683
+ " delay 0.1",
8684
+ " end repeat",
8685
+ " do script shellCmd in window 1",
8686
+ " end if",
8625
8687
  " end tell"
8626
8688
  ]
8627
8689
  });
@@ -8757,13 +8819,13 @@ function runPlistBuddy(plistPath, steps) {
8757
8819
  for (const step of steps) {
8758
8820
  if (Array.isArray(step)) {
8759
8821
  const [primary, fallback] = step;
8760
- const a = spawnSync6("/usr/libexec/PlistBuddy", ["-c", primary, plistPath], {
8822
+ const a = spawnSync7("/usr/libexec/PlistBuddy", ["-c", primary, plistPath], {
8761
8823
  stdio: "pipe",
8762
8824
  encoding: "utf-8"
8763
8825
  });
8764
8826
  if (a.status === 0) continue;
8765
8827
  if (fallback === null) continue;
8766
- const b = spawnSync6("/usr/libexec/PlistBuddy", ["-c", fallback, plistPath], {
8828
+ const b = spawnSync7("/usr/libexec/PlistBuddy", ["-c", fallback, plistPath], {
8767
8829
  stdio: "pipe",
8768
8830
  encoding: "utf-8"
8769
8831
  });
@@ -8773,7 +8835,7 @@ function runPlistBuddy(plistPath, steps) {
8773
8835
  );
8774
8836
  }
8775
8837
  } else {
8776
- const r = spawnSync6("/usr/libexec/PlistBuddy", ["-c", step, plistPath], {
8838
+ const r = spawnSync7("/usr/libexec/PlistBuddy", ["-c", step, plistPath], {
8777
8839
  stdio: "pipe",
8778
8840
  encoding: "utf-8"
8779
8841
  });
@@ -9317,7 +9379,7 @@ init_fs();
9317
9379
  init_config();
9318
9380
  init_playbooks();
9319
9381
  import { resolve as resolve5, dirname as dirname2 } from "path";
9320
- import { readdir as readdir3, readFile as readFile4 } from "fs/promises";
9382
+ import { readdir as readdir3, readFile as readFile5 } from "fs/promises";
9321
9383
  import { fileURLToPath } from "url";
9322
9384
  async function initCommand(options) {
9323
9385
  const root = syntaurRoot();
@@ -9369,7 +9431,7 @@ async function seedDefaultPlaybooks(playbooksDir3) {
9369
9431
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
9370
9432
  const targetPath = resolve5(playbooksDir3, entry.name);
9371
9433
  if (await fileExists(targetPath)) continue;
9372
- const content = await readFile4(resolve5(examplesDir, entry.name), "utf-8");
9434
+ const content = await readFile5(resolve5(examplesDir, entry.name), "utf-8");
9373
9435
  await writeFileSafe(targetPath, content);
9374
9436
  count++;
9375
9437
  }
@@ -9931,7 +9993,7 @@ function mergePatch(current, patch) {
9931
9993
  // src/utils/view-prefs.ts
9932
9994
  init_paths();
9933
9995
  init_fs();
9934
- import { readFile as readFile11, rename as rename3, unlink as unlink3 } from "fs/promises";
9996
+ import { readFile as readFile12, rename as rename3, unlink as unlink3 } from "fs/promises";
9935
9997
  import { resolve as resolve17 } from "path";
9936
9998
  function corruptFilePath() {
9937
9999
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
@@ -9954,7 +10016,7 @@ async function readViewPrefsFile() {
9954
10016
  }
9955
10017
  let raw;
9956
10018
  try {
9957
- raw = await readFile11(path, "utf-8");
10019
+ raw = await readFile12(path, "utf-8");
9958
10020
  } catch {
9959
10021
  return { ...DEFAULT_VIEW_PREFS_FILE };
9960
10022
  }
@@ -10137,7 +10199,7 @@ function isDashboardSlot(value) {
10137
10199
  // src/utils/saved-views.ts
10138
10200
  init_paths();
10139
10201
  init_fs();
10140
- import { readFile as readFile12, rename as rename4, unlink as unlink4 } from "fs/promises";
10202
+ import { readFile as readFile13, rename as rename4, unlink as unlink4 } from "fs/promises";
10141
10203
  import { randomUUID as randomUUID2 } from "crypto";
10142
10204
  import { resolve as resolve18 } from "path";
10143
10205
  function corruptFilePath2() {
@@ -10167,7 +10229,7 @@ async function readSavedViewsFile() {
10167
10229
  }
10168
10230
  let raw;
10169
10231
  try {
10170
- raw = await readFile12(path, "utf-8");
10232
+ raw = await readFile13(path, "utf-8");
10171
10233
  } catch {
10172
10234
  return cloneDefault();
10173
10235
  }
@@ -10487,7 +10549,7 @@ init_fs();
10487
10549
  init_git_worktree();
10488
10550
  import { Router as Router2 } from "express";
10489
10551
  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";
10552
+ import { rm, readFile as readFile16, open as fsOpen, stat as fsStat, realpath as fsRealpath } from "fs/promises";
10491
10553
  import { spawnSync as spawnSync3 } from "child_process";
10492
10554
 
10493
10555
  // src/utils/worktree-defaults.ts
@@ -10563,7 +10625,7 @@ function validateBranchName(name) {
10563
10625
  // src/dashboard/repository-candidates.ts
10564
10626
  init_fs();
10565
10627
  init_parser();
10566
- import { readdir as readdir9, readFile as readFile14 } from "fs/promises";
10628
+ import { readdir as readdir9, readFile as readFile15 } from "fs/promises";
10567
10629
  import { resolve as resolve20 } from "path";
10568
10630
  function toSourceAssignment(parsed, fallbackId) {
10569
10631
  const repository = parsed.workspace.repository?.trim();
@@ -10583,7 +10645,7 @@ async function getProjectRepositoryCandidates(projectsDir2, projectSlug) {
10583
10645
  const out = [];
10584
10646
  const projectPath = resolve20(projectsDir2, projectSlug, "project.md");
10585
10647
  if (await fileExists(projectPath)) {
10586
- const project = parseProject(await readFile14(projectPath, "utf-8"));
10648
+ const project = parseProject(await readFile15(projectPath, "utf-8"));
10587
10649
  for (const raw of project.repositories) {
10588
10650
  const path = raw.trim();
10589
10651
  if (!path) continue;
@@ -10600,7 +10662,7 @@ async function getProjectRepositoryCandidates(projectsDir2, projectSlug) {
10600
10662
  if (!entry.isDirectory()) continue;
10601
10663
  const assignmentMd = resolve20(assignmentsDir2, entry.name, "assignment.md");
10602
10664
  if (!await fileExists(assignmentMd)) continue;
10603
- const parsed = parseAssignmentFull(await readFile14(assignmentMd, "utf-8"));
10665
+ const parsed = parseAssignmentFull(await readFile15(assignmentMd, "utf-8"));
10604
10666
  const repo = parsed.workspace.repository?.trim();
10605
10667
  if (!repo) continue;
10606
10668
  const abs = resolve20(repo);
@@ -10623,7 +10685,7 @@ async function getStandaloneRepositoryCandidates(assignmentsDir2, excludeAssignm
10623
10685
  if (entry.name === excludeAssignmentId) continue;
10624
10686
  const assignmentMd = resolve20(assignmentsDir2, entry.name, "assignment.md");
10625
10687
  if (!await fileExists(assignmentMd)) continue;
10626
- const parsed = parseAssignmentFull(await readFile14(assignmentMd, "utf-8"));
10688
+ const parsed = parseAssignmentFull(await readFile15(assignmentMd, "utf-8"));
10627
10689
  const repo = parsed.workspace.repository?.trim();
10628
10690
  if (!repo) continue;
10629
10691
  const abs = resolve20(repo);
@@ -10644,7 +10706,7 @@ async function getProjectSourceAssignments(projectsDir2, projectSlug, excludeSlu
10644
10706
  if (entry.name === excludeSlug) continue;
10645
10707
  const assignmentMd = resolve20(assignmentsDir2, entry.name, "assignment.md");
10646
10708
  if (!await fileExists(assignmentMd)) continue;
10647
- const parsed = parseAssignmentFull(await readFile14(assignmentMd, "utf-8"));
10709
+ const parsed = parseAssignmentFull(await readFile15(assignmentMd, "utf-8"));
10648
10710
  const source = toSourceAssignment(parsed, entry.name);
10649
10711
  if (!source) continue;
10650
10712
  if (seen.has(entry.name)) continue;
@@ -10663,7 +10725,7 @@ async function getStandaloneSourceAssignments(assignmentsDir2, excludeAssignment
10663
10725
  if (entry.name === excludeAssignmentId) continue;
10664
10726
  const assignmentMd = resolve20(assignmentsDir2, entry.name, "assignment.md");
10665
10727
  if (!await fileExists(assignmentMd)) continue;
10666
- const parsed = parseAssignmentFull(await readFile14(assignmentMd, "utf-8"));
10728
+ const parsed = parseAssignmentFull(await readFile15(assignmentMd, "utf-8"));
10667
10729
  const source = toSourceAssignment(parsed, entry.name);
10668
10730
  if (!source) continue;
10669
10731
  if (seen.has(entry.name)) continue;
@@ -10834,7 +10896,7 @@ async function readCurrentDocument(filePath) {
10834
10896
  if (!await fileExists(filePath)) {
10835
10897
  return null;
10836
10898
  }
10837
- return readFile15(filePath, "utf-8");
10899
+ return readFile16(filePath, "utf-8");
10838
10900
  }
10839
10901
  var worktreeInFlight = /* @__PURE__ */ new Set();
10840
10902
  async function assertRepoRoot(repoInput) {
@@ -10888,7 +10950,7 @@ async function handleWorktreeCreate(req, res, ctx) {
10888
10950
  }
10889
10951
  worktreeInFlight.add(ctx.assignmentPath);
10890
10952
  try {
10891
- const parsed = parseAssignmentFull(await readFile15(ctx.assignmentPath, "utf-8"));
10953
+ const parsed = parseAssignmentFull(await readFile16(ctx.assignmentPath, "utf-8"));
10892
10954
  if (parsed.workspace.worktreePath) {
10893
10955
  res.status(409).json({ error: "Worktree already configured for this assignment" });
10894
10956
  return;
@@ -11258,7 +11320,7 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
11258
11320
  }
11259
11321
  const nextContentRaw = requireContent(req, res);
11260
11322
  if (!nextContentRaw) return;
11261
- const currentContent = await readFile15(filePath, "utf-8");
11323
+ const currentContent = await readFile16(filePath, "utf-8");
11262
11324
  const frontmatterBlock = extractFrontmatterBlock(currentContent);
11263
11325
  if (!frontmatterBlock) {
11264
11326
  res.status(500).json({ error: `${kind} file is malformed (no frontmatter)` });
@@ -11709,7 +11771,7 @@ ${nextBody}${nextBody.endsWith("\n") ? "" : "\n"}`;
11709
11771
  let currentContent;
11710
11772
  let currentCount = 0;
11711
11773
  if (await fileExists(commentsPath)) {
11712
- currentContent = await readFile15(commentsPath, "utf-8");
11774
+ currentContent = await readFile16(commentsPath, "utf-8");
11713
11775
  const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
11714
11776
  if (countMatch) currentCount = parseInt(countMatch[1], 10);
11715
11777
  } else {
@@ -11766,7 +11828,7 @@ ${entry}`;
11766
11828
  res.status(400).json({ error: "resolved (boolean) is required" });
11767
11829
  return;
11768
11830
  }
11769
- const content = await readFile15(commentsPath, "utf-8");
11831
+ const content = await readFile16(commentsPath, "utf-8");
11770
11832
  const parsed = parseComments(content);
11771
11833
  const target = parsed.entries.find((e) => e.id === commentId);
11772
11834
  if (!target) {
@@ -11813,7 +11875,7 @@ ${entry}`;
11813
11875
  });
11814
11876
  return;
11815
11877
  }
11816
- let content = await readFile15(projectPath, "utf-8");
11878
+ let content = await readFile16(projectPath, "utf-8");
11817
11879
  content = setTopLevelField(content, "workspace", workspace ?? null);
11818
11880
  content = setTopLevelField(content, "updated", nowTimestamp());
11819
11881
  await writeFileForce(projectPath, content);
@@ -11850,7 +11912,7 @@ ${entry}`;
11850
11912
  return;
11851
11913
  }
11852
11914
  const assignmentPath = resolve21(resolved.assignmentDir, "assignment.md");
11853
- let content = await readFile15(assignmentPath, "utf-8");
11915
+ let content = await readFile16(assignmentPath, "utf-8");
11854
11916
  content = setTopLevelField(content, "workspaceGroup", workspaceGroup ?? null);
11855
11917
  content = setTopLevelField(content, "updated", nowTimestamp());
11856
11918
  await writeFileForce(assignmentPath, content);
@@ -12067,7 +12129,7 @@ ${entry}`;
12067
12129
  return;
12068
12130
  }
12069
12131
  const assignmentPath = resolve21(resolved.assignmentDir, "assignment.md");
12070
- const parsedForSlug = parseAssignmentFull(await readFile15(assignmentPath, "utf-8"));
12132
+ const parsedForSlug = parseAssignmentFull(await readFile16(assignmentPath, "utf-8"));
12071
12133
  const assignmentSlugForBranch = parsedForSlug.slug || resolved.id;
12072
12134
  await handleWorktreeCreate(req, res, {
12073
12135
  assignmentPath,
@@ -12096,7 +12158,7 @@ ${entry}`;
12096
12158
  res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}, or null to clear.` });
12097
12159
  return;
12098
12160
  }
12099
- let content = await readFile15(projectPath, "utf-8");
12161
+ let content = await readFile16(projectPath, "utf-8");
12100
12162
  content = setTopLevelField(content, "statusOverride", status ?? null);
12101
12163
  content = setTopLevelField(content, "updated", nowTimestamp());
12102
12164
  await writeFileForce(projectPath, content);
@@ -12129,7 +12191,7 @@ ${entry}`;
12129
12191
  res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}.` });
12130
12192
  return;
12131
12193
  }
12132
- let content = await readFile15(assignmentPath, "utf-8");
12194
+ let content = await readFile16(assignmentPath, "utf-8");
12133
12195
  content = setTopLevelField(content, "status", status);
12134
12196
  content = setTopLevelField(content, "updated", nowTimestamp());
12135
12197
  if (status !== "blocked") {
@@ -12155,7 +12217,7 @@ ${entry}`;
12155
12217
  res.status(404).json({ error: `Project "${projectSlug}" not found` });
12156
12218
  return;
12157
12219
  }
12158
- const content = await readFile15(projectPath, "utf-8");
12220
+ const content = await readFile16(projectPath, "utf-8");
12159
12221
  await writeFileForce(projectPath, applyArchiveFields(content, true, archiveReason(req.body)));
12160
12222
  const project = await getProjectDetail(projectsDir2, projectSlug);
12161
12223
  res.json({ project });
@@ -12172,7 +12234,7 @@ ${entry}`;
12172
12234
  res.status(404).json({ error: `Project "${projectSlug}" not found` });
12173
12235
  return;
12174
12236
  }
12175
- const content = await readFile15(projectPath, "utf-8");
12237
+ const content = await readFile16(projectPath, "utf-8");
12176
12238
  await writeFileForce(projectPath, applyArchiveFields(content, false, null));
12177
12239
  const project = await getProjectDetail(projectsDir2, projectSlug);
12178
12240
  res.json({ project });
@@ -12189,7 +12251,7 @@ ${entry}`;
12189
12251
  res.status(404).json({ error: "Assignment not found" });
12190
12252
  return;
12191
12253
  }
12192
- const content = await readFile15(assignmentPath, "utf-8");
12254
+ const content = await readFile16(assignmentPath, "utf-8");
12193
12255
  await writeFileForce(assignmentPath, applyArchiveFields(content, archived, archived ? archiveReason(req.body) : null));
12194
12256
  const assignment = await getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug);
12195
12257
  res.json({ assignment });
@@ -12222,7 +12284,7 @@ ${entry}`;
12222
12284
  return;
12223
12285
  }
12224
12286
  const assignmentPath = resolve21(resolved.assignmentDir, "assignment.md");
12225
- const content = await readFile15(assignmentPath, "utf-8");
12287
+ const content = await readFile16(assignmentPath, "utf-8");
12226
12288
  await writeFileForce(assignmentPath, applyArchiveFields(content, archived, archived ? archiveReason(req.body) : null));
12227
12289
  const assignment = await getAssignmentDetailById(projectsDir2, assignmentsDir2, id);
12228
12290
  res.json({ assignment });
@@ -12263,7 +12325,7 @@ ${entry}`;
12263
12325
  res.status(400).json({ error: validation.error });
12264
12326
  return;
12265
12327
  }
12266
- let content = await readFile15(assignmentPath, "utf-8");
12328
+ let content = await readFile16(assignmentPath, "utf-8");
12267
12329
  content = setTopLevelField(content, "assignee", validation.value);
12268
12330
  content = setTopLevelField(content, "updated", nowTimestamp());
12269
12331
  await writeFileForce(assignmentPath, content);
@@ -12294,7 +12356,7 @@ ${entry}`;
12294
12356
  res.status(400).json({ error: validation.error });
12295
12357
  return;
12296
12358
  }
12297
- let content = await readFile15(assignmentPath, "utf-8");
12359
+ let content = await readFile16(assignmentPath, "utf-8");
12298
12360
  content = setTopLevelField(content, "title", validation.value);
12299
12361
  content = setTopLevelField(content, "updated", nowTimestamp());
12300
12362
  await writeFileForce(assignmentPath, content);
@@ -12812,7 +12874,7 @@ ${entry}`;
12812
12874
  res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}.` });
12813
12875
  return;
12814
12876
  }
12815
- let content = await readFile15(assignmentPath, "utf-8");
12877
+ let content = await readFile16(assignmentPath, "utf-8");
12816
12878
  content = setTopLevelField(content, "status", status);
12817
12879
  content = setTopLevelField(content, "updated", nowTimestamp());
12818
12880
  if (status !== "blocked") {
@@ -12848,7 +12910,7 @@ ${entry}`;
12848
12910
  res.status(400).json({ error: validation.error });
12849
12911
  return;
12850
12912
  }
12851
- let content = await readFile15(assignmentPath, "utf-8");
12913
+ let content = await readFile16(assignmentPath, "utf-8");
12852
12914
  content = setTopLevelField(content, "assignee", validation.value);
12853
12915
  content = setTopLevelField(content, "updated", nowTimestamp());
12854
12916
  await writeFileForce(assignmentPath, content);
@@ -12881,7 +12943,7 @@ ${entry}`;
12881
12943
  res.status(400).json({ error: validation.error });
12882
12944
  return;
12883
12945
  }
12884
- let content = await readFile15(assignmentPath, "utf-8");
12946
+ let content = await readFile16(assignmentPath, "utf-8");
12885
12947
  content = setTopLevelField(content, "title", validation.value);
12886
12948
  content = setTopLevelField(content, "updated", nowTimestamp());
12887
12949
  await writeFileForce(assignmentPath, content);
@@ -13018,7 +13080,7 @@ async function appendCommentTo(assignmentDir, assignmentRef, req, res, reloadDet
13018
13080
  let currentContent;
13019
13081
  let currentCount = 0;
13020
13082
  if (await fileExists(commentsPath)) {
13021
- currentContent = await readFile15(commentsPath, "utf-8");
13083
+ currentContent = await readFile16(commentsPath, "utf-8");
13022
13084
  const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
13023
13085
  if (countMatch) currentCount = parseInt(countMatch[1], 10);
13024
13086
  } else {
@@ -13058,7 +13120,7 @@ async function toggleCommentResolvedAt(assignmentDir, commentId, req, res, reloa
13058
13120
  res.status(400).json({ error: "resolved (boolean) is required" });
13059
13121
  return;
13060
13122
  }
13061
- const content = await readFile15(commentsPath, "utf-8");
13123
+ const content = await readFile16(commentsPath, "utf-8");
13062
13124
  const parsed = parseComments(content);
13063
13125
  const target = parsed.entries.find((e) => e.id === commentId);
13064
13126
  if (!target) {
@@ -13844,7 +13906,7 @@ import { Router as Router8 } from "express";
13844
13906
 
13845
13907
  // src/utils/status-config-resolution.ts
13846
13908
  init_frontmatter();
13847
- import { readFile as readFile16, writeFile as writeFile4, rm as rm2 } from "fs/promises";
13909
+ import { readFile as readFile17, writeFile as writeFile4, rm as rm2 } from "fs/promises";
13848
13910
  import { dirname as dirname5 } from "path";
13849
13911
 
13850
13912
  // src/utils/assignment-walk.ts
@@ -13927,7 +13989,7 @@ async function scanAssignmentsByStatus(projectsDir2, standaloneDir, ids) {
13927
13989
  const assignmentPath = `${entry.assignmentDir}/assignment.md`;
13928
13990
  let content;
13929
13991
  try {
13930
- content = await readFile16(assignmentPath, "utf-8");
13992
+ content = await readFile17(assignmentPath, "utf-8");
13931
13993
  } catch (err2) {
13932
13994
  const code = err2?.code;
13933
13995
  if (code === "ENOENT") {
@@ -13990,7 +14052,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
13990
14052
  const list = affected.get(r.id) ?? [];
13991
14053
  for (const a of list) {
13992
14054
  try {
13993
- const content = await readFile16(a.path, "utf-8");
14055
+ const content = await readFile17(a.path, "utf-8");
13994
14056
  buffer.set(a.path, content);
13995
14057
  } catch (err2) {
13996
14058
  const code = err2?.code;
@@ -14018,7 +14080,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
14018
14080
  for (const a of list) {
14019
14081
  let current;
14020
14082
  try {
14021
- current = await readFile16(a.path, "utf-8");
14083
+ current = await readFile17(a.path, "utf-8");
14022
14084
  } catch (err2) {
14023
14085
  const code = err2?.code;
14024
14086
  if (code === "ENOENT") {
@@ -14067,7 +14129,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
14067
14129
  const list = affected.get(r.id) ?? [];
14068
14130
  for (const a of list) {
14069
14131
  try {
14070
- const current = await readFile16(a.path, "utf-8");
14132
+ const current = await readFile17(a.path, "utf-8");
14071
14133
  const fm = parseAssignmentFrontmatter(current);
14072
14134
  if (fm.status !== r.id) {
14073
14135
  console.warn(
@@ -15456,7 +15518,7 @@ init_playbook();
15456
15518
  init_playbooks();
15457
15519
  import { Router as Router11 } from "express";
15458
15520
  import { resolve as resolve26 } from "path";
15459
- import { readFile as readFile17 } from "fs/promises";
15521
+ import { readFile as readFile18 } from "fs/promises";
15460
15522
  function statusForPlaybookError(code) {
15461
15523
  switch (code) {
15462
15524
  case "manifest":
@@ -15538,7 +15600,7 @@ function createPlaybooksRouter(playbooksDir3) {
15538
15600
  return;
15539
15601
  }
15540
15602
  const filePath = resolve26(playbooksDir3, resolved.filename);
15541
- const content = await readFile17(filePath, "utf-8");
15603
+ const content = await readFile18(filePath, "utf-8");
15542
15604
  res.json({
15543
15605
  documentType: "playbook",
15544
15606
  title: `Edit Playbook: ${resolved.slug}`,
@@ -15889,8 +15951,8 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
15889
15951
  router.post("/:workspace/archive", async (req, res) => {
15890
15952
  try {
15891
15953
  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");
15954
+ const { resolve: resolve82 } = await import("path");
15955
+ const { readFile: readFile56 } = await import("fs/promises");
15894
15956
  const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
15895
15957
  const workspace = getWorkspaceParam(req.params.workspace);
15896
15958
  const checklist = await readChecklist(todosDir2, workspace);
@@ -15906,10 +15968,10 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
15906
15968
  (e) => e.itemIds.every((id) => completedIds.has(id))
15907
15969
  );
15908
15970
  const archFile = archivePath2(todosDir2, workspace, checklist.archiveInterval);
15909
- await ensureDir(resolve79(todosDir2, "archive"));
15971
+ await ensureDir(resolve82(todosDir2, "archive"));
15910
15972
  let archContent = "";
15911
15973
  if (await fileExists(archFile)) {
15912
- archContent = await readFile54(archFile, "utf-8");
15974
+ archContent = await readFile56(archFile, "utf-8");
15913
15975
  archContent = archContent.trimEnd() + "\n\n";
15914
15976
  } else {
15915
15977
  archContent = `---
@@ -16191,7 +16253,7 @@ workspace: ${workspace}
16191
16253
  const { readConfig: readConfig3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
16192
16254
  const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
16193
16255
  const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
16194
- const { readFile: readFile54 } = await import("fs/promises");
16256
+ const { readFile: readFile56 } = await import("fs/promises");
16195
16257
  const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
16196
16258
  const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
16197
16259
  let assignmentRef;
@@ -16212,7 +16274,7 @@ workspace: ${workspace}
16212
16274
  }
16213
16275
  const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
16214
16276
  if (!await fileExists2(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
16215
- let content = await readFile54(assignmentMdPath2, "utf-8");
16277
+ let content = await readFile56(assignmentMdPath2, "utf-8");
16216
16278
  content = appendTodosToAssignmentBody2(
16217
16279
  content,
16218
16280
  items.map((it) => ({
@@ -16400,7 +16462,7 @@ init_fs();
16400
16462
  init_paths();
16401
16463
  init_slug();
16402
16464
  import { Router as Router13 } from "express";
16403
- import { mkdir as mkdir3, readFile as readFile18, rename as rename6 } from "fs/promises";
16465
+ import { mkdir as mkdir3, readFile as readFile19, rename as rename6 } from "fs/promises";
16404
16466
  import { resolve as resolve27, dirname as dirname7 } from "path";
16405
16467
  init_promote_todos();
16406
16468
  init_api();
@@ -16625,7 +16687,7 @@ function createProjectTodosRouter(projectsDir2, broadcast, workspaceTodosDir) {
16625
16687
  const archFile = archivePath(todosDir2, slug, checklist.archiveInterval);
16626
16688
  let archContent = "";
16627
16689
  if (await fileExists(archFile)) {
16628
- archContent = await readFile18(archFile, "utf-8");
16690
+ archContent = await readFile19(archFile, "utf-8");
16629
16691
  archContent = archContent.trimEnd() + "\n\n";
16630
16692
  } else {
16631
16693
  archContent = `---
@@ -17082,7 +17144,7 @@ workspace: ${slug}
17082
17144
  }
17083
17145
  const assignmentMdPath2 = resolve27(assignmentDir, "assignment.md");
17084
17146
  if (!await fileExists(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
17085
- let content = await readFile18(assignmentMdPath2, "utf-8");
17147
+ let content = await readFile19(assignmentMdPath2, "utf-8");
17086
17148
  content = appendTodosToAssignmentBody2(
17087
17149
  content,
17088
17150
  items.map((it) => ({
@@ -17286,7 +17348,7 @@ init_fs();
17286
17348
  init_paths();
17287
17349
  init_parser2();
17288
17350
  import { randomBytes as randomBytes2 } from "crypto";
17289
- import { readFile as readFile19 } from "fs/promises";
17351
+ import { readFile as readFile20 } from "fs/promises";
17290
17352
  var BUNDLE_ID_REGEX = /^[a-f0-9]{4}$/;
17291
17353
  var SCOPE_VALUES = /* @__PURE__ */ new Set(["workspace", "project", "global"]);
17292
17354
  var SCOPE_ID_REGEX = /^[a-z0-9_][a-z0-9_-]*$/;
@@ -17389,7 +17451,7 @@ ${lines}
17389
17451
  async function readBundles(todosDir2) {
17390
17452
  const path = bundlesPath(todosDir2);
17391
17453
  if (!await fileExists(path)) return [];
17392
- const content = await readFile19(path, "utf-8");
17454
+ const content = await readFile20(path, "utf-8");
17393
17455
  return parseBundles(content).bundles;
17394
17456
  }
17395
17457
  async function writeBundles(todosDir2, bundles) {
@@ -17566,7 +17628,7 @@ init_fs();
17566
17628
  init_config2();
17567
17629
  import { execFile as execFile2 } from "child_process";
17568
17630
  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";
17631
+ 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
17632
  import { resolve as resolve29, join as join3 } from "path";
17571
17633
  import { tmpdir } from "os";
17572
17634
  var exec2 = promisify2(execFile2);
@@ -17627,7 +17689,7 @@ async function acquireLock() {
17627
17689
  return lockPath;
17628
17690
  } catch (err2) {
17629
17691
  if (err2.code === "EEXIST") {
17630
- const pid = await readFile20(lockPath, "utf-8").catch(() => "");
17692
+ const pid = await readFile21(lockPath, "utf-8").catch(() => "");
17631
17693
  throw new Error(
17632
17694
  `Backup operation already in progress (lock file at ${lockPath}, pid ${pid.trim() || "unknown"}). If stale, delete the file and retry.`
17633
17695
  );
@@ -17674,7 +17736,7 @@ function resolveCategoriesStrict(csv) {
17674
17736
  return parseCategoriesStrict(parts);
17675
17737
  }
17676
17738
  async function readSanitizedConfig(configPath) {
17677
- const content = await readFile20(configPath, "utf-8");
17739
+ const content = await readFile21(configPath, "utf-8");
17678
17740
  return content.replace(/^(\s*lastBackup:\s*).*$/m, "$1null").replace(/^(\s*lastRestore:\s*).*$/m, "$1null");
17679
17741
  }
17680
17742
  async function backupToGithub(overrides) {
@@ -19066,7 +19128,7 @@ init_frontmatter();
19066
19128
  init_timestamp();
19067
19129
  init_assignment_resolver();
19068
19130
  import { resolve as resolve33 } from "path";
19069
- import { readFile as readFile21, writeFile as writeFile7 } from "fs/promises";
19131
+ import { readFile as readFile22, writeFile as writeFile7 } from "fs/promises";
19070
19132
  async function resolveTarget(target, options) {
19071
19133
  const config = await readConfig();
19072
19134
  const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
@@ -19098,7 +19160,7 @@ async function resolveTarget(target, options) {
19098
19160
  return null;
19099
19161
  }
19100
19162
  async function writeArchiveState(filePath, archived, reason) {
19101
- const content = await readFile21(filePath, "utf-8");
19163
+ const content = await readFile22(filePath, "utf-8");
19102
19164
  const updated = updateAssignmentFile(content, {
19103
19165
  archived,
19104
19166
  archivedAt: archived ? nowTimestamp() : null,
@@ -19167,7 +19229,7 @@ init_config2();
19167
19229
  init_frontmatter();
19168
19230
  init_timestamp();
19169
19231
  import { resolve as resolve34 } from "path";
19170
- import { readdir as readdir13, readFile as readFile22 } from "fs/promises";
19232
+ import { readdir as readdir13, readFile as readFile23 } from "fs/promises";
19171
19233
  var PROMOTABLE_STATUSES = /* @__PURE__ */ new Set(["pending"]);
19172
19234
  function objectiveIsFleshedOut(content) {
19173
19235
  const match = content.match(/##\s+Objective\s*\n([\s\S]*?)(?=\n##\s+|$)/);
@@ -19193,7 +19255,7 @@ async function collectCandidates(baseDirs) {
19193
19255
  if (await fileExists(directAssignmentMd)) {
19194
19256
  const fm = await parseSafe(directAssignmentMd);
19195
19257
  if (fm && PROMOTABLE_STATUSES.has(fm.status)) {
19196
- const content = await readFile22(directAssignmentMd, "utf-8");
19258
+ const content = await readFile23(directAssignmentMd, "utf-8");
19197
19259
  if (objectiveIsFleshedOut(content) && hasAcceptanceCriteria(content)) {
19198
19260
  candidates.push({
19199
19261
  projectSlug: null,
@@ -19216,7 +19278,7 @@ async function collectCandidates(baseDirs) {
19216
19278
  if (!await fileExists(assignmentMd)) continue;
19217
19279
  const fm = await parseSafe(assignmentMd);
19218
19280
  if (!fm || !PROMOTABLE_STATUSES.has(fm.status)) continue;
19219
- const content = await readFile22(assignmentMd, "utf-8");
19281
+ const content = await readFile23(assignmentMd, "utf-8");
19220
19282
  if (!objectiveIsFleshedOut(content)) continue;
19221
19283
  if (!hasAcceptanceCriteria(content)) continue;
19222
19284
  candidates.push({
@@ -19233,7 +19295,7 @@ async function collectCandidates(baseDirs) {
19233
19295
  }
19234
19296
  async function parseSafe(path) {
19235
19297
  try {
19236
- const content = await readFile22(path, "utf-8");
19298
+ const content = await readFile23(path, "utf-8");
19237
19299
  return parseAssignmentFrontmatter(content);
19238
19300
  } catch {
19239
19301
  return null;
@@ -19262,7 +19324,7 @@ async function migrateStatusesCommand(options) {
19262
19324
  const now = nowTimestamp();
19263
19325
  let migrated = 0;
19264
19326
  for (const c2 of candidates) {
19265
- const content = await readFile22(c2.assignmentMd, "utf-8");
19327
+ const content = await readFile23(c2.assignmentMd, "utf-8");
19266
19328
  const updated = updateAssignmentFile(content, {
19267
19329
  status: c2.toStatus,
19268
19330
  updated: now
@@ -19320,7 +19382,7 @@ import {
19320
19382
  readdir as readdir14,
19321
19383
  symlink,
19322
19384
  lstat,
19323
- readFile as readFile23,
19385
+ readFile as readFile24,
19324
19386
  readlink,
19325
19387
  rename as rename8,
19326
19388
  rm as rm4,
@@ -19362,8 +19424,8 @@ function getPluginManifestRelativePath(pluginKind) {
19362
19424
  return pluginKind === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json";
19363
19425
  }
19364
19426
  function getDefaultPluginTargetDir(pluginKind) {
19365
- const home = homedir3();
19366
- return pluginKind === "claude" ? resolve36(home, ".claude", "plugins", "syntaur") : resolve36(home, "plugins", "syntaur");
19427
+ const home2 = homedir3();
19428
+ return pluginKind === "claude" ? resolve36(home2, ".claude", "plugins", "syntaur") : resolve36(home2, "plugins", "syntaur");
19367
19429
  }
19368
19430
  function getDefaultMarketplacePath() {
19369
19431
  return resolve36(homedir3(), ".agents", "plugins", "marketplace.json");
@@ -19381,7 +19443,7 @@ function getInstallMarkerPath(targetDir) {
19381
19443
  return resolve36(targetDir, INSTALL_MARKER_FILENAME);
19382
19444
  }
19383
19445
  async function readPackageManifest(packageRoot) {
19384
- const raw = await readFile23(resolve36(packageRoot, "package.json"), "utf-8");
19446
+ const raw = await readFile24(resolve36(packageRoot, "package.json"), "utf-8");
19385
19447
  return JSON.parse(raw);
19386
19448
  }
19387
19449
  async function readJsonFileIfExists(pathValue) {
@@ -19389,7 +19451,7 @@ async function readJsonFileIfExists(pathValue) {
19389
19451
  return null;
19390
19452
  }
19391
19453
  try {
19392
- const raw = await readFile23(pathValue, "utf-8");
19454
+ const raw = await readFile24(pathValue, "utf-8");
19393
19455
  return JSON.parse(raw);
19394
19456
  } catch {
19395
19457
  return null;
@@ -19405,7 +19467,7 @@ async function readPluginManifestName(targetDir, pluginKind) {
19405
19467
  if (!await fileExists(manifestPath)) {
19406
19468
  return void 0;
19407
19469
  }
19408
- const raw = await readFile23(manifestPath, "utf-8");
19470
+ const raw = await readFile24(manifestPath, "utf-8");
19409
19471
  const parsed = JSON.parse(raw);
19410
19472
  return parsed.name;
19411
19473
  }
@@ -19415,7 +19477,7 @@ async function readInstallMetadata(targetDir) {
19415
19477
  return null;
19416
19478
  }
19417
19479
  try {
19418
- const raw = await readFile23(markerPath, "utf-8");
19480
+ const raw = await readFile24(markerPath, "utf-8");
19419
19481
  return JSON.parse(raw);
19420
19482
  } catch {
19421
19483
  return null;
@@ -19538,7 +19600,7 @@ async function writeClaudeMarketplaceFile(manifestPath, marketplace) {
19538
19600
  }
19539
19601
  if (await fileExists(manifestPath)) {
19540
19602
  try {
19541
- const prev = await readFile23(manifestPath, "utf-8");
19603
+ const prev = await readFile24(manifestPath, "utf-8");
19542
19604
  JSON.parse(prev);
19543
19605
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
19544
19606
  await writeFile8(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
@@ -19686,7 +19748,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
19686
19748
  const manifestPath = getClaudeKnownMarketplacesPath();
19687
19749
  let existing = {};
19688
19750
  if (await fileExists(manifestPath)) {
19689
- const raw = await readFile23(manifestPath, "utf-8");
19751
+ const raw = await readFile24(manifestPath, "utf-8");
19690
19752
  try {
19691
19753
  const parsed = JSON.parse(raw);
19692
19754
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
@@ -19713,7 +19775,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
19713
19775
  existing[name].autoUpdate = true;
19714
19776
  await ensureDir(dirname10(manifestPath));
19715
19777
  if (await fileExists(manifestPath)) {
19716
- const prev = await readFile23(manifestPath, "utf-8");
19778
+ const prev = await readFile24(manifestPath, "utf-8");
19717
19779
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
19718
19780
  await writeFile8(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
19719
19781
  }
@@ -19731,7 +19793,7 @@ async function setSyntaurPluginEnabled(options) {
19731
19793
  const key = `syntaur@${options.marketplaceName}`;
19732
19794
  let parsed = {};
19733
19795
  if (await fileExists(settingsPath)) {
19734
- const raw = await readFile23(settingsPath, "utf-8");
19796
+ const raw = await readFile24(settingsPath, "utf-8");
19735
19797
  try {
19736
19798
  parsed = JSON.parse(raw);
19737
19799
  } catch {
@@ -19750,7 +19812,7 @@ async function setSyntaurPluginEnabled(options) {
19750
19812
  await ensureDir(dirname10(settingsPath));
19751
19813
  if (await fileExists(settingsPath)) {
19752
19814
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
19753
- const prev = await readFile23(settingsPath, "utf-8");
19815
+ const prev = await readFile24(settingsPath, "utf-8");
19754
19816
  await writeFile8(`${settingsPath}.bak-${stamp}`, prev, "utf-8");
19755
19817
  }
19756
19818
  const tmpPath = `${settingsPath}.tmp`;
@@ -19988,7 +20050,7 @@ async function readMarketplaceFile(marketplacePath) {
19988
20050
  plugins: []
19989
20051
  };
19990
20052
  }
19991
- const raw = await readFile23(marketplacePath, "utf-8");
20053
+ const raw = await readFile24(marketplacePath, "utf-8");
19992
20054
  const parsed = JSON.parse(raw);
19993
20055
  return {
19994
20056
  name: parsed.name ?? "local",
@@ -20224,13 +20286,13 @@ async function textPrompt(question, defaultValue) {
20224
20286
 
20225
20287
  // src/utils/install-skills.ts
20226
20288
  init_fs();
20227
- import { readFile as readFile25, readdir as readdir15, mkdir as mkdir4, copyFile, rm as rm5, lstat as lstat2 } from "fs/promises";
20289
+ import { readFile as readFile26, readdir as readdir15, mkdir as mkdir4, copyFile, rm as rm5, lstat as lstat2 } from "fs/promises";
20228
20290
  import { resolve as resolve38, relative as relative3, join as join4 } from "path";
20229
20291
  import { homedir as homedir5 } from "os";
20230
20292
 
20231
20293
  // src/utils/plugin-state.ts
20232
20294
  init_fs();
20233
- import { readFile as readFile24 } from "fs/promises";
20295
+ import { readFile as readFile25 } from "fs/promises";
20234
20296
  import { resolve as resolve37 } from "path";
20235
20297
  import { homedir as homedir4 } from "os";
20236
20298
  function settingsPathFor(agent) {
@@ -20241,7 +20303,7 @@ async function readJsonOrNull(path) {
20241
20303
  if (!path) return null;
20242
20304
  if (!await fileExists(path)) return null;
20243
20305
  try {
20244
- const raw = await readFile24(path, "utf-8");
20306
+ const raw = await readFile25(path, "utf-8");
20245
20307
  return JSON.parse(raw);
20246
20308
  } catch {
20247
20309
  return null;
@@ -20315,7 +20377,7 @@ async function walkFiles(root) {
20315
20377
  }
20316
20378
  async function filesEqual(a, b) {
20317
20379
  try {
20318
- const [ba, bb] = await Promise.all([readFile25(a), readFile25(b)]);
20380
+ const [ba, bb] = await Promise.all([readFile26(a), readFile26(b)]);
20319
20381
  if (ba.length !== bb.length) return false;
20320
20382
  return ba.equals(bb);
20321
20383
  } catch {
@@ -20416,6 +20478,24 @@ async function installSkillsWithReport(options) {
20416
20478
  }
20417
20479
  return { results };
20418
20480
  }
20481
+ async function installSkillsToDir(options) {
20482
+ const source = options.sourceDir ?? await getSkillsDir();
20483
+ if (!await fileExists(source)) {
20484
+ throw new Error(
20485
+ `Syntaur skills not found at ${source}. Reinstall syntaur: npm install -g syntaur@latest`
20486
+ );
20487
+ }
20488
+ const force = options.force ?? false;
20489
+ const skillNames = await discoverSkillNames(source);
20490
+ await mkdir4(options.targetDir, { recursive: true });
20491
+ const results = [];
20492
+ for (const skill of skillNames) {
20493
+ const srcDir = join4(source, skill);
20494
+ const destDir = join4(options.targetDir, skill);
20495
+ results.push(await installSkillDir(srcDir, destDir, skill, force));
20496
+ }
20497
+ return results;
20498
+ }
20419
20499
  async function uninstallSkills(options) {
20420
20500
  const targetRoot = options.targetDir ?? defaultSkillTargetDir(options.target);
20421
20501
  if (!await fileExists(targetRoot)) return [];
@@ -20437,7 +20517,7 @@ async function uninstallSkills(options) {
20437
20517
  if (await isSymlink(destDir)) continue;
20438
20518
  const skillMd = join4(destDir, "SKILL.md");
20439
20519
  if (!await fileExists(skillMd)) continue;
20440
- const content = await readFile25(skillMd, "utf-8").catch(() => "");
20520
+ const content = await readFile26(skillMd, "utf-8").catch(() => "");
20441
20521
  const match = content.match(/^name:\s*(\S+)\s*$/m);
20442
20522
  if (!match || match[1] !== skill) continue;
20443
20523
  await rm5(destDir, { recursive: true, force: true });
@@ -20965,7 +21045,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
20965
21045
  `Spawn failed: ${msg}. Verify the terminal is installed and on PATH.`
20966
21046
  );
20967
21047
  }
20968
- await new Promise((resolve79, reject) => {
21048
+ await new Promise((resolve82, reject) => {
20969
21049
  let settled = false;
20970
21050
  let stderr = "";
20971
21051
  const finishOk = () => {
@@ -20975,7 +21055,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
20975
21055
  child.unref();
20976
21056
  } catch {
20977
21057
  }
20978
- resolve79();
21058
+ resolve82();
20979
21059
  };
20980
21060
  const finishErr = (remediation) => {
20981
21061
  if (settled) return;
@@ -21017,13 +21097,29 @@ function buildTerminalInvocation(plan) {
21017
21097
  return {
21018
21098
  command: "osascript",
21019
21099
  args: [
21100
+ "-e",
21101
+ 'set wasRunning to application "Terminal" is running',
21020
21102
  "-e",
21021
21103
  'tell application "Terminal"',
21022
21104
  "-e",
21023
21105
  "activate",
21024
21106
  "-e",
21107
+ "if wasRunning then",
21108
+ "-e",
21025
21109
  `do script ${appleScriptString(cdAndRun)}`,
21026
21110
  "-e",
21111
+ "else",
21112
+ "-e",
21113
+ "repeat until (count of windows) > 0",
21114
+ "-e",
21115
+ "delay 0.1",
21116
+ "-e",
21117
+ "end repeat",
21118
+ "-e",
21119
+ `do script ${appleScriptString(cdAndRun)} in window 1`,
21120
+ "-e",
21121
+ "end if",
21122
+ "-e",
21027
21123
  "end tell"
21028
21124
  ]
21029
21125
  };
@@ -21130,7 +21226,7 @@ function normalizeSlashes(p) {
21130
21226
  }
21131
21227
  function detectInstallKind(scriptUrl, opts = {}) {
21132
21228
  const realpath3 = opts.realpath ?? realpathSync.native;
21133
- const readFile54 = opts.readFile ?? ((p) => readFileSync(p, "utf-8"));
21229
+ const readFile56 = opts.readFile ?? ((p) => readFileSync(p, "utf-8"));
21134
21230
  const ua = opts.envUserAgent !== void 0 ? opts.envUserAgent : process.env.npm_config_user_agent ?? "";
21135
21231
  const resolved = resolveScriptPath(scriptUrl, realpath3);
21136
21232
  if (resolved === null) {
@@ -21151,7 +21247,7 @@ function detectInstallKind(scriptUrl, opts = {}) {
21151
21247
  const pkgJsonPath = join5(dir, "package.json");
21152
21248
  let raw;
21153
21249
  try {
21154
- raw = readFile54(pkgJsonPath);
21250
+ raw = readFile56(pkgJsonPath);
21155
21251
  } catch {
21156
21252
  const parent2 = dirname11(dir);
21157
21253
  if (parent2 === dir) break;
@@ -21236,20 +21332,20 @@ async function maybeNudgeForNpxInstall(scriptUrl) {
21236
21332
  init_paths();
21237
21333
  init_fs();
21238
21334
  import { fileURLToPath as fileURLToPath6 } from "url";
21239
- import { readFile as readFile27 } from "fs/promises";
21335
+ import { readFile as readFile28 } from "fs/promises";
21240
21336
  import { dirname as dirname13, join as join7, resolve as resolve41 } from "path";
21241
21337
  import { spawn as spawn5 } from "child_process";
21242
21338
  import { createInterface as createInterface2 } from "readline/promises";
21243
21339
 
21244
21340
  // src/utils/version.ts
21245
21341
  import { fileURLToPath as fileURLToPath5 } from "url";
21246
- import { readFile as readFile26 } from "fs/promises";
21342
+ import { readFile as readFile27 } from "fs/promises";
21247
21343
  import { dirname as dirname12, join as join6 } from "path";
21248
21344
  async function readPackageVersion(scriptUrl) {
21249
21345
  try {
21250
21346
  const scriptPath = fileURLToPath5(scriptUrl);
21251
21347
  const pkgRoot = dirname12(dirname12(scriptPath));
21252
- const raw = await readFile26(join6(pkgRoot, "package.json"), "utf-8");
21348
+ const raw = await readFile27(join6(pkgRoot, "package.json"), "utf-8");
21253
21349
  const parsed = JSON.parse(raw);
21254
21350
  return typeof parsed.version === "string" ? parsed.version : null;
21255
21351
  } catch {
@@ -21279,7 +21375,7 @@ function isRunningViaNpx(scriptUrl) {
21279
21375
  async function readState() {
21280
21376
  if (!await fileExists(STATE_FILE)) return null;
21281
21377
  try {
21282
- const raw = await readFile27(STATE_FILE, "utf-8");
21378
+ const raw = await readFile28(STATE_FILE, "utf-8");
21283
21379
  return JSON.parse(raw);
21284
21380
  } catch {
21285
21381
  return null;
@@ -21338,7 +21434,7 @@ async function readGlobalVersion() {
21338
21434
  try {
21339
21435
  const manifestPath = join7(rootPath, "syntaur", "package.json");
21340
21436
  if (!await fileExists(manifestPath)) return null;
21341
- const raw = await readFile27(manifestPath, "utf-8");
21437
+ const raw = await readFile28(manifestPath, "utf-8");
21342
21438
  const parsed = JSON.parse(raw);
21343
21439
  return typeof parsed.version === "string" ? parsed.version : null;
21344
21440
  } catch {
@@ -21696,7 +21792,7 @@ async function refreshPluginSkills(options, pm, runner, getManagedDir, resolveFr
21696
21792
  // src/commands/install-statusline.ts
21697
21793
  init_paths();
21698
21794
  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";
21795
+ 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
21796
  import { resolve as resolve43, dirname as dirname15 } from "path";
21701
21797
  import { homedir as homedir7 } from "os";
21702
21798
  import { fileURLToPath as fileURLToPath8 } from "url";
@@ -21704,7 +21800,7 @@ import { fileURLToPath as fileURLToPath8 } from "url";
21704
21800
  // src/commands/configure-statusline.ts
21705
21801
  init_paths();
21706
21802
  init_fs();
21707
- import { readFile as readFile28, writeFile as writeFile10 } from "fs/promises";
21803
+ import { readFile as readFile29, writeFile as writeFile10 } from "fs/promises";
21708
21804
  import { resolve as resolve42, dirname as dirname14 } from "path";
21709
21805
  import { spawnSync as spawnSync5 } from "child_process";
21710
21806
  import { checkbox, input as input2, confirm } from "@inquirer/prompts";
@@ -21731,7 +21827,7 @@ function getConfigPath(installRoot) {
21731
21827
  async function readConfig2(path) {
21732
21828
  if (!await fileExists(path)) return null;
21733
21829
  try {
21734
- const raw = await readFile28(path, "utf-8");
21830
+ const raw = await readFile29(path, "utf-8");
21735
21831
  const parsed = JSON.parse(raw);
21736
21832
  if (!parsed || typeof parsed !== "object") return null;
21737
21833
  const segments = Array.isArray(parsed.segments) ? parsed.segments.filter(isSegmentName) : [];
@@ -21919,7 +22015,7 @@ function getPackageStatuslineSource() {
21919
22015
  }
21920
22016
  async function readSettingsJson(settingsPath) {
21921
22017
  if (!await fileExists(settingsPath)) return {};
21922
- const raw = await readFile29(settingsPath, "utf-8");
22018
+ const raw = await readFile30(settingsPath, "utf-8");
21923
22019
  if (raw.trim() === "") return {};
21924
22020
  try {
21925
22021
  const parsed = JSON.parse(raw);
@@ -22107,7 +22203,7 @@ async function uninstallStatuslineCommand(options = {}) {
22107
22203
  let restored = null;
22108
22204
  if (await fileExists(backupPath)) {
22109
22205
  try {
22110
- const raw = await readFile29(backupPath, "utf-8");
22206
+ const raw = await readFile30(backupPath, "utf-8");
22111
22207
  const parsed = JSON.parse(raw);
22112
22208
  const prev = parsed?.previousStatusLine;
22113
22209
  if (prev && typeof prev === "object" && typeof prev.command === "string") {
@@ -22284,6 +22380,353 @@ async function uninstallSkillsCommand(options) {
22284
22380
  // src/commands/setup.ts
22285
22381
  import { execSync } from "child_process";
22286
22382
  init_config2();
22383
+
22384
+ // src/commands/cross-agent-install.ts
22385
+ init_fs();
22386
+ import { spawnSync as spawnSync6 } from "child_process";
22387
+ import { resolve as resolve46 } from "path";
22388
+ import { readFile as readFile31 } from "fs/promises";
22389
+
22390
+ // src/commands/setup-adapter.ts
22391
+ init_paths();
22392
+ init_fs();
22393
+ init_config2();
22394
+ init_slug();
22395
+ import { resolve as resolve45 } from "path";
22396
+
22397
+ // src/targets/registry.ts
22398
+ init_fs();
22399
+ import { homedir as homedir8 } from "os";
22400
+ import { resolve as resolve44 } from "path";
22401
+ function home(...segments) {
22402
+ return resolve44(homedir8(), ...segments);
22403
+ }
22404
+ function hermesHome() {
22405
+ const env = process.env.HERMES_HOME;
22406
+ return env && env.length > 0 ? resolve44(env) : home(".hermes");
22407
+ }
22408
+ function hermesSkillsDir() {
22409
+ return resolve44(hermesHome(), "skills");
22410
+ }
22411
+ function isHermesHomeCustom() {
22412
+ return hermesHome() !== home(".hermes");
22413
+ }
22414
+ function codexHome() {
22415
+ const env = process.env.CODEX_HOME;
22416
+ return env && env.length > 0 ? resolve44(env) : home(".codex");
22417
+ }
22418
+ var detectDir = (dir) => () => fileExists(dir);
22419
+ var AGENT_TARGETS = [
22420
+ {
22421
+ id: "cursor",
22422
+ displayName: "Cursor",
22423
+ skillsShAgentId: "cursor",
22424
+ detect: detectDir(home(".cursor")),
22425
+ skillsDir: { global: home(".cursor", "skills") },
22426
+ instructions: {
22427
+ files: [
22428
+ { path: ".cursor/rules/syntaur-protocol.mdc", renderer: "cursorProtocol" },
22429
+ { path: ".cursor/rules/syntaur-assignment.mdc", renderer: "cursorAssignment" }
22430
+ ]
22431
+ }
22432
+ },
22433
+ {
22434
+ // codex is BOTH an adapter (writes AGENTS.md) AND a native plugin.
22435
+ id: "codex",
22436
+ displayName: "Codex",
22437
+ skillsShAgentId: "codex",
22438
+ nativePlugin: "codex",
22439
+ detect: detectDir(codexHome()),
22440
+ skillsDir: { global: resolve44(codexHome(), "skills") },
22441
+ instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] }
22442
+ },
22443
+ {
22444
+ id: "opencode",
22445
+ displayName: "OpenCode",
22446
+ skillsShAgentId: "opencode",
22447
+ detect: detectDir(home(".config", "opencode")),
22448
+ skillsDir: { global: home(".config", "opencode", "skills") },
22449
+ instructions: {
22450
+ files: [
22451
+ { path: "AGENTS.md", renderer: "codexAgents" },
22452
+ { path: "opencode.json", renderer: "openCodeConfig" }
22453
+ ]
22454
+ }
22455
+ },
22456
+ {
22457
+ // claude has NO adapter today (not in the old SUPPORTED_FRAMEWORKS) — the
22458
+ // full plugin path owns its skills/hooks/commands. Native-plugin only.
22459
+ id: "claude",
22460
+ displayName: "Claude Code",
22461
+ skillsShAgentId: "claude-code",
22462
+ nativePlugin: "claude",
22463
+ detect: detectDir(home(".claude")),
22464
+ skillsDir: { global: home(".claude", "skills") }
22465
+ },
22466
+ {
22467
+ id: "pi",
22468
+ displayName: "Pi",
22469
+ skillsShAgentId: "pi",
22470
+ detect: detectDir(home(".pi")),
22471
+ skillsDir: { global: home(".pi", "agent", "skills") },
22472
+ instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] }
22473
+ },
22474
+ {
22475
+ id: "openclaw",
22476
+ displayName: "OpenClaw",
22477
+ skillsShAgentId: "openclaw",
22478
+ detect: detectDir(home(".openclaw")),
22479
+ skillsDir: { global: home(".openclaw", "skills") },
22480
+ instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] }
22481
+ },
22482
+ {
22483
+ id: "hermes",
22484
+ displayName: "Hermes Agent",
22485
+ skillsShAgentId: "hermes-agent",
22486
+ detect: () => fileExists(hermesHome()),
22487
+ skillsDir: { global: hermesSkillsDir() },
22488
+ instructions: { files: [{ path: "SOUL.md", renderer: "hermesSoul" }] }
22489
+ }
22490
+ ];
22491
+ var AGENT_TARGETS_BY_ID = Object.fromEntries(
22492
+ AGENT_TARGETS.map((t) => [t.id, t])
22493
+ );
22494
+ function getAgentTarget(id) {
22495
+ return AGENT_TARGETS_BY_ID[id];
22496
+ }
22497
+ function agentTargetIds() {
22498
+ return AGENT_TARGETS.map((t) => t.id);
22499
+ }
22500
+ function adapterTargets() {
22501
+ return AGENT_TARGETS.filter((t) => t.instructions !== void 0);
22502
+ }
22503
+
22504
+ // src/targets/renderers.ts
22505
+ init_cursor_rules();
22506
+ init_codex_agents();
22507
+ init_opencode_config();
22508
+ init_hermes_soul();
22509
+ var RENDERERS = {
22510
+ codexAgents: (ctx) => renderCodexAgents(ctx),
22511
+ cursorProtocol: () => renderCursorProtocol(),
22512
+ cursorAssignment: (ctx) => renderCursorAssignment(ctx),
22513
+ openCodeConfig: (ctx) => renderOpenCodeConfig({ projectDir: ctx.projectDir }),
22514
+ hermesSoul: (ctx) => renderHermesSoul(ctx)
22515
+ };
22516
+
22517
+ // src/commands/setup-adapter.ts
22518
+ async function setupAdapterCommand(framework, options) {
22519
+ const target = getAgentTarget(framework);
22520
+ if (!target || !target.instructions) {
22521
+ const supported = adapterTargets().map((t) => t.id).join(", ");
22522
+ throw new Error(
22523
+ `Unsupported framework "${framework}". Supported: ${supported}`
22524
+ );
22525
+ }
22526
+ if (!options.project) {
22527
+ throw new Error("--project <slug> is required.");
22528
+ }
22529
+ if (!options.assignment) {
22530
+ throw new Error("--assignment <slug> is required.");
22531
+ }
22532
+ if (!isValidSlug(options.project)) {
22533
+ throw new Error(
22534
+ `Invalid project slug "${options.project}". Slugs must be lowercase, hyphen-separated, with no special characters.`
22535
+ );
22536
+ }
22537
+ if (!isValidSlug(options.assignment)) {
22538
+ throw new Error(
22539
+ `Invalid assignment slug "${options.assignment}". Slugs must be lowercase, hyphen-separated, with no special characters.`
22540
+ );
22541
+ }
22542
+ const config = await readConfig();
22543
+ const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
22544
+ const projectDir = resolve45(baseDir, options.project);
22545
+ const assignmentDir = resolve45(projectDir, "assignments", options.assignment);
22546
+ const projectMdPath = resolve45(projectDir, "project.md");
22547
+ if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
22548
+ throw new Error(`Project "${options.project}" not found at ${projectDir}.`);
22549
+ }
22550
+ const assignmentMdPath2 = resolve45(assignmentDir, "assignment.md");
22551
+ if (!await fileExists(assignmentDir) || !await fileExists(assignmentMdPath2)) {
22552
+ throw new Error(
22553
+ `Assignment "${options.assignment}" not found at ${assignmentDir}.`
22554
+ );
22555
+ }
22556
+ const cwd = process.cwd();
22557
+ const rendererParams = {
22558
+ projectSlug: options.project,
22559
+ assignmentSlug: options.assignment,
22560
+ projectDir,
22561
+ assignmentDir
22562
+ };
22563
+ const writtenFiles = [];
22564
+ const upToDateFiles = [];
22565
+ const skippedFiles = [];
22566
+ for (const file of target.instructions.files) {
22567
+ const filePath = resolve45(cwd, file.path);
22568
+ const content = RENDERERS[file.renderer](rendererParams);
22569
+ const status = await writeFileReport(filePath, content, {
22570
+ force: options.force
22571
+ });
22572
+ if (status === "differs-preserved") {
22573
+ skippedFiles.push(filePath);
22574
+ } else if (status === "already-current") {
22575
+ upToDateFiles.push(filePath);
22576
+ } else {
22577
+ writtenFiles.push(filePath);
22578
+ }
22579
+ }
22580
+ if (writtenFiles.length > 0) {
22581
+ console.log(`Generated ${target.id} adapter files:`);
22582
+ for (const f of writtenFiles) {
22583
+ console.log(` ${f}`);
22584
+ }
22585
+ }
22586
+ if (upToDateFiles.length > 0) {
22587
+ console.log(`Already up-to-date:`);
22588
+ for (const f of upToDateFiles) {
22589
+ console.log(` ${f}`);
22590
+ }
22591
+ }
22592
+ if (skippedFiles.length > 0) {
22593
+ console.log(`Skipped (exists with different content, use --force to overwrite):`);
22594
+ for (const f of skippedFiles) {
22595
+ console.log(` ${f}`);
22596
+ }
22597
+ }
22598
+ if (writtenFiles.length === 0 && skippedFiles.length === 0 && upToDateFiles.length > 0) {
22599
+ console.log(`No changes. All ${target.id} adapter files are already up-to-date.`);
22600
+ }
22601
+ }
22602
+
22603
+ // src/commands/cross-agent-install.ts
22604
+ init_config2();
22605
+ var DEFAULT_SKILLS_SOURCE = "prong-horn/syntaur";
22606
+ function parseTargetIds(options) {
22607
+ const raw = [options.target, options.agent].filter(Boolean).join(",");
22608
+ const ids = raw.split(",").map((s) => s.trim()).filter(Boolean);
22609
+ return [...new Set(ids)];
22610
+ }
22611
+ function isNpxAvailable() {
22612
+ try {
22613
+ const r = spawnSync6("npx", ["--version"], { stdio: "ignore" });
22614
+ return !r.error && r.status === 0;
22615
+ } catch {
22616
+ return false;
22617
+ }
22618
+ }
22619
+ async function readAssignmentContext() {
22620
+ const p = resolve46(process.cwd(), ".syntaur", "context.json");
22621
+ if (!await fileExists(p)) return null;
22622
+ try {
22623
+ return JSON.parse(await readFile31(p, "utf-8"));
22624
+ } catch {
22625
+ return null;
22626
+ }
22627
+ }
22628
+ async function crossAgentInstallCommand(options) {
22629
+ const ids = parseTargetIds(options);
22630
+ if (ids.length === 0) {
22631
+ throw new Error("No agents specified. Use --target <id> or --agent <id>.");
22632
+ }
22633
+ const targets = [];
22634
+ for (const id of ids) {
22635
+ const t = getAgentTarget(id);
22636
+ if (!t) {
22637
+ throw new Error(
22638
+ `Unknown agent "${id}". Known agents: ${agentTargetIds().join(", ")}`
22639
+ );
22640
+ }
22641
+ if (t.nativePlugin) {
22642
+ throw new Error(
22643
+ `"${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"}\`).`
22644
+ );
22645
+ }
22646
+ targets.push(t);
22647
+ }
22648
+ const dryRun = Boolean(options.dryRun);
22649
+ const force = Boolean(options.force);
22650
+ const scope = "global";
22651
+ const prefix = dryRun ? "[dry-run] " : "";
22652
+ if (!dryRun && !await isSyntaurDataInstalled()) {
22653
+ await initCommand({});
22654
+ }
22655
+ const skillsShIds = targets.map((t) => t.skillsShAgentId ?? t.id);
22656
+ const argv = ["skills", "add", DEFAULT_SKILLS_SOURCE, "--agent", ...skillsShIds];
22657
+ console.log(`${prefix}Tier 1 (skills): npx ${argv.join(" ")}`);
22658
+ let tier1Done = false;
22659
+ if (!dryRun) {
22660
+ if (isNpxAvailable()) {
22661
+ const r = spawnSync6("npx", argv, { stdio: "inherit" });
22662
+ tier1Done = !r.error && r.status === 0;
22663
+ if (!tier1Done) {
22664
+ console.log("`npx skills add` failed; falling back to offline copy.");
22665
+ }
22666
+ } else {
22667
+ console.log("npx not available; using offline skill copy.");
22668
+ }
22669
+ }
22670
+ for (const t of targets) {
22671
+ const globalDir = t.id === "hermes" ? hermesSkillsDir() : t.skillsDir?.global;
22672
+ if (!globalDir) continue;
22673
+ const offlineNeeded = !tier1Done;
22674
+ const hermesCustom = t.id === "hermes" && isHermesHomeCustom();
22675
+ if (!offlineNeeded && !hermesCustom) continue;
22676
+ if (dryRun) {
22677
+ const label = hermesCustom ? "offline copy (always \u2014 custom $HERMES_HOME)" : "offline copy (fallback if npx unavailable)";
22678
+ console.log(`${prefix}${label} -> ${globalDir}`);
22679
+ continue;
22680
+ }
22681
+ const reason = hermesCustom && tier1Done ? " ($HERMES_HOME reconcile)" : "";
22682
+ await installSkillsToDir({ targetDir: globalDir, force });
22683
+ console.log(`Copied skills -> ${globalDir}${reason}`);
22684
+ }
22685
+ const adapterTargets2 = targets.filter((t) => t.instructions);
22686
+ if (adapterTargets2.length > 0) {
22687
+ const ctx = await readAssignmentContext();
22688
+ const haveCtx = Boolean(ctx?.projectSlug && ctx?.assignmentSlug);
22689
+ for (const t of adapterTargets2) {
22690
+ if (dryRun) {
22691
+ for (const f of t.instructions.files) {
22692
+ console.log(
22693
+ `${prefix}Tier 2 (${t.id}): ${resolve46(process.cwd(), f.path)}`
22694
+ );
22695
+ }
22696
+ continue;
22697
+ }
22698
+ if (!haveCtx) {
22699
+ console.log(
22700
+ `No project-nested assignment context in cwd; skipping Tier-2 files for ${t.id}.`
22701
+ );
22702
+ continue;
22703
+ }
22704
+ try {
22705
+ await setupAdapterCommand(t.id, {
22706
+ project: ctx.projectSlug,
22707
+ assignment: ctx.assignmentSlug,
22708
+ force
22709
+ });
22710
+ } catch (err2) {
22711
+ const msg = err2 instanceof Error ? err2.message : String(err2);
22712
+ console.log(`Tier 2 for ${t.id} skipped: ${msg}`);
22713
+ }
22714
+ }
22715
+ }
22716
+ if (!dryRun) {
22717
+ const current = (await readConfig()).integrations.installedAgents ?? {};
22718
+ const next = { ...current };
22719
+ for (const t of targets) {
22720
+ next[t.id] = { scope };
22721
+ }
22722
+ await updateIntegrationConfig({ installedAgents: next });
22723
+ }
22724
+ console.log(
22725
+ `${prefix}Done. Agents: ${targets.map((t) => t.displayName).join(", ")}.`
22726
+ );
22727
+ }
22728
+
22729
+ // src/commands/setup.ts
22287
22730
  function isCliInstalled(command) {
22288
22731
  try {
22289
22732
  execSync(`which ${command}`, { stdio: "ignore" });
@@ -22301,6 +22744,15 @@ function printNonInteractiveSetupHelp() {
22301
22744
  console.error(` npx syntaur@latest setup --yes --dashboard`);
22302
22745
  }
22303
22746
  async function setupCommand(options) {
22747
+ if (options.dryRun && !options.target && !options.agent) {
22748
+ throw new Error(
22749
+ "--dry-run only applies to cross-agent install. Pass --target <id> or --agent <id>."
22750
+ );
22751
+ }
22752
+ if (options.target || options.agent) {
22753
+ await crossAgentInstallCommand(options);
22754
+ return;
22755
+ }
22304
22756
  const initialized = await isSyntaurDataInstalled();
22305
22757
  const interactive = isInteractiveTerminal();
22306
22758
  if (!initialized) {
@@ -22385,7 +22837,7 @@ async function setupCommand(options) {
22385
22837
  }
22386
22838
 
22387
22839
  // src/commands/uninstall.ts
22388
- import { resolve as resolve44 } from "path";
22840
+ import { resolve as resolve47 } from "path";
22389
22841
  init_paths();
22390
22842
  function expandTargets(options) {
22391
22843
  if (options.all) {
@@ -22465,7 +22917,7 @@ async function uninstallCommand(options) {
22465
22917
  const configuredProjectDir = await getConfiguredProjectDir();
22466
22918
  await removeSyntaurData();
22467
22919
  console.log(`Removed ${syntaurRoot()}`);
22468
- if (configuredProjectDir && resolve44(configuredProjectDir) !== resolve44(syntaurRoot(), "projects")) {
22920
+ if (configuredProjectDir && resolve47(configuredProjectDir) !== resolve47(syntaurRoot(), "projects")) {
22469
22921
  console.warn(
22470
22922
  `Warning: config.md pointed to an external project directory (${configuredProjectDir}). That directory was not removed automatically.`
22471
22923
  );
@@ -22476,114 +22928,11 @@ async function uninstallCommand(options) {
22476
22928
  }
22477
22929
  }
22478
22930
 
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
22931
  // src/commands/track-session.ts
22583
22932
  init_paths();
22584
22933
  init_fs();
22585
22934
  init_config2();
22586
- import { resolve as resolve46 } from "path";
22935
+ import { resolve as resolve48 } from "path";
22587
22936
  init_session_db();
22588
22937
  init_agent_sessions();
22589
22938
  async function trackSessionCommand(options) {
@@ -22598,7 +22947,7 @@ async function trackSessionCommand(options) {
22598
22947
  if (options.project) {
22599
22948
  const config = await readConfig();
22600
22949
  const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
22601
- const projectDir = resolve46(baseDir, options.project);
22950
+ const projectDir = resolve48(baseDir, options.project);
22602
22951
  if (!await fileExists(projectDir)) {
22603
22952
  throw new Error(
22604
22953
  `Project "${options.project}" not found at ${projectDir}.`
@@ -22730,8 +23079,8 @@ function formatInstallUrlHandlerError(err2) {
22730
23079
  init_config2();
22731
23080
  init_paths();
22732
23081
  init_fs();
22733
- import { resolve as resolve48, isAbsolute as isAbsolute7 } from "path";
22734
- import { readFile as readFile30 } from "fs/promises";
23082
+ import { resolve as resolve50, isAbsolute as isAbsolute7 } from "path";
23083
+ import { readFile as readFile32 } from "fs/promises";
22735
23084
  import { select, confirm as confirm2, input as input3 } from "@inquirer/prompts";
22736
23085
  async function browseCommand(options) {
22737
23086
  const config = await readConfig();
@@ -22800,7 +23149,7 @@ async function pickAgent2(agents) {
22800
23149
  return picked;
22801
23150
  }
22802
23151
  async function ensureWorktree(opts) {
22803
- const assignmentPath = resolve48(
23152
+ const assignmentPath = resolve50(
22804
23153
  opts.projectsDir,
22805
23154
  opts.projectSlug,
22806
23155
  "assignments",
@@ -22810,7 +23159,7 @@ async function ensureWorktree(opts) {
22810
23159
  if (!await fileExists(assignmentPath)) {
22811
23160
  return void 0;
22812
23161
  }
22813
- const content = await readFile30(assignmentPath, "utf-8");
23162
+ const content = await readFile32(assignmentPath, "utf-8");
22814
23163
  const { parseAssignmentFrontmatter: parseAssignmentFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
22815
23164
  const fm = parseAssignmentFrontmatter2(content);
22816
23165
  const { workspace } = fm;
@@ -22880,7 +23229,7 @@ async function ensureWorktree(opts) {
22880
23229
  async function runCreate(opts) {
22881
23230
  const { createWorktreeAndRecord: createWorktreeAndRecord2, GitWorktreeError: GitWorktreeError2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
22882
23231
  const expandedWorktree = expandHome(opts.worktreePath);
22883
- const absWorktree = isAbsolute7(expandedWorktree) ? expandedWorktree : resolve48(expandedWorktree);
23232
+ const absWorktree = isAbsolute7(expandedWorktree) ? expandedWorktree : resolve50(expandedWorktree);
22884
23233
  try {
22885
23234
  await createWorktreeAndRecord2({
22886
23235
  assignmentPath: opts.assignmentPath,
@@ -22909,7 +23258,7 @@ init_paths();
22909
23258
  init_fs();
22910
23259
  init_playbook();
22911
23260
  init_playbooks();
22912
- import { resolve as resolve49 } from "path";
23261
+ import { resolve as resolve51 } from "path";
22913
23262
  async function createPlaybookCommand(name, options) {
22914
23263
  if (!name.trim()) {
22915
23264
  throw new Error("Playbook name cannot be empty.");
@@ -22922,7 +23271,7 @@ async function createPlaybookCommand(name, options) {
22922
23271
  }
22923
23272
  const dir = playbooksDir();
22924
23273
  await ensureDir(dir);
22925
- const filePath = resolve49(dir, `${slug}.md`);
23274
+ const filePath = resolve51(dir, `${slug}.md`);
22926
23275
  if (await fileExists(filePath)) {
22927
23276
  throw new Error(
22928
23277
  `Playbook "${slug}" already exists at ${filePath}
@@ -22944,8 +23293,8 @@ init_paths();
22944
23293
  init_fs();
22945
23294
  init_parser();
22946
23295
  init_config2();
22947
- import { readdir as readdir16, readFile as readFile31 } from "fs/promises";
22948
- import { resolve as resolve50 } from "path";
23296
+ import { readdir as readdir16, readFile as readFile33 } from "fs/promises";
23297
+ import { resolve as resolve52 } from "path";
22949
23298
  async function listPlaybooksCommand(options = {}) {
22950
23299
  const dir = playbooksDir();
22951
23300
  if (!await fileExists(dir)) {
@@ -22960,8 +23309,8 @@ async function listPlaybooksCommand(options = {}) {
22960
23309
  );
22961
23310
  const rows = [];
22962
23311
  for (const entry of mdFiles) {
22963
- const filePath = resolve50(dir, entry.name);
22964
- const raw = await readFile31(filePath, "utf-8");
23312
+ const filePath = resolve52(dir, entry.name);
23313
+ const raw = await readFile33(filePath, "utf-8");
22965
23314
  const parsed = parsePlaybook(raw);
22966
23315
  const slug = parsed.slug || entry.name.replace(/\.md$/, "");
22967
23316
  const disabled = disabledSet.has(slug);
@@ -23084,14 +23433,14 @@ init_fs();
23084
23433
  init_config2();
23085
23434
  init_slug();
23086
23435
  import { Command as Command2 } from "commander";
23087
- import { readFile as readFile33 } from "fs/promises";
23088
- import { resolve as resolve52 } from "path";
23436
+ import { readFile as readFile35 } from "fs/promises";
23437
+ import { resolve as resolve54 } from "path";
23089
23438
 
23090
23439
  // src/commands/bundle.ts
23091
23440
  init_paths();
23092
23441
  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";
23442
+ import { mkdir as mkdir7, readFile as readFile34, readdir as readdir17, rm as rm7, writeFile as writeFile12 } from "fs/promises";
23443
+ import { resolve as resolve53 } from "path";
23095
23444
  init_parser2();
23096
23445
  init_fs();
23097
23446
  init_config2();
@@ -23109,7 +23458,7 @@ async function resolveBundleScope(options) {
23109
23458
  throw new Error(`Invalid project slug: "${options.project}".`);
23110
23459
  }
23111
23460
  const config = await readConfig();
23112
- const projectMd = resolve51(config.defaultProjectDir, options.project, "project.md");
23461
+ const projectMd = resolve53(config.defaultProjectDir, options.project, "project.md");
23113
23462
  if (!await fileExists(projectMd)) {
23114
23463
  throw new Error(`Project "${options.project}" not found.`);
23115
23464
  }
@@ -23179,10 +23528,10 @@ function pickNextPlanFile(planDir, existingFiles) {
23179
23528
  const m = f.match(/^plan-v(\d+)\.md$/);
23180
23529
  if (m) versions.add(parseInt(m[1], 10));
23181
23530
  }
23182
- if (versions.size === 0) return { target: resolve51(planDir, "plan.md"), version: 1 };
23531
+ if (versions.size === 0) return { target: resolve53(planDir, "plan.md"), version: 1 };
23183
23532
  let n = 2;
23184
23533
  while (versions.has(n)) n++;
23185
- return { target: resolve51(planDir, `plan-v${n}.md`), version: n };
23534
+ return { target: resolve53(planDir, `plan-v${n}.md`), version: n };
23186
23535
  }
23187
23536
  function dedupePreserveOrder(ids) {
23188
23537
  const out = [];
@@ -23266,7 +23615,7 @@ bundleCommand.command("new").description("Create a new bundle from 2+ existing t
23266
23615
  if (options.plan) {
23267
23616
  const planDir = bundlePlanDir(sc.todosPath, sc.scopeId, id);
23268
23617
  await ensureDir(planDir);
23269
- const target = resolve51(planDir, "plan.md");
23618
+ const target = resolve53(planDir, "plan.md");
23270
23619
  const memberLines = items.map((it) => `- ${it.description} [t:${it.id}]`).join("\n");
23271
23620
  const stub = [
23272
23621
  "---",
@@ -23442,7 +23791,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
23442
23791
  }
23443
23792
  const repository = options.repository ?? process.cwd();
23444
23793
  const parentBranch = options.parentBranch ?? "main";
23445
- const worktreePath = options.worktreePath ?? resolve51(repository, ".worktrees", options.branch);
23794
+ const worktreePath = options.worktreePath ?? resolve53(repository, ".worktrees", options.branch);
23446
23795
  const checklist = await readChecklist(sc.todosPath, sc.checklistKey);
23447
23796
  for (const memberId of bundle.todoIds) {
23448
23797
  const item = checklist.items.find((i) => i.id === memberId);
@@ -23452,8 +23801,8 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
23452
23801
  }
23453
23802
  const bundlesFilePath = bundlesPath(sc.todosPath);
23454
23803
  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;
23804
+ const bundlesSnapshot = await fileExists(bundlesFilePath) ? await readFile34(bundlesFilePath, "utf-8") : null;
23805
+ const checklistSnapshot = await fileExists(checklistFilePath) ? await readFile34(checklistFilePath, "utf-8") : null;
23457
23806
  const record = async () => {
23458
23807
  bundle.branch = options.branch;
23459
23808
  bundle.worktreePath = worktreePath;
@@ -23469,7 +23818,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
23469
23818
  try {
23470
23819
  await writeBundles(sc.todosPath, bundles);
23471
23820
  await writeChecklist(sc.todosPath, checklist);
23472
- const ctxDir = resolve51(worktreePath, ".syntaur");
23821
+ const ctxDir = resolve53(worktreePath, ".syntaur");
23473
23822
  await mkdir7(ctxDir, { recursive: true });
23474
23823
  const payload = {
23475
23824
  bundleId: bundle.id,
@@ -23483,7 +23832,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
23483
23832
  repository,
23484
23833
  boundAt: nowISO()
23485
23834
  };
23486
- await writeFile12(resolve51(ctxDir, "context.json"), JSON.stringify(payload, null, 2) + "\n");
23835
+ await writeFile12(resolve53(ctxDir, "context.json"), JSON.stringify(payload, null, 2) + "\n");
23487
23836
  } catch (err2) {
23488
23837
  try {
23489
23838
  if (bundlesSnapshot === null) {
@@ -23673,7 +24022,7 @@ async function resolveScope(options) {
23673
24022
  throw new Error(`Invalid project slug: "${options.project}".`);
23674
24023
  }
23675
24024
  const config = await readConfig();
23676
- const projectMd = resolve52(config.defaultProjectDir, options.project, "project.md");
24025
+ const projectMd = resolve54(config.defaultProjectDir, options.project, "project.md");
23677
24026
  if (!await fileExists(projectMd)) {
23678
24027
  throw new Error(`Project "${options.project}" not found.`);
23679
24028
  }
@@ -23996,10 +24345,10 @@ todoCommand.command("archive").description("Archive completed todos and their lo
23996
24345
  (e) => e.itemIds.every((id) => completedIds.has(id))
23997
24346
  );
23998
24347
  const archFile = archivePath(todosPath, workspace, checklist.archiveInterval);
23999
- await ensureDir(resolve52(todosPath, "archive"));
24348
+ await ensureDir(resolve54(todosPath, "archive"));
24000
24349
  let archContent = "";
24001
24350
  if (await fileExists(archFile)) {
24002
- archContent = await readFile33(archFile, "utf-8");
24351
+ archContent = await readFile35(archFile, "utf-8");
24003
24352
  archContent = archContent.trimEnd() + "\n\n";
24004
24353
  } else {
24005
24354
  archContent = `---
@@ -24176,12 +24525,12 @@ function describeScope(scope) {
24176
24525
  }
24177
24526
  async function injectPromotedTodos(assignmentDir, todos, scopeLabel) {
24178
24527
  const { resolve: resolvePath2 } = await import("path");
24179
- const { readFile: readFile54 } = await import("fs/promises");
24528
+ const { readFile: readFile56 } = await import("fs/promises");
24180
24529
  const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
24181
24530
  const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
24182
24531
  const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
24183
24532
  const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
24184
- let content = await readFile54(assignmentMdPath2, "utf-8");
24533
+ let content = await readFile56(assignmentMdPath2, "utf-8");
24185
24534
  content = appendTodosToAssignmentBody2(
24186
24535
  content,
24187
24536
  todos.map((t) => ({
@@ -24232,7 +24581,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
24232
24581
  );
24233
24582
  let target;
24234
24583
  if (existingFiles.length === 0) {
24235
- target = resolve52(planDir, "plan.md");
24584
+ target = resolve54(planDir, "plan.md");
24236
24585
  } else {
24237
24586
  const versions = /* @__PURE__ */ new Set();
24238
24587
  for (const f of existingFiles) {
@@ -24242,7 +24591,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
24242
24591
  }
24243
24592
  let n = 2;
24244
24593
  while (versions.has(n)) n++;
24245
- target = resolve52(planDir, `plan-v${n}.md`);
24594
+ target = resolve54(planDir, `plan-v${n}.md`);
24246
24595
  }
24247
24596
  if (!await fileExists(target)) {
24248
24597
  const stub = `---
@@ -24431,24 +24780,24 @@ backupCommand.command("config").description("Show or update backup configuration
24431
24780
 
24432
24781
  // src/commands/doctor.ts
24433
24782
  import { Command as Command4 } from "commander";
24434
- import { readFile as readFile41 } from "fs/promises";
24435
- import { isAbsolute as isAbsolute10, resolve as resolve64 } from "path";
24783
+ import { readFile as readFile43 } from "fs/promises";
24784
+ import { isAbsolute as isAbsolute10, resolve as resolve67 } from "path";
24436
24785
 
24437
24786
  // src/utils/doctor/index.ts
24438
24787
  import { fileURLToPath as fileURLToPath11 } from "url";
24439
- import { readFile as readFile40 } from "fs/promises";
24440
- import { dirname as dirname19, join as join12 } from "path";
24788
+ import { readFile as readFile42 } from "fs/promises";
24789
+ import { dirname as dirname19, join as join13 } from "path";
24441
24790
 
24442
24791
  // src/utils/doctor/context.ts
24443
24792
  init_config2();
24444
24793
  init_paths();
24445
24794
  init_fs();
24446
24795
  import Database4 from "better-sqlite3";
24447
- import { resolve as resolve53 } from "path";
24796
+ import { resolve as resolve55 } from "path";
24448
24797
  async function buildCheckContext(cwd = process.cwd()) {
24449
24798
  const config = await readConfig();
24450
24799
  const root = syntaurRoot();
24451
- const dbPath = resolve53(root, "syntaur.db");
24800
+ const dbPath = resolve55(root, "syntaur.db");
24452
24801
  let db5 = null;
24453
24802
  let dbError = null;
24454
24803
  if (await fileExists(dbPath)) {
@@ -24482,8 +24831,8 @@ function closeCheckContext(ctx) {
24482
24831
  // src/utils/doctor/checks/env.ts
24483
24832
  init_fs();
24484
24833
  init_paths();
24485
- import { resolve as resolve54, isAbsolute as isAbsolute8 } from "path";
24486
- import { readFile as readFile34, stat as stat3 } from "fs/promises";
24834
+ import { resolve as resolve56, isAbsolute as isAbsolute8 } from "path";
24835
+ import { readFile as readFile36, stat as stat3 } from "fs/promises";
24487
24836
  import { fileURLToPath as fileURLToPath10 } from "url";
24488
24837
  import { dirname as dirname17, join as join10 } from "path";
24489
24838
  var CATEGORY = "env";
@@ -24523,7 +24872,7 @@ var configValid = {
24523
24872
  category: CATEGORY,
24524
24873
  title: "~/.syntaur/config.md is valid",
24525
24874
  async run(ctx) {
24526
- const configPath = resolve54(ctx.syntaurRoot, "config.md");
24875
+ const configPath = resolve56(ctx.syntaurRoot, "config.md");
24527
24876
  if (!await fileExists(configPath)) {
24528
24877
  return {
24529
24878
  id: this.id,
@@ -24540,7 +24889,7 @@ var configValid = {
24540
24889
  autoFixable: false
24541
24890
  };
24542
24891
  }
24543
- const content = await readFile34(configPath, "utf-8");
24892
+ const content = await readFile36(configPath, "utf-8");
24544
24893
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
24545
24894
  if (!fmMatch || fmMatch[1].trim() === "") {
24546
24895
  return {
@@ -24820,7 +25169,7 @@ async function readLocalPkg() {
24820
25169
  for (let i = 0; i < 6; i++) {
24821
25170
  const candidate = join10(dir, "package.json");
24822
25171
  try {
24823
- const text = await readFile34(candidate, "utf-8");
25172
+ const text = await readFile36(candidate, "utf-8");
24824
25173
  return JSON.parse(text);
24825
25174
  } catch {
24826
25175
  dir = dirname17(dir);
@@ -24872,7 +25221,7 @@ function versionGte(a, b) {
24872
25221
 
24873
25222
  // src/utils/doctor/checks/structure.ts
24874
25223
  init_fs();
24875
- import { resolve as resolve55 } from "path";
25224
+ import { resolve as resolve57 } from "path";
24876
25225
  import { readdir as readdir18, stat as stat4 } from "fs/promises";
24877
25226
  var CATEGORY2 = "structure";
24878
25227
  var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
@@ -24892,7 +25241,7 @@ var projectsDir = {
24892
25241
  category: CATEGORY2,
24893
25242
  title: "projects/ directory exists",
24894
25243
  async run(ctx) {
24895
- const p = resolve55(ctx.syntaurRoot, "projects");
25244
+ const p = resolve57(ctx.syntaurRoot, "projects");
24896
25245
  if (!await fileExists(p)) {
24897
25246
  return {
24898
25247
  id: this.id,
@@ -24917,7 +25266,7 @@ var playbooksDir2 = {
24917
25266
  category: CATEGORY2,
24918
25267
  title: "playbooks/ directory exists",
24919
25268
  async run(ctx) {
24920
- const p = resolve55(ctx.syntaurRoot, "playbooks");
25269
+ const p = resolve57(ctx.syntaurRoot, "playbooks");
24921
25270
  if (!await fileExists(p)) {
24922
25271
  return {
24923
25272
  id: this.id,
@@ -24942,7 +25291,7 @@ var todosDirValid = {
24942
25291
  category: CATEGORY2,
24943
25292
  title: "todos/ directory is readable (if present)",
24944
25293
  async run(ctx) {
24945
- const p = resolve55(ctx.syntaurRoot, "todos");
25294
+ const p = resolve57(ctx.syntaurRoot, "todos");
24946
25295
  if (!await fileExists(p)) {
24947
25296
  return {
24948
25297
  id: this.id,
@@ -24973,7 +25322,7 @@ var serversDirValid = {
24973
25322
  category: CATEGORY2,
24974
25323
  title: "servers/ directory is readable (if present)",
24975
25324
  async run(ctx) {
24976
- const p = resolve55(ctx.syntaurRoot, "servers");
25325
+ const p = resolve57(ctx.syntaurRoot, "servers");
24977
25326
  if (!await fileExists(p)) {
24978
25327
  return {
24979
25328
  id: this.id,
@@ -25018,7 +25367,7 @@ var knownFilesRecognized = {
25018
25367
  title: this.title,
25019
25368
  status: "warn",
25020
25369
  detail: `unexpected top-level entries: ${unexpected.join(", ")}`,
25021
- affected: unexpected.map((n) => resolve55(ctx.syntaurRoot, n)),
25370
+ affected: unexpected.map((n) => resolve57(ctx.syntaurRoot, n)),
25022
25371
  remediation: {
25023
25372
  kind: "manual",
25024
25373
  suggestion: "Review these entries \u2014 they may be leftover state from older versions",
@@ -25047,7 +25396,7 @@ function pass2(check) {
25047
25396
 
25048
25397
  // src/utils/doctor/checks/project.ts
25049
25398
  init_fs();
25050
- import { resolve as resolve56 } from "path";
25399
+ import { resolve as resolve58 } from "path";
25051
25400
  import { readdir as readdir19, stat as stat5 } from "fs/promises";
25052
25401
  var CATEGORY3 = "project";
25053
25402
  var REQUIRED_PROJECT_FILES = [
@@ -25077,10 +25426,10 @@ async function listProjects2(ctx) {
25077
25426
  for (const e of entries) {
25078
25427
  if (!e.isDirectory()) continue;
25079
25428
  if (e.name.startsWith(".") || e.name.startsWith("_")) continue;
25080
- const projectDir = resolve56(dir, e.name);
25429
+ const projectDir = resolve58(dir, e.name);
25081
25430
  let looksLikeProject = false;
25082
25431
  for (const marker of PROJECT_MARKERS) {
25083
- if (await fileExists(resolve56(projectDir, marker))) {
25432
+ if (await fileExists(resolve58(projectDir, marker))) {
25084
25433
  looksLikeProject = true;
25085
25434
  break;
25086
25435
  }
@@ -25099,7 +25448,7 @@ var requiredFiles = {
25099
25448
  for (const projectDir of projects) {
25100
25449
  const missing = [];
25101
25450
  for (const rel of REQUIRED_PROJECT_FILES) {
25102
- const p = resolve56(projectDir, rel);
25451
+ const p = resolve58(projectDir, rel);
25103
25452
  if (!await fileExists(p)) missing.push(rel);
25104
25453
  }
25105
25454
  if (missing.length === 0) continue;
@@ -25109,7 +25458,7 @@ var requiredFiles = {
25109
25458
  title: this.title,
25110
25459
  status: "error",
25111
25460
  detail: `project at ${projectDir} is missing: ${missing.join(", ")}`,
25112
- affected: missing.map((m) => resolve56(projectDir, m)),
25461
+ affected: missing.map((m) => resolve58(projectDir, m)),
25113
25462
  remediation: {
25114
25463
  kind: "manual",
25115
25464
  suggestion: "Recreate the missing scaffold files from templates",
@@ -25132,7 +25481,7 @@ var manifestStale = {
25132
25481
  const projects = await listProjects2(ctx);
25133
25482
  const results = [];
25134
25483
  for (const projectDir of projects) {
25135
- const manifestPath = resolve56(projectDir, "manifest.md");
25484
+ const manifestPath = resolve58(projectDir, "manifest.md");
25136
25485
  if (!await fileExists(manifestPath)) continue;
25137
25486
  const manifestMtime = (await stat5(manifestPath)).mtimeMs;
25138
25487
  const newestAssignment = await newestAssignmentMtime(projectDir);
@@ -25181,7 +25530,7 @@ var orphanFiles = {
25181
25530
  title: this.title,
25182
25531
  status: "warn",
25183
25532
  detail: `project at ${projectDir} has unexpected entries: ${orphans.join(", ")}`,
25184
- affected: orphans.map((o) => resolve56(projectDir, o)),
25533
+ affected: orphans.map((o) => resolve58(projectDir, o)),
25185
25534
  autoFixable: false
25186
25535
  });
25187
25536
  }
@@ -25191,7 +25540,7 @@ var orphanFiles = {
25191
25540
  };
25192
25541
  var projectChecks = [requiredFiles, manifestStale, orphanFiles];
25193
25542
  async function newestAssignmentMtime(projectDir) {
25194
- const assignmentsRoot = resolve56(projectDir, "assignments");
25543
+ const assignmentsRoot = resolve58(projectDir, "assignments");
25195
25544
  if (!await fileExists(assignmentsRoot)) return 0;
25196
25545
  let newest = 0;
25197
25546
  let entries;
@@ -25202,7 +25551,7 @@ async function newestAssignmentMtime(projectDir) {
25202
25551
  }
25203
25552
  for (const e of entries) {
25204
25553
  if (!e.isDirectory()) continue;
25205
- const assignmentMd = resolve56(assignmentsRoot, e.name, "assignment.md");
25554
+ const assignmentMd = resolve58(assignmentsRoot, e.name, "assignment.md");
25206
25555
  try {
25207
25556
  const s = await stat5(assignmentMd);
25208
25557
  if (s.mtimeMs > newest) newest = s.mtimeMs;
@@ -25226,8 +25575,8 @@ init_fs();
25226
25575
  init_parser();
25227
25576
  init_types();
25228
25577
  init_paths();
25229
- import { resolve as resolve57 } from "path";
25230
- import { readFile as readFile35, readdir as readdir20 } from "fs/promises";
25578
+ import { resolve as resolve59 } from "path";
25579
+ import { readFile as readFile37, readdir as readdir20 } from "fs/promises";
25231
25580
  var CATEGORY4 = "assignment";
25232
25581
  var STATUSES_REQUIRING_HANDOFF = /* @__PURE__ */ new Set(["review", "completed"]);
25233
25582
  var PRE_WORKSPACE_STATUSES = /* @__PURE__ */ new Set([
@@ -25321,7 +25670,7 @@ var invalidStatus = {
25321
25670
  const allowed = configuredStatuses(ctx);
25322
25671
  const results = [];
25323
25672
  for (const a of withAssignmentMd) {
25324
- const path = resolve57(a.assignmentDir, "assignment.md");
25673
+ const path = resolve59(a.assignmentDir, "assignment.md");
25325
25674
  const parsed = await parseSafe2(path);
25326
25675
  if (!parsed) continue;
25327
25676
  if (!allowed.has(parsed.status)) {
@@ -25354,7 +25703,7 @@ var workspaceMissing = {
25354
25703
  const terminal = terminalStatuses(ctx);
25355
25704
  const results = [];
25356
25705
  for (const a of withAssignmentMd) {
25357
- const path = resolve57(a.assignmentDir, "assignment.md");
25706
+ const path = resolve59(a.assignmentDir, "assignment.md");
25358
25707
  const parsed = await parseSafe2(path);
25359
25708
  if (!parsed) continue;
25360
25709
  if (terminal.has(parsed.status)) continue;
@@ -25401,12 +25750,12 @@ var requiredFilesByStatus = {
25401
25750
  const { withAssignmentMd } = await listAssignments(ctx);
25402
25751
  const results = [];
25403
25752
  for (const a of withAssignmentMd) {
25404
- const assignmentPath = resolve57(a.assignmentDir, "assignment.md");
25753
+ const assignmentPath = resolve59(a.assignmentDir, "assignment.md");
25405
25754
  const parsed = await parseSafe2(assignmentPath);
25406
25755
  if (!parsed) continue;
25407
25756
  const missing = [];
25408
25757
  if (STATUSES_REQUIRING_HANDOFF.has(parsed.status)) {
25409
- const handoffPath = resolve57(a.assignmentDir, "handoff.md");
25758
+ const handoffPath = resolve59(a.assignmentDir, "handoff.md");
25410
25759
  if (!await fileExists(handoffPath)) missing.push("handoff.md");
25411
25760
  }
25412
25761
  if (missing.length === 0) continue;
@@ -25416,7 +25765,7 @@ var requiredFilesByStatus = {
25416
25765
  title: this.title,
25417
25766
  status: "warn",
25418
25767
  detail: `${a.projectSlug}/${a.assignmentSlug} (status: ${parsed.status}) is missing ${missing.join(", ")}`,
25419
- affected: missing.map((m) => resolve57(a.assignmentDir, m)),
25768
+ affected: missing.map((m) => resolve59(a.assignmentDir, m)),
25420
25769
  remediation: {
25421
25770
  kind: "manual",
25422
25771
  suggestion: `Create the missing ${missing.join(" and ")} files for this assignment`,
@@ -25439,7 +25788,7 @@ var companionFilesScaffolded = {
25439
25788
  for (const a of withAssignmentMd) {
25440
25789
  const missing = [];
25441
25790
  for (const filename of ["progress.md", "comments.md"]) {
25442
- if (!await fileExists(resolve57(a.assignmentDir, filename))) {
25791
+ if (!await fileExists(resolve59(a.assignmentDir, filename))) {
25443
25792
  missing.push(filename);
25444
25793
  }
25445
25794
  }
@@ -25451,7 +25800,7 @@ var companionFilesScaffolded = {
25451
25800
  title: this.title,
25452
25801
  status: "warn",
25453
25802
  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)),
25803
+ affected: missing.map((m) => resolve59(a.assignmentDir, m)),
25455
25804
  remediation: {
25456
25805
  kind: "manual",
25457
25806
  suggestion: `Create ${missing.join(" and ")} with the renderProgress/renderComments templates, or re-scaffold via the CLI`,
@@ -25484,7 +25833,7 @@ var typeDefinition = {
25484
25833
  const { withAssignmentMd } = await listAssignments(ctx);
25485
25834
  const results = [];
25486
25835
  for (const a of withAssignmentMd) {
25487
- const path = resolve57(a.assignmentDir, "assignment.md");
25836
+ const path = resolve59(a.assignmentDir, "assignment.md");
25488
25837
  const parsed = await parseSafe2(path);
25489
25838
  if (!parsed) continue;
25490
25839
  if (!parsed.type) continue;
@@ -25518,7 +25867,7 @@ var projectFrontmatterMatchesContainer = {
25518
25867
  const { withAssignmentMd } = await listAssignments(ctx);
25519
25868
  const results = [];
25520
25869
  for (const a of withAssignmentMd) {
25521
- const path = resolve57(a.assignmentDir, "assignment.md");
25870
+ const path = resolve59(a.assignmentDir, "assignment.md");
25522
25871
  const parsed = await parseSafe2(path);
25523
25872
  if (!parsed) continue;
25524
25873
  if (a.standalone) {
@@ -25569,13 +25918,13 @@ var draftMissingObjective = {
25569
25918
  const { withAssignmentMd } = await listAssignments(ctx);
25570
25919
  const results = [];
25571
25920
  for (const a of withAssignmentMd) {
25572
- const path = resolve57(a.assignmentDir, "assignment.md");
25921
+ const path = resolve59(a.assignmentDir, "assignment.md");
25573
25922
  const parsed = await parseSafe2(path);
25574
25923
  if (!parsed) continue;
25575
25924
  if (parsed.status !== "draft") continue;
25576
25925
  let raw;
25577
25926
  try {
25578
- raw = await readFile35(path, "utf-8");
25927
+ raw = await readFile37(path, "utf-8");
25579
25928
  } catch {
25580
25929
  continue;
25581
25930
  }
@@ -25608,7 +25957,7 @@ var readyToImplementMissingPlan = {
25608
25957
  const { withAssignmentMd } = await listAssignments(ctx);
25609
25958
  const results = [];
25610
25959
  for (const a of withAssignmentMd) {
25611
- const path = resolve57(a.assignmentDir, "assignment.md");
25960
+ const path = resolve59(a.assignmentDir, "assignment.md");
25612
25961
  const parsed = await parseSafe2(path);
25613
25962
  if (!parsed) continue;
25614
25963
  if (parsed.status !== "ready_to_implement") continue;
@@ -25617,7 +25966,7 @@ var readyToImplementMissingPlan = {
25617
25966
  let hasPlanContent = false;
25618
25967
  for (const f of planFiles) {
25619
25968
  try {
25620
- const c2 = await readFile35(resolve57(a.assignmentDir, f), "utf-8");
25969
+ const c2 = await readFile37(resolve59(a.assignmentDir, f), "utf-8");
25621
25970
  if (c2.trim().length > 0) {
25622
25971
  hasPlanContent = true;
25623
25972
  break;
@@ -25633,7 +25982,7 @@ var readyToImplementMissingPlan = {
25633
25982
  title: this.title,
25634
25983
  status: "warn",
25635
25984
  detail: `${label} (status: ready_to_implement) has no plan.md or plan-v<N>.md`,
25636
- affected: [resolve57(a.assignmentDir, "plan.md")],
25985
+ affected: [resolve59(a.assignmentDir, "plan.md")],
25637
25986
  remediation: {
25638
25987
  kind: "manual",
25639
25988
  suggestion: `Write a plan with '/plan-assignment' (or 'syntaur plan'), then re-mark ready_to_implement`,
@@ -25660,7 +26009,7 @@ var assignmentChecks = [
25660
26009
  ];
25661
26010
  async function parseSafe2(path) {
25662
26011
  try {
25663
- const content = await readFile35(path, "utf-8");
26012
+ const content = await readFile37(path, "utf-8");
25664
26013
  return parseAssignmentFull(content);
25665
26014
  } catch {
25666
26015
  return null;
@@ -25679,7 +26028,7 @@ function pass4(check, detail) {
25679
26028
 
25680
26029
  // src/utils/doctor/checks/dashboard.ts
25681
26030
  init_fs();
25682
- import { resolve as resolve58 } from "path";
26031
+ import { resolve as resolve60 } from "path";
25683
26032
  var CATEGORY5 = "dashboard";
25684
26033
  var dbReachable = {
25685
26034
  id: "dashboard.db-reachable",
@@ -25693,7 +26042,7 @@ var dbReachable = {
25693
26042
  title: this.title,
25694
26043
  status: "error",
25695
26044
  detail: `could not open syntaur.db: ${ctx.dbError ?? "unknown error"}`,
25696
- affected: [resolve58(ctx.syntaurRoot, "syntaur.db")],
26045
+ affected: [resolve60(ctx.syntaurRoot, "syntaur.db")],
25697
26046
  remediation: {
25698
26047
  kind: "manual",
25699
26048
  suggestion: "Start the dashboard once (`syntaur dashboard`) to initialize the DB, or restore it from backup",
@@ -25711,7 +26060,7 @@ var dbReachable = {
25711
26060
  title: this.title,
25712
26061
  status: "error",
25713
26062
  detail: 'syntaur.db is missing the expected "sessions" table',
25714
- affected: [resolve58(ctx.syntaurRoot, "syntaur.db")],
26063
+ affected: [resolve60(ctx.syntaurRoot, "syntaur.db")],
25715
26064
  autoFixable: false
25716
26065
  };
25717
26066
  }
@@ -25723,7 +26072,7 @@ var dbReachable = {
25723
26072
  title: this.title,
25724
26073
  status: "error",
25725
26074
  detail: `syntaur.db query failed: ${err2 instanceof Error ? err2.message : String(err2)}`,
25726
- affected: [resolve58(ctx.syntaurRoot, "syntaur.db")],
26075
+ affected: [resolve60(ctx.syntaurRoot, "syntaur.db")],
25727
26076
  autoFixable: false
25728
26077
  };
25729
26078
  }
@@ -25749,7 +26098,7 @@ var ghostSessions = {
25749
26098
  const results = [];
25750
26099
  for (const row of rows) {
25751
26100
  if (!row.project_slug) continue;
25752
- const projectPath = resolve58(projectsDir2, row.project_slug, "project.md");
26101
+ const projectPath = resolve60(projectsDir2, row.project_slug, "project.md");
25753
26102
  if (!await fileExists(projectPath)) {
25754
26103
  results.push({
25755
26104
  id: this.id,
@@ -25768,7 +26117,7 @@ var ghostSessions = {
25768
26117
  continue;
25769
26118
  }
25770
26119
  if (row.assignment_slug) {
25771
- const assignmentPath = resolve58(
26120
+ const assignmentPath = resolve60(
25772
26121
  projectsDir2,
25773
26122
  row.project_slug,
25774
26123
  "assignments",
@@ -25820,9 +26169,9 @@ function skipped(check, reason) {
25820
26169
 
25821
26170
  // src/utils/doctor/checks/integrations.ts
25822
26171
  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";
26172
+ import { resolve as resolve61, dirname as dirname18, basename as basename5 } from "path";
26173
+ import { readdir as readdir21, readFile as readFile38 } from "fs/promises";
26174
+ import { homedir as homedir10 } from "os";
25826
26175
  var CATEGORY6 = "integrations";
25827
26176
  var claudePluginLinked = {
25828
26177
  id: "integrations.claude-plugin-linked",
@@ -25903,10 +26252,10 @@ var backupConfigured = {
25903
26252
  }
25904
26253
  };
25905
26254
  async function readKnownMarketplaces() {
25906
- const path = resolve59(homedir9(), ".claude", "plugins", "known_marketplaces.json");
26255
+ const path = resolve61(homedir10(), ".claude", "plugins", "known_marketplaces.json");
25907
26256
  if (!await fileExists(path)) return {};
25908
26257
  try {
25909
- const raw = await readFile36(path, "utf-8");
26258
+ const raw = await readFile38(path, "utf-8");
25910
26259
  return JSON.parse(raw);
25911
26260
  } catch {
25912
26261
  return {};
@@ -25940,7 +26289,7 @@ var claudeMarketplaceRegistered = {
25940
26289
  };
25941
26290
  }
25942
26291
  const marketplaceRoot = dirname18(pluginsParent);
25943
- const marketplaceManifest = resolve59(marketplaceRoot, ".claude-plugin", "marketplace.json");
26292
+ const marketplaceManifest = resolve61(marketplaceRoot, ".claude-plugin", "marketplace.json");
25944
26293
  if (!await fileExists(marketplaceManifest)) {
25945
26294
  return {
25946
26295
  id: this.id,
@@ -25959,7 +26308,7 @@ var claudeMarketplaceRegistered = {
25959
26308
  }
25960
26309
  let parsed = {};
25961
26310
  try {
25962
- parsed = JSON.parse(await readFile36(marketplaceManifest, "utf-8"));
26311
+ parsed = JSON.parse(await readFile38(marketplaceManifest, "utf-8"));
25963
26312
  } catch {
25964
26313
  return {
25965
26314
  id: this.id,
@@ -25991,7 +26340,7 @@ var claudeMarketplaceRegistered = {
25991
26340
  title: this.title,
25992
26341
  status: "error",
25993
26342
  detail: issues.join("; "),
25994
- affected: [marketplaceManifest, resolve59(homedir9(), ".claude", "plugins", "known_marketplaces.json")],
26343
+ affected: [marketplaceManifest, resolve61(homedir10(), ".claude", "plugins", "known_marketplaces.json")],
25995
26344
  remediation: {
25996
26345
  kind: "manual",
25997
26346
  suggestion: "Re-run install-plugin to ensure both files are in sync.",
@@ -26031,8 +26380,8 @@ function skipped2(check, reason) {
26031
26380
  init_fs();
26032
26381
  init_parser();
26033
26382
  init_types();
26034
- import { resolve as resolve60 } from "path";
26035
- import { readFile as readFile37 } from "fs/promises";
26383
+ import { resolve as resolve62 } from "path";
26384
+ import { readFile as readFile39 } from "fs/promises";
26036
26385
  var CATEGORY7 = "workspace";
26037
26386
  var ASSIGNMENT_FIELDS = ["projectSlug", "assignmentSlug", "projectDir", "assignmentDir"];
26038
26387
  var BUNDLE_FIELDS = ["bundleId", "bundleScope", "bundleScopeId"];
@@ -26053,12 +26402,12 @@ function isStandaloneSession(ctx) {
26053
26402
  return !hasAnyAssignmentField(ctx) && !hasAnyBundleField(ctx) && typeof ctx.sessionId === "string" && ctx.sessionId.length > 0;
26054
26403
  }
26055
26404
  async function loadContext(ctx) {
26056
- const path = resolve60(ctx.cwd, ".syntaur", "context.json");
26405
+ const path = resolve62(ctx.cwd, ".syntaur", "context.json");
26057
26406
  if (!await fileExists(path)) {
26058
26407
  return { data: null, path, exists: false, parseError: null };
26059
26408
  }
26060
26409
  try {
26061
- const raw = await readFile37(path, "utf-8");
26410
+ const raw = await readFile39(path, "utf-8");
26062
26411
  return { data: JSON.parse(raw), path, exists: true, parseError: null };
26063
26412
  } catch (err2) {
26064
26413
  return {
@@ -26152,7 +26501,7 @@ var contextAssignmentResolves = {
26152
26501
  if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to resolve");
26153
26502
  if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to resolve");
26154
26503
  if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
26155
- const assignmentMd = resolve60(data.assignmentDir, "assignment.md");
26504
+ const assignmentMd = resolve62(data.assignmentDir, "assignment.md");
26156
26505
  if (!await fileExists(assignmentMd)) {
26157
26506
  return {
26158
26507
  id: this.id,
@@ -26182,10 +26531,10 @@ var contextTerminal = {
26182
26531
  if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to check");
26183
26532
  if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to check");
26184
26533
  if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
26185
- const assignmentMd = resolve60(data.assignmentDir, "assignment.md");
26534
+ const assignmentMd = resolve62(data.assignmentDir, "assignment.md");
26186
26535
  if (!await fileExists(assignmentMd)) return skipped3(this, "assignment file missing");
26187
26536
  try {
26188
- const content = await readFile37(assignmentMd, "utf-8");
26537
+ const content = await readFile39(assignmentMd, "utf-8");
26189
26538
  const parsed = parseAssignmentFull(content);
26190
26539
  const terminal = terminalStatuses2(ctx);
26191
26540
  if (terminal.has(parsed.status)) {
@@ -26241,7 +26590,7 @@ function skipped3(check, reason) {
26241
26590
  init_config2();
26242
26591
  import { isAbsolute as isAbsolute9 } from "path";
26243
26592
  import { access as access2, constants as fsConstants } from "fs/promises";
26244
- import { spawnSync as spawnSync7 } from "child_process";
26593
+ import { spawnSync as spawnSync8 } from "child_process";
26245
26594
  var CATEGORY8 = "agents";
26246
26595
  var agentsResolvable = {
26247
26596
  id: "agents.commands-resolvable",
@@ -26305,7 +26654,7 @@ async function checkAgent(agent) {
26305
26654
  };
26306
26655
  }
26307
26656
  }
26308
- const result = spawnSync7("which", [agent.command], { encoding: "utf-8" });
26657
+ const result = spawnSync8("which", [agent.command], { encoding: "utf-8" });
26309
26658
  if (result.status === 0 && result.stdout.trim().length > 0) {
26310
26659
  return {
26311
26660
  ...base,
@@ -26330,16 +26679,16 @@ var agentChecks = [agentsResolvable];
26330
26679
 
26331
26680
  // src/utils/doctor/checks/terminal.ts
26332
26681
  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";
26682
+ import { spawnSync as spawnSync9 } from "child_process";
26683
+ import { readFile as readFile40 } from "fs/promises";
26684
+ import { resolve as resolve63 } from "path";
26336
26685
  init_paths();
26337
26686
  init_fs();
26338
26687
  var CATEGORY9 = "terminal";
26339
26688
  async function readRawTerminalKey() {
26340
- const configPath = resolve61(syntaurRoot(), "config.md");
26689
+ const configPath = resolve63(syntaurRoot(), "config.md");
26341
26690
  if (!await fileExists(configPath)) return null;
26342
- const content = await readFile38(configPath, "utf-8");
26691
+ const content = await readFile40(configPath, "utf-8");
26343
26692
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
26344
26693
  if (!fmMatch) return null;
26345
26694
  const line = fmMatch[1].split("\n").find((l) => /^terminal:\s*/.test(l));
@@ -26452,7 +26801,7 @@ var kittyRemoteControl = {
26452
26801
  autoFixable: false
26453
26802
  };
26454
26803
  }
26455
- const result = spawnSync8("kitty", ["@", "ls"], {
26804
+ const result = spawnSync9("kitty", ["@", "ls"], {
26456
26805
  encoding: "utf-8",
26457
26806
  timeout: 2e3
26458
26807
  });
@@ -26489,13 +26838,13 @@ var terminalChecks = [
26489
26838
 
26490
26839
  // src/utils/doctor/checks/skills.ts
26491
26840
  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";
26841
+ import { resolve as resolve64, join as join11 } from "path";
26842
+ import { readdir as readdir22, readFile as readFile41, lstat as lstat4 } from "fs/promises";
26843
+ import { homedir as homedir11 } from "os";
26495
26844
  var CATEGORY10 = "skills";
26496
26845
  var skillTargets = [
26497
- { agent: "claude", dir: resolve62(homedir10(), ".claude", "skills"), label: "~/.claude/skills" },
26498
- { agent: "codex", dir: resolve62(homedir10(), ".codex", "skills"), label: "~/.codex/skills" }
26846
+ { agent: "claude", dir: resolve64(homedir11(), ".claude", "skills"), label: "~/.claude/skills" },
26847
+ { agent: "codex", dir: resolve64(homedir11(), ".codex", "skills"), label: "~/.codex/skills" }
26499
26848
  ];
26500
26849
  var skillsDedupCheck = {
26501
26850
  id: "skills.dedup",
@@ -26519,7 +26868,7 @@ var skillsDedupCheck = {
26519
26868
  if (!KNOWN_SKILLS.includes(entry.name)) continue;
26520
26869
  const skillMd = join11(dir, entry.name, "SKILL.md");
26521
26870
  if (!await fileExists(skillMd)) continue;
26522
- const content = await readFile39(skillMd, "utf-8").catch(() => "");
26871
+ const content = await readFile41(skillMd, "utf-8").catch(() => "");
26523
26872
  const match = content.match(/^name:\s*(\S+)\s*$/m);
26524
26873
  if (!match || match[1] !== entry.name) continue;
26525
26874
  let isSymlink2 = false;
@@ -26567,14 +26916,101 @@ var skillsDedupCheck = {
26567
26916
  };
26568
26917
  var skillsChecks = [skillsDedupCheck];
26569
26918
 
26919
+ // src/utils/doctor/checks/cross-agent.ts
26920
+ init_fs();
26921
+ import { join as join12, resolve as resolve65 } from "path";
26922
+ var CATEGORY11 = "cross-agent";
26923
+ async function countSyntaurSkills(dir) {
26924
+ if (!await fileExists(dir)) return 0;
26925
+ let n = 0;
26926
+ for (const skill of KNOWN_SKILLS) {
26927
+ if (await fileExists(join12(dir, skill, "SKILL.md"))) n++;
26928
+ }
26929
+ return n;
26930
+ }
26931
+ var crossAgentSkillsCheck = {
26932
+ id: "cross-agent.skills",
26933
+ category: CATEGORY11,
26934
+ title: "Cross-agent targets have Syntaur skills + protocol files",
26935
+ async run(ctx) {
26936
+ const installed = ctx.config.integrations.installedAgents ?? {};
26937
+ const total = KNOWN_SKILLS.length;
26938
+ const lines = [];
26939
+ const problems = [];
26940
+ const affected = [];
26941
+ let considered = 0;
26942
+ for (const t of AGENT_TARGETS) {
26943
+ if (t.nativePlugin) continue;
26944
+ const dir = t.skillsDir?.global;
26945
+ if (!dir) continue;
26946
+ const recorded = Boolean(installed[t.id]);
26947
+ const detected = await t.detect();
26948
+ if (!recorded && !detected) continue;
26949
+ considered++;
26950
+ const present = await countSyntaurSkills(dir);
26951
+ lines.push(`${t.displayName}: ${present}/${total} skills (${dir})`);
26952
+ if (recorded && present < total) {
26953
+ problems.push(
26954
+ `${t.displayName}: ${present === 0 ? "no Syntaur skills" : `incomplete skills (${present}/${total})`}`
26955
+ );
26956
+ affected.push(dir);
26957
+ }
26958
+ if (recorded && t.instructions) {
26959
+ for (const f of t.instructions.files) {
26960
+ const p = resolve65(ctx.cwd, f.path);
26961
+ if (!await fileExists(p)) {
26962
+ problems.push(`${t.displayName}: missing protocol file ${f.path} in cwd`);
26963
+ affected.push(p);
26964
+ }
26965
+ }
26966
+ }
26967
+ }
26968
+ if (considered === 0) {
26969
+ return {
26970
+ id: this.id,
26971
+ category: this.category,
26972
+ title: this.title,
26973
+ status: "skipped",
26974
+ detail: "No cross-agent targets detected or recorded.",
26975
+ autoFixable: false
26976
+ };
26977
+ }
26978
+ if (problems.length > 0) {
26979
+ return {
26980
+ id: this.id,
26981
+ category: this.category,
26982
+ title: this.title,
26983
+ status: "warn",
26984
+ detail: `${problems.join("; ")}. (${lines.join("; ")})`,
26985
+ affected,
26986
+ remediation: {
26987
+ kind: "manual",
26988
+ suggestion: "Re-run `syntaur setup --target <id>` (from the assignment workspace, to also write protocol files) to complete the install.",
26989
+ command: null
26990
+ },
26991
+ autoFixable: false
26992
+ };
26993
+ }
26994
+ return {
26995
+ id: this.id,
26996
+ category: this.category,
26997
+ title: this.title,
26998
+ status: "pass",
26999
+ detail: lines.join("; "),
27000
+ autoFixable: false
27001
+ };
27002
+ }
27003
+ };
27004
+ var crossAgentChecks = [crossAgentSkillsCheck];
27005
+
26570
27006
  // src/utils/doctor/checks/bundles.ts
26571
27007
  init_fs();
26572
27008
  init_paths();
26573
- import { resolve as resolve63 } from "path";
27009
+ import { resolve as resolve66 } from "path";
26574
27010
  import { readdir as readdir23 } from "fs/promises";
26575
- import { spawnSync as spawnSync9 } from "child_process";
27011
+ import { spawnSync as spawnSync10 } from "child_process";
26576
27012
  init_parser2();
26577
- var CATEGORY11 = "bundles";
27013
+ var CATEGORY12 = "bundles";
26578
27014
  async function listScopes(ctx) {
26579
27015
  const out = [];
26580
27016
  const td = todosDir();
@@ -26600,7 +27036,7 @@ async function listScopes(ctx) {
26600
27036
  if (!e.isDirectory()) continue;
26601
27037
  const slug = e.name;
26602
27038
  if (typeof slug !== "string" || slug.startsWith(".")) continue;
26603
- const projectMd = resolve63(ctx.config.defaultProjectDir, slug, "project.md");
27039
+ const projectMd = resolve66(ctx.config.defaultProjectDir, slug, "project.md");
26604
27040
  if (!await fileExists(projectMd)) continue;
26605
27041
  out.push({
26606
27042
  scopeLabel: `project:${slug}`,
@@ -26655,7 +27091,7 @@ async function gatherBundlesByScope(scopes) {
26655
27091
  }
26656
27092
  var orphanBundleId = {
26657
27093
  id: "bundles.orphan-bundleid",
26658
- category: CATEGORY11,
27094
+ category: CATEGORY12,
26659
27095
  title: "Every todo with a bundleId points at an existing bundle in the same scope",
26660
27096
  async run(ctx) {
26661
27097
  const scopes = await listScopes(ctx);
@@ -26678,7 +27114,7 @@ var orphanBundleId = {
26678
27114
  };
26679
27115
  var missingMembers = {
26680
27116
  id: "bundles.missing-members",
26681
- category: CATEGORY11,
27117
+ category: CATEGORY12,
26682
27118
  title: "Every bundle member exists in the bundle's scope checklist",
26683
27119
  async run(ctx) {
26684
27120
  const scopes = await listScopes(ctx);
@@ -26697,7 +27133,7 @@ var missingMembers = {
26697
27133
  };
26698
27134
  var scopeMismatch = {
26699
27135
  id: "bundles.scope-mismatch",
26700
- category: CATEGORY11,
27136
+ category: CATEGORY12,
26701
27137
  title: "Every bundle member's bundleId matches the bundle id",
26702
27138
  async run(ctx) {
26703
27139
  const scopes = await listScopes(ctx);
@@ -26715,7 +27151,7 @@ var scopeMismatch = {
26715
27151
  };
26716
27152
  var minMembers = {
26717
27153
  id: "bundles.min-members",
26718
- category: CATEGORY11,
27154
+ category: CATEGORY12,
26719
27155
  title: "Every bundle has at least 2 members",
26720
27156
  async run(ctx) {
26721
27157
  const scopes = await listScopes(ctx);
@@ -26731,7 +27167,7 @@ var minMembers = {
26731
27167
  };
26732
27168
  var stalePlanDir = {
26733
27169
  id: "bundles.stale-plan-dir",
26734
- category: CATEGORY11,
27170
+ category: CATEGORY12,
26735
27171
  title: "Every bundle's persisted planDir still exists on disk",
26736
27172
  async run(ctx) {
26737
27173
  const scopes = await listScopes(ctx);
@@ -26755,7 +27191,7 @@ var stalePlanDir = {
26755
27191
  };
26756
27192
  var staleWorktree = {
26757
27193
  id: "bundles.stale-worktree",
26758
- category: CATEGORY11,
27194
+ category: CATEGORY12,
26759
27195
  title: "Every bundle's persisted worktree still exists in the repo",
26760
27196
  async run(ctx) {
26761
27197
  const scopes = await listScopes(ctx);
@@ -26765,7 +27201,7 @@ var staleWorktree = {
26765
27201
  if (bs.bundle.worktreePath === null || bs.bundle.repository === null) continue;
26766
27202
  const onDisk = await fileExists(bs.bundle.worktreePath);
26767
27203
  let gitKnowsIt = false;
26768
- const gitOut = spawnSync9("git", ["-C", bs.bundle.repository, "worktree", "list", "--porcelain"], { encoding: "utf-8" });
27204
+ const gitOut = spawnSync10("git", ["-C", bs.bundle.repository, "worktree", "list", "--porcelain"], { encoding: "utf-8" });
26769
27205
  if (gitOut.status === 0) {
26770
27206
  gitKnowsIt = gitOut.stdout.split("\n").some((l) => l.trim() === `worktree ${bs.bundle.worktreePath}`);
26771
27207
  }
@@ -26805,6 +27241,7 @@ function allChecks() {
26805
27241
  ...agentChecks,
26806
27242
  ...terminalChecks,
26807
27243
  ...skillsChecks,
27244
+ ...crossAgentChecks,
26808
27245
  ...bundleChecks
26809
27246
  ];
26810
27247
  }
@@ -26891,7 +27328,7 @@ async function readVersion() {
26891
27328
  let dir = dirname19(here);
26892
27329
  for (let i = 0; i < 6; i++) {
26893
27330
  try {
26894
- const raw = await readFile40(join12(dir, "package.json"), "utf-8");
27331
+ const raw = await readFile42(join13(dir, "package.json"), "utf-8");
26895
27332
  const parsed = JSON.parse(raw);
26896
27333
  return typeof parsed.version === "string" ? parsed.version : null;
26897
27334
  } catch {
@@ -26988,7 +27425,7 @@ var REQUIRED_WORKSPACE_FIELDS = [
26988
27425
  ];
26989
27426
  var ISO_DATE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
26990
27427
  async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
26991
- const absolute = isAbsolute10(inputPath) ? inputPath : resolve64(cwd, inputPath);
27428
+ const absolute = isAbsolute10(inputPath) ? inputPath : resolve67(cwd, inputPath);
26992
27429
  const errors = [];
26993
27430
  const warnings = [];
26994
27431
  if (!await fileExists(absolute)) {
@@ -27001,7 +27438,7 @@ async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
27001
27438
  }
27002
27439
  let content;
27003
27440
  try {
27004
- content = await readFile41(absolute, "utf-8");
27441
+ content = await readFile43(absolute, "utf-8");
27005
27442
  } catch (err2) {
27006
27443
  return {
27007
27444
  ok: false,
@@ -27325,8 +27762,8 @@ init_uuid();
27325
27762
  init_timestamp();
27326
27763
  init_assignment_resolver();
27327
27764
  init_templates();
27328
- import { resolve as resolve65 } from "path";
27329
- import { readFile as readFile42 } from "fs/promises";
27765
+ import { resolve as resolve68 } from "path";
27766
+ import { readFile as readFile44 } from "fs/promises";
27330
27767
  function shortId() {
27331
27768
  return generateId().split("-")[0];
27332
27769
  }
@@ -27356,7 +27793,7 @@ async function commentCommand(target, text, options = {}) {
27356
27793
  if (!isValidSlug(target)) {
27357
27794
  throw new Error(`Invalid assignment slug "${target}".`);
27358
27795
  }
27359
- assignmentDir = resolve65(baseDir, options.project, "assignments", target);
27796
+ assignmentDir = resolve68(baseDir, options.project, "assignments", target);
27360
27797
  assignmentRef = target;
27361
27798
  } else {
27362
27799
  const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
@@ -27366,13 +27803,13 @@ async function commentCommand(target, text, options = {}) {
27366
27803
  assignmentDir = resolved.assignmentDir;
27367
27804
  assignmentRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
27368
27805
  }
27369
- const commentsPath = resolve65(assignmentDir, "comments.md");
27806
+ const commentsPath = resolve68(assignmentDir, "comments.md");
27370
27807
  const timestamp = nowTimestamp();
27371
27808
  const author = options.author ?? process.env.USER ?? "unknown";
27372
27809
  let currentContent;
27373
27810
  let currentCount = 0;
27374
27811
  if (await fileExists(commentsPath)) {
27375
- currentContent = await readFile42(commentsPath, "utf-8");
27812
+ currentContent = await readFile44(commentsPath, "utf-8");
27376
27813
  const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
27377
27814
  if (countMatch) currentCount = parseInt(countMatch[1], 10);
27378
27815
  } else {
@@ -27406,7 +27843,7 @@ ${entry}`;
27406
27843
  }
27407
27844
 
27408
27845
  // src/commands/capture.ts
27409
- import { resolve as resolve69, relative as relative4, dirname as dirname20 } from "path";
27846
+ import { resolve as resolve72, relative as relative4, dirname as dirname20 } from "path";
27410
27847
  import { copyFile as copyFile3, mkdir as mkdir9, realpath as realpath2, rm as rm12, stat as stat8, writeFile as writeFile14 } from "fs/promises";
27411
27848
  import { existsSync as existsSync6 } from "fs";
27412
27849
 
@@ -27417,8 +27854,8 @@ init_config2();
27417
27854
  init_slug();
27418
27855
  init_assignment_resolver();
27419
27856
  init_parser();
27420
- import { resolve as resolve66 } from "path";
27421
- import { readFile as readFile43 } from "fs/promises";
27857
+ import { resolve as resolve69 } from "path";
27858
+ import { readFile as readFile45 } from "fs/promises";
27422
27859
  var AssignmentTargetError = class extends Error {
27423
27860
  };
27424
27861
  function classifyContext(ctx) {
@@ -27430,10 +27867,10 @@ function classifyContext(ctx) {
27430
27867
  return "empty";
27431
27868
  }
27432
27869
  async function readAssignmentFrontmatterId(assignmentDir) {
27433
- const path = resolve66(assignmentDir, "assignment.md");
27870
+ const path = resolve69(assignmentDir, "assignment.md");
27434
27871
  if (!await fileExists(path)) return null;
27435
27872
  try {
27436
- const content = await readFile43(path, "utf-8");
27873
+ const content = await readFile45(path, "utf-8");
27437
27874
  const [fm] = extractFrontmatter(content);
27438
27875
  return getField(fm, "id");
27439
27876
  } catch {
@@ -27441,10 +27878,10 @@ async function readAssignmentFrontmatterId(assignmentDir) {
27441
27878
  }
27442
27879
  }
27443
27880
  async function readContextJson(cwd) {
27444
- const path = resolve66(cwd, ".syntaur", "context.json");
27881
+ const path = resolve69(cwd, ".syntaur", "context.json");
27445
27882
  if (!await fileExists(path)) return null;
27446
27883
  try {
27447
- const raw = await readFile43(path, "utf-8");
27884
+ const raw = await readFile45(path, "utf-8");
27448
27885
  return JSON.parse(raw);
27449
27886
  } catch {
27450
27887
  return null;
@@ -27465,15 +27902,15 @@ async function resolveAssignmentTarget(input4, opts = {}) {
27465
27902
  if (!isValidSlug(input4)) {
27466
27903
  throw new AssignmentTargetError(`Invalid assignment slug "${input4}".`);
27467
27904
  }
27468
- const projectDir = resolve66(baseDir, opts.project);
27469
- const projectMdPath = resolve66(projectDir, "project.md");
27905
+ const projectDir = resolve69(baseDir, opts.project);
27906
+ const projectMdPath = resolve69(projectDir, "project.md");
27470
27907
  if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
27471
27908
  throw new AssignmentTargetError(
27472
27909
  `Project "${opts.project}" not found at ${projectDir}.`
27473
27910
  );
27474
27911
  }
27475
- const assignmentDir = resolve66(projectDir, "assignments", input4);
27476
- const assignmentMdPath2 = resolve66(assignmentDir, "assignment.md");
27912
+ const assignmentDir = resolve69(projectDir, "assignments", input4);
27913
+ const assignmentMdPath2 = resolve69(assignmentDir, "assignment.md");
27477
27914
  if (!await fileExists(assignmentMdPath2)) {
27478
27915
  throw new AssignmentTargetError(
27479
27916
  `Assignment "${input4}" not found in project "${opts.project}".`
@@ -27512,7 +27949,7 @@ async function resolveAssignmentTarget(input4, opts = {}) {
27512
27949
  }
27513
27950
  if (ctx.assignmentDir) {
27514
27951
  const dir = expandHome(ctx.assignmentDir);
27515
- const assignmentMdPath2 = resolve66(dir, "assignment.md");
27952
+ const assignmentMdPath2 = resolve69(dir, "assignment.md");
27516
27953
  if (!await fileExists(assignmentMdPath2)) {
27517
27954
  throw new AssignmentTargetError(
27518
27955
  `.syntaur/context.json points to a missing assignment dir: ${dir}.`
@@ -27541,8 +27978,8 @@ async function resolveAssignmentTarget(input4, opts = {}) {
27541
27978
  `.syntaur/context.json contains invalid slugs: project="${ctx.projectSlug}" assignment="${ctx.assignmentSlug}".`
27542
27979
  );
27543
27980
  }
27544
- const assignmentDir = resolve66(baseDir, ctx.projectSlug, "assignments", ctx.assignmentSlug);
27545
- const assignmentMdPath2 = resolve66(assignmentDir, "assignment.md");
27981
+ const assignmentDir = resolve69(baseDir, ctx.projectSlug, "assignments", ctx.assignmentSlug);
27982
+ const assignmentMdPath2 = resolve69(assignmentDir, "assignment.md");
27546
27983
  if (!await fileExists(assignmentMdPath2)) {
27547
27984
  throw new AssignmentTargetError(
27548
27985
  `.syntaur/context.json points to a missing assignment: ${assignmentDir}.`
@@ -27605,7 +28042,7 @@ init_fs();
27605
28042
  import { spawn as spawn7 } from "child_process";
27606
28043
  import { mkdtemp as mkdtemp2, rm as rm8, stat as stat6 } from "fs/promises";
27607
28044
  import { tmpdir as tmpdir3 } from "os";
27608
- import { join as join13 } from "path";
28045
+ import { join as join14 } from "path";
27609
28046
  function argsFor(mode, pngPath) {
27610
28047
  switch (mode) {
27611
28048
  case "interactive":
@@ -27638,8 +28075,8 @@ async function captureScreenshot(mode) {
27638
28075
  "screencapture is only available on macOS. Use --file <path> to attach an existing image."
27639
28076
  );
27640
28077
  }
27641
- const tmpDir = await mkdtemp2(join13(tmpdir3(), "syntaur-screenshot-"));
27642
- const pngPath = join13(tmpDir, "shot.png");
28078
+ const tmpDir = await mkdtemp2(join14(tmpdir3(), "syntaur-screenshot-"));
28079
+ const pngPath = join14(tmpDir, "shot.png");
27643
28080
  const cleanup = async () => {
27644
28081
  await rm8(tmpDir, { recursive: true, force: true }).catch(() => {
27645
28082
  });
@@ -27677,9 +28114,9 @@ async function captureScreenshot(mode) {
27677
28114
 
27678
28115
  // src/utils/asciinema.ts
27679
28116
  import { spawn as spawn8 } from "child_process";
27680
- import { mkdtemp as mkdtemp3, readFile as readFile44, rm as rm9 } from "fs/promises";
28117
+ import { mkdtemp as mkdtemp3, readFile as readFile46, rm as rm9 } from "fs/promises";
27681
28118
  import { tmpdir as tmpdir4 } from "os";
27682
- import { join as join14 } from "path";
28119
+ import { join as join15 } from "path";
27683
28120
  var SAFE_RE = /^[A-Za-z0-9_@%+=:,./-]+$/;
27684
28121
  function shellQuote2(s) {
27685
28122
  if (s.length === 0) return `''`;
@@ -27716,8 +28153,8 @@ function runAsciinema(args, stdio) {
27716
28153
  });
27717
28154
  }
27718
28155
  async function captureAsciinema(opts) {
27719
- const tmpDir = await mkdtemp3(join14(tmpdir4(), "syntaur-asciinema-"));
27720
- const castPath = join14(tmpDir, "session.cast");
28156
+ const tmpDir = await mkdtemp3(join15(tmpdir4(), "syntaur-asciinema-"));
28157
+ const castPath = join15(tmpDir, "session.cast");
27721
28158
  const cleanup = async () => {
27722
28159
  await rm9(tmpDir, { recursive: true, force: true }).catch(() => {
27723
28160
  });
@@ -27740,7 +28177,7 @@ async function captureAsciinema(opts) {
27740
28177
  }
27741
28178
  throw err2;
27742
28179
  }
27743
- const text = await readFile44(castPath, "utf8").catch(() => null);
28180
+ const text = await readFile46(castPath, "utf8").catch(() => null);
27744
28181
  if (text === null) {
27745
28182
  throw new Error(
27746
28183
  `asciinema produced no cast file at ${castPath} (exit ${exitCode}). Try running 'asciinema rec ${castPath}' directly to diagnose.`
@@ -27768,9 +28205,9 @@ async function captureAsciinema(opts) {
27768
28205
  // src/utils/recording.ts
27769
28206
  init_paths();
27770
28207
  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";
28208
+ 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
28209
  import { tmpdir as tmpdir5 } from "os";
27773
- import { join as join15, resolve as resolve67 } from "path";
28210
+ import { join as join16, resolve as resolve70 } from "path";
27774
28211
  import { setTimeout as sleep } from "timers/promises";
27775
28212
  function sigintPollIntervalMs() {
27776
28213
  const raw = process.env.SYNTAUR_RECORDING_POLL_INTERVAL_MS;
@@ -27791,13 +28228,13 @@ function sigtermWaitMs() {
27791
28228
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : 1e3;
27792
28229
  }
27793
28230
  function pidfilePath() {
27794
- return resolve67(syntaurRoot(), "recording.pid");
28231
+ return resolve70(syntaurRoot(), "recording.pid");
27795
28232
  }
27796
28233
  function logPath2() {
27797
- return resolve67(syntaurRoot(), "recording.log");
28234
+ return resolve70(syntaurRoot(), "recording.log");
27798
28235
  }
27799
28236
  function sidecarPath() {
27800
- return resolve67(syntaurRoot(), "recording.json");
28237
+ return resolve70(syntaurRoot(), "recording.json");
27801
28238
  }
27802
28239
  function ffmpegArgs(device, fps, mp4Path) {
27803
28240
  return [
@@ -27842,7 +28279,7 @@ async function acquirePidfile(pidfile) {
27842
28279
  } catch (err2) {
27843
28280
  if (err2.code !== "EEXIST") throw err2;
27844
28281
  if (attempt === 1) throw err2;
27845
- const existing = (await readFile45(pidfile, "utf-8").catch(() => "")).trim();
28282
+ const existing = (await readFile47(pidfile, "utf-8").catch(() => "")).trim();
27846
28283
  if (existing.startsWith(STARTING_SENTINEL_PREFIX)) {
27847
28284
  const parentPidRaw = existing.slice(STARTING_SENTINEL_PREFIX.length);
27848
28285
  const parentPid = Number.parseInt(parentPidRaw, 10);
@@ -27895,8 +28332,8 @@ async function startRecording(input4) {
27895
28332
  let acquiredPid = null;
27896
28333
  try {
27897
28334
  logHandle = await open3(log, "w");
27898
- tmpDir = await mkdtemp4(join15(tmpdir5(), "syntaur-recording-"));
27899
- const mp4Path = join15(tmpDir, "recording.mp4");
28335
+ tmpDir = await mkdtemp4(join16(tmpdir5(), "syntaur-recording-"));
28336
+ const mp4Path = join16(tmpDir, "recording.mp4");
27900
28337
  let child;
27901
28338
  try {
27902
28339
  child = spawn9("ffmpeg", ffmpegArgs(input4.device, input4.fps, mp4Path), {
@@ -27944,7 +28381,7 @@ async function startRecording(input4) {
27944
28381
  logHandle = null;
27945
28382
  if (warmupMs > 0) await sleep(warmupMs);
27946
28383
  if (!await isProcessAlive(pid)) {
27947
- const tail = await readFile45(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
28384
+ const tail = await readFile47(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
27948
28385
  acquiredPid = null;
27949
28386
  throw new Error(
27950
28387
  `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 +28438,7 @@ ${tail}`
28001
28438
  async function stopRecording() {
28002
28439
  const pidfile = pidfilePath();
28003
28440
  const sidecar = sidecarPath();
28004
- const pidRaw = await readFile45(pidfile, "utf-8").catch(() => null);
28441
+ const pidRaw = await readFile47(pidfile, "utf-8").catch(() => null);
28005
28442
  if (pidRaw === null) {
28006
28443
  throw new Error(
28007
28444
  `No active recording found (no pidfile at ${pidfile}). Did you run --start?`
@@ -28011,7 +28448,7 @@ async function stopRecording() {
28011
28448
  if (!Number.isInteger(pid) || pid <= 0) {
28012
28449
  throw new Error(`Pidfile at ${pidfile} is corrupt (got "${pidRaw}").`);
28013
28450
  }
28014
- const sidecarRaw = await readFile45(sidecar, "utf-8").catch(() => null);
28451
+ const sidecarRaw = await readFile47(sidecar, "utf-8").catch(() => null);
28015
28452
  if (sidecarRaw === null) {
28016
28453
  throw new Error(
28017
28454
  `No recording sidecar at ${sidecar}. The recording state is inconsistent \u2014 delete ${pidfile} and re-run --start.`
@@ -28076,7 +28513,7 @@ async function stopRecording() {
28076
28513
  // src/db/proof-db.ts
28077
28514
  init_paths();
28078
28515
  import Database5 from "better-sqlite3";
28079
- import { resolve as resolve68 } from "path";
28516
+ import { resolve as resolve71 } from "path";
28080
28517
  var db4 = null;
28081
28518
  var PROOF_SCHEMA_VERSION = "1";
28082
28519
  var SCHEMA_SQL4 = `
@@ -28096,7 +28533,7 @@ CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);
28096
28533
  `;
28097
28534
  function initProofDb(dbPath) {
28098
28535
  if (db4) return db4;
28099
- const finalPath = dbPath ?? resolve68(syntaurRoot(), "syntaur.db");
28536
+ const finalPath = dbPath ?? resolve71(syntaurRoot(), "syntaur.db");
28100
28537
  db4 = new Database5(finalPath);
28101
28538
  db4.pragma("journal_mode = WAL");
28102
28539
  db4.exec(SCHEMA_SQL4);
@@ -28144,9 +28581,9 @@ function listArtifactsByAssignment(assignmentId) {
28144
28581
 
28145
28582
  // src/utils/transcribers/elevenlabs.ts
28146
28583
  import { spawn as spawn10 } from "child_process";
28147
- import { mkdtemp as mkdtemp5, readFile as readFile46, rm as rm11 } from "fs/promises";
28584
+ import { mkdtemp as mkdtemp5, readFile as readFile48, rm as rm11 } from "fs/promises";
28148
28585
  import { tmpdir as tmpdir6 } from "os";
28149
- import { join as join16 } from "path";
28586
+ import { join as join17 } from "path";
28150
28587
  var SCRIBE_URL = "https://api.elevenlabs.io/v1/speech-to-text";
28151
28588
  var NO_AUDIO_MARKERS = [
28152
28589
  "Stream map '0:a:0' matches no streams",
@@ -28208,7 +28645,7 @@ async function extractAudio(videoAbsPath, wavOut) {
28208
28645
  throw new TranscribeFfmpegError(`ffmpeg failed (exit ${result.code}): ${tail}`);
28209
28646
  }
28210
28647
  async function callScribe(wavPath, apiKey, opts) {
28211
- const audio = await readFile46(wavPath);
28648
+ const audio = await readFile48(wavPath);
28212
28649
  const form = new FormData();
28213
28650
  form.set("file", new Blob([new Uint8Array(audio)], { type: "audio/wav" }), "audio.wav");
28214
28651
  form.set("model_id", "scribe_v1");
@@ -28236,8 +28673,8 @@ var elevenLabsScribe = {
28236
28673
  "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
28674
  );
28238
28675
  }
28239
- const tmp = await mkdtemp5(join16(tmpdir6(), "syntaur-transcribe-"));
28240
- const wav = join16(tmp, "audio.wav");
28676
+ const tmp = await mkdtemp5(join17(tmpdir6(), "syntaur-transcribe-"));
28677
+ const wav = join17(tmp, "audio.wav");
28241
28678
  try {
28242
28679
  await extractAudio(videoAbsPath, wav);
28243
28680
  return await callScribe(wav, apiKey, opts);
@@ -28524,7 +28961,7 @@ async function captureCommand(target, options = {}) {
28524
28961
  });
28525
28962
  }
28526
28963
  if (options.file) {
28527
- const expanded = options.file.startsWith("~/") ? resolve69(process.env.HOME ?? "", options.file.slice(2)) : resolve69(options.file);
28964
+ const expanded = options.file.startsWith("~/") ? resolve72(process.env.HOME ?? "", options.file.slice(2)) : resolve72(options.file);
28528
28965
  if (!await fileExists(expanded)) {
28529
28966
  throw new Error(`--file does not exist: ${options.file}`);
28530
28967
  }
@@ -28565,7 +29002,7 @@ async function captureCommand(target, options = {}) {
28565
29002
  }
28566
29003
  initProofDb();
28567
29004
  const subdir = criterionIndex === null ? "untagged" : String(criterionIndex);
28568
- const destDir = resolve69(proofDir(resolved.assignmentDir), subdir);
29005
+ const destDir = resolve72(proofDir(resolved.assignmentDir), subdir);
28569
29006
  if (resolvedSource) await mkdir9(destDir, { recursive: true });
28570
29007
  const ext = resolvedSource ? extensionForKind(kind) : null;
28571
29008
  let id = null;
@@ -28574,7 +29011,7 @@ async function captureCommand(target, options = {}) {
28574
29011
  let lastErr = null;
28575
29012
  for (let attempt = 0; attempt < MAX_ID_RETRIES; attempt += 1) {
28576
29013
  const candidate = generateArtifactId();
28577
- const candidateAbsPath = resolvedSource && ext ? resolve69(destDir, `${candidate}.${ext}`) : null;
29014
+ const candidateAbsPath = resolvedSource && ext ? resolve72(destDir, `${candidate}.${ext}`) : null;
28578
29015
  const candidateRel = candidateAbsPath ? relative4(resolved.assignmentDir, candidateAbsPath) : null;
28579
29016
  try {
28580
29017
  insertArtifact({
@@ -28618,7 +29055,7 @@ async function captureCommand(target, options = {}) {
28618
29055
  }
28619
29056
  }
28620
29057
  if (options.transcribe && kind === "video" && absPath && id) {
28621
- const sidecarPath2 = resolve69(destDir, `${id}.transcript.md`);
29058
+ const sidecarPath2 = resolve72(destDir, `${id}.transcript.md`);
28622
29059
  if (existsSync6(sidecarPath2)) {
28623
29060
  console.warn(
28624
29061
  `transcript: ${sidecarPath2} already exists, skipping (delete to re-transcribe)`
@@ -28650,7 +29087,7 @@ async function captureCommand(target, options = {}) {
28650
29087
  const tagSuffix = criterionIndex === null ? "untagged" : `criterion ${criterionIndex}`;
28651
29088
  console.log(`Captured artifact ${id} (${kind}) for ${ref} \u2014 ${tagSuffix}.`);
28652
29089
  if (relativeFilePath) {
28653
- console.log(` file: ${resolve69(resolved.assignmentDir, relativeFilePath)}`);
29090
+ console.log(` file: ${resolve72(resolved.assignmentDir, relativeFilePath)}`);
28654
29091
  }
28655
29092
  } catch (err2) {
28656
29093
  if (options.stop && kind === "video" && shelloutCleanup && resolvedSource) {
@@ -28673,8 +29110,8 @@ async function captureCommand(target, options = {}) {
28673
29110
 
28674
29111
  // src/commands/proof.ts
28675
29112
  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";
29113
+ import { readFile as readFile49, writeFile as writeFile15, rename as rename9, stat as stat9 } from "fs/promises";
29114
+ import { resolve as resolve73, relative as relative5, isAbsolute as isAbsolute11, dirname as dirname21 } from "path";
28678
29115
  import { randomBytes as randomBytes4 } from "crypto";
28679
29116
 
28680
29117
  // src/utils/acceptance-criteria-parse.ts
@@ -28914,11 +29351,11 @@ function renderProofHtml(params2, inlineFiles = /* @__PURE__ */ new Map(), trans
28914
29351
 
28915
29352
  // src/commands/proof.ts
28916
29353
  async function readAssignmentMeta(assignmentDir) {
28917
- const path = resolve70(assignmentDir, "assignment.md");
29354
+ const path = resolve73(assignmentDir, "assignment.md");
28918
29355
  if (!await fileExists(path)) {
28919
29356
  return { title: "", body: "" };
28920
29357
  }
28921
- const content = await readFile47(path, "utf-8");
29358
+ const content = await readFile49(path, "utf-8");
28922
29359
  const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
28923
29360
  let title = "";
28924
29361
  if (fmMatch) {
@@ -28965,7 +29402,7 @@ async function loadInlineFiles(rows, assignmentDir) {
28965
29402
  for (const r of rows) {
28966
29403
  if (!r.file_path) continue;
28967
29404
  if (r.kind !== "http" && r.kind !== "text") continue;
28968
- const abs = resolve70(assignmentDir, r.file_path);
29405
+ const abs = resolve73(assignmentDir, r.file_path);
28969
29406
  if (!isWithin(proofRoot, abs)) {
28970
29407
  out.set(r.file_path, null);
28971
29408
  continue;
@@ -28980,7 +29417,7 @@ async function loadInlineFiles(rows, assignmentDir) {
28980
29417
  continue;
28981
29418
  }
28982
29419
  try {
28983
- out.set(r.file_path, await readFile47(abs, "utf-8"));
29420
+ out.set(r.file_path, await readFile49(abs, "utf-8"));
28984
29421
  } catch {
28985
29422
  out.set(r.file_path, null);
28986
29423
  }
@@ -28992,14 +29429,14 @@ async function loadTranscriptSidecars(rows, assignmentDir) {
28992
29429
  const proofRoot = proofDir(assignmentDir);
28993
29430
  for (const r of rows) {
28994
29431
  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`);
29432
+ const videoAbs = resolve73(assignmentDir, r.file_path);
29433
+ const sidecar = resolve73(dirname21(videoAbs), `${r.id}.transcript.md`);
28997
29434
  if (!isWithin(proofRoot, sidecar)) continue;
28998
29435
  if (!await fileExists(sidecar)) continue;
28999
29436
  const st = await stat9(sidecar);
29000
29437
  if (st.size > INLINE_TEXT_LIMIT_BYTES) continue;
29001
29438
  try {
29002
- out.set(r.id, await readFile47(sidecar, "utf-8"));
29439
+ out.set(r.id, await readFile49(sidecar, "utf-8"));
29003
29440
  } catch {
29004
29441
  }
29005
29442
  }
@@ -29033,8 +29470,8 @@ async function proofBuildCommand(target, options = {}) {
29033
29470
  };
29034
29471
  const md = renderProofMarkdown(renderParams);
29035
29472
  const html = renderProofHtml(renderParams, inlineFiles, transcriptSidecars);
29036
- const mdPath = resolve70(resolved.assignmentDir, "proof.md");
29037
- const htmlPath = resolve70(resolved.assignmentDir, "proof.html");
29473
+ const mdPath = resolve73(resolved.assignmentDir, "proof.md");
29474
+ const htmlPath = resolve73(resolved.assignmentDir, "proof.html");
29038
29475
  await atomicWrite(mdPath, md);
29039
29476
  await atomicWrite(htmlPath, html);
29040
29477
  console.log(`Wrote ${htmlPath}`);
@@ -29699,7 +30136,7 @@ async function runCcusage(opts = {}) {
29699
30136
  };
29700
30137
  }
29701
30138
  function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
29702
- return new Promise((resolve79) => {
30139
+ return new Promise((resolve82) => {
29703
30140
  const child = spawn11(binary, args, {
29704
30141
  env: env ?? process.env,
29705
30142
  stdio: ["ignore", "pipe", "pipe"]
@@ -29713,7 +30150,7 @@ function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
29713
30150
  if (settled) return;
29714
30151
  settled = true;
29715
30152
  clearTimeout(timer2);
29716
- resolve79(result);
30153
+ resolve82(result);
29717
30154
  };
29718
30155
  const timer2 = setTimeout(() => {
29719
30156
  timedOut = true;
@@ -29758,8 +30195,8 @@ function isoToCcusageDate(iso) {
29758
30195
  // src/usage/cwd-extractor.ts
29759
30196
  init_paths();
29760
30197
  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";
30198
+ import { join as join18 } from "path";
30199
+ import { homedir as homedir12 } from "os";
29763
30200
  var SCAN_LINE_CAP = 50;
29764
30201
  var TAIL_READ_BYTES = 8 * 1024;
29765
30202
  var TAIL_READ_BYTES_MAX = 64 * 1024;
@@ -29833,12 +30270,12 @@ async function* walkClaudeProjects(opts = {}) {
29833
30270
  const dirs = await listDirSafe(root);
29834
30271
  for (const dirent of dirs) {
29835
30272
  if (!dirent.isDirectory) continue;
29836
- const dirPath = join17(root, dirent.name);
30273
+ const dirPath = join18(root, dirent.name);
29837
30274
  const files = await listDirSafe(dirPath);
29838
30275
  let cachedCwd = null;
29839
30276
  for (const f of files) {
29840
30277
  if (!f.isFile || !f.name.endsWith(".jsonl")) continue;
29841
- const filePath = join17(dirPath, f.name);
30278
+ const filePath = join18(dirPath, f.name);
29842
30279
  if (opts.sinceMtimeMs !== void 0) {
29843
30280
  const mtime = await mtimeMs(filePath);
29844
30281
  if (mtime !== null && mtime < opts.sinceMtimeMs) continue;
@@ -29875,8 +30312,8 @@ function resolveCodexSessionsRoot(override) {
29875
30312
  const fromSessionsEnv = process.env.CODEX_SESSIONS_DIR;
29876
30313
  if (fromSessionsEnv && fromSessionsEnv.length > 0) return expandHome(fromSessionsEnv);
29877
30314
  const fromHomeEnv = process.env.CODEX_HOME;
29878
- if (fromHomeEnv && fromHomeEnv.length > 0) return join17(expandHome(fromHomeEnv), "sessions");
29879
- return join17(homedir11(), ".codex", "sessions");
30315
+ if (fromHomeEnv && fromHomeEnv.length > 0) return join18(expandHome(fromHomeEnv), "sessions");
30316
+ return join18(homedir12(), ".codex", "sessions");
29880
30317
  }
29881
30318
  async function listDirSafe(path) {
29882
30319
  try {
@@ -29896,7 +30333,7 @@ async function* walkJsonlRecursive(root) {
29896
30333
  const current = stack.pop();
29897
30334
  const entries = await listDirSafe(current);
29898
30335
  for (const e of entries) {
29899
- const full = join17(current, e.name);
30336
+ const full = join18(current, e.name);
29900
30337
  if (e.isDirectory) {
29901
30338
  stack.push(full);
29902
30339
  } else if (e.isFile && e.name.endsWith(".jsonl")) {
@@ -30235,8 +30672,8 @@ init_slug();
30235
30672
  init_timestamp();
30236
30673
  init_assignment_resolver();
30237
30674
  init_assignment_todos();
30238
- import { resolve as resolve71 } from "path";
30239
- import { readFile as readFile48 } from "fs/promises";
30675
+ import { resolve as resolve74 } from "path";
30676
+ import { readFile as readFile50 } from "fs/promises";
30240
30677
  async function requestCommand(target, text, options = {}) {
30241
30678
  if (!text || !text.trim()) {
30242
30679
  throw new Error("Request text cannot be empty.");
@@ -30252,7 +30689,7 @@ async function requestCommand(target, text, options = {}) {
30252
30689
  if (!isValidSlug(target)) {
30253
30690
  throw new Error(`Invalid assignment slug "${target}".`);
30254
30691
  }
30255
- assignmentDir = resolve71(baseDir, options.project, "assignments", target);
30692
+ assignmentDir = resolve74(baseDir, options.project, "assignments", target);
30256
30693
  targetRef = target;
30257
30694
  } else {
30258
30695
  const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
@@ -30262,12 +30699,12 @@ async function requestCommand(target, text, options = {}) {
30262
30699
  assignmentDir = resolved.assignmentDir;
30263
30700
  targetRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
30264
30701
  }
30265
- const assignmentMdPath2 = resolve71(assignmentDir, "assignment.md");
30702
+ const assignmentMdPath2 = resolve74(assignmentDir, "assignment.md");
30266
30703
  if (!await fileExists(assignmentMdPath2)) {
30267
30704
  throw new Error(`assignment.md not found at ${assignmentMdPath2}`);
30268
30705
  }
30269
30706
  const source = options.from ?? process.env.SYNTAUR_ASSIGNMENT ?? "unknown";
30270
- let content = await readFile48(assignmentMdPath2, "utf-8");
30707
+ let content = await readFile50(assignmentMdPath2, "utf-8");
30271
30708
  content = appendTodosToAssignmentBody(content, [
30272
30709
  { description: `${text.trim()} (from: ${source})` }
30273
30710
  ]);
@@ -30280,13 +30717,13 @@ async function requestCommand(target, text, options = {}) {
30280
30717
  init_fs();
30281
30718
  init_paths();
30282
30719
  import { Command as Command9 } from "commander";
30283
- import { readFile as readFile49, readdir as readdir25 } from "fs/promises";
30284
- import { resolve as resolve72 } from "path";
30720
+ import { readFile as readFile51, readdir as readdir25 } from "fs/promises";
30721
+ import { resolve as resolve75 } from "path";
30285
30722
  async function readContextAssignmentDir(cwd) {
30286
- const path = resolve72(cwd, ".syntaur", "context.json");
30723
+ const path = resolve75(cwd, ".syntaur", "context.json");
30287
30724
  if (!await fileExists(path)) return null;
30288
30725
  try {
30289
- const raw = await readFile49(path, "utf-8");
30726
+ const raw = await readFile51(path, "utf-8");
30290
30727
  const ctx = JSON.parse(raw);
30291
30728
  if (typeof ctx.assignmentDir === "string" && ctx.assignmentDir.length > 0) {
30292
30729
  return ctx.assignmentDir;
@@ -30300,9 +30737,9 @@ async function resolveAssignmentDir(opts) {
30300
30737
  const cwd = opts.cwd ?? process.cwd();
30301
30738
  if (opts.assignment) {
30302
30739
  if (opts.project) {
30303
- return resolve72(defaultProjectDir(), opts.project, "assignments", opts.assignment);
30740
+ return resolve75(defaultProjectDir(), opts.project, "assignments", opts.assignment);
30304
30741
  }
30305
- return resolve72(assignmentsDir(), opts.assignment);
30742
+ return resolve75(assignmentsDir(), opts.assignment);
30306
30743
  }
30307
30744
  const fromCtx = await readContextAssignmentDir(cwd);
30308
30745
  if (fromCtx) return fromCtx;
@@ -30445,7 +30882,7 @@ async function runPlanVersion(options) {
30445
30882
  if (!await fileExists(assignmentDir)) {
30446
30883
  throw new Error(`Assignment directory does not exist: ${assignmentDir}`);
30447
30884
  }
30448
- const assignmentMdPath2 = resolve72(assignmentDir, "assignment.md");
30885
+ const assignmentMdPath2 = resolve75(assignmentDir, "assignment.md");
30449
30886
  if (!await fileExists(assignmentMdPath2)) {
30450
30887
  throw new Error(`Missing assignment.md at: ${assignmentMdPath2}`);
30451
30888
  }
@@ -30457,15 +30894,15 @@ async function runPlanVersion(options) {
30457
30894
  }
30458
30895
  const current = planFiles[planFiles.length - 1];
30459
30896
  const next = nextPlanFileName(current.version);
30460
- const newPath = resolve72(assignmentDir, next.fileName);
30897
+ const newPath = resolve75(assignmentDir, next.fileName);
30461
30898
  if (await fileExists(newPath) && !options.force) {
30462
30899
  throw new Error(`${next.fileName} already exists. Use --force to overwrite.`);
30463
30900
  }
30464
- const assignmentMd = await readFile49(assignmentMdPath2, "utf-8");
30901
+ const assignmentMd = await readFile51(assignmentMdPath2, "utf-8");
30465
30902
  const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
30466
30903
  const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
30467
- const oldPlanPath = resolve72(assignmentDir, current.fileName);
30468
- const oldPlanContent = await readFile49(oldPlanPath, "utf-8");
30904
+ const oldPlanPath = resolve75(assignmentDir, current.fileName);
30905
+ const oldPlanContent = await readFile51(oldPlanPath, "utf-8");
30469
30906
  const oldBody = oldPlanContent.replace(/^---[\s\S]*?\n---\n?/, "");
30470
30907
  const carriedTodos = extractUncheckedTodos(oldBody);
30471
30908
  const stub = buildNewPlanStub({
@@ -30502,26 +30939,26 @@ planCommand.command("version").description(
30502
30939
  // src/commands/session.ts
30503
30940
  init_fs();
30504
30941
  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";
30942
+ import { readFile as readFile52, readdir as readdir26, stat as stat11 } from "fs/promises";
30943
+ import { resolve as resolve76 } from "path";
30507
30944
  async function readContext(cwd) {
30508
- const path = resolve73(cwd, ".syntaur", "context.json");
30945
+ const path = resolve76(cwd, ".syntaur", "context.json");
30509
30946
  if (!await fileExists(path)) return null;
30510
30947
  try {
30511
- const raw = await readFile50(path, "utf-8");
30948
+ const raw = await readFile52(path, "utf-8");
30512
30949
  return JSON.parse(raw);
30513
30950
  } catch {
30514
30951
  return null;
30515
30952
  }
30516
30953
  }
30517
30954
  async function findLatestSessionSummary(assignmentDir) {
30518
- const sessionsRoot = resolve73(assignmentDir, "sessions");
30955
+ const sessionsRoot = resolve76(assignmentDir, "sessions");
30519
30956
  if (!await fileExists(sessionsRoot)) return null;
30520
30957
  const entries = await readdir26(sessionsRoot, { withFileTypes: true });
30521
30958
  let best = null;
30522
30959
  for (const entry of entries) {
30523
30960
  if (!entry.isDirectory()) continue;
30524
- const summaryPath = resolve73(sessionsRoot, entry.name, "summary.md");
30961
+ const summaryPath = resolve76(sessionsRoot, entry.name, "summary.md");
30525
30962
  if (!await fileExists(summaryPath)) continue;
30526
30963
  const st = await stat11(summaryPath);
30527
30964
  if (best === null || st.mtime.getTime() > best.mtime.getTime()) {
@@ -30531,9 +30968,9 @@ async function findLatestSessionSummary(assignmentDir) {
30531
30968
  return best;
30532
30969
  }
30533
30970
  async function findOpenHandoff(assignmentDir) {
30534
- const handoffPath = resolve73(assignmentDir, "handoff.md");
30971
+ const handoffPath = resolve76(assignmentDir, "handoff.md");
30535
30972
  if (!await fileExists(handoffPath)) return null;
30536
- const content = await readFile50(handoffPath, "utf-8");
30973
+ const content = await readFile52(handoffPath, "utf-8");
30537
30974
  const body = content.replace(/^---[\s\S]*?\n---\n?/, "").trim();
30538
30975
  if (body.length === 0) return null;
30539
30976
  if (/^<!--[\s\S]*-->$/.test(body)) return null;
@@ -30641,13 +31078,13 @@ init_git_worktree();
30641
31078
  init_fs();
30642
31079
  init_paths();
30643
31080
  import { Command as Command11 } from "commander";
30644
- import { readFile as readFile51 } from "fs/promises";
30645
- import { resolve as resolve74 } from "path";
31081
+ import { readFile as readFile53 } from "fs/promises";
31082
+ import { resolve as resolve77 } from "path";
30646
31083
  async function readContext2(cwd) {
30647
- const path = resolve74(cwd, ".syntaur", "context.json");
31084
+ const path = resolve77(cwd, ".syntaur", "context.json");
30648
31085
  if (!await fileExists(path)) return null;
30649
31086
  try {
30650
- return JSON.parse(await readFile51(path, "utf-8"));
31087
+ return JSON.parse(await readFile53(path, "utf-8"));
30651
31088
  } catch {
30652
31089
  return null;
30653
31090
  }
@@ -30655,7 +31092,7 @@ async function readContext2(cwd) {
30655
31092
  async function resolveAssignmentPath2(opts) {
30656
31093
  if (opts.assignment) {
30657
31094
  if (opts.project) {
30658
- return resolve74(
31095
+ return resolve77(
30659
31096
  defaultProjectDir(),
30660
31097
  opts.project,
30661
31098
  "assignments",
@@ -30663,10 +31100,10 @@ async function resolveAssignmentPath2(opts) {
30663
31100
  "assignment.md"
30664
31101
  );
30665
31102
  }
30666
- return resolve74(assignmentsDir(), opts.assignment, "assignment.md");
31103
+ return resolve77(assignmentsDir(), opts.assignment, "assignment.md");
30667
31104
  }
30668
31105
  const ctx = await readContext2(opts.cwd);
30669
- if (ctx?.assignmentDir) return resolve74(ctx.assignmentDir, "assignment.md");
31106
+ if (ctx?.assignmentDir) return resolve77(ctx.assignmentDir, "assignment.md");
30670
31107
  throw new Error(
30671
31108
  "No active assignment. Pass --assignment <slug> [--project <slug>] or run from a workspace with .syntaur/context.json."
30672
31109
  );
@@ -30677,7 +31114,7 @@ async function runWorktreeCreate(options, cwd = process.cwd()) {
30677
31114
  }
30678
31115
  const repository = options.repository ?? cwd;
30679
31116
  const parentBranch = options.parentBranch ?? "main";
30680
- const worktreePath = options.worktreePath ?? resolve74(repository, ".worktrees", options.branch);
31117
+ const worktreePath = options.worktreePath ?? resolve77(repository, ".worktrees", options.branch);
30681
31118
  const assignmentPath = await resolveAssignmentPath2({
30682
31119
  assignment: options.assignment,
30683
31120
  project: options.project,
@@ -30714,13 +31151,13 @@ init_paths();
30714
31151
  init_fs();
30715
31152
  init_slug();
30716
31153
  import { Command as Command12 } from "commander";
30717
- import { resolve as resolve76 } from "path";
31154
+ import { resolve as resolve79 } from "path";
30718
31155
 
30719
31156
  // src/utils/project-indexes.ts
30720
31157
  init_parser();
30721
31158
  init_fs();
30722
- import { readdir as readdir27, readFile as readFile52 } from "fs/promises";
30723
- import { resolve as resolve75 } from "path";
31159
+ import { readdir as readdir27, readFile as readFile54 } from "fs/promises";
31160
+ import { resolve as resolve78 } from "path";
30724
31161
  function nowIso3() {
30725
31162
  return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
30726
31163
  }
@@ -30739,7 +31176,7 @@ function joinList(items) {
30739
31176
  return items.length === 0 ? "\u2014" : items.map(escapeCell).join(", ");
30740
31177
  }
30741
31178
  async function rebuildResourcesIndex(projectDir) {
30742
- const dir = resolve75(projectDir, "resources");
31179
+ const dir = resolve78(projectDir, "resources");
30743
31180
  await ensureDir(dir);
30744
31181
  const files = await listSlugFiles(dir);
30745
31182
  const slug = readProjectSlug(projectDir);
@@ -30755,7 +31192,7 @@ async function rebuildResourcesIndex(projectDir) {
30755
31192
  lines.push("| Name | Category | Source | Related Assignments | Updated |");
30756
31193
  lines.push("|------|----------|--------|---------------------|---------|");
30757
31194
  for (const fileName of files) {
30758
- const content = await readFile52(resolve75(dir, fileName), "utf-8");
31195
+ const content = await readFile54(resolve78(dir, fileName), "utf-8");
30759
31196
  const parsed = parseResource(content);
30760
31197
  const slugBase = fileName.replace(/\.md$/, "");
30761
31198
  const name = parsed.name || slugBase;
@@ -30765,12 +31202,12 @@ async function rebuildResourcesIndex(projectDir) {
30765
31202
  );
30766
31203
  }
30767
31204
  lines.push("");
30768
- const indexPath = resolve75(dir, "_index.md");
31205
+ const indexPath = resolve78(dir, "_index.md");
30769
31206
  await writeFileForce(indexPath, lines.join("\n"));
30770
31207
  return { total: files.length, path: indexPath };
30771
31208
  }
30772
31209
  async function rebuildMemoriesIndex(projectDir) {
30773
- const dir = resolve75(projectDir, "memories");
31210
+ const dir = resolve78(projectDir, "memories");
30774
31211
  await ensureDir(dir);
30775
31212
  const files = await listSlugFiles(dir);
30776
31213
  const slug = readProjectSlug(projectDir);
@@ -30786,7 +31223,7 @@ async function rebuildMemoriesIndex(projectDir) {
30786
31223
  lines.push("| Name | Source | Scope | Source Assignment | Updated |");
30787
31224
  lines.push("|------|--------|-------|-------------------|---------|");
30788
31225
  for (const fileName of files) {
30789
- const content = await readFile52(resolve75(dir, fileName), "utf-8");
31226
+ const content = await readFile54(resolve78(dir, fileName), "utf-8");
30790
31227
  const parsed = parseMemory(content);
30791
31228
  const slugBase = fileName.replace(/\.md$/, "");
30792
31229
  const name = parsed.name || slugBase;
@@ -30796,7 +31233,7 @@ async function rebuildMemoriesIndex(projectDir) {
30796
31233
  );
30797
31234
  }
30798
31235
  lines.push("");
30799
- const indexPath = resolve75(dir, "_index.md");
31236
+ const indexPath = resolve78(dir, "_index.md");
30800
31237
  await writeFileForce(indexPath, lines.join("\n"));
30801
31238
  return { total: files.length, path: indexPath };
30802
31239
  }
@@ -30834,8 +31271,8 @@ async function runResourceAdd(options) {
30834
31271
  if (!isValidSlug(options.project)) {
30835
31272
  throw new Error(`Invalid project slug: "${options.project}".`);
30836
31273
  }
30837
- const projectDir = resolve76(defaultProjectDir(), options.project);
30838
- if (!await fileExists(resolve76(projectDir, "project.md"))) {
31274
+ const projectDir = resolve79(defaultProjectDir(), options.project);
31275
+ if (!await fileExists(resolve79(projectDir, "project.md"))) {
30839
31276
  throw new Error(`Project "${options.project}" not found at ${projectDir}.`);
30840
31277
  }
30841
31278
  if (!options.name) throw new Error("--name is required.");
@@ -30844,7 +31281,7 @@ async function runResourceAdd(options) {
30844
31281
  if (!isValidSlug(slug)) {
30845
31282
  throw new Error(`Invalid resource slug: "${slug}".`);
30846
31283
  }
30847
- const filePath = resolve76(projectDir, "resources", `${slug}.md`);
31284
+ const filePath = resolve79(projectDir, "resources", `${slug}.md`);
30848
31285
  if (await fileExists(filePath) && !options.force) {
30849
31286
  throw new Error(
30850
31287
  `Resource "${slug}" already exists at ${filePath}. Use --force to overwrite.`
@@ -30879,7 +31316,7 @@ init_paths();
30879
31316
  init_fs();
30880
31317
  init_slug();
30881
31318
  import { Command as Command13 } from "commander";
30882
- import { resolve as resolve77 } from "path";
31319
+ import { resolve as resolve80 } from "path";
30883
31320
  function nowIso5() {
30884
31321
  return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
30885
31322
  }
@@ -30914,8 +31351,8 @@ async function runMemoryAdd(options) {
30914
31351
  if (!isValidSlug(options.project)) {
30915
31352
  throw new Error(`Invalid project slug: "${options.project}".`);
30916
31353
  }
30917
- const projectDir = resolve77(defaultProjectDir(), options.project);
30918
- if (!await fileExists(resolve77(projectDir, "project.md"))) {
31354
+ const projectDir = resolve80(defaultProjectDir(), options.project);
31355
+ if (!await fileExists(resolve80(projectDir, "project.md"))) {
30919
31356
  throw new Error(`Project "${options.project}" not found at ${projectDir}.`);
30920
31357
  }
30921
31358
  if (!options.name) throw new Error("--name is required.");
@@ -30924,7 +31361,7 @@ async function runMemoryAdd(options) {
30924
31361
  if (!isValidSlug(slug)) {
30925
31362
  throw new Error(`Invalid memory slug: "${slug}".`);
30926
31363
  }
30927
- const filePath = resolve77(projectDir, "memories", `${slug}.md`);
31364
+ const filePath = resolve80(projectDir, "memories", `${slug}.md`);
30928
31365
  if (await fileExists(filePath) && !options.force) {
30929
31366
  throw new Error(
30930
31367
  `Memory "${slug}" already exists at ${filePath}. Use --force to overwrite.`
@@ -30961,8 +31398,8 @@ init_paths();
30961
31398
  init_fs();
30962
31399
  init_frontmatter();
30963
31400
  import { Command as Command14 } from "commander";
30964
- import { readFile as readFile53 } from "fs/promises";
30965
- import { resolve as resolve78 } from "path";
31401
+ import { readFile as readFile55 } from "fs/promises";
31402
+ import { resolve as resolve81 } from "path";
30966
31403
  var AGE_PATTERN = /^(\d+)([dhwm])$/i;
30967
31404
  function parseAgeToCutoff(age) {
30968
31405
  const match = age.match(AGE_PATTERN);
@@ -30981,7 +31418,7 @@ function parseAgeToCutoff(age) {
30981
31418
  }
30982
31419
  function assignmentMdPath(item) {
30983
31420
  if (item.projectSlug) {
30984
- return resolve78(
31421
+ return resolve81(
30985
31422
  defaultProjectDir(),
30986
31423
  item.projectSlug,
30987
31424
  "assignments",
@@ -30989,13 +31426,13 @@ function assignmentMdPath(item) {
30989
31426
  "assignment.md"
30990
31427
  );
30991
31428
  }
30992
- return resolve78(assignmentsDir(), item.id, "assignment.md");
31429
+ return resolve81(assignmentsDir(), item.id, "assignment.md");
30993
31430
  }
30994
31431
  async function loadTags(item) {
30995
31432
  const path = assignmentMdPath(item);
30996
31433
  if (!await fileExists(path)) return [];
30997
31434
  try {
30998
- const content = await readFile53(path, "utf-8");
31435
+ const content = await readFile55(path, "utf-8");
30999
31436
  return parseAssignmentFrontmatter(content).tags;
31000
31437
  } catch {
31001
31438
  return [];
@@ -31467,7 +31904,8 @@ function spliceDashDashFromArgv(argv) {
31467
31904
  // src/index.ts
31468
31905
  {
31469
31906
  const sub = process.argv[2];
31470
- if (sub !== "update" && sub !== "upgrade") {
31907
+ const isDryRunSetup = sub === "setup" && process.argv.slice(3).includes("--dry-run");
31908
+ if (sub !== "update" && sub !== "upgrade" && !isDryRunSetup) {
31471
31909
  await maybePromptInstall(import.meta.url);
31472
31910
  await maybeNudgeForNpxInstall(import.meta.url);
31473
31911
  }
@@ -31712,7 +32150,7 @@ program.command("reopen").description("Reopen a completed or failed assignment")
31712
32150
  process.exit(1);
31713
32151
  }
31714
32152
  });
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) => {
32153
+ 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
32154
  try {
31717
32155
  await setupCommand(options);
31718
32156
  } catch (error) {