@bastani/atomic 0.8.17 → 0.8.18
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.
- package/CHANGELOG.md +16 -0
- package/dist/builtin/intercom/CHANGELOG.md +5 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/CHANGELOG.md +5 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +5 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/web-access/CHANGELOG.md +5 -0
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +25 -0
- package/dist/builtin/workflows/README.md +62 -3
- package/dist/builtin/workflows/builtin/deep-research-codebase.ts +555 -537
- package/dist/builtin/workflows/builtin/goal.ts +5 -0
- package/dist/builtin/workflows/builtin/open-claude-design.ts +3 -3
- package/dist/builtin/workflows/builtin/ralph.ts +737 -713
- package/dist/builtin/workflows/builtin/shared-prompts.ts +11 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/discovery.ts +61 -22
- package/dist/builtin/workflows/src/extension/index.ts +2 -0
- package/dist/builtin/workflows/src/extension/runtime.ts +4 -0
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +4 -0
- package/dist/builtin/workflows/src/runs/foreground/executor.ts +96 -6
- package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +2 -0
- package/dist/builtin/workflows/src/runs/shared/workflow-runner.ts +7 -0
- package/dist/builtin/workflows/src/runs/shared/worktree.ts +214 -1
- package/dist/builtin/workflows/src/sdk-surface.ts +2 -0
- package/dist/builtin/workflows/src/shared/types.ts +32 -3
- package/dist/builtin/workflows/src/workflows/define-workflow.ts +18 -1
- package/dist/core/agent-session-services.d.ts +2 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +1 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +3 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +16 -5
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js +40 -28
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/sdk.d.ts +9 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +2 -2
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/system-prompt.d.ts +2 -0
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +22 -13
- package/dist/core/system-prompt.js.map +1 -1
- package/docs/quickstart.md +13 -5
- package/docs/sdk.md +20 -5
- package/docs/workflows.md +44 -17
- package/examples/sdk/05-tools.ts +22 -1
- package/examples/sdk/README.md +7 -3
- package/package.json +1 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const WORKER_PREFLIGHT_CONTRACT = [
|
|
2
|
+
"Before normal implementation delegation, determine whether this checkout appears initialized for its actual language, framework, and build system.",
|
|
3
|
+
"Do not rely on hard-coded assumptions about JavaScript, TypeScript, Python, Rust, Go, Java, mobile, or any other ecosystem. Infer the project type and setup requirements from repository evidence.",
|
|
4
|
+
"Inspect source layout, setup docs, package/build manifests, lockfiles, toolchain files, generated-artifact conventions, CI workflows, workflow configuration, and package scripts or equivalent task definitions.",
|
|
5
|
+
"Look for evidence that dependencies, generated files, local toolchains, submodules, codegen outputs, or other project-specific initialization artifacts are missing for this checkout.",
|
|
6
|
+
"When repository evidence shows missing initialization, run or delegate the appropriate documented setup command before implementation work.",
|
|
7
|
+
"You are responsible for initializing the checkout when setup commands are documented; missing dependencies, generated files, or local toolchains are setup work, not user handoff work.",
|
|
8
|
+
"Once setup succeeds, continue normal implementation orchestration. Do not treat missing dependencies or generated setup artifacts in a fresh worktree as implementation failures.",
|
|
9
|
+
"If setup requirements cannot be determined confidently, delegate a focused discovery task before implementation instead of guessing.",
|
|
10
|
+
"If setup remains blocked after evidence-based discovery and setup attempts, report the blocker with commands tried and the exact evidence needed to continue.",
|
|
11
|
+
].join("\n");
|
|
@@ -149,19 +149,17 @@ export interface DiscoveryResult {
|
|
|
149
149
|
// ---------------------------------------------------------------------------
|
|
150
150
|
|
|
151
151
|
/**
|
|
152
|
-
* Validate a candidate value as a WorkflowDefinition.
|
|
152
|
+
* Validate a candidate value as a WorkflowDefinition by shape only.
|
|
153
|
+
*
|
|
154
|
+
* Discovery intentionally does not invoke workflow run functions: user-authored
|
|
155
|
+
* run bodies may perform filesystem, network, or other side effects before the
|
|
156
|
+
* first ctx.stage()/ctx.task()/ctx.chain()/ctx.parallel() call. Runtime empty
|
|
157
|
+
* graph validation remains the authoritative guard that a workflow creates at
|
|
158
|
+
* least one stage when it is actually invoked.
|
|
159
|
+
*
|
|
153
160
|
* Returns null when valid, or a human-readable rejection reason string.
|
|
154
161
|
*/
|
|
155
|
-
function
|
|
156
|
-
// Discovery must not execute workflow bodies: run functions are arbitrary
|
|
157
|
-
// user code and may perform I/O before the first stage. Use a conservative
|
|
158
|
-
// static guard instead so obvious no-stage definitions are rejected before
|
|
159
|
-
// they can register and render an empty graph.
|
|
160
|
-
const source = Function.prototype.toString.call(run);
|
|
161
|
-
return /\.\s*(?:stage|task|chain|parallel)\s*\(/.test(source);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function validateDefinition(value: unknown): string | null {
|
|
162
|
+
function validateDefinitionShape(value: unknown): string | null {
|
|
165
163
|
if (value === null || typeof value !== "object") {
|
|
166
164
|
return "export is not an object";
|
|
167
165
|
}
|
|
@@ -179,9 +177,6 @@ function validateDefinition(value: unknown): string | null {
|
|
|
179
177
|
if (typeof d["run"] !== "function") {
|
|
180
178
|
return "run must be a function";
|
|
181
179
|
}
|
|
182
|
-
if (!workflowRunCreatesStage(d["run"] as WorkflowDefinition["run"])) {
|
|
183
|
-
return "run must create at least one workflow stage via ctx.stage(), ctx.task(), ctx.chain(), or ctx.parallel(); otherwise the workflow graph is empty (cachedLayout.length === 0)";
|
|
184
|
-
}
|
|
185
180
|
return null;
|
|
186
181
|
}
|
|
187
182
|
|
|
@@ -215,14 +210,58 @@ function validateConfig(config: unknown): string | null {
|
|
|
215
210
|
}
|
|
216
211
|
|
|
217
212
|
/** Merge a batch of candidates into registry state, first-seen wins. */
|
|
218
|
-
function applyBatch(
|
|
213
|
+
async function applyBatch(
|
|
214
|
+
candidates: Array<{ value: unknown; exportKey: string; kind: DiscoveryKind; filePath?: string; configuredName?: string }>,
|
|
215
|
+
registry: WorkflowRegistry,
|
|
216
|
+
sources: DiscoverySource[],
|
|
217
|
+
diagnostics: DiscoveryDiagnostic[],
|
|
218
|
+
): Promise<WorkflowRegistry> {
|
|
219
|
+
for (const { value, exportKey, kind, filePath, configuredName } of candidates) {
|
|
220
|
+
const reason = validateDefinitionShape(value);
|
|
221
|
+
if (reason !== null) {
|
|
222
|
+
diagnostics.push({
|
|
223
|
+
level: "error",
|
|
224
|
+
code: "INVALID_DEFINITION",
|
|
225
|
+
message: `${kind} export "${exportKey}" rejected: ${reason}`,
|
|
226
|
+
source: filePath ?? exportKey,
|
|
227
|
+
});
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const def = value as WorkflowDefinition;
|
|
232
|
+
const key = def.normalizedName;
|
|
233
|
+
|
|
234
|
+
if (registry.has(key)) {
|
|
235
|
+
diagnostics.push({
|
|
236
|
+
level: "warn",
|
|
237
|
+
code: "DUPLICATE_NAME",
|
|
238
|
+
message: `${kind} export "${exportKey}" skipped: normalizedName "${key}" already registered`,
|
|
239
|
+
source: filePath ?? exportKey,
|
|
240
|
+
});
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
registry = registry.register(def);
|
|
245
|
+
sources.push({
|
|
246
|
+
id: key,
|
|
247
|
+
kind,
|
|
248
|
+
name: def.name,
|
|
249
|
+
...(filePath !== undefined ? { filePath } : {}),
|
|
250
|
+
...(configuredName !== undefined ? { configuredName } : {}),
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return registry;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Merge bundled startup candidates with shape-only validation to keep startup seeding synchronous. */
|
|
257
|
+
function applyBatchShapeOnly(
|
|
219
258
|
candidates: Array<{ value: unknown; exportKey: string; kind: DiscoveryKind; filePath?: string; configuredName?: string }>,
|
|
220
259
|
registry: WorkflowRegistry,
|
|
221
260
|
sources: DiscoverySource[],
|
|
222
261
|
diagnostics: DiscoveryDiagnostic[],
|
|
223
262
|
): WorkflowRegistry {
|
|
224
263
|
for (const { value, exportKey, kind, filePath, configuredName } of candidates) {
|
|
225
|
-
const reason =
|
|
264
|
+
const reason = validateDefinitionShape(value);
|
|
226
265
|
if (reason !== null) {
|
|
227
266
|
diagnostics.push({
|
|
228
267
|
level: "error",
|
|
@@ -508,14 +547,14 @@ export async function discoverWorkflows(
|
|
|
508
547
|
const hasEntries = Array.isArray(pw) ? pw.length > 0 : Object.keys(pw).length > 0;
|
|
509
548
|
if (hasEntries) {
|
|
510
549
|
const candidates = await loadFromPaths(pw, "settings-project", cwd, diagnostics);
|
|
511
|
-
registry = applyBatch(candidates, registry, sources, diagnostics);
|
|
550
|
+
registry = await applyBatch(candidates, registry, sources, diagnostics);
|
|
512
551
|
}
|
|
513
552
|
}
|
|
514
553
|
|
|
515
554
|
// 2. project-local
|
|
516
555
|
for (const dir of getProjectConfigPaths(cwd, "workflows").reverse()) {
|
|
517
556
|
const candidates = await loadFromDir(dir, "project-local", diagnostics);
|
|
518
|
-
registry = applyBatch(candidates, registry, sources, diagnostics);
|
|
557
|
+
registry = await applyBatch(candidates, registry, sources, diagnostics);
|
|
519
558
|
}
|
|
520
559
|
|
|
521
560
|
// 3. settings-global
|
|
@@ -524,14 +563,14 @@ export async function discoverWorkflows(
|
|
|
524
563
|
const hasEntries = Array.isArray(gw) ? gw.length > 0 : Object.keys(gw).length > 0;
|
|
525
564
|
if (hasEntries) {
|
|
526
565
|
const candidates = await loadFromPaths(gw, "settings-global", homeDir, diagnostics);
|
|
527
|
-
registry = applyBatch(candidates, registry, sources, diagnostics);
|
|
566
|
+
registry = await applyBatch(candidates, registry, sources, diagnostics);
|
|
528
567
|
}
|
|
529
568
|
}
|
|
530
569
|
|
|
531
570
|
// 4. user-global — canonical Atomic path plus legacy pi path
|
|
532
571
|
for (const dir of CONFIG_DIR_NAMES.map((name) => join(homeDir, name, "agent", "workflows")).reverse()) {
|
|
533
572
|
const candidates = await loadFromDir(dir, "user-global", diagnostics);
|
|
534
|
-
registry = applyBatch(candidates, registry, sources, diagnostics);
|
|
573
|
+
registry = await applyBatch(candidates, registry, sources, diagnostics);
|
|
535
574
|
}
|
|
536
575
|
|
|
537
576
|
// 5. package workflows
|
|
@@ -539,7 +578,7 @@ export async function discoverWorkflows(
|
|
|
539
578
|
const hasEntries = Array.isArray(packageWorkflowPaths) ? packageWorkflowPaths.length > 0 : Object.keys(packageWorkflowPaths).length > 0;
|
|
540
579
|
if (hasEntries) {
|
|
541
580
|
const candidates = await loadFromPaths(packageWorkflowPaths, "package", cwd, diagnostics);
|
|
542
|
-
registry = applyBatch(candidates, registry, sources, diagnostics);
|
|
581
|
+
registry = await applyBatch(candidates, registry, sources, diagnostics);
|
|
543
582
|
}
|
|
544
583
|
}
|
|
545
584
|
|
|
@@ -602,7 +641,7 @@ function discoverBundledManifest(): DiscoveryResult {
|
|
|
602
641
|
kind: "bundled" as DiscoveryKind,
|
|
603
642
|
}));
|
|
604
643
|
|
|
605
|
-
registry =
|
|
644
|
+
registry = applyBatchShapeOnly(candidates, registry, sources, diagnostics);
|
|
606
645
|
|
|
607
646
|
return { registry, sources, errors: diagnostics };
|
|
608
647
|
}
|
|
@@ -456,6 +456,8 @@ export interface WorkflowToolArgs extends StageOptions {
|
|
|
456
456
|
maxOutput?: WorkflowMaxOutput;
|
|
457
457
|
artifacts?: boolean;
|
|
458
458
|
worktree?: boolean;
|
|
459
|
+
gitWorktreeDir?: string;
|
|
460
|
+
baseBranch?: string;
|
|
459
461
|
}
|
|
460
462
|
|
|
461
463
|
// ---------------------------------------------------------------------------
|
|
@@ -190,6 +190,8 @@ export function createExtensionRuntime(opts: ExtensionRuntimeOpts = {}): Extensi
|
|
|
190
190
|
maxOutput,
|
|
191
191
|
artifacts,
|
|
192
192
|
worktree,
|
|
193
|
+
gitWorktreeDir,
|
|
194
|
+
baseBranch,
|
|
193
195
|
...stageOptions
|
|
194
196
|
} = args;
|
|
195
197
|
|
|
@@ -206,6 +208,8 @@ export function createExtensionRuntime(opts: ExtensionRuntimeOpts = {}): Extensi
|
|
|
206
208
|
...(maxOutput !== undefined ? { maxOutput } : {}),
|
|
207
209
|
...(typeof artifacts === "boolean" ? { artifacts } : {}),
|
|
208
210
|
...(typeof worktree === "boolean" ? { worktree } : {}),
|
|
211
|
+
...(typeof gitWorktreeDir === "string" ? { gitWorktreeDir } : {}),
|
|
212
|
+
...(typeof baseBranch === "string" ? { baseBranch } : {}),
|
|
209
213
|
};
|
|
210
214
|
}
|
|
211
215
|
|
|
@@ -66,6 +66,8 @@ const WorkflowTaskOptionProperties = {
|
|
|
66
66
|
outputMode: Type.Optional(Type.Union([Type.Literal("inline"), Type.Literal("file-only")])),
|
|
67
67
|
reads: Type.Optional(Type.Union([Type.Array(Type.String()), Type.Literal(false)])),
|
|
68
68
|
worktree: Type.Optional(Type.Boolean()),
|
|
69
|
+
gitWorktreeDir: Type.Optional(Type.String()),
|
|
70
|
+
baseBranch: Type.Optional(Type.String()),
|
|
69
71
|
maxOutput: Type.Optional(MaxOutputSchema),
|
|
70
72
|
artifacts: Type.Optional(Type.Boolean()),
|
|
71
73
|
};
|
|
@@ -83,6 +85,8 @@ const ParallelChainStepSchema = Type.Object({
|
|
|
83
85
|
concurrency: Type.Optional(Type.Number()),
|
|
84
86
|
failFast: Type.Optional(Type.Boolean()),
|
|
85
87
|
worktree: Type.Optional(Type.Boolean()),
|
|
88
|
+
gitWorktreeDir: Type.Optional(Type.String()),
|
|
89
|
+
baseBranch: Type.Optional(Type.String()),
|
|
86
90
|
});
|
|
87
91
|
|
|
88
92
|
export const WorkflowParametersSchema = Type.Object({
|
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
createWorktrees,
|
|
57
57
|
diffWorktrees,
|
|
58
58
|
findWorktreeTaskCwdConflict,
|
|
59
|
+
setupGitWorktree,
|
|
59
60
|
formatWorktreeDiffSummary,
|
|
60
61
|
formatWorktreeTaskCwdConflict,
|
|
61
62
|
type WorktreeSetup,
|
|
@@ -82,6 +83,8 @@ export interface RunContinuationOpts {
|
|
|
82
83
|
|
|
83
84
|
export interface RunOpts {
|
|
84
85
|
adapters?: StageAdapters;
|
|
86
|
+
/** Invocation working directory exposed to workflow definitions as ctx.cwd. */
|
|
87
|
+
cwd?: string;
|
|
85
88
|
/** HIL adapter injected by the pi runtime or test harness. */
|
|
86
89
|
ui?: WorkflowUIAdapter;
|
|
87
90
|
/** Internal detached-run mode: surface ctx.ui.* as node-local workflow prompt stages. */
|
|
@@ -186,6 +189,23 @@ function resolveInputConcurrency(
|
|
|
186
189
|
return Math.floor(value);
|
|
187
190
|
}
|
|
188
191
|
|
|
192
|
+
function resolveInputRuntimeDefaults(
|
|
193
|
+
def: Pick<WorkflowDefinition, "inputBindings">,
|
|
194
|
+
resolvedInputs: ResolvedInputs,
|
|
195
|
+
): Partial<StageOptions> {
|
|
196
|
+
const defaults: Partial<StageOptions> = {};
|
|
197
|
+
const worktree = def.inputBindings?.worktree;
|
|
198
|
+
if (worktree !== undefined) {
|
|
199
|
+
const gitWorktreeDir = resolvedInputs[worktree.gitWorktreeDir];
|
|
200
|
+
if (typeof gitWorktreeDir === "string" && gitWorktreeDir.trim().length > 0) {
|
|
201
|
+
defaults.gitWorktreeDir = gitWorktreeDir;
|
|
202
|
+
const baseBranch = worktree.baseBranch === undefined ? undefined : resolvedInputs[worktree.baseBranch];
|
|
203
|
+
if (typeof baseBranch === "string") defaults.baseBranch = baseBranch;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return defaults;
|
|
207
|
+
}
|
|
208
|
+
|
|
189
209
|
// ---------------------------------------------------------------------------
|
|
190
210
|
// HIL unavailable fallback — rejects with precise per-primitive error
|
|
191
211
|
// ---------------------------------------------------------------------------
|
|
@@ -397,6 +417,8 @@ function taskStageOptions(options: WorkflowTaskExecutionOptions): StageOptions {
|
|
|
397
417
|
outputMode: _outputMode,
|
|
398
418
|
reads: _reads,
|
|
399
419
|
worktree: _worktree,
|
|
420
|
+
gitWorktreeDir: _gitWorktreeDir,
|
|
421
|
+
baseBranch: _baseBranch,
|
|
400
422
|
maxOutput: _maxOutput,
|
|
401
423
|
artifacts: _artifacts,
|
|
402
424
|
...stageOptions
|
|
@@ -550,6 +572,8 @@ function directTaskWithDefaults(
|
|
|
550
572
|
output,
|
|
551
573
|
outputMode,
|
|
552
574
|
worktree,
|
|
575
|
+
gitWorktreeDir,
|
|
576
|
+
baseBranch,
|
|
553
577
|
maxOutput,
|
|
554
578
|
artifacts,
|
|
555
579
|
...stageDefaults
|
|
@@ -567,6 +591,8 @@ function directTaskWithDefaults(
|
|
|
567
591
|
...(item.output === undefined && output !== undefined ? { output } : {}),
|
|
568
592
|
...(item.outputMode === undefined && outputMode !== undefined ? { outputMode } : {}),
|
|
569
593
|
...(item.worktree === undefined && worktree !== undefined ? { worktree } : {}),
|
|
594
|
+
...(item.gitWorktreeDir === undefined && gitWorktreeDir !== undefined ? { gitWorktreeDir } : {}),
|
|
595
|
+
...(item.baseBranch === undefined && baseBranch !== undefined ? { baseBranch } : {}),
|
|
570
596
|
...(item.maxOutput === undefined && maxOutput !== undefined ? { maxOutput } : {}),
|
|
571
597
|
...(item.artifacts === undefined && artifacts !== undefined ? { artifacts } : {}),
|
|
572
598
|
};
|
|
@@ -714,6 +740,42 @@ function normalizeDirectTaskCwd(cwd: string | undefined): string | undefined {
|
|
|
714
740
|
return isAbsolute(cwd) ? cwd : resolve(process.cwd(), cwd);
|
|
715
741
|
}
|
|
716
742
|
|
|
743
|
+
function resolveWorktreeCwdOverride(cwd: string | undefined, worktreeCwd: string): string | undefined {
|
|
744
|
+
if (cwd === undefined || cwd.length === 0) return undefined;
|
|
745
|
+
return isAbsolute(cwd) ? cwd : resolve(worktreeCwd, cwd);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
function stageOptionsWithInputDefaults<T extends StageOptions>(options: T | undefined, inputDefaults: Partial<StageOptions>): T | undefined {
|
|
749
|
+
const defaults = withoutUndefinedProperties(inputDefaults);
|
|
750
|
+
if (Object.keys(defaults).length === 0) return options;
|
|
751
|
+
return { ...defaults, ...withoutUndefinedProperties(options ?? {}) } as T;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
function stageOptionsWithGitWorktree<T extends StageOptions>(options: T | undefined, workflowInvocationCwd: string): T | undefined {
|
|
755
|
+
if (options === undefined) return undefined;
|
|
756
|
+
if (typeof options.gitWorktreeDir !== "string" || options.gitWorktreeDir.trim().length === 0) {
|
|
757
|
+
return options;
|
|
758
|
+
}
|
|
759
|
+
const setup = setupGitWorktree({
|
|
760
|
+
gitWorktreeDir: options.gitWorktreeDir,
|
|
761
|
+
baseBranch: options.baseBranch,
|
|
762
|
+
cwd: workflowInvocationCwd,
|
|
763
|
+
});
|
|
764
|
+
const explicitCwd = resolveWorktreeCwdOverride(options.cwd, setup.cwd);
|
|
765
|
+
return { ...options, gitWorktreeDir: undefined, baseBranch: undefined, cwd: explicitCwd ?? setup.cwd };
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
function workflowCwdWithInputWorktree(inputDefaults: Partial<StageOptions>, workflowInvocationCwd: string): string {
|
|
769
|
+
if (typeof inputDefaults.gitWorktreeDir !== "string" || inputDefaults.gitWorktreeDir.trim().length === 0) {
|
|
770
|
+
return workflowInvocationCwd;
|
|
771
|
+
}
|
|
772
|
+
return setupGitWorktree({
|
|
773
|
+
gitWorktreeDir: inputDefaults.gitWorktreeDir,
|
|
774
|
+
baseBranch: inputDefaults.baseBranch,
|
|
775
|
+
cwd: workflowInvocationCwd,
|
|
776
|
+
}).cwd;
|
|
777
|
+
}
|
|
778
|
+
|
|
717
779
|
function directWorktreeDiffsDir(options: WorkflowDirectOptions, setup: WorktreeSetup, runId: string, scope: string): string {
|
|
718
780
|
const baseDir = options.chainDir ?? join(setup.cwd, CONFIG_DIR_NAME, "workflows");
|
|
719
781
|
return join(baseDir, "worktree-diffs", runId, scope);
|
|
@@ -732,6 +794,10 @@ function prepareDirectWorktrees(
|
|
|
732
794
|
};
|
|
733
795
|
}
|
|
734
796
|
|
|
797
|
+
if (typeof options.gitWorktreeDir === "string" || tasks.some((task) => typeof task.gitWorktreeDir === "string")) {
|
|
798
|
+
throw new Error("pi-workflows: worktree and gitWorktreeDir are mutually exclusive; use gitWorktreeDir for a reusable worktree or worktree:true for temporary isolated worktrees.");
|
|
799
|
+
}
|
|
800
|
+
|
|
735
801
|
const sharedCwd = resolveSharedDirectWorktreeCwd(tasks);
|
|
736
802
|
const conflict = findWorktreeTaskCwdConflict(
|
|
737
803
|
tasks.map((task) => ({ agent: task.name, cwd: normalizeDirectTaskCwd(task.cwd) })),
|
|
@@ -884,6 +950,13 @@ function workflowDetailsFromRun(
|
|
|
884
950
|
};
|
|
885
951
|
}
|
|
886
952
|
|
|
953
|
+
const EMPTY_WORKFLOW_GRAPH_ERROR_MESSAGE = "Workflow run completed without creating any workflow stages. Create at least one stage with ctx.stage(), ctx.task(), ctx.chain(), or ctx.parallel().";
|
|
954
|
+
|
|
955
|
+
function assertWorkflowCreatedStage(runSnapshot: RunSnapshot): void {
|
|
956
|
+
if (runSnapshot.stages.length > 0) return;
|
|
957
|
+
throw new Error(EMPTY_WORKFLOW_GRAPH_ERROR_MESSAGE);
|
|
958
|
+
}
|
|
959
|
+
|
|
887
960
|
function defineDirectWorkflow(
|
|
888
961
|
name: string,
|
|
889
962
|
runFn: WorkflowDefinition["run"],
|
|
@@ -1010,8 +1083,13 @@ async function runDirectChainStep(
|
|
|
1010
1083
|
runId: string,
|
|
1011
1084
|
): Promise<{ results: WorkflowTaskResult[]; artifacts: WorkflowArtifact[] }> {
|
|
1012
1085
|
if ("parallel" in step) {
|
|
1013
|
-
const
|
|
1014
|
-
|
|
1086
|
+
const stepOptions = {
|
|
1087
|
+
...options,
|
|
1088
|
+
worktree: options.worktree === true || step.worktree === true,
|
|
1089
|
+
...(step.gitWorktreeDir !== undefined ? { gitWorktreeDir: step.gitWorktreeDir } : {}),
|
|
1090
|
+
...(step.baseBranch !== undefined ? { baseBranch: step.baseBranch } : {}),
|
|
1091
|
+
};
|
|
1092
|
+
const expanded = expandedParallelTasks(step.parallel.map((item) => directTaskWithDefaults(item, stepOptions)));
|
|
1015
1093
|
const prepared = prepareDirectWorktrees(expanded, stepOptions, `${runId}-s${index}`, `step-${index}`);
|
|
1016
1094
|
try {
|
|
1017
1095
|
const steps = prepared.tasks.map((item) =>
|
|
@@ -1485,6 +1563,13 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1485
1563
|
// 4. Create GraphFrontierTracker and per-run ConcurrencyLimiter
|
|
1486
1564
|
const tracker = new GraphFrontierTracker();
|
|
1487
1565
|
const inputConcurrency = resolveInputConcurrency(def.inputs, resolvedInputs);
|
|
1566
|
+
const inputRuntimeDefaults = resolveInputRuntimeDefaults(def, resolvedInputs);
|
|
1567
|
+
const workflowInvocationCwd = opts.cwd ?? process.cwd();
|
|
1568
|
+
let workflowCwd: string | undefined;
|
|
1569
|
+
const resolveWorkflowCwd = (): string => {
|
|
1570
|
+
workflowCwd ??= workflowCwdWithInputWorktree(inputRuntimeDefaults, workflowInvocationCwd);
|
|
1571
|
+
return workflowCwd;
|
|
1572
|
+
};
|
|
1488
1573
|
const limiter = createRunLimiter(inputConcurrency ?? opts.config?.defaultConcurrency);
|
|
1489
1574
|
interface ReleaseBarrier {
|
|
1490
1575
|
readonly promise: Promise<void>;
|
|
@@ -1819,11 +1904,13 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1819
1904
|
// 5. Build WorkflowRunContext
|
|
1820
1905
|
const ctx: WorkflowRunContext<TInputs> = {
|
|
1821
1906
|
inputs: resolvedInputs as TInputs,
|
|
1907
|
+
get cwd() { return resolveWorkflowCwd(); },
|
|
1822
1908
|
// Prompt nodes and caller-provided UI adapters are mutually exclusive;
|
|
1823
1909
|
// executor-owned prompt nodes intentionally take precedence when enabled.
|
|
1824
1910
|
ui: opts.usePromptNodesForUi === true ? buildPromptNodeUiAdapter() : opts.ui ?? makeUnavailableUIContext(),
|
|
1825
1911
|
|
|
1826
1912
|
stage(name: string, options?: StageOptions, stageFailFastScope?: ParallelFailFastScope) {
|
|
1913
|
+
options = stageOptionsWithGitWorktree(stageOptionsWithInputDefaults(options, inputRuntimeDefaults), workflowInvocationCwd);
|
|
1827
1914
|
// a. Generate stageId
|
|
1828
1915
|
const stageId = crypto.randomUUID();
|
|
1829
1916
|
|
|
@@ -2369,16 +2456,17 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
2369
2456
|
|
|
2370
2457
|
async task(name: string, options: WorkflowTaskOptions, stageFailFastScope?: ParallelFailFastScope): Promise<WorkflowTaskResult> {
|
|
2371
2458
|
const runTaskOnce = async (taskOptions: WorkflowTaskOptions): Promise<WorkflowTaskResult> => {
|
|
2459
|
+
const resolvedTaskOptions = stageOptionsWithGitWorktree(stageOptionsWithInputDefaults(taskOptions, inputRuntimeDefaults), workflowInvocationCwd) ?? taskOptions;
|
|
2372
2460
|
const stage = (ctx.stage as typeof ctx.stage & ((stageName: string, stageOptions?: StageOptions, scope?: ParallelFailFastScope) => StageContext))(
|
|
2373
2461
|
name,
|
|
2374
|
-
taskStageOptions(
|
|
2462
|
+
taskStageOptions(resolvedTaskOptions),
|
|
2375
2463
|
stageFailFastScope,
|
|
2376
2464
|
);
|
|
2377
2465
|
const rawText = await stage.prompt(
|
|
2378
|
-
applyTaskContext(`${taskReadInstruction(
|
|
2379
|
-
taskPromptOptions(
|
|
2466
|
+
applyTaskContext(`${taskReadInstruction(resolvedTaskOptions)}${taskPrompt(resolvedTaskOptions)}`, taskPrevious(resolvedTaskOptions)),
|
|
2467
|
+
taskPromptOptions(resolvedTaskOptions),
|
|
2380
2468
|
);
|
|
2381
|
-
const text = truncateTaskOutput(rawText,
|
|
2469
|
+
const text = truncateTaskOutput(rawText, resolvedTaskOptions.maxOutput);
|
|
2382
2470
|
const sessionId = (() => {
|
|
2383
2471
|
try {
|
|
2384
2472
|
return stage.sessionId;
|
|
@@ -2475,6 +2563,8 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
2475
2563
|
return finalizeKilled(runId, runSnapshot, activeStore, opts.persistence, opts.onRunEnd);
|
|
2476
2564
|
}
|
|
2477
2565
|
|
|
2566
|
+
assertWorkflowCreatedStage(runSnapshot);
|
|
2567
|
+
|
|
2478
2568
|
const recorded = activeStore.recordRunEnd(runId, "completed", result);
|
|
2479
2569
|
opts.onRunEnd?.(runId, "completed", result);
|
|
2480
2570
|
|
|
@@ -144,6 +144,8 @@ function stripWorkflowOnlyOptions(options: StageOptions | undefined): CreateAgen
|
|
|
144
144
|
context,
|
|
145
145
|
forkFromSessionFile,
|
|
146
146
|
sessionDir,
|
|
147
|
+
gitWorktreeDir: _gitWorktreeDir,
|
|
148
|
+
baseBranch: _baseBranch,
|
|
147
149
|
...sessionOptions
|
|
148
150
|
} = options;
|
|
149
151
|
if (sessionOptions.sessionManager === undefined) {
|
|
@@ -45,6 +45,8 @@ export interface WorkflowDefinition extends StageOptions {
|
|
|
45
45
|
output?: string | false;
|
|
46
46
|
outputMode?: WorkflowOutputMode;
|
|
47
47
|
worktree?: boolean;
|
|
48
|
+
gitWorktreeDir?: string;
|
|
49
|
+
baseBranch?: string;
|
|
48
50
|
maxOutput?: WorkflowMaxOutput;
|
|
49
51
|
artifacts?: boolean;
|
|
50
52
|
}
|
|
@@ -91,6 +93,7 @@ function runOptionsWithAdapters(
|
|
|
91
93
|
|
|
92
94
|
return {
|
|
93
95
|
...options.runOptions,
|
|
96
|
+
cwd: options.cwd ?? options.runOptions?.cwd,
|
|
94
97
|
...(config !== undefined ? { config } : {}),
|
|
95
98
|
adapters: buildRuntimeAdapters(options.pi ?? {}, adapterOptions),
|
|
96
99
|
store: createStore(),
|
|
@@ -164,6 +167,8 @@ function directOptions(definition: WorkflowDefinition): WorkflowDirectOptions {
|
|
|
164
167
|
output,
|
|
165
168
|
outputMode,
|
|
166
169
|
worktree,
|
|
170
|
+
gitWorktreeDir,
|
|
171
|
+
baseBranch,
|
|
167
172
|
maxOutput,
|
|
168
173
|
artifacts,
|
|
169
174
|
...stageOptions
|
|
@@ -180,6 +185,8 @@ function directOptions(definition: WorkflowDefinition): WorkflowDirectOptions {
|
|
|
180
185
|
...(output !== undefined ? { output } : {}),
|
|
181
186
|
...(outputMode !== undefined ? { outputMode } : {}),
|
|
182
187
|
...(typeof worktree === "boolean" ? { worktree } : {}),
|
|
188
|
+
...(typeof gitWorktreeDir === "string" ? { gitWorktreeDir } : {}),
|
|
189
|
+
...(typeof baseBranch === "string" ? { baseBranch } : {}),
|
|
183
190
|
...(maxOutput !== undefined ? { maxOutput } : {}),
|
|
184
191
|
...(typeof artifacts === "boolean" ? { artifacts } : {}),
|
|
185
192
|
};
|