automata-cli 0.1.0-009-codex-flag-implement-next.75 → 0.1.0-010-sonarcloud-prompts-execute.79

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 +214 -25
  2. package/package.json +1 -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 Command5 } 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,7 @@ 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_SONAR_PROMPT = "You are an expert software engineer. You have been given the URL of a SonarCloud analysis for this pull request. Please visit the SonarCloud analysis URL provided and fix all new issues reported. Focus on code smells, bugs, and vulnerabilities flagged as new in this PR. Make targeted, minimal changes that resolve each issue without altering unrelated code.";
26
27
  var CONFIG_DIR = ".automata";
27
28
  var CONFIG_FILE = "config.json";
28
29
  function configPath() {
@@ -53,22 +54,42 @@ var TECHNIQUE_OPTIONS = [
53
54
  { label: "By Assignee", value: "assignee" },
54
55
  { label: "By Title Contains", value: "title-contains" }
55
56
  ];
57
+ var MAIN_MENU_OPTIONS = ["Remote / Mode", "Implement-Next", "Prompts"];
56
58
  function ConfigWizard() {
57
59
  const existing = readConfig();
58
60
  const initialRemoteIndex = REMOTE_OPTIONS.findIndex((o) => o.value === existing.remoteType);
59
61
  const initialTechIndex = TECHNIQUE_OPTIONS.findIndex((o) => o.value === existing.issueDiscoveryTechnique);
60
- const [screen, setScreen] = useState("remote");
62
+ const [screen, setScreen] = useState("main");
63
+ const [mainMenuIndex, setMainMenuIndex] = useState(0);
61
64
  const [selectedRemoteIndex, setSelectedRemoteIndex] = useState(initialRemoteIndex >= 0 ? initialRemoteIndex : 0);
62
65
  const [selectedTechIndex, setSelectedTechIndex] = useState(initialTechIndex >= 0 ? initialTechIndex : 0);
63
66
  const [discoveryValue, setDiscoveryValue] = useState(existing.issueDiscoveryValue ?? "");
64
67
  const [systemPrompt, setSystemPrompt] = useState(existing.claudeSystemPrompt ?? "");
68
+ const [sonarPrompt, setSonarPrompt] = useState(existing.prompts?.sonar ?? DEFAULT_SONAR_PROMPT);
65
69
  const [pendingRemote, setPendingRemote] = useState(existing.remoteType ?? "gh");
66
70
  const [pendingTechnique, setPendingTechnique] = useState(
67
71
  existing.issueDiscoveryTechnique ?? "label"
68
72
  );
69
73
  const { exit } = useApp();
70
74
  useInput((input, key) => {
71
- if (screen === "remote") {
75
+ if (screen === "main") {
76
+ if (key.upArrow) {
77
+ setMainMenuIndex((i) => i > 0 ? i - 1 : MAIN_MENU_OPTIONS.length - 1);
78
+ } else if (key.downArrow) {
79
+ setMainMenuIndex((i) => i < MAIN_MENU_OPTIONS.length - 1 ? i + 1 : 0);
80
+ } else if (key.return) {
81
+ const chosen = MAIN_MENU_OPTIONS[mainMenuIndex];
82
+ if (chosen === "Remote / Mode") {
83
+ setScreen("remote");
84
+ } else if (chosen === "Implement-Next") {
85
+ setScreen("technique");
86
+ } else {
87
+ setScreen("prompts-menu");
88
+ }
89
+ } else if (key.escape || key.ctrl && input === "c") {
90
+ exit();
91
+ }
92
+ } else if (screen === "remote") {
72
93
  if (key.upArrow) {
73
94
  setSelectedRemoteIndex((i) => i > 0 ? i - 1 : REMOTE_OPTIONS.length - 1);
74
95
  } else if (key.downArrow) {
@@ -82,7 +103,9 @@ function ConfigWizard() {
82
103
  writeConfig({ ...existing, remoteType: chosen.value });
83
104
  exit();
84
105
  }
85
- } else if (key.escape || key.ctrl && input === "c") {
106
+ } else if (key.escape) {
107
+ setScreen("main");
108
+ } else if (key.ctrl && input === "c") {
86
109
  exit();
87
110
  }
88
111
  } else if (screen === "technique") {
@@ -94,7 +117,9 @@ function ConfigWizard() {
94
117
  const chosen = TECHNIQUE_OPTIONS[selectedTechIndex];
95
118
  setPendingTechnique(chosen.value);
96
119
  setScreen("value");
97
- } else if (key.escape || key.ctrl && input === "c") {
120
+ } else if (key.escape) {
121
+ setScreen("main");
122
+ } else if (key.ctrl && input === "c") {
98
123
  exit();
99
124
  }
100
125
  } else if (screen === "value") {
@@ -102,7 +127,9 @@ function ConfigWizard() {
102
127
  setScreen("system-prompt");
103
128
  } else if (key.backspace || key.delete) {
104
129
  setDiscoveryValue((v) => v.slice(0, -1));
105
- } else if (key.escape || key.ctrl && input === "c") {
130
+ } else if (key.escape) {
131
+ setScreen("main");
132
+ } else if (key.ctrl && input === "c") {
106
133
  exit();
107
134
  } else if (input && !key.ctrl && !key.meta) {
108
135
  setDiscoveryValue((v) => v + input);
@@ -119,29 +146,67 @@ function ConfigWizard() {
119
146
  exit();
120
147
  } else if (key.backspace || key.delete) {
121
148
  setSystemPrompt((v) => v.slice(0, -1));
122
- } else if (key.escape || key.ctrl && input === "c") {
149
+ } else if (key.escape) {
150
+ setScreen("main");
151
+ } else if (key.ctrl && input === "c") {
123
152
  exit();
124
153
  } else if (input && !key.ctrl && !key.meta) {
125
154
  setSystemPrompt((v) => v + input);
126
155
  }
156
+ } else if (screen === "prompts-menu") {
157
+ if (key.return) {
158
+ setScreen("sonar-prompt");
159
+ } else if (key.escape) {
160
+ setScreen("main");
161
+ } else if (key.ctrl && input === "c") {
162
+ exit();
163
+ }
164
+ } else if (screen === "sonar-prompt") {
165
+ if (key.return) {
166
+ writeConfig({
167
+ ...existing,
168
+ prompts: { ...existing.prompts, sonar: sonarPrompt || void 0 }
169
+ });
170
+ exit();
171
+ } else if (key.backspace || key.delete) {
172
+ setSonarPrompt((v) => v.slice(0, -1));
173
+ } else if (key.escape) {
174
+ setScreen("prompts-menu");
175
+ } else if (key.ctrl && input === "c") {
176
+ exit();
177
+ } else if (input && !key.ctrl && !key.meta) {
178
+ setSonarPrompt((v) => v + input);
179
+ }
127
180
  }
128
181
  });
129
- if (screen === "remote") {
182
+ if (screen === "main") {
130
183
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
131
184
  /* @__PURE__ */ jsx(Text, { bold: true, children: "Configure Automata" }),
132
185
  /* @__PURE__ */ jsx(Text, { children: " " }),
186
+ MAIN_MENU_OPTIONS.map((option, index) => /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: index === mainMenuIndex ? "cyan" : void 0, children: [
187
+ index === mainMenuIndex ? "\u276F " : " ",
188
+ option
189
+ ] }) }, option)),
190
+ /* @__PURE__ */ jsx(Text, { children: " " }),
191
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 to move \xB7 Enter to select \xB7 Ctrl+C to cancel" })
192
+ ] });
193
+ }
194
+ if (screen === "remote") {
195
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
196
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "Remote / Mode" }),
197
+ /* @__PURE__ */ jsx(Text, { children: " " }),
133
198
  /* @__PURE__ */ jsx(Text, { children: "Remote environment type:" }),
