pr-shepherd 0.22.0 → 0.24.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 (72) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +87 -125
  3. package/bin/cli/args.mjs +2 -0
  4. package/bin/cli/fix-formatter.mjs +3 -1
  5. package/bin/cli/formatters.mjs +3 -45
  6. package/bin/cli/handlers.mjs +22 -1
  7. package/bin/cli/help-command-pages.mjs +27 -2
  8. package/bin/cli/help-top-page.mjs +2 -0
  9. package/bin/cli/iterate-instructions.mjs +1 -1
  10. package/bin/cli/list-formatters.mjs +5 -2
  11. package/bin/cli/mark-files-as-viewed-flags.mjs +34 -0
  12. package/bin/cli/mark-files-as-viewed-formatter.mjs +52 -0
  13. package/bin/cli/mutate-formatter.mjs +50 -0
  14. package/bin/cli-parser.mjs +11 -2
  15. package/bin/commands/check.mjs +25 -56
  16. package/bin/commands/commit-suggestion-instruction.mjs +1 -1
  17. package/bin/commands/iterate/classify.mjs +32 -31
  18. package/bin/commands/iterate/fix-code.mjs +23 -19
  19. package/bin/commands/iterate/index.mjs +4 -1
  20. package/bin/commands/iterate/render.mjs +6 -3
  21. package/bin/commands/iterate/stall.mjs +1 -1
  22. package/bin/commands/mark-files-as-viewed.mjs +220 -0
  23. package/bin/commands/resolve-instructions.mjs +5 -2
  24. package/bin/commands/resolve-mutate.mjs +49 -4
  25. package/bin/commands/resolve.mjs +21 -78
  26. package/bin/commands/shepherd-journal.mjs +1 -1
  27. package/bin/comments/authors.mjs +32 -0
  28. package/bin/comments/minimize-policy.mjs +8 -4
  29. package/bin/comments/pending-ops.mjs +6 -0
  30. package/bin/comments/resolve.mjs +28 -11
  31. package/bin/comments/review-thread-markers.mjs +18 -0
  32. package/bin/comments/review-visibility.mjs +14 -0
  33. package/bin/comments/thread-visibility.mjs +60 -0
  34. package/bin/comments/visible-comments.mjs +2 -2
  35. package/bin/config/load.mjs +7 -0
  36. package/bin/config.json +14 -0
  37. package/bin/github/batch-parser-helpers.mjs +3 -4
  38. package/bin/github/batch-parsers.mjs +8 -6
  39. package/bin/github/client.mjs +1 -2
  40. package/bin/github/gql/batch-pr.gql +3 -0
  41. package/bin/reporters/agent.mjs +2 -0
  42. package/bin/state/seen-comments.mjs +40 -4
  43. package/bin/threads/transcript.mjs +6 -6
  44. package/package.json +8 -4
  45. package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
  46. package/plugins/pr-shepherd/skills/mark-files-as-viewed/SKILL.md +31 -0
  47. package/bin/checks/triage.test-support.mjs +0 -60
  48. package/bin/cli/iterate-lean.test-support.mjs +0 -3
  49. package/bin/cli-parser.clean.test-support.mjs +0 -44
  50. package/bin/cli-parser.commit-suggestion.test-support.mjs +0 -65
  51. package/bin/cli-parser.iterate-fix.test-support.mjs +0 -43
  52. package/bin/cli-parser.iterate-fixtures.mjs +0 -75
  53. package/bin/cli-parser.iterate.test-support.mjs +0 -44
  54. package/bin/cli-parser.test-support.mjs +0 -42
  55. package/bin/commands/check.test-support.mjs +0 -147
  56. package/bin/commands/clean.test-support.mjs +0 -47
  57. package/bin/commands/commit-suggestion.apply.test-support.mjs +0 -87
  58. package/bin/commands/commit-suggestion.test-support.mjs +0 -112
  59. package/bin/commands/iterate-stall.test-support.mjs +0 -24
  60. package/bin/commands/iterate-test-support.mjs +0 -150
  61. package/bin/commands/iterate.fix-code-in-progress.test-support.mjs +0 -123
  62. package/bin/commands/poll.test-support.mjs +0 -77
  63. package/bin/commands/resolve.test-support.mjs +0 -114
  64. package/bin/commands/shepherd-journal.test-support.mjs +0 -7
  65. package/bin/comments/outdated.mjs +0 -15
  66. package/bin/comments/resolve.test-support.mjs +0 -39
  67. package/bin/github/batch-parsers.test-support.mjs +0 -66
  68. package/bin/github/batch.test-support.mjs +0 -66
  69. package/bin/github/client.test-support.mjs +0 -55
  70. package/bin/github/http.test-support.mjs +0 -51
  71. package/bin/state/seen-comments.test-support.mjs +0 -19
  72. package/bin/suggestions/patch.test-support.mjs +0 -2
