next-issue 2.5.0 → 2.5.2

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
@@ -64,8 +64,9 @@ and exits with code 1. For each issue:
64
64
  reviewer reads files and runs commands in the worktree, so it can prove a
65
65
  finding with the tests or the type check. The harness then throws away every
66
66
  change that the reviewer left, so no such change can reach a later commit.
67
- The reviewer returns its verdict with a `submit_result` tool: a summary and one entry per
68
- finding, each rated `blocking` or `minor`. The findings are the only ground
67
+ The reviewer ends its last message with its verdict as a json block: a
68
+ summary and one entry per finding, each rated `blocking` or `minor`. A
69
+ reply without a valid block hands the issue to a person. The findings are the only ground
69
70
  truth. The reviewer gives no approve or reject field, because such a field
70
71
  can contradict the findings. The harness puts the result on the pull
71
72
  request.
@@ -246,9 +247,9 @@ Three levels of console output:
246
247
  - `gh`, authenticated with write access to the repository
247
248
  - pi, logged in to a model provider. The agents use the model, the thinking
248
249
  level and the extensions from your pi settings, except the `pi-next-issue`
249
- extension. The implementer and the fixer get the `read`, `bash`, `edit`,
250
- `write`, `grep`, `find` and `ls` tools, and the reviewer gets all of them
251
- except `edit` and `write`.
250
+ extension. The implementer and the fixer get the default tools of your pi
251
+ settings, the `grep`, `find` and `ls` tools and the tools of your
252
+ extensions. The reviewer gets all of them except `edit` and `write`.
252
253
 
253
254
  ## Configuration
254
255
 
package/dist/agents.mjs CHANGED
@@ -1,15 +1,14 @@
1
1
  import { join, sep } from 'node:path';
2
- import { createAgentSession, DefaultResourceLoader, defineTool, getAgentDir, ModelRuntime, SessionManager, } from '@earendil-works/pi-coding-agent';
2
+ import { createAgentSession, DefaultResourceLoader, getAgentDir, ModelRuntime, SessionManager, } from '@earendil-works/pi-coding-agent';
3
3
  import { onStop } from './exec.mjs';
4
4
  import { emptyUsage } from './observe.mjs';
5
5
  const REVIEWING = {
6
- tools: ['read', 'bash', 'grep', 'find', 'ls'],
6
+ tools: ['+grep', '+find', '+ls', '-edit', '-write'],
7
7
  };
8
8
  const CODING = {
9
- tools: ['read', 'bash', 'edit', 'write', 'grep', 'find', 'ls'],
9
+ tools: ['+grep', '+find', '+ls'],
10
10
  };
11
11
  export { CODING, REVIEWING };
12
- export const SUBMIT_TOOL = 'submit_result';
13
12
  export function isOwnExtension(path) {
14
13
  return path.split(sep).includes('pi-next-issue');
15
14
  }
