automata-cli 0.1.0-bugfix-nextwork.57 → 0.1.0-bugfix-automata-deletion.94
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/dist/index.js +575 -88
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command6 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/version.ts
|
|
7
7
|
import { readFileSync } from "fs";
|
|
@@ -23,6 +23,9 @@ import { Box, Text, useInput, useApp } from "ink";
|
|
|
23
23
|
// src/config/configStore.ts
|
|
24
24
|
import { readFileSync as readFileSync2, writeFileSync, mkdirSync } from "fs";
|
|
25
25
|
import { join } from "path";
|
|
26
|
+
var DEFAULT_CLAUDE_SYSTEM_PROMPT = "You are an expert software engineer. Implement the following issue according to the project's existing conventions and style. Make minimal, targeted changes that satisfy the requirements. Run tests and linting before finishing.";
|
|
27
|
+
var DEFAULT_FIX_COMMENTS_PROMPT = "You are an expert software engineer reviewing a pull request. Below are the open review comments left by reviewers on this PR. Please address each comment by making the appropriate code changes. Focus on the reviewer's concerns and make minimal, targeted changes that resolve each comment without altering unrelated code.";
|
|
28
|
+
var DEFAULT_SONAR_PROMPT = "You are an expert software engineer. You have been given the URL of a SonarCloud analysis for this pull request. If the `sonar-quality-gate` skill is available in this repository, use it. The project is public, so use the SonarCloud REST API directly (no authentication required) rather than scraping the URL. Inspect both the quality gate and the list of issues for this pull request. If the quality gate fails because of duplication or another metric-based condition, use the relevant Sonar APIs to identify the affected files and details instead of relying only on the issues endpoint. Fix all new issues and quality-gate failures reported. Focus on code smells, bugs, vulnerabilities, and blocking quality-gate conditions flagged in this PR. Make targeted, minimal changes that resolve each issue without altering unrelated code.";
|
|
26
29
|
var CONFIG_DIR = ".automata";
|
|
27
30
|
var CONFIG_FILE = "config.json";
|
|
28
31
|
function configPath() {
|
|
@@ -53,22 +56,47 @@ var TECHNIQUE_OPTIONS = [
|
|
|
53
56
|
{ label: "By Assignee", value: "assignee" },
|
|
54
57
|
{ label: "By Title Contains", value: "title-contains" }
|
|
55
58
|
];
|
|
59
|
+
var MAIN_MENU_OPTIONS = ["Remote / Mode", "Implement-Next", "Prompts"];
|
|
60
|
+
var PROMPTS_MENU_OPTIONS = ["Sonar", "Fix-Comments"];
|
|
56
61
|
function ConfigWizard() {
|
|
57
62
|
const existing = readConfig();
|
|
58
63
|
const initialRemoteIndex = REMOTE_OPTIONS.findIndex((o) => o.value === existing.remoteType);
|
|
59
64
|
const initialTechIndex = TECHNIQUE_OPTIONS.findIndex((o) => o.value === existing.issueDiscoveryTechnique);
|
|
60
|
-
const [screen, setScreen] = useState("
|
|
65
|
+
const [screen, setScreen] = useState("main");
|
|
66
|
+
const [mainMenuIndex, setMainMenuIndex] = useState(0);
|
|
61
67
|
const [selectedRemoteIndex, setSelectedRemoteIndex] = useState(initialRemoteIndex >= 0 ? initialRemoteIndex : 0);
|
|
62
68
|
const [selectedTechIndex, setSelectedTechIndex] = useState(initialTechIndex >= 0 ? initialTechIndex : 0);
|
|
63
69
|
const [discoveryValue, setDiscoveryValue] = useState(existing.issueDiscoveryValue ?? "");
|
|
64
70
|
const [systemPrompt, setSystemPrompt] = useState(existing.claudeSystemPrompt ?? "");
|
|
71
|
+
const [promptsMenuIndex, setPromptsMenuIndex] = useState(0);
|
|
72
|
+
const [sonarPrompt, setSonarPrompt] = useState(existing.prompts?.sonar ?? DEFAULT_SONAR_PROMPT);
|
|
73
|
+
const [fixCommentsPrompt, setFixCommentsPrompt] = useState(
|
|
74
|
+
existing.prompts?.fixComments ?? DEFAULT_FIX_COMMENTS_PROMPT
|
|
75
|
+
);
|
|
65
76
|
const [pendingRemote, setPendingRemote] = useState(existing.remoteType ?? "gh");
|
|
66
77
|
const [pendingTechnique, setPendingTechnique] = useState(
|
|
67
78
|
existing.issueDiscoveryTechnique ?? "label"
|
|
68
79
|
);
|
|
69
80
|
const { exit } = useApp();
|
|
70
81
|
useInput((input, key) => {
|
|
71
|
-
if (screen === "
|
|
82
|
+
if (screen === "main") {
|
|
83
|
+
if (key.upArrow) {
|
|
84
|
+
setMainMenuIndex((i) => i > 0 ? i - 1 : MAIN_MENU_OPTIONS.length - 1);
|
|
85
|
+
} else if (key.downArrow) {
|
|
86
|
+
setMainMenuIndex((i) => i < MAIN_MENU_OPTIONS.length - 1 ? i + 1 : 0);
|
|
87
|
+
} else if (key.return) {
|
|
88
|
+
const chosen = MAIN_MENU_OPTIONS[mainMenuIndex];
|
|
89
|
+
if (chosen === "Remote / Mode") {
|
|
90
|
+
setScreen("remote");
|
|
91
|
+
} else if (chosen === "Implement-Next") {
|
|
92
|
+
setScreen("technique");
|
|
93
|
+
} else {
|
|
94
|
+
setScreen("prompts-menu");
|
|
95
|
+
}
|
|
96
|
+
} else if (key.escape || key.ctrl && input === "c") {
|
|
97
|
+
exit();
|
|
98
|
+
}
|
|
99
|
+
} else if (screen === "remote") {
|
|
72
100
|
if (key.upArrow) {
|
|
73
101
|
setSelectedRemoteIndex((i) => i > 0 ? i - 1 : REMOTE_OPTIONS.length - 1);
|
|
74
102
|
} else if (key.downArrow) {
|
|
@@ -82,7 +110,9 @@ function ConfigWizard() {
|
|
|
82
110
|
writeConfig({ ...existing, remoteType: chosen.value });
|
|
83
111
|
exit();
|
|
84
112
|
}
|
|
85
|
-
} else if (key.escape
|
|
113
|
+
} else if (key.escape) {
|
|
114
|
+
setScreen("main");
|
|
115
|
+
} else if (key.ctrl && input === "c") {
|
|
86
116
|
exit();
|
|
87
117
|
}
|
|
88
118
|
} else if (screen === "technique") {
|
|
@@ -94,7 +124,9 @@ function ConfigWizard() {
|
|
|
94
124
|
const chosen = TECHNIQUE_OPTIONS[selectedTechIndex];
|
|
95
125
|
setPendingTechnique(chosen.value);
|
|
96
126
|
setScreen("value");
|
|
97
|
-
} else if (key.escape
|
|
127
|
+
} else if (key.escape) {
|
|
128
|
+
setScreen("main");
|
|
129
|
+
} else if (key.ctrl && input === "c") {
|
|
98
130
|
exit();
|
|
99
131
|
}
|
|
100
132
|
} else if (screen === "value") {
|
|
@@ -102,7 +134,9 @@ function ConfigWizard() {
|
|
|
102
134
|
setScreen("system-prompt");
|
|
103
135
|
} else if (key.backspace || key.delete) {
|
|
104
136
|
setDiscoveryValue((v) => v.slice(0, -1));
|
|
105
|
-
} else if (key.escape
|
|
137
|
+
} else if (key.escape) {
|
|
138
|
+
setScreen("main");
|
|
139
|
+
} else if (key.ctrl && input === "c") {
|
|
106
140
|
exit();
|
|
107
141
|
} else if (input && !key.ctrl && !key.meta) {
|
|
108
142
|
setDiscoveryValue((v) => v + input);
|
|
@@ -119,29 +153,94 @@ function ConfigWizard() {
|
|
|
119
153
|
exit();
|
|
120
154
|
} else if (key.backspace || key.delete) {
|
|
121
155
|
setSystemPrompt((v) => v.slice(0, -1));
|
|
122
|
-
} else if (key.escape
|
|
156
|
+
} else if (key.escape) {
|
|
157
|
+
setScreen("main");
|
|
158
|
+
} else if (key.ctrl && input === "c") {
|
|
123
159
|
exit();
|
|
124
160
|
} else if (input && !key.ctrl && !key.meta) {
|
|
125
161
|
setSystemPrompt((v) => v + input);
|
|
126
162
|
}
|
|
163
|
+
} else if (screen === "prompts-menu") {
|
|
164
|
+
if (key.upArrow) {
|
|
165
|
+
setPromptsMenuIndex((i) => i > 0 ? i - 1 : PROMPTS_MENU_OPTIONS.length - 1);
|
|
166
|
+
} else if (key.downArrow) {
|
|
167
|
+
setPromptsMenuIndex((i) => i < PROMPTS_MENU_OPTIONS.length - 1 ? i + 1 : 0);
|
|
168
|
+
} else if (key.return) {
|
|
169
|
+
const chosen = PROMPTS_MENU_OPTIONS[promptsMenuIndex];
|
|
170
|
+
if (chosen === "Sonar") {
|
|
171
|
+
setScreen("sonar-prompt");
|
|
172
|
+
} else {
|
|
173
|
+
setScreen("fix-comments-prompt");
|
|
174
|
+
}
|
|
175
|
+
} else if (key.escape) {
|
|
176
|
+
setScreen("main");
|
|
177
|
+
} else if (key.ctrl && input === "c") {
|
|
178
|
+
exit();
|
|
179
|
+
}
|
|
180
|
+
} else if (screen === "sonar-prompt") {
|
|
181
|
+
if (key.return) {
|
|
182
|
+
const current = readConfig();
|
|
183
|
+
writeConfig({
|
|
184
|
+
...current,
|
|
185
|
+
prompts: { ...current.prompts, sonar: sonarPrompt || void 0 }
|
|
186
|
+
});
|
|
187
|
+
setScreen("prompts-menu");
|
|
188
|
+
} else if (key.backspace || key.delete) {
|
|
189
|
+
setSonarPrompt((v) => v.slice(0, -1));
|
|
190
|
+
} else if (key.escape) {
|
|
191
|
+
setScreen("prompts-menu");
|
|
192
|
+
} else if (key.ctrl && input === "c") {
|
|
193
|
+
exit();
|
|
194
|
+
} else if (input && !key.ctrl && !key.meta) {
|
|
195
|
+
setSonarPrompt((v) => v + input);
|
|
196
|
+
}
|
|
197
|
+
} else if (screen === "fix-comments-prompt") {
|
|
198
|
+
if (key.return) {
|
|
199
|
+
const current = readConfig();
|
|
200
|
+
writeConfig({
|
|
201
|
+
...current,
|
|
202
|
+
prompts: { ...current.prompts, fixComments: fixCommentsPrompt || void 0 }
|
|
203
|
+
});
|
|
204
|
+
setScreen("prompts-menu");
|
|
205
|
+
} else if (key.backspace || key.delete) {
|
|
206
|
+
setFixCommentsPrompt((v) => v.slice(0, -1));
|
|
207
|
+
} else if (key.escape) {
|
|
208
|
+
setScreen("prompts-menu");
|
|
209
|
+
} else if (key.ctrl && input === "c") {
|
|
210
|
+
exit();
|
|
211
|
+
} else if (input && !key.ctrl && !key.meta) {
|
|
212
|
+
setFixCommentsPrompt((v) => v + input);
|
|
213
|
+
}
|
|
127
214
|
}
|
|
128
215
|
});
|
|
129
|
-
if (screen === "
|
|
216
|
+
if (screen === "main") {
|
|
130
217
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
|
|
131
218
|
/* @__PURE__ */ jsx(Text, { bold: true, children: "Configure Automata" }),
|
|
132
219
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
220
|
+
MAIN_MENU_OPTIONS.map((option, index) => /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: index === mainMenuIndex ? "cyan" : void 0, children: [
|
|
221
|
+
index === mainMenuIndex ? "\u276F " : " ",
|
|
222
|
+
option
|
|
223
|
+
] }) }, option)),
|
|
224
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
225
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 to move \xB7 Enter to select \xB7 Ctrl+C to cancel" })
|
|
226
|
+
] });
|
|
227
|
+
}
|
|
228
|
+
if (screen === "remote") {
|
|
229
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
|
|
230
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Remote / Mode" }),
|
|
231
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
133
232
|
/* @__PURE__ */ jsx(Text, { children: "Remote environment type:" }),
|
|
134
233
|
REMOTE_OPTIONS.map((option, index) => /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: index === selectedRemoteIndex ? "cyan" : void 0, children: [
|
|
135
234
|
index === selectedRemoteIndex ? "\u276F " : " ",
|
|
136
235
|
option.label
|
|
137
236
|
] }) }, option.value)),
|
|
138
237
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
139
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 to move \xB7 Enter to confirm \xB7 Ctrl+C to cancel" })
|
|
238
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 to move \xB7 Enter to confirm \xB7 Esc to go back \xB7 Ctrl+C to cancel" })
|
|
140
239
|
] });
|
|
141
240
|
}
|
|
142
241
|
if (screen === "technique") {
|
|
143
242
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
|
|
144
|
-
/* @__PURE__ */ jsx(Text, { bold: true, children: "
|
|
243
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Implement-Next \u2014 Issue Discovery Technique" }),
|
|
145
244
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
146
245
|
/* @__PURE__ */ jsx(Text, { children: "How to find the next issue to work on:" }),
|
|
147
246
|
TECHNIQUE_OPTIONS.map((option, index) => /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: index === selectedTechIndex ? "cyan" : void 0, children: [
|
|
@@ -149,13 +248,13 @@ function ConfigWizard() {
|
|
|
149
248
|
option.label
|
|
150
249
|
] }) }, option.value)),
|
|
151
250
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
152
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 to move \xB7 Enter to confirm \xB7 Ctrl+C to cancel" })
|
|
251
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 to move \xB7 Enter to confirm \xB7 Esc to go back \xB7 Ctrl+C to cancel" })
|
|
153
252
|
] });
|
|
154
253
|
}
|
|
155
254
|
if (screen === "value") {
|
|
156
255
|
const techLabel = TECHNIQUE_OPTIONS.find((t) => t.value === pendingTechnique)?.label ?? pendingTechnique;
|
|
157
256
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
|
|
158
|
-
/* @__PURE__ */ jsx(Text, { bold: true, children: "
|
|
257
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Implement-Next \u2014 Issue Discovery Value" }),
|
|
159
258
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
160
259
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
161
260
|
techLabel,
|
|
@@ -167,22 +266,66 @@ function ConfigWizard() {
|
|
|
167
266
|
] })
|
|
168
267
|
] }),
|
|
169
268
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
170
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Type value \xB7 Enter to
|
|
269
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Type value \xB7 Enter to continue \xB7 Esc to go back \xB7 Ctrl+C to cancel" })
|
|
270
|
+
] });
|
|
271
|
+
}
|
|
272
|
+
if (screen === "system-prompt") {
|
|
273
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
|
|
274
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Implement-Next \u2014 Claude System Prompt" }),
|
|
275
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
276
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
277
|
+
"System prompt (optional):",
|
|
278
|
+
" ",
|
|
279
|
+
/* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
|
|
280
|
+
systemPrompt,
|
|
281
|
+
/* @__PURE__ */ jsx(Text, { children: "_" })
|
|
282
|
+
] })
|
|
283
|
+
] }),
|
|
284
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
285
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Type prompt \xB7 Enter to save and exit \xB7 Esc to go back \xB7 Ctrl+C to cancel" })
|
|
286
|
+
] });
|
|
287
|
+
}
|
|
288
|
+
if (screen === "prompts-menu") {
|
|
289
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
|
|
290
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Prompts" }),
|
|
291
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
292
|
+
PROMPTS_MENU_OPTIONS.map((option, index) => /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: index === promptsMenuIndex ? "cyan" : void 0, children: [
|
|
293
|
+
index === promptsMenuIndex ? "\u276F " : " ",
|
|
294
|
+
option
|
|
295
|
+
] }) }, option)),
|
|
296
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
297
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 to move \xB7 Enter to edit \xB7 Esc to go back \xB7 Ctrl+C to cancel" })
|
|
298
|
+
] });
|
|
299
|
+
}
|
|
300
|
+
if (screen === "sonar-prompt") {
|
|
301
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
|
|
302
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Prompts \u2014 Sonar" }),
|
|
303
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
304
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
305
|
+
"Sonar prompt:",
|
|
306
|
+
" ",
|
|
307
|
+
/* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
|
|
308
|
+
sonarPrompt,
|
|
309
|
+
/* @__PURE__ */ jsx(Text, { children: "_" })
|
|
310
|
+
] })
|
|
311
|
+
] }),
|
|
312
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
313
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Type prompt \xB7 Enter to save \xB7 Esc to go back \xB7 Ctrl+C to cancel" })
|
|
171
314
|
] });
|
|
172
315
|
}
|
|
173
316
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
|
|
174
|
-
/* @__PURE__ */ jsx(Text, { bold: true, children: "
|
|
317
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Prompts \u2014 Fix-Comments" }),
|
|
175
318
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
176
319
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
177
|
-
"
|
|
320
|
+
"Fix-Comments prompt:",
|
|
178
321
|
" ",
|
|
179
322
|
/* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
|
|
180
|
-
|
|
323
|
+
fixCommentsPrompt,
|
|
181
324
|
/* @__PURE__ */ jsx(Text, { children: "_" })
|
|
182
325
|
] })
|
|
183
326
|
] }),
|
|
184
327
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
185
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Type prompt \xB7 Enter to save
|
|
328
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Type prompt \xB7 Enter to save \xB7 Esc to go back \xB7 Ctrl+C to cancel" })
|
|
186
329
|
] });
|
|
187
330
|
}
|
|
188
331
|
|
|
@@ -335,7 +478,31 @@ function fetchCheckRunOutputs(ownerRepo, sha) {
|
|
|
335
478
|
}
|
|
336
479
|
return map;
|
|
337
480
|
}
|
|
338
|
-
function
|
|
481
|
+
function extractSonarProjectKey(url) {
|
|
482
|
+
try {
|
|
483
|
+
const parsed = new URL(url);
|
|
484
|
+
const id = parsed.searchParams.get("id");
|
|
485
|
+
return id ?? null;
|
|
486
|
+
} catch {
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
async function fetchSonarNewIssues(projectKey, prNumber) {
|
|
491
|
+
const apiUrl = `https://sonarcloud.io/api/issues/search?componentKeys=${encodeURIComponent(projectKey)}&pullRequest=${String(prNumber)}&resolved=false&ps=1`;
|
|
492
|
+
const controller = new AbortController();
|
|
493
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
494
|
+
try {
|
|
495
|
+
const response = await fetch(apiUrl, { signal: controller.signal });
|
|
496
|
+
if (!response.ok) return null;
|
|
497
|
+
const data = await response.json();
|
|
498
|
+
return data.paging?.total ?? null;
|
|
499
|
+
} catch {
|
|
500
|
+
return null;
|
|
501
|
+
} finally {
|
|
502
|
+
clearTimeout(timeoutId);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
async function getPrInfoGh(branch) {
|
|
339
506
|
const { stdout, stderr, status } = run2("gh", [
|
|
340
507
|
"pr",
|
|
341
508
|
"view",
|
|
@@ -365,9 +532,35 @@ function getPrInfoGh(branch) {
|
|
|
365
532
|
detailsUrl: enriched?.detailsUrl || c.detailsUrl || ""
|
|
366
533
|
};
|
|
367
534
|
});
|
|
368
|
-
|
|
535
|
+
const sonarCheck = checks.find((c) => {
|
|
536
|
+
try {
|
|
537
|
+
const hostname = new URL(c.detailsUrl).hostname;
|
|
538
|
+
return hostname === "sonarcloud.io" || hostname.endsWith(".sonarcloud.io");
|
|
539
|
+
} catch {
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
let sonarcloudUrl;
|
|
544
|
+
let sonarNewIssues;
|
|
545
|
+
if (sonarCheck) {
|
|
546
|
+
sonarcloudUrl = sonarCheck.detailsUrl;
|
|
547
|
+
const projectKey = extractSonarProjectKey(sonarCheck.detailsUrl);
|
|
548
|
+
if (projectKey) {
|
|
549
|
+
sonarNewIssues = await fetchSonarNewIssues(projectKey, raw.number);
|
|
550
|
+
} else {
|
|
551
|
+
sonarNewIssues = null;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return {
|
|
555
|
+
number: raw.number,
|
|
556
|
+
title: raw.title,
|
|
557
|
+
state: raw.state,
|
|
558
|
+
url: raw.url,
|
|
559
|
+
checks,
|
|
560
|
+
...sonarcloudUrl === void 0 ? {} : { sonarcloudUrl, sonarNewIssues }
|
|
561
|
+
};
|
|
369
562
|
}
|
|
370
|
-
function getPrInfo2(branch) {
|
|
563
|
+
async function getPrInfo2(branch) {
|
|
371
564
|
const config = readConfig();
|
|
372
565
|
if (config.remoteType === "azdo") {
|
|
373
566
|
return getPrInfo();
|
|
@@ -471,6 +664,23 @@ function getPrComments(branch) {
|
|
|
471
664
|
}
|
|
472
665
|
return getPrCommentsGh(branch);
|
|
473
666
|
}
|
|
667
|
+
function resolveCurrentBranchComments() {
|
|
668
|
+
let branch;
|
|
669
|
+
try {
|
|
670
|
+
branch = getCurrentBranch();
|
|
671
|
+
} catch (err) {
|
|
672
|
+
return { ok: false, kind: "error", message: err.message };
|
|
673
|
+
}
|
|
674
|
+
let raw;
|
|
675
|
+
try {
|
|
676
|
+
raw = getPrComments(branch);
|
|
677
|
+
} catch (err) {
|
|
678
|
+
return { ok: false, kind: "error", message: err.message };
|
|
679
|
+
}
|
|
680
|
+
if (raw === "unsupported") return { ok: false, kind: "unsupported" };
|
|
681
|
+
if (raw === null) return { ok: false, kind: "no-pr", branch };
|
|
682
|
+
return { ok: true, branch, comments: raw };
|
|
683
|
+
}
|
|
474
684
|
var SEMVER_RE = /^v?(\d+)\.(\d+)\.(\d+)$/;
|
|
475
685
|
function getLatestTagOnMaster() {
|
|
476
686
|
const { stdout, status } = run2("git", [
|
|
@@ -553,33 +763,34 @@ function formatChecks(checks) {
|
|
|
553
763
|
const sym = checkSymbol(check);
|
|
554
764
|
const pending = check.status !== "COMPLETED" ? " (pending)" : "";
|
|
555
765
|
lines.push(` ${sym} ${check.name}${pending}`);
|
|
556
|
-
if (check.conclusion !== null && FAIL_CONCLUSIONS.has(check.conclusion)) {
|
|
557
|
-
const desc = check.description.trim();
|
|
558
|
-
const url = check.detailsUrl.trim();
|
|
559
|
-
if (desc) lines.push(` Details: ${desc}`);
|
|
560
|
-
if (url) lines.push(` URL: ${url}`);
|
|
561
|
-
if (!desc && !url) lines.push(` Details: (no details available)`);
|
|
562
|
-
}
|
|
563
766
|
}
|
|
564
767
|
return lines.join("\n") + "\n";
|
|
565
768
|
}
|
|
566
769
|
function sleep(ms) {
|
|
567
770
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
568
771
|
}
|
|
772
|
+
function isSonarCheck(check) {
|
|
773
|
+
try {
|
|
774
|
+
const hostname = new URL(check.detailsUrl).hostname;
|
|
775
|
+
return hostname === "sonarcloud.io" || hostname.endsWith(".sonarcloud.io");
|
|
776
|
+
} catch {
|
|
777
|
+
return false;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
569
780
|
function formatFailedChecks(failed) {
|
|
570
|
-
const lines = [];
|
|
781
|
+
const lines = ["FailedChecks:"];
|
|
571
782
|
for (const check of failed) {
|
|
572
783
|
lines.push(` \u2717 ${check.name}`);
|
|
573
784
|
const desc = check.description.trim();
|
|
574
785
|
const url = check.detailsUrl.trim();
|
|
575
786
|
if (desc) lines.push(` Details: ${desc}`);
|
|
576
|
-
if (url) lines.push(` URL: ${url}`);
|
|
787
|
+
if (url && (isSonarCheck(check) || !desc)) lines.push(` URL: ${url}`);
|
|
577
788
|
if (!desc && !url) lines.push(` Details: (no details available)`);
|
|
578
789
|
}
|
|
579
790
|
return lines.join("\n") + "\n";
|
|
580
791
|
}
|
|
581
792
|
var POLL_INTERVAL_MS = 1e4;
|
|
582
|
-
var getPrInfoCmd = new Command2("get-pr-info").description("Show pull request info for the current branch").option("--json", "Output as JSON").option("--wait-finish-checks", "Poll until all checks complete, then
|
|
793
|
+
var getPrInfoCmd = new Command2("get-pr-info").description("Show pull request info for the current branch").option("--json", "Output as JSON").option("--wait-finish-checks", "Poll until all checks complete, then print the normal get-pr-info output").addHelpText(
|
|
583
794
|
"after",
|
|
584
795
|
`
|
|
585
796
|
Check status symbols:
|
|
@@ -588,10 +799,11 @@ Check status symbols:
|
|
|
588
799
|
\u25CF Pending (status: QUEUED or IN_PROGRESS)
|
|
589
800
|
\u25CB Skipped (conclusion: SKIPPED or NEUTRAL)
|
|
590
801
|
|
|
591
|
-
|
|
802
|
+
When checks fail, details are printed in a trailing FailedChecks section.
|
|
592
803
|
See docs/git.md for full output reference.`
|
|
593
804
|
).action(async (options) => {
|
|
594
805
|
let branch;
|
|
806
|
+
let pr = null;
|
|
595
807
|
try {
|
|
596
808
|
branch = getCurrentBranch();
|
|
597
809
|
} catch (err) {
|
|
@@ -600,54 +812,31 @@ See docs/git.md for full output reference.`
|
|
|
600
812
|
process.exit(1);
|
|
601
813
|
}
|
|
602
814
|
if (options.waitFinishChecks) {
|
|
603
|
-
let pr2;
|
|
604
815
|
while (true) {
|
|
605
816
|
try {
|
|
606
|
-
|
|
817
|
+
pr = await getPrInfo2(branch);
|
|
607
818
|
} catch (err) {
|
|
608
819
|
process.stderr.write(`Error: ${err.message}
|
|
609
820
|
`);
|
|
610
821
|
process.exit(1);
|
|
611
822
|
}
|
|
612
|
-
if (
|
|
613
|
-
|
|
614
|
-
`);
|
|
615
|
-
process.exit(1);
|
|
823
|
+
if (pr === null) {
|
|
824
|
+
break;
|
|
616
825
|
}
|
|
617
|
-
const running =
|
|
826
|
+
const running = pr.checks.filter((c) => c.status !== "COMPLETED");
|
|
618
827
|
if (running.length === 0) break;
|
|
619
828
|
process.stdout.write(`Waiting for ${running.length} check(s) to complete...
|
|
620
829
|
`);
|
|
621
830
|
await sleep(POLL_INTERVAL_MS);
|
|
622
831
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
process.stdout.write(`All checks passed. \u2713
|
|
629
|
-
`);
|
|
630
|
-
}
|
|
631
|
-
process.exit(0);
|
|
632
|
-
} else {
|
|
633
|
-
if (options.json) {
|
|
634
|
-
process.stdout.write(JSON.stringify({ result: "failed", failed }, null, 2) + "\n");
|
|
635
|
-
} else {
|
|
636
|
-
process.stdout.write(`${failed.length} check(s) failed:
|
|
832
|
+
} else {
|
|
833
|
+
try {
|
|
834
|
+
pr = await getPrInfo2(branch);
|
|
835
|
+
} catch (err) {
|
|
836
|
+
process.stderr.write(`Error: ${err.message}
|
|
637
837
|
`);
|
|
638
|
-
process.stdout.write(formatFailedChecks(failed));
|
|
639
|
-
}
|
|
640
838
|
process.exit(1);
|
|
641
839
|
}
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
644
|
-
let pr;
|
|
645
|
-
try {
|
|
646
|
-
pr = getPrInfo2(branch);
|
|
647
|
-
} catch (err) {
|
|
648
|
-
process.stderr.write(`Error: ${err.message}
|
|
649
|
-
`);
|
|
650
|
-
process.exit(1);
|
|
651
840
|
}
|
|
652
841
|
if (pr === null) {
|
|
653
842
|
process.stdout.write(`No pull request found for branch: ${branch}
|
|
@@ -657,13 +846,24 @@ See docs/git.md for full output reference.`
|
|
|
657
846
|
if (options.json) {
|
|
658
847
|
process.stdout.write(JSON.stringify(pr, null, 2) + "\n");
|
|
659
848
|
} else {
|
|
849
|
+
const failed = pr.checks.filter((c) => c.conclusion !== null && FAIL_CONCLUSIONS.has(c.conclusion));
|
|
660
850
|
process.stdout.write(`PR: #${pr.number}
|
|
661
851
|
Title: ${pr.title}
|
|
662
852
|
State: ${pr.state}
|
|
663
853
|
URL: ${pr.url}
|
|
664
854
|
`);
|
|
855
|
+
if (pr.sonarcloudUrl !== void 0) {
|
|
856
|
+
process.stdout.write(`Sonar: ${pr.sonarcloudUrl}
|
|
857
|
+
`);
|
|
858
|
+
const issueStr = pr.sonarNewIssues === null || pr.sonarNewIssues === void 0 ? "unavailable" : String(pr.sonarNewIssues);
|
|
859
|
+
process.stdout.write(`Sonar New Issues: ${issueStr}
|
|
860
|
+
`);
|
|
861
|
+
}
|
|
665
862
|
process.stdout.write(formatCheckSummary(pr.checks));
|
|
666
863
|
process.stdout.write(formatChecks(pr.checks));
|
|
864
|
+
if (failed.length > 0) {
|
|
865
|
+
process.stdout.write(formatFailedChecks(failed));
|
|
866
|
+
}
|
|
667
867
|
}
|
|
668
868
|
});
|
|
669
869
|
var ANSI_ESCAPE_RE = new RegExp("\x1B(?:[@-Z\\\\-_]|\\[[0-?]*[ -/]*[@-~])", "g");
|
|
@@ -724,7 +924,7 @@ ${safeBody}`);
|
|
|
724
924
|
}
|
|
725
925
|
process.stdout.write(lines.join("\n\n") + "\n");
|
|
726
926
|
});
|
|
727
|
-
var finishFeatureCmd = new Command2("finish-feature").description("Clean up a merged feature branch: checkout develop, pull, and delete local branch").action(() => {
|
|
927
|
+
var finishFeatureCmd = new Command2("finish-feature").description("Clean up a merged feature branch: checkout develop, pull, and delete local branch").action(async () => {
|
|
728
928
|
let branch;
|
|
729
929
|
try {
|
|
730
930
|
branch = getCurrentBranch();
|
|
@@ -745,7 +945,7 @@ var finishFeatureCmd = new Command2("finish-feature").description("Clean up a me
|
|
|
745
945
|
}
|
|
746
946
|
let pr;
|
|
747
947
|
try {
|
|
748
|
-
pr = getPrInfo2(branch);
|
|
948
|
+
pr = await getPrInfo2(branch);
|
|
749
949
|
} catch (err) {
|
|
750
950
|
process.stderr.write(`Error: ${err.message}
|
|
751
951
|
`);
|
|
@@ -876,9 +1076,6 @@ var gitCommand = new Command2("git").description("Git workflow commands (some re
|
|
|
876
1076
|
|
|
877
1077
|
// src/commands/getReady.ts
|
|
878
1078
|
import { Command as Command3 } from "commander";
|
|
879
|
-
import { spawnSync as spawnSync4 } from "child_process";
|
|
880
|
-
import { existsSync } from "fs";
|
|
881
|
-
import { delimiter, join as join2 } from "path";
|
|
882
1079
|
|
|
883
1080
|
// src/config/githubService.ts
|
|
884
1081
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
@@ -937,7 +1134,42 @@ function postComment(issueNumber, body) {
|
|
|
937
1134
|
}
|
|
938
1135
|
}
|
|
939
1136
|
|
|
940
|
-
// src/
|
|
1137
|
+
// src/claude/claudeService.ts
|
|
1138
|
+
import { spawn, spawnSync as spawnSync4 } from "child_process";
|
|
1139
|
+
import { createInterface } from "readline";
|
|
1140
|
+
import { existsSync } from "fs";
|
|
1141
|
+
import { delimiter, join as join2 } from "path";
|
|
1142
|
+
|
|
1143
|
+
// src/cli/spawnUtils.ts
|
|
1144
|
+
function truncate(str, max) {
|
|
1145
|
+
return str.length > max ? str.slice(0, max) + "..." : str;
|
|
1146
|
+
}
|
|
1147
|
+
function handleSpawnError(error, toolName) {
|
|
1148
|
+
if (!error) return;
|
|
1149
|
+
const err = error;
|
|
1150
|
+
if (err.code === "ENOENT") {
|
|
1151
|
+
process.stderr.write(`Error: \`${toolName}\` CLI is not installed or not on PATH.
|
|
1152
|
+
`);
|
|
1153
|
+
process.exit(1);
|
|
1154
|
+
}
|
|
1155
|
+
process.stderr.write(`Error: ${err.message}
|
|
1156
|
+
`);
|
|
1157
|
+
process.exit(1);
|
|
1158
|
+
}
|
|
1159
|
+
function handleExitCode(status, toolName) {
|
|
1160
|
+
if (status === null) {
|
|
1161
|
+
process.stderr.write(`Error: ${toolName} terminated abnormally (exit code is null, likely due to a signal).
|
|
1162
|
+
`);
|
|
1163
|
+
process.exit(1);
|
|
1164
|
+
}
|
|
1165
|
+
if (status !== 0) {
|
|
1166
|
+
process.stderr.write(`Error: ${toolName} exited with code ${status}.
|
|
1167
|
+
`);
|
|
1168
|
+
process.exit(status);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
// src/claude/claudeService.ts
|
|
941
1173
|
function resolveCommand(name) {
|
|
942
1174
|
const pathDirs = (process.env["PATH"] ?? "").split(delimiter);
|
|
943
1175
|
for (const dir of pathDirs) {
|
|
@@ -946,30 +1178,147 @@ function resolveCommand(name) {
|
|
|
946
1178
|
}
|
|
947
1179
|
return name;
|
|
948
1180
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
1181
|
+
var MODEL_IDS = {
|
|
1182
|
+
opus: "claude-opus-4-6",
|
|
1183
|
+
sonnet: "claude-sonnet-4-6",
|
|
1184
|
+
haiku: "claude-haiku-4-5-20251001"
|
|
1185
|
+
};
|
|
1186
|
+
function resolveModelOption(opts) {
|
|
1187
|
+
const selected = ["opus", "sonnet", "haiku"].filter((m) => opts[m]);
|
|
1188
|
+
if (selected.length > 1) {
|
|
1189
|
+
process.stderr.write(`Error: --${selected[0]} and --${selected[1]} are mutually exclusive.
|
|
1190
|
+
`);
|
|
1191
|
+
process.exit(1);
|
|
1192
|
+
}
|
|
1193
|
+
return selected.length === 1 ? MODEL_IDS[selected[0]] : void 0;
|
|
1194
|
+
}
|
|
1195
|
+
function invokeClaudeCode(prompt, options = {}) {
|
|
1196
|
+
if (options.verbose) {
|
|
1197
|
+
return invokeClaudeCodeVerbose(prompt, options.yolo ?? false, options.model);
|
|
1198
|
+
}
|
|
1199
|
+
invokeClaudeCodeSync(prompt, options.yolo ?? false, options.model);
|
|
1200
|
+
}
|
|
1201
|
+
function invokeClaudeCodeSync(prompt, yolo, model) {
|
|
953
1202
|
const claudeBin = resolveCommand("claude");
|
|
954
|
-
const args =
|
|
1203
|
+
const args = [];
|
|
1204
|
+
if (yolo) args.push("--dangerously-skip-permissions");
|
|
1205
|
+
if (model) args.push("--model", model);
|
|
1206
|
+
args.push("-p", prompt);
|
|
955
1207
|
const result = spawnSync4(claudeBin, args, { encoding: "utf8", stdio: "inherit" });
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1208
|
+
handleSpawnError(result.error, "claude");
|
|
1209
|
+
handleExitCode(result.status, "Claude Code");
|
|
1210
|
+
}
|
|
1211
|
+
function invokeClaudeCodeVerbose(prompt, yolo, model) {
|
|
1212
|
+
return new Promise((resolve2) => {
|
|
1213
|
+
const claudeBin = resolveCommand("claude");
|
|
1214
|
+
const args = [];
|
|
1215
|
+
if (yolo) args.push("--dangerously-skip-permissions");
|
|
1216
|
+
if (model) args.push("--model", model);
|
|
1217
|
+
args.push("--verbose", "--output-format", "stream-json", "-p", prompt);
|
|
1218
|
+
const child = spawn(claudeBin, args, { stdio: ["inherit", "pipe", "inherit"] });
|
|
1219
|
+
const rl = createInterface({ input: child.stdout });
|
|
1220
|
+
let turnCount = 0;
|
|
1221
|
+
child.on("error", (err) => {
|
|
1222
|
+
handleSpawnError(err, "claude");
|
|
1223
|
+
});
|
|
1224
|
+
rl.on("line", (line) => {
|
|
1225
|
+
try {
|
|
1226
|
+
const event = JSON.parse(line);
|
|
1227
|
+
formatEvent(event, turnCount);
|
|
1228
|
+
if (event["type"] === "assistant") turnCount++;
|
|
1229
|
+
} catch {
|
|
1230
|
+
}
|
|
1231
|
+
});
|
|
1232
|
+
child.on("close", (code) => {
|
|
1233
|
+
handleExitCode(code, "Claude Code");
|
|
1234
|
+
resolve2();
|
|
1235
|
+
});
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
function formatEvent(event, turnCount) {
|
|
1239
|
+
const type = event["type"];
|
|
1240
|
+
if (type === "assistant") {
|
|
1241
|
+
const message = event["message"];
|
|
1242
|
+
const content = message?.["content"];
|
|
1243
|
+
if (!content) return;
|
|
1244
|
+
for (const block of content) {
|
|
1245
|
+
if (block["type"] === "tool_use") {
|
|
1246
|
+
const toolName = block["name"];
|
|
1247
|
+
const input = block["input"];
|
|
1248
|
+
const summary = summarizeTool(toolName, input);
|
|
1249
|
+
process.stderr.write(` [step ${turnCount + 1}] ${summary}
|
|
1250
|
+
`);
|
|
1251
|
+
} else if (block["type"] === "text") {
|
|
1252
|
+
const text = block["text"] ?? "";
|
|
1253
|
+
if (text.length > 0) {
|
|
1254
|
+
const preview = text.length > 120 ? text.slice(0, 120) + "..." : text;
|
|
1255
|
+
const firstLine = preview.split("\n")[0];
|
|
1256
|
+
process.stderr.write(` [step ${turnCount + 1}] ${firstLine}
|
|
1257
|
+
`);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
961
1260
|
}
|
|
962
|
-
|
|
1261
|
+
} else if (type === "result") {
|
|
1262
|
+
const result = event["result"];
|
|
1263
|
+
const cost = event["cost_usd"];
|
|
1264
|
+
const duration = event["duration_ms"];
|
|
1265
|
+
const turns = event["num_turns"];
|
|
1266
|
+
process.stderr.write("\n--- Result ---\n");
|
|
1267
|
+
if (cost !== void 0 || duration !== void 0 || turns !== void 0) {
|
|
1268
|
+
const parts = [];
|
|
1269
|
+
if (turns !== void 0) parts.push(`${turns} turns`);
|
|
1270
|
+
if (duration !== void 0) parts.push(`${(duration / 1e3).toFixed(1)}s`);
|
|
1271
|
+
if (cost !== void 0) parts.push(`$${cost.toFixed(4)}`);
|
|
1272
|
+
process.stderr.write(` [info] ${parts.join(" | ")}
|
|
963
1273
|
`);
|
|
964
|
-
|
|
1274
|
+
}
|
|
1275
|
+
if (result) {
|
|
1276
|
+
process.stdout.write(result + "\n");
|
|
1277
|
+
}
|
|
965
1278
|
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
`
|
|
969
|
-
|
|
1279
|
+
}
|
|
1280
|
+
function summarizeTool(name, input) {
|
|
1281
|
+
if (!input) return `tool: ${name}`;
|
|
1282
|
+
switch (name) {
|
|
1283
|
+
case "Read":
|
|
1284
|
+
return `reading ${input["file_path"] ?? "file"}`;
|
|
1285
|
+
case "Write":
|
|
1286
|
+
return `writing ${input["file_path"] ?? "file"}`;
|
|
1287
|
+
case "Edit":
|
|
1288
|
+
return `editing ${input["file_path"] ?? "file"}`;
|
|
1289
|
+
case "Bash":
|
|
1290
|
+
return `running: ${truncate(String(input["command"] ?? ""), 80)}`;
|
|
1291
|
+
case "Glob":
|
|
1292
|
+
return `searching files: ${input["pattern"] ?? ""}`;
|
|
1293
|
+
case "Grep":
|
|
1294
|
+
return `searching content: ${truncate(String(input["pattern"] ?? ""), 60)}`;
|
|
1295
|
+
case "Agent":
|
|
1296
|
+
return `spawning agent: ${input["description"] ?? name}`;
|
|
1297
|
+
default:
|
|
1298
|
+
return `tool: ${name}`;
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
// src/codex/codexService.ts
|
|
1303
|
+
import { spawnSync as spawnSync5 } from "child_process";
|
|
1304
|
+
function invokeCodexCode(prompt, options = {}) {
|
|
1305
|
+
if (options.verbose) {
|
|
1306
|
+
process.stderr.write("Warning: --verbose is not supported for Codex and will be ignored.\n");
|
|
970
1307
|
}
|
|
1308
|
+
invokeCodexCodeSync(prompt, options.yolo ?? false);
|
|
1309
|
+
}
|
|
1310
|
+
function invokeCodexCodeSync(prompt, yolo) {
|
|
1311
|
+
const codexBin = resolveCommand("codex");
|
|
1312
|
+
const args = ["exec"];
|
|
1313
|
+
if (yolo) args.push("--dangerously-bypass-approvals-and-sandbox");
|
|
1314
|
+
args.push(prompt);
|
|
1315
|
+
const result = spawnSync5(codexBin, args, { encoding: "utf8", stdio: "inherit" });
|
|
1316
|
+
handleSpawnError(result.error, "codex");
|
|
1317
|
+
handleExitCode(result.status, "Codex");
|
|
971
1318
|
}
|
|
972
|
-
|
|
1319
|
+
|
|
1320
|
+
// src/commands/getReady.ts
|
|
1321
|
+
var implementNextCommand = new Command3("implement-next").description("Find the next open GitHub issue matching the configured filter, claim it, and invoke the AI code assistant (Claude or Codex)").option("--json", "Output issue details as JSON").option("--no-claude", "Skip all AI invocation (Claude or Codex) after claiming the issue").option("--codex", "Use Codex CLI instead of Claude Code").option("--query-only", "Print issue content and exit without claiming or invoking any AI tools").option("--yolo", "Launch with --dangerously-skip-permissions (Claude) or --dangerously-bypass-approvals-and-sandbox (Codex)").option("--verbose", "Show step-by-step progress summary and final result").option("--opus", "Use claude-opus-4-6").option("--sonnet", "Use claude-sonnet-4-6").option("--haiku", "Use claude-haiku-4-5-20251001").action(async (options) => {
|
|
973
1322
|
const config = readConfig();
|
|
974
1323
|
if (config.remoteType !== "gh") {
|
|
975
1324
|
process.stderr.write(
|
|
@@ -1022,16 +1371,154 @@ ${issue.body}
|
|
|
1022
1371
|
process.exit(1);
|
|
1023
1372
|
}
|
|
1024
1373
|
if (options.claude !== false) {
|
|
1025
|
-
|
|
1374
|
+
const systemPrompt = config.claudeSystemPrompt ?? DEFAULT_CLAUDE_SYSTEM_PROMPT;
|
|
1375
|
+
const prompt = `${systemPrompt}
|
|
1376
|
+
|
|
1377
|
+
${issue.body}`;
|
|
1378
|
+
if (options.codex) {
|
|
1379
|
+
await invokeCodexCode(prompt, { yolo: options.yolo, verbose: options.verbose });
|
|
1380
|
+
} else {
|
|
1381
|
+
const model = resolveModelOption(options);
|
|
1382
|
+
await invokeClaudeCode(prompt, { yolo: options.yolo, verbose: options.verbose, model });
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
});
|
|
1386
|
+
|
|
1387
|
+
// src/commands/test.ts
|
|
1388
|
+
import { Command as Command4 } from "commander";
|
|
1389
|
+
var testClaudeCmd = new Command4("claude").description("Test Claude Code invocation with a user-supplied prompt").requiredOption("--prompt <string>", "Prompt to send to Claude Code").option("--yolo", "Launch Claude Code with --dangerously-skip-permissions").option("--verbose", "Show step-by-step progress summary and final result").option("--opus", "Use claude-opus-4-6").option("--sonnet", "Use claude-sonnet-4-6").option("--haiku", "Use claude-haiku-4-5-20251001").action(async (options) => {
|
|
1390
|
+
const model = resolveModelOption(options);
|
|
1391
|
+
await invokeClaudeCode(options.prompt, { yolo: options.yolo, verbose: options.verbose, model });
|
|
1392
|
+
});
|
|
1393
|
+
var testCodexCmd = new Command4("codex").description("Test Codex CLI invocation with a user-supplied prompt").requiredOption("--prompt <string>", "Prompt to send to Codex CLI").option("--yolo", "Launch Codex with --dangerously-bypass-approvals-and-sandbox").option("--verbose", "Not supported for Codex; prints a warning and is otherwise ignored").action((options) => {
|
|
1394
|
+
invokeCodexCode(options.prompt, { yolo: options.yolo, verbose: options.verbose });
|
|
1395
|
+
});
|
|
1396
|
+
var testCommand = new Command4("test").description("Test commands for verifying automata integrations").addCommand(testClaudeCmd).addCommand(testCodexCmd);
|
|
1397
|
+
|
|
1398
|
+
// src/commands/executePrompt.ts
|
|
1399
|
+
import { Command as Command5 } from "commander";
|
|
1400
|
+
var PUSH_INSTRUCTION = "Once all changes are complete, stage every modified file, create a single commit with a clear and descriptive commit message that summarises what was fixed, and push the branch to the remote.";
|
|
1401
|
+
function withPush(prompt, push) {
|
|
1402
|
+
return push ? `${prompt}
|
|
1403
|
+
|
|
1404
|
+
${PUSH_INSTRUCTION}` : prompt;
|
|
1405
|
+
}
|
|
1406
|
+
function addAiOptions(cmd) {
|
|
1407
|
+
return cmd.option("--codex", "Use Codex CLI instead of Claude Code").option("--verbose", "Show step-by-step progress (Claude only; ignored for Codex)").option("--push", "Append instruction to commit and push changes after the AI finishes").option("--opus", "Use claude-opus-4-6 (Claude only)").option("--sonnet", "Use claude-sonnet-4-6 (Claude only)").option("--haiku", "Use claude-haiku-4-5-20251001 (Claude only)");
|
|
1408
|
+
}
|
|
1409
|
+
var executeSonarCmd = addAiOptions(
|
|
1410
|
+
new Command5("sonar").description(
|
|
1411
|
+
"Check the current branch for a SonarCloud analysis and invoke the AI with the Sonar prompt and analysis URL"
|
|
1412
|
+
)
|
|
1413
|
+
).action(async (options) => {
|
|
1414
|
+
let branch;
|
|
1415
|
+
try {
|
|
1416
|
+
branch = getCurrentBranch();
|
|
1417
|
+
} catch (err) {
|
|
1418
|
+
process.stderr.write(`Error: ${err.message}
|
|
1419
|
+
`);
|
|
1420
|
+
process.exit(1);
|
|
1421
|
+
}
|
|
1422
|
+
let pr;
|
|
1423
|
+
try {
|
|
1424
|
+
pr = await getPrInfo2(branch);
|
|
1425
|
+
} catch (err) {
|
|
1426
|
+
process.stderr.write(`Error: ${err.message}
|
|
1427
|
+
`);
|
|
1428
|
+
process.exit(1);
|
|
1429
|
+
}
|
|
1430
|
+
if (pr === null) {
|
|
1431
|
+
process.stderr.write(`Error: No pull request found for branch: ${branch}
|
|
1432
|
+
`);
|
|
1433
|
+
process.exit(1);
|
|
1434
|
+
}
|
|
1435
|
+
if (!pr.sonarcloudUrl) {
|
|
1436
|
+
process.stderr.write(
|
|
1437
|
+
`Error: No SonarCloud analysis found for PR #${pr.number}. Ensure a SonarCloud check is configured on this repository.
|
|
1438
|
+
`
|
|
1439
|
+
);
|
|
1440
|
+
process.exit(1);
|
|
1441
|
+
}
|
|
1442
|
+
const config = readConfig();
|
|
1443
|
+
const sonarPromptText = config.prompts?.sonar ?? DEFAULT_SONAR_PROMPT;
|
|
1444
|
+
const fullPrompt = withPush(
|
|
1445
|
+
`${sonarPromptText}
|
|
1446
|
+
|
|
1447
|
+
SonarCloud analysis URL: ${pr.sonarcloudUrl}`,
|
|
1448
|
+
options.push
|
|
1449
|
+
);
|
|
1450
|
+
if (options.codex) {
|
|
1451
|
+
invokeCodexCode(fullPrompt, { yolo: true });
|
|
1452
|
+
} else {
|
|
1453
|
+
const model = resolveModelOption(options);
|
|
1454
|
+
await invokeClaudeCode(fullPrompt, { yolo: true, verbose: options.verbose, model });
|
|
1455
|
+
}
|
|
1456
|
+
});
|
|
1457
|
+
function formatComments(comments) {
|
|
1458
|
+
return comments.map((c) => {
|
|
1459
|
+
const loc = c.line === null ? `${c.path}:(file)` : `${c.path}:${String(c.line)}`;
|
|
1460
|
+
return `[${c.author}] on ${loc}
|
|
1461
|
+
${c.body}`;
|
|
1462
|
+
}).join("\n\n");
|
|
1463
|
+
}
|
|
1464
|
+
var executeFixCommentsCmd = addAiOptions(
|
|
1465
|
+
new Command5("fix-comments").description(
|
|
1466
|
+
"Fetch open review comments on the current PR and invoke the AI with the Fix-Comments prompt"
|
|
1467
|
+
)
|
|
1468
|
+
).action(async (options) => {
|
|
1469
|
+
const result = resolveCurrentBranchComments();
|
|
1470
|
+
if (!result.ok) {
|
|
1471
|
+
if (result.kind === "error") {
|
|
1472
|
+
process.stderr.write(`Error: ${result.message}
|
|
1473
|
+
`);
|
|
1474
|
+
process.exit(1);
|
|
1475
|
+
}
|
|
1476
|
+
if (result.kind === "unsupported") {
|
|
1477
|
+
process.stderr.write(
|
|
1478
|
+
`Error: fix-comments is not supported for Azure DevOps. See docs/azdo-gap.md for details.
|
|
1479
|
+
`
|
|
1480
|
+
);
|
|
1481
|
+
process.exit(1);
|
|
1482
|
+
}
|
|
1483
|
+
process.stderr.write(`Error: No pull request found for branch: ${result.branch}
|
|
1484
|
+
`);
|
|
1485
|
+
process.exit(1);
|
|
1486
|
+
}
|
|
1487
|
+
const { comments } = result;
|
|
1488
|
+
if (comments.length === 0) {
|
|
1489
|
+
process.stderr.write(`Error: No open review comments found on the pull request.
|
|
1490
|
+
`);
|
|
1491
|
+
process.exit(1);
|
|
1492
|
+
}
|
|
1493
|
+
process.stdout.write(`Found ${String(comments.length)} open review comment${comments.length === 1 ? "" : "s"} on PR. Invoking AI\u2026
|
|
1494
|
+
`);
|
|
1495
|
+
const config = readConfig();
|
|
1496
|
+
const promptText = config.prompts?.fixComments ?? DEFAULT_FIX_COMMENTS_PROMPT;
|
|
1497
|
+
const fullPrompt = withPush(
|
|
1498
|
+
`${promptText}
|
|
1499
|
+
|
|
1500
|
+
Open review comments:
|
|
1501
|
+
|
|
1502
|
+
${formatComments(comments)}`,
|
|
1503
|
+
options.push
|
|
1504
|
+
);
|
|
1505
|
+
if (options.codex) {
|
|
1506
|
+
invokeCodexCode(fullPrompt, { yolo: true });
|
|
1507
|
+
} else {
|
|
1508
|
+
const model = resolveModelOption(options);
|
|
1509
|
+
await invokeClaudeCode(fullPrompt, { yolo: true, verbose: options.verbose, model });
|
|
1026
1510
|
}
|
|
1027
1511
|
});
|
|
1512
|
+
var executePromptCommand = new Command5("execute-prompt").description("Execute a configured custom prompt using an AI assistant").addCommand(executeSonarCmd).addCommand(executeFixCommentsCmd);
|
|
1028
1513
|
|
|
1029
1514
|
// src/index.ts
|
|
1030
|
-
var program = new
|
|
1515
|
+
var program = new Command6();
|
|
1031
1516
|
program.name("automata").description("Automata CLI tool").version(version, "-v, --version");
|
|
1032
1517
|
program.addCommand(configCommand);
|
|
1033
1518
|
program.addCommand(gitCommand);
|
|
1034
1519
|
program.addCommand(implementNextCommand);
|
|
1520
|
+
program.addCommand(testCommand);
|
|
1521
|
+
program.addCommand(executePromptCommand);
|
|
1035
1522
|
program.showHelpAfterError();
|
|
1036
1523
|
program.parse();
|
|
1037
1524
|
if (process.argv.length <= 2) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "automata-cli",
|
|
3
|
-
"version": "0.1.0-bugfix-
|
|
3
|
+
"version": "0.1.0-bugfix-automata-deletion.94",
|
|
4
4
|
"description": "Automata CLI tool",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"@types/node": "^25.5.0",
|
|
34
34
|
"@types/react": "^19.2.14",
|
|
35
35
|
"eslint": "^10.1.0",
|
|
36
|
+
"ink-testing-library": "^4.0.0",
|
|
36
37
|
"prettier": "^3.8.1",
|
|
37
38
|
"tsup": "^8.5.1",
|
|
38
39
|
"typescript": "^5.9.3",
|