agentweaver 0.1.19 → 0.1.20
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 +47 -7
- package/dist/artifacts.js +9 -0
- package/dist/executors/git-commit-executor.js +24 -6
- package/dist/flow-state.js +3 -8
- package/dist/git/git-diff-parser.js +223 -0
- package/dist/git/git-service.js +562 -0
- package/dist/git/git-stage-selection.js +24 -0
- package/dist/git/git-status-parser.js +171 -0
- package/dist/git/git-types.js +1 -0
- package/dist/index.js +450 -108
- package/dist/interactive/auto-flow.js +644 -0
- package/dist/interactive/controller.js +417 -9
- package/dist/interactive/progress.js +194 -1
- package/dist/interactive/state.js +25 -0
- package/dist/interactive/web/index.js +97 -12
- package/dist/interactive/web/protocol.js +216 -1
- package/dist/interactive/web/server.js +72 -14
- package/dist/interactive/web/static/app.js +1603 -49
- package/dist/interactive/web/static/index.html +76 -11
- package/dist/interactive/web/static/styles.css +1 -1
- package/dist/interactive/web/static/styles.input.css +901 -47
- package/dist/pipeline/auto-flow-blocks.js +307 -0
- package/dist/pipeline/auto-flow-config.js +273 -0
- package/dist/pipeline/auto-flow-identity.js +49 -0
- package/dist/pipeline/auto-flow-presets.js +52 -0
- package/dist/pipeline/auto-flow-resolver.js +830 -0
- package/dist/pipeline/auto-flow-types.js +17 -0
- package/dist/pipeline/context.js +1 -0
- package/dist/pipeline/declarative-flows.js +27 -1
- package/dist/pipeline/flow-specs/auto-common-guided.json +11 -0
- package/dist/pipeline/flow-specs/auto-golang.json +12 -1
- package/dist/pipeline/flow-specs/bugz/bug-analyze.json +54 -1
- package/dist/pipeline/flow-specs/gitlab/gitlab-diff-review.json +19 -1
- package/dist/pipeline/flow-specs/gitlab/gitlab-review.json +33 -1
- package/dist/pipeline/flow-specs/review/review-project.json +19 -1
- package/dist/pipeline/flow-specs/task-source/manual-jira-input.json +70 -0
- package/dist/pipeline/node-registry.js +9 -0
- package/dist/pipeline/nodes/codex-prompt-node.js +8 -1
- package/dist/pipeline/nodes/flow-run-node.js +5 -3
- package/dist/pipeline/nodes/git-status-node.js +2 -168
- package/dist/pipeline/nodes/manual-jira-task-input-node.js +146 -0
- package/dist/pipeline/nodes/opencode-prompt-node.js +8 -1
- package/dist/pipeline/nodes/plan-codex-node.js +8 -1
- package/dist/pipeline/spec-loader.js +14 -4
- package/dist/runtime/artifact-catalog.js +29 -5
- package/dist/runtime/settings.js +114 -0
- package/dist/scope.js +14 -4
- package/package.json +1 -1
- package/dist/pipeline/flow-specs/auto-common.json +0 -179
- package/dist/pipeline/flow-specs/auto-simple.json +0 -141
package/dist/index.js
CHANGED
|
@@ -12,8 +12,11 @@ import { AGENTWEAVER_REVIEW_BLOCKING_SEVERITIES_ENV, parseReviewSeverityCsv, res
|
|
|
12
12
|
import { summarizeBuildFailure as summarizeBuildFailureViaPipeline } from "./pipeline/build-failure-summary.js";
|
|
13
13
|
import { runNodeChecks } from "./pipeline/checks.js";
|
|
14
14
|
import { createPipelineContext } from "./pipeline/context.js";
|
|
15
|
-
import { collectFlowRoutingGroups, loadDeclarativeFlow } from "./pipeline/declarative-flows.js";
|
|
15
|
+
import { collectFlowRoutingGroups, loadDeclarativeFlow, } from "./pipeline/declarative-flows.js";
|
|
16
16
|
import { runExpandedPhase } from "./pipeline/declarative-flow-runner.js";
|
|
17
|
+
import { listAutoFlowConfigs, loadAutoFlowConfigByName } from "./pipeline/auto-flow-config.js";
|
|
18
|
+
import { formatAutoFlowDryRunPreview, persistResolvedAutoFlowArtifacts, resolveAutoFlow, } from "./pipeline/auto-flow-resolver.js";
|
|
19
|
+
import { autoFlowIdentityForSelection, defaultAutoFlowSelection, isRestartArchivingFlowId, } from "./pipeline/auto-flow-identity.js";
|
|
17
20
|
import { builtInCommandFlowFile, findCatalogEntry, flowRoutingGroups, isBuiltInCommandFlowId, loadInteractiveFlowCatalog, toDeclarativeFlowRef, } from "./pipeline/flow-catalog.js";
|
|
18
21
|
import { createPipelineRegistryContext } from "./pipeline/plugin-loader.js";
|
|
19
22
|
import { DEFAULT_LAUNCH_PROFILE, } from "./pipeline/launch-profile-config.js";
|
|
@@ -33,10 +36,11 @@ import { describeExecutionRouting, executorsForRoutingGroups, resolveExecutionRo
|
|
|
33
36
|
import { requestInteractiveExecutionRouting } from "./runtime/interactive-execution-routing.js";
|
|
34
37
|
import { createInteractiveSession } from "./interactive/create-interactive-session.js";
|
|
35
38
|
import { createWebInteractiveSession } from "./interactive/web/index.js";
|
|
36
|
-
import {
|
|
39
|
+
import { autoFlowSelectionForFlowId, createConfigAutoFlowDefinition, createPresetAutoFlowDefinition, } from "./interactive/auto-flow.js";
|
|
40
|
+
import { bye, getOutputAdapter, printError, printInfo, printPanel, renderPanel, printSummary, setFlowExecutionState, stripAnsi, } from "./tui.js";
|
|
37
41
|
import { requestUserInputInTerminal } from "./user-input.js";
|
|
38
42
|
import { runDoctorCommand } from "./doctor/index.js";
|
|
39
|
-
import { requestJiraContext, resolveProjectScope, } from "./scope.js";
|
|
43
|
+
import { requestJiraContext, requestOptionalJiraContext, resolveProjectScope, } from "./scope.js";
|
|
40
44
|
const COMMANDS = [
|
|
41
45
|
"auto",
|
|
42
46
|
"auto-golang",
|
|
@@ -77,6 +81,17 @@ function writeStdoutSync(text) {
|
|
|
77
81
|
function writeStderrSync(text) {
|
|
78
82
|
writeSync(process.stderr.fd, text);
|
|
79
83
|
}
|
|
84
|
+
function printPanelSync(title, text) {
|
|
85
|
+
const adapter = getOutputAdapter();
|
|
86
|
+
if (adapter.renderAuxiliaryOutput === false) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (adapter.renderPanelsAsPlainText) {
|
|
90
|
+
writeStdoutSync(`${title}\n${text}\n\n`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
writeStdoutSync(`${renderPanel(title, text)}\n`);
|
|
94
|
+
}
|
|
80
95
|
function createRuntimeServices(signal) {
|
|
81
96
|
return {
|
|
82
97
|
resolveCmd,
|
|
@@ -175,18 +190,18 @@ function usage() {
|
|
|
175
190
|
agentweaver review-loop [--dry] [--verbose] [--prompt <text>] [--scope <name>] [--blocking-severities <list>] [<jira-browse-url|jira-issue-key>]
|
|
176
191
|
agentweaver run-go-tests-loop [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
177
192
|
agentweaver run-go-linter-loop [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
178
|
-
agentweaver auto [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] <jira-browse-url|jira-issue-key>
|
|
193
|
+
agentweaver auto [--preset <simple|standard>|--config <name>] [--dry-run-flow] [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] [<jira-browse-url|jira-issue-key>]
|
|
179
194
|
agentweaver auto --help-phases
|
|
180
195
|
agentweaver auto-golang [--dry] [--verbose] [--prompt <text>] [<jira-browse-url|jira-issue-key>]
|
|
181
196
|
agentweaver auto-golang [--dry] [--verbose] [--prompt <text>] --from <phase> [<jira-browse-url|jira-issue-key>]
|
|
182
197
|
agentweaver auto-golang --help-phases
|
|
183
|
-
agentweaver auto-common-guided [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] [--accept-playbook-draft] <jira-browse-url|jira-issue-key>
|
|
184
|
-
agentweaver auto-common [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] <jira-browse-url|jira-issue-key>
|
|
198
|
+
agentweaver auto-common-guided [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] [--accept-playbook-draft] [<jira-browse-url|jira-issue-key>]
|
|
199
|
+
agentweaver auto-common [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] [<jira-browse-url|jira-issue-key>]
|
|
185
200
|
agentweaver auto-common --help-phases
|
|
186
|
-
agentweaver auto-simple [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] <jira-browse-url|jira-issue-key>
|
|
201
|
+
agentweaver auto-simple [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] [<jira-browse-url|jira-issue-key>]
|
|
187
202
|
agentweaver auto-simple --help-phases
|
|
188
|
-
agentweaver auto-status [<jira-browse-url|jira-issue-key>]
|
|
189
|
-
agentweaver auto-reset [<jira-browse-url|jira-issue-key>]
|
|
203
|
+
agentweaver auto-status [--preset <simple|standard>|--config <name>] [<jira-browse-url|jira-issue-key>]
|
|
204
|
+
agentweaver auto-reset [--preset <simple|standard>|--config <name>] [<jira-browse-url|jira-issue-key>]
|
|
190
205
|
|
|
191
206
|
Interactive Mode:
|
|
192
207
|
When started without a command, the script opens an interactive UI.
|
|
@@ -199,13 +214,16 @@ Flags:
|
|
|
199
214
|
--no-open Web command only: print the Web UI URL without opening a browser
|
|
200
215
|
--host Web command only: bind Web UI to this host (default: 127.0.0.1)
|
|
201
216
|
--listen-all Web command only: bind Web UI to 0.0.0.0
|
|
202
|
-
--dry Fetch
|
|
217
|
+
--dry Fetch or collect task context, but print codex/opencode commands instead of executing them
|
|
218
|
+
--preset Auto, auto-status, and auto-reset: resolve the simple or standard preset (default: standard)
|
|
219
|
+
--config Auto, auto-status, and auto-reset: load .agentweaver/flow-configs/<name>.yaml or ~/.agentweaver/flow-configs/<name>.yaml
|
|
220
|
+
--dry-run-flow Auto command only: validate and preview flow resolution without running workflow steps or writing resolver artifacts
|
|
203
221
|
--verbose Show live stdout/stderr of launched commands
|
|
204
222
|
--scope Explicit workflow scope name for non-Jira runs except instant-task
|
|
205
223
|
--prompt Extra prompt text appended to the base prompt
|
|
206
224
|
--resume Resume an interrupted run when valid
|
|
207
225
|
--continue Continue a terminated iterative run when valid
|
|
208
|
-
--restart
|
|
226
|
+
--restart Start a fresh run; end-to-end attempt flows archive the active attempt first
|
|
209
227
|
--blocking-severities Comma-separated severities that block merge and drive review-fix auto-selection
|
|
210
228
|
--md-lang Language for workflow markdown artifacts only: en (English) or ru (Russian, default)
|
|
211
229
|
--accept-playbook-draft Non-interactively accept generated playbook content for playbook-init or auto-common-guided missing-manifest runs
|
|
@@ -229,7 +247,10 @@ Optional environment variables:
|
|
|
229
247
|
${WEB_AUTH_PASSWORD_ENV} Web UI Basic auth password; required for external Web UI binding
|
|
230
248
|
|
|
231
249
|
Notes:
|
|
232
|
-
-
|
|
250
|
+
- agentweaver auto defaults to --preset standard, equivalent to auto-common. Use --dry-run-flow to inspect the resolved preset or saved config before execution.
|
|
251
|
+
- Saved auto flow configs are YAML files named .agentweaver/flow-configs/<name>.yaml or ~/.agentweaver/flow-configs/<name>.yaml; project configs take precedence over user configs.
|
|
252
|
+
- Successful configurable auto runs write flow-config.yaml, resolved-flow.json, and resolved-flow-summary.json under the current scope .artifacts directory. --dry-run-flow writes none of them.
|
|
253
|
+
- auto-golang, auto-common-guided, auto-common, auto-simple, and configurable auto ask for Jira input when Jira is not passed as an argument; leave it empty to paste the task description manually in the next step. task-describe can also work from a manual task description without Jira.
|
|
233
254
|
- agentweaver web binds to 127.0.0.1 by default on an operating-system-assigned port and does not require auth unless Web UI credentials are configured.
|
|
234
255
|
- External Web UI binding through --listen-all, --host 0.0.0.0, --host ::, non-loopback IPs, or hostnames other than localhost requires ${WEB_AUTH_USERNAME_ENV} and ${WEB_AUTH_PASSWORD_ENV}.
|
|
235
256
|
- Web UI Basic auth over plain HTTP is suitable only on trusted networks; use TLS termination or a reverse proxy on untrusted networks.
|
|
@@ -409,8 +430,15 @@ function scopeWithRestoredJiraContext(scope, state) {
|
|
|
409
430
|
return resolveProjectScope(null, state.jiraRef);
|
|
410
431
|
}
|
|
411
432
|
function buildInteractiveBaseConfig(flowId, scope) {
|
|
412
|
-
|
|
433
|
+
const autoFlowSelection = autoFlowSelectionForFlowId(flowId);
|
|
434
|
+
const command = autoFlowSelection
|
|
435
|
+
? autoFlowSelection.kind === "preset" && autoFlowSelection.preset === "simple"
|
|
436
|
+
? "auto-simple"
|
|
437
|
+
: "auto-common"
|
|
438
|
+
: flowId;
|
|
439
|
+
return buildBaseConfig(command, {
|
|
413
440
|
...(flowId !== "instant-task" && scope.jiraRef ? { jiraRef: scope.jiraRef } : {}),
|
|
441
|
+
...(autoFlowSelection ? { autoFlowSelection } : {}),
|
|
414
442
|
});
|
|
415
443
|
}
|
|
416
444
|
async function lookupInteractiveFlowResume(flowEntry, currentScope) {
|
|
@@ -448,9 +476,31 @@ async function lookupInteractiveFlowResume(flowEntry, currentScope) {
|
|
|
448
476
|
...availability,
|
|
449
477
|
};
|
|
450
478
|
}
|
|
479
|
+
async function lookupInteractiveAutoFlowResume(selection, currentScope) {
|
|
480
|
+
const resolved = await resolveAutoFlow(selection, {
|
|
481
|
+
cwd: process.cwd(),
|
|
482
|
+
scopeKey: currentScope.scopeKey,
|
|
483
|
+
});
|
|
484
|
+
const identity = autoFlowIdentityForSelection(selection, resolved);
|
|
485
|
+
const state = loadFlowRunState(currentScope.scopeKey, identity.flowId);
|
|
486
|
+
const availability = classifyFlowLaunchAvailability(state);
|
|
487
|
+
if (state && availability.resume.available) {
|
|
488
|
+
return {
|
|
489
|
+
...availability,
|
|
490
|
+
details: buildFlowResumeDetails(state),
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
if (state && availability.continue.available) {
|
|
494
|
+
return {
|
|
495
|
+
...availability,
|
|
496
|
+
details: buildFlowContinueDetails(state),
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
return availability;
|
|
500
|
+
}
|
|
451
501
|
async function printAutoPhasesHelp() {
|
|
452
502
|
const phaseLines = ["Available auto-golang phases:", "", ...(await autoPhaseIds())];
|
|
453
|
-
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>");
|
|
503
|
+
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>");
|
|
454
504
|
printPanel("Auto-Golang Phases", phaseLines.join("\n"), "magenta");
|
|
455
505
|
}
|
|
456
506
|
async function autoCommonPhaseIds(fileName = "auto-common.json") {
|
|
@@ -458,7 +508,7 @@ async function autoCommonPhaseIds(fileName = "auto-common.json") {
|
|
|
458
508
|
}
|
|
459
509
|
async function printAutoCommonPhasesHelp(command = "auto-common", fileName = "auto-common.json") {
|
|
460
510
|
const phaseLines = [`Available ${command} phases:`, "", ...(await autoCommonPhaseIds(fileName))];
|
|
461
|
-
phaseLines.push("", `You can run ${command} with:`, `agentweaver ${command} <jira
|
|
511
|
+
phaseLines.push("", `You can run ${command} with:`, `agentweaver ${command} [<jira>]`);
|
|
462
512
|
printPanel(command === "auto-common-guided" ? "Auto-Common Guided Phases" : "Auto-Common Phases", phaseLines.join("\n"), "magenta");
|
|
463
513
|
}
|
|
464
514
|
async function autoSimplePhaseIds() {
|
|
@@ -466,7 +516,7 @@ async function autoSimplePhaseIds() {
|
|
|
466
516
|
}
|
|
467
517
|
async function printAutoSimplePhasesHelp() {
|
|
468
518
|
const phaseLines = ["Available auto-simple phases:", "", ...(await autoSimplePhaseIds())];
|
|
469
|
-
phaseLines.push("", "You can run auto-simple with:", "agentweaver auto-simple <jira>");
|
|
519
|
+
phaseLines.push("", "You can run auto-simple with:", "agentweaver auto-simple [<jira>]");
|
|
470
520
|
printPanel("Auto-Simple Phases", phaseLines.join("\n"), "magenta");
|
|
471
521
|
}
|
|
472
522
|
function nextReviewIterationForTask(taskKey) {
|
|
@@ -486,13 +536,16 @@ function buildBaseConfig(command, options = {}) {
|
|
|
486
536
|
autoFromPhase: options.autoFromPhase ?? null,
|
|
487
537
|
mdLang: options.mdLang ?? null,
|
|
488
538
|
dryRun: options.dryRun ?? false,
|
|
539
|
+
dryRunFlow: options.dryRunFlow ?? false,
|
|
489
540
|
verbose: options.verbose ?? false,
|
|
490
541
|
...(options.doctorArgs !== undefined ? { doctorArgs: options.doctorArgs } : {}),
|
|
491
542
|
...(options.acceptPlaybookDraft !== undefined ? { acceptPlaybookDraft: options.acceptPlaybookDraft } : {}),
|
|
543
|
+
...(options.autoFlowSelection !== undefined ? { autoFlowSelection: options.autoFlowSelection } : {}),
|
|
492
544
|
};
|
|
493
545
|
}
|
|
494
546
|
function commandRequiresTask(command) {
|
|
495
|
-
return (command === "
|
|
547
|
+
return (command === "auto" ||
|
|
548
|
+
command === "plan-revise" ||
|
|
496
549
|
command === "bug-analyze" ||
|
|
497
550
|
command === "bug-fix" ||
|
|
498
551
|
command === "design-review" ||
|
|
@@ -504,6 +557,13 @@ function commandRequiresTask(command) {
|
|
|
504
557
|
command === "auto-status" ||
|
|
505
558
|
command === "auto-reset");
|
|
506
559
|
}
|
|
560
|
+
function commandSupportsManualTaskSource(command) {
|
|
561
|
+
return (command === "auto" ||
|
|
562
|
+
command === "auto-golang" ||
|
|
563
|
+
command === "auto-common-guided" ||
|
|
564
|
+
command === "auto-common" ||
|
|
565
|
+
command === "auto-simple");
|
|
566
|
+
}
|
|
507
567
|
function commandSupportsProjectScope(command) {
|
|
508
568
|
return (command === "plan" ||
|
|
509
569
|
command === "git-commit" ||
|
|
@@ -519,7 +579,21 @@ function commandSupportsProjectScope(command) {
|
|
|
519
579
|
command === "run-go-tests-loop" ||
|
|
520
580
|
command === "run-go-linter-loop");
|
|
521
581
|
}
|
|
522
|
-
|
|
582
|
+
function hasJiraConfig(config) {
|
|
583
|
+
return Boolean(config.scope.jiraRef && config.jiraBrowseUrl && config.jiraApiUrl && config.jiraTaskFile);
|
|
584
|
+
}
|
|
585
|
+
function syncJiraEnv(config) {
|
|
586
|
+
if (hasJiraConfig(config)) {
|
|
587
|
+
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl;
|
|
588
|
+
process.env.JIRA_API_URL = config.jiraApiUrl;
|
|
589
|
+
process.env.JIRA_TASK_FILE = config.jiraTaskFile;
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
delete process.env.JIRA_BROWSE_URL;
|
|
593
|
+
delete process.env.JIRA_API_URL;
|
|
594
|
+
delete process.env.JIRA_TASK_FILE;
|
|
595
|
+
}
|
|
596
|
+
async function resolveScopeForCommand(config, requestUserInput, launchMode) {
|
|
523
597
|
if (config.command === "instant-task") {
|
|
524
598
|
if (config.scopeName?.trim()) {
|
|
525
599
|
throw new TaskRunnerError("Command 'instant-task' rejects explicit scope overrides. The current branch-derived scope is the only supported lineage identity.");
|
|
@@ -537,13 +611,23 @@ async function resolveScopeForCommand(config, requestUserInput) {
|
|
|
537
611
|
}
|
|
538
612
|
if (commandRequiresTask(config.command)) {
|
|
539
613
|
try {
|
|
614
|
+
if (commandSupportsManualTaskSource(config.command)) {
|
|
615
|
+
if (launchMode === "resume" || launchMode === "continue") {
|
|
616
|
+
return resolveProjectScope(config.scopeName);
|
|
617
|
+
}
|
|
618
|
+
const jiraContext = await requestOptionalJiraContext(requestUserInput);
|
|
619
|
+
return resolveProjectScope(config.scopeName, jiraContext?.jiraRef ?? null);
|
|
620
|
+
}
|
|
540
621
|
const jiraContext = await requestJiraContext(requestUserInput);
|
|
541
622
|
return resolveProjectScope(config.scopeName, jiraContext.jiraRef);
|
|
542
623
|
}
|
|
543
624
|
catch (error) {
|
|
544
625
|
if (error instanceof TaskRunnerError && error.message.includes("no TTY is available")) {
|
|
545
|
-
throw new TaskRunnerError(
|
|
546
|
-
|
|
626
|
+
throw new TaskRunnerError(commandSupportsManualTaskSource(config.command)
|
|
627
|
+
? `Command '${config.command}' requires Jira input or a manual task description.\n` +
|
|
628
|
+
"Pass Jira issue key / browse URL as an argument, or run the command in an interactive terminal, leave Jira empty, and paste the task description in the next step."
|
|
629
|
+
: `Command '${config.command}' requires a Jira task.\n` +
|
|
630
|
+
"Pass Jira issue key / browse URL as an argument, or run the command in an interactive terminal.");
|
|
547
631
|
}
|
|
548
632
|
throw error;
|
|
549
633
|
}
|
|
@@ -611,9 +695,12 @@ function resolveExecutorPrerequisite(executor, registryContext) {
|
|
|
611
695
|
}
|
|
612
696
|
}
|
|
613
697
|
async function checkPrerequisites(config, launchProfile, executionRouting) {
|
|
698
|
+
const groups = await commandRoutingGroupsForPrerequisiteChecks(config.command, process.cwd());
|
|
699
|
+
await checkPrerequisitesForRoutingGroups(groups, launchProfile, executionRouting);
|
|
700
|
+
}
|
|
701
|
+
async function checkPrerequisitesForRoutingGroups(groups, launchProfile, executionRouting) {
|
|
614
702
|
const registryContext = await createPipelineRegistryContext(process.cwd());
|
|
615
703
|
const routing = routingForPrerequisites(launchProfile, executionRouting);
|
|
616
|
-
const groups = await commandRoutingGroupsForPrerequisiteChecks(config.command, process.cwd());
|
|
617
704
|
for (const executor of executorsForRoutingGroups(routing, groups)) {
|
|
618
705
|
resolveExecutorPrerequisite(executor, registryContext);
|
|
619
706
|
}
|
|
@@ -621,6 +708,124 @@ async function checkPrerequisites(config, launchProfile, executionRouting) {
|
|
|
621
708
|
async function checkAutoPrerequisites(config, launchProfile, executionRouting) {
|
|
622
709
|
await checkPrerequisites(config, launchProfile, executionRouting);
|
|
623
710
|
}
|
|
711
|
+
async function checkResolvedAutoPrerequisites(resolved, launchProfile, executionRouting) {
|
|
712
|
+
const groups = resolved.execution.kind === "built-in"
|
|
713
|
+
? await collectFlowRoutingGroups(await loadDeclarativeFlow({ source: "built-in", fileName: resolved.execution.specFile }), process.cwd())
|
|
714
|
+
: await collectFlowRoutingGroups(resolved.execution.flow, process.cwd(), new Set(), { inMemoryFlows: resolved.execution.inMemoryFlows });
|
|
715
|
+
await checkPrerequisitesForRoutingGroups(groups, launchProfile, executionRouting);
|
|
716
|
+
}
|
|
717
|
+
async function loadResolvedAutoFlowExecution(resolved) {
|
|
718
|
+
if (resolved.execution.kind === "built-in") {
|
|
719
|
+
return {
|
|
720
|
+
flow: await loadDeclarativeFlow({ source: "built-in", fileName: resolved.execution.specFile }),
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
return {
|
|
724
|
+
flow: resolved.execution.flow,
|
|
725
|
+
inMemoryFlows: resolved.execution.inMemoryFlows,
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
async function runResolvedAutoFlow(resolved, identity, config, overrides = {}, requestUserInput = requestUserInputInTerminal, setSummary, launchMode = "restart", runtime = runtimeServices, forceRefreshSummary = false) {
|
|
729
|
+
const { flow, inMemoryFlows } = await loadResolvedAutoFlowExecution(resolved);
|
|
730
|
+
const mergedFlowParams = {
|
|
731
|
+
...defaultDeclarativeFlowParams(config, false, overrides),
|
|
732
|
+
...autoFlowParams(config, forceRefreshSummary),
|
|
733
|
+
};
|
|
734
|
+
await runLoadedDeclarativeFlow(identity.flowId, flow, config, withCanonicalReviewLoopParams(flow.kind, mergedFlowParams), overrides, requestUserInput, setSummary, launchMode, runtime, inMemoryFlows);
|
|
735
|
+
}
|
|
736
|
+
function formatAutoFlowSource(resolved) {
|
|
737
|
+
const source = resolved.document.source;
|
|
738
|
+
if (source.type === "preset") {
|
|
739
|
+
return `preset ${source.preset}`;
|
|
740
|
+
}
|
|
741
|
+
const shadowed = source.shadowedUserPath ? ` (shadowed user config: ${source.shadowedUserPath})` : "";
|
|
742
|
+
return `${source.type} ${source.configName} at ${source.path}${shadowed}`;
|
|
743
|
+
}
|
|
744
|
+
function resolverArtifactStatusLines(resolved) {
|
|
745
|
+
const paths = resolved.document.artifactPolicy.artifactPaths;
|
|
746
|
+
if (!paths) {
|
|
747
|
+
return ["Resolver artifacts: not available for this scope"];
|
|
748
|
+
}
|
|
749
|
+
return [
|
|
750
|
+
"Resolver artifacts:",
|
|
751
|
+
` - flow-config.yaml: ${paths.flowConfigYaml}${existsSync(paths.flowConfigYaml) ? "" : " (missing)"}`,
|
|
752
|
+
` - resolved-flow.json: ${paths.resolvedFlowJson}${existsSync(paths.resolvedFlowJson) ? "" : " (missing)"}`,
|
|
753
|
+
` - resolved-flow-summary.json: ${paths.resolvedFlowSummaryJson}${existsSync(paths.resolvedFlowSummaryJson) ? "" : " (missing)"}`,
|
|
754
|
+
];
|
|
755
|
+
}
|
|
756
|
+
async function printConfigurableAutoStatus(config, resolved, identity) {
|
|
757
|
+
const stateFile = flowStateFile(config.scope.scopeKey, identity.flowId);
|
|
758
|
+
const state = loadFlowRunState(config.scope.scopeKey, identity.flowId);
|
|
759
|
+
const { flow } = await loadResolvedAutoFlowExecution(resolved);
|
|
760
|
+
const phaseLabels = new Map(resolved.document.phases.map((phase) => [phase.id, phase.label]));
|
|
761
|
+
const lines = [
|
|
762
|
+
`Issue: ${config.taskKey}`,
|
|
763
|
+
`Target: ${identity.displayLabel}`,
|
|
764
|
+
`Selected command: ${identity.selectedCommand}`,
|
|
765
|
+
`Effective flow ID: ${identity.flowId}`,
|
|
766
|
+
`Source: ${formatAutoFlowSource(resolved)}`,
|
|
767
|
+
`Base preset: ${resolved.document.basePreset}`,
|
|
768
|
+
`Execution target: ${resolved.document.executionTarget.kind}`,
|
|
769
|
+
`Resolver fingerprint: ${resolved.document.fingerprint}`,
|
|
770
|
+
`State file: ${stateFile}`,
|
|
771
|
+
];
|
|
772
|
+
if (!state) {
|
|
773
|
+
lines.push("", "Status: no saved state", "", "Phases:");
|
|
774
|
+
for (const phase of flow.phases) {
|
|
775
|
+
const label = phaseLabels.get(phase.id);
|
|
776
|
+
lines.push(` - ${phase.id}${label && label !== phase.id ? ` - ${label}` : ""}`);
|
|
777
|
+
}
|
|
778
|
+
lines.push("", ...resolverArtifactStatusLines(resolved));
|
|
779
|
+
printPanelSync("Auto Status", lines.join("\n"));
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
const currentStep = findCurrentFlowExecutionStep(state) ?? state.currentStep ?? "-";
|
|
783
|
+
lines.push("", `Status: ${state.status}`, `Current step: ${currentStep}`, `Updated: ${state.updatedAt}`, `Continuation: ${state.continuation?.continueEligible ? "eligible" : "not eligible"}`);
|
|
784
|
+
if (state.continuation?.stopPhaseId && state.continuation?.stopStepId) {
|
|
785
|
+
lines.push(`Stopped at: ${state.continuation.stopPhaseId}:${state.continuation.stopStepId}`);
|
|
786
|
+
}
|
|
787
|
+
if (state.selectedRoutingPreset) {
|
|
788
|
+
lines.push(`Routing preset: ${state.selectedRoutingPreset.label}`);
|
|
789
|
+
}
|
|
790
|
+
if (state.executionRouting) {
|
|
791
|
+
lines.push(`Default route: ${state.executionRouting.defaultRoute.executor} / ${state.executionRouting.defaultRoute.model}`);
|
|
792
|
+
lines.push(`Routing fingerprint: ${state.executionRouting.fingerprint}`);
|
|
793
|
+
}
|
|
794
|
+
else if (state.launchProfile) {
|
|
795
|
+
lines.push(`Launch profile: ${state.launchProfile.executor} / ${state.launchProfile.model}`);
|
|
796
|
+
}
|
|
797
|
+
if (state.lastError) {
|
|
798
|
+
lines.push(`Last error: ${state.lastError.step ?? "-"} (exit ${state.lastError.returnCode ?? "-"}, ${state.lastError.message ?? "-"})`);
|
|
799
|
+
}
|
|
800
|
+
lines.push("", "Phases:");
|
|
801
|
+
for (const phase of flow.phases) {
|
|
802
|
+
const phaseState = state.executionState.phases.find((candidate) => candidate.id === phase.id);
|
|
803
|
+
const label = phaseLabels.get(phase.id);
|
|
804
|
+
lines.push(`[${phaseState?.status ?? "pending"}] ${phase.id}${label && label !== phase.id ? ` - ${label}` : ""}`);
|
|
805
|
+
for (const step of phase.steps) {
|
|
806
|
+
const stepState = phaseState?.steps.find((candidate) => candidate.id === step.id);
|
|
807
|
+
lines.push(` - [${stepState?.status ?? "pending"}] ${step.id}`);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
if (state.executionState.terminated) {
|
|
811
|
+
lines.push("", `Execution terminated: ${state.executionState.terminationReason ?? "yes"}`);
|
|
812
|
+
}
|
|
813
|
+
lines.push("", ...resolverArtifactStatusLines(resolved));
|
|
814
|
+
printPanelSync("Auto Status", lines.join("\n"));
|
|
815
|
+
}
|
|
816
|
+
async function printConfigurableAutoReset(config, resolved, identity) {
|
|
817
|
+
const stateFile = flowStateFile(config.scope.scopeKey, identity.flowId);
|
|
818
|
+
const removed = resetFlowRunState(config.scope.scopeKey, identity.flowId);
|
|
819
|
+
const lines = [
|
|
820
|
+
`Issue: ${config.taskKey}`,
|
|
821
|
+
`Target: ${identity.displayLabel}`,
|
|
822
|
+
`Effective flow ID: ${identity.flowId}`,
|
|
823
|
+
`Source: ${formatAutoFlowSource(resolved)}`,
|
|
824
|
+
removed ? `State file ${stateFile} removed.` : `No flow state file found at ${stateFile}.`,
|
|
825
|
+
"Resolver artifacts were left unchanged.",
|
|
826
|
+
];
|
|
827
|
+
printPanelSync("Auto Reset", lines.join("\n"));
|
|
828
|
+
}
|
|
624
829
|
function autoFlowParams(config, forceRefreshSummary = false) {
|
|
625
830
|
return {
|
|
626
831
|
jiraApiUrl: config.jiraApiUrl,
|
|
@@ -711,6 +916,10 @@ function loadInstantTaskInputDefaults(taskKey) {
|
|
|
711
916
|
}
|
|
712
917
|
function interactiveFlowDefinition(entry) {
|
|
713
918
|
const flow = entry.flow;
|
|
919
|
+
const autoFlowSelection = autoFlowSelectionForFlowId(entry.id);
|
|
920
|
+
const autoFlow = autoFlowSelection?.kind === "preset"
|
|
921
|
+
? createPresetAutoFlowDefinition(autoFlowSelection.preset)
|
|
922
|
+
: null;
|
|
714
923
|
return {
|
|
715
924
|
id: entry.id,
|
|
716
925
|
label: entry.id,
|
|
@@ -718,6 +927,7 @@ function interactiveFlowDefinition(entry) {
|
|
|
718
927
|
source: entry.source,
|
|
719
928
|
treePath: [...entry.treePath],
|
|
720
929
|
...(entry.source !== "built-in" ? { sourcePath: entry.absolutePath } : {}),
|
|
930
|
+
...(autoFlow ? { autoFlow } : {}),
|
|
721
931
|
phases: flow.phases.map((phase) => ({
|
|
722
932
|
id: phase.id,
|
|
723
933
|
repeatVars: Object.fromEntries(Object.entries(phase.repeatVars).map(([key, value]) => [key, value])),
|
|
@@ -727,8 +937,40 @@ function interactiveFlowDefinition(entry) {
|
|
|
727
937
|
})),
|
|
728
938
|
};
|
|
729
939
|
}
|
|
730
|
-
function
|
|
731
|
-
|
|
940
|
+
function savedAutoFlowDefinitions(cwd) {
|
|
941
|
+
const definitions = [];
|
|
942
|
+
for (const item of listAutoFlowConfigs(cwd)) {
|
|
943
|
+
try {
|
|
944
|
+
const loaded = loadAutoFlowConfigByName(item.name, cwd);
|
|
945
|
+
definitions.push({
|
|
946
|
+
id: `auto-config:${loaded.config.name}`,
|
|
947
|
+
label: `auto-config:${loaded.config.name}`,
|
|
948
|
+
description: `Saved configurable auto-flow config '${loaded.config.name}'.`,
|
|
949
|
+
source: "built-in",
|
|
950
|
+
treePath: ["default", "auto-configs", loaded.config.name],
|
|
951
|
+
autoFlow: createConfigAutoFlowDefinition({
|
|
952
|
+
config: loaded.config,
|
|
953
|
+
source: {
|
|
954
|
+
type: loaded.source.type === "project" ? "project-config" : "user-config",
|
|
955
|
+
configName: loaded.config.name,
|
|
956
|
+
path: loaded.source.path,
|
|
957
|
+
...(loaded.source.shadowedUserPath ? { shadowedUserPath: loaded.source.shadowedUserPath } : {}),
|
|
958
|
+
},
|
|
959
|
+
}),
|
|
960
|
+
phases: [],
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
catch (error) {
|
|
964
|
+
printError(error.message);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
return definitions;
|
|
968
|
+
}
|
|
969
|
+
function interactiveFlowDefinitions(catalog, cwd = process.cwd()) {
|
|
970
|
+
return [
|
|
971
|
+
...catalog.map((entry) => interactiveFlowDefinition(entry)),
|
|
972
|
+
...savedAutoFlowDefinitions(cwd),
|
|
973
|
+
];
|
|
732
974
|
}
|
|
733
975
|
function publishFlowState(flowId, executionState) {
|
|
734
976
|
setFlowExecutionState(flowId, stripExecutionStatePayload(executionState));
|
|
@@ -766,7 +1008,7 @@ function findCurrentFlowExecutionStep(state) {
|
|
|
766
1008
|
}
|
|
767
1009
|
return null;
|
|
768
1010
|
}
|
|
769
|
-
async function
|
|
1011
|
+
async function runLoadedDeclarativeFlow(flowId, flow, config, flowParams, overrides = {}, requestUserInput = requestUserInputInTerminal, setSummary, launchMode = "restart", runtime = runtimeServices, inMemoryFlows) {
|
|
770
1012
|
const context = await createPipelineContext({
|
|
771
1013
|
issueKey: config.taskKey,
|
|
772
1014
|
jiraRef: config.jiraRef,
|
|
@@ -777,8 +1019,8 @@ async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, over
|
|
|
777
1019
|
...(setSummary ? { setSummary } : {}),
|
|
778
1020
|
requestUserInput,
|
|
779
1021
|
...(overrides.executionRouting ? { executionRouting: overrides.executionRouting } : {}),
|
|
1022
|
+
...(inMemoryFlows ? { inMemoryFlows } : {}),
|
|
780
1023
|
});
|
|
781
|
-
const flow = await loadDeclarativeFlow(flowRef);
|
|
782
1024
|
const initialExecutionState = {
|
|
783
1025
|
flowKind: flow.kind,
|
|
784
1026
|
flowVersion: flow.version,
|
|
@@ -793,7 +1035,7 @@ async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, over
|
|
|
793
1035
|
if (persistedState && launchMode === "resume") {
|
|
794
1036
|
await validateDeclarativeFlowResumeState({
|
|
795
1037
|
id: flowId,
|
|
796
|
-
source: flow.source,
|
|
1038
|
+
source: flow.source === "generated" ? "built-in" : flow.source,
|
|
797
1039
|
fileName: flow.fileName,
|
|
798
1040
|
absolutePath: flow.absolutePath,
|
|
799
1041
|
treePath: [],
|
|
@@ -808,14 +1050,14 @@ async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, over
|
|
|
808
1050
|
persistedState = prepareFlowStateForContinue(persistedState, flow.phases);
|
|
809
1051
|
}
|
|
810
1052
|
else if (launchMode === "restart") {
|
|
811
|
-
if (existingStateForRestart) {
|
|
1053
|
+
if (existingStateForRestart && isRestartArchivingFlowId(flowId)) {
|
|
812
1054
|
archiveActiveAttempt(config.scope.scopeKey);
|
|
813
1055
|
}
|
|
814
1056
|
resetFlowRunState(config.scope.scopeKey, flowId);
|
|
815
1057
|
}
|
|
816
1058
|
const executionState = persistedState?.executionState ?? initialExecutionState;
|
|
817
1059
|
const state = persistedState
|
|
818
|
-
?? createFlowRunState(config.scope.scopeKey, flowId, executionState, config.jiraRef, overrides.launchProfile, overrides.executionRouting, overrides.selectedRoutingPreset);
|
|
1060
|
+
?? createFlowRunState(config.scope.scopeKey, flowId, executionState, config.scope.jiraRef ?? null, overrides.launchProfile, overrides.executionRouting, overrides.selectedRoutingPreset);
|
|
819
1061
|
if (overrides.executionRouting) {
|
|
820
1062
|
state.executionRouting = overrides.executionRouting;
|
|
821
1063
|
state.routingFingerprint = overrides.executionRouting.fingerprint;
|
|
@@ -877,6 +1119,10 @@ async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, over
|
|
|
877
1119
|
throw error;
|
|
878
1120
|
}
|
|
879
1121
|
}
|
|
1122
|
+
async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, overrides = {}, requestUserInput = requestUserInputInTerminal, setSummary, launchMode = "restart", runtime = runtimeServices) {
|
|
1123
|
+
const flow = await loadDeclarativeFlow(flowRef);
|
|
1124
|
+
await runLoadedDeclarativeFlow(flowId, flow, config, flowParams, overrides, requestUserInput, setSummary, launchMode, runtime);
|
|
1125
|
+
}
|
|
880
1126
|
async function runDeclarativeFlowBySpecFile(fileName, config, flowParams, overrides = {}, requestUserInput = requestUserInputInTerminal, setSummary, launchMode = "restart", runtime = runtimeServices) {
|
|
881
1127
|
const mergedFlowParams = {
|
|
882
1128
|
...defaultDeclarativeFlowParams(config, false, overrides),
|
|
@@ -1020,7 +1266,7 @@ async function summarizeBuildFailure(output) {
|
|
|
1020
1266
|
}), output);
|
|
1021
1267
|
}
|
|
1022
1268
|
function requireJiraConfig(config) {
|
|
1023
|
-
if (!config
|
|
1269
|
+
if (!hasJiraConfig(config)) {
|
|
1024
1270
|
throw new TaskRunnerError(`Command '${config.command}' requires Jira context in the current project scope.`);
|
|
1025
1271
|
}
|
|
1026
1272
|
}
|
|
@@ -1029,7 +1275,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
1029
1275
|
const exitCode = await runDoctorCommand(baseConfig.doctorArgs ?? []);
|
|
1030
1276
|
return exitCode === 0;
|
|
1031
1277
|
}
|
|
1032
|
-
const config = buildRuntimeConfig(baseConfig, resolvedScope ?? (await resolveScopeForCommand(baseConfig, requestUserInput)));
|
|
1278
|
+
const config = buildRuntimeConfig(baseConfig, resolvedScope ?? (await resolveScopeForCommand(baseConfig, requestUserInput, explicitLaunchMode)));
|
|
1033
1279
|
const flowOverrides = executionRouting
|
|
1034
1280
|
? {
|
|
1035
1281
|
launchProfile: executionRouting.defaultRoute,
|
|
@@ -1039,9 +1285,45 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
1039
1285
|
: launchProfile
|
|
1040
1286
|
? { launchProfile }
|
|
1041
1287
|
: {};
|
|
1042
|
-
const
|
|
1043
|
-
|
|
1044
|
-
|
|
1288
|
+
const configurableAutoSelection = config.autoFlowSelection
|
|
1289
|
+
?? (config.command === "auto" || config.command === "auto-status" || config.command === "auto-reset"
|
|
1290
|
+
? defaultAutoFlowSelection()
|
|
1291
|
+
: undefined);
|
|
1292
|
+
const resolvedConfigurableAuto = configurableAutoSelection
|
|
1293
|
+
? await resolveAutoFlow(configurableAutoSelection, {
|
|
1294
|
+
cwd: process.cwd(),
|
|
1295
|
+
scopeKey: config.scope.scopeKey,
|
|
1296
|
+
})
|
|
1297
|
+
: null;
|
|
1298
|
+
const configurableAutoIdentity = resolvedConfigurableAuto && configurableAutoSelection
|
|
1299
|
+
? autoFlowIdentityForSelection(configurableAutoSelection, resolvedConfigurableAuto)
|
|
1300
|
+
: null;
|
|
1301
|
+
if (resolvedConfigurableAuto && configurableAutoIdentity) {
|
|
1302
|
+
if (config.command !== "auto-status" && config.command !== "auto-reset") {
|
|
1303
|
+
config.command = configurableAutoIdentity.selectedCommand;
|
|
1304
|
+
}
|
|
1305
|
+
if (config.dryRunFlow) {
|
|
1306
|
+
writeStdoutSync(formatAutoFlowDryRunPreview(resolvedConfigurableAuto));
|
|
1307
|
+
return false;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
if (resolvedConfigurableAuto && configurableAutoIdentity && config.command === "auto-status") {
|
|
1311
|
+
await printConfigurableAutoStatus(config, resolvedConfigurableAuto, configurableAutoIdentity);
|
|
1312
|
+
return false;
|
|
1313
|
+
}
|
|
1314
|
+
if (resolvedConfigurableAuto && configurableAutoIdentity && config.command === "auto-reset") {
|
|
1315
|
+
await printConfigurableAutoReset(config, resolvedConfigurableAuto, configurableAutoIdentity);
|
|
1316
|
+
return false;
|
|
1317
|
+
}
|
|
1318
|
+
const launchFlowId = configurableAutoIdentity?.flowId ?? config.command;
|
|
1319
|
+
const launchMode = await chooseLaunchMode(launchFlowId, config.scope.scopeKey, explicitLaunchMode, requestUserInput);
|
|
1320
|
+
if (resolvedConfigurableAuto && configurableAutoIdentity) {
|
|
1321
|
+
syncJiraEnv(config);
|
|
1322
|
+
await checkResolvedAutoPrerequisites(resolvedConfigurableAuto, launchProfile, executionRouting);
|
|
1323
|
+
persistResolvedAutoFlowArtifacts(config.scope.scopeKey, resolvedConfigurableAuto);
|
|
1324
|
+
await runResolvedAutoFlow(resolvedConfigurableAuto, configurableAutoIdentity, config, flowOverrides, requestUserInput, setSummary, launchMode, runtime, forceRefreshSummary);
|
|
1325
|
+
return false;
|
|
1326
|
+
}
|
|
1045
1327
|
if (config.command === "instant-task") {
|
|
1046
1328
|
await checkPrerequisites(config, launchProfile, executionRouting);
|
|
1047
1329
|
const hasPersistedInstantTaskState = loadFlowRunState(config.scope.scopeKey, "instant-task") !== null;
|
|
@@ -1064,10 +1346,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
1064
1346
|
return !config.dryRun && existsSync(readyToMergeFile(config.taskKey));
|
|
1065
1347
|
}
|
|
1066
1348
|
if (config.command === "auto-golang") {
|
|
1067
|
-
|
|
1068
|
-
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl;
|
|
1069
|
-
process.env.JIRA_API_URL = config.jiraApiUrl;
|
|
1070
|
-
process.env.JIRA_TASK_FILE = config.jiraTaskFile;
|
|
1349
|
+
syncJiraEnv(config);
|
|
1071
1350
|
let effectiveLaunchMode = launchMode;
|
|
1072
1351
|
let effectiveLaunchProfile = launchProfile;
|
|
1073
1352
|
let effectiveExecutionRouting = executionRouting;
|
|
@@ -1098,81 +1377,22 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
1098
1377
|
return false;
|
|
1099
1378
|
}
|
|
1100
1379
|
if (config.command === "auto-common" || config.command === "auto-common-guided") {
|
|
1101
|
-
requireJiraConfig(config);
|
|
1102
1380
|
await checkAutoPrerequisites(config, launchProfile, executionRouting);
|
|
1103
|
-
|
|
1104
|
-
process.env.JIRA_API_URL = config.jiraApiUrl;
|
|
1105
|
-
process.env.JIRA_TASK_FILE = config.jiraTaskFile;
|
|
1381
|
+
syncJiraEnv(config);
|
|
1106
1382
|
await runDeclarativeFlowBySpecFile(config.command === "auto-common-guided" ? "auto-common-guided.json" : "auto-common.json", config, autoFlowParams(config, forceRefreshSummary), flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
1107
1383
|
return false;
|
|
1108
1384
|
}
|
|
1109
1385
|
if (config.command === "auto-simple") {
|
|
1110
|
-
requireJiraConfig(config);
|
|
1111
1386
|
await checkAutoPrerequisites(config, launchProfile, executionRouting);
|
|
1112
|
-
|
|
1113
|
-
process.env.JIRA_API_URL = config.jiraApiUrl;
|
|
1114
|
-
process.env.JIRA_TASK_FILE = config.jiraTaskFile;
|
|
1387
|
+
syncJiraEnv(config);
|
|
1115
1388
|
await runDeclarativeFlowBySpecFile("auto-simple.json", config, autoFlowParams(config, forceRefreshSummary), flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
1116
1389
|
return false;
|
|
1117
1390
|
}
|
|
1118
|
-
if (config.command === "auto-status") {
|
|
1119
|
-
const state = loadFlowRunState(config.scope.scopeKey, "auto-golang");
|
|
1120
|
-
if (!state) {
|
|
1121
|
-
printPanel("Auto-Golang Status", `No flow state file found for ${config.taskKey}.`, "yellow");
|
|
1122
|
-
return false;
|
|
1123
|
-
}
|
|
1124
|
-
const currentStep = findCurrentFlowExecutionStep(state) ?? state.currentStep ?? "-";
|
|
1125
|
-
const phaseOrder = (await loadDeclarativeFlow({ source: "built-in", fileName: "auto-golang.json" })).phases;
|
|
1126
|
-
const lines = [
|
|
1127
|
-
`Issue: ${config.taskKey}`,
|
|
1128
|
-
`Status: ${state.status}`,
|
|
1129
|
-
`Current step: ${currentStep}`,
|
|
1130
|
-
`Updated: ${state.updatedAt}`,
|
|
1131
|
-
];
|
|
1132
|
-
if (state.executionRouting) {
|
|
1133
|
-
lines.push(`Default route: ${state.executionRouting.defaultRoute.executor} / ${state.executionRouting.defaultRoute.model}`);
|
|
1134
|
-
lines.push(`Routing fingerprint: ${state.executionRouting.fingerprint}`);
|
|
1135
|
-
}
|
|
1136
|
-
else if (state.launchProfile) {
|
|
1137
|
-
lines.push(`Launch profile: ${state.launchProfile.executor} / ${state.launchProfile.model}`);
|
|
1138
|
-
}
|
|
1139
|
-
if (state.lastError) {
|
|
1140
|
-
lines.push(`Last error: ${state.lastError.step ?? "-"} (exit ${state.lastError.returnCode ?? "-"}, ${state.lastError.message ?? "-"})`);
|
|
1141
|
-
}
|
|
1142
|
-
lines.push("");
|
|
1143
|
-
for (const phase of phaseOrder) {
|
|
1144
|
-
const phaseState = state.executionState.phases.find((candidate) => candidate.id === phase.id);
|
|
1145
|
-
lines.push(`[${phaseState?.status ?? "pending"}] ${phase.id}`);
|
|
1146
|
-
for (const step of phase.steps) {
|
|
1147
|
-
const stepState = phaseState?.steps.find((candidate) => candidate.id === step.id);
|
|
1148
|
-
lines.push(` - [${stepState?.status ?? "pending"}] ${step.id}`);
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
if (state.executionState.terminated) {
|
|
1152
|
-
lines.push("", `Execution terminated: ${state.executionState.terminationReason ?? "yes"}`);
|
|
1153
|
-
}
|
|
1154
|
-
printPanel("Auto-Golang Status", lines.join("\n"), "cyan");
|
|
1155
|
-
return false;
|
|
1156
|
-
}
|
|
1157
|
-
if (config.command === "auto-reset") {
|
|
1158
|
-
const removed = resetFlowRunState(config.scope.scopeKey, "auto-golang");
|
|
1159
|
-
printPanel("Auto-Golang Reset", removed ? `State file ${flowStateFile(config.scope.scopeKey, "auto-golang")} removed.` : "No flow state file found.", "yellow");
|
|
1160
|
-
return false;
|
|
1161
|
-
}
|
|
1162
1391
|
await checkPrerequisites(config, launchProfile, executionRouting);
|
|
1163
|
-
|
|
1164
|
-
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl ?? "";
|
|
1165
|
-
process.env.JIRA_API_URL = config.jiraApiUrl ?? "";
|
|
1166
|
-
process.env.JIRA_TASK_FILE = config.jiraTaskFile ?? "";
|
|
1167
|
-
}
|
|
1168
|
-
else {
|
|
1169
|
-
delete process.env.JIRA_BROWSE_URL;
|
|
1170
|
-
delete process.env.JIRA_API_URL;
|
|
1171
|
-
delete process.env.JIRA_TASK_FILE;
|
|
1172
|
-
}
|
|
1392
|
+
syncJiraEnv(config);
|
|
1173
1393
|
if (config.command === "plan") {
|
|
1174
1394
|
let taskContextIteration;
|
|
1175
|
-
if (config
|
|
1395
|
+
if (hasJiraConfig(config)) {
|
|
1176
1396
|
requireJiraConfig(config);
|
|
1177
1397
|
if (config.verbose) {
|
|
1178
1398
|
process.stdout.write(`Fetching Jira issue from browse URL: ${config.jiraBrowseUrl}\n`);
|
|
@@ -1191,7 +1411,21 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
1191
1411
|
}, flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
1192
1412
|
}
|
|
1193
1413
|
else {
|
|
1194
|
-
|
|
1414
|
+
const latestTaskContext = latestArtifactIteration(config.taskKey, "task-context", "json");
|
|
1415
|
+
if (latestTaskContext !== null) {
|
|
1416
|
+
taskContextIteration = latestTaskContext;
|
|
1417
|
+
}
|
|
1418
|
+
else {
|
|
1419
|
+
taskContextIteration = nextArtifactIteration(config.taskKey, "task-context", "json");
|
|
1420
|
+
await runDeclarativeFlowBySpecFile("task-source/manual-jira-input.json", config, {
|
|
1421
|
+
taskKey: config.taskKey,
|
|
1422
|
+
}, flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
1423
|
+
await runDeclarativeFlowBySpecFile("normalize-task-source.json", config, {
|
|
1424
|
+
taskKey: config.taskKey,
|
|
1425
|
+
iteration: taskContextIteration,
|
|
1426
|
+
extraPrompt: config.extraPrompt,
|
|
1427
|
+
}, flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
1428
|
+
}
|
|
1195
1429
|
}
|
|
1196
1430
|
await runDeclarativeFlowBySpecFile("plan.json", config, {
|
|
1197
1431
|
taskKey: config.taskKey,
|
|
@@ -1489,8 +1723,10 @@ async function parseCliArgs(argv) {
|
|
|
1489
1723
|
writeStderrSync(`${usage()}\n`);
|
|
1490
1724
|
process.exit(1);
|
|
1491
1725
|
}
|
|
1492
|
-
const
|
|
1726
|
+
const isConfigurableAutoCommand = rawCommand === "auto";
|
|
1727
|
+
const supportsAutoFlowSelection = rawCommand === "auto" || rawCommand === "auto-status" || rawCommand === "auto-reset";
|
|
1493
1728
|
let dry = false;
|
|
1729
|
+
let dryRunFlow = false;
|
|
1494
1730
|
let verbose = false;
|
|
1495
1731
|
let prompt;
|
|
1496
1732
|
let autoFromPhase;
|
|
@@ -1501,9 +1737,26 @@ async function parseCliArgs(argv) {
|
|
|
1501
1737
|
let mdLang;
|
|
1502
1738
|
let launchMode;
|
|
1503
1739
|
let acceptPlaybookDraft = false;
|
|
1740
|
+
let autoPreset;
|
|
1741
|
+
let autoConfigName;
|
|
1504
1742
|
let webNoOpen = process.env.AGENTWEAVER_WEB_NO_OPEN === "1";
|
|
1505
1743
|
let webHost;
|
|
1506
1744
|
const doctorArgs = [];
|
|
1745
|
+
const readRequiredValue = (flag, index) => {
|
|
1746
|
+
const value = argv[index + 1]?.trim();
|
|
1747
|
+
if (!value || value.startsWith("-")) {
|
|
1748
|
+
writeStderrSync(`Error: ${flag} requires a value.\n`);
|
|
1749
|
+
process.exit(1);
|
|
1750
|
+
}
|
|
1751
|
+
return value;
|
|
1752
|
+
};
|
|
1753
|
+
const parsePresetValue = (value) => {
|
|
1754
|
+
if (value === "simple" || value === "standard") {
|
|
1755
|
+
return value;
|
|
1756
|
+
}
|
|
1757
|
+
writeStderrSync("Error: --preset accepts only 'simple' or 'standard'.\n");
|
|
1758
|
+
process.exit(1);
|
|
1759
|
+
};
|
|
1507
1760
|
for (let index = 1; index < argv.length; index += 1) {
|
|
1508
1761
|
const token = argv[index] ?? "";
|
|
1509
1762
|
if (token === "--dry") {
|
|
@@ -1514,6 +1767,58 @@ async function parseCliArgs(argv) {
|
|
|
1514
1767
|
verbose = true;
|
|
1515
1768
|
continue;
|
|
1516
1769
|
}
|
|
1770
|
+
if (token === "--dry-run-flow") {
|
|
1771
|
+
if (!isConfigurableAutoCommand) {
|
|
1772
|
+
writeStderrSync("Error: --dry-run-flow is only supported after the auto command.\n");
|
|
1773
|
+
process.exit(1);
|
|
1774
|
+
}
|
|
1775
|
+
dryRunFlow = true;
|
|
1776
|
+
continue;
|
|
1777
|
+
}
|
|
1778
|
+
if (token === "--preset") {
|
|
1779
|
+
if (!supportsAutoFlowSelection) {
|
|
1780
|
+
writeStderrSync("Error: --preset is only supported after auto, auto-status, or auto-reset.\n");
|
|
1781
|
+
process.exit(1);
|
|
1782
|
+
}
|
|
1783
|
+
autoPreset = parsePresetValue(readRequiredValue("--preset", index));
|
|
1784
|
+
index += 1;
|
|
1785
|
+
continue;
|
|
1786
|
+
}
|
|
1787
|
+
if (token.startsWith("--preset=")) {
|
|
1788
|
+
if (!supportsAutoFlowSelection) {
|
|
1789
|
+
writeStderrSync("Error: --preset is only supported after auto, auto-status, or auto-reset.\n");
|
|
1790
|
+
process.exit(1);
|
|
1791
|
+
}
|
|
1792
|
+
const value = token.slice("--preset=".length).trim();
|
|
1793
|
+
if (!value) {
|
|
1794
|
+
writeStderrSync("Error: --preset requires a value.\n");
|
|
1795
|
+
process.exit(1);
|
|
1796
|
+
}
|
|
1797
|
+
autoPreset = parsePresetValue(value);
|
|
1798
|
+
continue;
|
|
1799
|
+
}
|
|
1800
|
+
if (token === "--config") {
|
|
1801
|
+
if (!supportsAutoFlowSelection) {
|
|
1802
|
+
writeStderrSync("Error: --config is only supported after auto, auto-status, or auto-reset.\n");
|
|
1803
|
+
process.exit(1);
|
|
1804
|
+
}
|
|
1805
|
+
autoConfigName = readRequiredValue("--config", index);
|
|
1806
|
+
index += 1;
|
|
1807
|
+
continue;
|
|
1808
|
+
}
|
|
1809
|
+
if (token.startsWith("--config=")) {
|
|
1810
|
+
if (!supportsAutoFlowSelection) {
|
|
1811
|
+
writeStderrSync("Error: --config is only supported after auto, auto-status, or auto-reset.\n");
|
|
1812
|
+
process.exit(1);
|
|
1813
|
+
}
|
|
1814
|
+
const value = token.slice("--config=".length).trim();
|
|
1815
|
+
if (!value) {
|
|
1816
|
+
writeStderrSync("Error: --config requires a value.\n");
|
|
1817
|
+
process.exit(1);
|
|
1818
|
+
}
|
|
1819
|
+
autoConfigName = value;
|
|
1820
|
+
continue;
|
|
1821
|
+
}
|
|
1517
1822
|
if (token === "--help-phases") {
|
|
1518
1823
|
helpPhases = true;
|
|
1519
1824
|
continue;
|
|
@@ -1523,7 +1828,7 @@ async function parseCliArgs(argv) {
|
|
|
1523
1828
|
continue;
|
|
1524
1829
|
}
|
|
1525
1830
|
if (token === "--no-open") {
|
|
1526
|
-
if (
|
|
1831
|
+
if (rawCommand !== "web") {
|
|
1527
1832
|
writeStderrSync("Error: --no-open is only supported after the web command.\n");
|
|
1528
1833
|
process.exit(1);
|
|
1529
1834
|
}
|
|
@@ -1531,7 +1836,7 @@ async function parseCliArgs(argv) {
|
|
|
1531
1836
|
continue;
|
|
1532
1837
|
}
|
|
1533
1838
|
if (token === "--listen-all") {
|
|
1534
|
-
if (
|
|
1839
|
+
if (rawCommand !== "web") {
|
|
1535
1840
|
writeStderrSync("Error: --listen-all is only supported after the web command.\n");
|
|
1536
1841
|
process.exit(1);
|
|
1537
1842
|
}
|
|
@@ -1539,7 +1844,7 @@ async function parseCliArgs(argv) {
|
|
|
1539
1844
|
continue;
|
|
1540
1845
|
}
|
|
1541
1846
|
if (token === "--host") {
|
|
1542
|
-
if (
|
|
1847
|
+
if (rawCommand !== "web") {
|
|
1543
1848
|
writeStderrSync("Error: --host is only supported after the web command.\n");
|
|
1544
1849
|
process.exit(1);
|
|
1545
1850
|
}
|
|
@@ -1553,7 +1858,7 @@ async function parseCliArgs(argv) {
|
|
|
1553
1858
|
continue;
|
|
1554
1859
|
}
|
|
1555
1860
|
if (token.startsWith("--host=")) {
|
|
1556
|
-
if (
|
|
1861
|
+
if (rawCommand !== "web") {
|
|
1557
1862
|
writeStderrSync("Error: --host is only supported after the web command.\n");
|
|
1558
1863
|
process.exit(1);
|
|
1559
1864
|
}
|
|
@@ -1620,13 +1925,29 @@ async function parseCliArgs(argv) {
|
|
|
1620
1925
|
}
|
|
1621
1926
|
continue;
|
|
1622
1927
|
}
|
|
1623
|
-
if (
|
|
1928
|
+
if (rawCommand === "doctor") {
|
|
1624
1929
|
doctorArgs.push(token);
|
|
1625
1930
|
}
|
|
1626
1931
|
else {
|
|
1627
1932
|
jiraRef = token;
|
|
1628
1933
|
}
|
|
1629
1934
|
}
|
|
1935
|
+
if (autoPreset && autoConfigName) {
|
|
1936
|
+
writeStderrSync("Error: --preset and --config are mutually exclusive.\n");
|
|
1937
|
+
process.exit(1);
|
|
1938
|
+
}
|
|
1939
|
+
const autoFlowSelection = supportsAutoFlowSelection
|
|
1940
|
+
? autoConfigName
|
|
1941
|
+
? { kind: "config", name: autoConfigName }
|
|
1942
|
+
: autoPreset
|
|
1943
|
+
? { kind: "preset", preset: autoPreset }
|
|
1944
|
+
: defaultAutoFlowSelection()
|
|
1945
|
+
: undefined;
|
|
1946
|
+
const command = isConfigurableAutoCommand
|
|
1947
|
+
? autoFlowSelection?.kind === "preset" && autoFlowSelection.preset === "simple"
|
|
1948
|
+
? "auto-simple"
|
|
1949
|
+
: "auto-common"
|
|
1950
|
+
: rawCommand;
|
|
1630
1951
|
if (command === "auto-golang" && helpPhases) {
|
|
1631
1952
|
await printAutoPhasesHelp();
|
|
1632
1953
|
process.exit(0);
|
|
@@ -1642,6 +1963,7 @@ async function parseCliArgs(argv) {
|
|
|
1642
1963
|
return {
|
|
1643
1964
|
command: command,
|
|
1644
1965
|
dry,
|
|
1966
|
+
dryRunFlow,
|
|
1645
1967
|
verbose,
|
|
1646
1968
|
helpPhases,
|
|
1647
1969
|
...(jiraRef !== undefined ? { jiraRef } : {}),
|
|
@@ -1653,6 +1975,7 @@ async function parseCliArgs(argv) {
|
|
|
1653
1975
|
...(doctorArgs.length > 0 ? { doctorArgs } : {}),
|
|
1654
1976
|
...(launchMode !== undefined ? { launchMode } : {}),
|
|
1655
1977
|
...(acceptPlaybookDraft ? { acceptPlaybookDraft } : {}),
|
|
1978
|
+
...(autoFlowSelection !== undefined ? { autoFlowSelection } : {}),
|
|
1656
1979
|
...(command === "web" ? { webNoOpen } : {}),
|
|
1657
1980
|
...(command === "web" && webHost !== undefined ? { webHost } : {}),
|
|
1658
1981
|
};
|
|
@@ -1666,9 +1989,11 @@ function buildConfigFromArgs(args) {
|
|
|
1666
1989
|
...(args.autoFromPhase !== undefined ? { autoFromPhase: args.autoFromPhase } : {}),
|
|
1667
1990
|
...(args.mdLang !== undefined ? { mdLang: args.mdLang } : {}),
|
|
1668
1991
|
dryRun: args.dry,
|
|
1992
|
+
dryRunFlow: args.dryRunFlow,
|
|
1669
1993
|
verbose: args.verbose,
|
|
1670
1994
|
...(args.doctorArgs !== undefined ? { doctorArgs: args.doctorArgs } : {}),
|
|
1671
1995
|
...(args.acceptPlaybookDraft !== undefined ? { acceptPlaybookDraft: args.acceptPlaybookDraft } : {}),
|
|
1996
|
+
...(args.autoFlowSelection !== undefined ? { autoFlowSelection: args.autoFlowSelection } : {}),
|
|
1672
1997
|
});
|
|
1673
1998
|
}
|
|
1674
1999
|
async function runInteractiveWithSessionFactory(createSession, jiraRef, forceRefresh = false, scopeName, installSignalCleanup = false) {
|
|
@@ -1728,6 +2053,10 @@ async function runInteractiveWithSessionFactory(createSession, jiraRef, forceRef
|
|
|
1728
2053
|
flows: interactiveFlowDefinitions(flowCatalog),
|
|
1729
2054
|
getRunConfirmation: async (flowId) => {
|
|
1730
2055
|
refreshScopeFromGit("git scope refresh before launch confirmation");
|
|
2056
|
+
const autoFlowSelection = autoFlowSelectionForFlowId(flowId);
|
|
2057
|
+
if (autoFlowSelection) {
|
|
2058
|
+
return await lookupInteractiveAutoFlowResume(autoFlowSelection, currentScope);
|
|
2059
|
+
}
|
|
1731
2060
|
const flowEntry = findCatalogEntry(flowId, flowCatalog);
|
|
1732
2061
|
if (!flowEntry) {
|
|
1733
2062
|
throw new TaskRunnerError(`Unknown flow: ${flowId}`);
|
|
@@ -1741,6 +2070,19 @@ async function runInteractiveWithSessionFactory(createSession, jiraRef, forceRef
|
|
|
1741
2070
|
activeAbortController = abortController;
|
|
1742
2071
|
activeFlowId = flowId;
|
|
1743
2072
|
try {
|
|
2073
|
+
const autoFlowSelection = autoFlowSelectionForFlowId(flowId);
|
|
2074
|
+
if (autoFlowSelection) {
|
|
2075
|
+
const previousScopeKey = currentScope.scopeKey;
|
|
2076
|
+
const baseConfig = buildInteractiveBaseConfig(flowId, currentScope);
|
|
2077
|
+
const nextScope = await resolveScopeForCommand(baseConfig, (form) => ui.requestUserInput(form), launchMode);
|
|
2078
|
+
currentScope = nextScope;
|
|
2079
|
+
ui.setScope(currentScope.scopeKey, currentScope.jiraIssueKey ?? null, currentScope.gitBranchName);
|
|
2080
|
+
if (previousScopeKey !== currentScope.scopeKey || currentScope.jiraIssueKey) {
|
|
2081
|
+
syncInteractiveTaskSummary(ui, currentScope, forceRefresh);
|
|
2082
|
+
}
|
|
2083
|
+
await executeCommand(baseConfig, true, (form) => ui.requestUserInput(form), currentScope, (markdown) => ui.setSummary(markdown), forceRefresh, launchMode, undefined, undefined, undefined, createRuntimeServices(abortController.signal));
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
1744
2086
|
const flowEntry = findCatalogEntry(flowId, flowCatalog);
|
|
1745
2087
|
if (!flowEntry) {
|
|
1746
2088
|
throw new TaskRunnerError(`Unknown flow: ${flowId}`);
|
|
@@ -1765,7 +2107,7 @@ async function runInteractiveWithSessionFactory(createSession, jiraRef, forceRef
|
|
|
1765
2107
|
const previousScopeKey = currentScope.scopeKey;
|
|
1766
2108
|
const baseConfig = buildInteractiveBaseConfig(flowId, currentScope);
|
|
1767
2109
|
if (flowEntry.source === "built-in" && isBuiltInCommandFlowId(flowId)) {
|
|
1768
|
-
const nextScope = await resolveScopeForCommand(baseConfig, (form) => ui.requestUserInput(form));
|
|
2110
|
+
const nextScope = await resolveScopeForCommand(baseConfig, (form) => ui.requestUserInput(form), launchMode);
|
|
1769
2111
|
currentScope = nextScope;
|
|
1770
2112
|
}
|
|
1771
2113
|
else if (flowRequiresTaskScope(flowEntry) && !currentScope.jiraRef) {
|