@@ -19,24 +18,6 @@ export async function runAgent(recorder, request) {
19
18
  const agentDir = runtime.agentDir ?? getAgentDir();
20
19
  const started = Date.now();
21
20
  log.event('agent.start', { model: request.model, timeoutMs: request.timeoutMs }, 'normal');
22
- let structured;
23
- const customTools = [];
24
- if (request.outputSchema !== undefined) {
25
- customTools.push(defineTool({
26
- name: SUBMIT_TOOL,
27
- label: 'Submit result',
28
- description: 'Give the result. Call it once, as your last action.',
29
- parameters: request.outputSchema,
30
- async execute(_toolCallId, params) {
31
- structured = params;
32
- return {
33
- content: [{ type: 'text', text: 'Result saved.' }],
34
- details: {},
35
- terminate: true,
36
- };
37
- },
38
- }));
39
- }
40
21
  const resourceLoader = new DefaultResourceLoader({
41
22
  cwd: request.cwd,
42
23
  agentDir,
@@ -57,8 +38,7 @@ export async function runAgent(recorder, request) {
57
38
  agentDir,
58
39
  resourceLoader,
59
40
  sessionManager: SessionManager.create(request.cwd, recorder.sessionDir),
60
- tools: [...request.profile.tools, ...customTools.map((tool) => tool.name)],
61
- customTools,
41
+ tools: request.profile.tools,
62
42
  modelRuntime,
63
43
  ...(runtime.settingsManager === undefined ? {} : { settingsManager: runtime.settingsManager }),
64
44
  ...(model === undefined ? {} : { model }),
@@ -105,7 +85,6 @@ export async function runAgent(recorder, request) {
105
85
  costUsd: stats.cost,
106
86
  model: name,
107
87
  sessionId: stats.sessionId,
108
- structured,
109
88
  };
110
89
  }
111
90
  finally {
package/dist/pipeline.mjs CHANGED
@@ -7,7 +7,7 @@ import { message } from './observe.mjs';
7
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, savedIssues, writeState, } from './state.mjs';
10
- import { blockingFindings, fingerprint, formatFindings, formatVerdict, openFindings, readVerdict, VERDICT_SCHEMA, } from './verdict.mjs';
10
+ import { blockingFindings, fingerprint, formatFindings, formatVerdict, openFindings, parseVerdict, } from './verdict.mjs';
11
11
  export const PORTS = {
12
12
  addWorktree,
13
13
  commentOnIssue,
@@ -362,12 +362,11 @@ async function reviewRound(job) {
362
362
  profile: REVIEWING,
363
363
  model: config.models.reviewer,
364
364
  timeoutMs: config.agentTimeoutMinutes * 60_000,
365
- outputSchema: VERDICT_SCHEMA,
366
365
  }));
367
366
  if (await ports.discardChanges(worktree, before)) {
368
367
  log.event('review.discard', { round: state.reviewRounds }, 'quiet');
369
368
  }
370
- const verdict = readVerdict(review.structured);
369
+ const verdict = parseVerdict(review.text);
371
370
  if (verdict === undefined) {
372
371
  await escalate(job, 'The reviewer gave no verdict.');
373
372
  return { done: true, report: await finish(job, 'needs-human', 'no verdict') };
package/dist/prompts.mjs CHANGED
@@ -60,7 +60,8 @@ export function reviewPrompt(issue, comments, diff, earlier) {
60
60
  'The harness discards each change that you make, so a change is only lost work.',
61
61
  scope,
62
62
  'Mark a finding blocking only for a wrong result, a missing part of the issue, a regression or a broken convention.',
63
- 'Give the result with the submit_result tool: a short summary and one entry per finding. The findings decide the outcome, so a problem that you leave out counts as no problem.',
63
+ 'End your final message with the result as a json block in a ```json fence, in the form {"summary": "<short summary>", "findings": [{"severity": "blocking" or "minor", "detail": "<what is wrong and where>"}]}.',
64
+ 'Give one entry per finding, and an empty list when nothing is wrong. The findings decide the outcome, so a problem that you leave out counts as no problem.',
64
65
  ].join('\n\n');
65
66
  }
66
67
  const UNRELATED_RULE = [
package/dist/verdict.mjs CHANGED
@@ -1,26 +1,3 @@
1
- export const VERDICT_SCHEMA = {
2
- type: 'object',
3
- properties: {
4
- summary: { type: 'string', description: 'Short summary of the review' },
5
- findings: {
6
- type: 'array',
7
- description: 'One entry per problem. Empty when nothing is wrong.',
8
- items: {
9
- type: 'object',
10
- properties: {
11
- severity: {
12
- type: 'string',
13
- enum: ['blocking', 'minor'],
14
- description: 'blocking for a wrong result, a missing part of the issue, a regression or a broken convention; minor for taste and style',
15
- },
16
- detail: { type: 'string', description: 'What is wrong and where' },
17
- },
18
- required: ['severity', 'detail'],
19
- },
20
- },
21
- },
22
- required: ['summary', 'findings'],
23
- };
24
1
  const SEVERITIES = ['blocking', 'minor'];
25
2
  export function readVerdict(value) {
26
3
  if (typeof value !== 'object' || value === null) {
@@ -45,6 +22,19 @@ export function readVerdict(value) {
45
22
  }
46
23
  return { summary, findings: checked };
47
24
  }
25
+ const JSON_BLOCK = /```json[^\n]*\n([\s\S]*?)```/g;
26
+ export function parseVerdict(text) {
27
+ const last = [...text.matchAll(JSON_BLOCK)].at(-1);
28
+ if (last === undefined) {
29
+ return undefined;
30
+ }
31
+ try {
32
+ return readVerdict(JSON.parse(last[1]));
33
+ }
34
+ catch {
35
+ return undefined;
36
+ }
37
+ }
48
38
  export function blockingFindings(verdict) {
49
39
  return verdict.findings.filter((finding) => finding.severity === 'blocking');
50
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-issue",
3
- "version": "2.5.0",
3
+ "version": "2.5.2",
4
4
  "description": "Autonomous issue harness driven by the pi SDK",
5
5
  "keywords": [
6
6
  "github",