134
199
  REMOTE_OPTIONS.map((option, index) => /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: index === selectedRemoteIndex ? "cyan" : void 0, children: [
135
200
  index === selectedRemoteIndex ? "\u276F " : " ",
136
201
  option.label
137
202
  ] }) }, option.value)),
138
203
  /* @__PURE__ */ jsx(Text, { children: " " }),
139
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 to move \xB7 Enter to confirm \xB7 Ctrl+C to cancel" })
204
+ /* @__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
205
  ] });
141
206
  }
142
207
  if (screen === "technique") {
143
208
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
144
- /* @__PURE__ */ jsx(Text, { bold: true, children: "Configure Issue Discovery Technique" }),
209
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "Implement-Next \u2014 Issue Discovery Technique" }),
145
210
  /* @__PURE__ */ jsx(Text, { children: " " }),
146
211
  /* @__PURE__ */ jsx(Text, { children: "How to find the next issue to work on:" }),
147
212
  TECHNIQUE_OPTIONS.map((option, index) => /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: index === selectedTechIndex ? "cyan" : void 0, children: [
@@ -149,13 +214,13 @@ function ConfigWizard() {
149
214
  option.label
150
215
  ] }) }, option.value)),
151
216
  /* @__PURE__ */ jsx(Text, { children: " " }),
