pr-shepherd 0.19.0 → 0.21.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.
Files changed (57) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +23 -56
  3. package/bin/checks/triage.test-support.mjs +1 -2
  4. package/bin/cli/default-poll.mjs +32 -0
  5. package/bin/cli/fix-formatter.mjs +3 -3
  6. package/bin/cli/formatters.mjs +5 -4
  7. package/bin/cli/handlers.mjs +0 -1
  8. package/bin/cli/help-command-pages.mjs +153 -0
  9. package/bin/cli/help-top-page.mjs +59 -0
  10. package/bin/cli/help.mjs +4 -30
  11. package/bin/cli/iterate-emitter.mjs +0 -1
  12. package/bin/cli/iterate-formatter.mjs +4 -6
  13. package/bin/cli/iterate-instructions.mjs +6 -8
  14. package/bin/cli/iterate-lean.mjs +4 -6
  15. package/bin/cli/iterate-lean.test-support.mjs +0 -2
  16. package/bin/cli/list-formatters.mjs +3 -0
  17. package/bin/cli/poll-handler.mjs +0 -1
  18. package/bin/cli/runner.mjs +2 -103
  19. package/bin/cli/validate-default-args.mjs +32 -0
  20. package/bin/cli-parser.clean.test-support.mjs +0 -1
  21. package/bin/cli-parser.commit-suggestion.test-support.mjs +2 -3
  22. package/bin/cli-parser.iterate-fix.test-support.mjs +1 -2
  23. package/bin/cli-parser.iterate-fixtures.mjs +1 -1
  24. package/bin/cli-parser.iterate.test-support.mjs +1 -2
  25. package/bin/cli-parser.mjs +7 -7
  26. package/bin/cli-parser.test-support.mjs +1 -2
  27. package/bin/commands/check.mjs +2 -1
  28. package/bin/commands/check.test-support.mjs +1 -2
  29. package/bin/commands/clean.test-support.mjs +0 -1
  30. package/bin/commands/commit-suggestion-instruction.mjs +2 -2
  31. package/bin/commands/commit-suggestion.apply.test-support.mjs +1 -2
  32. package/bin/commands/commit-suggestion.mjs +6 -2
  33. package/bin/commands/commit-suggestion.test-support.mjs +2 -3
  34. package/bin/commands/iterate/classify.mjs +2 -2
  35. package/bin/commands/iterate/fix-code.mjs +3 -3
  36. package/bin/commands/iterate/index.mjs +0 -1
  37. package/bin/commands/iterate/render.mjs +4 -3
  38. package/bin/commands/iterate-stall.test-support.mjs +0 -1
  39. package/bin/commands/iterate.fix-code-in-progress.test-support.mjs +2 -2
  40. package/bin/commands/poll.mjs +12 -3
  41. package/bin/commands/resolve-instructions.mjs +6 -7
  42. package/bin/commands/resolve.mjs +1 -1
  43. package/bin/commands/resolve.test-support.mjs +2 -2
  44. package/bin/commands/shepherd-journal.test-support.mjs +0 -2
  45. package/bin/comments/resolve.test-support.mjs +1 -2
  46. package/bin/comments/visible-comments.mjs +3 -2
  47. package/bin/config/load.mjs +0 -7
  48. package/bin/config.json +0 -3
  49. package/bin/github/batch-parsers.test-support.mjs +1 -2
  50. package/bin/github/batch.test-support.mjs +1 -2
  51. package/bin/github/http.test-support.mjs +1 -2
  52. package/bin/reporters/agent.mjs +1 -0
  53. package/bin/suggestions/patch.test-support.mjs +0 -2
  54. package/package.json +1 -1
  55. package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
  56. package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +11 -27
  57. package/bin/cli/default-iterate.mjs +0 -50
