agentweaver 0.1.6 → 0.1.8

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/Dockerfile.codex CHANGED
@@ -45,9 +45,10 @@ RUN GOBIN=/usr/local/bin go install github.com/golangci/golangci-lint/v2/cmd/gol
45
45
  ENV PATH="/usr/local/go/bin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
46
46
 
47
47
  COPY verify_build.sh /usr/local/bin/verify_build.sh
48
- COPY run_tests.sh /usr/local/bin/run_tests.sh
49
- COPY run_linter.sh /usr/local/bin/run_linter.sh
50
- RUN chmod +x /usr/local/bin/verify_build.sh /usr/local/bin/run_tests.sh /usr/local/bin/run_linter.sh
48
+ COPY run_go_tests.py /usr/local/bin/run_go_tests.py
49
+ COPY run_go_linter.py /usr/local/bin/run_go_linter.py
50
+ COPY run_go_coverage.sh /usr/local/bin/run_go_coverage.sh
51
+ RUN chmod +x /usr/local/bin/verify_build.sh /usr/local/bin/run_go_tests.py /usr/local/bin/run_go_linter.py /usr/local/bin/run_go_coverage.sh
51
52
 
52
53
  WORKDIR /workspace
53
54
 
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  It orchestrates a flow like:
6
6
 
7
- `plan -> implement -> run-linter-loop -> run-tests-loop -> review -> review-fix`
7
+ `plan -> implement -> run-go-linter-loop -> run-go-tests-loop -> review -> review-fix`
8
8
 
9
9
  The package is designed to run as an npm CLI and includes an interactive terminal UI built on `neo-blessed`.
10
10
 
@@ -13,8 +13,9 @@ The package is designed to run as an npm CLI and includes an interactive termina
13
13
  - Fetches a Jira issue by key or browse URL
14
14
  - Fetches GitLab merge request review comments into reusable markdown and JSON artifacts
15
15
  - Generates workflow artifacts such as design, implementation plan, QA plan, bug analysis, reviews, and summaries
16
- - Machine-readable JSON artifacts are stored under `.agentweaver-<TASK>/.artifacts/` and act as the source of truth between workflow steps; Markdown artifacts remain for human inspection
17
- - Runs workflow stages like `bug-analyze`, `bug-fix`, `mr-description`, `plan`, `task-describe`, `implement`, `review`, `review-fix`, `run-tests-loop`, `run-linter-loop`, and `auto`
16
+ - Machine-readable JSON artifacts are stored under `.agentweaver/scopes/<scope-key>/.artifacts/` and act as the source of truth between workflow steps; Markdown artifacts remain for human inspection
17
+ - Workflow artifacts are isolated by scope; for Jira-driven flows the scope key defaults to the Jira task key, otherwise it defaults to `<git-branch>--<worktree-hash>`
18
+ - Runs workflow stages like `bug-analyze`, `bug-fix`, `mr-description`, `plan`, `task-describe`, `implement`, `review`, `review-fix`, `run-go-tests-loop`, `run-go-linter-loop`, and `auto`
18
19
  - Persists compact `auto` pipeline state on disk so runs can resume without storing large agent outputs
19
20
  - Uses Docker runtime services for isolated Codex execution and build verification
20
21
 
@@ -25,7 +26,7 @@ The CLI now uses an executor + node + declarative flow architecture.
25
26
  - `src/index.ts` remains the CLI entrypoint and high-level orchestration layer
26
27
  - `src/executors/` contains first-class executors for external actions such as Jira fetch, GitLab review fetch, local Codex, Docker-based build verification, Claude, Claude summaries, and process execution
27
28
  - `src/pipeline/nodes/` contains reusable runtime nodes built on top of executors
28
- - `src/pipeline/flow-specs/` contains declarative JSON flow specs for `preflight`, `bug-analyze`, `bug-fix`, `gitlab-review`, `mr-description`, `plan`, `task-describe`, `implement`, `review`, `review-fix`, `run-tests-loop`, `run-linter-loop`, and `auto`
29
+ - `src/pipeline/flow-specs/` contains declarative JSON flow specs for `preflight`, `bug-analyze`, `bug-fix`, `gitlab-review`, `mr-description`, `plan`, `task-describe`, `implement`, `review`, `review-fix`, `run-go-tests-loop`, `run-go-linter-loop`, and `auto`
29
30
  - `src/runtime/` contains shared runtime services such as command resolution, Docker runtime environment setup, and subprocess execution
