agentweaver 0.1.3 → 0.1.5

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.
Files changed (33) hide show
  1. package/README.md +48 -14
  2. package/dist/artifacts.js +86 -3
  3. package/dist/executors/verify-build-executor.js +110 -9
  4. package/dist/index.js +170 -18
  5. package/dist/interactive-ui.js +525 -33
  6. package/dist/pipeline/checks.js +5 -0
  7. package/dist/pipeline/context.js +1 -0
  8. package/dist/pipeline/declarative-flow-runner.js +16 -0
  9. package/dist/pipeline/flow-specs/auto.json +191 -3
  10. package/dist/pipeline/flow-specs/bug-analyze.json +140 -0
  11. package/dist/pipeline/flow-specs/bug-fix.json +44 -0
  12. package/dist/pipeline/flow-specs/implement.json +12 -0
  13. package/dist/pipeline/flow-specs/mr-description.json +89 -0
  14. package/dist/pipeline/flow-specs/plan.json +52 -0
  15. package/dist/pipeline/flow-specs/preflight.json +32 -0
  16. package/dist/pipeline/flow-specs/review-fix.json +79 -1
  17. package/dist/pipeline/flow-specs/review.json +79 -0
  18. package/dist/pipeline/flow-specs/run-linter-loop.json +149 -0
  19. package/dist/pipeline/flow-specs/run-tests-loop.json +149 -0
  20. package/dist/pipeline/flow-specs/task-describe.json +89 -0
  21. package/dist/pipeline/node-registry.js +19 -0
  22. package/dist/pipeline/nodes/flow-run-node.js +40 -0
  23. package/dist/pipeline/nodes/review-findings-form-node.js +65 -0
  24. package/dist/pipeline/nodes/user-input-node.js +93 -0
  25. package/dist/pipeline/nodes/verify-build-node.js +1 -0
  26. package/dist/pipeline/prompt-registry.js +6 -1
  27. package/dist/pipeline/spec-compiler.js +13 -0
  28. package/dist/pipeline/spec-validator.js +12 -0
  29. package/dist/pipeline/value-resolver.js +49 -4
  30. package/dist/prompts.js +46 -14
  31. package/dist/structured-artifacts.js +272 -0
  32. package/dist/user-input.js +171 -0
  33. package/package.json +1 -1
package/README.md CHANGED
@@ -11,9 +11,10 @@ The package is designed to run as an npm CLI and includes an interactive termina
11
11
  ## What It Does
12
12
 
13
13
  - Fetches a Jira issue by key or browse URL
14
- - Generates workflow artifacts such as design, implementation plan, QA plan, reviews, and summaries
15
- - Runs workflow stages like `plan`, `implement`, `review`, `review-fix`, `test`, and `auto`
16
- - Persists `auto` pipeline state on disk so runs can resume
14
+ - Generates workflow artifacts such as design, implementation plan, QA plan, bug analysis, reviews, and summaries
15
+ - 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
16
+ - Runs workflow stages like `bug-analyze`, `bug-fix`, `mr-description`, `plan`, `task-describe`, `implement`, `review`, `review-fix`, `test`, and `auto`
17
+ - Persists compact `auto` pipeline state on disk so runs can resume without storing large agent outputs
17
18
  - Uses Docker runtime services for isolated Codex execution and build verification
18
19
 
19
20
  ## Architecture
@@ -23,7 +24,7 @@ The CLI now uses an executor + node + declarative flow architecture.
23
24
  - `src/index.ts` remains the CLI entrypoint and high-level orchestration layer
24
25
  - `src/executors/` contains first-class executors for external actions such as Jira fetch, local Codex, Docker-based build verification, Claude, Claude summaries, and process execution
25
26
  - `src/pipeline/nodes/` contains reusable runtime nodes built on top of executors
26
- - `src/pipeline/flow-specs/` contains declarative JSON flow specs for `preflight`, `plan`, `implement`, `review`, `review-fix`, `test`, `test-fix`, `test-linter-fix`, and `auto`
27
+ - `src/pipeline/flow-specs/` contains declarative JSON flow specs for `preflight`, `bug-analyze`, `bug-fix`, `mr-description`, `plan`, `task-describe`, `implement`, `review`, `review-fix`, `test`, `test-fix`, `test-linter-fix`, `run-tests-loop`, `run-linter-loop`, and `auto`
27
28
  - `src/runtime/` contains shared runtime services such as command resolution, Docker runtime environment setup, and subprocess execution
