@ravshansbox/pi-next-issue 6.0.2 → 6.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 +6 -2
- package/index.ts +55 -8
- package/package.json +1 -1
- package/src/protocol.ts +5 -1
- package/src/run.ts +24 -2
package/README.md
CHANGED
|
@@ -13,8 +13,12 @@ It needs `git`, `npx` and an authenticated [`gh`](https://cli.github.com/). It w
|
|
|
13
13
|
Run this in pi, inside a git repository:
|
|
14
14
|
|
|
15
15
|
- `/next-issue start [flags]` starts a run of this repository when none is going. The flags are those of the `next-issue` CLI: `--once`, `--max <n>`, `--watch`, `--hunt`, `--reset`, `--verbose` and `--quiet`. For example, `/next-issue start --hunt` hunts for bugs and keeps watching, like `next-issue --hunt`. The worker refuses any other argument, or a command such as `init`, and a note says why. When a run is going, a note says so, and nothing starts or restarts.
|
|
16
|
-
- `/next-issue stop` stops the run of this repository.
|
|
17
|
-
-
|
|
16
|
+
- `/next-issue stop [--after issue|round|now]` stops the run of this repository. When no run is going, a note says so. An unknown value or flag shows an error and stops nothing.
|
|
17
|
+
- `--after issue`, the default: the run claims no new issue and starts no new hunt. It finishes the issues that it holds and a hunt that is going, then ends as `stopped`.
|
|
18
|
+
- `--after round`: each running issue stops after its current round and keeps its saved state, worktree and labels, so the next run resumes it. A hunt that is going ends first.
|
|
19
|
+
- `--after now`: the run stops every agent, command and hunt at once. The worker keeps running.
|
|
20
|
+
- A stop with a later point than one already asked for changes nothing. A stop with an earlier point replaces it.
|
|
21
|
+
- `/next-issue` with no argument shows the usage: `start [flags]`, `stop [--after issue|round|now]` and `answer`. Pi suggests these as you type after `/next-issue `. After `/next-issue start `, pi suggests the flags that are not typed yet, each with a short description.
|
|
18
22
|
- The `next_issue_start` tool lets the model start a run. It takes an optional `args` array with the same flags as `/next-issue start`. When no run is going, it starts a run like `/next-issue start`. When a run is going, it starts nothing and reports the current run, which picks up new issues by itself. It never stops a run, and it does not ask for confirmation. Its result says whether it started a run.
|
|
19
23
|
- The `next_issue_status` tool lets the model see the run of this repository. It returns the footer line, then one line per handled issue with its outcome, for example `#190 done`. When the worker has no run of this repository, it returns `no run of <root>`. When no worker is going, it returns `no worker is running` and does not start one. It reports no other repository.
|
|
20
24
|
|
package/index.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { errorText } from './src/errors.ts';
|
|
|
8
8
|
import { must } from './src/exec.ts';
|
|
9
9
|
import { repoRoot } from './src/git.ts';
|
|
10
10
|
import type { Progress, Question } from './src/protocol.ts';
|
|
11
|
-
import { requestStart, requestStop, startRun, statusOf } from './src/run.ts';
|
|
11
|
+
import { parseStopArgs, requestStart, requestStop, startRun, statusOf } from './src/run.ts';
|
|
12
12
|
import {
|
|
13
13
|
type Action,
|
|
14
14
|
cannotNote,
|
|
@@ -33,13 +33,59 @@ const QUESTIONS_KEY = 'next-issue-questions';
|
|
|
33
33
|
const RENDER_MS = 1000;
|
|
34
34
|
const QUESTIONS_MS = 60_000;
|
|
35
35
|
const SUBCOMMANDS: Action[] = ['start', 'stop', 'answer'];
|
|
36
|
-
const
|
|
36
|
+
const START_FLAGS = [
|
|
37
|
+
{ flag: '--once', insert: '--once', description: 'Stop after the first handled issue' },
|
|
38
|
+
{ flag: '--max', insert: '--max ', argument: '<n>', description: 'Handle at most n issues' },
|
|
39
|
+
{
|
|
40
|
+
flag: '--watch',
|
|
41
|
+
insert: '--watch',
|
|
42
|
+
description: 'Keep running, and read the open issues again after each pass',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
flag: '--hunt',
|
|
46
|
+
insert: '--hunt',
|
|
47
|
+
description: 'Hunt the repository for bugs alongside the issues',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
flag: '--reset',
|
|
51
|
+
insert: '--reset',
|
|
52
|
+
description: 'Drop the saved budgets and findings, and keep the rest of the state',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
flag: '--verbose',
|
|
56
|
+
insert: '--verbose',
|
|
57
|
+
description: 'Show every command and all agent output',
|
|
58
|
+
},
|
|
59
|
+
{ flag: '--quiet', insert: '--quiet', description: 'Show only the milestones and the summary' },
|
|
60
|
+
];
|
|
61
|
+
const FLAGS = START_FLAGS.map(({ flag, argument }) =>
|
|
62
|
+
argument ? `${flag} ${argument}` : flag,
|
|
63
|
+
).join(', ');
|
|
37
64
|
const USAGE = [
|
|
38
65
|
'/next-issue start [flags] start a run of this repository, with flags from: ' + FLAGS,
|
|
39
|
-
'/next-issue stop
|
|
66
|
+
'/next-issue stop [--after issue|round|now]',
|
|
67
|
+
' stop the run of this repository after the running issues (issue, the default), after the current round (round), or at once (now)',
|
|
40
68
|
'/next-issue answer answer the waiting questions',
|
|
41
69
|
].join('\n');
|
|
42
70
|
|
|
71
|
+
function completions(prefix: string) {
|
|
72
|
+
if (!prefix.includes(' ')) {
|
|
73
|
+
return SUBCOMMANDS.filter((name) => name.startsWith(prefix)).map((name) => ({
|
|
74
|
+
value: name,
|
|
75
|
+
label: name,
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
if (!prefix.startsWith('start ')) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
const head = prefix.slice(0, prefix.lastIndexOf(' ') + 1);
|
|
82
|
+
const partial = prefix.slice(head.length);
|
|
83
|
+
const typed = head.split(/\s+/);
|
|
84
|
+
return START_FLAGS.filter(({ flag }) => flag.startsWith(partial) && !typed.includes(flag)).map(
|
|
85
|
+
({ flag, insert, description }) => ({ value: head + insert, label: flag, description }),
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
43
89
|
function background(callback: () => void, ms: number): ReturnType<typeof setInterval> {
|
|
44
90
|
const timer = setInterval(callback, ms);
|
|
45
91
|
timer.unref?.();
|
|
@@ -257,10 +303,10 @@ export function createExtension(options: Options) {
|
|
|
257
303
|
});
|
|
258
304
|
|
|
259
305
|
pi.registerCommand('next-issue', {
|
|
260
|
-
description: `"start [flags]" starts working through the GitHub issues of this repository, with flags from ${FLAGS}. "stop" stops the run. "answer" answers the waiting questions`,
|
|
306
|
+
description: `"start [flags]" starts working through the GitHub issues of this repository, with flags from ${FLAGS}. "stop [--after issue|round|now]" stops the run after the running issues (the default), after the current round, or at once. "answer" answers the waiting questions`,
|
|
261
307
|
getArgumentCompletions: (prefix) => {
|
|
262
|
-
const matches =
|
|
263
|
-
return matches.length === 0 ? null : matches
|
|
308
|
+
const matches = completions(prefix);
|
|
309
|
+
return matches.length === 0 ? null : matches;
|
|
264
310
|
},
|
|
265
311
|
handler: async (args, ctx) => {
|
|
266
312
|
const [subcommand, ...flags] = args.trim().split(/\s+/).filter(Boolean);
|
|
@@ -274,11 +320,12 @@ export function createExtension(options: Options) {
|
|
|
274
320
|
}
|
|
275
321
|
};
|
|
276
322
|
try {
|
|
323
|
+
const after = subcommand === 'stop' ? parseStopArgs(flags) : undefined;
|
|
277
324
|
const { client, root, progress } = await connect(ctx);
|
|
278
325
|
if (subcommand === 'answer') {
|
|
279
326
|
await answerAll(ctx.ui, options.gh, client, root);
|
|
280
|
-
} else if (
|
|
281
|
-
notify(requestStop(client, root, progress));
|
|
327
|
+
} else if (after !== undefined) {
|
|
328
|
+
notify(requestStop(client, root, progress, after));
|
|
282
329
|
} else {
|
|
283
330
|
notify(requestStart(client, root, progress, flags));
|
|
284
331
|
}
|
package/package.json
CHANGED
package/src/protocol.ts
CHANGED
|
@@ -28,10 +28,14 @@ export type Question = {
|
|
|
28
28
|
labels: { needsHuman: string; skip: string };
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
+
export const STOP_POINTS = ['issue', 'round', 'now'] as const;
|
|
32
|
+
|
|
33
|
+
export type StopPoint = (typeof STOP_POINTS)[number];
|
|
34
|
+
|
|
31
35
|
export type ClientMessage =
|
|
32
36
|
| { type: 'hello'; version: string }
|
|
33
37
|
| { type: 'start'; root: string; args?: string[] }
|
|
34
|
-
| { type: 'stop'; root: string }
|
|
38
|
+
| { type: 'stop'; root: string; after?: StopPoint }
|
|
35
39
|
| { type: 'questions'; root: string; id?: string };
|
|
36
40
|
|
|
37
41
|
export type WorkerMessage =
|
package/src/run.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { going, type Progress } from './protocol.ts';
|
|
1
|
+
import { going, type Progress, STOP_POINTS, type StopPoint } from './protocol.ts';
|
|
2
2
|
import { accepted } from './requests.ts';
|
|
3
3
|
import { formatStatus, handledNote, type Note } from './status.ts';
|
|
4
4
|
import type { WorkerClient } from './worker.ts';
|
|
@@ -57,14 +57,36 @@ export function requestStart(
|
|
|
57
57
|
return undefined;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
export function parseStopArgs(args: string[]): StopPoint {
|
|
61
|
+
const [flag, value, ...rest] = args;
|
|
62
|
+
if (flag === undefined) {
|
|
63
|
+
return 'issue';
|
|
64
|
+
}
|
|
65
|
+
if (flag !== '--after') {
|
|
66
|
+
throw new Error(`stop does not take ${flag}`);
|
|
67
|
+
}
|
|
68
|
+
const point = STOP_POINTS.find((candidate) => candidate === value);
|
|
69
|
+
if (point === undefined) {
|
|
70
|
+
const known = 'issue, round or now';
|
|
71
|
+
throw new Error(
|
|
72
|
+
value === undefined ? `--after takes ${known}` : `--after takes ${known}, not ${value}`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
if (rest[0] !== undefined) {
|
|
76
|
+
throw new Error(`stop does not take ${rest[0]}`);
|
|
77
|
+
}
|
|
78
|
+
return point;
|
|
79
|
+
}
|
|
80
|
+
|
|
60
81
|
export function requestStop(
|
|
61
82
|
client: WorkerClient,
|
|
62
83
|
root: string,
|
|
63
84
|
progress: Progress | undefined,
|
|
85
|
+
after: StopPoint,
|
|
64
86
|
): Note | undefined {
|
|
65
87
|
if (!going(progress)) {
|
|
66
88
|
return { text: `No run of ${root} is going.`, level: 'info' };
|
|
67
89
|
}
|
|
68
|
-
client.send({ type: 'stop', root });
|
|
90
|
+
client.send({ type: 'stop', root, after });
|
|
69
91
|
return undefined;
|
|
70
92
|
}
|