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
@@ -9,7 +9,7 @@ declare class OpenUrlError extends Error {
9
9
  }
10
10
  type SessionMode = 'resume' | 'fork';
11
11
  interface ParsedOpenUrl {
12
- kind: 'assignment' | 'session';
12
+ kind: 'assignment' | 'session' | 'standalone';
13
13
  id: string;
14
14
  /**
15
15
  * Optional one-shot terminal override. When present, the launch plan uses
@@ -43,6 +43,13 @@ interface ParsedOpenUrl {
43
43
  * to the fallback seed), distinct from `undefined` (no override).
44
44
  */
45
45
  prompt?: string;
46
+ /**
47
+ * Optional one-shot Claude `--agent <name>` identity (the `agentName=` query
48
+ * param). The dashboard's discovered-agent picker sends a chosen agent
49
+ * definition here so the launched session adopts it. Only set for
50
+ * `kind === 'assignment'`. Length-bounded like `prompt`.
51
+ */
52
+ agentName?: string;
46
53
  }
47
54
  /**
48
55
  * Parse a `syntaur://open?assignment=<id>` or `syntaur://open?session=<id>` URL.
@@ -110,6 +117,24 @@ interface AgentConfig {
110
117
  * seed. Takes precedence over `playbook`. Single-line (see `serializeAgentsConfig`).
111
118
  */
112
119
  launchPrompt?: string;
120
+ /**
121
+ * Optional Claude agent identity, injected as `--agent <value>` (see
122
+ * `agentNameArgs`). When set, the launched session adopts that agent's
123
+ * definition (system prompt + its own model frontmatter), so any profile
124
+ * `model` is suppressed on a fresh launch (see `buildAgentArgv`). Mutually
125
+ * exclusive with `workdir`. Meaningful only for Claude-compatible runners;
126
+ * `--agent` is simply injected when present regardless of runner. Single-line.
127
+ */
128
+ agentName?: string;
129
+ /**
130
+ * Optional launch cwd override (absolute or `~`-prefixed) for directory-style
131
+ * agents (pi/codex) that load their identity from a launch directory. When set,
132
+ * the session is spawned from this directory instead of the worktree, and the
133
+ * worktree path is injected into the launch prompt (`@worktree` / a synthesized
134
+ * seed) so the agent can `cd` into the code. Mutually exclusive with
135
+ * `agentName`. Existence is validated at launch time, not at config-write time.
136
+ */
137
+ workdir?: string;
113
138
  }
114
139
 
115
140
  /**
@@ -371,7 +396,7 @@ interface LaunchPlan {
371
396
  };
372
397
  }
373
398
  interface ResolveLaunchPlanInput {
374
- kind: 'assignment' | 'session';
399
+ kind: 'assignment' | 'session' | 'standalone';
375
400
  id: string;
376
401
  /**
377
402
  * Only consulted when `kind === 'session'`. Defaults to `'resume'` so
@@ -407,6 +432,14 @@ interface ResolveLaunchPlanInput {
407
432
  * reusing `agent.launchPrompt`). Per-launch only — never written to config.
408
433
  */
409
434
  promptOverride?: string;
435
+ /**
436
+ * Only consulted when `kind === 'assignment'`. A one-shot Claude `--agent
437
+ * <name>` identity, wired from `?agentName=<name>` on the incoming
438
+ * `syntaur://` URL (the dashboard's discovered-agent picker). When defined it
439
+ * is applied over the resolved agent before argv assembly, so the launched
440
+ * session adopts that agent definition. Presence-significant; per-launch only.
441
+ */
442
+ agentName?: string;
410
443
  }
411
444
  /**
412
445
  * Pick the agent the "Open in agent" flow should use. Order of preference:
@@ -558,11 +591,28 @@ interface ResolveLaunchPromptInput {
558
591
  playbook?: string | null;
559
592
  /** Assignment id (optional only to represent the rare slug-fallback seed). */
560
593
  id?: string;