152
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 to move \xB7 Enter to confirm \xB7 Ctrl+C to cancel" })
217
+ /* @__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
218
  ] });
154
219
  }
155
220
  if (screen === "value") {
156
221
  const techLabel = TECHNIQUE_OPTIONS.find((t) => t.value === pendingTechnique)?.label ?? pendingTechnique;
157
222
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
158
- /* @__PURE__ */ jsx(Text, { bold: true, children: "Configure Issue Discovery Value" }),
223
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "Implement-Next \u2014 Issue Discovery Value" }),
159
224
  /* @__PURE__ */ jsx(Text, { children: " " }),
160
225
  /* @__PURE__ */ jsxs(Text, { children: [
161
226
  techLabel,
@@ -167,22 +232,50 @@ function ConfigWizard() {
167
232
  ] })
168
233
  ] }),
169
234
  /* @__PURE__ */ jsx(Text, { children: " " }),
170
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Type value \xB7 Enter to confirm \xB7 Ctrl+C to cancel" })
235
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Type value \xB7 Enter to continue \xB7 Esc to go back \xB7 Ctrl+C to cancel" })
236
+ ] });
237
+ }
238
+ if (screen === "system-prompt") {
239
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
240
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "Implement-Next \u2014 Claude System Prompt" }),
241
+ /* @__PURE__ */ jsx(Text, { children: " " }),
242
+ /* @__PURE__ */ jsxs(Text, { children: [
243
+ "System prompt (optional):",
244
+ " ",
245
+ /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
246
+ systemPrompt,
247
+ /* @__PURE__ */ jsx(Text, { children: "_" })
248
+ ] })
249
+ ] }),
250
+ /* @__PURE__ */ jsx(Text, { children: " " }),
251
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Type prompt \xB7 Enter to save and exit \xB7 Esc to go back \xB7 Ctrl+C to cancel" })
252
+ ] });
253
+ }
254
+ if (screen === "prompts-menu") {
255
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
256
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "Prompts" }),
257
+ /* @__PURE__ */ jsx(Text, { children: " " }),
258
+ /* @__PURE__ */ jsxs(Box, { children: [
259
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u276F " }),
260
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: "Sonar" })
261
+ ] }),
262
+ /* @__PURE__ */ jsx(Text, { children: " " }),
263
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Enter to edit Sonar prompt \xB7 Esc to go back \xB7 Ctrl+C to cancel" })
171
264
  ] });
172
265
  }
173
266
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
174
- /* @__PURE__ */ jsx(Text, { bold: true, children: "Configure Claude System Prompt" }),
267
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "Prompts \u2014 Sonar" }),
175
268
  /* @__PURE__ */ jsx(Text, { children: " " }),
176
269
  /* @__PURE__ */ jsxs(Text, { children: [
177
- "System prompt (optional):",
270
+ "Sonar prompt:",
178
271
  " ",
179
272
  /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
180
- systemPrompt,
273
+ sonarPrompt,
181
274
  /* @__PURE__ */ jsx(Text, { children: "_" })
182
275
  ] })
183
276
  ] }),
184
277
  /* @__PURE__ */ jsx(Text, { children: " " }),
185
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Type prompt \xB7 Enter to save and exit \xB7 Ctrl+C to cancel" })
278
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Type prompt \xB7 Enter to save and exit \xB7 Esc to go back \xB7 Ctrl+C to cancel" })
186
279
  ] });
187
280
  }
188
281
 