30
31
 
31
32
  This keeps command handlers focused on choosing a flow and providing parameters instead of assembling prompts and subprocess wiring inline.
@@ -44,8 +45,9 @@ This keeps command handlers focused on choosing a flow and providing parameters
44
45
  - `docker-compose.yml` — runtime services for Codex and build verification
45
46
  - `Dockerfile.codex` — container image for Codex runtime
46
47
  - `verify_build.sh` — aggregated verification entrypoint used by `verify-build`
47
- - `run_tests.sh` — isolated test and coverage verification entrypoint
48
- - `run_linter.sh` — isolated generate + lint verification entrypoint
48
+ - `run_go_tests.py` — isolated Go test verification entrypoint
49
+ - `run_go_linter.py` — isolated Go generate + lint verification entrypoint
50
+ - `run_go_coverage.sh` — isolated Go coverage verification entrypoint
49
51
  - `package.json` — npm package metadata and scripts
50
52
  - `tsconfig.json` — TypeScript configuration
51
53
 
@@ -117,15 +119,19 @@ Direct CLI usage:
117
119
 
118
120
  ```bash
119
121
  agentweaver plan DEMO-3288
122
+ agentweaver plan
120
123
  agentweaver bug-analyze DEMO-3288
121
124
  agentweaver bug-fix DEMO-3288
122
125
  agentweaver gitlab-review DEMO-3288
123
126
  agentweaver mr-description DEMO-3288
124
127
  agentweaver task-describe DEMO-3288
125
128
  agentweaver implement DEMO-3288
129
+ agentweaver review
126
130
  agentweaver review DEMO-3288
127
- agentweaver run-tests-loop DEMO-3288
128
- agentweaver run-linter-loop DEMO-3288
131
+ agentweaver review --scope release-prep
132
+ agentweaver run-go-tests-loop DEMO-3288
133
+ agentweaver run-go-tests-loop
134
+ agentweaver run-go-linter-loop DEMO-3288
129
135
  agentweaver auto DEMO-3288
130
136
  ```
131
137
 
@@ -133,11 +139,13 @@ From source checkout:
133
139
 
134
140
  ```bash
135
141
  node dist/index.js plan DEMO-3288
142
+ node dist/index.js plan
136
143
  node dist/index.js bug-analyze DEMO-3288
137
144
  node dist/index.js bug-fix DEMO-3288
138
145
  node dist/index.js gitlab-review DEMO-3288
139
146
  node dist/index.js mr-description DEMO-3288
140
147
  node dist/index.js task-describe DEMO-3288
148
+ node dist/index.js review
141
149
  node dist/index.js auto DEMO-3288
142
150
  ```
143
151
 
@@ -145,6 +153,7 @@ Interactive mode:
145
153
 
146
154
  ```bash
147
155
  agentweaver DEMO-3288
156
+ agentweaver
148
157
  ```
149
158
 
150
159
  When you run from a working project directory, set `AGENTWEAVER_HOME` to the AgentWeaver installation:
@@ -165,6 +174,9 @@ agentweaver auto-reset DEMO-3288
165
174
  Notes:
166
175
 
167
176
  - `--verbose` streams child process `stdout/stderr` in direct CLI mode
177
+ - task-only commands such as `plan` and `auto` ask for Jira task via interactive `user-input` when it is omitted
178
+ - scope-flexible commands such as `review`, `review-fix`, `run-go-tests-loop`, and `run-go-linter-loop` use the current git branch by default when Jira task is omitted
179
+ - `--scope <name>` lets you override the default project scope name
168
180
  - the interactive `Activity` pane is intentionally structured: it shows launch separators, prompts, summaries, and short status messages instead of raw Codex/Claude logs by default
169
181
 
170
182
  ## Interactive TUI
