agentweaver 0.1.6 → 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.
@@ -0,0 +1,231 @@
1
+ {
2
+ "kind": "run-go-linter-loop-flow",
3
+ "version": 1,
4
+ "phases": [
5
+ {
6
+ "id": "run_go_linter_try_1",
7
+ "steps": [
8
+ {
9
+ "id": "run_go_linter",
10
+ "node": "local-script-check",
11
+ "params": {
12
+ "argv": {
13
+ "list": [
14
+ { "ref": "params.runGoLinterScript" }
15
+ ]
16
+ },
17
+ "outputFile": {
18
+ "artifact": {
19
+ "kind": "run-go-linter-result-json-file",
20
+ "taskKey": { "ref": "params.taskKey" },
21
+ "iteration": { "const": 1 }
22
+ }
23
+ },
24
+ "labelText": {
25
+ "const": "Running run_go_linter.py locally (attempt 1)"
26
+ }
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
+ ],
42
+ "stopFlowIf": {
43
+ "equals": [
44
+ { "ref": "steps.run_go_linter_try_1.run_go_linter.outputs.parsed.ok" },
45
+ { "const": true }
46
+ ]
47
+ }
48
+ },
49
+ {
50
+ "id": "fix_go_linter",
51
+ "when": {
52
+ "equals": [
53
+ { "ref": "steps.run_go_linter_try_1.run_go_linter.outputs.parsed.ok" },
54
+ { "const": false }
55
+ ]
56
+ },
57
+ "node": "codex-local-prompt",
58
+ "prompt": {
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
+ },
69
+ "extraPrompt": {
70
+ "appendPrompt": {
71
+ "base": { "ref": "params.extraPrompt" },
72
+ "suffix": {
73
+ "template": "Последний результат run_go_linter.py сохранён в {result_file}. exitCode={exit_code}, summary={summary}.\nПолный raw-вывод последнего запуска:\n{raw_output}\nИсправь только то, что мешает успешному прохождению проверки.",
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
+ },
82
+ "exit_code": { "ref": "steps.run_go_linter_try_1.run_go_linter.outputs.parsed.exitCode" },
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" }
85
+ }
86
+ }
87
+ }
88
+ },
89
+ "format": "task-prompt"
90
+ },
91
+ "params": {
92
+ "labelText": {
93
+ "const": "Running Codex Go linter loop fix (attempt 1)"
94
+ },
95
+ "model": { "const": "gpt-5.4" }
96
+ }
97
+ }
98
+ ]
99
+ },
100
+ {
101
+ "repeat": {
102
+ "var": "attempt",
103
+ "from": 2,
104
+ "to": 5
105
+ },
106
+ "phases": [
107
+ {
108
+ "id": "run_go_linter_try_${attempt}",
109
+ "when": {
110
+ "equals": [
111
+ { "ref": "steps.run_go_linter_try_${attempt_minus_one}.run_go_linter.outputs.parsed.ok" },
112
+ { "const": false }
113
+ ]
114
+ },
115
+ "steps": [
116
+ {
117
+ "id": "run_go_linter",
118
+ "node": "local-script-check",
119
+ "params": {
120
+ "argv": {
121
+ "list": [
122
+ { "ref": "params.runGoLinterScript" }
123
+ ]
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
+ },
132
+ "labelText": {
133
+ "template": "Running run_go_linter.py locally (attempt ${attempt})"
134
+ }
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
+ ],
150
+ "stopFlowIf": {
151
+ "equals": [
152
+ { "ref": "steps.run_go_linter_try_${attempt}.run_go_linter.outputs.parsed.ok" },
153
+ { "const": true }
154
+ ]
155
+ }
156
+ },
157
+ {
158
+ "id": "fix_go_linter",
159
+ "when": {
160
+ "equals": [
161
+ { "ref": "steps.run_go_linter_try_${attempt}.run_go_linter.outputs.parsed.ok" },
162
+ { "const": false }
163
+ ]
164
+ },
165
+ "node": "codex-local-prompt",
166
+ "prompt": {
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
+ },
177
+ "extraPrompt": {
178
+ "appendPrompt": {
179
+ "base": { "ref": "params.extraPrompt" },
180
+ "suffix": {
181
+ "template": "Последний результат run_go_linter.py сохранён в {result_file}. exitCode={exit_code}, summary={summary}.\nПолный raw-вывод последнего запуска:\n{raw_output}\nИсправь только то, что мешает успешному прохождению проверки.",
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
+ },
190
+ "exit_code": { "ref": "steps.run_go_linter_try_${attempt}.run_go_linter.outputs.parsed.exitCode" },
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" }
193
+ }
194
+ }
195
+ }
196
+ },
197
+ "format": "task-prompt"
198
+ },
199
+ "params": {
200
+ "labelText": {
201
+ "template": "Running Codex Go linter loop fix (attempt ${attempt})"
202
+ },
203
+ "model": { "const": "gpt-5.4" }
204
+ }
205
+ }
206
+ ]
207
+ }
208
+ ]
209
+ },
210
+ {
211
+ "id": "run_go_linter_failed",
212
+ "steps": [
213
+ {
214
+ "id": "assert_run_go_linter_success",
215
+ "node": "file-check",
216
+ "params": {
217
+ "path": { "ref": "params.runGoLinterScript" }
218
+ },
219
+ "expect": [
220
+ {
221
+ "kind": "step-output",
222
+ "value": { "ref": "steps.run_go_linter_try_5.run_go_linter.outputs.parsed.ok" },
223
+ "equals": { "const": true },
224
+ "message": "run-go-linter-loop exhausted all attempts without a successful run_go_linter.py execution."
225
+ }
226
+ ]
227
+ }
228
+ ]
229
+ }
230
+ ]
231
+ }
@@ -0,0 +1,231 @@
1
+ {
2
+ "kind": "run-go-tests-loop-flow",
3
+ "version": 1,
4
+ "phases": [
5
+ {
6
+ "id": "run_go_tests_try_1",
7
+ "steps": [
8
+ {
9
+ "id": "run_go_tests",
10
+ "node": "local-script-check",
11
+ "params": {
12
+ "argv": {
13
+ "list": [
14
+ { "ref": "params.runGoTestsScript" }
15
+ ]
16
+ },
17
+ "outputFile": {
18
+ "artifact": {
19
+ "kind": "run-go-tests-result-json-file",
20
+ "taskKey": { "ref": "params.taskKey" },
21
+ "iteration": { "const": 1 }
22
+ }
23
+ },
24
+ "labelText": {
25
+ "const": "Running run_go_tests.py locally (attempt 1)"
26
+ }
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
+ ],
42
+ "stopFlowIf": {
43
+ "equals": [
44
+ { "ref": "steps.run_go_tests_try_1.run_go_tests.outputs.parsed.ok" },
45
+ { "const": true }
46
+ ]
47
+ }
48
+ },
49
+ {
50
+ "id": "fix_go_tests",
51
+ "when": {
52
+ "equals": [
53
+ { "ref": "steps.run_go_tests_try_1.run_go_tests.outputs.parsed.ok" },
54
+ { "const": false }
55
+ ]
56
+ },
57
+ "node": "codex-local-prompt",
58
+ "prompt": {
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
+ },
69
+ "extraPrompt": {
70
+ "appendPrompt": {
71
+ "base": { "ref": "params.extraPrompt" },
72
+ "suffix": {
73
+ "template": "Последний результат run_go_tests.py сохранён в {result_file}. exitCode={exit_code}, summary={summary}.\nПолный raw-вывод последнего запуска:\n{raw_output}\nИсправь только то, что мешает успешному прохождению проверки.",
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
+ },
82
+ "exit_code": { "ref": "steps.run_go_tests_try_1.run_go_tests.outputs.parsed.exitCode" },
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" }
85
+ }
86
+ }
87
+ }
88
+ },
89
+ "format": "task-prompt"
90
+ },
91
+ "params": {
92
+ "labelText": {
93
+ "const": "Running Codex Go tests loop fix (attempt 1)"
94
+ },
95
+ "model": { "const": "gpt-5.4" }
96
+ }
97
+ }
98
+ ]
99
+ },
100
+ {
101
+ "repeat": {
102
+ "var": "attempt",
103
+ "from": 2,
104
+ "to": 5
105
+ },
106
+ "phases": [
107
+ {
108
+ "id": "run_go_tests_try_${attempt}",
109
+ "when": {
110
+ "equals": [
111
+ { "ref": "steps.run_go_tests_try_${attempt_minus_one}.run_go_tests.outputs.parsed.ok" },
112
+ { "const": false }
113
+ ]
114
+ },
115
+ "steps": [
116
+ {
117
+ "id": "run_go_tests",
118
+ "node": "local-script-check",
119
+ "params": {
120
+ "argv": {
121
+ "list": [
122
+ { "ref": "params.runGoTestsScript" }
123
+ ]
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
+ },
132
+ "labelText": {
133
+ "template": "Running run_go_tests.py locally (attempt ${attempt})"
134
+ }
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
+ ],
150
+ "stopFlowIf": {
151
+ "equals": [
152
+ { "ref": "steps.run_go_tests_try_${attempt}.run_go_tests.outputs.parsed.ok" },
153
+ { "const": true }
154
+ ]
155
+ }
156
+ },
157
+ {
158
+ "id": "fix_go_tests",
159
+ "when": {
160
+ "equals": [
161
+ { "ref": "steps.run_go_tests_try_${attempt}.run_go_tests.outputs.parsed.ok" },
162
+ { "const": false }
163
+ ]
164
+ },
165
+ "node": "codex-local-prompt",
166
+ "prompt": {
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
+ },
177
+ "extraPrompt": {
178
+ "appendPrompt": {
179
+ "base": { "ref": "params.extraPrompt" },
180
+ "suffix": {
181
+ "template": "Последний результат run_go_tests.py сохранён в {result_file}. exitCode={exit_code}, summary={summary}.\nПолный raw-вывод последнего запуска:\n{raw_output}\nИсправь только то, что мешает успешному прохождению проверки.",
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
+ },
190
+ "exit_code": { "ref": "steps.run_go_tests_try_${attempt}.run_go_tests.outputs.parsed.exitCode" },
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" }
193
+ }
194
+ }
195
+ }
196
+ },
197
+ "format": "task-prompt"
198
+ },
199
+ "params": {
200
+ "labelText": {
201
+ "template": "Running Codex Go tests loop fix (attempt ${attempt})"
202
+ },
203
+ "model": { "const": "gpt-5.4" }
204
+ }
205
+ }
206
+ ]
207
+ }
208
+ ]
209
+ },
210
+ {
211
+ "id": "run_go_tests_failed",
212
+ "steps": [
213
+ {
214
+ "id": "assert_run_go_tests_success",
215
+ "node": "file-check",
216
+ "params": {
217
+ "path": { "ref": "params.runGoTestsScript" }
218
+ },
219
+ "expect": [
220
+ {
221
+ "kind": "step-output",
222
+ "value": { "ref": "steps.run_go_tests_try_5.run_go_tests.outputs.parsed.ok" },
223
+ "equals": { "const": true },
224
+ "message": "run-go-tests-loop exhausted all attempts without a successful run_go_tests.py execution."
225
+ }
226
+ ]
227
+ }
228
+ ]
229
+ }
230
+ ]
231
+ }
@@ -5,11 +5,36 @@
5
5
  {
6
6
  "id": "task_describe",
7
7
  "steps": [
8
+ {
9
+ "id": "fetch_jira",
10
+ "node": "jira-fetch",
11
+ "params": {
12
+ "jiraApiUrl": { "ref": "params.jiraApiUrl" },
13
+ "outputFile": {
14
+ "artifact": {
15
+ "kind": "jira-task-file",
16
+ "taskKey": { "ref": "params.taskKey" }
17
+ }
18
+ }
19
+ },
20
+ "expect": [
21
+ {
22
+ "kind": "require-file",
23
+ "path": {
24
+ "artifact": {
25
+ "kind": "jira-task-file",
26
+ "taskKey": { "ref": "params.taskKey" }
27
+ }
28
+ },
29
+ "message": "Jira fetch node did not produce the Jira task file."
30
+ }
31
+ ]
32
+ },
8
33
  {
9
34
  "id": "run_codex_task_describe",
10
35
  "node": "codex-local-prompt",
11
36
  "prompt": {
12
- "inlineTemplate": "Посмотри задачу в {jira_task_file}. Проанализируй код и оформи краткое описание для Jira, без лишних подробностей. Сначала запиши source-of-truth JSON в {jira_description_json_file} в виде объекта { summary: string }, затем markdown-версию в {jira_description_file}.",
37
+ "templateRef": "task-describe",
13
38
  "vars": {
14
39
  "jira_task_file": {
15
40
  "artifact": {
@@ -8,6 +8,7 @@ import { fileCheckNode } from "./nodes/file-check-node.js";
8
8
  import { flowRunNode } from "./nodes/flow-run-node.js";
9
9
  import { gitlabReviewArtifactsNode } from "./nodes/gitlab-review-artifacts-node.js";
10
10
  import { jiraFetchNode } from "./nodes/jira-fetch-node.js";
11
+ import { jiraIssueCheckNode } from "./nodes/jira-issue-check-node.js";
11
12
  import { localScriptCheckNode } from "./nodes/local-script-check-node.js";
12
13
  import { planCodexNode } from "./nodes/plan-codex-node.js";
13
14
  import { reviewClaudeNode } from "./nodes/review-claude-node.js";
@@ -27,6 +28,7 @@ const builtInNodes = {
27
28
  "flow-run": flowRunNode,
28
29
  "gitlab-review-artifacts": gitlabReviewArtifactsNode,
29
30
  "jira-fetch": jiraFetchNode,
31
+ "jira-issue-check": jiraIssueCheckNode,
30
32
  "local-script-check": localScriptCheckNode,
31
33
  "plan-codex": planCodexNode,
32
34
  "review-claude": reviewClaudeNode,
@@ -62,6 +64,12 @@ const builtInNodeMetadata = {
62
64
  requiredParams: ["gitlabReviewJsonFile", "reviewFile", "reviewJsonFile"],
63
65
  },
64
66
  "jira-fetch": { kind: "jira-fetch", version: 1, prompt: "forbidden", requiredParams: ["jiraApiUrl", "outputFile"] },
67
+ "jira-issue-check": {
68
+ kind: "jira-issue-check",
69
+ version: 1,
70
+ prompt: "forbidden",
71
+ requiredParams: ["jiraTaskFile", "allowedIssueTypes"],
72
+ },
65
73
  "local-script-check": { kind: "local-script-check", version: 1, prompt: "forbidden", requiredParams: ["argv", "labelText"] },
66
74
  "plan-codex": { kind: "plan-codex", version: 1, prompt: "forbidden", requiredParams: ["prompt", "requiredArtifacts"] },
67
75
  "review-claude": {
@@ -0,0 +1,53 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { TaskRunnerError } from "../../errors.js";
3
+ import { printInfo } from "../../tui.js";
4
+ function extractIssueTypeName(path) {
5
+ let parsed;
6
+ try {
7
+ parsed = JSON.parse(readFileSync(path, "utf8"));
8
+ }
9
+ catch (error) {
10
+ throw new TaskRunnerError(`Failed to parse Jira issue JSON ${path}: ${error.message}`);
11
+ }
12
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
13
+ throw new TaskRunnerError(`Jira issue payload in ${path} must be a JSON object.`);
14
+ }
15
+ const fields = parsed.fields;
16
+ if (!fields || typeof fields !== "object" || Array.isArray(fields)) {
17
+ throw new TaskRunnerError(`Jira issue payload in ${path} does not contain 'fields'.`);
18
+ }
19
+ const issueType = fields.issuetype;
20
+ if (!issueType || typeof issueType !== "object" || Array.isArray(issueType)) {
21
+ throw new TaskRunnerError(`Jira issue payload in ${path} does not contain 'fields.issuetype'.`);
22
+ }
23
+ const issueTypeName = issueType.name;
24
+ if (typeof issueTypeName !== "string" || issueTypeName.trim().length === 0) {
25
+ throw new TaskRunnerError(`Jira issue payload in ${path} does not contain 'fields.issuetype.name'.`);
26
+ }
27
+ return issueTypeName.trim();
28
+ }
29
+ export const jiraIssueCheckNode = {
30
+ kind: "jira-issue-check",
31
+ version: 1,
32
+ async run(_context, params) {
33
+ if (params.labelText) {
34
+ printInfo(params.labelText);
35
+ }
36
+ const issueType = extractIssueTypeName(params.jiraTaskFile);
37
+ const normalizedIssueType = issueType.toLowerCase();
38
+ const allowedIssueTypes = params.allowedIssueTypes
39
+ .map((candidate) => candidate.trim())
40
+ .filter((candidate) => candidate.length > 0);
41
+ if (allowedIssueTypes.length === 0) {
42
+ throw new TaskRunnerError("jira-issue-check requires at least one allowed issue type.");
43
+ }
44
+ const isAllowed = allowedIssueTypes.some((candidate) => candidate.toLowerCase() === normalizedIssueType);
45
+ if (!isAllowed) {
46
+ throw new TaskRunnerError(`Flow 'bug-analyze' supports only Jira issue types: ${allowedIssueTypes.join(", ")}. ` +
47
+ `Fetched issue type: ${issueType}.`);
48
+ }
49
+ return {
50
+ value: { issueType },
51
+ };
52
+ },
53
+ };
@@ -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
  },