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.
Files changed (2) hide show
  1. package/dist/index.js +575 -88
  2. 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 Command4 } from "commander";
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("remote");
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 === "remote") {
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 || key.ctrl && input === "c") {
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 || key.ctrl && input === "c") {
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 || key.ctrl && input === "c") {
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 || key.ctrl && input === "c") {
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 === "remote") {
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: "Configure Issue Discovery Technique" }),
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: "Configure Issue Discovery Value" }),
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 confirm \xB7 Ctrl+C to cancel" })
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: "Configure Claude System Prompt" }),
317
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "Prompts \u2014 Fix-Comments" }),
175
318
  /* @__PURE__ */ jsx(Text, { children: " " }),
176
319
  /* @__PURE__ */ jsxs(Text, { children: [
177
- "System prompt (optional):",
320
+ "Fix-Comments prompt:",
178
321
  " ",
179
322
  /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
180
- systemPrompt,
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 and exit \xB7 Ctrl+C to cancel" })
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 getPrInfoGh(branch) {
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
- return { number: raw.number, title: raw.title, state: raw.state, url: raw.url, checks };
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 report pass/fail (exit 1 on failure)").addHelpText(
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
- Failure details are printed beneath each \u2717 check.
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
- pr2 = getPrInfo2(branch);
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 (pr2 === null) {
613
- process.stderr.write(`Error: No pull request found for branch: ${branch}
614
- `);
615
- process.exit(1);
823
+ if (pr === null) {
824
+ break;
616
825
  }
617
- const running = pr2.checks.filter((c) => c.status !== "COMPLETED");
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
- const failed = pr2.checks.filter((c) => c.conclusion !== null && FAIL_CONCLUSIONS.has(c.conclusion));
624
- if (failed.length === 0) {
625
- if (options.json) {
626
- process.stdout.write(JSON.stringify({ result: "passed" }, null, 2) + "\n");
627
- } else {
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/commands/getReady.ts
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
- function invokeClaudeCode(issue, systemPrompt, yolo) {
950
- const prompt = systemPrompt ? `${systemPrompt}
951
-
952
- ${issue.body}` : issue.body;
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 = yolo ? ["--dangerously-skip-permissions", "-p", prompt] : ["-p", prompt];
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
- if (result.error) {
957
- const err = result.error;
958
- if (err.code === "ENOENT") {
959
- process.stderr.write("Error: `claude` CLI is not installed or not on PATH.\n");
960
- process.exit(1);
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
- process.stderr.write(`Error: ${err.message}
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
- process.exit(1);
1274
+ }
1275
+ if (result) {
1276
+ process.stdout.write(result + "\n");
1277
+ }
965
1278
  }
966
- if (result.status !== 0) {
967
- process.stderr.write(`Error: Claude Code exited with code ${result.status ?? "unknown"}.
968
- `);
969
- process.exit(result.status ?? 1);
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
- var implementNextCommand = new Command3("implement-next").description("Find the next open GitHub issue matching the configured filter, claim it, and invoke Claude Code").option("--json", "Output issue details as JSON").option("--no-claude", "Skip Claude Code invocation after claiming the issue").option("--query-only", "Print issue content and exit without claiming or invoking Claude").option("--yolo", "Launch Claude Code with --dangerously-skip-permissions").action((options) => {
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
- invokeClaudeCode(issue, config.claudeSystemPrompt, options.yolo ?? false);
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 Command4();
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-nextwork.57",
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",