syntaur 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dashboard/dist/assets/{_basePickBy-Bcut0btZ.js → _basePickBy-ij-Ukp6s.js} +1 -1
  2. package/dashboard/dist/assets/{_baseUniq-AQSP2JEk.js → _baseUniq-CZKk9gZR.js} +1 -1
  3. package/dashboard/dist/assets/{arc-BLTpY9lc.js → arc-C30UbJZB.js} +1 -1
  4. package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-CJtwMY_X.js → architectureDiagram-2XIMDMQ5-BDVieGIr.js} +1 -1
  5. package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-Don-O7X7.js → blockDiagram-WCTKOSBZ-DZYY4t9w.js} +1 -1
  6. package/dashboard/dist/assets/{c4Diagram-IC4MRINW-C_M3yTTB.js → c4Diagram-IC4MRINW-B019MXol.js} +1 -1
  7. package/dashboard/dist/assets/channel-DH4gshIt.js +1 -0
  8. package/dashboard/dist/assets/{chunk-4BX2VUAB-CGss0jXe.js → chunk-4BX2VUAB-DrkTwY15.js} +1 -1
  9. package/dashboard/dist/assets/{chunk-55IACEB6-BatoPJga.js → chunk-55IACEB6-mFTAE8DD.js} +1 -1
  10. package/dashboard/dist/assets/{chunk-FMBD7UC4-DxH4wO82.js → chunk-FMBD7UC4-VgDZaNoS.js} +1 -1
  11. package/dashboard/dist/assets/{chunk-JSJVCQXG-BL3izAFQ.js → chunk-JSJVCQXG-C_KXaq-c.js} +1 -1
  12. package/dashboard/dist/assets/{chunk-KX2RTZJC-GnqXwnge.js → chunk-KX2RTZJC-DI-P_pPL.js} +1 -1
  13. package/dashboard/dist/assets/{chunk-NQ4KR5QH-gvCn4QMb.js → chunk-NQ4KR5QH-TgYAsxTk.js} +1 -1
  14. package/dashboard/dist/assets/{chunk-QZHKN3VN-CYGWogyi.js → chunk-QZHKN3VN-Drfv_VpM.js} +1 -1
  15. package/dashboard/dist/assets/{chunk-WL4C6EOR-D9mVTQ1F.js → chunk-WL4C6EOR-CpLwvo_U.js} +1 -1
  16. package/dashboard/dist/assets/classDiagram-VBA2DB6C-emsfh8H4.js +1 -0
  17. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-emsfh8H4.js +1 -0
  18. package/dashboard/dist/assets/clone-gdeRwgBN.js +1 -0
  19. package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-CUWQCKt4.js → cose-bilkent-S5V4N54A-CkKtF37m.js} +1 -1
  20. package/dashboard/dist/assets/{dagre-KLK3FWXG-CH3ijEvV.js → dagre-KLK3FWXG-BBlY_FL3.js} +1 -1
  21. package/dashboard/dist/assets/{diagram-E7M64L7V-sq83lpV1.js → diagram-E7M64L7V-DLsFDLHm.js} +1 -1
  22. package/dashboard/dist/assets/{diagram-IFDJBPK2-BzQG_rtq.js → diagram-IFDJBPK2--sb7diMG.js} +1 -1
  23. package/dashboard/dist/assets/{diagram-P4PSJMXO-Dg0eZn0q.js → diagram-P4PSJMXO-D2LuEWVt.js} +1 -1
  24. package/dashboard/dist/assets/{erDiagram-INFDFZHY-4b9eQ0uj.js → erDiagram-INFDFZHY-C1BEeili.js} +1 -1
  25. package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-C9fzKcsZ.js → flowDiagram-PKNHOUZH-BpbapQbU.js} +1 -1
  26. package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-Bzt6i9SH.js → ganttDiagram-A5KZAMGK-Io60qUuG.js} +1 -1
  27. package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-D0wFOagh.js → gitGraphDiagram-K3NZZRJ6-oemlGgRh.js} +1 -1
  28. package/dashboard/dist/assets/{graph-EEIGvqDh.js → graph-BZb-lGfH.js} +1 -1
  29. package/dashboard/dist/assets/index-BSVCsfvM.css +1 -0
  30. package/dashboard/dist/assets/index-CXWVuGs-.js +481 -0
  31. package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-DLYMsj1D.js → infoDiagram-LFFYTUFH-Ca4mwnZF.js} +1 -1
  32. package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-DVebKkzl.js → ishikawaDiagram-PHBUUO56-9zuQ8y8W.js} +1 -1
  33. package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-BsmgOWVw.js → journeyDiagram-4ABVD52K-OdeeOdMx.js} +1 -1
  34. package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-BTnHf0ey.js → kanban-definition-K7BYSVSG-Cie4JtFn.js} +1 -1
  35. package/dashboard/dist/assets/{layout-BbM7HRvv.js → layout-Bmx2mvFv.js} +1 -1
  36. package/dashboard/dist/assets/{linear-C37bJKPO.js → linear-CW6K_-MX.js} +1 -1
  37. package/dashboard/dist/assets/{mermaid.core-MZ_JgnRL.js → mermaid.core-DmfO6BgK.js} +4 -4
  38. package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-CgHS4hFo.js → mindmap-definition-YRQLILUH-L6b3vG79.js} +1 -1
  39. package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-CmAgopJe.js → pieDiagram-SKSYHLDU-CkHTCIWg.js} +1 -1
  40. package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-BvzYUPR6.js → quadrantDiagram-337W2JSQ-B9MqhhIC.js} +1 -1
  41. package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-Bs52VP7k.js → requirementDiagram-Z7DCOOCP-CyHAfXCK.js} +1 -1
  42. package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-aXvGPR1o.js → sankeyDiagram-WA2Y5GQK-DHNzGGyE.js} +1 -1
  43. package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-CzgcfU6K.js → sequenceDiagram-2WXFIKYE-BVvcJkrx.js} +1 -1
  44. package/dashboard/dist/assets/{stateDiagram-RAJIS63D-BXBJf9Hq.js → stateDiagram-RAJIS63D-CZ2cknh7.js} +1 -1
  45. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-C4CPervD.js +1 -0
  46. package/dashboard/dist/assets/{timeline-definition-YZTLITO2-BsXp26Ai.js → timeline-definition-YZTLITO2-BXUtlVyd.js} +1 -1
  47. package/dashboard/dist/assets/{treemap-KZPCXAKY-C3WbDii1.js → treemap-KZPCXAKY-Dgi-hMKM.js} +1 -1
  48. package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-B28LMHWd.js → vennDiagram-LZ73GAT5-C9zGrrUQ.js} +1 -1
  49. package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-C3Xwz8mS.js → xychartDiagram-JWTSCODW-Dq71BUtc.js} +1 -1
  50. package/dashboard/dist/index.html +2 -2
  51. package/dashboard/dist/syntaur-logo.svg +14 -0
  52. package/dist/dashboard/server.js +335 -8
  53. package/dist/dashboard/server.js.map +1 -1
  54. package/dist/index.js +1256 -131
  55. package/dist/index.js.map +1 -1
  56. package/package.json +1 -1
  57. package/dashboard/dist/assets/channel-BfXmPwE5.js +0 -1
  58. package/dashboard/dist/assets/classDiagram-VBA2DB6C-D7_G1qy0.js +0 -1
  59. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-D7_G1qy0.js +0 -1
  60. package/dashboard/dist/assets/clone-BKG-N796.js +0 -1
  61. package/dashboard/dist/assets/index-Bu6ma6my.css +0 -1
  62. package/dashboard/dist/assets/index-C7f0ySJE.js +0 -481
  63. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-QqOtsuOs.js +0 -1
