syntaur 0.76.0 → 0.77.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 (68) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dashboard/dist/assets/{_basePickBy-BiwSunXO.js → _basePickBy-CmtR3VlC.js} +1 -1
  3. package/dashboard/dist/assets/{_baseUniq-DsjKPXxP.js → _baseUniq-d5yVqkuO.js} +1 -1
  4. package/dashboard/dist/assets/{arc-DlEs4wsF.js → arc-BTfvlUzk.js} +1 -1
  5. package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-DPvCrO57.js → architectureDiagram-2XIMDMQ5-ComKmfQn.js} +1 -1
  6. package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-CpNdCY-r.js → blockDiagram-WCTKOSBZ-69xtTOqC.js} +1 -1
  7. package/dashboard/dist/assets/{c4Diagram-IC4MRINW-CC29QJCs.js → c4Diagram-IC4MRINW-B5MsrIyR.js} +1 -1
  8. package/dashboard/dist/assets/channel-B55sx3Pw.js +1 -0
  9. package/dashboard/dist/assets/{chunk-4BX2VUAB-DKyy-k2D.js → chunk-4BX2VUAB-BV-rQx2S.js} +1 -1
  10. package/dashboard/dist/assets/{chunk-55IACEB6-DjobngQk.js → chunk-55IACEB6-BHoZ0_2C.js} +1 -1
  11. package/dashboard/dist/assets/{chunk-FMBD7UC4-BUftm7dV.js → chunk-FMBD7UC4-BZppz6s9.js} +1 -1
  12. package/dashboard/dist/assets/{chunk-JSJVCQXG-Nv7rQv4t.js → chunk-JSJVCQXG-BKJIycjQ.js} +1 -1
  13. package/dashboard/dist/assets/{chunk-KX2RTZJC-Dg6UhNB9.js → chunk-KX2RTZJC-Drv6rL3f.js} +1 -1
  14. package/dashboard/dist/assets/{chunk-NQ4KR5QH-D8neQrJ_.js → chunk-NQ4KR5QH-u1ws00gN.js} +1 -1
  15. package/dashboard/dist/assets/{chunk-QZHKN3VN-Uj9c1naA.js → chunk-QZHKN3VN-zCyNaZcO.js} +1 -1
  16. package/dashboard/dist/assets/{chunk-WL4C6EOR-DfeSsP3z.js → chunk-WL4C6EOR-Dz0hDBUx.js} +1 -1
  17. package/dashboard/dist/assets/classDiagram-VBA2DB6C-Kgg-J05j.js +1 -0
  18. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-Kgg-J05j.js +1 -0
  19. package/dashboard/dist/assets/clone-DXxPBQmb.js +1 -0
  20. package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-CLfZNKcO.js → cose-bilkent-S5V4N54A-CO0Ms-ys.js} +1 -1
  21. package/dashboard/dist/assets/{dagre-KLK3FWXG-DyJLICZx.js → dagre-KLK3FWXG-BDBGlQ5i.js} +1 -1
  22. package/dashboard/dist/assets/{diagram-E7M64L7V-DhQc7Ta3.js → diagram-E7M64L7V-C1jhhA3a.js} +1 -1
  23. package/dashboard/dist/assets/{diagram-IFDJBPK2-C6kFO7hB.js → diagram-IFDJBPK2-DYA2G930.js} +1 -1
  24. package/dashboard/dist/assets/{diagram-P4PSJMXO-B6oCiOXk.js → diagram-P4PSJMXO-iox20fhC.js} +1 -1
  25. package/dashboard/dist/assets/{erDiagram-INFDFZHY-BsMPaSwJ.js → erDiagram-INFDFZHY-CdrG4ZUW.js} +1 -1
  26. package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-DLxEqKtZ.js → flowDiagram-PKNHOUZH-CMuagI-6.js} +1 -1
  27. package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-D70pvqxG.js → ganttDiagram-A5KZAMGK-C8jgdfRO.js} +1 -1
  28. package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-D74VHtlE.js → gitGraphDiagram-K3NZZRJ6-Chu0FgBt.js} +1 -1
  29. package/dashboard/dist/assets/{graph-C8bz_IEO.js → graph-RWP7g4cL.js} +1 -1
  30. package/dashboard/dist/assets/{index-h-X_8om3.js → index-BFpKR1Z0.js} +163 -148
  31. package/dashboard/dist/assets/{index-BetqH_3H.css → index-C6KBRx3q.css} +1 -1
  32. package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-DivBd0Pz.js → infoDiagram-LFFYTUFH-bIlmxe7x.js} +1 -1
  33. package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-BGihUeOE.js → ishikawaDiagram-PHBUUO56-CvvXAmzr.js} +1 -1
  34. package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-C7U4cWHQ.js → journeyDiagram-4ABVD52K-BlSVnlQk.js} +1 -1
  35. package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-CFp_hDTm.js → kanban-definition-K7BYSVSG-D6kAus7L.js} +1 -1
  36. package/dashboard/dist/assets/{layout-DMSDPqCs.js → layout-BIoVL4m2.js} +1 -1
  37. package/dashboard/dist/assets/{linear-CCC-meYc.js → linear-B4ySyXON.js} +1 -1
  38. package/dashboard/dist/assets/{mermaid.core-BDa2cc2K.js → mermaid.core-B_8IjgQO.js} +4 -4
  39. package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-DLKvONhO.js → mindmap-definition-YRQLILUH-B6RTS8xi.js} +1 -1
  40. package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-DAJoGZ93.js → pieDiagram-SKSYHLDU-EntB8Xmg.js} +1 -1
  41. package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-Bo7haX1W.js → quadrantDiagram-337W2JSQ-UTEbe4g6.js} +1 -1
  42. package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-uEvGz33v.js → requirementDiagram-Z7DCOOCP-qG2oV68i.js} +1 -1
  43. package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-cjYoCGCM.js → sankeyDiagram-WA2Y5GQK-C-EzJpn8.js} +1 -1
  44. package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-CYXcNklt.js → sequenceDiagram-2WXFIKYE-rCjfcAr5.js} +1 -1
  45. package/dashboard/dist/assets/{stateDiagram-RAJIS63D-B5We7jAw.js → stateDiagram-RAJIS63D-C3hoQmLC.js} +1 -1
  46. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-rHFeeHzp.js +1 -0
  47. package/dashboard/dist/assets/{timeline-definition-YZTLITO2-CEqVmnVQ.js → timeline-definition-YZTLITO2-2gGWKHHW.js} +1 -1
  48. package/dashboard/dist/assets/{treemap-KZPCXAKY-CE1puPeN.js → treemap-KZPCXAKY-BV6TTFvC.js} +1 -1
  49. package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-CiiuRUjK.js → vennDiagram-LZ73GAT5-C1aeXQ29.js} +1 -1
  50. package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-B0OuxFm9.js → xychartDiagram-JWTSCODW-BDbba6Mg.js} +1 -1
  51. package/dashboard/dist/index.html +2 -2
  52. package/dist/dashboard/server.js +845 -147
  53. package/dist/dashboard/server.js.map +1 -1
  54. package/dist/index.js +1290 -547
  55. package/dist/index.js.map +1 -1
  56. package/dist/launch/index.d.ts +41 -0
  57. package/dist/launch/index.js +200 -21
  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-IutbH48W.js +0 -1
  65. package/dashboard/dist/assets/classDiagram-VBA2DB6C-8IywRj9I.js +0 -1
  66. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-8IywRj9I.js +0 -1
  67. package/dashboard/dist/assets/clone-C6saGiCd.js +0 -1
  68. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-CX9-EpBi.js +0 -1
