@ravshansbox/pi-next-issue 1.0.12 → 1.0.14

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
@@ -22,11 +22,11 @@ The run does not belong to a session: it goes on after `/new`, `/resume`, a fork
22
22
 
23
23
  ## What it does
24
24
 
25
- Each loop fetches the 200 oldest open issues with the `status:todo` label and none of `status:in-progress`, `status:done`, `status:needs-human` or `status:blocked`, and the open issues with saved state that it can read. It takes the oldest one with saved state, else the oldest one with the `status:todo` label and none of `status:in-progress`, `status:done`, `status:needs-human` or `status:blocked`. The run ends when no such issue is left, or at the first error. For each issue:
25
+ Each loop fetches the 200 oldest open issues with the `status:todo` label and none of `status:in-progress`, `status:done`, `status:needs-human` or `status:blocked`, and the open issues with saved state that it can read. It takes the oldest one with saved state and none of `status:done`, `status:needs-human` or `status:blocked`, else the oldest one with the `status:todo` label and none of `status:in-progress`, `status:done`, `status:needs-human` or `status:blocked`. The run ends when no such issue is left, or at the first error. For each issue:
26
26
 
27
27
  1. Swap `status:todo` for `status:in-progress`.
28
28
  2. Add a git worktree at `.worktrees/<n>` on the `<n>` branch, from the remote default branch.
29
- 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.
29
+ 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
30
  4. Open a pull request that closes the issue.
31
31
  5. Wait for the checks. On a red build, the fixer agent gets the failed logs, up to `maxCiFixes` times.
32
32
  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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ravshansbox/pi-next-issue",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Pi extension that works through GitHub issues in the background",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/git.ts CHANGED
@@ -77,6 +77,16 @@ export async function commit(cwd: string, message: string): Promise<boolean> {
77
77
  return true;
78
78
  }
79
79
 
80
+ export async function ahead(
81
+ cwd: string,
82
+ remote: string,
83
+ base: string,
84
+ ): Promise<string | undefined> {
85
+ const range = `${remote}/${base}..HEAD`;
86
+ const subjects = await must('git', ['log', '--reverse', '--format=%s', range], { cwd });
87
+ return subjects.split('\n')[0] || undefined;
88
+ }
89
+
80
90
  export async function push(cwd: string, remote: string, branch: string): Promise<void> {
81
91
  await must('git', ['push', '-u', remote, `HEAD:refs/heads/${branch}`], { cwd });
82
92
  }
package/src/ports.ts CHANGED
@@ -20,6 +20,7 @@ export async function createPorts(root: string, config: Config): Promise<Ports>
20
20
  commentOnIssue: (issue, body) => github.commentOnIssue(repo, issue, body),
21
21
  worktree: (issue, branch) => git.worktree(root, config.remote, base, issue, branch),
22
22
  commit: (cwd, message) => git.commit(cwd, message),
23
+ ahead: (cwd) => git.ahead(cwd, config.remote, base),
23
24
  push: (cwd, branch) => git.push(cwd, config.remote, branch),
24
25
  discard: (cwd, branch) => git.discard(cwd, config.remote, branch),
25
26
  diff: (cwd) => git.diff(cwd, config.remote, base),
package/src/runner.ts CHANGED
@@ -45,6 +45,7 @@ export type Ports = {
45
45
  commentOnIssue(issue: number, body: string): Promise<void>;
46
46
  worktree(issue: number, branch: string): Promise<string>;
47
47
  commit(cwd: string, message: string): Promise<boolean>;
48
+ ahead(cwd: string): Promise<string | undefined>;
48
49
  push(cwd: string, branch: string): Promise<void>;
49
50
  discard(cwd: string, branch: string): Promise<void>;
50
51
  diff(cwd: string): Promise<string>;
@@ -99,6 +100,10 @@ function ready(issue: Issue): boolean {
99
100
  );
100
101
  }
101
102
 
103
+ function resumable(issue: Issue): boolean {
104
+ return !issue.labels.some((label) => label !== LABELS.inProgress && STOP_LABELS.includes(label));
105
+ }
106
+
102
107
  export class Runner {
103
108
  progress: Progress = { status: 'idle', tools: 0, handled: [] };
104
109
  private readonly controller = new AbortController();
@@ -137,7 +142,8 @@ export class Runner {
137
142
  const saved = new Set(await this.ports.savedIssues());
138
143
  const fresh = issues.filter((item) => !seen.has(item.number));
139
144
  const next =
140
- fresh.find((item) => saved.has(item.number)) ?? fresh.find((item) => ready(item));
145
+ fresh.find((item) => saved.has(item.number) && resumable(item)) ??
146
+ fresh.find((item) => ready(item));
141
147
  if (next === undefined) {
142
148
  this.update({ status: 'finished', stage: undefined, issue: undefined, title: undefined });
143
149
  return;
@@ -237,6 +243,11 @@ export class Runner {
237
243
  switch (state.phase) {
238
244
  case 'implement': {
239
245
  this.stage('implement');
246
+ const kept = saved === undefined ? undefined : await ports.ahead(cwd);
247
+ if (kept !== undefined) {
248
+ await save({ phase: 'publish', title: kept });
249
+ break;
250
+ }
240
251
  const reply = await this.agent('work', cwd, implementPrompt(issue, comments));
241
252
  const title = parseCommitSubject(reply, `feat: ${issue.title}`);
242
253
  if (!(await ports.commit(cwd, title))) {