agentweaver 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Dockerfile.codex CHANGED
@@ -45,10 +45,10 @@ RUN GOBIN=/usr/local/bin go install github.com/golangci/golangci-lint/v2/cmd/gol
45
45
  ENV PATH="/usr/local/go/bin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
46
46
 
47
47
  COPY verify_build.sh /usr/local/bin/verify_build.sh
48
- COPY run_go_tests.sh /usr/local/bin/run_go_tests.sh
49
- COPY run_go_linter.sh /usr/local/bin/run_go_linter.sh
48
+ COPY run_go_tests.py /usr/local/bin/run_go_tests.py
49
+ COPY run_go_linter.py /usr/local/bin/run_go_linter.py
50
50
  COPY run_go_coverage.sh /usr/local/bin/run_go_coverage.sh
51
- RUN chmod +x /usr/local/bin/verify_build.sh /usr/local/bin/run_go_tests.sh /usr/local/bin/run_go_linter.sh /usr/local/bin/run_go_coverage.sh
51
+ RUN chmod +x /usr/local/bin/verify_build.sh /usr/local/bin/run_go_tests.py /usr/local/bin/run_go_linter.py /usr/local/bin/run_go_coverage.sh
52
52
 
53
53
  WORKDIR /workspace
54
54
 
package/README.md CHANGED
@@ -45,8 +45,8 @@ This keeps command handlers focused on choosing a flow and providing parameters
45
45
  - `docker-compose.yml` — runtime services for Codex and build verification
46
46
  - `Dockerfile.codex` — container image for Codex runtime
47
47
  - `verify_build.sh` — aggregated verification entrypoint used by `verify-build`
48
- - `run_go_tests.sh` — isolated Go test verification entrypoint
49
- - `run_go_linter.sh` — isolated Go generate + lint verification entrypoint
48
+ - `run_go_tests.py` — isolated Go test verification entrypoint
49
+ - `run_go_linter.py` — isolated Go generate + lint verification entrypoint
50
50
  - `run_go_coverage.sh` — isolated Go coverage verification entrypoint
51
51
  - `package.json` — npm package metadata and scripts
52
52
  - `tsconfig.json` — TypeScript configuration
@@ -212,8 +212,8 @@ Main services:
212
212
  - `codex` — interactive Codex container
213
213
  - `codex-exec` — non-interactive `codex exec`
214
214
  - `verify-build` — project verification script inside container
215
- - `run-go-tests` — isolated `run_go_tests.sh` execution inside container
216
- - `run-go-linter` — isolated `run_go_linter.sh` execution inside container
215
+ - `run-go-tests` — isolated `run_go_tests.py` execution inside container
216
+ - `run-go-linter` — isolated `run_go_linter.py` execution inside container
217
217
  - `run-go-coverage` — isolated `run_go_coverage.sh` execution inside container
218
218
  - `codex-login` — interactive login container
219
219
  - `dockerd` — internal Docker daemon for testcontainers/build flows
