agentweaver 0.1.15 → 0.1.16
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 +26 -9
- package/dist/artifact-manifest.js +219 -0
- package/dist/artifacts.js +15 -0
- package/dist/doctor/checks/env-diagnostics.js +25 -0
- package/dist/doctor/checks/flow-readiness.js +15 -18
- package/dist/flow-state.js +75 -15
- package/dist/index.js +391 -175
- package/dist/interactive/blessed-session.js +361 -0
- package/dist/interactive/controller.js +1293 -0
- package/dist/interactive/create-interactive-session.js +5 -0
- package/dist/interactive/ink/index.js +576 -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/context.js +1 -0
- package/dist/pipeline/declarative-flow-runner.js +212 -6
- package/dist/pipeline/declarative-flows.js +27 -0
- package/dist/pipeline/execution-routing-config.js +15 -0
- package/dist/pipeline/flow-catalog.js +19 -3
- package/dist/pipeline/flow-run-resume.js +29 -0
- package/dist/pipeline/flow-specs/auto-common.json +89 -360
- package/dist/pipeline/flow-specs/auto-golang.json +58 -363
- 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 +304 -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 +48 -70
- 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/node-registry.js +41 -1
- 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 +226 -7
- 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 +32 -12
- 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/prompt-registry.js +3 -1
- package/dist/pipeline/prompt-runtime.js +4 -1
- package/dist/pipeline/review-iteration.js +26 -0
- package/dist/pipeline/spec-compiler.js +2 -0
- package/dist/pipeline/spec-types.js +3 -0
- package/dist/pipeline/spec-validator.js +14 -0
- package/dist/pipeline/value-resolver.js +74 -1
- package/dist/prompts.js +36 -14
- package/dist/review-severity.js +45 -0
- package/dist/runtime/artifact-registry.js +402 -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 +227 -0
- package/dist/runtime/interactive-execution-routing.js +462 -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/package.json +6 -3
package/dist/index.js
CHANGED
|
@@ -3,27 +3,34 @@ 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 { 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
8
|
import { createFlowRunState, hasResumableFlowState, loadFlowRunState, 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 { DEFAULT_LAUNCH_PROFILE, } from "./pipeline/launch-profile-config.js";
|
|
19
|
+
import { withCanonicalReviewLoopParams } from "./pipeline/review-iteration.js";
|
|
18
20
|
import { evaluateCondition, resolveValue } from "./pipeline/value-resolver.js";
|
|
19
21
|
import { resolveCmd } from "./runtime/command-resolution.js";
|
|
20
22
|
import { loadTieredEnv } from "./runtime/env-loader.js";
|
|
21
23
|
import { agentweaverHome } from "./runtime/agentweaver-home.js";
|
|
22
24
|
import { runCommand } from "./runtime/process-runner.js";
|
|
25
|
+
import { createArtifactRegistry } from "./runtime/artifact-registry.js";
|
|
23
26
|
import { resolveDesignReviewInputContract } from "./runtime/design-review-input-contract.js";
|
|
24
27
|
import { resolvePlanReviseInputContract } from "./runtime/plan-revise-input-contract.js";
|
|
28
|
+
import { resolveLatestPlanningBundle } from "./runtime/planning-bundle.js";
|
|
29
|
+
import { inspectReviewInputContract, resolveReviewInputContract } from "./runtime/review-input-contract.js";
|
|
25
30
|
import { clearReadyToMergeFile } from "./runtime/ready-to-merge.js";
|
|
26
|
-
import {
|
|
31
|
+
import { describeExecutionRouting, executorsForRoutingGroups, resolveExecutionRouting, } from "./runtime/execution-routing.js";
|
|
32
|
+
import { requestInteractiveExecutionRouting } from "./runtime/interactive-execution-routing.js";
|
|
33
|
+
import { createInteractiveSession } from "./interactive/create-interactive-session.js";
|
|
27
34
|
import { bye, printError, printInfo, printPanel, printSummary, setFlowExecutionState, stripAnsi, } from "./tui.js";
|
|
28
35
|
import { requestUserInputInTerminal } from "./user-input.js";
|
|
29
36
|
import { runDoctorCommand } from "./doctor/index.js";
|
|
@@ -31,6 +38,7 @@ import { detectGitBranchName, requestJiraContext, resolveProjectScope, } from ".
|
|
|
31
38
|
const COMMANDS = [
|
|
32
39
|
"auto-golang",
|
|
33
40
|
"auto-common",
|
|
41
|
+
"auto-simple",
|
|
34
42
|
"auto-status",
|
|
35
43
|
"auto-reset",
|
|
36
44
|
"bug-analyze",
|
|
@@ -40,6 +48,7 @@ const COMMANDS = [
|
|
|
40
48
|
"git-commit",
|
|
41
49
|
"gitlab-diff-review",
|
|
42
50
|
"gitlab-review",
|
|
51
|
+
"instant-task",
|
|
43
52
|
"mr-description",
|
|
44
53
|
"plan",
|
|
45
54
|
"plan-revise",
|
|
@@ -57,6 +66,7 @@ function createRuntimeServices(signal) {
|
|
|
57
66
|
return {
|
|
58
67
|
resolveCmd,
|
|
59
68
|
runCommand: (argv, options = {}) => runCommand(argv, { ...options, ...(signal ? { signal } : {}) }),
|
|
69
|
+
artifactRegistry: createArtifactRegistry(),
|
|
60
70
|
};
|
|
61
71
|
}
|
|
62
72
|
const runtimeServices = createRuntimeServices();
|
|
@@ -106,19 +116,24 @@ function usage() {
|
|
|
106
116
|
agentweaver bug-fix [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
107
117
|
agentweaver design-review [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
108
118
|
agentweaver doctor [<category>|<check-id>] [--json]
|
|
119
|
+
agentweaver instant-task [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>]
|
|
109
120
|
agentweaver mr-description [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
110
121
|
agentweaver plan [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] [<jira-browse-url|jira-issue-key>]
|
|
111
122
|
agentweaver plan-revise [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
112
123
|
agentweaver task-describe [--dry] [--verbose] [--prompt <text>] [<jira-browse-url|jira-issue-key>]
|
|
113
124
|
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>]
|
|
125
|
+
agentweaver review [--dry] [--verbose] [--prompt <text>] [--scope <name>] [--blocking-severities <list>] [<jira-browse-url|jira-issue-key>]
|
|
126
|
+
agentweaver review-fix [--dry] [--verbose] [--prompt <text>] [--scope <name>] [--blocking-severities <list>] [<jira-browse-url|jira-issue-key>]
|
|
127
|
+
agentweaver review-loop [--dry] [--verbose] [--prompt <text>] [--scope <name>] [--blocking-severities <list>] [<jira-browse-url|jira-issue-key>]
|
|
117
128
|
agentweaver run-go-tests-loop [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
118
129
|
agentweaver run-go-linter-loop [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
119
130
|
agentweaver auto-golang [--dry] [--verbose] [--prompt <text>] [<jira-browse-url|jira-issue-key>]
|
|
120
131
|
agentweaver auto-golang [--dry] [--verbose] [--prompt <text>] --from <phase> [<jira-browse-url|jira-issue-key>]
|
|
121
132
|
agentweaver auto-golang --help-phases
|
|
133
|
+
agentweaver auto-common [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] <jira-browse-url|jira-issue-key>
|
|
134
|
+
agentweaver auto-common --help-phases
|
|
135
|
+
agentweaver auto-simple [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] <jira-browse-url|jira-issue-key>
|
|
136
|
+
agentweaver auto-simple --help-phases
|
|
122
137
|
agentweaver auto-status [<jira-browse-url|jira-issue-key>]
|
|
123
138
|
agentweaver auto-reset [<jira-browse-url|jira-issue-key>]
|
|
124
139
|
|
|
@@ -132,8 +147,9 @@ Flags:
|
|
|
132
147
|
--force In interactive mode, regenerate task summary in Jira-backed flows
|
|
133
148
|
--dry Fetch Jira task, but print codex/opencode commands instead of executing them
|
|
134
149
|
--verbose Show live stdout/stderr of launched commands
|
|
135
|
-
--scope Explicit workflow scope name for non-Jira runs
|
|
150
|
+
--scope Explicit workflow scope name for non-Jira runs except instant-task
|
|
136
151
|
--prompt Extra prompt text appended to the base prompt
|
|
152
|
+
--blocking-severities Comma-separated severities that block merge and drive review-fix auto-selection
|
|
137
153
|
--md-lang Language for markdown output files: en (English) or ru (Russian, default)
|
|
138
154
|
|
|
139
155
|
Required environment variables:
|
|
@@ -145,6 +161,7 @@ Optional environment variables:
|
|
|
145
161
|
JIRA_BASE_URL
|
|
146
162
|
GITLAB_TOKEN
|
|
147
163
|
AGENTWEAVER_HOME
|
|
164
|
+
${AGENTWEAVER_REVIEW_BLOCKING_SEVERITIES_ENV}
|
|
148
165
|
CODEX_BIN
|
|
149
166
|
CODEX_MODEL
|
|
150
167
|
OPENCODE_BIN
|
|
@@ -152,8 +169,11 @@ Optional environment variables:
|
|
|
152
169
|
|
|
153
170
|
Notes:
|
|
154
171
|
- 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.
|
|
172
|
+
- instant-task always uses the current branch-derived project scope and rejects explicit scope overrides or Jira arguments.
|
|
155
173
|
- 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
|
|
174
|
+
- gitlab-review and gitlab-diff-review ask for GitLab merge request URL via user-input.
|
|
175
|
+
- ${AGENTWEAVER_REVIEW_BLOCKING_SEVERITIES_ENV} sets the default blocking severities. Default: blocker,critical,high.
|
|
176
|
+
- Interactive mode requires Ink runtime dependencies and a real TTY.`;
|
|
157
177
|
}
|
|
158
178
|
function packageVersion() {
|
|
159
179
|
const packageJsonPath = path.join(PACKAGE_ROOT, "package.json");
|
|
@@ -183,7 +203,11 @@ function buildFlowResumeDetails(state) {
|
|
|
183
203
|
`Current step: ${currentStep}`,
|
|
184
204
|
`Updated: ${state.updatedAt}`,
|
|
185
205
|
];
|
|
186
|
-
if (state.
|
|
206
|
+
if (state.executionRouting) {
|
|
207
|
+
lines.push(`Default route: ${state.executionRouting.defaultRoute.executor} / ${state.executionRouting.defaultRoute.model}`);
|
|
208
|
+
lines.push(`Routing fingerprint: ${state.executionRouting.fingerprint}`);
|
|
209
|
+
}
|
|
210
|
+
else if (state.launchProfile) {
|
|
187
211
|
lines.push(`Launch profile: ${state.launchProfile.executor} / ${state.launchProfile.model}`);
|
|
188
212
|
}
|
|
189
213
|
if (state.lastError) {
|
|
@@ -191,79 +215,6 @@ function buildFlowResumeDetails(state) {
|
|
|
191
215
|
}
|
|
192
216
|
return lines.join("\n");
|
|
193
217
|
}
|
|
194
|
-
function launchProfileSelectionForm() {
|
|
195
|
-
const defaultExecutor = DEFAULT_LAUNCH_PROFILE.executor;
|
|
196
|
-
return {
|
|
197
|
-
formId: "flow-launch-profile",
|
|
198
|
-
title: "LLM Launch Settings",
|
|
199
|
-
description: `Select an executor for the flow. Current default: ${defaultExecutor}.`,
|
|
200
|
-
submitLabel: "Continue",
|
|
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
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
218
|
function buildResolverContext(pipelineContext, flowParams, flowConstants, repeatVars, executionState) {
|
|
268
219
|
return {
|
|
269
220
|
flowParams,
|
|
@@ -337,13 +288,21 @@ function validateDeclarativePhaseResumeState(phase, phaseState, pipelineContext,
|
|
|
337
288
|
}
|
|
338
289
|
}
|
|
339
290
|
}
|
|
340
|
-
function validateDeclarativeFlowResumeState(flowEntry, config, state,
|
|
341
|
-
if (state.
|
|
342
|
-
|
|
343
|
-
|
|
291
|
+
function validateDeclarativeFlowResumeState(flowEntry, config, state, executionRouting, runtime = runtimeServices) {
|
|
292
|
+
if (state.flowId === "auto-common") {
|
|
293
|
+
const persistedPhaseIds = state.executionState.phases.map((p) => p.id);
|
|
294
|
+
const hasLegacyPlanningGatePhases = persistedPhaseIds.some((id) => ["design_review", "verdict", "plan_revision", "design_review_repeat", "verdict_repeat"].includes(id));
|
|
295
|
+
if (hasLegacyPlanningGatePhases) {
|
|
296
|
+
throw new TaskRunnerError("Resume is impossible because the persisted state was created with the legacy phase graph. Use restart.");
|
|
344
297
|
}
|
|
345
|
-
|
|
346
|
-
|
|
298
|
+
}
|
|
299
|
+
const persistedFingerprint = state.routingFingerprint ?? state.executionRouting?.fingerprint ?? state.launchProfile?.fingerprint;
|
|
300
|
+
if (persistedFingerprint) {
|
|
301
|
+
if (!executionRouting) {
|
|
302
|
+
throw new TaskRunnerError("Resume is impossible because execution routing is missing. Use restart.");
|
|
303
|
+
}
|
|
304
|
+
if (persistedFingerprint !== executionRouting.fingerprint) {
|
|
305
|
+
throw new TaskRunnerError("Resume is impossible because execution routing changed. Use restart.");
|
|
347
306
|
}
|
|
348
307
|
}
|
|
349
308
|
if (flowRequiresTaskScope(flowEntry) && !config.jiraRef) {
|
|
@@ -357,8 +316,9 @@ function validateDeclarativeFlowResumeState(flowEntry, config, state, launchProf
|
|
|
357
316
|
...(config.mdLang !== undefined ? { mdLang: config.mdLang } : {}),
|
|
358
317
|
runtime,
|
|
359
318
|
requestUserInput: requestUserInputInTerminal,
|
|
319
|
+
...(executionRouting ? { executionRouting } : {}),
|
|
360
320
|
});
|
|
361
|
-
const flowParams = defaultDeclarativeFlowParams(config, false,
|
|
321
|
+
const flowParams = defaultDeclarativeFlowParams(config, false, executionRouting ? { executionRouting, launchProfile: executionRouting.defaultRoute } : {});
|
|
362
322
|
for (const phase of flowEntry.flow.phases) {
|
|
363
323
|
const phaseState = state.executionState.phases.find((candidate) => candidate.id === phase.id);
|
|
364
324
|
if (!phaseState) {
|
|
@@ -375,7 +335,7 @@ function scopeWithRestoredJiraContext(scope, state) {
|
|
|
375
335
|
}
|
|
376
336
|
function buildInteractiveBaseConfig(flowId, scope) {
|
|
377
337
|
return buildBaseConfig(flowId, {
|
|
378
|
-
...(scope.jiraRef ? { jiraRef: scope.jiraRef } : {}),
|
|
338
|
+
...(flowId !== "instant-task" && scope.jiraRef ? { jiraRef: scope.jiraRef } : {}),
|
|
379
339
|
});
|
|
380
340
|
}
|
|
381
341
|
function lookupInteractiveFlowResume(flowEntry, currentScope) {
|
|
@@ -385,7 +345,7 @@ function lookupInteractiveFlowResume(flowEntry, currentScope) {
|
|
|
385
345
|
const effectiveScope = scopeWithRestoredJiraContext(currentScope, directState);
|
|
386
346
|
const baseConfig = buildInteractiveBaseConfig(flowEntry.id, effectiveScope);
|
|
387
347
|
const config = buildRuntimeConfig(baseConfig, effectiveScope);
|
|
388
|
-
validateDeclarativeFlowResumeState(flowEntry, config, directState, directState.
|
|
348
|
+
validateDeclarativeFlowResumeState(flowEntry, config, directState, directState.executionRouting);
|
|
389
349
|
return {
|
|
390
350
|
resumeAvailable: true,
|
|
391
351
|
hasExistingState: true,
|
|
@@ -418,6 +378,14 @@ function printAutoCommonPhasesHelp() {
|
|
|
418
378
|
phaseLines.push("", "You can run auto-common with:", "agentweaver auto-common <jira>");
|
|
419
379
|
printPanel("Auto-Common Phases", phaseLines.join("\n"), "magenta");
|
|
420
380
|
}
|
|
381
|
+
function autoSimplePhaseIds() {
|
|
382
|
+
return loadDeclarativeFlow({ source: "built-in", fileName: "auto-simple.json" }).phases.map((phase) => phase.id);
|
|
383
|
+
}
|
|
384
|
+
function printAutoSimplePhasesHelp() {
|
|
385
|
+
const phaseLines = ["Available auto-simple phases:", "", ...autoSimplePhaseIds()];
|
|
386
|
+
phaseLines.push("", "You can run auto-simple with:", "agentweaver auto-simple <jira>");
|
|
387
|
+
printPanel("Auto-Simple Phases", phaseLines.join("\n"), "magenta");
|
|
388
|
+
}
|
|
421
389
|
function nextReviewIterationForTask(taskKey) {
|
|
422
390
|
return nextArtifactIteration(taskKey, "review");
|
|
423
391
|
}
|
|
@@ -430,6 +398,7 @@ function buildBaseConfig(command, options = {}) {
|
|
|
430
398
|
jiraRef: options.jiraRef ?? null,
|
|
431
399
|
scopeName: options.scopeName ?? null,
|
|
432
400
|
reviewFixPoints: options.reviewFixPoints ?? null,
|
|
401
|
+
reviewBlockingSeverities: options.reviewBlockingSeverities ?? resolveReviewBlockingSeveritiesFromEnv(),
|
|
433
402
|
extraPrompt: options.extraPrompt ?? null,
|
|
434
403
|
autoFromPhase: options.autoFromPhase ? validateAutoPhaseId(options.autoFromPhase) : null,
|
|
435
404
|
mdLang: options.mdLang ?? null,
|
|
@@ -439,21 +408,23 @@ function buildBaseConfig(command, options = {}) {
|
|
|
439
408
|
};
|
|
440
409
|
}
|
|
441
410
|
function commandRequiresTask(command) {
|
|
442
|
-
return (command === "plan" ||
|
|
443
|
-
command === "plan-revise" ||
|
|
411
|
+
return (command === "plan-revise" ||
|
|
444
412
|
command === "bug-analyze" ||
|
|
445
413
|
command === "bug-fix" ||
|
|
446
414
|
command === "design-review" ||
|
|
447
415
|
command === "mr-description" ||
|
|
448
416
|
command === "auto-golang" ||
|
|
449
417
|
command === "auto-common" ||
|
|
418
|
+
command === "auto-simple" ||
|
|
450
419
|
command === "auto-status" ||
|
|
451
420
|
command === "auto-reset");
|
|
452
421
|
}
|
|
453
422
|
function commandSupportsProjectScope(command) {
|
|
454
|
-
return (command === "
|
|
423
|
+
return (command === "plan" ||
|
|
424
|
+
command === "git-commit" ||
|
|
455
425
|
command === "gitlab-diff-review" ||
|
|
456
426
|
command === "gitlab-review" ||
|
|
427
|
+
command === "instant-task" ||
|
|
457
428
|
command === "task-describe" ||
|
|
458
429
|
command === "implement" ||
|
|
459
430
|
command === "review" ||
|
|
@@ -463,9 +434,21 @@ function commandSupportsProjectScope(command) {
|
|
|
463
434
|
command === "run-go-linter-loop");
|
|
464
435
|
}
|
|
465
436
|
async function resolveScopeForCommand(config, requestUserInput) {
|
|
437
|
+
if (config.command === "instant-task") {
|
|
438
|
+
if (config.scopeName?.trim()) {
|
|
439
|
+
throw new TaskRunnerError("Command 'instant-task' rejects explicit scope overrides. The current branch-derived scope is the only supported lineage identity.");
|
|
440
|
+
}
|
|
441
|
+
if (config.jiraRef?.trim()) {
|
|
442
|
+
throw new TaskRunnerError("Command 'instant-task' does not accept a Jira task argument. Start it without a positional Jira reference.");
|
|
443
|
+
}
|
|
444
|
+
return resolveProjectScope();
|
|
445
|
+
}
|
|
466
446
|
if (config.jiraRef?.trim()) {
|
|
467
447
|
return resolveProjectScope(config.scopeName, config.jiraRef);
|
|
468
448
|
}
|
|
449
|
+
if (config.command === "plan") {
|
|
450
|
+
return resolveProjectScope(config.scopeName);
|
|
451
|
+
}
|
|
469
452
|
if (commandRequiresTask(config.command)) {
|
|
470
453
|
try {
|
|
471
454
|
const jiraContext = await requestJiraContext(requestUserInput);
|
|
@@ -496,27 +479,61 @@ function buildRuntimeConfig(baseConfig, scope) {
|
|
|
496
479
|
...(scope.jiraTaskFile ? { jiraTaskFile: scope.jiraTaskFile } : {}),
|
|
497
480
|
};
|
|
498
481
|
}
|
|
499
|
-
function
|
|
500
|
-
if (
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
482
|
+
function routingForPrerequisites(launchProfile, executionRouting) {
|
|
483
|
+
if (executionRouting) {
|
|
484
|
+
return executionRouting;
|
|
485
|
+
}
|
|
486
|
+
return resolveExecutionRouting({
|
|
487
|
+
defaultRoute: launchProfile
|
|
488
|
+
? {
|
|
489
|
+
executor: launchProfile.executor,
|
|
490
|
+
model: launchProfile.model,
|
|
491
|
+
}
|
|
492
|
+
: {
|
|
493
|
+
executor: DEFAULT_LAUNCH_PROFILE.executor,
|
|
494
|
+
model: DEFAULT_LAUNCH_PROFILE.model,
|
|
495
|
+
},
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
function flowSpecFileForPrerequisiteChecks(command) {
|
|
499
|
+
return isBuiltInCommandFlowId(command) ? builtInCommandFlowFile(command) : null;
|
|
500
|
+
}
|
|
501
|
+
function commandRoutingGroupsForPrerequisiteChecks(command, cwd) {
|
|
502
|
+
const fileName = flowSpecFileForPrerequisiteChecks(command);
|
|
503
|
+
if (!fileName) {
|
|
504
|
+
return [];
|
|
505
|
+
}
|
|
506
|
+
return collectFlowRoutingGroups(loadDeclarativeFlow({ source: "built-in", fileName }), cwd);
|
|
507
|
+
}
|
|
508
|
+
function resolveExecutorPrerequisite(executor) {
|
|
509
|
+
if (executor === "codex") {
|
|
508
510
|
resolveCmd("codex", "CODEX_BIN");
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
resolveCmd("opencode", "OPENCODE_BIN");
|
|
514
|
+
}
|
|
515
|
+
function checkPrerequisites(config, launchProfile, executionRouting) {
|
|
516
|
+
const routing = routingForPrerequisites(launchProfile, executionRouting);
|
|
517
|
+
const groups = commandRoutingGroupsForPrerequisiteChecks(config.command, process.cwd());
|
|
518
|
+
for (const executor of executorsForRoutingGroups(routing, groups)) {
|
|
519
|
+
resolveExecutorPrerequisite(executor);
|
|
509
520
|
}
|
|
510
521
|
}
|
|
511
|
-
function checkAutoPrerequisites(config) {
|
|
512
|
-
|
|
522
|
+
function checkAutoPrerequisites(config, launchProfile, executionRouting) {
|
|
523
|
+
checkPrerequisites(config, launchProfile, executionRouting);
|
|
513
524
|
}
|
|
514
525
|
function autoFlowParams(config, forceRefreshSummary = false) {
|
|
515
526
|
return {
|
|
516
527
|
jiraApiUrl: config.jiraApiUrl,
|
|
517
528
|
taskKey: config.taskKey,
|
|
529
|
+
taskContextIteration: nextArtifactIteration(config.taskKey, "task-context", "json"),
|
|
530
|
+
taskSummaryIteration: nextArtifactIteration(config.taskKey, "task"),
|
|
531
|
+
designIteration: nextArtifactIteration(config.taskKey, "design"),
|
|
532
|
+
planIteration: nextArtifactIteration(config.taskKey, "plan"),
|
|
533
|
+
qaIteration: nextArtifactIteration(config.taskKey, "qa"),
|
|
518
534
|
extraPrompt: config.extraPrompt,
|
|
519
535
|
reviewFixPoints: config.reviewFixPoints,
|
|
536
|
+
reviewBlockingSeverities: config.reviewBlockingSeverities,
|
|
520
537
|
forceRefresh: forceRefreshSummary,
|
|
521
538
|
mdLang: config.mdLang,
|
|
522
539
|
runGoTestsScript: path.join(agentweaverHome(PACKAGE_ROOT), "run_go_tests.py"),
|
|
@@ -525,6 +542,72 @@ function autoFlowParams(config, forceRefreshSummary = false) {
|
|
|
525
542
|
runGoLinterIteration: nextArtifactIteration(config.taskKey, "run-go-linter-result", "json"),
|
|
526
543
|
};
|
|
527
544
|
}
|
|
545
|
+
function reviewFlowParamsFromContract(config) {
|
|
546
|
+
const contract = resolveReviewInputContract(config.taskKey);
|
|
547
|
+
return {
|
|
548
|
+
taskKey: config.taskKey,
|
|
549
|
+
planningIteration: contract.planningIteration,
|
|
550
|
+
designFile: contract.designFile,
|
|
551
|
+
designJsonFile: contract.designJsonFile,
|
|
552
|
+
planFile: contract.planFile,
|
|
553
|
+
planJsonFile: contract.planJsonFile,
|
|
554
|
+
hasTaskContextJsonFile: contract.hasTaskContextJsonFile,
|
|
555
|
+
taskContextJsonFilePath: contract.taskContextJsonFilePath,
|
|
556
|
+
taskContextJsonFile: contract.taskContextJsonFile,
|
|
557
|
+
hasJiraTaskFile: contract.hasJiraTaskFile,
|
|
558
|
+
jiraTaskFilePath: contract.jiraTaskFilePath,
|
|
559
|
+
jiraTaskFile: contract.jiraTaskFile,
|
|
560
|
+
hasTaskInputJsonFile: contract.hasTaskInputJsonFile,
|
|
561
|
+
taskInputJsonFilePath: contract.taskInputJsonFilePath,
|
|
562
|
+
taskInputJsonFile: contract.taskInputJsonFile,
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
function hasStructuredReviewInputs(taskKey) {
|
|
566
|
+
const inspection = inspectReviewInputContract(taskKey);
|
|
567
|
+
if (inspection.status === "ready") {
|
|
568
|
+
return true;
|
|
569
|
+
}
|
|
570
|
+
if (inspection.status === "missing-planning") {
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
throw new TaskRunnerError(`Structured review requires a normalized task-context artifact, or legacy Jira/instant-task context, in scope '${taskKey}'.`);
|
|
574
|
+
}
|
|
575
|
+
function latestTaskContextIteration(taskKey) {
|
|
576
|
+
const iteration = latestArtifactIteration(taskKey, "task-context", "json");
|
|
577
|
+
if (iteration === null) {
|
|
578
|
+
throw new TaskRunnerError(`Plan mode requires a normalized task-context artifact in scope '${taskKey}'.`);
|
|
579
|
+
}
|
|
580
|
+
return iteration;
|
|
581
|
+
}
|
|
582
|
+
function loadInstantTaskInputDefaults(taskKey) {
|
|
583
|
+
const artifactPath = instantTaskInputJsonFile(taskKey);
|
|
584
|
+
if (!existsSync(artifactPath)) {
|
|
585
|
+
return null;
|
|
586
|
+
}
|
|
587
|
+
try {
|
|
588
|
+
validateStructuredArtifacts([{ path: artifactPath, schemaId: "user-input/v1" }], "Instant-task source input structured artifact is invalid.");
|
|
589
|
+
const parsed = JSON.parse(readFileSync(artifactPath, "utf8"));
|
|
590
|
+
const values = parsed.values;
|
|
591
|
+
if (!values || typeof values !== "object" || Array.isArray(values)) {
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
const normalizedEntries = [];
|
|
595
|
+
for (const [key, value] of Object.entries(values)) {
|
|
596
|
+
if (typeof value === "string" || typeof value === "boolean") {
|
|
597
|
+
normalizedEntries.push([key, value]);
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
600
|
+
if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
|
|
601
|
+
normalizedEntries.push([key, [...value]]);
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return Object.fromEntries(normalizedEntries);
|
|
606
|
+
}
|
|
607
|
+
catch {
|
|
608
|
+
return null;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
528
611
|
function interactiveFlowDefinition(entry) {
|
|
529
612
|
const flow = entry.flow;
|
|
530
613
|
return {
|
|
@@ -592,12 +675,14 @@ async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, over
|
|
|
592
675
|
runtime,
|
|
593
676
|
...(setSummary ? { setSummary } : {}),
|
|
594
677
|
requestUserInput,
|
|
678
|
+
...(overrides.executionRouting ? { executionRouting: overrides.executionRouting } : {}),
|
|
595
679
|
});
|
|
596
680
|
const flow = loadDeclarativeFlow(flowRef);
|
|
597
681
|
const initialExecutionState = {
|
|
598
682
|
flowKind: flow.kind,
|
|
599
683
|
flowVersion: flow.version,
|
|
600
684
|
terminated: false,
|
|
685
|
+
terminationOutcome: "success",
|
|
601
686
|
phases: [],
|
|
602
687
|
};
|
|
603
688
|
let persistedState = launchMode === "resume" ? loadFlowRunState(config.scope.scopeKey, flowId) : null;
|
|
@@ -609,7 +694,10 @@ async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, over
|
|
|
609
694
|
absolutePath: flow.absolutePath,
|
|
610
695
|
treePath: [],
|
|
611
696
|
flow,
|
|
612
|
-
}, config, persistedState, overrides.launchProfile
|
|
697
|
+
}, config, persistedState, overrides.executionRouting ?? (overrides.launchProfile ? resolveExecutionRouting({ defaultRoute: {
|
|
698
|
+
executor: overrides.launchProfile.executor,
|
|
699
|
+
model: overrides.launchProfile.model,
|
|
700
|
+
} }) : undefined), runtime);
|
|
613
701
|
persistedState = prepareFlowStateForResume(persistedState);
|
|
614
702
|
}
|
|
615
703
|
else if (launchMode === "restart") {
|
|
@@ -617,10 +705,18 @@ async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, over
|
|
|
617
705
|
}
|
|
618
706
|
const executionState = persistedState?.executionState ?? initialExecutionState;
|
|
619
707
|
const state = persistedState
|
|
620
|
-
?? createFlowRunState(config.scope.scopeKey, flowId, executionState, config.jiraRef, overrides.launchProfile);
|
|
621
|
-
if (overrides.
|
|
708
|
+
?? createFlowRunState(config.scope.scopeKey, flowId, executionState, config.jiraRef, overrides.launchProfile, overrides.executionRouting, overrides.selectedRoutingPreset);
|
|
709
|
+
if (overrides.executionRouting) {
|
|
710
|
+
state.executionRouting = overrides.executionRouting;
|
|
711
|
+
state.routingFingerprint = overrides.executionRouting.fingerprint;
|
|
712
|
+
state.launchProfile = overrides.executionRouting.defaultRoute;
|
|
713
|
+
}
|
|
714
|
+
else if (overrides.launchProfile) {
|
|
622
715
|
state.launchProfile = overrides.launchProfile;
|
|
623
716
|
}
|
|
717
|
+
if (overrides.selectedRoutingPreset) {
|
|
718
|
+
state.selectedRoutingPreset = overrides.selectedRoutingPreset;
|
|
719
|
+
}
|
|
624
720
|
state.status = "running";
|
|
625
721
|
state.lastError = null;
|
|
626
722
|
state.currentStep = findCurrentFlowExecutionStep(state);
|
|
@@ -645,7 +741,12 @@ async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, over
|
|
|
645
741
|
},
|
|
646
742
|
});
|
|
647
743
|
}
|
|
648
|
-
|
|
744
|
+
if (executionState.terminated) {
|
|
745
|
+
state.status = executionState.terminationOutcome === "success" ? "completed" : "blocked";
|
|
746
|
+
}
|
|
747
|
+
else {
|
|
748
|
+
state.status = "completed";
|
|
749
|
+
}
|
|
649
750
|
state.currentStep = null;
|
|
650
751
|
state.lastError = null;
|
|
651
752
|
state.executionState = executionState;
|
|
@@ -671,12 +772,24 @@ async function runDeclarativeFlowBySpecFile(fileName, config, flowParams, overri
|
|
|
671
772
|
...defaultDeclarativeFlowParams(config, false, overrides),
|
|
672
773
|
...flowParams,
|
|
673
774
|
};
|
|
674
|
-
await runDeclarativeFlowByRef(config.command, { source: "built-in", fileName }, config, mergedFlowParams, overrides, requestUserInput, setSummary, launchMode, runtime);
|
|
775
|
+
await runDeclarativeFlowByRef(config.command, { source: "built-in", fileName }, config, withCanonicalReviewLoopParams(loadDeclarativeFlow({ source: "built-in", fileName }).kind, mergedFlowParams), overrides, requestUserInput, setSummary, launchMode, runtime);
|
|
675
776
|
}
|
|
676
777
|
function defaultDeclarativeFlowParams(config, forceRefreshSummary = false, overrides = {}) {
|
|
677
778
|
const iteration = nextReviewIterationForTask(config.taskKey);
|
|
678
779
|
const latestIteration = latestArtifactIteration(config.taskKey, "review");
|
|
679
|
-
const
|
|
780
|
+
const latestTaskContext = latestArtifactIteration(config.taskKey, "task-context", "json");
|
|
781
|
+
const executionRouting = overrides.executionRouting ?? resolveExecutionRouting({
|
|
782
|
+
defaultRoute: overrides.launchProfile
|
|
783
|
+
? {
|
|
784
|
+
executor: overrides.launchProfile.executor,
|
|
785
|
+
model: overrides.launchProfile.model,
|
|
786
|
+
}
|
|
787
|
+
: {
|
|
788
|
+
executor: DEFAULT_LAUNCH_PROFILE.executor,
|
|
789
|
+
model: DEFAULT_LAUNCH_PROFILE.model,
|
|
790
|
+
},
|
|
791
|
+
});
|
|
792
|
+
const launchProfile = executionRouting.defaultRoute;
|
|
680
793
|
return {
|
|
681
794
|
taskKey: config.taskKey,
|
|
682
795
|
jiraRef: config.jiraRef,
|
|
@@ -687,12 +800,16 @@ function defaultDeclarativeFlowParams(config, forceRefreshSummary = false, overr
|
|
|
687
800
|
workspaceDir: scopeWorkspaceDir(config.taskKey),
|
|
688
801
|
extraPrompt: config.extraPrompt,
|
|
689
802
|
reviewFixPoints: config.reviewFixPoints,
|
|
803
|
+
reviewBlockingSeverities: config.reviewBlockingSeverities,
|
|
690
804
|
mdLang: config.mdLang,
|
|
691
805
|
llmExecutor: launchProfile.executor,
|
|
692
806
|
llmModel: launchProfile.model,
|
|
693
807
|
launchProfile,
|
|
808
|
+
executionRouting,
|
|
694
809
|
iteration,
|
|
810
|
+
baseIteration: iteration,
|
|
695
811
|
latestIteration,
|
|
812
|
+
taskContextIteration: latestTaskContext ?? nextArtifactIteration(config.taskKey, "task-context", "json"),
|
|
696
813
|
taskSummaryIteration: nextArtifactIteration(config.taskKey, "task"),
|
|
697
814
|
designIteration: nextArtifactIteration(config.taskKey, "design"),
|
|
698
815
|
planIteration: nextArtifactIteration(config.taskKey, "plan"),
|
|
@@ -738,20 +855,50 @@ function requireJiraConfig(config) {
|
|
|
738
855
|
throw new TaskRunnerError(`Command '${config.command}' requires Jira context in the current project scope.`);
|
|
739
856
|
}
|
|
740
857
|
}
|
|
741
|
-
async function executeCommand(baseConfig, runFollowupVerify = true, requestUserInput = requestUserInputInTerminal, resolvedScope, setSummary, forceRefreshSummary = false, launchMode = "restart", launchProfile, runtime = runtimeServices) {
|
|
858
|
+
async function executeCommand(baseConfig, runFollowupVerify = true, requestUserInput = requestUserInputInTerminal, resolvedScope, setSummary, forceRefreshSummary = false, launchMode = "restart", launchProfile, executionRouting, selectedRoutingPreset, runtime = runtimeServices) {
|
|
742
859
|
if (baseConfig.command === "doctor") {
|
|
743
860
|
const exitCode = await runDoctorCommand(baseConfig.doctorArgs ?? []);
|
|
744
861
|
return exitCode === 0;
|
|
745
862
|
}
|
|
746
863
|
const config = buildRuntimeConfig(baseConfig, resolvedScope ?? (await resolveScopeForCommand(baseConfig, requestUserInput)));
|
|
864
|
+
const flowOverrides = executionRouting
|
|
865
|
+
? {
|
|
866
|
+
launchProfile: executionRouting.defaultRoute,
|
|
867
|
+
executionRouting,
|
|
868
|
+
...(selectedRoutingPreset ? { selectedRoutingPreset } : {}),
|
|
869
|
+
}
|
|
870
|
+
: launchProfile
|
|
871
|
+
? { launchProfile }
|
|
872
|
+
: {};
|
|
873
|
+
if (config.command === "instant-task") {
|
|
874
|
+
checkPrerequisites(config, launchProfile, executionRouting);
|
|
875
|
+
const hasPersistedInstantTaskState = loadFlowRunState(config.scope.scopeKey, "instant-task") !== null;
|
|
876
|
+
const repromptInstantTaskInput = launchMode === "restart"
|
|
877
|
+
&& hasPersistedInstantTaskState
|
|
878
|
+
&& requestUserInput !== requestUserInputInTerminal;
|
|
879
|
+
await runDeclarativeFlowBySpecFile("instant-task.json", config, {
|
|
880
|
+
taskKey: config.taskKey,
|
|
881
|
+
taskContextIteration: nextArtifactIteration(config.taskKey, "task-context", "json"),
|
|
882
|
+
designIteration: nextArtifactIteration(config.taskKey, "design"),
|
|
883
|
+
planIteration: nextArtifactIteration(config.taskKey, "plan"),
|
|
884
|
+
qaIteration: nextArtifactIteration(config.taskKey, "qa"),
|
|
885
|
+
extraPrompt: config.extraPrompt,
|
|
886
|
+
mdLang: config.mdLang,
|
|
887
|
+
repromptInstantTaskInput,
|
|
888
|
+
...(repromptInstantTaskInput
|
|
889
|
+
? { prefilledInstantTaskInputValues: loadInstantTaskInputDefaults(config.taskKey) ?? undefined }
|
|
890
|
+
: {}),
|
|
891
|
+
}, flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
892
|
+
return !config.dryRun && existsSync(readyToMergeFile(config.taskKey));
|
|
893
|
+
}
|
|
747
894
|
if (config.command === "auto-golang") {
|
|
748
895
|
requireJiraConfig(config);
|
|
749
|
-
checkAutoPrerequisites(config);
|
|
750
896
|
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl;
|
|
751
897
|
process.env.JIRA_API_URL = config.jiraApiUrl;
|
|
752
898
|
process.env.JIRA_TASK_FILE = config.jiraTaskFile;
|
|
753
899
|
let effectiveLaunchMode = launchMode;
|
|
754
900
|
let effectiveLaunchProfile = launchProfile;
|
|
901
|
+
let effectiveExecutionRouting = executionRouting;
|
|
755
902
|
if (config.autoFromPhase) {
|
|
756
903
|
const flow = loadDeclarativeFlow({ source: "built-in", fileName: "auto-golang.json" });
|
|
757
904
|
const persistedState = loadFlowRunState(config.scope.scopeKey, "auto-golang");
|
|
@@ -762,18 +909,37 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
762
909
|
saveFlowRunState(persistedState);
|
|
763
910
|
effectiveLaunchMode = "resume";
|
|
764
911
|
effectiveLaunchProfile ??= persistedState.launchProfile;
|
|
912
|
+
effectiveExecutionRouting ??= persistedState.executionRouting;
|
|
765
913
|
printPanel("Auto-Golang Resume", `Auto-golang pipeline will continue from phase: ${config.autoFromPhase}`, "yellow");
|
|
766
914
|
}
|
|
767
|
-
|
|
915
|
+
checkAutoPrerequisites(config, effectiveLaunchProfile, effectiveExecutionRouting);
|
|
916
|
+
await runDeclarativeFlowBySpecFile("auto-golang.json", config, autoFlowParams(config, forceRefreshSummary), effectiveExecutionRouting
|
|
917
|
+
? {
|
|
918
|
+
launchProfile: effectiveExecutionRouting.defaultRoute,
|
|
919
|
+
executionRouting: effectiveExecutionRouting,
|
|
920
|
+
...(selectedRoutingPreset ? { selectedRoutingPreset } : {}),
|
|
921
|
+
}
|
|
922
|
+
: effectiveLaunchProfile
|
|
923
|
+
? { launchProfile: effectiveLaunchProfile }
|
|
924
|
+
: {}, requestUserInput, setSummary, effectiveLaunchMode, runtime);
|
|
768
925
|
return false;
|
|
769
926
|
}
|
|
770
927
|
if (config.command === "auto-common") {
|
|
771
928
|
requireJiraConfig(config);
|
|
772
|
-
checkAutoPrerequisites(config);
|
|
929
|
+
checkAutoPrerequisites(config, launchProfile, executionRouting);
|
|
930
|
+
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl;
|
|
931
|
+
process.env.JIRA_API_URL = config.jiraApiUrl;
|
|
932
|
+
process.env.JIRA_TASK_FILE = config.jiraTaskFile;
|
|
933
|
+
await runDeclarativeFlowBySpecFile("auto-common.json", config, autoFlowParams(config, forceRefreshSummary), flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
934
|
+
return false;
|
|
935
|
+
}
|
|
936
|
+
if (config.command === "auto-simple") {
|
|
937
|
+
requireJiraConfig(config);
|
|
938
|
+
checkAutoPrerequisites(config, launchProfile, executionRouting);
|
|
773
939
|
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl;
|
|
774
940
|
process.env.JIRA_API_URL = config.jiraApiUrl;
|
|
775
941
|
process.env.JIRA_TASK_FILE = config.jiraTaskFile;
|
|
776
|
-
await runDeclarativeFlowBySpecFile("auto-
|
|
942
|
+
await runDeclarativeFlowBySpecFile("auto-simple.json", config, autoFlowParams(config, forceRefreshSummary), flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
777
943
|
return false;
|
|
778
944
|
}
|
|
779
945
|
if (config.command === "auto-status") {
|
|
@@ -790,7 +956,11 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
790
956
|
`Current step: ${currentStep}`,
|
|
791
957
|
`Updated: ${state.updatedAt}`,
|
|
792
958
|
];
|
|
793
|
-
if (state.
|
|
959
|
+
if (state.executionRouting) {
|
|
960
|
+
lines.push(`Default route: ${state.executionRouting.defaultRoute.executor} / ${state.executionRouting.defaultRoute.model}`);
|
|
961
|
+
lines.push(`Routing fingerprint: ${state.executionRouting.fingerprint}`);
|
|
962
|
+
}
|
|
963
|
+
else if (state.launchProfile) {
|
|
794
964
|
lines.push(`Launch profile: ${state.launchProfile.executor} / ${state.launchProfile.model}`);
|
|
795
965
|
}
|
|
796
966
|
if (state.lastError) {
|
|
@@ -816,7 +986,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
816
986
|
printPanel("Auto-Golang Reset", removed ? `State file ${flowStateFile(config.scope.scopeKey, "auto-golang")} removed.` : "No flow state file found.", "yellow");
|
|
817
987
|
return false;
|
|
818
988
|
}
|
|
819
|
-
checkPrerequisites(config);
|
|
989
|
+
checkPrerequisites(config, launchProfile, executionRouting);
|
|
820
990
|
if (config.jiraBrowseUrl && config.jiraApiUrl && config.jiraTaskFile) {
|
|
821
991
|
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl ?? "";
|
|
822
992
|
process.env.JIRA_API_URL = config.jiraApiUrl ?? "";
|
|
@@ -828,22 +998,37 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
828
998
|
delete process.env.JIRA_TASK_FILE;
|
|
829
999
|
}
|
|
830
1000
|
if (config.command === "plan") {
|
|
831
|
-
|
|
832
|
-
if (config.
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
1001
|
+
let taskContextIteration;
|
|
1002
|
+
if (config.jiraRef) {
|
|
1003
|
+
requireJiraConfig(config);
|
|
1004
|
+
if (config.verbose) {
|
|
1005
|
+
process.stdout.write(`Fetching Jira issue from browse URL: ${config.jiraBrowseUrl}\n`);
|
|
1006
|
+
process.stdout.write(`Resolved Jira API URL: ${config.jiraApiUrl}\n`);
|
|
1007
|
+
process.stdout.write(`Saving Jira issue JSON to: ${config.jiraTaskFile}\n`);
|
|
1008
|
+
}
|
|
1009
|
+
taskContextIteration = nextArtifactIteration(config.taskKey, "task-context", "json");
|
|
1010
|
+
await runDeclarativeFlowBySpecFile("task-source/jira-fetch.json", config, {
|
|
1011
|
+
jiraApiUrl: config.jiraApiUrl,
|
|
1012
|
+
taskKey: config.taskKey,
|
|
1013
|
+
}, flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
1014
|
+
await runDeclarativeFlowBySpecFile("normalize-task-source.json", config, {
|
|
1015
|
+
taskKey: config.taskKey,
|
|
1016
|
+
iteration: taskContextIteration,
|
|
1017
|
+
extraPrompt: config.extraPrompt,
|
|
1018
|
+
}, flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
1019
|
+
}
|
|
1020
|
+
else {
|
|
1021
|
+
taskContextIteration = latestTaskContextIteration(config.taskKey);
|
|
836
1022
|
}
|
|
837
1023
|
await runDeclarativeFlowBySpecFile("plan.json", config, {
|
|
838
|
-
jiraApiUrl: config.jiraApiUrl,
|
|
839
1024
|
taskKey: config.taskKey,
|
|
840
|
-
|
|
1025
|
+
taskContextIteration,
|
|
841
1026
|
designIteration: nextArtifactIteration(config.taskKey, "design"),
|
|
842
1027
|
planIteration: nextArtifactIteration(config.taskKey, "plan"),
|
|
843
1028
|
qaIteration: nextArtifactIteration(config.taskKey, "qa"),
|
|
844
1029
|
extraPrompt: config.extraPrompt,
|
|
845
1030
|
forceRefresh: forceRefreshSummary,
|
|
846
|
-
},
|
|
1031
|
+
}, flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
847
1032
|
return false;
|
|
848
1033
|
}
|
|
849
1034
|
if (config.command === "bug-analyze") {
|
|
@@ -862,7 +1047,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
862
1047
|
bugFixPlanIteration: nextArtifactIteration(config.taskKey, "bug-fix-plan"),
|
|
863
1048
|
extraPrompt: config.extraPrompt,
|
|
864
1049
|
forceRefresh: forceRefreshSummary,
|
|
865
|
-
},
|
|
1050
|
+
}, flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
866
1051
|
return false;
|
|
867
1052
|
}
|
|
868
1053
|
if (config.command === "design-review") {
|
|
@@ -884,6 +1069,9 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
884
1069
|
qaJsonFilePath: inputContract.qaJsonFilePath,
|
|
885
1070
|
qaFile: inputContract.qaFile,
|
|
886
1071
|
qaJsonFile: inputContract.qaJsonFile,
|
|
1072
|
+
hasTaskContextJsonFile: inputContract.hasTaskContextJsonFile,
|
|
1073
|
+
taskContextJsonFilePath: inputContract.taskContextJsonFilePath,
|
|
1074
|
+
taskContextJsonFile: inputContract.taskContextJsonFile,
|
|
887
1075
|
hasJiraTaskFile: inputContract.hasJiraTaskFile,
|
|
888
1076
|
jiraTaskFilePath: inputContract.jiraTaskFilePath,
|
|
889
1077
|
jiraTaskFile: inputContract.jiraTaskFile,
|
|
@@ -896,8 +1084,11 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
896
1084
|
hasPlanningAnswersJsonFile: inputContract.hasPlanningAnswersJsonFile,
|
|
897
1085
|
planningAnswersJsonFilePath: inputContract.planningAnswersJsonFilePath,
|
|
898
1086
|
planningAnswersJsonFile: inputContract.planningAnswersJsonFile,
|
|
1087
|
+
hasTaskInputJsonFile: inputContract.hasTaskInputJsonFile,
|
|
1088
|
+
taskInputJsonFilePath: inputContract.taskInputJsonFilePath,
|
|
1089
|
+
taskInputJsonFile: inputContract.taskInputJsonFile,
|
|
899
1090
|
extraPrompt: config.extraPrompt,
|
|
900
|
-
},
|
|
1091
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
901
1092
|
if (!config.dryRun) {
|
|
902
1093
|
printSummary("Design Review", `Artifacts:\n${designReviewFile(config.taskKey, iteration)}\n${designReviewJsonFile(config.taskKey, iteration)}`);
|
|
903
1094
|
}
|
|
@@ -930,6 +1121,9 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
930
1121
|
revisedPlanJsonFile: inputContract.revisedPlanJsonFile,
|
|
931
1122
|
revisedQaFile: inputContract.revisedQaFile,
|
|
932
1123
|
revisedQaJsonFile: inputContract.revisedQaJsonFile,
|
|
1124
|
+
hasTaskContextJsonFile: inputContract.hasTaskContextJsonFile,
|
|
1125
|
+
taskContextJsonFilePath: inputContract.taskContextJsonFilePath,
|
|
1126
|
+
taskContextJsonFile: inputContract.taskContextJsonFile,
|
|
933
1127
|
hasJiraTaskFile: inputContract.hasJiraTaskFile,
|
|
934
1128
|
jiraTaskFilePath: inputContract.jiraTaskFilePath,
|
|
935
1129
|
jiraTaskFile: inputContract.jiraTaskFile,
|
|
@@ -942,8 +1136,11 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
942
1136
|
hasPlanningAnswersJsonFile: inputContract.hasPlanningAnswersJsonFile,
|
|
943
1137
|
planningAnswersJsonFilePath: inputContract.planningAnswersJsonFilePath,
|
|
944
1138
|
planningAnswersJsonFile: inputContract.planningAnswersJsonFile,
|
|
1139
|
+
hasTaskInputJsonFile: inputContract.hasTaskInputJsonFile,
|
|
1140
|
+
taskInputJsonFilePath: inputContract.taskInputJsonFilePath,
|
|
1141
|
+
taskInputJsonFile: inputContract.taskInputJsonFile,
|
|
945
1142
|
extraPrompt: config.extraPrompt,
|
|
946
|
-
},
|
|
1143
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
947
1144
|
if (!config.dryRun) {
|
|
948
1145
|
printSummary("Plan Revise", `Artifacts:\n${inputContract.revisedDesignFile}\n${inputContract.revisedDesignJsonFile}\n${inputContract.revisedPlanFile}\n${inputContract.revisedPlanJsonFile}\n${inputContract.revisedQaFile}\n${inputContract.revisedQaJsonFile}`);
|
|
949
1146
|
}
|
|
@@ -959,7 +1156,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
959
1156
|
extraPrompt: config.extraPrompt,
|
|
960
1157
|
reviewFixSelectionJsonFile: reviewFixSelectionJsonFile(config.taskKey, iteration),
|
|
961
1158
|
reviewFixPoints: config.reviewFixPoints,
|
|
962
|
-
},
|
|
1159
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
963
1160
|
if (!config.dryRun) {
|
|
964
1161
|
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
1162
|
}
|
|
@@ -973,7 +1170,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
973
1170
|
iteration,
|
|
974
1171
|
gitlabDiffIteration,
|
|
975
1172
|
extraPrompt: config.extraPrompt,
|
|
976
|
-
},
|
|
1173
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
977
1174
|
if (!config.dryRun) {
|
|
978
1175
|
printSummary("GitLab Diff Review", `Artifacts:\n${gitlabDiffFile(config.taskKey)}\n${gitlabDiffJsonFile(config.taskKey)}\n${reviewFile(config.taskKey, iteration)}\n${reviewJsonFile(config.taskKey, iteration)}`);
|
|
979
1176
|
}
|
|
@@ -991,7 +1188,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
991
1188
|
await runDeclarativeFlowBySpecFile("bugz/bug-fix.json", config, {
|
|
992
1189
|
taskKey: config.taskKey,
|
|
993
1190
|
extraPrompt: config.extraPrompt,
|
|
994
|
-
},
|
|
1191
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
995
1192
|
return false;
|
|
996
1193
|
}
|
|
997
1194
|
if (config.command === "mr-description") {
|
|
@@ -1001,7 +1198,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
1001
1198
|
taskKey: config.taskKey,
|
|
1002
1199
|
iteration: nextArtifactIteration(config.taskKey, "mr-description"),
|
|
1003
1200
|
extraPrompt: config.extraPrompt,
|
|
1004
|
-
},
|
|
1201
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1005
1202
|
return false;
|
|
1006
1203
|
}
|
|
1007
1204
|
if (config.command === "task-describe") {
|
|
@@ -1011,42 +1208,33 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
1011
1208
|
taskKey: config.taskKey,
|
|
1012
1209
|
iteration,
|
|
1013
1210
|
extraPrompt: config.extraPrompt,
|
|
1014
|
-
},
|
|
1211
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1015
1212
|
return false;
|
|
1016
1213
|
}
|
|
1017
1214
|
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.");
|
|
1215
|
+
const planningBundle = resolveLatestPlanningBundle(config.taskKey);
|
|
1024
1216
|
await runDeclarativeFlowBySpecFile("implement.json", config, {
|
|
1025
1217
|
taskKey: config.taskKey,
|
|
1218
|
+
planningIteration: planningBundle.planningIteration,
|
|
1026
1219
|
extraPrompt: config.extraPrompt,
|
|
1027
|
-
},
|
|
1220
|
+
}, flowOverrides, requestUserInput, undefined, launchMode);
|
|
1028
1221
|
return false;
|
|
1029
1222
|
}
|
|
1030
1223
|
if (config.command === "review") {
|
|
1031
1224
|
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.");
|
|
1225
|
+
if (hasStructuredReviewInputs(config.taskKey)) {
|
|
1038
1226
|
await runDeclarativeFlowBySpecFile("review/review.json", config, {
|
|
1039
|
-
|
|
1227
|
+
...reviewFlowParamsFromContract(config),
|
|
1040
1228
|
iteration,
|
|
1041
1229
|
extraPrompt: config.extraPrompt,
|
|
1042
|
-
},
|
|
1230
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1043
1231
|
}
|
|
1044
1232
|
else {
|
|
1045
1233
|
await runDeclarativeFlowBySpecFile("review/review-project.json", config, {
|
|
1046
1234
|
taskKey: config.taskKey,
|
|
1047
1235
|
iteration,
|
|
1048
1236
|
extraPrompt: config.extraPrompt,
|
|
1049
|
-
},
|
|
1237
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1050
1238
|
}
|
|
1051
1239
|
return !config.dryRun && existsSync(readyToMergeFile(config.taskKey));
|
|
1052
1240
|
}
|
|
@@ -1065,23 +1253,21 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
1065
1253
|
reviewFixSelectionJsonFile: reviewFixSelectionJsonFile(config.taskKey, latestIteration),
|
|
1066
1254
|
extraPrompt: config.extraPrompt,
|
|
1067
1255
|
reviewFixPoints: config.reviewFixPoints,
|
|
1068
|
-
},
|
|
1256
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1069
1257
|
return false;
|
|
1070
1258
|
}
|
|
1071
1259
|
if (config.command === "review-loop") {
|
|
1072
1260
|
const iteration = nextReviewIterationForTask(config.taskKey);
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
taskKey: config.taskKey,
|
|
1082
|
-
iteration,
|
|
1261
|
+
const reviewLoopSpec = hasStructuredReviewInputs(config.taskKey)
|
|
1262
|
+
? "review/review-loop.json"
|
|
1263
|
+
: "review/review-project-loop.json";
|
|
1264
|
+
await runDeclarativeFlowBySpecFile(reviewLoopSpec, config, {
|
|
1265
|
+
...(reviewLoopSpec === "review/review-loop.json"
|
|
1266
|
+
? reviewFlowParamsFromContract(config)
|
|
1267
|
+
: { taskKey: config.taskKey }),
|
|
1268
|
+
baseIteration: iteration,
|
|
1083
1269
|
extraPrompt: config.extraPrompt,
|
|
1084
|
-
},
|
|
1270
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1085
1271
|
return !config.dryRun && existsSync(readyToMergeFile(config.taskKey));
|
|
1086
1272
|
}
|
|
1087
1273
|
if (config.command === "run-go-tests-loop" || config.command === "run-go-linter-loop") {
|
|
@@ -1092,14 +1278,14 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
1092
1278
|
runGoTestsIteration: nextArtifactIteration(config.taskKey, "run-go-tests-result", "json"),
|
|
1093
1279
|
runGoLinterIteration: nextArtifactIteration(config.taskKey, "run-go-linter-result", "json"),
|
|
1094
1280
|
extraPrompt: config.extraPrompt,
|
|
1095
|
-
},
|
|
1281
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1096
1282
|
return false;
|
|
1097
1283
|
}
|
|
1098
1284
|
if (config.command === "git-commit") {
|
|
1099
1285
|
await runDeclarativeFlowBySpecFile("git-commit.json", config, {
|
|
1100
1286
|
taskKey: config.taskKey,
|
|
1101
1287
|
extraPrompt: config.extraPrompt,
|
|
1102
|
-
},
|
|
1288
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1103
1289
|
return false;
|
|
1104
1290
|
}
|
|
1105
1291
|
throw new TaskRunnerError(`Unsupported command: ${config.command}`);
|
|
@@ -1127,6 +1313,7 @@ function parseCliArgs(argv) {
|
|
|
1127
1313
|
let prompt;
|
|
1128
1314
|
let autoFromPhase;
|
|
1129
1315
|
let scopeName;
|
|
1316
|
+
let reviewBlockingSeverities;
|
|
1130
1317
|
let helpPhases = false;
|
|
1131
1318
|
let jiraRef;
|
|
1132
1319
|
let mdLang;
|
|
@@ -1155,6 +1342,15 @@ function parseCliArgs(argv) {
|
|
|
1155
1342
|
index += 1;
|
|
1156
1343
|
continue;
|
|
1157
1344
|
}
|
|
1345
|
+
if (token === "--blocking-severities") {
|
|
1346
|
+
reviewBlockingSeverities = parseReviewSeverityCsv(argv[index + 1] ?? "");
|
|
1347
|
+
index += 1;
|
|
1348
|
+
continue;
|
|
1349
|
+
}
|
|
1350
|
+
if (token.startsWith("--blocking-severities=")) {
|
|
1351
|
+
reviewBlockingSeverities = parseReviewSeverityCsv(token.slice("--blocking-severities=".length));
|
|
1352
|
+
continue;
|
|
1353
|
+
}
|
|
1158
1354
|
if (token === "--from") {
|
|
1159
1355
|
autoFromPhase = argv[index + 1];
|
|
1160
1356
|
index += 1;
|
|
@@ -1198,6 +1394,10 @@ function parseCliArgs(argv) {
|
|
|
1198
1394
|
printAutoCommonPhasesHelp();
|
|
1199
1395
|
process.exit(0);
|
|
1200
1396
|
}
|
|
1397
|
+
if (command === "auto-simple" && helpPhases) {
|
|
1398
|
+
printAutoSimplePhasesHelp();
|
|
1399
|
+
process.exit(0);
|
|
1400
|
+
}
|
|
1201
1401
|
return {
|
|
1202
1402
|
command: command,
|
|
1203
1403
|
dry,
|
|
@@ -1205,6 +1405,7 @@ function parseCliArgs(argv) {
|
|
|
1205
1405
|
helpPhases,
|
|
1206
1406
|
...(jiraRef !== undefined ? { jiraRef } : {}),
|
|
1207
1407
|
...(scopeName !== undefined ? { scopeName } : {}),
|
|
1408
|
+
...(reviewBlockingSeverities !== undefined ? { reviewBlockingSeverities } : {}),
|
|
1208
1409
|
...(prompt !== undefined ? { prompt } : {}),
|
|
1209
1410
|
...(autoFromPhase !== undefined ? { autoFromPhase } : {}),
|
|
1210
1411
|
...(mdLang !== undefined ? { mdLang } : {}),
|
|
@@ -1215,6 +1416,7 @@ function buildConfigFromArgs(args) {
|
|
|
1215
1416
|
return buildBaseConfig(args.command, {
|
|
1216
1417
|
...(args.jiraRef !== undefined ? { jiraRef: args.jiraRef } : {}),
|
|
1217
1418
|
...(args.scopeName !== undefined ? { scopeName: args.scopeName } : {}),
|
|
1419
|
+
...(args.reviewBlockingSeverities !== undefined ? { reviewBlockingSeverities: args.reviewBlockingSeverities } : {}),
|
|
1218
1420
|
...(args.prompt !== undefined ? { extraPrompt: args.prompt } : {}),
|
|
1219
1421
|
...(args.autoFromPhase !== undefined ? { autoFromPhase: args.autoFromPhase } : {}),
|
|
1220
1422
|
...(args.mdLang !== undefined ? { mdLang: args.mdLang } : {}),
|
|
@@ -1230,7 +1432,7 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1230
1432
|
let activeAbortController = null;
|
|
1231
1433
|
let activeFlowId = null;
|
|
1232
1434
|
let exiting = false;
|
|
1233
|
-
const ui =
|
|
1435
|
+
const ui = createInteractiveSession({
|
|
1234
1436
|
scopeKey: currentScope.scopeKey,
|
|
1235
1437
|
jiraIssueKey: currentScope.jiraIssueKey ?? null,
|
|
1236
1438
|
summaryText: "",
|
|
@@ -1255,16 +1457,23 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1255
1457
|
if (!flowEntry) {
|
|
1256
1458
|
throw new TaskRunnerError(`Unknown flow: ${flowId}`);
|
|
1257
1459
|
}
|
|
1460
|
+
const routingGroups = flowRoutingGroups(flowEntry, process.cwd());
|
|
1258
1461
|
const resumeState = launchMode === "resume" ? loadFlowRunState(currentScope.scopeKey, flowId) : null;
|
|
1259
1462
|
if (resumeState) {
|
|
1260
1463
|
currentScope = scopeWithRestoredJiraContext(currentScope, resumeState);
|
|
1261
1464
|
}
|
|
1262
|
-
const
|
|
1263
|
-
? resumeState?.
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1465
|
+
const routingSelection = launchMode === "resume"
|
|
1466
|
+
? (resumeState?.executionRouting
|
|
1467
|
+
? {
|
|
1468
|
+
routing: resumeState.executionRouting,
|
|
1469
|
+
selectedPreset: resumeState.selectedRoutingPreset ?? { kind: "custom", label: "Saved routing" },
|
|
1470
|
+
}
|
|
1471
|
+
: null)
|
|
1472
|
+
: await requestInteractiveExecutionRouting(flowEntry, (form) => ui.requestUserInput(form));
|
|
1473
|
+
if (launchMode === "resume" && !routingSelection?.routing) {
|
|
1474
|
+
throw new TaskRunnerError("Resume is impossible because execution routing was not saved. Use restart.");
|
|
1267
1475
|
}
|
|
1476
|
+
const launchProfile = routingSelection?.routing?.defaultRoute;
|
|
1268
1477
|
const previousScopeKey = currentScope.scopeKey;
|
|
1269
1478
|
const baseConfig = buildInteractiveBaseConfig(flowId, currentScope);
|
|
1270
1479
|
if (flowEntry.source === "built-in" && isBuiltInCommandFlowId(flowId)) {
|
|
@@ -1279,13 +1488,20 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1279
1488
|
if (previousScopeKey !== currentScope.scopeKey || currentScope.jiraIssueKey) {
|
|
1280
1489
|
syncInteractiveTaskSummary(ui, currentScope, forceRefresh);
|
|
1281
1490
|
}
|
|
1282
|
-
|
|
1491
|
+
if (routingSelection?.routing) {
|
|
1492
|
+
printPanel("Effective Launch Config", `preset: ${routingSelection.selectedPreset.label}\nmode: ${launchMode}\n${describeExecutionRouting(routingSelection.routing, routingGroups)}`, "cyan");
|
|
1493
|
+
}
|
|
1283
1494
|
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));
|
|
1495
|
+
await executeCommand(baseConfig, true, (form) => ui.requestUserInput(form), currentScope, (markdown) => ui.setSummary(markdown), forceRefresh, launchMode, launchProfile, routingSelection?.routing, routingSelection?.selectedPreset, createRuntimeServices(abortController.signal));
|
|
1285
1496
|
return;
|
|
1286
1497
|
}
|
|
1287
1498
|
const runtimeConfig = buildRuntimeConfig(baseConfig, currentScope);
|
|
1288
|
-
|
|
1499
|
+
const flowOverrides = {
|
|
1500
|
+
...(launchProfile ? { launchProfile } : {}),
|
|
1501
|
+
...(routingSelection?.routing ? { executionRouting: routingSelection.routing } : {}),
|
|
1502
|
+
...(routingSelection?.selectedPreset ? { selectedRoutingPreset: routingSelection.selectedPreset } : {}),
|
|
1503
|
+
};
|
|
1504
|
+
await runDeclarativeFlowByRef(flowId, toDeclarativeFlowRef(flowEntry), runtimeConfig, defaultDeclarativeFlowParams(runtimeConfig, forceRefresh, flowOverrides), flowOverrides, (form) => ui.requestUserInput(form), (markdown) => ui.setSummary(markdown), launchMode, createRuntimeServices(abortController.signal));
|
|
1289
1505
|
}
|
|
1290
1506
|
catch (error) {
|
|
1291
1507
|
if (error instanceof FlowInterruptedError) {
|