automata-cli 0.1.0-feature-021-execute-command.127 → 0.1.0-feature-028-normalize-execute-prompt.153
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 +13 -0
- package/dist/index.js +137 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -113,6 +113,19 @@ See [docs/execute.md](docs/execute.md) for full details.
|
|
|
113
113
|
|
|
114
114
|
---
|
|
115
115
|
|
|
116
|
+
## `automata execute-prompt`
|
|
117
|
+
|
|
118
|
+
Run predefined AI workflows that gather context from the current branch before invoking Claude or Codex.
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
automata execute-prompt sonar --with claude
|
|
122
|
+
automata execute-prompt fix-comments --with codex --model o3
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
See [docs/execute-prompt.md](docs/execute-prompt.md) for full details.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
116
129
|
## Development
|
|
117
130
|
|
|
118
131
|
### Prerequisites
|
package/dist/index.js
CHANGED
|
@@ -1564,6 +1564,7 @@ import { Command as Command3 } from "commander";
|
|
|
1564
1564
|
|
|
1565
1565
|
// src/config/githubService.ts
|
|
1566
1566
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
1567
|
+
var GITHUB_ISSUE_COMMENT_URL_RE = /github\.com\/([^/]+\/[^/]+)\/issues\/\d+#issuecomment-(\d+)/;
|
|
1567
1568
|
function run3(cmd, args) {
|
|
1568
1569
|
const result = spawnSync3(cmd, args, { encoding: "utf8" });
|
|
1569
1570
|
if (result.error) {
|
|
@@ -1613,6 +1614,73 @@ function postComment(issueNumber, body) {
|
|
|
1613
1614
|
if (status !== 0) {
|
|
1614
1615
|
throw new Error(stderr.trim() || `Failed to post comment on issue #${issueNumber}.`);
|
|
1615
1616
|
}
|
|
1617
|
+
const match = /https:\/\/github\.com\/[^\s]+#issuecomment-\d+/.exec(stderr);
|
|
1618
|
+
return match ? match[0] : void 0;
|
|
1619
|
+
}
|
|
1620
|
+
function editComment(commentUrl, body) {
|
|
1621
|
+
const match = GITHUB_ISSUE_COMMENT_URL_RE.exec(commentUrl);
|
|
1622
|
+
if (!match) {
|
|
1623
|
+
throw new Error(`Cannot parse comment URL: ${commentUrl}`);
|
|
1624
|
+
}
|
|
1625
|
+
const [, ownerRepo, commentId] = match;
|
|
1626
|
+
const { stderr, status } = run3("gh", [
|
|
1627
|
+
"api",
|
|
1628
|
+
`repos/${ownerRepo}/issues/comments/${commentId}`,
|
|
1629
|
+
"-X",
|
|
1630
|
+
"PATCH",
|
|
1631
|
+
"-f",
|
|
1632
|
+
`body=${body}`
|
|
1633
|
+
]);
|
|
1634
|
+
if (status !== 0) {
|
|
1635
|
+
throw new Error(stderr.trim() || `Failed to edit comment ${commentId}.`);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
function getCurrentBranchPr(branch) {
|
|
1639
|
+
const args = ["pr", "view"];
|
|
1640
|
+
if (branch) {
|
|
1641
|
+
args.push(branch);
|
|
1642
|
+
}
|
|
1643
|
+
args.push("--json", "number,url,body");
|
|
1644
|
+
const { stdout, stderr, status } = run3("gh", args);
|
|
1645
|
+
if (status !== 0) {
|
|
1646
|
+
if (stderr.includes("no pull requests found") || stderr.includes("Could not resolve")) {
|
|
1647
|
+
return null;
|
|
1648
|
+
}
|
|
1649
|
+
throw new Error(stderr.trim() || "Failed to query PR for current branch.");
|
|
1650
|
+
}
|
|
1651
|
+
return JSON.parse(stdout);
|
|
1652
|
+
}
|
|
1653
|
+
function addClosesRefToPr(prNumber, issueNumber) {
|
|
1654
|
+
const { stdout, status: viewStatus } = run3("gh", [
|
|
1655
|
+
"pr",
|
|
1656
|
+
"view",
|
|
1657
|
+
String(prNumber),
|
|
1658
|
+
"--json",
|
|
1659
|
+
"body",
|
|
1660
|
+
"-q",
|
|
1661
|
+
".body"
|
|
1662
|
+
]);
|
|
1663
|
+
if (viewStatus !== 0) {
|
|
1664
|
+
throw new Error(`Failed to read PR #${prNumber} body.`);
|
|
1665
|
+
}
|
|
1666
|
+
const currentBody = stdout.trimEnd();
|
|
1667
|
+
const closesRef = `Closes #${issueNumber}`;
|
|
1668
|
+
if (currentBody.includes(closesRef)) {
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
const newBody = currentBody + `
|
|
1672
|
+
|
|
1673
|
+
${closesRef}`;
|
|
1674
|
+
const { stderr, status } = run3("gh", ["pr", "edit", String(prNumber), "--body", newBody]);
|
|
1675
|
+
if (status !== 0) {
|
|
1676
|
+
throw new Error(stderr.trim() || `Failed to update PR #${prNumber} body.`);
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
function addCopilotReviewer(prNumber) {
|
|
1680
|
+
const { stderr, status } = run3("gh", ["pr", "edit", String(prNumber), "--add-reviewer", "@copilot"]);
|
|
1681
|
+
if (status !== 0) {
|
|
1682
|
+
throw new Error(stderr.trim() || `Failed to add Copilot reviewer to PR #${prNumber}.`);
|
|
1683
|
+
}
|
|
1616
1684
|
}
|
|
1617
1685
|
|
|
1618
1686
|
// src/claude/claudeService.ts
|
|
@@ -1659,20 +1727,6 @@ function resolveCommand(name) {
|
|
|
1659
1727
|
}
|
|
1660
1728
|
return name;
|
|
1661
1729
|
}
|
|
1662
|
-
var MODEL_IDS = {
|
|
1663
|
-
opus: "claude-opus-4-6",
|
|
1664
|
-
sonnet: "claude-sonnet-4-6",
|
|
1665
|
-
haiku: "claude-haiku-4-5-20251001"
|
|
1666
|
-
};
|
|
1667
|
-
function resolveModelOption(opts) {
|
|
1668
|
-
const selected = ["opus", "sonnet", "haiku"].filter((m) => opts[m]);
|
|
1669
|
-
if (selected.length > 1) {
|
|
1670
|
-
process.stderr.write(`Error: --${selected[0]} and --${selected[1]} are mutually exclusive.
|
|
1671
|
-
`);
|
|
1672
|
-
process.exit(1);
|
|
1673
|
-
}
|
|
1674
|
-
return selected.length === 1 ? MODEL_IDS[selected[0]] : void 0;
|
|
1675
|
-
}
|
|
1676
1730
|
function invokeClaudeCode(prompt, options = {}) {
|
|
1677
1731
|
if (options.verbose) {
|
|
1678
1732
|
return invokeClaudeCodeVerbose(prompt, options.yolo ?? false, options.model);
|
|
@@ -1879,7 +1933,7 @@ Title: ${issue.title}
|
|
|
1879
1933
|
}
|
|
1880
1934
|
return issue;
|
|
1881
1935
|
}
|
|
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("--
|
|
1936
|
+
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("--with <executor>", "Executor to use: claude or codex", "claude").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("--silent", "Suppress step-by-step Claude output; show only the final summary").option("--model <string>", "Model identifier to pass to the executor").option("--take-first", "When multiple issues match, pick the first without prompting").option("--limit <n>", "Max issues to fetch and display (default: 10)", "10").option("--ask-copilot-review", "Request a Copilot code review on the PR after AI invocation finishes").action(async (options) => {
|
|
1883
1937
|
const config = readConfig();
|
|
1884
1938
|
validateConfig(config);
|
|
1885
1939
|
const limit = Number.parseInt(options.limit, 10);
|
|
@@ -1908,8 +1962,19 @@ ${issue.body}
|
|
|
1908
1962
|
if (options.queryOnly) {
|
|
1909
1963
|
process.exit(0);
|
|
1910
1964
|
}
|
|
1965
|
+
let executor;
|
|
1966
|
+
if (options.claude !== false) {
|
|
1967
|
+
const requestedExecutor = options.with.toLowerCase();
|
|
1968
|
+
if (requestedExecutor !== "claude" && requestedExecutor !== "codex") {
|
|
1969
|
+
process.stderr.write(`Error: --with must be 'claude' or 'codex', got '${options.with}'.
|
|
1970
|
+
`);
|
|
1971
|
+
process.exit(1);
|
|
1972
|
+
}
|
|
1973
|
+
executor = requestedExecutor;
|
|
1974
|
+
}
|
|
1975
|
+
let commentUrl;
|
|
1911
1976
|
try {
|
|
1912
|
-
postComment(issue.number, "working");
|
|
1977
|
+
commentUrl = postComment(issue.number, "working");
|
|
1913
1978
|
} catch (err) {
|
|
1914
1979
|
process.stderr.write(`Error: ${err.message}
|
|
1915
1980
|
`);
|
|
@@ -1922,12 +1987,44 @@ ${issue.body}
|
|
|
1922
1987
|
${systemPrompt}
|
|
1923
1988
|
|
|
1924
1989
|
${issue.body}`;
|
|
1925
|
-
if (
|
|
1926
|
-
|
|
1990
|
+
if (executor === "codex") {
|
|
1991
|
+
if (options.silent) {
|
|
1992
|
+
process.stderr.write("Warning: --silent is only supported with Claude and has no effect when used with Codex.\n");
|
|
1993
|
+
}
|
|
1994
|
+
invokeCodexCode(prompt, { yolo: options.yolo, model: options.model });
|
|
1927
1995
|
} else {
|
|
1928
|
-
|
|
1929
|
-
|
|
1996
|
+
await invokeClaudeCode(prompt, { yolo: options.yolo, verbose: !options.silent, model: options.model });
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
try {
|
|
2000
|
+
const pr = getCurrentBranchPr();
|
|
2001
|
+
if (pr) {
|
|
2002
|
+
if (commentUrl) {
|
|
2003
|
+
try {
|
|
2004
|
+
editComment(commentUrl, `Working on this in PR #${pr.number} \u2014 ${pr.url}`);
|
|
2005
|
+
} catch (err) {
|
|
2006
|
+
process.stderr.write(`Warning: could not update issue comment: ${err.message}
|
|
2007
|
+
`);
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
try {
|
|
2011
|
+
addClosesRefToPr(pr.number, issue.number);
|
|
2012
|
+
} catch (err) {
|
|
2013
|
+
process.stderr.write(`Warning: could not add Closes #${issue.number} to PR: ${err.message}
|
|
2014
|
+
`);
|
|
2015
|
+
}
|
|
2016
|
+
if (options.askCopilotReview) {
|
|
2017
|
+
try {
|
|
2018
|
+
addCopilotReviewer(pr.number);
|
|
2019
|
+
} catch (err) {
|
|
2020
|
+
process.stderr.write(`Warning: could not request Copilot review: ${err.message}
|
|
2021
|
+
`);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
1930
2024
|
}
|
|
2025
|
+
} catch (err) {
|
|
2026
|
+
process.stderr.write(`Warning: could not detect current branch PR: ${err.message}
|
|
2027
|
+
`);
|
|
1931
2028
|
}
|
|
1932
2029
|
});
|
|
1933
2030
|
|
|
@@ -2000,7 +2097,24 @@ function formatPrInfoContext(pr) {
|
|
|
2000
2097
|
return JSON.stringify(pr, null, 2);
|
|
2001
2098
|
}
|
|
2002
2099
|
function addAiOptions(cmd) {
|
|
2003
|
-
return cmd.
|
|
2100
|
+
return cmd.requiredOption("--with <executor>", "Executor to use: claude or codex").option("--model <string>", "Model identifier to pass to the executor").option("--silent", "Suppress step-by-step Claude output; show only the final summary").option("--push", "Append instruction to commit and push changes after the AI finishes");
|
|
2101
|
+
}
|
|
2102
|
+
function resolveExecutor(withOption) {
|
|
2103
|
+
const executor = withOption.toLowerCase();
|
|
2104
|
+
if (executor !== "claude" && executor !== "codex") {
|
|
2105
|
+
process.stderr.write(`Error: --with must be 'claude' or 'codex', got '${withOption}'.
|
|
2106
|
+
`);
|
|
2107
|
+
process.exit(1);
|
|
2108
|
+
}
|
|
2109
|
+
return executor;
|
|
2110
|
+
}
|
|
2111
|
+
function invokeSelectedExecutor(prompt, options) {
|
|
2112
|
+
const executor = resolveExecutor(options.with);
|
|
2113
|
+
if (executor === "codex") {
|
|
2114
|
+
invokeCodexCode(prompt, { yolo: true, model: options.model });
|
|
2115
|
+
return;
|
|
2116
|
+
}
|
|
2117
|
+
return invokeClaudeCode(prompt, { yolo: true, verbose: !options.silent, model: options.model });
|
|
2004
2118
|
}
|
|
2005
2119
|
var executeSonarCmd = addAiOptions(
|
|
2006
2120
|
new Command5("sonar").description(
|
|
@@ -2046,12 +2160,7 @@ Current PR context from automata git get-pr-info --json:
|
|
|
2046
2160
|
${formatPrInfoContext(pr)}`,
|
|
2047
2161
|
options.push
|
|
2048
2162
|
);
|
|
2049
|
-
|
|
2050
|
-
invokeCodexCode(fullPrompt, { yolo: true });
|
|
2051
|
-
} else {
|
|
2052
|
-
const model = resolveModelOption(options);
|
|
2053
|
-
await invokeClaudeCode(fullPrompt, { yolo: true, verbose: options.verbose, model });
|
|
2054
|
-
}
|
|
2163
|
+
await invokeSelectedExecutor(fullPrompt, options);
|
|
2055
2164
|
});
|
|
2056
2165
|
function formatComments(comments) {
|
|
2057
2166
|
return comments.map((c) => {
|
|
@@ -2101,12 +2210,7 @@ Open review comments:
|
|
|
2101
2210
|
${formatComments(comments)}`,
|
|
2102
2211
|
options.push
|
|
2103
2212
|
);
|
|
2104
|
-
|
|
2105
|
-
invokeCodexCode(fullPrompt, { yolo: true });
|
|
2106
|
-
} else {
|
|
2107
|
-
const model = resolveModelOption(options);
|
|
2108
|
-
await invokeClaudeCode(fullPrompt, { yolo: true, verbose: options.verbose, model });
|
|
2109
|
-
}
|
|
2213
|
+
await invokeSelectedExecutor(fullPrompt, options);
|
|
2110
2214
|
});
|
|
2111
2215
|
var executePromptCommand = new Command5("execute-prompt").description("Execute a configured custom prompt using an AI assistant").addCommand(executeSonarCmd).addCommand(executeFixCommentsCmd);
|
|
2112
2216
|
|