package/dist/artifacts.js CHANGED
@@ -156,6 +156,12 @@ export function reviewFixJsonFile(taskKey, iteration) {
156
156
  export function reviewFixSelectionJsonFile(taskKey, iteration) {
157
157
  return artifactJsonFile("review-fix-selection", taskKey, iteration);
158
158
  }
159
+ export function runGoLinterResultJsonFile(taskKey, iteration) {
160
+ return artifactJsonFile("run-go-linter-result", taskKey, iteration);
161
+ }
162
+ export function runGoTestsResultJsonFile(taskKey, iteration) {
163
+ return artifactJsonFile("run-go-tests-result", taskKey, iteration);
164
+ }
159
165
  export function requireArtifacts(paths, message) {
160
166
  const missing = paths.filter((filePath) => !existsSync(filePath));
161
167
  if (missing.length > 0) {
package/dist/index.js CHANGED
@@ -414,8 +414,8 @@ function buildBaseConfig(command, options = {}) {
414
414
  dryRun: options.dryRun ?? false,
415
415
  verbose: options.verbose ?? false,
416
416
  dockerComposeFile: defaultDockerComposeFile(PACKAGE_ROOT),
417
- runGoTestsScript: path.join(homeDir, "run_go_tests.sh"),
418
- runGoLinterScript: path.join(homeDir, "run_go_linter.sh"),
417
+ runGoTestsScript: path.join(homeDir, "run_go_tests.py"),
418
+ runGoLinterScript: path.join(homeDir, "run_go_linter.py"),
419
419
  runGoCoverageScript: path.join(homeDir, "run_go_coverage.sh"),
420
420
  };
421
421
  }
@@ -522,8 +522,8 @@ const FLOW_DESCRIPTIONS = {
522
522
  implement: "Реализует задачу по утверждённым design/plan артефактам и при необходимости запускает post-verify сборки.",
523
523
  review: "Запускает Claude-код-ревью текущих изменений, валидирует structured findings, затем готовит ответ на замечания через Codex.",
524
524
  "review-fix": "Исправляет замечания после review-reply, обновляет код и прогоняет обязательные проверки после правок.",
525
- "run-go-tests-loop": "Циклически запускает `./run_go_tests.sh` локально, анализирует последнюю ошибку и правит код до успешного прохождения или исчерпания попыток.",
526
- "run-go-linter-loop": "Циклически запускает `./run_go_linter.sh` локально, исправляет проблемы линтера или генерации и повторяет попытки до успеха.",
525
+ "run-go-tests-loop": "Циклически запускает `./run_go_tests.py` локально, анализирует последнюю ошибку и правит код до успешного прохождения или исчерпания попыток.",
526
+ "run-go-linter-loop": "Циклически запускает `./run_go_linter.py` локально, исправляет проблемы линтера или генерации и повторяет попытки до успеха.",
527
527
  };
528
528
  function flowDescription(id) {
529
529
  return FLOW_DESCRIPTIONS[id] ?? "Описание для этого flow пока не задано.";
@@ -1128,11 +1128,11 @@ function buildConfigFromArgs(args) {
1128
1128
  }
1129
1129
  async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
1130
1130
  let currentScope = jiraRef?.trim() ? resolveTaskScope(jiraRef, scopeName) : resolveProjectScope(scopeName);
1131
- const currentIssueKey = currentScope.scopeKey;
1132
1131
  const gitBranchName = detectGitBranchName();
1133
1132
  let exiting = false;
1134
1133
  const ui = new InteractiveUi({
1135
- issueKey: currentIssueKey,
1134
+ scopeKey: currentScope.scopeKey,
1135
+ jiraIssueKey: currentScope.scopeType === "task" ? currentScope.jiraIssueKey : null,
1136
1136
  summaryText: "",
1137
1137
  cwd: process.cwd(),
1138
1138
  gitBranchName,
@@ -1186,7 +1186,7 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
1186
1186
  });
1187
1187
  const nextScope = await resolveScopeForCommand(baseConfig, (form) => ui.requestUserInput(form));
1188
1188
  currentScope = nextScope;
1189
- ui.setIssueKey(currentScope.scopeKey);
1189
+ ui.setScope(currentScope.scopeKey, currentScope.scopeType === "task" ? currentScope.jiraIssueKey : null);
1190
1190
  if (currentScope.scopeType === "task" && (previousScopeType !== "task" || previousScopeKey !== currentScope.scopeKey)) {
1191
1191
  syncInteractiveTaskSummary(ui, currentScope, forceRefresh);
1192
1192
  }
@@ -4,6 +4,8 @@ import { renderMarkdownToTerminal } from "./markdown.js";
4
4
  import { setOutputAdapter, stripAnsi } from "./tui.js";
5
5
  import { TaskRunnerError } from "./errors.js";
6
6
  import { buildInitialUserInputValues, validateUserInputValues, } from "./user-input.js";
7
+ const CONFIRM_MIN_WIDTH = 44;
8
+ const CONFIRM_MIN_HEIGHT = 8;
7
9
  export class InteractiveUi {
8
10
  options;
9
11
  screen;
@@ -39,7 +41,8 @@ export class InteractiveUi {
39
41
  failedFlowId = null;
40
42
  activeFormSession = null;
41
43
  confirmSession = null;
42
- issueKey;
44
+ scopeKey;
45
+ jiraIssueKey;
43
46
  summaryVisible;
44
47
  constructor(options) {
45
48
  this.options = options;
@@ -48,12 +51,13 @@ export class InteractiveUi {
48
51
  }
49
52
  this.flowMap = new Map(options.flows.map((flow) => [flow.id, flow]));
50
53
  this.selectedFlowId = options.flows[0]?.id ?? "auto";
51
- this.issueKey = options.issueKey;
54
+ this.scopeKey = options.scopeKey;
55
+ this.jiraIssueKey = options.jiraIssueKey ?? null;
52
56
  this.summaryVisible = options.summaryText.trim().length > 0;
53
57
  this.screen = blessed.screen({
54
58
  smartCSR: true,
55
59
  fullUnicode: true,
56
- title: `AgentWeaver ${this.issueKey}`,
60
+ title: `AgentWeaver ${this.scopeKey}`,
57
61
  dockBorders: true,
58
62
  autoPadding: false,
59
63
  });
@@ -584,7 +588,8 @@ export class InteractiveUi {
584
588
  this.header.setContent([
585
589
  "{bold}AgentWeaver{/bold}",
586
590
  divider,
587
- `{bold}Issue{/bold} {green-fg}${this.issueKey}{/green-fg}`,
591
+ `{bold}Scope{/bold} {green-fg}${this.scopeKey}{/green-fg}`,
592
+ this.jiraIssueKey ? `${divider}{bold}Jira{/bold} {yellow-fg}${this.jiraIssueKey}{/yellow-fg}` : "",
588
593
  divider,
589
594
  `{bold}Flow{/bold} ${flowLabel}`,
590
595
  divider,
@@ -1180,7 +1185,17 @@ export class InteractiveUi {
1180
1185
  lines.push("", session.details.trim());
1181
1186
  }
1182
1187
  lines.push("", actionLabels, "", "Left/Right or Tab: choose Enter: confirm Esc: cancel");
1183
- this.confirm.setContent(lines.join("\n"));
1188
+ const content = lines.join("\n");
1189
+ const contentLines = content.split("\n");
1190
+ const lineCount = contentLines.length;
1191
+ const maxLineLength = contentLines.reduce((max, line) => Math.max(max, stripAnsi(line).length), 0);
1192
+ const screenWidth = Math.max(Number(this.screen.width ?? 0), CONFIRM_MIN_WIDTH);
1193
+ const screenHeight = Math.max(Number(this.screen.height ?? 0), CONFIRM_MIN_HEIGHT);
1194
+ const desiredWidth = Math.max(CONFIRM_MIN_WIDTH, maxLineLength + 6);
1195
+ const desiredHeight = Math.max(CONFIRM_MIN_HEIGHT, lineCount + 4);
1196
+ this.confirm.width = Math.min(desiredWidth, Math.max(CONFIRM_MIN_WIDTH, screenWidth - 4));
1197
+ this.confirm.height = Math.min(desiredHeight, Math.max(CONFIRM_MIN_HEIGHT, screenHeight - 2));
1198
+ this.confirm.setContent(content);
1184
1199
  this.confirm.show();
1185
1200
  this.confirm.setFront();
1186
1201
  this.confirm.focus();
@@ -1298,9 +1313,10 @@ export class InteractiveUi {
1298
1313
  }
1299
1314
  this.requestRender();
1300
1315
  }
1301
- setIssueKey(issueKey) {
1302
- this.issueKey = issueKey;
1303
- this.screen.title = `AgentWeaver ${issueKey}`;
1316
+ setScope(scopeKey, jiraIssueKey) {
1317
+ this.scopeKey = scopeKey;
1318
+ this.jiraIssueKey = jiraIssueKey ?? null;
1319
+ this.screen.title = `AgentWeaver ${scopeKey}`;
1304
1320
  this.updateHeader();
1305
1321
  this.requestRender();
1306
1322
  }
@@ -42,7 +42,7 @@
42
42
  }
43
43
  },
44
44
  "allowedIssueTypes": {
45
- "const": ["Bug"]
45
+ "const": ["Bug", "Ошибка"]
46
46
  }
47
47
  }
48
48
  },
@@ -14,10 +14,31 @@
14
14
  { "ref": "params.runGoLinterScript" }
15
15
  ]
16
16
  },
17
+ "outputFile": {
18
+ "artifact": {
19
+ "kind": "run-go-linter-result-json-file",
20
+ "taskKey": { "ref": "params.taskKey" },
21
+ "iteration": { "const": 1 }
22
+ }
23
+ },
17
24
  "labelText": {
18
- "const": "Running run_go_linter.sh locally (attempt 1)"
25
+ "const": "Running run_go_linter.py locally (attempt 1)"
19
26
  }
20
27
  },
28
+ "expect": [
29
+ {
30
+ "kind": "require-file",
31
+ "when": { "not": { "ref": "context.dryRun" } },
32
+ "path": {
33
+ "artifact": {
34
+ "kind": "run-go-linter-result-json-file",
35
+ "taskKey": { "ref": "params.taskKey" },
36
+ "iteration": { "const": 1 }
37
+ }
38
+ },
39
+ "message": "run-go-linter-loop did not produce the structured linter result artifact."
40
+ }
41
+ ],
21
42
  "stopFlowIf": {
22
43
  "equals": [
23
44
  { "ref": "steps.run_go_linter_try_1.run_go_linter.outputs.parsed.ok" },
@@ -36,14 +57,31 @@
36
57
  "node": "codex-local-prompt",
37
58
  "prompt": {
38
59
  "templateRef": "run-go-linter-loop-fix",
60
+ "vars": {
61
+ "linter_result_json_file": {
62
+ "artifact": {
63
+ "kind": "run-go-linter-result-json-file",
64
+ "taskKey": { "ref": "params.taskKey" },
65
+ "iteration": { "const": 1 }
66
+ }
67
+ }
68
+ },
39
69
  "extraPrompt": {
40
70
  "appendPrompt": {
41
71
  "base": { "ref": "params.extraPrompt" },
42
72
  "suffix": {
43
- "template": "Последний результат run_go_linter.sh: exitCode={exit_code}, summary={summary}. Исправь только то, что мешает успешному прохождению проверки.",
73
+ "template": "Последний результат run_go_linter.py сохранён в {result_file}. exitCode={exit_code}, summary={summary}.\nПолный raw-вывод последнего запуска:\n{raw_output}\nИсправь только то, что мешает успешному прохождению проверки.",
44
74
  "vars": {
75
+ "result_file": {
76
+ "artifact": {
77
+ "kind": "run-go-linter-result-json-file",
78
+ "taskKey": { "ref": "params.taskKey" },
79
+ "iteration": { "const": 1 }
80
+ }
81
+ },
45
82
  "exit_code": { "ref": "steps.run_go_linter_try_1.run_go_linter.outputs.parsed.exitCode" },
46
- "summary": { "ref": "steps.run_go_linter_try_1.run_go_linter.outputs.parsed.summary" }
83
+ "summary": { "ref": "steps.run_go_linter_try_1.run_go_linter.outputs.parsed.summary" },
84
+ "raw_output": { "ref": "steps.run_go_linter_try_1.run_go_linter.outputs.parsed.details.raw" }
47
85
  }
48
86
  }
49
87
  }
@@ -84,10 +122,31 @@
84
122
  { "ref": "params.runGoLinterScript" }
85
123
  ]
86
124
  },
125
+ "outputFile": {
126
+ "artifact": {
127
+ "kind": "run-go-linter-result-json-file",
128
+ "taskKey": { "ref": "params.taskKey" },
129
+ "iteration": { "ref": "repeat.attempt" }
130
+ }
131
+ },
87
132
  "labelText": {
88
- "template": "Running run_go_linter.sh locally (attempt ${attempt})"
133
+ "template": "Running run_go_linter.py locally (attempt ${attempt})"
89
134
  }
90
135
  },
136
+ "expect": [
137
+ {
138
+ "kind": "require-file",
139
+ "when": { "not": { "ref": "context.dryRun" } },
140
+ "path": {
141
+ "artifact": {
142
+ "kind": "run-go-linter-result-json-file",
143
+ "taskKey": { "ref": "params.taskKey" },
144
+ "iteration": { "ref": "repeat.attempt" }
145
+ }
146
+ },
147
+ "message": "run-go-linter-loop did not produce the structured linter result artifact."
148
+ }
149
+ ],
91
150
  "stopFlowIf": {
92
151
  "equals": [
93
152
  { "ref": "steps.run_go_linter_try_${attempt}.run_go_linter.outputs.parsed.ok" },
@@ -106,14 +165,31 @@
106
165
  "node": "codex-local-prompt",
107
166
  "prompt": {
108
167
  "templateRef": "run-go-linter-loop-fix",
168
+ "vars": {
169
+ "linter_result_json_file": {
170
+ "artifact": {
171
+ "kind": "run-go-linter-result-json-file",
172
+ "taskKey": { "ref": "params.taskKey" },
173
+ "iteration": { "ref": "repeat.attempt" }
174
+ }
175
+ }
176
+ },
109
177
  "extraPrompt": {
110
178
  "appendPrompt": {
111
179
  "base": { "ref": "params.extraPrompt" },
112
180
  "suffix": {
113
- "template": "Последний результат run_go_linter.sh: exitCode={exit_code}, summary={summary}. Исправь только то, что мешает успешному прохождению проверки.",
181
+ "template": "Последний результат run_go_linter.py сохранён в {result_file}. exitCode={exit_code}, summary={summary}.\nПолный raw-вывод последнего запуска:\n{raw_output}\nИсправь только то, что мешает успешному прохождению проверки.",
114
182
  "vars": {
183
+ "result_file": {
184
+ "artifact": {
185
+ "kind": "run-go-linter-result-json-file",
186
+ "taskKey": { "ref": "params.taskKey" },
187
+ "iteration": { "ref": "repeat.attempt" }
188
+ }
189
+ },
115
190
  "exit_code": { "ref": "steps.run_go_linter_try_${attempt}.run_go_linter.outputs.parsed.exitCode" },
116
- "summary": { "ref": "steps.run_go_linter_try_${attempt}.run_go_linter.outputs.parsed.summary" }
191
+ "summary": { "ref": "steps.run_go_linter_try_${attempt}.run_go_linter.outputs.parsed.summary" },
192
+ "raw_output": { "ref": "steps.run_go_linter_try_${attempt}.run_go_linter.outputs.parsed.details.raw" }
117
193
  }
118
194
  }
119
195
  }
@@ -145,7 +221,7 @@
145
221
  "kind": "step-output",
146
222
  "value": { "ref": "steps.run_go_linter_try_5.run_go_linter.outputs.parsed.ok" },
147
223
  "equals": { "const": true },
148
- "message": "run-go-linter-loop exhausted all attempts without a successful run_go_linter.sh execution."
224
+ "message": "run-go-linter-loop exhausted all attempts without a successful run_go_linter.py execution."
149
225
  }
150
226
  ]
151
227
  }
@@ -14,10 +14,31 @@
14
14
  { "ref": "params.runGoTestsScript" }
15
15
  ]
16
16
  },
17
+ "outputFile": {
18
+ "artifact": {
19
+ "kind": "run-go-tests-result-json-file",
20
+ "taskKey": { "ref": "params.taskKey" },
21
+ "iteration": { "const": 1 }
22
+ }
23
+ },
17
24
  "labelText": {
18
- "const": "Running run_go_tests.sh locally (attempt 1)"
25
+ "const": "Running run_go_tests.py locally (attempt 1)"
19
26
  }
20
27
  },
28
+ "expect": [
29
+ {
30
+ "kind": "require-file",
31
+ "when": { "not": { "ref": "context.dryRun" } },
32
+ "path": {
33
+ "artifact": {
34
+ "kind": "run-go-tests-result-json-file",
35
+ "taskKey": { "ref": "params.taskKey" },
36
+ "iteration": { "const": 1 }
37
+ }
38
+ },
39
+ "message": "run-go-tests-loop did not produce the structured tests result artifact."
40
+ }
41
+ ],
21
42
  "stopFlowIf": {
22
43
  "equals": [
23
44
  { "ref": "steps.run_go_tests_try_1.run_go_tests.outputs.parsed.ok" },
@@ -36,14 +57,31 @@
36
57
  "node": "codex-local-prompt",
37
58
  "prompt": {
38
59
  "templateRef": "run-go-tests-loop-fix",
60
+ "vars": {
61
+ "tests_result_json_file": {
62
+ "artifact": {
63
+ "kind": "run-go-tests-result-json-file",
64
+ "taskKey": { "ref": "params.taskKey" },
65
+ "iteration": { "const": 1 }
66
+ }
67
+ }
68
+ },
39
69
  "extraPrompt": {
40
70
  "appendPrompt": {
41
71
  "base": { "ref": "params.extraPrompt" },
42
72
  "suffix": {
43
- "template": "Последний результат run_go_tests.sh: exitCode={exit_code}, summary={summary}. Исправь только то, что мешает успешному прохождению проверки.",
73
+ "template": "Последний результат run_go_tests.py сохранён в {result_file}. exitCode={exit_code}, summary={summary}.\nПолный raw-вывод последнего запуска:\n{raw_output}\nИсправь только то, что мешает успешному прохождению проверки.",
44
74
  "vars": {
75
+ "result_file": {
76
+ "artifact": {
77
+ "kind": "run-go-tests-result-json-file",
78
+ "taskKey": { "ref": "params.taskKey" },
79
+ "iteration": { "const": 1 }
80
+ }
81
+ },
45
82
  "exit_code": { "ref": "steps.run_go_tests_try_1.run_go_tests.outputs.parsed.exitCode" },
46
- "summary": { "ref": "steps.run_go_tests_try_1.run_go_tests.outputs.parsed.summary" }
83
+ "summary": { "ref": "steps.run_go_tests_try_1.run_go_tests.outputs.parsed.summary" },
84
+ "raw_output": { "ref": "steps.run_go_tests_try_1.run_go_tests.outputs.parsed.details.raw" }
47
85
  }
48
86
  }
49
87
  }
@@ -84,10 +122,31 @@
84
122
  { "ref": "params.runGoTestsScript" }
85
123
  ]
86
124
  },
125
+ "outputFile": {
126
+ "artifact": {
127
+ "kind": "run-go-tests-result-json-file",
128
+ "taskKey": { "ref": "params.taskKey" },
129
+ "iteration": { "ref": "repeat.attempt" }
130
+ }
131
+ },
87
132
  "labelText": {
88
- "template": "Running run_go_tests.sh locally (attempt ${attempt})"
133
+ "template": "Running run_go_tests.py locally (attempt ${attempt})"
89
134
  }
90
135
  },
136
+ "expect": [
137
+ {
138
+ "kind": "require-file",
139
+ "when": { "not": { "ref": "context.dryRun" } },
140
+ "path": {
141
+ "artifact": {
142
+ "kind": "run-go-tests-result-json-file",
143
+ "taskKey": { "ref": "params.taskKey" },
144
+ "iteration": { "ref": "repeat.attempt" }
145
+ }
146
+ },
147
+ "message": "run-go-tests-loop did not produce the structured tests result artifact."
148
+ }
149
+ ],
91
150
  "stopFlowIf": {
92
151
  "equals": [
93
152
  { "ref": "steps.run_go_tests_try_${attempt}.run_go_tests.outputs.parsed.ok" },
@@ -106,14 +165,31 @@
106
165
  "node": "codex-local-prompt",
107
166
  "prompt": {
108
167
  "templateRef": "run-go-tests-loop-fix",
168
+ "vars": {
169
+ "tests_result_json_file": {
170
+ "artifact": {
171
+ "kind": "run-go-tests-result-json-file",
172
+ "taskKey": { "ref": "params.taskKey" },
173
+ "iteration": { "ref": "repeat.attempt" }
174
+ }
175
+ }
176
+ },
109
177
  "extraPrompt": {
110
178
  "appendPrompt": {
111
179
  "base": { "ref": "params.extraPrompt" },
112
180
  "suffix": {
113
- "template": "Последний результат run_go_tests.sh: exitCode={exit_code}, summary={summary}. Исправь только то, что мешает успешному прохождению проверки.",
181
+ "template": "Последний результат run_go_tests.py сохранён в {result_file}. exitCode={exit_code}, summary={summary}.\nПолный raw-вывод последнего запуска:\n{raw_output}\nИсправь только то, что мешает успешному прохождению проверки.",
114
182
  "vars": {
183
+ "result_file": {
184
+ "artifact": {
185
+ "kind": "run-go-tests-result-json-file",
186
+ "taskKey": { "ref": "params.taskKey" },
187
+ "iteration": { "ref": "repeat.attempt" }
188
+ }
189
+ },
115
190
  "exit_code": { "ref": "steps.run_go_tests_try_${attempt}.run_go_tests.outputs.parsed.exitCode" },
116
- "summary": { "ref": "steps.run_go_tests_try_${attempt}.run_go_tests.outputs.parsed.summary" }
191
+ "summary": { "ref": "steps.run_go_tests_try_${attempt}.run_go_tests.outputs.parsed.summary" },
192
+ "raw_output": { "ref": "steps.run_go_tests_try_${attempt}.run_go_tests.outputs.parsed.details.raw" }
117
193
  }
118
194
  }
119
195
  }
@@ -145,7 +221,7 @@
145
221
  "kind": "step-output",
146
222
  "value": { "ref": "steps.run_go_tests_try_5.run_go_tests.outputs.parsed.ok" },
147
223
  "equals": { "const": true },
148
- "message": "run-go-tests-loop exhausted all attempts without a successful run_go_tests.sh execution."
224
+ "message": "run-go-tests-loop exhausted all attempts without a successful run_go_tests.py execution."
149
225
  }
150
226
  ]
151
227
  }
@@ -34,7 +34,7 @@
34
34
  "id": "run_codex_task_describe",
35
35
  "node": "codex-local-prompt",
36
36
  "prompt": {
37
- "inlineTemplate": "Посмотри задачу в {jira_task_file}. Проанализируй код и оформи краткое описание для Jira, без лишних подробностей. Сначала запиши source-of-truth JSON в {jira_description_json_file} в виде объекта { summary: string }, затем markdown-версию в {jira_description_file}.",
37
+ "templateRef": "task-describe",
38
38
  "vars": {
39
39
  "jira_task_file": {
40
40
  "artifact": {
@@ -1,3 +1,5 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import path from "node:path";
1
3
  import { TaskRunnerError } from "../../errors.js";
2
4
  import { printInfo } from "../../tui.js";
3
5
  function parseStructuredResult(output, commandLabel) {
@@ -59,22 +61,62 @@ function parseStructuredResult(output, commandLabel) {
59
61
  }
60
62
  throw new TaskRunnerError(`Structured result is missing or invalid in output of '${commandLabel}'.`);
61
63
  }
64
+ function fallbackStructuredResult(output, commandLabel, exitCode) {
65
+ return {
66
+ ok: false,
67
+ kind: "check",
68
+ stage: commandLabel,
69
+ exitCode,
70
+ summary: `${commandLabel} failed`,
71
+ command: commandLabel,
72
+ details: output.trim().length > 0 ? { raw: output } : {},
73
+ };
74
+ }
75
+ function persistStructuredResult(filePath, parsed) {
76
+ mkdirSync(path.dirname(filePath), { recursive: true });
77
+ writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}\n`, "utf8");
78
+ }
62
79
  export const localScriptCheckNode = {
63
80
  kind: "local-script-check",
64
81
  version: 1,
65
82
  async run(context, params) {
66
83
  printInfo(params.labelText);
67
- const output = await context.runtime.runCommand(params.argv, {
68
- dryRun: context.dryRun,
69
- verbose: context.verbose,
70
- label: params.argv.join(" "),
71
- printFailureOutput: true,
72
- env: { ...context.env },
73
- });
84
+ const commandLabel = params.argv.join(" ");
85
+ let output = "";
86
+ let parsed;
87
+ try {
88
+ output = await context.runtime.runCommand(params.argv, {
89
+ dryRun: context.dryRun,
90
+ verbose: context.verbose,
91
+ label: commandLabel,
92
+ printFailureOutput: true,
93
+ env: { ...context.env },
94
+ });
95
+ parsed = parseStructuredResult(output, commandLabel);
96
+ }
97
+ catch (error) {
98
+ output = String(error.output ?? "");
99
+ const exitCode = Number(error.returnCode ?? 1);
100
+ try {
101
+ parsed = parseStructuredResult(output, commandLabel);
102
+ }
103
+ catch {
104
+ parsed = fallbackStructuredResult(output, commandLabel, exitCode);
105
+ }
106
+ }
107
+ if (typeof parsed.details.raw !== "string") {
108
+ parsed.details = {
109
+ ...parsed.details,
110
+ raw: output,
111
+ };
112
+ }
113
+ if (params.outputFile) {
114
+ persistStructuredResult(params.outputFile, parsed);
115
+ }
74
116
  return {
75
117
  value: {
76
118
  output,
77
- parsed: parseStructuredResult(output, params.argv.join(" ")),
119
+ parsed,
78
120
  },
79
121
  };
80
122
  },
@@ -1,8 +1,9 @@
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_PROJECT_PROMPT_TEMPLATE, REVIEW_PROMPT_TEMPLATE, REVIEW_REPLY_PROJECT_PROMPT_TEMPLATE, REVIEW_REPLY_PROMPT_TEMPLATE, REVIEW_REPLY_SUMMARY_PROMPT_TEMPLATE, REVIEW_SUMMARY_PROMPT_TEMPLATE, RUN_GO_LINTER_LOOP_FIX_PROMPT_TEMPLATE, RUN_GO_TESTS_LOOP_FIX_PROMPT_TEMPLATE, TASK_SUMMARY_PROMPT_TEMPLATE, } from "../prompts.js";
1
+ import { BUG_ANALYZE_PROMPT_TEMPLATE, BUG_FIX_PROMPT_TEMPLATE, IMPLEMENT_PROMPT_TEMPLATE, JIRA_DESCRIPTION_PROMPT_TEMPLATE, MR_DESCRIPTION_PROMPT_TEMPLATE, PLAN_PROMPT_TEMPLATE, REVIEW_FIX_PROMPT_TEMPLATE, REVIEW_PROJECT_PROMPT_TEMPLATE, REVIEW_PROMPT_TEMPLATE, REVIEW_REPLY_PROJECT_PROMPT_TEMPLATE, REVIEW_REPLY_PROMPT_TEMPLATE, REVIEW_REPLY_SUMMARY_PROMPT_TEMPLATE, REVIEW_SUMMARY_PROMPT_TEMPLATE, RUN_GO_LINTER_LOOP_FIX_PROMPT_TEMPLATE, RUN_GO_TESTS_LOOP_FIX_PROMPT_TEMPLATE, TASK_SUMMARY_PROMPT_TEMPLATE, } from "../prompts.js";
2
2
  const promptTemplates = {
3
3
  "bug-analyze": BUG_ANALYZE_PROMPT_TEMPLATE,
4
4
  "bug-fix": BUG_FIX_PROMPT_TEMPLATE,
5
5
  implement: IMPLEMENT_PROMPT_TEMPLATE,
6
+ "task-describe": JIRA_DESCRIPTION_PROMPT_TEMPLATE,
6
7
  "mr-description": MR_DESCRIPTION_PROMPT_TEMPLATE,
7
8
  plan: PLAN_PROMPT_TEMPLATE,
8
9
  review: REVIEW_PROMPT_TEMPLATE,
@@ -1,5 +1,5 @@
1
1
  import { existsSync } from "node:fs";
2
- import { artifactFile, bugAnalyzeArtifacts, bugAnalyzeFile, bugAnalyzeJsonFile, bugFixDesignFile, bugFixDesignJsonFile, bugFixPlanFile, bugFixPlanJsonFile, designFile, designJsonFile, gitlabReviewFile, gitlabReviewInputJsonFile, gitlabReviewJsonFile, jiraDescriptionFile, jiraDescriptionJsonFile, jiraTaskFile, mrDescriptionFile, mrDescriptionJsonFile, planArtifacts, planFile, planJsonFile, qaFile, qaJsonFile, readyToMergeFile, reviewFile, reviewFixFile, reviewFixJsonFile, reviewJsonFile, reviewReplyFile, reviewReplyJsonFile, taskSummaryFile, taskSummaryJsonFile, } from "../artifacts.js";
2
+ import { artifactFile, bugAnalyzeArtifacts, bugAnalyzeFile, bugAnalyzeJsonFile, bugFixDesignFile, bugFixDesignJsonFile, bugFixPlanFile, bugFixPlanJsonFile, designFile, designJsonFile, gitlabReviewFile, gitlabReviewInputJsonFile, gitlabReviewJsonFile, jiraDescriptionFile, jiraDescriptionJsonFile, jiraTaskFile, mrDescriptionFile, mrDescriptionJsonFile, planArtifacts, planFile, planJsonFile, qaFile, qaJsonFile, readyToMergeFile, reviewFile, reviewFixFile, reviewFixJsonFile, reviewJsonFile, reviewReplyFile, reviewReplyJsonFile, runGoLinterResultJsonFile, runGoTestsResultJsonFile, 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) {
@@ -144,6 +144,16 @@ function resolveArtifact(spec, context) {
144
144
  throw new TaskRunnerError("review-reply-json-file requires iteration");
145
145
  }
146
146
  return reviewReplyJsonFile(taskKey, iteration);
147
+ case "run-go-linter-result-json-file":
148
+ if (iteration === undefined) {
149
+ throw new TaskRunnerError("run-go-linter-result-json-file requires iteration");
150
+ }
151
+ return runGoLinterResultJsonFile(taskKey, iteration);
152
+ case "run-go-tests-result-json-file":
153
+ if (iteration === undefined) {
154
+ throw new TaskRunnerError("run-go-tests-result-json-file requires iteration");
155
+ }
156
+ return runGoTestsResultJsonFile(taskKey, iteration);
147
157
  case "review-reply-summary-file":
148
158
  if (iteration === undefined) {
149
159
  throw new TaskRunnerError("review-reply-summary-file requires iteration");
package/dist/prompts.js CHANGED
@@ -65,8 +65,13 @@ export const REVIEW_FIX_PROMPT_TEMPLATE = "Используй только ст
65
65
  export const TASK_SUMMARY_PROMPT_TEMPLATE = "Посмотри в {jira_task_file}. " +
66
66
  "Сделай краткое резюме задачи, на 1-2 абзаца. " +
67
67
  "Сначала запиши source-of-truth JSON в {task_summary_json_file} в виде объекта { summary: string }, затем markdown-версию в {task_summary_file}.";
68
- export const RUN_GO_TESTS_LOOP_FIX_PROMPT_TEMPLATE = "Запусти ./run_go_tests.sh, проанализируй последнюю ошибку проверки, исправь код и подготовь изменения так, чтобы следующий прогон run_go_tests.sh прошёл успешно.";
69
- export const RUN_GO_LINTER_LOOP_FIX_PROMPT_TEMPLATE = "Запусти ./run_go_linter.sh, проанализируй последнюю ошибку линтера или генерации, исправь код и подготовь изменения так, чтобы следующий прогон run_go_linter.sh прошёл успешно.";
68
+ export const JIRA_DESCRIPTION_PROMPT_TEMPLATE = "Посмотри задачу в {jira_task_file}. " +
69
+ "Проанализируй код и оформи краткое описание для Jira, упомяни ключевые точки, модели данных, сервисы, REST-методы. " +
70
+ "Сначала запиши source-of-truth JSON в {jira_description_json_file} в виде объекта { summary: string }, затем markdown-версию в {jira_description_file}.";
71
+ export const RUN_GO_TESTS_LOOP_FIX_PROMPT_TEMPLATE = "Используй структурированный результат последнего запуска run_go_tests.py из {tests_result_json_file} как source of truth. " +
72
+ "Проанализируй последнюю ошибку проверки, исправь код и подготовь изменения так, чтобы следующий прогон run_go_tests.py прошёл успешно.";
73
+ export const RUN_GO_LINTER_LOOP_FIX_PROMPT_TEMPLATE = "Используй структурированный результат последнего запуска run_go_linter.py из {linter_result_json_file} как source of truth. " +
74
+ "Проанализируй последнюю ошибку линтера или генерации, исправь код и подготовь изменения так, чтобы следующий прогон run_go_linter.py прошёл успешно.";
70
75
  export const AUTO_REVIEW_FIX_EXTRA_PROMPT = "Исправлять только блокеры, критикалы и важные";
71
76
  export function formatTemplate(template, values) {
72
77
  let result = template;