agentweaver 0.1.10 → 0.1.11
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 +218 -224
- package/dist/artifacts.js +100 -55
- package/dist/executors/{codex-local-executor.js → codex-executor.js} +4 -4
- package/dist/executors/configs/{codex-local-config.js → codex-config.js} +1 -1
- package/dist/executors/configs/jira-fetch-config.js +2 -0
- package/dist/executors/configs/telegram-notifier-config.js +3 -0
- package/dist/executors/fetch-gitlab-diff-executor.js +1 -1
- package/dist/executors/fetch-gitlab-review-executor.js +1 -1
- package/dist/executors/git-commit-executor.js +25 -0
- package/dist/executors/telegram-notifier-executor.js +54 -0
- package/dist/flow-state.js +46 -1
- package/dist/gitlab.js +13 -8
- package/dist/index.js +469 -514
- package/dist/interactive-ui.js +144 -43
- package/dist/jira.js +52 -5
- package/dist/pipeline/auto-flow.js +6 -6
- package/dist/pipeline/context.js +1 -0
- package/dist/pipeline/flow-catalog.js +34 -4
- package/dist/pipeline/flow-model-settings.js +77 -0
- package/dist/pipeline/flow-specs/auto-common.json +446 -0
- package/dist/pipeline/flow-specs/auto-golang.json +563 -0
- package/dist/pipeline/flow-specs/{bug-analyze.json → bugz/bug-analyze.json} +43 -25
- package/dist/pipeline/flow-specs/{bug-fix.json → bugz/bug-fix.json} +5 -4
- package/dist/pipeline/flow-specs/git-commit.json +196 -0
- package/dist/pipeline/flow-specs/{gitlab-diff-review.json → gitlab/gitlab-diff-review.json} +20 -50
- package/dist/pipeline/flow-specs/gitlab/gitlab-review.json +165 -0
- package/dist/pipeline/flow-specs/{mr-description.json → gitlab/mr-description.json} +17 -10
- package/dist/pipeline/flow-specs/{run-go-linter-loop.json → go/run-go-linter-loop.json} +40 -14
- package/dist/pipeline/flow-specs/{run-go-tests-loop.json → go/run-go-tests-loop.json} +40 -14
- package/dist/pipeline/flow-specs/implement.json +5 -4
- package/dist/pipeline/flow-specs/plan.json +40 -148
- package/dist/pipeline/flow-specs/{review-fix.json → review/review-fix.json} +73 -13
- package/dist/pipeline/flow-specs/review/review-loop.json +280 -0
- package/dist/pipeline/flow-specs/review/review-project.json +87 -0
- package/dist/pipeline/flow-specs/review/review.json +126 -0
- package/dist/pipeline/flow-specs/task-describe.json +191 -11
- package/dist/pipeline/launch-profile-config.js +38 -0
- package/dist/pipeline/node-registry.js +75 -45
- package/dist/pipeline/nodes/build-failure-summary-node.js +16 -29
- package/dist/pipeline/nodes/build-review-fix-prompt-node.js +36 -0
- package/dist/pipeline/nodes/codex-prompt-node.js +41 -0
- package/dist/pipeline/nodes/commit-message-form-node.js +79 -0
- package/dist/pipeline/nodes/git-commit-form-node.js +138 -0
- package/dist/pipeline/nodes/git-commit-node.js +28 -0
- package/dist/pipeline/nodes/git-status-node.js +221 -0
- package/dist/pipeline/nodes/gitlab-review-artifacts-node.js +10 -6
- package/dist/pipeline/nodes/jira-context-node.js +10 -0
- package/dist/pipeline/nodes/llm-prompt-node.js +62 -0
- package/dist/pipeline/nodes/plan-codex-node.js +1 -1
- package/dist/pipeline/nodes/read-file-node.js +11 -0
- package/dist/pipeline/nodes/review-findings-form-node.js +18 -14
- package/dist/pipeline/nodes/select-files-form-node.js +72 -0
- package/dist/pipeline/nodes/telegram-notifier-node.js +28 -0
- package/dist/pipeline/nodes/user-input-node.js +29 -8
- package/dist/pipeline/nodes/write-selection-file-node.js +46 -0
- package/dist/pipeline/prompt-registry.js +2 -4
- package/dist/pipeline/prompt-runtime.js +13 -3
- package/dist/pipeline/registry.js +6 -8
- package/dist/pipeline/spec-compiler.js +5 -0
- package/dist/pipeline/spec-types.js +7 -3
- package/dist/pipeline/spec-validator.js +4 -0
- package/dist/pipeline/types.js +1 -0
- package/dist/pipeline/value-resolver.js +40 -38
- package/dist/prompts.js +104 -110
- package/dist/runtime/agentweaver-home.js +8 -0
- package/dist/runtime/command-resolution.js +0 -38
- package/dist/runtime/env-loader.js +43 -0
- package/dist/structured-artifact-schema-registry.js +53 -0
- package/dist/structured-artifact-schemas.json +0 -20
- package/dist/structured-artifacts.js +3 -43
- package/dist/user-input.js +30 -2
- package/package.json +2 -6
- package/Dockerfile.codex +0 -56
- package/dist/executors/claude-executor.js +0 -46
- package/dist/executors/codex-docker-executor.js +0 -27
- package/dist/executors/configs/claude-config.js +0 -12
- package/dist/executors/configs/codex-docker-config.js +0 -10
- package/dist/executors/configs/verify-build-config.js +0 -7
- package/dist/executors/verify-build-executor.js +0 -123
- package/dist/pipeline/flow-specs/auto.json +0 -979
- package/dist/pipeline/flow-specs/gitlab-review.json +0 -317
- package/dist/pipeline/flow-specs/opencode/auto-opencode.json +0 -1365
- package/dist/pipeline/flow-specs/opencode/bugz/bug-analyze-opencode.json +0 -382
- package/dist/pipeline/flow-specs/opencode/bugz/bug-fix-opencode.json +0 -56
- package/dist/pipeline/flow-specs/opencode/gitlab/gitlab-diff-review-opencode.json +0 -308
- package/dist/pipeline/flow-specs/opencode/gitlab/gitlab-review-opencode.json +0 -437
- package/dist/pipeline/flow-specs/opencode/gitlab/mr-description-opencode.json +0 -117
- package/dist/pipeline/flow-specs/opencode/go/run-go-linter-loop-opencode.json +0 -321
- package/dist/pipeline/flow-specs/opencode/go/run-go-tests-loop-opencode.json +0 -321
- package/dist/pipeline/flow-specs/opencode/implement-opencode.json +0 -64
- package/dist/pipeline/flow-specs/opencode/plan-opencode.json +0 -603
- package/dist/pipeline/flow-specs/opencode/review/review-fix-opencode.json +0 -209
- package/dist/pipeline/flow-specs/opencode/review/review-opencode.json +0 -452
- package/dist/pipeline/flow-specs/opencode/task-describe-opencode.json +0 -148
- package/dist/pipeline/flow-specs/review-project.json +0 -243
- package/dist/pipeline/flow-specs/review.json +0 -312
- package/dist/pipeline/flows/preflight-flow.js +0 -19
- package/dist/pipeline/nodes/claude-prompt-node.js +0 -54
- package/dist/pipeline/nodes/codex-docker-prompt-node.js +0 -32
- package/dist/pipeline/nodes/codex-local-prompt-node.js +0 -32
- package/dist/pipeline/nodes/review-claude-node.js +0 -38
- package/dist/pipeline/nodes/review-reply-codex-node.js +0 -40
- package/dist/pipeline/nodes/verify-build-node.js +0 -15
- package/dist/runtime/docker-runtime.js +0 -51
- package/docker-compose.yml +0 -445
- package/verify_build.sh +0 -105
package/dist/index.js
CHANGED
|
@@ -1,29 +1,37 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { existsSync, readdirSync, readFileSync
|
|
2
|
+
import { existsSync, readdirSync, 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 { REVIEW_FILE_RE,
|
|
6
|
+
import { REVIEW_FILE_RE, bugAnalyzeArtifacts, bugAnalyzeJsonFile, bugFixDesignJsonFile, bugFixPlanJsonFile, designJsonFile, gitlabDiffFile, gitlabDiffJsonFile, ensureScopeWorkspaceDir, gitlabReviewFile, gitlabReviewJsonFile, latestArtifactIteration, nextArtifactIteration, planJsonFile, planArtifacts, qaJsonFile, readyToMergeFile, requireArtifacts, reviewFile, reviewFixSelectionJsonFile, reviewJsonFile, scopeWorkspaceDir, flowStateFile, taskSummaryFile, } from "./artifacts.js";
|
|
7
7
|
import { FlowInterruptedError, TaskRunnerError } from "./errors.js";
|
|
8
|
-
import { createFlowRunState, hasResumableFlowState, loadFlowRunState, prepareFlowStateForResume, resetFlowRunState, saveFlowRunState, stripExecutionStatePayload, } from "./flow-state.js";
|
|
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
11
|
import { summarizeBuildFailure as summarizeBuildFailureViaPipeline } from "./pipeline/build-failure-summary.js";
|
|
12
|
+
import { runNodeChecks } from "./pipeline/checks.js";
|
|
12
13
|
import { createPipelineContext } from "./pipeline/context.js";
|
|
13
|
-
import { loadAutoFlow } from "./pipeline/auto-flow.js";
|
|
14
14
|
import { loadDeclarativeFlow } from "./pipeline/declarative-flows.js";
|
|
15
|
-
import {
|
|
15
|
+
import { runExpandedPhase } from "./pipeline/declarative-flow-runner.js";
|
|
16
16
|
import { findCatalogEntry, isBuiltInCommandFlowId, loadInteractiveFlowCatalog, toDeclarativeFlowRef } from "./pipeline/flow-catalog.js";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
17
|
+
import { ALLOWED_MODELS_BY_EXECUTOR, defaultModelForExecutor, DEFAULT_LAUNCH_PROFILE, LLM_EXECUTOR_IDS, resolveLaunchProfile, } from "./pipeline/launch-profile-config.js";
|
|
18
|
+
import { evaluateCondition, resolveValue } from "./pipeline/value-resolver.js";
|
|
19
|
+
import { resolveCmd } from "./runtime/command-resolution.js";
|
|
20
|
+
import { loadTieredEnv } from "./runtime/env-loader.js";
|
|
21
|
+
import { agentweaverHome } from "./runtime/agentweaver-home.js";
|
|
19
22
|
import { runCommand } from "./runtime/process-runner.js";
|
|
20
23
|
import { InteractiveUi } from "./interactive-ui.js";
|
|
21
24
|
import { bye, printError, printInfo, printPanel, printSummary, setFlowExecutionState, stripAnsi, } from "./tui.js";
|
|
22
25
|
import { requestUserInputInTerminal } from "./user-input.js";
|
|
23
26
|
import { attachJiraContext, detectGitBranchName, requestJiraContext, resolveProjectScope, } from "./scope.js";
|
|
24
27
|
const COMMANDS = [
|
|
28
|
+
"auto-golang",
|
|
29
|
+
"auto-common",
|
|
30
|
+
"auto-status",
|
|
31
|
+
"auto-reset",
|
|
25
32
|
"bug-analyze",
|
|
26
33
|
"bug-fix",
|
|
34
|
+
"git-commit",
|
|
27
35
|
"gitlab-diff-review",
|
|
28
36
|
"gitlab-review",
|
|
29
37
|
"mr-description",
|
|
@@ -32,20 +40,15 @@ const COMMANDS = [
|
|
|
32
40
|
"implement",
|
|
33
41
|
"review",
|
|
34
42
|
"review-fix",
|
|
43
|
+
"review-loop",
|
|
35
44
|
"run-go-tests-loop",
|
|
36
45
|
"run-go-linter-loop",
|
|
37
|
-
"auto",
|
|
38
|
-
"auto-status",
|
|
39
|
-
"auto-reset",
|
|
40
46
|
];
|
|
41
|
-
const AUTO_STATE_SCHEMA_VERSION = 3;
|
|
42
47
|
const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
43
48
|
const PACKAGE_ROOT = path.resolve(MODULE_DIR, "..");
|
|
44
49
|
function createRuntimeServices(signal) {
|
|
45
50
|
return {
|
|
46
51
|
resolveCmd,
|
|
47
|
-
resolveDockerComposeCmd,
|
|
48
|
-
dockerRuntimeEnv: () => dockerRuntimeEnv(PACKAGE_ROOT),
|
|
49
52
|
runCommand: (argv, options = {}) => runCommand(argv, { ...options, ...(signal ? { signal } : {}) }),
|
|
50
53
|
};
|
|
51
54
|
}
|
|
@@ -81,26 +84,31 @@ function formatProcessFailure(error) {
|
|
|
81
84
|
}
|
|
82
85
|
return `${baseMessage}\nПричина:\n${preview}`;
|
|
83
86
|
}
|
|
87
|
+
function escapeRegExp(value) {
|
|
88
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
89
|
+
}
|
|
84
90
|
function usage() {
|
|
85
91
|
return `Usage:
|
|
86
92
|
agentweaver
|
|
87
93
|
agentweaver <jira-browse-url|jira-issue-key>
|
|
88
94
|
agentweaver --force <jira-browse-url|jira-issue-key>
|
|
95
|
+
agentweaver git-commit [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
89
96
|
agentweaver gitlab-diff-review [--dry] [--verbose] [--prompt <text>] [--scope <name>]
|
|
90
97
|
agentweaver gitlab-review [--dry] [--verbose] [--prompt <text>] [--scope <name>]
|
|
91
98
|
agentweaver bug-analyze [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
92
99
|
agentweaver bug-fix [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
93
100
|
agentweaver mr-description [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
94
|
-
agentweaver plan [--dry] [--verbose] [--prompt <text>] [<jira-browse-url|jira-issue-key>]
|
|
95
|
-
agentweaver task-describe [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
101
|
+
agentweaver plan [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] [<jira-browse-url|jira-issue-key>]
|
|
102
|
+
agentweaver task-describe [--dry] [--verbose] [--prompt <text>] [<jira-browse-url|jira-issue-key>]
|
|
96
103
|
agentweaver implement [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
97
104
|
agentweaver review [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
98
105
|
agentweaver review-fix [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
106
|
+
agentweaver review-loop [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
99
107
|
agentweaver run-go-tests-loop [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
100
108
|
agentweaver run-go-linter-loop [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
101
|
-
agentweaver auto [--dry] [--verbose] [--prompt <text>] [<jira-browse-url|jira-issue-key>]
|
|
102
|
-
agentweaver auto [--dry] [--verbose] [--prompt <text>] --from <phase> [<jira-browse-url|jira-issue-key>]
|
|
103
|
-
agentweaver auto --help-phases
|
|
109
|
+
agentweaver auto-golang [--dry] [--verbose] [--prompt <text>] [<jira-browse-url|jira-issue-key>]
|
|
110
|
+
agentweaver auto-golang [--dry] [--verbose] [--prompt <text>] --from <phase> [<jira-browse-url|jira-issue-key>]
|
|
111
|
+
agentweaver auto-golang --help-phases
|
|
104
112
|
agentweaver auto-status [<jira-browse-url|jira-issue-key>]
|
|
105
113
|
agentweaver auto-reset [<jira-browse-url|jira-issue-key>]
|
|
106
114
|
|
|
@@ -112,28 +120,28 @@ Interactive Mode:
|
|
|
112
120
|
Flags:
|
|
113
121
|
--version Show package version
|
|
114
122
|
--force In interactive mode, regenerate task summary in Jira-backed flows
|
|
115
|
-
--dry Fetch Jira task, but print
|
|
123
|
+
--dry Fetch Jira task, but print codex/opencode commands instead of executing them
|
|
116
124
|
--verbose Show live stdout/stderr of launched commands
|
|
117
125
|
--scope Explicit workflow scope name for non-Jira runs
|
|
118
126
|
--prompt Extra prompt text appended to the base prompt
|
|
127
|
+
--md-lang Language for markdown output files: en (English) or ru (Russian, default)
|
|
119
128
|
|
|
120
129
|
Required environment variables:
|
|
121
|
-
JIRA_API_KEY Jira API
|
|
130
|
+
JIRA_API_KEY Jira API token used for Jira-backed flows (Bearer by default, or Basic with Jira Cloud)
|
|
122
131
|
|
|
123
132
|
Optional environment variables:
|
|
133
|
+
JIRA_USERNAME Required for Jira Cloud Basic auth (usually Atlassian account email)
|
|
134
|
+
JIRA_AUTH_MODE Override Jira auth mode: auto | basic | bearer
|
|
124
135
|
JIRA_BASE_URL
|
|
125
136
|
GITLAB_TOKEN
|
|
126
137
|
AGENTWEAVER_HOME
|
|
127
|
-
DOCKER_COMPOSE_BIN
|
|
128
138
|
CODEX_BIN
|
|
129
139
|
CODEX_MODEL
|
|
130
140
|
OPENCODE_BIN
|
|
131
141
|
OPENCODE_MODEL
|
|
132
|
-
CLAUDE_BIN
|
|
133
|
-
CLAUDE_MODEL
|
|
134
142
|
|
|
135
143
|
Notes:
|
|
136
|
-
-
|
|
144
|
+
- 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.
|
|
137
145
|
- All flow state and artifacts are stored in the current project scope by default.
|
|
138
146
|
- gitlab-review and gitlab-diff-review ask for GitLab merge request URL via user-input.`;
|
|
139
147
|
}
|
|
@@ -145,212 +153,235 @@ function packageVersion() {
|
|
|
145
153
|
}
|
|
146
154
|
return raw.version;
|
|
147
155
|
}
|
|
148
|
-
function nowIso8601() {
|
|
149
|
-
return new Date().toISOString();
|
|
150
|
-
}
|
|
151
156
|
function normalizeAutoPhaseId(phaseId) {
|
|
152
157
|
return phaseId.trim().toLowerCase().replaceAll("-", "_");
|
|
153
158
|
}
|
|
154
|
-
function buildAutoSteps() {
|
|
155
|
-
return loadAutoFlow().phases.map((phase) => ({
|
|
156
|
-
id: phase.id,
|
|
157
|
-
status: "pending",
|
|
158
|
-
}));
|
|
159
|
-
}
|
|
160
159
|
function autoPhaseIds() {
|
|
161
|
-
return
|
|
160
|
+
return loadDeclarativeFlow({ source: "built-in", fileName: "auto-golang.json" }).phases.map((phase) => phase.id);
|
|
162
161
|
}
|
|
163
162
|
function validateAutoPhaseId(phaseId) {
|
|
164
163
|
const normalized = normalizeAutoPhaseId(phaseId);
|
|
165
164
|
if (!autoPhaseIds().includes(normalized)) {
|
|
166
|
-
throw new TaskRunnerError(`Unknown auto phase: ${phaseId}\nUse 'agentweaver auto --help-phases' or '/help auto' to list valid phases.`);
|
|
165
|
+
throw new TaskRunnerError(`Unknown auto-golang phase: ${phaseId}\nUse 'agentweaver auto-golang --help-phases' or '/help auto-golang' to list valid phases.`);
|
|
167
166
|
}
|
|
168
167
|
return normalized;
|
|
169
168
|
}
|
|
170
|
-
function
|
|
171
|
-
const
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
maxReviewIterations,
|
|
180
|
-
updatedAt: nowIso8601(),
|
|
181
|
-
steps: buildAutoSteps(),
|
|
182
|
-
executionState: {
|
|
183
|
-
flowKind: autoFlow.kind,
|
|
184
|
-
flowVersion: autoFlow.version,
|
|
185
|
-
terminated: false,
|
|
186
|
-
phases: [],
|
|
187
|
-
},
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
function loadAutoPipelineState(config) {
|
|
191
|
-
const filePath = autoStateFile(config.taskKey);
|
|
192
|
-
if (!existsSync(filePath)) {
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
let raw;
|
|
196
|
-
try {
|
|
197
|
-
raw = JSON.parse(readFileSync(filePath, "utf8"));
|
|
169
|
+
function buildFlowResumeDetails(state) {
|
|
170
|
+
const currentStep = findCurrentFlowExecutionStep(state) ?? state.currentStep ?? "-";
|
|
171
|
+
const lines = [
|
|
172
|
+
"Interrupted run found.",
|
|
173
|
+
`Current step: ${currentStep}`,
|
|
174
|
+
`Updated: ${state.updatedAt}`,
|
|
175
|
+
];
|
|
176
|
+
if (state.launchProfile) {
|
|
177
|
+
lines.push(`Launch profile: ${state.launchProfile.executor} / ${state.launchProfile.model}`);
|
|
198
178
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
202
|
-
if (!raw || typeof raw !== "object") {
|
|
203
|
-
throw new TaskRunnerError(`Invalid auto state file format: ${filePath}`);
|
|
204
|
-
}
|
|
205
|
-
const state = raw;
|
|
206
|
-
if (state.schemaVersion !== AUTO_STATE_SCHEMA_VERSION) {
|
|
207
|
-
throw new TaskRunnerError(`Unsupported auto state schema in ${filePath}: ${state.schemaVersion}`);
|
|
208
|
-
}
|
|
209
|
-
if (!state.executionState) {
|
|
210
|
-
const autoFlow = loadAutoFlow();
|
|
211
|
-
state.executionState = {
|
|
212
|
-
flowKind: autoFlow.kind,
|
|
213
|
-
flowVersion: autoFlow.version,
|
|
214
|
-
terminated: false,
|
|
215
|
-
phases: [],
|
|
216
|
-
};
|
|
179
|
+
if (state.lastError) {
|
|
180
|
+
lines.push(`Last error: ${state.lastError.message ?? "-"} (exit ${state.lastError.returnCode ?? "-"})`);
|
|
217
181
|
}
|
|
218
|
-
|
|
219
|
-
return state;
|
|
182
|
+
return lines.join("\n");
|
|
220
183
|
}
|
|
221
|
-
function
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
184
|
+
function launchProfileSelectionForm() {
|
|
185
|
+
const defaultExecutor = DEFAULT_LAUNCH_PROFILE.executor;
|
|
186
|
+
return {
|
|
187
|
+
formId: "flow-launch-profile",
|
|
188
|
+
title: "Настройки запуска LLM",
|
|
189
|
+
description: `Выберите executor для запуска flow. Текущий default: ${defaultExecutor}.`,
|
|
190
|
+
submitLabel: "Continue",
|
|
191
|
+
fields: [
|
|
192
|
+
{
|
|
193
|
+
id: "executor",
|
|
194
|
+
type: "single-select",
|
|
195
|
+
label: "Executor",
|
|
196
|
+
required: true,
|
|
197
|
+
default: "default",
|
|
198
|
+
options: [
|
|
199
|
+
{ value: "default", label: `default (${defaultExecutor})` },
|
|
200
|
+
...LLM_EXECUTOR_IDS.map((id) => ({
|
|
201
|
+
value: id,
|
|
202
|
+
label: id === defaultExecutor ? `${id} [default]` : id,
|
|
203
|
+
})),
|
|
204
|
+
],
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
};
|
|
228
208
|
}
|
|
229
|
-
function
|
|
230
|
-
|
|
231
|
-
|
|
209
|
+
function launchModelSelectionForm(executor) {
|
|
210
|
+
const resolvedExecutor = executor === "default" ? DEFAULT_LAUNCH_PROFILE.executor : executor;
|
|
211
|
+
const defaultModel = defaultModelForExecutor(resolvedExecutor);
|
|
212
|
+
const options = executor === "default"
|
|
213
|
+
? [{ value: "default", label: `default (${DEFAULT_LAUNCH_PROFILE.model})` }]
|
|
214
|
+
: [
|
|
215
|
+
{ value: "default", label: `default (${defaultModel})` },
|
|
216
|
+
...ALLOWED_MODELS_BY_EXECUTOR[executor].map((model) => ({
|
|
217
|
+
value: model,
|
|
218
|
+
label: model === defaultModel ? `${model} [default]` : model,
|
|
219
|
+
})),
|
|
220
|
+
];
|
|
221
|
+
return {
|
|
222
|
+
formId: "flow-launch-model",
|
|
223
|
+
title: "Настройки запуска LLM",
|
|
224
|
+
description: `Выберите модель для запуска flow. Текущий default для ${resolvedExecutor}: ${defaultModel}.`,
|
|
225
|
+
submitLabel: "Start",
|
|
226
|
+
fields: [
|
|
227
|
+
{
|
|
228
|
+
id: "model",
|
|
229
|
+
type: "single-select",
|
|
230
|
+
label: "Model",
|
|
231
|
+
required: true,
|
|
232
|
+
default: "default",
|
|
233
|
+
options,
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
};
|
|
232
237
|
}
|
|
233
|
-
function
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
238
|
+
async function requestInteractiveLaunchProfile(requestUserInput) {
|
|
239
|
+
const executorFormResult = await requestUserInput(launchProfileSelectionForm());
|
|
240
|
+
const rawExecutor = String(executorFormResult.values.executor ?? "default");
|
|
241
|
+
const executor = rawExecutor === "default" ? "default" : LLM_EXECUTOR_IDS.find((id) => id === rawExecutor);
|
|
242
|
+
if (!executor) {
|
|
243
|
+
throw new TaskRunnerError(`Unsupported launch executor '${rawExecutor}'.`);
|
|
244
|
+
}
|
|
245
|
+
const modelFormResult = await requestUserInput(launchModelSelectionForm(executor));
|
|
246
|
+
const rawModel = String(modelFormResult.values.model ?? "default").trim();
|
|
247
|
+
return resolveLaunchProfile({
|
|
248
|
+
executor,
|
|
249
|
+
model: rawModel.length > 0 ? rawModel : "default",
|
|
250
|
+
}, DEFAULT_LAUNCH_PROFILE);
|
|
240
251
|
}
|
|
241
|
-
function
|
|
242
|
-
return
|
|
252
|
+
function buildResolverContext(pipelineContext, flowParams, flowConstants, repeatVars, executionState) {
|
|
253
|
+
return {
|
|
254
|
+
flowParams,
|
|
255
|
+
flowConstants,
|
|
256
|
+
pipelineContext,
|
|
257
|
+
repeatVars,
|
|
258
|
+
executionState,
|
|
259
|
+
};
|
|
243
260
|
}
|
|
244
|
-
function
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
261
|
+
function resolveResumeChecks(step, context) {
|
|
262
|
+
return (step.expect ?? [])
|
|
263
|
+
.filter((expectation) => evaluateCondition(expectation.when, context))
|
|
264
|
+
.flatMap((expectation) => {
|
|
265
|
+
if (expectation.kind === "step-output") {
|
|
266
|
+
const value = resolveValue(expectation.value, context);
|
|
267
|
+
if (expectation.equals !== undefined) {
|
|
268
|
+
const expected = resolveValue(expectation.equals, context);
|
|
269
|
+
if (value !== expected) {
|
|
270
|
+
throw new TaskRunnerError(expectation.message);
|
|
271
|
+
}
|
|
272
|
+
return [];
|
|
273
|
+
}
|
|
274
|
+
if (!value) {
|
|
275
|
+
throw new TaskRunnerError(expectation.message);
|
|
276
|
+
}
|
|
277
|
+
return [];
|
|
249
278
|
}
|
|
250
|
-
|
|
251
|
-
|
|
279
|
+
if (expectation.kind === "require-artifacts") {
|
|
280
|
+
const value = resolveValue(expectation.paths, context);
|
|
281
|
+
if (!Array.isArray(value) || value.some((candidate) => typeof candidate !== "string")) {
|
|
282
|
+
throw new TaskRunnerError("Expectation 'require-artifacts' must resolve to string[]");
|
|
283
|
+
}
|
|
284
|
+
return [{ kind: "require-artifacts", paths: value, message: expectation.message }];
|
|
285
|
+
}
|
|
286
|
+
if (expectation.kind === "require-file") {
|
|
287
|
+
const value = resolveValue(expectation.path, context);
|
|
288
|
+
if (typeof value !== "string") {
|
|
289
|
+
throw new TaskRunnerError("Expectation 'require-file' must resolve to string");
|
|
290
|
+
}
|
|
291
|
+
return [{ kind: "require-file", path: value, message: expectation.message }];
|
|
292
|
+
}
|
|
293
|
+
const items = expectation.items.map((item) => {
|
|
294
|
+
const value = resolveValue(item.path, context);
|
|
295
|
+
if (typeof value !== "string") {
|
|
296
|
+
throw new TaskRunnerError("Expectation 'require-structured-artifacts' item path must resolve to string");
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
path: value,
|
|
300
|
+
schemaId: item.schemaId,
|
|
301
|
+
};
|
|
302
|
+
});
|
|
303
|
+
return [{ kind: "require-structured-artifacts", items, message: expectation.message }];
|
|
304
|
+
});
|
|
252
305
|
}
|
|
253
|
-
function
|
|
254
|
-
if (
|
|
255
|
-
return
|
|
256
|
-
}
|
|
257
|
-
if (state.executionState.terminated) {
|
|
258
|
-
return "completed";
|
|
259
|
-
}
|
|
260
|
-
if (state.steps.some((candidate) => candidate.status === "running")) {
|
|
261
|
-
return "running";
|
|
262
|
-
}
|
|
263
|
-
if (state.steps.some((candidate) => candidate.status === "pending")) {
|
|
264
|
-
return "pending";
|
|
265
|
-
}
|
|
266
|
-
if (state.steps.some((candidate) => candidate.status === "skipped")) {
|
|
267
|
-
return "completed";
|
|
306
|
+
function validateDeclarativePhaseResumeState(phase, phaseState, pipelineContext, flowParams, flowConstants, executionState) {
|
|
307
|
+
if (phaseState.status === "done") {
|
|
308
|
+
return;
|
|
268
309
|
}
|
|
269
|
-
|
|
270
|
-
|
|
310
|
+
for (const [stepIndex, step] of phase.steps.entries()) {
|
|
311
|
+
const stepState = phaseState.steps[stepIndex];
|
|
312
|
+
if (!stepState || stepState.status !== "done") {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
const context = buildResolverContext(pipelineContext, flowParams, flowConstants, step.repeatVars, executionState);
|
|
316
|
+
const checks = resolveResumeChecks(step, context);
|
|
317
|
+
try {
|
|
318
|
+
runNodeChecks(checks);
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
throw new TaskRunnerError(`Resume is impossible for '${phase.id}:${step.id}' because required artifacts are missing or invalid. Use restart.\n${error.message}`);
|
|
322
|
+
}
|
|
271
323
|
}
|
|
272
|
-
return state.status;
|
|
273
324
|
}
|
|
274
|
-
function
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
];
|
|
282
|
-
if (state.lastError) {
|
|
283
|
-
lines.push(`Last error: ${state.lastError.step ?? "-"} (exit ${state.lastError.returnCode ?? "-"}, ${state.lastError.message ?? "-"})`);
|
|
284
|
-
}
|
|
285
|
-
lines.push("");
|
|
286
|
-
for (const step of state.steps) {
|
|
287
|
-
lines.push(`[${step.status}] ${step.id}${step.note ? ` (${step.note})` : ""}`);
|
|
288
|
-
const phaseState = state.executionState.phases.find((candidate) => candidate.id === step.id);
|
|
289
|
-
for (const childStep of phaseState?.steps ?? []) {
|
|
290
|
-
lines.push(` - [${childStep.status}] ${childStep.id}`);
|
|
325
|
+
function validateDeclarativeFlowResumeState(flowEntry, config, state, launchProfile, runtime = runtimeServices) {
|
|
326
|
+
if (state.launchProfile) {
|
|
327
|
+
if (!launchProfile) {
|
|
328
|
+
throw new TaskRunnerError("Resume is impossible because launch profile is missing. Use restart.");
|
|
329
|
+
}
|
|
330
|
+
if (state.launchProfile.fingerprint !== launchProfile.fingerprint) {
|
|
331
|
+
throw new TaskRunnerError(`Resume is impossible because launch profile changed (${state.launchProfile.executor}/${state.launchProfile.model} -> ${launchProfile.executor}/${launchProfile.model}). Use restart.`);
|
|
291
332
|
}
|
|
292
333
|
}
|
|
293
|
-
if (
|
|
294
|
-
|
|
334
|
+
if (flowRequiresTaskScope(flowEntry) && !config.jiraRef) {
|
|
335
|
+
throw new TaskRunnerError("Resume is impossible because Jira context is missing for this flow state. Use restart.");
|
|
295
336
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
337
|
+
const pipelineContext = createPipelineContext({
|
|
338
|
+
issueKey: config.taskKey,
|
|
339
|
+
jiraRef: config.jiraRef,
|
|
340
|
+
dryRun: config.dryRun,
|
|
341
|
+
verbose: config.verbose,
|
|
342
|
+
...(config.mdLang !== undefined ? { mdLang: config.mdLang } : {}),
|
|
343
|
+
runtime,
|
|
344
|
+
requestUserInput: requestUserInputInTerminal,
|
|
345
|
+
});
|
|
346
|
+
const flowParams = defaultDeclarativeFlowParams(config, false, launchProfile ? { launchProfile } : {});
|
|
347
|
+
for (const phase of flowEntry.flow.phases) {
|
|
348
|
+
const phaseState = state.executionState.phases.find((candidate) => candidate.id === phase.id);
|
|
301
349
|
if (!phaseState) {
|
|
302
350
|
continue;
|
|
303
351
|
}
|
|
304
|
-
|
|
305
|
-
step.startedAt = phaseState.startedAt ?? null;
|
|
306
|
-
step.finishedAt = phaseState.finishedAt ?? null;
|
|
307
|
-
step.note = null;
|
|
308
|
-
if (phaseState.status === "skipped") {
|
|
309
|
-
step.note = "condition not met";
|
|
310
|
-
step.returnCode ??= 0;
|
|
311
|
-
}
|
|
312
|
-
else if (phaseState.status === "done") {
|
|
313
|
-
step.returnCode ??= 0;
|
|
314
|
-
if (state.executionState.terminated && state.executionState.terminationReason?.startsWith(`Stopped by ${step.id}:`)) {
|
|
315
|
-
step.note = "stop condition met";
|
|
316
|
-
}
|
|
317
|
-
}
|
|
352
|
+
validateDeclarativePhaseResumeState(phase, phaseState, pipelineContext, flowParams, flowEntry.flow.constants, state.executionState);
|
|
318
353
|
}
|
|
319
|
-
state.currentStep = findCurrentExecutionStep(state);
|
|
320
|
-
state.status = deriveAutoPipelineStatus(state);
|
|
321
354
|
}
|
|
322
|
-
function
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
"Interrupted auto run found.",
|
|
326
|
-
`Current step: ${currentStep}`,
|
|
327
|
-
`Updated: ${state.updatedAt}`,
|
|
328
|
-
];
|
|
329
|
-
if (state.lastError) {
|
|
330
|
-
lines.push(`Last error: ${state.lastError.message ?? "-"} (exit ${state.lastError.returnCode ?? "-"})`);
|
|
355
|
+
function scopeWithRestoredJiraContext(scope, state) {
|
|
356
|
+
if (scope.jiraRef || !state?.jiraRef?.trim()) {
|
|
357
|
+
return scope;
|
|
331
358
|
}
|
|
332
|
-
return
|
|
333
|
-
}
|
|
334
|
-
function buildFlowResumeDetails(state) {
|
|
335
|
-
const currentStep = findCurrentFlowExecutionStep(state) ?? state.currentStep ?? "-";
|
|
336
|
-
const lines = [
|
|
337
|
-
"Interrupted run found.",
|
|
338
|
-
`Current step: ${currentStep}`,
|
|
339
|
-
`Updated: ${state.updatedAt}`,
|
|
340
|
-
];
|
|
341
|
-
if (state.lastError) {
|
|
342
|
-
lines.push(`Last error: ${state.lastError.message ?? "-"} (exit ${state.lastError.returnCode ?? "-"})`);
|
|
343
|
-
}
|
|
344
|
-
return lines.join("\n");
|
|
359
|
+
return attachJiraContext(scope, state.jiraRef);
|
|
345
360
|
}
|
|
346
361
|
function lookupInteractiveFlowResume(flowEntry, currentScope) {
|
|
347
362
|
const directState = loadFlowRunState(currentScope.scopeKey, flowEntry.id);
|
|
348
363
|
if (directState && hasResumableFlowState(directState)) {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
364
|
+
try {
|
|
365
|
+
const effectiveScope = scopeWithRestoredJiraContext(currentScope, directState);
|
|
366
|
+
const baseConfig = buildBaseConfig(flowEntry.id, {
|
|
367
|
+
...(effectiveScope.jiraRef ? { jiraRef: effectiveScope.jiraRef } : {}),
|
|
368
|
+
scopeName: effectiveScope.scopeKey,
|
|
369
|
+
});
|
|
370
|
+
const config = buildRuntimeConfig(baseConfig, effectiveScope);
|
|
371
|
+
validateDeclarativeFlowResumeState(flowEntry, config, directState, directState.launchProfile);
|
|
372
|
+
return {
|
|
373
|
+
resumeAvailable: true,
|
|
374
|
+
hasExistingState: true,
|
|
375
|
+
details: buildFlowResumeDetails(directState),
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
catch (error) {
|
|
379
|
+
return {
|
|
380
|
+
resumeAvailable: false,
|
|
381
|
+
hasExistingState: true,
|
|
382
|
+
details: `Interrupted run found, but resume is unavailable.\n${error.message}`,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
354
385
|
}
|
|
355
386
|
return {
|
|
356
387
|
resumeAvailable: false,
|
|
@@ -358,37 +389,17 @@ function lookupInteractiveFlowResume(flowEntry, currentScope) {
|
|
|
358
389
|
};
|
|
359
390
|
}
|
|
360
391
|
function printAutoPhasesHelp() {
|
|
361
|
-
const phaseLines = ["Available auto phases:", "", ...autoPhaseIds()];
|
|
362
|
-
phaseLines.push("", "You can resume auto from a phase with:", "agentweaver auto --from <phase> <jira>", "or in interactive mode:", "/auto --from <phase>");
|
|
363
|
-
printPanel("Auto Phases", phaseLines.join("\n"), "magenta");
|
|
392
|
+
const phaseLines = ["Available auto-golang phases:", "", ...autoPhaseIds()];
|
|
393
|
+
phaseLines.push("", "You can resume auto-golang from a phase with:", "agentweaver auto-golang --from <phase> <jira>", "or in interactive mode:", "/auto-golang --from <phase>");
|
|
394
|
+
printPanel("Auto-Golang Phases", phaseLines.join("\n"), "magenta");
|
|
364
395
|
}
|
|
365
|
-
function
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
if (!line || line.startsWith("#")) {
|
|
373
|
-
continue;
|
|
374
|
-
}
|
|
375
|
-
if (line.startsWith("export ")) {
|
|
376
|
-
line = line.slice(7).trim();
|
|
377
|
-
}
|
|
378
|
-
const separatorIndex = line.indexOf("=");
|
|
379
|
-
if (separatorIndex < 0) {
|
|
380
|
-
continue;
|
|
381
|
-
}
|
|
382
|
-
const key = line.slice(0, separatorIndex).trim();
|
|
383
|
-
if (!key || process.env[key] !== undefined) {
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
let value = line.slice(separatorIndex + 1).trim();
|
|
387
|
-
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
388
|
-
value = value.slice(1, -1);
|
|
389
|
-
}
|
|
390
|
-
process.env[key] = value;
|
|
391
|
-
}
|
|
396
|
+
function autoCommonPhaseIds() {
|
|
397
|
+
return loadDeclarativeFlow({ source: "built-in", fileName: "auto-common.json" }).phases.map((phase) => phase.id);
|
|
398
|
+
}
|
|
399
|
+
function printAutoCommonPhasesHelp() {
|
|
400
|
+
const phaseLines = ["Available auto-common phases:", "", ...autoCommonPhaseIds()];
|
|
401
|
+
phaseLines.push("", "You can run auto-common with:", "agentweaver auto-common <jira>");
|
|
402
|
+
printPanel("Auto-Common Phases", phaseLines.join("\n"), "magenta");
|
|
392
403
|
}
|
|
393
404
|
function nextReviewIterationForTask(taskKey) {
|
|
394
405
|
let maxIndex = 0;
|
|
@@ -400,33 +411,14 @@ function nextReviewIterationForTask(taskKey) {
|
|
|
400
411
|
if (!entry.isFile()) {
|
|
401
412
|
continue;
|
|
402
413
|
}
|
|
403
|
-
const match = REVIEW_FILE_RE.exec(entry.name)
|
|
414
|
+
const match = REVIEW_FILE_RE.exec(entry.name);
|
|
404
415
|
if (match && match[1] === taskKey) {
|
|
405
416
|
maxIndex = Math.max(maxIndex, Number.parseInt(match[2] ?? "0", 10));
|
|
406
417
|
}
|
|
407
418
|
}
|
|
408
419
|
return maxIndex + 1;
|
|
409
420
|
}
|
|
410
|
-
function latestReviewReplyIteration(taskKey) {
|
|
411
|
-
let maxIndex = null;
|
|
412
|
-
const workspaceDir = scopeWorkspaceDir(taskKey);
|
|
413
|
-
if (!existsSync(workspaceDir)) {
|
|
414
|
-
return null;
|
|
415
|
-
}
|
|
416
|
-
for (const entry of readdirSync(workspaceDir, { withFileTypes: true })) {
|
|
417
|
-
if (!entry.isFile()) {
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
const match = REVIEW_REPLY_FILE_RE.exec(entry.name);
|
|
421
|
-
if (match && match[1] === taskKey) {
|
|
422
|
-
const current = Number.parseInt(match[2] ?? "0", 10);
|
|
423
|
-
maxIndex = maxIndex === null ? current : Math.max(maxIndex, current);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
return maxIndex;
|
|
427
|
-
}
|
|
428
421
|
function buildBaseConfig(command, options = {}) {
|
|
429
|
-
const homeDir = agentweaverHome(PACKAGE_ROOT);
|
|
430
422
|
return {
|
|
431
423
|
command,
|
|
432
424
|
jiraRef: options.jiraRef ?? null,
|
|
@@ -434,12 +426,9 @@ function buildBaseConfig(command, options = {}) {
|
|
|
434
426
|
reviewFixPoints: options.reviewFixPoints ?? null,
|
|
435
427
|
extraPrompt: options.extraPrompt ?? null,
|
|
436
428
|
autoFromPhase: options.autoFromPhase ? validateAutoPhaseId(options.autoFromPhase) : null,
|
|
429
|
+
mdLang: options.mdLang ?? null,
|
|
437
430
|
dryRun: options.dryRun ?? false,
|
|
438
431
|
verbose: options.verbose ?? false,
|
|
439
|
-
dockerComposeFile: defaultDockerComposeFile(PACKAGE_ROOT),
|
|
440
|
-
runGoTestsScript: path.join(homeDir, "run_go_tests.py"),
|
|
441
|
-
runGoLinterScript: path.join(homeDir, "run_go_linter.py"),
|
|
442
|
-
runGoCoverageScript: path.join(homeDir, "run_go_coverage.sh"),
|
|
443
432
|
};
|
|
444
433
|
}
|
|
445
434
|
function commandRequiresTask(command) {
|
|
@@ -447,17 +436,20 @@ function commandRequiresTask(command) {
|
|
|
447
436
|
command === "bug-analyze" ||
|
|
448
437
|
command === "bug-fix" ||
|
|
449
438
|
command === "mr-description" ||
|
|
450
|
-
command === "
|
|
451
|
-
command === "auto" ||
|
|
439
|
+
command === "auto-golang" ||
|
|
440
|
+
command === "auto-common" ||
|
|
452
441
|
command === "auto-status" ||
|
|
453
442
|
command === "auto-reset");
|
|
454
443
|
}
|
|
455
444
|
function commandSupportsProjectScope(command) {
|
|
456
|
-
return (command === "
|
|
445
|
+
return (command === "git-commit" ||
|
|
446
|
+
command === "gitlab-diff-review" ||
|
|
457
447
|
command === "gitlab-review" ||
|
|
448
|
+
command === "task-describe" ||
|
|
458
449
|
command === "implement" ||
|
|
459
450
|
command === "review" ||
|
|
460
451
|
command === "review-fix" ||
|
|
452
|
+
command === "review-loop" ||
|
|
461
453
|
command === "run-go-tests-loop" ||
|
|
462
454
|
command === "run-go-linter-loop");
|
|
463
455
|
}
|
|
@@ -506,41 +498,40 @@ function checkPrerequisites(config) {
|
|
|
506
498
|
config.command === "run-go-linter-loop") {
|
|
507
499
|
resolveCmd("codex", "CODEX_BIN");
|
|
508
500
|
}
|
|
509
|
-
if (config.command === "review" || config.command === "gitlab-diff-review") {
|
|
510
|
-
resolveCmd("claude", "CLAUDE_BIN");
|
|
511
|
-
}
|
|
512
501
|
}
|
|
513
502
|
function checkAutoPrerequisites(config) {
|
|
514
503
|
resolveCmd("codex", "CODEX_BIN");
|
|
515
|
-
resolveCmd("claude", "CLAUDE_BIN");
|
|
516
504
|
}
|
|
517
505
|
function autoFlowParams(config, forceRefreshSummary = false) {
|
|
518
506
|
return {
|
|
519
507
|
jiraApiUrl: config.jiraApiUrl,
|
|
520
508
|
taskKey: config.taskKey,
|
|
521
|
-
dockerComposeFile: config.dockerComposeFile,
|
|
522
|
-
runGoTestsScript: config.runGoTestsScript,
|
|
523
|
-
runGoLinterScript: config.runGoLinterScript,
|
|
524
|
-
runGoCoverageScript: config.runGoCoverageScript,
|
|
525
509
|
extraPrompt: config.extraPrompt,
|
|
526
510
|
reviewFixPoints: config.reviewFixPoints,
|
|
527
511
|
forceRefresh: forceRefreshSummary,
|
|
512
|
+
mdLang: config.mdLang,
|
|
513
|
+
runGoTestsScript: path.join(agentweaverHome(PACKAGE_ROOT), "run_go_tests.py"),
|
|
514
|
+
runGoLinterScript: path.join(agentweaverHome(PACKAGE_ROOT), "run_go_linter.py"),
|
|
515
|
+
runGoTestsIteration: nextArtifactIteration(config.taskKey, "run-go-tests-result", "json"),
|
|
516
|
+
runGoLinterIteration: nextArtifactIteration(config.taskKey, "run-go-linter-result", "json"),
|
|
528
517
|
};
|
|
529
518
|
}
|
|
530
519
|
const FLOW_DESCRIPTIONS = {
|
|
531
|
-
auto: "
|
|
532
|
-
"bug-analyze": "
|
|
533
|
-
"
|
|
534
|
-
"gitlab-review": "
|
|
535
|
-
"
|
|
536
|
-
"
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
"
|
|
543
|
-
"
|
|
520
|
+
"auto-golang": "Full task pipeline: planning, implementation, checks, review, review replies, and repeated iterations until ready to merge.",
|
|
521
|
+
"bug-analyze": "Analyzes bug from Jira and creates structured artifacts: root cause hypothesis, fix design, and implementation plan.",
|
|
522
|
+
"git-commit": "Collects git status/diff, generates commit message via LLM, allows file selection and commit confirmation.",
|
|
523
|
+
"gitlab-diff-review": "Requests GitLab MR URL via user-input, downloads merge request diff via API, and runs code review with markdown and structured JSON artifacts.",
|
|
524
|
+
"gitlab-review": "Requests GitLab MR URL via user-input, downloads code review comments via API, and saves markdown plus structured JSON artifact.",
|
|
525
|
+
"bug-fix": "Takes bug-analyze results as source of truth and implements the bug fix in code.",
|
|
526
|
+
"mr-description": "Prepares a brief intent description for a merge request based on the task and current changes.",
|
|
527
|
+
plan: "Loads task from Jira and creates design, implementation plan, and QA plan in structured JSON and markdown.",
|
|
528
|
+
"task-describe": "Builds a brief task description either from Jira or from quick user-input without Jira.",
|
|
529
|
+
implement: "Implements the task from approved design/plan artifacts and runs post-verify builds if needed.",
|
|
530
|
+
review: "Runs code review of current changes, validates structured findings, then prepares a reply to comments.",
|
|
531
|
+
"review-fix": "Fixes issues after review-reply, updates code, and runs mandatory checks after modifications.",
|
|
532
|
+
"review-loop": "Iteratively runs review and review-fix cycles up to 5 times until ready-to-merge is achieved.",
|
|
533
|
+
"run-go-tests-loop": "Cycles through `./run_go_tests.py` locally, analyzes the last error, and fixes code until successful or attempts exhausted.",
|
|
534
|
+
"run-go-linter-loop": "Cycles through `./run_go_linter.py` locally, fixes linter or generation issues, and retries until success.",
|
|
544
535
|
};
|
|
545
536
|
function flowDescription(id) {
|
|
546
537
|
return FLOW_DESCRIPTIONS[id] ?? "Описание для этого flow пока не задано.";
|
|
@@ -602,12 +593,13 @@ function findCurrentFlowExecutionStep(state) {
|
|
|
602
593
|
}
|
|
603
594
|
return null;
|
|
604
595
|
}
|
|
605
|
-
async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, requestUserInput = requestUserInputInTerminal, setSummary, launchMode = "restart", runtime = runtimeServices) {
|
|
596
|
+
async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, overrides = {}, requestUserInput = requestUserInputInTerminal, setSummary, launchMode = "restart", runtime = runtimeServices) {
|
|
606
597
|
const context = createPipelineContext({
|
|
607
598
|
issueKey: config.taskKey,
|
|
608
599
|
jiraRef: config.jiraRef,
|
|
609
600
|
dryRun: config.dryRun,
|
|
610
601
|
verbose: config.verbose,
|
|
602
|
+
...(config.mdLang !== undefined ? { mdLang: config.mdLang } : {}),
|
|
611
603
|
runtime,
|
|
612
604
|
...(setSummary ? { setSummary } : {}),
|
|
613
605
|
requestUserInput,
|
|
@@ -621,13 +613,25 @@ async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, requ
|
|
|
621
613
|
};
|
|
622
614
|
let persistedState = launchMode === "resume" ? loadFlowRunState(config.scope.scopeKey, flowId) : null;
|
|
623
615
|
if (persistedState && launchMode === "resume") {
|
|
616
|
+
validateDeclarativeFlowResumeState({
|
|
617
|
+
id: flowId,
|
|
618
|
+
source: flow.source,
|
|
619
|
+
fileName: flow.fileName,
|
|
620
|
+
absolutePath: flow.absolutePath,
|
|
621
|
+
treePath: [],
|
|
622
|
+
flow,
|
|
623
|
+
}, config, persistedState, overrides.launchProfile, runtime);
|
|
624
624
|
persistedState = prepareFlowStateForResume(persistedState);
|
|
625
625
|
}
|
|
626
626
|
else if (launchMode === "restart") {
|
|
627
627
|
resetFlowRunState(config.scope.scopeKey, flowId);
|
|
628
628
|
}
|
|
629
629
|
const executionState = persistedState?.executionState ?? initialExecutionState;
|
|
630
|
-
const state = persistedState
|
|
630
|
+
const state = persistedState
|
|
631
|
+
?? createFlowRunState(config.scope.scopeKey, flowId, executionState, config.jiraRef, overrides.launchProfile);
|
|
632
|
+
if (overrides.launchProfile) {
|
|
633
|
+
state.launchProfile = overrides.launchProfile;
|
|
634
|
+
}
|
|
631
635
|
state.status = "running";
|
|
632
636
|
state.lastError = null;
|
|
633
637
|
state.currentStep = findCurrentFlowExecutionStep(state);
|
|
@@ -673,12 +677,17 @@ async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, requ
|
|
|
673
677
|
throw error;
|
|
674
678
|
}
|
|
675
679
|
}
|
|
676
|
-
async function runDeclarativeFlowBySpecFile(fileName, config, flowParams, requestUserInput = requestUserInputInTerminal, setSummary, launchMode = "restart", runtime = runtimeServices) {
|
|
677
|
-
|
|
680
|
+
async function runDeclarativeFlowBySpecFile(fileName, config, flowParams, overrides = {}, requestUserInput = requestUserInputInTerminal, setSummary, launchMode = "restart", runtime = runtimeServices) {
|
|
681
|
+
const mergedFlowParams = {
|
|
682
|
+
...defaultDeclarativeFlowParams(config, false, overrides),
|
|
683
|
+
...flowParams,
|
|
684
|
+
};
|
|
685
|
+
await runDeclarativeFlowByRef(config.command, { source: "built-in", fileName }, config, mergedFlowParams, overrides, requestUserInput, setSummary, launchMode, runtime);
|
|
678
686
|
}
|
|
679
|
-
function defaultDeclarativeFlowParams(config, forceRefreshSummary = false) {
|
|
687
|
+
function defaultDeclarativeFlowParams(config, forceRefreshSummary = false, overrides = {}) {
|
|
680
688
|
const iteration = nextReviewIterationForTask(config.taskKey);
|
|
681
|
-
const latestIteration =
|
|
689
|
+
const latestIteration = latestArtifactIteration(config.taskKey, "review");
|
|
690
|
+
const launchProfile = overrides.launchProfile ?? resolveLaunchProfile({ executor: "default", model: "default" }, DEFAULT_LAUNCH_PROFILE);
|
|
682
691
|
return {
|
|
683
692
|
taskKey: config.taskKey,
|
|
684
693
|
jiraRef: config.jiraRef,
|
|
@@ -686,14 +695,19 @@ function defaultDeclarativeFlowParams(config, forceRefreshSummary = false) {
|
|
|
686
695
|
jiraApiUrl: config.jiraApiUrl,
|
|
687
696
|
jiraTaskFile: config.jiraTaskFile,
|
|
688
697
|
scopeKey: config.scope.scopeKey,
|
|
689
|
-
|
|
690
|
-
runGoTestsScript: config.runGoTestsScript,
|
|
691
|
-
runGoLinterScript: config.runGoLinterScript,
|
|
692
|
-
runGoCoverageScript: config.runGoCoverageScript,
|
|
698
|
+
workspaceDir: scopeWorkspaceDir(config.taskKey),
|
|
693
699
|
extraPrompt: config.extraPrompt,
|
|
694
700
|
reviewFixPoints: config.reviewFixPoints,
|
|
701
|
+
mdLang: config.mdLang,
|
|
702
|
+
llmExecutor: launchProfile.executor,
|
|
703
|
+
llmModel: launchProfile.model,
|
|
704
|
+
launchProfile,
|
|
695
705
|
iteration,
|
|
696
706
|
latestIteration,
|
|
707
|
+
taskSummaryIteration: nextArtifactIteration(config.taskKey, "task"),
|
|
708
|
+
designIteration: nextArtifactIteration(config.taskKey, "design"),
|
|
709
|
+
planIteration: nextArtifactIteration(config.taskKey, "plan"),
|
|
710
|
+
qaIteration: nextArtifactIteration(config.taskKey, "qa"),
|
|
697
711
|
...(latestIteration !== null ? { reviewFixSelectionJsonFile: reviewFixSelectionJsonFile(config.taskKey, latestIteration) } : {}),
|
|
698
712
|
forceRefresh: forceRefreshSummary,
|
|
699
713
|
};
|
|
@@ -719,88 +733,13 @@ function flowRequiresTaskScope(entry) {
|
|
|
719
733
|
}
|
|
720
734
|
return valueReferencesTaskScopeParams(entry.flow.phases);
|
|
721
735
|
}
|
|
722
|
-
async function runAutoPhaseViaSpec(config, phaseId, executionState, state, setSummary, forceRefreshSummary = false, runtime = runtimeServices) {
|
|
723
|
-
const context = createPipelineContext({
|
|
724
|
-
issueKey: config.taskKey,
|
|
725
|
-
jiraRef: config.jiraRef,
|
|
726
|
-
dryRun: config.dryRun,
|
|
727
|
-
verbose: config.verbose,
|
|
728
|
-
runtime,
|
|
729
|
-
...(setSummary ? { setSummary } : {}),
|
|
730
|
-
requestUserInput: requestUserInputInTerminal,
|
|
731
|
-
});
|
|
732
|
-
const autoFlow = loadAutoFlow();
|
|
733
|
-
const phase = findPhaseById(autoFlow.phases, phaseId);
|
|
734
|
-
publishFlowState("auto", executionState);
|
|
735
|
-
try {
|
|
736
|
-
const result = await runExpandedPhase(phase, context, autoFlowParams(config, forceRefreshSummary), autoFlow.constants, {
|
|
737
|
-
executionState,
|
|
738
|
-
flowKind: autoFlow.kind,
|
|
739
|
-
flowVersion: autoFlow.version,
|
|
740
|
-
onStateChange: async (state) => {
|
|
741
|
-
publishFlowState("auto", state);
|
|
742
|
-
},
|
|
743
|
-
onStepStart: async (_phase, step) => {
|
|
744
|
-
if (!state) {
|
|
745
|
-
return;
|
|
746
|
-
}
|
|
747
|
-
state.currentStep = `${phaseId}:${step.id}`;
|
|
748
|
-
saveAutoPipelineState(state);
|
|
749
|
-
},
|
|
750
|
-
});
|
|
751
|
-
if (state) {
|
|
752
|
-
state.executionState = result.executionState;
|
|
753
|
-
syncAndSaveAutoPipelineState(state);
|
|
754
|
-
}
|
|
755
|
-
return result.status === "skipped" ? "skipped" : "done";
|
|
756
|
-
}
|
|
757
|
-
catch (error) {
|
|
758
|
-
if (!config.dryRun) {
|
|
759
|
-
const output = String(error.output ?? "");
|
|
760
|
-
if (output.trim()) {
|
|
761
|
-
printError("Build verification failed");
|
|
762
|
-
printSummary("Build Failure Summary", await summarizeBuildFailure(output));
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
throw error;
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
function rewindAutoPipelineState(state, phaseId) {
|
|
769
|
-
const targetPhaseId = validateAutoPhaseId(phaseId);
|
|
770
|
-
let phaseSeen = false;
|
|
771
|
-
for (const step of state.steps) {
|
|
772
|
-
if (step.id === targetPhaseId) {
|
|
773
|
-
phaseSeen = true;
|
|
774
|
-
}
|
|
775
|
-
if (phaseSeen) {
|
|
776
|
-
step.status = "pending";
|
|
777
|
-
step.startedAt = null;
|
|
778
|
-
step.finishedAt = null;
|
|
779
|
-
step.returnCode = null;
|
|
780
|
-
step.note = null;
|
|
781
|
-
}
|
|
782
|
-
else {
|
|
783
|
-
step.status = "done";
|
|
784
|
-
step.returnCode = 0;
|
|
785
|
-
step.finishedAt ??= nowIso8601();
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
state.status = "pending";
|
|
789
|
-
state.currentStep = null;
|
|
790
|
-
state.lastError = null;
|
|
791
|
-
const targetIndex = state.executionState.phases.findIndex((phase) => phase.id === targetPhaseId);
|
|
792
|
-
if (targetIndex >= 0) {
|
|
793
|
-
state.executionState.phases = state.executionState.phases.slice(0, targetIndex);
|
|
794
|
-
}
|
|
795
|
-
state.executionState.terminated = false;
|
|
796
|
-
delete state.executionState.terminationReason;
|
|
797
|
-
}
|
|
798
736
|
async function summarizeBuildFailure(output) {
|
|
799
737
|
return summarizeBuildFailureViaPipeline(createPipelineContext({
|
|
800
738
|
issueKey: "build-failure-summary",
|
|
801
739
|
jiraRef: "build-failure-summary",
|
|
802
740
|
dryRun: false,
|
|
803
741
|
verbose: false,
|
|
742
|
+
mdLang: null,
|
|
804
743
|
runtime: runtimeServices,
|
|
805
744
|
requestUserInput: requestUserInputInTerminal,
|
|
806
745
|
}), output);
|
|
@@ -810,27 +749,78 @@ function requireJiraConfig(config) {
|
|
|
810
749
|
throw new TaskRunnerError(`Command '${config.command}' requires Jira context in the current project scope.`);
|
|
811
750
|
}
|
|
812
751
|
}
|
|
813
|
-
async function executeCommand(baseConfig, runFollowupVerify = true, requestUserInput = requestUserInputInTerminal, resolvedScope, setSummary, forceRefreshSummary = false, launchMode = "restart", runtime = runtimeServices) {
|
|
752
|
+
async function executeCommand(baseConfig, runFollowupVerify = true, requestUserInput = requestUserInputInTerminal, resolvedScope, setSummary, forceRefreshSummary = false, launchMode = "restart", launchProfile, runtime = runtimeServices) {
|
|
814
753
|
const config = buildRuntimeConfig(baseConfig, resolvedScope ?? (await resolveScopeForCommand(baseConfig, requestUserInput)));
|
|
815
|
-
if (config.command === "auto") {
|
|
816
|
-
|
|
817
|
-
|
|
754
|
+
if (config.command === "auto-golang") {
|
|
755
|
+
requireJiraConfig(config);
|
|
756
|
+
checkAutoPrerequisites(config);
|
|
757
|
+
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl;
|
|
758
|
+
process.env.JIRA_API_URL = config.jiraApiUrl;
|
|
759
|
+
process.env.JIRA_TASK_FILE = config.jiraTaskFile;
|
|
760
|
+
let effectiveLaunchMode = launchMode;
|
|
761
|
+
let effectiveLaunchProfile = launchProfile;
|
|
762
|
+
if (config.autoFromPhase) {
|
|
763
|
+
const flow = loadDeclarativeFlow({ source: "built-in", fileName: "auto-golang.json" });
|
|
764
|
+
const persistedState = loadFlowRunState(config.scope.scopeKey, "auto-golang");
|
|
765
|
+
if (!persistedState) {
|
|
766
|
+
throw new TaskRunnerError(`Cannot restart auto-golang from phase '${config.autoFromPhase}' because persisted flow state was not found.`);
|
|
767
|
+
}
|
|
768
|
+
rewindFlowRunStateToPhase(persistedState, flow.phases, config.autoFromPhase);
|
|
769
|
+
saveFlowRunState(persistedState);
|
|
770
|
+
effectiveLaunchMode = "resume";
|
|
771
|
+
effectiveLaunchProfile ??= persistedState.launchProfile;
|
|
772
|
+
printPanel("Auto-Golang Resume", `Auto-golang pipeline will continue from phase: ${config.autoFromPhase}`, "yellow");
|
|
818
773
|
}
|
|
819
|
-
await
|
|
774
|
+
await runDeclarativeFlowBySpecFile("auto-golang.json", config, autoFlowParams(config, forceRefreshSummary), effectiveLaunchProfile ? { launchProfile: effectiveLaunchProfile } : {}, requestUserInput, setSummary, effectiveLaunchMode, runtime);
|
|
775
|
+
return false;
|
|
776
|
+
}
|
|
777
|
+
if (config.command === "auto-common") {
|
|
778
|
+
requireJiraConfig(config);
|
|
779
|
+
checkAutoPrerequisites(config);
|
|
780
|
+
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl;
|
|
781
|
+
process.env.JIRA_API_URL = config.jiraApiUrl;
|
|
782
|
+
process.env.JIRA_TASK_FILE = config.jiraTaskFile;
|
|
783
|
+
await runDeclarativeFlowBySpecFile("auto-common.json", config, autoFlowParams(config, forceRefreshSummary), launchProfile ? { launchProfile } : {}, requestUserInput, setSummary, launchMode, runtime);
|
|
820
784
|
return false;
|
|
821
785
|
}
|
|
822
786
|
if (config.command === "auto-status") {
|
|
823
|
-
const state =
|
|
787
|
+
const state = loadFlowRunState(config.scope.scopeKey, "auto-golang");
|
|
824
788
|
if (!state) {
|
|
825
|
-
printPanel("Auto Status", `No
|
|
789
|
+
printPanel("Auto-Golang Status", `No flow state file found for ${config.taskKey}.`, "yellow");
|
|
826
790
|
return false;
|
|
827
791
|
}
|
|
828
|
-
|
|
792
|
+
const currentStep = findCurrentFlowExecutionStep(state) ?? state.currentStep ?? "-";
|
|
793
|
+
const phaseOrder = loadDeclarativeFlow({ source: "built-in", fileName: "auto-golang.json" }).phases;
|
|
794
|
+
const lines = [
|
|
795
|
+
`Issue: ${config.taskKey}`,
|
|
796
|
+
`Status: ${state.status}`,
|
|
797
|
+
`Current step: ${currentStep}`,
|
|
798
|
+
`Updated: ${state.updatedAt}`,
|
|
799
|
+
];
|
|
800
|
+
if (state.launchProfile) {
|
|
801
|
+
lines.push(`Launch profile: ${state.launchProfile.executor} / ${state.launchProfile.model}`);
|
|
802
|
+
}
|
|
803
|
+
if (state.lastError) {
|
|
804
|
+
lines.push(`Last error: ${state.lastError.step ?? "-"} (exit ${state.lastError.returnCode ?? "-"}, ${state.lastError.message ?? "-"})`);
|
|
805
|
+
}
|
|
806
|
+
lines.push("");
|
|
807
|
+
for (const phase of phaseOrder) {
|
|
808
|
+
const phaseState = state.executionState.phases.find((candidate) => candidate.id === phase.id);
|
|
809
|
+
lines.push(`[${phaseState?.status ?? "pending"}] ${phase.id}`);
|
|
810
|
+
for (const step of phase.steps) {
|
|
811
|
+
const stepState = phaseState?.steps.find((candidate) => candidate.id === step.id);
|
|
812
|
+
lines.push(` - [${stepState?.status ?? "pending"}] ${step.id}`);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
if (state.executionState.terminated) {
|
|
816
|
+
lines.push("", `Execution terminated: ${state.executionState.terminationReason ?? "yes"}`);
|
|
817
|
+
}
|
|
818
|
+
printPanel("Auto-Golang Status", lines.join("\n"), "cyan");
|
|
829
819
|
return false;
|
|
830
820
|
}
|
|
831
821
|
if (config.command === "auto-reset") {
|
|
832
|
-
const removed =
|
|
833
|
-
printPanel("Auto Reset", removed ? `State file ${
|
|
822
|
+
const removed = resetFlowRunState(config.scope.scopeKey, "auto-golang");
|
|
823
|
+
printPanel("Auto-Golang Reset", removed ? `State file ${flowStateFile(config.scope.scopeKey, "auto-golang")} removed.` : "No flow state file found.", "yellow");
|
|
834
824
|
return false;
|
|
835
825
|
}
|
|
836
826
|
checkPrerequisites(config);
|
|
@@ -854,9 +844,13 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
854
844
|
await runDeclarativeFlowBySpecFile("plan.json", config, {
|
|
855
845
|
jiraApiUrl: config.jiraApiUrl,
|
|
856
846
|
taskKey: config.taskKey,
|
|
847
|
+
taskSummaryIteration: nextArtifactIteration(config.taskKey, "task"),
|
|
848
|
+
designIteration: nextArtifactIteration(config.taskKey, "design"),
|
|
849
|
+
planIteration: nextArtifactIteration(config.taskKey, "plan"),
|
|
850
|
+
qaIteration: nextArtifactIteration(config.taskKey, "qa"),
|
|
857
851
|
extraPrompt: config.extraPrompt,
|
|
858
852
|
forceRefresh: forceRefreshSummary,
|
|
859
|
-
}, requestUserInput, setSummary, launchMode, runtime);
|
|
853
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, setSummary, launchMode, runtime);
|
|
860
854
|
return false;
|
|
861
855
|
}
|
|
862
856
|
if (config.command === "bug-analyze") {
|
|
@@ -866,21 +860,27 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
866
860
|
process.stdout.write(`Resolved Jira API URL: ${config.jiraApiUrl}\n`);
|
|
867
861
|
process.stdout.write(`Saving Jira issue JSON to: ${config.jiraTaskFile}\n`);
|
|
868
862
|
}
|
|
869
|
-
await runDeclarativeFlowBySpecFile("bug-analyze.json", config, {
|
|
863
|
+
await runDeclarativeFlowBySpecFile("bugz/bug-analyze.json", config, {
|
|
870
864
|
jiraApiUrl: config.jiraApiUrl,
|
|
871
865
|
taskKey: config.taskKey,
|
|
866
|
+
taskSummaryIteration: nextArtifactIteration(config.taskKey, "task"),
|
|
867
|
+
bugAnalyzeIteration: nextArtifactIteration(config.taskKey, "bug-analyze"),
|
|
868
|
+
bugFixDesignIteration: nextArtifactIteration(config.taskKey, "bug-fix-design"),
|
|
869
|
+
bugFixPlanIteration: nextArtifactIteration(config.taskKey, "bug-fix-plan"),
|
|
872
870
|
extraPrompt: config.extraPrompt,
|
|
873
871
|
forceRefresh: forceRefreshSummary,
|
|
874
|
-
}, requestUserInput, setSummary, launchMode, runtime);
|
|
872
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, setSummary, launchMode, runtime);
|
|
875
873
|
return false;
|
|
876
874
|
}
|
|
877
875
|
if (config.command === "gitlab-review") {
|
|
878
876
|
const iteration = nextReviewIterationForTask(config.taskKey);
|
|
879
|
-
|
|
877
|
+
const gitlabReviewIteration = nextArtifactIteration(config.taskKey, "gitlab-review");
|
|
878
|
+
await runDeclarativeFlowBySpecFile("gitlab/gitlab-review.json", config, {
|
|
880
879
|
taskKey: config.taskKey,
|
|
881
880
|
iteration,
|
|
881
|
+
gitlabReviewIteration,
|
|
882
882
|
extraPrompt: config.extraPrompt,
|
|
883
|
-
}, requestUserInput, undefined, launchMode, runtime);
|
|
883
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
884
884
|
if (!config.dryRun) {
|
|
885
885
|
printSummary("GitLab Review", `Artifacts:\n${gitlabReviewFile(config.taskKey)}\n${gitlabReviewJsonFile(config.taskKey)}`);
|
|
886
886
|
}
|
|
@@ -888,11 +888,13 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
888
888
|
}
|
|
889
889
|
if (config.command === "gitlab-diff-review") {
|
|
890
890
|
const iteration = nextReviewIterationForTask(config.taskKey);
|
|
891
|
-
|
|
891
|
+
const gitlabDiffIteration = nextArtifactIteration(config.taskKey, "gitlab-diff");
|
|
892
|
+
await runDeclarativeFlowBySpecFile("gitlab/gitlab-diff-review.json", config, {
|
|
892
893
|
taskKey: config.taskKey,
|
|
893
894
|
iteration,
|
|
895
|
+
gitlabDiffIteration,
|
|
894
896
|
extraPrompt: config.extraPrompt,
|
|
895
|
-
}, requestUserInput, undefined, launchMode, runtime);
|
|
897
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
896
898
|
if (!config.dryRun) {
|
|
897
899
|
printSummary("GitLab Diff Review", `Artifacts:\n${gitlabDiffFile(config.taskKey)}\n${gitlabDiffJsonFile(config.taskKey)}\n${reviewFile(config.taskKey, iteration)}\n${reviewJsonFile(config.taskKey, iteration)}`);
|
|
898
900
|
}
|
|
@@ -907,28 +909,30 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
907
909
|
{ path: bugFixDesignJsonFile(config.taskKey), schemaId: "bug-fix-design/v1" },
|
|
908
910
|
{ path: bugFixPlanJsonFile(config.taskKey), schemaId: "bug-fix-plan/v1" },
|
|
909
911
|
], "Bug-fix mode requires valid structured artifacts from the bug analysis phase.");
|
|
910
|
-
await runDeclarativeFlowBySpecFile("bug-fix.json", config, {
|
|
912
|
+
await runDeclarativeFlowBySpecFile("bugz/bug-fix.json", config, {
|
|
911
913
|
taskKey: config.taskKey,
|
|
912
914
|
extraPrompt: config.extraPrompt,
|
|
913
|
-
}, requestUserInput, undefined, launchMode, runtime);
|
|
915
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
914
916
|
return false;
|
|
915
917
|
}
|
|
916
918
|
if (config.command === "mr-description") {
|
|
917
919
|
requireJiraConfig(config);
|
|
918
920
|
requireJiraTaskFile(config.jiraTaskFile);
|
|
919
|
-
await runDeclarativeFlowBySpecFile("mr-description.json", config, {
|
|
921
|
+
await runDeclarativeFlowBySpecFile("gitlab/mr-description.json", config, {
|
|
920
922
|
taskKey: config.taskKey,
|
|
923
|
+
iteration: nextArtifactIteration(config.taskKey, "mr-description"),
|
|
921
924
|
extraPrompt: config.extraPrompt,
|
|
922
|
-
}, requestUserInput, undefined, launchMode, runtime);
|
|
925
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
923
926
|
return false;
|
|
924
927
|
}
|
|
925
928
|
if (config.command === "task-describe") {
|
|
926
|
-
|
|
929
|
+
const iteration = nextArtifactIteration(config.taskKey, "jira-description");
|
|
927
930
|
await runDeclarativeFlowBySpecFile("task-describe.json", config, {
|
|
928
931
|
jiraApiUrl: config.jiraApiUrl,
|
|
929
932
|
taskKey: config.taskKey,
|
|
933
|
+
iteration,
|
|
930
934
|
extraPrompt: config.extraPrompt,
|
|
931
|
-
}, requestUserInput, undefined, launchMode, runtime);
|
|
935
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
932
936
|
return false;
|
|
933
937
|
}
|
|
934
938
|
if (config.command === "implement") {
|
|
@@ -941,7 +945,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
941
945
|
await runDeclarativeFlowBySpecFile("implement.json", config, {
|
|
942
946
|
taskKey: config.taskKey,
|
|
943
947
|
extraPrompt: config.extraPrompt,
|
|
944
|
-
}, requestUserInput, undefined, launchMode);
|
|
948
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode);
|
|
945
949
|
return false;
|
|
946
950
|
}
|
|
947
951
|
if (config.command === "review") {
|
|
@@ -952,142 +956,73 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
952
956
|
{ path: designJsonFile(config.taskKey), schemaId: "implementation-design/v1" },
|
|
953
957
|
{ path: planJsonFile(config.taskKey), schemaId: "implementation-plan/v1" },
|
|
954
958
|
], "Review mode requires valid structured plan artifacts from the planning phase.");
|
|
955
|
-
await runDeclarativeFlowBySpecFile("review.json", config, {
|
|
959
|
+
await runDeclarativeFlowBySpecFile("review/review.json", config, {
|
|
956
960
|
taskKey: config.taskKey,
|
|
957
961
|
iteration,
|
|
958
962
|
extraPrompt: config.extraPrompt,
|
|
959
|
-
}, requestUserInput, undefined, launchMode, runtime);
|
|
963
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
960
964
|
}
|
|
961
965
|
else {
|
|
962
|
-
await runDeclarativeFlowBySpecFile("review-project.json", config, {
|
|
966
|
+
await runDeclarativeFlowBySpecFile("review/review-project.json", config, {
|
|
963
967
|
taskKey: config.taskKey,
|
|
964
968
|
iteration,
|
|
965
969
|
extraPrompt: config.extraPrompt,
|
|
966
|
-
}, requestUserInput, undefined, launchMode, runtime);
|
|
970
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
967
971
|
}
|
|
968
972
|
return !config.dryRun && existsSync(readyToMergeFile(config.taskKey));
|
|
969
973
|
}
|
|
970
974
|
if (config.command === "review-fix") {
|
|
971
|
-
const latestIteration =
|
|
975
|
+
const latestIteration = latestArtifactIteration(config.taskKey, "review");
|
|
972
976
|
if (latestIteration === null) {
|
|
973
|
-
throw new TaskRunnerError("Review-fix mode requires at least one review
|
|
977
|
+
throw new TaskRunnerError("Review-fix mode requires at least one review artifact.");
|
|
974
978
|
}
|
|
975
979
|
validateStructuredArtifacts([
|
|
976
980
|
{ path: reviewJsonFile(config.taskKey, latestIteration), schemaId: "review-findings/v1" },
|
|
977
|
-
{ path: reviewReplyJsonFile(config.taskKey, latestIteration), schemaId: "review-reply/v1" },
|
|
978
981
|
], "Review-fix mode requires valid structured review artifacts.");
|
|
979
|
-
await runDeclarativeFlowBySpecFile("review-fix.json", config, {
|
|
982
|
+
await runDeclarativeFlowBySpecFile("review/review-fix.json", config, {
|
|
980
983
|
taskKey: config.taskKey,
|
|
981
984
|
latestIteration,
|
|
982
985
|
reviewFixSelectionJsonFile: reviewFixSelectionJsonFile(config.taskKey, latestIteration),
|
|
983
986
|
extraPrompt: config.extraPrompt,
|
|
984
987
|
reviewFixPoints: config.reviewFixPoints,
|
|
985
|
-
}, requestUserInput, undefined, launchMode, runtime);
|
|
988
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
986
989
|
return false;
|
|
987
990
|
}
|
|
991
|
+
if (config.command === "review-loop") {
|
|
992
|
+
const iteration = nextReviewIterationForTask(config.taskKey);
|
|
993
|
+
if (config.jiraBrowseUrl && config.jiraApiUrl && config.jiraTaskFile) {
|
|
994
|
+
requireJiraConfig(config);
|
|
995
|
+
validateStructuredArtifacts([
|
|
996
|
+
{ path: designJsonFile(config.taskKey), schemaId: "implementation-design/v1" },
|
|
997
|
+
{ path: planJsonFile(config.taskKey), schemaId: "implementation-plan/v1" },
|
|
998
|
+
], "Review-loop mode requires valid structured plan artifacts from the planning phase.");
|
|
999
|
+
}
|
|
1000
|
+
await runDeclarativeFlowBySpecFile("review/review-loop.json", config, {
|
|
1001
|
+
taskKey: config.taskKey,
|
|
1002
|
+
iteration,
|
|
1003
|
+
extraPrompt: config.extraPrompt,
|
|
1004
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
1005
|
+
return !config.dryRun && existsSync(readyToMergeFile(config.taskKey));
|
|
1006
|
+
}
|
|
988
1007
|
if (config.command === "run-go-tests-loop" || config.command === "run-go-linter-loop") {
|
|
989
|
-
await runDeclarativeFlowBySpecFile(config.command === "run-go-tests-loop" ? "run-go-tests-loop.json" : "run-go-linter-loop.json", config, {
|
|
1008
|
+
await runDeclarativeFlowBySpecFile(config.command === "run-go-tests-loop" ? "go/run-go-tests-loop.json" : "go/run-go-linter-loop.json", config, {
|
|
990
1009
|
taskKey: config.taskKey,
|
|
991
|
-
runGoTestsScript:
|
|
992
|
-
runGoLinterScript:
|
|
1010
|
+
runGoTestsScript: path.join(agentweaverHome(PACKAGE_ROOT), "run_go_tests.py"),
|
|
1011
|
+
runGoLinterScript: path.join(agentweaverHome(PACKAGE_ROOT), "run_go_linter.py"),
|
|
1012
|
+
runGoTestsIteration: nextArtifactIteration(config.taskKey, "run-go-tests-result", "json"),
|
|
1013
|
+
runGoLinterIteration: nextArtifactIteration(config.taskKey, "run-go-linter-result", "json"),
|
|
993
1014
|
extraPrompt: config.extraPrompt,
|
|
994
|
-
}, requestUserInput, undefined, launchMode, runtime);
|
|
1015
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
995
1016
|
return false;
|
|
996
1017
|
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
const executionState = {
|
|
1004
|
-
flowKind: autoFlow.kind,
|
|
1005
|
-
flowVersion: autoFlow.version,
|
|
1006
|
-
terminated: false,
|
|
1007
|
-
phases: [],
|
|
1008
|
-
};
|
|
1009
|
-
publishFlowState("auto", executionState);
|
|
1010
|
-
for (const phase of autoFlow.phases) {
|
|
1011
|
-
printInfo(`Dry-run auto phase: ${phase.id}`);
|
|
1012
|
-
await runAutoPhaseViaSpec(config, phase.id, executionState, undefined, setSummary, forceRefreshSummary, runtime);
|
|
1013
|
-
if (executionState.terminated) {
|
|
1014
|
-
break;
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
async function runAutoPipeline(config, setSummary, forceRefreshSummary = false, runtime = runtimeServices) {
|
|
1019
|
-
requireJiraConfig(config);
|
|
1020
|
-
if (config.dryRun) {
|
|
1021
|
-
await runAutoPipelineDryRun(config, setSummary, forceRefreshSummary, runtime);
|
|
1022
|
-
return;
|
|
1023
|
-
}
|
|
1024
|
-
checkAutoPrerequisites(config);
|
|
1025
|
-
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl;
|
|
1026
|
-
process.env.JIRA_API_URL = config.jiraApiUrl;
|
|
1027
|
-
process.env.JIRA_TASK_FILE = config.jiraTaskFile;
|
|
1028
|
-
let state = loadAutoPipelineState(config) ?? createAutoPipelineState(config);
|
|
1029
|
-
if (config.autoFromPhase) {
|
|
1030
|
-
rewindAutoPipelineState(state, config.autoFromPhase);
|
|
1031
|
-
printPanel("Auto Resume", `Auto pipeline will continue from phase: ${config.autoFromPhase}`, "yellow");
|
|
1032
|
-
saveAutoPipelineState(state);
|
|
1033
|
-
}
|
|
1034
|
-
else if (!existsSync(autoStateFile(config.taskKey))) {
|
|
1035
|
-
saveAutoPipelineState(state);
|
|
1036
|
-
}
|
|
1037
|
-
printInfo("Running auto pipeline with persisted state");
|
|
1038
|
-
while (true) {
|
|
1039
|
-
const step = nextAutoStep(state);
|
|
1040
|
-
if (!step) {
|
|
1041
|
-
syncAndSaveAutoPipelineState(state);
|
|
1042
|
-
if (state.status === "completed") {
|
|
1043
|
-
printPanel("Auto", "Auto pipeline finished", "green");
|
|
1044
|
-
}
|
|
1045
|
-
else {
|
|
1046
|
-
printInfo(`Auto pipeline finished with status: ${state.status}`);
|
|
1047
|
-
}
|
|
1048
|
-
return;
|
|
1049
|
-
}
|
|
1050
|
-
state.status = "running";
|
|
1051
|
-
state.currentStep = step.id;
|
|
1052
|
-
step.status = "running";
|
|
1053
|
-
step.startedAt = nowIso8601();
|
|
1054
|
-
step.finishedAt = null;
|
|
1055
|
-
step.returnCode = null;
|
|
1056
|
-
step.note = null;
|
|
1057
|
-
state.lastError = null;
|
|
1058
|
-
saveAutoPipelineState(state);
|
|
1059
|
-
try {
|
|
1060
|
-
printInfo(`Running auto step: ${step.id}`);
|
|
1061
|
-
const status = await runAutoPhaseViaSpec(config, step.id, state.executionState, state, setSummary, forceRefreshSummary, runtime);
|
|
1062
|
-
step.status = status;
|
|
1063
|
-
step.finishedAt = nowIso8601();
|
|
1064
|
-
step.returnCode = 0;
|
|
1065
|
-
if (status === "skipped") {
|
|
1066
|
-
step.note = "condition not met";
|
|
1067
|
-
}
|
|
1068
|
-
syncAndSaveAutoPipelineState(state);
|
|
1069
|
-
}
|
|
1070
|
-
catch (error) {
|
|
1071
|
-
const returnCode = Number(error.returnCode ?? 1);
|
|
1072
|
-
step.status = "failed";
|
|
1073
|
-
step.finishedAt = nowIso8601();
|
|
1074
|
-
step.returnCode = returnCode;
|
|
1075
|
-
state.status = "blocked";
|
|
1076
|
-
state.currentStep = step.id;
|
|
1077
|
-
state.lastError = {
|
|
1078
|
-
step: step.id,
|
|
1079
|
-
returnCode,
|
|
1080
|
-
message: "command failed",
|
|
1081
|
-
};
|
|
1082
|
-
saveAutoPipelineState(state);
|
|
1083
|
-
throw error;
|
|
1084
|
-
}
|
|
1085
|
-
if (state.executionState.terminated) {
|
|
1086
|
-
syncAndSaveAutoPipelineState(state);
|
|
1087
|
-
printPanel("Auto", "Auto pipeline finished", "green");
|
|
1088
|
-
return;
|
|
1089
|
-
}
|
|
1018
|
+
if (config.command === "git-commit") {
|
|
1019
|
+
await runDeclarativeFlowBySpecFile("git-commit.json", config, {
|
|
1020
|
+
taskKey: config.taskKey,
|
|
1021
|
+
extraPrompt: config.extraPrompt,
|
|
1022
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
1023
|
+
return false;
|
|
1090
1024
|
}
|
|
1025
|
+
throw new TaskRunnerError(`Unsupported command: ${config.command}`);
|
|
1091
1026
|
}
|
|
1092
1027
|
function parseCliArgs(argv) {
|
|
1093
1028
|
if (argv.includes("--version") || argv.includes("-v")) {
|
|
@@ -1114,6 +1049,7 @@ function parseCliArgs(argv) {
|
|
|
1114
1049
|
let scopeName;
|
|
1115
1050
|
let helpPhases = false;
|
|
1116
1051
|
let jiraRef;
|
|
1052
|
+
let mdLang;
|
|
1117
1053
|
for (let index = 1; index < argv.length; index += 1) {
|
|
1118
1054
|
const token = argv[index] ?? "";
|
|
1119
1055
|
if (token === "--dry") {
|
|
@@ -1143,12 +1079,39 @@ function parseCliArgs(argv) {
|
|
|
1143
1079
|
index += 1;
|
|
1144
1080
|
continue;
|
|
1145
1081
|
}
|
|
1082
|
+
if (token === "--md-lang") {
|
|
1083
|
+
const langValue = argv[index + 1];
|
|
1084
|
+
if (langValue === "en" || langValue === "ru") {
|
|
1085
|
+
mdLang = langValue;
|
|
1086
|
+
}
|
|
1087
|
+
else {
|
|
1088
|
+
process.stderr.write("Error: --md-lang accepts only 'en' or 'ru' as values.\n");
|
|
1089
|
+
process.exit(1);
|
|
1090
|
+
}
|
|
1091
|
+
index += 1;
|
|
1092
|
+
continue;
|
|
1093
|
+
}
|
|
1094
|
+
if (token.startsWith("--md-lang=")) {
|
|
1095
|
+
const langValue = token.slice("--md-lang=".length);
|
|
1096
|
+
if (langValue === "en" || langValue === "ru") {
|
|
1097
|
+
mdLang = langValue;
|
|
1098
|
+
}
|
|
1099
|
+
else {
|
|
1100
|
+
process.stderr.write("Error: --md-lang accepts only 'en' or 'ru' as values.\n");
|
|
1101
|
+
process.exit(1);
|
|
1102
|
+
}
|
|
1103
|
+
continue;
|
|
1104
|
+
}
|
|
1146
1105
|
jiraRef = token;
|
|
1147
1106
|
}
|
|
1148
|
-
if (command === "auto" && helpPhases) {
|
|
1107
|
+
if (command === "auto-golang" && helpPhases) {
|
|
1149
1108
|
printAutoPhasesHelp();
|
|
1150
1109
|
process.exit(0);
|
|
1151
1110
|
}
|
|
1111
|
+
if (command === "auto-common" && helpPhases) {
|
|
1112
|
+
printAutoCommonPhasesHelp();
|
|
1113
|
+
process.exit(0);
|
|
1114
|
+
}
|
|
1152
1115
|
return {
|
|
1153
1116
|
command: command,
|
|
1154
1117
|
dry,
|
|
@@ -1158,6 +1121,7 @@ function parseCliArgs(argv) {
|
|
|
1158
1121
|
...(scopeName !== undefined ? { scopeName } : {}),
|
|
1159
1122
|
...(prompt !== undefined ? { prompt } : {}),
|
|
1160
1123
|
...(autoFromPhase !== undefined ? { autoFromPhase } : {}),
|
|
1124
|
+
...(mdLang !== undefined ? { mdLang } : {}),
|
|
1161
1125
|
};
|
|
1162
1126
|
}
|
|
1163
1127
|
function buildConfigFromArgs(args) {
|
|
@@ -1166,6 +1130,7 @@ function buildConfigFromArgs(args) {
|
|
|
1166
1130
|
...(args.scopeName !== undefined ? { scopeName: args.scopeName } : {}),
|
|
1167
1131
|
...(args.prompt !== undefined ? { extraPrompt: args.prompt } : {}),
|
|
1168
1132
|
...(args.autoFromPhase !== undefined ? { autoFromPhase: args.autoFromPhase } : {}),
|
|
1133
|
+
...(args.mdLang !== undefined ? { mdLang: args.mdLang } : {}),
|
|
1169
1134
|
dryRun: args.dry,
|
|
1170
1135
|
verbose: args.verbose,
|
|
1171
1136
|
});
|
|
@@ -1183,34 +1148,13 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1183
1148
|
summaryText: "",
|
|
1184
1149
|
cwd: process.cwd(),
|
|
1185
1150
|
gitBranchName,
|
|
1151
|
+
version: packageVersion(),
|
|
1186
1152
|
flows: interactiveFlowDefinitions(flowCatalog),
|
|
1187
1153
|
getRunConfirmation: async (flowId) => {
|
|
1188
1154
|
const flowEntry = findCatalogEntry(flowId, flowCatalog);
|
|
1189
1155
|
if (!flowEntry) {
|
|
1190
1156
|
throw new TaskRunnerError(`Unknown flow: ${flowId}`);
|
|
1191
1157
|
}
|
|
1192
|
-
if (flowId === "auto") {
|
|
1193
|
-
if (!currentScope.jiraRef) {
|
|
1194
|
-
return { resumeAvailable: false, hasExistingState: false };
|
|
1195
|
-
}
|
|
1196
|
-
const baseConfig = buildBaseConfig("auto", {
|
|
1197
|
-
jiraRef: currentScope.jiraRef,
|
|
1198
|
-
scopeName: currentScope.scopeKey,
|
|
1199
|
-
});
|
|
1200
|
-
const state = loadAutoPipelineState(buildRuntimeConfig(baseConfig, currentScope));
|
|
1201
|
-
if (!state) {
|
|
1202
|
-
return { resumeAvailable: false, hasExistingState: false };
|
|
1203
|
-
}
|
|
1204
|
-
const status = deriveAutoPipelineStatus(state);
|
|
1205
|
-
if (status === "completed") {
|
|
1206
|
-
return { resumeAvailable: false, hasExistingState: true };
|
|
1207
|
-
}
|
|
1208
|
-
return {
|
|
1209
|
-
resumeAvailable: true,
|
|
1210
|
-
hasExistingState: true,
|
|
1211
|
-
details: buildAutoResumeDetails(state),
|
|
1212
|
-
};
|
|
1213
|
-
}
|
|
1214
1158
|
const resumeLookup = lookupInteractiveFlowResume(flowEntry, currentScope);
|
|
1215
1159
|
return resumeLookup;
|
|
1216
1160
|
},
|
|
@@ -1223,6 +1167,16 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1223
1167
|
if (!flowEntry) {
|
|
1224
1168
|
throw new TaskRunnerError(`Unknown flow: ${flowId}`);
|
|
1225
1169
|
}
|
|
1170
|
+
const resumeState = launchMode === "resume" ? loadFlowRunState(currentScope.scopeKey, flowId) : null;
|
|
1171
|
+
if (resumeState) {
|
|
1172
|
+
currentScope = scopeWithRestoredJiraContext(currentScope, resumeState);
|
|
1173
|
+
}
|
|
1174
|
+
const launchProfile = launchMode === "resume"
|
|
1175
|
+
? resumeState?.launchProfile
|
|
1176
|
+
: await requestInteractiveLaunchProfile((form) => ui.requestUserInput(form));
|
|
1177
|
+
if (!launchProfile) {
|
|
1178
|
+
throw new TaskRunnerError("Resume is impossible because launch profile was not saved. Use restart.");
|
|
1179
|
+
}
|
|
1226
1180
|
const previousScopeKey = currentScope.scopeKey;
|
|
1227
1181
|
const baseConfig = buildBaseConfig(flowId, {
|
|
1228
1182
|
...(currentScope.jiraRef ? { jiraRef: currentScope.jiraRef } : {}),
|
|
@@ -1240,12 +1194,13 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1240
1194
|
if (previousScopeKey !== currentScope.scopeKey || currentScope.jiraIssueKey) {
|
|
1241
1195
|
syncInteractiveTaskSummary(ui, currentScope, forceRefresh);
|
|
1242
1196
|
}
|
|
1197
|
+
printPanel("Effective Launch Config", `executor: ${launchProfile.executor}\nmodel: ${launchProfile.model}\nmode: ${launchMode}`, "cyan");
|
|
1243
1198
|
if (flowEntry.source === "built-in" && isBuiltInCommandFlowId(flowId)) {
|
|
1244
|
-
await executeCommand(baseConfig, true, (form) => ui.requestUserInput(form), currentScope, (markdown) => ui.setSummary(markdown), forceRefresh, launchMode, createRuntimeServices(abortController.signal));
|
|
1199
|
+
await executeCommand(baseConfig, true, (form) => ui.requestUserInput(form), currentScope, (markdown) => ui.setSummary(markdown), forceRefresh, launchMode, launchProfile, createRuntimeServices(abortController.signal));
|
|
1245
1200
|
return;
|
|
1246
1201
|
}
|
|
1247
1202
|
const runtimeConfig = buildRuntimeConfig(baseConfig, currentScope);
|
|
1248
|
-
await runDeclarativeFlowByRef(flowId, toDeclarativeFlowRef(flowEntry), runtimeConfig, defaultDeclarativeFlowParams(runtimeConfig, forceRefresh), (form) => ui.requestUserInput(form), (markdown) => ui.setSummary(markdown), launchMode, createRuntimeServices(abortController.signal));
|
|
1203
|
+
await runDeclarativeFlowByRef(flowId, toDeclarativeFlowRef(flowEntry), runtimeConfig, defaultDeclarativeFlowParams(runtimeConfig, forceRefresh, { launchProfile }), { launchProfile }, (form) => ui.requestUserInput(form), (markdown) => ui.setSummary(markdown), launchMode, createRuntimeServices(abortController.signal));
|
|
1249
1204
|
}
|
|
1250
1205
|
catch (error) {
|
|
1251
1206
|
if (error instanceof FlowInterruptedError) {
|
|
@@ -1309,7 +1264,7 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1309
1264
|
});
|
|
1310
1265
|
}
|
|
1311
1266
|
export async function main(argv = process.argv.slice(2)) {
|
|
1312
|
-
|
|
1267
|
+
loadTieredEnv(process.cwd());
|
|
1313
1268
|
let forceRefresh = false;
|
|
1314
1269
|
const args = [...argv];
|
|
1315
1270
|
if (args[0] === "--force") {
|