automata-cli 0.6.0-develop.256 → 0.6.0-develop.268
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.
|
@@ -152,6 +152,8 @@ function ConfigWizard() {
|
|
|
152
152
|
);
|
|
153
153
|
const [doWorkClaudeModel, setDoWorkClaudeModel] = useState(existing.doWork?.models?.claude ?? "");
|
|
154
154
|
const [doWorkCodexModel, setDoWorkCodexModel] = useState(existing.doWork?.models?.codex ?? "");
|
|
155
|
+
const [doWorkClaudeEffort, setDoWorkClaudeEffort] = useState(existing.doWork?.effort?.claude ?? "");
|
|
156
|
+
const [doWorkCodexEffort, setDoWorkCodexEffort] = useState(existing.doWork?.effort?.codex ?? "");
|
|
155
157
|
const [doWorkLockStale, setDoWorkLockStale] = useState(
|
|
156
158
|
String(existing.doWork?.lockStaleMinutes ?? DEFAULT_DO_WORK.lockStaleMinutes)
|
|
157
159
|
);
|
|
@@ -269,13 +271,23 @@ function ConfigWizard() {
|
|
|
269
271
|
},
|
|
270
272
|
"do-work-claude-model": {
|
|
271
273
|
setValue: setDoWorkClaudeModel,
|
|
272
|
-
onSubmit: () => setScreen("do-work-
|
|
274
|
+
onSubmit: () => setScreen("do-work-claude-effort"),
|
|
273
275
|
onBack: () => setScreen("do-work-executor")
|
|
274
276
|
},
|
|
277
|
+
"do-work-claude-effort": {
|
|
278
|
+
setValue: setDoWorkClaudeEffort,
|
|
279
|
+
onSubmit: () => setScreen("do-work-codex-model"),
|
|
280
|
+
onBack: () => setScreen("do-work-claude-model")
|
|
281
|
+
},
|
|
275
282
|
"do-work-codex-model": {
|
|
276
283
|
setValue: setDoWorkCodexModel,
|
|
284
|
+
onSubmit: () => setScreen("do-work-codex-effort"),
|
|
285
|
+
onBack: () => setScreen("do-work-claude-effort")
|
|
286
|
+
},
|
|
287
|
+
"do-work-codex-effort": {
|
|
288
|
+
setValue: setDoWorkCodexEffort,
|
|
277
289
|
onSubmit: () => setScreen("do-work-max-runs"),
|
|
278
|
-
onBack: () => setScreen("do-work-
|
|
290
|
+
onBack: () => setScreen("do-work-codex-model")
|
|
279
291
|
},
|
|
280
292
|
"do-work-max-runs": {
|
|
281
293
|
setValue: (update) => {
|
|
@@ -291,7 +303,7 @@ function ConfigWizard() {
|
|
|
291
303
|
setValidationError("");
|
|
292
304
|
setScreen("do-work-lock-stale");
|
|
293
305
|
},
|
|
294
|
-
onBack: () => setScreen("do-work-codex-
|
|
306
|
+
onBack: () => setScreen("do-work-codex-effort")
|
|
295
307
|
},
|
|
296
308
|
"do-work-lock-stale": {
|
|
297
309
|
setValue: (update) => {
|
|
@@ -317,6 +329,10 @@ function ConfigWizard() {
|
|
|
317
329
|
claude: doWorkClaudeModel.trim() || void 0,
|
|
318
330
|
codex: doWorkCodexModel.trim() || void 0
|
|
319
331
|
},
|
|
332
|
+
effort: {
|
|
333
|
+
claude: doWorkClaudeEffort.trim() || void 0,
|
|
334
|
+
codex: doWorkCodexEffort.trim() || void 0
|
|
335
|
+
},
|
|
320
336
|
maxRunsPerTick: maxRuns ?? void 0,
|
|
321
337
|
lockStaleMinutes: parsed
|
|
322
338
|
}
|
|
@@ -470,12 +486,24 @@ function ConfigWizard() {
|
|
|
470
486
|
value: doWorkClaudeModel,
|
|
471
487
|
hint: `Type model \xB7 Enter to continue \xB7 ${BACK}`
|
|
472
488
|
},
|
|
489
|
+
"do-work-claude-effort": {
|
|
490
|
+
title: "Do Work \u2014 Claude Effort",
|
|
491
|
+
label: "Default reasoning effort when the executor is Claude (blank = the executor's own default):",
|
|
492
|
+
value: doWorkClaudeEffort,
|
|
493
|
+
hint: `Type effort \xB7 Enter to continue \xB7 ${BACK}`
|
|
494
|
+
},
|
|
473
495
|
"do-work-codex-model": {
|
|
474
496
|
title: "Do Work \u2014 Codex Model",
|
|
475
497
|
label: "Default model when the executor is Codex (blank = the executor's own default):",
|
|
476
498
|
value: doWorkCodexModel,
|
|
477
499
|
hint: `Type model \xB7 Enter to continue \xB7 ${BACK}`
|
|
478
500
|
},
|
|
501
|
+
"do-work-codex-effort": {
|
|
502
|
+
title: "Do Work \u2014 Codex Effort",
|
|
503
|
+
label: "Default reasoning effort when the executor is Codex (blank = the executor's own default):",
|
|
504
|
+
value: doWorkCodexEffort,
|
|
505
|
+
hint: `Type effort \xB7 Enter to continue \xB7 ${BACK}`
|
|
506
|
+
},
|
|
479
507
|
"do-work-max-runs": {
|
|
480
508
|
title: "Do Work \u2014 Max Runs Per Tick",
|
|
481
509
|
label: "Model runs allowed per tick (0 = unlimited):",
|
package/dist/index.js
CHANGED
|
@@ -34,9 +34,9 @@ function writeDoWork(patch) {
|
|
|
34
34
|
writeConfig({ ...current, doWork: { ...current.doWork, ...patch } });
|
|
35
35
|
}
|
|
36
36
|
function parseNonNegativeInt(value, label) {
|
|
37
|
-
const
|
|
38
|
-
const parsed = Number.parseInt(
|
|
39
|
-
if (Number.isNaN(parsed) || parsed < 0 || String(parsed) !==
|
|
37
|
+
const trimmed2 = value.trim();
|
|
38
|
+
const parsed = Number.parseInt(trimmed2, 10);
|
|
39
|
+
if (Number.isNaN(parsed) || parsed < 0 || String(parsed) !== trimmed2 || !Number.isSafeInteger(parsed)) {
|
|
40
40
|
process.stderr.write(`Error: ${label} must be a non-negative integer (got "${value}").
|
|
41
41
|
`);
|
|
42
42
|
process.exit(1);
|
|
@@ -147,6 +147,24 @@ var configSetDoWorkModel = new Command("do-work-model").description("Set the def
|
|
|
147
147
|
process.stdout.write(`do-work ${executor} model set to: ${model}
|
|
148
148
|
`);
|
|
149
149
|
});
|
|
150
|
+
var configSetDoWorkEffort = new Command("do-work-effort").description("Set the default reasoning effort `do-work` passes to one executor").argument("<executor>", `Executor: ${VALID_EXECUTORS.join(", ")}`).argument("<value>", "Effort level, forwarded to the executor unchanged").action((executor, value) => {
|
|
151
|
+
if (!VALID_EXECUTORS.includes(executor)) {
|
|
152
|
+
process.stderr.write(
|
|
153
|
+
`Error: invalid executor "${executor}". Must be one of: ${VALID_EXECUTORS.join(", ")}
|
|
154
|
+
`
|
|
155
|
+
);
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
const effort = value.trim();
|
|
159
|
+
if (effort.length === 0) {
|
|
160
|
+
process.stderr.write("Error: do-work-effort requires a non-empty effort level.\n");
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
const current = readRawConfig();
|
|
164
|
+
writeDoWork({ effort: { ...current.doWork?.effort, [executor]: effort } });
|
|
165
|
+
process.stdout.write(`do-work ${executor} effort set to: ${effort}
|
|
166
|
+
`);
|
|
167
|
+
});
|
|
150
168
|
var configSetDoWorkMaxRuns = new Command("do-work-max-runs").description("Set the maximum number of model runs `do-work` performs per tick (0 = unlimited)").argument("<value>", "Non-negative integer").action((value) => {
|
|
151
169
|
const maxRunsPerTick = parseNonNegativeInt(value, "do-work-max-runs");
|
|
152
170
|
writeDoWork({ maxRunsPerTick });
|
|
@@ -189,12 +207,12 @@ var configSetDoWorkPrompt = new Command("do-work-prompt").description("Set the t
|
|
|
189
207
|
process.stdout.write(`do-work ${turnKind} prompt set.
|
|
190
208
|
`);
|
|
191
209
|
});
|
|
192
|
-
var configSet = new Command("set").description("Set a configuration value").addCommand(configSetType).addCommand(configSetIssueDiscoveryTechnique).addCommand(configSetIssueDiscoveryValue).addCommand(configSetClaudeSystemPrompt).addCommand(configSetAllowedUsers).addCommand(configSetAgentUser).addCommand(configSetDoWorkBaseBranch).addCommand(configSetDoWorkProtectedBranches).addCommand(configSetDoWorkExecutor).addCommand(configSetDoWorkModel).addCommand(configSetDoWorkMaxRuns).addCommand(configSetDoWorkLockStaleMinutes).addCommand(configSetDoWorkPrompt);
|
|
210
|
+
var configSet = new Command("set").description("Set a configuration value").addCommand(configSetType).addCommand(configSetIssueDiscoveryTechnique).addCommand(configSetIssueDiscoveryValue).addCommand(configSetClaudeSystemPrompt).addCommand(configSetAllowedUsers).addCommand(configSetAgentUser).addCommand(configSetDoWorkBaseBranch).addCommand(configSetDoWorkProtectedBranches).addCommand(configSetDoWorkExecutor).addCommand(configSetDoWorkModel).addCommand(configSetDoWorkEffort).addCommand(configSetDoWorkMaxRuns).addCommand(configSetDoWorkLockStaleMinutes).addCommand(configSetDoWorkPrompt);
|
|
193
211
|
var configCommand = new Command("config").description("Configure automata settings").addCommand(configSet).action(async () => {
|
|
194
212
|
const [{ render }, React, { ConfigWizard }] = await Promise.all([
|
|
195
213
|
import("ink"),
|
|
196
214
|
import("react"),
|
|
197
|
-
import("./ConfigWizard-
|
|
215
|
+
import("./ConfigWizard-HDHKPY6J.js")
|
|
198
216
|
]);
|
|
199
217
|
const { waitUntilExit } = render(React.createElement(ConfigWizard));
|
|
200
218
|
await waitUntilExit();
|
|
@@ -1539,6 +1557,15 @@ function handleExitCode(status, toolName) {
|
|
|
1539
1557
|
process.exit(status);
|
|
1540
1558
|
}
|
|
1541
1559
|
}
|
|
1560
|
+
function resolveEffortOption(value) {
|
|
1561
|
+
if (value === void 0) return void 0;
|
|
1562
|
+
const trimmed2 = value.trim();
|
|
1563
|
+
if (trimmed2.length === 0) {
|
|
1564
|
+
process.stderr.write("Error: --effort must be a non-empty level.\n");
|
|
1565
|
+
process.exit(1);
|
|
1566
|
+
}
|
|
1567
|
+
return trimmed2;
|
|
1568
|
+
}
|
|
1542
1569
|
|
|
1543
1570
|
// src/cli/childRegistry.ts
|
|
1544
1571
|
var active = /* @__PURE__ */ new Set();
|
|
@@ -1595,6 +1622,7 @@ function buildClaudeArgs(prompt, options = {}) {
|
|
|
1595
1622
|
const args = [];
|
|
1596
1623
|
if (options.yolo) args.push("--dangerously-skip-permissions");
|
|
1597
1624
|
if (options.model) args.push("--model", options.model);
|
|
1625
|
+
if (options.effort) args.push("--effort", options.effort);
|
|
1598
1626
|
if (options.verbose) args.push("--verbose", "--output-format", "stream-json");
|
|
1599
1627
|
args.push("-p", prompt);
|
|
1600
1628
|
return args;
|
|
@@ -1602,7 +1630,12 @@ function buildClaudeArgs(prompt, options = {}) {
|
|
|
1602
1630
|
function runClaude(prompt, options = {}) {
|
|
1603
1631
|
return new Promise((resolve2, reject) => {
|
|
1604
1632
|
const claudeBin = resolveCommand("claude");
|
|
1605
|
-
const args = buildClaudeArgs(prompt, {
|
|
1633
|
+
const args = buildClaudeArgs(prompt, {
|
|
1634
|
+
yolo: true,
|
|
1635
|
+
model: options.model,
|
|
1636
|
+
effort: options.effort,
|
|
1637
|
+
verbose: true
|
|
1638
|
+
});
|
|
1606
1639
|
const child = spawn(claudeBin, args, { stdio: ["inherit", "pipe", "inherit"] });
|
|
1607
1640
|
trackChild(child);
|
|
1608
1641
|
const rl = createInterface({ input: child.stdout });
|
|
@@ -1639,21 +1672,21 @@ function runClaude(prompt, options = {}) {
|
|
|
1639
1672
|
}
|
|
1640
1673
|
function invokeClaudeCode(prompt, options = {}) {
|
|
1641
1674
|
if (options.verbose) {
|
|
1642
|
-
return invokeClaudeCodeVerbose(prompt, options.yolo ?? false, options.model);
|
|
1675
|
+
return invokeClaudeCodeVerbose(prompt, options.yolo ?? false, options.model, options.effort);
|
|
1643
1676
|
}
|
|
1644
|
-
invokeClaudeCodeSync(prompt, options.yolo ?? false, options.model);
|
|
1677
|
+
invokeClaudeCodeSync(prompt, options.yolo ?? false, options.model, options.effort);
|
|
1645
1678
|
}
|
|
1646
|
-
function invokeClaudeCodeSync(prompt, yolo, model) {
|
|
1679
|
+
function invokeClaudeCodeSync(prompt, yolo, model, effort) {
|
|
1647
1680
|
const claudeBin = resolveCommand("claude");
|
|
1648
|
-
const args = buildClaudeArgs(prompt, { yolo, model, verbose: false });
|
|
1681
|
+
const args = buildClaudeArgs(prompt, { yolo, model, effort, verbose: false });
|
|
1649
1682
|
const result = spawnSync4(claudeBin, args, { encoding: "utf8", stdio: "inherit" });
|
|
1650
1683
|
handleSpawnError(result.error, "claude");
|
|
1651
1684
|
handleExitCode(result.status, "Claude Code");
|
|
1652
1685
|
}
|
|
1653
|
-
function invokeClaudeCodeVerbose(prompt, yolo, model) {
|
|
1686
|
+
function invokeClaudeCodeVerbose(prompt, yolo, model, effort) {
|
|
1654
1687
|
return new Promise((resolve2) => {
|
|
1655
1688
|
const claudeBin = resolveCommand("claude");
|
|
1656
|
-
const args = buildClaudeArgs(prompt, { yolo, model, verbose: true });
|
|
1689
|
+
const args = buildClaudeArgs(prompt, { yolo, model, effort, verbose: true });
|
|
1657
1690
|
const child = spawn(claudeBin, args, { stdio: ["inherit", "pipe", "inherit"] });
|
|
1658
1691
|
const rl = createInterface({ input: child.stdout });
|
|
1659
1692
|
let turnCount = 0;
|
|
@@ -1753,10 +1786,14 @@ function summarizeTool(name, input) {
|
|
|
1753
1786
|
|
|
1754
1787
|
// src/codex/codexService.ts
|
|
1755
1788
|
import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
|
|
1789
|
+
function toTomlBasicString(value) {
|
|
1790
|
+
return JSON.stringify(value);
|
|
1791
|
+
}
|
|
1756
1792
|
function buildCodexArgs(prompt, options = {}) {
|
|
1757
1793
|
const args = ["exec"];
|
|
1758
1794
|
if (options.yolo) args.push("--dangerously-bypass-approvals-and-sandbox");
|
|
1759
1795
|
if (options.model) args.push("--model", options.model);
|
|
1796
|
+
if (options.effort) args.push("-c", `model_reasoning_effort=${toTomlBasicString(options.effort)}`);
|
|
1760
1797
|
args.push(prompt);
|
|
1761
1798
|
return args;
|
|
1762
1799
|
}
|
|
@@ -1764,11 +1801,11 @@ function invokeCodexCode(prompt, options = {}) {
|
|
|
1764
1801
|
if (options.verbose) {
|
|
1765
1802
|
process.stderr.write("Warning: --verbose is not supported for Codex and will be ignored.\n");
|
|
1766
1803
|
}
|
|
1767
|
-
invokeCodexCodeSync(prompt, options.yolo ?? false, options.model);
|
|
1804
|
+
invokeCodexCodeSync(prompt, options.yolo ?? false, options.model, options.effort);
|
|
1768
1805
|
}
|
|
1769
|
-
function invokeCodexCodeSync(prompt, yolo, model) {
|
|
1806
|
+
function invokeCodexCodeSync(prompt, yolo, model, effort) {
|
|
1770
1807
|
const codexBin = resolveCommand("codex");
|
|
1771
|
-
const args = buildCodexArgs(prompt, { yolo, model });
|
|
1808
|
+
const args = buildCodexArgs(prompt, { yolo, model, effort });
|
|
1772
1809
|
const result = spawnSync5(codexBin, args, { encoding: "utf8", stdio: "inherit" });
|
|
1773
1810
|
handleSpawnError(result.error, "codex");
|
|
1774
1811
|
handleExitCode(result.status, "Codex");
|
|
@@ -1776,7 +1813,7 @@ function invokeCodexCodeSync(prompt, yolo, model) {
|
|
|
1776
1813
|
function runCodex(prompt, options = {}) {
|
|
1777
1814
|
return new Promise((resolve2, reject) => {
|
|
1778
1815
|
const codexBin = resolveCommand("codex");
|
|
1779
|
-
const args = buildCodexArgs(prompt, { yolo: true, model: options.model });
|
|
1816
|
+
const args = buildCodexArgs(prompt, { yolo: true, model: options.model, effort: options.effort });
|
|
1780
1817
|
const child = spawn2(codexBin, args, { stdio: "inherit" });
|
|
1781
1818
|
trackChild(child);
|
|
1782
1819
|
child.on("error", (err) => {
|
|
@@ -1881,7 +1918,7 @@ Title: ${issue.title}
|
|
|
1881
1918
|
}
|
|
1882
1919
|
return issue;
|
|
1883
1920
|
}
|
|
1884
|
-
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) => {
|
|
1921
|
+
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("--effort <level>", "Reasoning effort 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) => {
|
|
1885
1922
|
const config = readConfig();
|
|
1886
1923
|
validateConfig(config);
|
|
1887
1924
|
const limit = Number.parseInt(options.limit, 10);
|
|
@@ -1919,6 +1956,7 @@ ${issue.body}
|
|
|
1919
1956
|
`);
|
|
1920
1957
|
process.exit(1);
|
|
1921
1958
|
}
|
|
1959
|
+
const effort = resolveEffortOption(options.effort);
|
|
1922
1960
|
if (options.claude !== false) {
|
|
1923
1961
|
const systemPrompt = config.claudeSystemPrompt ?? DEFAULT_CLAUDE_SYSTEM_PROMPT;
|
|
1924
1962
|
const prompt = `Resolving issue #${issue.number}:
|
|
@@ -1930,9 +1968,14 @@ ${issue.body}`;
|
|
|
1930
1968
|
if (options.silent) {
|
|
1931
1969
|
process.stderr.write("Warning: --silent is only supported with Claude and has no effect when used with Codex.\n");
|
|
1932
1970
|
}
|
|
1933
|
-
invokeCodexCode(prompt, { yolo: options.yolo, model: options.model });
|
|
1971
|
+
invokeCodexCode(prompt, { yolo: options.yolo, model: options.model, effort });
|
|
1934
1972
|
} else {
|
|
1935
|
-
await invokeClaudeCode(prompt, {
|
|
1973
|
+
await invokeClaudeCode(prompt, {
|
|
1974
|
+
yolo: options.yolo,
|
|
1975
|
+
verbose: !options.silent,
|
|
1976
|
+
model: options.model,
|
|
1977
|
+
effort
|
|
1978
|
+
});
|
|
1936
1979
|
}
|
|
1937
1980
|
}
|
|
1938
1981
|
linkPrToIssue(issue.number, commentUrl, options.askCopilotReview === true);
|
|
@@ -2021,7 +2064,7 @@ async function resolvePrompt(options) {
|
|
|
2021
2064
|
process.stderr.write("Error: No prompt provided. Use --prompt, --file-prompt, or pipe via stdin.\n");
|
|
2022
2065
|
process.exit(1);
|
|
2023
2066
|
}
|
|
2024
|
-
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) => {
|
|
2067
|
+
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").option("--effort <level>", "Reasoning effort to pass to the executor").action(async (options) => {
|
|
2025
2068
|
const executor = options.with.toLowerCase();
|
|
2026
2069
|
if (executor !== "claude" && executor !== "codex") {
|
|
2027
2070
|
process.stderr.write(`Error: --with must be 'claude' or 'codex', got '${options.with}'.
|
|
@@ -2029,10 +2072,16 @@ var executeCommand = new Command4("execute").description("Delegate work to an AI
|
|
|
2029
2072
|
process.exit(1);
|
|
2030
2073
|
}
|
|
2031
2074
|
const prompt = await resolvePrompt(options);
|
|
2075
|
+
const effort = resolveEffortOption(options.effort);
|
|
2032
2076
|
if (executor === "codex") {
|
|
2033
|
-
invokeCodexCode(prompt, { yolo: true, model: options.model });
|
|
2077
|
+
invokeCodexCode(prompt, { yolo: true, model: options.model, effort });
|
|
2034
2078
|
} else {
|
|
2035
|
-
await invokeClaudeCode(prompt, {
|
|
2079
|
+
await invokeClaudeCode(prompt, {
|
|
2080
|
+
yolo: true,
|
|
2081
|
+
verbose: !options.silent,
|
|
2082
|
+
model: options.model,
|
|
2083
|
+
effort
|
|
2084
|
+
});
|
|
2036
2085
|
}
|
|
2037
2086
|
});
|
|
2038
2087
|
|
|
@@ -2083,8 +2132,8 @@ function analyzeSurface(messages, p) {
|
|
|
2083
2132
|
}
|
|
2084
2133
|
function lastAuthorClass(messages, p) {
|
|
2085
2134
|
if (messages.length === 0) return "none";
|
|
2086
|
-
const
|
|
2087
|
-
return
|
|
2135
|
+
const newest2 = [...messages].sort(byCreatedAt).at(-1);
|
|
2136
|
+
return newest2 === void 0 ? "none" : classify(newest2.author, p);
|
|
2088
2137
|
}
|
|
2089
2138
|
function formatMessages(messages) {
|
|
2090
2139
|
return messages.map((message) => {
|
|
@@ -2154,7 +2203,7 @@ function pluralSuffix(count) {
|
|
|
2154
2203
|
return count === 1 ? "" : "s";
|
|
2155
2204
|
}
|
|
2156
2205
|
function addAiOptions(cmd) {
|
|
2157
|
-
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");
|
|
2206
|
+
return cmd.requiredOption("--with <executor>", "Executor to use: claude or codex").option("--model <string>", "Model identifier to pass to the executor").option("--effort <level>", "Reasoning effort 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");
|
|
2158
2207
|
}
|
|
2159
2208
|
function resolveExecutor2(withOption) {
|
|
2160
2209
|
const executor = withOption.toLowerCase();
|
|
@@ -2166,11 +2215,17 @@ function resolveExecutor2(withOption) {
|
|
|
2166
2215
|
return executor;
|
|
2167
2216
|
}
|
|
2168
2217
|
function invokeSelectedExecutor(prompt, executor, options) {
|
|
2218
|
+
const effort = resolveEffortOption(options.effort);
|
|
2169
2219
|
if (executor === "codex") {
|
|
2170
|
-
invokeCodexCode(prompt, { yolo: true, model: options.model });
|
|
2220
|
+
invokeCodexCode(prompt, { yolo: true, model: options.model, effort });
|
|
2171
2221
|
return;
|
|
2172
2222
|
}
|
|
2173
|
-
return invokeClaudeCode(prompt, {
|
|
2223
|
+
return invokeClaudeCode(prompt, {
|
|
2224
|
+
yolo: true,
|
|
2225
|
+
verbose: !options.silent,
|
|
2226
|
+
model: options.model,
|
|
2227
|
+
effort
|
|
2228
|
+
});
|
|
2174
2229
|
}
|
|
2175
2230
|
var executeSonarCmd = addAiOptions(
|
|
2176
2231
|
new Command5("sonar").description(
|
|
@@ -2853,9 +2908,9 @@ function selectLinkedPr(linkedPrs) {
|
|
|
2853
2908
|
function formatThreads(threads) {
|
|
2854
2909
|
return threads.map((thread) => {
|
|
2855
2910
|
const location = thread.line === null ? `${thread.path}:(file)` : `${thread.path}:${String(thread.line)}`;
|
|
2856
|
-
const
|
|
2857
|
-
const author =
|
|
2858
|
-
const body =
|
|
2911
|
+
const newest2 = thread.comments.at(-1);
|
|
2912
|
+
const author = newest2?.author ?? "unknown";
|
|
2913
|
+
const body = newest2?.body ?? "";
|
|
2859
2914
|
const link = thread.url === null ? "" : `
|
|
2860
2915
|
${thread.url}`;
|
|
2861
2916
|
return `[${author}] ${location}${link}
|
|
@@ -2921,6 +2976,101 @@ function composePrompt(input) {
|
|
|
2921
2976
|
return lines.join("\n");
|
|
2922
2977
|
}
|
|
2923
2978
|
|
|
2979
|
+
// src/github/runDirective.ts
|
|
2980
|
+
var VALID_TOOLS = ["claude", "codex"];
|
|
2981
|
+
var TOOL_PATTERN = /(?<![a-z0-9_:-])tool:([a-z0-9._-]+)/gi;
|
|
2982
|
+
var MODEL_PATTERN = /(?<![a-z0-9_:-])model:([a-z0-9._/+@-]+)/gi;
|
|
2983
|
+
function lastCapture(body, pattern) {
|
|
2984
|
+
const matches = [...body.matchAll(new RegExp(pattern.source, pattern.flags))];
|
|
2985
|
+
return matches.at(-1)?.[1];
|
|
2986
|
+
}
|
|
2987
|
+
function parseRunDirective(body) {
|
|
2988
|
+
const tool = lastCapture(body, TOOL_PATTERN);
|
|
2989
|
+
return {
|
|
2990
|
+
tool: tool === void 0 ? void 0 : tool.toLowerCase(),
|
|
2991
|
+
model: lastCapture(body, MODEL_PATTERN)
|
|
2992
|
+
};
|
|
2993
|
+
}
|
|
2994
|
+
function newest(messages) {
|
|
2995
|
+
let found = null;
|
|
2996
|
+
for (const message of messages) {
|
|
2997
|
+
if (found === null || message.createdAt >= found.createdAt) found = message;
|
|
2998
|
+
}
|
|
2999
|
+
return found;
|
|
3000
|
+
}
|
|
3001
|
+
function triggeringMessage(item) {
|
|
3002
|
+
if (item.turn === "issue-discuss") return newest(item.issueAnalysis.newMessages);
|
|
3003
|
+
const prLastAgentAt = item.prAnalysis?.lastAgentAt ?? null;
|
|
3004
|
+
const threadComments = item.actionableThreads.flatMap(
|
|
3005
|
+
(thread) => (
|
|
3006
|
+
// The thread's comments are already filtered to authorized and agent
|
|
3007
|
+
// accounts; the agent's own are not a trigger, and anything the agent has
|
|
3008
|
+
// since answered is not either.
|
|
3009
|
+
thread.comments.filter(
|
|
3010
|
+
(comment) => prLastAgentAt === null || comment.createdAt > prLastAgentAt
|
|
3011
|
+
)
|
|
3012
|
+
)
|
|
3013
|
+
);
|
|
3014
|
+
return newest([
|
|
3015
|
+
...item.issueAnalysis.newMessages,
|
|
3016
|
+
...item.prAnalysis?.newMessages ?? [],
|
|
3017
|
+
...threadComments
|
|
3018
|
+
]);
|
|
3019
|
+
}
|
|
3020
|
+
function isExecutor(value) {
|
|
3021
|
+
return VALID_TOOLS.includes(value);
|
|
3022
|
+
}
|
|
3023
|
+
function baselineExecutorSource(withOption, configExecutor) {
|
|
3024
|
+
if (withOption !== void 0) return "option";
|
|
3025
|
+
if (configExecutor !== void 0) return "config";
|
|
3026
|
+
return "default";
|
|
3027
|
+
}
|
|
3028
|
+
function trimmed(value) {
|
|
3029
|
+
if (value === void 0) return void 0;
|
|
3030
|
+
const cleaned = value.trim();
|
|
3031
|
+
return cleaned.length === 0 ? void 0 : cleaned;
|
|
3032
|
+
}
|
|
3033
|
+
function resolvePerExecutor(option, configured, switched) {
|
|
3034
|
+
if (!switched && option !== void 0) return { value: option, source: "option" };
|
|
3035
|
+
const fromConfig = trimmed(configured);
|
|
3036
|
+
if (fromConfig !== void 0) return { value: fromConfig, source: "config" };
|
|
3037
|
+
return { value: void 0, source: "none" };
|
|
3038
|
+
}
|
|
3039
|
+
function resolveExecution(input) {
|
|
3040
|
+
const { directive, withOption, modelOption, configExecutor, configModels } = input;
|
|
3041
|
+
if (directive.tool !== void 0 && !isExecutor(directive.tool)) {
|
|
3042
|
+
return { ok: false, invalidTool: directive.tool };
|
|
3043
|
+
}
|
|
3044
|
+
const baseline = withOption ?? configExecutor ?? input.defaultExecutor;
|
|
3045
|
+
const baselineSource = baselineExecutorSource(withOption, configExecutor);
|
|
3046
|
+
const executor = directive.tool ?? baseline;
|
|
3047
|
+
const executorSource = directive.tool === void 0 ? baselineSource : "message";
|
|
3048
|
+
const switched = directive.tool !== void 0 && directive.tool !== baseline;
|
|
3049
|
+
const effort = resolvePerExecutor(input.effortOption, input.configEfforts?.[executor], switched);
|
|
3050
|
+
const common = {
|
|
3051
|
+
ok: true,
|
|
3052
|
+
executor,
|
|
3053
|
+
executorSource,
|
|
3054
|
+
effort: effort.value,
|
|
3055
|
+
effortSource: effort.source
|
|
3056
|
+
};
|
|
3057
|
+
if (directive.model !== void 0) {
|
|
3058
|
+
return { ...common, model: directive.model, modelSource: "message" };
|
|
3059
|
+
}
|
|
3060
|
+
const model = resolvePerExecutor(modelOption, configModels?.[executor], switched);
|
|
3061
|
+
return { ...common, model: model.value, modelSource: model.source };
|
|
3062
|
+
}
|
|
3063
|
+
function describeExecution(execution) {
|
|
3064
|
+
const model = execution.model === void 0 ? " (no model override)" : ` \xB7 model ${execution.model}`;
|
|
3065
|
+
const effort = execution.effort === void 0 ? "" : ` \xB7 effort ${execution.effort}`;
|
|
3066
|
+
const fromMessage = execution.executorSource === "message" || execution.modelSource === "message" ? " \u2014 from the message" : "";
|
|
3067
|
+
return `${execution.executor}${model}${effort}${fromMessage}`;
|
|
3068
|
+
}
|
|
3069
|
+
function describeInvalidTool(invalidTool) {
|
|
3070
|
+
const valid = VALID_TOOLS.map((tool) => `\`${tool}\``).join(" and ");
|
|
3071
|
+
return `the newest message asks for \`tool:${invalidTool}\`, which is not an executor automata knows (valid values are ${valid})`;
|
|
3072
|
+
}
|
|
3073
|
+
|
|
2924
3074
|
// src/github/markerReconciliation.ts
|
|
2925
3075
|
function isAuthorized(author, p) {
|
|
2926
3076
|
const login2 = author.toLowerCase();
|
|
@@ -3266,11 +3416,11 @@ function fail(message) {
|
|
|
3266
3416
|
process.exit(1);
|
|
3267
3417
|
}
|
|
3268
3418
|
function parsePositiveInt(value, label) {
|
|
3269
|
-
const
|
|
3270
|
-
if (!/^\d+$/.test(
|
|
3419
|
+
const trimmed2 = value.trim();
|
|
3420
|
+
if (!/^\d+$/.test(trimmed2)) {
|
|
3271
3421
|
fail(`${label} must be a positive integer (got "${value}").`);
|
|
3272
3422
|
}
|
|
3273
|
-
const parsed = Number(
|
|
3423
|
+
const parsed = Number(trimmed2);
|
|
3274
3424
|
if (!Number.isSafeInteger(parsed) || parsed <= 0) {
|
|
3275
3425
|
fail(`${label} must be a positive integer within the safe range (got "${value}").`);
|
|
3276
3426
|
}
|
|
@@ -3304,13 +3454,13 @@ function resolveSettings(options) {
|
|
|
3304
3454
|
}
|
|
3305
3455
|
validateDoWorkConfig(config.doWork);
|
|
3306
3456
|
const doWork = config.doWork ?? {};
|
|
3307
|
-
let
|
|
3457
|
+
let withOption;
|
|
3308
3458
|
if (options.with !== void 0) {
|
|
3309
3459
|
const requested = options.with.toLowerCase();
|
|
3310
3460
|
if (requested !== "claude" && requested !== "codex") {
|
|
3311
3461
|
fail(`--with must be 'claude' or 'codex', got '${options.with}'.`);
|
|
3312
3462
|
}
|
|
3313
|
-
|
|
3463
|
+
withOption = requested;
|
|
3314
3464
|
}
|
|
3315
3465
|
if (options.dryRun !== true) {
|
|
3316
3466
|
checkAuthenticatedIdentity(agentUser, allowedUsers);
|
|
@@ -3318,9 +3468,16 @@ function resolveSettings(options) {
|
|
|
3318
3468
|
return {
|
|
3319
3469
|
baseBranch: doWork.baseBranch ?? DEFAULT_DO_WORK.baseBranch,
|
|
3320
3470
|
protectedBranches: doWork.protectedBranches ?? DEFAULT_DO_WORK.protectedBranches,
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3471
|
+
withOption,
|
|
3472
|
+
modelOption: options.model,
|
|
3473
|
+
configExecutor: doWork.executor,
|
|
3474
|
+
configModels: doWork.models,
|
|
3475
|
+
// Rejected here rather than per item: an empty `--effort` is an operator
|
|
3476
|
+
// mistake on this invocation, not a property of any one work item. The
|
|
3477
|
+
// configured per-executor defaults are trimmed inside `resolveExecution`,
|
|
3478
|
+
// which is where the executor in use is finally known.
|
|
3479
|
+
effortOption: resolveEffortOption(options.effort),
|
|
3480
|
+
configEfforts: doWork.effort,
|
|
3324
3481
|
maxRuns: options.maxRuns !== void 0 ? parsePositiveInt(options.maxRuns, "--max-runs") : doWork.maxRunsPerTick ?? DEFAULT_DO_WORK.maxRunsPerTick,
|
|
3325
3482
|
lockStaleMinutes: doWork.lockStaleMinutes ?? DEFAULT_DO_WORK.lockStaleMinutes,
|
|
3326
3483
|
limit: parsePositiveInt(options.limit, "--limit"),
|
|
@@ -3353,7 +3510,30 @@ function checkAuthenticatedIdentity(agentUser, allowedUsers) {
|
|
|
3353
3510
|
`\`gh\` is authenticated as "${login2}" but agentUser is "${agentUser}". Comments posted under that identity are neither the agent's nor an authorized user's, so they are filtered out of the conversation: the answer boundary would never advance and the same message would start a run on every tick. Authenticate \`gh\` as the agent account (${agentUser}) in this environment, or correct \`agentUser\`.`
|
|
3354
3511
|
);
|
|
3355
3512
|
}
|
|
3356
|
-
function
|
|
3513
|
+
function resolveItemExecution(item, settings) {
|
|
3514
|
+
const trigger = triggeringMessage(item);
|
|
3515
|
+
return resolveExecution({
|
|
3516
|
+
directive: trigger === null ? { tool: void 0, model: void 0 } : parseRunDirective(trigger.body),
|
|
3517
|
+
withOption: settings.withOption,
|
|
3518
|
+
modelOption: settings.modelOption,
|
|
3519
|
+
configExecutor: settings.configExecutor,
|
|
3520
|
+
configModels: settings.configModels,
|
|
3521
|
+
effortOption: settings.effortOption,
|
|
3522
|
+
configEfforts: settings.configEfforts,
|
|
3523
|
+
defaultExecutor: DEFAULT_DO_WORK.executor
|
|
3524
|
+
});
|
|
3525
|
+
}
|
|
3526
|
+
function toExecution(resolved) {
|
|
3527
|
+
return {
|
|
3528
|
+
executor: resolved.executor,
|
|
3529
|
+
executorSource: resolved.executorSource,
|
|
3530
|
+
model: resolved.model,
|
|
3531
|
+
modelSource: resolved.modelSource,
|
|
3532
|
+
effort: resolved.effort,
|
|
3533
|
+
effortSource: resolved.effortSource
|
|
3534
|
+
};
|
|
3535
|
+
}
|
|
3536
|
+
function planRun(item, settings, execution) {
|
|
3357
3537
|
const prompt = composePrompt({
|
|
3358
3538
|
item,
|
|
3359
3539
|
repo: getRepoSlug(),
|
|
@@ -3361,16 +3541,20 @@ function planRun(item, settings) {
|
|
|
3361
3541
|
baseBranch: settings.baseBranch,
|
|
3362
3542
|
frame: settings.prompts[item.turn]
|
|
3363
3543
|
});
|
|
3364
|
-
const bin = resolveCommand(
|
|
3365
|
-
const args =
|
|
3544
|
+
const bin = resolveCommand(execution.executor === "codex" ? "codex" : "claude");
|
|
3545
|
+
const args = execution.executor === "codex" ? buildCodexArgs(prompt, { yolo: true, model: execution.model, effort: execution.effort }) : buildClaudeArgs(prompt, {
|
|
3546
|
+
yolo: true,
|
|
3547
|
+
verbose: true,
|
|
3548
|
+
model: execution.model,
|
|
3549
|
+
effort: execution.effort
|
|
3550
|
+
});
|
|
3366
3551
|
return { prompt, bin, args, command: [bin, ...args].map(shellQuote).join(" ") };
|
|
3367
3552
|
}
|
|
3368
|
-
function describePlannedRun(item, settings, run5) {
|
|
3553
|
+
function describePlannedRun(item, settings, run5, execution) {
|
|
3369
3554
|
const rule = "\u2500".repeat(72);
|
|
3370
3555
|
const branchAction = item.turn === "pr-work" ? " and fast-forward" : " and pull";
|
|
3371
3556
|
const assignment = item.needsAssignment ? `would assign to ${settings.participants.agentUser}` : "already assigned";
|
|
3372
3557
|
const markerTarget = item.turn === "pr-work" && item.pr ? `pull request #${String(item.pr.number)}` : `issue #${String(item.issue.number)}`;
|
|
3373
|
-
const modelNote = settings.model === void 0 ? " (no model override)" : ` \xB7 model ${settings.model}`;
|
|
3374
3558
|
const lines = [
|
|
3375
3559
|
rule,
|
|
3376
3560
|
`Issue #${String(item.issue.number)} \u2014 ${item.issue.title}`,
|
|
@@ -3380,7 +3564,7 @@ function describePlannedRun(item, settings, run5) {
|
|
|
3380
3564
|
` Branch ${item.branch} (would check out${branchAction})`,
|
|
3381
3565
|
` Assign ${assignment}`,
|
|
3382
3566
|
` Marker would post on ${markerTarget}`,
|
|
3383
|
-
` Executor ${
|
|
3567
|
+
` Executor ${describeExecution(execution)}`,
|
|
3384
3568
|
" Permissions bypassed (do-work always runs unattended)",
|
|
3385
3569
|
` Prompt ${String(run5.prompt.length)} chars \u2014 frame + assembled context`,
|
|
3386
3570
|
"",
|
|
@@ -3396,6 +3580,19 @@ function describePlannedRun(item, settings, run5) {
|
|
|
3396
3580
|
];
|
|
3397
3581
|
return lines.join("\n") + "\n";
|
|
3398
3582
|
}
|
|
3583
|
+
function describeRefusedRun(item, refusal) {
|
|
3584
|
+
const rule = "\u2500".repeat(72);
|
|
3585
|
+
return [
|
|
3586
|
+
rule,
|
|
3587
|
+
`Issue #${String(item.issue.number)} \u2014 ${item.issue.title}`,
|
|
3588
|
+
rule,
|
|
3589
|
+
` Turn ${item.turn}`,
|
|
3590
|
+
` Why ${item.reason}`,
|
|
3591
|
+
` Executor refused \u2014 ${refusal}`,
|
|
3592
|
+
" Command none; a real tick would post the working marker and then replace it with this refusal",
|
|
3593
|
+
""
|
|
3594
|
+
].join("\n") + "\n";
|
|
3595
|
+
}
|
|
3399
3596
|
function isPlainObject(value) {
|
|
3400
3597
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3401
3598
|
}
|
|
@@ -3427,6 +3624,7 @@ function validateDoWorkConfig(section) {
|
|
|
3427
3624
|
validateOptionalInt(section, "lockStaleMinutes", "doWork.lockStaleMinutes", 1, "a positive integer");
|
|
3428
3625
|
validateProtectedBranches(section["protectedBranches"]);
|
|
3429
3626
|
validateSettingContainer(section["models"], "models", ["claude", "codex"]);
|
|
3627
|
+
validateSettingContainer(section["effort"], "effort", ["claude", "codex"]);
|
|
3430
3628
|
validateSettingContainer(section["prompts"], "prompts", ["issueDiscuss", "prWork"]);
|
|
3431
3629
|
}
|
|
3432
3630
|
function validateProtectedBranches(value) {
|
|
@@ -3659,12 +3857,12 @@ function reportNoAnswer(item, marker, runError) {
|
|
|
3659
3857
|
}
|
|
3660
3858
|
return runError === null ? { outcome: "answered-no-reply", detail: "run finished but posted no answer", reason: "no-answer" } : { outcome: "failed", detail: `run failed: ${runError.message}`, reason: "no-answer" };
|
|
3661
3859
|
}
|
|
3662
|
-
async function invokeExecutor(prompt,
|
|
3663
|
-
if (
|
|
3664
|
-
await runCodex(prompt, { model:
|
|
3860
|
+
async function invokeExecutor(prompt, execution, silent) {
|
|
3861
|
+
if (execution.executor === "codex") {
|
|
3862
|
+
await runCodex(prompt, { model: execution.model, effort: execution.effort });
|
|
3665
3863
|
return;
|
|
3666
3864
|
}
|
|
3667
|
-
await runClaude(prompt, { model:
|
|
3865
|
+
await runClaude(prompt, { model: execution.model, effort: execution.effort, printSteps: !silent });
|
|
3668
3866
|
}
|
|
3669
3867
|
function repairIssueLink(item, baseBranch) {
|
|
3670
3868
|
try {
|
|
@@ -3696,6 +3894,18 @@ function repairIssueLink(item, baseBranch) {
|
|
|
3696
3894
|
return false;
|
|
3697
3895
|
}
|
|
3698
3896
|
}
|
|
3897
|
+
function refuseBeforeRun(base, marker, detail, markerText) {
|
|
3898
|
+
progress(` failed: ${detail}
|
|
3899
|
+
`);
|
|
3900
|
+
inFlightMarker = null;
|
|
3901
|
+
try {
|
|
3902
|
+
updateMarker(marker, markerText);
|
|
3903
|
+
} catch (err) {
|
|
3904
|
+
progress(` warning: could not update the marker comment: ${err.message}
|
|
3905
|
+
`);
|
|
3906
|
+
}
|
|
3907
|
+
return { ...base, outcome: "failed", detail };
|
|
3908
|
+
}
|
|
3699
3909
|
async function processItem(planned, settings, silent) {
|
|
3700
3910
|
progress(`
|
|
3701
3911
|
#${String(planned.issue.number)} ${planned.turn}: ${planned.reason}
|
|
@@ -3764,24 +3974,27 @@ async function processItem(planned, settings, silent) {
|
|
|
3764
3974
|
});
|
|
3765
3975
|
const oversized = describeOversizedPrompt(prompt);
|
|
3766
3976
|
if (oversized !== null) {
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
marker,
|
|
3774
|
-
`automata do-work: could not start a run because ${detail} Summarise the discussion in a new issue, or shorten the thread, and try again.`
|
|
3775
|
-
);
|
|
3776
|
-
} catch (err) {
|
|
3777
|
-
progress(` warning: could not update the marker comment: ${err.message}
|
|
3778
|
-
`);
|
|
3779
|
-
}
|
|
3780
|
-
return { ...base, outcome: "failed", detail };
|
|
3977
|
+
return refuseBeforeRun(
|
|
3978
|
+
base,
|
|
3979
|
+
marker,
|
|
3980
|
+
oversized,
|
|
3981
|
+
`automata do-work: could not start a run because ${oversized} Summarise the discussion in a new issue, or shorten the thread, and try again.`
|
|
3982
|
+
);
|
|
3781
3983
|
}
|
|
3984
|
+
const resolved = resolveItemExecution(item, settings);
|
|
3985
|
+
if (!resolved.ok) {
|
|
3986
|
+
const detail = describeInvalidTool(resolved.invalidTool);
|
|
3987
|
+
return refuseBeforeRun(
|
|
3988
|
+
base,
|
|
3989
|
+
marker,
|
|
3990
|
+
detail,
|
|
3991
|
+
`automata do-work: ${detail}. No run was started. Reply here with a corrected directive, or none at all, to have another attempt made.`
|
|
3992
|
+
);
|
|
3993
|
+
}
|
|
3994
|
+
const execution = toExecution(resolved);
|
|
3782
3995
|
let runError = null;
|
|
3783
3996
|
try {
|
|
3784
|
-
await invokeExecutor(prompt,
|
|
3997
|
+
await invokeExecutor(prompt, execution, silent);
|
|
3785
3998
|
} catch (err) {
|
|
3786
3999
|
runError = err;
|
|
3787
4000
|
}
|
|
@@ -3791,7 +4004,7 @@ async function processItem(planned, settings, silent) {
|
|
|
3791
4004
|
progress(` ${reconciled.detail}
|
|
3792
4005
|
`);
|
|
3793
4006
|
const outcome = adjustOutcome(reconciled, item, settings, buriedByNote);
|
|
3794
|
-
return { ...base, outcome: outcome.outcome, detail: outcome.detail, ranExecutor };
|
|
4007
|
+
return { ...base, outcome: outcome.outcome, detail: outcome.detail, ranExecutor, execution };
|
|
3795
4008
|
}
|
|
3796
4009
|
function describeOversizedPrompt(prompt) {
|
|
3797
4010
|
const MAX_PROMPT_BYTES = 96 * 1024;
|
|
@@ -3827,13 +4040,33 @@ function summarize(reports) {
|
|
|
3827
4040
|
return;
|
|
3828
4041
|
}
|
|
3829
4042
|
for (const report of reports) {
|
|
3830
|
-
|
|
4043
|
+
const ran = report.execution === void 0 ? "" : ` \xB7 ${describeExecution(report.execution)}`;
|
|
4044
|
+
out(` #${String(report.issue)} ${report.turn ?? "-"} ${report.outcome} \u2014 ${report.detail}${ran}
|
|
3831
4045
|
`);
|
|
3832
4046
|
}
|
|
3833
4047
|
}
|
|
4048
|
+
function toItemJson(report) {
|
|
4049
|
+
return {
|
|
4050
|
+
issue: report.issue,
|
|
4051
|
+
title: report.title,
|
|
4052
|
+
turn: report.turn,
|
|
4053
|
+
outcome: report.outcome,
|
|
4054
|
+
detail: report.detail,
|
|
4055
|
+
ranExecutor: report.ranExecutor ?? false,
|
|
4056
|
+
executor: report.execution?.executor ?? null,
|
|
4057
|
+
model: report.execution?.model ?? null,
|
|
4058
|
+
effort: report.execution?.effort ?? null,
|
|
4059
|
+
executorSource: report.execution?.executorSource ?? null,
|
|
4060
|
+
modelSource: report.execution?.modelSource ?? null,
|
|
4061
|
+
effortSource: report.execution?.effortSource ?? null
|
|
4062
|
+
};
|
|
4063
|
+
}
|
|
3834
4064
|
var doWorkCommand = new Command6("do-work").description(
|
|
3835
4065
|
"Run one tick of the autonomous loop: find the issues whose newest authorized message the agent has not answered, and answer them"
|
|
3836
|
-
).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(
|
|
4066
|
+
).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(
|
|
4067
|
+
"--effort <level>",
|
|
4068
|
+
"Reasoning effort to pass to the executor, overriding the configured default for it"
|
|
4069
|
+
).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(
|
|
3837
4070
|
"--dry-run",
|
|
3838
4071
|
"Print the work plan, plus a summary and the exact command that would be launched for each item, and exit without changing anything"
|
|
3839
4072
|
).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) => {
|
|
@@ -3976,31 +4209,69 @@ ${describePlan(decisions)}`;
|
|
|
3976
4209
|
const degraded = reports.some((report) => report.outcome !== "answered");
|
|
3977
4210
|
const exitCode = degraded ? 2 : 0;
|
|
3978
4211
|
if (options.json) {
|
|
3979
|
-
out(
|
|
4212
|
+
out(
|
|
4213
|
+
JSON.stringify(
|
|
4214
|
+
{ dryRun: false, plan: decisions.map(toPlanJson), items: reports.map(toItemJson), exitCode },
|
|
4215
|
+
null,
|
|
4216
|
+
2
|
|
4217
|
+
) + "\n"
|
|
4218
|
+
);
|
|
3980
4219
|
} else {
|
|
3981
4220
|
summarize(reports);
|
|
3982
4221
|
}
|
|
3983
4222
|
return exitCode;
|
|
3984
4223
|
}
|
|
4224
|
+
function toRunJson(entry) {
|
|
4225
|
+
if (entry.kind === "refused") {
|
|
4226
|
+
return {
|
|
4227
|
+
issue: entry.item.issue.number,
|
|
4228
|
+
turn: entry.item.turn,
|
|
4229
|
+
executor: null,
|
|
4230
|
+
model: null,
|
|
4231
|
+
effort: null,
|
|
4232
|
+
executorSource: null,
|
|
4233
|
+
modelSource: null,
|
|
4234
|
+
effortSource: null,
|
|
4235
|
+
refusal: entry.refusal,
|
|
4236
|
+
bin: null,
|
|
4237
|
+
args: null,
|
|
4238
|
+
command: null,
|
|
4239
|
+
prompt: null
|
|
4240
|
+
};
|
|
4241
|
+
}
|
|
4242
|
+
return {
|
|
4243
|
+
issue: entry.item.issue.number,
|
|
4244
|
+
turn: entry.item.turn,
|
|
4245
|
+
executor: entry.execution.executor,
|
|
4246
|
+
model: entry.execution.model ?? null,
|
|
4247
|
+
effort: entry.execution.effort ?? null,
|
|
4248
|
+
executorSource: entry.execution.executorSource,
|
|
4249
|
+
modelSource: entry.execution.modelSource,
|
|
4250
|
+
effortSource: entry.execution.effortSource,
|
|
4251
|
+
refusal: null,
|
|
4252
|
+
bin: entry.run.bin,
|
|
4253
|
+
args: entry.run.args,
|
|
4254
|
+
command: entry.run.command,
|
|
4255
|
+
prompt: entry.run.prompt
|
|
4256
|
+
};
|
|
4257
|
+
}
|
|
3985
4258
|
function reportDryRun(items, decisions, settings, options) {
|
|
3986
4259
|
const describable = settings.maxRuns > 0 ? items.slice(0, settings.maxRuns) : items;
|
|
3987
|
-
const planned = describable.map((item) =>
|
|
4260
|
+
const planned = describable.map((item) => {
|
|
4261
|
+
const resolved = resolveItemExecution(item, settings);
|
|
4262
|
+
if (!resolved.ok) {
|
|
4263
|
+
return { kind: "refused", item, refusal: describeInvalidTool(resolved.invalidTool) };
|
|
4264
|
+
}
|
|
4265
|
+
const execution = toExecution(resolved);
|
|
4266
|
+
return { kind: "run", item, execution, run: planRun(item, settings, execution) };
|
|
4267
|
+
});
|
|
3988
4268
|
if (options.json) {
|
|
3989
4269
|
out(
|
|
3990
4270
|
JSON.stringify(
|
|
3991
4271
|
{
|
|
3992
4272
|
dryRun: true,
|
|
3993
4273
|
plan: decisions.map(toPlanJson),
|
|
3994
|
-
runs: planned.map(
|
|
3995
|
-
issue: describable[index].issue.number,
|
|
3996
|
-
turn: describable[index].turn,
|
|
3997
|
-
executor: settings.executor,
|
|
3998
|
-
model: settings.model ?? null,
|
|
3999
|
-
bin: run5.bin,
|
|
4000
|
-
args: run5.args,
|
|
4001
|
-
command: run5.command,
|
|
4002
|
-
prompt: run5.prompt
|
|
4003
|
-
}))
|
|
4274
|
+
runs: planned.map(toRunJson)
|
|
4004
4275
|
},
|
|
4005
4276
|
null,
|
|
4006
4277
|
2
|
|
@@ -4008,8 +4279,10 @@ function reportDryRun(items, decisions, settings, options) {
|
|
|
4008
4279
|
);
|
|
4009
4280
|
return;
|
|
4010
4281
|
}
|
|
4011
|
-
for (const
|
|
4012
|
-
out(
|
|
4282
|
+
for (const entry of planned) {
|
|
4283
|
+
out(
|
|
4284
|
+
"\n" + (entry.kind === "refused" ? describeRefusedRun(entry.item, entry.refusal) : describePlannedRun(entry.item, settings, entry.run, entry.execution))
|
|
4285
|
+
);
|
|
4013
4286
|
}
|
|
4014
4287
|
const deferred = items.length - describable.length;
|
|
4015
4288
|
if (deferred > 0) {
|