561
- /** Records directory (where assignment.md lives), for `@assignment`. */
562
- assignmentDir: string;
563
- /** Null for a standalone assignment. */
594
+ /**
595
+ * Records directory (where assignment.md lives), for `@assignment`. Optional:
596
+ * standalone launches have no assignment, so `@assignment` warns + stays
597
+ * literal when this is absent.
598
+ */
599
+ assignmentDir?: string;
600
+ /** Null for a standalone assignment (required-but-nullable — pass `null`). */
564
601
  projectSlug: string | null;
565
- assignmentSlug: string;
602
+ /** Optional: absent for standalone launches (no assignment). */
603
+ assignmentSlug?: string;
604
+ /**
605
+ * Absolute worktree path, for the `@worktree` token and the directory-agent
606
+ * (`workdir`) default seed. Absent for standalone launches and for plain
607
+ * launches with no worktree; `@worktree` warns + stays literal when absent.
608
+ */
609
+ worktreePath?: string;
610
+ /**
611
+ * The directory the agent process is actually spawned from. When it differs
612
+ * from `worktreePath` the agent is a directory-agent (`workdir` set), which
613
+ * triggers the worktree-injecting default seed. Absent → no workdir agent.
614
+ */
615
+ spawnCwd?: string;
566
616
  /**
567
617
  * Installed playbook slugs, injected by the call site. When provided, a
568
618
  * well-formed `@<slug>` not in this set warns and is left literal. When
@@ -584,7 +634,11 @@ interface ResolveLaunchPromptResult {
584
634
  * 2. else `playbook` set → synthesize `<@assignment pointer> Run <clause> end-to-end.`
585
635
  * (built directly — no `@`-token re-resolution, so a playbook literally named
586
636
  * `assignment` cannot collide with the reserved token).
587
- * 3. else today's bare `/grab-assignment` seed.
637
+ * 3. else directory-agent (`worktreePath` set and != `spawnCwd`) → a plain-language
638
+ * seed pointing at the worktree + a `cd`/grab instruction (plain language
639
+ * because a single message fires only one leading slash-command).
640
+ * 4. else assignment context present → today's bare `/grab-assignment` seed.
641
+ * 5. else (standalone, no template/playbook) → empty prompt.
588
642
  * `template` wins over `playbook`.
589
643
  */
590
644
  declare function resolveLaunchPrompt(input: ResolveLaunchPromptInput): ResolveLaunchPromptResult;
@@ -609,6 +663,14 @@ declare function effectiveLaunchTemplate(input: {
609
663
  projectSlug: string | null;
610
664
  assignmentSlug: string;
611
665
  id?: string;
666
+ /**
667
+ * Set when the agent is a directory-agent (`workdir`). With no explicit
668
+ * `launchPrompt`/`playbook`, the box prefills a `@worktree`-based seed so that
669
+ * re-resolving it at launch (as `promptOverride`) injects the worktree path —
670
+ * a plain bare seed would drop it. The token (not the resolved path) is used
671
+ * so the single resolve at launch fills it in.
672
+ */
673
+ workdir?: string | null;
612
674
  }): string;
613
675
 
614
676
  type InstallKind = 'npx' | 'global' | 'local' | 'unknown';
@@ -1235,6 +1235,10 @@ function modelFlagArgs(agent) {
1235
1235
  const m = agent.model?.trim();
1236
1236
  return m ? ["--model", m] : [];
1237
1237
  }
