agentweaver 0.1.3 → 0.1.4

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 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
+ - For bug-analysis flows, structured JSON artifacts are the machine-readable source of truth, while Markdown artifacts are 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
@@ -25,6 +25,24 @@ export function designFile(taskKey) {
25
25
  export function planFile(taskKey) {
26
26
  return artifactFile("plan", taskKey, 1);
27
27
  }
28
+ export function bugAnalyzeFile(taskKey) {
29
+ return taskWorkspaceFile(taskKey, `bug-analyze-${taskKey}.md`);
30
+ }
31
+ export function bugAnalyzeJsonFile(taskKey) {
32
+ return taskWorkspaceFile(taskKey, `bug-analyze-${taskKey}.json`);
33
+ }
34
+ export function bugFixDesignFile(taskKey) {
35
+ return taskWorkspaceFile(taskKey, `bug-fix-design-${taskKey}.md`);
36
+ }
37
+ export function bugFixDesignJsonFile(taskKey) {
38
+ return taskWorkspaceFile(taskKey, `bug-fix-design-${taskKey}.json`);
39
+ }
40
+ export function bugFixPlanFile(taskKey) {
41
+ return taskWorkspaceFile(taskKey, `bug-fix-plan-${taskKey}.md`);
42
+ }
43
+ export function bugFixPlanJsonFile(taskKey) {
44
+ return taskWorkspaceFile(taskKey, `bug-fix-plan-${taskKey}.json`);
45
+ }
28
46
  export function qaFile(taskKey) {
29
47
  return artifactFile("qa", taskKey, 1);
30
48
  }
@@ -37,12 +55,28 @@ export function readyToMergeFile(taskKey) {
37
55
  export function jiraTaskFile(taskKey) {
38
56
  return taskWorkspaceFile(taskKey, `${taskKey}.json`);
39
57
  }
58
+ export function jiraDescriptionFile(taskKey) {
59
+ return taskWorkspaceFile(taskKey, `jira-${taskKey}-description.md`);
60
+ }
61
+ export function mrDescriptionFile(taskKey) {
62
+ return taskWorkspaceFile(taskKey, `mr-description-${taskKey}.md`);
63
+ }
40
64
  export function autoStateFile(taskKey) {
41
65
  return taskWorkspaceFile(taskKey, `.agentweaver-state-${taskKey}.json`);
42
66
  }
43
67
  export function planArtifacts(taskKey) {
44
68
  return [designFile(taskKey), planFile(taskKey), qaFile(taskKey)];
45
69
  }
70
+ export function bugAnalyzeArtifacts(taskKey) {
71
+ return [
72
+ bugAnalyzeFile(taskKey),
73
+ bugAnalyzeJsonFile(taskKey),
74
+ bugFixDesignFile(taskKey),
75
+ bugFixDesignJsonFile(taskKey),
76
+ bugFixPlanFile(taskKey),
77
+ bugFixPlanJsonFile(taskKey),
78
+ ];
79
+ }
46
80
  export function requireArtifacts(paths, message) {
47
81
  const missing = paths.filter((filePath) => !existsSync(filePath));
48
82
  if (missing.length > 0) {
@@ -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
  };
package/dist/index.js CHANGED
@@ -3,9 +3,10 @@ import { existsSync, readdirSync, readFileSync, rmSync, writeFileSync } from "no
3
3
  import path from "node:path";
4
4
  import process from "node:process";
5
5
  import { fileURLToPath } from "node:url";
6
- import { REVIEW_FILE_RE, REVIEW_REPLY_FILE_RE, autoStateFile, ensureTaskWorkspaceDir, jiraTaskFile, planArtifacts, readyToMergeFile, requireArtifacts, taskWorkspaceDir, taskSummaryFile, } from "./artifacts.js";
6
+ import { REVIEW_FILE_RE, REVIEW_REPLY_FILE_RE, autoStateFile, bugAnalyzeArtifacts, bugAnalyzeJsonFile, bugFixDesignJsonFile, bugFixPlanJsonFile, ensureTaskWorkspaceDir, jiraTaskFile, planArtifacts, readyToMergeFile, requireArtifacts, taskWorkspaceDir, taskSummaryFile, } from "./artifacts.js";
7
7
  import { TaskRunnerError } from "./errors.js";
8
8
  import { buildJiraApiUrl, buildJiraBrowseUrl, extractIssueKey, requireJiraTaskFile } from "./jira.js";
9
+ import { validateStructuredArtifacts } from "./structured-artifacts.js";
9
10
  import { summarizeBuildFailure as summarizeBuildFailureViaPipeline } from "./pipeline/build-failure-summary.js";
10
11
  import { createPipelineContext } from "./pipeline/context.js";
11
12
  import { loadAutoFlow } from "./pipeline/auto-flow.js";
@@ -18,18 +19,24 @@ import { runCommand } from "./runtime/process-runner.js";
18
19
  import { InteractiveUi } from "./interactive-ui.js";
19
20
  import { bye, printError, printInfo, printPanel, printSummary, setFlowExecutionState } from "./tui.js";
20
21
  const COMMANDS = [
22
+ "bug-analyze",
23
+ "bug-fix",
24
+ "mr-description",
21
25
  "plan",
26
+ "task-describe",
22
27
  "implement",
23
28
  "review",
24
29
  "review-fix",
25
30
  "test",
26
31
  "test-fix",
27
32
  "test-linter-fix",
33
+ "run-tests-loop",
34
+ "run-linter-loop",
28
35
  "auto",
29
36
  "auto-status",
30
37
  "auto-reset",
31
38
  ];
32
- const AUTO_STATE_SCHEMA_VERSION = 2;
39
+ const AUTO_STATE_SCHEMA_VERSION = 3;
33
40
  const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
34
41
  const PACKAGE_ROOT = path.resolve(MODULE_DIR, "..");
35
42
  const runtimeServices = {
@@ -42,13 +49,19 @@ function usage() {
42
49
  return `Usage:
43
50
  agentweaver <jira-browse-url|jira-issue-key>
44
51
  agentweaver --force <jira-browse-url|jira-issue-key>
52
+ agentweaver bug-analyze [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
53
+ agentweaver bug-fix [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
54
+ agentweaver mr-description [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
45
55
  agentweaver plan [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
56
+ agentweaver task-describe [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
46
57
  agentweaver implement [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
47
58
  agentweaver review [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
48
59
  agentweaver review-fix [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
49
60
  agentweaver test [--dry] [--verbose] <jira-browse-url|jira-issue-key>
50
61
  agentweaver test-fix [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
51
62
  agentweaver test-linter-fix [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
63
+ agentweaver run-tests-loop [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
64
+ agentweaver run-linter-loop [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
52
65
  agentweaver auto [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
53
66
  agentweaver auto [--dry] [--verbose] [--prompt <text>] --from <phase> <jira-browse-url|jira-issue-key>
54
67
  agentweaver auto --help-phases
@@ -67,7 +80,7 @@ Flags:
67
80
  --prompt Extra prompt text appended to the base prompt
68
81
 
69
82
  Required environment variables:
70
- JIRA_API_KEY Jira API key used in Authorization: Bearer <token> for plan
83
+ JIRA_API_KEY Jira API key used in Authorization: Bearer <token> for Jira-backed flows
71
84
 
72
85
  Optional environment variables:
73
86
  JIRA_BASE_URL
@@ -370,13 +383,20 @@ function buildConfig(command, jiraRef, options = {}) {
370
383
  };
371
384
  }
372
385
  function checkPrerequisites(config) {
373
- if (config.command === "plan" || config.command === "review") {
386
+ if (config.command === "bug-analyze" ||
387
+ config.command === "bug-fix" ||
388
+ config.command === "mr-description" ||
389
+ config.command === "plan" ||
390
+ config.command === "task-describe" ||
391
+ config.command === "review" ||
392
+ config.command === "run-tests-loop" ||
393
+ config.command === "run-linter-loop") {
374
394
  resolveCmd("codex", "CODEX_BIN");
375
395
  }
376
396
  if (config.command === "review") {
377
397
  resolveCmd("claude", "CLAUDE_BIN");
378
398
  }
379
- if (["implement", "review-fix", "test"].includes(config.command)) {
399
+ if (["implement", "review-fix", "test", "run-tests-loop", "run-linter-loop"].includes(config.command)) {
380
400
  resolveDockerComposeCmd();
381
401
  if (!existsSync(config.dockerComposeFile)) {
382
402
  throw new TaskRunnerError(`docker-compose file not found: ${config.dockerComposeFile}`);
@@ -431,13 +451,19 @@ function autoFlowDefinition() {
431
451
  function interactiveFlowDefinitions() {
432
452
  return [
433
453
  autoFlowDefinition(),
454
+ declarativeFlowDefinition("bug-analyze", "bug-analyze", "bug-analyze.json"),
455
+ declarativeFlowDefinition("bug-fix", "bug-fix", "bug-fix.json"),
456
+ declarativeFlowDefinition("mr-description", "mr-description", "mr-description.json"),
434
457
  declarativeFlowDefinition("plan", "plan", "plan.json"),
458
+ declarativeFlowDefinition("task-describe", "task-describe", "task-describe.json"),
435
459
  declarativeFlowDefinition("implement", "implement", "implement.json"),
436
460
  declarativeFlowDefinition("review", "review", "review.json"),
437
461
  declarativeFlowDefinition("review-fix", "review-fix", "review-fix.json"),
438
462
  declarativeFlowDefinition("test", "test", "test.json"),
439
463
  declarativeFlowDefinition("test-fix", "test-fix", "test-fix.json"),
440
464
  declarativeFlowDefinition("test-linter-fix", "test-linter-fix", "test-linter-fix.json"),
465
+ declarativeFlowDefinition("run-tests-loop", "run-tests-loop", "run-tests-loop.json"),
466
+ declarativeFlowDefinition("run-linter-loop", "run-linter-loop", "run-linter-loop.json"),
441
467
  ];
442
468
  }
443
469
  function publishFlowState(flowId, executionState) {
@@ -589,6 +615,49 @@ async function executeCommand(config, runFollowupVerify = true) {
589
615
  });
590
616
  return false;
591
617
  }
618
+ if (config.command === "bug-analyze") {
619
+ if (config.verbose) {
620
+ process.stdout.write(`Fetching Jira issue from browse URL: ${config.jiraBrowseUrl}\n`);
621
+ process.stdout.write(`Resolved Jira API URL: ${config.jiraApiUrl}\n`);
622
+ process.stdout.write(`Saving Jira issue JSON to: ${config.jiraTaskFile}\n`);
623
+ }
624
+ await runDeclarativeFlowBySpecFile("bug-analyze.json", config, {
625
+ jiraApiUrl: config.jiraApiUrl,
626
+ taskKey: config.taskKey,
627
+ extraPrompt: config.extraPrompt,
628
+ });
629
+ return false;
630
+ }
631
+ if (config.command === "bug-fix") {
632
+ requireJiraTaskFile(config.jiraTaskFile);
633
+ requireArtifacts(bugAnalyzeArtifacts(config.taskKey), "Bug-fix mode requires bug-analyze artifacts from the bug analysis phase.");
634
+ validateStructuredArtifacts([
635
+ { path: bugAnalyzeJsonFile(config.taskKey), schemaId: "bug-analysis/v1" },
636
+ { path: bugFixDesignJsonFile(config.taskKey), schemaId: "bug-fix-design/v1" },
637
+ { path: bugFixPlanJsonFile(config.taskKey), schemaId: "bug-fix-plan/v1" },
638
+ ], "Bug-fix mode requires valid structured artifacts from the bug analysis phase.");
639
+ await runDeclarativeFlowBySpecFile("bug-fix.json", config, {
640
+ taskKey: config.taskKey,
641
+ extraPrompt: config.extraPrompt,
642
+ });
643
+ return false;
644
+ }
645
+ if (config.command === "mr-description") {
646
+ requireJiraTaskFile(config.jiraTaskFile);
647
+ await runDeclarativeFlowBySpecFile("mr-description.json", config, {
648
+ taskKey: config.taskKey,
649
+ extraPrompt: config.extraPrompt,
650
+ });
651
+ return false;
652
+ }
653
+ if (config.command === "task-describe") {
654
+ requireJiraTaskFile(config.jiraTaskFile);
655
+ await runDeclarativeFlowBySpecFile("task-describe.json", config, {
656
+ taskKey: config.taskKey,
657
+ extraPrompt: config.extraPrompt,
658
+ });
659
+ return false;
660
+ }
592
661
  if (config.command === "implement") {
593
662
  requireJiraTaskFile(config.jiraTaskFile);
594
663
  requireArtifacts(planArtifacts(config.taskKey), "Implement mode requires plan artifacts from the planning phase.");
@@ -674,6 +743,14 @@ async function executeCommand(config, runFollowupVerify = true) {
674
743
  });
675
744
  return false;
676
745
  }
746
+ if (config.command === "run-tests-loop" || config.command === "run-linter-loop") {
747
+ await runDeclarativeFlowBySpecFile(config.command === "run-tests-loop" ? "run-tests-loop.json" : "run-linter-loop.json", config, {
748
+ taskKey: config.taskKey,
749
+ dockerComposeFile: config.dockerComposeFile,
750
+ extraPrompt: config.extraPrompt,
751
+ });
752
+ return false;
753
+ }
677
754
  throw new TaskRunnerError(`Unsupported command: ${config.command}`);
678
755
  }
679
756
  async function runAutoPipelineDryRun(config) {