agentweaver 0.1.9 → 0.1.10

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 (26) hide show
  1. package/README.md +60 -28
  2. package/dist/artifacts.js +1 -1
  3. package/dist/errors.js +7 -0
  4. package/dist/index.js +66 -34
  5. package/dist/interactive-ui.js +351 -44
  6. package/dist/pipeline/declarative-flows.js +7 -4
  7. package/dist/pipeline/flow-catalog.js +28 -21
  8. package/dist/pipeline/flow-specs/opencode/auto-opencode.json +1365 -0
  9. package/dist/pipeline/flow-specs/opencode/bugz/bug-analyze-opencode.json +382 -0
  10. package/dist/pipeline/flow-specs/opencode/bugz/bug-fix-opencode.json +56 -0
  11. package/dist/pipeline/flow-specs/opencode/gitlab/gitlab-diff-review-opencode.json +308 -0
  12. package/dist/pipeline/flow-specs/opencode/gitlab/gitlab-review-opencode.json +437 -0
  13. package/dist/pipeline/flow-specs/opencode/gitlab/mr-description-opencode.json +117 -0
  14. package/dist/pipeline/flow-specs/opencode/go/run-go-linter-loop-opencode.json +321 -0
  15. package/dist/pipeline/flow-specs/opencode/go/run-go-tests-loop-opencode.json +321 -0
  16. package/dist/pipeline/flow-specs/opencode/implement-opencode.json +64 -0
  17. package/dist/pipeline/flow-specs/{plan-opencode.json → opencode/plan-opencode.json} +4 -4
  18. package/dist/pipeline/flow-specs/opencode/review/review-fix-opencode.json +209 -0
  19. package/dist/pipeline/flow-specs/opencode/review/review-opencode.json +452 -0
  20. package/dist/pipeline/flow-specs/opencode/task-describe-opencode.json +148 -0
  21. package/dist/pipeline/spec-loader.js +18 -7
  22. package/dist/runtime/process-runner.js +45 -1
  23. package/package.json +1 -1
  24. package/dist/pipeline/flow-specs/preflight.json +0 -206
  25. package/dist/pipeline/flow-specs/run-linter-loop.json +0 -155
  26. package/dist/pipeline/flow-specs/run-tests-loop.json +0 -155
@@ -1,6 +1,7 @@
1
1
  import path from "node:path";
2
2
  import process from "node:process";
3
3
  import { spawn } from "node:child_process";
4
+ import { FlowInterruptedError } from "../errors.js";
4
5
  import { getExecutionState, getOutputAdapter, printFramedBlock, printInfo, setCurrentExecutor } from "../tui.js";
5
6
  import { shellQuote } from "./command-resolution.js";