1238
+ function agentNameArgs(agent) {
1239
+ const n = agent.agentName?.trim();
1240
+ return n ? ["--agent", n] : [];
1241
+ }
1238
1242
  function stripModelFlags(args) {
1239
1243
  const out = [];
1240
1244
  for (let i = 0; i < args.length; i++) {
@@ -2532,6 +2536,26 @@ function validateAgentList(agents) {
2532
2536
  `agent "${agent.id}" has invalid launchPrompt \u2014 must be a single line (no newlines)`
2533
2537
  );
2534
2538
  }
2539
+ if (agent.agentName !== void 0 && /[\r\n]/.test(agent.agentName)) {
2540
+ throw new AgentConfigError(
2541
+ `agent "${agent.id}" has invalid agentName \u2014 must be a single line (no newlines)`
2542
+ );
2543
+ }
2544
+ if (agent.workdir !== void 0 && /[\r\n]/.test(agent.workdir)) {
2545
+ throw new AgentConfigError(
2546
+ `agent "${agent.id}" has invalid workdir \u2014 must be a single line (no newlines)`
2547
+ );
2548
+ }
2549
+ if (agent.agentName !== void 0 && agent.agentName.trim() !== "" && agent.workdir !== void 0 && agent.workdir.trim() !== "") {
2550
+ throw new AgentConfigError(
2551
+ `agent "${agent.id}" sets both agentName and workdir \u2014 these are mutually exclusive`
2552
+ );
2553
+ }
2554
+ if (agent.agentName !== void 0 && agent.agentName.trim() !== "" && agent.model !== void 0 && agent.model.trim() !== "") {
2555
+ throw new AgentConfigError(
2556
+ `agent "${agent.id}" sets both agentName and model \u2014 the agent definition's own model is authoritative; remove the profile model`
2557
+ );
2558
+ }
2535
2559
  validateSessionInvocation(agent, "resume", agent.resume);
2536
2560
  validateSessionInvocation(agent, "fork", agent.fork);
2537
2561
  if (agent.default) defaults++;
@@ -3352,6 +3376,8 @@ function parseAgentsConfig(content) {
3352
3376
  ...current.model ? { model: current.model } : {},
3353
3377
  ...current.playbook ? { playbook: current.playbook } : {},
3354
3378
  ...current.launchPrompt ? { launchPrompt: current.launchPrompt } : {},
3379
+ ...current.agentName ? { agentName: current.agentName } : {},
3380
+ ...current.workdir ? { workdir: current.workdir } : {},
3355
3381
  ...current.resume ? { resume: current.resume } : {},
3356
3382
  ...current.fork ? { fork: current.fork } : {}
3357
3383
  });
@@ -3543,6 +3569,12 @@ function assignAgentField(target, key, rawValue) {
3543
3569
  case "launchPrompt":
3544
3570
  target.launchPrompt = value;
3545
3571
  break;
3572
+ case "agentName":
3573
+ target.agentName = value;
3574
+ break;
3575
+ case "workdir":
3576
+ target.workdir = value;
3577
+ break;
3546
3578
  }
3547
3579
  }