@@ -200,8 +212,9 @@ Main services:
200
212
  - `codex` — interactive Codex container
201
213
  - `codex-exec` — non-interactive `codex exec`
202
214
  - `verify-build` — project verification script inside container
203
- - `run-tests` — isolated `run_tests.sh` execution inside container
204
- - `run-linter` — isolated `run_linter.sh` execution inside container
215
+ - `run-go-tests` — isolated `run_go_tests.py` execution inside container
216
+ - `run-go-linter` — isolated `run_go_linter.py` execution inside container
217
+ - `run-go-coverage` — isolated `run_go_coverage.sh` execution inside container
205
218
  - `codex-login` — interactive login container
206
219
  - `dockerd` — internal Docker daemon for testcontainers/build flows
207
220
 
@@ -234,13 +247,19 @@ PROJECT_DIR="$PWD" docker compose -f "$AGENTWEAVER_HOME/docker-compose.yml" run
234
247
  Tests only:
235
248
 
236
249
  ```bash
237
- PROJECT_DIR="$PWD" docker compose -f "$AGENTWEAVER_HOME/docker-compose.yml" run --rm run-tests
250
+ PROJECT_DIR="$PWD" docker compose -f "$AGENTWEAVER_HOME/docker-compose.yml" run --rm run-go-tests
238
251
  ```
239
252
 
240
253
  Linter only:
241
254
 
242
255
  ```bash
243
- PROJECT_DIR="$PWD" docker compose -f "$AGENTWEAVER_HOME/docker-compose.yml" run --rm run-linter
256
+ PROJECT_DIR="$PWD" docker compose -f "$AGENTWEAVER_HOME/docker-compose.yml" run --rm run-go-linter
257
+ ```
258
+
259
+ Coverage only:
260
+
261
+ ```bash
262
+ PROJECT_DIR="$PWD" docker compose -f "$AGENTWEAVER_HOME/docker-compose.yml" run --rm run-go-coverage
244
263
  ```
245
264
 
246
265
  ## Development
package/dist/artifacts.js CHANGED
@@ -5,23 +5,41 @@ import { TaskRunnerError } from "./errors.js";
5
5
  export const REVIEW_FILE_RE = /^review-(.+)-(\d+)\.md$/;
6
6
  export const REVIEW_REPLY_FILE_RE = /^review-reply-(.+)-(\d+)\.md$/;
7
7
  export const READY_TO_MERGE_FILE = "ready-to-merge.md";