@@ -1,150 +0,0 @@
1
- import { vi, beforeEach, afterEach } from "vitest";
2
- const mockFetch = vi.fn();
3
- vi.stubGlobal("fetch", mockFetch);
4
- const { mockExecFile } = vi.hoisted(() => ({ mockExecFile: vi.fn() }));
5
- vi.mock("node:child_process", () => ({
6
- execFile: (cmd, args, optsOrCb, maybeCb) => {
7
- const cb = typeof optsOrCb === "function" ? optsOrCb : maybeCb;
8
- mockExecFile(cmd, args)
9
- .then((result) => cb(null, result))
10
- .catch((err) => cb(err, { stdout: "", stderr: err.stderr ?? "" }));
11
- },
12
- }));
13
- vi.mock("./check.mts", () => ({ runCheck: vi.fn() }));
14
- vi.mock("./ready-delay.mts", () => ({ updateReadyDelay: vi.fn() }));
15
- vi.mock("../github/client.mts", () => ({ getCurrentPrNumber: vi.fn().mockResolvedValue(42) }));
16
- vi.mock("../state/fix-attempts.mts", () => ({
17
- readFixAttempts: vi.fn().mockResolvedValue(null),
18
- writeFixAttempts: vi.fn().mockResolvedValue(undefined),
19
- }));
20
- vi.mock("../state/iterate-stall.mts", () => ({
21
- readStallState: vi.fn().mockResolvedValue(null),
22
- writeStallState: vi.fn().mockResolvedValue(undefined),
23
- clearStallState: vi.fn().mockResolvedValue(undefined),
24
- }));
25
- const { mockLoadConfig } = vi.hoisted(() => ({ mockLoadConfig: vi.fn() }));
26
- vi.mock("../config/load.mts", () => ({ loadConfig: mockLoadConfig }));
27
- import { runCheck } from "./check.mjs";
28
- import { updateReadyDelay } from "./ready-delay.mjs";
29
- import { getCurrentPrNumber } from "../github/client.mjs";
30
- import { readFixAttempts, writeFixAttempts } from "../state/fix-attempts.mjs";
31
- import { clearStallState, readStallState, writeStallState } from "../state/iterate-stall.mjs";
32
- import { buildEscalateHumanMessage, buildEscalateSuggestion, checkEscalateTriggers, } from "./iterate/escalate.mjs";
33
- import { buildRelevantChecks, buildWaitLog, getCurrentHeadSha } from "./iterate/helpers.mjs";
34
- const mockRunCheck = vi.mocked(runCheck);
35
- const mockUpdateReadyDelay = vi.mocked(updateReadyDelay);
36
- const mockGetCurrentPrNumber = vi.mocked(getCurrentPrNumber);
37
- const mockReadFixAttempts = vi.mocked(readFixAttempts);
38
- const mockWriteFixAttempts = vi.mocked(writeFixAttempts);
39
- const mockReadStallState = vi.mocked(readStallState);
40
- const mockWriteStallState = vi.mocked(writeStallState);
41
- const mockClearStallState = vi.mocked(clearStallState);
42
- const NOW = 1_700_000_000;
43
- const READY_STATE_DEFAULT = { isReady: true, shouldCancel: false, remainingSeconds: 300 };
44
- function makeReport(overrides = {}) {
45
- return {
46
- pr: 42,
47
- nodeId: "PR_kgDOAAA",
48
- repo: "owner/repo",
49
- status: "READY",
50
- baseBranch: "main",
51
- mergeStatus: {
52
- status: "CLEAN",
53
- state: "OPEN",
54
- isDraft: false,
55
- mergeable: "MERGEABLE",
56
- reviewDecision: "APPROVED",
57
- blockingBotReviewInProgress: false,
58
- mergeStateStatus: "CLEAN",
59
- },
60
- checks: {
61
- passing: [
62
- {
63
- name: "ci",
64
- status: "COMPLETED",
65
- conclusion: "SUCCESS",
66
- detailsUrl: "https://github.com/owner/repo/actions/runs/1",
67
- event: "pull_request",
68
- runId: "run-1",
69
- category: "passed",
70
- },
71
- ],
72
- failing: [],
73
- inProgress: [],
74
- skipped: [],
75
- filtered: [],
76
- filteredNames: [],
77
- blockedByFilteredCheck: false,
78
- },
79
- threads: {
80
- actionable: [],
81
- resolutionOnly: [],
82
- autoResolved: [],
83
- autoResolveErrors: [],
84
- firstLook: [],
85
- },
86
- comments: { actionable: [], firstLook: [] },
87
- changesRequestedReviews: [],
88
- reviewSummaries: [],
89
- firstLookSummaries: [],
90
- editedSummaries: [],
91
- approvedReviews: [],
92
- branchProtection: null,
93
- ...overrides,
94
- };
95
- }
96
- function makeOpts(overrides = {}) {
97
- return { prNumber: 42, format: "json", readyDelaySeconds: 600, ...overrides };
98
- }
99
- function makeReview(id, author, body) {
100
- return { id, author, authorType: "Unknown", body };
101
- }
102
- function defaultConfig() {
103
- return {
104
- iterate: {
105
- fixAttemptsPerThread: 3,
106
- stallTimeoutMinutes: 60,
107
- minimizeApprovals: false,
108
- minimizeComments: "all",
109
- },
110
- watch: { readyDelayMinutes: 10 },
111
- resolve: {
112
- concurrency: 4,
113
- shaPoll: { intervalMs: 2000, maxAttempts: 10 },
114
- fetchReviewSummaries: true,
115
- },
116
- checks: { ciTriggerEvents: ["pull_request", "pull_request_target"] },
117
- mergeStatus: { blockingReviewerLogins: ["copilot"] },
118
- actions: { autoResolveOutdated: true, autoMarkReady: true, commitSuggestions: true },
119
- };
120
- }
121
- function registerIterateHooks(config = defaultConfig) {
122
- beforeEach(() => {
123
- vi.clearAllMocks();
124
- mockLoadConfig.mockReturnValue(config());
125
- process.env["GH_TOKEN"] = "test-token";
126
- mockExecFile.mockImplementation((cmd, args) => {
127
- if (cmd === "git" && args[0] === "rev-parse") {
128
- return Promise.resolve({ stdout: "abc123", stderr: "" });
129
- }
130
- return Promise.resolve({ stdout: "", stderr: "" });
131
- });
132
- mockFetch.mockResolvedValue({
133
- ok: true,
134
- status: 200,
135
- headers: new Headers({ "content-type": "application/json" }),
136
- json: () => Promise.resolve({ data: {} }),
137
- text: () => Promise.resolve('{"data":{}}'),
138
- });
139
- vi.useFakeTimers();
140
- vi.setSystemTime(NOW * 1000);
141
- mockUpdateReadyDelay.mockResolvedValue(READY_STATE_DEFAULT);
142
- mockReadFixAttempts.mockResolvedValue(null);
143
- mockWriteFixAttempts.mockResolvedValue(undefined);
144
- mockReadStallState.mockResolvedValue(null);
145
- mockWriteStallState.mockResolvedValue(undefined);
146
- mockClearStallState.mockResolvedValue(undefined);
147
- });
148
- afterEach(() => vi.useRealTimers());
149
- }
150
- export { NOW, buildEscalateHumanMessage, buildEscalateSuggestion, buildRelevantChecks, buildWaitLog, checkEscalateTriggers, defaultConfig, getCurrentHeadSha, makeOpts, makeReport, makeReview, mockExecFile, mockFetch, mockGetCurrentPrNumber, mockClearStallState, mockLoadConfig, mockReadFixAttempts, mockReadStallState, mockRunCheck, mockUpdateReadyDelay, mockWriteFixAttempts, mockWriteStallState, registerIterateHooks, };
@@ -1,123 +0,0 @@
1
- import { vi, beforeEach, afterEach } from "vitest";
2
- const mockFetch = vi.fn();
3
- vi.stubGlobal("fetch", mockFetch);
4
- const { mockExecFile } = vi.hoisted(() => ({ mockExecFile: vi.fn() }));
5
- vi.mock("node:child_process", () => ({
6
- execFile: (cmd, args, optsOrCb, maybeCb) => {
7
- const cb = typeof optsOrCb === "function" ? optsOrCb : maybeCb;
8
- mockExecFile(cmd, args)
9
- .then((result) => cb(null, result))
10
- .catch((err) => cb(err, { stdout: "", stderr: err.stderr ?? "" }));
11
- },
12
- }));
13
- vi.mock("./check.mts", () => ({ runCheck: vi.fn() }));
14
- vi.mock("./ready-delay.mts", () => ({ updateReadyDelay: vi.fn() }));
15
- vi.mock("../github/client.mts", () => ({
16
- getCurrentPrNumber: vi.fn().mockResolvedValue(42),
17
- }));
18
- vi.mock("../state/fix-attempts.mts", () => ({
19
- readFixAttempts: vi.fn().mockResolvedValue(null),
20
- writeFixAttempts: vi.fn().mockResolvedValue(undefined),
21
- }));
22
- vi.mock("../state/iterate-stall.mts", () => ({
23
- readStallState: vi.fn().mockResolvedValue(null),
24
- writeStallState: vi.fn().mockResolvedValue(undefined),
25
- }));
26
- vi.mock("../state/seen-comments.mts", () => ({
27
- markSeen: vi.fn().mockResolvedValue(undefined),
28
- }));
29
- const { mockLoadConfig } = vi.hoisted(() => ({ mockLoadConfig: vi.fn() }));
30
- vi.mock("../config/load.mts", () => ({ loadConfig: mockLoadConfig }));
31
- import { runIterate } from "./iterate/index.mjs";
32
- import { runCheck } from "./check.mjs";
33
- import { updateReadyDelay } from "./ready-delay.mjs";
34
- import { readFixAttempts, writeFixAttempts } from "../state/fix-attempts.mjs";
35
- import { readStallState, writeStallState } from "../state/iterate-stall.mjs";
36
- import { markSeen } from "../state/seen-comments.mjs";
37
- const mockRunCheck = vi.mocked(runCheck);
38
- const mockUpdateReadyDelay = vi.mocked(updateReadyDelay);
39
- const mockReadFixAttempts = vi.mocked(readFixAttempts);
40
- const mockWriteFixAttempts = vi.mocked(writeFixAttempts);
41
- const mockReadStallState = vi.mocked(readStallState);
42
- const mockWriteStallState = vi.mocked(writeStallState);
43
- const mockMarkSeen = vi.mocked(markSeen);
44
- function makeReport(overrides = {}) {
45
- return {
46
- pr: 42,
47
- nodeId: "PR_kgDOAAA",
48
- repo: "owner/repo",
49
- status: "READY",
50
- baseBranch: "main",
51
- branchProtection: null,
52
- mergeStatus: {
53
- status: "CLEAN",
54
- state: "OPEN",
55
- isDraft: false,
56
- mergeable: "MERGEABLE",
57
- reviewDecision: "APPROVED",
58
- blockingBotReviewInProgress: false,
59
- mergeStateStatus: "CLEAN",
60
- },
61
- checks: {
62
- passing: [],
63
- failing: [],
64
- inProgress: [],
65
- skipped: [],
66
- filtered: [],
67
- filteredNames: [],
68
- blockedByFilteredCheck: false,
69
- },
70
- threads: {
71
- actionable: [],
72
- resolutionOnly: [],
73
- autoResolved: [],
74
- autoResolveErrors: [],
75
- firstLook: [],
76
- },
77
- comments: { actionable: [], firstLook: [] },
78
- changesRequestedReviews: [],
79
- reviewSummaries: [],
80
- firstLookSummaries: [],
81
- editedSummaries: [],
82
- approvedReviews: [],
83
- ...overrides,
84
- };
85
- }
86
- function makeOpts(overrides = {}) {
87
- return {
88
- prNumber: 42,
89
- format: "json",
90
- readyDelaySeconds: 600,
91
- ...overrides,
92
- };
93
- }
94
- export function registerHooks() {
95
- beforeEach(() => {
96
- vi.clearAllMocks();
97
- mockExecFile.mockResolvedValue({ stdout: "abc1234\n", stderr: "" });
98
- mockLoadConfig.mockReturnValue({
99
- iterate: {
100
- fixAttemptsPerThread: 3,
101
- stallTimeoutMinutes: 60,
102
- minimizeApprovals: false,
103
- },
104
- watch: { readyDelayMinutes: 10 },
105
- resolve: {
106
- concurrency: 4,
107
- shaPoll: { intervalMs: 2000, maxAttempts: 10 },
108
- fetchReviewSummaries: true,
109
- },
110
- checks: { ciTriggerEvents: ["pull_request"] },
111
- mergeStatus: { blockingReviewerLogins: [] },
112
- actions: { autoResolveOutdated: false, autoMarkReady: false, commitSuggestions: false },
113
- });
114
- mockReadFixAttempts.mockResolvedValue(null);
115
- mockWriteFixAttempts.mockResolvedValue(undefined);
116
- mockReadStallState.mockResolvedValue(null);
117
- mockWriteStallState.mockResolvedValue(undefined);
118
- });
119
- afterEach(() => {
120
- vi.restoreAllMocks();
121
- });
122
- }
123
- export { makeOpts, makeReport, mockExecFile, mockFetch, mockLoadConfig, mockReadFixAttempts, mockReadStallState, mockMarkSeen, mockRunCheck, mockUpdateReadyDelay, mockWriteFixAttempts, mockWriteStallState, readFixAttempts, readStallState, runCheck, runIterate, updateReadyDelay, writeFixAttempts, writeStallState, };
@@ -1,77 +0,0 @@
1
- import { vi, beforeEach, afterEach } from "vitest";
2
- vi.mock("./iterate/index.mts", () => ({ runIterate: vi.fn() }));
3
- import { runIterate } from "./iterate/index.mjs";
4
- const mockRunIterate = vi.mocked(runIterate);
5
- function makeWaitResult(overrides = {}) {
6
- return {
7
- action: "wait",
8
- pr: 42,
9
- repo: "owner/repo",
10
- status: "IN_PROGRESS",
11
- state: "OPEN",
12
- mergeStateStatus: "BLOCKED",
13
- mergeStatus: "BLOCKED",
14
- reviewDecision: "REVIEW_REQUIRED",
15
- blockingBotReviewInProgress: false,
16
- isDraft: false,
17
- shouldCancel: false,
18
- remainingSeconds: 0,
19
- summary: { passing: 2, failing: 0, inProgress: 1, skipped: 0, filtered: 0 },
20
- baseBranch: "main",
21
- checks: [],
22
- log: "WAIT: 2 passing, 1 in-progress",
23
- ...overrides,
24
- };
25
- }
26
- function makeCancelResult() {
27
- return {
28
- action: "cancel",
29
- pr: 42,
30
- repo: "owner/repo",
31
- status: "READY",
32
- state: "MERGED",
33
- mergeStateStatus: "CLEAN",
34
- mergeStatus: "CLEAN",
35
- reviewDecision: "APPROVED",
36
- blockingBotReviewInProgress: false,
37
- isDraft: false,
38
- shouldCancel: true,
39
- remainingSeconds: 0,
40
- summary: { passing: 3, failing: 0, inProgress: 0, skipped: 0, filtered: 0 },
41
- baseBranch: "main",
42
- checks: [],
43
- reason: "merged",
44
- log: "CANCEL: PR #42 is merged — stopping",
45
- };
46
- }
47
- function makeMarkReadyResult() {
48
- return {
49
- action: "mark_ready",
50
- pr: 42,
51
- repo: "owner/repo",
52
- status: "READY",
53
- state: "OPEN",
54
- mergeStateStatus: "CLEAN",
55
- mergeStatus: "CLEAN",
56
- reviewDecision: "APPROVED",
57
- blockingBotReviewInProgress: false,
58
- isDraft: false,
59
- shouldCancel: false,
60
- remainingSeconds: 0,
61
- summary: { passing: 3, failing: 0, inProgress: 0, skipped: 0, filtered: 0 },
62
- baseBranch: "main",
63
- checks: [],
64
- markedReady: true,
65
- log: "MARKED READY: PR #42 converted from draft to ready for review",
66
- };
67
- }
68
- function registerPollHooks() {
69
- beforeEach(() => {
70
- vi.clearAllMocks();
71
- vi.useFakeTimers();
72
- });
73
- afterEach(() => {
74
- vi.useRealTimers();
75
- });
76
- }
77
- export { mockRunIterate, makeWaitResult, makeCancelResult, makeMarkReadyResult, registerPollHooks };
@@ -1,114 +0,0 @@
1
- import { vi, beforeEach } from "vitest";
2
- vi.mock("../github/client.mts", () => ({
3
- getRepoInfo: vi.fn().mockResolvedValue({ owner: "owner", name: "repo" }),
4
- getCurrentPrNumber: vi.fn().mockResolvedValue(42),
5
- }));
6
- vi.mock("../state/seen-comments.mts", async (importOriginal) => {
7
- const actual = await importOriginal();
8
- return {
9
- ...actual,
10
- loadSeenMap: vi.fn().mockResolvedValue(new Map()),
11
- markSeen: vi.fn().mockResolvedValue(undefined),
12
- };
13
- });
14
- vi.mock("../github/batch.mts", () => ({
15
- fetchPrBatch: vi.fn(),
16
- }));
17
- vi.mock("../comments/resolve.mts", () => ({
18
- autoResolveOutdated: vi.fn(),
19
- applyResolveOptions: vi.fn(),
20
- }));
21
- vi.mock("../config/load.mts", () => ({
22
- loadConfig: vi.fn().mockReturnValue({
23
- resolve: {
24
- shaPoll: { intervalMs: 2000, maxAttempts: 10 },
25
- fetchReviewSummaries: true,
26
- },
27
- actions: {
28
- autoResolveOutdated: true,
29
- autoMarkReady: true,
30
- commitSuggestions: true,
31
- },
32
- }),
33
- }));
34
- import { runResolveFetch, runResolveMutate } from "./resolve.mjs";
35
- import { getCurrentPrNumber } from "../github/client.mjs";
36
- import { fetchPrBatch } from "../github/batch.mjs";
37
- import { autoResolveOutdated, applyResolveOptions } from "../comments/resolve.mjs";
38
- import { loadConfig } from "../config/load.mjs";
39
- import { loadSeenMap, markSeen, hashBody } from "../state/seen-comments.mjs";
40
- const mockGetCurrentPrNumber = vi.mocked(getCurrentPrNumber);
41
- const mockFetchPrBatch = vi.mocked(fetchPrBatch);
42
- const mockAutoResolveOutdated = vi.mocked(autoResolveOutdated);
43
- const mockApplyResolveOptions = vi.mocked(applyResolveOptions);
44
- const mockLoadConfig = vi.mocked(loadConfig);
45
- const mockLoadSeenMap = vi.mocked(loadSeenMap);
46
- const mockMarkSeen = vi.mocked(markSeen);
47
- const BASE_OPTS = { format: "text" };
48
- function makeBatchData(overrides = {}) {
49
- return {
50
- nodeId: "PR_kgDOAAA",
51
- number: 42,
52
- state: "OPEN",
53
- isDraft: false,
54
- mergeable: "MERGEABLE",
55
- mergeStateStatus: "CLEAN",
56
- reviewDecision: "APPROVED",
57
- headRefOid: "abc123",
58
- headRefName: "feature",
59
- headRepoWithOwner: "owner/repo",
60
- baseRefName: "main",
61
- reviewRequests: [],
62
- latestReviews: [],
63
- reviewThreads: [],
64
- comments: [],
65
- changesRequestedReviews: [],
66
- reviewSummaries: [],
67
- approvedReviews: [],
68
- checks: [],
69
- branchProtection: null,
70
- ...overrides,
71
- };
72
- }
73
- function makeThread(overrides = {}) {
74
- return {
75
- id: "t-1",
76
- isResolved: false,
77
- isOutdated: false,
78
- isMinimized: false,
79
- path: "src/foo.ts",
80
- line: 1,
81
- startLine: null,
82
- author: "alice",
83
- authorType: "Unknown",
84
- body: "fix this",
85
- url: "",
86
- createdAtUnix: 1_700_000_000,
87
- ...overrides,
88
- };
89
- }
90
- function makeComment(overrides = {}) {
91
- return {
92
- id: "c-1",
93
- isMinimized: false,
94
- author: "bob",
95
- authorType: "Unknown",
96
- body: "nit",
97
- url: "",
98
- createdAtUnix: 1_700_000_000,
99
- ...overrides,
100
- };
101
- }
102
- export function registerHooks() {
103
- beforeEach(() => {
104
- vi.clearAllMocks();
105
- mockAutoResolveOutdated.mockResolvedValue({ resolved: [], errors: [] });
106
- mockApplyResolveOptions.mockResolvedValue({
107
- resolvedThreads: [],
108
- minimizedComments: [],
109
- dismissedReviews: [],
110
- errors: [],
111
- });
112
- });
113
- }
114
- export { BASE_OPTS, applyResolveOptions, autoResolveOutdated, fetchPrBatch, getCurrentPrNumber, hashBody, loadConfig, loadSeenMap, makeBatchData, makeComment, makeThread, markSeen, mockApplyResolveOptions, mockAutoResolveOutdated, mockFetchPrBatch, mockGetCurrentPrNumber, mockLoadConfig, mockLoadSeenMap, mockMarkSeen, runResolveFetch, runResolveMutate, };
@@ -1,7 +0,0 @@
1
- import { buildShepherdJournalInstruction, SHEPHERD_JOURNAL_APPEND_HINT, SHEPHERD_JOURNAL_FIRST_LOOK_GUIDANCE, SHEPHERD_JOURNAL_REFERENCE_GUIDANCE_THREADS_AND_COMMENTS_IN_ITEM_HEADINGS, SHEPHERD_JOURNAL_REFERENCE_GUIDANCE_THREADS_AND_COMMENTS_IN_ITEMS, SHEPHERD_JOURNAL_SECTION, SHEPHERD_JOURNAL_SECTION_PATTERN, } from "./shepherd-journal.mjs";
2
- import { buildFixInstructions } from "./iterate/render.mjs";
3
- import { buildFetchInstructions } from "./resolve-instructions.mjs";
4
- function countMentions(text, phrase) {
5
- return (text.match(new RegExp(phrase, "g")) ?? []).length;
6
- }
7
- export { SHEPHERD_JOURNAL_APPEND_HINT, SHEPHERD_JOURNAL_FIRST_LOOK_GUIDANCE, SHEPHERD_JOURNAL_REFERENCE_GUIDANCE_THREADS_AND_COMMENTS_IN_ITEMS, SHEPHERD_JOURNAL_REFERENCE_GUIDANCE_THREADS_AND_COMMENTS_IN_ITEM_HEADINGS, SHEPHERD_JOURNAL_SECTION, SHEPHERD_JOURNAL_SECTION_PATTERN, buildFetchInstructions, buildFixInstructions, buildShepherdJournalInstruction, countMentions, };
@@ -1,15 +0,0 @@
1
- /**
2
- * Determines which review threads should be auto-resolved as outdated.
3
- *
4
- * A thread is eligible for auto-resolution when:
5
- * - `isOutdated == true` (GitHub marks these when the diff hunk changed), AND
6
- * - `isResolved == false`.
7
- *
8
- * GitHub's `isOutdated` flag means the thread's referenced code has changed
9
- * enough that the comment no longer points to a live diff line. These threads
10
- * are visually collapsed on GitHub and are safe to resolve programmatically.
11
- */
12
- /** Returns the subset of threads that should be auto-resolved as outdated. */
13
- export function getOutdatedThreads(threads) {
14
- return threads.filter((t) => t.isOutdated && !t.isResolved);
15
- }
@@ -1,39 +0,0 @@
1
- import { vi, beforeEach } from "vitest";
2
- // ---------------------------------------------------------------------------
3
- // Mock github/client.mts before any imports.
4
- // ---------------------------------------------------------------------------
5
- vi.mock("../github/client.mts", () => ({
6
- graphqlWithRateLimit: vi.fn(),
7
- getPrHeadSha: vi.fn(),
8
- }));
9
- import { applyResolveOptions, autoResolveOutdated } from "./resolve.mjs";
10
- import { graphqlWithRateLimit, getPrHeadSha } from "../github/client.mjs";
11
- const mockGraphql = vi.mocked(graphqlWithRateLimit);
12
- const mockGetPrHeadSha = vi.mocked(getPrHeadSha);
13
- const REPO = { owner: "owner", name: "repo" };
14
- /** Build a mock response with the correct nested shape for each alias type (r/m/d). */
15
- function makeBulkResponse(doc) {
16
- const str = typeof doc === "string" ? doc : "";
17
- const data = {};
18
- for (const [, alias] of str.matchAll(/^\s+([a-z]\d+):/gm)) {
19
- if (alias.startsWith("r"))
20
- data[alias] = { thread: { isResolved: true } };
21
- else if (alias.startsWith("m"))
22
- data[alias] = { minimizedComment: { isMinimized: true } };
23
- else if (alias.startsWith("d"))
24
- data[alias] = { pullRequestReview: { state: "DISMISSED" } };
25
- else
26
- data[alias] = {};
27
- }
28
- return { data };
29
- }
30
- // ---------------------------------------------------------------------------
31
- // applyResolveOptions
32
- // ---------------------------------------------------------------------------
33
- export function registerHooks() {
34
- beforeEach(() => {
35
- vi.clearAllMocks();
36
- mockGraphql.mockImplementation(async (doc) => makeBulkResponse(doc));
37
- });
38
- }
39
- export { REPO, applyResolveOptions, autoResolveOutdated, getPrHeadSha, graphqlWithRateLimit, makeBulkResponse, mockGetPrHeadSha, mockGraphql, };
@@ -1,66 +0,0 @@
1
- import { vi, beforeEach } from "vitest";
2
- vi.mock("./client.mts", () => ({
3
- graphql: vi.fn(),
4
- graphqlWithRateLimit: vi.fn(),
5
- }));
6
- import { fetchPrBatch } from "./batch.mjs";
7
- import { graphql, graphqlWithRateLimit } from "./client.mjs";
8
- const mockGraphql = vi.mocked(graphql);
9
- const mockGraphqlWithRateLimit = vi.mocked(graphqlWithRateLimit);
10
- const REPO = { owner: "owner", name: "repo" };
11
- // ---------------------------------------------------------------------------
12
- // Helpers
13
- // ---------------------------------------------------------------------------
14
- function makeRawPr(overrides = {}) {
15
- return {
16
- id: "PR_kgDOAAA",
17
- number: 42,
18
- state: "OPEN",
19
- isDraft: false,
20
- mergeable: "MERGEABLE",
21
- mergeStateStatus: "CLEAN",
22
- reviewDecision: "APPROVED",
23
- headRefOid: "abc123",
24
- headRefName: "feature",
25
- headRepository: { nameWithOwner: "owner/repo" },
26
- baseRefName: "main",
27
- reviewRequests: { nodes: [] },
28
- latestReviews: { nodes: [] },
29
- reviewThreads: {
30
- pageInfo: { hasPreviousPage: false, startCursor: null },
31
- nodes: [],
32
- },
33
- comments: {
34
- pageInfo: { hasPreviousPage: false, startCursor: null },
35
- nodes: [],
36
- },
37
- changesRequestedReviews: {
38
- pageInfo: { hasPreviousPage: false, startCursor: null },
39
- nodes: [],
40
- },
41
- reviewSummaries: {
42
- pageInfo: { hasPreviousPage: false, startCursor: null },
43
- nodes: [],
44
- },
45
- approvedReviews: {
46
- pageInfo: { hasPreviousPage: false, startCursor: null },
47
- nodes: [],
48
- },
49
- commits: { nodes: [{ commit: { statusCheckRollup: null } }] },
50
- ...overrides,
51
- };
52
- }
53
- function makeResponse(pr = makeRawPr()) {
54
- return { data: { repository: { pullRequest: pr } } };
55
- }
56
- // ---------------------------------------------------------------------------
57
- // PR not found
58
- // ---------------------------------------------------------------------------
59
- export function registerHooks() {
60
- beforeEach(() => {
61
- vi.clearAllMocks();
62
- mockGraphql.mockResolvedValue(makeResponse());
63
- mockGraphqlWithRateLimit.mockResolvedValue(makeResponse());
64
- });
65
- }
66
- export { REPO, fetchPrBatch, graphql, graphqlWithRateLimit, makeRawPr, makeResponse, mockGraphql, mockGraphqlWithRateLimit, };
@@ -1,66 +0,0 @@
1
- import { vi, beforeEach } from "vitest";
2
- vi.mock("./client.mts", () => ({
3
- graphql: vi.fn(),
4
- graphqlWithRateLimit: vi.fn(),
5
- }));
6
- import { fetchPrBatch } from "./batch.mjs";
7
- import { graphql, graphqlWithRateLimit } from "./client.mjs";
8
- const mockGraphql = vi.mocked(graphql);
9
- const mockGraphqlWithRateLimit = vi.mocked(graphqlWithRateLimit);
10
- const REPO = { owner: "owner", name: "repo" };
11
- // ---------------------------------------------------------------------------
12
- // Helpers
13
- // ---------------------------------------------------------------------------
14
- function makeRawPr(overrides = {}) {
15
- return {
16
- id: "PR_kgDOAAA",
17
- number: 42,
18
- state: "OPEN",
19
- isDraft: false,
20
- mergeable: "MERGEABLE",
21
- mergeStateStatus: "CLEAN",
22
- reviewDecision: "APPROVED",
23
- headRefOid: "abc123",
24
- headRefName: "feature",
25
- headRepository: { nameWithOwner: "owner/repo" },
26
- baseRefName: "main",
27
- reviewRequests: { nodes: [] },
28
- latestReviews: { nodes: [] },
29
- reviewThreads: {
30
- pageInfo: { hasPreviousPage: false, startCursor: null },
31
- nodes: [],
32
- },
33
- comments: {
34
- pageInfo: { hasPreviousPage: false, startCursor: null },
35
- nodes: [],
36
- },
37
- changesRequestedReviews: {
38
- pageInfo: { hasPreviousPage: false, startCursor: null },
39
- nodes: [],
40
- },
41
- reviewSummaries: {
42
- pageInfo: { hasPreviousPage: false, startCursor: null },
43
- nodes: [],
44
- },
45
- approvedReviews: {
46
- pageInfo: { hasPreviousPage: false, startCursor: null },
47
- nodes: [],
48
- },
49
- commits: { nodes: [{ commit: { statusCheckRollup: null } }] },
50
- ...overrides,
51
- };
52
- }
53
- function makeResponse(pr = makeRawPr()) {
54
- return { data: { repository: { pullRequest: pr } } };
55
- }
56
- // ---------------------------------------------------------------------------
57
- // reviewSummaries — COMMENTED reviews surfaced for agent-driven minimize
58
- // ---------------------------------------------------------------------------
59
- export function registerHooks() {
60
- beforeEach(() => {
61
- vi.clearAllMocks();
62
- mockGraphql.mockResolvedValue(makeResponse());
63
- mockGraphqlWithRateLimit.mockResolvedValue(makeResponse());
64
- });
65
- }
66
- export { REPO, fetchPrBatch, graphql, graphqlWithRateLimit, makeRawPr, makeResponse, mockGraphql, mockGraphqlWithRateLimit, };