3548
3580
  function yamlQuoteScalar(value) {
@@ -3572,6 +3604,12 @@ function serializeAgentsConfig(agents) {
3572
3604
  if (a.launchPrompt) {
3573
3605
  lines.push(` launchPrompt: ${yamlQuoteScalar(a.launchPrompt)}`);
3574
3606
  }
3607
+ if (a.agentName) {
3608
+ lines.push(` agentName: ${yamlQuoteScalar(a.agentName)}`);
3609
+ }
3610
+ if (a.workdir) {
3611
+ lines.push(` workdir: ${yamlQuoteScalar(a.workdir)}`);
3612
+ }
3575
3613
  if (a.args && a.args.length > 0) {
3576
3614
  lines.push(` args:`);
3577
3615
  for (const arg of a.args) {
@@ -4219,7 +4257,9 @@ var init_config2 = __esm({
4219
4257
  "resolveFromShellAliases",
4220
4258
  "model",
4221
4259
  "playbook",
4222
- "launchPrompt"
4260
+ "launchPrompt",
4261
+ "agentName",
4262
+ "workdir"
4223
4263
  ]);
4224
4264
  migratedConfigPaths = /* @__PURE__ */ new Set();
4225
4265
  TerminalConfigError = class extends Error {
@@ -4463,6 +4503,21 @@ function resolveWorkspaceCwd(input) {
4463
4503
  invalidReason: `workspace path invalid for ${assignmentSlug}: tried worktreePath ${shown(worktreePath)} and repository ${shown(repository)} \u2014 neither is an existing directory`
4464
4504
  };
4465
4505
  }
4506
+ function resolveLaunchCwd(agent, worktreeCwd) {
4507
+ const wd = agent.workdir?.trim();
4508
+ if (wd) {
4509
+ const expanded = expandHome(wd);
4510
+ if (!isExistingDir(expanded)) {
4511
+ return {
4512
+ spawnCwd: worktreeCwd,
4513
+ worktreePath: worktreeCwd,
4514
+ invalidReason: `agent "${agent.id}" workdir ${agent.workdir} (resolved ${expanded}) is not an existing directory`
4515
+ };
4516
+ }
4517
+ return { spawnCwd: expanded, worktreePath: worktreeCwd, invalidReason: null };
4518
+ }
4519
+ return { spawnCwd: worktreeCwd, worktreePath: worktreeCwd, invalidReason: null };
4520
+ }
4466
4521
  function formatFallbackCwdWarning(opts) {
4467
4522
  const missing = [];
4468
4523
  if (!opts.worktreePath) missing.push("worktreePath");
@@ -4474,6 +4529,7 @@ function formatFallbackCwdWarning(opts) {
4474
4529
  var init_cwd = __esm({
4475
4530
  "src/launch/cwd.ts"() {
4476
4531
  "use strict";
4532
+ init_paths();
4477
4533
  }
4478
4534
  });
4479
4535
 
@@ -6982,6 +7038,7 @@ function parseOpenUrl(input) {
6982
7038
  }
6983
7039
  const assignmentVals = url.searchParams.getAll("assignment");
6984
7040
  const sessionVals = url.searchParams.getAll("session");
7041
+ const standaloneVals = url.searchParams.getAll("standalone");
6985
7042
  if (assignmentVals.length > 1) {
6986
7043
  throw new OpenUrlError(
6987
7044
  "duplicate-param",
@@ -6994,10 +7051,17 @@ function parseOpenUrl(input) {
6994
7051
  "URL has more than one `session` query param"
6995
7052
  );
6996
7053
  }
6997
- if (assignmentVals.length === 1 && sessionVals.length === 1) {
7054
+ if (standaloneVals.length > 1) {
7055
+ throw new OpenUrlError(
7056
+ "duplicate-param",
7057
+ "URL has more than one `standalone` query param"
7058
+ );
7059
+ }
7060
+ const kindsPresent = assignmentVals.length + sessionVals.length + standaloneVals.length;
7061
+ if (kindsPresent > 1) {
6998
7062
  throw new OpenUrlError(
6999
7063
  "both-ids",
7000
- "URL has both `assignment` and `session` query params \u2014 only one is allowed"
7064
+ "URL has more than one of `assignment`, `session`, `standalone` \u2014 only one is allowed"
7001
7065
  );
7002
7066
  }
7003
7067
  const terminalVals = url.searchParams.getAll("terminal");
@@ -7047,6 +7111,37 @@ function parseOpenUrl(input) {
7047
7111
  }
7048
7112
  prompt = value;
7049
7113
  }
7114
+ const agentNameVals = url.searchParams.getAll("agentName");
7115
+ if (agentNameVals.length > 1) {
7116
+ throw new OpenUrlError(
7117
+ "duplicate-param",
7118
+ "URL has more than one `agentName` query param"
7119
+ );
7120
+ }
7121
+ let agentName;
7122
+ if (agentNameVals.length === 1 && agentNameVals[0].trim() !== "") {
7123
+ const value = agentNameVals[0];
7124
+ if (value.length > MAX_OPEN_PROMPT_LENGTH) {
7125
+ throw new OpenUrlError(
7126
+ "invalid-prompt",
7127
+ `\`agentName\` query param exceeds ${MAX_OPEN_PROMPT_LENGTH} characters`
7128
+ );
7129
+ }
7130
+ agentName = value;
7131
+ }
7132
+ if (standaloneVals.length === 1) {
7133
+ const id = standaloneVals[0];
7134
+ if (id.trim() === "") {
7135
+ throw new OpenUrlError("missing-id", "`standalone` query param is empty");
7136
+ }
7137
+ return {
7138
+ kind: "standalone",
7139
+ id,
7140
+ ...terminal ? { terminal } : {},
7141
+ // standalone identifies the agent by `id`; keep an optional prompt override.
7142
+ ...prompt !== void 0 ? { prompt } : {}
7143
+ };
7144
+ }
7050
7145
  if (assignmentVals.length === 1) {
7051
7146
  const id = assignmentVals[0];
7052
7147
  if (id.trim() === "") {
@@ -7061,7 +7156,8 @@ function parseOpenUrl(input) {
7061
7156
  ...terminal ? { terminal } : {},
7062
7157
  ...agent ? { agent } : {},
7063
7158
  // assignment-only; keep '' (presence-significant) — hence !== undefined.
7064
- ...prompt !== void 0 ? { prompt } : {}
7159
+ ...prompt !== void 0 ? { prompt } : {},
7160
+ ...agentName ? { agentName } : {}
7065
7161
  };
7066
7162
  }
7067
7163
  if (sessionVals.length === 1) {
@@ -7097,7 +7193,7 @@ function parseOpenUrl(input) {
7097
7193
  }
7098
7194
  throw new OpenUrlError(
7099
7195
  "missing-id",
7100
- "URL must include either `assignment=<id>` or `session=<id>`"
7196
+ "URL must include one of `assignment=<id>`, `session=<id>`, or `standalone=<agentId>`"
7101
7197
  );
7102
7198
  }
7103
7199
 
@@ -7129,8 +7225,19 @@ function resolveTemplate(template, ctx) {
7129
7225
  const warnings = [];
7130
7226
  const prompt = template.replace(TOKEN_RE, (_match, boundary, token) => {
7131
7227
  if (token === "assignment") {
7228
+ if (!ctx.assignmentDir) {
7229
+ warnings.push(`launchPrompt: "@assignment" has no assignment context here \u2014 left as literal text.`);
7230
+ return boundary + "@assignment";
7231
+ }
7132
7232
  return boundary + assignmentPointer(ctx.id, ctx.assignmentDir);
7133
7233
  }
7234
+ if (token === "worktree") {
7235
+ if (!ctx.worktreePath) {
7236
+ warnings.push(`launchPrompt: "@worktree" has no worktree path here \u2014 left as literal text.`);
7237
+ return boundary + "@worktree";
7238
+ }
7239
+ return boundary + ctx.worktreePath;
7240
+ }
7134
7241
  if (!isValidSlug(token)) {
7135
7242
  warnings.push(`launchPrompt: "@${token}" is not a valid playbook token \u2014 left as literal text.`);
7136
7243
  return boundary + "@" + token;
@@ -7144,16 +7251,37 @@ function resolveTemplate(template, ctx) {
7144
7251
  return { prompt, warnings };
7145
7252
  }
7146
7253
  function resolveLaunchPrompt(input) {
7147
- const { template, playbook, id, assignmentDir, projectSlug, assignmentSlug, knownPlaybookSlugs } = input;
7254
+ const {
7255
+ template,
7256
+ playbook,
7257
+ id,
7258
+ assignmentDir,
7259
+ projectSlug,
7260
+ assignmentSlug,
7261
+ worktreePath,
7262
+ spawnCwd,
7263
+ knownPlaybookSlugs
7264
+ } = input;
7148
7265
  if (template && template.trim()) {
7149
- return resolveTemplate(template, { id, assignmentDir, knownPlaybookSlugs });
7266
+ return resolveTemplate(template, { id, assignmentDir, worktreePath, knownPlaybookSlugs });
7150
7267
  }
7151
7268
  const pb = playbook?.trim();
7152
7269
  if (pb) {
7153
- const pointer = assignmentPointer(id, assignmentDir);
7154
- return { prompt: `${pointer} Run ${runPlaybookClause(pb)} end-to-end.`, warnings: [] };
7270
+ const clause = runPlaybookClause(pb);
7271
+ const prompt = assignmentDir ? `${assignmentPointer(id, assignmentDir)} Run ${clause} end-to-end.` : `Run ${clause} end-to-end.`;
7272
+ return { prompt, warnings: [] };
7155
7273
  }
7156
- return { prompt: bareGrabSeed({ projectSlug, assignmentSlug, id }), warnings: [] };
7274
+ if (worktreePath && spawnCwd && worktreePath !== spawnCwd) {
7275
+ const grab = bareGrabSeed({ projectSlug, assignmentSlug: assignmentSlug ?? "", id });
7276
+ return {
7277
+ prompt: `The assignment's worktree is at ${worktreePath}. cd there, then grab the assignment with ${grab} and work it end-to-end.`,
7278
+ warnings: []
7279
+ };
7280
+ }
7281
+ if (!assignmentSlug && !id) {
7282
+ return { prompt: "", warnings: [] };
7283
+ }
7284
+ return { prompt: bareGrabSeed({ projectSlug, assignmentSlug: assignmentSlug ?? "", id }), warnings: [] };
7157
7285
  }
7158
7286
  function effectiveLaunchTemplate(input) {
7159
7287
  if (input.launchPrompt && input.launchPrompt.trim()) {
@@ -7163,6 +7291,14 @@ function effectiveLaunchTemplate(input) {
7163
7291
  if (pb) {
7164
7292
  return `@assignment Run ${runPlaybookClause(pb)} end-to-end.`;
7165
7293
  }
7294
+ if (input.workdir && input.workdir.trim()) {
7295
+ const grab = bareGrabSeed({
7296
+ projectSlug: input.projectSlug,
7297
+ assignmentSlug: input.assignmentSlug,
7298
+ id: input.id
7299
+ });
7300
+ return `The assignment's worktree is at @worktree. cd there, then grab the assignment with ${grab} and work it end-to-end.`;
7301
+ }
7166
7302
  return bareGrabSeed({
7167
7303
  projectSlug: input.projectSlug,
7168
7304
  assignmentSlug: input.assignmentSlug,
@@ -7196,8 +7332,10 @@ function shellQuote(arg) {
7196
7332
  }
7197
7333
  function buildAgentArgv(agent, prompt, env = process.env) {
7198
7334
  const position = agent.promptArgPosition ?? "first";
7199
- const baseArgs = applyModelFlag(agent, [...agent.args ?? []]);
7200
- const agentArgs = position === "first" ? [prompt, ...baseArgs] : position === "last" ? [...baseArgs, prompt] : baseArgs;
7335
+ const prefix = agentNameArgs(agent);
7336
+ const baseArgs = agent.agentName ? [...agent.args ?? []] : applyModelFlag(agent, [...agent.args ?? []]);
7337
+ const positioned = position === "first" ? [prompt, ...baseArgs] : position === "last" ? [...baseArgs, prompt] : baseArgs;
7338
+ const agentArgs = [...prefix, ...positioned];
7201
7339
  if (agent.resolveFromShellAliases) {
7202
7340
  const requested = env.SHELL;
7203
7341
  let shell = requested;
@@ -7277,8 +7415,60 @@ async function resolveLaunchPlan(input) {
7277
7415
  if (input.kind === "assignment") {
7278
7416
  return resolveAssignmentPlan(input, terminal);
7279
7417
  }
7418
+ if (input.kind === "standalone") {
7419
+ return resolveStandalonePlan(input, terminal);
7420
+ }
7280
7421
  return resolveSessionPlan(input, terminal);
7281
7422
  }
7423
+ async function resolveStandalonePlan(input, terminal) {
7424
+ const agentId = input.agentId ?? input.id;
7425
+ if (!agentId) {
7426
+ throw new LaunchError(
7427
+ "agent-not-configured",
7428
+ "A standalone launch requires an agent id."
7429
+ );
7430
+ }
7431
+ const agent = getAgents(input.config).find((a) => a.id === agentId);
7432
+ if (!agent) {
7433
+ throw new LaunchError(
7434
+ "agent-not-configured",
7435
+ `Agent "${agentId}" requested for a standalone launch is not in your agents list.`
7436
+ );
7437
+ }
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)) {
7447
+ throw new LaunchError(
7448
+ "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.`
7450
+ );
7451
+ }
7452
+ const knownPlaybookSlugs = await listPlaybookSlugs(playbooksDir());
7453
+ const template = input.promptOverride !== void 0 ? input.promptOverride : agent.launchPrompt;
7454
+ const { prompt, warnings: promptWarnings } = resolveLaunchPrompt({
7455
+ template,
7456
+ playbook: agent.playbook,
7457
+ projectSlug: null,
7458
+ knownPlaybookSlugs
7459
+ });
7460
+ const { argv, shellFallbackWarning } = buildFreshArgv(agent, prompt);
7461
+ return {
7462
+ terminal,
7463
+ cwd: expanded,
7464
+ argv,
7465
+ env: process.env,
7466
+ agentId: agent.id,
7467
+ fallbackWarning: null,
7468
+ shellFallbackWarning,
7469
+ promptWarnings
7470
+ };
7471
+ }
7282
7472
  async function resolveAssignmentPlan(input, terminal) {
7283
7473
  const resolved = await resolveAssignmentById(
7284
7474
  input.projectsDir,
@@ -7302,17 +7492,6 @@ async function resolveAssignmentPlan(input, terminal) {
7302
7492
  `Assignment ${input.id} resolver returned a directory but detail could not be loaded`
7303
7493
  );
7304
7494
  }
7305
- const picked = resolveWorkspaceCwd({
7306
- worktreePath: detail.workspace.worktreePath,
7307
- repository: detail.workspace.repository,
7308
- branch: detail.workspace.branch,
7309
- assignmentSlug: resolved.assignmentSlug
7310
- });
7311
- if (picked.cwd === null) {
7312
- throw new LaunchError("workspace-path-invalid", picked.invalidReason);
7313
- }
7314
- const cwd = picked.cwd;
7315
- const fallbackWarning = picked.fallbackWarning;
7316
7495
  let agent;
7317
7496
  if (input.agentId) {
7318
7497
  const found = getAgents(input.config).find((a) => a.id === input.agentId);
@@ -7326,6 +7505,30 @@ async function resolveAssignmentPlan(input, terminal) {
7326
7505
  } else {
7327
7506
  agent = pickAgent(input.config);
7328
7507
  }
7508
+ if (input.agentName !== void 0) {
7509
+ agent = { ...agent, agentName: input.agentName };
7510
+ }
7511
+ const picked = resolveWorkspaceCwd({
7512
+ worktreePath: detail.workspace.worktreePath,
7513
+ repository: detail.workspace.repository,
7514
+ branch: detail.workspace.branch,
7515
+ assignmentSlug: resolved.assignmentSlug
7516
+ });
7517
+ if (picked.cwd === null) {
7518
+ throw new LaunchError("workspace-path-invalid", picked.invalidReason);
7519
+ }
7520
+ const launchCwd = resolveLaunchCwd(agent, picked.cwd);
7521
+ if (launchCwd.invalidReason) {
7522
+ throw new LaunchError("workspace-path-invalid", launchCwd.invalidReason);
7523
+ }
7524
+ const cwd = launchCwd.spawnCwd;
7525
+ const fallbackWarning = picked.fallbackWarning;
7526
+ const promptWarningsExtra = [];
7527
+ if (input.agentName !== void 0 && input.agentName.trim() && agent.model?.trim()) {
7528
+ promptWarningsExtra.push(
7529
+ `profile model "${agent.model.trim()}" suppressed: agent "${input.agentName.trim()}" defines its own model`
7530
+ );
7531
+ }
7329
7532
  const knownPlaybookSlugs = await listPlaybookSlugs(playbooksDir());
7330
7533
  const template = input.promptOverride !== void 0 ? input.promptOverride : agent.launchPrompt;
7331
7534
  const { prompt, warnings: promptWarnings } = resolveLaunchPrompt({
@@ -7335,6 +7538,8 @@ async function resolveAssignmentPlan(input, terminal) {
7335
7538
  assignmentDir: resolved.assignmentDir,
7336
7539
  projectSlug: resolved.projectSlug,
7337
7540
  assignmentSlug: resolved.assignmentSlug,
7541
+ worktreePath: launchCwd.worktreePath,
7542
+ spawnCwd: launchCwd.spawnCwd,
7338
7543
  knownPlaybookSlugs
7339
7544
  });
7340
7545
  const { argv, shellFallbackWarning } = buildFreshArgv(agent, prompt);
@@ -7346,7 +7551,7 @@ async function resolveAssignmentPlan(input, terminal) {
7346
7551
  agentId: agent.id,
7347
7552
  fallbackWarning,
7348
7553
  shellFallbackWarning,
7349
- promptWarnings
7554
+ promptWarnings: [...promptWarningsExtra, ...promptWarnings]
7350
7555
  };
7351
7556
  }
7352
7557
  async function resolveSessionPlan(input, terminal) {