next-issue 2.17.0 → 2.19.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 CHANGED
@@ -304,12 +304,13 @@ Three levels of console output:
304
304
  - Node 24 or later
305
305
  - `gh`, authenticated with write access to the repository
306
306
  - pi, logged in to a model provider. The agents use the model, the thinking
307
- level and the extensions from your pi settings, except the `pi-next-issue`
308
- extension. The implementer and the fixer get the default tools of your pi
309
- settings, the `grep`, `find` and `ls` tools and the tools of your
310
- extensions. The reviewer gets all of them except `edit` and `write`. The
311
- agents run with `GIT_OPTIONAL_LOCKS=0`, so a `git status` from an agent or
312
- an extension does not hold the index lock while the harness commits.
307
+ level, the extensions and the skills from your pi settings, except the
308
+ extension and the skills of `pi-next-issue`. The implementer and the fixer
309
+ get the default tools of your pi settings, the `grep`, `find` and `ls` tools
310
+ and the tools of your extensions. The reviewer gets all of them except
311
+ `edit` and `write`. The agents run with `GIT_OPTIONAL_LOCKS=0`, so a
312
+ `git status` from an agent or an extension does not hold the index lock
313
+ while the harness commits.
313
314
 
314
315
  ## Configuration
315
316
 
package/dist/agents.mjs CHANGED
@@ -9,7 +9,7 @@ const CODING = {
9
9
  tools: ['+grep', '+find', '+ls'],
10
10
  };
11
11
  export { CODING, REVIEWING };