@@ -335,7 +428,27 @@ function fetchCheckRunOutputs(ownerRepo, sha) {
335
428
  }
336
429
  return map;
337
430
  }
338
- function getPrInfoGh(branch) {
431
+ function extractSonarProjectKey(url) {
432
+ try {
433
+ const parsed = new URL(url);
434
+ const id = parsed.searchParams.get("id");
435
+ return id ?? null;
436
+ } catch {
437
+ return null;
438
+ }
439
+ }
440
+ async function fetchSonarNewIssues(projectKey, prNumber) {
441
+ const apiUrl = `https://sonarcloud.io/api/issues/search?componentKeys=${encodeURIComponent(projectKey)}&pullRequest=${String(prNumber)}&resolved=false&ps=1`;
442
+ try {
443
+ const response = await fetch(apiUrl);
444
+ if (!response.ok) return null;
445
+ const data = await response.json();
446
+ return data.paging?.total ?? null;
447
+ } catch {
448
+ return null;
449
+ }
450
+ }
451
+ async function getPrInfoGh(branch) {
339
452
  const { stdout, stderr, status } = run2("gh", [
340
453
  "pr",
341
454
  "view",
@@ -365,9 +478,28 @@ function getPrInfoGh(branch) {
365
478
  detailsUrl: enriched?.detailsUrl || c.detailsUrl || ""
366
479
  };
367
480
  });
368
- return { number: raw.number, title: raw.title, state: raw.state, url: raw.url, checks };
481
+ const sonarCheck = checks.find((c) => c.detailsUrl.includes("sonarcloud.io"));
482
+ let sonarcloudUrl;
483
+ let sonarNewIssues;
484
+ if (sonarCheck) {
485
+ sonarcloudUrl = sonarCheck.detailsUrl;
486
+ const projectKey = extractSonarProjectKey(sonarCheck.detailsUrl);
487
+ if (projectKey) {
488
+ sonarNewIssues = await fetchSonarNewIssues(projectKey, raw.number);
489
+ } else {
490
+ sonarNewIssues = null;
491
+ }
492
+ }
493
+ return {
494
+ number: raw.number,
495
+ title: raw.title,
496
+ state: raw.state,
497
+ url: raw.url,
498
+ checks,
499
+ ...sonarcloudUrl !== void 0 ? { sonarcloudUrl, sonarNewIssues } : {}
500
+ };
369
501
  }
370
- function getPrInfo2(branch) {
502
+ async function getPrInfo2(branch) {
371
503
  const config = readConfig();
372
504
  if (config.remoteType === "azdo") {
373
505
  return getPrInfo();
@@ -603,7 +735,7 @@ See docs/git.md for full output reference.`
603
735
  let pr2;
604
736
  while (true) {
605
737
  try {
606
- pr2 = getPrInfo2(branch);
738
+ pr2 = await getPrInfo2(branch);
607
739
  } catch (err) {
608
740
  process.stderr.write(`Error: ${err.message}
609
741
  `);
@@ -643,7 +775,7 @@ See docs/git.md for full output reference.`
643
775
  }
644
776
  let pr;
645
777
  try {
646
- pr = getPrInfo2(branch);
778
+ pr = await getPrInfo2(branch);
647
779
  } catch (err) {
648
780
  process.stderr.write(`Error: ${err.message}
649
781
  `);
@@ -662,6 +794,13 @@ Title: ${pr.title}
662
794
  State: ${pr.state}
663
795
  URL: ${pr.url}
664
796
  `);
797
+ if (pr.sonarcloudUrl !== void 0) {
798
+ process.stdout.write(`Sonar: ${pr.sonarcloudUrl}
799
+ `);
800
+ const issueStr = pr.sonarNewIssues === null || pr.sonarNewIssues === void 0 ? "unavailable" : String(pr.sonarNewIssues);
801
+ process.stdout.write(`Sonar New Issues: ${issueStr}
802
+ `);
803
+ }
665
804
  process.stdout.write(formatCheckSummary(pr.checks));
666
805
  process.stdout.write(formatChecks(pr.checks));
667
806
  }
@@ -724,7 +863,7 @@ ${safeBody}`);
724
863
  }
725
864
  process.stdout.write(lines.join("\n\n") + "\n");
726
865
  });
