agentweaver 0.1.14 → 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 +29 -7
- package/dist/artifact-manifest.js +219 -0
- package/dist/artifacts.js +21 -1
- package/dist/doctor/checks/cwd-context.js +4 -3
- package/dist/doctor/checks/env-diagnostics.js +193 -71
- package/dist/doctor/checks/flow-readiness.js +212 -203
- package/dist/doctor/index.js +1 -1
- package/dist/doctor/orchestrator.js +18 -7
- package/dist/doctor/runner.js +9 -8
- package/dist/doctor/types.js +12 -0
- package/dist/flow-state.js +75 -15
- package/dist/index.js +499 -199
- 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 +23 -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 +249 -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 +24 -5
- 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 +267 -0
- 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 +62 -2
- 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 +49 -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 +70 -0
- 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 +5 -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 +5 -0
- package/dist/pipeline/spec-validator.js +14 -0
- package/dist/pipeline/value-resolver.js +84 -1
- package/dist/prompts.js +82 -13
- package/dist/review-severity.js +45 -0
- package/dist/runtime/artifact-registry.js +402 -0
- package/dist/runtime/design-review-input-contract.js +113 -0
- 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 +147 -0
- package/dist/runtime/planning-bundle.js +123 -0
- package/dist/runtime/ready-to-merge.js +31 -0
- package/dist/runtime/review-input-contract.js +100 -0
- package/dist/scope.js +11 -2
- package/dist/structured-artifact-schema-registry.js +10 -0
- package/dist/structured-artifact-schemas.json +257 -1
- package/dist/structured-artifacts.js +83 -6
- package/dist/user-input.js +70 -3
- package/package.json +6 -3
package/dist/index.js
CHANGED
|
@@ -1,43 +1,57 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { existsSync,
|
|
2
|
+
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 {
|
|
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";
|
|
23
|
-
import {
|
|
25
|
+
import { createArtifactRegistry } from "./runtime/artifact-registry.js";
|
|
26
|
+
import { resolveDesignReviewInputContract } from "./runtime/design-review-input-contract.js";
|
|
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";
|
|
30
|
+
import { clearReadyToMergeFile } from "./runtime/ready-to-merge.js";
|
|
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";
|
|
24
34
|
import { bye, printError, printInfo, printPanel, printSummary, setFlowExecutionState, stripAnsi, } from "./tui.js";
|
|
25
35
|
import { requestUserInputInTerminal } from "./user-input.js";
|
|
26
36
|
import { runDoctorCommand } from "./doctor/index.js";
|
|
27
|
-
import {
|
|
37
|
+
import { detectGitBranchName, requestJiraContext, resolveProjectScope, } from "./scope.js";
|
|
28
38
|
const COMMANDS = [
|
|
29
39
|
"auto-golang",
|
|
30
40
|
"auto-common",
|
|
41
|
+
"auto-simple",
|
|
31
42
|
"auto-status",
|
|
32
43
|
"auto-reset",
|
|
33
44
|
"bug-analyze",
|
|
34
45
|
"bug-fix",
|
|
46
|
+
"design-review",
|
|
35
47
|
"doctor",
|
|
36
48
|
"git-commit",
|
|
37
49
|
"gitlab-diff-review",
|
|
38
50
|
"gitlab-review",
|
|
51
|
+
"instant-task",
|
|
39
52
|
"mr-description",
|
|
40
53
|
"plan",
|
|
54
|
+
"plan-revise",
|
|
41
55
|
"task-describe",
|
|
42
56
|
"implement",
|
|
43
57
|
"review",
|
|
@@ -52,6 +66,7 @@ function createRuntimeServices(signal) {
|
|
|
52
66
|
return {
|
|
53
67
|
resolveCmd,
|
|
54
68
|
runCommand: (argv, options = {}) => runCommand(argv, { ...options, ...(signal ? { signal } : {}) }),
|
|
69
|
+
artifactRegistry: createArtifactRegistry(),
|
|
55
70
|
};
|
|
56
71
|
}
|
|
57
72
|
const runtimeServices = createRuntimeServices();
|
|
@@ -99,19 +114,26 @@ function usage() {
|
|
|
99
114
|
agentweaver gitlab-review [--dry] [--verbose] [--prompt <text>] [--scope <name>]
|
|
100
115
|
agentweaver bug-analyze [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
101
116
|
agentweaver bug-fix [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
117
|
+
agentweaver design-review [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
102
118
|
agentweaver doctor [<category>|<check-id>] [--json]
|
|
119
|
+
agentweaver instant-task [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>]
|
|
103
120
|
agentweaver mr-description [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
104
121
|
agentweaver plan [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] [<jira-browse-url|jira-issue-key>]
|
|
122
|
+
agentweaver plan-revise [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
105
123
|
agentweaver task-describe [--dry] [--verbose] [--prompt <text>] [<jira-browse-url|jira-issue-key>]
|
|
106
124
|
agentweaver implement [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
107
|
-
agentweaver review [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
108
|
-
agentweaver review-fix [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
109
|
-
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>]
|
|
110
128
|
agentweaver run-go-tests-loop [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
111
129
|
agentweaver run-go-linter-loop [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
112
130
|
agentweaver auto-golang [--dry] [--verbose] [--prompt <text>] [<jira-browse-url|jira-issue-key>]
|
|
113
131
|
agentweaver auto-golang [--dry] [--verbose] [--prompt <text>] --from <phase> [<jira-browse-url|jira-issue-key>]
|
|
114
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
|
|
115
137
|
agentweaver auto-status [<jira-browse-url|jira-issue-key>]
|
|
116
138
|
agentweaver auto-reset [<jira-browse-url|jira-issue-key>]
|
|
117
139
|
|
|
@@ -125,8 +147,9 @@ Flags:
|
|
|
125
147
|
--force In interactive mode, regenerate task summary in Jira-backed flows
|
|
126
148
|
--dry Fetch Jira task, but print codex/opencode commands instead of executing them
|
|
127
149
|
--verbose Show live stdout/stderr of launched commands
|
|
128
|
-
--scope Explicit workflow scope name for non-Jira runs
|
|
150
|
+
--scope Explicit workflow scope name for non-Jira runs except instant-task
|
|
129
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
|
|
130
153
|
--md-lang Language for markdown output files: en (English) or ru (Russian, default)
|
|
131
154
|
|
|
132
155
|
Required environment variables:
|
|
@@ -138,6 +161,7 @@ Optional environment variables:
|
|
|
138
161
|
JIRA_BASE_URL
|
|
139
162
|
GITLAB_TOKEN
|
|
140
163
|
AGENTWEAVER_HOME
|
|
164
|
+
${AGENTWEAVER_REVIEW_BLOCKING_SEVERITIES_ENV}
|
|
141
165
|
CODEX_BIN
|
|
142
166
|
CODEX_MODEL
|
|
143
167
|
OPENCODE_BIN
|
|
@@ -145,8 +169,11 @@ Optional environment variables:
|
|
|
145
169
|
|
|
146
170
|
Notes:
|
|
147
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.
|
|
148
173
|
- All flow state and artifacts are stored in the current project scope by default.
|
|
149
|
-
- 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.`;
|
|
150
177
|
}
|
|
151
178
|
function packageVersion() {
|
|
152
179
|
const packageJsonPath = path.join(PACKAGE_ROOT, "package.json");
|
|
@@ -176,7 +203,11 @@ function buildFlowResumeDetails(state) {
|
|
|
176
203
|
`Current step: ${currentStep}`,
|
|
177
204
|
`Updated: ${state.updatedAt}`,
|
|
178
205
|
];
|
|
179
|
-
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) {
|
|
180
211
|
lines.push(`Launch profile: ${state.launchProfile.executor} / ${state.launchProfile.model}`);
|
|
181
212
|
}
|
|
182
213
|
if (state.lastError) {
|
|
@@ -184,79 +215,6 @@ function buildFlowResumeDetails(state) {
|
|
|
184
215
|
}
|
|
185
216
|
return lines.join("\n");
|
|
186
217
|
}
|
|
187
|
-
function launchProfileSelectionForm() {
|
|
188
|
-
const defaultExecutor = DEFAULT_LAUNCH_PROFILE.executor;
|
|
189
|
-
return {
|
|
190
|
-
formId: "flow-launch-profile",
|
|
191
|
-
title: "LLM Launch Settings",
|
|
192
|
-
description: `Select an executor for the flow. Current default: ${defaultExecutor}.`,
|
|
193
|
-
submitLabel: "Continue",
|
|
194
|
-
fields: [
|
|
195
|
-
{
|
|
196
|
-
id: "executor",
|
|
197
|
-
type: "single-select",
|
|
198
|
-
label: "Executor",
|
|
199
|
-
required: true,
|
|
200
|
-
default: defaultExecutor,
|
|
201
|
-
options: LLM_EXECUTOR_IDS.map((id) => ({
|
|
202
|
-
value: id,
|
|
203
|
-
label: id === defaultExecutor ? `${id} [default]` : id,
|
|
204
|
-
})),
|
|
205
|
-
},
|
|
206
|
-
],
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
function launchModelSelectionForm(executor) {
|
|
210
|
-
const resolvedExecutor = executor === "default" ? DEFAULT_LAUNCH_PROFILE.executor : executor;
|
|
211
|
-
const defaultModel = defaultModelForExecutor(resolvedExecutor);
|
|
212
|
-
const options = ALLOWED_MODELS_BY_EXECUTOR[resolvedExecutor].map((model) => ({
|
|
213
|
-
value: model,
|
|
214
|
-
label: model === defaultModel ? `${model} [default]` : model,
|
|
215
|
-
}));
|
|
216
|
-
return {
|
|
217
|
-
formId: "flow-launch-model",
|
|
218
|
-
title: "LLM Launch Settings",
|
|
219
|
-
description: `Select a model for the flow. Current default for ${resolvedExecutor}: ${defaultModel}.`,
|
|
220
|
-
submitLabel: "Start",
|
|
221
|
-
fields: [
|
|
222
|
-
{
|
|
223
|
-
id: "model",
|
|
224
|
-
type: "single-select",
|
|
225
|
-
label: "Model",
|
|
226
|
-
required: true,
|
|
227
|
-
default: defaultModel,
|
|
228
|
-
options,
|
|
229
|
-
},
|
|
230
|
-
],
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
function isFormCancellation(error, formId) {
|
|
234
|
-
return error instanceof TaskRunnerError && error.message === `User cancelled form '${formId}'.`;
|
|
235
|
-
}
|
|
236
|
-
async function requestInteractiveLaunchProfile(requestUserInput) {
|
|
237
|
-
for (;;) {
|
|
238
|
-
const executorFormResult = await requestUserInput(launchProfileSelectionForm());
|
|
239
|
-
const rawExecutor = String(executorFormResult.values.executor ?? DEFAULT_LAUNCH_PROFILE.executor);
|
|
240
|
-
const executor = LLM_EXECUTOR_IDS.find((id) => id === rawExecutor);
|
|
241
|
-
if (!executor) {
|
|
242
|
-
throw new TaskRunnerError(`Unsupported launch executor '${rawExecutor}'.`);
|
|
243
|
-
}
|
|
244
|
-
try {
|
|
245
|
-
const modelFormResult = await requestUserInput(launchModelSelectionForm(executor));
|
|
246
|
-
const rawModel = String(modelFormResult.values.model ?? defaultModelForExecutor(executor)).trim();
|
|
247
|
-
return resolveLaunchProfile({
|
|
248
|
-
executor,
|
|
249
|
-
model: rawModel.length > 0 ? rawModel : defaultModelForExecutor(executor),
|
|
250
|
-
}, DEFAULT_LAUNCH_PROFILE);
|
|
251
|
-
}
|
|
252
|
-
catch (error) {
|
|
253
|
-
if (isFormCancellation(error, "flow-launch-model")) {
|
|
254
|
-
continue;
|
|
255
|
-
}
|
|
256
|
-
throw error;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
218
|
function buildResolverContext(pipelineContext, flowParams, flowConstants, repeatVars, executionState) {
|
|
261
219
|
return {
|
|
262
220
|
flowParams,
|
|
@@ -330,13 +288,21 @@ function validateDeclarativePhaseResumeState(phase, phaseState, pipelineContext,
|
|
|
330
288
|
}
|
|
331
289
|
}
|
|
332
290
|
}
|
|
333
|
-
function validateDeclarativeFlowResumeState(flowEntry, config, state,
|
|
334
|
-
if (state.
|
|
335
|
-
|
|
336
|
-
|
|
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.");
|
|
297
|
+
}
|
|
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.");
|
|
337
303
|
}
|
|
338
|
-
if (
|
|
339
|
-
throw new TaskRunnerError(
|
|
304
|
+
if (persistedFingerprint !== executionRouting.fingerprint) {
|
|
305
|
+
throw new TaskRunnerError("Resume is impossible because execution routing changed. Use restart.");
|
|
340
306
|
}
|
|
341
307
|
}
|
|
342
308
|
if (flowRequiresTaskScope(flowEntry) && !config.jiraRef) {
|
|
@@ -350,8 +316,9 @@ function validateDeclarativeFlowResumeState(flowEntry, config, state, launchProf
|
|
|
350
316
|
...(config.mdLang !== undefined ? { mdLang: config.mdLang } : {}),
|
|
351
317
|
runtime,
|
|
352
318
|
requestUserInput: requestUserInputInTerminal,
|
|
319
|
+
...(executionRouting ? { executionRouting } : {}),
|
|
353
320
|
});
|
|
354
|
-
const flowParams = defaultDeclarativeFlowParams(config, false,
|
|
321
|
+
const flowParams = defaultDeclarativeFlowParams(config, false, executionRouting ? { executionRouting, launchProfile: executionRouting.defaultRoute } : {});
|
|
355
322
|
for (const phase of flowEntry.flow.phases) {
|
|
356
323
|
const phaseState = state.executionState.phases.find((candidate) => candidate.id === phase.id);
|
|
357
324
|
if (!phaseState) {
|
|
@@ -364,19 +331,21 @@ function scopeWithRestoredJiraContext(scope, state) {
|
|
|
364
331
|
if (scope.jiraRef || !state?.jiraRef?.trim()) {
|
|
365
332
|
return scope;
|
|
366
333
|
}
|
|
367
|
-
return
|
|
334
|
+
return resolveProjectScope(null, state.jiraRef);
|
|
335
|
+
}
|
|
336
|
+
function buildInteractiveBaseConfig(flowId, scope) {
|
|
337
|
+
return buildBaseConfig(flowId, {
|
|
338
|
+
...(flowId !== "instant-task" && scope.jiraRef ? { jiraRef: scope.jiraRef } : {}),
|
|
339
|
+
});
|
|
368
340
|
}
|
|
369
341
|
function lookupInteractiveFlowResume(flowEntry, currentScope) {
|
|
370
342
|
const directState = loadFlowRunState(currentScope.scopeKey, flowEntry.id);
|
|
371
343
|
if (directState && hasResumableFlowState(directState)) {
|
|
372
344
|
try {
|
|
373
345
|
const effectiveScope = scopeWithRestoredJiraContext(currentScope, directState);
|
|
374
|
-
const baseConfig =
|
|
375
|
-
...(effectiveScope.jiraRef ? { jiraRef: effectiveScope.jiraRef } : {}),
|
|
376
|
-
scopeName: effectiveScope.scopeKey,
|
|
377
|
-
});
|
|
346
|
+
const baseConfig = buildInteractiveBaseConfig(flowEntry.id, effectiveScope);
|
|
378
347
|
const config = buildRuntimeConfig(baseConfig, effectiveScope);
|
|
379
|
-
validateDeclarativeFlowResumeState(flowEntry, config, directState, directState.
|
|
348
|
+
validateDeclarativeFlowResumeState(flowEntry, config, directState, directState.executionRouting);
|
|
380
349
|
return {
|
|
381
350
|
resumeAvailable: true,
|
|
382
351
|
hasExistingState: true,
|
|
@@ -409,22 +378,19 @@ function printAutoCommonPhasesHelp() {
|
|
|
409
378
|
phaseLines.push("", "You can run auto-common with:", "agentweaver auto-common <jira>");
|
|
410
379
|
printPanel("Auto-Common Phases", phaseLines.join("\n"), "magenta");
|
|
411
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
|
+
}
|
|
412
389
|
function nextReviewIterationForTask(taskKey) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
}
|
|
418
|
-
for (const entry of readdirSync(workspaceDir, { withFileTypes: true })) {
|
|
419
|
-
if (!entry.isFile()) {
|
|
420
|
-
continue;
|
|
421
|
-
}
|
|
422
|
-
const match = REVIEW_FILE_RE.exec(entry.name);
|
|
423
|
-
if (match && match[1] === taskKey) {
|
|
424
|
-
maxIndex = Math.max(maxIndex, Number.parseInt(match[2] ?? "0", 10));
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
return maxIndex + 1;
|
|
390
|
+
return nextArtifactIteration(taskKey, "review");
|
|
391
|
+
}
|
|
392
|
+
function nextDesignReviewIterationForTask(taskKey) {
|
|
393
|
+
return nextArtifactIteration(taskKey, "design-review");
|
|
428
394
|
}
|
|
429
395
|
function buildBaseConfig(command, options = {}) {
|
|
430
396
|
return {
|
|
@@ -432,6 +398,7 @@ function buildBaseConfig(command, options = {}) {
|
|
|
432
398
|
jiraRef: options.jiraRef ?? null,
|
|
433
399
|
scopeName: options.scopeName ?? null,
|
|
434
400
|
reviewFixPoints: options.reviewFixPoints ?? null,
|
|
401
|
+
reviewBlockingSeverities: options.reviewBlockingSeverities ?? resolveReviewBlockingSeveritiesFromEnv(),
|
|
435
402
|
extraPrompt: options.extraPrompt ?? null,
|
|
436
403
|
autoFromPhase: options.autoFromPhase ? validateAutoPhaseId(options.autoFromPhase) : null,
|
|
437
404
|
mdLang: options.mdLang ?? null,
|
|
@@ -441,19 +408,23 @@ function buildBaseConfig(command, options = {}) {
|
|
|
441
408
|
};
|
|
442
409
|
}
|
|
443
410
|
function commandRequiresTask(command) {
|
|
444
|
-
return (command === "plan" ||
|
|
411
|
+
return (command === "plan-revise" ||
|
|
445
412
|
command === "bug-analyze" ||
|
|
446
413
|
command === "bug-fix" ||
|
|
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,103 @@ 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);
|
|
1051
|
+
return false;
|
|
1052
|
+
}
|
|
1053
|
+
if (config.command === "design-review") {
|
|
1054
|
+
const iteration = nextDesignReviewIterationForTask(config.taskKey);
|
|
1055
|
+
const inputContract = resolveDesignReviewInputContract(config.taskKey);
|
|
1056
|
+
if (!config.dryRun) {
|
|
1057
|
+
clearReadyToMergeFile(config.taskKey);
|
|
1058
|
+
}
|
|
1059
|
+
await runDeclarativeFlowBySpecFile("design-review.json", config, {
|
|
1060
|
+
taskKey: config.taskKey,
|
|
1061
|
+
iteration,
|
|
1062
|
+
planningIteration: inputContract.planningIteration,
|
|
1063
|
+
designFile: inputContract.designFile,
|
|
1064
|
+
designJsonFile: inputContract.designJsonFile,
|
|
1065
|
+
planFile: inputContract.planFile,
|
|
1066
|
+
planJsonFile: inputContract.planJsonFile,
|
|
1067
|
+
hasQaArtifacts: inputContract.hasQaArtifacts,
|
|
1068
|
+
qaFilePath: inputContract.qaFilePath,
|
|
1069
|
+
qaJsonFilePath: inputContract.qaJsonFilePath,
|
|
1070
|
+
qaFile: inputContract.qaFile,
|
|
1071
|
+
qaJsonFile: inputContract.qaJsonFile,
|
|
1072
|
+
hasTaskContextJsonFile: inputContract.hasTaskContextJsonFile,
|
|
1073
|
+
taskContextJsonFilePath: inputContract.taskContextJsonFilePath,
|
|
1074
|
+
taskContextJsonFile: inputContract.taskContextJsonFile,
|
|
1075
|
+
hasJiraTaskFile: inputContract.hasJiraTaskFile,
|
|
1076
|
+
jiraTaskFilePath: inputContract.jiraTaskFilePath,
|
|
1077
|
+
jiraTaskFile: inputContract.jiraTaskFile,
|
|
1078
|
+
hasJiraAttachmentsManifestFile: inputContract.hasJiraAttachmentsManifestFile,
|
|
1079
|
+
jiraAttachmentsManifestFilePath: inputContract.jiraAttachmentsManifestFilePath,
|
|
1080
|
+
jiraAttachmentsManifestFile: inputContract.jiraAttachmentsManifestFile,
|
|
1081
|
+
hasJiraAttachmentsContextFile: inputContract.hasJiraAttachmentsContextFile,
|
|
1082
|
+
jiraAttachmentsContextFilePath: inputContract.jiraAttachmentsContextFilePath,
|
|
1083
|
+
jiraAttachmentsContextFile: inputContract.jiraAttachmentsContextFile,
|
|
1084
|
+
hasPlanningAnswersJsonFile: inputContract.hasPlanningAnswersJsonFile,
|
|
1085
|
+
planningAnswersJsonFilePath: inputContract.planningAnswersJsonFilePath,
|
|
1086
|
+
planningAnswersJsonFile: inputContract.planningAnswersJsonFile,
|
|
1087
|
+
hasTaskInputJsonFile: inputContract.hasTaskInputJsonFile,
|
|
1088
|
+
taskInputJsonFilePath: inputContract.taskInputJsonFilePath,
|
|
1089
|
+
taskInputJsonFile: inputContract.taskInputJsonFile,
|
|
1090
|
+
extraPrompt: config.extraPrompt,
|
|
1091
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1092
|
+
if (!config.dryRun) {
|
|
1093
|
+
printSummary("Design Review", `Artifacts:\n${designReviewFile(config.taskKey, iteration)}\n${designReviewJsonFile(config.taskKey, iteration)}`);
|
|
1094
|
+
}
|
|
1095
|
+
return false;
|
|
1096
|
+
}
|
|
1097
|
+
if (config.command === "plan-revise") {
|
|
1098
|
+
const inputContract = resolvePlanReviseInputContract(config.taskKey);
|
|
1099
|
+
if (!config.dryRun) {
|
|
1100
|
+
clearReadyToMergeFile(config.taskKey);
|
|
1101
|
+
}
|
|
1102
|
+
await runDeclarativeFlowBySpecFile("plan-revise.json", config, {
|
|
1103
|
+
taskKey: config.taskKey,
|
|
1104
|
+
reviewIteration: inputContract.reviewIteration,
|
|
1105
|
+
reviewFile: inputContract.reviewFile,
|
|
1106
|
+
reviewJsonFile: inputContract.reviewJsonFile,
|
|
1107
|
+
sourcePlanningIteration: inputContract.sourcePlanningIteration,
|
|
1108
|
+
outputIteration: inputContract.outputIteration,
|
|
1109
|
+
designFile: inputContract.designFile,
|
|
1110
|
+
designJsonFile: inputContract.designJsonFile,
|
|
1111
|
+
planFile: inputContract.planFile,
|
|
1112
|
+
planJsonFile: inputContract.planJsonFile,
|
|
1113
|
+
hasQaArtifacts: inputContract.hasQaArtifacts,
|
|
1114
|
+
qaFilePath: inputContract.qaFilePath,
|
|
1115
|
+
qaJsonFilePath: inputContract.qaJsonFilePath,
|
|
1116
|
+
qaFile: inputContract.qaFile,
|
|
1117
|
+
qaJsonFile: inputContract.qaJsonFile,
|
|
1118
|
+
revisedDesignFile: inputContract.revisedDesignFile,
|
|
1119
|
+
revisedDesignJsonFile: inputContract.revisedDesignJsonFile,
|
|
1120
|
+
revisedPlanFile: inputContract.revisedPlanFile,
|
|
1121
|
+
revisedPlanJsonFile: inputContract.revisedPlanJsonFile,
|
|
1122
|
+
revisedQaFile: inputContract.revisedQaFile,
|
|
1123
|
+
revisedQaJsonFile: inputContract.revisedQaJsonFile,
|
|
1124
|
+
hasTaskContextJsonFile: inputContract.hasTaskContextJsonFile,
|
|
1125
|
+
taskContextJsonFilePath: inputContract.taskContextJsonFilePath,
|
|
1126
|
+
taskContextJsonFile: inputContract.taskContextJsonFile,
|
|
1127
|
+
hasJiraTaskFile: inputContract.hasJiraTaskFile,
|
|
1128
|
+
jiraTaskFilePath: inputContract.jiraTaskFilePath,
|
|
1129
|
+
jiraTaskFile: inputContract.jiraTaskFile,
|
|
1130
|
+
hasJiraAttachmentsManifestFile: inputContract.hasJiraAttachmentsManifestFile,
|
|
1131
|
+
jiraAttachmentsManifestFilePath: inputContract.jiraAttachmentsManifestFilePath,
|
|
1132
|
+
jiraAttachmentsManifestFile: inputContract.jiraAttachmentsManifestFile,
|
|
1133
|
+
hasJiraAttachmentsContextFile: inputContract.hasJiraAttachmentsContextFile,
|
|
1134
|
+
jiraAttachmentsContextFilePath: inputContract.jiraAttachmentsContextFilePath,
|
|
1135
|
+
jiraAttachmentsContextFile: inputContract.jiraAttachmentsContextFile,
|
|
1136
|
+
hasPlanningAnswersJsonFile: inputContract.hasPlanningAnswersJsonFile,
|
|
1137
|
+
planningAnswersJsonFilePath: inputContract.planningAnswersJsonFilePath,
|
|
1138
|
+
planningAnswersJsonFile: inputContract.planningAnswersJsonFile,
|
|
1139
|
+
hasTaskInputJsonFile: inputContract.hasTaskInputJsonFile,
|
|
1140
|
+
taskInputJsonFilePath: inputContract.taskInputJsonFilePath,
|
|
1141
|
+
taskInputJsonFile: inputContract.taskInputJsonFile,
|
|
1142
|
+
extraPrompt: config.extraPrompt,
|
|
1143
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1144
|
+
if (!config.dryRun) {
|
|
1145
|
+
printSummary("Plan Revise", `Artifacts:\n${inputContract.revisedDesignFile}\n${inputContract.revisedDesignJsonFile}\n${inputContract.revisedPlanFile}\n${inputContract.revisedPlanJsonFile}\n${inputContract.revisedQaFile}\n${inputContract.revisedQaJsonFile}`);
|
|
1146
|
+
}
|
|
866
1147
|
return false;
|
|
867
1148
|
}
|
|
868
1149
|
if (config.command === "gitlab-review") {
|
|
@@ -875,7 +1156,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
875
1156
|
extraPrompt: config.extraPrompt,
|
|
876
1157
|
reviewFixSelectionJsonFile: reviewFixSelectionJsonFile(config.taskKey, iteration),
|
|
877
1158
|
reviewFixPoints: config.reviewFixPoints,
|
|
878
|
-
},
|
|
1159
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
879
1160
|
if (!config.dryRun) {
|
|
880
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)}`);
|
|
881
1162
|
}
|
|
@@ -889,7 +1170,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
889
1170
|
iteration,
|
|
890
1171
|
gitlabDiffIteration,
|
|
891
1172
|
extraPrompt: config.extraPrompt,
|
|
892
|
-
},
|
|
1173
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
893
1174
|
if (!config.dryRun) {
|
|
894
1175
|
printSummary("GitLab Diff Review", `Artifacts:\n${gitlabDiffFile(config.taskKey)}\n${gitlabDiffJsonFile(config.taskKey)}\n${reviewFile(config.taskKey, iteration)}\n${reviewJsonFile(config.taskKey, iteration)}`);
|
|
895
1176
|
}
|
|
@@ -907,7 +1188,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
907
1188
|
await runDeclarativeFlowBySpecFile("bugz/bug-fix.json", config, {
|
|
908
1189
|
taskKey: config.taskKey,
|
|
909
1190
|
extraPrompt: config.extraPrompt,
|
|
910
|
-
},
|
|
1191
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
911
1192
|
return false;
|
|
912
1193
|
}
|
|
913
1194
|
if (config.command === "mr-description") {
|
|
@@ -917,7 +1198,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
917
1198
|
taskKey: config.taskKey,
|
|
918
1199
|
iteration: nextArtifactIteration(config.taskKey, "mr-description"),
|
|
919
1200
|
extraPrompt: config.extraPrompt,
|
|
920
|
-
},
|
|
1201
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
921
1202
|
return false;
|
|
922
1203
|
}
|
|
923
1204
|
if (config.command === "task-describe") {
|
|
@@ -927,42 +1208,33 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
927
1208
|
taskKey: config.taskKey,
|
|
928
1209
|
iteration,
|
|
929
1210
|
extraPrompt: config.extraPrompt,
|
|
930
|
-
},
|
|
1211
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
931
1212
|
return false;
|
|
932
1213
|
}
|
|
933
1214
|
if (config.command === "implement") {
|
|
934
|
-
|
|
935
|
-
validateStructuredArtifacts([
|
|
936
|
-
{ path: designJsonFile(config.taskKey), schemaId: "implementation-design/v1" },
|
|
937
|
-
{ path: planJsonFile(config.taskKey), schemaId: "implementation-plan/v1" },
|
|
938
|
-
{ path: qaJsonFile(config.taskKey), schemaId: "qa-plan/v1" },
|
|
939
|
-
], "Implement mode requires valid structured plan artifacts from the planning phase.");
|
|
1215
|
+
const planningBundle = resolveLatestPlanningBundle(config.taskKey);
|
|
940
1216
|
await runDeclarativeFlowBySpecFile("implement.json", config, {
|
|
941
1217
|
taskKey: config.taskKey,
|
|
1218
|
+
planningIteration: planningBundle.planningIteration,
|
|
942
1219
|
extraPrompt: config.extraPrompt,
|
|
943
|
-
},
|
|
1220
|
+
}, flowOverrides, requestUserInput, undefined, launchMode);
|
|
944
1221
|
return false;
|
|
945
1222
|
}
|
|
946
1223
|
if (config.command === "review") {
|
|
947
1224
|
const iteration = nextReviewIterationForTask(config.taskKey);
|
|
948
|
-
if (config.
|
|
949
|
-
requireJiraConfig(config);
|
|
950
|
-
validateStructuredArtifacts([
|
|
951
|
-
{ path: designJsonFile(config.taskKey), schemaId: "implementation-design/v1" },
|
|
952
|
-
{ path: planJsonFile(config.taskKey), schemaId: "implementation-plan/v1" },
|
|
953
|
-
], "Review mode requires valid structured plan artifacts from the planning phase.");
|
|
1225
|
+
if (hasStructuredReviewInputs(config.taskKey)) {
|
|
954
1226
|
await runDeclarativeFlowBySpecFile("review/review.json", config, {
|
|
955
|
-
|
|
1227
|
+
...reviewFlowParamsFromContract(config),
|
|
956
1228
|
iteration,
|
|
957
1229
|
extraPrompt: config.extraPrompt,
|
|
958
|
-
},
|
|
1230
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
959
1231
|
}
|
|
960
1232
|
else {
|
|
961
1233
|
await runDeclarativeFlowBySpecFile("review/review-project.json", config, {
|
|
962
1234
|
taskKey: config.taskKey,
|
|
963
1235
|
iteration,
|
|
964
1236
|
extraPrompt: config.extraPrompt,
|
|
965
|
-
},
|
|
1237
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
966
1238
|
}
|
|
967
1239
|
return !config.dryRun && existsSync(readyToMergeFile(config.taskKey));
|
|
968
1240
|
}
|
|
@@ -981,23 +1253,21 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
981
1253
|
reviewFixSelectionJsonFile: reviewFixSelectionJsonFile(config.taskKey, latestIteration),
|
|
982
1254
|
extraPrompt: config.extraPrompt,
|
|
983
1255
|
reviewFixPoints: config.reviewFixPoints,
|
|
984
|
-
},
|
|
1256
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
985
1257
|
return false;
|
|
986
1258
|
}
|
|
987
1259
|
if (config.command === "review-loop") {
|
|
988
1260
|
const iteration = nextReviewIterationForTask(config.taskKey);
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
taskKey: config.taskKey,
|
|
998
|
-
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,
|
|
999
1269
|
extraPrompt: config.extraPrompt,
|
|
1000
|
-
},
|
|
1270
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1001
1271
|
return !config.dryRun && existsSync(readyToMergeFile(config.taskKey));
|
|
1002
1272
|
}
|
|
1003
1273
|
if (config.command === "run-go-tests-loop" || config.command === "run-go-linter-loop") {
|
|
@@ -1008,14 +1278,14 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
1008
1278
|
runGoTestsIteration: nextArtifactIteration(config.taskKey, "run-go-tests-result", "json"),
|
|
1009
1279
|
runGoLinterIteration: nextArtifactIteration(config.taskKey, "run-go-linter-result", "json"),
|
|
1010
1280
|
extraPrompt: config.extraPrompt,
|
|
1011
|
-
},
|
|
1281
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1012
1282
|
return false;
|
|
1013
1283
|
}
|
|
1014
1284
|
if (config.command === "git-commit") {
|
|
1015
1285
|
await runDeclarativeFlowBySpecFile("git-commit.json", config, {
|
|
1016
1286
|
taskKey: config.taskKey,
|
|
1017
1287
|
extraPrompt: config.extraPrompt,
|
|
1018
|
-
},
|
|
1288
|
+
}, flowOverrides, requestUserInput, undefined, launchMode, runtime);
|
|
1019
1289
|
return false;
|
|
1020
1290
|
}
|
|
1021
1291
|
throw new TaskRunnerError(`Unsupported command: ${config.command}`);
|
|
@@ -1043,6 +1313,7 @@ function parseCliArgs(argv) {
|
|
|
1043
1313
|
let prompt;
|
|
1044
1314
|
let autoFromPhase;
|
|
1045
1315
|
let scopeName;
|
|
1316
|
+
let reviewBlockingSeverities;
|
|
1046
1317
|
let helpPhases = false;
|
|
1047
1318
|
let jiraRef;
|
|
1048
1319
|
let mdLang;
|
|
@@ -1071,6 +1342,15 @@ function parseCliArgs(argv) {
|
|
|
1071
1342
|
index += 1;
|
|
1072
1343
|
continue;
|
|
1073
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
|
+
}
|
|
1074
1354
|
if (token === "--from") {
|
|
1075
1355
|
autoFromPhase = argv[index + 1];
|
|
1076
1356
|
index += 1;
|
|
@@ -1114,6 +1394,10 @@ function parseCliArgs(argv) {
|
|
|
1114
1394
|
printAutoCommonPhasesHelp();
|
|
1115
1395
|
process.exit(0);
|
|
1116
1396
|
}
|
|
1397
|
+
if (command === "auto-simple" && helpPhases) {
|
|
1398
|
+
printAutoSimplePhasesHelp();
|
|
1399
|
+
process.exit(0);
|
|
1400
|
+
}
|
|
1117
1401
|
return {
|
|
1118
1402
|
command: command,
|
|
1119
1403
|
dry,
|
|
@@ -1121,6 +1405,7 @@ function parseCliArgs(argv) {
|
|
|
1121
1405
|
helpPhases,
|
|
1122
1406
|
...(jiraRef !== undefined ? { jiraRef } : {}),
|
|
1123
1407
|
...(scopeName !== undefined ? { scopeName } : {}),
|
|
1408
|
+
...(reviewBlockingSeverities !== undefined ? { reviewBlockingSeverities } : {}),
|
|
1124
1409
|
...(prompt !== undefined ? { prompt } : {}),
|
|
1125
1410
|
...(autoFromPhase !== undefined ? { autoFromPhase } : {}),
|
|
1126
1411
|
...(mdLang !== undefined ? { mdLang } : {}),
|
|
@@ -1131,6 +1416,7 @@ function buildConfigFromArgs(args) {
|
|
|
1131
1416
|
return buildBaseConfig(args.command, {
|
|
1132
1417
|
...(args.jiraRef !== undefined ? { jiraRef: args.jiraRef } : {}),
|
|
1133
1418
|
...(args.scopeName !== undefined ? { scopeName: args.scopeName } : {}),
|
|
1419
|
+
...(args.reviewBlockingSeverities !== undefined ? { reviewBlockingSeverities: args.reviewBlockingSeverities } : {}),
|
|
1134
1420
|
...(args.prompt !== undefined ? { extraPrompt: args.prompt } : {}),
|
|
1135
1421
|
...(args.autoFromPhase !== undefined ? { autoFromPhase: args.autoFromPhase } : {}),
|
|
1136
1422
|
...(args.mdLang !== undefined ? { mdLang: args.mdLang } : {}),
|
|
@@ -1146,7 +1432,7 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1146
1432
|
let activeAbortController = null;
|
|
1147
1433
|
let activeFlowId = null;
|
|
1148
1434
|
let exiting = false;
|
|
1149
|
-
const ui =
|
|
1435
|
+
const ui = createInteractiveSession({
|
|
1150
1436
|
scopeKey: currentScope.scopeKey,
|
|
1151
1437
|
jiraIssueKey: currentScope.jiraIssueKey ?? null,
|
|
1152
1438
|
summaryText: "",
|
|
@@ -1171,40 +1457,51 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1171
1457
|
if (!flowEntry) {
|
|
1172
1458
|
throw new TaskRunnerError(`Unknown flow: ${flowId}`);
|
|
1173
1459
|
}
|
|
1460
|
+
const routingGroups = flowRoutingGroups(flowEntry, process.cwd());
|
|
1174
1461
|
const resumeState = launchMode === "resume" ? loadFlowRunState(currentScope.scopeKey, flowId) : null;
|
|
1175
1462
|
if (resumeState) {
|
|
1176
1463
|
currentScope = scopeWithRestoredJiraContext(currentScope, resumeState);
|
|
1177
1464
|
}
|
|
1178
|
-
const
|
|
1179
|
-
? resumeState?.
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
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.");
|
|
1183
1475
|
}
|
|
1476
|
+
const launchProfile = routingSelection?.routing?.defaultRoute;
|
|
1184
1477
|
const previousScopeKey = currentScope.scopeKey;
|
|
1185
|
-
const baseConfig =
|
|
1186
|
-
...(currentScope.jiraRef ? { jiraRef: currentScope.jiraRef } : {}),
|
|
1187
|
-
scopeName: currentScope.scopeKey,
|
|
1188
|
-
});
|
|
1478
|
+
const baseConfig = buildInteractiveBaseConfig(flowId, currentScope);
|
|
1189
1479
|
if (flowEntry.source === "built-in" && isBuiltInCommandFlowId(flowId)) {
|
|
1190
1480
|
const nextScope = await resolveScopeForCommand(baseConfig, (form) => ui.requestUserInput(form));
|
|
1191
1481
|
currentScope = nextScope;
|
|
1192
1482
|
}
|
|
1193
1483
|
else if (flowRequiresTaskScope(flowEntry) && !currentScope.jiraRef) {
|
|
1194
1484
|
const jiraContext = await requestJiraContext((form) => ui.requestUserInput(form));
|
|
1195
|
-
currentScope =
|
|
1485
|
+
currentScope = resolveProjectScope(null, jiraContext.jiraRef);
|
|
1196
1486
|
}
|
|
1197
1487
|
ui.setScope(currentScope.scopeKey, currentScope.jiraIssueKey ?? null);
|
|
1198
1488
|
if (previousScopeKey !== currentScope.scopeKey || currentScope.jiraIssueKey) {
|
|
1199
1489
|
syncInteractiveTaskSummary(ui, currentScope, forceRefresh);
|
|
1200
1490
|
}
|
|
1201
|
-
|
|
1491
|
+
if (routingSelection?.routing) {
|
|
1492
|
+
printPanel("Effective Launch Config", `preset: ${routingSelection.selectedPreset.label}\nmode: ${launchMode}\n${describeExecutionRouting(routingSelection.routing, routingGroups)}`, "cyan");
|
|
1493
|
+
}
|
|
1202
1494
|
if (flowEntry.source === "built-in" && isBuiltInCommandFlowId(flowId)) {
|
|
1203
|
-
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));
|
|
1204
1496
|
return;
|
|
1205
1497
|
}
|
|
1206
1498
|
const runtimeConfig = buildRuntimeConfig(baseConfig, currentScope);
|
|
1207
|
-
|
|
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));
|
|
1208
1505
|
}
|
|
1209
1506
|
catch (error) {
|
|
1210
1507
|
if (error instanceof FlowInterruptedError) {
|
|
@@ -1283,7 +1580,10 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1283
1580
|
return await runInteractive(args[0] ?? "", forceRefresh);
|
|
1284
1581
|
}
|
|
1285
1582
|
const parsedArgs = parseCliArgs(args);
|
|
1286
|
-
await executeCommand(buildConfigFromArgs(parsedArgs));
|
|
1583
|
+
const commandCompleted = await executeCommand(buildConfigFromArgs(parsedArgs));
|
|
1584
|
+
if (parsedArgs.command === "doctor") {
|
|
1585
|
+
return commandCompleted ? 0 : 1;
|
|
1586
|
+
}
|
|
1287
1587
|
return 0;
|
|
1288
1588
|
}
|
|
1289
1589
|
catch (error) {
|