@@ -70,6 +70,10 @@ declare function parseOpenUrl(input: string): ParsedOpenUrl;
70
70
  type BindableActionKind = 'new-workspace' | 'new-project' | 'new-todo' | 'new-assignment';
71
71
 
72
72
  type PromptArgPosition = 'first' | 'last' | 'none';
73
+ /** Runner family an agent belongs to — the intrinsic "type badge". */
74
+ type RunnerKind = 'claude' | 'pi' | 'codex';
75
+ /** Which discovery source a registered agent's on-disk def was adopted from. */
76
+ type AgentSourceKind = 'claude-global' | 'claude-project' | 'directory';
73
77
  /**
74
78
  * Per-agent argv recipe for continuing a recorded session in a specific mode.
75
79
  *
@@ -135,6 +139,27 @@ interface AgentConfig {
135
139
  * `agentName`. Existence is validated at launch time, not at config-write time.
136
140
  */
137
141
  workdir?: string;
142
+ /**
143
+ * Intrinsic runner family — the type badge shown in the unified agent list
144
+ * (claude / pi / codex). Authoritative when set; `resolveRunner` infers it for
145
+ * legacy rows. Never a user-toggled "mode". When set explicitly it constrains
146
+ * the identity field: a `claude` runner uses `agentName` (not `workdir`); a
147
+ * `pi`/`codex` runner uses `workdir` (not `agentName`) — enforced in
148
+ * `validateAgentList`.
149
+ */
150
+ runner?: RunnerKind;
151
+ /**
152
+ * Source pointer (flat scalars, NOT a nested object — so they ride the scalar
153
+ * config-field machinery) to the on-disk def a registered agent was adopted
154
+ * from. The def stays the source of truth for identity content; these exist for
155
+ * display, dedupe ("already registered"), and refresh. `sourcePath` is the
156
+ * `.md` file for a claude agent or the directory for a directory agent (===
157
+ * `workdir`). `sourceRepo` is the repo root for a `claude-project` def (used to
158
+ * reason about cross-repo `--agent` resolution).
159
+ */
160
+ sourceKind?: AgentSourceKind;
161
+ sourcePath?: string;
162
+ sourceRepo?: string;
138
163
  }