727
- var finishFeatureCmd = new Command2("finish-feature").description("Clean up a merged feature branch: checkout develop, pull, and delete local branch").action(() => {
866
+ var finishFeatureCmd = new Command2("finish-feature").description("Clean up a merged feature branch: checkout develop, pull, and delete local branch").action(async () => {
728
867
  let branch;
729
868
  try {
730
869
  branch = getCurrentBranch();
@@ -745,7 +884,7 @@ var finishFeatureCmd = new Command2("finish-feature").description("Clean up a me
745
884
  }
746
885
  let pr;
747
886
  try {
748
- pr = getPrInfo2(branch);
887
+ pr = await getPrInfo2(branch);
749
888
  } catch (err) {
750
889
  process.stderr.write(`Error: ${err.message}
751
890
  `);
@@ -1194,13 +1333,63 @@ var testCodexCmd = new Command4("codex").description("Test Codex CLI invocation
1194
1333
  });
1195
1334
  var testCommand = new Command4("test").description("Test commands for verifying automata integrations").addCommand(testClaudeCmd).addCommand(testCodexCmd);
1196
1335
 
1336
+ // src/commands/executePrompt.ts
1337
+ import { Command as Command5 } from "commander";
1338
+ var executeSonarCmd = new Command5("sonar").description(
1339
+ "Check the current branch for a SonarCloud analysis and invoke the AI with the Sonar prompt and analysis URL"
1340
+ ).option("--codex", "Use Codex CLI instead of Claude Code").option("--verbose", "Show step-by-step progress (Claude only; ignored for Codex)").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)").action(
1341
+ async (options) => {
1342
+ let branch;
1343
+ try {
1344
+ branch = getCurrentBranch();
1345
+ } catch (err) {
1346
+ process.stderr.write(`Error: ${err.message}
1347
+ `);
1348
+ process.exit(1);
1349
+ }
1350
+ let pr;
1351
+ try {
1352
+ pr = await getPrInfo2(branch);
1353
+ } catch (err) {
1354
+ process.stderr.write(`Error: ${err.message}
1355
+ `);
1356
+ process.exit(1);
1357
+ }
1358
+ if (pr === null) {
1359
+ process.stderr.write(`Error: No pull request found for branch: ${branch}
1360
+ `);
1361
+ process.exit(1);
1362
+ }
1363
+ if (!pr.sonarcloudUrl) {
1364
+ process.stderr.write(
1365
+ `Error: No SonarCloud analysis found for PR #${pr.number}. Ensure a SonarCloud check is configured on this repository.
1366
+ `
1367
+ );
1368
+ process.exit(1);
1369
+ }
1370
+ const config = readConfig();
1371
+ const sonarPromptText = config.prompts?.sonar ?? DEFAULT_SONAR_PROMPT;
1372
+ const fullPrompt = `${sonarPromptText}
1373
+
1374
+ SonarCloud analysis URL: ${pr.sonarcloudUrl}`;
1375
+ if (options.codex) {
1376
+ invokeCodexCode(fullPrompt, { verbose: options.verbose });
1377
+ } else {
1378
+ const model = resolveModelOption(options);
1379
+ await invokeClaudeCode(fullPrompt, { verbose: options.verbose, model });
1380
+ }
1381
+ }
1382
+ );
1383
+ var executePromptCommand = new Command5("execute-prompt").description("Execute a configured custom prompt using an AI assistant").addCommand(executeSonarCmd);
1384
+
1197
1385
  // src/index.ts
1198
- var program = new Command5();
1386
+ var program = new Command6();
1199
1387
  program.name("automata").description("Automata CLI tool").version(version, "-v, --version");
1200
1388
  program.addCommand(configCommand);
1201
1389
  program.addCommand(gitCommand);
1202
1390
  program.addCommand(implementNextCommand);
1203
1391
  program.addCommand(testCommand);
1392
+ program.addCommand(executePromptCommand);
1204
1393
  program.showHelpAfterError();
1205
1394
  program.parse();
1206
1395
  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-009-codex-flag-implement-next.75",
3
+ "version": "0.1.0-010-sonarcloud-prompts-execute.79",
4
4
  "description": "Automata CLI tool",
5
5
  "type": "module",
6
6
  "bin": {