8
- export function taskWorkspaceDir(taskKey) {
9
- return path.join(process.cwd(), `.agentweaver-${taskKey}`);
8
+ export function scopesRootDir() {
9
+ return path.join(process.cwd(), ".agentweaver", "scopes");
10
10
  }
11
- export function ensureTaskWorkspaceDir(taskKey) {
12
- const workspaceDir = taskWorkspaceDir(taskKey);
11
+ export function scopeWorkspaceDir(scopeKey) {
12
+ return path.join(scopesRootDir(), scopeKey);
13
+ }
14
+ export function ensureScopeWorkspaceDir(scopeKey) {
15
+ const workspaceDir = scopeWorkspaceDir(scopeKey);
13
16
  mkdirSync(workspaceDir, { recursive: true });
14
- mkdirSync(taskArtifactsDir(taskKey), { recursive: true });
17
+ mkdirSync(scopeArtifactsDir(scopeKey), { recursive: true });
15
18
  return workspaceDir;
16
19
  }
20
+ export function scopeWorkspaceFile(scopeKey, fileName) {
21
+ return path.join(scopeWorkspaceDir(scopeKey), fileName);
22
+ }
23
+ export function scopeArtifactsDir(scopeKey) {
24
+ return path.join(scopeWorkspaceDir(scopeKey), ".artifacts");
25
+ }
26
+ export function scopeArtifactsFile(scopeKey, fileName) {
27
+ return path.join(scopeArtifactsDir(scopeKey), fileName);
28
+ }
29
+ export function taskWorkspaceDir(taskKey) {
30
+ return scopeWorkspaceDir(taskKey);
31
+ }
32
+ export function ensureTaskWorkspaceDir(taskKey) {
33
+ return ensureScopeWorkspaceDir(taskKey);
34
+ }
17
35
  export function taskWorkspaceFile(taskKey, fileName) {
18
- return path.join(taskWorkspaceDir(taskKey), fileName);
36
+ return scopeWorkspaceFile(taskKey, fileName);
19
37
  }
20
38
  export function taskArtifactsDir(taskKey) {
21
- return path.join(taskWorkspaceDir(taskKey), ".artifacts");
39
+ return scopeArtifactsDir(taskKey);
22
40
  }
23
41
  export function taskArtifactsFile(taskKey, fileName) {
24
- return path.join(taskArtifactsDir(taskKey), fileName);
42
+ return scopeArtifactsFile(taskKey, fileName);
25
43
  }
26
44
  export function artifactFile(prefix, taskKey, iteration) {
27
45
  return taskWorkspaceFile(taskKey, `${prefix}-${taskKey}-${iteration}.md`);
@@ -101,6 +119,9 @@ export function gitlabReviewInputJsonFile(taskKey) {
101
119
  export function autoStateFile(taskKey) {
102
120
  return taskArtifactsFile(taskKey, `.agentweaver-state-${taskKey}.json`);
103
121
  }
122
+ export function flowStateFile(scopeKey, flowId) {
123
+ return scopeArtifactsFile(scopeKey, `.agentweaver-flow-state-${flowId}.json`);
124
+ }
104
125
  export function planArtifacts(taskKey) {
105
126
  return [designFile(taskKey), designJsonFile(taskKey), planFile(taskKey), planJsonFile(taskKey), qaFile(taskKey), qaJsonFile(taskKey)];
106
127
  }
@@ -135,6 +156,12 @@ export function reviewFixJsonFile(taskKey, iteration) {
135
156
  export function reviewFixSelectionJsonFile(taskKey, iteration) {
136
157
  return artifactJsonFile("review-fix-selection", taskKey, iteration);
137
158
  }
159
+ export function runGoLinterResultJsonFile(taskKey, iteration) {
160
+ return artifactJsonFile("run-go-linter-result", taskKey, iteration);
161
+ }
162
+ export function runGoTestsResultJsonFile(taskKey, iteration) {
163
+ return artifactJsonFile("run-go-tests-result", taskKey, iteration);
164
+ }
138
165
  export function requireArtifacts(paths, message) {
139
166
  const missing = paths.filter((filePath) => !existsSync(filePath));
140
167
  if (missing.length > 0) {
@@ -0,0 +1,134 @@
1
+ import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
+ import { ensureScopeWorkspaceDir, flowStateFile } from "./artifacts.js";
3
+ import { TaskRunnerError } from "./errors.js";
4
+ const FLOW_STATE_SCHEMA_VERSION = 1;
5
+ function nowIso8601() {
6
+ return new Date().toISOString();
7
+ }
8
+ export function stripExecutionStatePayload(executionState) {
9
+ return {
10
+ flowKind: executionState.flowKind,
11
+ flowVersion: executionState.flowVersion,
12
+ terminated: executionState.terminated,
13
+ ...(executionState.terminationReason ? { terminationReason: executionState.terminationReason } : {}),
14
+ phases: executionState.phases.map((phase) => ({
15
+ id: phase.id,
16
+ status: phase.status,
17
+ repeatVars: { ...phase.repeatVars },
18
+ ...(phase.startedAt ? { startedAt: phase.startedAt } : {}),
19
+ ...(phase.finishedAt ? { finishedAt: phase.finishedAt } : {}),
20
+ steps: phase.steps.map((step) => ({
21
+ id: step.id,
22
+ status: step.status,
23
+ ...(step.outputs ? { outputs: step.outputs } : {}),
24
+ ...(step.value !== undefined ? { value: step.value } : {}),
25
+ ...(step.startedAt ? { startedAt: step.startedAt } : {}),
26
+ ...(step.finishedAt ? { finishedAt: step.finishedAt } : {}),
27
+ ...(step.stopFlow !== undefined ? { stopFlow: step.stopFlow } : {}),
28
+ })),
29
+ })),
30
+ };
31
+ }
32
+ export function createFlowRunState(scopeKey, flowId, executionState) {
33
+ return {
34
+ schemaVersion: FLOW_STATE_SCHEMA_VERSION,
35
+ flowId,
36
+ scopeKey,
37
+ status: "pending",
38
+ currentStep: null,
39
+ updatedAt: nowIso8601(),
40
+ executionState: stripExecutionStatePayload(executionState),
41
+ };
42
+ }
43
+ export function loadFlowRunState(scopeKey, flowId) {
44
+ const filePath = flowStateFile(scopeKey, flowId);
45
+ if (!existsSync(filePath)) {
46
+ return null;
47
+ }
48
+ let raw;
49
+ try {
50
+ raw = JSON.parse(readFileSync(filePath, "utf8"));
51
+ }
52
+ catch (error) {
53
+ throw new TaskRunnerError(`Failed to parse flow state file ${filePath}: ${error.message}`);
54
+ }
55
+ if (!raw || typeof raw !== "object") {
56
+ throw new TaskRunnerError(`Invalid flow state file format: ${filePath}`);
57
+ }
58
+ const state = raw;
59
+ if (state.schemaVersion !== FLOW_STATE_SCHEMA_VERSION) {
60
+ throw new TaskRunnerError(`Unsupported flow state schema in ${filePath}: ${state.schemaVersion}`);
61
+ }
62
+ if (state.flowId !== flowId) {
63
+ throw new TaskRunnerError(`Flow state ${filePath} belongs to flow '${state.flowId}', expected '${flowId}'`);
64
+ }
65
+ return state;
66
+ }
67
+ export function saveFlowRunState(state) {
68
+ state.updatedAt = nowIso8601();
69
+ ensureScopeWorkspaceDir(state.scopeKey);
70
+ writeFileSync(flowStateFile(state.scopeKey, state.flowId), `${JSON.stringify({
71
+ ...state,
72
+ executionState: stripExecutionStatePayload(state.executionState),
73
+ }, null, 2)}\n`, "utf8");
74
+ }
75
+ export function resetFlowRunState(scopeKey, flowId) {
76
+ const filePath = flowStateFile(scopeKey, flowId);
77
+ if (!existsSync(filePath)) {
78
+ return false;
79
+ }
80
+ rmSync(filePath);
81
+ return true;
82
+ }
83
+ export function hasResumableFlowState(state) {
84
+ if (!state) {
85
+ return false;
86
+ }
87
+ if (state.executionState.terminated) {
88
+ return false;
89
+ }
90
+ if (state.status === "completed") {
91
+ return false;
92
+ }
93
+ if (state.status === "running" || state.status === "blocked") {
94
+ return true;
95
+ }
96
+ return state.executionState.phases.some((phase) => phase.steps.some((step) => step.status === "done" || step.status === "running"));
97
+ }
98
+ function normalizeStepState(step) {
99
+ if (step.status !== "running") {
100
+ return step;
101
+ }
102
+ const { finishedAt: _finishedAt, outputs: _outputs, value: _value, stopFlow: _stopFlow, ...rest } = step;
103
+ return {
104
+ ...rest,
105
+ status: "pending",
106
+ };
107
+ }
108
+ function normalizePhaseState(phase) {
109
+ const normalizedSteps = phase.steps.map(normalizeStepState);
110
+ if (phase.status !== "running") {
111
+ return {
112
+ ...phase,
113
+ steps: normalizedSteps,
114
+ };
115
+ }
116
+ const { finishedAt: _finishedAt, ...rest } = phase;
117
+ return {
118
+ ...rest,
119
+ status: "pending",
120
+ steps: normalizedSteps,
121
+ };
122
+ }
123
+ export function prepareFlowStateForResume(state) {
124
+ state.status = "pending";
125
+ state.lastError = null;
126
+ state.currentStep = null;
127
+ state.executionState = {
128
+ ...state.executionState,
129
+ terminated: false,
130
+ phases: state.executionState.phases.map(normalizePhaseState),
131
+ };
132
+ delete state.executionState.terminationReason;
133
+ return state;
134
+ }