139
164
 
140
165
  /**
@@ -322,6 +347,18 @@ interface HotkeyBindingsConfig {
322
347
  * - `off`: no automatic DB writes (manual `track-session` still works).
323
348
  */
324
349
  type SessionAutoTrack = 'all' | 'workspaces-only' | 'off';
350
+ /**
351
+ * Multi-source agent discovery settings. Sources are individually toggleable;
352
+ * `roots` is the depth-1 directory-scan root list (default `~`). Persisted as an
353
+ * `agentDiscovery:` block whose one-level keys read back as dotted `fm` keys
354
+ * (roots is a `:`-separated scalar to avoid list-parsing in the top-level map).
355
+ */
356
+ interface AgentDiscoveryConfig {
357
+ claudeGlobal: boolean;
358
+ claudeProject: boolean;
359
+ directory: boolean;
360
+ roots: string[];
361
+ }
325
362
  interface SyntaurConfig {
326
363
  version: string;
327
364
  defaultProjectDir: string;
@@ -350,6 +387,10 @@ interface SyntaurConfig {
350
387
  /** Opt-in: run the read-only staleness watchdog on the dashboard loop (emits
351
388
  * staleness-detected/cleared audit events; never mutates status). Off by default. */
352
389
  stalenessWatchdog: boolean;
390
+ /** Default cwd for a standalone claude-agent launch (null → home at launch). */
391
+ standaloneDefaultCwd: string | null;
392
+ /** Multi-source agent discovery settings (sources on/off + scan roots). */
393
+ agentDiscovery: AgentDiscoveryConfig;
353
394
  }
354
395
 
355
396
  /**
@@ -1239,6 +1239,14 @@ function agentNameArgs(agent) {
1239
1239
  const n = agent.agentName?.trim();
1240
1240
  return n ? ["--agent", n] : [];
1241
1241
  }
1242
+ function resolveRunner(agent) {
1243
+ if (agent.runner) return agent.runner;
1244
+ if (agent.agentName && agent.agentName.trim() !== "") return "claude";
1245
+ const cmd = agent.command ?? "";
1246
+ if (/claude/i.test(cmd) || agent.id === "claude") return "claude";
1247
+ if (/codex/i.test(cmd) || agent.id === "codex") return "codex";
1248
+ return "pi";
1249
+ }
1242
1250
  function stripModelFlags(args) {
1243
1251
  const out = [];
1244
1252
  for (let i = 0; i < args.length; i++) {
@@ -2451,6 +2459,7 @@ __export(config_exports, {
2451
2459
  parseStatusConfig: () => parseStatusConfig,
2452
2460
  parseTerminalConfig: () => parseTerminalConfig,
2453
2461
  readConfig: () => readConfig,
2462
+ serializeAgentDiscoveryConfig: () => serializeAgentDiscoveryConfig,
2454
2463
  serializeSearchConfig: () => serializeSearchConfig,
2455
2464
  serializeStatusConfig: () => serializeStatusConfig,
2456
2465
  toTitleCase: () => toTitleCase,
@@ -2464,6 +2473,7 @@ __export(config_exports, {
2464
2473
  validateDeriveShape: () => validateDeriveShape,
2465
2474
  validateFactDeclarations: () => validateFactDeclarations,
2466
2475
  validateStalenessConfig: () => validateStalenessConfig,
2476
+ writeAgentDiscoveryConfig: () => writeAgentDiscoveryConfig,
2467
2477
  writeAgentsConfig: () => writeAgentsConfig,
2468
2478
  writeHotkeyBindingsConfig: () => writeHotkeyBindingsConfig,
2469
2479
  writeSearchConfig: () => writeSearchConfig,
@@ -2556,6 +2566,50 @@ function validateAgentList(agents) {
2556
2566
  `agent "${agent.id}" sets both agentName and model \u2014 the agent definition's own model is authoritative; remove the profile model`
2557
2567
  );
2558
2568
  }
2569
+ if (agent.runner !== void 0 && !RUNNER_KINDS.includes(agent.runner)) {
2570
+ throw new AgentConfigError(
2571
+ `agent "${agent.id}" has invalid runner "${agent.runner}" \u2014 must be one of ${RUNNER_KINDS.join(", ")}`
2572
+ );
2573
+ }
2574
+ if (agent.sourceKind !== void 0 && !AGENT_SOURCE_KINDS.includes(agent.sourceKind)) {
2575
+ throw new AgentConfigError(
2576
+ `agent "${agent.id}" has invalid sourceKind "${agent.sourceKind}" \u2014 must be one of ${AGENT_SOURCE_KINDS.join(", ")}`
2577
+ );
2578
+ }
2579
+ if (agent.sourcePath !== void 0 && /[\r\n]/.test(agent.sourcePath)) {
2580
+ throw new AgentConfigError(
2581
+ `agent "${agent.id}" has invalid sourcePath \u2014 must be a single line (no newlines)`
2582
+ );
2583
+ }
2584
+ if (agent.sourceRepo !== void 0 && /[\r\n]/.test(agent.sourceRepo)) {
2585
+ throw new AgentConfigError(
2586
+ `agent "${agent.id}" has invalid sourceRepo \u2014 must be a single line (no newlines)`
2587
+ );
2588
+ }
2589
+ if (agent.runner === "claude" && agent.workdir !== void 0 && agent.workdir.trim() !== "") {
2590
+ throw new AgentConfigError(
2591
+ `agent "${agent.id}" has runner "claude" but sets workdir \u2014 a claude agent uses agentName, not workdir`
2592
+ );
2593
+ }
2594
+ if ((agent.runner === "pi" || agent.runner === "codex") && agent.agentName !== void 0 && agent.agentName.trim() !== "") {
2595
+ throw new AgentConfigError(
2596
+ `agent "${agent.id}" has runner "${agent.runner}" but sets agentName \u2014 a directory agent uses workdir, not agentName`
2597
+ );
2598
+ }
2599
+ if (agent.runner !== void 0 && agent.sourceKind !== void 0) {
2600
+ const directorySource = agent.sourceKind === "directory";
2601
+ const claudeRunner = agent.runner === "claude";
2602
+ if (directorySource && claudeRunner) {
2603
+ throw new AgentConfigError(
2604
+ `agent "${agent.id}" has sourceKind "directory" but runner "claude" \u2014 a directory source is a pi/codex agent`
2605
+ );
2606
+ }
2607
+ if (!directorySource && !claudeRunner) {
2608
+ throw new AgentConfigError(
2609
+ `agent "${agent.id}" has sourceKind "${agent.sourceKind}" but runner "${agent.runner}" \u2014 a ${agent.sourceKind} source is a claude agent`
2610
+ );
2611
+ }
2612
+ }
2559
2613
  validateSessionInvocation(agent, "resume", agent.resume);
2560
2614
  validateSessionInvocation(agent, "fork", agent.fork);
2561
2615
  if (agent.default) defaults++;
@@ -2658,6 +2712,21 @@ function parseInstalledAgents(fm) {
2658
2712
  }
2659
2713
  return Object.keys(installedAgents).length > 0 ? { installedAgents } : {};
2660
2714
  }
2715
+ function parseAgentDiscoveryFromFm(fm) {
2716
+ const flag = (key, def) => {
2717
+ const v = fm[key];
2718
+ if (v === void 0) return def;
2719
+ return String(v).toLowerCase() === "true";
2720
+ };
2721
+ const rootsRaw = fm["agentDiscovery.roots"];
2722
+ const roots = rootsRaw !== void 0 && String(rootsRaw).trim() !== "" ? String(rootsRaw).split(":").map((r) => r.trim()).filter((r) => r.length > 0) : [];
2723
+ return {
2724
+ claudeGlobal: flag("agentDiscovery.claudeGlobal", true),
2725
+ claudeProject: flag("agentDiscovery.claudeProject", true),
2726
+ directory: flag("agentDiscovery.directory", true),
2727
+ roots: roots.length > 0 ? roots : DEFAULT_CONFIG.agentDiscovery.roots.slice()
2728
+ };
2729
+ }
2661
2730
  function parseStatusConfig(content) {
2662
2731
  const match = content.match(/^---\n([\s\S]*?)\n---/);
2663
2732
  if (!match) return null;
@@ -3166,6 +3235,43 @@ ${cleanedFm}
3166
3235
  ---${afterFrontmatter}`;
3167
3236
  await writeFileForce(configPath, newContent);
3168
3237
  }
3238
+ function serializeAgentDiscoveryConfig(cfg) {
3239
+ return [
3240
+ "agentDiscovery:",
3241
+ ` claudeGlobal: ${cfg.claudeGlobal}`,
3242
+ ` claudeProject: ${cfg.claudeProject}`,
3243
+ ` directory: ${cfg.directory}`,
3244
+ ` roots: ${cfg.roots.join(":")}`
3245
+ ].join("\n");
3246
+ }
3247
+ async function writeAgentDiscoveryConfig(cfg, standaloneDefaultCwd) {
3248
+ const configPath = resolve7(syntaurRoot(), "config.md");
3249
+ const block = serializeAgentDiscoveryConfig(cfg);
3250
+ const scalarLine = standaloneDefaultCwd ? `standaloneDefaultCwd: ${standaloneDefaultCwd}` : "";
3251
+ const additions = [block, scalarLine].filter(Boolean).join("\n");
3252
+ const existing = await fileExists(configPath) ? await readFile5(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
3253
+ const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
3254
+ if (!fmMatch) {
3255
+ const content = `---
3256
+ version: "2.0"
3257
+ defaultProjectDir: ${defaultProjectDir()}
3258
+ ${additions}
3259
+ ---
3260
+ ${existing}`;
3261
+ await writeFileForce(configPath, content);
3262
+ return;
3263
+ }
3264
+ const fmBlock = fmMatch[2];
3265
+ const afterFrontmatter = existing.slice(fmMatch[0].length);
3266
+ let cleanedFm = stripTopLevelBlock(fmBlock, "agentDiscovery");
3267
+ cleanedFm = stripTopLevelScalar(cleanedFm, "standaloneDefaultCwd");
3268
+ const newFm = `${cleanedFm}
3269
+ ${additions}`.replace(/^\n+/, "").replace(/\n+$/, "");
3270
+ const newContent = `---
3271
+ ${newFm}
3272
+ ---${afterFrontmatter}`;
3273
+ await writeFileForce(configPath, newContent);
3274
+ }
3169
3275
  function stripTopLevelScalar(fmBlock, key) {
3170
3276
  const lines = fmBlock.split("\n");
3171
3277
  const keyRegex = new RegExp(`^${key}:\\s*\\S`);
@@ -3378,6 +3484,10 @@ function parseAgentsConfig(content) {
3378
3484
  ...current.launchPrompt ? { launchPrompt: current.launchPrompt } : {},
3379
3485
  ...current.agentName ? { agentName: current.agentName } : {},
3380
3486
  ...current.workdir ? { workdir: current.workdir } : {},
3487
+ ...current.runner ? { runner: current.runner } : {},
3488
+ ...current.sourceKind ? { sourceKind: current.sourceKind } : {},
3489
+ ...current.sourcePath ? { sourcePath: current.sourcePath } : {},
3490
+ ...current.sourceRepo ? { sourceRepo: current.sourceRepo } : {},
3381
3491
  ...current.resume ? { resume: current.resume } : {},
3382
3492
  ...current.fork ? { fork: current.fork } : {}
3383
3493
  });
@@ -3575,6 +3685,18 @@ function assignAgentField(target, key, rawValue) {
3575
3685
  case "workdir":
3576
3686
  target.workdir = value;
3577
3687
  break;
3688
+ case "runner":
3689
+ target.runner = value;
3690
+ break;
3691
+ case "sourceKind":
3692
+ target.sourceKind = value;
3693
+ break;
3694
+ case "sourcePath":
3695
+ target.sourcePath = value;
3696
+ break;
3697
+ case "sourceRepo":
3698
+ target.sourceRepo = value;
3699
+ break;
3578
3700
  }
3579
3701
  }
3580
3702
  function yamlQuoteScalar(value) {
@@ -3610,6 +3732,18 @@ function serializeAgentsConfig(agents) {
3610
3732
  if (a.workdir) {
3611
3733
  lines.push(` workdir: ${yamlQuoteScalar(a.workdir)}`);
3612
3734
  }
3735
+ if (a.runner) {
3736
+ lines.push(` runner: ${yamlQuoteScalar(a.runner)}`);
3737
+ }
3738
+ if (a.sourceKind) {
3739
+ lines.push(` sourceKind: ${yamlQuoteScalar(a.sourceKind)}`);
3740
+ }
3741
+ if (a.sourcePath) {
3742
+ lines.push(` sourcePath: ${yamlQuoteScalar(a.sourcePath)}`);
3743
+ }
3744
+ if (a.sourceRepo) {
3745
+ lines.push(` sourceRepo: ${yamlQuoteScalar(a.sourceRepo)}`);
3746
+ }
3613
3747
  if (a.args && a.args.length > 0) {
3614
3748
  lines.push(` args:`);
3615
3749
  for (const arg of a.args) {
@@ -4095,7 +4229,12 @@ async function readConfig() {
4095
4229
  searchConfig: parseSearchConfig(content),
4096
4230
  workspaceVisibility: parseWorkspaceVisibilityConfig(fmBlock),
4097
4231
  staleness: parseStalenessConfig(content),
4098
- stalenessWatchdog: String(fm["stalenessWatchdog"]).toLowerCase() === "true"
4232
+ stalenessWatchdog: String(fm["stalenessWatchdog"]).toLowerCase() === "true",
4233
+ standaloneDefaultCwd: parseOptionalAbsolutePath(
4234
+ fm["standaloneDefaultCwd"],
4235
+ "standaloneDefaultCwd"
4236
+ ),
4237
+ agentDiscovery: parseAgentDiscoveryFromFm(fm)
4099
4238
  };
4100
4239
  }
4101
4240
  function getAssignmentTypes(config) {
@@ -4158,7 +4297,7 @@ async function updateAgentsConfig(mutation, options = {}) {
4158
4297
  await writeAgentsConfig(next);
4159
4298
  return { previous, next, written: true };
4160
4299
  }
4161
- var STALENESS_KEY_TO_FIELD, DURATION_RE, DURATION_UNIT_MS, DEFAULT_ASSIGNMENT_TYPES, DEFAULT_CONFIG, AUTO_CREATE_WORKTREE_VALUES, SESSION_AUTO_TRACK_VALUES, AgentConfigError, DEFAULT_STATUS_COLORS, KNOWN_AGENT_SCALAR_FIELDS, migratedConfigPaths, TerminalConfigError;
4300
+ var STALENESS_KEY_TO_FIELD, DURATION_RE, DURATION_UNIT_MS, DEFAULT_ASSIGNMENT_TYPES, DEFAULT_CONFIG, AUTO_CREATE_WORKTREE_VALUES, SESSION_AUTO_TRACK_VALUES, RUNNER_KINDS, AGENT_SOURCE_KINDS, AgentConfigError, DEFAULT_STATUS_COLORS, KNOWN_AGENT_SCALAR_FIELDS, migratedConfigPaths, TerminalConfigError;
4162
4301
  var init_config2 = __esm({
4163
4302
  "src/utils/config.ts"() {
4164
4303
  "use strict";
@@ -4234,10 +4373,23 @@ var init_config2 = __esm({
4234
4373
  hidden: []
4235
4374
  },
4236
4375
  staleness: null,
4237
- stalenessWatchdog: false
4376
+ stalenessWatchdog: false,
4377
+ standaloneDefaultCwd: null,
4378
+ agentDiscovery: {
4379
+ claudeGlobal: true,
4380
+ claudeProject: true,
4381
+ directory: true,
4382
+ roots: ["~"]
4383
+ }
4238
4384
  };
4239
4385
  AUTO_CREATE_WORKTREE_VALUES = ["skip", "ask", "always"];
4240
4386
  SESSION_AUTO_TRACK_VALUES = ["all", "workspaces-only", "off"];
4387
+ RUNNER_KINDS = ["claude", "pi", "codex"];
4388
+ AGENT_SOURCE_KINDS = [
4389
+ "claude-global",
4390
+ "claude-project",
4391
+ "directory"
4392
+ ];
4241
4393
  AgentConfigError = class extends Error {
4242
4394
  };
4243
4395
  DEFAULT_STATUS_COLORS = {
@@ -4259,7 +4411,11 @@ var init_config2 = __esm({
4259
4411
  "playbook",
4260
4412
  "launchPrompt",
4261
4413
  "agentName",
4262
- "workdir"
4414
+ "workdir",
4415
+ "runner",
4416
+ "sourceKind",
4417
+ "sourcePath",
4418
+ "sourceRepo"
4263
4419
  ]);
4264
4420
  migratedConfigPaths = /* @__PURE__ */ new Set();
4265
4421
  TerminalConfigError = class extends Error {
@@ -4473,6 +4629,7 @@ var init_playbooks = __esm({
4473
4629
 
4474
4630
  // src/launch/cwd.ts
4475
4631
  import { existsSync, statSync } from "fs";
4632
+ import { homedir as homedir2 } from "os";
4476
4633
  import { isAbsolute as isAbsolute2 } from "path";
4477
4634
  function isExistingDir(p) {
4478
4635
  if (!p || !isAbsolute2(p)) return false;
@@ -4518,6 +4675,31 @@ function resolveLaunchCwd(agent, worktreeCwd) {
4518
4675
  }
4519
4676
  return { spawnCwd: worktreeCwd, worktreePath: worktreeCwd, invalidReason: null };
4520
4677
  }
4678
+ function resolveStandaloneCwd(agent, standaloneDefaultCwd) {
4679
+ const wd = agent.workdir?.trim();
4680
+ if (wd) {
4681
+ const expanded = expandHome(wd);
4682
+ if (!isExistingDir(expanded)) {
4683
+ return {
4684
+ cwd: null,
4685
+ invalidReason: `agent "${agent.id}" workdir ${agent.workdir} (resolved ${expanded}) is not an existing directory \u2014 a standalone launch requires a valid workdir.`
4686
+ };
4687
+ }
4688
+ return { cwd: expanded, invalidReason: null };
4689
+ }
4690
+ if (resolveRunner(agent) === "claude") {
4691
+ const configured = standaloneDefaultCwd?.trim();
4692
+ if (configured) {
4693
+ const expanded = expandHome(configured);
4694
+ if (isExistingDir(expanded)) return { cwd: expanded, invalidReason: null };
4695
+ }
4696
+ return { cwd: homedir2(), invalidReason: null };
4697
+ }
4698
+ return {
4699
+ cwd: null,
4700
+ invalidReason: `agent "${agent.id}" has no workdir \u2014 a standalone launch of a ${resolveRunner(agent)} agent requires a valid workdir.`
4701
+ };
4702
+ }
4521
4703
  function formatFallbackCwdWarning(opts) {
4522
4704
  const missing = [];
4523
4705
  if (!opts.worktreePath) missing.push("worktreePath");
@@ -4529,6 +4711,7 @@ function formatFallbackCwdWarning(opts) {
4529
4711
  var init_cwd = __esm({
4530
4712
  "src/launch/cwd.ts"() {
4531
4713
  "use strict";
4714
+ init_agents_schema();
4532
4715
  init_paths();
4533
4716
  }
4534
4717
  });
@@ -7435,18 +7618,14 @@ async function resolveStandalonePlan(input, terminal) {
7435
7618
  `Agent "${agentId}" requested for a standalone launch is not in your agents list.`
7436
7619
  );
7437
7620
  }
7438
- const wd = agent.workdir?.trim();
7439
- if (!wd) {
7440
- throw new LaunchError(
7441
- "workspace-path-invalid",
7442
- `Agent "${agent.id}" has no workdir \u2014 a standalone launch requires a valid workdir.`
7443
- );
7444
- }
7445
- const expanded = expandHome(wd);
7446
- if (!isExistingDir(expanded)) {
7621
+ const { cwd: spawnCwd, invalidReason } = resolveStandaloneCwd(
7622
+ agent,
7623
+ input.config.standaloneDefaultCwd
7624
+ );
7625
+ if (spawnCwd === null) {
7447
7626
  throw new LaunchError(
7448
7627
  "workspace-path-invalid",
7449
- `Agent "${agent.id}" workdir ${agent.workdir} (resolved ${expanded}) is not an existing directory \u2014 a standalone launch requires a valid workdir.`
7628
+ invalidReason ?? `Agent "${agent.id}" has no valid standalone launch directory.`
7450
7629
  );
7451
7630
  }
7452
7631
  const knownPlaybookSlugs = await listPlaybookSlugs(playbooksDir());
@@ -7460,7 +7639,7 @@ async function resolveStandalonePlan(input, terminal) {
7460
7639
  const { argv, shellFallbackWarning } = buildFreshArgv(agent, prompt);
7461
7640
  return {
7462
7641
  terminal,
7463
- cwd: expanded,
7642
+ cwd: spawnCwd,
7464
7643
  argv,
7465
7644
  env: process.env,
7466
7645
  agentId: agent.id,
@@ -7627,13 +7806,13 @@ async function resolveSessionPlan(input, terminal) {
7627
7806
 
7628
7807
  // src/launch/execute.ts
7629
7808
  import { spawn as spawn3 } from "child_process";
7630
- import { homedir as homedir5 } from "os";
7809
+ import { homedir as homedir6 } from "os";
7631
7810
  import { basename as basename2, join as join6, resolve as resolve18 } from "path";
7632
7811
 
7633
7812
  // src/utils/terminal-probe.ts
7634
7813
  import { spawnSync as spawnSync2 } from "child_process";
7635
7814
  import { existsSync as existsSync2 } from "fs";
7636
- import { homedir as homedir2 } from "os";
7815
+ import { homedir as homedir3 } from "os";
7637
7816
  import { join as join3 } from "path";
7638
7817
  var APP_BUNDLE_NAMES = {
7639
7818
  iterm: "iTerm.app",
@@ -7645,7 +7824,7 @@ var APP_FIXED_PATHS = {
7645
7824
  "terminal-app": "/System/Applications/Utilities/Terminal.app"
7646
7825
  };
7647
7826
  function defaultApplicationsDirs() {
7648
- return ["/Applications", join3(homedir2(), "Applications")];
7827
+ return ["/Applications", join3(homedir3(), "Applications")];
7649
7828
  }
7650
7829
  function findAppBundle(terminal, dirs = defaultApplicationsDirs()) {
7651
7830
  const fixed = APP_FIXED_PATHS[terminal];
@@ -7707,7 +7886,7 @@ init_config2();
7707
7886
  // src/utils/session-id.ts
7708
7887
  import { execFileSync as execFileSync2 } from "child_process";
7709
7888
  import { mkdirSync, readFileSync as readFileSync2, statSync as statSync3, writeFileSync } from "fs";
7710
- import { homedir as homedir4 } from "os";
7889
+ import { homedir as homedir5 } from "os";
7711
7890
  import { dirname as dirname3, join as join5 } from "path";
7712
7891
 
7713
7892
  // src/utils/process-info.ts
@@ -7730,7 +7909,7 @@ function captureProcessStartedAt(pid) {
7730
7909
  init_paths();
7731
7910
  import { open as open2, readdir as readdir10, stat as stat3 } from "fs/promises";
7732
7911
  import { join as join4 } from "path";
7733
- import { homedir as homedir3 } from "os";
7912
+ import { homedir as homedir4 } from "os";
7734
7913
 
7735
7914
  // src/utils/transcript.ts
7736
7915
  import { open } from "fs/promises";
@@ -7848,7 +8027,7 @@ async function registerLaunchAtBirth(plan, child) {
7848
8027
  const sessionId = plan.session?.sessionId ?? null;
7849
8028
  const procStart = captureProcessStartedAt(pid);
7850
8029
  const envDir = process.env.SYNTAUR_RUNTIME_SESSIONS_DIR;
7851
- const markerDir = envDir && envDir.length > 0 ? envDir : join6(homedir5(), ".syntaur", "runtime", "sessions");
8030
+ const markerDir = envDir && envDir.length > 0 ? envDir : join6(homedir6(), ".syntaur", "runtime", "sessions");
7852
8031
  writeRuntimeMarker(
7853
8032
  pid,
7854
8033
  {