agentweaver 0.1.15 → 0.1.17
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/README.md +76 -19
- package/dist/artifact-manifest.js +219 -0
- package/dist/artifacts.js +88 -3
- package/dist/doctor/checks/env-diagnostics.js +25 -0
- package/dist/doctor/checks/executors.js +2 -2
- package/dist/doctor/checks/flow-readiness.js +15 -18
- package/dist/flow-state.js +212 -15
- package/dist/index.js +539 -209
- package/dist/interactive/blessed-session.js +361 -0
- package/dist/interactive/controller.js +1326 -0
- package/dist/interactive/create-interactive-session.js +5 -0
- package/dist/interactive/ink/index.js +597 -0
- package/dist/interactive/progress.js +245 -0
- package/dist/interactive/selectors.js +14 -0
- package/dist/interactive/session.js +1 -0
- package/dist/interactive/state.js +34 -0
- package/dist/interactive/tree.js +155 -0
- package/dist/interactive/types.js +1 -0
- package/dist/interactive/view-model.js +1 -0
- package/dist/interactive-ui.js +159 -194
- package/dist/pipeline/auto-flow.js +9 -6
- package/dist/pipeline/context.js +7 -5
- package/dist/pipeline/declarative-flow-runner.js +212 -6
- package/dist/pipeline/declarative-flows.js +63 -17
- package/dist/pipeline/execution-routing-config.js +15 -0
- package/dist/pipeline/flow-catalog.js +50 -12
- package/dist/pipeline/flow-run-resume.js +29 -0
- package/dist/pipeline/flow-specs/auto-common.json +90 -360
- package/dist/pipeline/flow-specs/auto-golang.json +81 -360
- package/dist/pipeline/flow-specs/auto-simple.json +141 -0
- package/dist/pipeline/flow-specs/bugz/bug-analyze.json +2 -0
- package/dist/pipeline/flow-specs/bugz/bug-fix.json +1 -0
- package/dist/pipeline/flow-specs/design-review/design-review-loop.json +316 -0
- package/dist/pipeline/flow-specs/design-review.json +10 -0
- package/dist/pipeline/flow-specs/gitlab/gitlab-diff-review.json +11 -0
- package/dist/pipeline/flow-specs/gitlab/gitlab-review.json +2 -0
- package/dist/pipeline/flow-specs/gitlab/mr-description.json +1 -0
- package/dist/pipeline/flow-specs/go/run-go-linter-loop.json +2 -0
- package/dist/pipeline/flow-specs/go/run-go-tests-loop.json +2 -0
- package/dist/pipeline/flow-specs/implement.json +13 -6
- package/dist/pipeline/flow-specs/instant-task.json +177 -0
- package/dist/pipeline/flow-specs/normalize-task-source.json +311 -0
- package/dist/pipeline/flow-specs/plan-revise.json +7 -1
- package/dist/pipeline/flow-specs/plan.json +51 -71
- package/dist/pipeline/flow-specs/review/review-fix.json +24 -4
- package/dist/pipeline/flow-specs/review/review-loop.json +351 -45
- package/dist/pipeline/flow-specs/review/review-project-loop.json +590 -0
- package/dist/pipeline/flow-specs/review/review-project.json +12 -0
- package/dist/pipeline/flow-specs/review/review.json +37 -31
- package/dist/pipeline/flow-specs/task-describe.json +2 -0
- package/dist/pipeline/flow-specs/task-source/jira-fetch.json +70 -0
- package/dist/pipeline/flow-specs/task-source/manual-input.json +216 -0
- package/dist/pipeline/launch-profile-config.js +30 -18
- package/dist/pipeline/node-contract.js +1 -0
- package/dist/pipeline/node-registry.js +115 -6
- package/dist/pipeline/node-runner.js +3 -2
- package/dist/pipeline/nodes/build-review-fix-prompt-node.js +5 -1
- package/dist/pipeline/nodes/clear-ready-to-merge-node.js +11 -0
- package/dist/pipeline/nodes/commit-message-form-node.js +8 -0
- package/dist/pipeline/nodes/design-review-verdict-node.js +36 -0
- package/dist/pipeline/nodes/ensure-summary-json-node.js +13 -2
- package/dist/pipeline/nodes/fetch-gitlab-diff-node.js +19 -2
- package/dist/pipeline/nodes/fetch-gitlab-review-node.js +19 -2
- package/dist/pipeline/nodes/flow-run-node.js +242 -8
- package/dist/pipeline/nodes/git-commit-form-node.js +8 -0
- package/dist/pipeline/nodes/gitlab-review-artifacts-node.js +19 -2
- package/dist/pipeline/nodes/jira-fetch-node.js +50 -4
- package/dist/pipeline/nodes/llm-prompt-node.js +38 -36
- package/dist/pipeline/nodes/planning-bundle-node.js +10 -0
- package/dist/pipeline/nodes/review-verdict-node.js +86 -0
- package/dist/pipeline/nodes/select-files-form-node.js +8 -0
- package/dist/pipeline/nodes/structured-summary-node.js +24 -0
- package/dist/pipeline/nodes/user-input-node.js +38 -3
- package/dist/pipeline/nodes/write-selection-file-node.js +20 -4
- package/dist/pipeline/plugin-loader.js +389 -0
- package/dist/pipeline/plugin-types.js +1 -0
- package/dist/pipeline/prompt-registry.js +3 -1
- package/dist/pipeline/prompt-runtime.js +4 -1
- package/dist/pipeline/registry.js +71 -4
- package/dist/pipeline/review-iteration.js +26 -0
- package/dist/pipeline/spec-compiler.js +3 -0
- package/dist/pipeline/spec-loader.js +14 -0
- package/dist/pipeline/spec-types.js +3 -0
- package/dist/pipeline/spec-validator.js +20 -0
- package/dist/pipeline/value-resolver.js +76 -2
- package/dist/plugin-sdk.js +1 -0
- package/dist/prompts.js +36 -14
- package/dist/review-severity.js +45 -0
- package/dist/runtime/artifact-registry.js +405 -0
- package/dist/runtime/design-review-input-contract.js +17 -16
- package/dist/runtime/env-loader.js +3 -0
- package/dist/runtime/execution-routing-store.js +134 -0
- package/dist/runtime/execution-routing.js +233 -0
- package/dist/runtime/interactive-execution-routing.js +471 -0
- package/dist/runtime/plan-revise-input-contract.js +35 -32
- package/dist/runtime/planning-bundle.js +123 -0
- package/dist/runtime/ready-to-merge.js +22 -1
- package/dist/runtime/review-input-contract.js +100 -0
- package/dist/structured-artifact-schema-registry.js +9 -0
- package/dist/structured-artifact-schemas.json +140 -1
- package/dist/structured-artifacts.js +77 -6
- package/dist/user-input.js +70 -3
- package/docs/example/.flows/examples/claude-example.json +50 -0
- package/docs/example/.plugins/claude-example-plugin/index.js +149 -0
- package/docs/example/.plugins/claude-example-plugin/plugin.json +8 -0
- package/docs/examples/.flows/claude-example.json +50 -0
- package/docs/examples/.plugins/claude-example-plugin/index.js +149 -0
- package/docs/examples/.plugins/claude-example-plugin/plugin.json +8 -0
- package/docs/plugin-sdk.md +731 -0
- package/package.json +11 -4
package/dist/index.js
CHANGED
|
@@ -3,27 +3,35 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { bugAnalyzeArtifacts, bugAnalyzeJsonFile, bugFixDesignJsonFile, bugFixPlanJsonFile, designReviewFile, designReviewJsonFile,
|
|
6
|
+
import { archiveActiveAttempt, bugAnalyzeArtifacts, bugAnalyzeJsonFile, bugFixDesignJsonFile, bugFixPlanJsonFile, designReviewFile, designReviewJsonFile, gitlabDiffFile, gitlabDiffJsonFile, ensureScopeWorkspaceDir, gitlabReviewFile, gitlabReviewJsonFile, instantTaskInputJsonFile, latestArtifactIteration, nextArtifactIteration, readyToMergeFile, requireArtifacts, reviewAssessmentFile, reviewAssessmentJsonFile, reviewFile, reviewFixSelectionJsonFile, reviewJsonFile, scopeWorkspaceDir, flowStateFile, taskSummaryFile, } from "./artifacts.js";
|
|
7
7
|
import { FlowInterruptedError, TaskRunnerError } from "./errors.js";
|
|
8
|
-
import { createFlowRunState,
|
|
8
|
+
import { createFlowRunState, classifyFlowLaunchAvailability, loadFlowRunState, prepareFlowStateForContinue, prepareFlowStateForResume, resetFlowRunState, rewindFlowRunStateToPhase, saveFlowRunState, stripExecutionStatePayload, } from "./flow-state.js";
|
|
9
9
|
import { requireJiraTaskFile } from "./jira.js";
|
|
10
10
|
import { validateStructuredArtifacts } from "./structured-artifacts.js";
|
|
11
|
+
import { AGENTWEAVER_REVIEW_BLOCKING_SEVERITIES_ENV, parseReviewSeverityCsv, resolveReviewBlockingSeveritiesFromEnv, } from "./review-severity.js";
|
|
11
12
|
import { summarizeBuildFailure as summarizeBuildFailureViaPipeline } from "./pipeline/build-failure-summary.js";
|
|
12
13
|
import { runNodeChecks } from "./pipeline/checks.js";
|
|
13
14
|
import { createPipelineContext } from "./pipeline/context.js";
|
|
14
|
-
import { loadDeclarativeFlow } from "./pipeline/declarative-flows.js";
|
|
15
|
+
import { collectFlowRoutingGroups, loadDeclarativeFlow } from "./pipeline/declarative-flows.js";
|
|
15
16
|
import { runExpandedPhase } from "./pipeline/declarative-flow-runner.js";
|
|
16
|
-
import { findCatalogEntry, isBuiltInCommandFlowId, loadInteractiveFlowCatalog, toDeclarativeFlowRef } from "./pipeline/flow-catalog.js";
|
|
17
|
-
import {
|
|
17
|
+
import { builtInCommandFlowFile, findCatalogEntry, flowRoutingGroups, isBuiltInCommandFlowId, loadInteractiveFlowCatalog, toDeclarativeFlowRef, } from "./pipeline/flow-catalog.js";
|
|
18
|
+
import { createPipelineRegistryContext } from "./pipeline/plugin-loader.js";
|
|
19
|
+
import { DEFAULT_LAUNCH_PROFILE, } from "./pipeline/launch-profile-config.js";
|
|
20
|
+
import { withCanonicalReviewLoopParams } from "./pipeline/review-iteration.js";
|
|
18
21
|
import { evaluateCondition, resolveValue } from "./pipeline/value-resolver.js";
|
|
19
22
|
import { resolveCmd } from "./runtime/command-resolution.js";
|
|
20
23
|
import { loadTieredEnv } from "./runtime/env-loader.js";
|
|
21
24
|
import { agentweaverHome } from "./runtime/agentweaver-home.js";
|
|
22
25
|
import { runCommand } from "./runtime/process-runner.js";
|
|
26
|
+
import { createArtifactRegistry } from "./runtime/artifact-registry.js";
|
|
23
27
|
import { resolveDesignReviewInputContract } from "./runtime/design-review-input-contract.js";
|
|
24
28
|
import { resolvePlanReviseInputContract } from "./runtime/plan-revise-input-contract.js";
|
|
29
|
+
import { resolveLatestPlanningBundle } from "./runtime/planning-bundle.js";
|
|
30
|
+
import { inspectReviewInputContract, resolveReviewInputContract } from "./runtime/review-input-contract.js";
|
|
25
31
|
import { clearReadyToMergeFile } from "./runtime/ready-to-merge.js";
|
|
26
|
-
import {
|
|
32
|
+
import { describeExecutionRouting, executorsForRoutingGroups, resolveExecutionRouting, } from "./runtime/execution-routing.js";
|
|
33
|
+
import { requestInteractiveExecutionRouting } from "./runtime/interactive-execution-routing.js";
|
|
34
|
+
import { createInteractiveSession } from "./interactive/create-interactive-session.js";
|
|
27
35
|
import { bye, printError, printInfo, printPanel, printSummary, setFlowExecutionState, stripAnsi, } from "./tui.js";
|
|
28
36
|
import { requestUserInputInTerminal } from "./user-input.js";
|
|
29
37
|
import { runDoctorCommand } from "./doctor/index.js";
|
|
@@ -31,6 +39,7 @@ import { detectGitBranchName, requestJiraContext, resolveProjectScope, } from ".
|
|
|
31
39
|
const COMMANDS = [
|
|
32
40
|
"auto-golang",
|
|
33
41
|
"auto-common",
|
|
42
|
+
"auto-simple",
|
|
34
43
|
"auto-status",
|
|
35
44
|
"auto-reset",
|
|
36
45
|
"bug-analyze",
|
|
@@ -40,6 +49,7 @@ const COMMANDS = [
|
|
|
40
49
|
"git-commit",
|
|
41
50
|
"gitlab-diff-review",
|
|
42
51
|
"gitlab-review",
|
|
52
|
+
"instant-task",
|
|
43
53
|
"mr-description",
|
|
44
54
|
"plan",
|
|
45
55
|
"plan-revise",
|
|
@@ -57,6 +67,7 @@ function createRuntimeServices(signal) {
|
|
|
57
67
|
return {
|
|
58
68
|
resolveCmd,
|
|
59
69
|
runCommand: (argv, options = {}) => runCommand(argv, { ...options, ...(signal ? { signal } : {}) }),
|
|
70
|
+
artifactRegistry: createArtifactRegistry(),
|
|
60
71
|
};
|
|
61
72
|
}
|
|
62
73
|
const runtimeServices = createRuntimeServices();
|
|
@@ -106,19 +117,24 @@ function usage() {
|
|
|
106
117
|
agentweaver bug-fix [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
107
118
|
agentweaver design-review [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
108
119
|
agentweaver doctor [<category>|<check-id>] [--json]
|
|
120
|
+
agentweaver instant-task [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>]
|
|
109
121
|
agentweaver mr-description [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
110
122
|
agentweaver plan [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] [<jira-browse-url|jira-issue-key>]
|
|
111
123
|
agentweaver plan-revise [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
112
124
|
agentweaver task-describe [--dry] [--verbose] [--prompt <text>] [<jira-browse-url|jira-issue-key>]
|
|
113
125
|
agentweaver implement [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
114
|
-
agentweaver review [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
115
|
-
agentweaver review-fix [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
116
|
-
agentweaver review-loop [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
126
|
+
agentweaver review [--dry] [--verbose] [--prompt <text>] [--scope <name>] [--blocking-severities <list>] [<jira-browse-url|jira-issue-key>]
|
|
127
|
+
agentweaver review-fix [--dry] [--verbose] [--prompt <text>] [--scope <name>] [--blocking-severities <list>] [<jira-browse-url|jira-issue-key>]
|
|
128
|
+
agentweaver review-loop [--dry] [--verbose] [--prompt <text>] [--scope <name>] [--blocking-severities <list>] [<jira-browse-url|jira-issue-key>]
|
|
117
129
|
agentweaver run-go-tests-loop [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
118
130
|
agentweaver run-go-linter-loop [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
119
131
|
agentweaver auto-golang [--dry] [--verbose] [--prompt <text>] [<jira-browse-url|jira-issue-key>]
|
|
120
132
|
agentweaver auto-golang [--dry] [--verbose] [--prompt <text>] --from <phase> [<jira-browse-url|jira-issue-key>]
|
|
121
133
|
agentweaver auto-golang --help-phases
|
|
134
|
+
agentweaver auto-common [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] <jira-browse-url|jira-issue-key>
|
|
135
|
+
agentweaver auto-common --help-phases
|
|
136
|
+
agentweaver auto-simple [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] <jira-browse-url|jira-issue-key>
|
|
137
|
+
agentweaver auto-simple --help-phases
|
|
122
138
|
agentweaver auto-status [<jira-browse-url|jira-issue-key>]
|
|
123
139
|
agentweaver auto-reset [<jira-browse-url|jira-issue-key>]
|
|
124
140
|
|
|
@@ -132,8 +148,12 @@ Flags:
|
|
|
132
148
|
--force In interactive mode, regenerate task summary in Jira-backed flows
|
|
133
149
|
--dry Fetch Jira task, but print codex/opencode commands instead of executing them
|
|
134
150
|
--verbose Show live stdout/stderr of launched commands
|
|
135
|
-
--scope Explicit workflow scope name for non-Jira runs
|
|
151
|
+
--scope Explicit workflow scope name for non-Jira runs except instant-task
|
|
136
152
|
--prompt Extra prompt text appended to the base prompt
|
|
153
|
+
--resume Resume an interrupted run when valid
|
|
154
|
+
--continue Continue a terminated iterative run when valid
|
|
155
|
+
--restart Archive the active attempt and start a fresh run
|
|
156
|
+
--blocking-severities Comma-separated severities that block merge and drive review-fix auto-selection
|
|
137
157
|
--md-lang Language for markdown output files: en (English) or ru (Russian, default)
|
|
138
158
|
|
|
139
159
|
Required environment variables:
|
|
@@ -145,6 +165,7 @@ Optional environment variables:
|
|
|
145
165
|
JIRA_BASE_URL
|
|
146
166
|
GITLAB_TOKEN
|
|
147
167
|
AGENTWEAVER_HOME
|
|
168
|
+
${AGENTWEAVER_REVIEW_BLOCKING_SEVERITIES_ENV}
|
|
148
169
|
CODEX_BIN
|
|
149
170
|
CODEX_MODEL
|
|
150
171
|
OPENCODE_BIN
|
|
@@ -152,8 +173,11 @@ Optional environment variables:
|
|
|
152
173
|
|
|
153
174
|
Notes:
|
|
154
175
|
- Jira-backed task flows will ask for Jira task via user-input when it is not passed as an argument. task-describe can also work from a manual task description without Jira.
|
|
176
|
+
- instant-task always uses the current branch-derived project scope and rejects explicit scope overrides or Jira arguments.
|
|
155
177
|
- All flow state and artifacts are stored in the current project scope by default.
|
|
156
|
-
- gitlab-review and gitlab-diff-review ask for GitLab merge request URL via user-input
|
|
178
|
+
- gitlab-review and gitlab-diff-review ask for GitLab merge request URL via user-input.
|
|
179
|
+
- ${AGENTWEAVER_REVIEW_BLOCKING_SEVERITIES_ENV} sets the default blocking severities. Default: blocker,critical,high.
|
|
180
|
+
- Interactive mode requires Ink runtime dependencies and a real TTY.`;
|
|
157
181
|
}
|
|
158
182
|
function packageVersion() {
|
|
159
183
|
const packageJsonPath = path.join(PACKAGE_ROOT, "package.json");
|
|
@@ -166,12 +190,12 @@ function packageVersion() {
|
|
|
166
190
|
function normalizeAutoPhaseId(phaseId) {
|
|
167
191
|
return phaseId.trim().toLowerCase().replaceAll("-", "_");
|
|
168
192
|
}
|
|
169
|
-
function autoPhaseIds() {
|
|
170
|
-
return loadDeclarativeFlow({ source: "built-in", fileName: "auto-golang.json" }).phases.map((phase) => phase.id);
|
|
193
|
+
async function autoPhaseIds() {
|
|
194
|
+
return (await loadDeclarativeFlow({ source: "built-in", fileName: "auto-golang.json" })).phases.map((phase) => phase.id);
|
|
171
195
|
}
|
|
172
|
-
function validateAutoPhaseId(phaseId) {
|
|
196
|
+
async function validateAutoPhaseId(phaseId) {
|
|
173
197
|
const normalized = normalizeAutoPhaseId(phaseId);
|
|
174
|
-
if (!autoPhaseIds().includes(normalized)) {
|
|
198
|
+
if (!(await autoPhaseIds()).includes(normalized)) {
|
|
175
199
|
throw new TaskRunnerError(`Unknown auto-golang phase: ${phaseId}\nUse 'agentweaver auto-golang --help-phases' or '/help auto-golang' to list valid phases.`);
|
|
176
200
|
}
|
|
177
201
|
return normalized;
|
|
@@ -183,7 +207,11 @@ function buildFlowResumeDetails(state) {
|
|
|
183
207
|
`Current step: ${currentStep}`,
|
|
184
208
|
`Updated: ${state.updatedAt}`,
|
|
185
209
|
];
|
|
186
|
-
if (state.
|
|
210
|
+
if (state.executionRouting) {
|
|
211
|
+
lines.push(`Default route: ${state.executionRouting.defaultRoute.executor} / ${state.executionRouting.defaultRoute.model}`);
|
|
212
|
+
lines.push(`Routing fingerprint: ${state.executionRouting.fingerprint}`);
|
|
213
|
+
}
|
|
214
|
+
else if (state.launchProfile) {
|
|
187
215
|
lines.push(`Launch profile: ${state.launchProfile.executor} / ${state.launchProfile.model}`);
|
|
188
216
|
}
|
|
189
217
|
if (state.lastError) {
|
|
@@ -191,78 +219,16 @@ function buildFlowResumeDetails(state) {
|
|
|
191
219
|
}
|
|
192
220
|
return lines.join("\n");
|
|
193
221
|
}
|
|
194
|
-
function
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
fields: [
|
|
202
|
-
{
|
|
203
|
-
id: "executor",
|
|
204
|
-
type: "single-select",
|
|
205
|
-
label: "Executor",
|
|
206
|
-
required: true,
|
|
207
|
-
default: defaultExecutor,
|
|
208
|
-
options: LLM_EXECUTOR_IDS.map((id) => ({
|
|
209
|
-
value: id,
|
|
210
|
-
label: id === defaultExecutor ? `${id} [default]` : id,
|
|
211
|
-
})),
|
|
212
|
-
},
|
|
213
|
-
],
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
function launchModelSelectionForm(executor) {
|
|
217
|
-
const resolvedExecutor = executor === "default" ? DEFAULT_LAUNCH_PROFILE.executor : executor;
|
|
218
|
-
const defaultModel = defaultModelForExecutor(resolvedExecutor);
|
|
219
|
-
const options = ALLOWED_MODELS_BY_EXECUTOR[resolvedExecutor].map((model) => ({
|
|
220
|
-
value: model,
|
|
221
|
-
label: model === defaultModel ? `${model} [default]` : model,
|
|
222
|
-
}));
|
|
223
|
-
return {
|
|
224
|
-
formId: "flow-launch-model",
|
|
225
|
-
title: "LLM Launch Settings",
|
|
226
|
-
description: `Select a model for the flow. Current default for ${resolvedExecutor}: ${defaultModel}.`,
|
|
227
|
-
submitLabel: "Start",
|
|
228
|
-
fields: [
|
|
229
|
-
{
|
|
230
|
-
id: "model",
|
|
231
|
-
type: "single-select",
|
|
232
|
-
label: "Model",
|
|
233
|
-
required: true,
|
|
234
|
-
default: defaultModel,
|
|
235
|
-
options,
|
|
236
|
-
},
|
|
237
|
-
],
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
function isFormCancellation(error, formId) {
|
|
241
|
-
return error instanceof TaskRunnerError && error.message === `User cancelled form '${formId}'.`;
|
|
242
|
-
}
|
|
243
|
-
async function requestInteractiveLaunchProfile(requestUserInput) {
|
|
244
|
-
for (;;) {
|
|
245
|
-
const executorFormResult = await requestUserInput(launchProfileSelectionForm());
|
|
246
|
-
const rawExecutor = String(executorFormResult.values.executor ?? DEFAULT_LAUNCH_PROFILE.executor);
|
|
247
|
-
const executor = LLM_EXECUTOR_IDS.find((id) => id === rawExecutor);
|
|
248
|
-
if (!executor) {
|
|
249
|
-
throw new TaskRunnerError(`Unsupported launch executor '${rawExecutor}'.`);
|
|
250
|
-
}
|
|
251
|
-
try {
|
|
252
|
-
const modelFormResult = await requestUserInput(launchModelSelectionForm(executor));
|
|
253
|
-
const rawModel = String(modelFormResult.values.model ?? defaultModelForExecutor(executor)).trim();
|
|
254
|
-
return resolveLaunchProfile({
|
|
255
|
-
executor,
|
|
256
|
-
model: rawModel.length > 0 ? rawModel : defaultModelForExecutor(executor),
|
|
257
|
-
}, DEFAULT_LAUNCH_PROFILE);
|
|
258
|
-
}
|
|
259
|
-
catch (error) {
|
|
260
|
-
if (isFormCancellation(error, "flow-launch-model")) {
|
|
261
|
-
continue;
|
|
262
|
-
}
|
|
263
|
-
throw error;
|
|
264
|
-
}
|
|
222
|
+
function buildFlowContinueDetails(state) {
|
|
223
|
+
const lines = [
|
|
224
|
+
"Continuable loop boundary found.",
|
|
225
|
+
`Updated: ${state.updatedAt}`,
|
|
226
|
+
];
|
|
227
|
+
if (state.continuation?.stopPhaseId && state.continuation?.stopStepId) {
|
|
228
|
+
lines.push(`Stopped at: ${state.continuation.stopPhaseId}:${state.continuation.stopStepId}`);
|
|
265
229
|
}
|
|
230
|
+
lines.push("Continue will preserve existing artifacts and start the next iteration from active inputs.");
|
|
231
|
+
return lines.join("\n");
|
|
266
232
|
}
|
|
267
233
|
function buildResolverContext(pipelineContext, flowParams, flowConstants, repeatVars, executionState) {
|
|
268
234
|
return {
|
|
@@ -337,19 +303,27 @@ function validateDeclarativePhaseResumeState(phase, phaseState, pipelineContext,
|
|
|
337
303
|
}
|
|
338
304
|
}
|
|
339
305
|
}
|
|
340
|
-
function validateDeclarativeFlowResumeState(flowEntry, config, state,
|
|
341
|
-
if (state.
|
|
342
|
-
|
|
343
|
-
|
|
306
|
+
async function validateDeclarativeFlowResumeState(flowEntry, config, state, executionRouting, runtime = runtimeServices) {
|
|
307
|
+
if (state.flowId === "auto-common") {
|
|
308
|
+
const persistedPhaseIds = state.executionState.phases.map((p) => p.id);
|
|
309
|
+
const hasLegacyPlanningGatePhases = persistedPhaseIds.some((id) => ["design_review", "verdict", "plan_revision", "design_review_repeat", "verdict_repeat"].includes(id));
|
|
310
|
+
if (hasLegacyPlanningGatePhases) {
|
|
311
|
+
throw new TaskRunnerError("Resume is impossible because the persisted state was created with the legacy phase graph. Use restart.");
|
|
344
312
|
}
|
|
345
|
-
|
|
346
|
-
|
|
313
|
+
}
|
|
314
|
+
const persistedFingerprint = state.routingFingerprint ?? state.executionRouting?.fingerprint ?? state.launchProfile?.fingerprint;
|
|
315
|
+
if (persistedFingerprint) {
|
|
316
|
+
if (!executionRouting) {
|
|
317
|
+
throw new TaskRunnerError("Resume is impossible because execution routing is missing. Use restart.");
|
|
318
|
+
}
|
|
319
|
+
if (persistedFingerprint !== executionRouting.fingerprint) {
|
|
320
|
+
throw new TaskRunnerError("Resume is impossible because execution routing changed. Use restart.");
|
|
347
321
|
}
|
|
348
322
|
}
|
|
349
323
|
if (flowRequiresTaskScope(flowEntry) && !config.jiraRef) {
|
|
350
324
|
throw new TaskRunnerError("Resume is impossible because Jira context is missing for this flow state. Use restart.");
|
|
351
325
|
}
|
|
352
|
-
const pipelineContext = createPipelineContext({
|
|
326
|
+
const pipelineContext = await createPipelineContext({
|
|
353
327
|
issueKey: config.taskKey,
|
|
354
328
|
jiraRef: config.jiraRef,
|
|
355
329
|
dryRun: config.dryRun,
|
|
@@ -357,8 +331,9 @@ function validateDeclarativeFlowResumeState(flowEntry, config, state, launchProf
|
|
|
357
331
|
...(config.mdLang !== undefined ? { mdLang: config.mdLang } : {}),
|
|
358
332
|
runtime,
|
|
359
333
|
requestUserInput: requestUserInputInTerminal,
|
|
334
|
+
...(executionRouting ? { executionRouting } : {}),
|
|
360
335
|
});
|
|
361
|
-
const flowParams = defaultDeclarativeFlowParams(config, false,
|
|
336
|
+
const flowParams = defaultDeclarativeFlowParams(config, false, executionRouting ? { executionRouting, launchProfile: executionRouting.defaultRoute } : {});
|
|
362
337
|
for (const phase of flowEntry.flow.phases) {
|
|
363
338
|
const phaseState = state.executionState.phases.find((candidate) => candidate.id === phase.id);
|
|
364
339
|
if (!phaseState) {
|
|
@@ -375,49 +350,65 @@ function scopeWithRestoredJiraContext(scope, state) {
|
|
|
375
350
|
}
|
|
376
351
|
function buildInteractiveBaseConfig(flowId, scope) {
|
|
377
352
|
return buildBaseConfig(flowId, {
|
|
378
|
-
...(scope.jiraRef ? { jiraRef: scope.jiraRef } : {}),
|
|
353
|
+
...(flowId !== "instant-task" && scope.jiraRef ? { jiraRef: scope.jiraRef } : {}),
|
|
379
354
|
});
|
|
380
355
|
}
|
|
381
|
-
function lookupInteractiveFlowResume(flowEntry, currentScope) {
|
|
356
|
+
async function lookupInteractiveFlowResume(flowEntry, currentScope) {
|
|
382
357
|
const directState = loadFlowRunState(currentScope.scopeKey, flowEntry.id);
|
|
383
|
-
|
|
358
|
+
const availability = classifyFlowLaunchAvailability(directState);
|
|
359
|
+
if (directState && availability.resume.available) {
|
|
384
360
|
try {
|
|
385
361
|
const effectiveScope = scopeWithRestoredJiraContext(currentScope, directState);
|
|
386
362
|
const baseConfig = buildInteractiveBaseConfig(flowEntry.id, effectiveScope);
|
|
387
363
|
const config = buildRuntimeConfig(baseConfig, effectiveScope);
|
|
388
|
-
validateDeclarativeFlowResumeState(flowEntry, config, directState, directState.
|
|
364
|
+
await validateDeclarativeFlowResumeState(flowEntry, config, directState, directState.executionRouting);
|
|
389
365
|
return {
|
|
390
|
-
|
|
391
|
-
hasExistingState: true,
|
|
366
|
+
...availability,
|
|
392
367
|
details: buildFlowResumeDetails(directState),
|
|
393
368
|
};
|
|
394
369
|
}
|
|
395
370
|
catch (error) {
|
|
396
371
|
return {
|
|
397
|
-
|
|
398
|
-
|
|
372
|
+
...availability,
|
|
373
|
+
resume: {
|
|
374
|
+
available: false,
|
|
375
|
+
reason: error.message,
|
|
376
|
+
},
|
|
399
377
|
details: `Interrupted run found, but resume is unavailable.\n${error.message}`,
|
|
400
378
|
};
|
|
401
379
|
}
|
|
402
380
|
}
|
|
381
|
+
if (directState && availability.continue.available) {
|
|
382
|
+
return {
|
|
383
|
+
...availability,
|
|
384
|
+
details: buildFlowContinueDetails(directState),
|
|
385
|
+
};
|
|
386
|
+
}
|
|
403
387
|
return {
|
|
404
|
-
|
|
405
|
-
hasExistingState: Boolean(directState),
|
|
388
|
+
...availability,
|
|
406
389
|
};
|
|
407
390
|
}
|
|
408
|
-
function printAutoPhasesHelp() {
|
|
409
|
-
const phaseLines = ["Available auto-golang phases:", "", ...autoPhaseIds()];
|
|
391
|
+
async function printAutoPhasesHelp() {
|
|
392
|
+
const phaseLines = ["Available auto-golang phases:", "", ...(await autoPhaseIds())];
|
|
410
393
|
phaseLines.push("", "You can resume auto-golang from a phase with:", "agentweaver auto-golang --from <phase> <jira>", "or in interactive mode:", "/auto-golang --from <phase>");
|
|
411
394
|
printPanel("Auto-Golang Phases", phaseLines.join("\n"), "magenta");
|
|
412
395
|
}
|
|
413
|
-
function autoCommonPhaseIds() {
|
|
414
|
-
return loadDeclarativeFlow({ source: "built-in", fileName: "auto-common.json" }).phases.map((phase) => phase.id);
|
|
396
|
+
async function autoCommonPhaseIds() {
|
|
397
|
+
return (await loadDeclarativeFlow({ source: "built-in", fileName: "auto-common.json" })).phases.map((phase) => phase.id);
|
|
415
398
|
}
|
|
416
|
-
function printAutoCommonPhasesHelp() {
|
|
417
|
-
const phaseLines = ["Available auto-common phases:", "", ...autoCommonPhaseIds()];
|
|
399
|
+
async function printAutoCommonPhasesHelp() {
|
|
400
|
+
const phaseLines = ["Available auto-common phases:", "", ...(await autoCommonPhaseIds())];
|
|
418
401
|
phaseLines.push("", "You can run auto-common with:", "agentweaver auto-common <jira>");
|
|
419
402
|
printPanel("Auto-Common Phases", phaseLines.join("\n"), "magenta");
|
|
420
403
|
}
|
|
404
|
+
async function autoSimplePhaseIds() {
|
|
405
|
+
return (await loadDeclarativeFlow({ source: "built-in", fileName: "auto-simple.json" })).phases.map((phase) => phase.id);
|
|
406
|
+
}
|
|
407
|
+
async function printAutoSimplePhasesHelp() {
|
|
408
|
+
const phaseLines = ["Available auto-simple phases:", "", ...(await autoSimplePhaseIds())];
|
|
409
|
+
phaseLines.push("", "You can run auto-simple with:", "agentweaver auto-simple <jira>");
|
|
410
|
+
printPanel("Auto-Simple Phases", phaseLines.join("\n"), "magenta");
|
|
411
|
+
}
|
|
421
412
|
function nextReviewIterationForTask(taskKey) {
|
|
422
413
|
return nextArtifactIteration(taskKey, "review");
|
|
423
414
|
}
|
|
@@ -430,8 +421,9 @@ function buildBaseConfig(command, options = {}) {
|
|
|
430
421
|
jiraRef: options.jiraRef ?? null,
|
|
431
422
|
scopeName: options.scopeName ?? null,
|
|
432
423
|
reviewFixPoints: options.reviewFixPoints ?? null,
|
|
424
|
+
reviewBlockingSeverities: options.reviewBlockingSeverities ?? resolveReviewBlockingSeveritiesFromEnv(),
|
|
433
425
|
extraPrompt: options.extraPrompt ?? null,
|
|
434
|
-
autoFromPhase: options.autoFromPhase
|
|
426
|
+
autoFromPhase: options.autoFromPhase ?? null,
|
|
435
427
|
mdLang: options.mdLang ?? null,
|
|
436
428
|
dryRun: options.dryRun ?? false,
|
|
437
429
|
verbose: options.verbose ?? false,
|
|
@@ -439,21 +431,23 @@ function buildBaseConfig(command, options = {}) {
|
|
|
439
431
|
};
|
|
440
432
|
}
|
|
441
433
|
function commandRequiresTask(command) {
|
|
442
|
-
return (command === "plan" ||
|
|
443
|
-
command === "plan-revise" ||
|
|
434
|
+
return (command === "plan-revise" ||
|
|
444
435
|
command === "bug-analyze" ||
|
|
445
436
|
command === "bug-fix" ||
|
|
446
437
|
command === "design-review" ||
|
|
447
438
|
command === "mr-description" ||
|
|
448
439
|
command === "auto-golang" ||
|
|
449
440
|
command === "auto-common" ||
|
|
441
|
+
command === "auto-simple" ||
|
|
450
442
|
command === "auto-status" ||
|
|
451
443
|
command === "auto-reset");
|
|
452
444
|
}
|
|
453
445
|
function commandSupportsProjectScope(command) {
|
|
454
|
-
return (command === "
|
|
446
|
+
return (command === "plan" ||
|
|
447
|
+
command === "git-commit" ||
|
|
455
448
|
command === "gitlab-diff-review" ||
|
|
456
449
|
command === "gitlab-review" ||
|
|
450
|
+
command === "instant-task" ||
|
|
457
451
|
command === "task-describe" ||
|
|
458
452
|
command === "implement" ||
|
|
459
453
|
command === "review" ||
|
|
@@ -463,9 +457,21 @@ function commandSupportsProjectScope(command) {
|
|
|
463
457
|
command === "run-go-linter-loop");
|
|
464
458
|
}
|
|
465
459
|
async function resolveScopeForCommand(config, requestUserInput) {
|
|
460
|
+
if (config.command === "instant-task") {
|
|
461
|
+
if (config.scopeName?.trim()) {
|
|
462
|
+
throw new TaskRunnerError("Command 'instant-task' rejects explicit scope overrides. The current branch-derived scope is the only supported lineage identity.");
|
|
463
|
+
}
|
|
464
|
+
if (config.jiraRef?.trim()) {
|
|
465
|
+
throw new TaskRunnerError("Command 'instant-task' does not accept a Jira task argument. Start it without a positional Jira reference.");
|
|
466
|
+
}
|
|
467
|
+
return resolveProjectScope();
|
|
468
|
+
}
|
|
466
469
|
if (config.jiraRef?.trim()) {
|
|
467
470
|
return resolveProjectScope(config.scopeName, config.jiraRef);
|
|
468
471
|
}
|
|
472
|
+
if (config.command === "plan") {
|
|
473
|
+
return resolveProjectScope(config.scopeName);
|
|
474
|
+
}
|
|
469
475
|
if (commandRequiresTask(config.command)) {
|
|
470
476
|
try {
|
|
471
477
|
const jiraContext = await requestJiraContext(requestUserInput);
|
|
@@ -496,27 +502,74 @@ function buildRuntimeConfig(baseConfig, scope) {
|
|
|
496
502
|
...(scope.jiraTaskFile ? { jiraTaskFile: scope.jiraTaskFile } : {}),
|
|
497
503
|
};
|
|
498
504
|
}
|
|
499
|
-
function
|
|
500
|
-
if (
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
505
|
+
function routingForPrerequisites(launchProfile, executionRouting) {
|
|
506
|
+
if (executionRouting) {
|
|
507
|
+
return executionRouting;
|
|
508
|
+
}
|
|
509
|
+
return resolveExecutionRouting({
|
|
510
|
+
defaultRoute: launchProfile
|
|
511
|
+
? {
|
|
512
|
+
executor: launchProfile.executor,
|
|
513
|
+
model: launchProfile.model,
|
|
514
|
+
}
|
|
515
|
+
: {
|
|
516
|
+
executor: DEFAULT_LAUNCH_PROFILE.executor,
|
|
517
|
+
model: DEFAULT_LAUNCH_PROFILE.model,
|
|
518
|
+
},
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
function flowSpecFileForPrerequisiteChecks(command) {
|
|
522
|
+
return isBuiltInCommandFlowId(command) ? builtInCommandFlowFile(command) : null;
|
|
523
|
+
}
|
|
524
|
+
async function commandRoutingGroupsForPrerequisiteChecks(command, cwd) {
|
|
525
|
+
const fileName = flowSpecFileForPrerequisiteChecks(command);
|
|
526
|
+
if (!fileName) {
|
|
527
|
+
return [];
|
|
528
|
+
}
|
|
529
|
+
return collectFlowRoutingGroups(await loadDeclarativeFlow({ source: "built-in", fileName }), cwd);
|
|
530
|
+
}
|
|
531
|
+
function resolveExecutorPrerequisite(executor, registryContext) {
|
|
532
|
+
if (executor === "codex") {
|
|
508
533
|
resolveCmd("codex", "CODEX_BIN");
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
if (executor === "opencode") {
|
|
537
|
+
resolveCmd("opencode", "OPENCODE_BIN");
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
const definition = registryContext.executors.get(executor);
|
|
541
|
+
const config = definition.defaultConfig;
|
|
542
|
+
if (config
|
|
543
|
+
&& typeof config === "object"
|
|
544
|
+
&& !Array.isArray(config)
|
|
545
|
+
&& typeof config.defaultCommand === "string"
|
|
546
|
+
&& typeof config.commandEnvVar === "string") {
|
|
547
|
+
resolveCmd(config.defaultCommand, config.commandEnvVar);
|
|
509
548
|
}
|
|
510
549
|
}
|
|
511
|
-
function
|
|
512
|
-
|
|
550
|
+
async function checkPrerequisites(config, launchProfile, executionRouting) {
|
|
551
|
+
const registryContext = await createPipelineRegistryContext(process.cwd());
|
|
552
|
+
const routing = routingForPrerequisites(launchProfile, executionRouting);
|
|
553
|
+
const groups = await commandRoutingGroupsForPrerequisiteChecks(config.command, process.cwd());
|
|
554
|
+
for (const executor of executorsForRoutingGroups(routing, groups)) {
|
|
555
|
+
resolveExecutorPrerequisite(executor, registryContext);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
async function checkAutoPrerequisites(config, launchProfile, executionRouting) {
|
|
559
|
+
await checkPrerequisites(config, launchProfile, executionRouting);
|
|
513
560
|
}
|
|
514
561
|
function autoFlowParams(config, forceRefreshSummary = false) {
|
|
515
562
|
return {
|
|
516
563
|
jiraApiUrl: config.jiraApiUrl,
|
|
517
564
|
taskKey: config.taskKey,
|
|
565
|
+
taskContextIteration: nextArtifactIteration(config.taskKey, "task-context", "json"),
|
|
566
|
+
taskSummaryIteration: nextArtifactIteration(config.taskKey, "task"),
|
|
567
|
+
designIteration: nextArtifactIteration(config.taskKey, "design"),
|
|
568
|
+
planIteration: nextArtifactIteration(config.taskKey, "plan"),
|
|
569
|
+
qaIteration: nextArtifactIteration(config.taskKey, "qa"),
|
|
518
570
|
extraPrompt: config.extraPrompt,
|
|
519
571
|
reviewFixPoints: config.reviewFixPoints,
|
|
572
|
+
reviewBlockingSeverities: config.reviewBlockingSeverities,
|
|
520
573
|
forceRefresh: forceRefreshSummary,
|
|
521
574
|
mdLang: config.mdLang,
|
|
522
575
|
runGoTestsScript: path.join(agentweaverHome(PACKAGE_ROOT), "run_go_tests.py"),
|
|
@@ -525,6 +578,72 @@ function autoFlowParams(config, forceRefreshSummary = false) {
|
|
|
525
578
|
runGoLinterIteration: nextArtifactIteration(config.taskKey, "run-go-linter-result", "json"),
|
|
526
579
|
};
|
|
527
580
|
}
|
|
581
|
+
function reviewFlowParamsFromContract(config) {
|
|
582
|
+
const contract = resolveReviewInputContract(config.taskKey);
|
|
583
|
+
return {
|
|
584
|
+
taskKey: config.taskKey,
|
|
585
|
+
planningIteration: contract.planningIteration,
|
|
586
|
+
designFile: contract.designFile,
|
|
587
|
+
designJsonFile: contract.designJsonFile,
|
|
588
|
+
planFile: contract.planFile,
|
|
589
|
+
planJsonFile: contract.planJsonFile,
|
|
590
|
+
hasTaskContextJsonFile: contract.hasTaskContextJsonFile,
|
|
591
|
+
taskContextJsonFilePath: contract.taskContextJsonFilePath,
|
|
592
|
+
taskContextJsonFile: contract.taskContextJsonFile,
|
|
593
|
+
hasJiraTaskFile: contract.hasJiraTaskFile,
|
|
594
|
+
jiraTaskFilePath: contract.jiraTaskFilePath,
|
|
595
|
+
jiraTaskFile: contract.jiraTaskFile,
|
|
596
|
+
hasTaskInputJsonFile: contract.hasTaskInputJsonFile,
|
|
597
|
+
taskInputJsonFilePath: contract.taskInputJsonFilePath,
|
|
598
|
+
taskInputJsonFile: contract.taskInputJsonFile,
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
function hasStructuredReviewInputs(taskKey) {
|
|
602
|
+
const inspection = inspectReviewInputContract(taskKey);
|
|
603
|
+
if (inspection.status === "ready") {
|
|
604
|
+
return true;
|
|
605
|
+
}
|
|
606
|
+
if (inspection.status === "missing-planning") {
|
|
607
|
+
return false;
|
|
608
|
+
}
|
|
609
|
+
throw new TaskRunnerError(`Structured review requires a normalized task-context artifact, or legacy Jira/instant-task context, in scope '${taskKey}'.`);
|
|
610
|
+
}
|
|
611
|
+
function latestTaskContextIteration(taskKey) {
|
|
612
|
+
const iteration = latestArtifactIteration(taskKey, "task-context", "json");
|
|
613
|
+
if (iteration === null) {
|
|
614
|
+
throw new TaskRunnerError(`Plan mode requires a normalized task-context artifact in scope '${taskKey}'.`);
|
|
615
|
+
}
|
|
616
|
+
return iteration;
|
|
617
|
+
}
|
|
618
|
+
function loadInstantTaskInputDefaults(taskKey) {
|
|
619
|
+
const artifactPath = instantTaskInputJsonFile(taskKey);
|
|
620
|
+
if (!existsSync(artifactPath)) {
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
try {
|
|
624
|
+
validateStructuredArtifacts([{ path: artifactPath, schemaId: "user-input/v1" }], "Instant-task source input structured artifact is invalid.");
|
|
625
|
+
const parsed = JSON.parse(readFileSync(artifactPath, "utf8"));
|
|
626
|
+
const values = parsed.values;
|
|
627
|
+
if (!values || typeof values !== "object" || Array.isArray(values)) {
|
|
628
|
+
return null;
|
|
629
|
+
}
|
|
630
|
+
const normalizedEntries = [];
|
|
631
|
+
for (const [key, value] of Object.entries(values)) {
|
|
632
|
+
if (typeof value === "string" || typeof value === "boolean") {
|
|
633
|
+
normalizedEntries.push([key, value]);
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
|
|
637
|
+
normalizedEntries.push([key, [...value]]);
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return Object.fromEntries(normalizedEntries);
|
|
642
|
+
}
|
|
643
|
+
catch {
|
|
644
|
+
return null;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
528
647
|
function interactiveFlowDefinition(entry) {
|
|
529
648
|
const flow = entry.flow;
|
|
530
649
|
return {
|
|
@@ -533,7 +652,7 @@ function interactiveFlowDefinition(entry) {
|
|
|
533
652
|
description: flow.description ?? "No description available for this flow.",
|
|
534
653
|
source: entry.source,
|
|
535
654
|
treePath: [...entry.treePath],
|
|
536
|
-
...(entry.source
|
|
655
|
+
...(entry.source !== "built-in" ? { sourcePath: entry.absolutePath } : {}),
|
|
537
656
|
phases: flow.phases.map((phase) => ({
|
|
538
657
|
id: phase.id,
|
|
539
658
|
repeatVars: Object.fromEntries(Object.entries(phase.repeatVars).map(([key, value]) => [key, value])),
|
|
@@ -583,7 +702,7 @@ function findCurrentFlowExecutionStep(state) {
|
|
|
583
702
|
return null;
|
|
584
703
|
}
|
|
585
704
|
async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, overrides = {}, requestUserInput = requestUserInputInTerminal, setSummary, launchMode = "restart", runtime = runtimeServices) {
|
|
586
|
-
const context = createPipelineContext({
|
|
705
|
+
const context = await createPipelineContext({
|
|
587
706
|
issueKey: config.taskKey,
|
|
588
707
|
jiraRef: config.jiraRef,
|
|
589
708
|
dryRun: config.dryRun,
|
|
@@ -592,35 +711,57 @@ async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, over
|
|
|
592
711
|
runtime,
|
|
593
712
|
...(setSummary ? { setSummary } : {}),
|
|
594
713
|
requestUserInput,
|
|
714
|
+
...(overrides.executionRouting ? { executionRouting: overrides.executionRouting } : {}),
|
|
595
715
|
});
|
|
596
|
-
const flow = loadDeclarativeFlow(flowRef);
|
|
716
|
+
const flow = await loadDeclarativeFlow(flowRef);
|
|
597
717
|
const initialExecutionState = {
|
|
598
718
|
flowKind: flow.kind,
|
|
599
719
|
flowVersion: flow.version,
|
|
600
720
|
terminated: false,
|
|
721
|
+
terminationOutcome: "success",
|
|
601
722
|
phases: [],
|
|
602
723
|
};
|
|
603
|
-
|
|
724
|
+
const existingStateForRestart = launchMode === "restart" ? loadFlowRunState(config.scope.scopeKey, flowId) : null;
|
|
725
|
+
let persistedState = launchMode === "resume" || launchMode === "continue"
|
|
726
|
+
? loadFlowRunState(config.scope.scopeKey, flowId)
|
|
727
|
+
: null;
|
|
604
728
|
if (persistedState && launchMode === "resume") {
|
|
605
|
-
validateDeclarativeFlowResumeState({
|
|
729
|
+
await validateDeclarativeFlowResumeState({
|
|
606
730
|
id: flowId,
|
|
607
731
|
source: flow.source,
|
|
608
732
|
fileName: flow.fileName,
|
|
609
733
|
absolutePath: flow.absolutePath,
|
|
610
734
|
treePath: [],
|
|
611
735
|
flow,
|
|
612
|
-
}, config, persistedState, overrides.launchProfile
|
|
736
|
+
}, config, persistedState, overrides.executionRouting ?? (overrides.launchProfile ? resolveExecutionRouting({ defaultRoute: {
|
|
737
|
+
executor: overrides.launchProfile.executor,
|
|
738
|
+
model: overrides.launchProfile.model,
|
|
739
|
+
} }) : undefined), runtime);
|
|
613
740
|
persistedState = prepareFlowStateForResume(persistedState);
|
|
614
741
|
}
|
|
742
|
+
else if (persistedState && launchMode === "continue") {
|
|
743
|
+
persistedState = prepareFlowStateForContinue(persistedState, flow.phases);
|
|
744
|
+
}
|
|
615
745
|
else if (launchMode === "restart") {
|
|
746
|
+
if (existingStateForRestart) {
|
|
747
|
+
archiveActiveAttempt(config.scope.scopeKey);
|
|
748
|
+
}
|
|
616
749
|
resetFlowRunState(config.scope.scopeKey, flowId);
|
|
617
750
|
}
|
|
618
751
|
const executionState = persistedState?.executionState ?? initialExecutionState;
|
|
619
752
|
const state = persistedState
|
|
620
|
-
?? createFlowRunState(config.scope.scopeKey, flowId, executionState, config.jiraRef, overrides.launchProfile);
|
|
621
|
-
if (overrides.
|
|
753
|
+
?? createFlowRunState(config.scope.scopeKey, flowId, executionState, config.jiraRef, overrides.launchProfile, overrides.executionRouting, overrides.selectedRoutingPreset);
|
|
754
|
+
if (overrides.executionRouting) {
|
|
755
|
+
state.executionRouting = overrides.executionRouting;
|
|
756
|
+
state.routingFingerprint = overrides.executionRouting.fingerprint;
|
|
757
|
+
state.launchProfile = overrides.executionRouting.defaultRoute;
|
|
758
|
+
}
|
|
759
|
+
else if (overrides.launchProfile) {
|
|
622
760
|
state.launchProfile = overrides.launchProfile;
|
|
623
761
|
}
|
|
762
|
+
if (overrides.selectedRoutingPreset) {
|
|
763
|
+
state.selectedRoutingPreset = overrides.selectedRoutingPreset;
|
|
764
|
+
}
|
|
624
765
|
state.status = "running";
|
|
625
766
|
state.lastError = null;
|
|
626
767
|
state.currentStep = findCurrentFlowExecutionStep(state);
|
|
@@ -645,7 +786,12 @@ async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, over
|
|
|
645
786
|
},
|
|
646
787
|
});
|
|
647
788
|
}
|
|
648
|
-
|
|
789
|
+
if (executionState.terminated) {
|
|
790
|
+
state.status = executionState.terminationOutcome === "success" ? "completed" : "blocked";
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
793
|
+
state.status = "completed";
|
|
794
|
+
}
|
|
649
795
|
state.currentStep = null;
|
|
650
796
|
state.lastError = null;
|
|
651
797
|
state.executionState = executionState;
|
|
@@ -671,12 +817,24 @@ async function runDeclarativeFlowBySpecFile(fileName, config, flowParams, overri
|
|
|
671
817
|
...defaultDeclarativeFlowParams(config, false, overrides),
|
|
672
818
|
...flowParams,
|
|
673
819
|
};
|
|
674
|
-
await runDeclarativeFlowByRef(config.command, { source: "built-in", fileName }, config, mergedFlowParams, overrides, requestUserInput, setSummary, launchMode, runtime);
|
|
820
|
+
await runDeclarativeFlowByRef(config.command, { source: "built-in", fileName }, config, withCanonicalReviewLoopParams((await loadDeclarativeFlow({ source: "built-in", fileName })).kind, mergedFlowParams), overrides, requestUserInput, setSummary, launchMode, runtime);
|
|
675
821
|
}
|
|
676
822
|
function defaultDeclarativeFlowParams(config, forceRefreshSummary = false, overrides = {}) {
|
|
677
823
|
const iteration = nextReviewIterationForTask(config.taskKey);
|
|
678
824
|
const latestIteration = latestArtifactIteration(config.taskKey, "review");
|
|
679
|
-
const
|
|
825
|
+
const latestTaskContext = latestArtifactIteration(config.taskKey, "task-context", "json");
|
|
826
|
+
const executionRouting = overrides.executionRouting ?? resolveExecutionRouting({
|
|
827
|
+
defaultRoute: overrides.launchProfile
|
|
828
|
+
? {
|
|
829
|
+
executor: overrides.launchProfile.executor,
|
|
830
|
+
model: overrides.launchProfile.model,
|
|
831
|
+
}
|
|
832
|
+
: {
|
|
833
|
+
executor: DEFAULT_LAUNCH_PROFILE.executor,
|
|
834
|
+
model: DEFAULT_LAUNCH_PROFILE.model,
|
|
835
|
+
},
|
|
836
|
+
});
|
|
837
|
+
const launchProfile = executionRouting.defaultRoute;
|
|
680
838
|
return {
|
|
681
839
|
taskKey: config.taskKey,
|
|
682
840
|
jiraRef: config.jiraRef,
|
|
@@ -687,12 +845,17 @@ function defaultDeclarativeFlowParams(config, forceRefreshSummary = false, overr
|
|
|
687
845
|
workspaceDir: scopeWorkspaceDir(config.taskKey),
|
|
688
846
|
extraPrompt: config.extraPrompt,
|
|
689
847
|
reviewFixPoints: config.reviewFixPoints,
|
|
848
|
+
reviewBlockingSeverities: config.reviewBlockingSeverities,
|
|
690
849
|
mdLang: config.mdLang,
|
|
691
850
|
llmExecutor: launchProfile.executor,
|
|
692
851
|
llmModel: launchProfile.model,
|
|
693
852
|
launchProfile,
|
|
853
|
+
executionRouting,
|
|
694
854
|
iteration,
|
|
855
|
+
baseIteration: iteration,
|
|
856
|
+
designReviewBaseIteration: nextDesignReviewIterationForTask(config.taskKey),
|
|
695
857
|
latestIteration,
|
|
858
|
+
taskContextIteration: latestTaskContext ?? nextArtifactIteration(config.taskKey, "task-context", "json"),
|
|
696
859
|
taskSummaryIteration: nextArtifactIteration(config.taskKey, "task"),
|
|
697
860
|
designIteration: nextArtifactIteration(config.taskKey, "design"),
|
|
698
861
|
planIteration: nextArtifactIteration(config.taskKey, "plan"),
|
|
@@ -701,6 +864,60 @@ function defaultDeclarativeFlowParams(config, forceRefreshSummary = false, overr
|
|
|
701
864
|
forceRefresh: forceRefreshSummary,
|
|
702
865
|
};
|
|
703
866
|
}
|
|
867
|
+
function countAvailableNonRestartActions(availability) {
|
|
868
|
+
return Number(availability.resume.available) + Number(availability.continue.available);
|
|
869
|
+
}
|
|
870
|
+
async function chooseLaunchMode(flowId, scopeKey, explicitLaunchMode, requestUserInput) {
|
|
871
|
+
const state = loadFlowRunState(scopeKey, flowId);
|
|
872
|
+
const availability = classifyFlowLaunchAvailability(state);
|
|
873
|
+
if (explicitLaunchMode) {
|
|
874
|
+
const selectedAvailability = availability[explicitLaunchMode];
|
|
875
|
+
if (!selectedAvailability.available) {
|
|
876
|
+
throw new TaskRunnerError(`${explicitLaunchMode.charAt(0).toUpperCase()}${explicitLaunchMode.slice(1)} is not available for '${flowId}'. ${selectedAvailability.reason}`);
|
|
877
|
+
}
|
|
878
|
+
return explicitLaunchMode;
|
|
879
|
+
}
|
|
880
|
+
if (!availability.hasExistingState) {
|
|
881
|
+
return "restart";
|
|
882
|
+
}
|
|
883
|
+
const availableNonRestart = countAvailableNonRestartActions(availability);
|
|
884
|
+
if (availableNonRestart === 0) {
|
|
885
|
+
return "restart";
|
|
886
|
+
}
|
|
887
|
+
const interactive = requestUserInput !== requestUserInputInTerminal || (process.stdin.isTTY && process.stdout.isTTY);
|
|
888
|
+
if (!interactive) {
|
|
889
|
+
throw new TaskRunnerError(`Multiple actions are valid for '${flowId}'. Re-run with one of: --resume, --continue, --restart.`);
|
|
890
|
+
}
|
|
891
|
+
const result = await requestUserInput({
|
|
892
|
+
formId: `launch-mode-${flowId}`,
|
|
893
|
+
title: "Launch Action",
|
|
894
|
+
description: `Select how to start '${flowId}'.`,
|
|
895
|
+
submitLabel: "Start",
|
|
896
|
+
fields: [
|
|
897
|
+
{
|
|
898
|
+
id: "launchMode",
|
|
899
|
+
type: "single-select",
|
|
900
|
+
label: "Action",
|
|
901
|
+
required: true,
|
|
902
|
+
default: availability.continue.available ? "continue" : availability.resume.available ? "resume" : "restart",
|
|
903
|
+
options: [
|
|
904
|
+
...(availability.resume.available
|
|
905
|
+
? [{ value: "resume", label: "Resume", description: availability.resume.reason }]
|
|
906
|
+
: []),
|
|
907
|
+
...(availability.continue.available
|
|
908
|
+
? [{ value: "continue", label: "Continue", description: availability.continue.reason }]
|
|
909
|
+
: []),
|
|
910
|
+
{ value: "restart", label: "Restart", description: availability.restart.reason },
|
|
911
|
+
],
|
|
912
|
+
},
|
|
913
|
+
],
|
|
914
|
+
});
|
|
915
|
+
const selected = result.values.launchMode;
|
|
916
|
+
if (selected !== "resume" && selected !== "continue" && selected !== "restart") {
|
|
917
|
+
throw new TaskRunnerError(`Invalid launch action selected for '${flowId}'.`);
|
|
918
|
+
}
|
|
919
|
+
return selected;
|
|
920
|
+
}
|
|
704
921
|
const TASK_SCOPE_PARAM_REFS = new Set(["params.jiraApiUrl", "params.jiraBrowseUrl", "params.jiraTaskFile"]);
|
|
705
922
|
function valueReferencesTaskScopeParams(value) {
|
|
706
923
|
if (Array.isArray(value)) {
|
|
@@ -723,7 +940,7 @@ function flowRequiresTaskScope(entry) {
|
|
|
723
940
|
return valueReferencesTaskScopeParams(entry.flow.phases);
|
|
724
941
|
}
|
|
725
942
|
async function summarizeBuildFailure(output) {
|
|
726
|
-
return summarizeBuildFailureViaPipeline(createPipelineContext({
|
|
943
|
+
return summarizeBuildFailureViaPipeline(await createPipelineContext({
|
|
727
944
|
issueKey: "build-failure-summary",
|
|
728
945
|
jiraRef: "build-failure-summary",
|
|
729
946
|
dryRun: false,
|
|
@@ -738,22 +955,56 @@ function requireJiraConfig(config) {
|
|
|
738
955
|
throw new TaskRunnerError(`Command '${config.command}' requires Jira context in the current project scope.`);
|
|
739
956
|
}
|
|
740
957
|
}
|
|
741
|
-
async function executeCommand(baseConfig, runFollowupVerify = true, requestUserInput = requestUserInputInTerminal, resolvedScope, setSummary, forceRefreshSummary = false,
|
|
958
|
+
async function executeCommand(baseConfig, runFollowupVerify = true, requestUserInput = requestUserInputInTerminal, resolvedScope, setSummary, forceRefreshSummary = false, explicitLaunchMode, launchProfile, executionRouting, selectedRoutingPreset, runtime = runtimeServices) {
|
|
742
959
|
if (baseConfig.command === "doctor") {
|
|
743
960
|
const exitCode = await runDoctorCommand(baseConfig.doctorArgs ?? []);
|
|
744
961
|
return exitCode === 0;
|
|
745
962
|
}
|
|
746
963
|
const config = buildRuntimeConfig(baseConfig, resolvedScope ?? (await resolveScopeForCommand(baseConfig, requestUserInput)));
|
|
964
|
+
const flowOverrides = executionRouting
|
|
965
|
+
? {
|
|
966
|
+
launchProfile: executionRouting.defaultRoute,
|
|
967
|
+
executionRouting,
|
|
968
|
+
...(selectedRoutingPreset ? { selectedRoutingPreset } : {}),
|
|
969
|
+
}
|
|
970
|
+
: launchProfile
|
|
971
|
+
? { launchProfile }
|
|
972
|
+
: {};
|
|
973
|
+
const launchMode = config.command === "auto-status" || config.command === "auto-reset"
|
|
974
|
+
? "restart"
|
|
975
|
+
: await chooseLaunchMode(config.command, config.scope.scopeKey, explicitLaunchMode, requestUserInput);
|
|
976
|
+
if (config.command === "instant-task") {
|
|
977
|
+
await checkPrerequisites(config, launchProfile, executionRouting);
|
|
978
|
+
const hasPersistedInstantTaskState = loadFlowRunState(config.scope.scopeKey, "instant-task") !== null;
|
|
979
|
+
const repromptInstantTaskInput = launchMode === "restart"
|
|
980
|
+
&& hasPersistedInstantTaskState
|
|
981
|
+
&& requestUserInput !== requestUserInputInTerminal;
|
|
982
|
+
await runDeclarativeFlowBySpecFile("instant-task.json", config, {
|
|
983
|
+
taskKey: config.taskKey,
|
|
984
|
+
taskContextIteration: nextArtifactIteration(config.taskKey, "task-context", "json"),
|
|
985
|
+
designIteration: nextArtifactIteration(config.taskKey, "design"),
|
|
986
|
+
planIteration: nextArtifactIteration(config.taskKey, "plan"),
|
|
987
|
+
qaIteration: nextArtifactIteration(config.taskKey, "qa"),
|
|
988
|
+
extraPrompt: config.extraPrompt,
|
|
989
|
+
mdLang: config.mdLang,
|
|
990
|
+
repromptInstantTaskInput,
|
|
991
|
+
...(repromptInstantTaskInput
|
|
992
|
+
? { prefilledInstantTaskInputValues: loadInstantTaskInputDefaults(config.taskKey) ?? undefined }
|
|
993
|
+
: {}),
|
|
994
|
+
}, flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
995
|
+
return !config.dryRun && existsSync(readyToMergeFile(config.taskKey));
|
|
996
|
+
}
|
|
747
997
|
if (config.command === "auto-golang") {
|
|
748
998
|
requireJiraConfig(config);
|
|
749
|
-
checkAutoPrerequisites(config);
|
|
750
999
|
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl;
|
|
751
1000
|
process.env.JIRA_API_URL = config.jiraApiUrl;
|
|
752
1001
|
process.env.JIRA_TASK_FILE = config.jiraTaskFile;
|
|
753
1002
|
let effectiveLaunchMode = launchMode;
|
|
754
1003
|
let effectiveLaunchProfile = launchProfile;
|
|
1004
|
+
let effectiveExecutionRouting = executionRouting;
|
|
755
1005
|
if (config.autoFromPhase) {
|
|
756
|
-
|
|
1006
|
+
config.autoFromPhase = await validateAutoPhaseId(config.autoFromPhase);
|
|
1007
|
+
const flow = await loadDeclarativeFlow({ source: "built-in", fileName: "auto-golang.json" });
|
|
757
1008
|
const persistedState = loadFlowRunState(config.scope.scopeKey, "auto-golang");
|
|
758
1009
|
if (!persistedState) {
|
|
759
1010
|
throw new TaskRunnerError(`Cannot restart auto-golang from phase '${config.autoFromPhase}' because persisted flow state was not found.`);
|
|
@@ -762,18 +1013,37 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
762
1013
|
saveFlowRunState(persistedState);
|
|
763
1014
|
effectiveLaunchMode = "resume";
|
|
764
1015
|
effectiveLaunchProfile ??= persistedState.launchProfile;
|
|
1016
|
+
effectiveExecutionRouting ??= persistedState.executionRouting;
|
|
765
1017
|
printPanel("Auto-Golang Resume", `Auto-golang pipeline will continue from phase: ${config.autoFromPhase}`, "yellow");
|
|
766
1018
|
}
|
|
767
|
-
await
|
|
1019
|
+
await checkAutoPrerequisites(config, effectiveLaunchProfile, effectiveExecutionRouting);
|
|
1020
|
+
await runDeclarativeFlowBySpecFile("auto-golang.json", config, autoFlowParams(config, forceRefreshSummary), effectiveExecutionRouting
|
|
1021
|
+
? {
|
|
1022
|
+
launchProfile: effectiveExecutionRouting.defaultRoute,
|
|
1023
|
+
executionRouting: effectiveExecutionRouting,
|
|
1024
|
+
...(selectedRoutingPreset ? { selectedRoutingPreset } : {}),
|
|
1025
|
+
}
|
|
1026
|
+
: effectiveLaunchProfile
|
|
1027
|
+
? { launchProfile: effectiveLaunchProfile }
|
|
1028
|
+
: {}, requestUserInput, setSummary, effectiveLaunchMode, runtime);
|
|
768
1029
|
return false;
|
|
769
1030
|
}
|
|
770
1031
|
if (config.command === "auto-common") {
|
|
771
1032
|
requireJiraConfig(config);
|
|
772
|
-
checkAutoPrerequisites(config);
|
|
1033
|
+
await checkAutoPrerequisites(config, launchProfile, executionRouting);
|
|
773
1034
|
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl;
|
|
774
1035
|
process.env.JIRA_API_URL = config.jiraApiUrl;
|
|
775
1036
|
process.env.JIRA_TASK_FILE = config.jiraTaskFile;
|
|
776
|
-
await runDeclarativeFlowBySpecFile("auto-common.json", config, autoFlowParams(config, forceRefreshSummary),
|
|
1037
|
+
await runDeclarativeFlowBySpecFile("auto-common.json", config, autoFlowParams(config, forceRefreshSummary), flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
1038
|
+
return false;
|
|
1039
|
+
}
|
|
1040
|
+
if (config.command === "auto-simple") {
|
|
1041
|
+
requireJiraConfig(config);
|
|
1042
|
+
await checkAutoPrerequisites(config, launchProfile, executionRouting);
|
|
1043
|
+
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl;
|
|
1044
|
+
process.env.JIRA_API_URL = config.jiraApiUrl;
|
|
1045
|
+
process.env.JIRA_TASK_FILE = config.jiraTaskFile;
|
|
1046
|
+
await runDeclarativeFlowBySpecFile("auto-simple.json", config, autoFlowParams(config, forceRefreshSummary), flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
777
1047
|
return false;
|
|
778
1048
|
}
|
|
779
1049
|
if (config.command === "auto-status") {
|
|
@@ -783,14 +1053,18 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
783
1053
|
return false;
|
|
784
1054
|
}
|
|
785
1055
|
const currentStep = findCurrentFlowExecutionStep(state) ?? state.currentStep ?? "-";
|
|
786
|
-
const phaseOrder = loadDeclarativeFlow({ source: "built-in", fileName: "auto-golang.json" }).phases;
|
|
1056
|
+
const phaseOrder = (await loadDeclarativeFlow({ source: "built-in", fileName: "auto-golang.json" })).phases;
|
|
787
1057
|
const lines = [
|
|
788
1058
|
`Issue: ${config.taskKey}`,
|
|
789
1059
|
`Status: ${state.status}`,
|
|
790
1060
|
`Current step: ${currentStep}`,
|
|
791
1061
|
`Updated: ${state.updatedAt}`,
|
|
792
1062
|
];
|
|
793
|
-
if (state.
|
|
1063
|
+
if (state.executionRouting) {
|
|
1064
|
+
lines.push(`Default route: ${state.executionRouting.defaultRoute.executor} / ${state.executionRouting.defaultRoute.model}`);
|
|
1065
|
+
lines.push(`Routing fingerprint: ${state.executionRouting.fingerprint}`);
|
|
1066
|
+
}
|
|
1067
|
+
else if (state.launchProfile) {
|
|
794
1068
|
lines.push(`Launch profile: ${state.launchProfile.executor} / ${state.launchProfile.model}`);
|
|
795
1069
|
}
|
|
796
1070
|
if (state.lastError) {
|
|
@@ -816,7 +1090,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
816
1090
|
printPanel("Auto-Golang Reset", removed ? `State file ${flowStateFile(config.scope.scopeKey, "auto-golang")} removed.` : "No flow state file found.", "yellow");
|
|
817
1091
|
return false;
|
|
818
1092
|
}
|
|
819
|
-
checkPrerequisites(config);
|
|
1093
|
+
await checkPrerequisites(config, launchProfile, executionRouting);
|
|
820
1094
|
if (config.jiraBrowseUrl && config.jiraApiUrl && config.jiraTaskFile) {
|
|
821
1095
|
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl ?? "";
|
|
822
1096
|
process.env.JIRA_API_URL = config.jiraApiUrl ?? "";
|
|
@@ -828,22 +1102,37 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
828
1102
|
delete process.env.JIRA_TASK_FILE;
|
|
829
1103
|
}
|
|
830
1104
|
if (config.command === "plan") {
|
|
831
|
-
|
|
832
|
-
if (config.
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
1105
|
+
let taskContextIteration;
|
|
1106
|
+
if (config.jiraRef) {
|
|
1107
|
+
requireJiraConfig(config);
|
|
1108
|
+
if (config.verbose) {
|
|
1109
|
+
process.stdout.write(`Fetching Jira issue from browse URL: ${config.jiraBrowseUrl}\n`);
|
|
1110
|
+
process.stdout.write(`Resolved Jira API URL: ${config.jiraApiUrl}\n`);
|
|
1111
|
+
process.stdout.write(`Saving Jira issue JSON to: ${config.jiraTaskFile}\n`);
|
|
1112
|
+
}
|
|
1113
|
+
taskContextIteration = nextArtifactIteration(config.taskKey, "task-context", "json");
|
|
1114
|
+
await runDeclarativeFlowBySpecFile("task-source/jira-fetch.json", config, {
|
|
1115
|
+
jiraApiUrl: config.jiraApiUrl,
|
|
1116
|
+
taskKey: config.taskKey,
|
|
1117
|
+
}, flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
1118
|
+
await runDeclarativeFlowBySpecFile("normalize-task-source.json", config, {
|
|
1119
|
+
taskKey: config.taskKey,
|
|
1120
|
+
iteration: taskContextIteration,
|
|
1121
|
+
extraPrompt: config.extraPrompt,
|
|
1122
|
+
}, flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
1123
|
+
}
|
|
1124
|
+
else {
|
|
1125
|
+
taskContextIteration = latestTaskContextIteration(config.taskKey);
|
|
836
1126
|
}
|
|
837
1127
|
await runDeclarativeFlowBySpecFile("plan.json", config, {
|
|
838
|
-
jiraApiUrl: config.jiraApiUrl,
|
|
839
1128
|
taskKey: config.taskKey,
|
|
840
|
-
|
|
1129
|
+
taskContextIteration,
|
|
841
1130
|
designIteration: nextArtifactIteration(config.taskKey, "design"),
|
|
842
1131
|
planIteration: nextArtifactIteration(config.taskKey, "plan"),
|
|
843
1132
|
qaIteration: nextArtifactIteration(config.taskKey, "qa"),
|
|
844
1133
|
extraPrompt: config.extraPrompt,
|
|
845
1134
|
forceRefresh: forceRefreshSummary,
|
|
846
|
-
},
|
|
1135
|
+
}, flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
847
1136
|
return false;
|
|
848
1137
|
}
|
|
849
1138
|
if (config.command === "bug-analyze") {
|
|
@@ -862,7 +1151,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
862
1151
|
bugFixPlanIteration: nextArtifactIteration(config.taskKey, "bug-fix-plan"),
|
|
863
1152
|
extraPrompt: config.extraPrompt,
|
|
864
1153
|
forceRefresh: forceRefreshSummary,
|
|
865
|
-
},
|
|
1154
|
+
}, flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
866
1155
|
return false;
|
|
867
1156
|
}
|
|
868
1157
|
if (config.command === "design-review") {
|
|
@@ -884,6 +1173,9 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
884
1173
|
qaJsonFilePath: inputContract.qaJsonFilePath,
|
|
885
1174
|
qaFile: inputContract.qaFile,
|
|
886
1175
|
qaJsonFile: inputContract.qaJsonFile,
|
|
1176
|
+
hasTaskContextJsonFile: inputContract.hasTaskContextJsonFile,
|
|
1177
|
+
taskContextJsonFilePath: inputContract.taskContextJsonFilePath,
|
|
1178
|
+
taskContextJsonFile: inputContract.taskContextJsonFile,
|
|
887
1179
|
hasJiraTaskFile: inputContract.hasJiraTaskFile,
|
|
888
1180
|
jiraTaskFilePath: inputContract.jiraTaskFilePath,
|
|
889
1181
|
jiraTaskFile: inputContract.jiraTaskFile,
|
|
@@ -896,8 +1188,11 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
896
1188
|
hasPlanningAnswersJsonFile: inputContract.hasPlanningAnswersJsonFile,
|
|
897
1189
|
planningAnswersJsonFilePath: inputContract.planningAnswersJsonFilePath,
|
|
898
1190
|
planningAnswersJsonFile: inputContract.planningAnswersJsonFile,
|
|
1191
|
+
hasTaskInputJsonFile: inputContract.hasTaskInputJsonFile,
|
|
1192
|
+
taskInputJsonFilePath: inputContract.taskInputJsonFilePath,
|
|
1193
|
+
taskInputJsonFile: inputContract.taskInputJsonFile,
|
|
899
1194
|
extraPrompt: config.extraPrompt,
|
|
900
|
-
},
|
|
1195
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
901
1196
|
if (!config.dryRun) {
|
|
902
1197
|
printSummary("Design Review", `Artifacts:\n${designReviewFile(config.taskKey, iteration)}\n${designReviewJsonFile(config.taskKey, iteration)}`);
|
|
903
1198
|
}
|
|
@@ -930,6 +1225,9 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
930
1225
|
revisedPlanJsonFile: inputContract.revisedPlanJsonFile,
|
|
931
1226
|
revisedQaFile: inputContract.revisedQaFile,
|
|
932
1227
|
revisedQaJsonFile: inputContract.revisedQaJsonFile,
|
|
1228
|
+
hasTaskContextJsonFile: inputContract.hasTaskContextJsonFile,
|
|
1229
|
+
taskContextJsonFilePath: inputContract.taskContextJsonFilePath,
|
|
1230
|
+
taskContextJsonFile: inputContract.taskContextJsonFile,
|
|
933
1231
|
hasJiraTaskFile: inputContract.hasJiraTaskFile,
|
|
934
1232
|
jiraTaskFilePath: inputContract.jiraTaskFilePath,
|
|
935
1233
|
jiraTaskFile: inputContract.jiraTaskFile,
|
|
@@ -942,8 +1240,11 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
942
1240
|
hasPlanningAnswersJsonFile: inputContract.hasPlanningAnswersJsonFile,
|
|
943
1241
|
planningAnswersJsonFilePath: inputContract.planningAnswersJsonFilePath,
|
|
944
1242
|
planningAnswersJsonFile: inputContract.planningAnswersJsonFile,
|
|
1243
|
+
hasTaskInputJsonFile: inputContract.hasTaskInputJsonFile,
|
|
1244
|
+
taskInputJsonFilePath: inputContract.taskInputJsonFilePath,
|
|
1245
|
+
taskInputJsonFile: inputContract.taskInputJsonFile,
|
|
945
1246
|
extraPrompt: config.extraPrompt,
|
|
946
|
-
},
|
|
1247
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
947
1248
|
if (!config.dryRun) {
|
|
948
1249
|
printSummary("Plan Revise", `Artifacts:\n${inputContract.revisedDesignFile}\n${inputContract.revisedDesignJsonFile}\n${inputContract.revisedPlanFile}\n${inputContract.revisedPlanJsonFile}\n${inputContract.revisedQaFile}\n${inputContract.revisedQaJsonFile}`);
|
|
949
1250
|
}
|
|
@@ -959,7 +1260,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
959
1260
|
extraPrompt: config.extraPrompt,
|
|
960
1261
|
reviewFixSelectionJsonFile: reviewFixSelectionJsonFile(config.taskKey, iteration),
|
|
961
1262
|
reviewFixPoints: config.reviewFixPoints,
|
|
962
|
-
},
|
|
1263
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
963
1264
|
if (!config.dryRun) {
|
|
964
1265
|
printSummary("GitLab Review", `Artifacts:\n${gitlabReviewFile(config.taskKey)}\n${gitlabReviewJsonFile(config.taskKey)}\n${reviewFile(config.taskKey, iteration)}\n${reviewJsonFile(config.taskKey, iteration)}\n${reviewAssessmentFile(config.taskKey, iteration)}\n${reviewAssessmentJsonFile(config.taskKey, iteration)}`);
|
|
965
1266
|
}
|
|
@@ -973,7 +1274,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
973
1274
|
iteration,
|
|
974
1275
|
gitlabDiffIteration,
|
|
975
1276
|
extraPrompt: config.extraPrompt,
|
|
976
|
-
},
|
|
1277
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
977
1278
|
if (!config.dryRun) {
|
|
978
1279
|
printSummary("GitLab Diff Review", `Artifacts:\n${gitlabDiffFile(config.taskKey)}\n${gitlabDiffJsonFile(config.taskKey)}\n${reviewFile(config.taskKey, iteration)}\n${reviewJsonFile(config.taskKey, iteration)}`);
|
|
979
1280
|
}
|
|
@@ -991,7 +1292,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
991
1292
|
await runDeclarativeFlowBySpecFile("bugz/bug-fix.json", config, {
|
|
992
1293
|
taskKey: config.taskKey,
|
|
993
1294
|
extraPrompt: config.extraPrompt,
|
|
994
|
-
},
|
|
1295
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
995
1296
|
return false;
|
|
996
1297
|
}
|
|
997
1298
|
if (config.command === "mr-description") {
|
|
@@ -1001,7 +1302,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
1001
1302
|
taskKey: config.taskKey,
|
|
1002
1303
|
iteration: nextArtifactIteration(config.taskKey, "mr-description"),
|
|
1003
1304
|
extraPrompt: config.extraPrompt,
|
|
1004
|
-
},
|
|
1305
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1005
1306
|
return false;
|
|
1006
1307
|
}
|
|
1007
1308
|
if (config.command === "task-describe") {
|
|
@@ -1011,42 +1312,33 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
1011
1312
|
taskKey: config.taskKey,
|
|
1012
1313
|
iteration,
|
|
1013
1314
|
extraPrompt: config.extraPrompt,
|
|
1014
|
-
},
|
|
1315
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1015
1316
|
return false;
|
|
1016
1317
|
}
|
|
1017
1318
|
if (config.command === "implement") {
|
|
1018
|
-
|
|
1019
|
-
validateStructuredArtifacts([
|
|
1020
|
-
{ path: designJsonFile(config.taskKey), schemaId: "implementation-design/v1" },
|
|
1021
|
-
{ path: planJsonFile(config.taskKey), schemaId: "implementation-plan/v1" },
|
|
1022
|
-
{ path: qaJsonFile(config.taskKey), schemaId: "qa-plan/v1" },
|
|
1023
|
-
], "Implement mode requires valid structured plan artifacts from the planning phase.");
|
|
1319
|
+
const planningBundle = resolveLatestPlanningBundle(config.taskKey);
|
|
1024
1320
|
await runDeclarativeFlowBySpecFile("implement.json", config, {
|
|
1025
1321
|
taskKey: config.taskKey,
|
|
1322
|
+
planningIteration: planningBundle.planningIteration,
|
|
1026
1323
|
extraPrompt: config.extraPrompt,
|
|
1027
|
-
},
|
|
1324
|
+
}, flowOverrides, requestUserInput, undefined, launchMode);
|
|
1028
1325
|
return false;
|
|
1029
1326
|
}
|
|
1030
1327
|
if (config.command === "review") {
|
|
1031
1328
|
const iteration = nextReviewIterationForTask(config.taskKey);
|
|
1032
|
-
if (config.
|
|
1033
|
-
requireJiraConfig(config);
|
|
1034
|
-
validateStructuredArtifacts([
|
|
1035
|
-
{ path: designJsonFile(config.taskKey), schemaId: "implementation-design/v1" },
|
|
1036
|
-
{ path: planJsonFile(config.taskKey), schemaId: "implementation-plan/v1" },
|
|
1037
|
-
], "Review mode requires valid structured plan artifacts from the planning phase.");
|
|
1329
|
+
if (hasStructuredReviewInputs(config.taskKey)) {
|
|
1038
1330
|
await runDeclarativeFlowBySpecFile("review/review.json", config, {
|
|
1039
|
-
|
|
1331
|
+
...reviewFlowParamsFromContract(config),
|
|
1040
1332
|
iteration,
|
|
1041
1333
|
extraPrompt: config.extraPrompt,
|
|
1042
|
-
},
|
|
1334
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1043
1335
|
}
|
|
1044
1336
|
else {
|
|
1045
1337
|
await runDeclarativeFlowBySpecFile("review/review-project.json", config, {
|
|
1046
1338
|
taskKey: config.taskKey,
|
|
1047
1339
|
iteration,
|
|
1048
1340
|
extraPrompt: config.extraPrompt,
|
|
1049
|
-
},
|
|
1341
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1050
1342
|
}
|
|
1051
1343
|
return !config.dryRun && existsSync(readyToMergeFile(config.taskKey));
|
|
1052
1344
|
}
|
|
@@ -1065,23 +1357,21 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
1065
1357
|
reviewFixSelectionJsonFile: reviewFixSelectionJsonFile(config.taskKey, latestIteration),
|
|
1066
1358
|
extraPrompt: config.extraPrompt,
|
|
1067
1359
|
reviewFixPoints: config.reviewFixPoints,
|
|
1068
|
-
},
|
|
1360
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1069
1361
|
return false;
|
|
1070
1362
|
}
|
|
1071
1363
|
if (config.command === "review-loop") {
|
|
1072
1364
|
const iteration = nextReviewIterationForTask(config.taskKey);
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
taskKey: config.taskKey,
|
|
1082
|
-
iteration,
|
|
1365
|
+
const reviewLoopSpec = hasStructuredReviewInputs(config.taskKey)
|
|
1366
|
+
? "review/review-loop.json"
|
|
1367
|
+
: "review/review-project-loop.json";
|
|
1368
|
+
await runDeclarativeFlowBySpecFile(reviewLoopSpec, config, {
|
|
1369
|
+
...(reviewLoopSpec === "review/review-loop.json"
|
|
1370
|
+
? reviewFlowParamsFromContract(config)
|
|
1371
|
+
: { taskKey: config.taskKey }),
|
|
1372
|
+
baseIteration: iteration,
|
|
1083
1373
|
extraPrompt: config.extraPrompt,
|
|
1084
|
-
},
|
|
1374
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1085
1375
|
return !config.dryRun && existsSync(readyToMergeFile(config.taskKey));
|
|
1086
1376
|
}
|
|
1087
1377
|
if (config.command === "run-go-tests-loop" || config.command === "run-go-linter-loop") {
|
|
@@ -1092,19 +1382,19 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
1092
1382
|
runGoTestsIteration: nextArtifactIteration(config.taskKey, "run-go-tests-result", "json"),
|
|
1093
1383
|
runGoLinterIteration: nextArtifactIteration(config.taskKey, "run-go-linter-result", "json"),
|
|
1094
1384
|
extraPrompt: config.extraPrompt,
|
|
1095
|
-
},
|
|
1385
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1096
1386
|
return false;
|
|
1097
1387
|
}
|
|
1098
1388
|
if (config.command === "git-commit") {
|
|
1099
1389
|
await runDeclarativeFlowBySpecFile("git-commit.json", config, {
|
|
1100
1390
|
taskKey: config.taskKey,
|
|
1101
1391
|
extraPrompt: config.extraPrompt,
|
|
1102
|
-
},
|
|
1392
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1103
1393
|
return false;
|
|
1104
1394
|
}
|
|
1105
1395
|
throw new TaskRunnerError(`Unsupported command: ${config.command}`);
|
|
1106
1396
|
}
|
|
1107
|
-
function parseCliArgs(argv) {
|
|
1397
|
+
async function parseCliArgs(argv) {
|
|
1108
1398
|
if (argv.includes("--version") || argv.includes("-v")) {
|
|
1109
1399
|
process.stdout.write(`${packageVersion()}\n`);
|
|
1110
1400
|
process.exit(0);
|
|
@@ -1127,9 +1417,11 @@ function parseCliArgs(argv) {
|
|
|
1127
1417
|
let prompt;
|
|
1128
1418
|
let autoFromPhase;
|
|
1129
1419
|
let scopeName;
|
|
1420
|
+
let reviewBlockingSeverities;
|
|
1130
1421
|
let helpPhases = false;
|
|
1131
1422
|
let jiraRef;
|
|
1132
1423
|
let mdLang;
|
|
1424
|
+
let launchMode;
|
|
1133
1425
|
const doctorArgs = [];
|
|
1134
1426
|
for (let index = 1; index < argv.length; index += 1) {
|
|
1135
1427
|
const token = argv[index] ?? "";
|
|
@@ -1145,6 +1437,14 @@ function parseCliArgs(argv) {
|
|
|
1145
1437
|
helpPhases = true;
|
|
1146
1438
|
continue;
|
|
1147
1439
|
}
|
|
1440
|
+
if (token === "--resume" || token === "--continue" || token === "--restart") {
|
|
1441
|
+
if (launchMode) {
|
|
1442
|
+
process.stderr.write("Error: --resume, --continue, and --restart are mutually exclusive.\n");
|
|
1443
|
+
process.exit(1);
|
|
1444
|
+
}
|
|
1445
|
+
launchMode = token.slice(2);
|
|
1446
|
+
continue;
|
|
1447
|
+
}
|
|
1148
1448
|
if (token === "--prompt") {
|
|
1149
1449
|
prompt = argv[index + 1];
|
|
1150
1450
|
index += 1;
|
|
@@ -1155,6 +1455,15 @@ function parseCliArgs(argv) {
|
|
|
1155
1455
|
index += 1;
|
|
1156
1456
|
continue;
|
|
1157
1457
|
}
|
|
1458
|
+
if (token === "--blocking-severities") {
|
|
1459
|
+
reviewBlockingSeverities = parseReviewSeverityCsv(argv[index + 1] ?? "");
|
|
1460
|
+
index += 1;
|
|
1461
|
+
continue;
|
|
1462
|
+
}
|
|
1463
|
+
if (token.startsWith("--blocking-severities=")) {
|
|
1464
|
+
reviewBlockingSeverities = parseReviewSeverityCsv(token.slice("--blocking-severities=".length));
|
|
1465
|
+
continue;
|
|
1466
|
+
}
|
|
1158
1467
|
if (token === "--from") {
|
|
1159
1468
|
autoFromPhase = argv[index + 1];
|
|
1160
1469
|
index += 1;
|
|
@@ -1191,11 +1500,15 @@ function parseCliArgs(argv) {
|
|
|
1191
1500
|
}
|
|
1192
1501
|
}
|
|
1193
1502
|
if (command === "auto-golang" && helpPhases) {
|
|
1194
|
-
printAutoPhasesHelp();
|
|
1503
|
+
await printAutoPhasesHelp();
|
|
1195
1504
|
process.exit(0);
|
|
1196
1505
|
}
|
|
1197
1506
|
if (command === "auto-common" && helpPhases) {
|
|
1198
|
-
printAutoCommonPhasesHelp();
|
|
1507
|
+
await printAutoCommonPhasesHelp();
|
|
1508
|
+
process.exit(0);
|
|
1509
|
+
}
|
|
1510
|
+
if (command === "auto-simple" && helpPhases) {
|
|
1511
|
+
await printAutoSimplePhasesHelp();
|
|
1199
1512
|
process.exit(0);
|
|
1200
1513
|
}
|
|
1201
1514
|
return {
|
|
@@ -1205,16 +1518,19 @@ function parseCliArgs(argv) {
|
|
|
1205
1518
|
helpPhases,
|
|
1206
1519
|
...(jiraRef !== undefined ? { jiraRef } : {}),
|
|
1207
1520
|
...(scopeName !== undefined ? { scopeName } : {}),
|
|
1521
|
+
...(reviewBlockingSeverities !== undefined ? { reviewBlockingSeverities } : {}),
|
|
1208
1522
|
...(prompt !== undefined ? { prompt } : {}),
|
|
1209
1523
|
...(autoFromPhase !== undefined ? { autoFromPhase } : {}),
|
|
1210
1524
|
...(mdLang !== undefined ? { mdLang } : {}),
|
|
1211
1525
|
...(doctorArgs.length > 0 ? { doctorArgs } : {}),
|
|
1526
|
+
...(launchMode !== undefined ? { launchMode } : {}),
|
|
1212
1527
|
};
|
|
1213
1528
|
}
|
|
1214
1529
|
function buildConfigFromArgs(args) {
|
|
1215
1530
|
return buildBaseConfig(args.command, {
|
|
1216
1531
|
...(args.jiraRef !== undefined ? { jiraRef: args.jiraRef } : {}),
|
|
1217
1532
|
...(args.scopeName !== undefined ? { scopeName: args.scopeName } : {}),
|
|
1533
|
+
...(args.reviewBlockingSeverities !== undefined ? { reviewBlockingSeverities: args.reviewBlockingSeverities } : {}),
|
|
1218
1534
|
...(args.prompt !== undefined ? { extraPrompt: args.prompt } : {}),
|
|
1219
1535
|
...(args.autoFromPhase !== undefined ? { autoFromPhase: args.autoFromPhase } : {}),
|
|
1220
1536
|
...(args.mdLang !== undefined ? { mdLang: args.mdLang } : {}),
|
|
@@ -1226,11 +1542,11 @@ function buildConfigFromArgs(args) {
|
|
|
1226
1542
|
async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
1227
1543
|
let currentScope = resolveProjectScope(scopeName, jiraRef);
|
|
1228
1544
|
const gitBranchName = detectGitBranchName();
|
|
1229
|
-
const flowCatalog = loadInteractiveFlowCatalog(process.cwd());
|
|
1545
|
+
const flowCatalog = await loadInteractiveFlowCatalog(process.cwd());
|
|
1230
1546
|
let activeAbortController = null;
|
|
1231
1547
|
let activeFlowId = null;
|
|
1232
1548
|
let exiting = false;
|
|
1233
|
-
const ui =
|
|
1549
|
+
const ui = createInteractiveSession({
|
|
1234
1550
|
scopeKey: currentScope.scopeKey,
|
|
1235
1551
|
jiraIssueKey: currentScope.jiraIssueKey ?? null,
|
|
1236
1552
|
summaryText: "",
|
|
@@ -1243,7 +1559,7 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1243
1559
|
if (!flowEntry) {
|
|
1244
1560
|
throw new TaskRunnerError(`Unknown flow: ${flowId}`);
|
|
1245
1561
|
}
|
|
1246
|
-
const resumeLookup = lookupInteractiveFlowResume(flowEntry, currentScope);
|
|
1562
|
+
const resumeLookup = await lookupInteractiveFlowResume(flowEntry, currentScope);
|
|
1247
1563
|
return resumeLookup;
|
|
1248
1564
|
},
|
|
1249
1565
|
onRun: async (flowId, launchMode) => {
|
|
@@ -1255,16 +1571,23 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1255
1571
|
if (!flowEntry) {
|
|
1256
1572
|
throw new TaskRunnerError(`Unknown flow: ${flowId}`);
|
|
1257
1573
|
}
|
|
1574
|
+
const routingGroups = await flowRoutingGroups(flowEntry, process.cwd());
|
|
1258
1575
|
const resumeState = launchMode === "resume" ? loadFlowRunState(currentScope.scopeKey, flowId) : null;
|
|
1259
1576
|
if (resumeState) {
|
|
1260
1577
|
currentScope = scopeWithRestoredJiraContext(currentScope, resumeState);
|
|
1261
1578
|
}
|
|
1262
|
-
const
|
|
1263
|
-
? resumeState?.
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1579
|
+
const routingSelection = launchMode === "resume"
|
|
1580
|
+
? (resumeState?.executionRouting
|
|
1581
|
+
? {
|
|
1582
|
+
routing: resumeState.executionRouting,
|
|
1583
|
+
selectedPreset: resumeState.selectedRoutingPreset ?? { kind: "custom", label: "Saved routing" },
|
|
1584
|
+
}
|
|
1585
|
+
: null)
|
|
1586
|
+
: await requestInteractiveExecutionRouting(flowEntry, (form) => ui.requestUserInput(form));
|
|
1587
|
+
if (launchMode === "resume" && !routingSelection?.routing) {
|
|
1588
|
+
throw new TaskRunnerError("Resume is impossible because execution routing was not saved. Use restart.");
|
|
1267
1589
|
}
|
|
1590
|
+
const launchProfile = routingSelection?.routing?.defaultRoute;
|
|
1268
1591
|
const previousScopeKey = currentScope.scopeKey;
|
|
1269
1592
|
const baseConfig = buildInteractiveBaseConfig(flowId, currentScope);
|
|
1270
1593
|
if (flowEntry.source === "built-in" && isBuiltInCommandFlowId(flowId)) {
|
|
@@ -1279,13 +1602,20 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1279
1602
|
if (previousScopeKey !== currentScope.scopeKey || currentScope.jiraIssueKey) {
|
|
1280
1603
|
syncInteractiveTaskSummary(ui, currentScope, forceRefresh);
|
|
1281
1604
|
}
|
|
1282
|
-
|
|
1605
|
+
if (routingSelection?.routing) {
|
|
1606
|
+
printPanel("Effective Launch Config", `preset: ${routingSelection.selectedPreset.label}\nmode: ${launchMode}\n${describeExecutionRouting(routingSelection.routing, routingGroups)}`, "cyan");
|
|
1607
|
+
}
|
|
1283
1608
|
if (flowEntry.source === "built-in" && isBuiltInCommandFlowId(flowId)) {
|
|
1284
|
-
await executeCommand(baseConfig, true, (form) => ui.requestUserInput(form), currentScope, (markdown) => ui.setSummary(markdown), forceRefresh, launchMode, launchProfile, createRuntimeServices(abortController.signal));
|
|
1609
|
+
await executeCommand(baseConfig, true, (form) => ui.requestUserInput(form), currentScope, (markdown) => ui.setSummary(markdown), forceRefresh, launchMode, launchProfile, routingSelection?.routing, routingSelection?.selectedPreset, createRuntimeServices(abortController.signal));
|
|
1285
1610
|
return;
|
|
1286
1611
|
}
|
|
1287
1612
|
const runtimeConfig = buildRuntimeConfig(baseConfig, currentScope);
|
|
1288
|
-
|
|
1613
|
+
const flowOverrides = {
|
|
1614
|
+
...(launchProfile ? { launchProfile } : {}),
|
|
1615
|
+
...(routingSelection?.routing ? { executionRouting: routingSelection.routing } : {}),
|
|
1616
|
+
...(routingSelection?.selectedPreset ? { selectedRoutingPreset: routingSelection.selectedPreset } : {}),
|
|
1617
|
+
};
|
|
1618
|
+
await runDeclarativeFlowByRef(flowId, toDeclarativeFlowRef(flowEntry), runtimeConfig, defaultDeclarativeFlowParams(runtimeConfig, forceRefresh, flowOverrides), flowOverrides, (form) => ui.requestUserInput(form), (markdown) => ui.setSummary(markdown), launchMode, createRuntimeServices(abortController.signal));
|
|
1289
1619
|
}
|
|
1290
1620
|
catch (error) {
|
|
1291
1621
|
if (error instanceof FlowInterruptedError) {
|
|
@@ -1363,8 +1693,8 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1363
1693
|
if (args.length === 1 && !args[0]?.startsWith("-") && !COMMANDS.includes(args[0])) {
|
|
1364
1694
|
return await runInteractive(args[0] ?? "", forceRefresh);
|
|
1365
1695
|
}
|
|
1366
|
-
const parsedArgs = parseCliArgs(args);
|
|
1367
|
-
const commandCompleted = await executeCommand(buildConfigFromArgs(parsedArgs));
|
|
1696
|
+
const parsedArgs = await parseCliArgs(args);
|
|
1697
|
+
const commandCompleted = await executeCommand(buildConfigFromArgs(parsedArgs), true, requestUserInputInTerminal, undefined, undefined, false, parsedArgs.launchMode);
|
|
1368
1698
|
if (parsedArgs.command === "doctor") {
|
|
1369
1699
|
return commandCompleted ? 0 : 1;
|
|
1370
1700
|
}
|