agentweaver 0.1.9 → 0.1.10
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 +60 -28
- package/dist/artifacts.js +1 -1
- package/dist/errors.js +7 -0
- package/dist/index.js +66 -34
- package/dist/interactive-ui.js +351 -44
- package/dist/pipeline/declarative-flows.js +7 -4
- package/dist/pipeline/flow-catalog.js +28 -21
- package/dist/pipeline/flow-specs/opencode/auto-opencode.json +1365 -0
- package/dist/pipeline/flow-specs/opencode/bugz/bug-analyze-opencode.json +382 -0
- package/dist/pipeline/flow-specs/opencode/bugz/bug-fix-opencode.json +56 -0
- package/dist/pipeline/flow-specs/opencode/gitlab/gitlab-diff-review-opencode.json +308 -0
- package/dist/pipeline/flow-specs/opencode/gitlab/gitlab-review-opencode.json +437 -0
- package/dist/pipeline/flow-specs/opencode/gitlab/mr-description-opencode.json +117 -0
- package/dist/pipeline/flow-specs/opencode/go/run-go-linter-loop-opencode.json +321 -0
- package/dist/pipeline/flow-specs/opencode/go/run-go-tests-loop-opencode.json +321 -0
- package/dist/pipeline/flow-specs/opencode/implement-opencode.json +64 -0
- package/dist/pipeline/flow-specs/{plan-opencode.json → opencode/plan-opencode.json} +4 -4
- package/dist/pipeline/flow-specs/opencode/review/review-fix-opencode.json +209 -0
- package/dist/pipeline/flow-specs/opencode/review/review-opencode.json +452 -0
- package/dist/pipeline/flow-specs/opencode/task-describe-opencode.json +148 -0
- package/dist/pipeline/spec-loader.js +18 -7
- package/dist/runtime/process-runner.js +45 -1
- package/package.json +1 -1
- package/dist/pipeline/flow-specs/preflight.json +0 -206
- package/dist/pipeline/flow-specs/run-linter-loop.json +0 -155
- package/dist/pipeline/flow-specs/run-tests-loop.json +0 -155
package/README.md
CHANGED
|
@@ -1,37 +1,53 @@
|
|
|
1
1
|
# AgentWeaver
|
|
2
2
|
|
|
3
|
-
`AgentWeaver` is a TypeScript/Node.js CLI for engineering
|
|
3
|
+
`AgentWeaver` is a TypeScript/Node.js CLI for harness engineering around coding agents.
|
|
4
4
|
|
|
5
|
-
It
|
|
5
|
+
It brings Jira context, GitLab review artifacts, agent-driven steps via Codex and Claude, an interactive terminal UI, and fully automated workflows into one controlled execution harness.
|
|
6
|
+
|
|
7
|
+
A typical flow looks like:
|
|
6
8
|
|
|
7
9
|
`plan -> implement -> run-go-linter-loop -> run-go-tests-loop -> review -> review-fix`
|
|
8
10
|
|
|
9
|
-
The
|
|
11
|
+
The point is not the specific chain above, but that `AgentWeaver` lets you design, run, and reuse agent harnesses:
|
|
12
|
+
|
|
13
|
+
- with declarative flows and isolated executors
|
|
14
|
+
- with artifacts that survive restarts and let runs resume from the right point
|
|
15
|
+
- with a TUI for semi-automatic operation and visibility
|
|
16
|
+
- with an `auto` mode for fully automated flows without manual handoff
|
|
17
|
+
|
|
18
|
+
The package runs as an npm CLI and includes a full-screen TUI built on `neo-blessed`.
|
|
10
19
|
|
|
11
20
|
## What It Does
|
|
12
21
|
|
|
13
|
-
- Fetches a Jira issue by key or browse URL
|
|
14
|
-
- Fetches GitLab
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
- Runs workflow stages like `bug-analyze`, `bug-fix`, `gitlab-diff-review`, `mr-description`, `plan`, `task-describe`, `implement`, `review`, `review-fix`, `run-go-tests-loop`, `run-go-linter-loop`, and `auto`
|
|
20
|
-
- Persists compact `auto` pipeline state on disk so runs can resume without storing large agent outputs
|
|
22
|
+
- Fetches a Jira issue by key or browse URL and turns it into working context for agent steps
|
|
23
|
+
- Fetches GitLab review comments and diffs into reusable Markdown and JSON artifacts
|
|
24
|
+
- Runs agent stages such as `plan`, `implement`, `review`, and `review-fix`, plus verification loops such as `run-go-tests-loop` and `run-go-linter-loop`
|
|
25
|
+
- Stores machine-readable JSON artifacts under `.agentweaver/scopes/<scope-key>/.artifacts/` and uses them as the source of truth between steps
|
|
26
|
+
- Isolates workflows by scope: for Jira-backed runs this is usually the issue key, otherwise it defaults to `<git-branch>--<worktree-hash>`
|
|
27
|
+
- Persists compact `auto` pipeline state on disk so runs can resume without keeping full agent transcripts
|
|
21
28
|
- Uses Docker runtime services for isolated Codex execution and build verification
|
|
22
29
|
|
|
30
|
+
In short, `AgentWeaver` is for cases where you do not want a one-off LLM script, but a durable engineering harness around agents.
|
|
31
|
+
|
|
32
|
+
## Why AgentWeaver
|
|
33
|
+
|
|
34
|
+
- Harness engineering instead of ad-hoc prompting. Flows, executors, prompts, and artifacts are separate layers rather than one mixed script.
|
|
35
|
+
- Agent runtime instead of single-shot calls. You can build sequences where one agent plans, another implements, and the next verifies and fixes.
|
|
36
|
+
- TUI instead of blind shell execution. The terminal UI gives you an operational view of flow state, activity, and artifacts.
|
|
37
|
+
- Full automation instead of manual step switching. `auto` can run end-to-end flows that move through planning, implementation, verification, and review on their own.
|
|
38
|
+
|
|
23
39
|
## Architecture
|
|
24
40
|
|
|
25
|
-
The CLI
|
|
41
|
+
The CLI is built around an `executor + node + declarative flow` architecture that fits harness engineering well.
|
|
26
42
|
|
|
27
|
-
- `src/index.ts` remains the CLI entrypoint and
|
|
28
|
-
- `src/executors/` contains first-class executors for external actions such as Jira
|
|
43
|
+
- `src/index.ts` remains the CLI entrypoint and top-level orchestration layer
|
|
44
|
+
- `src/executors/` contains first-class executors for external actions such as Jira, GitLab, local Codex, Docker-based build verification, Claude, and process execution
|
|
29
45
|
- `src/pipeline/nodes/` contains reusable runtime nodes built on top of executors
|
|
30
46
|
- `src/pipeline/flow-specs/` contains declarative JSON flow specs for `preflight`, `bug-analyze`, `bug-fix`, `gitlab-diff-review`, `gitlab-review`, `mr-description`, `plan`, `task-describe`, `implement`, `review`, `review-fix`, `run-go-tests-loop`, `run-go-linter-loop`, and `auto`
|
|
31
|
-
- project-local
|
|
32
|
-
- `src/runtime/` contains shared runtime services such as command resolution, Docker runtime
|
|
47
|
+
- project-local flows can be added under `.agentweaver/.flows/*.json`; they are discovered from the current workspace at runtime
|
|
48
|
+
- `src/runtime/` contains shared runtime services such as command resolution, Docker runtime setup, and subprocess execution
|
|
33
49
|
|
|
34
|
-
This keeps command handlers focused on
|
|
50
|
+
This keeps command handlers focused on selecting flows and passing parameters instead of assembling prompts, subprocess wiring, and side effects inline.
|
|
35
51
|
|
|
36
52
|
## Repository Layout
|
|
37
53
|
|
|
@@ -118,6 +134,12 @@ GIT_ALLOW_PROTOCOL=file:https:ssh
|
|
|
118
134
|
|
|
119
135
|
## Usage
|
|
120
136
|
|
|
137
|
+
Primary usage modes:
|
|
138
|
+
|
|
139
|
+
- direct execution of individual stages for controlled agent work
|
|
140
|
+
- interactive TUI mode for selecting flows and observing progress
|
|
141
|
+
- fully automated `auto` mode for end-to-end pipelines
|
|
142
|
+
|
|
121
143
|
Direct CLI usage:
|
|
122
144
|
|
|
123
145
|
```bash
|
|
@@ -179,18 +201,28 @@ agentweaver auto-reset DEMO-3288
|
|
|
179
201
|
Notes:
|
|
180
202
|
|
|
181
203
|
- `--verbose` streams child process `stdout/stderr` in direct CLI mode
|
|
182
|
-
- task-only commands such as `plan` and `auto` ask for Jira task via interactive `user-input` when it is omitted
|
|
183
|
-
- scope-flexible commands such as `gitlab-diff-review`, `gitlab-review`, `review`, `review-fix`, `run-go-tests-loop`, and `run-go-linter-loop` use the current git branch by default when Jira task is omitted
|
|
184
|
-
- `gitlab-review` and `gitlab-diff-review` ask for GitLab merge request URL via interactive `user-input`
|
|
185
|
-
- `--scope <name>` lets you override the default
|
|
186
|
-
- the interactive `Activity` pane
|
|
204
|
+
- task-only commands such as `plan` and `auto` ask for a Jira task via interactive `user-input` when it is omitted
|
|
205
|
+
- scope-flexible commands such as `gitlab-diff-review`, `gitlab-review`, `review`, `review-fix`, `run-go-tests-loop`, and `run-go-linter-loop` use the current git branch by default when a Jira task is omitted
|
|
206
|
+
- `gitlab-review` and `gitlab-diff-review` ask for a GitLab merge request URL via interactive `user-input`
|
|
207
|
+
- `--scope <name>` lets you override the default workflow scope name
|
|
208
|
+
- the interactive `Activity` pane intentionally shows structured events, prompts, summaries, and short statuses instead of raw Codex/Claude logs by default
|
|
209
|
+
|
|
210
|
+
For fully automated flows, the main entrypoint looks like:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
agentweaver auto DEMO-3288
|
|
214
|
+
agentweaver auto-status DEMO-3288
|
|
215
|
+
agentweaver auto-reset DEMO-3288
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
This lets you run an agent pipeline as a reproducible process rather than a loose set of manual steps.
|
|
187
219
|
|
|
188
220
|
## Interactive TUI
|
|
189
221
|
|
|
190
|
-
Interactive mode opens a full-screen
|
|
222
|
+
Interactive mode opens a full-screen TUI that works as an operator console for the agent harness:
|
|
191
223
|
|
|
192
224
|
- flow list
|
|
193
|
-
- current flow
|
|
225
|
+
- current progress for the selected flow
|
|
194
226
|
- activity log
|
|
195
227
|
- task summary pane
|
|
196
228
|
- keyboard navigation between panes
|
|
@@ -205,11 +237,11 @@ Current navigation:
|
|
|
205
237
|
|
|
206
238
|
Flow discovery and highlighting:
|
|
207
239
|
|
|
208
|
-
- built-in
|
|
209
|
-
- project-local
|
|
210
|
-
- project-local
|
|
240
|
+
- built-in flows are loaded from `src/pipeline/flow-specs/`
|
|
241
|
+
- project-local flows are loaded from `.agentweaver/.flows/*.json`
|
|
242
|
+
- project-local flows are shown in a different color in the `Flows` pane
|
|
211
243
|
- when a project-local flow is selected, the description pane also shows its source file path
|
|
212
|
-
- if a local flow conflicts with a built-in flow id or uses unknown node / executor / prompt / schema types, interactive startup fails fast with a validation error
|
|
244
|
+
- if a local flow conflicts with a built-in flow id or uses unknown `node` / `executor` / `prompt` / `schema` types, interactive startup fails fast with a validation error
|
|
213
245
|
|
|
214
246
|
Activity pane behavior:
|
|
215
247
|
|
package/dist/artifacts.js
CHANGED
|
@@ -144,7 +144,7 @@ export function autoStateFile(taskKey) {
|
|
|
144
144
|
return taskArtifactsFile(taskKey, `.agentweaver-state-${taskKey}.json`);
|
|
145
145
|
}
|
|
146
146
|
export function flowStateFile(scopeKey, flowId) {
|
|
147
|
-
return scopeArtifactsFile(scopeKey, `.agentweaver-flow-state-${flowId}.json`);
|
|
147
|
+
return scopeArtifactsFile(scopeKey, `.agentweaver-flow-state-${encodeURIComponent(flowId)}.json`);
|
|
148
148
|
}
|
|
149
149
|
export function planArtifacts(taskKey) {
|
|
150
150
|
return [designFile(taskKey), designJsonFile(taskKey), planFile(taskKey), planJsonFile(taskKey), qaFile(taskKey), qaJsonFile(taskKey)];
|
package/dist/errors.js
CHANGED
|
@@ -4,3 +4,10 @@ export class TaskRunnerError extends Error {
|
|
|
4
4
|
this.name = "TaskRunnerError";
|
|
5
5
|
}
|
|
6
6
|
}
|
|
7
|
+
export class FlowInterruptedError extends TaskRunnerError {
|
|
8
|
+
returnCode = 130;
|
|
9
|
+
constructor(message = "Flow interrupted by user.") {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "FlowInterruptedError";
|
|
12
|
+
}
|
|
13
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { REVIEW_FILE_RE, REVIEW_REPLY_FILE_RE, autoStateFile, bugAnalyzeArtifacts, bugAnalyzeJsonFile, bugFixDesignJsonFile, bugFixPlanJsonFile, designJsonFile, gitlabDiffFile, gitlabDiffJsonFile, ensureScopeWorkspaceDir, gitlabReviewFile, gitlabReviewJsonFile, planJsonFile, planArtifacts, qaJsonFile, readyToMergeFile, requireArtifacts, reviewFile, reviewReplyJsonFile, reviewFixSelectionJsonFile, reviewJsonFile, scopeWorkspaceDir, taskSummaryFile, } from "./artifacts.js";
|
|
7
|
-
import { TaskRunnerError } from "./errors.js";
|
|
7
|
+
import { FlowInterruptedError, TaskRunnerError } from "./errors.js";
|
|
8
8
|
import { createFlowRunState, hasResumableFlowState, loadFlowRunState, prepareFlowStateForResume, resetFlowRunState, saveFlowRunState, stripExecutionStatePayload, } from "./flow-state.js";
|
|
9
9
|
import { requireJiraTaskFile } from "./jira.js";
|
|
10
10
|
import { validateStructuredArtifacts } from "./structured-artifacts.js";
|
|
@@ -41,12 +41,15 @@ const COMMANDS = [
|
|
|
41
41
|
const AUTO_STATE_SCHEMA_VERSION = 3;
|
|
42
42
|
const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
43
43
|
const PACKAGE_ROOT = path.resolve(MODULE_DIR, "..");
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
44
|
+
function createRuntimeServices(signal) {
|
|
45
|
+
return {
|
|
46
|
+
resolveCmd,
|
|
47
|
+
resolveDockerComposeCmd,
|
|
48
|
+
dockerRuntimeEnv: () => dockerRuntimeEnv(PACKAGE_ROOT),
|
|
49
|
+
runCommand: (argv, options = {}) => runCommand(argv, { ...options, ...(signal ? { signal } : {}) }),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const runtimeServices = createRuntimeServices();
|
|
50
53
|
function buildFailureOutputPreview(output) {
|
|
51
54
|
const normalized = stripAnsi(output).replace(/\r\n/g, "\n").trim();
|
|
52
55
|
if (!normalized) {
|
|
@@ -104,7 +107,7 @@ function usage() {
|
|
|
104
107
|
Interactive Mode:
|
|
105
108
|
When started without a command, the script opens an interactive UI.
|
|
106
109
|
If a Jira task is provided, interactive mode starts in the current project scope with Jira context attached.
|
|
107
|
-
Use Up/Down to
|
|
110
|
+
Use Up/Down to move in the flow tree, Left/Right to collapse or expand folders, Enter to toggle a folder or run a flow, h for help, q to exit.
|
|
108
111
|
|
|
109
112
|
Flags:
|
|
110
113
|
--version Show package version
|
|
@@ -549,6 +552,7 @@ function interactiveFlowDefinition(entry) {
|
|
|
549
552
|
label: entry.id,
|
|
550
553
|
description: flowDescription(entry.id),
|
|
551
554
|
source: entry.source,
|
|
555
|
+
treePath: [...entry.treePath],
|
|
552
556
|
...(entry.source === "project-local" ? { sourcePath: entry.absolutePath } : {}),
|
|
553
557
|
phases: flow.phases.map((phase) => ({
|
|
554
558
|
id: phase.id,
|
|
@@ -598,13 +602,13 @@ function findCurrentFlowExecutionStep(state) {
|
|
|
598
602
|
}
|
|
599
603
|
return null;
|
|
600
604
|
}
|
|
601
|
-
async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, requestUserInput = requestUserInputInTerminal, setSummary, launchMode = "restart") {
|
|
605
|
+
async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, requestUserInput = requestUserInputInTerminal, setSummary, launchMode = "restart", runtime = runtimeServices) {
|
|
602
606
|
const context = createPipelineContext({
|
|
603
607
|
issueKey: config.taskKey,
|
|
604
608
|
jiraRef: config.jiraRef,
|
|
605
609
|
dryRun: config.dryRun,
|
|
606
610
|
verbose: config.verbose,
|
|
607
|
-
runtime
|
|
611
|
+
runtime,
|
|
608
612
|
...(setSummary ? { setSummary } : {}),
|
|
609
613
|
requestUserInput,
|
|
610
614
|
});
|
|
@@ -669,10 +673,12 @@ async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, requ
|
|
|
669
673
|
throw error;
|
|
670
674
|
}
|
|
671
675
|
}
|
|
672
|
-
async function runDeclarativeFlowBySpecFile(fileName, config, flowParams, requestUserInput = requestUserInputInTerminal, setSummary, launchMode = "restart") {
|
|
673
|
-
await runDeclarativeFlowByRef(config.command, { source: "built-in", fileName }, config, flowParams, requestUserInput, setSummary, launchMode);
|
|
676
|
+
async function runDeclarativeFlowBySpecFile(fileName, config, flowParams, requestUserInput = requestUserInputInTerminal, setSummary, launchMode = "restart", runtime = runtimeServices) {
|
|
677
|
+
await runDeclarativeFlowByRef(config.command, { source: "built-in", fileName }, config, flowParams, requestUserInput, setSummary, launchMode, runtime);
|
|
674
678
|
}
|
|
675
679
|
function defaultDeclarativeFlowParams(config, forceRefreshSummary = false) {
|
|
680
|
+
const iteration = nextReviewIterationForTask(config.taskKey);
|
|
681
|
+
const latestIteration = latestReviewReplyIteration(config.taskKey);
|
|
676
682
|
return {
|
|
677
683
|
taskKey: config.taskKey,
|
|
678
684
|
jiraRef: config.jiraRef,
|
|
@@ -686,6 +692,9 @@ function defaultDeclarativeFlowParams(config, forceRefreshSummary = false) {
|
|
|
686
692
|
runGoCoverageScript: config.runGoCoverageScript,
|
|
687
693
|
extraPrompt: config.extraPrompt,
|
|
688
694
|
reviewFixPoints: config.reviewFixPoints,
|
|
695
|
+
iteration,
|
|
696
|
+
latestIteration,
|
|
697
|
+
...(latestIteration !== null ? { reviewFixSelectionJsonFile: reviewFixSelectionJsonFile(config.taskKey, latestIteration) } : {}),
|
|
689
698
|
forceRefresh: forceRefreshSummary,
|
|
690
699
|
};
|
|
691
700
|
}
|
|
@@ -710,13 +719,13 @@ function flowRequiresTaskScope(entry) {
|
|
|
710
719
|
}
|
|
711
720
|
return valueReferencesTaskScopeParams(entry.flow.phases);
|
|
712
721
|
}
|
|
713
|
-
async function runAutoPhaseViaSpec(config, phaseId, executionState, state, setSummary, forceRefreshSummary = false) {
|
|
722
|
+
async function runAutoPhaseViaSpec(config, phaseId, executionState, state, setSummary, forceRefreshSummary = false, runtime = runtimeServices) {
|
|
714
723
|
const context = createPipelineContext({
|
|
715
724
|
issueKey: config.taskKey,
|
|
716
725
|
jiraRef: config.jiraRef,
|
|
717
726
|
dryRun: config.dryRun,
|
|
718
727
|
verbose: config.verbose,
|
|
719
|
-
runtime
|
|
728
|
+
runtime,
|
|
720
729
|
...(setSummary ? { setSummary } : {}),
|
|
721
730
|
requestUserInput: requestUserInputInTerminal,
|
|
722
731
|
});
|
|
@@ -801,13 +810,13 @@ function requireJiraConfig(config) {
|
|
|
801
810
|
throw new TaskRunnerError(`Command '${config.command}' requires Jira context in the current project scope.`);
|
|
802
811
|
}
|
|
803
812
|
}
|
|
804
|
-
async function executeCommand(baseConfig, runFollowupVerify = true, requestUserInput = requestUserInputInTerminal, resolvedScope, setSummary, forceRefreshSummary = false, launchMode = "restart") {
|
|
813
|
+
async function executeCommand(baseConfig, runFollowupVerify = true, requestUserInput = requestUserInputInTerminal, resolvedScope, setSummary, forceRefreshSummary = false, launchMode = "restart", runtime = runtimeServices) {
|
|
805
814
|
const config = buildRuntimeConfig(baseConfig, resolvedScope ?? (await resolveScopeForCommand(baseConfig, requestUserInput)));
|
|
806
815
|
if (config.command === "auto") {
|
|
807
816
|
if (launchMode === "restart") {
|
|
808
817
|
resetAutoPipelineState(config);
|
|
809
818
|
}
|
|
810
|
-
await runAutoPipeline(config, setSummary, forceRefreshSummary);
|
|
819
|
+
await runAutoPipeline(config, setSummary, forceRefreshSummary, runtime);
|
|
811
820
|
return false;
|
|
812
821
|
}
|
|
813
822
|
if (config.command === "auto-status") {
|
|
@@ -847,7 +856,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
847
856
|
taskKey: config.taskKey,
|
|
848
857
|
extraPrompt: config.extraPrompt,
|
|
849
858
|
forceRefresh: forceRefreshSummary,
|
|
850
|
-
}, requestUserInput, setSummary, launchMode);
|
|
859
|
+
}, requestUserInput, setSummary, launchMode, runtime);
|
|
851
860
|
return false;
|
|
852
861
|
}
|
|
853
862
|
if (config.command === "bug-analyze") {
|
|
@@ -862,7 +871,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
862
871
|
taskKey: config.taskKey,
|
|
863
872
|
extraPrompt: config.extraPrompt,
|
|
864
873
|
forceRefresh: forceRefreshSummary,
|
|
865
|
-
}, requestUserInput, setSummary, launchMode);
|
|
874
|
+
}, requestUserInput, setSummary, launchMode, runtime);
|
|
866
875
|
return false;
|
|
867
876
|
}
|
|
868
877
|
if (config.command === "gitlab-review") {
|
|
@@ -871,7 +880,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
871
880
|
taskKey: config.taskKey,
|
|
872
881
|
iteration,
|
|
873
882
|
extraPrompt: config.extraPrompt,
|
|
874
|
-
}, requestUserInput, undefined, launchMode);
|
|
883
|
+
}, requestUserInput, undefined, launchMode, runtime);
|
|
875
884
|
if (!config.dryRun) {
|
|
876
885
|
printSummary("GitLab Review", `Artifacts:\n${gitlabReviewFile(config.taskKey)}\n${gitlabReviewJsonFile(config.taskKey)}`);
|
|
877
886
|
}
|
|
@@ -883,7 +892,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
883
892
|
taskKey: config.taskKey,
|
|
884
893
|
iteration,
|
|
885
894
|
extraPrompt: config.extraPrompt,
|
|
886
|
-
}, requestUserInput, undefined, launchMode);
|
|
895
|
+
}, requestUserInput, undefined, launchMode, runtime);
|
|
887
896
|
if (!config.dryRun) {
|
|
888
897
|
printSummary("GitLab Diff Review", `Artifacts:\n${gitlabDiffFile(config.taskKey)}\n${gitlabDiffJsonFile(config.taskKey)}\n${reviewFile(config.taskKey, iteration)}\n${reviewJsonFile(config.taskKey, iteration)}`);
|
|
889
898
|
}
|
|
@@ -901,7 +910,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
901
910
|
await runDeclarativeFlowBySpecFile("bug-fix.json", config, {
|
|
902
911
|
taskKey: config.taskKey,
|
|
903
912
|
extraPrompt: config.extraPrompt,
|
|
904
|
-
}, requestUserInput, undefined, launchMode);
|
|
913
|
+
}, requestUserInput, undefined, launchMode, runtime);
|
|
905
914
|
return false;
|
|
906
915
|
}
|
|
907
916
|
if (config.command === "mr-description") {
|
|
@@ -910,7 +919,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
910
919
|
await runDeclarativeFlowBySpecFile("mr-description.json", config, {
|
|
911
920
|
taskKey: config.taskKey,
|
|
912
921
|
extraPrompt: config.extraPrompt,
|
|
913
|
-
}, requestUserInput, undefined, launchMode);
|
|
922
|
+
}, requestUserInput, undefined, launchMode, runtime);
|
|
914
923
|
return false;
|
|
915
924
|
}
|
|
916
925
|
if (config.command === "task-describe") {
|
|
@@ -919,7 +928,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
919
928
|
jiraApiUrl: config.jiraApiUrl,
|
|
920
929
|
taskKey: config.taskKey,
|
|
921
930
|
extraPrompt: config.extraPrompt,
|
|
922
|
-
}, requestUserInput, undefined, launchMode);
|
|
931
|
+
}, requestUserInput, undefined, launchMode, runtime);
|
|
923
932
|
return false;
|
|
924
933
|
}
|
|
925
934
|
if (config.command === "implement") {
|
|
@@ -947,14 +956,14 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
947
956
|
taskKey: config.taskKey,
|
|
948
957
|
iteration,
|
|
949
958
|
extraPrompt: config.extraPrompt,
|
|
950
|
-
}, requestUserInput, undefined, launchMode);
|
|
959
|
+
}, requestUserInput, undefined, launchMode, runtime);
|
|
951
960
|
}
|
|
952
961
|
else {
|
|
953
962
|
await runDeclarativeFlowBySpecFile("review-project.json", config, {
|
|
954
963
|
taskKey: config.taskKey,
|
|
955
964
|
iteration,
|
|
956
965
|
extraPrompt: config.extraPrompt,
|
|
957
|
-
}, requestUserInput, undefined, launchMode);
|
|
966
|
+
}, requestUserInput, undefined, launchMode, runtime);
|
|
958
967
|
}
|
|
959
968
|
return !config.dryRun && existsSync(readyToMergeFile(config.taskKey));
|
|
960
969
|
}
|
|
@@ -973,7 +982,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
973
982
|
reviewFixSelectionJsonFile: reviewFixSelectionJsonFile(config.taskKey, latestIteration),
|
|
974
983
|
extraPrompt: config.extraPrompt,
|
|
975
984
|
reviewFixPoints: config.reviewFixPoints,
|
|
976
|
-
}, requestUserInput, undefined, launchMode);
|
|
985
|
+
}, requestUserInput, undefined, launchMode, runtime);
|
|
977
986
|
return false;
|
|
978
987
|
}
|
|
979
988
|
if (config.command === "run-go-tests-loop" || config.command === "run-go-linter-loop") {
|
|
@@ -982,12 +991,12 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
982
991
|
runGoTestsScript: config.runGoTestsScript,
|
|
983
992
|
runGoLinterScript: config.runGoLinterScript,
|
|
984
993
|
extraPrompt: config.extraPrompt,
|
|
985
|
-
}, requestUserInput, undefined, launchMode);
|
|
994
|
+
}, requestUserInput, undefined, launchMode, runtime);
|
|
986
995
|
return false;
|
|
987
996
|
}
|
|
988
997
|
throw new TaskRunnerError(`Unsupported command: ${config.command}`);
|
|
989
998
|
}
|
|
990
|
-
async function runAutoPipelineDryRun(config, setSummary, forceRefreshSummary = false) {
|
|
999
|
+
async function runAutoPipelineDryRun(config, setSummary, forceRefreshSummary = false, runtime = runtimeServices) {
|
|
991
1000
|
checkAutoPrerequisites(config);
|
|
992
1001
|
printInfo("Dry-run auto pipeline from declarative spec");
|
|
993
1002
|
const autoFlow = loadAutoFlow();
|
|
@@ -1000,16 +1009,16 @@ async function runAutoPipelineDryRun(config, setSummary, forceRefreshSummary = f
|
|
|
1000
1009
|
publishFlowState("auto", executionState);
|
|
1001
1010
|
for (const phase of autoFlow.phases) {
|
|
1002
1011
|
printInfo(`Dry-run auto phase: ${phase.id}`);
|
|
1003
|
-
await runAutoPhaseViaSpec(config, phase.id, executionState, undefined, setSummary, forceRefreshSummary);
|
|
1012
|
+
await runAutoPhaseViaSpec(config, phase.id, executionState, undefined, setSummary, forceRefreshSummary, runtime);
|
|
1004
1013
|
if (executionState.terminated) {
|
|
1005
1014
|
break;
|
|
1006
1015
|
}
|
|
1007
1016
|
}
|
|
1008
1017
|
}
|
|
1009
|
-
async function runAutoPipeline(config, setSummary, forceRefreshSummary = false) {
|
|
1018
|
+
async function runAutoPipeline(config, setSummary, forceRefreshSummary = false, runtime = runtimeServices) {
|
|
1010
1019
|
requireJiraConfig(config);
|
|
1011
1020
|
if (config.dryRun) {
|
|
1012
|
-
await runAutoPipelineDryRun(config, setSummary, forceRefreshSummary);
|
|
1021
|
+
await runAutoPipelineDryRun(config, setSummary, forceRefreshSummary, runtime);
|
|
1013
1022
|
return;
|
|
1014
1023
|
}
|
|
1015
1024
|
checkAutoPrerequisites(config);
|
|
@@ -1049,7 +1058,7 @@ async function runAutoPipeline(config, setSummary, forceRefreshSummary = false)
|
|
|
1049
1058
|
saveAutoPipelineState(state);
|
|
1050
1059
|
try {
|
|
1051
1060
|
printInfo(`Running auto step: ${step.id}`);
|
|
1052
|
-
const status = await runAutoPhaseViaSpec(config, step.id, state.executionState, state, setSummary, forceRefreshSummary);
|
|
1061
|
+
const status = await runAutoPhaseViaSpec(config, step.id, state.executionState, state, setSummary, forceRefreshSummary, runtime);
|
|
1053
1062
|
step.status = status;
|
|
1054
1063
|
step.finishedAt = nowIso8601();
|
|
1055
1064
|
step.returnCode = 0;
|
|
@@ -1165,6 +1174,8 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1165
1174
|
let currentScope = resolveProjectScope(scopeName, jiraRef);
|
|
1166
1175
|
const gitBranchName = detectGitBranchName();
|
|
1167
1176
|
const flowCatalog = loadInteractiveFlowCatalog(process.cwd());
|
|
1177
|
+
let activeAbortController = null;
|
|
1178
|
+
let activeFlowId = null;
|
|
1168
1179
|
let exiting = false;
|
|
1169
1180
|
const ui = new InteractiveUi({
|
|
1170
1181
|
scopeKey: currentScope.scopeKey,
|
|
@@ -1204,6 +1215,9 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1204
1215
|
return resumeLookup;
|
|
1205
1216
|
},
|
|
1206
1217
|
onRun: async (flowId, launchMode) => {
|
|
1218
|
+
const abortController = new AbortController();
|
|
1219
|
+
activeAbortController = abortController;
|
|
1220
|
+
activeFlowId = flowId;
|
|
1207
1221
|
try {
|
|
1208
1222
|
const flowEntry = findCatalogEntry(flowId, flowCatalog);
|
|
1209
1223
|
if (!flowEntry) {
|
|
@@ -1227,13 +1241,18 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1227
1241
|
syncInteractiveTaskSummary(ui, currentScope, forceRefresh);
|
|
1228
1242
|
}
|
|
1229
1243
|
if (flowEntry.source === "built-in" && isBuiltInCommandFlowId(flowId)) {
|
|
1230
|
-
await executeCommand(baseConfig, true, (form) => ui.requestUserInput(form), currentScope, (markdown) => ui.setSummary(markdown), forceRefresh, launchMode);
|
|
1244
|
+
await executeCommand(baseConfig, true, (form) => ui.requestUserInput(form), currentScope, (markdown) => ui.setSummary(markdown), forceRefresh, launchMode, createRuntimeServices(abortController.signal));
|
|
1231
1245
|
return;
|
|
1232
1246
|
}
|
|
1233
1247
|
const runtimeConfig = buildRuntimeConfig(baseConfig, currentScope);
|
|
1234
|
-
await runDeclarativeFlowByRef(flowId, toDeclarativeFlowRef(flowEntry), runtimeConfig, defaultDeclarativeFlowParams(runtimeConfig, forceRefresh), (form) => ui.requestUserInput(form), (markdown) => ui.setSummary(markdown), launchMode);
|
|
1248
|
+
await runDeclarativeFlowByRef(flowId, toDeclarativeFlowRef(flowEntry), runtimeConfig, defaultDeclarativeFlowParams(runtimeConfig, forceRefresh), (form) => ui.requestUserInput(form), (markdown) => ui.setSummary(markdown), launchMode, createRuntimeServices(abortController.signal));
|
|
1235
1249
|
}
|
|
1236
1250
|
catch (error) {
|
|
1251
|
+
if (error instanceof FlowInterruptedError) {
|
|
1252
|
+
ui.appendLog(`[interrupt] ${error.message}`);
|
|
1253
|
+
printInfo(error.message);
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1237
1256
|
if (error instanceof TaskRunnerError) {
|
|
1238
1257
|
ui.setFlowFailed(flowId);
|
|
1239
1258
|
printError(error.message);
|
|
@@ -1247,6 +1266,19 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1247
1266
|
}
|
|
1248
1267
|
throw error;
|
|
1249
1268
|
}
|
|
1269
|
+
finally {
|
|
1270
|
+
if (activeAbortController === abortController) {
|
|
1271
|
+
activeAbortController = null;
|
|
1272
|
+
activeFlowId = null;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
},
|
|
1276
|
+
onInterrupt: async (flowId) => {
|
|
1277
|
+
if (!activeAbortController || activeFlowId !== flowId) {
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
ui.interruptActiveForm();
|
|
1281
|
+
activeAbortController.abort();
|
|
1250
1282
|
},
|
|
1251
1283
|
onExit: () => {
|
|
1252
1284
|
exiting = true;
|