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.
- package/README.md +14 -0
- package/dist/index.js +202 -44
- 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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1757
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(`
|
|
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 =
|
|
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/
|
|
1934
|
+
// src/commands/execute.ts
|
|
1935
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
1823
1936
|
import { Command as Command4 } from "commander";
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
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(
|
|
2119
|
+
program.addCommand(executeCommand);
|
|
1962
2120
|
program.addCommand(executePromptCommand);
|
|
1963
2121
|
program.showHelpAfterError();
|
|
1964
2122
|
program.parse();
|