12
- export function isOwnExtension(path) {
12
+ export function isOwnResource(path) {
13
13
  return path.split(sep).includes('pi-next-issue');
14
14
  }
15
15
  export async function runAgent(recorder, request) {
@@ -24,7 +24,11 @@ export async function runAgent(recorder, request) {
24
24
  agentDir,
25
25
  extensionsOverride: (base) => ({
26
26
  ...base,
27
- extensions: base.extensions.filter((extension) => !isOwnExtension(extension.resolvedPath)),
27
+ extensions: base.extensions.filter((extension) => !isOwnResource(extension.resolvedPath)),
28
+ }),
29
+ skillsOverride: (base) => ({
30
+ ...base,
31
+ skills: base.skills.filter((skill) => !isOwnResource(skill.filePath)),
28
32
  }),
29
33
  });
30
34
  await resourceLoader.reload();
package/dist/fix.mjs CHANGED
@@ -4,16 +4,19 @@ import { message } from './observe.mjs';
4
4
  import { fixPrompt, hookPrompt, parseCommitSubject, parseUnrelated, } from './prompts.mjs';
5
5
  import { writeState } from './state.mjs';
6
6
  export async function fixRound(job, reason, detail, earlier, kind, force = false, from) {
7
- const { context, issue, log, worktree, branch } = job;
8
- const { ports } = context;
7
+ const { context, issue, log, state, worktree, branch } = job;
8
+ const { ports, config } = context;
9
9
  const before = from ?? (await ports.revision(worktree));
10
- const result = await log.step('fix', { reason }, () => ports.runAgent(log, {
10
+ const budget = kind === 'review'
11
+ ? { round: state.reviewRounds, limit: config.maxReviewRounds }
12
+ : { round: state.ciFixes, limit: config.maxCiFixes };
13
+ const result = await log.step('fix', { reason, cause: kind, ...budget }, () => ports.runAgent(log, {
11
14
  name: 'fixer',
12
15
  cwd: worktree,
13
16
  prompt: fixPrompt(issue, reason, detail, earlier, kind, job.reply),
14
17
  profile: CODING,
15
- model: context.config.models.fixer,
16
- timeoutMs: context.config.agentTimeoutMinutes * 60_000,
18
+ model: config.models.fixer,
19
+ timeoutMs: config.agentTimeoutMinutes * 60_000,
17
20
  }));
18
21
  const unrelated = parseUnrelated(result.text);
19
22
  if (unrelated !== undefined) {
@@ -33,7 +36,7 @@ export async function fixRound(job, reason, detail, earlier, kind, force = false
33
36
  log.event('commit.empty', { reason }, 'quiet');
34
37
  return { committed: false };
35
38
  }
36
- await ports.push(worktree, context.config.remote, branch, force);
39
+ await ports.push(worktree, config.remote, branch, force);
37
40
  return { committed: true };
38
41
  }
39
42
  export async function commitWork(job, before, subject, role) {
@@ -59,7 +62,7 @@ export async function commitWork(job, before, subject, role) {
59
62
  await writeState(repo, state);
60
63
  const output = message(error).slice(-config.logMaxChars);
61
64
  log.event('commit.fail', { role, output }, 'quiet');
62
- const fix = await log.step('commit-fix', { role }, () => ports.runAgent(log, {
65
+ const fix = await log.step('commit-fix', { role, round: state.commitFixes, limit: config.maxCommitFixes }, () => ports.runAgent(log, {
63
66
  name: role,
64
67
  cwd: worktree,
65
68
  prompt: hookPrompt(issue, comments, role, output),
package/dist/progress.mjs CHANGED
@@ -9,6 +9,20 @@ function toolLine(record) {
9
9
  const line = typeof command === 'string' ? `${tool} ${command.split('\n')[0]}` : tool;
10
10
  return line.length > TOOL_LINE_MAX ? `${line.slice(0, TOOL_LINE_MAX - 1)}…` : line;
11
11
  }
12
+ function stageOf(step, record) {
13
+ const cause = record['cause'];
14
+ const name = step === 'fix' && typeof cause === 'string'
15
+ ? `${cause} fix`
16
+ : step === 'commit-fix'
17
+ ? 'commit fix'
18
+ : step;
19
+ const round = record['round'];
20
+ const limit = record['limit'];
21
+ if (typeof round !== 'number') {
22
+ return name;
23
+ }
24
+ return typeof limit === 'number' ? `${name} ${round}/${limit}` : `${name} ${round}`;
25
+ }
12
26
  function change(progress, issue, update) {
13
27
  return {
14
28
  ...progress,
@@ -45,16 +59,15 @@ export function applyEvent(progress, record) {
45
59
  const issues = progress.issues.filter((current) => current.issue !== issue);
46
60
  return { ...progress, issues, handled };
47
61
  }
62
+ if (kind === 'issues') {
63
+ return { ...progress, open: Number(record['open']) };
64
+ }
48
65
  if (kind === 'issues.fail') {
49
66
  return { ...progress, error: String(record['error']) };
50
67
  }
51
68
  if (kind.endsWith('.start') && !NOT_STAGES.has(kind)) {
52
- const stage = kind.slice(0, -'.start'.length);
53
- const round = record['round'];
54
- return change(progress, issue, (current) => ({
55
- ...current,
56
- stage: typeof round === 'number' ? `${stage} ${round}` : stage,
57
- }));
69
+ const stage = stageOf(kind.slice(0, -'.start'.length), record);
70
+ return change(progress, issue, (current) => ({ ...current, stage }));
58
71
  }
59
72
  return progress;
60
73
  }
package/dist/review.mjs CHANGED
@@ -22,6 +22,7 @@ export async function reviewRound(job) {
22
22
  const patch = capDiff(full, config.diffMaxChars);
23
23
  const fields = {
24
24
  round: state.reviewRounds,
25
+ limit: config.maxReviewRounds,
25
26
  diffChars: full.length,
26
27
  cut: full.length > config.diffMaxChars,
27
28
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-issue",
3
- "version": "2.17.0",
3
+ "version": "2.19.0",
4
4
  "description": "Autonomous issue harness driven by the pi SDK",
5
5
  "keywords": [
6
6
  "github",