automata-cli 0.3.0 → 0.4.0-develop.131

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 (3) hide show
  1. package/README.md +14 -0
  2. package/dist/index.js +202 -44
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -99,6 +99,20 @@ See [docs/implement-next.md](docs/implement-next.md) for full details.
99
99
 
100
100
  ---
101
101
 
102
+ ## `automata execute`
103
+
104
+ Delegate work to an AI executor (Claude or Codex) by providing a prompt inline, from a file, or via stdin.
105
+
106
+ ```bash
107
+ automata execute --with claude --prompt "refactor the auth module"
108
+ automata execute --with codex --file-prompt prompts/fix.md
109
+ echo "fix lint errors" | automata execute --with claude
110
+ ```
111
+
112
+ See [docs/execute.md](docs/execute.md) for full details.
113
+
114
+ ---
115
+
102
116
  ## Development
103
117
 
104
118
  ### Prerequisites
package/dist/index.js CHANGED
@@ -557,6 +557,14 @@ function extractSonarProjectKey(url) {
557
557
  return null;
558
558
  }
559
559
  }
560
+ function buildSonarPullRequestUrl(projectKey, prNumber) {
561
+ return `https://sonarcloud.io/summary/new_code?id=${encodeURIComponent(projectKey)}&pullRequest=${String(prNumber)}`;
562
+ }
563
+ function resolveSonarPullRequestUrl(url, prNumber) {
564
+ const projectKey = extractSonarProjectKey(url);
565
+ if (!projectKey) return url;
566
+ return buildSonarPullRequestUrl(projectKey, prNumber);
567
+ }
560
568
  function isSonarUrl(url) {
561
569
  try {
562
570
  const hostname = new URL(url).hostname;
@@ -671,13 +679,28 @@ async function fetchSonarJson(apiUrl) {
671
679
  clearTimeout(timeoutId);
672
680
  }
673
681
  }
674
- async function fetchSonarNewIssues(projectKey, prNumber) {
682
+ async function fetchSonarNewIssues(projectKey, prNumber, sonarcloudUrl) {
675
683
  const apiUrl = `https://sonarcloud.io/api/issues/search?componentKeys=${encodeURIComponent(projectKey)}&pullRequest=${String(prNumber)}&resolved=false&ps=1`;
676
684
  const response = await fetchSonarJson(apiUrl);
677
685
  if (!response.ok) {
678
- return null;
686
+ if (response.status === 401) {
687
+ return {
688
+ total: null,
689
+ note: `SonarCloud project is private. Open the Sonar URL in an authenticated browser: ${sonarcloudUrl}`
690
+ };
691
+ }
692
+ return {
693
+ total: null,
694
+ note: "SonarCloud new-issue count is unavailable right now."
695
+ };
696
+ }
697
+ if (typeof response.data.paging?.total === "number") {
698
+ return { total: response.data.paging.total };
679
699
  }
680
- return response.data.paging?.total ?? null;
700
+ return {
701
+ total: null,
702
+ note: "SonarCloud did not return a new-issue count for this pull request."
703
+ };
681
704
  }
682
705
  async function fetchSonarGateViolations(projectKey, prNumber) {
683
706
  const apiUrl = `https://sonarcloud.io/api/qualitygates/project_status?projectKey=${encodeURIComponent(projectKey)}&pullRequest=${String(prNumber)}`;
@@ -793,19 +816,22 @@ async function fetchSonarFailureSummary(projectKey, prNumber, sonarcloudUrl) {
793
816
  if (!gateResult.ok && gateResult.status === 401) {
794
817
  return {
795
818
  summary: sonarPrivateFailureSummary(sonarcloudUrl),
796
- issueTotal: null
819
+ issueTotal: null,
820
+ issueNote: `SonarCloud project is private. Open the Sonar URL in an authenticated browser: ${sonarcloudUrl}`
797
821
  };
798
822
  }
799
823
  if (!issuesResult.ok && issuesResult.status === 401) {
800
824
  return {
801
825
  summary: sonarPrivateFailureSummary(sonarcloudUrl),
802
- issueTotal: null
826
+ issueTotal: null,
827
+ issueNote: `SonarCloud project is private. Open the Sonar URL in an authenticated browser: ${sonarcloudUrl}`
803
828
  };
804
829
  }
805
830
  if (!hotspotsResult.ok && hotspotsResult.status === 401) {
806
831
  return {
807
832
  summary: sonarPrivateFailureSummary(sonarcloudUrl),
808
- issueTotal: null
833
+ issueTotal: null,
834
+ issueNote: `SonarCloud project is private. Open the Sonar URL in an authenticated browser: ${sonarcloudUrl}`
809
835
  };
810
836
  }
811
837
  const gateViolations = gateResult.ok ? gateResult.data.gateViolations : [];
@@ -813,6 +839,7 @@ async function fetchSonarFailureSummary(projectKey, prNumber, sonarcloudUrl) {
813
839
  const securityHotspots = hotspotsResult.ok ? hotspotsResult.data : [];
814
840
  const qualityGateStatus = gateResult.ok ? gateResult.data.qualityGateStatus : void 0;
815
841
  const issueTotal = issuesResult.ok ? issuesResult.data.total : null;
842
+ const issueNote = issuesResult.ok ? void 0 : "SonarCloud new-issue count is unavailable right now.";
816
843
  if (gateViolations.length === 0 && issues.length === 0 && securityHotspots.length === 0 && !qualityGateStatus) {
817
844
  return {
818
845
  summary: {
@@ -822,7 +849,8 @@ async function fetchSonarFailureSummary(projectKey, prNumber, sonarcloudUrl) {
822
849
  securityHotspots: [],
823
850
  unavailableMessage: "SonarCloud failure details are unavailable right now."
824
851
  },
825
- issueTotal
852
+ issueTotal,
853
+ issueNote
826
854
  };
827
855
  }
828
856
  return {
@@ -833,7 +861,8 @@ async function fetchSonarFailureSummary(projectKey, prNumber, sonarcloudUrl) {
833
861
  issues,
834
862
  securityHotspots
835
863
  },
836
- issueTotal
864
+ issueTotal,
865
+ issueNote
837
866
  };
838
867
  }
839
868
  async function getPrInfoGh(branch) {
@@ -851,12 +880,16 @@ async function getPrInfoGh(branch) {
851
880
  throw new Error(stderr.trim() || "Failed to query GitHub. Is `gh` installed and authenticated?");
852
881
  }
853
882
  const raw = JSON.parse(stdout);
854
- const failedChecks = (raw.statusCheckRollup ?? []).filter(
883
+ const statusChecks = raw.statusCheckRollup ?? [];
884
+ const failedChecks = statusChecks.filter(
855
885
  (c) => c.conclusion !== null && ["FAILURE", "TIMED_OUT", "ACTION_REQUIRED", "CANCELLED"].includes(c.conclusion)
856
886
  );
857
- const ownerRepo = failedChecks.length > 0 ? parseOwnerRepo() : null;
887
+ const sonarNeedsCheckRunOutput = statusChecks.some(
888
+ (c) => (isSonarUrl(c.detailsUrl) || c.name.toLowerCase().includes("sonar")) && !extractSonarProjectKey(c.detailsUrl)
889
+ );
890
+ const ownerRepo = failedChecks.length > 0 || sonarNeedsCheckRunOutput ? parseOwnerRepo() : null;
858
891
  const checkOutputs = ownerRepo ? fetchCheckRunOutputs(ownerRepo, raw.headRefOid) : /* @__PURE__ */ new Map();
859
- const checks = (raw.statusCheckRollup ?? []).map((c) => {
892
+ const checks = statusChecks.map((c) => {
860
893
  const enriched = checkOutputs.get(c.name);
861
894
  return {
862
895
  name: c.name,
@@ -869,20 +902,25 @@ async function getPrInfoGh(branch) {
869
902
  const sonarCheck = checks.find((c) => isSonarUrl(c.detailsUrl));
870
903
  let sonarcloudUrl;
871
904
  let sonarNewIssues;
905
+ let sonarNewIssuesNote;
872
906
  let sonarFailures;
873
907
  if (sonarCheck) {
874
- sonarcloudUrl = sonarCheck.detailsUrl;
875
- const projectKey = extractSonarProjectKey(sonarCheck.detailsUrl);
908
+ sonarcloudUrl = resolveSonarPullRequestUrl(sonarCheck.detailsUrl, raw.number);
909
+ const projectKey = extractSonarProjectKey(sonarcloudUrl);
876
910
  if (projectKey) {
877
911
  if (sonarCheck.conclusion !== null && SONAR_FAIL_CONCLUSIONS.has(sonarCheck.conclusion)) {
878
912
  const sonarSummary = await fetchSonarFailureSummary(projectKey, raw.number, sonarcloudUrl);
879
913
  sonarFailures = sonarSummary.summary;
880
914
  sonarNewIssues = sonarSummary.issueTotal;
915
+ sonarNewIssuesNote = sonarSummary.issueNote;
881
916
  } else {
882
- sonarNewIssues = await fetchSonarNewIssues(projectKey, raw.number);
917
+ const sonarNewIssuesResult = await fetchSonarNewIssues(projectKey, raw.number, sonarcloudUrl);
918
+ sonarNewIssues = sonarNewIssuesResult.total;
919
+ sonarNewIssuesNote = sonarNewIssuesResult.note;
883
920
  }
884
921
  } else {
885
922
  sonarNewIssues = null;
923
+ sonarNewIssuesNote = "Could not determine the SonarCloud project key from the check URL.";
886
924
  }
887
925
  }
888
926
  return {
@@ -891,7 +929,7 @@ async function getPrInfoGh(branch) {
891
929
  state: raw.state,
892
930
  url: raw.url,
893
931
  checks,
894
- ...sonarcloudUrl === void 0 ? {} : { sonarcloudUrl, sonarNewIssues },
932
+ ...sonarcloudUrl === void 0 ? {} : { sonarcloudUrl, sonarNewIssues, sonarNewIssuesNote },
895
933
  ...sonarFailures === void 0 ? {} : { sonarFailures }
896
934
  };
897
935
  }
@@ -1221,6 +1259,12 @@ function formatSonarFailures(sonarFailures, sonarcloudUrl) {
1221
1259
  }
1222
1260
  return lines.join("\n") + "\n";
1223
1261
  }
1262
+ function sonarFailureNote(sonarFailures) {
1263
+ if (!sonarFailures) return void 0;
1264
+ if (sonarFailures.status === "private") return sonarFailures.privateMessage;
1265
+ if (sonarFailures.status === "unavailable") return sonarFailures.unavailableMessage;
1266
+ return void 0;
1267
+ }
1224
1268
  var POLL_INTERVAL_MS = 1e4;
1225
1269
  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(
1226
1270
  "after",
@@ -1290,6 +1334,11 @@ URL: ${pr.url}
1290
1334
  const issueStr = pr.sonarNewIssues === null || pr.sonarNewIssues === void 0 ? "unavailable" : String(pr.sonarNewIssues);
1291
1335
  process.stdout.write(`Sonar New Issues: ${issueStr}
1292
1336
  `);
1337
+ const failureNote = sonarFailureNote(pr.sonarFailures);
1338
+ if (pr.sonarNewIssuesNote && pr.sonarNewIssuesNote !== failureNote) {
1339
+ process.stdout.write(`Sonar Note: ${sanitizeText(pr.sonarNewIssuesNote)}
1340
+ `);
1341
+ }
1293
1342
  }
1294
1343
  process.stdout.write(formatCheckSummary(pr.checks));
1295
1344
  process.stdout.write(formatChecks(pr.checks));
@@ -1510,6 +1559,7 @@ minor segment is incremented (e.g. 1.2.0 \u2192 1.3.0).`
1510
1559
  var gitCommand = new Command2("git").description("Git workflow commands (some require gh CLI)").addCommand(getPrInfoCmd).addCommand(getPrCommentsCmd).addCommand(finishFeatureCmd).addCommand(publishReleaseCmd);
1511
1560
 
1512
1561
  // src/commands/getReady.ts
1562
+ import { createInterface as createInterface2 } from "readline";
1513
1563
  import { Command as Command3 } from "commander";
1514
1564
 
1515
1565
  // src/config/githubService.ts
@@ -1529,14 +1579,14 @@ function run3(cmd, args) {
1529
1579
  status: result.status ?? 1
1530
1580
  };
1531
1581
  }
1532
- function listIssues(technique, value) {
1582
+ function listIssues(technique, value, limit = 10) {
1533
1583
  const baseArgs = [
1534
1584
  "issue",
1535
1585
  "list",
1536
1586
  "--state",
1537
1587
  "open",
1538
1588
  "--limit",
1539
- "1",
1589
+ String(limit),
1540
1590
  "--json",
1541
1591
  "number,title,body,url"
1542
1592
  ];
@@ -1556,11 +1606,7 @@ function listIssues(technique, value) {
1556
1606
  if (status !== 0) {
1557
1607
  throw new Error(stderr.trim() || "Failed to query GitHub issues. Is `gh` installed and authenticated?");
1558
1608
  }
1559
- const issues = JSON.parse(stdout);
1560
- if (issues.length === 0) {
1561
- return null;
1562
- }
1563
- return issues[0];
1609
+ return JSON.parse(stdout);
1564
1610
  }
1565
1611
  function postComment(issueNumber, body) {
1566
1612
  const { stderr, status } = run3("gh", ["issue", "comment", String(issueNumber), "--body", body]);
@@ -1740,12 +1786,13 @@ function invokeCodexCode(prompt, options = {}) {
1740
1786
  if (options.verbose) {
1741
1787
  process.stderr.write("Warning: --verbose is not supported for Codex and will be ignored.\n");
1742
1788
  }
1743
- invokeCodexCodeSync(prompt, options.yolo ?? false);
1789
+ invokeCodexCodeSync(prompt, options.yolo ?? false, options.model);
1744
1790
  }
1745
- function invokeCodexCodeSync(prompt, yolo) {
1791
+ function invokeCodexCodeSync(prompt, yolo, model) {
1746
1792
  const codexBin = resolveCommand("codex");
1747
1793
  const args = ["exec"];
1748
1794
  if (yolo) args.push("--dangerously-bypass-approvals-and-sandbox");
1795
+ if (model) args.push("--model", model);
1749
1796
  args.push(prompt);
1750
1797
  const result = spawnSync5(codexBin, args, { encoding: "utf8", stdio: "inherit" });
1751
1798
  handleSpawnError(result.error, "codex");
@@ -1753,8 +1800,37 @@ function invokeCodexCodeSync(prompt, yolo) {
1753
1800
  }
1754
1801
 
1755
1802
  // src/commands/getReady.ts
1756
- 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) => {
1757
- const config = readConfig();
1803
+ function writeOverflowHint(output, issues, limit) {
1804
+ if (issues.length === limit) {
1805
+ output.write(`(Showing first ${limit} matching issues \u2014 there may be more. Use --limit to fetch more.)
1806
+ `);
1807
+ }
1808
+ }
1809
+ function writeIssueList(output, issues, limit) {
1810
+ output.write("\nAvailable issues:\n");
1811
+ for (let i = 0; i < issues.length; i++) {
1812
+ output.write(` [${i + 1}] #${issues[i].number} - ${issues[i].title}
1813
+ `);
1814
+ }
1815
+ writeOverflowHint(output, issues, limit);
1816
+ }
1817
+ async function promptSelection(issues, limit, output) {
1818
+ writeIssueList(output, issues, limit);
1819
+ const rl = createInterface2({ input: process.stdin, output });
1820
+ const answer = await new Promise(
1821
+ (resolve3) => rl.question(`
1822
+ Select issue (1-${issues.length}): `, resolve3)
1823
+ );
1824
+ rl.close();
1825
+ const n = Number.parseInt(answer.trim(), 10);
1826
+ if (Number.isNaN(n) || n < 1 || n > issues.length) {
1827
+ process.stderr.write(`Error: Invalid selection "${answer.trim()}". Enter a number between 1 and ${issues.length}.
1828
+ `);
1829
+ process.exit(1);
1830
+ }
1831
+ return issues[n - 1];
1832
+ }
1833
+ function validateConfig(config) {
1758
1834
  if (config.remoteType !== "gh") {
1759
1835
  process.stderr.write(
1760
1836
  "Error: implement-next is not supported in Azure DevOps mode. Work item discovery is not available in azdo-cli. See docs/azdo-gap.md for details.\n"
@@ -1773,24 +1849,58 @@ var implementNextCommand = new Command3("implement-next").description("Find the
1773
1849
  );
1774
1850
  process.exit(1);
1775
1851
  }
1852
+ }
1853
+ async function resolveIssue(issues, options, limit) {
1854
+ const selectionOutput = options.json ? process.stderr : process.stdout;
1855
+ if (issues.length === 0) {
1856
+ process.stdout.write("No issues found matching the configured filter.\n");
1857
+ process.exit(0);
1858
+ }
1859
+ if (issues.length > 1 && options.queryOnly) {
1860
+ writeIssueList(selectionOutput, issues, limit);
1861
+ process.exit(0);
1862
+ }
1776
1863
  let issue;
1864
+ if (issues.length === 1) {
1865
+ issue = issues[0];
1866
+ selectionOutput.write(`Issue: #${issue.number}
1867
+ Title: ${issue.title}
1868
+ `);
1869
+ } else if (options.takeFirst) {
1870
+ issue = issues[0];
1871
+ selectionOutput.write(`Selecting issue #${issue.number}: ${issue.title}
1872
+ `);
1873
+ } else {
1874
+ issue = await promptSelection(issues, limit, selectionOutput);
1875
+ selectionOutput.write(`
1876
+ Issue: #${issue.number}
1877
+ Title: ${issue.title}
1878
+ `);
1879
+ }
1880
+ return issue;
1881
+ }
1882
+ 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").option("--take-first", "When multiple issues match, pick the first without prompting").option("--limit <n>", "Max issues to fetch and display (default: 10)", "10").action(async (options) => {
1883
+ const config = readConfig();
1884
+ validateConfig(config);
1885
+ const limit = Number.parseInt(options.limit, 10);
1886
+ if (Number.isNaN(limit) || limit <= 0) {
1887
+ process.stderr.write(`Error: --limit must be a positive integer (got "${options.limit}").
1888
+ `);
1889
+ process.exit(1);
1890
+ }
1891
+ let issues;
1777
1892
  try {
1778
- issue = listIssues(config.issueDiscoveryTechnique, config.issueDiscoveryValue);
1893
+ issues = listIssues(config.issueDiscoveryTechnique, config.issueDiscoveryValue, limit);
1779
1894
  } catch (err) {
1780
1895
  process.stderr.write(`Error: ${err.message}
1781
1896
  `);
1782
1897
  process.exit(1);
1783
1898
  }
1784
- if (issue === null) {
1785
- process.stdout.write("No issues found matching the configured filter.\n");
1786
- process.exit(0);
1787
- }
1899
+ const issue = await resolveIssue(issues, options, limit);
1788
1900
  if (options.json) {
1789
1901
  process.stdout.write(JSON.stringify({ number: issue.number, title: issue.title, body: issue.body, url: issue.url }, null, 2) + "\n");
1790
1902
  } else {
1791
- process.stdout.write(`Issue: #${issue.number}
1792
- Title: ${issue.title}
1793
- URL: ${issue.url}
1903
+ process.stdout.write(`URL: ${issue.url}
1794
1904
 
1795
1905
  ${issue.body}
1796
1906
  `);
@@ -1807,7 +1917,9 @@ ${issue.body}
1807
1917
  }
1808
1918
  if (options.claude !== false) {
1809
1919
  const systemPrompt = config.claudeSystemPrompt ?? DEFAULT_CLAUDE_SYSTEM_PROMPT;
1810
- const prompt = `${systemPrompt}
1920
+ const prompt = `Resolving issue #${issue.number}:
1921
+
1922
+ ${systemPrompt}
1811
1923
 
1812
1924
  ${issue.body}`;
1813
1925
  if (options.codex) {
@@ -1819,16 +1931,62 @@ ${issue.body}`;
1819
1931
  }
1820
1932
  });
1821
1933
 
1822
- // src/commands/test.ts
1934
+ // src/commands/execute.ts
1935
+ import { readFileSync as readFileSync3 } from "fs";
1823
1936
  import { Command as Command4 } from "commander";
1824
- 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) => {
1825
- const model = resolveModelOption(options);
1826
- await invokeClaudeCode(options.prompt, { yolo: options.yolo, verbose: options.verbose, model });
1827
- });
1828
- 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) => {
1829
- invokeCodexCode(options.prompt, { yolo: options.yolo, verbose: options.verbose });
1937
+ function readStdin() {
1938
+ return new Promise((resolve3, reject) => {
1939
+ const chunks = [];
1940
+ process.stdin.on("data", (chunk) => chunks.push(chunk));
1941
+ process.stdin.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
1942
+ process.stdin.on("error", reject);
1943
+ });
1944
+ }
1945
+ async function resolvePrompt(options) {
1946
+ const hasCli = options.prompt !== void 0;
1947
+ const hasFile = options.filePrompt !== void 0;
1948
+ if (hasCli && hasFile) {
1949
+ process.stderr.write("Error: --prompt is mutually exclusive with --file-prompt.\n");
1950
+ process.exit(1);
1951
+ }
1952
+ if (hasCli) {
1953
+ return options.prompt;
1954
+ }
1955
+ if (hasFile) {
1956
+ const path = options.filePrompt;
1957
+ try {
1958
+ return readFileSync3(path, "utf8");
1959
+ } catch {
1960
+ process.stderr.write(`Error: Cannot read file: ${path}
1961
+ `);
1962
+ process.exit(1);
1963
+ }
1964
+ }
1965
+ if (!process.stdin.isTTY) {
1966
+ const content = await readStdin();
1967
+ if (content.trim().length === 0) {
1968
+ process.stderr.write("Error: No prompt provided. Use --prompt, --file-prompt, or pipe via stdin.\n");
1969
+ process.exit(1);
1970
+ }
1971
+ return content;
1972
+ }
1973
+ process.stderr.write("Error: No prompt provided. Use --prompt, --file-prompt, or pipe via stdin.\n");
1974
+ process.exit(1);
1975
+ }
1976
+ var executeCommand = new Command4("execute").description("Delegate work to an AI executor").requiredOption("--with <executor>", "Executor to use: claude or codex").option("--prompt <string>", "Prompt to send to the executor").option("--file-prompt <path>", "Path to a file whose content is used as the prompt").option("--silent", "Suppress step-by-step Claude output; show only the final summary").option("--model <string>", "Model identifier to pass to the executor").action(async (options) => {
1977
+ const executor = options.with.toLowerCase();
1978
+ if (executor !== "claude" && executor !== "codex") {
1979
+ process.stderr.write(`Error: --with must be 'claude' or 'codex', got '${options.with}'.
1980
+ `);
1981
+ process.exit(1);
1982
+ }
1983
+ const prompt = await resolvePrompt(options);
1984
+ if (executor === "codex") {
1985
+ invokeCodexCode(prompt, { yolo: true, model: options.model });
1986
+ } else {
1987
+ await invokeClaudeCode(prompt, { yolo: true, verbose: !options.silent, model: options.model });
1988
+ }
1830
1989
  });
1831
- var testCommand = new Command4("test").description("Test commands for verifying automata integrations").addCommand(testClaudeCmd).addCommand(testCodexCmd);
1832
1990
 
1833
1991
  // src/commands/executePrompt.ts
1834
1992
  import { Command as Command5 } from "commander";
@@ -1958,7 +2116,7 @@ program.name("automata").description("Automata CLI tool").version(version, "-v,
1958
2116
  program.addCommand(configCommand);
1959
2117
  program.addCommand(gitCommand);
1960
2118
  program.addCommand(implementNextCommand);
1961
- program.addCommand(testCommand);
2119
+ program.addCommand(executeCommand);
1962
2120
  program.addCommand(executePromptCommand);
1963
2121
  program.showHelpAfterError();
1964
2122
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "automata-cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.0-develop.131",
4
4
  "description": "Automata CLI tool",
5
5
  "type": "module",
6
6
  "bin": {