syntaur 0.23.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -107,6 +107,9 @@ interface IntegrationConfig {
107
107
  claudePluginDir: string | null;
108
108
  codexPluginDir: string | null;
109
109
  codexMarketplacePath: string | null;
110
+ installedAgents?: Record<string, {
111
+ scope: 'project' | 'global';
112
+ }>;
110
113
  }
111
114
  interface OnboardingConfig {
112
115
  completed: boolean;
@@ -45,7 +45,7 @@ var init_paths = __esm({
45
45
  });
46
46
 
47
47
  // src/utils/fs.ts
48
- import { mkdir, writeFile, access, rename } from "fs/promises";
48
+ import { mkdir, writeFile, readFile, access, rename } from "fs/promises";
49
49
  import { dirname, join } from "path";
50
50
  async function ensureDir(dir) {
51
51
  await mkdir(dir, { recursive: true });
@@ -92,7 +92,7 @@ var init_timestamp = __esm({
92
92
  });
93
93
 
94
94
  // src/utils/fs-migration.ts
95
- import { readdir, readFile, rename as rename2, writeFile as writeFile2 } from "fs/promises";
95
+ import { readdir, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "fs/promises";
96
96
  import { resolve as resolve2 } from "path";
97
97
  async function migrateLegacyProjectFiles(projectsDir) {
98
98
  const result = {
@@ -161,7 +161,7 @@ async function migrateLegacyArchivedProjects(projectsDir) {
161
161
  const projectMd = resolve2(projectsDir, entry.name, "project.md");
162
162
  try {
163
163
  if (!await fileExists(projectMd)) continue;
164
- const content = await readFile(projectMd, "utf-8");
164
+ const content = await readFile2(projectMd, "utf-8");
165
165
  if (readFrontmatterField(content, "statusOverride") !== "archived") continue;
166
166
  let next = setFrontmatterField(content, "archived", true);
167
167
  if (readFrontmatterField(content, "archivedAt") === null) {
@@ -186,7 +186,7 @@ async function migrateLegacyConfig(configPath) {
186
186
  if (!await fileExists(configPath)) return result;
187
187
  let content;
188
188
  try {
189
- content = await readFile(configPath, "utf-8");
189
+ content = await readFile2(configPath, "utf-8");
190
190
  } catch {
191
191
  return result;
192
192
  }
@@ -338,7 +338,7 @@ var init_agents_schema = __esm({
338
338
  });
339
339
 
340
340
  // src/utils/config.ts
341
- import { readFile as readFile2 } from "fs/promises";
341
+ import { readFile as readFile3 } from "fs/promises";
342
342
  import { spawnSync } from "child_process";
343
343
  import { resolve as resolve3, isAbsolute } from "path";
344
344
  function parseAgentCommand(value, agentId) {
@@ -466,6 +466,18 @@ function parseFrontmatter(content) {
466
466
  }
467
467
  return result;
468
468
  }
469
+ function parseInstalledAgents(fm) {
470
+ const prefix = "integrations.installedAgents.";
471
+ const installedAgents = {};
472
+ for (const [key, value] of Object.entries(fm)) {
473
+ if (!key.startsWith(prefix)) continue;
474
+ const id = key.slice(prefix.length);
475
+ if (!id) continue;
476
+ const scope = value === "project" ? "project" : "global";
477
+ installedAgents[id] = { scope };
478
+ }
479
+ return Object.keys(installedAgents).length > 0 ? { installedAgents } : {};
480
+ }
469
481
  function parseStatusConfig(content) {
470
482
  const match = content.match(/^---\n([\s\S]*?)\n---/);
471
483
  if (!match) return null;
@@ -880,7 +892,7 @@ async function readConfig() {
880
892
  migratedConfigPaths.add(configPath);
881
893
  await migrateLegacyConfig(configPath);
882
894
  }
883
- const content = await readFile2(configPath, "utf-8");
895
+ const content = await readFile3(configPath, "utf-8");
884
896
  const fm = parseFrontmatter(content);
885
897
  if (Object.keys(fm).length === 0) {
886
898
  console.warn("Warning: ~/.syntaur/config.md has malformed frontmatter, using defaults");
@@ -919,7 +931,8 @@ async function readConfig() {
919
931
  codexMarketplacePath: parseOptionalAbsolutePath(
920
932
  fm["integrations.codexMarketplacePath"],
921
933
  "integrations.codexMarketplacePath"
922
- )
934
+ ),
935
+ ...parseInstalledAgents(fm)
923
936
  },
924
937
  backup: fm["backup.repo"] || fm["backup.categories"] ? {
925
938
  repo: fm["backup.repo"] && fm["backup.repo"] !== "null" ? fm["backup.repo"] : null,
@@ -1304,7 +1317,7 @@ var init_parser = __esm({
1304
1317
 
1305
1318
  // src/utils/assignment-resolver.ts
1306
1319
  import { resolve as resolve4 } from "path";
1307
- import { readdir as readdir2, readFile as readFile3 } from "fs/promises";
1320
+ import { readdir as readdir2, readFile as readFile4 } from "fs/promises";
1308
1321
  async function resolveAssignmentById(projectsDir, assignmentsDir, id) {
1309
1322
  let standaloneMatch = null;
1310
1323
  let projectMatch = null;
@@ -1313,7 +1326,7 @@ async function resolveAssignmentById(projectsDir, assignmentsDir, id) {
1313
1326
  if (await fileExists(standalonePath)) {
1314
1327
  let workspaceGroup = null;
1315
1328
  try {
1316
- const content = await readFile3(standalonePath, "utf-8");
1329
+ const content = await readFile4(standalonePath, "utf-8");
1317
1330
  const [fm] = extractFrontmatter(content);
1318
1331
  workspaceGroup = getField(fm, "workspaceGroup");
1319
1332
  } catch {
@@ -1341,7 +1354,7 @@ async function resolveAssignmentById(projectsDir, assignmentsDir, id) {
1341
1354
  const aPath = resolve4(assignmentsPath, a.name, "assignment.md");
1342
1355
  if (!await fileExists(aPath)) continue;
1343
1356
  try {
1344
- const content = await readFile3(aPath, "utf-8");
1357
+ const content = await readFile4(aPath, "utf-8");
1345
1358
  const [fm] = extractFrontmatter(content);
1346
1359
  const fileId = getField(fm, "id");
1347
1360
  if (fileId === id) {
@@ -1460,7 +1473,7 @@ var init_frontmatter = __esm({
1460
1473
 
1461
1474
  // src/todos/parser.ts
1462
1475
  import { randomBytes } from "crypto";
1463
- import { readFile as readFile4 } from "fs/promises";
1476
+ import { readFile as readFile5 } from "fs/promises";
1464
1477
  import { resolve as resolve5 } from "path";
1465
1478
  var init_parser2 = __esm({
1466
1479
  "src/todos/parser.ts"() {
@@ -1483,7 +1496,7 @@ var init_linked_todos = __esm({
1483
1496
 
1484
1497
  // src/lifecycle/transitions.ts
1485
1498
  import { resolve as resolve7 } from "path";
1486
- import { readFile as readFile5 } from "fs/promises";
1499
+ import { readFile as readFile6 } from "fs/promises";
1487
1500
  var init_transitions = __esm({
1488
1501
  "src/lifecycle/transitions.ts"() {
1489
1502
  "use strict";
@@ -1515,7 +1528,7 @@ var init_slug = __esm({
1515
1528
 
1516
1529
  // src/utils/playbooks.ts
1517
1530
  import { resolve as resolve8 } from "path";
1518
- import { readdir as readdir4, readFile as readFile6, unlink } from "fs/promises";
1531
+ import { readdir as readdir4, readFile as readFile7, unlink } from "fs/promises";
1519
1532
  var init_playbooks = __esm({
1520
1533
  "src/utils/playbooks.ts"() {
1521
1534
  "use strict";
@@ -1741,7 +1754,7 @@ var init_session_db = __esm({
1741
1754
  });
1742
1755
 
1743
1756
  // src/dashboard/agent-sessions.ts
1744
- import { readFile as readFile7 } from "fs/promises";
1757
+ import { readFile as readFile8 } from "fs/promises";
1745
1758
  import { resolve as resolve10 } from "path";
1746
1759
  function rowToSession(row) {
1747
1760
  return {
@@ -1780,7 +1793,7 @@ var init_overviewCopy = __esm({
1780
1793
  });
1781
1794
 
1782
1795
  // src/dashboard/api.ts
1783
- import { readdir as readdir6, readFile as readFile8, writeFile as writeFile3 } from "fs/promises";
1796
+ import { readdir as readdir6, readFile as readFile9, writeFile as writeFile3 } from "fs/promises";
1784
1797
  import { resolve as resolve11, dirname as dirname2, basename } from "path";
1785
1798
  function activeAssignments(items) {
1786
1799
  return items.filter((item) => item.archived !== true);
@@ -1809,7 +1822,7 @@ async function computeStandaloneRecords(assignmentsDir) {
1809
1822
  const assignmentMdPath = resolve11(assignmentDir, "assignment.md");
1810
1823
  if (!await fileExists(assignmentMdPath)) continue;
1811
1824
  try {
1812
- const content = await readFile8(assignmentMdPath, "utf-8");
1825
+ const content = await readFile9(assignmentMdPath, "utf-8");
1813
1826
  const record = parseAssignmentFull(content);
1814
1827
  records.push({ assignmentDir, id: entry.name, record });
1815
1828
  } catch {
@@ -1905,18 +1918,18 @@ async function getAssignmentDetail(projectsDir, projectSlug, assignmentSlug) {
1905
1918
  if (!await fileExists(assignmentMdPath)) {
1906
1919
  return null;
1907
1920
  }
1908
- const assignmentContent = await readFile8(assignmentMdPath, "utf-8");
1921
+ const assignmentContent = await readFile9(assignmentMdPath, "utf-8");
1909
1922
  const assignment = parseAssignmentFull(assignmentContent);
1910
1923
  let projectWorkspace = null;
1911
1924
  const projectMdPath = resolve11(projectsDir, projectSlug, "project.md");
1912
1925
  if (await fileExists(projectMdPath)) {
1913
- const projectContent = await readFile8(projectMdPath, "utf-8");
1926
+ const projectContent = await readFile9(projectMdPath, "utf-8");
1914
1927
  projectWorkspace = parseProject(projectContent).workspace;
1915
1928
  }
1916
1929
  let plan = null;
1917
1930
  const planPath = resolve11(assignmentDir, "plan.md");
1918
1931
  if (await fileExists(planPath)) {
1919
- const planContent = await readFile8(planPath, "utf-8");
1932
+ const planContent = await readFile9(planPath, "utf-8");
1920
1933
  const parsed = parsePlan(planContent);
1921
1934
  plan = {
1922
1935
  status: parsed.status,
@@ -1927,7 +1940,7 @@ async function getAssignmentDetail(projectsDir, projectSlug, assignmentSlug) {
1927
1940
  let scratchpad = null;
1928
1941
  const scratchpadPath = resolve11(assignmentDir, "scratchpad.md");
1929
1942
  if (await fileExists(scratchpadPath)) {
1930
- const scratchpadContent = await readFile8(scratchpadPath, "utf-8");
1943
+ const scratchpadContent = await readFile9(scratchpadPath, "utf-8");
1931
1944
  const parsed = parseScratchpad(scratchpadContent);
1932
1945
  scratchpad = {
1933
1946
  updated: parsed.updated,
@@ -1937,7 +1950,7 @@ async function getAssignmentDetail(projectsDir, projectSlug, assignmentSlug) {
1937
1950
  let handoff = null;
1938
1951
  const handoffPath = resolve11(assignmentDir, "handoff.md");
1939
1952
  if (await fileExists(handoffPath)) {
1940
- const handoffContent = await readFile8(handoffPath, "utf-8");
1953
+ const handoffContent = await readFile9(handoffPath, "utf-8");
1941
1954
  const parsed = parseHandoff(handoffContent);
1942
1955
  handoff = {
1943
1956
  updated: parsed.updated,
@@ -1948,7 +1961,7 @@ async function getAssignmentDetail(projectsDir, projectSlug, assignmentSlug) {
1948
1961
  let decisionRecord = null;
1949
1962
  const decisionRecordPath = resolve11(assignmentDir, "decision-record.md");
1950
1963
  if (await fileExists(decisionRecordPath)) {
1951
- const decisionRecordContent = await readFile8(decisionRecordPath, "utf-8");
1964
+ const decisionRecordContent = await readFile9(decisionRecordPath, "utf-8");
1952
1965
  const parsed = parseDecisionRecord(decisionRecordContent);
1953
1966
  decisionRecord = {
1954
1967
  updated: parsed.updated,
@@ -1959,7 +1972,7 @@ async function getAssignmentDetail(projectsDir, projectSlug, assignmentSlug) {
1959
1972
  let progress = null;
1960
1973
  const progressPath = resolve11(assignmentDir, "progress.md");
1961
1974
  if (await fileExists(progressPath)) {
1962
- const progressContent = await readFile8(progressPath, "utf-8");
1975
+ const progressContent = await readFile9(progressPath, "utf-8");
1963
1976
  const parsed = parseProgress(progressContent);
1964
1977
  progress = {
1965
1978
  updated: parsed.updated,
@@ -1970,7 +1983,7 @@ async function getAssignmentDetail(projectsDir, projectSlug, assignmentSlug) {
1970
1983
  let comments = null;
1971
1984
  const commentsPath = resolve11(assignmentDir, "comments.md");
1972
1985
  if (await fileExists(commentsPath)) {
1973
- const commentsContent = await readFile8(commentsPath, "utf-8");
1986
+ const commentsContent = await readFile9(commentsPath, "utf-8");
1974
1987
  const parsed = parseComments(commentsContent);
1975
1988
  comments = {
1976
1989
  updated: parsed.updated,
@@ -2124,7 +2137,7 @@ async function countMentionsInAssignment(sourceDir, target) {
2124
2137
  const bodies = [];
2125
2138
  const assignmentMd = resolve11(sourceDir, "assignment.md");
2126
2139
  if (await fileExists(assignmentMd)) {
2127
- const content = await readFile8(assignmentMd, "utf-8");
2140
+ const content = await readFile9(assignmentMd, "utf-8");
2128
2141
  const todosMatch = content.match(/^## Todos\s*$([\s\S]*?)(?=^## |$(?![\r\n]))/m);
2129
2142
  if (todosMatch) bodies.push(todosMatch[1]);
2130
2143
  }
@@ -2132,7 +2145,7 @@ async function countMentionsInAssignment(sourceDir, target) {
2132
2145
  const path = resolve11(sourceDir, filename);
2133
2146
  if (await fileExists(path)) {
2134
2147
  try {
2135
- bodies.push(await readFile8(path, "utf-8"));
2148
+ bodies.push(await readFile9(path, "utf-8"));
2136
2149
  } catch {
2137
2150
  }
2138
2151
  }
@@ -2192,42 +2205,42 @@ async function buildStandaloneAssignmentDetail(resolved) {
2192
2205
  const assignmentDir = resolved.assignmentDir;
2193
2206
  const assignmentMdPath = resolve11(assignmentDir, "assignment.md");
2194
2207
  if (!await fileExists(assignmentMdPath)) return null;
2195
- const assignmentContent = await readFile8(assignmentMdPath, "utf-8");
2208
+ const assignmentContent = await readFile9(assignmentMdPath, "utf-8");
2196
2209
  const assignment = parseAssignmentFull(assignmentContent);
2197
2210
  let plan = null;
2198
2211
  const planPath = resolve11(assignmentDir, "plan.md");
2199
2212
  if (await fileExists(planPath)) {
2200
- const parsed = parsePlan(await readFile8(planPath, "utf-8"));
2213
+ const parsed = parsePlan(await readFile9(planPath, "utf-8"));
2201
2214
  plan = { status: parsed.status, updated: parsed.updated, body: parsed.body };
2202
2215
  }
2203
2216
  let scratchpad = null;
2204
2217
  const scratchpadPath = resolve11(assignmentDir, "scratchpad.md");
2205
2218
  if (await fileExists(scratchpadPath)) {
2206
- const parsed = parseScratchpad(await readFile8(scratchpadPath, "utf-8"));
2219
+ const parsed = parseScratchpad(await readFile9(scratchpadPath, "utf-8"));
2207
2220
  scratchpad = { updated: parsed.updated, body: parsed.body };
2208
2221
  }
2209
2222
  let handoff = null;
2210
2223
  const handoffPath = resolve11(assignmentDir, "handoff.md");
2211
2224
  if (await fileExists(handoffPath)) {
2212
- const parsed = parseHandoff(await readFile8(handoffPath, "utf-8"));
2225
+ const parsed = parseHandoff(await readFile9(handoffPath, "utf-8"));
2213
2226
  handoff = { updated: parsed.updated, handoffCount: parsed.handoffCount, body: parsed.body };
2214
2227
  }
2215
2228
  let decisionRecord = null;
2216
2229
  const decisionRecordPath = resolve11(assignmentDir, "decision-record.md");
2217
2230
  if (await fileExists(decisionRecordPath)) {
2218
- const parsed = parseDecisionRecord(await readFile8(decisionRecordPath, "utf-8"));
2231
+ const parsed = parseDecisionRecord(await readFile9(decisionRecordPath, "utf-8"));
2219
2232
  decisionRecord = { updated: parsed.updated, decisionCount: parsed.decisionCount, body: parsed.body };
2220
2233
  }
2221
2234
  let progress = null;
2222
2235
  const progressPath = resolve11(assignmentDir, "progress.md");
2223
2236
  if (await fileExists(progressPath)) {
2224
- const parsed = parseProgress(await readFile8(progressPath, "utf-8"));
2237
+ const parsed = parseProgress(await readFile9(progressPath, "utf-8"));
2225
2238
  progress = { updated: parsed.updated, entryCount: parsed.entryCount, entries: parsed.entries };
2226
2239
  }
2227
2240
  let comments = null;
2228
2241
  const commentsPath = resolve11(assignmentDir, "comments.md");
2229
2242
  if (await fileExists(commentsPath)) {
2230
- const parsed = parseComments(await readFile8(commentsPath, "utf-8"));
2243
+ const parsed = parseComments(await readFile9(commentsPath, "utf-8"));
2231
2244
  comments = { updated: parsed.updated, entryCount: parsed.entryCount, entries: parsed.entries };
2232
2245
  }
2233
2246
  const detail = {
@@ -2293,7 +2306,7 @@ async function computeProjectRecords(projectsDir, traces) {
2293
2306
  return null;
2294
2307
  }
2295
2308
  const t0 = traces ? performance.now() : 0;
2296
- const projectContent = await readFile8(projectMdPath, "utf-8");
2309
+ const projectContent = await readFile9(projectMdPath, "utf-8");
2297
2310
  const project = parseProject(projectContent);
2298
2311
  if (traces) accumulatePhase(traces, "parse-project-md", performance.now() - t0);
2299
2312
  const t1 = traces ? performance.now() : 0;
@@ -2347,7 +2360,7 @@ async function listAssignmentRecords(projectPath, traces) {
2347
2360
  return null;
2348
2361
  }
2349
2362
  const t0 = traces ? performance.now() : 0;
2350
- const content = await readFile8(assignmentMd, "utf-8");
2363
+ const content = await readFile9(assignmentMd, "utf-8");
2351
2364
  const parsed = parseAssignmentFull(content);
2352
2365
  if (traces) accumulatePhase(traces, "read-assignment-md", performance.now() - t0);
2353
2366
  return parsed;
@@ -2360,7 +2373,7 @@ async function listAssignmentRecords(projectPath, traces) {
2360
2373
  async function loadDependencyGraph(projectPath, assignments) {
2361
2374
  const statusPath = resolve11(projectPath, "_status.md");
2362
2375
  if (await fileExists(statusPath)) {
2363
- const statusContent = await readFile8(statusPath, "utf-8");
2376
+ const statusContent = await readFile9(statusPath, "utf-8");
2364
2377
  const parsed = parseStatus(statusContent);
2365
2378
  const derivedGraph = extractMermaidGraph(parsed.body);
2366
2379
  if (derivedGraph) {
@@ -2493,7 +2506,7 @@ async function getUnmetDependencies(projectPath, dependsOn, terminalStatuses, de
2493
2506
  unmet.push(`${dependency} (missing)`);
2494
2507
  continue;
2495
2508
  }
2496
- const content = await readFile8(dependencyPath, "utf-8");
2509
+ const content = await readFile9(dependencyPath, "utf-8");
2497
2510
  const parsed = parseAssignmentFull(content);
2498
2511
  if (!terminals.has(parsed.status)) {
2499
2512
  unmet.push(`${dependency} (${parsed.status})`);
@@ -2519,7 +2532,7 @@ async function countOpenQuestions(projectPath, assignmentSlug) {
2519
2532
  return 0;
2520
2533
  }
2521
2534
  try {
2522
- const content = await readFile8(commentsPath, "utf-8");
2535
+ const content = await readFile9(commentsPath, "utf-8");
2523
2536
  const parsed = parseComments(content);
2524
2537
  return parsed.entries.filter(
2525
2538
  (e) => e.type === "question" && e.resolved !== true
@@ -3224,7 +3237,7 @@ function normalizeSlashes(p) {
3224
3237
  }
3225
3238
  function detectInstallKind(scriptUrl, opts = {}) {
3226
3239
  const realpath = opts.realpath ?? realpathSync.native;
3227
- const readFile9 = opts.readFile ?? ((p) => readFileSync(p, "utf-8"));
3240
+ const readFile10 = opts.readFile ?? ((p) => readFileSync(p, "utf-8"));
3228
3241
  const ua = opts.envUserAgent !== void 0 ? opts.envUserAgent : process.env.npm_config_user_agent ?? "";
3229
3242
  const resolved = resolveScriptPath(scriptUrl, realpath);
3230
3243
  if (resolved === null) {
@@ -3245,7 +3258,7 @@ function detectInstallKind(scriptUrl, opts = {}) {
3245
3258
  const pkgJsonPath = join2(dir, "package.json");
3246
3259
  let raw;
3247
3260
  try {
3248
- raw = readFile9(pkgJsonPath);
3261
+ raw = readFile10(pkgJsonPath);
3249
3262
  } catch {
3250
3263
  const parent2 = dirname3(dir);
3251
3264
  if (parent2 === dir) break;