next-issue 0.19.0 → 0.20.0
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 +5 -1
- package/dist/config.mjs +2 -0
- package/dist/pipeline.mjs +32 -8
- package/dist/prompts.mjs +10 -0
- package/dist/state.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,7 +33,9 @@ For each open issue, oldest first:
|
|
|
33
33
|
local branch that fell behind the remote is fast-forwarded. A fast-forward
|
|
34
34
|
of the base that is not safe leaves the local base branch as it is and
|
|
35
35
|
writes a `base.behind` event. A new branch then starts from the remote base.
|
|
36
|
-
4. Let the implementer agent do the work, then commit and push.
|
|
36
|
+
4. Let the implementer agent do the work, then commit and push. When a git
|
|
37
|
+
hook stops a commit, the agent that made the change gets the hook output,
|
|
38
|
+
fixes it, and the harness commits again. The budget is `maxCommitFixes`.
|
|
37
39
|
5. Open a draft pull request that closes the issue.
|
|
38
40
|
6. Wait for the checks, once the pull request shows the pushed commit. A
|
|
39
41
|
pending state waits again, up to `checkTimeoutMinutes`. A read that fails
|
|
@@ -238,6 +240,7 @@ does not know all stop the run, so a typo cannot pass without a word.
|
|
|
238
240
|
"remote": "origin",
|
|
239
241
|
"issueLimit": 100,
|
|
240
242
|
"maxCiFixes": 5,
|
|
243
|
+
"maxCommitFixes": 10,
|
|
241
244
|
"maxReviewRounds": 5,
|
|
242
245
|
"checkIntervalSeconds": 15,
|
|
243
246
|
"checkGraceSeconds": 60,
|
|
@@ -271,6 +274,7 @@ does not know all stop the run, so a typo cannot pass without a word.
|
|
|
271
274
|
| `remote` | `origin` | The git remote for the repository and the branches |
|
|
272
275
|
| `issueLimit` | `100` | The maximum number of open issues to read |
|
|
273
276
|
| `maxCiFixes` | `5` | The budget for check fixes per issue |
|
|
277
|
+
| `maxCommitFixes` | `10` | The budget for fixes of a commit that a git hook stopped, per issue |
|
|
274
278
|
| `maxReviewRounds` | `5` | The budget for review rounds per issue |
|
|
275
279
|
| `checkIntervalSeconds` | `15` | The time between two check states |
|
|
276
280
|
| `checkGraceSeconds` | `60` | The time to wait for the first check to show |
|
package/dist/config.mjs
CHANGED
|
@@ -7,6 +7,7 @@ const DEFAULTS = {
|
|
|
7
7
|
remote: "origin",
|
|
8
8
|
issueLimit: 100,
|
|
9
9
|
maxCiFixes: 5,
|
|
10
|
+
maxCommitFixes: 10,
|
|
10
11
|
maxReviewRounds: 5,
|
|
11
12
|
checkIntervalSeconds: 15,
|
|
12
13
|
checkGraceSeconds: 60,
|
|
@@ -35,6 +36,7 @@ const FIELDS = [...Object.keys(DEFAULTS), "setupCommand"];
|
|
|
35
36
|
const WHOLE_FIELDS = [
|
|
36
37
|
["issueLimit", 1],
|
|
37
38
|
["maxCiFixes", 0],
|
|
39
|
+
["maxCommitFixes", 0],
|
|
38
40
|
["maxReviewRounds", 1],
|
|
39
41
|
["checkIntervalSeconds", 1],
|
|
40
42
|
["checkGraceSeconds", 0],
|
package/dist/pipeline.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { run } from "./exec.mjs";
|
|
|
4
4
|
import { addWorktree, branchName, commitAll, deleteBranch, diff, discardChanges, hasWorktree, isDirty, push, removeWorktree, resetToBase, revision, worktreePath, } from "./git.mjs";
|
|
5
5
|
import { commentOnPr, createPr, failedBaseLogs, failedCheckLogs, findPr, issueComments, markPrReady, mergeCommit, mergePr, openBlockers, reopenIssue, setLabel, waitForBaseChecks, waitForChecks, } from "./github.mjs";
|
|
6
6
|
import { message } from "./observe.mjs";
|
|
7
|
-
import { capDiff, fixPrompt, implementPrompt, parseCommitSubject, parseUnrelated, reviewPrompt, } from "./prompts.mjs";
|
|
7
|
+
import { capDiff, fixPrompt, hookPrompt, implementPrompt, parseCommitSubject, parseUnrelated, reviewPrompt, } from "./prompts.mjs";
|
|
8
8
|
import { detectSetupCommand } from "./setup.mjs";
|
|
9
9
|
import { dropState, readState, resetState, writeState, } from "./state.mjs";
|
|
10
10
|
import { blockingFindings, fingerprint, formatFindings, formatVerdict, openFindings, readVerdict, VERDICT_SCHEMA, } from "./verdict.mjs";
|
|
@@ -166,7 +166,7 @@ async function work(context, issue, log, state) {
|
|
|
166
166
|
timeoutMs: config.agentTimeoutMinutes * 60_000,
|
|
167
167
|
}));
|
|
168
168
|
const subject = parseCommitSubject(result.text, `fix: resolve issue #${issue.number}`);
|
|
169
|
-
if (!(await commitWork(job, before, subject))) {
|
|
169
|
+
if (!(await commitWork(job, before, subject, "implementer"))) {
|
|
170
170
|
await escalate(job, "The implementer produced no change.");
|
|
171
171
|
return finish(job, "needs-human", "no implementation commit");
|
|
172
172
|
}
|
|
@@ -450,19 +450,43 @@ async function fixRound(job, reason, detail, earlier, kind) {
|
|
|
450
450
|
return { committed: false, unrelated, stray };
|
|
451
451
|
}
|
|
452
452
|
const subject = parseCommitSubject(result.text, `fix: address feedback on issue #${issue.number}`);
|
|
453
|
-
if (!(await commitWork(job, before, subject))) {
|
|
453
|
+
if (!(await commitWork(job, before, subject, "fixer"))) {
|
|
454
454
|
log.event("commit.empty", { reason }, "quiet");
|
|
455
455
|
return { committed: false };
|
|
456
456
|
}
|
|
457
457
|
await ports.push(worktree, context.config.remote, branch);
|
|
458
458
|
return { committed: true };
|
|
459
459
|
}
|
|
460
|
-
async function commitWork(job, before, subject) {
|
|
461
|
-
const { context, issue, log, worktree } = job;
|
|
462
|
-
|
|
463
|
-
|
|
460
|
+
async function commitWork(job, before, subject, role) {
|
|
461
|
+
const { context, issue, log, state, worktree, comments } = job;
|
|
462
|
+
const { repo, config, ports } = context;
|
|
463
|
+
for (;;) {
|
|
464
|
+
try {
|
|
465
|
+
if (await ports.commitAll(worktree, `${subject}\n\nRefs #${issue.number}`)) {
|
|
466
|
+
log.event("commit", { subject });
|
|
467
|
+
}
|
|
468
|
+
return (await ports.revision(worktree)) !== before;
|
|
469
|
+
}
|
|
470
|
+
catch (error) {
|
|
471
|
+
const used = state.commitFixes ?? 0;
|
|
472
|
+
if (used >= config.maxCommitFixes) {
|
|
473
|
+
throw error;
|
|
474
|
+
}
|
|
475
|
+
state.commitFixes = used + 1;
|
|
476
|
+
await writeState(repo, state);
|
|
477
|
+
const output = message(error).slice(-config.logMaxChars);
|
|
478
|
+
log.event("commit.fail", { role, output }, "quiet");
|
|
479
|
+
await log.step("commit-fix", { role }, () => ports.runAgent(log, {
|
|
480
|
+
name: role,
|
|
481
|
+
cwd: worktree,
|
|
482
|
+
prompt: hookPrompt(issue, comments, role, output),
|
|
483
|
+
profile: CODING,
|
|
484
|
+
model: config.models[role],
|
|
485
|
+
agent: config.agent,
|
|
486
|
+
timeoutMs: config.agentTimeoutMinutes * 60_000,
|
|
487
|
+
}));
|
|
488
|
+
}
|
|
464
489
|
}
|
|
465
|
-
return (await context.ports.revision(worktree)) !== before;
|
|
466
490
|
}
|
|
467
491
|
async function cleanUp(context, log, issue) {
|
|
468
492
|
const path = worktreePath(context.repo, issue);
|
package/dist/prompts.mjs
CHANGED
|
@@ -21,6 +21,16 @@ export function implementPrompt(issue, comments) {
|
|
|
21
21
|
COMMIT_RULES,
|
|
22
22
|
].join("\n\n");
|
|
23
23
|
}
|
|
24
|
+
export function hookPrompt(issue, comments, role, output) {
|
|
25
|
+
return [
|
|
26
|
+
`You are the ${role}. The harness could not commit your work for the issue below, because a git hook failed.`,
|
|
27
|
+
issueBlock(issue, comments),
|
|
28
|
+
`## Hook output\n${output}`,
|
|
29
|
+
"## Rules",
|
|
30
|
+
"Fix what the hook reports and keep the rest of the work in this worktree. Do not skip or weaken the hook.",
|
|
31
|
+
COMMIT_RULES,
|
|
32
|
+
].join("\n\n");
|
|
33
|
+
}
|
|
24
34
|
export function capDiff(diff, maxChars) {
|
|
25
35
|
return diff.length <= maxChars
|
|
26
36
|
? diff
|
package/dist/state.mjs
CHANGED
|
@@ -16,7 +16,7 @@ export async function readState(repo, issue) {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
export function resetState(state) {
|
|
19
|
-
return { ...state, ciFixes: 0, reviewRounds: 0, reviewLog: [] };
|
|
19
|
+
return { ...state, ciFixes: 0, commitFixes: 0, reviewRounds: 0, reviewLog: [] };
|
|
20
20
|
}
|
|
21
21
|
export async function writeState(repo, state) {
|
|
22
22
|
await mkdir(dir(repo), { recursive: true });
|