6
7
  export function formatCommand(argv, env) {
@@ -34,8 +35,11 @@ function formatLaunchDetails(statusLabel) {
34
35
  return lines.join("\n");
35
36
  }
36
37
  export async function runCommand(argv, options = {}) {
37
- const { env, dryRun = false, verbose = false, label, printFailureOutput = true } = options;
38
+ const { env, dryRun = false, verbose = false, label, printFailureOutput = true, signal } = options;
38
39
  const outputAdapter = getOutputAdapter();
40
+ if (signal?.aborted) {
41
+ throw new FlowInterruptedError();
42
+ }
39
43
  if (dryRun) {
40
44
  setCurrentExecutor(label ?? path.basename(argv[0] ?? argv.join(" ")));
41
45
  outputAdapter.writeStdout(`${formatCommand(argv, env)}\n`);
@@ -48,9 +52,29 @@ export async function runCommand(argv, options = {}) {
48
52
  stdio: "inherit",
49
53
  env,
50
54
  });
55
+ let abortTimer = null;
56
+ const abortHandler = () => {
57
+ child.kill("SIGTERM");
58
+ abortTimer = setTimeout(() => {
59
+ child.kill("SIGKILL");
60
+ }, 2000);
61
+ };
62
+ signal?.addEventListener("abort", abortHandler, { once: true });
51
63
  child.on("exit", (code) => (code === 0 ? resolve() : reject(new Error(String(code ?? 1)))));
52
64
  child.on("error", reject);
65
+ child.on("close", () => {
66
+ signal?.removeEventListener("abort", abortHandler);
67
+ if (abortTimer) {
68
+ clearTimeout(abortTimer);
69
+ }
70
+ });
53
71
  }).catch((error) => {
72
+ if (signal?.aborted) {
73
+ throw Object.assign(new FlowInterruptedError(), {
74
+ returnCode: 130,
75
+ output: "",
76
+ });
77
+ }
54
78
  const code = Number.parseInt(error.message, 10);
55
79
  throw Object.assign(new Error(`Command failed with exit code ${Number.isNaN(code) ? 1 : code}`), {
56
80
  returnCode: Number.isNaN(code) ? 1 : code,
@@ -86,12 +110,32 @@ export async function runCommand(argv, options = {}) {
86
110
  }
87
111
  try {
88
112
  const exitCode = await new Promise((resolve, reject) => {
113
+ let abortTimer = null;
114
+ const abortHandler = () => {
115
+ child.kill("SIGTERM");
116
+ abortTimer = setTimeout(() => {
117
+ child.kill("SIGKILL");
118
+ }, 2000);
119
+ };
120
+ signal?.addEventListener("abort", abortHandler, { once: true });
89
121
  child.on("error", reject);
90
122
  child.on("exit", (code) => resolve(code ?? 1));
123
+ child.on("close", () => {
124
+ signal?.removeEventListener("abort", abortHandler);
125
+ if (abortTimer) {
126
+ clearTimeout(abortTimer);
127
+ }
128
+ });
91
129
  });
92
130
  if (outputAdapter.renderAuxiliaryOutput !== false) {
93
131
  printInfo(`Закончили работу: ${statusLabel} (${formatDuration(Date.now() - startedAt)})`);
94
132
  }
133
+ if (signal?.aborted) {
134
+ throw Object.assign(new FlowInterruptedError(), {
135
+ returnCode: 130,
136
+ output,
137
+ });
138
+ }
95
139
  if (exitCode !== 0) {
96
140
  if (output && printFailureOutput && outputAdapter.supportsTransientStatus) {
97
141
  process.stderr.write(output);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentweaver",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "CLI orchestrator for Jira/Codex/Claude engineering workflows",
5
5
  "keywords": [
6
6
  "agent",
@@ -1,206 +0,0 @@
1
- {
2
- "kind": "interactive-preflight-flow",
3
- "version": 1,
4
- "phases": [
5
- {
6
- "id": "preflight",
7
- "steps": [
8
- {
9
- "id": "check_commands",
10
- "node": "command-check",
11
- "params": {
12
- "commands": {
13
- "const": [
14
- { "commandName": "codex", "envVarName": "CODEX_BIN" },
15
- { "commandName": "claude", "envVarName": "CLAUDE_BIN" }
16
- ]
17
- }
18
- }
19
- },
20
- {
21
- "id": "fetch_jira_if_needed",
22
- "when": {
23
- "any": [
24
- { "ref": "params.forceRefresh" },
25
- {
26
- "not": {
27
- "exists": {
28
- "artifact": {
29
- "kind": "jira-task-file",
30
- "taskKey": { "ref": "params.taskKey" }
31
- }
32
- }
33
- }
34
- }
35
- ]
36
- },
37
- "node": "jira-fetch",
38
- "params": {
39
- "jiraApiUrl": { "ref": "params.jiraApiUrl" },
40
- "outputFile": {
41
- "artifact": {
42
- "kind": "jira-task-file",
43
- "taskKey": { "ref": "params.taskKey" }
44
- }
45
- }
46
- },
47
- "expect": [
48
- {
49
- "kind": "require-file",
50
- "path": {
51
- "artifact": {
52
- "kind": "jira-task-file",
53
- "taskKey": { "ref": "params.taskKey" }
54
- }
55
- },
56
- "message": "Jira fetch node did not produce the Jira task file."
57
- }
58
- ]
59
- },
60
- {
61
- "id": "load_existing_task_summary",
62
- "when": {
63
- "all": [
64
- {
65
- "not": { "ref": "params.forceRefresh" }
66
- },
67
- {
68
- "exists": {
69
- "artifact": {
70
- "kind": "jira-task-file",
71
- "taskKey": { "ref": "params.taskKey" }
72
- }
73
- }
74
- },
75
- {
76
- "exists": {
77
- "artifact": {
78
- "kind": "task-summary-file",
79
- "taskKey": { "ref": "params.taskKey" }
80
- }
81
- }
82
- }
83
- ]
84
- },
85
- "node": "summary-file-load",
86
- "params": {
87
- "path": {
88
- "artifact": {
89
- "kind": "task-summary-file",
90
- "taskKey": { "ref": "params.taskKey" }
91
- }
92
- }
93
- }
94
- },
95
- {
96
- "id": "generate_task_summary",
97
- "when": {
98
- "any": [
99
- { "ref": "params.forceRefresh" },
100
- {
101
- "not": {
102
- "exists": {
103
- "artifact": {
104
- "kind": "task-summary-file",
105
- "taskKey": { "ref": "params.taskKey" }
106
- }
107
- }
108
- }
109
- }
110
- ]
111
- },
112
- "node": "claude-prompt",
113
- "prompt": {
114
- "templateRef": "task-summary",
115
- "vars": {
116
- "jira_task_file": {
117
- "artifact": {
118
- "kind": "jira-task-file",
119
- "taskKey": { "ref": "params.taskKey" }
120
- }
121
- },
122
- "task_summary_file": {
123
- "artifact": {
124
- "kind": "task-summary-file",
125
- "taskKey": { "ref": "params.taskKey" }
126
- }
127
- },
128
- "task_summary_json_file": {
129
- "artifact": {
130
- "kind": "task-summary-json-file",
131
- "taskKey": { "ref": "params.taskKey" }
132
- }
133
- }
134
- },
135
- "format": "plain"
136
- },
137
- "params": {
138
- "labelText": { "const": "Preparing task summary" },
139
- "outputFile": {
140
- "artifact": {
141
- "kind": "task-summary-file",
142
- "taskKey": { "ref": "params.taskKey" }
143
- }
144
- },
145
- "requiredArtifacts": {
146
- "list": [
147
- {
148
- "artifact": {
149
- "kind": "task-summary-json-file",
150
- "taskKey": { "ref": "params.taskKey" }
151
- }
152
- }
153
- ]
154
- },
155
- "model": { "const": "haiku" }
156
- },
157
- "expect": [
158
- {
159
- "kind": "require-artifacts",
160
- "when": { "not": { "ref": "context.dryRun" } },
161
- "paths": {
162
- "list": [
163
- {
164
- "artifact": {
165
- "kind": "task-summary-file",
166
- "taskKey": { "ref": "params.taskKey" }
167
- }
168
- }
169
- ]
170
- },
171
- "message": "Claude summary did not produce the task summary artifact."
172
- },
173
- {
174
- "kind": "require-structured-artifacts",
175
- "when": { "not": { "ref": "context.dryRun" } },
176
- "items": [
177
- {
178
- "path": {
179
- "artifact": {
180
- "kind": "task-summary-json-file",
181
- "taskKey": { "ref": "params.taskKey" }
182
- }
183
- },
184
- "schemaId": "task-summary/v1"
185
- }
186
- ],
187
- "message": "Claude summary produced invalid structured artifacts."
188
- }
189
- ],
190
- "after": [
191
- {
192
- "kind": "set-summary-from-file",
193
- "when": { "not": { "ref": "context.dryRun" } },
194
- "path": {
195
- "artifact": {
196
- "kind": "task-summary-file",
197
- "taskKey": { "ref": "params.taskKey" }
198
- }
199
- }
200
- }
201
- ]
202
- }
203
- ]
204
- }
205
- ]
206
- }
@@ -1,155 +0,0 @@
1
- {
2
- "kind": "run-linter-loop-flow",
3
- "version": 1,
4
- "phases": [
5
- {
6
- "id": "run_linter_try_1",
7
- "steps": [
8
- {
9
- "id": "run_linter",
10
- "node": "local-script-check",
11
- "params": {
12
- "argv": {
13
- "list": [
14
- { "ref": "params.runLinterScript" }
15
- ]
16
- },
17
- "labelText": {
18
- "const": "Running run_linter.sh locally (attempt 1)"
19
- }
20
- },
21
- "stopFlowIf": {
22
- "equals": [
23
- { "ref": "steps.run_linter_try_1.run_linter.outputs.parsed.ok" },
24
- { "const": true }
25
- ]
26
- }
27
- },
28
- {
29
- "id": "fix_linter",
30
- "when": {
31
- "equals": [
32
- { "ref": "steps.run_linter_try_1.run_linter.outputs.parsed.ok" },
33
- { "const": false }
34
- ]
35
- },
36
- "node": "codex-local-prompt",
37
- "prompt": {
38
- "templateRef": "run-linter-loop-fix",
39
- "extraPrompt": {
40
- "appendPrompt": {
41
- "base": { "ref": "params.extraPrompt" },
42
- "suffix": {
43
- "template": "Последний результат run_linter.sh: exitCode={exit_code}, summary={summary}. Исправь только то, что мешает успешному прохождению проверки.",
44
- "vars": {
45
- "exit_code": { "ref": "steps.run_linter_try_1.run_linter.outputs.parsed.exitCode" },
46
- "summary": { "ref": "steps.run_linter_try_1.run_linter.outputs.parsed.summary" }
47
- }
48
- }
49
- }
50
- },
51
- "format": "task-prompt"
52
- },
53
- "params": {
54
- "labelText": {
55
- "const": "Running Codex linter loop fix (attempt 1)"
56
- },
57
- "model": { "const": "gpt-5.4" }
58
- }
59
- }
60
- ]
61
- },
62
- {
63
- "repeat": {
64
- "var": "attempt",
65
- "from": 2,
66
- "to": 5
67
- },
68
- "phases": [
69
- {
70
- "id": "run_linter_try_${attempt}",
71
- "when": {
72
- "equals": [
73
- { "ref": "steps.run_linter_try_${attempt_minus_one}.run_linter.outputs.parsed.ok" },
74
- { "const": false }
75
- ]
76
- },
77
- "steps": [
78
- {
79
- "id": "run_linter",
80
- "node": "local-script-check",
81
- "params": {
82
- "argv": {
83
- "list": [
84
- { "ref": "params.runLinterScript" }
85
- ]
86
- },
87
- "labelText": {
88
- "template": "Running run_linter.sh locally (attempt ${attempt})"
89
- }
90
- },
91
- "stopFlowIf": {
92
- "equals": [
93
- { "ref": "steps.run_linter_try_${attempt}.run_linter.outputs.parsed.ok" },
94
- { "const": true }
95
- ]
96
- }
97
- },
98
- {
99
- "id": "fix_linter",
100
- "when": {
101
- "equals": [
102
- { "ref": "steps.run_linter_try_${attempt}.run_linter.outputs.parsed.ok" },
103
- { "const": false }
104
- ]
105
- },
106
- "node": "codex-local-prompt",
107
- "prompt": {
108
- "templateRef": "run-linter-loop-fix",
109
- "extraPrompt": {
110
- "appendPrompt": {
111
- "base": { "ref": "params.extraPrompt" },
112
- "suffix": {
113
- "template": "Последний результат run_linter.sh: exitCode={exit_code}, summary={summary}. Исправь только то, что мешает успешному прохождению проверки.",
114
- "vars": {
115
- "exit_code": { "ref": "steps.run_linter_try_${attempt}.run_linter.outputs.parsed.exitCode" },
116
- "summary": { "ref": "steps.run_linter_try_${attempt}.run_linter.outputs.parsed.summary" }
117
- }
118
- }
119
- }
120
- },
121
- "format": "task-prompt"
122
- },
123
- "params": {
124
- "labelText": {
125
- "template": "Running Codex linter loop fix (attempt ${attempt})"
126
- },
127
- "model": { "const": "gpt-5.4" }
128
- }
129
- }
130
- ]
131
- }
132
- ]
133
- },
134
- {
135
- "id": "run_linter_failed",
136
- "steps": [
137
- {
138
- "id": "assert_run_linter_success",
139
- "node": "file-check",
140
- "params": {
141
- "path": { "ref": "params.runLinterScript" }
142
- },
143
- "expect": [
144
- {
145
- "kind": "step-output",
146
- "value": { "ref": "steps.run_linter_try_5.run_linter.outputs.parsed.ok" },
147
- "equals": { "const": true },
148
- "message": "run-linter-loop exhausted all attempts without a successful run_linter.sh execution."
149
- }
150
- ]
151
- }
152
- ]
153
- }
154
- ]
155
- }
@@ -1,155 +0,0 @@
1
- {
2
- "kind": "run-tests-loop-flow",
3
- "version": 1,
4
- "phases": [
5
- {
6
- "id": "run_tests_try_1",
7
- "steps": [
8
- {
9
- "id": "run_tests",
10
- "node": "local-script-check",
11
- "params": {
12
- "argv": {
13
- "list": [
14
- { "ref": "params.runTestsScript" }
15
- ]
16
- },
17
- "labelText": {
18
- "const": "Running run_tests.sh locally (attempt 1)"
19
- }
20
- },
21
- "stopFlowIf": {
22
- "equals": [
23
- { "ref": "steps.run_tests_try_1.run_tests.outputs.parsed.ok" },
24
- { "const": true }
25
- ]
26
- }
27
- },
28
- {
29
- "id": "fix_tests",
30
- "when": {
31
- "equals": [
32
- { "ref": "steps.run_tests_try_1.run_tests.outputs.parsed.ok" },
33
- { "const": false }
34
- ]
35
- },
36
- "node": "codex-local-prompt",
37
- "prompt": {
38
- "templateRef": "run-tests-loop-fix",
39
- "extraPrompt": {
40
- "appendPrompt": {
41
- "base": { "ref": "params.extraPrompt" },
42
- "suffix": {
43
- "template": "Последний результат run_tests.sh: exitCode={exit_code}, summary={summary}. Исправь только то, что мешает успешному прохождению проверки.",
44
- "vars": {
45
- "exit_code": { "ref": "steps.run_tests_try_1.run_tests.outputs.parsed.exitCode" },
46
- "summary": { "ref": "steps.run_tests_try_1.run_tests.outputs.parsed.summary" }
47
- }
48
- }
49
- }
50
- },
51
- "format": "task-prompt"
52
- },
53
- "params": {
54
- "labelText": {
55
- "const": "Running Codex tests loop fix (attempt 1)"
56
- },
57
- "model": { "const": "gpt-5.4" }
58
- }
59
- }
60
- ]
61
- },
62
- {
63
- "repeat": {
64
- "var": "attempt",
65
- "from": 2,
66
- "to": 5
67
- },
68
- "phases": [
69
- {
70
- "id": "run_tests_try_${attempt}",
71
- "when": {
72
- "equals": [
73
- { "ref": "steps.run_tests_try_${attempt_minus_one}.run_tests.outputs.parsed.ok" },
74
- { "const": false }
75
- ]
76
- },
77
- "steps": [
78
- {
79
- "id": "run_tests",
80
- "node": "local-script-check",
81
- "params": {
82
- "argv": {
83
- "list": [
84
- { "ref": "params.runTestsScript" }
85
- ]
86
- },
87
- "labelText": {
88
- "template": "Running run_tests.sh locally (attempt ${attempt})"
89
- }
90
- },
91
- "stopFlowIf": {
92
- "equals": [
93
- { "ref": "steps.run_tests_try_${attempt}.run_tests.outputs.parsed.ok" },
94
- { "const": true }
95
- ]
96
- }
97
- },
98
- {
99
- "id": "fix_tests",
100
- "when": {
101
- "equals": [
102
- { "ref": "steps.run_tests_try_${attempt}.run_tests.outputs.parsed.ok" },
103
- { "const": false }
104
- ]
105
- },
106
- "node": "codex-local-prompt",
107
- "prompt": {
108
- "templateRef": "run-tests-loop-fix",
109
- "extraPrompt": {
110
- "appendPrompt": {
111
- "base": { "ref": "params.extraPrompt" },
112
- "suffix": {
113
- "template": "Последний результат run_tests.sh: exitCode={exit_code}, summary={summary}. Исправь только то, что мешает успешному прохождению проверки.",
114
- "vars": {
115
- "exit_code": { "ref": "steps.run_tests_try_${attempt}.run_tests.outputs.parsed.exitCode" },
116
- "summary": { "ref": "steps.run_tests_try_${attempt}.run_tests.outputs.parsed.summary" }
117
- }
118
- }
119
- }
120
- },
121
- "format": "task-prompt"
122
- },
123
- "params": {
124
- "labelText": {
125
- "template": "Running Codex tests loop fix (attempt ${attempt})"
126
- },
127
- "model": { "const": "gpt-5.4" }
128
- }
129
- }
130
- ]
131
- }
132
- ]
133
- },
134
- {
135
- "id": "run_tests_failed",
136
- "steps": [
137
- {
138
- "id": "assert_run_tests_success",
139
- "node": "file-check",
140
- "params": {
141
- "path": { "ref": "params.runTestsScript" }
142
- },
143
- "expect": [
144
- {
145
- "kind": "step-output",
146
- "value": { "ref": "steps.run_tests_try_5.run_tests.outputs.parsed.ok" },
147
- "equals": { "const": true },
148
- "message": "run-tests-loop exhausted all attempts without a successful run_tests.sh execution."
149
- }
150
- ]
151
- }
152
- ]
153
- }
154
- ]
155
- }