@@ -13,8 +13,9 @@ export function classifyVisibleComments(comments, seenMap, minimizeComments) {
13
13
  const cls = classifyItem(c.id, c.body, seenMap);
14
14
  if (cls === "unchanged")
15
15
  continue;
16
- actionable.push(c);
17
- toMarkSeen.push(c);
16
+ const comment = cls === "edited" ? { ...c, edited: true } : c;
17
+ actionable.push(comment);
18
+ toMarkSeen.push(comment);
18
19
  }
19
20
  return { actionable, minimizeIds, toMarkSeen };
20
21
  }
@@ -3,7 +3,6 @@ import { join, dirname } from "node:path";
3
3
  import { homedir } from "node:os";
4
4
  import { parse } from "yaml";
5
5
  import builtins from "../config.json" with { type: "json" };
6
- import { parseCliRunner } from "../cli/runner.mjs";
7
6
  const MINIMIZE_COMMENTS_POLICIES = ["all", "bots", "users", "none"];
8
7
  const RC_FILENAME = ".pr-shepherdrc.yml";
9
8
  function findRcFile(startDir) {
@@ -61,12 +60,6 @@ export function loadConfig() {
61
60
  const raw = readFileSync(rcPath, "utf8");
62
61
  const parsed = (parse(raw) ?? {});
63
62
  const config = deepMerge(defaults, parsed);
64
- // Validate cli and watch at load time so misconfigurations are caught once
65
- // with the rc file path in context, rather than during instruction rendering.
66
- if (config.cli === null || Array.isArray(config.cli) || typeof config.cli !== "object") {
67
- throw new Error(`Invalid config: cli must be a plain object, got ${JSON.stringify(config.cli)}`);
68
- }
69
- config.cli.runner = parseCliRunner(config.cli.runner);
70
63
  config.iterate.minimizeComments = parseMinimizeCommentsPolicy(config.iterate.minimizeComments);
71
64
  configCache.set(cwd, config);
72
65
  return config;
package/bin/config.json CHANGED
@@ -1,7 +1,4 @@
1
1
  {
2
- "cli": {
3
- "runner": "auto"
4
- },
5
2
  "iterate": {
6
3
  "fixAttemptsPerThread": 3,
7
4
  "stallTimeoutMinutes": 30,
@@ -1,5 +1,4 @@
1
- // @ts-nocheck
2
- import { describe, it, expect, vi, beforeEach } from "vitest";
1
+ import { vi, beforeEach } from "vitest";
3
2
  vi.mock("./client.mts", () => ({
4
3
  graphql: vi.fn(),
5
4
  graphqlWithRateLimit: vi.fn(),
@@ -1,5 +1,4 @@
1
- // @ts-nocheck
2
- import { describe, it, expect, vi, beforeEach } from "vitest";
1
+ import { vi, beforeEach } from "vitest";
3
2
  vi.mock("./client.mts", () => ({
4
3
  graphql: vi.fn(),
5
4
  graphqlWithRateLimit: vi.fn(),
@@ -1,5 +1,4 @@
1
- // @ts-nocheck
2
- import { describe, it, expect, vi, beforeEach } from "vitest";
1
+ import { vi, beforeEach } from "vitest";
3
2
  // ---------------------------------------------------------------------------
4
3
  // Stub fetch and child_process globally before any imports.
5
4
  // ---------------------------------------------------------------------------
@@ -32,6 +32,7 @@ export function toAgentComment(c) {
32
32
  ...(c.authorType !== undefined && { authorType: c.authorType }),
33
33
  body: c.body,
34
34
  url: c.url,
35
+ ...(c.edited === true && { edited: true }),
35
36
  };
36
37
  }
37
38
  export function toAgentCheck(c) {
@@ -1,4 +1,2 @@
1
- // @ts-nocheck
2
- import { describe, it, expect } from "vitest";
3
1
  import { buildUnifiedDiff } from "./patch.mjs";
4
2
  export { buildUnifiedDiff };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pr-shepherd",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "description": "Autonomous PR CI monitor and review-comment resolver for agentic coding tools",
5
5
  "license": "MIT",
6
6
  "author": "Jonathan Ong",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pr-shepherd",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "description": "Autonomous PR CI monitor and review-comment resolver for Codex.",
5
5
  "author": {
6
6
  "name": "Jonathan Ong",
@@ -8,7 +8,7 @@ allowed-tools: ["Bash", "Read", "Grep", "Edit", "Write", "Glob", "Skill"]
8
8
 
9
9
  # pr-shepherd
10
10
 
11
- One-tick dispatcher for iterating a PR to completion.
11
+ Poll dispatcher for iterating a PR to completion.
12
12
 
13
13
  ## Arguments: $ARGUMENTS
14
14
 
@@ -20,40 +20,24 @@ One-tick dispatcher for iterating a PR to completion.
20
20
  - Otherwise, infer: `gh pr view --json number --jq .number`
21
21
  - If no PR found, report an error and stop.
22
22
 
23
- 2. **Short-circuit if merged or closed:**
23
+ 2. **Run `pr-shepherd`:**
24
24
 
25
25
  ```bash
26
- gh pr view <N> --json state --jq '.state'
26
+ pr-shepherd <N> --interval 45s --timeout 4m
27
27
  ```
28
28
 
29
- If `MERGED` or `CLOSED`, output: `PR #N is already merged/closed. Nothing to do.` and stop.
29
+ Do not pass `$ARGUMENTS` through as extra flags. If you need to inspect supported options, run `pr-shepherd --help`.
30
30
 
31
- 3. **Select the package runner** from the target repository root:
32
- - Prefer `package.json` `packageManager`: `pnpm@...` → `pnpm exec`, `yarn@...` → `yarn run`, `bun@...` → `bunx`, `npm@...` → `npx`.
33
- - If `packageManager` is absent, use lockfiles: `pnpm-lock.yaml` → `pnpm exec`, `yarn.lock` → `yarn run`, `bun.lock` / `bun.lockb` → `bunx`, `package-lock.json` or no signal → `npx`.
31
+ Print the full output. Follow the `## Instructions` section exactly for the current action.
34
32
 
35
- 4. **Run one iterate tick:**
33
+ 3. **Persistence:** Continuously call `pr-shepherd <N> --interval 45s --timeout 4m` until the CLI returns `[CANCEL]` or `[ESCALATE]`, unless the human directs you to stop. Every other action is non-terminal:
34
+ - `[WAIT]`: call `pr-shepherd <N> --interval 45s --timeout 4m` again.
35
+ - `[MARK_READY]`: call `pr-shepherd <N> --interval 45s --timeout 4m` again.
36
+ - `[FIX_CODE]`: follow the output's `## Instructions`, then call `pr-shepherd <N> --interval 45s --timeout 4m` again.
36
37
 
37
- If the package is missing in the target repository, tell the user to install pr-shepherd with the matching dev-dependency command: `pnpm add -D pr-shepherd`, `yarn add -D pr-shepherd`, `bun add -d pr-shepherd`, or `npm install --save-dev pr-shepherd`.
38
+ Treat a nonzero poll exit code as PR state only when the output contains a matching `# PR #N [ACTION]` heading. Exit code `1` can also mean a command or validation failure; if there is no `[FIX_CODE]` heading, surface the error and stop instead of looping.
38
39
 
39
- ```bash
40
- <runner> pr-shepherd <N>
41
- ```
42
-
43
- Print the full output. Follow the `## Instructions` section exactly.
44
-
45
- 5. **Persistence:** Do not stop polling/iterating unless directed by the human or until the CLI returns a terminal state (`[CANCEL]` or `[ESCALATE]`). Every other action — `[WAIT]`, `[MARK_READY]`, `[FIX_CODE]` — is non-terminal and must be followed by another tick via one of the strategies below.
46
-
47
- 6. **Stop conditions (terminal states):**
40
+ 4. **Stop conditions (terminal states):**
48
41
  - Stop when the CLI emits `[CANCEL]` (ready-delay completed, or PR merged/closed).
49
42
  - Stop when the CLI emits `[ESCALATE]`, including `stall-timeout` for repeated unchanged CI failures.
50
43
  - **Do NOT merge the pull request** unless the human has explicitly requested or allowed it.
51
-
52
- 7. **Non-terminal actions** (`[WAIT]`, `[MARK_READY]`, `[FIX_CODE]`) — follow the `## Instructions` in the output. Pick one iteration strategy:
53
- - **Blocking poll** — rerun as `<runner> pr-shepherd poll <N> [--interval <duration>] [--timeout <duration>]` (defaults: interval 30s, timeout 5m). Holds the agent turn until the action is non-WAIT or the timeout fires. Simplest when the agent cannot reliably schedule its own follow-up.
54
- - **Scheduled wakeup + one tick** — schedule a single session-only follow-up task to rerun `<runner> pr-shepherd <N>` after a fresh 30s–4m delay, then end the turn.
55
- - **Inline sleep + rerun** — sleep inline for a fresh 30s–4m delay, then rerun.
56
-
57
- **Never write a custom polling loop** (shell `while`/`until` loops, script files that loop over pr-shepherd output, etc.). Custom loops poll only for terminal states and silently skip `[FIX_CODE]` handling — actionable review threads, failing checks, and resolve commands get missed. Use `pr-shepherd poll` for WAIT-state waiting; it exits on any non-WAIT action so the caller handles `[FIX_CODE]` and other actionable outputs normally.
58
-
59
- Do not combine strategies (e.g., poll AND schedule a wakeup). Remember step 5 — every non-terminal output requires a follow-up tick.
@@ -1,50 +0,0 @@
1
- import { parsePrNumber } from "./args.mjs";
2
- import { USAGE } from "./help.mjs";
3
- const DEFAULT_ITERATE_FLAGS_WITH_VALUES = new Set(["--format", "--ready-delay", "--stall-timeout"]);
4
- const DEFAULT_ITERATE_BOOLEAN_FLAGS = new Set([
5
- "--verbose",
6
- "--no-auto-mark-ready",
7
- "--no-auto-cancel-actionable",
8
- ]);
9
- export function isDefaultIterateInvocation(subcommand) {
10
- if (subcommand === "--help" || subcommand === "-h")
11
- return false;
12
- return (subcommand === undefined ||
13
- parsePrNumber(subcommand) !== null ||
14
- isDefaultIterateFlag(subcommand));
15
- }
16
- export function validateDefaultIterateArgs(args) {
17
- let sawPr = false;
18
- for (let i = 0; i < args.length; i += 1) {
19
- const arg = args[i];
20
- if (DEFAULT_ITERATE_FLAGS_WITH_VALUES.has(arg)) {
21
- if (i + 1 >= args.length || args[i + 1].startsWith("--")) {
22
- writeDefaultUsageError(arg);
23
- return false;
24
- }
25
- i += 1;
26
- continue;
27
- }
28
- const inlineFlag = arg.split("=", 1)[0];
29
- if (DEFAULT_ITERATE_FLAGS_WITH_VALUES.has(inlineFlag))
30
- continue;
31
- if (DEFAULT_ITERATE_BOOLEAN_FLAGS.has(arg))
32
- continue;
33
- if (parsePrNumber(arg) !== null && !sawPr) {
34
- sawPr = true;
35
- continue;
36
- }
37
- writeDefaultUsageError(arg);
38
- return false;
39
- }
40
- return true;
41
- }
42
- function isDefaultIterateFlag(arg) {
43
- const name = arg.split("=", 1)[0];
44
- return DEFAULT_ITERATE_FLAGS_WITH_VALUES.has(name) || DEFAULT_ITERATE_BOOLEAN_FLAGS.has(arg);
45
- }
46
- function writeDefaultUsageError(arg) {
47
- process.stderr.write(`Unknown subcommand: ${arg}\n`);
48
- process.stderr.write(`${USAGE.top}\n`);
49
- process.exitCode = 1;
50
- }