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
@@ -0,0 +1,89 @@
1
+ {
2
+ "kind": "task-describe-flow",
3
+ "version": 1,
4
+ "phases": [
5
+ {
6
+ "id": "task_describe",
7
+ "steps": [
8
+ {
9
+ "id": "run_codex_task_describe",
10
+ "node": "codex-local-prompt",
11
+ "prompt": {
12
+ "inlineTemplate": "Посмотри задачу в {jira_task_file}. Проанализируй код и оформи краткое описание для Jira, без лишних подробностей. Сначала запиши source-of-truth JSON в {jira_description_json_file} в виде объекта { summary: string }, затем markdown-версию в {jira_description_file}.",
13
+ "vars": {
14
+ "jira_task_file": {
15
+ "artifact": {
16
+ "kind": "jira-task-file",
17
+ "taskKey": { "ref": "params.taskKey" }
18
+ }
19
+ },
20
+ "jira_description_file": {
21
+ "artifact": {
22
+ "kind": "jira-description-file",
23
+ "taskKey": { "ref": "params.taskKey" }
24
+ }
25
+ },
26
+ "jira_description_json_file": {
27
+ "artifact": {
28
+ "kind": "jira-description-json-file",
29
+ "taskKey": { "ref": "params.taskKey" }
30
+ }
31
+ }
32
+ },
33
+ "extraPrompt": { "ref": "params.extraPrompt" },
34
+ "format": "task-prompt"
35
+ },
36
+ "params": {
37
+ "labelText": { "const": "Running Codex task description mode" },
38
+ "model": { "const": "gpt-5.4" },
39
+ "requiredArtifacts": {
40
+ "list": [
41
+ {
42
+ "artifact": {
43
+ "kind": "jira-description-file",
44
+ "taskKey": { "ref": "params.taskKey" }
45
+ }
46
+ },
47
+ {
48
+ "artifact": {
49
+ "kind": "jira-description-json-file",
50
+ "taskKey": { "ref": "params.taskKey" }
51
+ }
52
+ }
53
+ ]
54
+ }
55
+ },
56
+ "expect": [
57
+ {
58
+ "kind": "require-file",
59
+ "when": { "not": { "ref": "context.dryRun" } },
60
+ "path": {
61
+ "artifact": {
62
+ "kind": "jira-description-file",
63
+ "taskKey": { "ref": "params.taskKey" }
64
+ }
65
+ },
66
+ "message": "Task describe mode did not produce the Jira description artifact."
67
+ },
68
+ {
69
+ "kind": "require-structured-artifacts",
70
+ "when": { "not": { "ref": "context.dryRun" } },
71
+ "items": [
72
+ {
73
+ "path": {
74
+ "artifact": {
75
+ "kind": "jira-description-json-file",
76
+ "taskKey": { "ref": "params.taskKey" }
77
+ }
78
+ },
79
+ "schemaId": "jira-description/v1"
80
+ }
81
+ ],
82
+ "message": "Task describe mode produced invalid structured artifacts."
83
+ }
84
+ ]
85
+ }
86
+ ]
87
+ }
88
+ ]
89
+ }
@@ -4,11 +4,14 @@ import { codexDockerPromptNode } from "./nodes/codex-docker-prompt-node.js";
4
4
  import { codexLocalPromptNode } from "./nodes/codex-local-prompt-node.js";
5
5
  import { commandCheckNode } from "./nodes/command-check-node.js";
6
6
  import { fileCheckNode } from "./nodes/file-check-node.js";
7
+ import { flowRunNode } from "./nodes/flow-run-node.js";
7
8
  import { jiraFetchNode } from "./nodes/jira-fetch-node.js";
8
9
  import { planCodexNode } from "./nodes/plan-codex-node.js";
9
10
  import { reviewClaudeNode } from "./nodes/review-claude-node.js";
11
+ import { reviewFindingsFormNode } from "./nodes/review-findings-form-node.js";
10
12
  import { reviewReplyCodexNode } from "./nodes/review-reply-codex-node.js";
11
13
  import { summaryFileLoadNode } from "./nodes/summary-file-load-node.js";
14
+ import { userInputNode } from "./nodes/user-input-node.js";
12
15
  import { verifyBuildNode } from "./nodes/verify-build-node.js";
