@bastani/atomic 0.9.5-alpha.8 → 0.9.5-alpha.9

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 (207) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/builtin/cursor/package.json +2 -2
  3. package/dist/builtin/intercom/package.json +1 -1
  4. package/dist/builtin/mcp/CHANGELOG.md +11 -0
  5. package/dist/builtin/mcp/README.md +6 -6
  6. package/dist/builtin/mcp/direct-tools.ts +41 -23
  7. package/dist/builtin/mcp/index.ts +31 -4
  8. package/dist/builtin/mcp/init.ts +4 -43
  9. package/dist/builtin/mcp/metadata-hydration.ts +37 -0
  10. package/dist/builtin/mcp/package.json +1 -1
  11. package/dist/builtin/mcp/proxy-info-modes.ts +67 -14
  12. package/dist/builtin/mcp/startup-warmup.ts +98 -0
  13. package/dist/builtin/mcp/tool-result-renderer.ts +3 -3
  14. package/dist/builtin/subagents/CHANGELOG.md +12 -0
  15. package/dist/builtin/subagents/package.json +1 -1
  16. package/dist/builtin/subagents/src/extension/index.ts +14 -25
  17. package/dist/builtin/subagents/src/extension/startup-maintenance.ts +89 -0
  18. package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +23 -18
  19. package/dist/builtin/subagents/src/runs/background/result-watcher.ts +20 -10
  20. package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +13 -3
  21. package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +10 -2
  22. package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +28 -1
  23. package/dist/builtin/subagents/src/tui/render-widget.ts +29 -0
  24. package/dist/builtin/web-access/package.json +1 -1
  25. package/dist/builtin/workflows/CHANGELOG.md +18 -0
  26. package/dist/builtin/workflows/README.md +1 -1
  27. package/dist/builtin/workflows/builtin/goal-runner.ts +39 -1
  28. package/dist/builtin/workflows/package.json +1 -1
  29. package/dist/builtin/workflows/skills/impeccable/SKILL.md +16 -13
  30. package/dist/builtin/workflows/skills/impeccable/reference/adapt.md +1 -0
  31. package/dist/builtin/workflows/skills/impeccable/reference/adapt.native.md +58 -0
  32. package/dist/builtin/workflows/skills/impeccable/reference/android.md +40 -0
  33. package/dist/builtin/workflows/skills/impeccable/reference/animate.md +2 -0
  34. package/dist/builtin/workflows/skills/impeccable/reference/audit.md +2 -0
  35. package/dist/builtin/workflows/skills/impeccable/reference/audit.native.md +139 -0
  36. package/dist/builtin/workflows/skills/impeccable/reference/bolder.md +69 -62
  37. package/dist/builtin/workflows/skills/impeccable/reference/critique.md +21 -7
  38. package/dist/builtin/workflows/skills/impeccable/reference/document.md +1 -1
  39. package/dist/builtin/workflows/skills/impeccable/reference/hooks.md +92 -0
  40. package/dist/builtin/workflows/skills/impeccable/reference/init.md +29 -7
  41. package/dist/builtin/workflows/skills/impeccable/reference/ios.md +45 -0
  42. package/dist/builtin/workflows/skills/impeccable/reference/layout.md +25 -1
  43. package/dist/builtin/workflows/skills/impeccable/reference/typeset.md +23 -1
  44. package/dist/builtin/workflows/skills/impeccable/scripts/context-signals.mjs +2 -1
  45. package/dist/builtin/workflows/skills/impeccable/scripts/context.mjs +75 -14
  46. package/dist/builtin/workflows/skills/impeccable/scripts/detector/browser/injected/index.mjs +1 -1
  47. package/dist/builtin/workflows/skills/impeccable/scripts/detector/cli/main.mjs +31 -0
  48. package/dist/builtin/workflows/skills/impeccable/scripts/detector/design-system.mjs +64 -0
  49. package/dist/builtin/workflows/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +118 -11
  50. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +16 -11
  51. package/dist/builtin/workflows/skills/impeccable/scripts/detector/registry/antipatterns.mjs +66 -0
  52. package/dist/builtin/workflows/skills/impeccable/scripts/detector/rules/checks.mjs +42 -10
  53. package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/fonts.mjs +30 -0
  54. package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/page.mjs +4 -6
  55. package/dist/builtin/workflows/skills/impeccable/scripts/hook-admin.mjs +660 -0
  56. package/dist/builtin/workflows/skills/impeccable/scripts/hook-before-edit.mjs +516 -0
  57. package/dist/builtin/workflows/skills/impeccable/scripts/hook-lib.mjs +1764 -0
  58. package/dist/builtin/workflows/skills/impeccable/scripts/hook.mjs +61 -0
  59. package/dist/builtin/workflows/skills/impeccable/scripts/lib/impeccable-config.mjs +4 -2
  60. package/dist/builtin/workflows/skills/impeccable/scripts/lib/is-generated.mjs +2 -2
  61. package/dist/builtin/workflows/skills/impeccable/scripts/live/svelte-component.mjs +13 -16
  62. package/dist/builtin/workflows/skills/impeccable/scripts/live-accept.mjs +17 -11
  63. package/dist/builtin/workflows/skills/impeccable/scripts/live-browser.js +104 -37
  64. package/dist/builtin/workflows/skills/impeccable/scripts/live-wrap.mjs +9 -0
  65. package/dist/builtin/workflows/src/durable/backend.ts +12 -0
  66. package/dist/builtin/workflows/src/durable/dbos-backend.ts +17 -1
  67. package/dist/builtin/workflows/src/durable/resume-catalog.ts +12 -0
  68. package/dist/builtin/workflows/src/durable/resume-runtime.ts +5 -2
  69. package/dist/builtin/workflows/src/durable/types.ts +16 -0
  70. package/dist/builtin/workflows/src/engine/primitives/task.ts +1 -0
  71. package/dist/builtin/workflows/src/engine/run-durable-finalize.ts +1 -2
  72. package/dist/builtin/workflows/src/engine/run-returned-status.ts +50 -16
  73. package/dist/builtin/workflows/src/engine/run.ts +20 -19
  74. package/dist/builtin/workflows/src/engine/runtime.ts +5 -0
  75. package/dist/builtin/workflows/src/extension/extension-factory.ts +3 -0
  76. package/dist/builtin/workflows/src/extension/extension-lifecycle.ts +9 -2
  77. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +137 -27
  78. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +22 -6
  79. package/dist/builtin/workflows/src/extension/public-types.ts +1 -1
  80. package/dist/builtin/workflows/src/extension/workflow-command-completions.ts +13 -1
  81. package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +19 -4
  82. package/dist/builtin/workflows/src/extension/workflow-command-surfaces.ts +9 -0
  83. package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +48 -23
  84. package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +20 -8
  85. package/dist/builtin/workflows/src/extension/workflow-tool.ts +24 -8
  86. package/dist/builtin/workflows/src/runs/background/status.ts +4 -3
  87. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +29 -8
  88. package/dist/builtin/workflows/src/runs/foreground/executor-hil.ts +2 -1
  89. package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +3 -2
  90. package/dist/builtin/workflows/src/runs/shared/worktree-git.ts +155 -20
  91. package/dist/builtin/workflows/src/runs/shared/worktree-types.ts +6 -0
  92. package/dist/builtin/workflows/src/runs/shared/worktree.ts +2 -1
  93. package/dist/builtin/workflows/src/shared/returned-run-status.ts +116 -0
  94. package/dist/builtin/workflows/src/shared/workflow-failures-decisions.ts +2 -0
  95. package/dist/builtin/workflows/src/tui/graph-view-render-helpers.ts +21 -3
  96. package/dist/builtin/workflows/src/tui/graph-view-state.ts +3 -0
  97. package/dist/builtin/workflows/src/tui/graph-view-types.ts +3 -0
  98. package/dist/builtin/workflows/src/tui/status-list.ts +22 -15
  99. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +4 -4
  100. package/dist/core/agent-session-auto-compaction.d.ts +23 -3
  101. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  102. package/dist/core/agent-session-auto-compaction.js +129 -17
  103. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  104. package/dist/core/agent-session-events.d.ts.map +1 -1
  105. package/dist/core/agent-session-events.js +7 -0
  106. package/dist/core/agent-session-events.js.map +1 -1
  107. package/dist/core/agent-session-methods.d.ts +6 -3
  108. package/dist/core/agent-session-methods.d.ts.map +1 -1
  109. package/dist/core/agent-session-methods.js.map +1 -1
  110. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  111. package/dist/core/agent-session-prompt.js +1 -1
  112. package/dist/core/agent-session-prompt.js.map +1 -1
  113. package/dist/core/agent-session.d.ts +4 -2
  114. package/dist/core/agent-session.d.ts.map +1 -1
  115. package/dist/core/agent-session.js +4 -2
  116. package/dist/core/agent-session.js.map +1 -1
  117. package/dist/core/extensions/loader-virtual-modules.d.ts +8 -1
  118. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  119. package/dist/core/extensions/loader-virtual-modules.js +2 -1
  120. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  121. package/dist/core/footer-data-provider.d.ts +6 -0
  122. package/dist/core/footer-data-provider.d.ts.map +1 -1
  123. package/dist/core/footer-data-provider.js +72 -29
  124. package/dist/core/footer-data-provider.js.map +1 -1
  125. package/dist/core/openai-responses-payload-sanitizer.d.ts +1 -0
  126. package/dist/core/openai-responses-payload-sanitizer.d.ts.map +1 -1
  127. package/dist/core/openai-responses-payload-sanitizer.js +24 -9
  128. package/dist/core/openai-responses-payload-sanitizer.js.map +1 -1
  129. package/dist/core/slash-commands.d.ts +2 -0
  130. package/dist/core/slash-commands.d.ts.map +1 -1
  131. package/dist/core/slash-commands.js +141 -0
  132. package/dist/core/slash-commands.js.map +1 -1
  133. package/dist/index.d.ts +1 -0
  134. package/dist/index.d.ts.map +1 -1
  135. package/dist/index.js +1 -0
  136. package/dist/index.js.map +1 -1
  137. package/dist/main-deferred-startup.d.ts.map +1 -1
  138. package/dist/main-deferred-startup.js +2 -2
  139. package/dist/main-deferred-startup.js.map +1 -1
  140. package/dist/main-early-input.d.ts.map +1 -1
  141. package/dist/main-early-input.js +7 -0
  142. package/dist/main-early-input.js.map +1 -1
  143. package/dist/main.d.ts.map +1 -1
  144. package/dist/main.js +1 -1
  145. package/dist/main.js.map +1 -1
  146. package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
  147. package/dist/modes/interactive/components/chat-session-host-actions.js +0 -1
  148. package/dist/modes/interactive/components/chat-session-host-actions.js.map +1 -1
  149. package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
  150. package/dist/modes/interactive/interactive-agent-events.js +1 -0
  151. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  152. package/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
  153. package/dist/modes/interactive/interactive-autocomplete.js +21 -9
  154. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  155. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  156. package/dist/modes/interactive/interactive-deferred-startup.js +41 -28
  157. package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
  158. package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
  159. package/dist/modes/interactive/interactive-hotkeys-debug.js +1 -0
  160. package/dist/modes/interactive/interactive-hotkeys-debug.js.map +1 -1
  161. package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
  162. package/dist/modes/interactive/interactive-input-handling.js +41 -6
  163. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  164. package/dist/modes/interactive/interactive-mode-base.d.ts +3 -0
  165. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  166. package/dist/modes/interactive/interactive-mode-base.js +3 -0
  167. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  168. package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -1
  169. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  170. package/dist/modes/interactive/interactive-mode-deps.js +1 -1
  171. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  172. package/dist/modes/interactive/interactive-mode-surface.d.ts +2 -2
  173. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  174. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  175. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  176. package/dist/modes/interactive/interactive-model-routing.js +3 -1
  177. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  178. package/dist/modes/interactive/interactive-process-lifecycle.d.ts.map +1 -1
  179. package/dist/modes/interactive/interactive-process-lifecycle.js +0 -3
  180. package/dist/modes/interactive/interactive-process-lifecycle.js.map +1 -1
  181. package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
  182. package/dist/modes/interactive/interactive-render-chat.js +15 -1
  183. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  184. package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
  185. package/dist/modes/interactive/interactive-session-routing.js +5 -1
  186. package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
  187. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  188. package/dist/modes/interactive/interactive-startup.js +20 -21
  189. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  190. package/dist/modes/interactive/theme/global-theme.d.ts.map +1 -1
  191. package/dist/modes/interactive/theme/global-theme.js +26 -2
  192. package/dist/modes/interactive/theme/global-theme.js.map +1 -1
  193. package/dist/utils/fs-watch.d.ts +24 -1
  194. package/dist/utils/fs-watch.d.ts.map +1 -1
  195. package/dist/utils/fs-watch.js +60 -5
  196. package/dist/utils/fs-watch.js.map +1 -1
  197. package/docs/compaction.md +5 -1
  198. package/docs/development.md +4 -0
  199. package/docs/extensions.md +5 -0
  200. package/docs/json.md +1 -1
  201. package/docs/quickstart.md +1 -1
  202. package/docs/settings.md +1 -1
  203. package/docs/subagents.md +4 -0
  204. package/docs/windows.md +4 -0
  205. package/docs/workflows.md +8 -6
  206. package/npm-shrinkwrap.json +23 -23
  207. package/package.json +2 -2
