agentweaver 0.1.4 → 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.
- package/README.md +1 -1
- package/dist/artifacts.js +55 -6
- package/dist/index.js +94 -19
- package/dist/interactive-ui.js +433 -28
- package/dist/pipeline/context.js +1 -0
- package/dist/pipeline/flow-specs/auto.json +174 -0
- package/dist/pipeline/flow-specs/implement.json +12 -0
- package/dist/pipeline/flow-specs/mr-description.json +28 -0
- package/dist/pipeline/flow-specs/plan.json +52 -0
- package/dist/pipeline/flow-specs/preflight.json +32 -0
- package/dist/pipeline/flow-specs/review-fix.json +79 -1
- package/dist/pipeline/flow-specs/review.json +79 -0
- package/dist/pipeline/flow-specs/task-describe.json +29 -1
- package/dist/pipeline/node-registry.js +16 -0
- package/dist/pipeline/nodes/review-findings-form-node.js +65 -0
- package/dist/pipeline/nodes/user-input-node.js +93 -0
- package/dist/pipeline/value-resolver.js +31 -4
- package/dist/prompts.js +26 -15
- package/dist/structured-artifacts.js +176 -82
- package/dist/user-input.js +171 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ The package is designed to run as an npm CLI and includes an interactive termina
|
|
|
12
12
|
|
|
13
13
|
- Fetches a Jira issue by key or browse URL
|
|
14
14
|
- Generates workflow artifacts such as design, implementation plan, QA plan, bug analysis, reviews, and summaries
|
|
15
|
-
-
|
|
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
16
|
- Runs workflow stages like `bug-analyze`, `bug-fix`, `mr-description`, `plan`, `task-describe`, `implement`, `review`, `review-fix`, `test`, and `auto`
|
|
17
17
|
- Persists compact `auto` pipeline state on disk so runs can resume without storing large agent outputs
|
|
18
18
|
- Uses Docker runtime services for isolated Codex execution and build verification
|
package/dist/artifacts.js
CHANGED
|
@@ -11,61 +11,89 @@ 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
|
+
}
|
|
28
44
|
export function bugAnalyzeFile(taskKey) {
|
|
29
45
|
return taskWorkspaceFile(taskKey, `bug-analyze-${taskKey}.md`);
|
|
30
46
|
}
|
|
31
47
|
export function bugAnalyzeJsonFile(taskKey) {
|
|
32
|
-
return
|
|
48
|
+
return taskArtifactsFile(taskKey, `bug-analyze-${taskKey}.json`);
|
|
33
49
|
}
|
|
34
50
|
export function bugFixDesignFile(taskKey) {
|
|
35
51
|
return taskWorkspaceFile(taskKey, `bug-fix-design-${taskKey}.md`);
|
|
36
52
|
}
|
|
37
53
|
export function bugFixDesignJsonFile(taskKey) {
|
|
38
|
-
return
|
|
54
|
+
return taskArtifactsFile(taskKey, `bug-fix-design-${taskKey}.json`);
|
|
39
55
|
}
|
|
40
56
|
export function bugFixPlanFile(taskKey) {
|
|
41
57
|
return taskWorkspaceFile(taskKey, `bug-fix-plan-${taskKey}.md`);
|
|
42
58
|
}
|
|
43
59
|
export function bugFixPlanJsonFile(taskKey) {
|
|
44
|
-
return
|
|
60
|
+
return taskArtifactsFile(taskKey, `bug-fix-plan-${taskKey}.json`);
|
|
45
61
|
}
|
|
46
62
|
export function qaFile(taskKey) {
|
|
47
63
|
return artifactFile("qa", taskKey, 1);
|
|
48
64
|
}
|
|
65
|
+
export function qaJsonFile(taskKey) {
|
|
66
|
+
return artifactJsonFile("qa", taskKey, 1);
|
|
67
|
+
}
|
|
49
68
|
export function taskSummaryFile(taskKey) {
|
|
50
69
|
return artifactFile("task", taskKey, 1);
|
|
51
70
|
}
|
|
71
|
+
export function taskSummaryJsonFile(taskKey) {
|
|
72
|
+
return artifactJsonFile("task", taskKey, 1);
|
|
73
|
+
}
|
|
52
74
|
export function readyToMergeFile(taskKey) {
|
|
53
75
|
return taskWorkspaceFile(taskKey, READY_TO_MERGE_FILE);
|
|
54
76
|
}
|
|
55
77
|
export function jiraTaskFile(taskKey) {
|
|
56
|
-
return
|
|
78
|
+
return taskArtifactsFile(taskKey, `${taskKey}.json`);
|
|
57
79
|
}
|
|
58
80
|
export function jiraDescriptionFile(taskKey) {
|
|
59
81
|
return taskWorkspaceFile(taskKey, `jira-${taskKey}-description.md`);
|
|
60
82
|
}
|
|
83
|
+
export function jiraDescriptionJsonFile(taskKey) {
|
|
84
|
+
return taskArtifactsFile(taskKey, `jira-${taskKey}-description.json`);
|
|
85
|
+
}
|
|
61
86
|
export function mrDescriptionFile(taskKey) {
|
|
62
87
|
return taskWorkspaceFile(taskKey, `mr-description-${taskKey}.md`);
|
|
63
88
|
}
|
|
89
|
+
export function mrDescriptionJsonFile(taskKey) {
|
|
90
|
+
return taskArtifactsFile(taskKey, `mr-description-${taskKey}.json`);
|
|
91
|
+
}
|
|
64
92
|
export function autoStateFile(taskKey) {
|
|
65
|
-
return
|
|
93
|
+
return taskArtifactsFile(taskKey, `.agentweaver-state-${taskKey}.json`);
|
|
66
94
|
}
|
|
67
95
|
export function planArtifacts(taskKey) {
|
|
68
|
-
return [designFile(taskKey), planFile(taskKey), qaFile(taskKey)];
|
|
96
|
+
return [designFile(taskKey), designJsonFile(taskKey), planFile(taskKey), planJsonFile(taskKey), qaFile(taskKey), qaJsonFile(taskKey)];
|
|
69
97
|
}
|
|
70
98
|
export function bugAnalyzeArtifacts(taskKey) {
|
|
71
99
|
return [
|
|
@@ -77,6 +105,27 @@ export function bugAnalyzeArtifacts(taskKey) {
|
|
|
77
105
|
bugFixPlanJsonFile(taskKey),
|
|
78
106
|
];
|
|
79
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);
|
|
128
|
+
}
|
|
80
129
|
export function requireArtifacts(paths, message) {
|
|
81
130
|
const missing = paths.filter((filePath) => !existsSync(filePath));
|
|
82
131
|
if (missing.length > 0) {
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ 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, bugAnalyzeArtifacts, bugAnalyzeJsonFile, bugFixDesignJsonFile, bugFixPlanJsonFile, ensureTaskWorkspaceDir, jiraTaskFile, planArtifacts, readyToMergeFile, requireArtifacts, taskWorkspaceDir, taskSummaryFile, } from "./artifacts.js";
|
|
6
|
+
import { REVIEW_FILE_RE, REVIEW_REPLY_FILE_RE, autoStateFile, bugAnalyzeArtifacts, bugAnalyzeJsonFile, bugFixDesignJsonFile, bugFixPlanJsonFile, designJsonFile, ensureTaskWorkspaceDir, jiraTaskFile, planJsonFile, planArtifacts, qaJsonFile, readyToMergeFile, requireArtifacts, reviewReplyJsonFile, reviewFixSelectionJsonFile, reviewJsonFile, taskWorkspaceDir, taskSummaryFile, } from "./artifacts.js";
|
|
7
7
|
import { TaskRunnerError } from "./errors.js";
|
|
8
8
|
import { buildJiraApiUrl, buildJiraBrowseUrl, extractIssueKey, requireJiraTaskFile } from "./jira.js";
|
|
9
9
|
import { validateStructuredArtifacts } from "./structured-artifacts.js";
|
|
@@ -17,7 +17,8 @@ import { resolveCmd, resolveDockerComposeCmd } from "./runtime/command-resolutio
|
|
|
17
17
|
import { defaultDockerComposeFile, dockerRuntimeEnv } from "./runtime/docker-runtime.js";
|
|
18
18
|
import { runCommand } from "./runtime/process-runner.js";
|
|
19
19
|
import { InteractiveUi } from "./interactive-ui.js";
|
|
20
|
-
import { bye, printError, printInfo, printPanel, printSummary, setFlowExecutionState } from "./tui.js";
|
|
20
|
+
import { bye, printError, printInfo, printPanel, printSummary, setFlowExecutionState, stripAnsi, } from "./tui.js";
|
|
21
|
+
import { requestUserInputInTerminal } from "./user-input.js";
|
|
21
22
|
const COMMANDS = [
|
|
22
23
|
"bug-analyze",
|
|
23
24
|
"bug-fix",
|
|
@@ -45,6 +46,37 @@ const runtimeServices = {
|
|
|
45
46
|
dockerRuntimeEnv: () => dockerRuntimeEnv(PACKAGE_ROOT),
|
|
46
47
|
runCommand,
|
|
47
48
|
};
|
|
49
|
+
function buildFailureOutputPreview(output) {
|
|
50
|
+
const normalized = stripAnsi(output).replace(/\r\n/g, "\n").trim();
|
|
51
|
+
if (!normalized) {
|
|
52
|
+
return "";
|
|
53
|
+
}
|
|
54
|
+
const lines = normalized
|
|
55
|
+
.split("\n")
|
|
56
|
+
.map((line) => line.trimEnd())
|
|
57
|
+
.filter((line) => line.trim().length > 0);
|
|
58
|
+
if (lines.length === 0) {
|
|
59
|
+
return "";
|
|
60
|
+
}
|
|
61
|
+
const previewLines = lines.slice(-8);
|
|
62
|
+
let preview = previewLines.join("\n");
|
|
63
|
+
const maxLength = 1200;
|
|
64
|
+
if (preview.length > maxLength) {
|
|
65
|
+
preview = `...${preview.slice(-(maxLength - 3))}`;
|
|
66
|
+
}
|
|
67
|
+
return preview;
|
|
68
|
+
}
|
|
69
|
+
function formatProcessFailure(error) {
|
|
70
|
+
const returnCode = Number(error.returnCode);
|
|
71
|
+
const baseMessage = !Number.isNaN(returnCode)
|
|
72
|
+
? `Command failed with exit code ${returnCode}`
|
|
73
|
+
: error.message?.trim() || "Command failed";
|
|
74
|
+
const preview = buildFailureOutputPreview(String(error.output ?? ""));
|
|
75
|
+
if (!preview) {
|
|
76
|
+
return baseMessage;
|
|
77
|
+
}
|
|
78
|
+
return `${baseMessage}\nПричина:\n${preview}`;
|
|
79
|
+
}
|
|
48
80
|
function usage() {
|
|
49
81
|
return `Usage:
|
|
50
82
|
agentweaver <jira-browse-url|jira-issue-key>
|
|
@@ -420,11 +452,31 @@ function autoFlowParams(config) {
|
|
|
420
452
|
reviewFixPoints: config.reviewFixPoints,
|
|
421
453
|
};
|
|
422
454
|
}
|
|
455
|
+
const FLOW_DESCRIPTIONS = {
|
|
456
|
+
auto: "Полный пайплайн задачи: планирование, реализация, проверки, ревью, ответы на ревью и повторные итерации до готовности к merge.",
|
|
457
|
+
"bug-analyze": "Анализирует баг по Jira и создаёт структурированные артефакты: гипотезу причины, дизайн исправления и план работ.",
|
|
458
|
+
"bug-fix": "Берёт результаты bug-analyze как source of truth и реализует исправление бага в коде.",
|
|
459
|
+
"mr-description": "Готовит краткое intent-описание для merge request на основе задачи и текущих изменений.",
|
|
460
|
+
plan: "Загружает задачу из Jira и создаёт дизайн, план реализации и QA-план в structured JSON и markdown.",
|
|
461
|
+
"task-describe": "Строит короткое резюме задачи на основе Jira-артефакта для быстрого ознакомления.",
|
|
462
|
+
implement: "Реализует задачу по утверждённым design/plan артефактам и при необходимости запускает post-verify сборки.",
|
|
463
|
+
review: "Запускает Claude-код-ревью текущих изменений, валидирует structured findings, затем готовит ответ на замечания через Codex.",
|
|
464
|
+
"review-fix": "Исправляет замечания после review-reply, обновляет код и прогоняет обязательные проверки после правок.",
|
|
465
|
+
test: "Запускает verify/build-проверку в контейнере и показывает результат с краткой сводкой при падении.",
|
|
466
|
+
"test-fix": "Прогоняет тесты, исправляет найденные проблемы и готовит код к следующей успешной проверке.",
|
|
467
|
+
"test-linter-fix": "Прогоняет линтер и генерацию, затем исправляет замечания для чистого прогона.",
|
|
468
|
+
"run-tests-loop": "Циклически запускает `./run_tests.sh`, анализирует последнюю ошибку и правит код до успешного прохождения или исчерпания попыток.",
|
|
469
|
+
"run-linter-loop": "Циклически запускает `./run_linter.sh`, исправляет проблемы линтера или генерации и повторяет попытки до успеха.",
|
|
470
|
+
};
|
|
471
|
+
function flowDescription(id) {
|
|
472
|
+
return FLOW_DESCRIPTIONS[id] ?? "Описание для этого flow пока не задано.";
|
|
473
|
+
}
|
|
423
474
|
function declarativeFlowDefinition(id, label, fileName) {
|
|
424
475
|
const flow = loadDeclarativeFlow(fileName);
|
|
425
476
|
return {
|
|
426
477
|
id,
|
|
427
478
|
label,
|
|
479
|
+
description: flowDescription(id),
|
|
428
480
|
phases: flow.phases.map((phase) => ({
|
|
429
481
|
id: phase.id,
|
|
430
482
|
repeatVars: Object.fromEntries(Object.entries(phase.repeatVars).map(([key, value]) => [key, value])),
|
|
@@ -439,6 +491,7 @@ function autoFlowDefinition() {
|
|
|
439
491
|
return {
|
|
440
492
|
id: "auto",
|
|
441
493
|
label: "auto",
|
|
494
|
+
description: flowDescription("auto"),
|
|
442
495
|
phases: flow.phases.map((phase) => ({
|
|
443
496
|
id: phase.id,
|
|
444
497
|
repeatVars: Object.fromEntries(Object.entries(phase.repeatVars).map(([key, value]) => [key, value])),
|
|
@@ -469,13 +522,14 @@ function interactiveFlowDefinitions() {
|
|
|
469
522
|
function publishFlowState(flowId, executionState) {
|
|
470
523
|
setFlowExecutionState(flowId, stripExecutionStatePayload(executionState));
|
|
471
524
|
}
|
|
472
|
-
async function runDeclarativeFlowBySpecFile(fileName, config, flowParams) {
|
|
525
|
+
async function runDeclarativeFlowBySpecFile(fileName, config, flowParams, requestUserInput = requestUserInputInTerminal) {
|
|
473
526
|
const context = createPipelineContext({
|
|
474
527
|
issueKey: config.taskKey,
|
|
475
528
|
jiraRef: config.jiraRef,
|
|
476
529
|
dryRun: config.dryRun,
|
|
477
530
|
verbose: config.verbose,
|
|
478
531
|
runtime: runtimeServices,
|
|
532
|
+
requestUserInput,
|
|
479
533
|
});
|
|
480
534
|
const flow = loadDeclarativeFlow(fileName);
|
|
481
535
|
const executionState = {
|
|
@@ -503,6 +557,7 @@ async function runAutoPhaseViaSpec(config, phaseId, executionState, state) {
|
|
|
503
557
|
dryRun: config.dryRun,
|
|
504
558
|
verbose: config.verbose,
|
|
505
559
|
runtime: runtimeServices,
|
|
560
|
+
requestUserInput: requestUserInputInTerminal,
|
|
506
561
|
});
|
|
507
562
|
const autoFlow = loadAutoFlow();
|
|
508
563
|
const phase = findPhaseById(autoFlow.phases, phaseId);
|
|
@@ -577,9 +632,10 @@ async function summarizeBuildFailure(output) {
|
|
|
577
632
|
dryRun: false,
|
|
578
633
|
verbose: false,
|
|
579
634
|
runtime: runtimeServices,
|
|
635
|
+
requestUserInput: requestUserInputInTerminal,
|
|
580
636
|
}), output);
|
|
581
637
|
}
|
|
582
|
-
async function executeCommand(config, runFollowupVerify = true) {
|
|
638
|
+
async function executeCommand(config, runFollowupVerify = true, requestUserInput = requestUserInputInTerminal) {
|
|
583
639
|
if (config.command === "auto") {
|
|
584
640
|
await runAutoPipeline(config);
|
|
585
641
|
return false;
|
|
@@ -612,7 +668,7 @@ async function executeCommand(config, runFollowupVerify = true) {
|
|
|
612
668
|
jiraApiUrl: config.jiraApiUrl,
|
|
613
669
|
taskKey: config.taskKey,
|
|
614
670
|
extraPrompt: config.extraPrompt,
|
|
615
|
-
});
|
|
671
|
+
}, requestUserInput);
|
|
616
672
|
return false;
|
|
617
673
|
}
|
|
618
674
|
if (config.command === "bug-analyze") {
|
|
@@ -625,7 +681,7 @@ async function executeCommand(config, runFollowupVerify = true) {
|
|
|
625
681
|
jiraApiUrl: config.jiraApiUrl,
|
|
626
682
|
taskKey: config.taskKey,
|
|
627
683
|
extraPrompt: config.extraPrompt,
|
|
628
|
-
});
|
|
684
|
+
}, requestUserInput);
|
|
629
685
|
return false;
|
|
630
686
|
}
|
|
631
687
|
if (config.command === "bug-fix") {
|
|
@@ -639,7 +695,7 @@ async function executeCommand(config, runFollowupVerify = true) {
|
|
|
639
695
|
await runDeclarativeFlowBySpecFile("bug-fix.json", config, {
|
|
640
696
|
taskKey: config.taskKey,
|
|
641
697
|
extraPrompt: config.extraPrompt,
|
|
642
|
-
});
|
|
698
|
+
}, requestUserInput);
|
|
643
699
|
return false;
|
|
644
700
|
}
|
|
645
701
|
if (config.command === "mr-description") {
|
|
@@ -647,7 +703,7 @@ async function executeCommand(config, runFollowupVerify = true) {
|
|
|
647
703
|
await runDeclarativeFlowBySpecFile("mr-description.json", config, {
|
|
648
704
|
taskKey: config.taskKey,
|
|
649
705
|
extraPrompt: config.extraPrompt,
|
|
650
|
-
});
|
|
706
|
+
}, requestUserInput);
|
|
651
707
|
return false;
|
|
652
708
|
}
|
|
653
709
|
if (config.command === "task-describe") {
|
|
@@ -655,19 +711,24 @@ async function executeCommand(config, runFollowupVerify = true) {
|
|
|
655
711
|
await runDeclarativeFlowBySpecFile("task-describe.json", config, {
|
|
656
712
|
taskKey: config.taskKey,
|
|
657
713
|
extraPrompt: config.extraPrompt,
|
|
658
|
-
});
|
|
714
|
+
}, requestUserInput);
|
|
659
715
|
return false;
|
|
660
716
|
}
|
|
661
717
|
if (config.command === "implement") {
|
|
662
718
|
requireJiraTaskFile(config.jiraTaskFile);
|
|
663
719
|
requireArtifacts(planArtifacts(config.taskKey), "Implement mode requires plan artifacts from the planning phase.");
|
|
720
|
+
validateStructuredArtifacts([
|
|
721
|
+
{ path: designJsonFile(config.taskKey), schemaId: "implementation-design/v1" },
|
|
722
|
+
{ path: planJsonFile(config.taskKey), schemaId: "implementation-plan/v1" },
|
|
723
|
+
{ path: qaJsonFile(config.taskKey), schemaId: "qa-plan/v1" },
|
|
724
|
+
], "Implement mode requires valid structured plan artifacts from the planning phase.");
|
|
664
725
|
try {
|
|
665
726
|
await runDeclarativeFlowBySpecFile("implement.json", config, {
|
|
666
727
|
taskKey: config.taskKey,
|
|
667
728
|
dockerComposeFile: config.dockerComposeFile,
|
|
668
729
|
extraPrompt: config.extraPrompt,
|
|
669
730
|
runFollowupVerify,
|
|
670
|
-
});
|
|
731
|
+
}, requestUserInput);
|
|
671
732
|
}
|
|
672
733
|
catch (error) {
|
|
673
734
|
if (!config.dryRun) {
|
|
@@ -683,25 +744,38 @@ async function executeCommand(config, runFollowupVerify = true) {
|
|
|
683
744
|
}
|
|
684
745
|
if (config.command === "review") {
|
|
685
746
|
requireJiraTaskFile(config.jiraTaskFile);
|
|
747
|
+
validateStructuredArtifacts([
|
|
748
|
+
{ path: designJsonFile(config.taskKey), schemaId: "implementation-design/v1" },
|
|
749
|
+
{ path: planJsonFile(config.taskKey), schemaId: "implementation-plan/v1" },
|
|
750
|
+
], "Review mode requires valid structured plan artifacts from the planning phase.");
|
|
686
751
|
const iteration = nextReviewIterationForTask(config.taskKey);
|
|
687
752
|
await runDeclarativeFlowBySpecFile("review.json", config, {
|
|
688
753
|
taskKey: config.taskKey,
|
|
689
754
|
iteration,
|
|
690
755
|
extraPrompt: config.extraPrompt,
|
|
691
|
-
});
|
|
756
|
+
}, requestUserInput);
|
|
692
757
|
return !config.dryRun && existsSync(readyToMergeFile(config.taskKey));
|
|
693
758
|
}
|
|
694
759
|
if (config.command === "review-fix") {
|
|
695
760
|
requireJiraTaskFile(config.jiraTaskFile);
|
|
761
|
+
const latestIteration = latestReviewReplyIteration(config.taskKey);
|
|
762
|
+
if (latestIteration === null) {
|
|
763
|
+
throw new TaskRunnerError("Review-fix mode requires at least one review-reply artifact.");
|
|
764
|
+
}
|
|
765
|
+
validateStructuredArtifacts([
|
|
766
|
+
{ path: reviewJsonFile(config.taskKey, latestIteration), schemaId: "review-findings/v1" },
|
|
767
|
+
{ path: reviewReplyJsonFile(config.taskKey, latestIteration), schemaId: "review-reply/v1" },
|
|
768
|
+
], "Review-fix mode requires valid structured review artifacts.");
|
|
696
769
|
try {
|
|
697
770
|
await runDeclarativeFlowBySpecFile("review-fix.json", config, {
|
|
698
771
|
taskKey: config.taskKey,
|
|
699
772
|
dockerComposeFile: config.dockerComposeFile,
|
|
700
|
-
latestIteration
|
|
773
|
+
latestIteration,
|
|
774
|
+
reviewFixSelectionJsonFile: reviewFixSelectionJsonFile(config.taskKey, latestIteration),
|
|
701
775
|
runFollowupVerify,
|
|
702
776
|
extraPrompt: config.extraPrompt,
|
|
703
777
|
reviewFixPoints: config.reviewFixPoints,
|
|
704
|
-
});
|
|
778
|
+
}, requestUserInput);
|
|
705
779
|
}
|
|
706
780
|
catch (error) {
|
|
707
781
|
if (!config.dryRun) {
|
|
@@ -721,7 +795,7 @@ async function executeCommand(config, runFollowupVerify = true) {
|
|
|
721
795
|
await runDeclarativeFlowBySpecFile("test.json", config, {
|
|
722
796
|
taskKey: config.taskKey,
|
|
723
797
|
dockerComposeFile: config.dockerComposeFile,
|
|
724
|
-
});
|
|
798
|
+
}, requestUserInput);
|
|
725
799
|
}
|
|
726
800
|
catch (error) {
|
|
727
801
|
if (!config.dryRun) {
|
|
@@ -740,7 +814,7 @@ async function executeCommand(config, runFollowupVerify = true) {
|
|
|
740
814
|
await runDeclarativeFlowBySpecFile(config.command === "test-fix" ? "test-fix.json" : "test-linter-fix.json", config, {
|
|
741
815
|
taskKey: config.taskKey,
|
|
742
816
|
extraPrompt: config.extraPrompt,
|
|
743
|
-
});
|
|
817
|
+
}, requestUserInput);
|
|
744
818
|
return false;
|
|
745
819
|
}
|
|
746
820
|
if (config.command === "run-tests-loop" || config.command === "run-linter-loop") {
|
|
@@ -748,7 +822,7 @@ async function executeCommand(config, runFollowupVerify = true) {
|
|
|
748
822
|
taskKey: config.taskKey,
|
|
749
823
|
dockerComposeFile: config.dockerComposeFile,
|
|
750
824
|
extraPrompt: config.extraPrompt,
|
|
751
|
-
});
|
|
825
|
+
}, requestUserInput);
|
|
752
826
|
return false;
|
|
753
827
|
}
|
|
754
828
|
throw new TaskRunnerError(`Unsupported command: ${config.command}`);
|
|
@@ -933,7 +1007,7 @@ async function runInteractive(jiraRef, forceRefresh = false) {
|
|
|
933
1007
|
onRun: async (flowId) => {
|
|
934
1008
|
try {
|
|
935
1009
|
const command = buildConfig(flowId, jiraRef);
|
|
936
|
-
await executeCommand(command);
|
|
1010
|
+
await executeCommand(command, true, (form) => ui.requestUserInput(form));
|
|
937
1011
|
}
|
|
938
1012
|
catch (error) {
|
|
939
1013
|
if (error instanceof TaskRunnerError) {
|
|
@@ -944,7 +1018,7 @@ async function runInteractive(jiraRef, forceRefresh = false) {
|
|
|
944
1018
|
const returnCode = Number(error.returnCode);
|
|
945
1019
|
if (!Number.isNaN(returnCode)) {
|
|
946
1020
|
ui.setFlowFailed(flowId);
|
|
947
|
-
printError(
|
|
1021
|
+
printError(formatProcessFailure(error));
|
|
948
1022
|
return;
|
|
949
1023
|
}
|
|
950
1024
|
throw error;
|
|
@@ -968,6 +1042,7 @@ async function runInteractive(jiraRef, forceRefresh = false) {
|
|
|
968
1042
|
setSummary: (markdown) => {
|
|
969
1043
|
ui.setSummary(markdown);
|
|
970
1044
|
},
|
|
1045
|
+
requestUserInput: (form) => ui.requestUserInput(form),
|
|
971
1046
|
}), {
|
|
972
1047
|
jiraApiUrl: config.jiraApiUrl,
|
|
973
1048
|
jiraTaskFile: config.jiraTaskFile,
|
|
@@ -1041,7 +1116,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1041
1116
|
}
|
|
1042
1117
|
const returnCode = Number(error.returnCode);
|
|
1043
1118
|
if (!Number.isNaN(returnCode)) {
|
|
1044
|
-
printError(
|
|
1119
|
+
printError(formatProcessFailure(error));
|
|
1045
1120
|
return returnCode || 1;
|
|
1046
1121
|
}
|
|
1047
1122
|
throw error;
|