syntaur 0.74.0 → 0.76.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.
Files changed (69) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dashboard/dist/assets/{_basePickBy-CBNQaqzq.js → _basePickBy-BiwSunXO.js} +1 -1
  3. package/dashboard/dist/assets/{_baseUniq-Bqwz8hnN.js → _baseUniq-DsjKPXxP.js} +1 -1
  4. package/dashboard/dist/assets/{arc-B6lVW0Fc.js → arc-DlEs4wsF.js} +1 -1
  5. package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-eKpHkGki.js → architectureDiagram-2XIMDMQ5-DPvCrO57.js} +1 -1
  6. package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-D3sd7ccQ.js → blockDiagram-WCTKOSBZ-CpNdCY-r.js} +1 -1
  7. package/dashboard/dist/assets/{c4Diagram-IC4MRINW-0qW21hVQ.js → c4Diagram-IC4MRINW-CC29QJCs.js} +1 -1
  8. package/dashboard/dist/assets/channel-IutbH48W.js +1 -0
  9. package/dashboard/dist/assets/{chunk-4BX2VUAB-tDdoFSYw.js → chunk-4BX2VUAB-DKyy-k2D.js} +1 -1
  10. package/dashboard/dist/assets/{chunk-55IACEB6-BU45eS64.js → chunk-55IACEB6-DjobngQk.js} +1 -1
  11. package/dashboard/dist/assets/{chunk-FMBD7UC4-C0iT8QWM.js → chunk-FMBD7UC4-BUftm7dV.js} +1 -1
  12. package/dashboard/dist/assets/{chunk-JSJVCQXG-CELv9Y6Y.js → chunk-JSJVCQXG-Nv7rQv4t.js} +1 -1
  13. package/dashboard/dist/assets/{chunk-KX2RTZJC-CQA_hH8g.js → chunk-KX2RTZJC-Dg6UhNB9.js} +1 -1
  14. package/dashboard/dist/assets/{chunk-NQ4KR5QH-BDv-d6qz.js → chunk-NQ4KR5QH-D8neQrJ_.js} +1 -1
  15. package/dashboard/dist/assets/{chunk-QZHKN3VN-BUOeJd34.js → chunk-QZHKN3VN-Uj9c1naA.js} +1 -1
  16. package/dashboard/dist/assets/{chunk-WL4C6EOR-DBc-Hsx0.js → chunk-WL4C6EOR-DfeSsP3z.js} +1 -1
  17. package/dashboard/dist/assets/classDiagram-VBA2DB6C-8IywRj9I.js +1 -0
  18. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-8IywRj9I.js +1 -0
  19. package/dashboard/dist/assets/clone-C6saGiCd.js +1 -0
  20. package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-C_Q4c3xo.js → cose-bilkent-S5V4N54A-CLfZNKcO.js} +1 -1
  21. package/dashboard/dist/assets/{dagre-KLK3FWXG-DdI10bBF.js → dagre-KLK3FWXG-DyJLICZx.js} +1 -1
  22. package/dashboard/dist/assets/{diagram-E7M64L7V-DtCF4Ea9.js → diagram-E7M64L7V-DhQc7Ta3.js} +1 -1
  23. package/dashboard/dist/assets/{diagram-IFDJBPK2-BVmpBmIK.js → diagram-IFDJBPK2-C6kFO7hB.js} +1 -1
  24. package/dashboard/dist/assets/{diagram-P4PSJMXO-BH4XFc0W.js → diagram-P4PSJMXO-B6oCiOXk.js} +1 -1
  25. package/dashboard/dist/assets/{erDiagram-INFDFZHY-CCjG3Ewp.js → erDiagram-INFDFZHY-BsMPaSwJ.js} +1 -1
  26. package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-V0PT07-Y.js → flowDiagram-PKNHOUZH-DLxEqKtZ.js} +1 -1
  27. package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-y_85pJZf.js → ganttDiagram-A5KZAMGK-D70pvqxG.js} +1 -1
  28. package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-B2maPPcL.js → gitGraphDiagram-K3NZZRJ6-D74VHtlE.js} +1 -1
  29. package/dashboard/dist/assets/{graph-DO1oCTrt.js → graph-C8bz_IEO.js} +1 -1
  30. package/dashboard/dist/assets/{index-BZwPAi8K.css → index-BetqH_3H.css} +1 -1
  31. package/dashboard/dist/assets/index-h-X_8om3.js +675 -0
  32. package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-BIaqpTlK.js → infoDiagram-LFFYTUFH-DivBd0Pz.js} +1 -1
  33. package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-BaBqTUzg.js → ishikawaDiagram-PHBUUO56-BGihUeOE.js} +1 -1
  34. package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-CS2l7gmE.js → journeyDiagram-4ABVD52K-C7U4cWHQ.js} +1 -1
  35. package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-B3Viag1o.js → kanban-definition-K7BYSVSG-CFp_hDTm.js} +1 -1
  36. package/dashboard/dist/assets/{layout-BQu5nxr5.js → layout-DMSDPqCs.js} +1 -1
  37. package/dashboard/dist/assets/{linear-DliLQN5_.js → linear-CCC-meYc.js} +1 -1
  38. package/dashboard/dist/assets/{mermaid.core-y_G2N_4o.js → mermaid.core-BDa2cc2K.js} +4 -4
  39. package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-EZz-bEA_.js → mindmap-definition-YRQLILUH-DLKvONhO.js} +1 -1
  40. package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-E67ZAs-E.js → pieDiagram-SKSYHLDU-DAJoGZ93.js} +1 -1
  41. package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-GU25jLyr.js → quadrantDiagram-337W2JSQ-Bo7haX1W.js} +1 -1
  42. package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-4Jfq0mB9.js → requirementDiagram-Z7DCOOCP-uEvGz33v.js} +1 -1
  43. package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-C22SY4ul.js → sankeyDiagram-WA2Y5GQK-cjYoCGCM.js} +1 -1
  44. package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-CkBh7V9t.js → sequenceDiagram-2WXFIKYE-CYXcNklt.js} +1 -1
  45. package/dashboard/dist/assets/{stateDiagram-RAJIS63D-n4DRlbfF.js → stateDiagram-RAJIS63D-B5We7jAw.js} +1 -1
  46. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-CX9-EpBi.js +1 -0
  47. package/dashboard/dist/assets/{timeline-definition-YZTLITO2-CGD5ojK7.js → timeline-definition-YZTLITO2-CEqVmnVQ.js} +1 -1
  48. package/dashboard/dist/assets/{treemap-KZPCXAKY-EZfHxNz6.js → treemap-KZPCXAKY-CE1puPeN.js} +1 -1
  49. package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-B9x6fx-l.js → vennDiagram-LZ73GAT5-CiiuRUjK.js} +1 -1
  50. package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-ufzpkH1I.js → xychartDiagram-JWTSCODW-B0OuxFm9.js} +1 -1
  51. package/dashboard/dist/index.html +2 -2
  52. package/dist/dashboard/server.js +509 -221
  53. package/dist/dashboard/server.js.map +1 -1
  54. package/dist/index.js +1210 -823
  55. package/dist/index.js.map +1 -1
  56. package/dist/launch/index.d.ts +69 -7
  57. package/dist/launch/index.js +229 -24
  58. package/dist/launch/index.js.map +1 -1
  59. package/package.json +1 -1
  60. package/platforms/claude-code/.claude-plugin/plugin.json +1 -1
  61. package/platforms/codex/.codex-plugin/plugin.json +1 -1
  62. package/platforms/hermes/plugins/syntaur/__pycache__/__init__.cpython-312.pyc +0 -0
  63. package/platforms/hermes/plugins/syntaur/__pycache__/boundary.cpython-312.pyc +0 -0
  64. package/dashboard/dist/assets/channel-BDsImlpM.js +0 -1
  65. package/dashboard/dist/assets/classDiagram-VBA2DB6C-BhdNnCQ2.js +0 -1
  66. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-BhdNnCQ2.js +0 -1
  67. package/dashboard/dist/assets/clone-B5RwQsX7.js +0 -1
  68. package/dashboard/dist/assets/index-CG8rQF5o.js +0 -670
  69. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-RpTmT3-9.js +0 -1
package/dist/index.js CHANGED
@@ -2643,6 +2643,10 @@ function modelFlagArgs(agent) {
2643
2643
  const m = agent.model?.trim();
2644
2644
  return m ? ["--model", m] : [];
2645
2645
  }
2646
+ function agentNameArgs(agent) {
2647
+ const n = agent.agentName?.trim();
2648
+ return n ? ["--agent", n] : [];
2649
+ }
2646
2650
  function stripModelFlags(args) {
2647
2651
  const out = [];
2648
2652
  for (let i = 0; i < args.length; i++) {
@@ -3998,6 +4002,26 @@ function validateAgentList(agents) {
3998
4002
  `agent "${agent.id}" has invalid launchPrompt \u2014 must be a single line (no newlines)`
3999
4003
  );
4000
4004
  }
4005
+ if (agent.agentName !== void 0 && /[\r\n]/.test(agent.agentName)) {
4006
+ throw new AgentConfigError(
4007
+ `agent "${agent.id}" has invalid agentName \u2014 must be a single line (no newlines)`
4008
+ );
4009
+ }
4010
+ if (agent.workdir !== void 0 && /[\r\n]/.test(agent.workdir)) {
4011
+ throw new AgentConfigError(
4012
+ `agent "${agent.id}" has invalid workdir \u2014 must be a single line (no newlines)`
4013
+ );
4014
+ }
4015
+ if (agent.agentName !== void 0 && agent.agentName.trim() !== "" && agent.workdir !== void 0 && agent.workdir.trim() !== "") {
4016
+ throw new AgentConfigError(
4017
+ `agent "${agent.id}" sets both agentName and workdir \u2014 these are mutually exclusive`
4018
+ );
4019
+ }
4020
+ if (agent.agentName !== void 0 && agent.agentName.trim() !== "" && agent.model !== void 0 && agent.model.trim() !== "") {
4021
+ throw new AgentConfigError(
4022
+ `agent "${agent.id}" sets both agentName and model \u2014 the agent definition's own model is authoritative; remove the profile model`
4023
+ );
4024
+ }
4001
4025
  validateSessionInvocation(agent, "resume", agent.resume);
4002
4026
  validateSessionInvocation(agent, "fork", agent.fork);
4003
4027
  if (agent.default) defaults++;
@@ -4818,6 +4842,8 @@ function parseAgentsConfig(content) {
4818
4842
  ...current.model ? { model: current.model } : {},
4819
4843
  ...current.playbook ? { playbook: current.playbook } : {},
4820
4844
  ...current.launchPrompt ? { launchPrompt: current.launchPrompt } : {},
4845
+ ...current.agentName ? { agentName: current.agentName } : {},
4846
+ ...current.workdir ? { workdir: current.workdir } : {},
4821
4847
  ...current.resume ? { resume: current.resume } : {},
4822
4848
  ...current.fork ? { fork: current.fork } : {}
4823
4849
  });
@@ -5009,6 +5035,12 @@ function assignAgentField(target, key, rawValue) {
5009
5035
  case "launchPrompt":
5010
5036
  target.launchPrompt = value;
5011
5037
  break;
5038
+ case "agentName":
5039
+ target.agentName = value;
5040
+ break;
5041
+ case "workdir":
5042
+ target.workdir = value;
5043
+ break;
5012
5044
  }
5013
5045
  }
5014
5046
  function yamlQuoteScalar(value) {
@@ -5038,6 +5070,12 @@ function serializeAgentsConfig(agents) {
5038
5070
  if (a.launchPrompt) {
5039
5071
  lines.push(` launchPrompt: ${yamlQuoteScalar(a.launchPrompt)}`);
5040
5072
  }
5073
+ if (a.agentName) {
5074
+ lines.push(` agentName: ${yamlQuoteScalar(a.agentName)}`);
5075
+ }
5076
+ if (a.workdir) {
5077
+ lines.push(` workdir: ${yamlQuoteScalar(a.workdir)}`);
5078
+ }
5041
5079
  if (a.args && a.args.length > 0) {
5042
5080
  lines.push(` args:`);
5043
5081
  for (const arg of a.args) {
@@ -5685,7 +5723,9 @@ var init_config2 = __esm({
5685
5723
  "resolveFromShellAliases",
5686
5724
  "model",
5687
5725
  "playbook",
5688
- "launchPrompt"
5726
+ "launchPrompt",
5727
+ "agentName",
5728
+ "workdir"
5689
5729
  ]);
5690
5730
  migratedConfigPaths = /* @__PURE__ */ new Set();
5691
5731
  TerminalConfigError = class extends Error {
@@ -7269,6 +7309,21 @@ function resolveWorkspaceCwd(input4) {
7269
7309
  invalidReason: `workspace path invalid for ${assignmentSlug}: tried worktreePath ${shown(worktreePath)} and repository ${shown(repository)} \u2014 neither is an existing directory`
7270
7310
  };
7271
7311
  }
7312
+ function resolveLaunchCwd(agent, worktreeCwd) {
7313
+ const wd = agent.workdir?.trim();
7314
+ if (wd) {
7315
+ const expanded = expandHome(wd);
7316
+ if (!isExistingDir(expanded)) {
7317
+ return {
7318
+ spawnCwd: worktreeCwd,
7319
+ worktreePath: worktreeCwd,
7320
+ invalidReason: `agent "${agent.id}" workdir ${agent.workdir} (resolved ${expanded}) is not an existing directory`
7321
+ };
7322
+ }
7323
+ return { spawnCwd: expanded, worktreePath: worktreeCwd, invalidReason: null };
7324
+ }
7325
+ return { spawnCwd: worktreeCwd, worktreePath: worktreeCwd, invalidReason: null };
7326
+ }
7272
7327
  function formatFallbackCwdWarning(opts) {
7273
7328
  const missing = [];
7274
7329
  if (!opts.worktreePath) missing.push("worktreePath");
@@ -7280,6 +7335,7 @@ function formatFallbackCwdWarning(opts) {
7280
7335
  var init_cwd = __esm({
7281
7336
  "src/launch/cwd.ts"() {
7282
7337
  "use strict";
7338
+ init_paths();
7283
7339
  }
7284
7340
  });
7285
7341
 
@@ -9220,8 +9276,8 @@ async function migrateFromMarkdown(projectsDir2) {
9220
9276
  return allSessions.length;
9221
9277
  }
9222
9278
  async function parseMarkdownSessionsIndex(filePath, projectSlug) {
9223
- const { readFile: readFile81 } = await import("fs/promises");
9224
- const raw2 = await readFile81(filePath, "utf-8");
9279
+ const { readFile: readFile82 } = await import("fs/promises");
9280
+ const raw2 = await readFile82(filePath, "utf-8");
9225
9281
  const sessions = [];
9226
9282
  const lines = raw2.split("\n");
9227
9283
  let inTable = false;
@@ -13535,6 +13591,425 @@ var init_session_id = __esm({
13535
13591
  }
13536
13592
  });
13537
13593
 
13594
+ // src/targets/renderers.ts
13595
+ var RENDERERS;
13596
+ var init_renderers = __esm({
13597
+ "src/targets/renderers.ts"() {
13598
+ "use strict";
13599
+ init_cursor_rules();
13600
+ init_codex_agents();
13601
+ init_opencode_config();
13602
+ init_hermes_soul();
13603
+ RENDERERS = {
13604
+ codexAgents: (ctx) => renderCodexAgents(ctx),
13605
+ cursorProtocol: () => renderCursorProtocol(),
13606
+ cursorAssignment: (ctx) => renderCursorAssignment(ctx),
13607
+ openCodeConfig: (ctx) => renderOpenCodeConfig({ projectDir: ctx.projectDir }),
13608
+ hermesSoul: (ctx) => renderHermesSoul(ctx)
13609
+ };
13610
+ }
13611
+ });
13612
+
13613
+ // src/targets/user-descriptors.ts
13614
+ import { resolve as resolve29 } from "path";
13615
+ import { readFile as readFile20, readdir as readdir15 } from "fs/promises";
13616
+ function userTargetsDir() {
13617
+ return resolve29(syntaurRoot(), "targets");
13618
+ }
13619
+ function msg(err2) {
13620
+ return err2 instanceof Error ? err2.message : String(err2);
13621
+ }
13622
+ function expandHomeAndEnv(p) {
13623
+ const envExpanded = p.replace(
13624
+ /\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g,
13625
+ (_m, braced, bare) => {
13626
+ const name = braced ?? bare ?? "";
13627
+ const v = process.env[name];
13628
+ return v ?? "";
13629
+ }
13630
+ );
13631
+ return resolve29(expandHome(envExpanded));
13632
+ }
13633
+ function compileDetect(spec) {
13634
+ switch (spec.kind) {
13635
+ case "pathExists": {
13636
+ const p = expandHomeAndEnv(spec.path);
13637
+ return () => fileExists(p);
13638
+ }
13639
+ case "anyPathExists": {
13640
+ const ps = spec.paths.map(expandHomeAndEnv);
13641
+ return async () => {
13642
+ for (const p of ps) {
13643
+ if (await fileExists(p)) return true;
13644
+ }
13645
+ return false;
13646
+ };
13647
+ }
13648
+ case "envSet": {
13649
+ const env = spec.env;
13650
+ return async () => {
13651
+ const v = process.env[env];
13652
+ return typeof v === "string" && v.length > 0;
13653
+ };
13654
+ }
13655
+ }
13656
+ }
13657
+ function isPlainObject(v) {
13658
+ return typeof v === "object" && v !== null && !Array.isArray(v);
13659
+ }
13660
+ function validateDetect(detect, errors) {
13661
+ if (!isPlainObject(detect)) {
13662
+ errors.push("detect must be an object");
13663
+ return;
13664
+ }
13665
+ switch (detect.kind) {
13666
+ case "pathExists":
13667
+ if (typeof detect.path !== "string" || detect.path.length === 0)
13668
+ errors.push('detect.path must be a non-empty string for kind "pathExists"');
13669
+ break;
13670
+ case "anyPathExists":
13671
+ if (!Array.isArray(detect.paths) || detect.paths.length === 0 || !detect.paths.every((p) => typeof p === "string" && p.length > 0))
13672
+ errors.push('detect.paths must be a non-empty string[] for kind "anyPathExists"');
13673
+ break;
13674
+ case "envSet":
13675
+ if (typeof detect.env !== "string" || detect.env.length === 0)
13676
+ errors.push('detect.env must be a non-empty string for kind "envSet"');
13677
+ break;
13678
+ default:
13679
+ errors.push(
13680
+ `detect.kind must be one of pathExists|anyPathExists|envSet (got ${JSON.stringify(detect.kind)})`
13681
+ );
13682
+ }
13683
+ }
13684
+ function validateInstructions(instructions, errors) {
13685
+ if (!isPlainObject(instructions)) {
13686
+ errors.push("instructions must be an object");
13687
+ return;
13688
+ }
13689
+ if (!Array.isArray(instructions.files) || instructions.files.length === 0) {
13690
+ errors.push("instructions.files must be a non-empty array");
13691
+ return;
13692
+ }
13693
+ instructions.files.forEach((f, i) => {
13694
+ if (!isPlainObject(f)) {
13695
+ errors.push(`instructions.files[${i}] must be an object`);
13696
+ return;
13697
+ }
13698
+ if (typeof f.path !== "string" || f.path.length === 0)
13699
+ errors.push(`instructions.files[${i}].path must be a non-empty string`);
13700
+ if (typeof f.renderer !== "string" || !VALID_RENDERER_KEYS.has(f.renderer))
13701
+ errors.push(
13702
+ `instructions.files[${i}].renderer ${JSON.stringify(f.renderer)} is not a known renderer (valid: ${[...VALID_RENDERER_KEYS].join(", ")})`
13703
+ );
13704
+ });
13705
+ }
13706
+ function validateUserDescriptor(data, builtinIds) {
13707
+ const errors = [];
13708
+ if (!isPlainObject(data)) {
13709
+ return { ok: false, errors: ["descriptor must be a JSON object"] };
13710
+ }
13711
+ for (const k of Object.keys(data)) {
13712
+ if (!ALLOWED_TOP_KEYS.has(k)) errors.push(`unknown field "${k}"`);
13713
+ }
13714
+ if (typeof data.id !== "string" || data.id.length === 0) {
13715
+ errors.push("id is required and must be a non-empty string");
13716
+ } else if (!ID_RE.test(data.id)) {
13717
+ errors.push(`id "${data.id}" must match ${ID_RE.source}`);
13718
+ } else if (builtinIds.has(data.id)) {
13719
+ errors.push(
13720
+ `id "${data.id}" collides with a built-in agent (built-ins cannot be overridden)`
13721
+ );
13722
+ }
13723
+ if (typeof data.displayName !== "string" || data.displayName.trim().length === 0) {
13724
+ errors.push("displayName is required and must be a non-empty string");
13725
+ }
13726
+ if (data.skillsShAgentId !== void 0 && (typeof data.skillsShAgentId !== "string" || data.skillsShAgentId.length === 0)) {
13727
+ errors.push("skillsShAgentId must be a non-empty string when present");
13728
+ }
13729
+ if (data.detect === void 0) {
13730
+ errors.push("detect is required");
13731
+ } else {
13732
+ validateDetect(data.detect, errors);
13733
+ }
13734
+ if (data.skillsDir !== void 0) {
13735
+ if (!isPlainObject(data.skillsDir)) {
13736
+ errors.push("skillsDir must be an object");
13737
+ } else {
13738
+ for (const k of Object.keys(data.skillsDir)) {
13739
+ if (k !== "project" && k !== "global") errors.push(`skillsDir.${k} is not a valid key`);
13740
+ }
13741
+ if (data.skillsDir.project !== void 0 && typeof data.skillsDir.project !== "string")
13742
+ errors.push("skillsDir.project must be a string");
13743
+ if (data.skillsDir.global !== void 0 && typeof data.skillsDir.global !== "string")
13744
+ errors.push("skillsDir.global must be a string");
13745
+ }
13746
+ }
13747
+ if (data.instructions !== void 0) {
13748
+ validateInstructions(data.instructions, errors);
13749
+ }
13750
+ if (errors.length > 0) return { ok: false, errors };
13751
+ return { ok: true, value: data };
13752
+ }
13753
+ function compileDescriptor(desc) {
13754
+ const target = {
13755
+ id: desc.id,
13756
+ displayName: desc.displayName,
13757
+ detect: compileDetect(desc.detect)
13758
+ };
13759
+ if (desc.skillsShAgentId) target.skillsShAgentId = desc.skillsShAgentId;
13760
+ if (desc.skillsDir) {
13761
+ const skillsDir = {};
13762
+ if (desc.skillsDir.global) skillsDir.global = expandHomeAndEnv(desc.skillsDir.global);
13763
+ if (desc.skillsDir.project) skillsDir.project = desc.skillsDir.project;
13764
+ target.skillsDir = skillsDir;
13765
+ }
13766
+ if (desc.instructions) target.instructions = desc.instructions;
13767
+ return target;
13768
+ }
13769
+ async function loadUserDescriptors(opts = {}) {
13770
+ const dir = opts.dir ?? userTargetsDir();
13771
+ const builtinIds = opts.builtinIds ?? /* @__PURE__ */ new Set();
13772
+ const warnings = [];
13773
+ if (!await fileExists(dir)) return { targets: [], warnings };
13774
+ let files;
13775
+ try {
13776
+ files = (await readdir15(dir)).filter((f) => f.endsWith(".json")).sort();
13777
+ } catch (err2) {
13778
+ return { targets: [], warnings: [`could not read ${dir}: ${msg(err2)}`] };
13779
+ }
13780
+ const targets = [];
13781
+ const seen = /* @__PURE__ */ new Set();
13782
+ for (const file of files) {
13783
+ const full = resolve29(dir, file);
13784
+ let raw2;
13785
+ try {
13786
+ raw2 = await readFile20(full, "utf-8");
13787
+ } catch (err2) {
13788
+ warnings.push(`skipped ${file}: ${msg(err2)}`);
13789
+ continue;
13790
+ }
13791
+ let parsed;
13792
+ try {
13793
+ parsed = JSON.parse(raw2);
13794
+ } catch (err2) {
13795
+ warnings.push(`skipped ${file}: invalid JSON: ${msg(err2)}`);
13796
+ continue;
13797
+ }
13798
+ const result = validateUserDescriptor(parsed, builtinIds);
13799
+ if (!result.ok) {
13800
+ warnings.push(`skipped ${file}: ${result.errors.join("; ")}`);
13801
+ continue;
13802
+ }
13803
+ if (seen.has(result.value.id)) {
13804
+ warnings.push(
13805
+ `skipped ${file}: duplicate id "${result.value.id}" (already loaded from an earlier file)`
13806
+ );
13807
+ continue;
13808
+ }
13809
+ seen.add(result.value.id);
13810
+ targets.push(compileDescriptor(result.value));
13811
+ }
13812
+ return { targets, warnings };
13813
+ }
13814
+ var ID_RE, VALID_RENDERER_KEYS, ALLOWED_TOP_KEYS;
13815
+ var init_user_descriptors = __esm({
13816
+ "src/targets/user-descriptors.ts"() {
13817
+ "use strict";
13818
+ init_fs();
13819
+ init_paths();
13820
+ init_renderers();
13821
+ ID_RE = /^[a-z0-9][a-z0-9_-]*$/;
13822
+ VALID_RENDERER_KEYS = new Set(Object.keys(RENDERERS));
13823
+ ALLOWED_TOP_KEYS = /* @__PURE__ */ new Set([
13824
+ "id",
13825
+ "displayName",
13826
+ "skillsShAgentId",
13827
+ "detect",
13828
+ "skillsDir",
13829
+ "instructions"
13830
+ ]);
13831
+ }
13832
+ });
13833
+
13834
+ // src/targets/registry.ts
13835
+ import { homedir as homedir4 } from "os";
13836
+ import { join as join5, resolve as resolve30 } from "path";
13837
+ function home(...segments) {
13838
+ return resolve30(homedir4(), ...segments);
13839
+ }
13840
+ function hermesHome() {
13841
+ const env = process.env.HERMES_HOME;
13842
+ return env && env.length > 0 ? resolve30(env) : home(".hermes");
13843
+ }
13844
+ function hermesSkillsDir() {
13845
+ return resolve30(hermesHome(), "skills");
13846
+ }
13847
+ function isHermesHomeCustom() {
13848
+ return hermesHome() !== home(".hermes");
13849
+ }
13850
+ function codexHome() {
13851
+ const env = process.env.CODEX_HOME;
13852
+ return env && env.length > 0 ? resolve30(env) : home(".codex");
13853
+ }
13854
+ function toDiscovered(meta) {
13855
+ if (!meta) return null;
13856
+ return {
13857
+ sessionId: meta.sessionId,
13858
+ cwd: meta.cwd,
13859
+ startedAt: meta.startTs,
13860
+ endedAt: meta.endTs,
13861
+ transcriptPath: meta.path
13862
+ };
13863
+ }
13864
+ function getAgentTarget(id) {
13865
+ return AGENT_TARGETS_BY_ID[id];
13866
+ }
13867
+ async function resolveAgentTargets() {
13868
+ const { targets: user, warnings } = await loadUserDescriptors({
13869
+ builtinIds: new Set(AGENT_TARGETS.map((t) => t.id))
13870
+ });
13871
+ return { targets: [...AGENT_TARGETS, ...user], warnings };
13872
+ }
13873
+ var detectDir, claudeSessions, codexSessions, piSessions, AGENT_TARGETS, AGENT_TARGETS_BY_ID;
13874
+ var init_registry = __esm({
13875
+ "src/targets/registry.ts"() {
13876
+ "use strict";
13877
+ init_fs();
13878
+ init_cwd_extractor();
13879
+ init_user_descriptors();
13880
+ detectDir = (dir) => () => fileExists(dir);
13881
+ claudeSessions = {
13882
+ globs: (root) => [join5(root ?? home(".claude", "projects"), "*", "*.jsonl")],
13883
+ parse: async (file) => toDiscovered(await extractClaudeSessionMeta(file)),
13884
+ walk: async function* (opts = {}) {
13885
+ for await (const meta of walkClaudeProjects({ root: opts.root, sinceMtimeMs: opts.sinceMtimeMs })) {
13886
+ const d = toDiscovered(meta);
13887
+ if (d) yield d;
13888
+ }
13889
+ }
13890
+ };
13891
+ codexSessions = {
13892
+ globs: (root) => [join5(root ?? resolveCodexSessionsRoot(), "**", "*.jsonl")],
13893
+ parse: async (file) => toDiscovered(await extractCodexSessionMeta(file)),
13894
+ walk: async function* (opts = {}) {
13895
+ for await (const meta of walkCodexSessions({ root: opts.root, sinceMtimeMs: opts.sinceMtimeMs })) {
13896
+ const d = toDiscovered(meta);
13897
+ if (d) yield d;
13898
+ }
13899
+ }
13900
+ };
13901
+ piSessions = {
13902
+ globs: (root) => [join5(root ?? resolvePiSessionsRoot(), "*", "*.jsonl")],
13903
+ parse: async (file) => toDiscovered(await extractPiSessionMeta(file)),
13904
+ walk: async function* (opts = {}) {
13905
+ for await (const meta of walkPiSessions({ root: opts.root, sinceMtimeMs: opts.sinceMtimeMs })) {
13906
+ const d = toDiscovered(meta);
13907
+ if (d) yield d;
13908
+ }
13909
+ }
13910
+ };
13911
+ AGENT_TARGETS = [
13912
+ {
13913
+ id: "cursor",
13914
+ displayName: "Cursor",
13915
+ skillsShAgentId: "cursor",
13916
+ detect: detectDir(home(".cursor")),
13917
+ skillsDir: { global: home(".cursor", "skills") },
13918
+ instructions: {
13919
+ files: [
13920
+ { path: ".cursor/rules/syntaur-protocol.mdc", renderer: "cursorProtocol" },
13921
+ { path: ".cursor/rules/syntaur-assignment.mdc", renderer: "cursorAssignment" }
13922
+ ]
13923
+ }
13924
+ },
13925
+ {
13926
+ // codex is BOTH an adapter (writes AGENTS.md) AND a native plugin.
13927
+ id: "codex",
13928
+ displayName: "Codex",
13929
+ skillsShAgentId: "codex",
13930
+ nativePlugin: "codex",
13931
+ detect: detectDir(codexHome()),
13932
+ skillsDir: { global: resolve30(codexHome(), "skills") },
13933
+ instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] },
13934
+ sessions: codexSessions
13935
+ },
13936
+ {
13937
+ id: "opencode",
13938
+ displayName: "OpenCode",
13939
+ skillsShAgentId: "opencode",
13940
+ detect: detectDir(home(".config", "opencode")),
13941
+ skillsDir: { global: home(".config", "opencode", "skills") },
13942
+ instructions: {
13943
+ files: [
13944
+ { path: "AGENTS.md", renderer: "codexAgents" },
13945
+ { path: "opencode.json", renderer: "openCodeConfig" }
13946
+ ]
13947
+ }
13948
+ },
13949
+ {
13950
+ // claude has NO adapter today (not in the old SUPPORTED_FRAMEWORKS) — the
13951
+ // full plugin path owns its skills/hooks/commands. Native-plugin only.
13952
+ id: "claude",
13953
+ displayName: "Claude Code",
13954
+ skillsShAgentId: "claude-code",
13955
+ nativePlugin: "claude",
13956
+ detect: detectDir(home(".claude")),
13957
+ skillsDir: { global: home(".claude", "skills") },
13958
+ sessions: claudeSessions,
13959
+ agentsDir: home(".claude", "agents")
13960
+ },
13961
+ {
13962
+ id: "pi",
13963
+ displayName: "Pi",
13964
+ skillsShAgentId: "pi",
13965
+ detect: detectDir(home(".pi")),
13966
+ skillsDir: { global: home(".pi", "agent", "skills") },
13967
+ instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] },
13968
+ sessions: piSessions,
13969
+ tier3: {
13970
+ kind: "pi-extension",
13971
+ source: "platforms/pi/extensions/syntaur",
13972
+ installDir: () => home(".pi", "agent", "extensions", "syntaur"),
13973
+ entry: "index.ts"
13974
+ }
13975
+ },
13976
+ {
13977
+ id: "openclaw",
13978
+ displayName: "OpenClaw",
13979
+ skillsShAgentId: "openclaw",
13980
+ detect: detectDir(home(".openclaw")),
13981
+ skillsDir: { global: home(".openclaw", "skills") },
13982
+ instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] },
13983
+ // OpenClaw runs on pi-coding-agent (design memo), so it reuses the pi
13984
+ // extension SOURCE; only the install dir differs.
13985
+ tier3: {
13986
+ kind: "pi-extension",
13987
+ source: "platforms/pi/extensions/syntaur",
13988
+ installDir: () => home(".openclaw", "extensions", "syntaur"),
13989
+ entry: "index.ts"
13990
+ }
13991
+ },
13992
+ {
13993
+ id: "hermes",
13994
+ displayName: "Hermes Agent",
13995
+ skillsShAgentId: "hermes-agent",
13996
+ detect: () => fileExists(hermesHome()),
13997
+ skillsDir: { global: hermesSkillsDir() },
13998
+ instructions: { files: [{ path: "SOUL.md", renderer: "hermesSoul" }] },
13999
+ tier3: {
14000
+ kind: "hermes-plugin",
14001
+ source: "platforms/hermes/plugins/syntaur",
14002
+ installDir: () => resolve30(hermesHome(), "plugins", "syntaur"),
14003
+ entry: "plugin.yaml"
14004
+ }
14005
+ }
14006
+ ];
14007
+ AGENT_TARGETS_BY_ID = Object.fromEntries(
14008
+ AGENT_TARGETS.map((t) => [t.id, t])
14009
+ );
14010
+ }
14011
+ });
14012
+
13538
14013
  // src/launch/launch-prompt.ts