@@ -625,6 +625,53 @@ var init_fs_migration = __esm({
625
625
  // src/utils/config.ts
626
626
  import { readFile as readFile3 } from "fs/promises";
627
627
  import { resolve as resolve4, isAbsolute } from "path";
628
+ function parseAgentCommand(value, agentId) {
629
+ if (typeof value !== "string" || value.trim() === "") {
630
+ throw new AgentConfigError(
631
+ `agent${agentId ? ` "${agentId}"` : ""} has empty command`
632
+ );
633
+ }
634
+ const expanded = expandHome(value.trim());
635
+ if (isAbsolute(expanded)) {
636
+ return resolve4(expanded);
637
+ }
638
+ if (expanded.includes("/")) {
639
+ throw new AgentConfigError(
640
+ `agent${agentId ? ` "${agentId}"` : ""} command "${value}" is a relative path \u2014 use an absolute path or a bare binary name`
641
+ );
642
+ }
643
+ return expanded;
644
+ }
645
+ function validateAgentList(agents) {
646
+ const seen = /* @__PURE__ */ new Set();
647
+ let defaults = 0;
648
+ for (const agent of agents) {
649
+ if (!AGENT_ID_PATTERN.test(agent.id)) {
650
+ throw new AgentConfigError(
651
+ `agent id "${agent.id}" is invalid \u2014 must match /^[a-z0-9][a-z0-9_-]*$/`
652
+ );
653
+ }
654
+ if (seen.has(agent.id)) {
655
+ throw new AgentConfigError(`duplicate agent id "${agent.id}"`);
656
+ }
657
+ seen.add(agent.id);
658
+ if (!agent.label || agent.label.trim() === "") {
659
+ throw new AgentConfigError(`agent "${agent.id}" has empty label`);
660
+ }
661
+ parseAgentCommand(agent.command, agent.id);
662
+ if (agent.promptArgPosition !== void 0 && !PROMPT_ARG_POSITIONS.includes(agent.promptArgPosition)) {
663
+ throw new AgentConfigError(
664
+ `agent "${agent.id}" has invalid promptArgPosition "${agent.promptArgPosition}" \u2014 expected first|last|none`
665
+ );
666
+ }
667
+ if (agent.default) defaults++;
668
+ }
669
+ if (defaults > 1) {
670
+ throw new AgentConfigError(
671
+ `more than one agent is marked default: true (only one is allowed)`
672
+ );
673
+ }
674
+ }
628
675
  function cloneDefaultConfig() {
629
676
  return {
630
677
  ...DEFAULT_CONFIG,
@@ -641,9 +688,14 @@ function cloneDefaultConfig() {
641
688
  definitions: DEFAULT_CONFIG.types.definitions.map((d) => ({ ...d })),
642
689
  default: DEFAULT_CONFIG.types.default
643
690
  } : null,
691
+ agents: DEFAULT_CONFIG.agents ? DEFAULT_CONFIG.agents.map((a) => ({
692
+ ...a,
693
+ ...a.args ? { args: [...a.args] } : {}
694
+ })) : null,
644
695
  playbooks: {
645
696
  disabled: [...DEFAULT_CONFIG.playbooks.disabled]
646
- }
697
+ },
698
+ theme: DEFAULT_CONFIG.theme ? { ...DEFAULT_CONFIG.theme } : null
647
699
  };
648
700
  }
649
701
  function parseFrontmatter(content) {
@@ -874,6 +926,71 @@ ${normalizedFm}
874
926
  ---${afterFrontmatter}`;
875
927
  await writeFileForce(configPath, newContent);
876
928
  }
929
+ function parseThemeConfig(content) {
930
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
931
+ if (!match) return null;
932
+ const fmBlock = match[1];
933
+ const blockStart = fmBlock.match(/^theme:\s*$/m);
934
+ if (!blockStart) return null;
935
+ const startIdx = fmBlock.indexOf(blockStart[0]) + blockStart[0].length;
936
+ const remaining = fmBlock.slice(startIdx).split("\n");
937
+ let preset = null;
938
+ for (const line of remaining) {
939
+ const trimmed = line.trimStart();
940
+ const indent = line.length - trimmed.length;
941
+ if (indent === 0 && trimmed.length > 0) break;
942
+ if (trimmed === "") continue;
943
+ if (indent === 2 && trimmed.startsWith("preset:")) {
944
+ const value = trimmed.slice("preset:".length).trim().replace(/^["']|["']$/g, "");
945
+ if (value.length > 0) preset = value;
946
+ }
947
+ }
948
+ if (!preset) return null;
949
+ return { preset };
950
+ }
951
+ function serializeThemeConfig(theme) {
952
+ return ["theme:", ` preset: ${theme.preset}`].join("\n");
953
+ }
954
+ async function writeThemeConfig(theme) {
955
+ const configPath = resolve4(syntaurRoot(), "config.md");
956
+ const themeBlock = serializeThemeConfig(theme);
957
+ const existing = await fileExists(configPath) ? await readFile3(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
958
+ const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
959
+ if (!fmMatch) {
960
+ const content = `---
961
+ version: "2.0"
962
+ defaultProjectDir: ${defaultProjectDir()}
963
+ ${themeBlock}
964
+ ---
965
+ ${existing}`;
966
+ await writeFileForce(configPath, content);
967
+ return;
968
+ }
969
+ const fmBlock = fmMatch[2];
970
+ const afterFrontmatter = existing.slice(fmMatch[0].length);
971
+ const cleanedFm = stripTopLevelBlock(fmBlock, "theme");
972
+ const newFm = `${cleanedFm}
973
+ ${themeBlock}`.replace(/^\n+/, "");
974
+ const normalizedFm = newFm.replace(/\n+$/, "");
975
+ const newContent = `---
976
+ ${normalizedFm}
977
+ ---${afterFrontmatter}`;
978
+ await writeFileForce(configPath, newContent);
979
+ }
980
+ async function deleteThemeConfig() {
981
+ const configPath = resolve4(syntaurRoot(), "config.md");
982
+ if (!await fileExists(configPath)) return;
983
+ const existing = await readFile3(configPath, "utf-8");
984
+ const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
985
+ if (!fmMatch) return;
986
+ const fmBlock = fmMatch[2];
987
+ const afterFrontmatter = existing.slice(fmMatch[0].length);
988
+ const cleanedFm = stripTopLevelBlock(fmBlock, "theme");
989
+ const newContent = `---
990
+ ${cleanedFm}
991
+ ---${afterFrontmatter}`;
992
+ await writeFileForce(configPath, newContent);
993
+ }
877
994
  function stripTopLevelBlock(fmBlock, key) {
878
995
  const blockStart = fmBlock.match(new RegExp(`^${key}:\\s*$`, "m"));
879
996
  if (!blockStart) {
@@ -910,6 +1027,164 @@ function parseOptionalAbsolutePath(value, fieldName) {
910
1027
  }
911
1028
  return resolve4(expanded);
912
1029
  }
1030
+ function parseAgentsConfig(content) {
1031
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
1032
+ if (!match) return null;
1033
+ const fmBlock = match[1];
1034
+ const agentsStart = fmBlock.match(/^agents:\s*$/m);
1035
+ if (!agentsStart) return null;
1036
+ const startIdx = fmBlock.indexOf(agentsStart[0]) + agentsStart[0].length;
1037
+ const remaining = fmBlock.slice(startIdx);
1038
+ const lines = remaining.split("\n");
1039
+ const agents = [];
1040
+ let current = null;
1041
+ let argsCapture = null;
1042
+ let argsBaseIndent = 0;
1043
+ function flushCurrent() {
1044
+ if (!current) return;
1045
+ if (!current.id || !current.command || !current.label) {
1046
+ current = null;
1047
+ return;
1048
+ }
1049
+ agents.push({
1050
+ id: current.id,
1051
+ label: current.label,
1052
+ command: current.command,
1053
+ ...current.args && current.args.length > 0 ? { args: current.args } : {},
1054
+ ...current.promptArgPosition ? { promptArgPosition: current.promptArgPosition } : {},
1055
+ ...current.default ? { default: true } : {},
1056
+ ...current.resolveFromShellAliases ? { resolveFromShellAliases: true } : {}
1057
+ });
1058
+ current = null;
1059
+ argsCapture = null;
1060
+ }
1061
+ for (let i = 0; i < lines.length; i++) {
1062
+ const line = lines[i];
1063
+ const trimmed = line.trimStart();
1064
+ const indent = line.length - trimmed.length;
1065
+ if (indent === 0 && trimmed !== "" && !trimmed.startsWith("#")) {
1066
+ break;
1067
+ }
1068
+ if (argsCapture) {
1069
+ if (indent > argsBaseIndent && trimmed.startsWith("- ")) {
1070
+ argsCapture.push(decodeYamlScalar(trimmed.slice(2).trim()));
1071
+ continue;
1072
+ } else {
1073
+ argsCapture = null;
1074
+ if (current) current.args = current.args ?? [];
1075
+ }
1076
+ }
1077
+ if (indent === 2 && trimmed.startsWith("- ")) {
1078
+ flushCurrent();
1079
+ current = {};
1080
+ const rest = trimmed.slice(2).trim();
1081
+ const colonIdx = rest.indexOf(":");
1082
+ if (colonIdx > 0) {
1083
+ const k = rest.slice(0, colonIdx).trim();
1084
+ const v = rest.slice(colonIdx + 1).trim();
1085
+ assignAgentField(current, k, v);
1086
+ }
1087
+ continue;
1088
+ }
1089
+ if (indent >= 4 && current) {
1090
+ const colonIdx = trimmed.indexOf(":");
1091
+ if (colonIdx <= 0) continue;
1092
+ const k = trimmed.slice(0, colonIdx).trim();
1093
+ const v = trimmed.slice(colonIdx + 1).trim();
1094
+ if (k === "args" && v === "") {
1095
+ argsCapture = [];
1096
+ argsBaseIndent = indent;
1097
+ current.args = argsCapture;
1098
+ continue;
1099
+ }
1100
+ assignAgentField(current, k, v);
1101
+ }
1102
+ }
1103
+ flushCurrent();
1104
+ if (agents.length === 0) return [];
1105
+ return agents;
1106
+ }
1107
+ function normalizeAgentsFromConfig(agents) {
1108
+ if (agents === null) return null;
1109
+ try {
1110
+ const normalized = agents.map((agent) => ({
1111
+ ...agent,
1112
+ command: parseAgentCommand(agent.command, agent.id)
1113
+ }));
1114
+ validateAgentList(normalized);
1115
+ return normalized;
1116
+ } catch (err) {
1117
+ const msg = err instanceof Error ? err.message : String(err);
1118
+ console.warn(
1119
+ `Warning: ~/.syntaur/config.md agents block is invalid (${msg}) \u2014 using built-in defaults`
1120
+ );
1121
+ return null;
1122
+ }
1123
+ }
1124
+ function decodeYamlScalar(value) {
1125
+ const trimmed = value.trim();
1126
+ if (trimmed.length >= 2 && trimmed.startsWith('"') && trimmed.endsWith('"')) {
1127
+ const body = trimmed.slice(1, -1);
1128
+ let out = "";
1129
+ for (let i = 0; i < body.length; i++) {
1130
+ const ch = body[i];
1131
+ if (ch === "\\" && i + 1 < body.length) {
1132
+ const next = body[i + 1];
1133
+ switch (next) {
1134
+ case "\\":
1135
+ out += "\\";
1136
+ break;
1137
+ case '"':
1138
+ out += '"';
1139
+ break;
1140
+ case "n":
1141
+ out += "\n";
1142
+ break;
1143
+ case "t":
1144
+ out += " ";
1145
+ break;
1146
+ case "r":
1147
+ out += "\r";
1148
+ break;
1149
+ default:
1150
+ out += next;
1151
+ break;
1152
+ }
1153
+ i++;
1154
+ continue;
1155
+ }
1156
+ out += ch;
1157
+ }
1158
+ return out;
1159
+ }
1160
+ if (trimmed.length >= 2 && trimmed.startsWith("'") && trimmed.endsWith("'")) {
1161
+ return trimmed.slice(1, -1).replace(/''/g, "'");
1162
+ }
1163
+ return trimmed;
1164
+ }
1165
+ function assignAgentField(target, key, rawValue) {
1166
+ const value = decodeYamlScalar(rawValue);
1167
+ switch (key) {
1168
+ case "id":
1169
+ target.id = value;
1170
+ break;
1171
+ case "label":
1172
+ target.label = value;
1173
+ break;
1174
+ case "command":
1175
+ target.command = value;
1176
+ break;
1177
+ case "promptArgPosition":
1178
+ target.promptArgPosition = value;
1179
+ break;
1180
+ case "default":
1181
+ target.default = value === "true";
1182
+ break;
1183
+ case "resolveFromShellAliases":
1184
+ target.resolveFromShellAliases = value === "true";
1185
+ break;
1186
+ }
1187
+ }
913
1188
  async function writeStatusConfig(statuses) {
914
1189
  const configPath = resolve4(syntaurRoot(), "config.md");
915
1190
  const statusBlock = serializeStatusConfig(statuses);
@@ -1043,7 +1318,10 @@ async function readConfig() {
1043
1318
  },
1044
1319
  agentDefaults: {
1045
1320
  trustLevel: fm["agentDefaults.trustLevel"] || DEFAULT_CONFIG.agentDefaults.trustLevel,
1046
- autoApprove: fm["agentDefaults.autoApprove"] === "true" || DEFAULT_CONFIG.agentDefaults.autoApprove
1321
+ autoApprove: fm["agentDefaults.autoApprove"] === "true" || DEFAULT_CONFIG.agentDefaults.autoApprove,
1322
+ autoCreateWorktree: AUTO_CREATE_WORKTREE_VALUES.includes(
1323
+ fm["agentDefaults.autoCreateWorktree"]
1324
+ ) ? fm["agentDefaults.autoCreateWorktree"] : DEFAULT_CONFIG.agentDefaults.autoCreateWorktree
1047
1325
  },
1048
1326
  integrations: {
1049
1327
  claudePluginDir: parseOptionalAbsolutePath(
@@ -1067,10 +1345,12 @@ async function readConfig() {
1067
1345
  } : null,
1068
1346
  statuses: parseStatusConfig(content),
1069
1347
  types: null,
1070
- playbooks: parsePlaybooksConfig(fmBlock)
1348
+ agents: normalizeAgentsFromConfig(parseAgentsConfig(content)),
1349
+ playbooks: parsePlaybooksConfig(fmBlock),
1350
+ theme: parseThemeConfig(content)
1071
1351
  };
1072
1352
  }
1073
- var DEFAULT_CONFIG, migratedConfigPaths;
1353
+ var DEFAULT_CONFIG, AGENT_ID_PATTERN, PROMPT_ARG_POSITIONS, AUTO_CREATE_WORKTREE_VALUES, AgentConfigError, migratedConfigPaths;
1074
1354
  var init_config2 = __esm({
1075
1355
  "src/utils/config.ts"() {
1076
1356
  "use strict";
@@ -1086,7 +1366,8 @@ var init_config2 = __esm({
1086
1366
  },
1087
1367
  agentDefaults: {
1088
1368
  trustLevel: "medium",
1089
- autoApprove: false
1369
+ autoApprove: false,
1370
+ autoCreateWorktree: "ask"
1090
1371
  },
1091
1372
  integrations: {
1092
1373
  claudePluginDir: null,
@@ -1096,9 +1377,16 @@ var init_config2 = __esm({
1096
1377
  backup: null,
1097
1378
  statuses: null,
1098
1379
  types: null,
1380
+ agents: null,
1099
1381
  playbooks: {
1100
1382
  disabled: []
1101
- }
1383
+ },
1384
+ theme: null
1385
+ };
1386
+ AGENT_ID_PATTERN = /^[a-z0-9][a-z0-9_-]*$/;
1387
+ PROMPT_ARG_POSITIONS = ["first", "last", "none"];
1388
+ AUTO_CREATE_WORKTREE_VALUES = ["skip", "ask", "always"];
1389
+ AgentConfigError = class extends Error {
1102
1390
  };
1103
1391
  migratedConfigPaths = /* @__PURE__ */ new Set();
1104
1392
  }
@@ -8734,6 +9022,43 @@ function createDashboardServer(options) {
8734
9022
  res.status(500).json({ error: "Failed to reset status config" });
8735
9023
  }
8736
9024
  });
9025
+ const THEME_PRESET_SLUGS = ["default", "ocean", "forest", "sunset"];
9026
+ const DEFAULT_THEME_PRESET = "default";
9027
+ app.get("/api/config/theme", async (_req, res) => {
9028
+ try {
9029
+ const config = await readConfig();
9030
+ const preset = config.theme?.preset ?? DEFAULT_THEME_PRESET;
9031
+ res.json({ preset, custom: config.theme !== null });
9032
+ } catch (error) {
9033
+ console.error("Error getting theme config:", error);
9034
+ res.status(500).json({ error: "Failed to get theme config" });
9035
+ }
9036
+ });
9037
+ app.post("/api/config/theme", async (req, res) => {
9038
+ try {
9039
+ const { preset } = req.body ?? {};
9040
+ if (typeof preset !== "string" || !THEME_PRESET_SLUGS.includes(preset)) {
9041
+ res.status(400).json({
9042
+ error: `preset must be one of: ${THEME_PRESET_SLUGS.join(", ")}`
9043
+ });
9044
+ return;
9045
+ }
9046
+ await writeThemeConfig({ preset });
9047
+ res.json({ preset, custom: true });
9048
+ } catch (error) {
9049
+ console.error("Error saving theme config:", error);
9050
+ res.status(500).json({ error: "Failed to save theme config" });
9051
+ }
9052
+ });
9053
+ app.delete("/api/config/theme", async (_req, res) => {
9054
+ try {
9055
+ await deleteThemeConfig();
9056
+ res.json({ preset: DEFAULT_THEME_PRESET, custom: false });
9057
+ } catch (error) {
9058
+ console.error("Error resetting theme config:", error);
9059
+ res.status(500).json({ error: "Failed to reset theme config" });
9060
+ }
9061
+ });
8737
9062
  app.get("/api/projects", async (req, res) => {
8738
9063
  try {
8739
9064
  let projects = await listProjects(projectsDir);
@@ -8873,7 +9198,9 @@ function createDashboardServer(options) {
8873
9198
  app.use("/api/projects/:projectId/todos", createProjectTodosRouter(projectsDir, broadcast));
8874
9199
  app.use("/api/backup", createBackupRouter());
8875
9200
  if (serveStaticUi && dashboardDistPath) {
8876
- app.use("/assets", express.static(resolve18(dashboardDistPath, "assets")));
9201
+ const sendOpts = { dotfiles: "allow" };
9202
+ app.use("/assets", express.static(resolve18(dashboardDistPath, "assets"), sendOpts));
9203
+ app.use(express.static(dashboardDistPath, { ...sendOpts, index: false, fallthrough: true }));
8877
9204
  app.get("{*path}", async (req, res) => {
8878
9205
  if (req.path.startsWith("/api") || req.path === "/ws" || req.path.startsWith("/assets")) {
8879
9206
  res.status(404).json({ error: "Not Found" });
@@ -8886,7 +9213,7 @@ function createDashboardServer(options) {
8886
9213
  );
8887
9214
  return;
8888
9215
  }
8889
- res.sendFile(indexPath, (err) => {
9216
+ res.sendFile(indexPath, sendOpts, (err) => {
8890
9217
  if (err) {
8891
9218
  console.error("Error sending dashboard index.html:", err);
8892
9219
  if (!res.headersSent) res.status(500).send("Dashboard load error");