@@ -12,7 +12,7 @@ import type { GraphOverlayPort } from "../tui/overlay-adapter.js";
12
12
  import type { ExtensionRuntime } from "./runtime.js";
13
13
  import type { WorkflowInputEntry, WorkflowToolResult } from "./render-result.js";
14
14
  import type { ExtensionAPI, PiCommandContext, PiArgumentCompletionResult } from "./public-types.js";
15
- import { workflowArgumentCompletions } from "./workflow-command-completions.js";
15
+ import { workflowArgumentCompletions, workflowArgumentCompletionsNeedWorkflowResources } from "./workflow-command-completions.js";
16
16
  import {
17
17
  createWorkflowCommandReporter,
18
18
  emitWorkflowCommandOutput,
@@ -23,7 +23,7 @@ import {
23
23
  type WorkflowCommandHandler,
24
24
  type WorkflowCommandOutputDetails,
25
25
  } from "./workflow-command-utils.js";
26
- import { emitTerminalRunDetailSurface } from "./workflow-command-surfaces.js";
26
+ import { emitTerminalRunDetailSurface, formatWorkflowResourceLoadWarning } from "./workflow-command-surfaces.js";
27
27
  import { handleRunControlCommand, type WorkflowRunControlDeps } from "./workflow-run-control-command.js";
28
28
  import { workflowPolicyFromContext } from "./workflow-policy.js";
29
29
  import {
@@ -39,6 +39,7 @@ export interface WorkflowSlashCommandDeps {
39
39
  runtimeForContext: (ctx?: PiCommandContext) => ExtensionRuntime;
40
40
  overlay: GraphOverlayPort;
41
41
  reloadWorkflowResources: () => Promise<void> | void;
42
+ ensureWorkflowResourcesLoaded: () => Promise<void> | void;
42
43
  runWithLifecycleSuppressedForPolicy: <T>(
43
44
  policy: WorkflowExecutionPolicy,
44
45
  fn: () => Promise<T>,
@@ -57,8 +58,11 @@ export function registerWorkflowSlashCommand(
57
58
  {
58
59
  description: "Run or inspect Atomic workflows. Usage: /workflow <name> [key=value…] | /workflow [list|status|connect|attach|interrupt|kill|pause|resume|inputs|reload] [args]",
59
60
  handler: (args, ctx) => workflowSlashHandler(args, ctx, pi, deps),
60
- getArgumentCompletions: (partial: string): PiArgumentCompletionResult =>
61
- workflowArgumentCompletions(partial, deps.runtimeProxy),
61
+ getArgumentCompletions: (partial: string): PiArgumentCompletionResult | Promise<PiArgumentCompletionResult> => {
62
+ const buildCompletions = (): PiArgumentCompletionResult => workflowArgumentCompletions(partial, deps.runtimeProxy);
63
+ if (!workflowArgumentCompletionsNeedWorkflowResources(partial)) return buildCompletions();
64
+ return Promise.resolve(deps.ensureWorkflowResourcesLoaded()).then(buildCompletions).catch(buildCompletions);
65
+ },
62
66
  },
63
67
  workflowCommands,
64
68
  );
@@ -76,10 +80,18 @@ async function workflowSlashHandler(
76
80
  const fail = (msg: string): void => reporter.error(msg);
77
81
  const withImplicitYesFlag = (tokens: string[]): string[] =>
78
82
  tokens.some((t) => t === "--yes" || t === "-y") ? tokens : [...tokens, "-y"];
83
+ const ensureWorkflowResourcesVisible = async (): Promise<void> => {
84
+ try {
85
+ await deps.ensureWorkflowResourcesLoaded();
86
+ } catch (error) {
87
+ ctx.ui?.notify(formatWorkflowResourceLoadWarning(error), "warning");
88
+ }
89
+ };
79
90
  const showWorkflowInputs = async (
80
91
  workflowName: string,
81
92
  command: WorkflowCommandOutputDetails["command"] = "inputs",
82
93
  ): Promise<void> => {
94
+ await ensureWorkflowResourcesVisible();
83
95
  const result = await deps.runtimeForContext(ctx).dispatch({ workflow: workflowName, inputs: {}, action: "inputs" }, { policy });
84
96
  if (result.action !== "inputs" || !("inputs" in result)) return;
85
97
  const inputResult = result as Extract<WorkflowToolResult, { action: "inputs" }>;
@@ -99,6 +111,7 @@ async function workflowSlashHandler(
99
111
  return;
100
112
  }
101
113
  if (!subcommand || subcommand === "list") {
114
+ await ensureWorkflowResourcesVisible();
102
115
  const items = deps.runtimeProxy.registry.all().map((def) => ({
103
116
  name: def.normalizedName,
104
117
  description: def.description,
@@ -163,6 +176,7 @@ async function workflowSlashHandler(
163
176
  typeof ctx.ui?.setEditorComponent === "function" || typeof ctx.ui?.custom === "function"
164
177
  );
165
178
  if (canOpenPicker) {
179
+ await ensureWorkflowResourcesVisible();
166
180
  const schemaResult = await deps.runtimeForContext(ctx).dispatch({ workflow: workflowName, inputs: {}, action: "inputs" }, { policy });
167
181
  const schema = schemaResult.action === "inputs" && "inputs" in schemaResult
168
182
  ? (schemaResult as Extract<WorkflowToolResult, { action: "inputs" }>)
@@ -185,6 +199,7 @@ async function workflowSlashHandler(
185
199
  }
186
200
  }
187
201
 
202
+ await ensureWorkflowResourcesVisible();
188
203
  const result = await deps.runWithLifecycleSuppressedForPolicy(policy, () =>
189
204
  deps.runtimeForContext(ctx).dispatch({ workflow: workflowName, inputs: mergedInputs, action: "run" }, { policy }),
190
205
  );
@@ -45,6 +45,15 @@ export function emitTerminalRunDetailSurface(
45
45
  emitChatSurface(pi, { kind: "detail", detail }, { content: renderRunDetail(detail, { width: 100 }) });
46
46
  }
47
47
 
48
+
49
+ export function formatWorkflowResourceLoadWarning(error: unknown): string {
50
+ const message = error instanceof Error ? error.message : String(error);
51
+ return [
52
+ "Workflow discovery diagnostics: workflow resources could not be fully refreshed.",
53
+ `- [error DISCOVERY_FAILED] workflow discovery: ${message}`,
54
+ "Using the currently loaded workflow registry; run `/workflow reload` after fixing the issue.",
55
+ ].join("\n");
56
+ }
48
57
  export function formatStartupDiagnostics(
49
58
  configResult: ConfigLoadResult | null,
50
59
  discoveryResult: DiscoveryResult | null,
@@ -22,12 +22,14 @@ import {
22
22
  resolveRunIdPrefix,
23
23
  resolveStageTarget,
24
24
  } from "./workflow-targets.js";
25
+ import { formatWorkflowResourceLoadWarning } from "./workflow-command-surfaces.js";
25
26
 
26
27
  export interface WorkflowRunControlDeps {
27
28
  pi: ExtensionAPI;
28
29
  overlay: GraphOverlayPort;
29
30
  getPersistence: () => WorkflowPersistencePort | undefined;
30
31
  runtimeForContext: (ctx?: PiCommandContext) => ExtensionRuntime;
32
+ ensureWorkflowResourcesLoaded: () => Promise<void> | void;
31
33
  }
32
34
 
33
35
  function resolveAttachStageId(runId: string, stageTarget: string | undefined): string | undefined | false {
@@ -61,6 +63,11 @@ async function handleDurableResume(
61
63
  ): Promise<boolean> {
62
64
  const print = (msg: string): void => reporter.info(msg);
63
65
  const fail = (msg: string): void => reporter.error(msg);
66
+ try {
67
+ await deps.ensureWorkflowResourcesLoaded();
68
+ } catch (error) {
69
+ ctx.ui?.notify(formatWorkflowResourceLoadWarning(error), "warning");
70
+ }
64
71
  const runtime = deps.runtimeForContext(ctx);
65
72
  const policy = workflowPolicyFromContext(ctx);
66
73
  // Hydrate the durable backend from DBOS (if configured) before listing so a
@@ -121,6 +128,13 @@ export async function handleRunControlCommand(
121
128
  const fail = (msg: string): void => reporter.error(msg);
122
129
  const canOpenPicker = (ui: PiCommandContext["ui"] | undefined): boolean =>
123
130
  policy.allowInputPicker && typeof ui?.custom === "function";
131
+ const ensureWorkflowResourcesVisible = async (): Promise<void> => {
132
+ try {
133
+ await deps.ensureWorkflowResourcesLoaded();
134
+ } catch (error) {
135
+ ctx.ui?.notify(formatWorkflowResourceLoadWarning(error), "warning");
136
+ }
137
+ };
124
138
  const confirmationPrompt = policy.allowHumanInput && typeof ctx.ui?.confirm === "function"
125
139
  ? ctx.ui.confirm.bind(ctx.ui)
126
140
  : undefined;
@@ -293,22 +307,23 @@ export async function handleRunControlCommand(
293
307
  const liveRuns = topLevelWorkflowRuns(store.runs()).filter((run) =>
294
308
  run.status === "paused" || (run.status === "failed" && run.resumable !== false),
295
309
  );
296
- // Durable entries: a `running` durable handle may be a crashed process
297
- // (cross-session crash recovery), so it stays selectable UNLESS it
298
- // matches an actively-executing live run in this session.
299
- const activeLiveIds = new Set(
300
- topLevelWorkflowRuns(store.runs())
301
- .filter((run) => run.endedAt === undefined && run.status === "running" && run.exitReason !== "quit")
302
- .map((run) => run.id),
303
- );
304
- const runtime = deps.runtimeForContext(ctx);
305
310
  let durableEntries: readonly ResumableWorkflowEntry[] = [];
306
- try {
307
- const prepared = await runtime.prepareDurableResumable(undefined);
308
- durableEntries = filterSelectorDurableEntries(runtime, prepared)
309
- .filter((entry) => !activeLiveIds.has(entry.workflowId));
310
- } catch (error) {
311
- if (liveRuns.length === 0) {
311
+ if (liveRuns.length === 0) {
312
+ // Durable entries: a `running` durable handle may be a crashed process
313
+ // (cross-session crash recovery), so it stays selectable UNLESS it
314
+ // matches an actively-executing live run in this session.
315
+ const activeLiveIds = new Set(
316
+ topLevelWorkflowRuns(store.runs())
317
+ .filter((run) => run.endedAt === undefined && run.status === "running" && run.exitReason !== "quit")
318
+ .map((run) => run.id),
319
+ );
320
+ await ensureWorkflowResourcesVisible();
321
+ const runtime = deps.runtimeForContext(ctx);
322
+ try {
323
+ const prepared = await runtime.prepareDurableResumable(undefined);
324
+ durableEntries = filterSelectorDurableEntries(runtime, prepared)
325
+ .filter((entry) => !activeLiveIds.has(entry.workflowId));
326
+ } catch (error) {
312
327
  const message = error instanceof Error ? error.message : String(error);
313
328
  fail(`Failed to list resumable workflows: ${message}`);
314
329
  return true;
@@ -326,11 +341,12 @@ export async function handleRunControlCommand(
326
341
  const isPaused = run?.status === "paused" || (run?.stages.some((s) => s.status === "paused") ?? false);
327
342
  const isResumableContinuation = run !== undefined && !isPaused && ((run.status === "failed" && run.endedAt !== undefined && run.resumable !== false) || (run.endedAt === undefined && run.resumable === true && run.failureRecoverability === "recoverable"));
328
343
  if (isResumableContinuation) {
344
+ await ensureWorkflowResourcesVisible();
329
345
  const continuation = deps.runtimeForContext(ctx).resumeFailedRun(resolved.runId, undefined, { policy });
330
346
  continuation.ok ? print(continuation.message) : fail(continuation.message);
331
347
  } else {
332
348
  const result = resumeRun(resolved.runId, {});
333
- if (result.ok && result.mode === "snapshot" && run?.exitReason === "quit") {
349
+ if (result.ok && !isPaused && result.mode === "snapshot" && run?.exitReason === "quit") {
334
350
  return await handleDurableResume(resolved.runId, ctx, reporter, deps);
335
351
  }
336
352
  if (result.ok && policy.allowInputPicker) deps.overlay.open(result.runId, overlaySurfaceFromContext(ctx));
@@ -389,7 +405,9 @@ export async function handleRunControlCommand(
389
405
  return true;
390
406
  }
391
407
  const run = store.runs().find((r) => r.id === stageRunId);
392
- const isPaused = run?.status === "paused" || (run?.stages.some((s) => s.status === "paused") ?? false);
408
+ const hadPausedRunState = run?.status === "paused";
409
+ const hadPausedStageState = run?.stages.some((s) => s.status === "paused") ?? false;
410
+ const isPaused = hadPausedRunState || hadPausedStageState;
393
411
  const isResumableContinuation = run !== undefined && !isPaused && ((run.status === "failed" && run.endedAt !== undefined && run.resumable !== false) || (run.endedAt === undefined && run.resumable === true && run.failureRecoverability === "recoverable"));
394
412
  const isActivelyRunning = run !== undefined && run.endedAt === undefined && run.status === "running" && !isPaused && run.exitReason !== "quit";
395
413
  if (isActivelyRunning && action === "resume") {
@@ -397,27 +415,34 @@ export async function handleRunControlCommand(
397
415
  return true;
398
416
  }
399
417
  if (isResumableContinuation) {
418
+ await ensureWorkflowResourcesVisible();
400
419
  const continuation = deps.runtimeForContext(ctx).resumeFailedRun(stageRunId, stageId, { policy });
401
420
  continuation.ok ? print(continuation.message) : fail(continuation.message);
402
421
  return true;
403
422
  }
423
+ // A quit, non-paused durable run is a resume shadow rather than a live
424
+ // stage-control pause. Routing directly to durable resume preserves the
425
+ // previous snapshot-only diversion without reopening a stale local overlay.
426
+ if (!isPaused && run?.exitReason === "quit" && action === "resume") {
427
+ return await handleDurableResume(stageRunId, ctx, reporter, deps);
428
+ }
404
429
  const result = resumeRun(stageRunId, { stageId, message });
405
430
  if (!result.ok) {
406
431
  fail(`Run not found: ${stageRunId.slice(0, 8)}`);
407
432
  return true;
408
433
  }
409
- if (result.mode === "snapshot" && run?.exitReason === "quit" && action === "resume") {
410
- return await handleDurableResume(stageRunId, ctx, reporter, deps);
411
- }
412
434
  if (!isPaused) {
413
435
  if (policy.allowInputPicker) deps.overlay.open(result.runId, overlaySurfaceFromContext(ctx));
414
436
  print(result.message ?? `Snapshot available: run ${result.runId} (${result.snapshot.name}) — status: ${result.snapshot.status}, stages: ${result.snapshot.stages.length}`);
415
437
  return true;
416
438
  }
417
439
  if (!message && stageId && policy.allowInputPicker) deps.overlay.open(runId, overlaySurfaceFromContext(ctx), stageId);
418
- result.resumed.length === 0
419
- ? fail(`No paused stages on run ${stageRunId.slice(0, 8)}.`)
420
- : print(`Resumed ${result.resumed.length} stage(s) on run ${stageRunId.slice(0, 8)}${message ? ` with message: "${message}"` : ""}.`);
440
+ if (result.resumed.length === 0) {
441
+ const runLevelResumed = hadPausedRunState && !hadPausedStageState && stageId === undefined && result.snapshot.status === "running";
442
+ runLevelResumed ? print(`Resumed run ${stageRunId.slice(0, 8)}.`) : fail(`No paused stages on run ${stageRunId.slice(0, 8)}.`);
443
+ } else {
444
+ print(`Resumed ${result.resumed.length} stage(s) on run ${stageRunId.slice(0, 8)}${message ? ` with message: "${message}"` : ""}.`);
445
+ }
421
446
  return true;
422
447
  }
423
448
 
@@ -24,12 +24,14 @@ import {
24
24
  resolveToolStageTarget,
25
25
  stageFailureMessage,
26
26
  } from "./workflow-targets.js";
27
+ import { formatWorkflowResourceLoadWarning } from "./workflow-command-surfaces.js";
27
28
 
28
29
  export interface WorkflowControlActionDeps {
29
30
  getPersistence: () => WorkflowPersistencePort | undefined;
30
31
  reloadWorkflowResources: () => Promise<void> | void;
31
- runtime: ExtensionRuntime;
32
+ getRuntime: () => ExtensionRuntime;
32
33
  policy: WorkflowExecutionPolicy;
34
+ ensureWorkflowResourcesLoaded: () => Promise<void> | void;
33
35
  }
34
36
 
35
37
  export function workflowPauseAction(args: WorkflowToolArgs): WorkflowToolResult {
@@ -166,10 +168,10 @@ export function workflowInterruptAction(args: WorkflowToolArgs): WorkflowToolRes
166
168
  };
167
169
  }
168
170
 
169
- export function workflowResumeAction(
171
+ export async function workflowResumeAction(
170
172
  args: WorkflowToolArgs,
171
- deps: Pick<WorkflowControlActionDeps, "runtime" | "policy">,
172
- ): WorkflowToolResult {
173
+ deps: Pick<WorkflowControlActionDeps, "getRuntime" | "policy" | "ensureWorkflowResourcesLoaded">,
174
+ ): Promise<WorkflowToolResult> {
173
175
  const target = resolveToolRunTarget(args, "No active run to resume.");
174
176
  if (target.kind === "all") return { action: "resume", runId: "--all", status: "noop", message: "Resume does not support --all." };
175
177
  if (target.kind === "ambiguous") return { action: "resume", runId: target.target, status: "noop", message: ambiguousRunMessage(target.target, target.matches) };
@@ -178,25 +180,35 @@ export function workflowResumeAction(
178
180
  if (!stage.ok) return { action: "resume", runId: target.runId, status: "noop", message: stage.message };
179
181
  const stageRunId = stage.runId ?? target.runId;
180
182
  const run = store.runs().find((r) => r.id === stageRunId);
181
- const isPaused = run?.status === "paused" || (run?.stages.some((s) => s.status === "paused") ?? false);
183
+ const hadPausedRunState = run?.status === "paused";
184
+ const hadPausedStageState = run?.stages.some((s) => s.status === "paused") ?? false;
185
+ const isPaused = hadPausedRunState || hadPausedStageState;
182
186
  const isResumableContinuation = run !== undefined && !isPaused && (
183
187
  (run.status === "failed" && run.endedAt !== undefined && run.resumable !== false) ||
184
188
  (run.endedAt === undefined && run.resumable === true && run.failureRecoverability === "recoverable")
185
189
  );
186
190
  if (isResumableContinuation) {
187
- const continuation = deps.runtime.resumeFailedRun(stageRunId, stage.stageId, { policy: deps.policy });
191
+ let warning: string | undefined;
192
+ try {
193
+ await deps.ensureWorkflowResourcesLoaded();
194
+ } catch (error) {
195
+ warning = formatWorkflowResourceLoadWarning(error);
196
+ }
197
+ const continuation = deps.getRuntime().resumeFailedRun(stageRunId, stage.stageId, { policy: deps.policy });
198
+ const message = warning === undefined ? continuation.message : `${warning}\n\n${continuation.message}`;
188
199
  return {
189
200
  action: "resume",
190
201
  runId: continuation.ok ? continuation.runId : stageRunId,
191
202
  status: continuation.ok ? "running" : "noop",
192
- message: continuation.message,
203
+ message,
193
204
  };
194
205
  }
195
206
  const result = resumeRun(stageRunId, { stageId: stage.stageId, message: args.message });
196
207
  if (result.ok) {
208
+ const runLevelResumed = hadPausedRunState && !hadPausedStageState && stage.stageId === undefined && result.snapshot.status === "running";
197
209
  const message = result.message ?? (isPaused
198
210
  ? result.resumed.length === 0
199
- ? `No paused stages on run ${result.runId.slice(0, 8)}.`
211
+ ? runLevelResumed ? `Resumed run ${result.runId.slice(0, 8)}.` : `No paused stages on run ${result.runId.slice(0, 8)}.`
200
212
  : `Resumed ${result.resumed.length} stage(s) on run ${result.runId.slice(0, 8)}${args.message ? ` with message: "${args.message}"` : ""}.`
201
213
  : `Snapshot available: run ${result.runId} (${result.snapshot.name}) — status: ${result.snapshot.status}, stages: ${result.snapshot.stages.length}`);
202
214
  return { action: "resume", runId: result.runId, status: "ok", message };
@@ -26,12 +26,14 @@ import {
26
26
  } from "./workflow-tool-inspection.js";
27
27
  import { workflowSendAction } from "./workflow-tool-send.js";
28
28
  import { isWorkflowStageToolContext, topLevelExpandedSnapshots } from "./workflow-targets.js";
29
+ import { formatWorkflowResourceLoadWarning } from "./workflow-command-surfaces.js";
29
30
 
30
31
  export function makeExecuteWorkflowTool(
31
32
  runtime: ExtensionRuntime | ((ctx: PiExecuteContext) => ExtensionRuntime),
32
33
  getPersistence: () => WorkflowPersistencePort | undefined,
33
34
  reloadWorkflowResources: () => Promise<void> | void,
34
- ) {
35
+ ensureWorkflowResourcesLoaded: () => Promise<void> | void = () => {},
36
+ ): (args: WorkflowToolArgs, ctx: PiExecuteContext) => Promise<WorkflowToolResult> {
35
37
  return async function executeWorkflowTool(
36
38
  args: WorkflowToolArgs,
37
39
  ctx: PiExecuteContext,
@@ -47,16 +49,28 @@ export function makeExecuteWorkflowTool(
47
49
  stages: [],
48
50
  };
49
51
  }
50
- const activeRuntime = typeof runtime === "function" ? runtime(ctx) : runtime;
51
52
  const policy: WorkflowExecutionPolicy = workflowPolicyFromContext(ctx);
53
+ const getRuntime = (): ExtensionRuntime => typeof runtime === "function" ? runtime(ctx) : runtime;
54
+ const ensureWorkflowResourcesVisible = async (): Promise<void> => {
55
+ try {
56
+ await ensureWorkflowResourcesLoaded();
57
+ } catch (error) {
58
+ ctx.ui?.notify?.(formatWorkflowResourceLoadWarning(error), "warning");
59
+ }
60
+ };
52
61
 
53
62
  switch (action) {
54
63
  case "get":
55
- return workflowGetResult(activeRuntime, args);
64
+ await ensureWorkflowResourcesVisible();
65
+ return workflowGetResult(getRuntime(), args);
56
66
  case "list":
57
- case "inputs":
58
- case "run":
59
- if (action === "run" && hasDirectExecutionMode(args)) {
67
+ case "inputs": {
68
+ await ensureWorkflowResourcesVisible();
69
+ return getRuntime().dispatch(args, { policy });
70
+ }
71
+ case "run": {
72
+ if (hasDirectExecutionMode(args)) {
73
+ const activeRuntime = getRuntime();
60
74
  const normalModeCount = directModeCount(args) + (hasNamedExecutionMode(args) ? 1 : 0);
61
75
  if (normalModeCount !== 1) {
62
76
  throw new Error("Workflow extension: specify exactly one normal execution mode: workflow, task, tasks, or chain");
@@ -64,7 +78,9 @@ export function makeExecuteWorkflowTool(
64
78
  const details = await activeRuntime.runDirect(withForkParentSession(args, ctx), { policy });
65
79
  return workflowRunResultFromDetails(details);
66
80
  }
67
- return activeRuntime.dispatch(args, { policy });
81
+ await ensureWorkflowResourcesVisible();
82
+ return getRuntime().dispatch(args, { policy });
83
+ }
68
84
  case "status": {
69
85
  const target = args.runId;
70
86
  if (target !== undefined) {
@@ -92,7 +108,7 @@ export function makeExecuteWorkflowTool(
92
108
  case "interrupt":
93
109
  return workflowInterruptAction(args);
94
110
  case "resume":
95
- return workflowResumeAction(args, { runtime: activeRuntime, policy });
111
+ return workflowResumeAction(args, { getRuntime, policy, ensureWorkflowResourcesLoaded });
96
112
  default: {
97
113
  const _exhaustive: never = action;
98
114
  throw new Error(`Workflow extension: unknown action "${_exhaustive}"`);
@@ -18,6 +18,7 @@ import { stageControlRegistry as defaultStageControlRegistry } from "../foregrou
18
18
  import { appendRunEnd } from "../../shared/persistence-session-entries.js";
19
19
  import { expandWorkflowGraph } from "../../shared/expanded-workflow-graph.js";
20
20
  import { topLevelWorkflowRuns } from "../../shared/run-visibility.js";
21
+ import { actionableReturnedStatusText, effectiveRunStatus, structuredRecoverableWorkflowFailureText } from "../../shared/returned-run-status.js";
21
22
 
22
23
  // ---------------------------------------------------------------------------
23
24
  // Types
@@ -115,7 +116,7 @@ export function statusRuns(opts?: { all?: boolean; store?: Store }): RunStatusEn
115
116
  return topLevelWorkflowRuns(snapshot.runs).map((run) => ({
116
117
  runId: run.id,
117
118
  name: run.name,
118
- status: run.status,
119
+ status: effectiveRunStatus(run),
119
120
  startedAt: run.startedAt,
120
121
  durationMs: run.durationMs,
121
122
  stageCount: expandWorkflowGraph(snapshot, run.id).stages.length,
@@ -470,7 +471,7 @@ export function inspectRun(
470
471
  const detail: RunDetail = {
471
472
  runId: copy.id,
472
473
  name: copy.name,
473
- status: copy.status,
474
+ status: effectiveRunStatus(copy),
474
475
  mode: expandedStages.length > 1 ? "chain" : "single",
475
476
  startedAt: copy.startedAt,
476
477
  endedAt: copy.endedAt,
@@ -481,7 +482,7 @@ export function inspectRun(
481
482
  inputs: copy.inputs,
482
483
  stages: expandedStages.map((stage) => structuredClone(stage)),
483
484
  result: copy.result,
484
- error: copy.error,
485
+ error: copy.error ?? (effectiveRunStatus(copy) === copy.status ? undefined : (structuredRecoverableWorkflowFailureText(copy) ?? actionableReturnedStatusText(copy.result))),
485
486
  exited: copy.exited,
486
487
  exitReason: copy.exitReason,
487
488
  failureKind: copy.failureKind,
@@ -23,14 +23,18 @@ import {
23
23
  createWorktrees,
24
24
  diffWorktrees,
25
25
  findWorktreeTaskCwdConflict,
26
- setupGitWorktree,
26
+ setupGitWorktreeCached,
27
27
  formatWorktreeDiffSummary,
28
28
  formatWorktreeTaskCwdConflict,
29
+ type GitWorktreeSetupCache,
30
+ type GitWorktreeSetupResult,
29
31
  type WorktreeSetup,
30
32
  } from "../shared/worktree.js";
31
33
  import type { RunOpts, RunResult } from "./executor-types.js";
32
34
  import { isWorkflowExitStatus } from "./executor-abort.js";
33
35
  import { directTaskWithDefaults, resolveWorkflowPath, taskBaseDir, withoutUndefinedProperties } from "./executor-task-prompts.js";
36
+ export { createGitWorktreeSetupCache } from "../shared/worktree.js";
37
+ export type { GitWorktreeSetupCache } from "../shared/worktree.js";
34
38
 
35
39
  export function directModelRequestsFromChain(
36
40
  chain: readonly WorkflowChainStep[],
@@ -223,27 +227,44 @@ export function stageOptionsWithInputDefaults<T extends StageOptions>(options: T
223
227
  return { ...defaults, ...withoutUndefinedProperties(options ?? {}) } as T;
224
228
  }
225
229
 
226
- export function stageOptionsWithGitWorktree<T extends StageOptions>(options: T | undefined, workflowInvocationCwd: string): T | undefined {
230
+ export function stageOptionsWithGitWorktree<T extends StageOptions>(options: T | undefined, workflowInvocationCwd: string, cache?: GitWorktreeSetupCache): T | undefined {
227
231
  if (options === undefined) return undefined;
228
232
  if (typeof options.gitWorktreeDir !== "string" || options.gitWorktreeDir.trim().length === 0) return options;
229
- const setup = setupGitWorktree({
233
+ const setup = setupGitWorktreeCached({
230
234
  gitWorktreeDir: options.gitWorktreeDir,
231
235
  baseBranch: options.baseBranch,
232
236
  cwd: workflowInvocationCwd,
233
- });
237
+ }, cache);
234
238
  const explicitCwd = resolveWorktreeCwdOverride(options.cwd, setup.cwd);
235
239
  return { ...options, gitWorktreeDir: undefined, baseBranch: undefined, cwd: explicitCwd ?? setup.cwd };
236
240
  }
237
241
 
238
- export function workflowCwdWithInputWorktree(inputDefaults: Partial<StageOptions>, workflowInvocationCwd: string): string {
242
+ export function setupWorkflowInputGitWorktree(inputDefaults: Partial<StageOptions>, workflowInvocationCwd: string, cache?: GitWorktreeSetupCache): GitWorktreeSetupResult | undefined {
239
243
  if (typeof inputDefaults.gitWorktreeDir !== "string" || inputDefaults.gitWorktreeDir.trim().length === 0) {
240
- return workflowInvocationCwd;
244
+ return undefined;
241
245
  }
242
- return setupGitWorktree({
246
+ return setupGitWorktreeCached({
243
247
  gitWorktreeDir: inputDefaults.gitWorktreeDir,
244
248
  baseBranch: inputDefaults.baseBranch,
245
249
  cwd: workflowInvocationCwd,
246
- }).cwd;
250
+ }, cache);
251
+ }
252
+
253
+ export function workflowCwdWithInputWorktree(inputDefaults: Partial<StageOptions>, workflowInvocationCwd: string, cache?: GitWorktreeSetupCache): string {
254
+ return setupWorkflowInputGitWorktree(inputDefaults, workflowInvocationCwd, cache)?.cwd ?? workflowInvocationCwd;
255
+ }
256
+
257
+ export function workflowInvocationMetadata(inputDefaults: Partial<StageOptions>, workflowInvocationCwd: string, cache?: GitWorktreeSetupCache): {
258
+ readonly invocationCwd: string;
259
+ readonly workflowCwd?: string;
260
+ readonly repositoryRoot?: string;
261
+ readonly gitWorktreeRoot?: string;
262
+ } {
263
+ const setup = setupWorkflowInputGitWorktree(inputDefaults, workflowInvocationCwd, cache);
264
+ return {
265
+ invocationCwd: workflowInvocationCwd,
266
+ ...(setup !== undefined ? { workflowCwd: setup.cwd, repositoryRoot: setup.repositoryRoot, gitWorktreeRoot: setup.worktreeRoot } : {}),
267
+ };
247
268
  }
248
269
 
249
270
  function directWorktreeDiffsDir(options: WorkflowDirectOptions, setup: WorktreeSetup, runId: string, scope: string): string {
@@ -310,7 +310,8 @@ export function toolResultHasChatAnswer(result: unknown): boolean {
310
310
  );
311
311
  }
312
312
 
313
- export const RESUME_CONTINUATION_PROMPT = "Continue where you left off.";
313
+ export const RESUME_CONTINUATION_PROMPT =
314
+ "Continue where you left off. If you believe you are finished with your original task (or a redefined task if the user told you), stop.";
314
315
 
315
316
  export function shouldInjectResumeContinuation(state: {
316
317
  readonly resumeOccurred: boolean;
@@ -24,7 +24,7 @@ import type { LiveStageMutableState, LiveStageRuntime, StageContextWithMeta, Sta
24
24
  import { createStageControlHandle } from "./executor-stage-control.js";
25
25
  import { createTrackedStageCaller } from "./executor-stage-call.js";
26
26
  import { createStageContext } from "./executor-stage-context.js";
27
- import { stageOptionsWithGitWorktree, stageOptionsWithInputDefaults } from "./executor-direct-helpers.js";
27
+ import { stageOptionsWithGitWorktree, stageOptionsWithInputDefaults, type GitWorktreeSetupCache } from "./executor-direct-helpers.js";
28
28
 
29
29
  export function createWorkflowStageFactory(input: {
30
30
  readonly runId: string;
@@ -38,6 +38,7 @@ export function createWorkflowStageFactory(input: {
38
38
  readonly limiter: ConcurrencyLimiter;
39
39
  readonly inputRuntimeDefaults: Partial<StageOptions>;
40
40
  readonly workflowInvocationCwd: string;
41
+ readonly gitWorktreeSetupCache: GitWorktreeSetupCache;
41
42
  readonly stageRegistry: StageControlRegistry;
42
43
  readonly exit: WorkflowExitManager;
43
44
  readonly classifyExecutorFailure: (error: unknown) => WorkflowFailure;
@@ -45,7 +46,7 @@ export function createWorkflowStageFactory(input: {
45
46
  }): (name: string, options?: StageOptions, stageFailFastScope?: ParallelFailFastScope) => StageContextWithMeta {
46
47
  return (name: string, options?: StageOptions, stageFailFastScope?: ParallelFailFastScope): StageContextWithMeta => {
47
48
  input.exit.throwIfWorkflowExitSelected();
48
- options = stageOptionsWithGitWorktree(stageOptionsWithInputDefaults(options, input.inputRuntimeDefaults), input.workflowInvocationCwd);
49
+ options = stageOptionsWithGitWorktree(stageOptionsWithInputDefaults(options, input.inputRuntimeDefaults), input.workflowInvocationCwd, input.gitWorktreeSetupCache);
49
50
  const stageId = crypto.randomUUID();
50
51
  const provisionalParentIds = input.tracker.onSpawn(stageId, name);
51
52
  const scopedParentIds = input.opts.continuation === undefined ? stageFailFastScope?.parentIds : undefined;