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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +87 -125
- package/bin/cli/args.mjs +2 -0
- package/bin/cli/fix-formatter.mjs +3 -1
- package/bin/cli/formatters.mjs +3 -45
- package/bin/cli/handlers.mjs +22 -1
- package/bin/cli/help-command-pages.mjs +27 -2
- package/bin/cli/help-top-page.mjs +2 -0
- package/bin/cli/iterate-instructions.mjs +1 -1
- package/bin/cli/list-formatters.mjs +5 -2
- package/bin/cli/mark-files-as-viewed-flags.mjs +34 -0
- package/bin/cli/mark-files-as-viewed-formatter.mjs +52 -0
- package/bin/cli/mutate-formatter.mjs +50 -0
- package/bin/cli-parser.mjs +11 -2
- package/bin/commands/check.mjs +25 -56
- package/bin/commands/commit-suggestion-instruction.mjs +1 -1
- package/bin/commands/iterate/classify.mjs +32 -31
- package/bin/commands/iterate/fix-code.mjs +23 -19
- package/bin/commands/iterate/index.mjs +4 -1
- package/bin/commands/iterate/render.mjs +6 -3
- package/bin/commands/iterate/stall.mjs +1 -1
- package/bin/commands/mark-files-as-viewed.mjs +220 -0
- package/bin/commands/resolve-instructions.mjs +5 -2
- package/bin/commands/resolve-mutate.mjs +49 -4
- package/bin/commands/resolve.mjs +21 -78
- package/bin/commands/shepherd-journal.mjs +1 -1
- package/bin/comments/authors.mjs +32 -0
- package/bin/comments/minimize-policy.mjs +8 -4
- package/bin/comments/pending-ops.mjs +6 -0
- package/bin/comments/resolve.mjs +28 -11
- package/bin/comments/review-thread-markers.mjs +18 -0
- package/bin/comments/review-visibility.mjs +14 -0
- package/bin/comments/thread-visibility.mjs +60 -0
- package/bin/comments/visible-comments.mjs +2 -2
- package/bin/config/load.mjs +7 -0
- package/bin/config.json +14 -0
- package/bin/github/batch-parser-helpers.mjs +3 -4
- package/bin/github/batch-parsers.mjs +8 -6
- package/bin/github/client.mjs +1 -2
- package/bin/github/gql/batch-pr.gql +3 -0
- package/bin/reporters/agent.mjs +2 -0
- package/bin/state/seen-comments.mjs +40 -4
- package/bin/threads/transcript.mjs +6 -6
- package/package.json +8 -4
- package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
- package/plugins/pr-shepherd/skills/mark-files-as-viewed/SKILL.md +31 -0
- package/bin/checks/triage.test-support.mjs +0 -60
- package/bin/cli/iterate-lean.test-support.mjs +0 -3
- package/bin/cli-parser.clean.test-support.mjs +0 -44
- package/bin/cli-parser.commit-suggestion.test-support.mjs +0 -65
- package/bin/cli-parser.iterate-fix.test-support.mjs +0 -43
- package/bin/cli-parser.iterate-fixtures.mjs +0 -75
- package/bin/cli-parser.iterate.test-support.mjs +0 -44
- package/bin/cli-parser.test-support.mjs +0 -42
- package/bin/commands/check.test-support.mjs +0 -147
- package/bin/commands/clean.test-support.mjs +0 -47
- package/bin/commands/commit-suggestion.apply.test-support.mjs +0 -87
- package/bin/commands/commit-suggestion.test-support.mjs +0 -112
- package/bin/commands/iterate-stall.test-support.mjs +0 -24
- package/bin/commands/iterate-test-support.mjs +0 -150
- package/bin/commands/iterate.fix-code-in-progress.test-support.mjs +0 -123
- package/bin/commands/poll.test-support.mjs +0 -77
- package/bin/commands/resolve.test-support.mjs +0 -114
- package/bin/commands/shepherd-journal.test-support.mjs +0 -7
- package/bin/comments/outdated.mjs +0 -15
- package/bin/comments/resolve.test-support.mjs +0 -39
- package/bin/github/batch-parsers.test-support.mjs +0 -66
- package/bin/github/batch.test-support.mjs +0 -66
- package/bin/github/client.test-support.mjs +0 -55
- package/bin/github/http.test-support.mjs +0 -51
- package/bin/state/seen-comments.test-support.mjs +0 -19
- 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, };
|