agentweaver 0.1.10 → 0.1.12
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 +109 -55
- package/dist/executors/{codex-local-executor.js → codex-executor.js} +6 -5
- 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/jira-fetch-executor.js +1 -0
- package/dist/executors/opencode-executor.js +22 -11
- package/dist/executors/process-executor.js +3 -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 +477 -514
- package/dist/interactive-ui.js +609 -88
- package/dist/jira.js +109 -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-review.json → gitlab/gitlab-review.json} +65 -133
- 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} +74 -13
- package/dist/pipeline/flow-specs/review/review-loop.json +282 -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 +252 -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/jira-fetch-node.js +3 -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 +48 -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 +43 -8
- package/dist/pipeline/nodes/write-selection-file-node.js +46 -0
- package/dist/pipeline/prompt-registry.js +3 -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 +9 -3
- package/dist/pipeline/spec-validator.js +4 -0
- package/dist/pipeline/types.js +1 -0
- package/dist/pipeline/value-resolver.js +50 -38
- package/dist/prompts.js +119 -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/runtime/process-runner.js +9 -3
- package/dist/structured-artifact-schema-registry.js +54 -0
- package/dist/structured-artifact-schemas.json +22 -20
- package/dist/structured-artifacts.js +3 -43
- package/dist/user-input.js +38 -3
- 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/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, reviewAssessmentFile, reviewAssessmentJsonFile, 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,240 @@ 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
|
-
|
|
182
|
+
return lines.join("\n");
|
|
183
|
+
}
|
|
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: defaultExecutor,
|
|
198
|
+
options: LLM_EXECUTOR_IDS.map((id) => ({
|
|
199
|
+
value: id,
|
|
200
|
+
label: id === defaultExecutor ? `${id} [default]` : id,
|
|
201
|
+
})),
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
};
|
|
220
205
|
}
|
|
221
|
-
function
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
206
|
+
function launchModelSelectionForm(executor) {
|
|
207
|
+
const resolvedExecutor = executor === "default" ? DEFAULT_LAUNCH_PROFILE.executor : executor;
|
|
208
|
+
const defaultModel = defaultModelForExecutor(resolvedExecutor);
|
|
209
|
+
const options = ALLOWED_MODELS_BY_EXECUTOR[resolvedExecutor].map((model) => ({
|
|
210
|
+
value: model,
|
|
211
|
+
label: model === defaultModel ? `${model} [default]` : model,
|
|
212
|
+
}));
|
|
213
|
+
return {
|
|
214
|
+
formId: "flow-launch-model",
|
|
215
|
+
title: "Настройки запуска LLM",
|
|
216
|
+
description: `Выберите модель для запуска flow. Текущий default для ${resolvedExecutor}: ${defaultModel}.`,
|
|
217
|
+
submitLabel: "Start",
|
|
218
|
+
fields: [
|
|
219
|
+
{
|
|
220
|
+
id: "model",
|
|
221
|
+
type: "single-select",
|
|
222
|
+
label: "Model",
|
|
223
|
+
required: true,
|
|
224
|
+
default: defaultModel,
|
|
225
|
+
options,
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
};
|
|
228
229
|
}
|
|
229
|
-
function
|
|
230
|
-
|
|
231
|
-
saveAutoPipelineState(state);
|
|
230
|
+
function isFormCancellation(error, formId) {
|
|
231
|
+
return error instanceof TaskRunnerError && error.message === `User cancelled form '${formId}'.`;
|
|
232
232
|
}
|
|
233
|
-
function
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
233
|
+
async function requestInteractiveLaunchProfile(requestUserInput) {
|
|
234
|
+
for (;;) {
|
|
235
|
+
const executorFormResult = await requestUserInput(launchProfileSelectionForm());
|
|
236
|
+
const rawExecutor = String(executorFormResult.values.executor ?? DEFAULT_LAUNCH_PROFILE.executor);
|
|
237
|
+
const executor = LLM_EXECUTOR_IDS.find((id) => id === rawExecutor);
|
|
238
|
+
if (!executor) {
|
|
239
|
+
throw new TaskRunnerError(`Unsupported launch executor '${rawExecutor}'.`);
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
const modelFormResult = await requestUserInput(launchModelSelectionForm(executor));
|
|
243
|
+
const rawModel = String(modelFormResult.values.model ?? defaultModelForExecutor(executor)).trim();
|
|
244
|
+
return resolveLaunchProfile({
|
|
245
|
+
executor,
|
|
246
|
+
model: rawModel.length > 0 ? rawModel : defaultModelForExecutor(executor),
|
|
247
|
+
}, DEFAULT_LAUNCH_PROFILE);
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
if (isFormCancellation(error, "flow-launch-model")) {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
throw error;
|
|
254
|
+
}
|
|
237
255
|
}
|
|
238
|
-
rmSync(filePath);
|
|
239
|
-
return true;
|
|
240
256
|
}
|
|
241
|
-
function
|
|
242
|
-
return
|
|
257
|
+
function buildResolverContext(pipelineContext, flowParams, flowConstants, repeatVars, executionState) {
|
|
258
|
+
return {
|
|
259
|
+
flowParams,
|
|
260
|
+
flowConstants,
|
|
261
|
+
pipelineContext,
|
|
262
|
+
repeatVars,
|
|
263
|
+
executionState,
|
|
264
|
+
};
|
|
243
265
|
}
|
|
244
|
-
function
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
266
|
+
function resolveResumeChecks(step, context) {
|
|
267
|
+
return (step.expect ?? [])
|
|
268
|
+
.filter((expectation) => evaluateCondition(expectation.when, context))
|
|
269
|
+
.flatMap((expectation) => {
|
|
270
|
+
if (expectation.kind === "step-output") {
|
|
271
|
+
const value = resolveValue(expectation.value, context);
|
|
272
|
+
if (expectation.equals !== undefined) {
|
|
273
|
+
const expected = resolveValue(expectation.equals, context);
|
|
274
|
+
if (value !== expected) {
|
|
275
|
+
throw new TaskRunnerError(expectation.message);
|
|
276
|
+
}
|
|
277
|
+
return [];
|
|
278
|
+
}
|
|
279
|
+
if (!value) {
|
|
280
|
+
throw new TaskRunnerError(expectation.message);
|
|
281
|
+
}
|
|
282
|
+
return [];
|
|
249
283
|
}
|
|
250
|
-
|
|
251
|
-
|
|
284
|
+
if (expectation.kind === "require-artifacts") {
|
|
285
|
+
const value = resolveValue(expectation.paths, context);
|
|
286
|
+
if (!Array.isArray(value) || value.some((candidate) => typeof candidate !== "string")) {
|
|
287
|
+
throw new TaskRunnerError("Expectation 'require-artifacts' must resolve to string[]");
|
|
288
|
+
}
|
|
289
|
+
return [{ kind: "require-artifacts", paths: value, message: expectation.message }];
|
|
290
|
+
}
|
|
291
|
+
if (expectation.kind === "require-file") {
|
|
292
|
+
const value = resolveValue(expectation.path, context);
|
|
293
|
+
if (typeof value !== "string") {
|
|
294
|
+
throw new TaskRunnerError("Expectation 'require-file' must resolve to string");
|
|
295
|
+
}
|
|
296
|
+
return [{ kind: "require-file", path: value, message: expectation.message }];
|
|
297
|
+
}
|
|
298
|
+
const items = expectation.items.map((item) => {
|
|
299
|
+
const value = resolveValue(item.path, context);
|
|
300
|
+
if (typeof value !== "string") {
|
|
301
|
+
throw new TaskRunnerError("Expectation 'require-structured-artifacts' item path must resolve to string");
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
path: value,
|
|
305
|
+
schemaId: item.schemaId,
|
|
306
|
+
};
|
|
307
|
+
});
|
|
308
|
+
return [{ kind: "require-structured-artifacts", items, message: expectation.message }];
|
|
309
|
+
});
|
|
252
310
|
}
|
|
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";
|
|
311
|
+
function validateDeclarativePhaseResumeState(phase, phaseState, pipelineContext, flowParams, flowConstants, executionState) {
|
|
312
|
+
if (phaseState.status === "done") {
|
|
313
|
+
return;
|
|
268
314
|
}
|
|
269
|
-
|
|
270
|
-
|
|
315
|
+
for (const [stepIndex, step] of phase.steps.entries()) {
|
|
316
|
+
const stepState = phaseState.steps[stepIndex];
|
|
317
|
+
if (!stepState || stepState.status !== "done") {
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
const context = buildResolverContext(pipelineContext, flowParams, flowConstants, step.repeatVars, executionState);
|
|
321
|
+
const checks = resolveResumeChecks(step, context);
|
|
322
|
+
try {
|
|
323
|
+
runNodeChecks(checks);
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
throw new TaskRunnerError(`Resume is impossible for '${phase.id}:${step.id}' because required artifacts are missing or invalid. Use restart.\n${error.message}`);
|
|
327
|
+
}
|
|
271
328
|
}
|
|
272
|
-
return state.status;
|
|
273
329
|
}
|
|
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}`);
|
|
330
|
+
function validateDeclarativeFlowResumeState(flowEntry, config, state, launchProfile, runtime = runtimeServices) {
|
|
331
|
+
if (state.launchProfile) {
|
|
332
|
+
if (!launchProfile) {
|
|
333
|
+
throw new TaskRunnerError("Resume is impossible because launch profile is missing. Use restart.");
|
|
334
|
+
}
|
|
335
|
+
if (state.launchProfile.fingerprint !== launchProfile.fingerprint) {
|
|
336
|
+
throw new TaskRunnerError(`Resume is impossible because launch profile changed (${state.launchProfile.executor}/${state.launchProfile.model} -> ${launchProfile.executor}/${launchProfile.model}). Use restart.`);
|
|
291
337
|
}
|
|
292
338
|
}
|
|
293
|
-
if (
|
|
294
|
-
|
|
339
|
+
if (flowRequiresTaskScope(flowEntry) && !config.jiraRef) {
|
|
340
|
+
throw new TaskRunnerError("Resume is impossible because Jira context is missing for this flow state. Use restart.");
|
|
295
341
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
342
|
+
const pipelineContext = createPipelineContext({
|
|
343
|
+
issueKey: config.taskKey,
|
|
344
|
+
jiraRef: config.jiraRef,
|
|
345
|
+
dryRun: config.dryRun,
|
|
346
|
+
verbose: config.verbose,
|
|
347
|
+
...(config.mdLang !== undefined ? { mdLang: config.mdLang } : {}),
|
|
348
|
+
runtime,
|
|
349
|
+
requestUserInput: requestUserInputInTerminal,
|
|
350
|
+
});
|
|
351
|
+
const flowParams = defaultDeclarativeFlowParams(config, false, launchProfile ? { launchProfile } : {});
|
|
352
|
+
for (const phase of flowEntry.flow.phases) {
|
|
353
|
+
const phaseState = state.executionState.phases.find((candidate) => candidate.id === phase.id);
|
|
301
354
|
if (!phaseState) {
|
|
302
355
|
continue;
|
|
303
356
|
}
|
|
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
|
-
}
|
|
318
|
-
}
|
|
319
|
-
state.currentStep = findCurrentExecutionStep(state);
|
|
320
|
-
state.status = deriveAutoPipelineStatus(state);
|
|
321
|
-
}
|
|
322
|
-
function buildAutoResumeDetails(state) {
|
|
323
|
-
const currentStep = findCurrentExecutionStep(state) ?? state.currentStep ?? "-";
|
|
324
|
-
const lines = [
|
|
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 ?? "-"})`);
|
|
357
|
+
validateDeclarativePhaseResumeState(phase, phaseState, pipelineContext, flowParams, flowEntry.flow.constants, state.executionState);
|
|
331
358
|
}
|
|
332
|
-
return lines.join("\n");
|
|
333
359
|
}
|
|
334
|
-
function
|
|
335
|
-
|
|
336
|
-
|
|
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 ?? "-"})`);
|
|
360
|
+
function scopeWithRestoredJiraContext(scope, state) {
|
|
361
|
+
if (scope.jiraRef || !state?.jiraRef?.trim()) {
|
|
362
|
+
return scope;
|
|
343
363
|
}
|
|
344
|
-
return
|
|
364
|
+
return attachJiraContext(scope, state.jiraRef);
|
|
345
365
|
}
|
|
346
366
|
function lookupInteractiveFlowResume(flowEntry, currentScope) {
|
|
347
367
|
const directState = loadFlowRunState(currentScope.scopeKey, flowEntry.id);
|
|
348
368
|
if (directState && hasResumableFlowState(directState)) {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
369
|
+
try {
|
|
370
|
+
const effectiveScope = scopeWithRestoredJiraContext(currentScope, directState);
|
|
371
|
+
const baseConfig = buildBaseConfig(flowEntry.id, {
|
|
372
|
+
...(effectiveScope.jiraRef ? { jiraRef: effectiveScope.jiraRef } : {}),
|
|
373
|
+
scopeName: effectiveScope.scopeKey,
|
|
374
|
+
});
|
|
375
|
+
const config = buildRuntimeConfig(baseConfig, effectiveScope);
|
|
376
|
+
validateDeclarativeFlowResumeState(flowEntry, config, directState, directState.launchProfile);
|
|
377
|
+
return {
|
|
378
|
+
resumeAvailable: true,
|
|
379
|
+
hasExistingState: true,
|
|
380
|
+
details: buildFlowResumeDetails(directState),
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
catch (error) {
|
|
384
|
+
return {
|
|
385
|
+
resumeAvailable: false,
|
|
386
|
+
hasExistingState: true,
|
|
387
|
+
details: `Interrupted run found, but resume is unavailable.\n${error.message}`,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
354
390
|
}
|
|
355
391
|
return {
|
|
356
392
|
resumeAvailable: false,
|
|
@@ -358,37 +394,17 @@ function lookupInteractiveFlowResume(flowEntry, currentScope) {
|
|
|
358
394
|
};
|
|
359
395
|
}
|
|
360
396
|
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");
|
|
397
|
+
const phaseLines = ["Available auto-golang phases:", "", ...autoPhaseIds()];
|
|
398
|
+
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>");
|
|
399
|
+
printPanel("Auto-Golang Phases", phaseLines.join("\n"), "magenta");
|
|
364
400
|
}
|
|
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
|
-
}
|
|
401
|
+
function autoCommonPhaseIds() {
|
|
402
|
+
return loadDeclarativeFlow({ source: "built-in", fileName: "auto-common.json" }).phases.map((phase) => phase.id);
|
|
403
|
+
}
|
|
404
|
+
function printAutoCommonPhasesHelp() {
|
|
405
|
+
const phaseLines = ["Available auto-common phases:", "", ...autoCommonPhaseIds()];
|
|
406
|
+
phaseLines.push("", "You can run auto-common with:", "agentweaver auto-common <jira>");
|
|
407
|
+
printPanel("Auto-Common Phases", phaseLines.join("\n"), "magenta");
|
|
392
408
|
}
|
|
393
409
|
function nextReviewIterationForTask(taskKey) {
|
|
394
410
|
let maxIndex = 0;
|
|
@@ -400,33 +416,14 @@ function nextReviewIterationForTask(taskKey) {
|
|
|
400
416
|
if (!entry.isFile()) {
|
|
401
417
|
continue;
|
|
402
418
|
}
|
|
403
|
-
const match = REVIEW_FILE_RE.exec(entry.name)
|
|
419
|
+
const match = REVIEW_FILE_RE.exec(entry.name);
|
|
404
420
|
if (match && match[1] === taskKey) {
|
|
405
421
|
maxIndex = Math.max(maxIndex, Number.parseInt(match[2] ?? "0", 10));
|
|
406
422
|
}
|
|
407
423
|
}
|
|
408
424
|
return maxIndex + 1;
|
|
409
425
|
}
|
|
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
426
|
function buildBaseConfig(command, options = {}) {
|
|
429
|
-
const homeDir = agentweaverHome(PACKAGE_ROOT);
|
|
430
427
|
return {
|
|
431
428
|
command,
|
|
432
429
|
jiraRef: options.jiraRef ?? null,
|
|
@@ -434,12 +431,9 @@ function buildBaseConfig(command, options = {}) {
|
|
|
434
431
|
reviewFixPoints: options.reviewFixPoints ?? null,
|
|
435
432
|
extraPrompt: options.extraPrompt ?? null,
|
|
436
433
|
autoFromPhase: options.autoFromPhase ? validateAutoPhaseId(options.autoFromPhase) : null,
|
|
434
|
+
mdLang: options.mdLang ?? null,
|
|
437
435
|
dryRun: options.dryRun ?? false,
|
|
438
436
|
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
437
|
};
|
|
444
438
|
}
|
|
445
439
|
function commandRequiresTask(command) {
|
|
@@ -447,17 +441,20 @@ function commandRequiresTask(command) {
|
|
|
447
441
|
command === "bug-analyze" ||
|
|
448
442
|
command === "bug-fix" ||
|
|
449
443
|
command === "mr-description" ||
|
|
450
|
-
command === "
|
|
451
|
-
command === "auto" ||
|
|
444
|
+
command === "auto-golang" ||
|
|
445
|
+
command === "auto-common" ||
|
|
452
446
|
command === "auto-status" ||
|
|
453
447
|
command === "auto-reset");
|
|
454
448
|
}
|
|
455
449
|
function commandSupportsProjectScope(command) {
|
|
456
|
-
return (command === "
|
|
450
|
+
return (command === "git-commit" ||
|
|
451
|
+
command === "gitlab-diff-review" ||
|
|
457
452
|
command === "gitlab-review" ||
|
|
453
|
+
command === "task-describe" ||
|
|
458
454
|
command === "implement" ||
|
|
459
455
|
command === "review" ||
|
|
460
456
|
command === "review-fix" ||
|
|
457
|
+
command === "review-loop" ||
|
|
461
458
|
command === "run-go-tests-loop" ||
|
|
462
459
|
command === "run-go-linter-loop");
|
|
463
460
|
}
|
|
@@ -506,41 +503,40 @@ function checkPrerequisites(config) {
|
|
|
506
503
|
config.command === "run-go-linter-loop") {
|
|
507
504
|
resolveCmd("codex", "CODEX_BIN");
|
|
508
505
|
}
|
|
509
|
-
if (config.command === "review" || config.command === "gitlab-diff-review") {
|
|
510
|
-
resolveCmd("claude", "CLAUDE_BIN");
|
|
511
|
-
}
|
|
512
506
|
}
|
|
513
507
|
function checkAutoPrerequisites(config) {
|
|
514
508
|
resolveCmd("codex", "CODEX_BIN");
|
|
515
|
-
resolveCmd("claude", "CLAUDE_BIN");
|
|
516
509
|
}
|
|
517
510
|
function autoFlowParams(config, forceRefreshSummary = false) {
|
|
518
511
|
return {
|
|
519
512
|
jiraApiUrl: config.jiraApiUrl,
|
|
520
513
|
taskKey: config.taskKey,
|
|
521
|
-
dockerComposeFile: config.dockerComposeFile,
|
|
522
|
-
runGoTestsScript: config.runGoTestsScript,
|
|
523
|
-
runGoLinterScript: config.runGoLinterScript,
|
|
524
|
-
runGoCoverageScript: config.runGoCoverageScript,
|
|
525
514
|
extraPrompt: config.extraPrompt,
|
|
526
515
|
reviewFixPoints: config.reviewFixPoints,
|
|
527
516
|
forceRefresh: forceRefreshSummary,
|
|
517
|
+
mdLang: config.mdLang,
|
|
518
|
+
runGoTestsScript: path.join(agentweaverHome(PACKAGE_ROOT), "run_go_tests.py"),
|
|
519
|
+
runGoLinterScript: path.join(agentweaverHome(PACKAGE_ROOT), "run_go_linter.py"),
|
|
520
|
+
runGoTestsIteration: nextArtifactIteration(config.taskKey, "run-go-tests-result", "json"),
|
|
521
|
+
runGoLinterIteration: nextArtifactIteration(config.taskKey, "run-go-linter-result", "json"),
|
|
528
522
|
};
|
|
529
523
|
}
|
|
530
524
|
const FLOW_DESCRIPTIONS = {
|
|
531
|
-
auto: "
|
|
532
|
-
"bug-analyze": "
|
|
533
|
-
"
|
|
534
|
-
"gitlab-review": "
|
|
535
|
-
"
|
|
536
|
-
"
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
"
|
|
543
|
-
"
|
|
525
|
+
"auto-golang": "Full task pipeline: planning, implementation, checks, review, review replies, and repeated iterations until ready to merge.",
|
|
526
|
+
"bug-analyze": "Analyzes bug from Jira and creates structured artifacts: root cause hypothesis, fix design, and implementation plan.",
|
|
527
|
+
"git-commit": "Collects git status/diff, generates commit message via LLM, allows file selection and commit confirmation.",
|
|
528
|
+
"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.",
|
|
529
|
+
"gitlab-review": "Requests GitLab MR URL via user-input, downloads code review comments via API, assesses which findings are fair and proposes fixes, then runs review-fix for the selected findings.",
|
|
530
|
+
"bug-fix": "Takes bug-analyze results as source of truth and implements the bug fix in code.",
|
|
531
|
+
"mr-description": "Prepares a brief intent description for a merge request based on the task and current changes.",
|
|
532
|
+
plan: "Loads task from Jira and creates design, implementation plan, and QA plan in structured JSON and markdown.",
|
|
533
|
+
"task-describe": "Builds a brief task description either from Jira or from quick user-input without Jira.",
|
|
534
|
+
implement: "Implements the task from approved design/plan artifacts and runs post-verify builds if needed.",
|
|
535
|
+
review: "Runs code review of current changes and writes structured findings artifacts.",
|
|
536
|
+
"review-fix": "Fixes issues after review-reply, updates code, and runs mandatory checks after modifications.",
|
|
537
|
+
"review-loop": "Iteratively runs review and review-fix cycles up to 5 times until ready-to-merge is achieved.",
|
|
538
|
+
"run-go-tests-loop": "Cycles through `./run_go_tests.py` locally, analyzes the last error, and fixes code until successful or attempts exhausted.",
|
|
539
|
+
"run-go-linter-loop": "Cycles through `./run_go_linter.py` locally, fixes linter or generation issues, and retries until success.",
|
|
544
540
|
};
|
|
545
541
|
function flowDescription(id) {
|
|
546
542
|
return FLOW_DESCRIPTIONS[id] ?? "Описание для этого flow пока не задано.";
|
|
@@ -602,12 +598,13 @@ function findCurrentFlowExecutionStep(state) {
|
|
|
602
598
|
}
|
|
603
599
|
return null;
|
|
604
600
|
}
|
|
605
|
-
async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, requestUserInput = requestUserInputInTerminal, setSummary, launchMode = "restart", runtime = runtimeServices) {
|
|
601
|
+
async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, overrides = {}, requestUserInput = requestUserInputInTerminal, setSummary, launchMode = "restart", runtime = runtimeServices) {
|
|
606
602
|
const context = createPipelineContext({
|
|
607
603
|
issueKey: config.taskKey,
|
|
608
604
|
jiraRef: config.jiraRef,
|
|
609
605
|
dryRun: config.dryRun,
|
|
610
606
|
verbose: config.verbose,
|
|
607
|
+
...(config.mdLang !== undefined ? { mdLang: config.mdLang } : {}),
|
|
611
608
|
runtime,
|
|
612
609
|
...(setSummary ? { setSummary } : {}),
|
|
613
610
|
requestUserInput,
|
|
@@ -621,13 +618,25 @@ async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, requ
|
|
|
621
618
|
};
|
|
622
619
|
let persistedState = launchMode === "resume" ? loadFlowRunState(config.scope.scopeKey, flowId) : null;
|
|
623
620
|
if (persistedState && launchMode === "resume") {
|
|
621
|
+
validateDeclarativeFlowResumeState({
|
|
622
|
+
id: flowId,
|
|
623
|
+
source: flow.source,
|
|
624
|
+
fileName: flow.fileName,
|
|
625
|
+
absolutePath: flow.absolutePath,
|
|
626
|
+
treePath: [],
|
|
627
|
+
flow,
|
|
628
|
+
}, config, persistedState, overrides.launchProfile, runtime);
|
|
624
629
|
persistedState = prepareFlowStateForResume(persistedState);
|
|
625
630
|
}
|
|
626
631
|
else if (launchMode === "restart") {
|
|
627
632
|
resetFlowRunState(config.scope.scopeKey, flowId);
|
|
628
633
|
}
|
|
629
634
|
const executionState = persistedState?.executionState ?? initialExecutionState;
|
|
630
|
-
const state = persistedState
|
|
635
|
+
const state = persistedState
|
|
636
|
+
?? createFlowRunState(config.scope.scopeKey, flowId, executionState, config.jiraRef, overrides.launchProfile);
|
|
637
|
+
if (overrides.launchProfile) {
|
|
638
|
+
state.launchProfile = overrides.launchProfile;
|
|
639
|
+
}
|
|
631
640
|
state.status = "running";
|
|
632
641
|
state.lastError = null;
|
|
633
642
|
state.currentStep = findCurrentFlowExecutionStep(state);
|
|
@@ -673,12 +682,17 @@ async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, requ
|
|
|
673
682
|
throw error;
|
|
674
683
|
}
|
|
675
684
|
}
|
|
676
|
-
async function runDeclarativeFlowBySpecFile(fileName, config, flowParams, requestUserInput = requestUserInputInTerminal, setSummary, launchMode = "restart", runtime = runtimeServices) {
|
|
677
|
-
|
|
685
|
+
async function runDeclarativeFlowBySpecFile(fileName, config, flowParams, overrides = {}, requestUserInput = requestUserInputInTerminal, setSummary, launchMode = "restart", runtime = runtimeServices) {
|
|
686
|
+
const mergedFlowParams = {
|
|
687
|
+
...defaultDeclarativeFlowParams(config, false, overrides),
|
|
688
|
+
...flowParams,
|
|
689
|
+
};
|
|
690
|
+
await runDeclarativeFlowByRef(config.command, { source: "built-in", fileName }, config, mergedFlowParams, overrides, requestUserInput, setSummary, launchMode, runtime);
|
|
678
691
|
}
|
|
679
|
-
function defaultDeclarativeFlowParams(config, forceRefreshSummary = false) {
|
|
692
|
+
function defaultDeclarativeFlowParams(config, forceRefreshSummary = false, overrides = {}) {
|
|
680
693
|
const iteration = nextReviewIterationForTask(config.taskKey);
|
|
681
|
-
const latestIteration =
|
|
694
|
+
const latestIteration = latestArtifactIteration(config.taskKey, "review");
|
|
695
|
+
const launchProfile = overrides.launchProfile ?? resolveLaunchProfile({ executor: "default", model: "default" }, DEFAULT_LAUNCH_PROFILE);
|
|
682
696
|
return {
|
|
683
697
|
taskKey: config.taskKey,
|
|
684
698
|
jiraRef: config.jiraRef,
|
|
@@ -686,14 +700,19 @@ function defaultDeclarativeFlowParams(config, forceRefreshSummary = false) {
|
|
|
686
700
|
jiraApiUrl: config.jiraApiUrl,
|
|
687
701
|
jiraTaskFile: config.jiraTaskFile,
|
|
688
702
|
scopeKey: config.scope.scopeKey,
|
|
689
|
-
|
|
690
|
-
runGoTestsScript: config.runGoTestsScript,
|
|
691
|
-
runGoLinterScript: config.runGoLinterScript,
|
|
692
|
-
runGoCoverageScript: config.runGoCoverageScript,
|
|
703
|
+
workspaceDir: scopeWorkspaceDir(config.taskKey),
|
|
693
704
|
extraPrompt: config.extraPrompt,
|
|
694
705
|
reviewFixPoints: config.reviewFixPoints,
|
|
706
|
+
mdLang: config.mdLang,
|
|
707
|
+
llmExecutor: launchProfile.executor,
|
|
708
|
+
llmModel: launchProfile.model,
|
|
709
|
+
launchProfile,
|
|
695
710
|
iteration,
|
|
696
711
|
latestIteration,
|
|
712
|
+
taskSummaryIteration: nextArtifactIteration(config.taskKey, "task"),
|
|
713
|
+
designIteration: nextArtifactIteration(config.taskKey, "design"),
|
|
714
|
+
planIteration: nextArtifactIteration(config.taskKey, "plan"),
|
|
715
|
+
qaIteration: nextArtifactIteration(config.taskKey, "qa"),
|
|
697
716
|
...(latestIteration !== null ? { reviewFixSelectionJsonFile: reviewFixSelectionJsonFile(config.taskKey, latestIteration) } : {}),
|
|
698
717
|
forceRefresh: forceRefreshSummary,
|
|
699
718
|
};
|
|
@@ -719,88 +738,13 @@ function flowRequiresTaskScope(entry) {
|
|
|
719
738
|
}
|
|
720
739
|
return valueReferencesTaskScopeParams(entry.flow.phases);
|
|
721
740
|
}
|
|
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
741
|
async function summarizeBuildFailure(output) {
|
|
799
742
|
return summarizeBuildFailureViaPipeline(createPipelineContext({
|
|
800
743
|
issueKey: "build-failure-summary",
|
|
801
744
|
jiraRef: "build-failure-summary",
|
|
802
745
|
dryRun: false,
|
|
803
746
|
verbose: false,
|
|
747
|
+
mdLang: null,
|
|
804
748
|
runtime: runtimeServices,
|
|
805
749
|
requestUserInput: requestUserInputInTerminal,
|
|
806
750
|
}), output);
|
|
@@ -810,27 +754,78 @@ function requireJiraConfig(config) {
|
|
|
810
754
|
throw new TaskRunnerError(`Command '${config.command}' requires Jira context in the current project scope.`);
|
|
811
755
|
}
|
|
812
756
|
}
|
|
813
|
-
async function executeCommand(baseConfig, runFollowupVerify = true, requestUserInput = requestUserInputInTerminal, resolvedScope, setSummary, forceRefreshSummary = false, launchMode = "restart", runtime = runtimeServices) {
|
|
757
|
+
async function executeCommand(baseConfig, runFollowupVerify = true, requestUserInput = requestUserInputInTerminal, resolvedScope, setSummary, forceRefreshSummary = false, launchMode = "restart", launchProfile, runtime = runtimeServices) {
|
|
814
758
|
const config = buildRuntimeConfig(baseConfig, resolvedScope ?? (await resolveScopeForCommand(baseConfig, requestUserInput)));
|
|
815
|
-
if (config.command === "auto") {
|
|
816
|
-
|
|
817
|
-
|
|
759
|
+
if (config.command === "auto-golang") {
|
|
760
|
+
requireJiraConfig(config);
|
|
761
|
+
checkAutoPrerequisites(config);
|
|
762
|
+
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl;
|
|
763
|
+
process.env.JIRA_API_URL = config.jiraApiUrl;
|
|
764
|
+
process.env.JIRA_TASK_FILE = config.jiraTaskFile;
|
|
765
|
+
let effectiveLaunchMode = launchMode;
|
|
766
|
+
let effectiveLaunchProfile = launchProfile;
|
|
767
|
+
if (config.autoFromPhase) {
|
|
768
|
+
const flow = loadDeclarativeFlow({ source: "built-in", fileName: "auto-golang.json" });
|
|
769
|
+
const persistedState = loadFlowRunState(config.scope.scopeKey, "auto-golang");
|
|
770
|
+
if (!persistedState) {
|
|
771
|
+
throw new TaskRunnerError(`Cannot restart auto-golang from phase '${config.autoFromPhase}' because persisted flow state was not found.`);
|
|
772
|
+
}
|
|
773
|
+
rewindFlowRunStateToPhase(persistedState, flow.phases, config.autoFromPhase);
|
|
774
|
+
saveFlowRunState(persistedState);
|
|
775
|
+
effectiveLaunchMode = "resume";
|
|
776
|
+
effectiveLaunchProfile ??= persistedState.launchProfile;
|
|
777
|
+
printPanel("Auto-Golang Resume", `Auto-golang pipeline will continue from phase: ${config.autoFromPhase}`, "yellow");
|
|
818
778
|
}
|
|
819
|
-
await
|
|
779
|
+
await runDeclarativeFlowBySpecFile("auto-golang.json", config, autoFlowParams(config, forceRefreshSummary), effectiveLaunchProfile ? { launchProfile: effectiveLaunchProfile } : {}, requestUserInput, setSummary, effectiveLaunchMode, runtime);
|
|
780
|
+
return false;
|
|
781
|
+
}
|
|
782
|
+
if (config.command === "auto-common") {
|
|
783
|
+
requireJiraConfig(config);
|
|
784
|
+
checkAutoPrerequisites(config);
|
|
785
|
+
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl;
|
|
786
|
+
process.env.JIRA_API_URL = config.jiraApiUrl;
|
|
787
|
+
process.env.JIRA_TASK_FILE = config.jiraTaskFile;
|
|
788
|
+
await runDeclarativeFlowBySpecFile("auto-common.json", config, autoFlowParams(config, forceRefreshSummary), launchProfile ? { launchProfile } : {}, requestUserInput, setSummary, launchMode, runtime);
|
|
820
789
|
return false;
|
|
821
790
|
}
|
|
822
791
|
if (config.command === "auto-status") {
|
|
823
|
-
const state =
|
|
792
|
+
const state = loadFlowRunState(config.scope.scopeKey, "auto-golang");
|
|
824
793
|
if (!state) {
|
|
825
|
-
printPanel("Auto Status", `No
|
|
794
|
+
printPanel("Auto-Golang Status", `No flow state file found for ${config.taskKey}.`, "yellow");
|
|
826
795
|
return false;
|
|
827
796
|
}
|
|
828
|
-
|
|
797
|
+
const currentStep = findCurrentFlowExecutionStep(state) ?? state.currentStep ?? "-";
|
|
798
|
+
const phaseOrder = loadDeclarativeFlow({ source: "built-in", fileName: "auto-golang.json" }).phases;
|
|
799
|
+
const lines = [
|
|
800
|
+
`Issue: ${config.taskKey}`,
|
|
801
|
+
`Status: ${state.status}`,
|
|
802
|
+
`Current step: ${currentStep}`,
|
|
803
|
+
`Updated: ${state.updatedAt}`,
|
|
804
|
+
];
|
|
805
|
+
if (state.launchProfile) {
|
|
806
|
+
lines.push(`Launch profile: ${state.launchProfile.executor} / ${state.launchProfile.model}`);
|
|
807
|
+
}
|
|
808
|
+
if (state.lastError) {
|
|
809
|
+
lines.push(`Last error: ${state.lastError.step ?? "-"} (exit ${state.lastError.returnCode ?? "-"}, ${state.lastError.message ?? "-"})`);
|
|
810
|
+
}
|
|
811
|
+
lines.push("");
|
|
812
|
+
for (const phase of phaseOrder) {
|
|
813
|
+
const phaseState = state.executionState.phases.find((candidate) => candidate.id === phase.id);
|
|
814
|
+
lines.push(`[${phaseState?.status ?? "pending"}] ${phase.id}`);
|
|
815
|
+
for (const step of phase.steps) {
|
|
816
|
+
const stepState = phaseState?.steps.find((candidate) => candidate.id === step.id);
|
|
817
|
+
lines.push(` - [${stepState?.status ?? "pending"}] ${step.id}`);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
if (state.executionState.terminated) {
|
|
821
|
+
lines.push("", `Execution terminated: ${state.executionState.terminationReason ?? "yes"}`);
|
|
822
|
+
}
|
|
823
|
+
printPanel("Auto-Golang Status", lines.join("\n"), "cyan");
|
|
829
824
|
return false;
|
|
830
825
|
}
|
|
831
826
|
if (config.command === "auto-reset") {
|
|
832
|
-
const removed =
|
|
833
|
-
printPanel("Auto Reset", removed ? `State file ${
|
|
827
|
+
const removed = resetFlowRunState(config.scope.scopeKey, "auto-golang");
|
|
828
|
+
printPanel("Auto-Golang Reset", removed ? `State file ${flowStateFile(config.scope.scopeKey, "auto-golang")} removed.` : "No flow state file found.", "yellow");
|
|
834
829
|
return false;
|
|
835
830
|
}
|
|
836
831
|
checkPrerequisites(config);
|
|
@@ -854,9 +849,13 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
854
849
|
await runDeclarativeFlowBySpecFile("plan.json", config, {
|
|
855
850
|
jiraApiUrl: config.jiraApiUrl,
|
|
856
851
|
taskKey: config.taskKey,
|
|
852
|
+
taskSummaryIteration: nextArtifactIteration(config.taskKey, "task"),
|
|
853
|
+
designIteration: nextArtifactIteration(config.taskKey, "design"),
|
|
854
|
+
planIteration: nextArtifactIteration(config.taskKey, "plan"),
|
|
855
|
+
qaIteration: nextArtifactIteration(config.taskKey, "qa"),
|
|
857
856
|
extraPrompt: config.extraPrompt,
|
|
858
857
|
forceRefresh: forceRefreshSummary,
|
|
859
|
-
}, requestUserInput, setSummary, launchMode, runtime);
|
|
858
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, setSummary, launchMode, runtime);
|
|
860
859
|
return false;
|
|
861
860
|
}
|
|
862
861
|
if (config.command === "bug-analyze") {
|
|
@@ -866,33 +865,43 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
866
865
|
process.stdout.write(`Resolved Jira API URL: ${config.jiraApiUrl}\n`);
|
|
867
866
|
process.stdout.write(`Saving Jira issue JSON to: ${config.jiraTaskFile}\n`);
|
|
868
867
|
}
|
|
869
|
-
await runDeclarativeFlowBySpecFile("bug-analyze.json", config, {
|
|
868
|
+
await runDeclarativeFlowBySpecFile("bugz/bug-analyze.json", config, {
|
|
870
869
|
jiraApiUrl: config.jiraApiUrl,
|
|
871
870
|
taskKey: config.taskKey,
|
|
871
|
+
taskSummaryIteration: nextArtifactIteration(config.taskKey, "task"),
|
|
872
|
+
bugAnalyzeIteration: nextArtifactIteration(config.taskKey, "bug-analyze"),
|
|
873
|
+
bugFixDesignIteration: nextArtifactIteration(config.taskKey, "bug-fix-design"),
|
|
874
|
+
bugFixPlanIteration: nextArtifactIteration(config.taskKey, "bug-fix-plan"),
|
|
872
875
|
extraPrompt: config.extraPrompt,
|
|
873
876
|
forceRefresh: forceRefreshSummary,
|
|
874
|
-
}, requestUserInput, setSummary, launchMode, runtime);
|
|
877
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, setSummary, launchMode, runtime);
|
|
875
878
|
return false;
|
|
876
879
|
}
|
|
877
880
|
if (config.command === "gitlab-review") {
|
|
878
881
|
const iteration = nextReviewIterationForTask(config.taskKey);
|
|
879
|
-
|
|
882
|
+
const gitlabReviewIteration = nextArtifactIteration(config.taskKey, "gitlab-review");
|
|
883
|
+
await runDeclarativeFlowBySpecFile("gitlab/gitlab-review.json", config, {
|
|
880
884
|
taskKey: config.taskKey,
|
|
881
885
|
iteration,
|
|
886
|
+
gitlabReviewIteration,
|
|
882
887
|
extraPrompt: config.extraPrompt,
|
|
883
|
-
|
|
888
|
+
reviewFixSelectionJsonFile: reviewFixSelectionJsonFile(config.taskKey, iteration),
|
|
889
|
+
reviewFixPoints: config.reviewFixPoints,
|
|
890
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
884
891
|
if (!config.dryRun) {
|
|
885
|
-
printSummary("GitLab Review", `Artifacts:\n${gitlabReviewFile(config.taskKey)}\n${gitlabReviewJsonFile(config.taskKey)}`);
|
|
892
|
+
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)}`);
|
|
886
893
|
}
|
|
887
894
|
return false;
|
|
888
895
|
}
|
|
889
896
|
if (config.command === "gitlab-diff-review") {
|
|
890
897
|
const iteration = nextReviewIterationForTask(config.taskKey);
|
|
891
|
-
|
|
898
|
+
const gitlabDiffIteration = nextArtifactIteration(config.taskKey, "gitlab-diff");
|
|
899
|
+
await runDeclarativeFlowBySpecFile("gitlab/gitlab-diff-review.json", config, {
|
|
892
900
|
taskKey: config.taskKey,
|
|
893
901
|
iteration,
|
|
902
|
+
gitlabDiffIteration,
|
|
894
903
|
extraPrompt: config.extraPrompt,
|
|
895
|
-
}, requestUserInput, undefined, launchMode, runtime);
|
|
904
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
896
905
|
if (!config.dryRun) {
|
|
897
906
|
printSummary("GitLab Diff Review", `Artifacts:\n${gitlabDiffFile(config.taskKey)}\n${gitlabDiffJsonFile(config.taskKey)}\n${reviewFile(config.taskKey, iteration)}\n${reviewJsonFile(config.taskKey, iteration)}`);
|
|
898
907
|
}
|
|
@@ -907,28 +916,30 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
907
916
|
{ path: bugFixDesignJsonFile(config.taskKey), schemaId: "bug-fix-design/v1" },
|
|
908
917
|
{ path: bugFixPlanJsonFile(config.taskKey), schemaId: "bug-fix-plan/v1" },
|
|
909
918
|
], "Bug-fix mode requires valid structured artifacts from the bug analysis phase.");
|
|
910
|
-
await runDeclarativeFlowBySpecFile("bug-fix.json", config, {
|
|
919
|
+
await runDeclarativeFlowBySpecFile("bugz/bug-fix.json", config, {
|
|
911
920
|
taskKey: config.taskKey,
|
|
912
921
|
extraPrompt: config.extraPrompt,
|
|
913
|
-
}, requestUserInput, undefined, launchMode, runtime);
|
|
922
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
914
923
|
return false;
|
|
915
924
|
}
|
|
916
925
|
if (config.command === "mr-description") {
|
|
917
926
|
requireJiraConfig(config);
|
|
918
927
|
requireJiraTaskFile(config.jiraTaskFile);
|
|
919
|
-
await runDeclarativeFlowBySpecFile("mr-description.json", config, {
|
|
928
|
+
await runDeclarativeFlowBySpecFile("gitlab/mr-description.json", config, {
|
|
920
929
|
taskKey: config.taskKey,
|
|
930
|
+
iteration: nextArtifactIteration(config.taskKey, "mr-description"),
|
|
921
931
|
extraPrompt: config.extraPrompt,
|
|
922
|
-
}, requestUserInput, undefined, launchMode, runtime);
|
|
932
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
923
933
|
return false;
|
|
924
934
|
}
|
|
925
935
|
if (config.command === "task-describe") {
|
|
926
|
-
|
|
936
|
+
const iteration = nextArtifactIteration(config.taskKey, "jira-description");
|
|
927
937
|
await runDeclarativeFlowBySpecFile("task-describe.json", config, {
|
|
928
938
|
jiraApiUrl: config.jiraApiUrl,
|
|
929
939
|
taskKey: config.taskKey,
|
|
940
|
+
iteration,
|
|
930
941
|
extraPrompt: config.extraPrompt,
|
|
931
|
-
}, requestUserInput, undefined, launchMode, runtime);
|
|
942
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
932
943
|
return false;
|
|
933
944
|
}
|
|
934
945
|
if (config.command === "implement") {
|
|
@@ -941,7 +952,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
941
952
|
await runDeclarativeFlowBySpecFile("implement.json", config, {
|
|
942
953
|
taskKey: config.taskKey,
|
|
943
954
|
extraPrompt: config.extraPrompt,
|
|
944
|
-
}, requestUserInput, undefined, launchMode);
|
|
955
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode);
|
|
945
956
|
return false;
|
|
946
957
|
}
|
|
947
958
|
if (config.command === "review") {
|
|
@@ -952,142 +963,74 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
952
963
|
{ path: designJsonFile(config.taskKey), schemaId: "implementation-design/v1" },
|
|
953
964
|
{ path: planJsonFile(config.taskKey), schemaId: "implementation-plan/v1" },
|
|
954
965
|
], "Review mode requires valid structured plan artifacts from the planning phase.");
|
|
955
|
-
await runDeclarativeFlowBySpecFile("review.json", config, {
|
|
966
|
+
await runDeclarativeFlowBySpecFile("review/review.json", config, {
|
|
956
967
|
taskKey: config.taskKey,
|
|
957
968
|
iteration,
|
|
958
969
|
extraPrompt: config.extraPrompt,
|
|
959
|
-
}, requestUserInput, undefined, launchMode, runtime);
|
|
970
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
960
971
|
}
|
|
961
972
|
else {
|
|
962
|
-
await runDeclarativeFlowBySpecFile("review-project.json", config, {
|
|
973
|
+
await runDeclarativeFlowBySpecFile("review/review-project.json", config, {
|
|
963
974
|
taskKey: config.taskKey,
|
|
964
975
|
iteration,
|
|
965
976
|
extraPrompt: config.extraPrompt,
|
|
966
|
-
}, requestUserInput, undefined, launchMode, runtime);
|
|
977
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
967
978
|
}
|
|
968
979
|
return !config.dryRun && existsSync(readyToMergeFile(config.taskKey));
|
|
969
980
|
}
|
|
970
981
|
if (config.command === "review-fix") {
|
|
971
|
-
const latestIteration =
|
|
982
|
+
const latestIteration = latestArtifactIteration(config.taskKey, "review");
|
|
972
983
|
if (latestIteration === null) {
|
|
973
|
-
throw new TaskRunnerError("Review-fix mode requires at least one review
|
|
984
|
+
throw new TaskRunnerError("Review-fix mode requires at least one review artifact.");
|
|
974
985
|
}
|
|
975
986
|
validateStructuredArtifacts([
|
|
976
987
|
{ path: reviewJsonFile(config.taskKey, latestIteration), schemaId: "review-findings/v1" },
|
|
977
|
-
{ path: reviewReplyJsonFile(config.taskKey, latestIteration), schemaId: "review-reply/v1" },
|
|
978
988
|
], "Review-fix mode requires valid structured review artifacts.");
|
|
979
|
-
await runDeclarativeFlowBySpecFile("review-fix.json", config, {
|
|
989
|
+
await runDeclarativeFlowBySpecFile("review/review-fix.json", config, {
|
|
980
990
|
taskKey: config.taskKey,
|
|
981
991
|
latestIteration,
|
|
992
|
+
reviewAssessmentJsonFile: null,
|
|
982
993
|
reviewFixSelectionJsonFile: reviewFixSelectionJsonFile(config.taskKey, latestIteration),
|
|
983
994
|
extraPrompt: config.extraPrompt,
|
|
984
995
|
reviewFixPoints: config.reviewFixPoints,
|
|
985
|
-
}, requestUserInput, undefined, launchMode, runtime);
|
|
996
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
986
997
|
return false;
|
|
987
998
|
}
|
|
999
|
+
if (config.command === "review-loop") {
|
|
1000
|
+
const iteration = nextReviewIterationForTask(config.taskKey);
|
|
1001
|
+
if (config.jiraBrowseUrl && config.jiraApiUrl && config.jiraTaskFile) {
|
|
1002
|
+
requireJiraConfig(config);
|
|
1003
|
+
validateStructuredArtifacts([
|
|
1004
|
+
{ path: designJsonFile(config.taskKey), schemaId: "implementation-design/v1" },
|
|
1005
|
+
{ path: planJsonFile(config.taskKey), schemaId: "implementation-plan/v1" },
|
|
1006
|
+
], "Review-loop mode requires valid structured plan artifacts from the planning phase.");
|
|
1007
|
+
}
|
|
1008
|
+
await runDeclarativeFlowBySpecFile("review/review-loop.json", config, {
|
|
1009
|
+
taskKey: config.taskKey,
|
|
1010
|
+
iteration,
|
|
1011
|
+
extraPrompt: config.extraPrompt,
|
|
1012
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
1013
|
+
return !config.dryRun && existsSync(readyToMergeFile(config.taskKey));
|
|
1014
|
+
}
|
|
988
1015
|
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, {
|
|
1016
|
+
await runDeclarativeFlowBySpecFile(config.command === "run-go-tests-loop" ? "go/run-go-tests-loop.json" : "go/run-go-linter-loop.json", config, {
|
|
990
1017
|
taskKey: config.taskKey,
|
|
991
|
-
runGoTestsScript:
|
|
992
|
-
runGoLinterScript:
|
|
1018
|
+
runGoTestsScript: path.join(agentweaverHome(PACKAGE_ROOT), "run_go_tests.py"),
|
|
1019
|
+
runGoLinterScript: path.join(agentweaverHome(PACKAGE_ROOT), "run_go_linter.py"),
|
|
1020
|
+
runGoTestsIteration: nextArtifactIteration(config.taskKey, "run-go-tests-result", "json"),
|
|
1021
|
+
runGoLinterIteration: nextArtifactIteration(config.taskKey, "run-go-linter-result", "json"),
|
|
993
1022
|
extraPrompt: config.extraPrompt,
|
|
994
|
-
}, requestUserInput, undefined, launchMode, runtime);
|
|
1023
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
995
1024
|
return false;
|
|
996
1025
|
}
|
|
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
|
-
}
|
|
1026
|
+
if (config.command === "git-commit") {
|
|
1027
|
+
await runDeclarativeFlowBySpecFile("git-commit.json", config, {
|
|
1028
|
+
taskKey: config.taskKey,
|
|
1029
|
+
extraPrompt: config.extraPrompt,
|
|
1030
|
+
}, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
|
|
1031
|
+
return false;
|
|
1090
1032
|
}
|
|
1033
|
+
throw new TaskRunnerError(`Unsupported command: ${config.command}`);
|
|
1091
1034
|
}
|
|
1092
1035
|
function parseCliArgs(argv) {
|
|
1093
1036
|
if (argv.includes("--version") || argv.includes("-v")) {
|
|
@@ -1114,6 +1057,7 @@ function parseCliArgs(argv) {
|
|
|
1114
1057
|
let scopeName;
|
|
1115
1058
|
let helpPhases = false;
|
|
1116
1059
|
let jiraRef;
|
|
1060
|
+
let mdLang;
|
|
1117
1061
|
for (let index = 1; index < argv.length; index += 1) {
|
|
1118
1062
|
const token = argv[index] ?? "";
|
|
1119
1063
|
if (token === "--dry") {
|
|
@@ -1143,12 +1087,39 @@ function parseCliArgs(argv) {
|
|
|
1143
1087
|
index += 1;
|
|
1144
1088
|
continue;
|
|
1145
1089
|
}
|
|
1090
|
+
if (token === "--md-lang") {
|
|
1091
|
+
const langValue = argv[index + 1];
|
|
1092
|
+
if (langValue === "en" || langValue === "ru") {
|
|
1093
|
+
mdLang = langValue;
|
|
1094
|
+
}
|
|
1095
|
+
else {
|
|
1096
|
+
process.stderr.write("Error: --md-lang accepts only 'en' or 'ru' as values.\n");
|
|
1097
|
+
process.exit(1);
|
|
1098
|
+
}
|
|
1099
|
+
index += 1;
|
|
1100
|
+
continue;
|
|
1101
|
+
}
|
|
1102
|
+
if (token.startsWith("--md-lang=")) {
|
|
1103
|
+
const langValue = token.slice("--md-lang=".length);
|
|
1104
|
+
if (langValue === "en" || langValue === "ru") {
|
|
1105
|
+
mdLang = langValue;
|
|
1106
|
+
}
|
|
1107
|
+
else {
|
|
1108
|
+
process.stderr.write("Error: --md-lang accepts only 'en' or 'ru' as values.\n");
|
|
1109
|
+
process.exit(1);
|
|
1110
|
+
}
|
|
1111
|
+
continue;
|
|
1112
|
+
}
|
|
1146
1113
|
jiraRef = token;
|
|
1147
1114
|
}
|
|
1148
|
-
if (command === "auto" && helpPhases) {
|
|
1115
|
+
if (command === "auto-golang" && helpPhases) {
|
|
1149
1116
|
printAutoPhasesHelp();
|
|
1150
1117
|
process.exit(0);
|
|
1151
1118
|
}
|
|
1119
|
+
if (command === "auto-common" && helpPhases) {
|
|
1120
|
+
printAutoCommonPhasesHelp();
|
|
1121
|
+
process.exit(0);
|
|
1122
|
+
}
|
|
1152
1123
|
return {
|
|
1153
1124
|
command: command,
|
|
1154
1125
|
dry,
|
|
@@ -1158,6 +1129,7 @@ function parseCliArgs(argv) {
|
|
|
1158
1129
|
...(scopeName !== undefined ? { scopeName } : {}),
|
|
1159
1130
|
...(prompt !== undefined ? { prompt } : {}),
|
|
1160
1131
|
...(autoFromPhase !== undefined ? { autoFromPhase } : {}),
|
|
1132
|
+
...(mdLang !== undefined ? { mdLang } : {}),
|
|
1161
1133
|
};
|
|
1162
1134
|
}
|
|
1163
1135
|
function buildConfigFromArgs(args) {
|
|
@@ -1166,6 +1138,7 @@ function buildConfigFromArgs(args) {
|
|
|
1166
1138
|
...(args.scopeName !== undefined ? { scopeName: args.scopeName } : {}),
|
|
1167
1139
|
...(args.prompt !== undefined ? { extraPrompt: args.prompt } : {}),
|
|
1168
1140
|
...(args.autoFromPhase !== undefined ? { autoFromPhase: args.autoFromPhase } : {}),
|
|
1141
|
+
...(args.mdLang !== undefined ? { mdLang: args.mdLang } : {}),
|
|
1169
1142
|
dryRun: args.dry,
|
|
1170
1143
|
verbose: args.verbose,
|
|
1171
1144
|
});
|
|
@@ -1183,34 +1156,13 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1183
1156
|
summaryText: "",
|
|
1184
1157
|
cwd: process.cwd(),
|
|
1185
1158
|
gitBranchName,
|
|
1159
|
+
version: packageVersion(),
|
|
1186
1160
|
flows: interactiveFlowDefinitions(flowCatalog),
|
|
1187
1161
|
getRunConfirmation: async (flowId) => {
|
|
1188
1162
|
const flowEntry = findCatalogEntry(flowId, flowCatalog);
|
|
1189
1163
|
if (!flowEntry) {
|
|
1190
1164
|
throw new TaskRunnerError(`Unknown flow: ${flowId}`);
|
|
1191
1165
|
}
|
|
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
1166
|
const resumeLookup = lookupInteractiveFlowResume(flowEntry, currentScope);
|
|
1215
1167
|
return resumeLookup;
|
|
1216
1168
|
},
|
|
@@ -1223,6 +1175,16 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1223
1175
|
if (!flowEntry) {
|
|
1224
1176
|
throw new TaskRunnerError(`Unknown flow: ${flowId}`);
|
|
1225
1177
|
}
|
|
1178
|
+
const resumeState = launchMode === "resume" ? loadFlowRunState(currentScope.scopeKey, flowId) : null;
|
|
1179
|
+
if (resumeState) {
|
|
1180
|
+
currentScope = scopeWithRestoredJiraContext(currentScope, resumeState);
|
|
1181
|
+
}
|
|
1182
|
+
const launchProfile = launchMode === "resume"
|
|
1183
|
+
? resumeState?.launchProfile
|
|
1184
|
+
: await requestInteractiveLaunchProfile((form) => ui.requestUserInput(form));
|
|
1185
|
+
if (!launchProfile) {
|
|
1186
|
+
throw new TaskRunnerError("Resume is impossible because launch profile was not saved. Use restart.");
|
|
1187
|
+
}
|
|
1226
1188
|
const previousScopeKey = currentScope.scopeKey;
|
|
1227
1189
|
const baseConfig = buildBaseConfig(flowId, {
|
|
1228
1190
|
...(currentScope.jiraRef ? { jiraRef: currentScope.jiraRef } : {}),
|
|
@@ -1240,12 +1202,13 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1240
1202
|
if (previousScopeKey !== currentScope.scopeKey || currentScope.jiraIssueKey) {
|
|
1241
1203
|
syncInteractiveTaskSummary(ui, currentScope, forceRefresh);
|
|
1242
1204
|
}
|
|
1205
|
+
printPanel("Effective Launch Config", `executor: ${launchProfile.executor}\nmodel: ${launchProfile.model}\nmode: ${launchMode}`, "cyan");
|
|
1243
1206
|
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));
|
|
1207
|
+
await executeCommand(baseConfig, true, (form) => ui.requestUserInput(form), currentScope, (markdown) => ui.setSummary(markdown), forceRefresh, launchMode, launchProfile, createRuntimeServices(abortController.signal));
|
|
1245
1208
|
return;
|
|
1246
1209
|
}
|
|
1247
1210
|
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));
|
|
1211
|
+
await runDeclarativeFlowByRef(flowId, toDeclarativeFlowRef(flowEntry), runtimeConfig, defaultDeclarativeFlowParams(runtimeConfig, forceRefresh, { launchProfile }), { launchProfile }, (form) => ui.requestUserInput(form), (markdown) => ui.setSummary(markdown), launchMode, createRuntimeServices(abortController.signal));
|
|
1249
1212
|
}
|
|
1250
1213
|
catch (error) {
|
|
1251
1214
|
if (error instanceof FlowInterruptedError) {
|
|
@@ -1309,7 +1272,7 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1309
1272
|
});
|
|
1310
1273
|
}
|
|
1311
1274
|
export async function main(argv = process.argv.slice(2)) {
|
|
1312
|
-
|
|
1275
|
+
loadTieredEnv(process.cwd());
|
|
1313
1276
|
let forceRefresh = false;
|
|
1314
1277
|
const args = [...argv];
|
|
1315
1278
|
if (args[0] === "--force") {
|