automata-cli 0.1.0-feature-030-do-work.190 → 0.1.0-feature-030-do-work.192
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/dist/index.js +97 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1483,6 +1483,10 @@ import { existsSync } from "fs";
|
|
|
1483
1483
|
import { delimiter, join } from "path";
|
|
1484
1484
|
|
|
1485
1485
|
// src/cli/spawnUtils.ts
|
|
1486
|
+
function shellQuote(arg) {
|
|
1487
|
+
if (/^[A-Za-z0-9_@%+=:,./-]+$/.test(arg)) return arg;
|
|
1488
|
+
return `'${arg.replaceAll("'", `'\\''`)}'`;
|
|
1489
|
+
}
|
|
1486
1490
|
function truncate(str, max) {
|
|
1487
1491
|
return str.length > max ? str.slice(0, max) + "..." : str;
|
|
1488
1492
|
}
|
|
@@ -1520,6 +1524,14 @@ function resolveCommand(name) {
|
|
|
1520
1524
|
}
|
|
1521
1525
|
return name;
|
|
1522
1526
|
}
|
|
1527
|
+
function buildClaudeArgs(prompt, options = {}) {
|
|
1528
|
+
const args = [];
|
|
1529
|
+
if (options.yolo) args.push("--dangerously-skip-permissions");
|
|
1530
|
+
if (options.model) args.push("--model", options.model);
|
|
1531
|
+
if (options.verbose) args.push("--verbose", "--output-format", "stream-json");
|
|
1532
|
+
args.push("-p", prompt);
|
|
1533
|
+
return args;
|
|
1534
|
+
}
|
|
1523
1535
|
function invokeClaudeCode(prompt, options = {}) {
|
|
1524
1536
|
if (options.verbose) {
|
|
1525
1537
|
return invokeClaudeCodeVerbose(prompt, options.yolo ?? false, options.model);
|
|
@@ -1528,10 +1540,7 @@ function invokeClaudeCode(prompt, options = {}) {
|
|
|
1528
1540
|
}
|
|
1529
1541
|
function invokeClaudeCodeSync(prompt, yolo, model) {
|
|
1530
1542
|
const claudeBin = resolveCommand("claude");
|
|
1531
|
-
const args =
|
|
1532
|
-
if (yolo) args.push("--dangerously-skip-permissions");
|
|
1533
|
-
if (model) args.push("--model", model);
|
|
1534
|
-
args.push("-p", prompt);
|
|
1543
|
+
const args = buildClaudeArgs(prompt, { yolo, model, verbose: false });
|
|
1535
1544
|
const result = spawnSync4(claudeBin, args, { encoding: "utf8", stdio: "inherit" });
|
|
1536
1545
|
handleSpawnError(result.error, "claude");
|
|
1537
1546
|
handleExitCode(result.status, "Claude Code");
|
|
@@ -1539,10 +1548,7 @@ function invokeClaudeCodeSync(prompt, yolo, model) {
|
|
|
1539
1548
|
function invokeClaudeCodeVerbose(prompt, yolo, model) {
|
|
1540
1549
|
return new Promise((resolve2) => {
|
|
1541
1550
|
const claudeBin = resolveCommand("claude");
|
|
1542
|
-
const args =
|
|
1543
|
-
if (yolo) args.push("--dangerously-skip-permissions");
|
|
1544
|
-
if (model) args.push("--model", model);
|
|
1545
|
-
args.push("--verbose", "--output-format", "stream-json", "-p", prompt);
|
|
1551
|
+
const args = buildClaudeArgs(prompt, { yolo, model, verbose: true });
|
|
1546
1552
|
const child = spawn(claudeBin, args, { stdio: ["inherit", "pipe", "inherit"] });
|
|
1547
1553
|
const rl = createInterface({ input: child.stdout });
|
|
1548
1554
|
let turnCount = 0;
|
|
@@ -1629,6 +1635,13 @@ function summarizeTool(name, input) {
|
|
|
1629
1635
|
|
|
1630
1636
|
// src/codex/codexService.ts
|
|
1631
1637
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
1638
|
+
function buildCodexArgs(prompt, options = {}) {
|
|
1639
|
+
const args = ["exec"];
|
|
1640
|
+
if (options.yolo) args.push("--dangerously-bypass-approvals-and-sandbox");
|
|
1641
|
+
if (options.model) args.push("--model", options.model);
|
|
1642
|
+
args.push(prompt);
|
|
1643
|
+
return args;
|
|
1644
|
+
}
|
|
1632
1645
|
function invokeCodexCode(prompt, options = {}) {
|
|
1633
1646
|
if (options.verbose) {
|
|
1634
1647
|
process.stderr.write("Warning: --verbose is not supported for Codex and will be ignored.\n");
|
|
@@ -1637,10 +1650,7 @@ function invokeCodexCode(prompt, options = {}) {
|
|
|
1637
1650
|
}
|
|
1638
1651
|
function invokeCodexCodeSync(prompt, yolo, model) {
|
|
1639
1652
|
const codexBin = resolveCommand("codex");
|
|
1640
|
-
const args =
|
|
1641
|
-
if (yolo) args.push("--dangerously-bypass-approvals-and-sandbox");
|
|
1642
|
-
if (model) args.push("--model", model);
|
|
1643
|
-
args.push(prompt);
|
|
1653
|
+
const args = buildCodexArgs(prompt, { yolo, model });
|
|
1644
1654
|
const result = spawnSync5(codexBin, args, { encoding: "utf8", stdio: "inherit" });
|
|
1645
1655
|
handleSpawnError(result.error, "codex");
|
|
1646
1656
|
handleExitCode(result.status, "Codex");
|
|
@@ -2881,6 +2891,45 @@ function checkAuthenticatedIdentity(agentUser, allowedUsers) {
|
|
|
2881
2891
|
`
|
|
2882
2892
|
);
|
|
2883
2893
|
}
|
|
2894
|
+
function planRun(item, settings, silent) {
|
|
2895
|
+
const prompt = composePrompt({
|
|
2896
|
+
item,
|
|
2897
|
+
repo: getRepoSlug(),
|
|
2898
|
+
agentUser: settings.participants.agentUser,
|
|
2899
|
+
baseBranch: settings.baseBranch,
|
|
2900
|
+
frame: settings.prompts[item.turn]
|
|
2901
|
+
});
|
|
2902
|
+
const bin = resolveCommand(settings.executor === "codex" ? "codex" : "claude");
|
|
2903
|
+
const args = settings.executor === "codex" ? buildCodexArgs(prompt, { yolo: true, model: settings.model }) : buildClaudeArgs(prompt, { yolo: true, verbose: !silent, model: settings.model });
|
|
2904
|
+
return { prompt, bin, args, command: [bin, ...args].map(shellQuote).join(" ") };
|
|
2905
|
+
}
|
|
2906
|
+
function describePlannedRun(item, settings, run5) {
|
|
2907
|
+
const rule = "\u2500".repeat(72);
|
|
2908
|
+
const lines = [
|
|
2909
|
+
rule,
|
|
2910
|
+
`Issue #${String(item.issue.number)} \u2014 ${item.issue.title}`,
|
|
2911
|
+
rule,
|
|
2912
|
+
` Turn ${item.turn}`,
|
|
2913
|
+
` Why ${item.reason}`,
|
|
2914
|
+
` Branch ${item.branch} (would check out${item.turn === "pr-work" ? " and fast-forward" : " and pull"})`,
|
|
2915
|
+
` Assign ${item.needsAssignment ? `would assign to ${settings.participants.agentUser}` : "already assigned"}`,
|
|
2916
|
+
` Marker would post on ${item.turn === "pr-work" && item.pr ? `pull request #${String(item.pr.number)}` : `issue #${String(item.issue.number)}`}`,
|
|
2917
|
+
` Executor ${settings.executor}${settings.model === void 0 ? " (no model override)" : ` \xB7 model ${settings.model}`}`,
|
|
2918
|
+
` Permissions bypassed (do-work always runs unattended)`,
|
|
2919
|
+
` Prompt ${String(run5.prompt.length)} chars \u2014 frame + assembled context`,
|
|
2920
|
+
"",
|
|
2921
|
+
" Command that would be launched:",
|
|
2922
|
+
// Printed flush-left and unindented on purpose: the prompt is a multi-line
|
|
2923
|
+
// quoted argument, so indenting the continuation lines would inject leading
|
|
2924
|
+
// whitespace into the prompt itself and the command would no longer be the
|
|
2925
|
+
// one that runs.
|
|
2926
|
+
rule,
|
|
2927
|
+
run5.command,
|
|
2928
|
+
rule,
|
|
2929
|
+
""
|
|
2930
|
+
];
|
|
2931
|
+
return lines.join("\n") + "\n";
|
|
2932
|
+
}
|
|
2884
2933
|
function discoverIssues(settings) {
|
|
2885
2934
|
const candidates = listCandidateIssues(settings.technique, settings.discoveryValue, settings.limit);
|
|
2886
2935
|
if (settings.onlyIssue === void 0) {
|
|
@@ -3056,7 +3105,10 @@ function summarize(reports) {
|
|
|
3056
3105
|
}
|
|
3057
3106
|
var doWorkCommand = new Command6("do-work").description(
|
|
3058
3107
|
"Run one tick of the autonomous loop: find the issues whose newest authorized message the agent has not answered, and answer them"
|
|
3059
|
-
).option("--with <executor>", "Executor to use: claude or codex (default: from config, else claude)").option("--model <string>", "Model identifier to pass to the executor, overriding the configured default for it").option("--issue <number>", "Restrict the tick to a single issue").option("--limit <n>", "Maximum number of issues to fetch", "10").option("--max-runs <n>", "Maximum number of model runs this tick").option(
|
|
3108
|
+
).option("--with <executor>", "Executor to use: claude or codex (default: from config, else claude)").option("--model <string>", "Model identifier to pass to the executor, overriding the configured default for it").option("--issue <number>", "Restrict the tick to a single issue").option("--limit <n>", "Maximum number of issues to fetch", "10").option("--max-runs <n>", "Maximum number of model runs this tick").option(
|
|
3109
|
+
"--dry-run",
|
|
3110
|
+
"Print the work plan, plus a summary and the exact command that would be launched for each item, and exit without changing anything"
|
|
3111
|
+
).option("--json", "Emit the work plan and outcomes as JSON on stdout").option("--silent", "Suppress step-by-step Claude output; show only the final summary").action(async (options) => {
|
|
3060
3112
|
const settings = resolveSettings(options);
|
|
3061
3113
|
const lock = acquireRunLock("do-work", settings.lockStaleMinutes);
|
|
3062
3114
|
if (!lock.ok) {
|
|
@@ -3100,9 +3152,40 @@ ${describePlan(decisions)}`;
|
|
|
3100
3152
|
out(planText);
|
|
3101
3153
|
}
|
|
3102
3154
|
if (options.dryRun) {
|
|
3155
|
+
const runnableInPlan = settings.maxRuns > 0 ? items.slice(0, settings.maxRuns) : items;
|
|
3156
|
+
const planned = runnableInPlan.map((item) => planRun(item, settings, options.silent === true));
|
|
3103
3157
|
if (options.json) {
|
|
3104
|
-
out(
|
|
3158
|
+
out(
|
|
3159
|
+
JSON.stringify(
|
|
3160
|
+
{
|
|
3161
|
+
dryRun: true,
|
|
3162
|
+
plan: decisions.map(toPlanJson),
|
|
3163
|
+
runs: planned.map((run5, index) => ({
|
|
3164
|
+
issue: runnableInPlan[index].issue.number,
|
|
3165
|
+
turn: runnableInPlan[index].turn,
|
|
3166
|
+
executor: settings.executor,
|
|
3167
|
+
model: settings.model ?? null,
|
|
3168
|
+
bin: run5.bin,
|
|
3169
|
+
args: run5.args,
|
|
3170
|
+
command: run5.command,
|
|
3171
|
+
prompt: run5.prompt
|
|
3172
|
+
}))
|
|
3173
|
+
},
|
|
3174
|
+
null,
|
|
3175
|
+
2
|
|
3176
|
+
) + "\n"
|
|
3177
|
+
);
|
|
3178
|
+
return 0;
|
|
3179
|
+
}
|
|
3180
|
+
for (const [index, run5] of planned.entries()) {
|
|
3181
|
+
out("\n" + describePlannedRun(runnableInPlan[index], settings, run5));
|
|
3182
|
+
}
|
|
3183
|
+
if (items.length > runnableInPlan.length) {
|
|
3184
|
+
out(`
|
|
3185
|
+
(${String(items.length - runnableInPlan.length)} further item(s) deferred by the run cap.)
|
|
3186
|
+
`);
|
|
3105
3187
|
}
|
|
3188
|
+
out("\nDry run: nothing was assigned, posted, checked out or executed.\n");
|
|
3106
3189
|
return 0;
|
|
3107
3190
|
}
|
|
3108
3191
|
const runnable = settings.maxRuns > 0 ? items.slice(0, settings.maxRuns) : items;
|