13539
14014
  function bareGrabSeed(params2) {
13540
14015
  if (params2.projectSlug) {
@@ -13556,8 +14031,19 @@ function resolveTemplate(template, ctx) {
13556
14031
  const warnings = [];
13557
14032
  const prompt = template.replace(TOKEN_RE, (_match, boundary, token) => {
13558
14033
  if (token === "assignment") {
14034
+ if (!ctx.assignmentDir) {
14035
+ warnings.push(`launchPrompt: "@assignment" has no assignment context here \u2014 left as literal text.`);
14036
+ return boundary + "@assignment";
14037
+ }
13559
14038
  return boundary + assignmentPointer(ctx.id, ctx.assignmentDir);
13560
14039
  }
14040
+ if (token === "worktree") {
14041
+ if (!ctx.worktreePath) {
14042
+ warnings.push(`launchPrompt: "@worktree" has no worktree path here \u2014 left as literal text.`);
14043
+ return boundary + "@worktree";
14044
+ }
14045
+ return boundary + ctx.worktreePath;
14046
+ }
13561
14047
  if (!isValidSlug(token)) {
13562
14048
  warnings.push(`launchPrompt: "@${token}" is not a valid playbook token \u2014 left as literal text.`);
13563
14049
  return boundary + "@" + token;
@@ -13571,16 +14057,37 @@ function resolveTemplate(template, ctx) {
13571
14057
  return { prompt, warnings };
13572
14058
  }
13573
14059
  function resolveLaunchPrompt(input4) {
13574
- const { template, playbook, id, assignmentDir, projectSlug, assignmentSlug, knownPlaybookSlugs } = input4;
14060
+ const {
14061
+ template,
14062
+ playbook,
14063
+ id,
14064
+ assignmentDir,
14065
+ projectSlug,
14066
+ assignmentSlug,
14067
+ worktreePath,
14068
+ spawnCwd,
14069
+ knownPlaybookSlugs
14070
+ } = input4;
13575
14071
  if (template && template.trim()) {
13576
- return resolveTemplate(template, { id, assignmentDir, knownPlaybookSlugs });
14072
+ return resolveTemplate(template, { id, assignmentDir, worktreePath, knownPlaybookSlugs });
13577
14073
  }
13578
14074
  const pb = playbook?.trim();
13579
14075
  if (pb) {
13580
- const pointer = assignmentPointer(id, assignmentDir);
13581
- return { prompt: `${pointer} Run ${runPlaybookClause(pb)} end-to-end.`, warnings: [] };
14076
+ const clause = runPlaybookClause(pb);
14077
+ const prompt = assignmentDir ? `${assignmentPointer(id, assignmentDir)} Run ${clause} end-to-end.` : `Run ${clause} end-to-end.`;
14078
+ return { prompt, warnings: [] };
13582
14079
  }
13583
- return { prompt: bareGrabSeed({ projectSlug, assignmentSlug, id }), warnings: [] };
14080
+ if (worktreePath && spawnCwd && worktreePath !== spawnCwd) {
14081
+ const grab = bareGrabSeed({ projectSlug, assignmentSlug: assignmentSlug ?? "", id });
14082
+ return {
14083
+ prompt: `The assignment's worktree is at ${worktreePath}. cd there, then grab the assignment with ${grab} and work it end-to-end.`,
14084
+ warnings: []
14085
+ };
14086
+ }
14087
+ if (!assignmentSlug && !id) {
14088
+ return { prompt: "", warnings: [] };
14089
+ }
14090
+ return { prompt: bareGrabSeed({ projectSlug, assignmentSlug: assignmentSlug ?? "", id }), warnings: [] };
13584
14091
  }
13585
14092
  function effectiveLaunchTemplate(input4) {
13586
14093
  if (input4.launchPrompt && input4.launchPrompt.trim()) {
@@ -13590,6 +14097,14 @@ function effectiveLaunchTemplate(input4) {
13590
14097
  if (pb) {
13591
14098
  return `@assignment Run ${runPlaybookClause(pb)} end-to-end.`;
13592
14099
  }
14100
+ if (input4.workdir && input4.workdir.trim()) {
14101
+ const grab = bareGrabSeed({
14102
+ projectSlug: input4.projectSlug,
14103
+ assignmentSlug: input4.assignmentSlug,
14104
+ id: input4.id
14105
+ });
14106
+ return `The assignment's worktree is at @worktree. cd there, then grab the assignment with ${grab} and work it end-to-end.`;
14107
+ }
13593
14108
  return bareGrabSeed({
13594
14109
  projectSlug: input4.projectSlug,
13595
14110
  assignmentSlug: input4.assignmentSlug,
@@ -13616,15 +14131,17 @@ __export(launch_exports, {
13616
14131
  });
13617
14132
  import { spawn as spawn2 } from "child_process";
13618
14133
  import { mkdir as mkdir2, writeFile as writeFile4 } from "fs/promises";
13619
- import { isAbsolute as isAbsolute5, resolve as resolve29 } from "path";
14134
+ import { isAbsolute as isAbsolute5, resolve as resolve31 } from "path";
13620
14135
  function shellQuote(arg) {
13621
14136
  if (arg === "") return "''";
13622
14137
  return `'${arg.replace(/'/g, `'\\''`)}'`;
13623
14138
  }
13624
14139
  function buildAgentArgv(agent, prompt, env = process.env) {
13625
14140
  const position = agent.promptArgPosition ?? "first";
13626
- const baseArgs = applyModelFlag(agent, [...agent.args ?? []]);
13627
- const agentArgs = position === "first" ? [prompt, ...baseArgs] : position === "last" ? [...baseArgs, prompt] : baseArgs;
14141
+ const prefix = agentNameArgs(agent);
14142
+ const baseArgs = agent.agentName ? [...agent.args ?? []] : applyModelFlag(agent, [...agent.args ?? []]);
14143
+ const positioned = position === "first" ? [prompt, ...baseArgs] : position === "last" ? [...baseArgs, prompt] : baseArgs;
14144
+ const agentArgs = [...prefix, ...positioned];
13628
14145
  if (agent.resolveFromShellAliases) {
13629
14146
  const requested = env.SHELL;
13630
14147
  let shell = requested;
@@ -13652,9 +14169,9 @@ async function launchAgent(options) {
13652
14169
  console.error(`Assignment not found: ${projectSlug}/${assignmentSlug}`);
13653
14170
  process.exit(1);
13654
14171
  }
13655
- const projectDir = resolve29(projectsDir2, projectSlug);
13656
- const assignmentDir = resolve29(projectDir, "assignments", assignmentSlug);
13657
- let workspaceDir;
14172
+ const projectDir = resolve31(projectsDir2, projectSlug);
14173
+ const assignmentDir = resolve31(projectDir, "assignments", assignmentSlug);
14174
+ let worktreeDir;
13658
14175
  if (cwdOverride) {
13659
14176
  if (!isExistingDir(cwdOverride)) {
13660
14177
  console.error(
@@ -13663,7 +14180,7 @@ async function launchAgent(options) {
13663
14180
  exitWith(1);
13664
14181
  return;
13665
14182
  }
13666
- workspaceDir = cwdOverride;
14183
+ worktreeDir = cwdOverride;
13667
14184
  } else {
13668
14185
  const picked = resolveWorkspaceCwd({
13669
14186
  worktreePath: detail.workspace.worktreePath,
@@ -13676,26 +14193,34 @@ async function launchAgent(options) {
13676
14193
  exitWith(1);
13677
14194
  return;
13678
14195
  }
13679
- workspaceDir = picked.cwd;
14196
+ worktreeDir = picked.cwd;
13680
14197
  const warning = picked.fallbackWarning ?? formatFallbackCwdWarning({
13681
14198
  assignmentSlug,
13682
- workspaceDir,
14199
+ workspaceDir: worktreeDir,
13683
14200
  worktreePath: detail.workspace.worktreePath,
13684
14201
  branch: detail.workspace.branch
13685
14202
  });
13686
14203
  if (warning) console.warn(warning);
13687
14204
  }
13688
- const contextDir = resolve29(workspaceDir, ".syntaur");
14205
+ const launchCwd = resolveLaunchCwd(agent, worktreeDir);
14206
+ if (launchCwd.invalidReason) {
14207
+ console.error(`syntaur: ${launchCwd.invalidReason} \u2014 refusing to launch.`);
14208
+ exitWith(1);
14209
+ return;
14210
+ }
14211
+ const spawnCwd = launchCwd.spawnCwd;
14212
+ const worktreePath = launchCwd.worktreePath;
14213
+ const contextDir = resolve31(worktreePath, ".syntaur");
13689
14214
  await mkdir2(contextDir, { recursive: true });
13690
14215
  const context = {
13691
14216
  repository: detail.workspace.repository ?? null,
13692
14217
  branch: detail.workspace.branch ?? null,
13693
14218
  worktreePath: detail.workspace.worktreePath ?? null,
13694
- workspaceRoot: workspaceDir,
14219
+ workspaceRoot: worktreePath,
13695
14220
  grabbedAt: (/* @__PURE__ */ new Date()).toISOString()
13696
14221
  };
13697
14222
  await writeFile4(
13698
- resolve29(contextDir, "context.json"),
14223
+ resolve31(contextDir, "context.json"),
13699
14224
  JSON.stringify(context, null, 2) + "\n"
13700
14225
  );
13701
14226
  const knownPlaybookSlugs = await listPlaybookSlugs(playbooksDir());
@@ -13706,6 +14231,8 @@ async function launchAgent(options) {
13706
14231
  assignmentDir,
13707
14232
  projectSlug,
13708
14233
  assignmentSlug,
14234
+ worktreePath,
14235
+ spawnCwd,
13709
14236
  knownPlaybookSlugs
13710
14237
  });
13711
14238
  for (const warning of warnings) console.warn(warning);
@@ -13716,7 +14243,7 @@ async function launchAgent(options) {
13716
14243
  const spawnImpl = options.spawnFn ?? spawn2;
13717
14244
  return new Promise((resolvePromise) => {
13718
14245
  const child = spawnImpl(argv.command, argv.args, {
13719
- cwd: workspaceDir,
14246
+ cwd: spawnCwd,
13720
14247
  stdio: "inherit"
13721
14248
  });
13722
14249
  child.on("error", (err2) => {
@@ -13951,12 +14478,12 @@ var init_assignment_todos = __esm({
13951
14478
 
13952
14479
  // src/utils/path-canon.ts
13953
14480
  import { realpathSync as realpathSync2 } from "fs";
13954
- import { resolve as resolve46 } from "path";
14481
+ import { resolve as resolve48 } from "path";
13955
14482
  function canonicalPath(p) {
13956
14483
  try {
13957
- return realpathSync2(resolve46(p));
14484
+ return realpathSync2(resolve48(p));
13958
14485
  } catch {
13959
- return resolve46(p).replace(/\/+$/, "");
14486
+ return resolve48(p).replace(/\/+$/, "");
13960
14487
  }
13961
14488
  }
13962
14489
  var init_path_canon = __esm({
@@ -13965,421 +14492,6 @@ var init_path_canon = __esm({
13965
14492
  }
13966
14493
  });
13967
14494
 
13968
- // src/targets/renderers.ts
13969
- var RENDERERS;
13970
- var init_renderers = __esm({
13971
- "src/targets/renderers.ts"() {
13972
- "use strict";
13973
- init_cursor_rules();
13974
- init_codex_agents();
13975
- init_opencode_config();
13976
- init_hermes_soul();
13977
- RENDERERS = {
13978
- codexAgents: (ctx) => renderCodexAgents(ctx),
13979
- cursorProtocol: () => renderCursorProtocol(),
13980
- cursorAssignment: (ctx) => renderCursorAssignment(ctx),
13981
- openCodeConfig: (ctx) => renderOpenCodeConfig({ projectDir: ctx.projectDir }),
13982
- hermesSoul: (ctx) => renderHermesSoul(ctx)
13983
- };
13984
- }
13985
- });
13986
-
13987
- // src/targets/user-descriptors.ts
13988
- import { resolve as resolve47 } from "path";
13989
- import { readFile as readFile31, readdir as readdir19 } from "fs/promises";
13990
- function userTargetsDir() {
13991
- return resolve47(syntaurRoot(), "targets");
13992
- }
13993
- function msg(err2) {
13994
- return err2 instanceof Error ? err2.message : String(err2);
13995
- }
13996
- function expandHomeAndEnv(p) {
13997
- const envExpanded = p.replace(
13998
- /\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g,
13999
- (_m, braced, bare) => {
14000
- const name = braced ?? bare ?? "";
14001
- const v = process.env[name];
14002
- return v ?? "";
14003
- }
14004
- );
14005
- return resolve47(expandHome(envExpanded));
14006
- }
14007
- function compileDetect(spec) {
14008
- switch (spec.kind) {
14009
- case "pathExists": {
14010
- const p = expandHomeAndEnv(spec.path);
14011
- return () => fileExists(p);
14012
- }
14013
- case "anyPathExists": {
14014
- const ps = spec.paths.map(expandHomeAndEnv);
14015
- return async () => {
14016
- for (const p of ps) {
14017
- if (await fileExists(p)) return true;
14018
- }
14019
- return false;
14020
- };
14021
- }
14022
- case "envSet": {
14023
- const env = spec.env;
14024
- return async () => {
14025
- const v = process.env[env];
14026
- return typeof v === "string" && v.length > 0;
14027
- };
14028
- }
14029
- }
14030
- }
14031
- function isPlainObject(v) {
14032
- return typeof v === "object" && v !== null && !Array.isArray(v);
14033
- }
14034
- function validateDetect(detect, errors) {
14035
- if (!isPlainObject(detect)) {
14036
- errors.push("detect must be an object");
14037
- return;
14038
- }
14039
- switch (detect.kind) {
14040
- case "pathExists":
14041
- if (typeof detect.path !== "string" || detect.path.length === 0)
14042
- errors.push('detect.path must be a non-empty string for kind "pathExists"');
14043
- break;
14044
- case "anyPathExists":
14045
- if (!Array.isArray(detect.paths) || detect.paths.length === 0 || !detect.paths.every((p) => typeof p === "string" && p.length > 0))
14046
- errors.push('detect.paths must be a non-empty string[] for kind "anyPathExists"');
14047
- break;
14048
- case "envSet":
14049
- if (typeof detect.env !== "string" || detect.env.length === 0)
14050
- errors.push('detect.env must be a non-empty string for kind "envSet"');
14051
- break;
14052
- default:
14053
- errors.push(
14054
- `detect.kind must be one of pathExists|anyPathExists|envSet (got ${JSON.stringify(detect.kind)})`
14055
- );
14056
- }
14057
- }
14058
- function validateInstructions(instructions, errors) {
14059
- if (!isPlainObject(instructions)) {
14060
- errors.push("instructions must be an object");
14061
- return;
14062
- }
14063
- if (!Array.isArray(instructions.files) || instructions.files.length === 0) {
14064
- errors.push("instructions.files must be a non-empty array");
14065
- return;
14066
- }
14067
- instructions.files.forEach((f, i) => {
14068
- if (!isPlainObject(f)) {
14069
- errors.push(`instructions.files[${i}] must be an object`);
14070
- return;
14071
- }
14072
- if (typeof f.path !== "string" || f.path.length === 0)
14073
- errors.push(`instructions.files[${i}].path must be a non-empty string`);
14074
- if (typeof f.renderer !== "string" || !VALID_RENDERER_KEYS.has(f.renderer))
14075
- errors.push(
14076
- `instructions.files[${i}].renderer ${JSON.stringify(f.renderer)} is not a known renderer (valid: ${[...VALID_RENDERER_KEYS].join(", ")})`
14077
- );
14078
- });
14079
- }
14080
- function validateUserDescriptor(data, builtinIds) {
14081
- const errors = [];
14082
- if (!isPlainObject(data)) {
14083
- return { ok: false, errors: ["descriptor must be a JSON object"] };
14084
- }
14085
- for (const k of Object.keys(data)) {
14086
- if (!ALLOWED_TOP_KEYS.has(k)) errors.push(`unknown field "${k}"`);
14087
- }
14088
- if (typeof data.id !== "string" || data.id.length === 0) {
14089
- errors.push("id is required and must be a non-empty string");
14090
- } else if (!ID_RE.test(data.id)) {
14091
- errors.push(`id "${data.id}" must match ${ID_RE.source}`);
14092
- } else if (builtinIds.has(data.id)) {
14093
- errors.push(
14094
- `id "${data.id}" collides with a built-in agent (built-ins cannot be overridden)`
14095
- );
14096
- }
14097
- if (typeof data.displayName !== "string" || data.displayName.trim().length === 0) {
14098
- errors.push("displayName is required and must be a non-empty string");
14099
- }
14100
- if (data.skillsShAgentId !== void 0 && (typeof data.skillsShAgentId !== "string" || data.skillsShAgentId.length === 0)) {
14101
- errors.push("skillsShAgentId must be a non-empty string when present");
14102
- }
14103
- if (data.detect === void 0) {
14104
- errors.push("detect is required");
14105
- } else {
14106
- validateDetect(data.detect, errors);
14107
- }
14108
- if (data.skillsDir !== void 0) {
14109
- if (!isPlainObject(data.skillsDir)) {
14110
- errors.push("skillsDir must be an object");
14111
- } else {
14112
- for (const k of Object.keys(data.skillsDir)) {
14113
- if (k !== "project" && k !== "global") errors.push(`skillsDir.${k} is not a valid key`);
14114
- }
14115
- if (data.skillsDir.project !== void 0 && typeof data.skillsDir.project !== "string")
14116
- errors.push("skillsDir.project must be a string");
14117
- if (data.skillsDir.global !== void 0 && typeof data.skillsDir.global !== "string")
14118
- errors.push("skillsDir.global must be a string");
14119
- }
14120
- }
14121
- if (data.instructions !== void 0) {
14122
- validateInstructions(data.instructions, errors);
14123
- }
14124
- if (errors.length > 0) return { ok: false, errors };
14125
- return { ok: true, value: data };
14126
- }
14127
- function compileDescriptor(desc) {
14128
- const target = {
14129
- id: desc.id,
14130
- displayName: desc.displayName,
14131
- detect: compileDetect(desc.detect)
14132
- };
14133
- if (desc.skillsShAgentId) target.skillsShAgentId = desc.skillsShAgentId;
14134
- if (desc.skillsDir) {
14135
- const skillsDir = {};
14136
- if (desc.skillsDir.global) skillsDir.global = expandHomeAndEnv(desc.skillsDir.global);
14137
- if (desc.skillsDir.project) skillsDir.project = desc.skillsDir.project;
14138
- target.skillsDir = skillsDir;
14139
- }
14140
- if (desc.instructions) target.instructions = desc.instructions;
14141
- return target;
14142
- }
14143
- async function loadUserDescriptors(opts = {}) {
14144
- const dir = opts.dir ?? userTargetsDir();
14145
- const builtinIds = opts.builtinIds ?? /* @__PURE__ */ new Set();
14146
- const warnings = [];
14147
- if (!await fileExists(dir)) return { targets: [], warnings };
14148
- let files;
14149
- try {
14150
- files = (await readdir19(dir)).filter((f) => f.endsWith(".json")).sort();
14151
- } catch (err2) {
14152
- return { targets: [], warnings: [`could not read ${dir}: ${msg(err2)}`] };
14153
- }
14154
- const targets = [];
14155
- const seen = /* @__PURE__ */ new Set();
14156
- for (const file of files) {
14157
- const full = resolve47(dir, file);
14158
- let raw2;
14159
- try {
14160
- raw2 = await readFile31(full, "utf-8");
14161
- } catch (err2) {
14162
- warnings.push(`skipped ${file}: ${msg(err2)}`);
14163
- continue;
14164
- }
14165
- let parsed;
14166
- try {
14167
- parsed = JSON.parse(raw2);
14168
- } catch (err2) {
14169
- warnings.push(`skipped ${file}: invalid JSON: ${msg(err2)}`);
14170
- continue;
14171
- }
14172
- const result = validateUserDescriptor(parsed, builtinIds);
14173
- if (!result.ok) {
14174
- warnings.push(`skipped ${file}: ${result.errors.join("; ")}`);
14175
- continue;
14176
- }
14177
- if (seen.has(result.value.id)) {
14178
- warnings.push(
14179
- `skipped ${file}: duplicate id "${result.value.id}" (already loaded from an earlier file)`
14180
- );
14181
- continue;
14182
- }
14183
- seen.add(result.value.id);
14184
- targets.push(compileDescriptor(result.value));
14185
- }
14186
- return { targets, warnings };
14187
- }
14188
- var ID_RE, VALID_RENDERER_KEYS, ALLOWED_TOP_KEYS;
14189
- var init_user_descriptors = __esm({
14190
- "src/targets/user-descriptors.ts"() {
14191
- "use strict";
14192
- init_fs();
14193
- init_paths();
14194
- init_renderers();
14195
- ID_RE = /^[a-z0-9][a-z0-9_-]*$/;
14196
- VALID_RENDERER_KEYS = new Set(Object.keys(RENDERERS));
14197
- ALLOWED_TOP_KEYS = /* @__PURE__ */ new Set([
14198
- "id",
14199
- "displayName",
14200
- "skillsShAgentId",
14201
- "detect",
14202
- "skillsDir",
14203
- "instructions"
14204
- ]);
14205
- }
14206
- });
14207
-
14208
- // src/targets/registry.ts
14209
- import { homedir as homedir7 } from "os";
14210
- import { join as join10, resolve as resolve48 } from "path";
14211
- function home(...segments) {
14212
- return resolve48(homedir7(), ...segments);
14213
- }
14214
- function hermesHome() {
14215
- const env = process.env.HERMES_HOME;
14216
- return env && env.length > 0 ? resolve48(env) : home(".hermes");
14217
- }
14218
- function hermesSkillsDir() {
14219
- return resolve48(hermesHome(), "skills");
14220
- }
14221
- function isHermesHomeCustom() {
14222
- return hermesHome() !== home(".hermes");
14223
- }
14224
- function codexHome() {
14225
- const env = process.env.CODEX_HOME;
14226
- return env && env.length > 0 ? resolve48(env) : home(".codex");
14227
- }
14228
- function toDiscovered(meta) {
14229
- if (!meta) return null;
14230
- return {
14231
- sessionId: meta.sessionId,
14232
- cwd: meta.cwd,
14233
- startedAt: meta.startTs,
14234
- endedAt: meta.endTs,
14235
- transcriptPath: meta.path
14236
- };
14237
- }
14238
- async function resolveAgentTargets() {
14239
- const { targets: user, warnings } = await loadUserDescriptors({
14240
- builtinIds: new Set(AGENT_TARGETS.map((t) => t.id))
14241
- });
14242
- return { targets: [...AGENT_TARGETS, ...user], warnings };
14243
- }
14244
- var detectDir, claudeSessions, codexSessions, piSessions, AGENT_TARGETS, AGENT_TARGETS_BY_ID;
14245
- var init_registry = __esm({
14246
- "src/targets/registry.ts"() {
14247
- "use strict";
14248
- init_fs();
14249
- init_cwd_extractor();
14250
- init_user_descriptors();
14251
- detectDir = (dir) => () => fileExists(dir);
14252
- claudeSessions = {
14253
- globs: (root) => [join10(root ?? home(".claude", "projects"), "*", "*.jsonl")],
14254
- parse: async (file) => toDiscovered(await extractClaudeSessionMeta(file)),
14255
- walk: async function* (opts = {}) {
14256
- for await (const meta of walkClaudeProjects({ root: opts.root, sinceMtimeMs: opts.sinceMtimeMs })) {
14257
- const d = toDiscovered(meta);
14258
- if (d) yield d;
14259
- }
14260
- }
14261
- };
14262
- codexSessions = {
14263
- globs: (root) => [join10(root ?? resolveCodexSessionsRoot(), "**", "*.jsonl")],
14264
- parse: async (file) => toDiscovered(await extractCodexSessionMeta(file)),
14265
- walk: async function* (opts = {}) {
14266
- for await (const meta of walkCodexSessions({ root: opts.root, sinceMtimeMs: opts.sinceMtimeMs })) {
14267
- const d = toDiscovered(meta);
14268
- if (d) yield d;
14269
- }
14270
- }
14271
- };
14272
- piSessions = {
14273
- globs: (root) => [join10(root ?? resolvePiSessionsRoot(), "*", "*.jsonl")],
14274
- parse: async (file) => toDiscovered(await extractPiSessionMeta(file)),
14275
- walk: async function* (opts = {}) {
14276
- for await (const meta of walkPiSessions({ root: opts.root, sinceMtimeMs: opts.sinceMtimeMs })) {
14277
- const d = toDiscovered(meta);
14278
- if (d) yield d;
14279
- }
14280
- }
14281
- };
14282
- AGENT_TARGETS = [
14283
- {
14284
- id: "cursor",
14285
- displayName: "Cursor",
14286
- skillsShAgentId: "cursor",
14287
- detect: detectDir(home(".cursor")),
14288
- skillsDir: { global: home(".cursor", "skills") },
14289
- instructions: {
14290
- files: [
14291
- { path: ".cursor/rules/syntaur-protocol.mdc", renderer: "cursorProtocol" },
14292
- { path: ".cursor/rules/syntaur-assignment.mdc", renderer: "cursorAssignment" }
14293
- ]
14294
- }
14295
- },
14296
- {
14297
- // codex is BOTH an adapter (writes AGENTS.md) AND a native plugin.
14298
- id: "codex",
14299
- displayName: "Codex",
14300
- skillsShAgentId: "codex",
14301
- nativePlugin: "codex",
14302
- detect: detectDir(codexHome()),
14303
- skillsDir: { global: resolve48(codexHome(), "skills") },
14304
- instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] },
14305
- sessions: codexSessions
14306
- },
14307
- {
14308
- id: "opencode",
14309
- displayName: "OpenCode",
14310
- skillsShAgentId: "opencode",
14311
- detect: detectDir(home(".config", "opencode")),
14312
- skillsDir: { global: home(".config", "opencode", "skills") },
14313
- instructions: {
14314
- files: [
14315
- { path: "AGENTS.md", renderer: "codexAgents" },
14316
- { path: "opencode.json", renderer: "openCodeConfig" }
14317
- ]
14318
- }
14319
- },
14320
- {
14321
- // claude has NO adapter today (not in the old SUPPORTED_FRAMEWORKS) — the
14322
- // full plugin path owns its skills/hooks/commands. Native-plugin only.
14323
- id: "claude",
14324
- displayName: "Claude Code",
14325
- skillsShAgentId: "claude-code",
14326
- nativePlugin: "claude",
14327
- detect: detectDir(home(".claude")),
14328
- skillsDir: { global: home(".claude", "skills") },
14329
- sessions: claudeSessions
14330
- },
14331
- {
14332
- id: "pi",
14333
- displayName: "Pi",
14334
- skillsShAgentId: "pi",
14335
- detect: detectDir(home(".pi")),
14336
- skillsDir: { global: home(".pi", "agent", "skills") },
14337
- instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] },
14338
- sessions: piSessions,
14339
- tier3: {
14340
- kind: "pi-extension",
14341
- source: "platforms/pi/extensions/syntaur",
14342
- installDir: () => home(".pi", "agent", "extensions", "syntaur"),
14343
- entry: "index.ts"
14344
- }
14345
- },
14346
- {
14347
- id: "openclaw",
14348
- displayName: "OpenClaw",
14349
- skillsShAgentId: "openclaw",
14350
- detect: detectDir(home(".openclaw")),
14351
- skillsDir: { global: home(".openclaw", "skills") },
14352
- instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] },
14353
- // OpenClaw runs on pi-coding-agent (design memo), so it reuses the pi
14354
- // extension SOURCE; only the install dir differs.
14355
- tier3: {
14356
- kind: "pi-extension",
14357
- source: "platforms/pi/extensions/syntaur",
14358
- installDir: () => home(".openclaw", "extensions", "syntaur"),
14359
- entry: "index.ts"
14360
- }
14361
- },
14362
- {
14363
- id: "hermes",
14364
- displayName: "Hermes Agent",
14365
- skillsShAgentId: "hermes-agent",
14366
- detect: () => fileExists(hermesHome()),
14367
- skillsDir: { global: hermesSkillsDir() },
14368
- instructions: { files: [{ path: "SOUL.md", renderer: "hermesSoul" }] },
14369
- tier3: {
14370
- kind: "hermes-plugin",
14371
- source: "platforms/hermes/plugins/syntaur",
14372
- installDir: () => resolve48(hermesHome(), "plugins", "syntaur"),
14373
- entry: "plugin.yaml"
14374
- }
14375
- }
14376
- ];
14377
- AGENT_TARGETS_BY_ID = Object.fromEntries(
14378
- AGENT_TARGETS.map((t) => [t.id, t])
14379
- );
14380
- }
14381
- });
14382
-
14383
14495
  // src/sessions/agent-view.ts
14384
14496
  import { execFile as execFile3 } from "child_process";
14385
14497
  import { promisify as promisify3 } from "util";
@@ -14740,7 +14852,7 @@ import {
14740
14852
  unlinkSync
14741
14853
  } from "fs";
14742
14854
  import { fileURLToPath as fileURLToPath9, pathToFileURL } from "url";
14743
- import { dirname as dirname21, resolve as resolve74, join as join16 } from "path";
14855
+ import { dirname as dirname21, resolve as resolve74, join as join17 } from "path";
14744
14856
  import { homedir as homedir13, tmpdir as tmpdir2 } from "os";
14745
14857
  import { spawnSync as spawnSync7 } from "child_process";
14746
14858
  function syntaurRootMjs() {
@@ -14748,7 +14860,7 @@ function syntaurRootMjs() {
14748
14860
  if (override3 && override3.length > 0) {
14749
14861
  return override3;
14750
14862
  }
14751
- return join16(homedir13(), ".syntaur");
14863
+ return join17(homedir13(), ".syntaur");
14752
14864
  }
14753
14865
  async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
14754
14866
  const { throwOnFailure } = options;
@@ -14781,19 +14893,19 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
14781
14893
  const pkgRoot = resolve74(dirname21(fileURLToPath9(import.meta.url)), "..");
14782
14894
  const cliBin = realpathSync4(resolve74(pkgRoot, "bin/syntaur.js"));
14783
14895
  const nodeBin = process.execPath;
14784
- const bundleParent = join16(
14896
+ const bundleParent = join17(
14785
14897
  homedir13(),
14786
14898
  "Library",
14787
14899
  "Application Support",
14788
14900
  "Syntaur"
14789
14901
  );
14790
- const bundlePath = join16(bundleParent, "syntaur-url.app");
14902
+ const bundlePath = join17(bundleParent, "syntaur-url.app");
14791
14903
  mkdirSync3(bundleParent, { recursive: true });
14792
14904
  if (existsSync6(bundlePath)) {
14793
14905
  rmSync(bundlePath, { recursive: true, force: true });
14794
14906
  }
14795
14907
  const installedTerminals = detectInstalledTerminals();
14796
- const scriptPath = join16(
14908
+ const scriptPath = join17(
14797
14909
  tmpdir2(),
14798
14910
  `syntaur-url-handler-${process.pid}.applescript`
14799
14911
  );
@@ -14812,7 +14924,7 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
14812
14924
  `osacompile exited with code ${compile.status}: ${compile.stderr || compile.stdout}`
14813
14925
  );
14814
14926
  }
14815
- const infoPlistPath = join16(bundlePath, "Contents", "Info.plist");
14927
+ const infoPlistPath = join17(bundlePath, "Contents", "Info.plist");
14816
14928
  if (!existsSync6(infoPlistPath)) {
14817
14929
  throw new Error(`osacompile did not produce ${infoPlistPath}`);
14818
14930
  }
@@ -14907,7 +15019,7 @@ function detectInstalledTerminals() {
14907
15019
  ghostty: "Ghostty.app",
14908
15020
  warp: "Warp.app"
14909
15021
  };
14910
- const appDirs = ["/Applications", join16(homedir13(), "Applications")];
15022
+ const appDirs = ["/Applications", join17(homedir13(), "Applications")];
14911
15023
  for (const [id, bundleId] of Object.entries(bundleIds)) {
14912
15024
  const r = spawnSync7(
14913
15025
  "mdfind",
@@ -14919,7 +15031,7 @@ function detectInstalledTerminals() {
14919
15031
  continue;
14920
15032
  }
14921
15033
  const name = bundleNames[id];
14922
- if (name && appDirs.some((dir) => existsSync6(join16(dir, name)))) {
15034
+ if (name && appDirs.some((dir) => existsSync6(join17(dir, name)))) {
14923
15035
  installed.add(id);
14924
15036
  }
14925
15037
  }
@@ -15643,7 +15755,7 @@ __export(launchd_exports, {
15643
15755
  import { execFileSync as execFileSync5 } from "child_process";
15644
15756
  import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, rmSync as rmSync2, realpathSync as realpathSync5, openSync as openSync2, closeSync as closeSync2 } from "fs";
15645
15757
  import { homedir as homedir16, userInfo } from "os";
15646
- import { dirname as dirname28, join as join26, resolve as resolve99 } from "path";
15758
+ import { dirname as dirname28, join as join27, resolve as resolve99 } from "path";
15647
15759
  function buildPlist(spec) {
15648
15760
  const args = [spec.nodePath, spec.syntaurBin, ...spec.command ?? ["schedule", "tick"]];
15649
15761
  const argXml = args.map((a) => ` <string>${escapeXml(a)}</string>`).join("\n");
@@ -15675,7 +15787,7 @@ function escapeXml(value) {
15675
15787
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
15676
15788
  }
15677
15789
  function defaultAcquireInstallLock(home2) {
15678
- const lockPath = join26(home2, ".syntaur", "install-launch-agent.lock");
15790
+ const lockPath = join27(home2, ".syntaur", "install-launch-agent.lock");
15679
15791
  mkdirSync4(dirname28(lockPath), { recursive: true });
15680
15792
  const fd = openSync2(lockPath, "wx");
15681
15793
  return () => {
@@ -15711,10 +15823,10 @@ function resolveSpec(config, deps) {
15711
15823
  nodePath: deps.nodePath ?? process.execPath,
15712
15824
  syntaurBin: absolutize(deps.syntaurBin ?? process.argv[1] ?? "syntaur"),
15713
15825
  intervalSeconds: deps.intervalSeconds ?? config.defaultIntervalSeconds,
15714
- outLog: join26(home2, ".syntaur", "logs", config.outLogName),
15715
- errLog: join26(home2, ".syntaur", "logs", config.errLogName)
15826
+ outLog: join27(home2, ".syntaur", "logs", config.outLogName),
15827
+ errLog: join27(home2, ".syntaur", "logs", config.errLogName)
15716
15828
  };
15717
- const plistPath = join26(home2, "Library", "LaunchAgents", `${spec.label}.plist`);
15829
+ const plistPath = join27(home2, "Library", "LaunchAgents", `${spec.label}.plist`);
15718
15830
  return { spec, plistPath, home: home2, uid };
15719
15831
  }
15720
15832
  function installAgent(config, deps) {
@@ -15724,8 +15836,8 @@ function installAgent(config, deps) {
15724
15836
  const run3 = deps.run ?? defaultRun;
15725
15837
  const releaseLock2 = (deps.acquireInstallLock ?? (() => defaultAcquireInstallLock(home2)))();
15726
15838
  try {
15727
- mkdirp(join26(home2, ".syntaur", "logs"));
15728
- mkdirp(join26(home2, "Library", "LaunchAgents"));
15839
+ mkdirp(join27(home2, ".syntaur", "logs"));
15840
+ mkdirp(join27(home2, "Library", "LaunchAgents"));
15729
15841
  writeFile17(plistPath, buildPlist(spec));
15730
15842
  const domain = `gui/${uid}`;
15731
15843
  run3("launchctl", ["bootout", `${domain}/${spec.label}`]);
@@ -20602,6 +20714,55 @@ function createAgentSessionsRouter(projectsDir2, broadcast, assignmentsDir2) {
20602
20714
  init_config2();
20603
20715
  init_agents_schema();
20604
20716
  import { Router as Router5 } from "express";
20717
+
20718
+ // src/targets/agent-definitions.ts
20719
+ init_parser();
20720
+ init_registry();
20721
+ import { readdir as readdir16, readFile as readFile21 } from "fs/promises";
20722
+ import { join as join6 } from "path";
20723
+ async function collectMarkdown(dir, out) {
20724
+ let entries;
20725
+ try {
20726
+ entries = await readdir16(dir, { withFileTypes: true });
20727
+ } catch {
20728
+ return;
20729
+ }
20730
+ for (const entry of entries) {
20731
+ const full = join6(dir, entry.name);
20732
+ if (entry.isDirectory()) {
20733
+ await collectMarkdown(full, out);
20734
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
20735
+ out.push(full);
20736
+ }
20737
+ }
20738
+ }
20739
+ async function discoverClaudeAgents(root) {
20740
+ const dir = root ?? getAgentTarget("claude")?.agentsDir;
20741
+ if (!dir) return [];
20742
+ const files = [];
20743
+ await collectMarkdown(dir, files);
20744
+ files.sort();
20745
+ const byName = /* @__PURE__ */ new Map();
20746
+ for (const file of files) {
20747
+ let content;
20748
+ try {
20749
+ content = await readFile21(file, "utf-8");
20750
+ } catch {
20751
+ continue;
20752
+ }
20753
+ const [frontmatter] = extractFrontmatter(content);
20754
+ if (!frontmatter) continue;
20755
+ const name = getField(frontmatter, "name")?.trim();
20756
+ if (!name) continue;
20757
+ if (byName.has(name)) continue;
20758
+ const description = getField(frontmatter, "description")?.trim() || void 0;
20759
+ const model = getField(frontmatter, "model")?.trim() || void 0;
20760
+ byName.set(name, { name, description, model, path: file });
20761
+ }
20762
+ return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
20763
+ }
20764
+
20765
+ // src/dashboard/api-agents.ts
20605
20766
  function mapAgentErrorToFieldErrors(err2) {
20606
20767
  const message = err2.message;
20607
20768
  let match = message.match(/^agent id "([^"]*)" is invalid/);
@@ -20689,6 +20850,38 @@ function mapAgentErrorToFieldErrors(err2) {
20689
20850
  ]
20690
20851
  };
20691
20852
  }
20853
+ match = message.match(/^agent "([^"]+)" has invalid agentName/);
20854
+ if (match) {
20855
+ return {
20856
+ error: message,
20857
+ fieldErrors: [{ id: match[1], field: "agentName", message: "must be a single line" }]
20858
+ };
20859
+ }
20860
+ match = message.match(/^agent "([^"]+)" has invalid workdir/);
20861
+ if (match) {
20862
+ return {
20863
+ error: message,
20864
+ fieldErrors: [{ id: match[1], field: "workdir", message: "must be a single line" }]
20865
+ };
20866
+ }
20867
+ match = message.match(/^agent "([^"]+)" sets both agentName and workdir/);
20868
+ if (match) {
20869
+ return {
20870
+ error: message,
20871
+ fieldErrors: [
20872
+ { id: match[1], field: "workdir", message: "agentName and workdir are mutually exclusive" }
20873
+ ]
20874
+ };
20875
+ }
20876
+ match = message.match(/^agent "([^"]+)" sets both agentName and model/);
20877
+ if (match) {
20878
+ return {
20879
+ error: message,
20880
+ fieldErrors: [
20881
+ { id: match[1], field: "model", message: "remove the profile model; the agent defines its own" }
20882
+ ]
20883
+ };
20884
+ }
20692
20885
  return { error: message };
20693
20886
  }
20694
20887
  function coerceAgentRow(raw2, index) {
@@ -20836,10 +21029,47 @@ function coerceAgentRow(raw2, index) {
20836
21029
  }
20837
21030
  if (entry.launchPrompt.trim()) cleaned.launchPrompt = entry.launchPrompt;
20838
21031
  }
21032
+ if (entry.agentName !== void 0) {
21033
+ if (typeof entry.agentName !== "string") {
21034
+ return {
21035
+ ok: false,
21036
+ status: 400,
21037
+ body: {
21038
+ error: `agents[${index}].agentName must be a string`,
21039
+ fieldErrors: [{ id, field: "agentName", message: "agentName must be a string" }]
21040
+ }
21041
+ };
21042
+ }
21043
+ const agentName = entry.agentName.trim();
21044
+ if (agentName) cleaned.agentName = agentName;
21045
+ }
21046
+ if (entry.workdir !== void 0) {
21047
+ if (typeof entry.workdir !== "string") {
21048
+ return {
21049
+ ok: false,
21050
+ status: 400,
21051
+ body: {
21052
+ error: `agents[${index}].workdir must be a string`,
21053
+ fieldErrors: [{ id, field: "workdir", message: "workdir must be a string" }]
21054
+ }
21055
+ };
21056
+ }
21057
+ const workdir = entry.workdir.trim();
21058
+ if (workdir) cleaned.workdir = workdir;
21059
+ }
20839
21060
  return { ok: true, value: cleaned };
20840
21061
  }
20841
21062
  function createAgentsRouter() {
20842
21063
  const router = Router5();
21064
+ router.get("/claude-discovered", async (_req, res) => {
21065
+ try {
21066
+ const agents = await discoverClaudeAgents();
21067
+ res.json({ agents });
21068
+ } catch (err2) {
21069
+ console.error("Error discovering Claude agents:", err2);
21070
+ res.status(500).json({ error: "Failed to discover Claude agents" });
21071
+ }
21072
+ });
20843
21073
  router.get("/", async (_req, res) => {
20844
21074
  try {
20845
21075
  const config = await readConfig();
@@ -20902,8 +21132,8 @@ import { Router as Router6 } from "express";
20902
21132
  // src/utils/terminal-probe.ts
20903
21133
  import { spawnSync as spawnSync4 } from "child_process";
20904
21134
  import { existsSync as existsSync2 } from "fs";
20905
- import { homedir as homedir4 } from "os";
20906
- import { join as join5 } from "path";
21135
+ import { homedir as homedir5 } from "os";
21136
+ import { join as join7 } from "path";
20907
21137
  var APP_BUNDLE_IDS = {
20908
21138
  "terminal-app": "com.apple.Terminal",
20909
21139
  iterm: "com.googlecode.iterm2",
@@ -20920,7 +21150,7 @@ var APP_FIXED_PATHS = {
20920
21150
  "terminal-app": "/System/Applications/Utilities/Terminal.app"
20921
21151
  };
20922
21152
  function defaultApplicationsDirs() {
20923
- return ["/Applications", join5(homedir4(), "Applications")];
21153
+ return ["/Applications", join7(homedir5(), "Applications")];
20924
21154
  }
20925
21155
  function findAppBundle(terminal, dirs = defaultApplicationsDirs()) {
20926
21156
  const fixed = APP_FIXED_PATHS[terminal];
@@ -20928,7 +21158,7 @@ function findAppBundle(terminal, dirs = defaultApplicationsDirs()) {
20928
21158
  const bundleName = APP_BUNDLE_NAMES[terminal];
20929
21159
  if (bundleName) {
20930
21160
  for (const dir of dirs) {
20931
- const candidate = join5(dir, bundleName);
21161
+ const candidate = join7(dir, bundleName);
20932
21162
  if (existsSync2(candidate)) return candidate;
20933
21163
  }
20934
21164
  }
@@ -20958,11 +21188,11 @@ var defaultCmuxLsappinfoRunner = () => {
20958
21188
  function resolveCmuxCli(applicationsDirsOverride, cliDirsOverride, lsappinfoRunnerOverride) {
20959
21189
  const bundle = findAppBundle("cmux", applicationsDirsOverride);
20960
21190
  if (bundle) {
20961
- const cli = join5(bundle, "Contents/Resources/bin/cmux");
21191
+ const cli = join7(bundle, "Contents/Resources/bin/cmux");
20962
21192
  if (existsSync2(cli)) return cli;
20963
21193
  }
20964
21194
  for (const dir of cliDirsOverride ?? CMUX_CLI_DIRS) {
20965
- const cli = join5(dir, "cmux");
21195
+ const cli = join7(dir, "cmux");
20966
21196
  if (existsSync2(cli)) return cli;
20967
21197
  }
20968
21198
  if (process.platform === "darwin") {
@@ -20971,7 +21201,7 @@ function resolveCmuxCli(applicationsDirsOverride, cliDirsOverride, lsappinfoRunn
20971
21201
  if (stdout) {
20972
21202
  const match = stdout.match(/"LSBundlePath"="([^"]+)"/);
20973
21203
  if (match) {
20974
- const cli = join5(match[1], "Contents/Resources/bin/cmux");
21204
+ const cli = join7(match[1], "Contents/Resources/bin/cmux");
20975
21205
  if (existsSync2(cli)) return cli;
20976
21206
  }
20977
21207
  }
@@ -21058,6 +21288,7 @@ function parseOpenUrl(input4) {
21058
21288
  }
21059
21289
  const assignmentVals = url.searchParams.getAll("assignment");
21060
21290
  const sessionVals = url.searchParams.getAll("session");
21291
+ const standaloneVals = url.searchParams.getAll("standalone");
21061
21292
  if (assignmentVals.length > 1) {
21062
21293
  throw new OpenUrlError(
21063
21294
  "duplicate-param",
@@ -21070,10 +21301,17 @@ function parseOpenUrl(input4) {
21070
21301
  "URL has more than one `session` query param"
21071
21302
  );
21072
21303
  }
21073
- if (assignmentVals.length === 1 && sessionVals.length === 1) {
21304
+ if (standaloneVals.length > 1) {
21305
+ throw new OpenUrlError(
21306
+ "duplicate-param",
21307
+ "URL has more than one `standalone` query param"
21308
+ );
21309
+ }
21310
+ const kindsPresent = assignmentVals.length + sessionVals.length + standaloneVals.length;
21311
+ if (kindsPresent > 1) {
21074
21312
  throw new OpenUrlError(
21075
21313
  "both-ids",
21076
- "URL has both `assignment` and `session` query params \u2014 only one is allowed"
21314
+ "URL has more than one of `assignment`, `session`, `standalone` \u2014 only one is allowed"
21077
21315
  );
21078
21316
  }
21079
21317
  const terminalVals = url.searchParams.getAll("terminal");
@@ -21123,6 +21361,37 @@ function parseOpenUrl(input4) {
21123
21361
  }
21124
21362
  prompt = value;
21125
21363
  }
21364
+ const agentNameVals = url.searchParams.getAll("agentName");
21365
+ if (agentNameVals.length > 1) {
21366
+ throw new OpenUrlError(
21367
+ "duplicate-param",
21368
+ "URL has more than one `agentName` query param"
21369
+ );
21370
+ }
21371
+ let agentName;
21372
+ if (agentNameVals.length === 1 && agentNameVals[0].trim() !== "") {
21373
+ const value = agentNameVals[0];
21374
+ if (value.length > MAX_OPEN_PROMPT_LENGTH) {
21375
+ throw new OpenUrlError(
21376
+ "invalid-prompt",
21377
+ `\`agentName\` query param exceeds ${MAX_OPEN_PROMPT_LENGTH} characters`
21378
+ );
21379
+ }
21380
+ agentName = value;
21381
+ }
21382
+ if (standaloneVals.length === 1) {
21383
+ const id = standaloneVals[0];
21384
+ if (id.trim() === "") {
21385
+ throw new OpenUrlError("missing-id", "`standalone` query param is empty");
21386
+ }
21387
+ return {
21388
+ kind: "standalone",
21389
+ id,
21390
+ ...terminal ? { terminal } : {},
21391
+ // standalone identifies the agent by `id`; keep an optional prompt override.
21392
+ ...prompt !== void 0 ? { prompt } : {}
21393
+ };
21394
+ }
21126
21395
  if (assignmentVals.length === 1) {
21127
21396
  const id = assignmentVals[0];
21128
21397
  if (id.trim() === "") {
@@ -21137,7 +21406,8 @@ function parseOpenUrl(input4) {
21137
21406
  ...terminal ? { terminal } : {},
21138
21407
  ...agent ? { agent } : {},
21139
21408
  // assignment-only; keep '' (presence-significant) — hence !== undefined.
21140
- ...prompt !== void 0 ? { prompt } : {}
21409
+ ...prompt !== void 0 ? { prompt } : {},
21410
+ ...agentName ? { agentName } : {}
21141
21411
  };
21142
21412
  }
21143
21413
  if (sessionVals.length === 1) {
@@ -21173,7 +21443,7 @@ function parseOpenUrl(input4) {
21173
21443
  }
21174
21444
  throw new OpenUrlError(
21175
21445
  "missing-id",
21176
- "URL must include either `assignment=<id>` or `session=<id>`"
21446
+ "URL must include one of `assignment=<id>`, `session=<id>`, or `standalone=<agentId>`"
21177
21447
  );
21178
21448
  }
21179
21449
 
@@ -21249,8 +21519,60 @@ async function resolveLaunchPlan(input4) {
21249
21519
  if (input4.kind === "assignment") {
21250
21520
  return resolveAssignmentPlan(input4, terminal);
21251
21521
  }
21522
+ if (input4.kind === "standalone") {
21523
+ return resolveStandalonePlan(input4, terminal);
21524
+ }
21252
21525
  return resolveSessionPlan(input4, terminal);
21253
21526
  }
21527
+ async function resolveStandalonePlan(input4, terminal) {
21528
+ const agentId = input4.agentId ?? input4.id;
21529
+ if (!agentId) {
21530
+ throw new LaunchError(
21531
+ "agent-not-configured",
21532
+ "A standalone launch requires an agent id."
21533
+ );
21534
+ }
21535
+ const agent = getAgents(input4.config).find((a) => a.id === agentId);
21536
+ if (!agent) {
21537
+ throw new LaunchError(
21538
+ "agent-not-configured",
21539
+ `Agent "${agentId}" requested for a standalone launch is not in your agents list.`
21540
+ );
21541
+ }
21542
+ const wd = agent.workdir?.trim();
21543
+ if (!wd) {
21544
+ throw new LaunchError(
21545
+ "workspace-path-invalid",
21546
+ `Agent "${agent.id}" has no workdir \u2014 a standalone launch requires a valid workdir.`
21547
+ );
21548
+ }
21549
+ const expanded = expandHome(wd);
21550
+ if (!isExistingDir(expanded)) {
21551
+ throw new LaunchError(
21552
+ "workspace-path-invalid",
21553
+ `Agent "${agent.id}" workdir ${agent.workdir} (resolved ${expanded}) is not an existing directory \u2014 a standalone launch requires a valid workdir.`
21554
+ );
21555
+ }
21556
+ const knownPlaybookSlugs = await listPlaybookSlugs(playbooksDir());
21557
+ const template = input4.promptOverride !== void 0 ? input4.promptOverride : agent.launchPrompt;
21558
+ const { prompt, warnings: promptWarnings } = resolveLaunchPrompt({
21559
+ template,
21560
+ playbook: agent.playbook,
21561
+ projectSlug: null,
21562
+ knownPlaybookSlugs
21563
+ });
21564
+ const { argv, shellFallbackWarning } = buildFreshArgv(agent, prompt);
21565
+ return {
21566
+ terminal,
21567
+ cwd: expanded,
21568
+ argv,
21569
+ env: process.env,
21570
+ agentId: agent.id,
21571
+ fallbackWarning: null,
21572
+ shellFallbackWarning,
21573
+ promptWarnings
21574
+ };
21575
+ }
21254
21576
  async function resolveAssignmentPlan(input4, terminal) {
21255
21577
  const resolved = await resolveAssignmentById(
21256
21578
  input4.projectsDir,
@@ -21274,17 +21596,6 @@ async function resolveAssignmentPlan(input4, terminal) {
21274
21596
  `Assignment ${input4.id} resolver returned a directory but detail could not be loaded`
21275
21597
  );
21276
21598
  }
21277
- const picked = resolveWorkspaceCwd({
21278
- worktreePath: detail.workspace.worktreePath,
21279
- repository: detail.workspace.repository,
21280
- branch: detail.workspace.branch,
21281
- assignmentSlug: resolved.assignmentSlug
21282
- });
21283
- if (picked.cwd === null) {
21284
- throw new LaunchError("workspace-path-invalid", picked.invalidReason);
21285
- }
21286
- const cwd = picked.cwd;
21287
- const fallbackWarning = picked.fallbackWarning;
21288
21599
  let agent;
21289
21600
  if (input4.agentId) {
21290
21601
  const found = getAgents(input4.config).find((a) => a.id === input4.agentId);
@@ -21298,6 +21609,30 @@ async function resolveAssignmentPlan(input4, terminal) {
21298
21609
  } else {
21299
21610
  agent = pickAgent(input4.config);
21300
21611
  }
21612
+ if (input4.agentName !== void 0) {
21613
+ agent = { ...agent, agentName: input4.agentName };
21614
+ }
21615
+ const picked = resolveWorkspaceCwd({
21616
+ worktreePath: detail.workspace.worktreePath,
21617
+ repository: detail.workspace.repository,
21618
+ branch: detail.workspace.branch,
21619
+ assignmentSlug: resolved.assignmentSlug
21620
+ });
21621
+ if (picked.cwd === null) {
21622
+ throw new LaunchError("workspace-path-invalid", picked.invalidReason);
21623
+ }
21624
+ const launchCwd = resolveLaunchCwd(agent, picked.cwd);
21625
+ if (launchCwd.invalidReason) {
21626
+ throw new LaunchError("workspace-path-invalid", launchCwd.invalidReason);
21627
+ }
21628
+ const cwd = launchCwd.spawnCwd;
21629
+ const fallbackWarning = picked.fallbackWarning;
21630
+ const promptWarningsExtra = [];
21631
+ if (input4.agentName !== void 0 && input4.agentName.trim() && agent.model?.trim()) {
21632
+ promptWarningsExtra.push(
21633
+ `profile model "${agent.model.trim()}" suppressed: agent "${input4.agentName.trim()}" defines its own model`
21634
+ );
21635
+ }
21301
21636
  const knownPlaybookSlugs = await listPlaybookSlugs(playbooksDir());
21302
21637
  const template = input4.promptOverride !== void 0 ? input4.promptOverride : agent.launchPrompt;
21303
21638
  const { prompt, warnings: promptWarnings } = resolveLaunchPrompt({
@@ -21307,6 +21642,8 @@ async function resolveAssignmentPlan(input4, terminal) {
21307
21642
  assignmentDir: resolved.assignmentDir,
21308
21643
  projectSlug: resolved.projectSlug,
21309
21644
  assignmentSlug: resolved.assignmentSlug,
21645
+ worktreePath: launchCwd.worktreePath,
21646
+ spawnCwd: launchCwd.spawnCwd,
21310
21647
  knownPlaybookSlugs
21311
21648
  });
21312
21649
  const { argv, shellFallbackWarning } = buildFreshArgv(agent, prompt);
@@ -21318,7 +21655,7 @@ async function resolveAssignmentPlan(input4, terminal) {
21318
21655
  agentId: agent.id,
21319
21656
  fallbackWarning,
21320
21657
  shellFallbackWarning,
21321
- promptWarnings
21658
+ promptWarnings: [...promptWarningsExtra, ...promptWarnings]
21322
21659
  };
21323
21660
  }
21324
21661
  async function resolveSessionPlan(input4, terminal) {
@@ -21395,8 +21732,8 @@ async function resolveSessionPlan(input4, terminal) {
21395
21732
  // src/launch/execute.ts
21396
21733
  init_launch();
21397
21734
  import { spawn as spawn3 } from "child_process";
21398
- import { homedir as homedir5 } from "os";
21399
- import { basename as basename5, join as join6, resolve as resolve30 } from "path";
21735
+ import { homedir as homedir6 } from "os";
21736
+ import { basename as basename5, join as join8, resolve as resolve32 } from "path";
21400
21737
  init_fs();
21401
21738
  init_config2();
21402
21739
  init_session_id();
@@ -21502,7 +21839,7 @@ async function registerLaunchAtBirth(plan, child) {
21502
21839
  const sessionId = plan.session?.sessionId ?? null;
21503
21840
  const procStart = captureProcessStartedAt(pid);
21504
21841
  const envDir = process.env.SYNTAUR_RUNTIME_SESSIONS_DIR;
21505
- const markerDir = envDir && envDir.length > 0 ? envDir : join6(homedir5(), ".syntaur", "runtime", "sessions");
21842
+ const markerDir = envDir && envDir.length > 0 ? envDir : join8(homedir6(), ".syntaur", "runtime", "sessions");
21506
21843
  writeRuntimeMarker(
21507
21844
  pid,
21508
21845
  {
@@ -21515,7 +21852,7 @@ async function registerLaunchAtBirth(plan, child) {
21515
21852
  markerDir
21516
21853
  );
21517
21854
  if (!sessionId) return;
21518
- if (autoTrack === "workspaces-only" && !await fileExists(resolve30(plan.cwd, ".syntaur", "context.json"))) {
21855
+ if (autoTrack === "workspaces-only" && !await fileExists(resolve32(plan.cwd, ".syntaur", "context.json"))) {
21519
21856
  return;
21520
21857
  }
21521
21858
  const { initSessionDb: initSessionDb2 } = await Promise.resolve().then(() => (init_session_db(), session_db_exports));
@@ -21694,7 +22031,7 @@ init_launch_prompt();
21694
22031
  init_paths();
21695
22032
  init_fs();
21696
22033
  import { fileURLToPath as fileURLToPath2 } from "url";
21697
- import { dirname as dirname7, resolve as resolve31, join as join7 } from "path";
22034
+ import { dirname as dirname7, resolve as resolve33, join as join9 } from "path";
21698
22035
  import { realpathSync, readFileSync as readFileSync3, mkdirSync as mkdirSync2 } from "fs";
21699
22036
  var NPX_PATTERNS = [
21700
22037
  { kind: "npm", re: /\/_npx\/([^/]+)\/node_modules(?:\/|$)/ },
@@ -21720,7 +22057,7 @@ function normalizeSlashes(p) {
21720
22057
  }
21721
22058
  function detectInstallKind(scriptUrl, opts = {}) {
21722
22059
  const realpath3 = opts.realpath ?? realpathSync.native;
21723
- const readFile81 = opts.readFile ?? ((p) => readFileSync3(p, "utf-8"));
22060
+ const readFile82 = opts.readFile ?? ((p) => readFileSync3(p, "utf-8"));
21724
22061
  const ua = opts.envUserAgent !== void 0 ? opts.envUserAgent : process.env.npm_config_user_agent ?? "";
21725
22062
  const resolved = resolveScriptPath(scriptUrl, realpath3);
21726
22063
  if (resolved === null) {
@@ -21738,10 +22075,10 @@ function detectInstallKind(scriptUrl, opts = {}) {
21738
22075
  }
21739
22076
  let dir = dirname7(resolved);
21740
22077
  for (let depth = 0; depth < 8; depth++) {
21741
- const pkgJsonPath = join7(dir, "package.json");
22078
+ const pkgJsonPath = join9(dir, "package.json");
21742
22079
  let raw2;
21743
22080
  try {
21744
- raw2 = readFile81(pkgJsonPath);
22081
+ raw2 = readFile82(pkgJsonPath);
21745
22082
  } catch {
21746
22083
  const parent2 = dirname7(dir);
21747
22084
  if (parent2 === dir) break;
@@ -21773,13 +22110,13 @@ function extractNpxHash(scriptUrl, opts = {}) {
21773
22110
  return null;
21774
22111
  }
21775
22112
  function nudgeStampDir() {
21776
- return resolve31(syntaurRoot(), "npx-handler-nudge");
22113
+ return resolve33(syntaurRoot(), "npx-handler-nudge");
21777
22114
  }
21778
22115
  function sanitizeHash(hash) {
21779
22116
  return hash.replace(/[^A-Za-z0-9_-]/g, "_") || "_";
21780
22117
  }
21781
22118
  function nudgeStampPath(hash) {
21782
- return join7(nudgeStampDir(), sanitizeHash(hash));
22119
+ return join9(nudgeStampDir(), sanitizeHash(hash));
21783
22120
  }
21784
22121
  async function hasNudgedHash(hash) {
21785
22122
  return fileExists(nudgeStampPath(hash));
@@ -21980,7 +22317,8 @@ function createLaunchPreflightRouter(projectsDir2, assignmentsDir2) {
21980
22317
  playbook: agent.playbook,
21981
22318
  projectSlug: resolved.projectSlug,
21982
22319
  assignmentSlug: resolved.assignmentSlug,
21983
- id: resolved.id
22320
+ id: resolved.id,
22321
+ workdir: agent.workdir
21984
22322
  });
21985
22323
  res.json({ template, knownPlaybookSlugs });
21986
22324
  } catch (error) {
@@ -22224,7 +22562,7 @@ init_frontmatter();
22224
22562
  init_event_emit();
22225
22563
  init_assignment_walk();
22226
22564
  init_timestamp();
22227
- import { readFile as readFile20, writeFile as writeFile5, rm as rm2 } from "fs/promises";
22565
+ import { readFile as readFile22, writeFile as writeFile5, rm as rm2 } from "fs/promises";
22228
22566
  import { dirname as dirname8 } from "path";
22229
22567
  var StatusResolutionError = class extends Error {
22230
22568
  constructor(message, code) {
@@ -22243,7 +22581,7 @@ async function scanAssignmentsByStatus(projectsDir2, standaloneDir, ids) {
22243
22581
  const assignmentPath = `${entry.assignmentDir}/assignment.md`;
22244
22582
  let content;
22245
22583
  try {
22246
- content = await readFile20(assignmentPath, "utf-8");
22584
+ content = await readFile22(assignmentPath, "utf-8");
22247
22585
  } catch (err2) {
22248
22586
  const code = err2?.code;
22249
22587
  if (code === "ENOENT") {
@@ -22306,7 +22644,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
22306
22644
  const list = affected.get(r.id) ?? [];
22307
22645
  for (const a of list) {
22308
22646
  try {
22309
- const content = await readFile20(a.path, "utf-8");
22647
+ const content = await readFile22(a.path, "utf-8");
22310
22648
  buffer.set(a.path, content);
22311
22649
  } catch (err2) {
22312
22650
  const code = err2?.code;
@@ -22335,7 +22673,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
22335
22673
  for (const a of list) {
22336
22674
  let current;
22337
22675
  try {
22338
- current = await readFile20(a.path, "utf-8");
22676
+ current = await readFile22(a.path, "utf-8");
22339
22677
  } catch (err2) {
22340
22678
  const code = err2?.code;
22341
22679
  if (code === "ENOENT") {
@@ -22406,7 +22744,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
22406
22744
  const list = affected.get(r.id) ?? [];
22407
22745
  for (const a of list) {
22408
22746
  try {
22409
- const current = await readFile20(a.path, "utf-8");
22747
+ const current = await readFile22(a.path, "utf-8");
22410
22748
  const fm = parseAssignmentFrontmatter(current);
22411
22749
  if (fm.status !== r.id) {
22412
22750
  console.warn(
@@ -22457,7 +22795,7 @@ async function scanAssignmentsReferencingStatus(projectsDir2, standaloneDir, id)
22457
22795
  const assignmentPath = `${entry.assignmentDir}/assignment.md`;
22458
22796
  let content;
22459
22797
  try {
22460
- content = await readFile20(assignmentPath, "utf-8");
22798
+ content = await readFile22(assignmentPath, "utf-8");
22461
22799
  } catch (err2) {
22462
22800
  const code = err2?.code;
22463
22801
  if (code === "ENOENT") continue;
@@ -22917,7 +23255,7 @@ import { Router as Router12 } from "express";
22917
23255
  init_paths();
22918
23256
  import Database3 from "better-sqlite3";
22919
23257
  import { randomUUID as randomUUID3 } from "crypto";
22920
- import { resolve as resolve32 } from "path";
23258
+ import { resolve as resolve34 } from "path";
22921
23259
  var db3 = null;
22922
23260
  var LEASE_SCHEMA_VERSION = "1";
22923
23261
  var SCHEMA_SQL3 = `
@@ -23041,7 +23379,7 @@ function isBusyError(err2) {
23041
23379
  }
23042
23380
  function initLeasesDb(dbPath) {
23043
23381
  if (db3) return db3;
23044
- const finalPath = dbPath ?? resolve32(syntaurRoot(), "syntaur.db");
23382
+ const finalPath = dbPath ?? resolve34(syntaurRoot(), "syntaur.db");
23045
23383
  db3 = new Database3(finalPath);
23046
23384
  db3.pragma("journal_mode = WAL");
23047
23385
  db3.pragma("busy_timeout = 5000");
@@ -23607,15 +23945,15 @@ init_fs();
23607
23945
  init_paths();
23608
23946
  init_timestamp();
23609
23947
  import { randomUUID as randomUUID4 } from "crypto";
23610
- import { readFile as readFile21, readdir as readdir15, rm as rm3 } from "fs/promises";
23611
- import { resolve as resolve33 } from "path";
23948
+ import { readFile as readFile23, readdir as readdir17, rm as rm3 } from "fs/promises";
23949
+ import { resolve as resolve35 } from "path";
23612
23950
  function schedulesDir() {
23613
23951
  const override3 = process.env.SYNTAUR_SCHEDULES_DIR;
23614
- if (override3 && override3.length > 0) return resolve33(override3);
23615
- return resolve33(syntaurRoot(), "schedules");
23952
+ if (override3 && override3.length > 0) return resolve35(override3);
23953
+ return resolve35(syntaurRoot(), "schedules");
23616
23954
  }
23617
23955
  function jobPath(id) {
23618
- return resolve33(schedulesDir(), `${jobFileSafe(id)}.md`);
23956
+ return resolve35(schedulesDir(), `${jobFileSafe(id)}.md`);
23619
23957
  }
23620
23958
  function jobFileSafe(id) {
23621
23959
  if (!/^[A-Za-z0-9_-]+$/.test(id)) {
@@ -23740,7 +24078,7 @@ function validateJob(raw2) {
23740
24078
  async function readJob(id) {
23741
24079
  const path = jobPath(id);
23742
24080
  if (!await fileExists(path)) return null;
23743
- return parseJobFile(await readFile21(path, "utf-8"));
24081
+ return parseJobFile(await readFile23(path, "utf-8"));
23744
24082
  }
23745
24083
  async function writeJob(job) {
23746
24084
  const next = { ...job, updatedAt: nowTimestamp() };
@@ -23750,7 +24088,7 @@ async function writeJob(job) {
23750
24088
  async function listJobs() {
23751
24089
  let names;
23752
24090
  try {
23753
- names = await readdir15(schedulesDir());
24091
+ names = await readdir17(schedulesDir());
23754
24092
  } catch {
23755
24093
  return [];
23756
24094
  }
@@ -23758,7 +24096,7 @@ async function listJobs() {
23758
24096
  for (const name of names) {
23759
24097
  if (!name.endsWith(".md")) continue;
23760
24098
  try {
23761
- jobs.push(parseJobFile(await readFile21(resolve33(schedulesDir(), name), "utf-8")));
24099
+ jobs.push(parseJobFile(await readFile23(resolve35(schedulesDir(), name), "utf-8")));
23762
24100
  } catch {
23763
24101
  }
23764
24102
  }
@@ -23768,13 +24106,13 @@ async function listJobs() {
23768
24106
 
23769
24107
  // src/schedules/event-log.ts
23770
24108
  init_timestamp();
23771
- import { appendFile, readFile as readFile22, mkdir as mkdir3 } from "fs/promises";
23772
- import { dirname as dirname9, resolve as resolve34 } from "path";
24109
+ import { appendFile, readFile as readFile24, mkdir as mkdir3 } from "fs/promises";
24110
+ import { dirname as dirname9, resolve as resolve36 } from "path";
23773
24111
  function eventLogPath(jobId) {
23774
24112
  if (!/^[A-Za-z0-9_-]+$/.test(jobId)) {
23775
24113
  throw new Error(`Unsafe schedule id: ${JSON.stringify(jobId)}`);
23776
24114
  }
23777
- return resolve34(schedulesDir(), `${jobId}.jsonl`);
24115
+ return resolve36(schedulesDir(), `${jobId}.jsonl`);
23778
24116
  }
23779
24117
  async function appendEvent(jobId, type, data) {
23780
24118
  const event = { type, at: nowTimestamp(), ...data ? { data } : {} };
@@ -23787,7 +24125,7 @@ async function appendEvent(jobId, type, data) {
23787
24125
  async function readEvents(jobId) {
23788
24126
  let content;
23789
24127
  try {
23790
- content = await readFile22(eventLogPath(jobId), "utf-8");
24128
+ content = await readFile24(eventLogPath(jobId), "utf-8");
23791
24129
  } catch {
23792
24130
  return [];
23793
24131
  }
@@ -23806,8 +24144,8 @@ async function readEvents(jobId) {
23806
24144
 
23807
24145
  // src/schedules/attempt.ts
23808
24146
  import { createHash as createHash3 } from "crypto";
23809
- import { open as open4, readFile as readFile23, stat as stat5, unlink as unlink6 } from "fs/promises";
23810
- import { resolve as resolve35 } from "path";
24147
+ import { open as open4, readFile as readFile25, stat as stat5, unlink as unlink6 } from "fs/promises";
24148
+ import { resolve as resolve37 } from "path";
23811
24149
 
23812
24150
  // src/schedules/triggers.ts
23813
24151
  import { Cron } from "croner";
@@ -23986,7 +24324,7 @@ var LOCK_WAIT_MS2 = 50;
23986
24324
  var LOCK_MAX_WAITS2 = 100;
23987
24325
  var MAX_CONSUMED_EDGES = 50;
23988
24326
  async function acquireJobLock(id) {
23989
- const lockPath = resolve35(schedulesDir(), `${id}.lock`);
24327
+ const lockPath = resolve37(schedulesDir(), `${id}.lock`);
23990
24328
  const token = `${process.pid}:${createHash3("sha256").update(`${Math.random()}${Date.now()}`).digest("hex").slice(0, 12)}`;
23991
24329
  for (let attempt = 0; attempt <= LOCK_MAX_WAITS2; attempt++) {
23992
24330
  try {
@@ -23995,7 +24333,7 @@ async function acquireJobLock(id) {
23995
24333
  await handle.close();
23996
24334
  return async () => {
23997
24335
  try {
23998
- const current = await readFile23(lockPath, "utf-8");
24336
+ const current = await readFile25(lockPath, "utf-8");
23999
24337
  if (current === token) await unlink6(lockPath);
24000
24338
  } catch {
24001
24339
  }
@@ -24301,7 +24639,7 @@ async function reapStale(jobs, deps) {
24301
24639
 
24302
24640
  // src/schedules/unattended.ts
24303
24641
  import { existsSync as existsSync3 } from "fs";
24304
- import { resolve as resolve36 } from "path";
24642
+ import { resolve as resolve38 } from "path";
24305
24643
  var UnattendedRefusalError = class extends Error {
24306
24644
  constructor(message) {
24307
24645
  super(message);
@@ -24317,7 +24655,7 @@ function assertUnattendedTerminalSupported(terminal) {
24317
24655
  }
24318
24656
  function isKillSwitchEngaged() {
24319
24657
  if (process.env.SYNTAUR_SCHEDULES_DISABLED === "1") return true;
24320
- return existsSync3(resolve36(schedulesDir(), "KILL"));
24658
+ return existsSync3(resolve38(schedulesDir(), "KILL"));
24321
24659
  }
24322
24660
  function canFire(job, deps) {
24323
24661
  const killSwitch = deps.killSwitch ?? isKillSwitchEngaged;
@@ -24468,20 +24806,20 @@ init_config2();
24468
24806
  init_paths();
24469
24807
  init_assignment_resolver();
24470
24808
  init_frontmatter();
24471
- import { readFile as readFile24 } from "fs/promises";
24472
- import { resolve as resolve37 } from "path";
24809
+ import { readFile as readFile26 } from "fs/promises";
24810
+ import { resolve as resolve39 } from "path";
24473
24811
 
24474
24812
  // src/schedules/launch-ack.ts
24475
24813
  init_session_id();
24476
24814
  import { readdirSync as readdirSync2 } from "fs";
24477
- import { homedir as homedir6 } from "os";
24478
- import { join as join8 } from "path";
24815
+ import { homedir as homedir7 } from "os";
24816
+ import { join as join10 } from "path";
24479
24817
  function defaultSleep(ms) {
24480
24818
  return new Promise((r) => setTimeout(r, ms));
24481
24819
  }
24482
24820
  function runtimeSessionsDir() {
24483
24821
  const override3 = process.env.SYNTAUR_RUNTIME_SESSIONS_DIR;
24484
- return override3 && override3.length > 0 ? override3 : join8(homedir6(), ".syntaur", "runtime", "sessions");
24822
+ return override3 && override3.length > 0 ? override3 : join10(homedir7(), ".syntaur", "runtime", "sessions");
24485
24823
  }
24486
24824
  function defaultAckProbe(handle) {
24487
24825
  const dir = runtimeSessionsDir();
@@ -24529,7 +24867,7 @@ async function defaultReadAssignment(assignmentId) {
24529
24867
  const resolved = await resolveAssignmentById(projectsDir2, assignmentsDir(), assignmentId);
24530
24868
  if (!resolved) return null;
24531
24869
  try {
24532
- const content = await readFile24(resolve37(resolved.assignmentDir, "assignment.md"), "utf-8");
24870
+ const content = await readFile26(resolve39(resolved.assignmentDir, "assignment.md"), "utf-8");
24533
24871
  return parseAssignmentFrontmatter(content);
24534
24872
  } catch {
24535
24873
  return null;
@@ -24621,7 +24959,7 @@ import { Router as Router14 } from "express";
24621
24959
  // src/db/usage-db.ts
24622
24960
  init_paths();
24623
24961
  import Database4 from "better-sqlite3";
24624
- import { resolve as resolve38 } from "path";
24962
+ import { resolve as resolve40 } from "path";
24625
24963
  var db4 = null;
24626
24964
  var USAGE_SCHEMA_VERSION = "1";
24627
24965
  var SCHEMA_SQL4 = `
@@ -24695,7 +25033,7 @@ function nowIso2() {
24695
25033
  }
24696
25034
  function initUsageDb(dbPath) {
24697
25035
  if (db4) return db4;
24698
- const finalPath = dbPath ?? resolve38(syntaurRoot(), "syntaur.db");
25036
+ const finalPath = dbPath ?? resolve40(syntaurRoot(), "syntaur.db");
24699
25037
  db4 = new Database4(finalPath);
24700
25038
  db4.pragma("journal_mode = WAL");
24701
25039
  db4.pragma("busy_timeout = 5000");
@@ -25308,14 +25646,14 @@ init_fs();
25308
25646
  init_parser();
25309
25647
  init_events_db();
25310
25648
  import { Router as Router15 } from "express";
25311
- import { resolve as resolve39 } from "path";
25312
- import { readFile as readFile25 } from "fs/promises";
25649
+ import { resolve as resolve41 } from "path";
25650
+ import { readFile as readFile27 } from "fs/promises";
25313
25651
  function createEventsRouter(projectsDir2, assignmentsDir2) {
25314
25652
  const router = Router15();
25315
25653
  router.get("/projects/:slug/assignments/:aslug/events", async (req2, res) => {
25316
25654
  try {
25317
25655
  const { slug, aslug } = req2.params;
25318
- const assignmentMdPath2 = resolve39(
25656
+ const assignmentMdPath2 = resolve41(
25319
25657
  projectsDir2,
25320
25658
  slug,
25321
25659
  "assignments",
@@ -25336,7 +25674,7 @@ function createEventsRouter(projectsDir2, assignmentsDir2) {
25336
25674
  router.get("/standalone/assignments/:id/events", async (req2, res) => {
25337
25675
  try {
25338
25676
  const { id: dirId } = req2.params;
25339
- const assignmentMdPath2 = resolve39(assignmentsDir2, dirId, "assignment.md");
25677
+ const assignmentMdPath2 = resolve41(assignmentsDir2, dirId, "assignment.md");
25340
25678
  const id = await readAssignmentId2(assignmentMdPath2) ?? dirId;
25341
25679
  res.json({ events: loadEvents(id, req2.query) });
25342
25680
  } catch (error) {
@@ -25348,7 +25686,7 @@ function createEventsRouter(projectsDir2, assignmentsDir2) {
25348
25686
  }
25349
25687
  async function readAssignmentId2(assignmentMdPath2) {
25350
25688
  if (!await fileExists(assignmentMdPath2)) return null;
25351
- const content = await readFile25(assignmentMdPath2, "utf-8");
25689
+ const content = await readFile27(assignmentMdPath2, "utf-8");
25352
25690
  const [fm] = extractFrontmatter(content);
25353
25691
  const id = getField(fm, "id");
25354
25692
  return id && id.length > 0 ? id : null;
@@ -25393,8 +25731,8 @@ init_assignment_walk();
25393
25731
  init_parser();
25394
25732
  init_facts();
25395
25733
  init_state_machine();
25396
- import { resolve as resolve40 } from "path";
25397
- import { readFile as readFile26 } from "fs/promises";
25734
+ import { resolve as resolve42 } from "path";
25735
+ import { readFile as readFile28 } from "fs/promises";
25398
25736
 
25399
25737
  // src/inbox/types.ts
25400
25738
  var INBOX_CATEGORIES = [
@@ -25574,7 +25912,7 @@ async function computeInbox(opts) {
25574
25912
  if (opts.project !== void 0 && entry.projectSlug !== opts.project) continue;
25575
25913
  let parsed;
25576
25914
  try {
25577
- const content = await readFile26(resolve40(entry.assignmentDir, "assignment.md"), "utf-8");
25915
+ const content = await readFile28(resolve42(entry.assignmentDir, "assignment.md"), "utf-8");
25578
25916
  parsed = parseAssignmentFull(content);
25579
25917
  } catch {
25580
25918
  continue;
@@ -25617,10 +25955,10 @@ async function computeInbox(opts) {
25617
25955
  });
25618
25956
  }
25619
25957
  if (!typeFilter || typeFilter.has("question")) {
25620
- const commentsPath = resolve40(entry.assignmentDir, "comments.md");
25958
+ const commentsPath = resolve42(entry.assignmentDir, "comments.md");
25621
25959
  if (await fileExists(commentsPath)) {
25622
25960
  try {
25623
- const content = await readFile26(commentsPath, "utf-8");
25961
+ const content = await readFile28(commentsPath, "utf-8");
25624
25962
  const parsedComments = parseComments(content);
25625
25963
  for (const c2 of unresolvedQuestions(parsedComments.entries)) {
25626
25964
  const since = resolveSince("question", parsed, now, c2);
@@ -25733,8 +26071,8 @@ init_fs();
25733
26071
  init_playbook();
25734
26072
  init_playbooks();
25735
26073
  import { Router as Router17 } from "express";
25736
- import { resolve as resolve41 } from "path";
25737
- import { readFile as readFile27 } from "fs/promises";
26074
+ import { resolve as resolve43 } from "path";
26075
+ import { readFile as readFile29 } from "fs/promises";
25738
26076
  function statusForPlaybookError(code) {
25739
26077
  switch (code) {
25740
26078
  case "manifest":
@@ -25815,8 +26153,8 @@ function createPlaybooksRouter(playbooksDir3) {
25815
26153
  res.status(404).json({ error: `Playbook "${req2.params.slug}" not found` });
25816
26154
  return;
25817
26155
  }
25818
- const filePath = resolve41(playbooksDir3, resolved.filename);
25819
- const content = await readFile27(filePath, "utf-8");
26156
+ const filePath = resolve43(playbooksDir3, resolved.filename);
26157
+ const content = await readFile29(filePath, "utf-8");
25820
26158
  res.json({
25821
26159
  documentType: "playbook",
25822
26160
  title: `Edit Playbook: ${resolved.slug}`,
@@ -25841,7 +26179,7 @@ function createPlaybooksRouter(playbooksDir3) {
25841
26179
  return;
25842
26180
  }
25843
26181
  await ensureDir(playbooksDir3);
25844
- const filePath = resolve41(playbooksDir3, `${slug}.md`);
26182
+ const filePath = resolve43(playbooksDir3, `${slug}.md`);
25845
26183
  if (await fileExists(filePath)) {
25846
26184
  res.status(409).json({ error: `Playbook "${slug}" already exists` });
25847
26185
  return;
@@ -25865,7 +26203,7 @@ function createPlaybooksRouter(playbooksDir3) {
25865
26203
  res.status(404).json({ error: `Playbook "${req2.params.slug}" not found` });
25866
26204
  return;
25867
26205
  }
25868
- const filePath = resolve41(playbooksDir3, resolved.filename);
26206
+ const filePath = resolve43(playbooksDir3, resolved.filename);
25869
26207
  await writeFileForce(filePath, content);
25870
26208
  await rebuildPlaybookManifest(playbooksDir3);
25871
26209
  res.json({ slug: resolved.slug, path: filePath });
@@ -25913,7 +26251,7 @@ init_parser2();
25913
26251
  init_fs();
25914
26252
  init_paths();
25915
26253
  import { Router as Router19 } from "express";
25916
- import { readdir as readdir17 } from "fs/promises";
26254
+ import { readdir as readdir19 } from "fs/promises";
25917
26255
  import { resolve as resolvePath, dirname as dirname11 } from "path";
25918
26256
  import { rename as rename6, mkdir as mkdir5 } from "fs/promises";
25919
26257
  init_slug();
@@ -25924,8 +26262,8 @@ init_api();
25924
26262
  import { raw } from "express";
25925
26263
 
25926
26264
  // src/todos/attachments.ts
25927
- import { mkdir as mkdir4, readdir as readdir16, stat as stat6, rename as rename5, rm as rm4, unlink as unlink7, writeFile as writeFile6, cp } from "fs/promises";
25928
- import { resolve as resolve42, basename as basename6, dirname as dirname10, extname } from "path";
26265
+ import { mkdir as mkdir4, readdir as readdir18, stat as stat6, rename as rename5, rm as rm4, unlink as unlink7, writeFile as writeFile6, cp } from "fs/promises";
26266
+ import { resolve as resolve44, basename as basename6, dirname as dirname10, extname } from "path";
25929
26267
 
25930
26268
  // src/utils/proof-artifact-id.ts
25931
26269
  import { randomBytes as randomBytes2 } from "crypto";
@@ -26036,12 +26374,12 @@ function sanitizeAttachmentName(name) {
26036
26374
  return n;
26037
26375
  }
26038
26376
  function attachmentsRootDir(todosDir2) {
26039
- return resolve42(todosDir2, "attachments");
26377
+ return resolve44(todosDir2, "attachments");
26040
26378
  }
26041
26379
  function attachmentDirFor(todosDir2, scopeId, todoId) {
26042
26380
  assertScope(scopeId);
26043
26381
  assertTodoId(todoId);
26044
- return resolve42(attachmentsRootDir(todosDir2), scopeId, todoId);
26382
+ return resolve44(attachmentsRootDir(todosDir2), scopeId, todoId);
26045
26383
  }
26046
26384
  async function dirExists(p) {
26047
26385
  try {
@@ -26055,7 +26393,7 @@ async function writeAttachment(todosDir2, scopeId, todoId, originalName, bytes)
26055
26393
  await mkdir4(dir, { recursive: true });
26056
26394
  const id = generateArtifactId();
26057
26395
  const filename = sanitizeAttachmentName(originalName);
26058
- await writeFile6(resolve42(dir, `${id}__${filename}`), bytes);
26396
+ await writeFile6(resolve44(dir, `${id}__${filename}`), bytes);
26059
26397
  return {
26060
26398
  id,
26061
26399
  filename,
@@ -26068,7 +26406,7 @@ async function listAttachments(todosDir2, scopeId, todoId) {
26068
26406
  const dir = attachmentDirFor(todosDir2, scopeId, todoId);
26069
26407
  let names;
26070
26408
  try {
26071
- names = await readdir16(dir);
26409
+ names = await readdir18(dir);
26072
26410
  } catch {
26073
26411
  return [];
26074
26412
  }
@@ -26080,7 +26418,7 @@ async function listAttachments(todosDir2, scopeId, todoId) {
26080
26418
  if (!ATTACHMENT_ID_RE.test(id)) continue;
26081
26419
  const filename = stored.slice(sep2 + 2);
26082
26420
  try {
26083
- const st = await stat6(resolve42(dir, stored));
26421
+ const st = await stat6(resolve44(dir, stored));
26084
26422
  if (!st.isFile()) continue;
26085
26423
  out.push({ id, filename, mime: mimeForName(filename), size: st.size, createdAt: st.mtime.toISOString() });
26086
26424
  } catch {
@@ -26091,10 +26429,10 @@ async function listAttachments(todosDir2, scopeId, todoId) {
26091
26429
  }
26092
26430
  async function readScopeAttachments(todosDir2, scopeId) {
26093
26431
  assertScope(scopeId);
26094
- const scopeDir = resolve42(attachmentsRootDir(todosDir2), scopeId);
26432
+ const scopeDir = resolve44(attachmentsRootDir(todosDir2), scopeId);
26095
26433
  let todoIds;
26096
26434
  try {
26097
- todoIds = await readdir16(scopeDir);
26435
+ todoIds = await readdir18(scopeDir);
26098
26436
  } catch {
26099
26437
  return {};
26100
26438
  }
@@ -26111,7 +26449,7 @@ async function resolveAttachmentFile(todosDir2, scopeId, todoId, attachmentId) {
26111
26449
  const dir = attachmentDirFor(todosDir2, scopeId, todoId);
26112
26450
  let names;
26113
26451
  try {
26114
- names = await readdir16(dir);
26452
+ names = await readdir18(dir);
26115
26453
  } catch {
26116
26454
  return null;
26117
26455
  }
@@ -26119,7 +26457,7 @@ async function resolveAttachmentFile(todosDir2, scopeId, todoId, attachmentId) {
26119
26457
  const stored = names.find((n) => n.startsWith(prefix));
26120
26458
  if (!stored) return null;
26121
26459
  const filename = stored.slice(prefix.length);
26122
- return { path: resolve42(dir, stored), filename, mime: mimeForName(filename) };
26460
+ return { path: resolve44(dir, stored), filename, mime: mimeForName(filename) };
26123
26461
  }
26124
26462
  async function deleteAttachment(todosDir2, scopeId, todoId, attachmentId) {
26125
26463
  const resolved = await resolveAttachmentFile(todosDir2, scopeId, todoId, attachmentId);
@@ -26415,7 +26753,7 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
26415
26753
  router.get("/", async (_req, res) => {
26416
26754
  try {
26417
26755
  await ensureDir(todosDir2);
26418
- const files = await readdir17(todosDir2).catch(() => []);
26756
+ const files = await readdir19(todosDir2).catch(() => []);
26419
26757
  const workspaces = [];
26420
26758
  for (const file of files) {
26421
26759
  if (typeof file !== "string") continue;
@@ -26536,7 +26874,7 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
26536
26874
  try {
26537
26875
  const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
26538
26876
  const { resolve: resolve112 } = await import("path");
26539
- const { readFile: readFile81 } = await import("fs/promises");
26877
+ const { readFile: readFile82 } = await import("fs/promises");
26540
26878
  const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
26541
26879
  const workspace = getWorkspaceParam(req2.params.workspace);
26542
26880
  const outcome = await wsLock(workspace, async () => {
@@ -26555,7 +26893,7 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
26555
26893
  await ensureDir(resolve112(todosDir2, "archive"));
26556
26894
  let archContent = "";
26557
26895
  if (await fileExists(archFile)) {
26558
- archContent = await readFile81(archFile, "utf-8");
26896
+ archContent = await readFile82(archFile, "utf-8");
26559
26897
  archContent = archContent.trimEnd() + "\n\n";
26560
26898
  } else {
26561
26899
  archContent = `---
@@ -26856,7 +27194,7 @@ workspace: ${workspace}
26856
27194
  const { readConfig: readConfig3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
26857
27195
  const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
26858
27196
  const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
26859
- const { readFile: readFile81 } = await import("fs/promises");
27197
+ const { readFile: readFile82 } = await import("fs/promises");
26860
27198
  const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
26861
27199
  const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
26862
27200
  let assignmentRef;
@@ -26877,7 +27215,7 @@ workspace: ${workspace}
26877
27215
  }
26878
27216
  const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
26879
27217
  if (!await fileExists2(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
26880
- let content = await readFile81(assignmentMdPath2, "utf-8");
27218
+ let content = await readFile82(assignmentMdPath2, "utf-8");
26881
27219
  content = appendTodosToAssignmentBody2(
26882
27220
  content,
26883
27221
  items.map((it) => ({
@@ -27072,8 +27410,8 @@ init_fs();
27072
27410
  init_paths();
27073
27411
  init_slug();
27074
27412
  import { Router as Router20 } from "express";
27075
- import { mkdir as mkdir6, readFile as readFile28, rename as rename7 } from "fs/promises";
27076
- import { resolve as resolve43, dirname as dirname12 } from "path";
27413
+ import { mkdir as mkdir6, readFile as readFile30, rename as rename7 } from "fs/promises";
27414
+ import { resolve as resolve45, dirname as dirname12 } from "path";
27077
27415
  init_promote_todos();
27078
27416
  init_api();
27079
27417
  var WORKSPACE_REGEX2 = /^[a-z0-9_][a-z0-9-]*$/;
@@ -27090,7 +27428,7 @@ function params(req2) {
27090
27428
  return req2.params;
27091
27429
  }
27092
27430
  async function projectExists(projectsDir2, slug) {
27093
- return fileExists(resolve43(projectsDir2, slug, "project.md"));
27431
+ return fileExists(resolve45(projectsDir2, slug, "project.md"));
27094
27432
  }
27095
27433
  async function ensureProjectTodosDir(projectsDir2, slug) {
27096
27434
  const todosDir2 = projectTodosDir(projectsDir2, slug);
@@ -27107,7 +27445,7 @@ async function ensureProjectTodosDir(projectsDir2, slug) {
27107
27445
  throw err2;
27108
27446
  }
27109
27447
  try {
27110
- await mkdir6(resolve43(todosDir2, "archive"), { recursive: false });
27448
+ await mkdir6(resolve45(todosDir2, "archive"), { recursive: false });
27111
27449
  } catch (err2) {
27112
27450
  const code = err2.code;
27113
27451
  if (code === "EEXIST") return;
@@ -27315,7 +27653,7 @@ function createProjectTodosRouter(projectsDir2, broadcast, workspaceTodosDir) {
27315
27653
  const archFile = archivePath(todosDir2, slug, checklist.archiveInterval);
27316
27654
  let archContent = "";
27317
27655
  if (await fileExists(archFile)) {
27318
- archContent = await readFile28(archFile, "utf-8");
27656
+ archContent = await readFile30(archFile, "utf-8");
27319
27657
  archContent = archContent.trimEnd() + "\n\n";
27320
27658
  } else {
27321
27659
  archContent = `---
@@ -27786,17 +28124,17 @@ workspace: ${slug}
27786
28124
  if (tg.includes("/")) {
27787
28125
  const parts = tg.split("/");
27788
28126
  if (parts.length !== 2) return { error: `Invalid target.assignment "${tg}"` };
27789
- assignmentDir = resolve43(projectsDir2, parts[0], "assignments", parts[1]);
28127
+ assignmentDir = resolve45(projectsDir2, parts[0], "assignments", parts[1]);
27790
28128
  assignmentRef = `${parts[0]}/${parts[1]}`;
27791
28129
  } else if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(tg)) {
27792
- assignmentDir = resolve43(assignmentsDirFn(), tg);
28130
+ assignmentDir = resolve45(assignmentsDirFn(), tg);
27793
28131
  assignmentRef = tg;
27794
28132
  } else {
27795
28133
  return { error: `Invalid target.assignment "${tg}"` };
27796
28134
  }
27797
- const assignmentMdPath2 = resolve43(assignmentDir, "assignment.md");
28135
+ const assignmentMdPath2 = resolve45(assignmentDir, "assignment.md");
27798
28136
  if (!await fileExists(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
27799
- let content = await readFile28(assignmentMdPath2, "utf-8");
28137
+ let content = await readFile30(assignmentMdPath2, "utf-8");
27800
28138
  content = appendTodosToAssignmentBody2(
27801
28139
  content,
27802
28140
  items.map((it) => ({
@@ -27999,7 +28337,7 @@ workspace: ${slug}
27999
28337
 
28000
28338
  // src/dashboard/api-bundles.ts
28001
28339
  import { Router as Router21 } from "express";
28002
- import { readdir as readdir18 } from "fs/promises";
28340
+ import { readdir as readdir20 } from "fs/promises";
28003
28341
 
28004
28342
  // src/todos/bundle-parser.ts
28005
28343
  init_parser();
@@ -28007,7 +28345,7 @@ init_fs();
28007
28345
  init_paths();
28008
28346
  init_parser2();
28009
28347
  import { randomBytes as randomBytes3 } from "crypto";
28010
- import { readFile as readFile29 } from "fs/promises";
28348
+ import { readFile as readFile31 } from "fs/promises";
28011
28349
  var BUNDLE_ID_REGEX = /^[a-f0-9]{4}$/;
28012
28350
  var SCOPE_VALUES = /* @__PURE__ */ new Set(["workspace", "project", "global"]);
28013
28351
  var SCOPE_ID_REGEX = /^[a-z0-9_][a-z0-9_-]*$/;
@@ -28110,7 +28448,7 @@ ${lines}
28110
28448
  async function readBundles(todosDir2) {
28111
28449
  const path = bundlesPath(todosDir2);
28112
28450
  if (!await fileExists(path)) return [];
28113
- const content = await readFile29(path, "utf-8");
28451
+ const content = await readFile31(path, "utf-8");
28114
28452
  return parseBundles(content).bundles;
28115
28453
  }
28116
28454
  async function writeBundles(todosDir2, bundles) {
@@ -28163,7 +28501,7 @@ function createBundlesRouter(todosDir2, broadcast) {
28163
28501
  try {
28164
28502
  await ensureDir(todosDir2);
28165
28503
  const bundles = await readBundles(todosDir2);
28166
- const workspaceFiles = await readdir18(todosDir2).catch(() => []);
28504
+ const workspaceFiles = await readdir20(todosDir2).catch(() => []);
28167
28505
  const itemsByKey = /* @__PURE__ */ new Map();
28168
28506
  for (const f of workspaceFiles) {
28169
28507
  if (typeof f !== "string") continue;
@@ -28216,7 +28554,7 @@ init_fs();
28216
28554
  init_paths();
28217
28555
  init_slug();
28218
28556
  import { Router as Router22 } from "express";
28219
- import { resolve as resolve44 } from "path";
28557
+ import { resolve as resolve46 } from "path";
28220
28558
  init_parser2();
28221
28559
  function deriveStatus2(bundle, items) {
28222
28560
  const members = bundle.todoIds.map((id) => items.find((i) => i.id === id)).filter((i) => i !== void 0);
@@ -28258,7 +28596,7 @@ function createProjectBundlesRouter(projectsDir2, broadcast) {
28258
28596
  router.get("/", async (req2, res) => {
28259
28597
  try {
28260
28598
  const slug = getProjectIdParam2(req2.params.projectId);
28261
- const projectMd = resolve44(projectsDir2, slug, "project.md");
28599
+ const projectMd = resolve46(projectsDir2, slug, "project.md");
28262
28600
  if (!await fileExists(projectMd)) {
28263
28601
  notFound2(res, slug);
28264
28602
  return;
@@ -28287,8 +28625,8 @@ init_fs();
28287
28625
  init_config2();
28288
28626
  import { execFile as execFile2 } from "child_process";
28289
28627
  import { promisify as promisify2 } from "util";
28290
- import { cp as cp2, mkdtemp, rm as rm5, readFile as readFile30, writeFile as writeFile7, unlink as unlink8, stat as stat7, open as open5, rename as rename8 } from "fs/promises";
28291
- import { resolve as resolve45, join as join9 } from "path";
28628
+ import { cp as cp2, mkdtemp, rm as rm5, readFile as readFile32, writeFile as writeFile7, unlink as unlink8, stat as stat7, open as open5, rename as rename8 } from "fs/promises";
28629
+ import { resolve as resolve47, join as join11 } from "path";
28292
28630
  import { tmpdir } from "os";
28293
28631
  var exec2 = promisify2(execFile2);
28294
28632
  var VALID_CATEGORIES = ["projects", "playbooks", "todos", "servers", "config"];
@@ -28328,7 +28666,7 @@ async function resolveCategoryPath(category) {
28328
28666
  case "servers":
28329
28667
  return { sourcePath: serversDir(), repoPath: "servers", isFile: false };
28330
28668
  case "config":
28331
- return { sourcePath: resolve45(syntaurRoot(), "config.md"), repoPath: "config.md", isFile: true };
28669
+ return { sourcePath: resolve47(syntaurRoot(), "config.md"), repoPath: "config.md", isFile: true };
28332
28670
  }
28333
28671
  }
28334
28672
  async function checkGitInstalled() {
@@ -28339,7 +28677,7 @@ async function checkGitInstalled() {
28339
28677
  }
28340
28678
  }
28341
28679
  async function acquireLock2() {
28342
- const lockPath = resolve45(syntaurRoot(), LOCK_FILE_NAME);
28680
+ const lockPath = resolve47(syntaurRoot(), LOCK_FILE_NAME);
28343
28681
  await ensureDir(syntaurRoot());
28344
28682
  try {
28345
28683
  const handle = await open5(lockPath, "wx");
@@ -28348,7 +28686,7 @@ async function acquireLock2() {
28348
28686
  return lockPath;
28349
28687
  } catch (err2) {
28350
28688
  if (err2.code === "EEXIST") {
28351
- const pid = await readFile30(lockPath, "utf-8").catch(() => "");
28689
+ const pid = await readFile32(lockPath, "utf-8").catch(() => "");
28352
28690
  throw new Error(
28353
28691
  `Backup operation already in progress (lock file at ${lockPath}, pid ${pid.trim() || "unknown"}). If stale, delete the file and retry.`
28354
28692
  );
@@ -28386,7 +28724,7 @@ async function copyRecursive(src, dest) {
28386
28724
  await ensureDir(dest);
28387
28725
  await cp2(src, dest, { recursive: true, force: true });
28388
28726
  } else {
28389
- await ensureDir(resolve45(dest, ".."));
28727
+ await ensureDir(resolve47(dest, ".."));
28390
28728
  await cp2(src, dest, { force: true });
28391
28729
  }
28392
28730
  }
@@ -28395,7 +28733,7 @@ function resolveCategoriesStrict(csv) {
28395
28733
  return parseCategoriesStrict(parts);
28396
28734
  }
28397
28735
  async function readSanitizedConfig(configPath2) {
28398
- const content = await readFile30(configPath2, "utf-8");
28736
+ const content = await readFile32(configPath2, "utf-8");
28399
28737
  return content.replace(/^(\s*lastBackup:\s*).*$/m, "$1null").replace(/^(\s*lastRestore:\s*).*$/m, "$1null");
28400
28738
  }
28401
28739
  async function backupToGithub(overrides) {
@@ -28418,11 +28756,11 @@ async function backupToGithub(overrides) {
28418
28756
  let tmpDir = null;
28419
28757
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
28420
28758
  try {
28421
- tmpDir = await mkdtemp(join9(tmpdir(), "syntaur-backup-"));
28759
+ tmpDir = await mkdtemp(join11(tmpdir(), "syntaur-backup-"));
28422
28760
  await cloneOrInit(repo, tmpDir);
28423
28761
  for (const category of categories) {
28424
28762
  const { sourcePath, repoPath, isFile } = await resolveCategoryPath(category);
28425
- const destPath = join9(tmpDir, repoPath);
28763
+ const destPath = join11(tmpDir, repoPath);
28426
28764
  if (isFile) {
28427
28765
  await rm5(destPath, { force: true });
28428
28766
  } else {
@@ -28434,7 +28772,7 @@ async function backupToGithub(overrides) {
28434
28772
  }
28435
28773
  if (category === "config") {
28436
28774
  const sanitized = await readSanitizedConfig(sourcePath);
28437
- await ensureDir(resolve45(destPath, ".."));
28775
+ await ensureDir(resolve47(destPath, ".."));
28438
28776
  await writeFile7(destPath, sanitized, "utf-8");
28439
28777
  } else {
28440
28778
  await copyRecursive(sourcePath, destPath);
@@ -28488,7 +28826,7 @@ async function backupToGithub(overrides) {
28488
28826
  }
28489
28827
  async function safeRestoreCategory(localPath, repoSrcPath, isFile) {
28490
28828
  if (isFile) {
28491
- await ensureDir(resolve45(localPath, ".."));
28829
+ await ensureDir(resolve47(localPath, ".."));
28492
28830
  await cp2(repoSrcPath, localPath, { force: true });
28493
28831
  return;
28494
28832
  }
@@ -28550,7 +28888,7 @@ async function restoreFromGithub(overrides) {
28550
28888
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
28551
28889
  try {
28552
28890
  await updateBackupConfig({ lastRestore: timestamp });
28553
- tmpDir = await mkdtemp(join9(tmpdir(), "syntaur-restore-"));
28891
+ tmpDir = await mkdtemp(join11(tmpdir(), "syntaur-restore-"));
28554
28892
  await cloneOrInit(repo, tmpDir);
28555
28893
  for (const category of categories) {
28556
28894
  if (category === "config") {
@@ -28559,7 +28897,7 @@ async function restoreFromGithub(overrides) {
28559
28897
  }
28560
28898
  try {
28561
28899
  const { sourcePath: localPath, repoPath, isFile } = await resolveCategoryPath(category);
28562
- const repoSrcPath = join9(tmpDir, repoPath);
28900
+ const repoSrcPath = join11(tmpDir, repoPath);
28563
28901
  if (!await fileExists(repoSrcPath)) {
28564
28902
  console.warn(`Category "${category}" not found in backup repo, skipping.`);
28565
28903
  continue;
@@ -28589,7 +28927,7 @@ async function restoreFromGithub(overrides) {
28589
28927
  }
28590
28928
  async function getBackupStatus() {
28591
28929
  const config = await readConfig();
28592
- const lockPath = resolve45(syntaurRoot(), LOCK_FILE_NAME);
28930
+ const lockPath = resolve47(syntaurRoot(), LOCK_FILE_NAME);
28593
28931
  const locked = await fileExists(lockPath);
28594
28932
  return {
28595
28933
  repo: config.backup?.repo ?? null,
@@ -30545,7 +30883,7 @@ init_git_worktree();
30545
30883
  init_recompute();
30546
30884
  init_event_emit();
30547
30885
  init_transitions();
30548
- import { readFile as readFile34 } from "fs/promises";
30886
+ import { readFile as readFile35 } from "fs/promises";
30549
30887
  import { resolve as resolve54, basename as basename7 } from "path";
30550
30888
 
30551
30889
  // src/utils/assignment-target.ts
@@ -30556,7 +30894,7 @@ init_slug();
30556
30894
  init_assignment_resolver();
30557
30895
  init_parser();
30558
30896
  import { resolve as resolve53 } from "path";
30559
- import { readFile as readFile32 } from "fs/promises";
30897
+ import { readFile as readFile33 } from "fs/promises";
30560
30898
  var AssignmentTargetError = class extends Error {
30561
30899
  };
30562
30900
  function classifyContext(ctx) {
@@ -30569,7 +30907,7 @@ async function readAssignmentFrontmatterId(assignmentDir) {
30569
30907
  const path = resolve53(assignmentDir, "assignment.md");
30570
30908
  if (!await fileExists(path)) return null;
30571
30909
  try {
30572
- const content = await readFile32(path, "utf-8");
30910
+ const content = await readFile33(path, "utf-8");
30573
30911
  const [fm] = extractFrontmatter(content);
30574
30912
  return getField(fm, "id");
30575
30913
  } catch {
@@ -30580,7 +30918,7 @@ async function readContextJson(cwd) {
30580
30918
  const path = resolve53(cwd, ".syntaur", "context.json");
30581
30919
  if (!await fileExists(path)) return null;
30582
30920
  try {
30583
- const raw2 = await readFile32(path, "utf-8");
30921
+ const raw2 = await readFile33(path, "utf-8");
30584
30922
  return JSON.parse(raw2);
30585
30923
  } catch {
30586
30924
  return null;
@@ -30765,7 +31103,7 @@ init_session_id();
30765
31103
  init_fs();
30766
31104
  init_frontmatter();
30767
31105
  init_recompute();
30768
- import { readFile as readFile33 } from "fs/promises";
31106
+ import { readFile as readFile34 } from "fs/promises";
30769
31107
  function computeDelta(stage, prevStage, fm) {
30770
31108
  const writes = {};
30771
31109
  if (stage === "implement") {
@@ -30783,7 +31121,7 @@ async function assertStageFactOnOpen(input4) {
30783
31121
  const pre = computeDelta(
30784
31122
  input4.stage,
30785
31123
  input4.prevStage,
30786
- parseAssignmentFrontmatter(await readFile33(input4.assignmentPath, "utf-8"))
31124
+ parseAssignmentFrontmatter(await readFile34(input4.assignmentPath, "utf-8"))
30787
31125
  );
30788
31126
  if (Object.keys(pre).length === 0) return;
30789
31127
  const context = await resolveDeriveContext();
@@ -30833,7 +31171,7 @@ async function resolveTarget(assignment, options) {
30833
31171
  async function inferActor(options) {
30834
31172
  if (options.agent) return `agent:${options.agent}`;
30835
31173
  try {
30836
- const raw2 = await readFile34(resolve54(process.cwd(), ".syntaur", "context.json"), "utf-8");
31174
+ const raw2 = await readFile35(resolve54(process.cwd(), ".syntaur", "context.json"), "utf-8");
30837
31175
  const ctx = JSON.parse(raw2);
30838
31176
  if (ctx.sessionId) return `agent:${ctx.sessionId.slice(0, 8)}`;
30839
31177
  } catch {
@@ -30842,7 +31180,7 @@ async function inferActor(options) {
30842
31180
  }
30843
31181
  async function emitDeriveEvent(target, type, actor, details) {
30844
31182
  try {
30845
- const fm = parseAssignmentFrontmatter(await readFile34(target.assignmentPath, "utf-8"));
31183
+ const fm = parseAssignmentFrontmatter(await readFile35(target.assignmentPath, "utf-8"));
30846
31184
  emitEvent({
30847
31185
  assignmentId: fm.id,
30848
31186
  projectSlug: target.projectDir ? basename7(target.projectDir) : null,
@@ -30903,7 +31241,7 @@ async function planApproveCommand(assignment, options) {
30903
31241
  throw new Error("No plan file found (plan.md / plan-v*.md). Write a plan before approving.");
30904
31242
  }
30905
31243
  approvedFile = planFile;
30906
- const planContent = await readFile34(resolve54(target2.assignmentDir, planFile), "utf-8");
31244
+ const planContent = await readFile35(resolve54(target2.assignmentDir, planFile), "utf-8");
30907
31245
  return updatePlanApproval(content, {
30908
31246
  file: planFile,
30909
31247
  digest: planDigest(planContent),
@@ -30976,7 +31314,7 @@ async function attestCommand(assignment, fact, options) {
30976
31314
  "No plan file found (plan.md / plan-v*.md). Write a plan before attesting a binds:plan fact."
30977
31315
  );
30978
31316
  }
30979
- const planContent = await readFile34(resolve54(target2.assignmentDir, planFile), "utf-8");
31317
+ const planContent = await readFile35(resolve54(target2.assignmentDir, planFile), "utf-8");
30980
31318
  record.file = planFile;
30981
31319
  record.digest = planDigest(planContent);
30982
31320
  } else if (binds === "commit") {
@@ -31067,13 +31405,13 @@ async function requestReviewCommand(assignment, options) {
31067
31405
  }
31068
31406
  const target = await resolveTarget(assignment, options);
31069
31407
  const context = await resolveDeriveContext();
31070
- const fm = parseAssignmentFrontmatter(await readFile34(target.assignmentPath, "utf-8"));
31408
+ const fm = parseAssignmentFrontmatter(await readFile35(target.assignmentPath, "utf-8"));
31071
31409
  await applyStageFact(assignment, options, target, fm, "review", "Review requested", context);
31072
31410
  }
31073
31411
  async function implementStartedCommand(assignment, options) {
31074
31412
  const target = await resolveTarget(assignment, options);
31075
31413
  const context = await resolveDeriveContext();
31076
- const fm = parseAssignmentFrontmatter(await readFile34(target.assignmentPath, "utf-8"));
31414
+ const fm = parseAssignmentFrontmatter(await readFile35(target.assignmentPath, "utf-8"));
31077
31415
  if (fm.dependsOn.length > 0 && target.projectDir) {
31078
31416
  const dep = await checkDependencies(target.projectDir, fm.dependsOn, context.terminalStatuses);
31079
31417
  if (!dep.satisfied) {
@@ -31204,7 +31542,7 @@ init_timestamp();
31204
31542
  init_assignment_resolver();
31205
31543
  init_event_emit();
31206
31544
  import { resolve as resolve55 } from "path";
31207
- import { readFile as readFile35, writeFile as writeFile9 } from "fs/promises";
31545
+ import { readFile as readFile36, writeFile as writeFile9 } from "fs/promises";
31208
31546
  async function resolveTarget2(target, options) {
31209
31547
  const config = await readConfig();
31210
31548
  const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
@@ -31236,7 +31574,7 @@ async function resolveTarget2(target, options) {
31236
31574
  return null;
31237
31575
  }
31238
31576
  async function writeArchiveState(filePath, archived, reason) {
31239
- const content = await readFile35(filePath, "utf-8");
31577
+ const content = await readFile36(filePath, "utf-8");
31240
31578
  const updated = updateAssignmentFile(content, {
31241
31579
  archived,
31242
31580
  archivedAt: archived ? nowTimestamp() : null,
@@ -31248,7 +31586,7 @@ async function writeArchiveState(filePath, archived, reason) {
31248
31586
  async function emitArchiveEvent(resolved, type, reason) {
31249
31587
  if (resolved.kind !== "assignment") return;
31250
31588
  try {
31251
- const fm = parseAssignmentFrontmatter(await readFile35(resolved.filePath, "utf-8"));
31589
+ const fm = parseAssignmentFrontmatter(await readFile36(resolved.filePath, "utf-8"));
31252
31590
  emitEvent({
31253
31591
  assignmentId: fm.id,
31254
31592
  projectSlug: fm.project,
@@ -31319,7 +31657,7 @@ init_frontmatter();
31319
31657
  init_timestamp();
31320
31658
  init_event_emit();
31321
31659
  import { resolve as resolve56 } from "path";
31322
- import { readdir as readdir20, readFile as readFile36 } from "fs/promises";
31660
+ import { readdir as readdir21, readFile as readFile37 } from "fs/promises";
31323
31661
  var PROMOTABLE_STATUSES = /* @__PURE__ */ new Set(["pending"]);
31324
31662
  function objectiveIsFleshedOut(content) {
31325
31663
  const match = content.match(/##\s+Objective\s*\n([\s\S]*?)(?=\n##\s+|$)/);
@@ -31337,7 +31675,7 @@ async function collectCandidates(baseDirs) {
31337
31675
  const candidates = [];
31338
31676
  for (const baseDir of baseDirs) {
31339
31677
  if (!await fileExists(baseDir)) continue;
31340
- const projects = await readdir20(baseDir, { withFileTypes: true });
31678
+ const projects = await readdir21(baseDir, { withFileTypes: true });
31341
31679
  for (const m of projects) {
31342
31680
  if (!m.isDirectory()) continue;
31343
31681
  if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
@@ -31345,7 +31683,7 @@ async function collectCandidates(baseDirs) {
31345
31683
  if (await fileExists(directAssignmentMd)) {
31346
31684
  const fm = await parseSafe(directAssignmentMd);
31347
31685
  if (fm && PROMOTABLE_STATUSES.has(fm.status)) {
31348
- const content = await readFile36(directAssignmentMd, "utf-8");
31686
+ const content = await readFile37(directAssignmentMd, "utf-8");
31349
31687
  if (objectiveIsFleshedOut(content) && hasAcceptanceCriteria(content)) {
31350
31688
  candidates.push({
31351
31689
  projectSlug: null,
@@ -31360,7 +31698,7 @@ async function collectCandidates(baseDirs) {
31360
31698
  }
31361
31699
  const assignmentsDir2 = resolve56(baseDir, m.name, "assignments");
31362
31700
  if (!await fileExists(assignmentsDir2)) continue;
31363
- const entries = await readdir20(assignmentsDir2, { withFileTypes: true });
31701
+ const entries = await readdir21(assignmentsDir2, { withFileTypes: true });
31364
31702
  for (const a of entries) {
31365
31703
  if (!a.isDirectory()) continue;
31366
31704
  if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
@@ -31368,7 +31706,7 @@ async function collectCandidates(baseDirs) {
31368
31706
  if (!await fileExists(assignmentMd)) continue;
31369
31707
  const fm = await parseSafe(assignmentMd);
31370
31708
  if (!fm || !PROMOTABLE_STATUSES.has(fm.status)) continue;
31371
- const content = await readFile36(assignmentMd, "utf-8");
31709
+ const content = await readFile37(assignmentMd, "utf-8");
31372
31710
  if (!objectiveIsFleshedOut(content)) continue;
31373
31711
  if (!hasAcceptanceCriteria(content)) continue;
31374
31712
  candidates.push({
@@ -31385,7 +31723,7 @@ async function collectCandidates(baseDirs) {
31385
31723
  }
31386
31724
  async function parseSafe(path) {
31387
31725
  try {
31388
- const content = await readFile36(path, "utf-8");
31726
+ const content = await readFile37(path, "utf-8");
31389
31727
  return parseAssignmentFrontmatter(content);
31390
31728
  } catch {
31391
31729
  return null;
@@ -31415,7 +31753,7 @@ async function migrateStatusesCommand(options) {
31415
31753
  let migrated = 0;
31416
31754
  await withSuppressedEvents(async () => {
31417
31755
  for (const c2 of candidates) {
31418
- const content = await readFile36(c2.assignmentMd, "utf-8");
31756
+ const content = await readFile37(c2.assignmentMd, "utf-8");
31419
31757
  const updated = appendStatusHistoryEntry(
31420
31758
  updateAssignmentFile(content, {
31421
31759
  status: c2.toStatus,
@@ -31438,10 +31776,10 @@ init_frontmatter();
31438
31776
  init_event_emit();
31439
31777
  init_types();
31440
31778
  import { resolve as resolve57 } from "path";
31441
- import { readdir as readdir21, readFile as readFile37 } from "fs/promises";
31779
+ import { readdir as readdir22, readFile as readFile38 } from "fs/promises";
31442
31780
  async function parseSafe2(path) {
31443
31781
  try {
31444
- return parseAssignmentFrontmatter(await readFile37(path, "utf-8"));
31782
+ return parseAssignmentFrontmatter(await readFile38(path, "utf-8"));
31445
31783
  } catch {
31446
31784
  return null;
31447
31785
  }
@@ -31464,7 +31802,7 @@ async function collectTargets(baseDirs, terminalStatuses3) {
31464
31802
  const seen = /* @__PURE__ */ new Set();
31465
31803
  for (const baseDir of baseDirs) {
31466
31804
  if (!await fileExists(baseDir)) continue;
31467
- const entries = await readdir21(baseDir, { withFileTypes: true });
31805
+ const entries = await readdir22(baseDir, { withFileTypes: true });
31468
31806
  for (const m of entries) {
31469
31807
  if (!m.isDirectory()) continue;
31470
31808
  if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
@@ -31485,7 +31823,7 @@ async function collectTargets(baseDirs, terminalStatuses3) {
31485
31823
  }
31486
31824
  const assignmentsBase = resolve57(baseDir, m.name, "assignments");
31487
31825
  if (!await fileExists(assignmentsBase)) continue;
31488
- const slugs = await readdir21(assignmentsBase, { withFileTypes: true });
31826
+ const slugs = await readdir22(assignmentsBase, { withFileTypes: true });
31489
31827
  for (const a of slugs) {
31490
31828
  if (!a.isDirectory()) continue;
31491
31829
  if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
@@ -31533,7 +31871,7 @@ async function migrateStatusHistoryCommand(options) {
31533
31871
  await withSuppressedEvents(async () => {
31534
31872
  for (const t of targets) {
31535
31873
  try {
31536
- const content = await readFile37(t.assignmentMd, "utf-8");
31874
+ const content = await readFile38(t.assignmentMd, "utf-8");
31537
31875
  if (parseAssignmentFrontmatter(content).statusHistory.length > 0) continue;
31538
31876
  const seededContent = appendStatusHistoryEntry(content, {
31539
31877
  at: t.seedAt,
@@ -31562,10 +31900,10 @@ init_config2();
31562
31900
  init_parser();
31563
31901
  init_events_db();
31564
31902
  import { resolve as resolve58 } from "path";
31565
- import { readdir as readdir22, readFile as readFile38 } from "fs/promises";
31903
+ import { readdir as readdir23, readFile as readFile39 } from "fs/promises";
31566
31904
  async function parseSafe3(path) {
31567
31905
  try {
31568
- return parseAssignmentFull(await readFile38(path, "utf-8"));
31906
+ return parseAssignmentFull(await readFile39(path, "utf-8"));
31569
31907
  } catch {
31570
31908
  return null;
31571
31909
  }
@@ -31598,7 +31936,7 @@ async function collectTargets2(baseDirs) {
31598
31936
  const seen = /* @__PURE__ */ new Set();
31599
31937
  for (const baseDir of baseDirs) {
31600
31938
  if (!await fileExists(baseDir)) continue;
31601
- const entries = await readdir22(baseDir, { withFileTypes: true });
31939
+ const entries = await readdir23(baseDir, { withFileTypes: true });
31602
31940
  for (const m of entries) {
31603
31941
  if (!m.isDirectory()) continue;
31604
31942
  if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
@@ -31622,7 +31960,7 @@ async function collectTargets2(baseDirs) {
31622
31960
  }
31623
31961
  const assignmentsBase = resolve58(baseDir, m.name, "assignments");
31624
31962
  if (!await fileExists(assignmentsBase)) continue;
31625
- const slugs = await readdir22(assignmentsBase, { withFileTypes: true });
31963
+ const slugs = await readdir23(assignmentsBase, { withFileTypes: true });
31626
31964
  for (const a of slugs) {
31627
31965
  if (!a.isDirectory()) continue;
31628
31966
  if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
@@ -31707,7 +32045,7 @@ init_frontmatter();
31707
32045
  init_facts();
31708
32046
  init_derive();
31709
32047
  init_recompute();
31710
- import { readdir as readdir23, readFile as readFile39 } from "fs/promises";
32048
+ import { readdir as readdir24, readFile as readFile40 } from "fs/promises";
31711
32049
  import { resolve as resolve59 } from "path";
31712
32050
  var IMPLEMENTATION_STATUSES = /* @__PURE__ */ new Set(["in_progress", "review", "code_review"]);
31713
32051
  var REVIEW_STATUSES = /* @__PURE__ */ new Set(["review", "code_review"]);
@@ -31715,14 +32053,14 @@ async function listTargets(projectsDir2, standaloneDir) {
31715
32053
  const targets = [];
31716
32054
  let projects = [];
31717
32055
  try {
31718
- projects = await readdir23(projectsDir2);
32056
+ projects = await readdir24(projectsDir2);
31719
32057
  } catch {
31720
32058
  }
31721
32059
  for (const project of projects) {
31722
32060
  const projectDir = resolve59(projectsDir2, project);
31723
32061
  let slugs = [];
31724
32062
  try {
31725
- slugs = await readdir23(resolve59(projectDir, "assignments"));
32063
+ slugs = await readdir24(resolve59(projectDir, "assignments"));
31726
32064
  } catch {
31727
32065
  continue;
31728
32066
  }
@@ -31733,7 +32071,7 @@ async function listTargets(projectsDir2, standaloneDir) {
31733
32071
  }
31734
32072
  let ids = [];
31735
32073
  try {
31736
- ids = await readdir23(standaloneDir);
32074
+ ids = await readdir24(standaloneDir);
31737
32075
  } catch {
31738
32076
  }
31739
32077
  for (const id of ids) {
@@ -31793,7 +32131,7 @@ async function migrateDeriveCommand(options) {
31793
32131
  for (const target of targets) {
31794
32132
  let content;
31795
32133
  try {
31796
- content = await readFile39(target.path, "utf-8");
32134
+ content = await readFile40(target.path, "utf-8");
31797
32135
  } catch {
31798
32136
  continue;
31799
32137
  }
@@ -32007,10 +32345,10 @@ init_config2();
32007
32345
  init_fs();
32008
32346
  import {
32009
32347
  cp as cp3,
32010
- readdir as readdir24,
32348
+ readdir as readdir25,
32011
32349
  symlink,
32012
32350
  lstat,
32013
- readFile as readFile40,
32351
+ readFile as readFile41,
32014
32352
  readlink,
32015
32353
  rename as rename9,
32016
32354
  rm as rm6,
@@ -32071,7 +32409,7 @@ function getInstallMarkerPath(targetDir) {
32071
32409
  return resolve64(targetDir, INSTALL_MARKER_FILENAME);
32072
32410
  }
32073
32411
  async function readPackageManifest(packageRoot) {
32074
- const raw2 = await readFile40(resolve64(packageRoot, "package.json"), "utf-8");
32412
+ const raw2 = await readFile41(resolve64(packageRoot, "package.json"), "utf-8");
32075
32413
  return JSON.parse(raw2);
32076
32414
  }
32077
32415
  async function readJsonFileIfExists(pathValue) {
@@ -32079,7 +32417,7 @@ async function readJsonFileIfExists(pathValue) {
32079
32417
  return null;
32080
32418
  }
32081
32419
  try {
32082
- const raw2 = await readFile40(pathValue, "utf-8");
32420
+ const raw2 = await readFile41(pathValue, "utf-8");
32083
32421
  return JSON.parse(raw2);
32084
32422
  } catch {
32085
32423
  return null;
@@ -32095,7 +32433,7 @@ async function readPluginManifestName(targetDir, pluginKind) {
32095
32433
  if (!await fileExists(manifestPath)) {
32096
32434
  return void 0;
32097
32435
  }
32098
- const raw2 = await readFile40(manifestPath, "utf-8");
32436
+ const raw2 = await readFile41(manifestPath, "utf-8");
32099
32437
  const parsed = JSON.parse(raw2);
32100
32438
  return parsed.name;
32101
32439
  }
@@ -32105,7 +32443,7 @@ async function readInstallMetadata(targetDir) {
32105
32443
  return null;
32106
32444
  }
32107
32445
  try {
32108
- const raw2 = await readFile40(markerPath, "utf-8");
32446
+ const raw2 = await readFile41(markerPath, "utf-8");
32109
32447
  return JSON.parse(raw2);
32110
32448
  } catch {
32111
32449
  return null;
@@ -32228,7 +32566,7 @@ async function writeClaudeMarketplaceFile(manifestPath, marketplace) {
32228
32566
  }
32229
32567
  if (await fileExists(manifestPath)) {
32230
32568
  try {
32231
- const prev = await readFile40(manifestPath, "utf-8");
32569
+ const prev = await readFile41(manifestPath, "utf-8");
32232
32570
  JSON.parse(prev);
32233
32571
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
32234
32572
  await writeFile10(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
@@ -32301,7 +32639,7 @@ async function listClaudeMarketplaceCandidates() {
32301
32639
  const [knownMarketplaces, activeMarketplaceNames, entries] = await Promise.all([
32302
32640
  readKnownClaudeMarketplaceRecords(),
32303
32641
  readInstalledClaudeMarketplaceNames(),
32304
- readdir24(rootDir, { withFileTypes: true })
32642
+ readdir25(rootDir, { withFileTypes: true })
32305
32643
  ]);
32306
32644
  const candidates = [];
32307
32645
  const seen = /* @__PURE__ */ new Set();
@@ -32376,7 +32714,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
32376
32714
  const manifestPath = getClaudeKnownMarketplacesPath();
32377
32715
  let existing = {};
32378
32716
  if (await fileExists(manifestPath)) {
32379
- const raw2 = await readFile40(manifestPath, "utf-8");
32717
+ const raw2 = await readFile41(manifestPath, "utf-8");
32380
32718
  try {
32381
32719
  const parsed = JSON.parse(raw2);
32382
32720
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
@@ -32403,7 +32741,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
32403
32741
  existing[name].autoUpdate = true;
32404
32742
  await ensureDir(dirname15(manifestPath));
32405
32743
  if (await fileExists(manifestPath)) {
32406
- const prev = await readFile40(manifestPath, "utf-8");
32744
+ const prev = await readFile41(manifestPath, "utf-8");
32407
32745
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
32408
32746
  await writeFile10(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
32409
32747
  }
@@ -32421,7 +32759,7 @@ async function setSyntaurPluginEnabled(options) {
32421
32759
  const key = `syntaur@${options.marketplaceName}`;
32422
32760
  let parsed = {};
32423
32761
  if (await fileExists(settingsPath)) {
32424
- const raw2 = await readFile40(settingsPath, "utf-8");
32762
+ const raw2 = await readFile41(settingsPath, "utf-8");
32425
32763
  try {
32426
32764
  parsed = JSON.parse(raw2);
32427
32765
  } catch {
@@ -32440,7 +32778,7 @@ async function setSyntaurPluginEnabled(options) {
32440
32778
  await ensureDir(dirname15(settingsPath));
32441
32779
  if (await fileExists(settingsPath)) {
32442
32780
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
32443
- const prev = await readFile40(settingsPath, "utf-8");
32781
+ const prev = await readFile41(settingsPath, "utf-8");
32444
32782
  await writeFile10(`${settingsPath}.bak-${stamp}`, prev, "utf-8");
32445
32783
  }
32446
32784
  const tmpPath = `${settingsPath}.tmp`;
@@ -32678,7 +33016,7 @@ async function readMarketplaceFile(marketplacePath) {
32678
33016
  plugins: []
32679
33017
  };
32680
33018
  }
32681
- const raw2 = await readFile40(marketplacePath, "utf-8");
33019
+ const raw2 = await readFile41(marketplacePath, "utf-8");
32682
33020
  const parsed = JSON.parse(raw2);
32683
33021
  return {
32684
33022
  name: parsed.name ?? "local",
@@ -32920,13 +33258,13 @@ async function textPrompt(question, defaultValue) {
32920
33258
 
32921
33259
  // src/utils/install-skills.ts
32922
33260
  init_fs();
32923
- import { readFile as readFile42, readdir as readdir25, mkdir as mkdir7, copyFile, rm as rm7, lstat as lstat2 } from "fs/promises";
32924
- import { resolve as resolve66, relative as relative3, join as join11 } from "path";
33261
+ import { readFile as readFile43, readdir as readdir26, mkdir as mkdir7, copyFile, rm as rm7, lstat as lstat2 } from "fs/promises";
33262
+ import { resolve as resolve66, relative as relative3, join as join12 } from "path";
32925
33263
  import { homedir as homedir10 } from "os";
32926
33264
 
32927
33265
  // src/utils/plugin-state.ts
32928
33266
  init_fs();
32929
- import { readFile as readFile41 } from "fs/promises";
33267
+ import { readFile as readFile42 } from "fs/promises";
32930
33268
  import { resolve as resolve65 } from "path";
32931
33269
  import { homedir as homedir9 } from "os";
32932
33270
  function settingsPathFor(agent) {
@@ -32937,7 +33275,7 @@ async function readJsonOrNull(path) {
32937
33275
  if (!path) return null;
32938
33276
  if (!await fileExists(path)) return null;
32939
33277
  try {
32940
- const raw2 = await readFile41(path, "utf-8");
33278
+ const raw2 = await readFile42(path, "utf-8");
32941
33279
  return JSON.parse(raw2);
32942
33280
  } catch {
32943
33281
  return null;
@@ -32996,9 +33334,9 @@ function defaultSkillTargetDir(target) {
32996
33334
  async function walkFiles(root) {
32997
33335
  const out = [];
32998
33336
  async function walk(dir) {
32999
- const entries = await readdir25(dir, { withFileTypes: true });
33337
+ const entries = await readdir26(dir, { withFileTypes: true });
33000
33338
  for (const entry of entries) {
33001
- const full = join11(dir, entry.name);
33339
+ const full = join12(dir, entry.name);
33002
33340
  if (entry.isDirectory()) {
33003
33341
  await walk(full);
33004
33342
  } else if (entry.isFile()) {
@@ -33011,7 +33349,7 @@ async function walkFiles(root) {
33011
33349
  }
33012
33350
  async function filesEqual(a, b) {
33013
33351
  try {
33014
- const [ba, bb] = await Promise.all([readFile42(a), readFile42(b)]);
33352
+ const [ba, bb] = await Promise.all([readFile43(a), readFile43(b)]);
33015
33353
  if (ba.length !== bb.length) return false;
33016
33354
  return ba.equals(bb);
33017
33355
  } catch {
@@ -33020,10 +33358,10 @@ async function filesEqual(a, b) {
33020
33358
  }
33021
33359
  async function copyDir(srcDir, destDir) {
33022
33360
  await mkdir7(destDir, { recursive: true });
33023
- const entries = await readdir25(srcDir, { withFileTypes: true });
33361
+ const entries = await readdir26(srcDir, { withFileTypes: true });
33024
33362
  for (const entry of entries) {
33025
- const src = join11(srcDir, entry.name);
33026
- const dest = join11(destDir, entry.name);
33363
+ const src = join12(srcDir, entry.name);
33364
+ const dest = join12(destDir, entry.name);
33027
33365
  if (entry.isDirectory()) {
33028
33366
  await copyDir(src, dest);
33029
33367
  } else if (entry.isFile()) {
@@ -33036,7 +33374,7 @@ async function skillMatches(srcDir, destDir) {
33036
33374
  const srcFiles = await walkFiles(srcDir);
33037
33375
  for (const srcFile of srcFiles) {
33038
33376
  const rel = relative3(srcDir, srcFile);
33039
- const destFile = join11(destDir, rel);
33377
+ const destFile = join12(destDir, rel);
33040
33378
  if (!await filesEqual(srcFile, destFile)) return false;
33041
33379
  }
33042
33380
  const destFiles = await walkFiles(destDir);
@@ -33074,12 +33412,12 @@ async function installSkillDir(srcDir, destDir, skillName, force) {
33074
33412
  return { skill: skillName, status: "differs-preserved", targetPath: destDir };
33075
33413
  }
33076
33414
  async function discoverSkillNames(sourceDir) {
33077
- const entries = await readdir25(sourceDir, { withFileTypes: true });
33415
+ const entries = await readdir26(sourceDir, { withFileTypes: true });
33078
33416
  const names = [];
33079
33417
  for (const entry of entries) {
33080
33418
  if (!entry.isDirectory()) continue;
33081
33419
  if (entry.name.startsWith(".")) continue;
33082
- if (await fileExists(join11(sourceDir, entry.name, "SKILL.md"))) {
33420
+ if (await fileExists(join12(sourceDir, entry.name, "SKILL.md"))) {
33083
33421
  names.push(entry.name);
33084
33422
  }
33085
33423
  }
@@ -33106,8 +33444,8 @@ async function installSkillsWithReport(options) {
33106
33444
  const results = [];
33107
33445
  await mkdir7(targetRoot, { recursive: true });
33108
33446
  for (const skill of skillNames) {
33109
- const srcDir = join11(source, skill);
33110
- const destDir = join11(targetRoot, skill);
33447
+ const srcDir = join12(source, skill);
33448
+ const destDir = join12(targetRoot, skill);
33111
33449
  results.push(await installSkillDir(srcDir, destDir, skill, force));
33112
33450
  }
33113
33451
  return { results };
@@ -33124,8 +33462,8 @@ async function installSkillsToDir(options) {
33124
33462
  await mkdir7(options.targetDir, { recursive: true });
33125
33463
  const results = [];
33126
33464
  for (const skill of skillNames) {
33127
- const srcDir = join11(source, skill);
33128
- const destDir = join11(options.targetDir, skill);
33465
+ const srcDir = join12(source, skill);
33466
+ const destDir = join12(options.targetDir, skill);
33129
33467
  results.push(await installSkillDir(srcDir, destDir, skill, force));
33130
33468
  }
33131
33469
  return results;
@@ -33146,12 +33484,12 @@ async function uninstallSkills(options) {
33146
33484
  }
33147
33485
  const removed = [];
33148
33486
  for (const skill of known) {
33149
- const destDir = join11(targetRoot, skill);
33487
+ const destDir = join12(targetRoot, skill);
33150
33488
  if (!await fileExists(destDir)) continue;
33151
33489
  if (await isSymlink(destDir)) continue;
33152
- const skillMd = join11(destDir, "SKILL.md");
33490
+ const skillMd = join12(destDir, "SKILL.md");
33153
33491
  if (!await fileExists(skillMd)) continue;
33154
- const content = await readFile42(skillMd, "utf-8").catch(() => "");
33492
+ const content = await readFile43(skillMd, "utf-8").catch(() => "");
33155
33493
  const match = content.match(/^name:\s*(\S+)\s*$/m);
33156
33494
  if (!match || match[1] !== skill) continue;
33157
33495
  await rm7(destDir, { recursive: true, force: true });
@@ -33346,20 +33684,20 @@ import { realpathSync as realpathSync3 } from "fs";
33346
33684
  init_paths();
33347
33685
  init_fs();
33348
33686
  import { fileURLToPath as fileURLToPath6 } from "url";
33349
- import { readFile as readFile44 } from "fs/promises";
33350
- import { dirname as dirname17, join as join13, resolve as resolve67 } from "path";
33687
+ import { readFile as readFile45 } from "fs/promises";
33688
+ import { dirname as dirname17, join as join14, resolve as resolve67 } from "path";
33351
33689
  import { spawn as spawn6 } from "child_process";
33352
33690
  import { createInterface as createInterface2 } from "readline/promises";
33353
33691
 
33354
33692
  // src/utils/version.ts
33355
33693
  import { fileURLToPath as fileURLToPath5 } from "url";
33356
- import { readFile as readFile43 } from "fs/promises";
33357
- import { dirname as dirname16, join as join12 } from "path";
33694
+ import { readFile as readFile44 } from "fs/promises";
33695
+ import { dirname as dirname16, join as join13 } from "path";
33358
33696
  async function readPackageVersion(scriptUrl) {
33359
33697
  try {
33360
33698
  const scriptPath = fileURLToPath5(scriptUrl);
33361
33699
  const pkgRoot = dirname16(dirname16(scriptPath));
33362
- const raw2 = await readFile43(join12(pkgRoot, "package.json"), "utf-8");
33700
+ const raw2 = await readFile44(join13(pkgRoot, "package.json"), "utf-8");
33363
33701
  const parsed = JSON.parse(raw2);
33364
33702
  return typeof parsed.version === "string" ? parsed.version : null;
33365
33703
  } catch {
@@ -33389,7 +33727,7 @@ function isRunningViaNpx(scriptUrl) {
33389
33727
  async function readState() {
33390
33728
  if (!await fileExists(STATE_FILE)) return null;
33391
33729
  try {
33392
- const raw2 = await readFile44(STATE_FILE, "utf-8");
33730
+ const raw2 = await readFile45(STATE_FILE, "utf-8");
33393
33731
  return JSON.parse(raw2);
33394
33732
  } catch {
33395
33733
  return null;
@@ -33403,7 +33741,7 @@ async function resolveNpmBin() {
33403
33741
  const nodeDir = dirname17(process.execPath);
33404
33742
  const isWin = process.platform === "win32";
33405
33743
  const npmName = isWin ? "npm.cmd" : "npm";
33406
- const nearNode = join13(nodeDir, npmName);
33744
+ const nearNode = join14(nodeDir, npmName);
33407
33745
  if (await fileExists(nearNode)) {
33408
33746
  return { cmd: nearNode, shell: false };
33409
33747
  }
@@ -33446,9 +33784,9 @@ async function readGlobalVersion() {
33446
33784
  });
33447
33785
  if (!rootPath) return null;
33448
33786
  try {
33449
- const manifestPath = join13(rootPath, "syntaur", "package.json");
33787
+ const manifestPath = join14(rootPath, "syntaur", "package.json");
33450
33788
  if (!await fileExists(manifestPath)) return null;
33451
- const raw2 = await readFile44(manifestPath, "utf-8");
33789
+ const raw2 = await readFile45(manifestPath, "utf-8");
33452
33790
  const parsed = JSON.parse(raw2);
33453
33791
  return typeof parsed.version === "string" ? parsed.version : null;
33454
33792
  } catch {
@@ -33567,7 +33905,7 @@ async function maybePromptInstall(scriptUrl) {
33567
33905
 
33568
33906
  // src/commands/update.ts
33569
33907
  import { existsSync as existsSync5 } from "fs";
33570
- import { join as join14 } from "path";
33908
+ import { join as join15 } from "path";
33571
33909
  import { homedir as homedir11 } from "os";
33572
33910
  var PACKAGE_MANAGERS = ["npm", "pnpm", "yarn", "bun"];
33573
33911
  var LOCAL_MSG = "Running from a dev/linked checkout \u2014 `syntaur update` won't touch a linked install. Pull + rebuild in your repo, then `syntaur install-plugin --force` to refresh skills.";
@@ -33609,12 +33947,12 @@ var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise) => {
33609
33947
  child.on("close", (code) => resolvePromise({ code: code ?? -1, stdout, stderr }));
33610
33948
  });
33611
33949
  function bunGlobalNodeModulesDir(env) {
33612
- const globalDir = env.BUN_INSTALL_GLOBAL_DIR ?? join14(env.BUN_INSTALL ?? join14(homedir11(), ".bun"), "install", "global");
33613
- return join14(globalDir, "node_modules");
33950
+ const globalDir = env.BUN_INSTALL_GLOBAL_DIR ?? join15(env.BUN_INSTALL ?? join15(homedir11(), ".bun"), "install", "global");
33951
+ return join15(globalDir, "node_modules");
33614
33952
  }
33615
33953
  async function defaultResolveFreshBin(pm, runner, env) {
33616
33954
  const binFromPkgRoot = (pkgRoot) => {
33617
- const entry = join14(pkgRoot, "syntaur", "bin", "syntaur.js");
33955
+ const entry = join15(pkgRoot, "syntaur", "bin", "syntaur.js");
33618
33956
  return existsSync5(entry) ? { cmd: process.execPath, baseArgs: [entry] } : null;
33619
33957
  };
33620
33958
  try {
@@ -33626,7 +33964,7 @@ async function defaultResolveFreshBin(pm, runner, env) {
33626
33964
  if (pm === "yarn") {
33627
33965
  const res = await runner("yarn", ["global", "dir"], { env, captureStdout: true });
33628
33966
  const dir = res.stdout.trim();
33629
- return dir ? binFromPkgRoot(join14(dir, "node_modules")) : null;
33967
+ return dir ? binFromPkgRoot(join15(dir, "node_modules")) : null;
33630
33968
  }
33631
33969
  return binFromPkgRoot(bunGlobalNodeModulesDir(env));
33632
33970
  } catch {
@@ -33806,7 +34144,7 @@ async function refreshPluginSkills(options, pm, runner, getManagedDir, resolveFr
33806
34144
  // src/commands/install-statusline.ts
33807
34145
  init_paths();
33808
34146
  init_fs();
33809
- import { readFile as readFile46, writeFile as writeFile12, copyFile as copyFile2, rm as rm8, stat as stat8, symlink as symlink2, unlink as unlink11, lstat as lstat3 } from "fs/promises";
34147
+ import { readFile as readFile47, writeFile as writeFile12, copyFile as copyFile2, rm as rm8, stat as stat8, symlink as symlink2, unlink as unlink11, lstat as lstat3 } from "fs/promises";
33810
34148
  import { resolve as resolve69, dirname as dirname19 } from "path";
33811
34149
  import { homedir as homedir12 } from "os";
33812
34150
  import { fileURLToPath as fileURLToPath8 } from "url";
@@ -33814,7 +34152,7 @@ import { fileURLToPath as fileURLToPath8 } from "url";
33814
34152
  // src/commands/configure-statusline.ts
33815
34153
  init_paths();
33816
34154
  init_fs();
33817
- import { readFile as readFile45, writeFile as writeFile11 } from "fs/promises";
34155
+ import { readFile as readFile46, writeFile as writeFile11 } from "fs/promises";
33818
34156
  import { resolve as resolve68, dirname as dirname18 } from "path";
33819
34157
  import { spawnSync as spawnSync5 } from "child_process";
33820
34158
  import { checkbox, input as input2, confirm } from "@inquirer/prompts";
@@ -33841,7 +34179,7 @@ function getConfigPath(installRoot) {
33841
34179
  async function readConfig2(path) {
33842
34180
  if (!await fileExists(path)) return null;
33843
34181
  try {
33844
- const raw2 = await readFile45(path, "utf-8");
34182
+ const raw2 = await readFile46(path, "utf-8");
33845
34183
  const parsed = JSON.parse(raw2);
33846
34184
  if (!parsed || typeof parsed !== "object") return null;
33847
34185
  const segments = Array.isArray(parsed.segments) ? parsed.segments.filter(isSegmentName) : [];
@@ -34029,7 +34367,7 @@ function getPackageStatuslineSource() {
34029
34367
  }
34030
34368
  async function readSettingsJson(settingsPath) {
34031
34369
  if (!await fileExists(settingsPath)) return {};
34032
- const raw2 = await readFile46(settingsPath, "utf-8");
34370
+ const raw2 = await readFile47(settingsPath, "utf-8");
34033
34371
  if (raw2.trim() === "") return {};
34034
34372
  try {
34035
34373
  const parsed = JSON.parse(raw2);
@@ -34217,7 +34555,7 @@ async function uninstallStatuslineCommand(options = {}) {
34217
34555
  let restored = null;
34218
34556
  if (await fileExists(backupPath)) {
34219
34557
  try {
34220
- const raw2 = await readFile46(backupPath, "utf-8");
34558
+ const raw2 = await readFile47(backupPath, "utf-8");
34221
34559
  const parsed = JSON.parse(raw2);
34222
34560
  const prev = parsed?.previousStatusLine;
34223
34561
  if (prev && typeof prev === "object" && typeof prev.command === "string") {
@@ -34398,7 +34736,7 @@ init_config2();
34398
34736
  // src/commands/cross-agent-install.ts
34399
34737
  init_fs();
34400
34738
  import { spawnSync as spawnSync6 } from "child_process";
34401
- import { dirname as dirname20, join as join15, resolve as resolve71 } from "path";
34739
+ import { dirname as dirname20, join as join16, resolve as resolve71 } from "path";
34402
34740
  import { cp as cp4, mkdir as mkdir9 } from "fs/promises";
34403
34741
 
34404
34742
  // src/commands/setup-adapter.ts
@@ -34509,7 +34847,7 @@ async function installTier3Plugin(t, opts = {}) {
34509
34847
  console.log(`${prefix}Tier 3 (${t.id}): ${plugin.source} -> ${installDir}`);
34510
34848
  return "dry-run";
34511
34849
  }
34512
- if (await fileExists(join15(installDir, plugin.entry)) && !opts.force) {
34850
+ if (await fileExists(join16(installDir, plugin.entry)) && !opts.force) {
34513
34851
  console.log(
34514
34852
  `Tier 3 (${t.id}): already installed at ${installDir} (use --force to overwrite).`
34515
34853
  );
@@ -34887,7 +35225,7 @@ init_cwd();
34887
35225
  init_session_db();
34888
35226
  init_agent_sessions();
34889
35227
  import { resolve as resolve73 } from "path";
34890
- import { readFile as readFile47 } from "fs/promises";
35228
+ import { readFile as readFile48 } from "fs/promises";
34891
35229
  async function trackSessionCommand(options, deps = {}) {
34892
35230
  if (!options.agent) {
34893
35231
  throw new Error("--agent <name> is required.");
@@ -34904,7 +35242,7 @@ async function trackSessionCommand(options, deps = {}) {
34904
35242
  const cwd = process.cwd();
34905
35243
  let legacyHint;
34906
35244
  try {
34907
- const raw2 = await readFile47(resolve73(cwd, ".syntaur", "context.json"), "utf-8");
35245
+ const raw2 = await readFile48(resolve73(cwd, ".syntaur", "context.json"), "utf-8");
34908
35246
  const parsed = JSON.parse(raw2);
34909
35247
  if (typeof parsed.sessionId === "string") legacyHint = parsed.sessionId;
34910
35248
  } catch {
@@ -34988,8 +35326,10 @@ async function urlCommand(input4, options = {}) {
34988
35326
  projectsDir: projectsDir2,
34989
35327
  assignmentsDir: assignmentsDir(),
34990
35328
  terminalOverride: parsed.terminal,
34991
- agentId: parsed.kind === "assignment" ? parsed.agent : void 0,
34992
- promptOverride: parsed.kind === "assignment" ? parsed.prompt : void 0
35329
+ // Standalone identifies its agent by `id`; assignment may pin one via `agent=`.
35330
+ agentId: parsed.kind === "standalone" ? parsed.id : parsed.kind === "assignment" ? parsed.agent : void 0,
35331
+ promptOverride: parsed.kind === "assignment" || parsed.kind === "standalone" ? parsed.prompt : void 0,
35332
+ agentName: parsed.kind === "assignment" ? parsed.agentName : void 0
34993
35333
  });
34994
35334
  emitPlanWarnings(plan);
34995
35335
  if (options.printPlan) {
@@ -35067,7 +35407,7 @@ init_config2();
35067
35407
  init_paths();
35068
35408
  init_fs();
35069
35409
  import { resolve as resolve75, isAbsolute as isAbsolute8 } from "path";
35070
- import { readFile as readFile48 } from "fs/promises";
35410
+ import { readFile as readFile49 } from "fs/promises";
35071
35411
  import { select, confirm as confirm2, input as input3 } from "@inquirer/prompts";
35072
35412
  async function browseCommand(options) {
35073
35413
  const config = await readConfig();
@@ -35146,7 +35486,7 @@ async function ensureWorktree(opts) {
35146
35486
  if (!await fileExists(assignmentPath)) {
35147
35487
  return void 0;
35148
35488
  }
35149
- const content = await readFile48(assignmentPath, "utf-8");
35489
+ const content = await readFile49(assignmentPath, "utf-8");
35150
35490
  const { parseAssignmentFrontmatter: parseAssignmentFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
35151
35491
  const fm = parseAssignmentFrontmatter2(content);
35152
35492
  const { workspace } = fm;
@@ -35280,7 +35620,7 @@ init_paths();
35280
35620
  init_fs();
35281
35621
  init_parser();
35282
35622
  init_config2();
35283
- import { readdir as readdir26, readFile as readFile49 } from "fs/promises";
35623
+ import { readdir as readdir27, readFile as readFile50 } from "fs/promises";
35284
35624
  import { resolve as resolve77 } from "path";
35285
35625
  async function listPlaybooksCommand(options = {}) {
35286
35626
  const dir = playbooksDir();
@@ -35290,14 +35630,14 @@ async function listPlaybooksCommand(options = {}) {
35290
35630
  }
35291
35631
  const config = await readConfig();
35292
35632
  const disabledSet = new Set(config.playbooks.disabled);
35293
- const entries = await readdir26(dir, { withFileTypes: true });
35633
+ const entries = await readdir27(dir, { withFileTypes: true });
35294
35634
  const mdFiles = entries.filter(
35295
35635
  (e) => e.isFile() && e.name.endsWith(".md") && !e.name.startsWith("_") && e.name !== "manifest.md"
35296
35636
  );
35297
35637
  const rows = [];
35298
35638
  for (const entry of mdFiles) {
35299
35639
  const filePath = resolve77(dir, entry.name);
35300
- const raw2 = await readFile49(filePath, "utf-8");
35640
+ const raw2 = await readFile50(filePath, "utf-8");
35301
35641
  const parsed = parsePlaybook(raw2);
35302
35642
  const slug = parsed.slug || entry.name.replace(/\.md$/, "");
35303
35643
  const disabled = disabledSet.has(slug);
@@ -35420,13 +35760,13 @@ init_fs();
35420
35760
  init_config2();
35421
35761
  init_slug();
35422
35762
  import { Command as Command2 } from "commander";
35423
- import { readFile as readFile51 } from "fs/promises";
35763
+ import { readFile as readFile52 } from "fs/promises";
35424
35764
  import { resolve as resolve79 } from "path";
35425
35765
 
35426
35766
  // src/commands/bundle.ts
35427
35767
  init_paths();
35428
35768
  import { Command } from "commander";
35429
- import { mkdir as mkdir10, readFile as readFile50, readdir as readdir27, rm as rm9, writeFile as writeFile13 } from "fs/promises";
35769
+ import { mkdir as mkdir10, readFile as readFile51, readdir as readdir28, rm as rm9, writeFile as writeFile13 } from "fs/promises";
35430
35770
  import { resolve as resolve78 } from "path";
35431
35771
  init_parser2();
35432
35772
  init_fs();
@@ -35719,7 +36059,7 @@ bundleCommand.command("plan").description("Create or open the bundle's shared pl
35719
36059
  }
35720
36060
  const planDir = bundlePlanDir(sc.todosPath, sc.scopeId, id);
35721
36061
  await ensureDir(planDir);
35722
- const existing = (await readdir27(planDir).catch(() => [])).filter((f) => /^plan(?:-v\d+)?\.md$/.test(f));
36062
+ const existing = (await readdir28(planDir).catch(() => [])).filter((f) => /^plan(?:-v\d+)?\.md$/.test(f));
35723
36063
  const { target } = pickNextPlanFile(planDir, existing);
35724
36064
  const checklist = await readChecklist(sc.todosPath, sc.checklistKey);
35725
36065
  if (!await fileExists(target)) {
@@ -35788,8 +36128,8 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
35788
36128
  }
35789
36129
  const bundlesFilePath = bundlesPath(sc.todosPath);
35790
36130
  const checklistFilePath = checklistPath(sc.todosPath, sc.checklistKey);
35791
- const bundlesSnapshot = await fileExists(bundlesFilePath) ? await readFile50(bundlesFilePath, "utf-8") : null;
35792
- const checklistSnapshot = await fileExists(checklistFilePath) ? await readFile50(checklistFilePath, "utf-8") : null;
36131
+ const bundlesSnapshot = await fileExists(bundlesFilePath) ? await readFile51(bundlesFilePath, "utf-8") : null;
36132
+ const checklistSnapshot = await fileExists(checklistFilePath) ? await readFile51(checklistFilePath, "utf-8") : null;
35793
36133
  const record = async () => {
35794
36134
  bundle.branch = options.branch;
35795
36135
  bundle.worktreePath = worktreePath;
@@ -36345,7 +36685,7 @@ todoCommand.command("archive").description("Archive completed todos and their lo
36345
36685
  await ensureDir(resolve79(todosPath, "archive"));
36346
36686
  let archContent = "";
36347
36687
  if (await fileExists(archFile)) {
36348
- archContent = await readFile51(archFile, "utf-8");
36688
+ archContent = await readFile52(archFile, "utf-8");
36349
36689
  archContent = archContent.trimEnd() + "\n\n";
36350
36690
  } else {
36351
36691
  archContent = `---
@@ -36523,12 +36863,12 @@ function describeScope(scope) {
36523
36863
  }
36524
36864
  async function injectPromotedTodos(assignmentDir, todos, scopeLabel) {
36525
36865
  const { resolve: resolvePath2 } = await import("path");
36526
- const { readFile: readFile81 } = await import("fs/promises");
36866
+ const { readFile: readFile82 } = await import("fs/promises");
36527
36867
  const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
36528
36868
  const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
36529
36869
  const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
36530
36870
  const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
36531
- let content = await readFile81(assignmentMdPath2, "utf-8");
36871
+ let content = await readFile82(assignmentMdPath2, "utf-8");
36532
36872
  content = appendTodosToAssignmentBody2(
36533
36873
  content,
36534
36874
  todos.map((t) => ({
@@ -36573,8 +36913,8 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
36573
36913
  }
36574
36914
  const planDir = todoPlanDir(todosPath, workspace, id);
36575
36915
  await ensureDir(planDir);
36576
- const { readdir: readdir40 } = await import("fs/promises");
36577
- const existingFiles = (await readdir40(planDir).catch(() => [])).filter(
36916
+ const { readdir: readdir41 } = await import("fs/promises");
36917
+ const existingFiles = (await readdir41(planDir).catch(() => [])).filter(
36578
36918
  (f) => /^plan(?:-v\d+)?\.md$/.test(f)
36579
36919
  );
36580
36920
  let target;
@@ -36783,13 +37123,13 @@ backupCommand.command("config").description("Show or update backup configuration
36783
37123
 
36784
37124
  // src/commands/doctor.ts
36785
37125
  import { Command as Command4 } from "commander";
36786
- import { readFile as readFile63 } from "fs/promises";
37126
+ import { readFile as readFile64 } from "fs/promises";
36787
37127
  import { isAbsolute as isAbsolute11, resolve as resolve93 } from "path";
36788
37128
 
36789
37129
  // src/utils/doctor/index.ts
36790
37130
  import { fileURLToPath as fileURLToPath12 } from "url";
36791
- import { readFile as readFile62 } from "fs/promises";
36792
- import { dirname as dirname25, join as join21 } from "path";
37131
+ import { readFile as readFile63 } from "fs/promises";
37132
+ import { dirname as dirname25, join as join22 } from "path";
36793
37133
 
36794
37134
  // src/utils/doctor/context.ts
36795
37135
  init_config2();
@@ -36835,9 +37175,9 @@ function closeCheckContext(ctx) {
36835
37175
  init_fs();
36836
37176
  init_paths();
36837
37177
  import { resolve as resolve81, isAbsolute as isAbsolute9 } from "path";
36838
- import { readFile as readFile52, stat as stat9 } from "fs/promises";
37178
+ import { readFile as readFile53, stat as stat9 } from "fs/promises";
36839
37179
  import { fileURLToPath as fileURLToPath10 } from "url";
36840
- import { dirname as dirname22, join as join17 } from "path";
37180
+ import { dirname as dirname22, join as join18 } from "path";
36841
37181
  var CATEGORY = "env";
36842
37182
  var syntaurRootExists = {
36843
37183
  id: "env.syntaur-root-exists",
@@ -36892,7 +37232,7 @@ var configValid = {
36892
37232
  autoFixable: false
36893
37233
  };
36894
37234
  }
36895
- const content = await readFile52(configPath2, "utf-8");
37235
+ const content = await readFile53(configPath2, "utf-8");
36896
37236
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
36897
37237
  if (!fmMatch || fmMatch[1].trim() === "") {
36898
37238
  return {
@@ -37170,9 +37510,9 @@ async function readLocalPkg() {
37170
37510
  const here = fileURLToPath10(import.meta.url);
37171
37511
  let dir = dirname22(here);
37172
37512
  for (let i = 0; i < 6; i++) {
37173
- const candidate = join17(dir, "package.json");
37513
+ const candidate = join18(dir, "package.json");
37174
37514
  try {
37175
- const text = await readFile52(candidate, "utf-8");
37515
+ const text = await readFile53(candidate, "utf-8");
37176
37516
  return JSON.parse(text);
37177
37517
  } catch {
37178
37518
  dir = dirname22(dir);
@@ -37225,7 +37565,7 @@ function versionGte(a, b) {
37225
37565
  // src/utils/doctor/checks/structure.ts
37226
37566
  init_fs();
37227
37567
  import { resolve as resolve82 } from "path";
37228
- import { readdir as readdir28, stat as stat10 } from "fs/promises";
37568
+ import { readdir as readdir29, stat as stat10 } from "fs/promises";
37229
37569
  var CATEGORY2 = "structure";
37230
37570
  var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
37231
37571
  "projects",
@@ -37356,7 +37696,7 @@ var knownFilesRecognized = {
37356
37696
  category: CATEGORY2,
37357
37697
  title: "No unexpected top-level entries under ~/.syntaur/",
37358
37698
  async run(ctx) {
37359
- const entries = await readdir28(ctx.syntaurRoot, { withFileTypes: true });
37699
+ const entries = await readdir29(ctx.syntaurRoot, { withFileTypes: true });
37360
37700
  const unexpected = [];
37361
37701
  for (const e of entries) {
37362
37702
  if (e.name.startsWith(".")) continue;
@@ -37400,7 +37740,7 @@ function pass2(check) {
37400
37740
  // src/utils/doctor/checks/project.ts
37401
37741
  init_fs();
37402
37742
  import { resolve as resolve83 } from "path";
37403
- import { readdir as readdir29, stat as stat11 } from "fs/promises";
37743
+ import { readdir as readdir30, stat as stat11 } from "fs/promises";
37404
37744
  var CATEGORY3 = "project";
37405
37745
  var REQUIRED_PROJECT_FILES = [
37406
37746
  "project.md",
@@ -37424,7 +37764,7 @@ var PROJECT_MARKERS = ["project.md", "manifest.md", "assignments"];
37424
37764
  async function listProjects2(ctx) {
37425
37765
  const dir = ctx.config.defaultProjectDir;
37426
37766
  if (!await fileExists(dir)) return [];
37427
- const entries = await readdir29(dir, { withFileTypes: true });
37767
+ const entries = await readdir30(dir, { withFileTypes: true });
37428
37768
  const result = [];
37429
37769
  for (const e of entries) {
37430
37770
  if (!e.isDirectory()) continue;
@@ -37518,7 +37858,7 @@ var orphanFiles = {
37518
37858
  const projects = await listProjects2(ctx);
37519
37859
  const results = [];
37520
37860
  for (const projectDir of projects) {
37521
- const entries = await readdir29(projectDir, { withFileTypes: true });
37861
+ const entries = await readdir30(projectDir, { withFileTypes: true });
37522
37862
  const orphans = [];
37523
37863
  for (const e of entries) {
37524
37864
  if (e.name.startsWith(".")) continue;
@@ -37548,7 +37888,7 @@ async function newestAssignmentMtime(projectDir) {
37548
37888
  let newest = 0;
37549
37889
  let entries;
37550
37890
  try {
37551
- entries = await readdir29(assignmentsRoot, { withFileTypes: true });
37891
+ entries = await readdir30(assignmentsRoot, { withFileTypes: true });
37552
37892
  } catch {
37553
37893
  return 0;
37554
37894
  }
@@ -37580,7 +37920,7 @@ init_types();
37580
37920
  init_paths();
37581
37921
  init_assignment_walk();
37582
37922
  import { resolve as resolve84 } from "path";
37583
- import { readFile as readFile53, readdir as readdir30 } from "fs/promises";
37923
+ import { readFile as readFile54, readdir as readdir31 } from "fs/promises";
37584
37924
  var CATEGORY4 = "assignment";
37585
37925
  var STATUSES_REQUIRING_HANDOFF = /* @__PURE__ */ new Set(["review", "completed"]);
37586
37926
  var PRE_WORKSPACE_STATUSES = /* @__PURE__ */ new Set([
@@ -37928,7 +38268,7 @@ var draftMissingObjective = {
37928
38268
  if (parsed.status !== "draft") continue;
37929
38269
  let raw2;
37930
38270
  try {
37931
- raw2 = await readFile53(path, "utf-8");
38271
+ raw2 = await readFile54(path, "utf-8");
37932
38272
  } catch {
37933
38273
  continue;
37934
38274
  }
@@ -37965,12 +38305,12 @@ var readyToImplementMissingPlan = {
37965
38305
  const parsed = await parseSafe4(path);
37966
38306
  if (!parsed) continue;
37967
38307
  if (parsed.status !== "ready_to_implement") continue;
37968
- const entries = await readdir30(a.assignmentDir).catch(() => []);
38308
+ const entries = await readdir31(a.assignmentDir).catch(() => []);
37969
38309
  const planFiles = entries.filter((f) => /^plan(?:-v\d+)?\.md$/i.test(f));
37970
38310
  let hasPlanContent = false;
37971
38311
  for (const f of planFiles) {
37972
38312
  try {
37973
- const c2 = await readFile53(resolve84(a.assignmentDir, f), "utf-8");
38313
+ const c2 = await readFile54(resolve84(a.assignmentDir, f), "utf-8");
37974
38314
  if (c2.trim().length > 0) {
37975
38315
  hasPlanContent = true;
37976
38316
  break;
@@ -38013,7 +38353,7 @@ var assignmentChecks = [
38013
38353
  ];
38014
38354
  async function parseSafe4(path) {
38015
38355
  try {
38016
- const content = await readFile53(path, "utf-8");
38356
+ const content = await readFile54(path, "utf-8");
38017
38357
  return parseAssignmentFull(content);
38018
38358
  } catch {
38019
38359
  return null;
@@ -38176,7 +38516,7 @@ function skipped(check, reason) {
38176
38516
  // src/utils/doctor/checks/integrations.ts
38177
38517
  init_fs();
38178
38518
  import { resolve as resolve86, dirname as dirname23, basename as basename9 } from "path";
38179
- import { readdir as readdir31, readFile as readFile54 } from "fs/promises";
38519
+ import { readdir as readdir32, readFile as readFile55 } from "fs/promises";
38180
38520
  import { homedir as homedir14 } from "os";
38181
38521
  var CATEGORY6 = "integrations";
38182
38522
  var claudePluginLinked = {
@@ -38239,7 +38579,7 @@ var backupConfigured = {
38239
38579
  if (ctx.config.backup?.repo) return pass6(this);
38240
38580
  const projectsDir2 = ctx.config.defaultProjectDir;
38241
38581
  if (!await fileExists(projectsDir2)) return skipped2(this, "no projects dir");
38242
- const entries = await readdir31(projectsDir2, { withFileTypes: true });
38582
+ const entries = await readdir32(projectsDir2, { withFileTypes: true });
38243
38583
  const hasProjects = entries.some((e) => e.isDirectory() && !e.name.startsWith(".") && !e.name.startsWith("_"));
38244
38584
  if (!hasProjects) return skipped2(this, "no projects yet");
38245
38585
  return {
@@ -38261,7 +38601,7 @@ async function readKnownMarketplaces() {
38261
38601
  const path = resolve86(homedir14(), ".claude", "plugins", "known_marketplaces.json");
38262
38602
  if (!await fileExists(path)) return {};
38263
38603
  try {
38264
- const raw2 = await readFile54(path, "utf-8");
38604
+ const raw2 = await readFile55(path, "utf-8");
38265
38605
  return JSON.parse(raw2);
38266
38606
  } catch {
38267
38607
  return {};
@@ -38314,7 +38654,7 @@ var claudeMarketplaceRegistered = {
38314
38654
  }
38315
38655
  let parsed = {};
38316
38656
  try {
38317
- parsed = JSON.parse(await readFile54(marketplaceManifest, "utf-8"));
38657
+ parsed = JSON.parse(await readFile55(marketplaceManifest, "utf-8"));
38318
38658
  } catch {
38319
38659
  return {
38320
38660
  id: this.id,
@@ -38387,7 +38727,7 @@ init_fs();
38387
38727
  init_parser();
38388
38728
  init_types();
38389
38729
  import { resolve as resolve87 } from "path";
38390
- import { readFile as readFile55 } from "fs/promises";
38730
+ import { readFile as readFile56 } from "fs/promises";
38391
38731
  var CATEGORY7 = "workspace";
38392
38732
  var ASSIGNMENT_FIELDS2 = ["projectSlug", "assignmentSlug", "projectDir", "assignmentDir"];
38393
38733
  var BUNDLE_FIELDS = ["bundleId", "bundleScope", "bundleScopeId"];
@@ -38419,7 +38759,7 @@ async function loadContext(ctx) {
38419
38759
  return { data: null, path, exists: false, parseError: null };
38420
38760
  }
38421
38761
  try {
38422
- const raw2 = await readFile55(path, "utf-8");
38762
+ const raw2 = await readFile56(path, "utf-8");
38423
38763
  return { data: JSON.parse(raw2), path, exists: true, parseError: null };
38424
38764
  } catch (err2) {
38425
38765
  return {
@@ -38531,7 +38871,7 @@ var contextTerminal = {
38531
38871
  const assignmentMd = resolve87(data.assignmentDir, "assignment.md");
38532
38872
  if (!await fileExists(assignmentMd)) return skipped3(this, "assignment file missing");
38533
38873
  try {
38534
- const content = await readFile55(assignmentMd, "utf-8");
38874
+ const content = await readFile56(assignmentMd, "utf-8");
38535
38875
  const parsed = parseAssignmentFull(content);
38536
38876
  const terminal = terminalStatuses2(ctx);
38537
38877
  if (terminal.has(parsed.status)) {
@@ -38677,7 +39017,7 @@ var agentChecks = [agentsResolvable];
38677
39017
  // src/utils/doctor/checks/terminal.ts
38678
39018
  init_config2();
38679
39019
  import { spawnSync as spawnSync9 } from "child_process";
38680
- import { readFile as readFile56 } from "fs/promises";
39020
+ import { readFile as readFile57 } from "fs/promises";
38681
39021
  import { resolve as resolve88 } from "path";
38682
39022
  init_paths();
38683
39023
  init_fs();
@@ -38685,7 +39025,7 @@ var CATEGORY9 = "terminal";
38685
39025
  async function readRawTerminalKey() {
38686
39026
  const configPath2 = resolve88(syntaurRoot(), "config.md");
38687
39027
  if (!await fileExists(configPath2)) return null;
38688
- const content = await readFile56(configPath2, "utf-8");
39028
+ const content = await readFile57(configPath2, "utf-8");
38689
39029
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
38690
39030
  if (!fmMatch) return null;
38691
39031
  const line = fmMatch[1].split("\n").find((l) => /^terminal:\s*/.test(l));
@@ -38835,8 +39175,8 @@ var terminalChecks = [
38835
39175
 
38836
39176
  // src/utils/doctor/checks/skills.ts
38837
39177
  init_fs();
38838
- import { resolve as resolve89, join as join18 } from "path";
38839
- import { readdir as readdir32, readFile as readFile57, lstat as lstat4 } from "fs/promises";
39178
+ import { resolve as resolve89, join as join19 } from "path";
39179
+ import { readdir as readdir33, readFile as readFile58, lstat as lstat4 } from "fs/promises";
38840
39180
  import { homedir as homedir15 } from "os";
38841
39181
  var CATEGORY10 = "skills";
38842
39182
  var skillTargets = [
@@ -38856,21 +39196,21 @@ var skillsDedupCheck = {
38856
39196
  const present = [];
38857
39197
  let entries;
38858
39198
  try {
38859
- entries = await readdir32(dir, { withFileTypes: true });
39199
+ entries = await readdir33(dir, { withFileTypes: true });
38860
39200
  } catch {
38861
39201
  continue;
38862
39202
  }
38863
39203
  for (const entry of entries) {
38864
39204
  if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
38865
39205
  if (!KNOWN_SKILLS.includes(entry.name)) continue;
38866
- const skillMd = join18(dir, entry.name, "SKILL.md");
39206
+ const skillMd = join19(dir, entry.name, "SKILL.md");
38867
39207
  if (!await fileExists(skillMd)) continue;
38868
- const content = await readFile57(skillMd, "utf-8").catch(() => "");
39208
+ const content = await readFile58(skillMd, "utf-8").catch(() => "");
38869
39209
  const match = content.match(/^name:\s*(\S+)\s*$/m);
38870
39210
  if (!match || match[1] !== entry.name) continue;
38871
39211
  let isSymlink2 = false;
38872
39212
  try {
38873
- isSymlink2 = (await lstat4(join18(dir, entry.name))).isSymbolicLink();
39213
+ isSymlink2 = (await lstat4(join19(dir, entry.name))).isSymbolicLink();
38874
39214
  } catch {
38875
39215
  }
38876
39216
  present.push({ name: entry.name, isSymlink: isSymlink2 });
@@ -38882,7 +39222,7 @@ var skillsDedupCheck = {
38882
39222
  findings.push(
38883
39223
  `${label}: ${nonSymlink.length} syntaur skill(s) installed globally while the syntaur plugin is enabled (${agent}) \u2014 duplicate registrations`
38884
39224
  );
38885
- for (const p of nonSymlink) affected.push(join18(dir, p.name));
39225
+ for (const p of nonSymlink) affected.push(join19(dir, p.name));
38886
39226
  }
38887
39227
  }
38888
39228
  }
@@ -38915,12 +39255,12 @@ var skillsChecks = [skillsDedupCheck];
38915
39255
 
38916
39256
  // src/utils/doctor/checks/cross-agent.ts
38917
39257
  init_fs();
38918
- import { join as join19, resolve as resolve90 } from "path";
38919
- import { readFile as readFile59 } from "fs/promises";
39258
+ import { join as join20, resolve as resolve90 } from "path";
39259
+ import { readFile as readFile60 } from "fs/promises";
38920
39260
 
38921
39261
  // src/utils/skill-frontmatter.ts
38922
39262
  import { createHash as createHash4 } from "crypto";
38923
- import { readFile as readFile58 } from "fs/promises";
39263
+ import { readFile as readFile59 } from "fs/promises";
38924
39264
  function stripQuotes(raw2) {
38925
39265
  const t = raw2.trim();
38926
39266
  if (t.length >= 2 && (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'"))) {
@@ -38964,7 +39304,7 @@ function readSkillIdentity(skillMdText) {
38964
39304
  return { name, hasDescription };
38965
39305
  }
38966
39306
  async function sha256File(path) {
38967
- return createHash4("sha256").update(await readFile58(path)).digest("hex");
39307
+ return createHash4("sha256").update(await readFile59(path)).digest("hex");
38968
39308
  }
38969
39309
 
38970
39310
  // src/utils/doctor/checks/cross-agent.ts
@@ -38974,14 +39314,14 @@ async function checkTargetSkillsIntegrity(installedDir, canonicalSkillsDir, know
38974
39314
  const problems = [];
38975
39315
  let valid = 0;
38976
39316
  for (const skill of knownSkills) {
38977
- const installedPath = join19(installedDir, skill, "SKILL.md");
39317
+ const installedPath = join20(installedDir, skill, "SKILL.md");
38978
39318
  if (!await fileExists(installedPath)) {
38979
39319
  problems.push({ skill, kind: "missing" });
38980
39320
  continue;
38981
39321
  }
38982
39322
  let text;
38983
39323
  try {
38984
- text = await readFile59(installedPath, "utf-8");
39324
+ text = await readFile60(installedPath, "utf-8");
38985
39325
  } catch {
38986
39326
  problems.push({ skill, kind: "invalid-frontmatter" });
38987
39327
  continue;
@@ -38992,7 +39332,7 @@ async function checkTargetSkillsIntegrity(installedDir, canonicalSkillsDir, know
38992
39332
  continue;
38993
39333
  }
38994
39334
  valid++;
38995
- const canonicalPath2 = join19(canonicalSkillsDir, skill, "SKILL.md");
39335
+ const canonicalPath2 = join20(canonicalSkillsDir, skill, "SKILL.md");
38996
39336
  if (await fileExists(canonicalPath2)) {
38997
39337
  const [installedHash, canonicalHash] = await Promise.all([
38998
39338
  sha256File(installedPath),
@@ -39067,7 +39407,7 @@ var crossAgentSkillsCheck = {
39067
39407
  }
39068
39408
  if (t.tier3) {
39069
39409
  const installDir = t.tier3.installDir();
39070
- const installed2 = await fileExists(join19(installDir, t.tier3.entry));
39410
+ const installed2 = await fileExists(join20(installDir, t.tier3.entry));
39071
39411
  lines.push(
39072
39412
  `${t.displayName}: Tier-3 ${t.tier3.kind} ${installed2 ? "installed" : "absent"} (${installDir})`
39073
39413
  );
@@ -39135,7 +39475,7 @@ var crossAgentChecks = [crossAgentSkillsCheck];
39135
39475
  init_fs();
39136
39476
  init_paths();
39137
39477
  import { resolve as resolve91 } from "path";
39138
- import { readdir as readdir33 } from "fs/promises";
39478
+ import { readdir as readdir34 } from "fs/promises";
39139
39479
  import { spawnSync as spawnSync10 } from "child_process";
39140
39480
  init_parser2();
39141
39481
  var CATEGORY12 = "bundles";
@@ -39143,7 +39483,7 @@ async function listScopes(ctx) {
39143
39483
  const out = [];
39144
39484
  const td = todosDir();
39145
39485
  if (await fileExists(td)) {
39146
- const entries = await readdir33(td).catch(() => []);
39486
+ const entries = await readdir34(td).catch(() => []);
39147
39487
  for (const f of entries) {
39148
39488
  if (typeof f !== "string") continue;
39149
39489
  if (!f.endsWith(".md") || f.endsWith("-log.md")) continue;
@@ -39159,7 +39499,7 @@ async function listScopes(ctx) {
39159
39499
  }
39160
39500
  }
39161
39501
  if (await fileExists(ctx.config.defaultProjectDir)) {
39162
- const projectEntries = await readdir33(ctx.config.defaultProjectDir, { withFileTypes: true }).catch(() => []);
39502
+ const projectEntries = await readdir34(ctx.config.defaultProjectDir, { withFileTypes: true }).catch(() => []);
39163
39503
  for (const e of projectEntries) {
39164
39504
  if (!e.isDirectory()) continue;
39165
39505
  const slug = e.name;
@@ -39357,8 +39697,8 @@ var bundleChecks = [
39357
39697
  ];
39358
39698
 
39359
39699
  // src/utils/doctor/checks/plugin.ts
39360
- import { readFile as readFile60 } from "fs/promises";
39361
- import { dirname as dirname24, join as join20 } from "path";
39700
+ import { readFile as readFile61 } from "fs/promises";
39701
+ import { dirname as dirname24, join as join21 } from "path";
39362
39702
  import { fileURLToPath as fileURLToPath11 } from "url";
39363
39703
  var CATEGORY13 = "plugin";
39364
39704
  var PLUGIN_KINDS = ["claude", "codex"];
@@ -39367,7 +39707,7 @@ async function readCliVersion() {
39367
39707
  let dir = dirname24(fileURLToPath11(import.meta.url));
39368
39708
  for (let i = 0; i < 8; i += 1) {
39369
39709
  try {
39370
- const parsed = JSON.parse(await readFile60(join20(dir, "package.json"), "utf-8"));
39710
+ const parsed = JSON.parse(await readFile61(join21(dir, "package.json"), "utf-8"));
39371
39711
  if (typeof parsed.version === "string" && parsed.version.length > 0) return parsed.version;
39372
39712
  } catch {
39373
39713
  }
@@ -39478,7 +39818,7 @@ var deriveConfigChecks = [deriveConfigValid];
39478
39818
  // src/utils/doctor/checks/staleness.ts
39479
39819
  init_config2();
39480
39820
  import { resolve as resolve92 } from "path";
39481
- import { readFile as readFile61 } from "fs/promises";
39821
+ import { readFile as readFile62 } from "fs/promises";
39482
39822
  var CATEGORY15 = "staleness";
39483
39823
  var stalenessConfigValid = {
39484
39824
  id: "staleness.valid",
@@ -39487,7 +39827,7 @@ var stalenessConfigValid = {
39487
39827
  async run(ctx) {
39488
39828
  let content;
39489
39829
  try {
39490
- content = await readFile61(resolve92(ctx.syntaurRoot, "config.md"), "utf-8");
39830
+ content = await readFile62(resolve92(ctx.syntaurRoot, "config.md"), "utf-8");
39491
39831
  } catch {
39492
39832
  return {
39493
39833
  id: this.id,
@@ -39640,7 +39980,7 @@ async function readVersion() {
39640
39980
  let dir = dirname25(here);
39641
39981
  for (let i = 0; i < 6; i++) {
39642
39982
  try {
39643
- const raw2 = await readFile62(join21(dir, "package.json"), "utf-8");
39983
+ const raw2 = await readFile63(join22(dir, "package.json"), "utf-8");
39644
39984
  const parsed = JSON.parse(raw2);
39645
39985
  return typeof parsed.version === "string" ? parsed.version : null;
39646
39986
  } catch {
@@ -39750,7 +40090,7 @@ async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
39750
40090
  }
39751
40091
  let content;
39752
40092
  try {
39753
- content = await readFile63(absolute, "utf-8");
40093
+ content = await readFile64(absolute, "utf-8");
39754
40094
  } catch (err2) {
39755
40095
  return {
39756
40096
  ok: false,
@@ -39876,6 +40216,7 @@ note: ${FIX_NOOP_NOTE}
39876
40216
 
39877
40217
  // src/commands/agents.ts
39878
40218
  init_config2();
40219
+ init_paths();
39879
40220
  import { Command as Command5 } from "commander";
39880
40221
  var agentsCommand = new Command5("agents").description(
39881
40222
  "Manage configurable agents used by `syntaur browse` and future launch flows"
@@ -39894,6 +40235,8 @@ agentsCommand.command("list").description("List configured agents (or built-in d
39894
40235
  if (agent.model) flags.push(`model=${agent.model}`);
39895
40236
  if (agent.playbook) flags.push(`playbook=${agent.playbook}`);
39896
40237
  if (agent.launchPrompt) flags.push(`launchPrompt=${truncateForList(agent.launchPrompt)}`);
40238
+ if (agent.agentName) flags.push(`agentName=${agent.agentName}`);
40239
+ if (agent.workdir) flags.push(`workdir=${truncateForList(agent.workdir)}`);
39897
40240
  const flagStr = flags.length > 0 ? ` [${flags.join(", ")}]` : "";
39898
40241
  const args = agent.args && agent.args.length > 0 ? ` ${agent.args.join(" ")}` : "";
39899
40242
  console.log(` ${agent.id.padEnd(12)} ${agent.label.padEnd(20)} ${agent.command}${args}${flagStr}`);
@@ -39902,7 +40245,7 @@ agentsCommand.command("list").description("List configured agents (or built-in d
39902
40245
  reportAndExit(error);
39903
40246
  }
39904
40247
  });
39905
- agentsCommand.command("add").description("Add a new agent to ~/.syntaur/config.md").requiredOption("--id <id>", "Agent id (slug)").requiredOption("--label <label>", "Display label").requiredOption("--command <command>", "Absolute path or bare binary name").option("--args <csv>", "Comma-separated default args").option("--prompt-arg-position <position>", "first | last | none").option("--default", "Mark this agent as the default launch target").option("--resolve-from-shell-aliases", "Run via $SHELL -i -c (for shell aliases)").option("--model <model>", "LLM model injected as --model <value> at launch").option("--playbook <slug>", "Playbook slug to run end-to-end on a fresh launch").option("--launch-prompt <text>", "Editable launch prompt with @assignment / @<playbook> tokens").option("--dry-run", "Validate and print the proposed config without writing").action(async (options) => {
40248
+ agentsCommand.command("add").description("Add a new agent to ~/.syntaur/config.md").requiredOption("--id <id>", "Agent id (slug)").requiredOption("--label <label>", "Display label").requiredOption("--command <command>", "Absolute path or bare binary name").option("--args <csv>", "Comma-separated default args").option("--prompt-arg-position <position>", "first | last | none").option("--default", "Mark this agent as the default launch target").option("--resolve-from-shell-aliases", "Run via $SHELL -i -c (for shell aliases)").option("--model <model>", "LLM model injected as --model <value> at launch").option("--playbook <slug>", "Playbook slug to run end-to-end on a fresh launch").option("--launch-prompt <text>", "Editable launch prompt with @assignment / @worktree / @<playbook> tokens").option("--agent-name <name>", "Claude agent identity injected as --agent <name> (Claude-compatible runners)").option("--workdir <path>", "Launch cwd override for directory-agents (pi/codex); mutually exclusive with --agent-name").option("--dry-run", "Validate and print the proposed config without writing").action(async (options) => {
39906
40249
  try {
39907
40250
  const agent = buildAgentFromOptions(options, null);
39908
40251
  const mutation = {
@@ -39942,7 +40285,7 @@ agentsCommand.command("remove <id>").description("Remove an agent from ~/.syntau
39942
40285
  reportAndExit(error);
39943
40286
  }
39944
40287
  });
39945
- agentsCommand.command("set <id>").description("Update one or more fields on an existing agent").option("--label <label>", "Display label").option("--command <command>", "Absolute path or bare binary name").option("--args <csv>", "Comma-separated default args").option("--prompt-arg-position <position>", "first | last | none").option("--default", "Mark this agent as the default (clears any prior default)").option("--no-default", "Unset the default flag on this agent").option("--resolve-from-shell-aliases", "Run via $SHELL -i -c (for shell aliases)").option("--no-resolve-from-shell-aliases", "Disable shell-alias resolution for this agent").option("--model <model>", "LLM model injected as --model <value> (empty string clears)").option("--playbook <slug>", "Playbook slug to run on a fresh launch (empty string clears)").option("--launch-prompt <text>", "Editable launch prompt with @-tokens (empty string clears)").option("--dry-run", "Validate and print the proposed config without writing").action(async (id, options) => {
40288
+ agentsCommand.command("set <id>").description("Update one or more fields on an existing agent").option("--label <label>", "Display label").option("--command <command>", "Absolute path or bare binary name").option("--args <csv>", "Comma-separated default args").option("--prompt-arg-position <position>", "first | last | none").option("--default", "Mark this agent as the default (clears any prior default)").option("--no-default", "Unset the default flag on this agent").option("--resolve-from-shell-aliases", "Run via $SHELL -i -c (for shell aliases)").option("--no-resolve-from-shell-aliases", "Disable shell-alias resolution for this agent").option("--model <model>", "LLM model injected as --model <value> (empty string clears)").option("--playbook <slug>", "Playbook slug to run on a fresh launch (empty string clears)").option("--launch-prompt <text>", "Editable launch prompt with @-tokens (empty string clears)").option("--agent-name <name>", "Claude agent identity injected as --agent <name> (empty string clears)").option("--workdir <path>", "Launch cwd override for directory-agents (empty string clears)").option("--dry-run", "Validate and print the proposed config without writing").action(async (id, options) => {
39946
40289
  try {
39947
40290
  const mutation = {
39948
40291
  kind: "set",
@@ -40003,6 +40346,36 @@ agentsCommand.command("reorder <ids>").description("Reorder agents (comma-separa
40003
40346
  reportAndExit(error);
40004
40347
  }
40005
40348
  });
40349
+ agentsCommand.command("launch <id>").description("Launch a directory-agent standalone (no assignment) from its workdir").option("--prompt <text>", "One-shot launch prompt override for this launch only").option("--cwd <path>", "Override the agent workdir for this launch only").action(async (id, options) => {
40350
+ try {
40351
+ const config = await readConfig();
40352
+ const agents = getAgents(config);
40353
+ const agent = agents.find((a) => a.id === id);
40354
+ if (!agent) {
40355
+ throw new AgentConfigError(`unknown agent id "${id}"`);
40356
+ }
40357
+ let effectiveConfig = config;
40358
+ if (options.cwd !== void 0) {
40359
+ const cloned = { ...agent, workdir: expandHome(options.cwd) };
40360
+ effectiveConfig = { ...config, agents: agents.map((a) => a.id === id ? cloned : a) };
40361
+ }
40362
+ const projectsDir2 = config.defaultProjectDir || defaultProjectDir();
40363
+ const plan = await resolveLaunchPlan({
40364
+ kind: "standalone",
40365
+ id,
40366
+ agentId: id,
40367
+ config: effectiveConfig,
40368
+ projectsDir: projectsDir2,
40369
+ assignmentsDir: assignmentsDir(),
40370
+ promptOverride: options.prompt
40371
+ });
40372
+ if (plan.shellFallbackWarning) console.error(plan.shellFallbackWarning);
40373
+ for (const warning of plan.promptWarnings ?? []) console.error(warning);
40374
+ await executeLaunchPlan(plan);
40375
+ } catch (error) {
40376
+ reportAndExit(error);
40377
+ }
40378
+ });
40006
40379
  function buildAgentFromOptions(options, existing) {
40007
40380
  const agent = {
40008
40381
  id: options.id,
@@ -40019,6 +40392,8 @@ function buildAgentFromOptions(options, existing) {
40019
40392
  if (options.model && options.model.trim()) agent.model = options.model.trim();
40020
40393
  if (options.playbook && options.playbook.trim()) agent.playbook = options.playbook.trim();
40021
40394
  if (options.launchPrompt && options.launchPrompt.trim()) agent.launchPrompt = options.launchPrompt;
40395
+ if (options.agentName && options.agentName.trim()) agent.agentName = options.agentName.trim();
40396
+ if (options.workdir && options.workdir.trim()) agent.workdir = options.workdir.trim();
40022
40397
  validateAgentList([...existing ? [] : [], agent]);
40023
40398
  return agent;
40024
40399
  }
@@ -40054,6 +40429,16 @@ function mergeOptionsIntoAgent(existing, options) {
40054
40429
  if (options.launchPrompt.trim()) merged.launchPrompt = options.launchPrompt;
40055
40430
  else delete merged.launchPrompt;
40056
40431
  }
40432
+ if (options.agentName !== void 0) {
40433
+ const agentName = options.agentName.trim();
40434
+ if (agentName) merged.agentName = agentName;
40435
+ else delete merged.agentName;
40436
+ }
40437
+ if (options.workdir !== void 0) {
40438
+ const workdir = options.workdir.trim();
40439
+ if (workdir) merged.workdir = workdir;
40440
+ else delete merged.workdir;
40441
+ }
40057
40442
  return merged;
40058
40443
  }
40059
40444
  function parseArgsCsv(csv) {
@@ -40094,6 +40479,8 @@ function formatAgentLine(a) {
40094
40479
  if (a.model) flags.push(`model=${a.model}`);
40095
40480
  if (a.playbook) flags.push(`playbook=${a.playbook}`);
40096
40481
  if (a.launchPrompt) flags.push(`launchPrompt=${a.launchPrompt}`);
40482
+ if (a.agentName) flags.push(`agentName=${a.agentName}`);
40483
+ if (a.workdir) flags.push(`workdir=${a.workdir}`);
40097
40484
  if (a.args && a.args.length > 0) flags.push(`args=[${a.args.join(", ")}]`);
40098
40485
  const suffix = flags.length > 0 ? ` (${flags.join(", ")})` : "";
40099
40486
  return `${a.id}: ${a.label} \u2192 ${a.command}${suffix}`;
@@ -40118,7 +40505,7 @@ init_frontmatter();
40118
40505
  init_event_emit();
40119
40506
  init_templates();
40120
40507
  import { resolve as resolve94 } from "path";
40121
- import { readFile as readFile64 } from "fs/promises";
40508
+ import { readFile as readFile65 } from "fs/promises";
40122
40509
  function shortId() {
40123
40510
  return generateId().split("-")[0];
40124
40511
  }
@@ -40167,7 +40554,7 @@ async function commentCommand(target, text, options = {}) {
40167
40554
  let currentContent;
40168
40555
  let currentCount = 0;
40169
40556
  if (await fileExists(commentsPath)) {
40170
- currentContent = await readFile64(commentsPath, "utf-8");
40557
+ currentContent = await readFile65(commentsPath, "utf-8");
40171
40558
  const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
40172
40559
  if (countMatch) currentCount = parseInt(countMatch[1], 10);
40173
40560
  } else {
@@ -40197,7 +40584,7 @@ ${entry}`;
40197
40584
  try {
40198
40585
  const assignmentMd = resolve94(assignmentDir, "assignment.md");
40199
40586
  if (await fileExists(assignmentMd)) {
40200
- const fm = parseAssignmentFrontmatter(await readFile64(assignmentMd, "utf-8"));
40587
+ const fm = parseAssignmentFrontmatter(await readFile65(assignmentMd, "utf-8"));
40201
40588
  emitEvent({
40202
40589
  assignmentId: fm.id,
40203
40590
  projectSlug,
@@ -40233,7 +40620,7 @@ init_fs();
40233
40620
  import { spawn as spawn8 } from "child_process";
40234
40621
  import { mkdtemp as mkdtemp2, rm as rm10, stat as stat12 } from "fs/promises";
40235
40622
  import { tmpdir as tmpdir3 } from "os";
40236
- import { join as join22 } from "path";
40623
+ import { join as join23 } from "path";
40237
40624
  function argsFor(mode, pngPath) {
40238
40625
  switch (mode) {
40239
40626
  case "interactive":
@@ -40266,8 +40653,8 @@ async function captureScreenshot(mode) {
40266
40653
  "screencapture is only available on macOS. Use --file <path> to attach an existing image."
40267
40654
  );
40268
40655
  }
40269
- const tmpDir = await mkdtemp2(join22(tmpdir3(), "syntaur-screenshot-"));
40270
- const pngPath = join22(tmpDir, "shot.png");
40656
+ const tmpDir = await mkdtemp2(join23(tmpdir3(), "syntaur-screenshot-"));
40657
+ const pngPath = join23(tmpDir, "shot.png");
40271
40658
  const cleanup = async () => {
40272
40659
  await rm10(tmpDir, { recursive: true, force: true }).catch(() => {
40273
40660
  });
@@ -40305,9 +40692,9 @@ async function captureScreenshot(mode) {
40305
40692
 
40306
40693
  // src/utils/asciinema.ts
40307
40694
  import { spawn as spawn9 } from "child_process";
40308
- import { mkdtemp as mkdtemp3, readFile as readFile65, rm as rm11 } from "fs/promises";
40695
+ import { mkdtemp as mkdtemp3, readFile as readFile66, rm as rm11 } from "fs/promises";
40309
40696
  import { tmpdir as tmpdir4 } from "os";
40310
- import { join as join23 } from "path";
40697
+ import { join as join24 } from "path";
40311
40698
  var SAFE_RE = /^[A-Za-z0-9_@%+=:,./-]+$/;
40312
40699
  function shellQuote2(s) {
40313
40700
  if (s.length === 0) return `''`;
@@ -40344,8 +40731,8 @@ function runAsciinema(args, stdio) {
40344
40731
  });
40345
40732
  }
40346
40733
  async function captureAsciinema(opts) {
40347
- const tmpDir = await mkdtemp3(join23(tmpdir4(), "syntaur-asciinema-"));
40348
- const castPath = join23(tmpDir, "session.cast");
40734
+ const tmpDir = await mkdtemp3(join24(tmpdir4(), "syntaur-asciinema-"));
40735
+ const castPath = join24(tmpDir, "session.cast");
40349
40736
  const cleanup = async () => {
40350
40737
  await rm11(tmpDir, { recursive: true, force: true }).catch(() => {
40351
40738
  });
@@ -40368,7 +40755,7 @@ async function captureAsciinema(opts) {
40368
40755
  }
40369
40756
  throw err2;
40370
40757
  }
40371
- const text = await readFile65(castPath, "utf8").catch(() => null);
40758
+ const text = await readFile66(castPath, "utf8").catch(() => null);
40372
40759
  if (text === null) {
40373
40760
  throw new Error(
40374
40761
  `asciinema produced no cast file at ${castPath} (exit ${exitCode}). Try running 'asciinema rec ${castPath}' directly to diagnose.`
@@ -40396,9 +40783,9 @@ async function captureAsciinema(opts) {
40396
40783
  // src/utils/recording.ts
40397
40784
  init_paths();
40398
40785
  import { spawn as spawn10 } from "child_process";
40399
- import { mkdir as mkdir11, mkdtemp as mkdtemp4, open as open6, readFile as readFile66, rm as rm12, stat as stat13, unlink as unlink12, writeFile as writeFile14 } from "fs/promises";
40786
+ import { mkdir as mkdir11, mkdtemp as mkdtemp4, open as open6, readFile as readFile67, rm as rm12, stat as stat13, unlink as unlink12, writeFile as writeFile14 } from "fs/promises";
40400
40787
  import { tmpdir as tmpdir5 } from "os";
40401
- import { join as join24, resolve as resolve95 } from "path";
40788
+ import { join as join25, resolve as resolve95 } from "path";
40402
40789
  import { setTimeout as sleep } from "timers/promises";
40403
40790
  function sigintPollIntervalMs() {
40404
40791
  const raw2 = process.env.SYNTAUR_RECORDING_POLL_INTERVAL_MS;
@@ -40470,7 +40857,7 @@ async function acquirePidfile(pidfile) {
40470
40857
  } catch (err2) {
40471
40858
  if (err2.code !== "EEXIST") throw err2;
40472
40859
  if (attempt === 1) throw err2;
40473
- const existing = (await readFile66(pidfile, "utf-8").catch(() => "")).trim();
40860
+ const existing = (await readFile67(pidfile, "utf-8").catch(() => "")).trim();
40474
40861
  if (existing.startsWith(STARTING_SENTINEL_PREFIX)) {
40475
40862
  const parentPidRaw = existing.slice(STARTING_SENTINEL_PREFIX.length);
40476
40863
  const parentPid = Number.parseInt(parentPidRaw, 10);
@@ -40523,8 +40910,8 @@ async function startRecording(input4) {
40523
40910
  let acquiredPid = null;
40524
40911
  try {
40525
40912
  logHandle = await open6(log, "w");
40526
- tmpDir = await mkdtemp4(join24(tmpdir5(), "syntaur-recording-"));
40527
- const mp4Path = join24(tmpDir, "recording.mp4");
40913
+ tmpDir = await mkdtemp4(join25(tmpdir5(), "syntaur-recording-"));
40914
+ const mp4Path = join25(tmpDir, "recording.mp4");
40528
40915
  let child;
40529
40916
  try {
40530
40917
  child = spawn10("ffmpeg", ffmpegArgs(input4.device, input4.fps, mp4Path), {
@@ -40572,7 +40959,7 @@ async function startRecording(input4) {
40572
40959
  logHandle = null;
40573
40960
  if (warmupMs > 0) await sleep(warmupMs);
40574
40961
  if (!await isProcessAlive(pid)) {
40575
- const tail = await readFile66(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
40962
+ const tail = await readFile67(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
40576
40963
  acquiredPid = null;
40577
40964
  throw new Error(
40578
40965
  `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}
@@ -40629,7 +41016,7 @@ ${tail}`
40629
41016
  async function stopRecording() {
40630
41017
  const pidfile = pidfilePath();
40631
41018
  const sidecar = sidecarPath();
40632
- const pidRaw = await readFile66(pidfile, "utf-8").catch(() => null);
41019
+ const pidRaw = await readFile67(pidfile, "utf-8").catch(() => null);
40633
41020
  if (pidRaw === null) {
40634
41021
  throw new Error(
40635
41022
  `No active recording found (no pidfile at ${pidfile}). Did you run --start?`
@@ -40639,7 +41026,7 @@ async function stopRecording() {
40639
41026
  if (!Number.isInteger(pid) || pid <= 0) {
40640
41027
  throw new Error(`Pidfile at ${pidfile} is corrupt (got "${pidRaw}").`);
40641
41028
  }
40642
- const sidecarRaw = await readFile66(sidecar, "utf-8").catch(() => null);
41029
+ const sidecarRaw = await readFile67(sidecar, "utf-8").catch(() => null);
40643
41030
  if (sidecarRaw === null) {
40644
41031
  throw new Error(
40645
41032
  `No recording sidecar at ${sidecar}. The recording state is inconsistent \u2014 delete ${pidfile} and re-run --start.`
@@ -40772,9 +41159,9 @@ function listArtifactsByAssignment(assignmentId) {
40772
41159
 
40773
41160
  // src/utils/transcribers/elevenlabs.ts
40774
41161
  import { spawn as spawn11 } from "child_process";
40775
- import { mkdtemp as mkdtemp5, readFile as readFile67, rm as rm13 } from "fs/promises";
41162
+ import { mkdtemp as mkdtemp5, readFile as readFile68, rm as rm13 } from "fs/promises";
40776
41163
  import { tmpdir as tmpdir6 } from "os";
40777
- import { join as join25 } from "path";
41164
+ import { join as join26 } from "path";
40778
41165
  var SCRIBE_URL = "https://api.elevenlabs.io/v1/speech-to-text";
40779
41166
  var NO_AUDIO_MARKERS = [
40780
41167
  "Stream map '0:a:0' matches no streams",
@@ -40836,7 +41223,7 @@ async function extractAudio(videoAbsPath, wavOut) {
40836
41223
  throw new TranscribeFfmpegError(`ffmpeg failed (exit ${result.code}): ${tail}`);
40837
41224
  }
40838
41225
  async function callScribe(wavPath, apiKey, opts) {
40839
- const audio = await readFile67(wavPath);
41226
+ const audio = await readFile68(wavPath);
40840
41227
  const form = new FormData();
40841
41228
  form.set("file", new Blob([new Uint8Array(audio)], { type: "audio/wav" }), "audio.wav");
40842
41229
  form.set("model_id", "scribe_v1");
@@ -40864,8 +41251,8 @@ var elevenLabsScribe = {
40864
41251
  "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."
40865
41252
  );
40866
41253
  }
40867
- const tmp = await mkdtemp5(join25(tmpdir6(), "syntaur-transcribe-"));
40868
- const wav = join25(tmp, "audio.wav");
41254
+ const tmp = await mkdtemp5(join26(tmpdir6(), "syntaur-transcribe-"));
41255
+ const wav = join26(tmp, "audio.wav");
40869
41256
  try {
40870
41257
  await extractAudio(videoAbsPath, wav);
40871
41258
  return await callScribe(wav, apiKey, opts);
@@ -41309,7 +41696,7 @@ async function captureCommand(target, options = {}) {
41309
41696
 
41310
41697
  // src/commands/proof.ts
41311
41698
  import { Command as Command6 } from "commander";
41312
- import { readFile as readFile68, writeFile as writeFile16, rename as rename10, stat as stat15 } from "fs/promises";
41699
+ import { readFile as readFile69, writeFile as writeFile16, rename as rename10, stat as stat15 } from "fs/promises";
41313
41700
  import { resolve as resolve98, relative as relative5, isAbsolute as isAbsolute12, dirname as dirname27 } from "path";
41314
41701
  import { randomBytes as randomBytes4 } from "crypto";
41315
41702
 
@@ -41554,7 +41941,7 @@ async function readAssignmentMeta(assignmentDir) {
41554
41941
  if (!await fileExists(path)) {
41555
41942
  return { title: "", body: "" };
41556
41943
  }
41557
- const content = await readFile68(path, "utf-8");
41944
+ const content = await readFile69(path, "utf-8");
41558
41945
  const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
41559
41946
  let title = "";
41560
41947
  if (fmMatch) {
@@ -41616,7 +42003,7 @@ async function loadInlineFiles(rows, assignmentDir) {
41616
42003
  continue;
41617
42004
  }
41618
42005
  try {
41619
- out.set(r.file_path, await readFile68(abs, "utf-8"));
42006
+ out.set(r.file_path, await readFile69(abs, "utf-8"));
41620
42007
  } catch {
41621
42008
  out.set(r.file_path, null);
41622
42009
  }
@@ -41635,7 +42022,7 @@ async function loadTranscriptSidecars(rows, assignmentDir) {
41635
42022
  const st = await stat15(sidecar);
41636
42023
  if (st.size > INLINE_TEXT_LIMIT_BYTES) continue;
41637
42024
  try {
41638
- out.set(r.id, await readFile68(sidecar, "utf-8"));
42025
+ out.set(r.id, await readFile69(sidecar, "utf-8"));
41639
42026
  } catch {
41640
42027
  }
41641
42028
  }
@@ -42531,7 +42918,7 @@ init_timestamp();
42531
42918
  init_assignment_resolver();
42532
42919
  init_assignment_todos();
42533
42920
  import { resolve as resolve100 } from "path";
42534
- import { readFile as readFile69 } from "fs/promises";
42921
+ import { readFile as readFile70 } from "fs/promises";
42535
42922
  async function requestCommand(target, text, options = {}) {
42536
42923
  if (!text || !text.trim()) {
42537
42924
  throw new Error("Request text cannot be empty.");
@@ -42562,7 +42949,7 @@ async function requestCommand(target, text, options = {}) {
42562
42949
  throw new Error(`assignment.md not found at ${assignmentMdPath2}`);
42563
42950
  }
42564
42951
  const source = options.from ?? process.env.SYNTAUR_ASSIGNMENT ?? "unknown";
42565
- let content = await readFile69(assignmentMdPath2, "utf-8");
42952
+ let content = await readFile70(assignmentMdPath2, "utf-8");
42566
42953
  content = appendTodosToAssignmentBody(content, [
42567
42954
  { description: `${text.trim()} (from: ${source})` }
42568
42955
  ]);
@@ -42577,7 +42964,7 @@ init_paths();
42577
42964
  init_config2();
42578
42965
  init_recompute();
42579
42966
  import { Command as Command10 } from "commander";
42580
- import { readFile as readFile70, readdir as readdir34 } from "fs/promises";
42967
+ import { readFile as readFile71, readdir as readdir35 } from "fs/promises";
42581
42968
  import { resolve as resolve101 } from "path";
42582
42969
  init_session_id();
42583
42970
  async function resolveAssignmentDir(opts) {
@@ -42604,7 +42991,7 @@ async function resolveAssignmentDir(opts) {
42604
42991
  var PLAN_PATTERN = /^plan(?:-v(\d+))?\.md$/;
42605
42992
  async function listPlanFiles(assignmentDir) {
42606
42993
  if (!await fileExists(assignmentDir)) return [];
42607
- const entries = await readdir34(assignmentDir, { withFileTypes: true });
42994
+ const entries = await readdir35(assignmentDir, { withFileTypes: true });
42608
42995
  const out = [];
42609
42996
  for (const e of entries) {
42610
42997
  if (!e.isFile()) continue;
@@ -42799,7 +43186,7 @@ async function runPlanCreate(options) {
42799
43186
  "plan.md already exists. Use --force to overwrite, or `syntaur plan version` to create the next version."
42800
43187
  );
42801
43188
  }
42802
- const assignmentMd = await readFile70(assignmentMdPath2, "utf-8");
43189
+ const assignmentMd = await readFile71(assignmentMdPath2, "utf-8");
42803
43190
  const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
42804
43191
  const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
42805
43192
  await writeFileForce(planPath, buildInitialPlanStub(slug));
@@ -42836,11 +43223,11 @@ async function runPlanVersion(options) {
42836
43223
  if (await fileExists(newPath) && !options.force) {
42837
43224
  throw new Error(`${next.fileName} already exists. Use --force to overwrite.`);
42838
43225
  }
42839
- const assignmentMd = await readFile70(assignmentMdPath2, "utf-8");
43226
+ const assignmentMd = await readFile71(assignmentMdPath2, "utf-8");
42840
43227
  const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
42841
43228
  const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
42842
43229
  const oldPlanPath = resolve101(assignmentDir, current.fileName);
42843
- const oldPlanContent = await readFile70(oldPlanPath, "utf-8");
43230
+ const oldPlanContent = await readFile71(oldPlanPath, "utf-8");
42844
43231
  const oldBody = oldPlanContent.replace(/^---[\s\S]*?\n---\n?/, "");
42845
43232
  const carriedTodos = extractUncheckedTodos(oldBody);
42846
43233
  const stub = buildNewPlanStub({
@@ -42911,7 +43298,7 @@ init_cwd();
42911
43298
  init_session_db();
42912
43299
  init_agent_sessions();
42913
43300
  import { Command as Command11 } from "commander";
42914
- import { readFile as readFile71, readdir as readdir35, stat as stat16 } from "fs/promises";
43301
+ import { readFile as readFile72, readdir as readdir36, stat as stat16 } from "fs/promises";
42915
43302
  import { resolve as resolve102 } from "path";
42916
43303
  init_engagement_db();
42917
43304
  init_parser();
@@ -42919,7 +43306,7 @@ async function readContext(cwd) {
42919
43306
  const path = resolve102(cwd, ".syntaur", "context.json");
42920
43307
  if (!await fileExists(path)) return null;
42921
43308
  try {
42922
- const raw2 = await readFile71(path, "utf-8");
43309
+ const raw2 = await readFile72(path, "utf-8");
42923
43310
  return JSON.parse(raw2);
42924
43311
  } catch {
42925
43312
  return null;
@@ -42928,7 +43315,7 @@ async function readContext(cwd) {
42928
43315
  async function findLatestSessionSummary(assignmentDir) {
42929
43316
  const sessionsRoot = resolve102(assignmentDir, "sessions");
42930
43317
  if (!await fileExists(sessionsRoot)) return null;
42931
- const entries = await readdir35(sessionsRoot, { withFileTypes: true });
43318
+ const entries = await readdir36(sessionsRoot, { withFileTypes: true });
42932
43319
  let best = null;
42933
43320
  for (const entry of entries) {
42934
43321
  if (!entry.isDirectory()) continue;
@@ -42944,7 +43331,7 @@ async function findLatestSessionSummary(assignmentDir) {
42944
43331
  async function findOpenHandoff(assignmentDir) {
42945
43332
  const handoffPath = resolve102(assignmentDir, "handoff.md");
42946
43333
  if (!await fileExists(handoffPath)) return null;
42947
- const content = await readFile71(handoffPath, "utf-8");
43334
+ const content = await readFile72(handoffPath, "utf-8");
42948
43335
  const body = content.replace(/^---[\s\S]*?\n---\n?/, "").trim();
42949
43336
  if (body.length === 0) return null;
42950
43337
  if (/^<!--[\s\S]*-->$/.test(body)) return null;
@@ -42954,7 +43341,7 @@ async function readAssignmentTitle(assignmentDir) {
42954
43341
  const path = resolve102(assignmentDir, "assignment.md");
42955
43342
  if (!await fileExists(path)) return null;
42956
43343
  try {
42957
- const content = await readFile71(path, "utf-8");
43344
+ const content = await readFile72(path, "utf-8");
42958
43345
  const [fm] = extractFrontmatter(content);
42959
43346
  return getField(fm, "title");
42960
43347
  } catch {
@@ -43171,13 +43558,13 @@ async function runSessionSave(options, cwd = process.cwd(), body) {
43171
43558
  const now = nowTimestamp();
43172
43559
  let created = now;
43173
43560
  if (await fileExists(summaryPath)) {
43174
- const existing = await readFile71(summaryPath, "utf-8");
43561
+ const existing = await readFile72(summaryPath, "utf-8");
43175
43562
  created = extractCreated(existing) ?? now;
43176
43563
  }
43177
43564
  let sectionBody2 = body;
43178
43565
  if (sectionBody2 === void 0) {
43179
43566
  if (options.fromFile) {
43180
- sectionBody2 = await readFile71(resolve102(cwd, options.fromFile), "utf-8");
43567
+ sectionBody2 = await readFile72(resolve102(cwd, options.fromFile), "utf-8");
43181
43568
  } else {
43182
43569
  sectionBody2 = await readStdin();
43183
43570
  }
@@ -43405,7 +43792,7 @@ sessionCommand.command("boundary").description(
43405
43792
  // src/commands/worktree.ts
43406
43793
  init_git_worktree();
43407
43794
  import { Command as Command12 } from "commander";
43408
- import { readFile as readFile72 } from "fs/promises";
43795
+ import { readFile as readFile73 } from "fs/promises";
43409
43796
  import { resolve as resolve103 } from "path";
43410
43797
  init_fs();
43411
43798
  init_paths();
@@ -43495,7 +43882,7 @@ async function runWorktreeRemove(options, cwd = process.cwd()) {
43495
43882
  if (!await fileExists(assignmentPath)) {
43496
43883
  throw new Error(`Assignment file not found: ${assignmentPath}`);
43497
43884
  }
43498
- const original = await readFile72(assignmentPath, "utf-8");
43885
+ const original = await readFile73(assignmentPath, "utf-8");
43499
43886
  const fm = parseAssignmentFrontmatter(original);
43500
43887
  const repository = options.repository ?? fm.workspace.repository ?? void 0;
43501
43888
  const worktreePath = fm.workspace.worktreePath ?? void 0;
@@ -43561,7 +43948,7 @@ async function runWorktreeGc(options, cwd = process.cwd()) {
43561
43948
  const owners = /* @__PURE__ */ new Map();
43562
43949
  for (const entry of walk.withAssignmentMd) {
43563
43950
  try {
43564
- const content = await readFile72(resolve103(entry.assignmentDir, "assignment.md"), "utf-8");
43951
+ const content = await readFile73(resolve103(entry.assignmentDir, "assignment.md"), "utf-8");
43565
43952
  const fm = parseAssignmentFrontmatter(content);
43566
43953
  const wp = fm.workspace?.worktreePath;
43567
43954
  if (!wp) continue;
@@ -43802,7 +44189,7 @@ worktreeCommand.command("gc").description(
43802
44189
  // src/commands/open.ts
43803
44190
  init_fs();
43804
44191
  import { Command as Command13 } from "commander";
43805
- import { readFile as readFile73 } from "fs/promises";
44192
+ import { readFile as readFile74 } from "fs/promises";
43806
44193
  import { resolve as resolve104 } from "path";
43807
44194
  init_frontmatter();
43808
44195
  init_config2();
@@ -43868,7 +44255,7 @@ async function runOpen(assignmentArg, options) {
43868
44255
  remediation: "check the assignment slug or --id"
43869
44256
  });
43870
44257
  }
43871
- const fm = parseAssignmentFrontmatter(await readFile73(assignmentPath, "utf-8"));
44258
+ const fm = parseAssignmentFrontmatter(await readFile74(assignmentPath, "utf-8"));
43872
44259
  const worktreePath = fm.workspace?.worktreePath;
43873
44260
  if (!worktreePath) {
43874
44261
  throw new SyntaurError("No worktree recorded for this assignment.", {
@@ -43934,12 +44321,12 @@ init_fs();
43934
44321
  init_slug();
43935
44322
  import { Command as Command14 } from "commander";
43936
44323
  import { resolve as resolve106 } from "path";
43937
- import { readFile as readFile75, readdir as readdir37, rm as rm15 } from "fs/promises";
44324
+ import { readFile as readFile76, readdir as readdir38, rm as rm15 } from "fs/promises";
43938
44325
 
43939
44326
  // src/utils/project-indexes.ts
43940
44327
  init_parser();
43941
44328
  init_fs();
43942
- import { readdir as readdir36, readFile as readFile74 } from "fs/promises";
44329
+ import { readdir as readdir37, readFile as readFile75 } from "fs/promises";
43943
44330
  import { resolve as resolve105 } from "path";
43944
44331
  function nowIso3() {
43945
44332
  return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
@@ -43949,7 +44336,7 @@ function readProjectSlug(projectDir) {
43949
44336
  }
43950
44337
  async function listSlugFiles(dir) {
43951
44338
  if (!await fileExists(dir)) return [];
43952
- const entries = await readdir36(dir, { withFileTypes: true });
44339
+ const entries = await readdir37(dir, { withFileTypes: true });
43953
44340
  return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && !e.name.startsWith("_")).map((e) => e.name).sort();
43954
44341
  }
43955
44342
  function escapeCell(value) {
@@ -43975,7 +44362,7 @@ async function rebuildResourcesIndex(projectDir) {
43975
44362
  lines.push("| Name | Category | Source | Related Assignments | Updated |");
43976
44363
  lines.push("|------|----------|--------|---------------------|---------|");
43977
44364
  for (const fileName of files) {
43978
- const content = await readFile74(resolve105(dir, fileName), "utf-8");
44365
+ const content = await readFile75(resolve105(dir, fileName), "utf-8");
43979
44366
  const parsed = parseResource(content);
43980
44367
  const slugBase = fileName.replace(/\.md$/, "");
43981
44368
  const name = parsed.name || slugBase;
@@ -44006,7 +44393,7 @@ async function rebuildMemoriesIndex(projectDir) {
44006
44393
  lines.push("| Name | Source | Scope | Source Assignment | Updated |");
44007
44394
  lines.push("|------|--------|-------|-------------------|---------|");
44008
44395
  for (const fileName of files) {
44009
- const content = await readFile74(resolve105(dir, fileName), "utf-8");
44396
+ const content = await readFile75(resolve105(dir, fileName), "utf-8");
44010
44397
  const parsed = parseMemory(content);
44011
44398
  const slugBase = fileName.replace(/\.md$/, "");
44012
44399
  const name = parsed.name || slugBase;
@@ -44113,7 +44500,7 @@ async function runResourceAdd(options) {
44113
44500
  async function listResourceSlugs(projectDir) {
44114
44501
  const dir = resolve106(projectDir, "resources");
44115
44502
  if (!await fileExists(dir)) return [];
44116
- const entries = await readdir37(dir, { withFileTypes: true });
44503
+ const entries = await readdir38(dir, { withFileTypes: true });
44117
44504
  return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "_index.md").map((e) => e.name.slice(0, -3)).sort();
44118
44505
  }
44119
44506
  async function runResourceList(project) {
@@ -44121,7 +44508,7 @@ async function runResourceList(project) {
44121
44508
  const slugs = await listResourceSlugs(projectDir);
44122
44509
  const out = [];
44123
44510
  for (const slug of slugs) {
44124
- const parsed = parseResource(await readFile75(resolve106(projectDir, "resources", `${slug}.md`), "utf-8"));
44511
+ const parsed = parseResource(await readFile76(resolve106(projectDir, "resources", `${slug}.md`), "utf-8"));
44125
44512
  out.push({ slug, name: parsed.name, category: parsed.category, source: parsed.source, updated: parsed.updated });
44126
44513
  }
44127
44514
  return out;
@@ -44130,7 +44517,7 @@ async function runResourceShow(project, slug) {
44130
44517
  const projectDir = await resolveProjectDir(project);
44131
44518
  const filePath = resolve106(projectDir, "resources", `${slug}.md`);
44132
44519
  if (!await fileExists(filePath)) throw new Error(`Resource "${slug}" not found in project "${project}".`);
44133
- const parsed = parseResource(await readFile75(filePath, "utf-8"));
44520
+ const parsed = parseResource(await readFile76(filePath, "utf-8"));
44134
44521
  return {
44135
44522
  slug,
44136
44523
  name: parsed.name,
@@ -44151,7 +44538,7 @@ async function runResourceUpdate(slug, options) {
44151
44538
  if (options.name === void 0 && options.source === void 0 && options.category === void 0 && options.relatedAssignments === void 0) {
44152
44539
  throw new Error("Provide at least one of --name, --source, --category, --related-assignments.");
44153
44540
  }
44154
- const original = await readFile75(filePath, "utf-8");
44541
+ const original = await readFile76(filePath, "utf-8");
44155
44542
  const content = editResourceFrontmatter(original, {
44156
44543
  name: options.name,
44157
44544
  category: options.category,
@@ -44248,7 +44635,7 @@ init_fs();
44248
44635
  init_slug();
44249
44636
  import { Command as Command15 } from "commander";
44250
44637
  import { resolve as resolve107 } from "path";
44251
- import { readFile as readFile76, readdir as readdir38, rm as rm16 } from "fs/promises";
44638
+ import { readFile as readFile77, readdir as readdir39, rm as rm16 } from "fs/promises";
44252
44639
  init_parser();
44253
44640
  function nowIso5() {
44254
44641
  return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
@@ -44346,7 +44733,7 @@ async function runMemoryAdd(options) {
44346
44733
  async function listMemorySlugs(projectDir) {
44347
44734
  const dir = resolve107(projectDir, "memories");
44348
44735
  if (!await fileExists(dir)) return [];
44349
- const entries = await readdir38(dir, { withFileTypes: true });
44736
+ const entries = await readdir39(dir, { withFileTypes: true });
44350
44737
  return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "_index.md").map((e) => e.name.slice(0, -3)).sort();
44351
44738
  }
44352
44739
  async function runMemoryList(project) {
@@ -44354,7 +44741,7 @@ async function runMemoryList(project) {
44354
44741
  const slugs = await listMemorySlugs(projectDir);
44355
44742
  const out = [];
44356
44743
  for (const slug of slugs) {
44357
- const parsed = parseMemory(await readFile76(resolve107(projectDir, "memories", `${slug}.md`), "utf-8"));
44744
+ const parsed = parseMemory(await readFile77(resolve107(projectDir, "memories", `${slug}.md`), "utf-8"));
44358
44745
  out.push({ slug, name: parsed.name, scope: parsed.scope, source: parsed.source, updated: parsed.updated });
44359
44746
  }
44360
44747
  return out;
@@ -44363,7 +44750,7 @@ async function runMemoryShow(project, slug) {
44363
44750
  const projectDir = await resolveProjectDir2(project);
44364
44751
  const filePath = resolve107(projectDir, "memories", `${slug}.md`);
44365
44752
  if (!await fileExists(filePath)) throw new Error(`Memory "${slug}" not found in project "${project}".`);
44366
- const parsed = parseMemory(await readFile76(filePath, "utf-8"));
44753
+ const parsed = parseMemory(await readFile77(filePath, "utf-8"));
44367
44754
  return {
44368
44755
  slug,
44369
44756
  name: parsed.name,
@@ -44387,7 +44774,7 @@ async function runMemoryUpdate(slug, options) {
44387
44774
  "Provide at least one of --name, --source, --scope, --source-assignment, --related-assignments."
44388
44775
  );
44389
44776
  }
44390
- const original = await readFile76(filePath, "utf-8");
44777
+ const original = await readFile77(filePath, "utf-8");
44391
44778
  const content = editMemoryFrontmatter(original, {
44392
44779
  name: options.name,
44393
44780
  source: options.source,
@@ -44490,7 +44877,7 @@ init_derive();
44490
44877
  init_recompute();
44491
44878
  init_query();
44492
44879
  import { Command as Command16 } from "commander";
44493
- import { readFile as readFile77 } from "fs/promises";
44880
+ import { readFile as readFile78 } from "fs/promises";
44494
44881
  import { dirname as dirname29, resolve as resolve108 } from "path";
44495
44882
  var AGE_PATTERN = /^(\d+)([dhwm])$/i;
44496
44883
  function parseAgeToCutoff(age) {
@@ -44524,7 +44911,7 @@ async function loadTags(item) {
44524
44911
  const path = assignmentMdPath(item);
44525
44912
  if (!await fileExists(path)) return [];
44526
44913
  try {
44527
- const content = await readFile77(path, "utf-8");
44914
+ const content = await readFile78(path, "utf-8");
44528
44915
  return parseAssignmentFrontmatter(content).tags;
44529
44916
  } catch {
44530
44917
  return [];
@@ -44588,7 +44975,7 @@ async function loadQueryItem(item, terminalStatuses3, now, declarations) {
44588
44975
  const path = assignmentMdPath(item);
44589
44976
  if (!await fileExists(path)) return null;
44590
44977
  try {
44591
- const content = await readFile77(path, "utf-8");
44978
+ const content = await readFile78(path, "utf-8");
44592
44979
  const fm = parseAssignmentFrontmatter(content);
44593
44980
  const body = content.replace(/^---\n[\s\S]*?\n---/, "");
44594
44981
  const assignmentDir = dirname29(path);
@@ -45367,7 +45754,7 @@ init_fs();
45367
45754
  init_timestamp();
45368
45755
  init_frontmatter();
45369
45756
  import { Command as Command21 } from "commander";
45370
- import { readFile as readFile78 } from "fs/promises";
45757
+ import { readFile as readFile79 } from "fs/promises";
45371
45758
  import { resolve as resolve109 } from "path";
45372
45759
  function renameAssignmentStatusRefs(content, id, newId, now) {
45373
45760
  const fm = parseAssignmentFrontmatter(content);
@@ -45394,7 +45781,7 @@ function configPath() {
45394
45781
  async function readStatusBlock() {
45395
45782
  const p = configPath();
45396
45783
  if (!await fileExists(p)) return null;
45397
- const content = await readFile78(p, "utf-8");
45784
+ const content = await readFile79(p, "utf-8");
45398
45785
  return parseStatusConfig(content);
45399
45786
  }
45400
45787
  async function requireStatusBlock() {
@@ -45678,7 +46065,7 @@ async function runStatusRename(id, opts) {
45678
46065
  printBlockDiff(before, after);
45679
46066
  const now2 = nowTimestamp();
45680
46067
  for (const a of affected) {
45681
- const original = await readFile78(a.path, "utf-8");
46068
+ const original = await readFile79(a.path, "utf-8");
45682
46069
  const rewritten = renameAssignmentStatusRefs(original, id, newId, now2);
45683
46070
  console.log(`
45684
46071
  --- ${a.display}/assignment.md`);
@@ -45689,9 +46076,9 @@ async function runStatusRename(id, opts) {
45689
46076
  }
45690
46077
  const cfgPath = configPath();
45691
46078
  const buffers = /* @__PURE__ */ new Map();
45692
- buffers.set(cfgPath, await fileExists(cfgPath) ? await readFile78(cfgPath, "utf-8") : "");
46079
+ buffers.set(cfgPath, await fileExists(cfgPath) ? await readFile79(cfgPath, "utf-8") : "");
45693
46080
  for (const a of affected) {
45694
- buffers.set(a.path, await readFile78(a.path, "utf-8"));
46081
+ buffers.set(a.path, await readFile79(a.path, "utf-8"));
45695
46082
  }
45696
46083
  const now = nowTimestamp();
45697
46084
  try {
@@ -45893,7 +46280,7 @@ init_config2();
45893
46280
  init_timestamp();
45894
46281
  init_frontmatter();
45895
46282
  import { Command as Command22 } from "commander";
45896
- import { readFile as readFile79 } from "fs/promises";
46283
+ import { readFile as readFile80 } from "fs/promises";
45897
46284
  import { resolve as resolve110 } from "path";
45898
46285
  init_session_id();
45899
46286
  async function resolveAssignmentPath3(opts) {
@@ -45949,7 +46336,7 @@ async function runWorkspaceSet(options, cwd = process.cwd()) {
45949
46336
  ${pre.errors.map((e) => ` - ${e}`).join("\n")}`
45950
46337
  );
45951
46338
  }
45952
- const original = await readFile79(path, "utf-8");
46339
+ const original = await readFile80(path, "utf-8");
45953
46340
  let next = updateAssignmentWorkspace(original, partial);
45954
46341
  next = updateAssignmentFile(next, { updated: nowTimestamp() });
45955
46342
  await writeFileForce(path, next);
@@ -45986,7 +46373,7 @@ init_config2();
45986
46373
  init_timestamp();
45987
46374
  init_templates();
45988
46375
  import { Command as Command23 } from "commander";
45989
- import { readFile as readFile80 } from "fs/promises";
46376
+ import { readFile as readFile81 } from "fs/promises";
45990
46377
  import { resolve as resolve111 } from "path";
45991
46378
  init_session_id();
45992
46379
  async function resolveAssignmentDir2(opts) {
@@ -46070,7 +46457,7 @@ async function runProgressLog(text, options, cwd = process.cwd()) {
46070
46457
  }
46071
46458
  const path = resolve111(dir, "progress.md");
46072
46459
  const now = nowTimestamp();
46073
- const content = await fileExists(path) ? await readFile80(path, "utf-8") : renderProgress({ assignment: slug, timestamp: now });
46460
+ const content = await fileExists(path) ? await readFile81(path, "utf-8") : renderProgress({ assignment: slug, timestamp: now });
46074
46461
  const next = appendProgressEntry(content, text, now);
46075
46462
  await writeFileForce(path, next);
46076
46463
  return path;
@@ -46090,10 +46477,10 @@ progressCommand.command("log").description("Append a timestamped entry to the as
46090
46477
 
46091
46478
  // src/cli-default-command.ts
46092
46479
  init_config2();
46093
- import { readdir as readdir39 } from "fs/promises";
46480
+ import { readdir as readdir40 } from "fs/promises";
46094
46481
  async function hasAnyProjectContent(projectsDir2) {
46095
46482
  try {
46096
- const entries = await readdir39(projectsDir2, { withFileTypes: true });
46483
+ const entries = await readdir40(projectsDir2, { withFileTypes: true });
46097
46484
  return entries.some((entry) => entry.isDirectory());
46098
46485
  } catch {
46099
46486
  return false;