@ravshansbox/pi-next-issue 1.1.0 → 1.2.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 +12 -14
- package/index.ts +26 -46
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,13 +10,11 @@ It needs `git` and an authenticated [`gh`](https://cli.github.com/). It works wi
|
|
|
10
10
|
|
|
11
11
|
## Use
|
|
12
12
|
|
|
13
|
-
Run
|
|
13
|
+
Run this in pi, inside a git repository:
|
|
14
14
|
|
|
15
|
-
- `/next-issue
|
|
16
|
-
- `/next-issue stop` stops the run. The issue keeps its saved state, and the next run resumes it.
|
|
17
|
-
- `/next-issue status` shows where the run is.
|
|
15
|
+
- `/next-issue` starts a run when none is going, and stops the run when one is going or stopping. A stopped issue keeps its saved state, and the next run resumes it.
|
|
18
16
|
|
|
19
|
-
The footer shows the issue, the stage, the number of tool calls and the time spent on the issue, for example `next-issue #12 · review 1/3 · 37 tools · 4m12s`. A line above the editor shows the last tool call. In the terminal, both use the dim colour of the theme, like the rest of the footer. A note appears when an issue is merged or needs a human.
|
|
17
|
+
The footer shows the issue, the stage, the number of tool calls and the time spent on the issue, for example `next-issue #12 · review 1/3 · 37 tools · 4m12s`. After a run ends, it keeps the last summary. A line above the editor shows the last tool call. In the terminal, both use the dim colour of the theme, like the rest of the footer. A note appears when an issue is merged or needs a human.
|
|
20
18
|
|
|
21
19
|
The run does not belong to a session: it goes on after `/new`, `/resume`, a fork or a reload, and the new session shows it. It stops when you quit pi.
|
|
22
20
|
|
|
@@ -28,7 +26,7 @@ Each loop fetches the 200 oldest open issues with the `status:todo` label and no
|
|
|
28
26
|
2. Add a git worktree at `.worktrees/<n>` on the `<n>` branch, from the remote default branch.
|
|
29
27
|
3. The implementer agent does the work. The extension commits it, with the subject from a `commit:` line in the reply of the agent, and pushes. When a resumed issue already has a commit ahead of the base, the extension keeps it and skips the implementer.
|
|
30
28
|
4. Open a pull request that closes the issue.
|
|
31
|
-
5. Wait for the checks. On a red build, the fixer agent gets the failed logs
|
|
29
|
+
5. Wait for the checks. On a red build, the fixer agent gets the failed logs. It gets at most `maxCiFixes` such fixes for the whole issue, and this count includes the checks after review fixes.
|
|
32
30
|
6. The reviewer agent judges the diff and gives a verdict with `blocking` and `minor` findings, which goes on the pull request. Any change the reviewer makes is thrown away.
|
|
33
31
|
7. While review rounds are left, the fixer gets every finding, and the checks and the review run again. In the last round only a blocking finding stops the merge.
|
|
34
32
|
8. Squash-merge the pull request, wait until it is merged, set `status:done`, and remove the worktree, the local branch, the remote branch and the saved state.
|
|
@@ -45,11 +43,11 @@ The state of each issue is saved in `.worktrees/<n>.json`. Each loop removes the
|
|
|
45
43
|
|
|
46
44
|
An optional `.pi/next-issue.json` in the repository sets these fields:
|
|
47
45
|
|
|
48
|
-
| Field | Default | Meaning
|
|
49
|
-
| --------------------- | ---------- |
|
|
50
|
-
| `remote` | `"origin"` | The git remote to push to.
|
|
51
|
-
| `maxCiFixes` | `3` | Fixes of red checks
|
|
52
|
-
| `maxReviewRounds` | `3` | Review rounds per issue.
|
|
53
|
-
| `checkTimeoutMinutes` | `30` | The longest wait for the checks, and for a merge.
|
|
54
|
-
| `diffMaxChars` | `60000` | The longest diff that the reviewer gets.
|
|
55
|
-
| `logMaxChars` | `20000` | The longest failed log that the fixer gets.
|
|
46
|
+
| Field | Default | Meaning |
|
|
47
|
+
| --------------------- | ---------- | --------------------------------------------------------------------------------- |
|
|
48
|
+
| `remote` | `"origin"` | The git remote to push to. |
|
|
49
|
+
| `maxCiFixes` | `3` | Fixes of red checks for the whole issue, including the checks after review fixes. |
|
|
50
|
+
| `maxReviewRounds` | `3` | Review rounds per issue. |
|
|
51
|
+
| `checkTimeoutMinutes` | `30` | The longest wait for the checks, and for a merge. |
|
|
52
|
+
| `diffMaxChars` | `60000` | The longest diff that the reviewer gets. |
|
|
53
|
+
| `logMaxChars` | `20000` | The longest failed log that the fixer gets. |
|
package/index.ts
CHANGED
|
@@ -148,59 +148,39 @@ export function createExtension(launch: Launch) {
|
|
|
148
148
|
});
|
|
149
149
|
|
|
150
150
|
pi.registerCommand('next-issue', {
|
|
151
|
-
description: '
|
|
152
|
-
|
|
153
|
-
const items = ['start', 'stop', 'status']
|
|
154
|
-
.filter((name) => name.startsWith(prefix))
|
|
155
|
-
.map((name) => ({ value: name, label: name }));
|
|
156
|
-
return items.length > 0 ? items : null;
|
|
157
|
-
},
|
|
158
|
-
handler: async (args, ctx) => {
|
|
159
|
-
const action = args.trim() || 'status';
|
|
151
|
+
description: 'Start or stop working through the GitHub issues of this repository',
|
|
152
|
+
handler: async (_args, ctx) => {
|
|
160
153
|
const current = holder().runner;
|
|
161
154
|
const busy =
|
|
162
155
|
current?.progress.status === 'running' || current?.progress.status === 'stopping';
|
|
163
|
-
if (
|
|
164
|
-
if (busy || holder().launching) {
|
|
165
|
-
ctx.ui.notify('next-issue is running already', 'warning');
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
holder().launching = true;
|
|
169
|
-
let runner: Runner;
|
|
170
|
-
try {
|
|
171
|
-
runner = await launch(ctx.cwd);
|
|
172
|
-
} catch (error) {
|
|
173
|
-
ctx.ui.notify(
|
|
174
|
-
`next-issue cannot start: ${error instanceof Error ? error.message : String(error)}`,
|
|
175
|
-
'error',
|
|
176
|
-
);
|
|
177
|
-
return;
|
|
178
|
-
} finally {
|
|
179
|
-
holder().launching = false;
|
|
180
|
-
}
|
|
181
|
-
holder().runner = runner;
|
|
182
|
-
if (ctx.hasUI) {
|
|
183
|
-
ui = ctx.ui;
|
|
184
|
-
coloured = ctx.mode === 'tui';
|
|
185
|
-
follow(runner);
|
|
186
|
-
}
|
|
187
|
-
void runner.run();
|
|
188
|
-
} else if (action === 'stop') {
|
|
189
|
-
if (!busy) {
|
|
190
|
-
ctx.ui.notify('next-issue is not running', 'info');
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
156
|
+
if (busy) {
|
|
193
157
|
current?.stop();
|
|
194
|
-
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (holder().launching) {
|
|
161
|
+
ctx.ui.notify('next-issue is starting already', 'warning');
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
holder().launching = true;
|
|
165
|
+
let runner: Runner;
|
|
166
|
+
try {
|
|
167
|
+
runner = await launch(ctx.cwd);
|
|
168
|
+
} catch (error) {
|
|
195
169
|
ctx.ui.notify(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
: formatStatus(current.progress, Date.now()),
|
|
199
|
-
'info',
|
|
170
|
+
`next-issue cannot start: ${error instanceof Error ? error.message : String(error)}`,
|
|
171
|
+
'error',
|
|
200
172
|
);
|
|
201
|
-
|
|
202
|
-
|
|
173
|
+
return;
|
|
174
|
+
} finally {
|
|
175
|
+
holder().launching = false;
|
|
176
|
+
}
|
|
177
|
+
holder().runner = runner;
|
|
178
|
+
if (ctx.hasUI) {
|
|
179
|
+
ui = ctx.ui;
|
|
180
|
+
coloured = ctx.mode === 'tui';
|
|
181
|
+
follow(runner);
|
|
203
182
|
}
|
|
183
|
+
void runner.run();
|
|
204
184
|
},
|
|
205
185
|
});
|
|
206
186
|
};
|