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.
Files changed (2) hide show
  1. package/dist/index.js +97 -14
  2. 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 = ["exec"];
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("--dry-run", "Print the work plan and exit without changing anything").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) => {
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(JSON.stringify({ dryRun: true, plan: decisions.map(toPlanJson) }, null, 2) + "\n");
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "automata-cli",
3
- "version": "0.1.0-feature-030-do-work.190",
3
+ "version": "0.1.0-feature-030-do-work.192",
4
4
  "description": "Automata CLI tool",
5
5
  "type": "module",
6
6
  "bin": {