28
29
 
29
30
  This keeps command handlers focused on choosing a flow and providing parameters instead of assembling prompts and subprocess wiring inline.
@@ -41,7 +42,9 @@ This keeps command handlers focused on choosing a flow and providing parameters
41
42
  - `src/runtime/` — shared runtime services used by executors
42
43
  - `docker-compose.yml` — runtime services for Codex and build verification
43
44
  - `Dockerfile.codex` — container image for Codex runtime
44
- - `verify_build.sh` — project-specific verification entrypoint used by `verify-build`
45
+ - `verify_build.sh` — aggregated verification entrypoint used by `verify-build`
46
+ - `run_tests.sh` — isolated test and coverage verification entrypoint
47
+ - `run_linter.sh` — isolated generate + lint verification entrypoint
45
48
  - `package.json` — npm package metadata and scripts
46
49
  - `tsconfig.json` — TypeScript configuration
47
50
 
@@ -50,7 +53,7 @@ This keeps command handlers focused on choosing a flow and providing parameters
50
53
  - Node.js `>= 18.19.0`
51
54
  - npm
52
55
  - Docker with `docker compose` or `docker-compose`
53
- - `codex` CLI for `plan` and Codex-driven steps
56
+ - `codex` CLI for `bug-analyze`, `bug-fix`, `mr-description`, `plan`, and other Codex-driven steps
54
57
  - `claude` CLI for review and summary steps
55
58
 
56
59
  ## Installation
@@ -112,8 +115,14 @@ Direct CLI usage:
112
115
 
113
116
  ```bash
114
117
  agentweaver plan DEMO-3288
118
+ agentweaver bug-analyze DEMO-3288
119
+ agentweaver bug-fix DEMO-3288
120
+ agentweaver mr-description DEMO-3288
121
+ agentweaver task-describe DEMO-3288
115
122
  agentweaver implement DEMO-3288
116
123
  agentweaver review DEMO-3288
124
+ agentweaver run-tests-loop DEMO-3288
125
+ agentweaver run-linter-loop DEMO-3288
117
126
  agentweaver auto DEMO-3288
118
127
  ```
119
128
 
@@ -121,6 +130,10 @@ From source checkout:
121
130
 
122
131
  ```bash
123
132
  node dist/index.js plan DEMO-3288
133
+ node dist/index.js bug-analyze DEMO-3288
134
+ node dist/index.js bug-fix DEMO-3288
135
+ node dist/index.js mr-description DEMO-3288
136
+ node dist/index.js task-describe DEMO-3288
124
137
  node dist/index.js auto DEMO-3288
125
138
  ```
126
139
 
@@ -145,28 +158,35 @@ agentweaver auto-status DEMO-3288
145
158
  agentweaver auto-reset DEMO-3288
146
159
  ```
147
160
 
161
+ Notes:
162
+
163
+ - `--verbose` streams child process `stdout/stderr` in direct CLI mode
164
+ - 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
165
+
148
166
  ## Interactive TUI
149
167
 
150
168
  Interactive mode opens a full-screen terminal UI with:
151
169
 
152
- - command input
170
+ - flow list
171
+ - current flow progress
153
172
  - activity log
154
173
  - task summary pane
155
- - command list/help
156
174
  - keyboard navigation between panes
157
175
 
158
176
  Current navigation:
159
177
 
160
- - `Enter` — run command
178
+ - `Enter` — run selected flow
161
179
  - `Tab` / `Shift+Tab` — switch panes
162
- - `Ctrl+J` — focus activity log
163
- - `Ctrl+K` — focus command input
164
- - `Ctrl+U` — focus task summary
165
- - `Ctrl+H` — focus commands pane
166
180
  - `PgUp` / `PgDn` / `Home` / `End` — scroll focused panes
167
- - `?` or `F1` — help overlay
181
+ - `h` — help overlay
168
182
  - `q` or `Ctrl+C` — exit
169
183
 
184
+ Activity pane behavior:
185
+
186
+ - each external launch is separated with a framed block that shows the current `node`, `executor`, and `model` when available
187
+ - prompts and summaries are rendered as plain text for readability
188
+ - live raw Codex/Claude output is not shown there in normal mode
189
+
170
190
  ## Docker Runtime
171
191
 
172
192
  Docker is used as an isolated execution environment for Codex and build/test verification.
@@ -176,6 +196,8 @@ Main services:
176
196
  - `codex` — interactive Codex container
177
197
  - `codex-exec` — non-interactive `codex exec`
178
198
  - `verify-build` — project verification script inside container
199
+ - `run-tests` — isolated `run_tests.sh` execution inside container
200
+ - `run-linter` — isolated `run_linter.sh` execution inside container
179
201
  - `codex-login` — interactive login container
180
202
  - `dockerd` — internal Docker daemon for testcontainers/build flows
181
203
 
@@ -205,6 +227,18 @@ Build verification:
205
227
  PROJECT_DIR="$PWD" docker compose -f "$AGENTWEAVER_HOME/docker-compose.yml" run --rm verify-build
206
228
  ```