13
16
  const builtInNodes = {
14
17
  "build-failure-summary": buildFailureSummaryNode,
@@ -17,11 +20,14 @@ const builtInNodes = {
17
20
  "codex-local-prompt": codexLocalPromptNode,
18
21
  "command-check": commandCheckNode,
19
22
  "file-check": fileCheckNode,
23
+ "flow-run": flowRunNode,
20
24
  "jira-fetch": jiraFetchNode,
21
25
  "plan-codex": planCodexNode,
22
26
  "review-claude": reviewClaudeNode,
27
+ "review-findings-form": reviewFindingsFormNode,
23
28
  "review-reply-codex": reviewReplyCodexNode,
24
29
  "summary-file-load": summaryFileLoadNode,
30
+ "user-input": userInputNode,
25
31
  "verify-build": verifyBuildNode,
26
32
  };
27
33
  const builtInNodeMetadata = {
@@ -36,6 +42,7 @@ const builtInNodeMetadata = {
36
42
  "codex-local-prompt": { kind: "codex-local-prompt", version: 1, prompt: "required", requiredParams: ["labelText"] },
37
43
  "command-check": { kind: "command-check", version: 1, prompt: "forbidden", requiredParams: ["commands"] },
38
44
  "file-check": { kind: "file-check", version: 1, prompt: "forbidden", requiredParams: ["path"] },
45
+ "flow-run": { kind: "flow-run", version: 1, prompt: "forbidden", requiredParams: ["fileName"] },
39
46
  "jira-fetch": { kind: "jira-fetch", version: 1, prompt: "forbidden", requiredParams: ["jiraApiUrl", "outputFile"] },
40
47
  "plan-codex": { kind: "plan-codex", version: 1, prompt: "forbidden", requiredParams: ["prompt", "requiredArtifacts"] },
41
48
  "review-claude": {
@@ -44,6 +51,12 @@ const builtInNodeMetadata = {
44
51
  prompt: "forbidden",
45
52
  requiredParams: ["jiraTaskFile", "taskKey", "iteration", "claudeCmd"],
46
53
  },
54
+ "review-findings-form": {
55
+ kind: "review-findings-form",
56
+ version: 1,
57
+ prompt: "forbidden",
58
+ requiredParams: ["reviewJsonFile", "formId", "title"],
59
+ },
47
60
  "review-reply-codex": {
48
61
  kind: "review-reply-codex",
49
62
  version: 1,
@@ -51,6 +64,12 @@ const builtInNodeMetadata = {
51
64
  requiredParams: ["jiraTaskFile", "taskKey", "iteration", "codexCmd"],
52
65
  },
53
66
  "summary-file-load": { kind: "summary-file-load", version: 1, prompt: "forbidden", requiredParams: ["path"] },
67
+ "user-input": {
68
+ kind: "user-input",
69
+ version: 1,
70
+ prompt: "forbidden",
71
+ requiredParams: ["formId", "title", "fields", "outputFile"],
72
+ },
54
73
  "verify-build": { kind: "verify-build", version: 1, prompt: "forbidden", requiredParams: ["dockerComposeFile", "labelText"] },
55
74
  };
56
75
  export function createNodeRegistry() {
@@ -0,0 +1,40 @@
1
+ import { printInfo } from "../../tui.js";
2
+ import { runExpandedPhase } from "../declarative-flow-runner.js";
3
+ import { loadDeclarativeFlow } from "../declarative-flows.js";
4
+ export const flowRunNode = {
5
+ kind: "flow-run",
6
+ version: 1,
7
+ async run(context, params) {
8
+ const { fileName, labelText, ...flowParams } = params;
9
+ if (typeof fileName !== "string" || fileName.trim().length === 0) {
10
+ throw new Error("flow-run node requires non-empty 'fileName' param");
11
+ }
12
+ if (labelText) {
13
+ printInfo(String(labelText));
14
+ }
15
+ const flow = loadDeclarativeFlow(fileName);
16
+ const executionState = {
17
+ flowKind: flow.kind,
18
+ flowVersion: flow.version,
19
+ terminated: false,
20
+ phases: [],
21
+ };
22
+ for (const phase of flow.phases) {
23
+ await runExpandedPhase(phase, context, flowParams, flow.constants, {
24
+ executionState,
25
+ flowKind: flow.kind,
26
+ flowVersion: flow.version,
27
+ });
28
+ if (executionState.terminated) {
29
+ break;
30
+ }
31
+ }
32
+ return {
33
+ value: {
34
+ flowKind: flow.kind,
35
+ flowVersion: flow.version,
36
+ executionState,
37
+ },
38
+ };
39
+ },
40
+ };
@@ -0,0 +1,65 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { TaskRunnerError } from "../../errors.js";
3
+ export const reviewFindingsFormNode = {
4
+ kind: "review-findings-form",
5
+ version: 1,
6
+ async run(_context, params) {
7
+ let parsed;
8
+ try {
9
+ parsed = JSON.parse(readFileSync(params.reviewJsonFile, "utf8"));
10
+ }
11
+ catch (error) {
12
+ throw new TaskRunnerError(`Failed to read review findings from ${params.reviewJsonFile}: ${error.message}`);
13
+ }
14
+ const review = parsed;
15
+ const findings = Array.isArray(review.findings) ? review.findings : [];
16
+ const selectableFindings = findings
17
+ .map((finding) => ({
18
+ severity: typeof finding.severity === "string" ? finding.severity.trim() : "",
19
+ title: typeof finding.title === "string" ? finding.title.trim() : "",
20
+ description: typeof finding.description === "string" ? finding.description.trim() : "",
21
+ }))
22
+ .filter((finding) => finding.title.length > 0);
23
+ const fields = [
24
+ {
25
+ id: "apply_all",
26
+ type: "boolean",
27
+ label: "Исправить все findings в этой итерации",
28
+ help: "Если включено, выбор списка ниже не ограничивает scope исправлений.",
29
+ default: selectableFindings.length === 0,
30
+ },
31
+ ];
32
+ if (selectableFindings.length > 0) {
33
+ fields.push({
34
+ id: "selected_findings",
35
+ type: "multi-select",
36
+ label: "Какие findings исправить сейчас",
37
+ help: "Space переключает пункт. Если apply_all=false, выберите хотя бы один finding.",
38
+ options: selectableFindings.map((finding) => ({
39
+ value: finding.title,
40
+ label: `[${finding.severity || "info"}] ${finding.title}`,
41
+ ...(finding.description ? { description: finding.description } : {}),
42
+ })),
43
+ default: [],
44
+ });
45
+ }
46
+ fields.push({
47
+ id: "extra_notes",
48
+ type: "text",
49
+ label: "Дополнительные указания",
50
+ help: "Короткий комментарий для этой итерации review-fix.",
51
+ default: "",
52
+ placeholder: "Например: исправить только блокеры",
53
+ });
54
+ return {
55
+ value: {
56
+ formId: params.formId,
57
+ title: params.title,
58
+ ...(params.description ? { description: params.description } : {}),
59
+ submitLabel: "Запустить review-fix",
60
+ fields,
61
+ findingCount: selectableFindings.length,
62
+ },
63
+ };
64
+ },
65
+ };
@@ -0,0 +1,93 @@
1
+ import { writeFileSync } from "node:fs";
2
+ import { TaskRunnerError } from "../../errors.js";
3
+ import { printSummary } from "../../tui.js";
4
+ import { requestUserInputInTerminal, validateUserInputValues, } from "../../user-input.js";
5
+ function labelForSingleValue(field, value) {
6
+ if (field.type !== "single-select" && field.type !== "multi-select") {
7
+ return value;
8
+ }
9
+ return field.options.find((option) => option.value === value)?.label ?? value;
10
+ }
11
+ function buildReviewFixPromptSuffix(params, values) {
12
+ const applyAll = values.apply_all === true;
13
+ const selectedFindings = Array.isArray(values.selected_findings)
14
+ ? values.selected_findings.filter((item) => typeof item === "string" && item.trim().length > 0)
15
+ : [];
16
+ const extraNotes = typeof values.extra_notes === "string" ? values.extra_notes.trim() : "";
17
+ if (!applyAll && selectedFindings.length === 0) {
18
+ throw new TaskRunnerError("Review-fix requires selecting at least one finding or enabling 'apply all'.");
19
+ }
20
+ const selectionSummary = applyAll
21
+ ? "Выбраны все findings."
22
+ : `Выбраны findings:\n- ${selectedFindings.join("\n- ")}`;
23
+ const promptSuffix = [
24
+ "Используй пользовательский выбор ниже как source of truth для scope текущего review-fix.",
25
+ `Файл выбора: ${params.outputFile}`,
26
+ `apply_all: ${applyAll ? "true" : "false"}`,
27
+ applyAll ? "Исправляй все findings текущей итерации." : `Исправляй только выбранные findings:\n- ${selectedFindings.join("\n- ")}`,
28
+ extraNotes ? `Дополнительные указания пользователя:\n${extraNotes}` : "",
29
+ ]
30
+ .filter((item) => item.trim().length > 0)
31
+ .join("\n\n");
32
+ const summaryText = extraNotes ? `${selectionSummary}\n\nЗаметка:\n${extraNotes}` : selectionSummary;
33
+ return { promptSuffix, summaryText };
34
+ }
35
+ function buildPromptSuffix(params, values) {
36
+ if (params.formId === "review-fix-selection") {
37
+ return buildReviewFixPromptSuffix(params, values);
38
+ }
39
+ const lines = params.fields.map((field) => {
40
+ const raw = values[field.id];
41
+ if (typeof raw === "boolean") {
42
+ return `${field.label}: ${raw ? "yes" : "no"}`;
43
+ }
44
+ if (typeof raw === "string") {
45
+ return `${field.label}: ${raw || "-"}`;
46
+ }
47
+ if (Array.isArray(raw)) {
48
+ const labels = raw.map((item) => labelForSingleValue(field, item));
49
+ return `${field.label}: ${labels.length > 0 ? labels.join(", ") : "-"}`;
50
+ }
51
+ return `${field.label}: -`;
52
+ });
53
+ const summaryText = lines.join("\n");
54
+ return {
55
+ promptSuffix: `Используй пользовательский ввод из файла ${params.outputFile}.\n\n${summaryText}`,
56
+ summaryText,
57
+ };
58
+ }
59
+ export const userInputNode = {
60
+ kind: "user-input",
61
+ version: 1,
62
+ async run(context, params) {
63
+ const form = {
64
+ formId: params.formId,
65
+ title: params.title,
66
+ ...(params.description ? { description: params.description } : {}),
67
+ ...(params.submitLabel ? { submitLabel: params.submitLabel } : {}),
68
+ fields: params.fields,
69
+ };
70
+ const requester = context.requestUserInput ?? requestUserInputInTerminal;
71
+ const result = await requester(form);
72
+ validateUserInputValues(form, result.values);
73
+ const rendered = buildPromptSuffix(params, result.values);
74
+ const artifact = {
75
+ form_id: result.formId,
76
+ submitted_at: result.submittedAt,
77
+ values: result.values,
78
+ };
79
+ writeFileSync(params.outputFile, `${JSON.stringify(artifact, null, 2)}\n`, "utf8");
80
+ printSummary(params.title, rendered.summaryText);
81
+ return {
82
+ value: {
83
+ formId: result.formId,
84
+ submittedAt: result.submittedAt,
85
+ values: result.values,
86
+ outputFile: params.outputFile,
87
+ promptSuffix: rendered.promptSuffix,
88
+ summaryText: rendered.summaryText,
89
+ },
90
+ outputs: [{ kind: "artifact", path: params.outputFile, required: true }],
91
+ };
92
+ },
93
+ };
@@ -8,6 +8,7 @@ export const verifyBuildNode = {
8
8
  const executor = context.executors.get("verify-build");
9
9
  const value = await executor.execute(toExecutorContext(context), {
10
10
  dockerComposeFile: params.dockerComposeFile,
11
+ ...(params.service ? { service: params.service } : {}),
11
12
  }, executor.defaultConfig);
12
13
  return { value };
13
14
  },
@@ -1,12 +1,17 @@
1
- import { IMPLEMENT_PROMPT_TEMPLATE, PLAN_PROMPT_TEMPLATE, REVIEW_FIX_PROMPT_TEMPLATE, REVIEW_PROMPT_TEMPLATE, REVIEW_REPLY_PROMPT_TEMPLATE, REVIEW_REPLY_SUMMARY_PROMPT_TEMPLATE, REVIEW_SUMMARY_PROMPT_TEMPLATE, TASK_SUMMARY_PROMPT_TEMPLATE, TEST_FIX_PROMPT_TEMPLATE, TEST_LINTER_FIX_PROMPT_TEMPLATE, } from "../prompts.js";
1
+ import { BUG_ANALYZE_PROMPT_TEMPLATE, BUG_FIX_PROMPT_TEMPLATE, IMPLEMENT_PROMPT_TEMPLATE, MR_DESCRIPTION_PROMPT_TEMPLATE, PLAN_PROMPT_TEMPLATE, REVIEW_FIX_PROMPT_TEMPLATE, REVIEW_PROMPT_TEMPLATE, REVIEW_REPLY_PROMPT_TEMPLATE, REVIEW_REPLY_SUMMARY_PROMPT_TEMPLATE, REVIEW_SUMMARY_PROMPT_TEMPLATE, RUN_LINTER_LOOP_FIX_PROMPT_TEMPLATE, RUN_TESTS_LOOP_FIX_PROMPT_TEMPLATE, TASK_SUMMARY_PROMPT_TEMPLATE, TEST_FIX_PROMPT_TEMPLATE, TEST_LINTER_FIX_PROMPT_TEMPLATE, } from "../prompts.js";
2
2
  const promptTemplates = {
3
+ "bug-analyze": BUG_ANALYZE_PROMPT_TEMPLATE,
4
+ "bug-fix": BUG_FIX_PROMPT_TEMPLATE,
3
5
  implement: IMPLEMENT_PROMPT_TEMPLATE,
6
+ "mr-description": MR_DESCRIPTION_PROMPT_TEMPLATE,
4
7
  plan: PLAN_PROMPT_TEMPLATE,
5
8
  review: REVIEW_PROMPT_TEMPLATE,
6
9
  "review-fix": REVIEW_FIX_PROMPT_TEMPLATE,
7
10
  "review-reply": REVIEW_REPLY_PROMPT_TEMPLATE,
8
11
  "review-reply-summary": REVIEW_REPLY_SUMMARY_PROMPT_TEMPLATE,
9
12
  "review-summary": REVIEW_SUMMARY_PROMPT_TEMPLATE,
13
+ "run-linter-loop-fix": RUN_LINTER_LOOP_FIX_PROMPT_TEMPLATE,
14
+ "run-tests-loop-fix": RUN_TESTS_LOOP_FIX_PROMPT_TEMPLATE,
10
15
  "task-summary": TASK_SUMMARY_PROMPT_TEMPLATE,
11
16
  "test-fix": TEST_FIX_PROMPT_TEMPLATE,
12
17
  "test-linter-fix": TEST_LINTER_FIX_PROMPT_TEMPLATE,
@@ -123,6 +123,18 @@ function interpolateExpectation(expectation, repeatVars) {
123
123
  message: interpolateText(expectation.message, repeatVars),
124
124
  };
125
125
  }
126
+ if (expectation.kind === "require-structured-artifacts") {
127
+ const when = expectation.when ? interpolateCondition(expectation.when, repeatVars) : undefined;
128
+ return {
129
+ kind: expectation.kind,
130
+ ...(when ? { when } : {}),
131
+ items: expectation.items.map((item) => ({
132
+ path: interpolateValueSpec(item.path, repeatVars),
133
+ schemaId: item.schemaId,
134
+ })),
135
+ message: interpolateText(expectation.message, repeatVars),
136
+ };
137
+ }
126
138
  const when = expectation.when ? interpolateCondition(expectation.when, repeatVars) : undefined;
127
139
  if (expectation.kind === "require-file") {
128
140
  return {
@@ -180,6 +192,7 @@ function expandRepeat(block) {
180
192
  for (let index = block.repeat.from; index <= block.repeat.to; index += 1) {
181
193
  const repeatVars = {
182
194
  [block.repeat.var]: index,
195
+ [`${block.repeat.var}_minus_one`]: index - 1,
183
196
  };
184
197
  for (const phase of block.phases) {
185
198
  phases.push(expandPhase(phase, repeatVars));
@@ -108,6 +108,12 @@ function validateExpectation(expectation, path) {
108
108
  validateValueSpec(expectation.paths, `${path}.paths`);
109
109
  return;
110
110
  }
111
+ if (expectation.kind === "require-structured-artifacts") {
112
+ expectation.items.forEach((item, index) => {
113
+ validateValueSpec(item.path, `${path}.items[${index}].path`);
114
+ });
115
+ return;
116
+ }
111
117
  if (expectation.kind === "require-file") {
112
118
  validateValueSpec(expectation.path, `${path}.path`);
113
119
  return;
@@ -268,6 +274,12 @@ export function validateExpandedPhases(phases) {
268
274
  validateExpandedValueSpec(expectation.paths, phases, phaseIndex, stepIndex, `phases.${phase.id}.steps.${step.id}.expect[${index}].paths`);
269
275
  return;
270
276
  }
277
+ if (expectation.kind === "require-structured-artifacts") {
278
+ expectation.items.forEach((item, itemIndex) => {
279
+ validateExpandedValueSpec(item.path, phases, phaseIndex, stepIndex, `phases.${phase.id}.steps.${step.id}.expect[${index}].items[${itemIndex}].path`);
280
+ });
281
+ return;
282
+ }
271
283
  if (expectation.kind === "require-file") {
272
284
  validateExpandedValueSpec(expectation.path, phases, phaseIndex, stepIndex, `phases.${phase.id}.steps.${step.id}.expect[${index}].path`);
273
285
  return;
@@ -1,5 +1,5 @@
1
1
  import { existsSync } from "node:fs";
2
- import { artifactFile, designFile, jiraTaskFile, planArtifacts, planFile, qaFile, readyToMergeFile, taskSummaryFile, } from "../artifacts.js";
2
+ import { artifactFile, bugAnalyzeArtifacts, bugAnalyzeFile, bugAnalyzeJsonFile, bugFixDesignFile, bugFixDesignJsonFile, bugFixPlanFile, bugFixPlanJsonFile, designFile, designJsonFile, jiraDescriptionFile, jiraDescriptionJsonFile, jiraTaskFile, mrDescriptionFile, mrDescriptionJsonFile, planArtifacts, planFile, planJsonFile, qaFile, qaJsonFile, readyToMergeFile, reviewFile, reviewFixFile, reviewFixJsonFile, reviewJsonFile, reviewReplyFile, reviewReplyJsonFile, taskSummaryFile, taskSummaryJsonFile, } from "../artifacts.js";
3
3
  import { TaskRunnerError } from "../errors.js";
4
4
  import { formatTemplate } from "../prompts.js";
5
5
  function readStepRef(segments, context, originalPath) {
@@ -72,31 +72,72 @@ function resolveArtifact(spec, context) {
72
72
  const taskKey = String(resolveValue(spec.taskKey, context));
73
73
  const iteration = spec.iteration === undefined ? undefined : Number(resolveValue(spec.iteration, context));
74
74
  switch (spec.kind) {
75
+ case "bug-analyze-file":
76
+ return bugAnalyzeFile(taskKey);
77
+ case "bug-analyze-json-file":
78
+ return bugAnalyzeJsonFile(taskKey);
79
+ case "bug-fix-design-file":
80
+ return bugFixDesignFile(taskKey);
81
+ case "bug-fix-design-json-file":
82
+ return bugFixDesignJsonFile(taskKey);
83
+ case "bug-fix-plan-file":
84
+ return bugFixPlanFile(taskKey);
85
+ case "bug-fix-plan-json-file":
86
+ return bugFixPlanJsonFile(taskKey);
75
87
  case "design-file":
76
88
  return designFile(taskKey);
89
+ case "design-json-file":
90
+ return designJsonFile(taskKey);
91
+ case "jira-description-file":
92
+ return jiraDescriptionFile(taskKey);
93
+ case "jira-description-json-file":
94
+ return jiraDescriptionJsonFile(taskKey);
77
95
  case "jira-task-file":
78
96
  return jiraTaskFile(taskKey);
97
+ case "mr-description-file":
98
+ return mrDescriptionFile(taskKey);
99
+ case "mr-description-json-file":
100
+ return mrDescriptionJsonFile(taskKey);
79
101
  case "plan-file":
80
102
  return planFile(taskKey);
103
+ case "plan-json-file":
104
+ return planJsonFile(taskKey);
81
105
  case "qa-file":
82
106
  return qaFile(taskKey);
107
+ case "qa-json-file":
108
+ return qaJsonFile(taskKey);
83
109
  case "ready-to-merge-file":
84
110
  return readyToMergeFile(taskKey);
85
111
  case "review-file":
86
112
  if (iteration === undefined) {
87
113
  throw new TaskRunnerError("review-file requires iteration");
88
114
  }
89
- return artifactFile("review", taskKey, iteration);
115
+ return reviewFile(taskKey, iteration);
116
+ case "review-json-file":
117
+ if (iteration === undefined) {
118
+ throw new TaskRunnerError("review-json-file requires iteration");
119
+ }
120
+ return reviewJsonFile(taskKey, iteration);
90
121
  case "review-fix-file":
91
122
  if (iteration === undefined) {
92
123
  throw new TaskRunnerError("review-fix-file requires iteration");
93
124
  }
94
- return artifactFile("review-fix", taskKey, iteration);
125
+ return reviewFixFile(taskKey, iteration);
126
+ case "review-fix-json-file":
127
+ if (iteration === undefined) {
128
+ throw new TaskRunnerError("review-fix-json-file requires iteration");
129
+ }
130
+ return reviewFixJsonFile(taskKey, iteration);
95
131
  case "review-reply-file":
96
132
  if (iteration === undefined) {
97
133
  throw new TaskRunnerError("review-reply-file requires iteration");
98
134
  }
99
- return artifactFile("review-reply", taskKey, iteration);
135
+ return reviewReplyFile(taskKey, iteration);
136
+ case "review-reply-json-file":
137
+ if (iteration === undefined) {
138
+ throw new TaskRunnerError("review-reply-json-file requires iteration");
139
+ }
140
+ return reviewReplyJsonFile(taskKey, iteration);
100
141
  case "review-reply-summary-file":
101
142
  if (iteration === undefined) {
102
143
  throw new TaskRunnerError("review-reply-summary-file requires iteration");
@@ -109,11 +150,15 @@ function resolveArtifact(spec, context) {
109
150
  return artifactFile("review-summary", taskKey, iteration);
110
151
  case "task-summary-file":
111
152
  return taskSummaryFile(taskKey);
153
+ case "task-summary-json-file":
154
+ return taskSummaryJsonFile(taskKey);
112
155
  }
113
156
  }
114
157
  function resolveArtifactList(spec, context) {
115
158
  const taskKey = String(resolveValue(spec.taskKey, context));
116
159
  switch (spec.kind) {
160
+ case "bug-analyze-artifacts":
161
+ return bugAnalyzeArtifacts(taskKey);
117
162
  case "plan-artifacts":
118
163
  return planArtifacts(taskKey);
119
164
  }
package/dist/prompts.js CHANGED
@@ -1,33 +1,65 @@
1
1
  export const BASE_PROMPT_HEADER = "Основная задача:";
2
2
  export const EXTRA_PROMPT_HEADER = "Дополнительные указания:";
3
3
  export const PLAN_PROMPT_TEMPLATE = "Посмотри и проанализируй задачу в {jira_task_file}. " +
4
- "Разработай системный дизайн решения, запиши в {design_file}. " +
5
- "Разработай подробный план реализации и запиши его в {plan_file}. " +
6
- "Разработай план тестирования для QA и запиши в {qa_file}. ";
7
- export const IMPLEMENT_PROMPT_TEMPLATE = "Проанализируй системный дизайн {design_file}, план реализации {plan_file} и приступай к реализации по плану. ";
4
+ "Сначала создай структурированные JSON-артефакты, они являются source of truth для следующих flow. " +
5
+ "Человекочитаемые markdown-файлы сделай как краткое производное представление этих JSON-артефактов для пользователя. " +
6
+ "Разработай системный дизайн решения и запиши JSON в {design_json_file}, затем markdown в {design_file}. " +
7
+ "Для {design_json_file} используй объект: { summary: string, goals: string[], non_goals: string[], components: string[], decisions: [{ component: string, decision: string, rationale: string }], risks: string[], open_questions: string[] }. " +
8
+ "Разработай подробный план реализации и запиши JSON в {plan_json_file}, затем markdown в {plan_file}. " +
9
+ "Для {plan_json_file} используй объект: { summary: string, prerequisites: string[], implementation_steps: [{ id: string, title: string, details: string }], tests: string[], rollout_notes: string[] }. " +
10
+ "Разработай план тестирования для QA и запиши JSON в {qa_json_file}, затем markdown в {qa_file}. " +
11
+ "Для {qa_json_file} используй объект: { summary: string, test_scenarios: [{ id: string, title: string, expected_result: string }], non_functional_checks: string[] }. " +
12
+ "JSON-файлы должны быть валидными и содержать только JSON без markdown-обёртки. ";
13
+ export const BUG_ANALYZE_PROMPT_TEMPLATE = "Посмотри и проанализируй баг в {jira_task_file}. " +
14
+ "Сначала создай структурированные JSON-артефакты, они являются source of truth для следующих flow. " +
15
+ "Человекочитаемые markdown-файлы сделай как краткое производное представление этих JSON-артефактов для пользователя. " +
16
+ "Запиши структурированный анализ бага в {bug_analyze_json_file}, затем краткую markdown-версию в {bug_analyze_file}. " +
17
+ "Запиши структурированный дизайн исправления в {bug_fix_design_json_file}, затем краткую markdown-версию в {bug_fix_design_file}. " +
18
+ "Запиши структурированный план реализации в {bug_fix_plan_json_file}, затем краткую markdown-версию в {bug_fix_plan_file}. " +
19
+ "JSON-файлы должны быть валидными и содержать только JSON без markdown-обёртки. " +
20
+ "Для {bug_analyze_json_file} используй объект: { summary: string, suspected_root_cause: { hypothesis: string, confidence: string }, reproduction_steps: string[], affected_components: string[], evidence: string[], risks: string[], open_questions: string[] }. " +
21
+ "Для {bug_fix_design_json_file} используй объект: { summary: string, goals: string[], non_goals: string[], target_components: string[], proposed_changes: [{ component: string, change: string, rationale: string }], alternatives_considered: [{ option: string, decision: string, rationale: string }], risks: string[], validation_strategy: string[] }. " +
22
+ "Для {bug_fix_plan_json_file} используй объект: { summary: string, prerequisites: string[], implementation_steps: [{ id: string, title: string, details: string }], tests: string[], rollout_notes: string[] }. ";
23
+ export const BUG_FIX_PROMPT_TEMPLATE = "Используй только структурированные артефакты как source of truth. " +
24
+ "Проанализируй баг по {bug_analyze_json_file}. " +
25
+ "Используй дизайн исправления из {bug_fix_design_json_file}. " +
26
+ "Используй план реализации из {bug_fix_plan_json_file}. " +
27
+ "Markdown-артефакты предназначены только для чтения человеком и не должны определять реализацию. " +
28
+ "После этого приступай к реализации исправления в коде. ";
29
+ export const MR_DESCRIPTION_PROMPT_TEMPLATE = "Посмотри задачу в {jira_task_file} и текущие изменения в репозитории. " +
30
+ "Подготовь очень краткое intent-описание для merge request без подробностей реализации, списков файлов и технических деталей. " +
31
+ "Сначала запиши source-of-truth JSON в {mr_description_json_file} в виде объекта { summary: string }, затем производную markdown-версию в {mr_description_file}. ";
32
+ export const IMPLEMENT_PROMPT_TEMPLATE = "Используй только структурированные артефакты как source of truth. " +
33
+ "Проанализируй системный дизайн {design_json_file}, план реализации {plan_json_file} и приступай к реализации по плану. " +
34
+ "Markdown-артефакты предназначены только для чтения человеком и не должны определять реализацию. ";
8
35
  export const REVIEW_PROMPT_TEMPLATE = "Проведи код-ревью текущих изменений. " +
9
- "Сверься с задачей в {jira_task_file}, дизайном {design_file} и планом {plan_file}. " +
10
- "Замечания и комментарии запиши в {review_file}. " +
11
- "Если больше нет блокеров, препятствующих merge - создай файл ready-to-merge.md.";
12
- export const REVIEW_REPLY_PROMPT_TEMPLATE = "Твой коллега провёл код-ревью и записал комментарии в {review_file}. " +
13
- "Проанализируй комментарии к код-ревью, сверься с задачей в {jira_task_file}, " +
14
- "дизайном {design_file}, планом {plan_file} и запиши свои комментарии в {review_reply_file}.";
36
+ "Используй только структурированные артефакты как source of truth: задачу в {jira_task_file}, дизайн в {design_json_file} и план в {plan_json_file}. " +
37
+ "Сначала запиши структурированный результат в {review_json_file} в виде объекта { summary: string, ready_to_merge: boolean, findings: [{ severity: string, title: string, description: string }] }. " +
38
+ "Затем запиши производную markdown-версию в {review_file}. " +
39
+ "Если ready_to_merge=true и нет блокеров, препятствующих merge - создай файл ready-to-merge.md.";
40
+ export const REVIEW_REPLY_PROMPT_TEMPLATE = "Твой коллега провёл код-ревью и записал структурированный результат в {review_json_file}. " +
41
+ "Используй только структурированные артефакты как source of truth: задачу в {jira_task_file}, дизайн в {design_json_file}, план в {plan_json_file} и review в {review_json_file}. " +
42
+ "Сначала запиши структурированный ответ в {review_reply_json_file} в виде объекта { summary: string, ready_to_merge: boolean, responses: [{ finding_title: string, disposition: string, action: string }] }. " +
43
+ "Затем запиши производную markdown-версию в {review_reply_file}.";
15
44
  export const REVIEW_SUMMARY_PROMPT_TEMPLATE = "Посмотри в {review_file}. " +
16
45
  "Сделай краткий список комментариев без подробностей, 3-7 пунктов. " +
17
46
  "Запиши результат в {review_summary_file}.";
18
47
  export const REVIEW_REPLY_SUMMARY_PROMPT_TEMPLATE = "Посмотри в {review_reply_file}. " +
19
48
  "Сделай краткий список ответов и итоговых действий без подробностей, 3-7 пунктов. " +
20
49
  "Запиши результат в {review_reply_summary_file}.";
21
- export const REVIEW_FIX_PROMPT_TEMPLATE = "Проанализируй комментарии в {review_reply_file}. " +
50
+ export const REVIEW_FIX_PROMPT_TEMPLATE = "Используй только структурированные артефакты как source of truth. " +
51
+ "Проанализируй комментарии в {review_reply_json_file}. " +
22
52
  "Исправь то, что содержится в дополнительных указаниях, а если таковых нет - исправь все пункты. " +
23
53
  "По окончании обязательно прогони вне песочницы линтер, все тесты, сгенерируй make swagger. " +
24
54
  "Исправь ошибки линтера и тестов, если будут. " +
25
- "По завершении резюме запиши в {review_fix_file}.";
55
+ "По завершении сначала запиши структурированный отчёт в {review_fix_json_file} в виде объекта { summary: string, completed_actions: string[], validation_steps: string[] }, затем производную markdown-версию в {review_fix_file}.";
26
56
  export const TASK_SUMMARY_PROMPT_TEMPLATE = "Посмотри в {jira_task_file}. " +
27
- "Сделай краткое резюме задачи, на 1-2 абзаца, " +
28
- "запиши в {task_summary_file}.";
57
+ "Сделай краткое резюме задачи, на 1-2 абзаца. " +
58
+ "Сначала запиши source-of-truth JSON в {task_summary_json_file} в виде объекта { summary: string }, затем markdown-версию в {task_summary_file}.";
29
59
  export const TEST_FIX_PROMPT_TEMPLATE = "Прогони тесты, исправь ошибки.";
30
60
  export const TEST_LINTER_FIX_PROMPT_TEMPLATE = "Прогони линтер, исправь замечания.";
61
+ export const RUN_TESTS_LOOP_FIX_PROMPT_TEMPLATE = "Запусти ./run_tests.sh, проанализируй последнюю ошибку проверки, исправь код и подготовь изменения так, чтобы следующий прогон run_tests.sh прошёл успешно.";
62
+ export const RUN_LINTER_LOOP_FIX_PROMPT_TEMPLATE = "Запусти ./run_linter.sh, проанализируй последнюю ошибку линтера или генерации, исправь код и подготовь изменения так, чтобы следующий прогон run_linter.sh прошёл успешно.";
31
63
  export const AUTO_REVIEW_FIX_EXTRA_PROMPT = "Исправлять только блокеры, критикалы и важные";
32
64
  export function formatTemplate(template, values) {
33
65
  let result = template;