207
229
 
230
+ Tests only:
231
+
232
+ ```bash
233
+ PROJECT_DIR="$PWD" docker compose -f "$AGENTWEAVER_HOME/docker-compose.yml" run --rm run-tests
234
+ ```
235
+
236
+ Linter only:
237
+
238
+ ```bash
239
+ PROJECT_DIR="$PWD" docker compose -f "$AGENTWEAVER_HOME/docker-compose.yml" run --rm run-linter
240
+ ```
241
+
208
242
  ## Development
209
243
 
210
244
  Install dependencies and build:
package/dist/artifacts.js CHANGED
@@ -11,37 +11,120 @@ export function taskWorkspaceDir(taskKey) {
11
11
  export function ensureTaskWorkspaceDir(taskKey) {
12
12
  const workspaceDir = taskWorkspaceDir(taskKey);
13
13
  mkdirSync(workspaceDir, { recursive: true });
14
+ mkdirSync(taskArtifactsDir(taskKey), { recursive: true });
14
15
  return workspaceDir;
15
16
  }
16
17
  export function taskWorkspaceFile(taskKey, fileName) {
17
18
  return path.join(taskWorkspaceDir(taskKey), fileName);
18
19
  }
20
+ export function taskArtifactsDir(taskKey) {
21
+ return path.join(taskWorkspaceDir(taskKey), ".artifacts");
22
+ }
23
+ export function taskArtifactsFile(taskKey, fileName) {
24
+ return path.join(taskArtifactsDir(taskKey), fileName);
25
+ }
19
26
  export function artifactFile(prefix, taskKey, iteration) {
20
27
  return taskWorkspaceFile(taskKey, `${prefix}-${taskKey}-${iteration}.md`);
21
28
  }
29
+ export function artifactJsonFile(prefix, taskKey, iteration) {
30
+ return taskArtifactsFile(taskKey, `${prefix}-${taskKey}-${iteration}.json`);
31
+ }
22
32
  export function designFile(taskKey) {
23
33
  return artifactFile("design", taskKey, 1);
24
34
  }
35
+ export function designJsonFile(taskKey) {
36
+ return artifactJsonFile("design", taskKey, 1);
37
+ }
25
38
  export function planFile(taskKey) {
26
39
  return artifactFile("plan", taskKey, 1);
27
40
  }
41
+ export function planJsonFile(taskKey) {
42
+ return artifactJsonFile("plan", taskKey, 1);
43
+ }
44
+ export function bugAnalyzeFile(taskKey) {
45
+ return taskWorkspaceFile(taskKey, `bug-analyze-${taskKey}.md`);
46
+ }
47
+ export function bugAnalyzeJsonFile(taskKey) {
48
+ return taskArtifactsFile(taskKey, `bug-analyze-${taskKey}.json`);
49
+ }
50
+ export function bugFixDesignFile(taskKey) {
51
+ return taskWorkspaceFile(taskKey, `bug-fix-design-${taskKey}.md`);
52
+ }
53
+ export function bugFixDesignJsonFile(taskKey) {
54
+ return taskArtifactsFile(taskKey, `bug-fix-design-${taskKey}.json`);
55
+ }
56
+ export function bugFixPlanFile(taskKey) {
57
+ return taskWorkspaceFile(taskKey, `bug-fix-plan-${taskKey}.md`);
58
+ }
59
+ export function bugFixPlanJsonFile(taskKey) {
60
+ return taskArtifactsFile(taskKey, `bug-fix-plan-${taskKey}.json`);
61
+ }
28
62
  export function qaFile(taskKey) {
29
63
  return artifactFile("qa", taskKey, 1);
30
64
  }
65
+ export function qaJsonFile(taskKey) {
66
+ return artifactJsonFile("qa", taskKey, 1);
67
+ }
31
68
  export function taskSummaryFile(taskKey) {
32
69
  return artifactFile("task", taskKey, 1);
33
70
  }
71
+ export function taskSummaryJsonFile(taskKey) {
72
+ return artifactJsonFile("task", taskKey, 1);
73
+ }
34
74
  export function readyToMergeFile(taskKey) {
35
75
  return taskWorkspaceFile(taskKey, READY_TO_MERGE_FILE);
36
76
  }
37
77
  export function jiraTaskFile(taskKey) {
38
- return taskWorkspaceFile(taskKey, `${taskKey}.json`);
78
+ return taskArtifactsFile(taskKey, `${taskKey}.json`);
79
+ }
80
+ export function jiraDescriptionFile(taskKey) {
81
+ return taskWorkspaceFile(taskKey, `jira-${taskKey}-description.md`);
82
+ }
83
+ export function jiraDescriptionJsonFile(taskKey) {
84
+ return taskArtifactsFile(taskKey, `jira-${taskKey}-description.json`);
85
+ }
86
+ export function mrDescriptionFile(taskKey) {
87
+ return taskWorkspaceFile(taskKey, `mr-description-${taskKey}.md`);
88
+ }
89
+ export function mrDescriptionJsonFile(taskKey) {
90
+ return taskArtifactsFile(taskKey, `mr-description-${taskKey}.json`);
39
91
  }
40
92
  export function autoStateFile(taskKey) {
41
- return taskWorkspaceFile(taskKey, `.agentweaver-state-${taskKey}.json`);
93
+ return taskArtifactsFile(taskKey, `.agentweaver-state-${taskKey}.json`);
42
94
  }
43
95
  export function planArtifacts(taskKey) {
44
- return [designFile(taskKey), planFile(taskKey), qaFile(taskKey)];
96
+ return [designFile(taskKey), designJsonFile(taskKey), planFile(taskKey), planJsonFile(taskKey), qaFile(taskKey), qaJsonFile(taskKey)];
97
+ }
98
+ export function bugAnalyzeArtifacts(taskKey) {
99
+ return [
100
+ bugAnalyzeFile(taskKey),
101
+ bugAnalyzeJsonFile(taskKey),
102
+ bugFixDesignFile(taskKey),
103
+ bugFixDesignJsonFile(taskKey),
104
+ bugFixPlanFile(taskKey),
105
+ bugFixPlanJsonFile(taskKey),
106
+ ];
107
+ }
108
+ export function reviewFile(taskKey, iteration) {
109
+ return artifactFile("review", taskKey, iteration);
110
+ }
111
+ export function reviewJsonFile(taskKey, iteration) {
112
+ return artifactJsonFile("review", taskKey, iteration);
113
+ }
114
+ export function reviewReplyFile(taskKey, iteration) {
115
+ return artifactFile("review-reply", taskKey, iteration);
116
+ }
117
+ export function reviewReplyJsonFile(taskKey, iteration) {
118
+ return artifactJsonFile("review-reply", taskKey, iteration);
119
+ }
120
+ export function reviewFixFile(taskKey, iteration) {
121
+ return artifactFile("review-fix", taskKey, iteration);
122
+ }
123
+ export function reviewFixJsonFile(taskKey, iteration) {
124
+ return artifactJsonFile("review-fix", taskKey, iteration);
125
+ }
126
+ export function reviewFixSelectionJsonFile(taskKey, iteration) {
127
+ return artifactJsonFile("review-fix-selection", taskKey, iteration);
45
128
  }
46
129
  export function requireArtifacts(paths, message) {
47
130
  const missing = paths.filter((filePath) => !existsSync(filePath));
@@ -1,22 +1,123 @@
1
1
  import { verifyBuildExecutorDefaultConfig } from "./configs/verify-build-config.js";
2
+ import { TaskRunnerError } from "../errors.js";
2
3
  import { processExecutor } from "./process-executor.js";
4
+ function parseStructuredResult(output, service) {
5
+ const lines = output
6
+ .split(/\r?\n/)
7
+ .map((line) => line.replace(/\u001b\[[0-9;]*m/g, "").trim())
8
+ .filter(Boolean);
9
+ if (lines.length === 0) {
10
+ throw new TaskRunnerError(`Structured result is missing from service '${service}' output.`);
11
+ }
12
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
13
+ const line = lines[index];
14
+ if (!line) {
15
+ continue;
16
+ }
17
+ const candidates = [];
18
+ if (line.startsWith("{") && line.endsWith("}")) {
19
+ candidates.push(line);
20
+ }
21
+ const firstBrace = line.indexOf("{");
22
+ const lastBrace = line.lastIndexOf("}");
23
+ if (firstBrace >= 0 && lastBrace > firstBrace) {
24
+ const slice = line.slice(firstBrace, lastBrace + 1).trim();
25
+ if (slice && !candidates.includes(slice)) {
26
+ candidates.push(slice);
27
+ }
28
+ }
29
+ for (const rawJson of candidates) {
30
+ let parsed;
31
+ try {
32
+ parsed = JSON.parse(rawJson);
33
+ }
34
+ catch {
35
+ continue;
36
+ }
37
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
38
+ continue;
39
+ }
40
+ const candidate = parsed;
41
+ if (typeof candidate.ok !== "boolean" ||
42
+ typeof candidate.kind !== "string" ||
43
+ typeof candidate.stage !== "string" ||
44
+ typeof candidate.exitCode !== "number" ||
45
+ typeof candidate.summary !== "string" ||
46
+ typeof candidate.command !== "string") {
47
+ continue;
48
+ }
49
+ const details = candidate.details;
50
+ if (details !== undefined && (!details || typeof details !== "object" || Array.isArray(details))) {
51
+ continue;
52
+ }
53
+ return {
54
+ ok: candidate.ok,
55
+ kind: candidate.kind,
56
+ stage: candidate.stage,
57
+ exitCode: candidate.exitCode,
58
+ summary: candidate.summary,
59
+ command: candidate.command,
60
+ details: details ?? {},
61
+ };
62
+ }
63
+ }
64
+ throw new TaskRunnerError(`Structured result is missing or invalid in service '${service}' output.`);
65
+ }
3
66
  export const verifyBuildExecutor = {
4
67
  kind: "verify-build",
5
68
  version: 1,
6
69
  defaultConfig: verifyBuildExecutorDefaultConfig,
7
70
  async execute(context, input, config) {
8
71
  const composeCommand = context.runtime.resolveDockerComposeCmd();
9
- const result = await processExecutor.execute(context, {
10
- argv: [...composeCommand, config.composeFileFlag, input.dockerComposeFile, ...config.runArgs, config.service],
11
- env: context.runtime.dockerRuntimeEnv(),
12
- verbose: config.verbose,
13
- label: config.service,
14
- }, {
15
- printFailureOutput: config.printFailureOutput,
16
- });
72
+ const service = input.service ?? config.service;
73
+ if (context.dryRun) {
74
+ await processExecutor.execute(context, {
75
+ argv: [...composeCommand, config.composeFileFlag, input.dockerComposeFile, ...config.runArgs, service],
76
+ env: context.runtime.dockerRuntimeEnv(),
77
+ verbose: config.verbose,
78
+ label: service,
79
+ }, {
80
+ printFailureOutput: config.printFailureOutput,
81
+ });
82
+ return {
83
+ output: "",
84
+ composeCommand,
85
+ parsed: {
86
+ ok: true,
87
+ kind: service,
88
+ stage: "dry_run",
89
+ exitCode: 0,
90
+ summary: `Dry run for service '${service}'`,
91
+ command: [...composeCommand, config.composeFileFlag, input.dockerComposeFile, ...config.runArgs, service].join(" "),
92
+ details: {},
93
+ },
94
+ };
95
+ }
96
+ let output = "";
97
+ let exitCode = 0;
98
+ try {
99
+ const result = await processExecutor.execute(context, {
100
+ argv: [...composeCommand, config.composeFileFlag, input.dockerComposeFile, ...config.runArgs, service],
101
+ env: context.runtime.dockerRuntimeEnv(),
102
+ verbose: config.verbose,
103
+ label: service,
104
+ }, {
105
+ printFailureOutput: config.printFailureOutput,
106
+ });
107
+ output = result.output;
108
+ }
109
+ catch (error) {
110
+ output = String(error.output ?? "");
111
+ exitCode = Number(error.returnCode ?? 1);
112
+ }
113
+ const parsed = parseStructuredResult(output, service);
114
+ if (parsed.exitCode !== exitCode && exitCode !== 0) {
115
+ throw new TaskRunnerError(`Structured result exit code mismatch for service '${service}': script=${parsed.exitCode}, runtime=${exitCode}.`);
116
+ }
17
117
  return {
18
- output: result.output,
118
+ output,
19
119
  composeCommand,
120
+ parsed,
20
121
  };
21
122
  },
22
123
  };