pr-shepherd 0.59.2 → 0.59.3
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 +3 -3
- package/bin/classify/types.d.mts +1 -1
- package/bin/commands/check.mjs +0 -2
- package/bin/commands/iterate/fix-loop-instruction.d.mts +2 -8
- package/bin/commands/iterate/fix-loop-instruction.mjs +8 -29
- package/bin/commands/iterate/render.d.mts +1 -1
- package/bin/commands/iterate/render.mjs +17 -13
- package/bin/commands/rule-auto-resolve-format.d.mts +0 -12
- package/bin/commands/rule-auto-resolve-format.mjs +2 -11
- package/bin/commands/rule-auto-resolve.d.mts +0 -5
- package/bin/commands/rule-auto-resolve.mjs +1 -42
- package/bin/commands/shepherd-journal.d.mts +0 -9
- package/bin/commands/shepherd-journal.mjs +0 -15
- package/bin/config/load.d.mts +2 -2
- package/bin/mcp/server.mjs +1 -1
- package/package.json +1 -1
- package/plugins/pr-shepherd/.claude-plugin/plugin.json +1 -1
- package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
- package/plugins/pr-shepherd/.codex.mcp.json +1 -1
- package/plugins/pr-shepherd/.mcp.json +1 -1
- package/plugins/pr-shepherd/skills/pr-shepherd/references/fix-code-loop.md +1 -2
- package/plugins/pr-shepherd/skills/pr-shepherd/references/merge-queue-ejection.md +1 -1
- package/plugins/pr-shepherd/skills/reduce-pr-noise/references/classifiers.md +1 -1
- package/src/classify/types.mts +1 -1
package/README.md
CHANGED
|
@@ -33,8 +33,8 @@ $ pr-shepherd 123
|
|
|
33
33
|
|
|
34
34
|
1. Fix each warranted item.
|
|
35
35
|
2. Triage `## Failing checks`. Playbook: "CI failure triage".
|
|
36
|
-
3.
|
|
37
|
-
4.
|
|
36
|
+
3. Optional: If you reject a code review suggestion, journal why: `pr-shepherd apply journal 123 '- <reason>'`.
|
|
37
|
+
4. Commit and push any changes.
|
|
38
38
|
5. `$DISMISS_MESSAGE`: one sentence on what changed. Run even if no code changed: `pr-shepherd apply review 123 --reply-thread-ids PRRT_kwDOSGizTs58XB1L --message "$DISMISS_MESSAGE" --require-sha HEAD`
|
|
39
39
|
6. Rerun Shepherd now.
|
|
40
40
|
```
|
|
@@ -151,7 +151,7 @@ See [docs/graphql-usage.md](docs/graphql-usage.md) and [docs/rest-usage.md](docs
|
|
|
151
151
|
|
|
152
152
|
1. **Install the MCP server and the CLI.** See [Install](#install) below.
|
|
153
153
|
2. **Write your policy into a skill or agent instruction.** Examples: "skip non-blocking review comments", "reply instead of fixing nits", "never merge". Shepherd reports state and the next action. Your instructions decide how the agent treats each comment. The shipped `pr-shepherd` skill is a thin dispatcher you can wrap ([docs/skills.md](docs/skills.md)).
|
|
154
|
-
3. **Configure Shepherd.** In `.pr-shepherdrc.yml`, turn on the bundled classification rules. They drop known bot noise before it reaches the agent, which saves more tokens and tool calls. With the default `actions.autoMinimizeSuppressed: true`, several rules also minimize matching comments on GitHub and
|
|
154
|
+
3. **Configure Shepherd.** In `.pr-shepherdrc.yml`, turn on the bundled classification rules. They drop known bot noise before it reaches the agent, which saves more tokens and tool calls. With the default `actions.autoMinimizeSuppressed: true`, several rules also minimize matching comments on GitHub and surface the mutation result without editing the PR body. See [docs/configuration.md](docs/configuration.md#classifystandard--default-).
|
|
155
155
|
|
|
156
156
|
```yaml
|
|
157
157
|
classify:
|
package/bin/classify/types.d.mts
CHANGED
|
@@ -29,7 +29,7 @@ export interface ClassifyAction {
|
|
|
29
29
|
readonly autoResolve?: boolean;
|
|
30
30
|
/** When true, hides the item from agent output (seen marker is still written). */
|
|
31
31
|
readonly suppress?: boolean;
|
|
32
|
-
/** Note included in the auto-resolve report
|
|
32
|
+
/** Note included in the auto-resolve report and tick line when this rule fires. */
|
|
33
33
|
readonly reason?: string;
|
|
34
34
|
}
|
|
35
35
|
export type ClassifyRule = (item: ClassifyItem) => ClassifyAction | null | undefined;
|
package/bin/commands/check.mjs
CHANGED
|
@@ -350,8 +350,6 @@ async function runScopedCheck(opts, context, repo, prNumber, stateKey) {
|
|
|
350
350
|
enabled: opts.autoMinimizeSuppressed === true,
|
|
351
351
|
partition: authorizedPartition,
|
|
352
352
|
batch: batchData,
|
|
353
|
-
prNumber,
|
|
354
|
-
repo,
|
|
355
353
|
});
|
|
356
354
|
const visibleMutationThreadIds = new Set([...threadVisibility.activeThreads, ...threadVisibility.resolutionOnlyThreads].map((thread) => thread.id));
|
|
357
355
|
const ruleAutoResolveThreadIds = [
|
|
@@ -1,9 +1,3 @@
|
|
|
1
1
|
import type { InstructionStyle } from "../../config/load.mts";
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
* only when the generic push applies: the "Fix-code loop" playbook tells the caller to push to the
|
|
5
|
-
* PR head branch, which would conflict with a conflict or queue-recovery push step. Without the
|
|
6
|
-
* generic push, the journal step stays inline with its citation clause. The rerun
|
|
7
|
-
* step stays inline because it is the one step a halted loop cannot recover from.
|
|
8
|
-
*/
|
|
9
|
-
export declare function buildPushJournalSteps(style: InstructionStyle, prReference: string | number, genericPush: boolean, wantsJournal: boolean): string[];
|
|
2
|
+
/** The generic push stays separate from conflict and queue-recovery push instructions. */
|
|
3
|
+
export declare function buildPushSteps(style: InstructionStyle, genericPush: boolean): string[];
|
|
@@ -1,35 +1,14 @@
|
|
|
1
1
|
import { renderShellCommand } from "../../cli/runner.mjs";
|
|
2
2
|
import { loadConfig } from "../../config/load.mjs";
|
|
3
|
-
import { buildShepherdJournalCommand, buildShepherdJournalInstruction, } from "../shepherd-journal.mjs";
|
|
4
3
|
import { playbookPointer } from "../playbook-pointer.mjs";
|
|
5
4
|
const FIX_CODE_LOOP_PLAYBOOK = "Fix-code loop";
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (journal)
|
|
14
|
-
parts.push(`Journal substantial decisions or rejections with \`${buildShepherdJournalCommand(prReference)}\`.`);
|
|
5
|
+
/** The generic push stays separate from conflict and queue-recovery push instructions. */
|
|
6
|
+
export function buildPushSteps(style, genericPush) {
|
|
7
|
+
if (!genericPush)
|
|
8
|
+
return [];
|
|
9
|
+
const push = "Commit and push any changes.";
|
|
10
|
+
if (style === "inline")
|
|
11
|
+
return [push];
|
|
15
12
|
const cli = renderShellCommand([...loadConfig().cliCommand, "playbook", FIX_CODE_LOOP_PLAYBOOK]);
|
|
16
|
-
|
|
17
|
-
return parts.join(" ");
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* The generic commit/push step and the journal step. Playbook style folds both into one pointer
|
|
21
|
-
* only when the generic push applies: the "Fix-code loop" playbook tells the caller to push to the
|
|
22
|
-
* PR head branch, which would conflict with a conflict or queue-recovery push step. Without the
|
|
23
|
-
* generic push, the journal step stays inline with its citation clause. The rerun
|
|
24
|
-
* step stays inline because it is the one step a halted loop cannot recover from.
|
|
25
|
-
*/
|
|
26
|
-
export function buildPushJournalSteps(style, prReference, genericPush, wantsJournal) {
|
|
27
|
-
if (style === "playbook" && genericPush)
|
|
28
|
-
return [buildFixLoopInstruction(prReference, wantsJournal)];
|
|
29
|
-
const steps = [];
|
|
30
|
-
if (genericPush)
|
|
31
|
-
steps.push("Commit and push any changes.");
|
|
32
|
-
if (wantsJournal)
|
|
33
|
-
steps.push(buildShepherdJournalInstruction(prReference));
|
|
34
|
-
return steps;
|
|
13
|
+
return [`${push} ${playbookPointer(FIX_CODE_LOOP_PLAYBOOK)} CLI: \`${cli}\`.`];
|
|
35
14
|
}
|
|
@@ -8,6 +8,6 @@ import type { InstructionStyle } from "../../config/load.mts";
|
|
|
8
8
|
*/
|
|
9
9
|
export declare function renderResolveCommand(rc: ResolveCommand): string;
|
|
10
10
|
export declare function buildFixInstructions(threads: AgentThread[], actionableComments: AgentComment[], checks: AgentCheck[], changesRequestedReviews: Review[], baseBranch: string, resolveCommand: ResolveCommand, hasConflicts: boolean, prReference: string | number, _cancelledCount: number, firstLookThreads?: FirstLookThread[], firstLookComments?: FirstLookComment[], firstLookSummaries?: Review[], editedSummaries?: Review[], _inProgressRunIds?: string[], resolutionOnlyThreads?: ReviewThread[], resolveOnlyCommand?: ResolveCommand, behindBaseHint?: string, // iterate.behindBaseHint — see buildBehindBaseHintInstruction
|
|
11
|
-
isBehind?: boolean,
|
|
11
|
+
isBehind?: boolean, _viewerCanUpdate?: boolean, hasExhaustedWorkflowRerun?: boolean, stackRebase?: string, // native stack layers rebase with gh-stack, not branch by branch
|
|
12
12
|
queueEjection?: QueueEjectionRecovery, // current merge-queue removal with failing queue checks
|
|
13
13
|
instructionStyle?: InstructionStyle): string[];
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { renderShellCommand } from "../../cli/runner.mjs";
|
|
1
|
+
import { buildPrShepherdCommand, renderShellCommand } from "../../cli/runner.mjs";
|
|
2
2
|
import { buildFailingCheckInstructions, buildCrStaleClause, buildBehindBaseHintInstruction, buildRepeatedWorkflowBranchRecoveryInstructions, buildResolveCommandInstruction, buildFixCompletionInstruction, } from "./check-instructions.mjs";
|
|
3
|
-
import { SHEPHERD_JOURNAL_FIRST_LOOK_GUIDANCE } from "../shepherd-journal.mjs";
|
|
4
3
|
import { isFailingAgentCheck } from "../../checks/conclusions.mjs";
|
|
5
4
|
import { buildCommitSuggestionInstruction } from "../commit-suggestion-instruction.mjs";
|
|
6
5
|
import { partitionFixThreads } from "./fix-instruction-threads.mjs";
|
|
7
6
|
import { buildBranchPushInstruction, buildConflictInstruction } from "./native-stack-rebase.mjs";
|
|
8
7
|
import { queueEjectionSteps } from "./queue-recovery-instructions.mjs";
|
|
9
|
-
import {
|
|
8
|
+
import { buildPushSteps } from "./fix-loop-instruction.mjs";
|
|
10
9
|
/**
|
|
11
10
|
* Render a resolve command as a shell snippet. Appends `--require-sha HEAD` when set: run after
|
|
12
11
|
* the push step, the local HEAD is the pushed PR head, and `apply review` rejects the mutations
|
|
@@ -18,7 +17,7 @@ export function renderResolveCommand(rc) {
|
|
|
18
17
|
}
|
|
19
18
|
const REQUIRE_HEAD_SHA = "--require-sha HEAD";
|
|
20
19
|
export function buildFixInstructions(threads, actionableComments, checks, changesRequestedReviews, baseBranch, resolveCommand, hasConflicts, prReference, _cancelledCount, firstLookThreads = [], firstLookComments = [], firstLookSummaries = [], editedSummaries = [], _inProgressRunIds = [], resolutionOnlyThreads = [], resolveOnlyCommand, behindBaseHint = "", // iterate.behindBaseHint — see buildBehindBaseHintInstruction
|
|
21
|
-
isBehind = false,
|
|
20
|
+
isBehind = false, _viewerCanUpdate = false, hasExhaustedWorkflowRerun = false, stackRebase, // native stack layers rebase with gh-stack, not branch by branch
|
|
22
21
|
queueEjection, // current merge-queue removal with failing queue checks
|
|
23
22
|
instructionStyle = "inline") {
|
|
24
23
|
const instructions = [];
|
|
@@ -48,8 +47,8 @@ instructionStyle = "inline") {
|
|
|
48
47
|
const firstLookTotal = firstLookThreads.length + firstLookComments.length;
|
|
49
48
|
if (firstLookTotal > 0)
|
|
50
49
|
instructions.push("Review every item under `## First-look items` before acting.");
|
|
51
|
-
if (firstLookSummaries.length > 0
|
|
52
|
-
instructions.push(
|
|
50
|
+
if (firstLookSummaries.length > 0)
|
|
51
|
+
instructions.push("Read each body under `## Review summaries (first look)` before acting.");
|
|
53
52
|
const editedTotal = editedSummaries.length +
|
|
54
53
|
actionableComments.filter((c) => c.edited).length +
|
|
55
54
|
firstLookThreads.filter((t) => t.edited).length +
|
|
@@ -72,19 +71,24 @@ instructionStyle = "inline") {
|
|
|
72
71
|
instructions.push(staleClause);
|
|
73
72
|
if (!hintWithConflictStep)
|
|
74
73
|
instructions.push(...branchUpdateHint);
|
|
74
|
+
const hasReviewFeedback = threads.length > 0 ||
|
|
75
|
+
resolutionOnlyThreads.length > 0 ||
|
|
76
|
+
firstLookThreads.length > 0 ||
|
|
77
|
+
changesRequestedReviews.length > 0 ||
|
|
78
|
+
actionableComments.length > 0 ||
|
|
79
|
+
firstLookSummaries.length > 0 ||
|
|
80
|
+
editedSummaries.length > 0;
|
|
81
|
+
if (hasReviewFeedback) {
|
|
82
|
+
const journalCommand = buildPrShepherdCommand(["apply", "journal", String(prReference)]).text;
|
|
83
|
+
instructions.push(`Optional: If you reject a code review suggestion, journal why: \`${journalCommand} '- <reason>'\`.`);
|
|
84
|
+
}
|
|
75
85
|
const hasReviewMutations = resolveCommand.hasMutations || resolveOnlyCommand?.hasMutations === true;
|
|
76
86
|
const mutationSuffix = hasReviewMutations ? " before review mutations" : "";
|
|
77
87
|
const genericPush = !(branchRecovery || queueEjection) && hasNonConflictHints;
|
|
78
|
-
const wantsJournal = viewerCanUpdate &&
|
|
79
|
-
(hasReviewMutations ||
|
|
80
|
-
hasNonConflictHints ||
|
|
81
|
-
firstLookTotal > 0 ||
|
|
82
|
-
firstLookSummaries.length > 0 ||
|
|
83
|
-
editedTotal > 0);
|
|
84
88
|
if (branchRecovery || queueEjection) {
|
|
85
89
|
instructions.push(buildBranchPushInstruction(stackRebase, hasConflicts, mutationSuffix, !branchRecovery));
|
|
86
90
|
}
|
|
87
|
-
instructions.push(...
|
|
91
|
+
instructions.push(...buildPushSteps(instructionStyle, genericPush));
|
|
88
92
|
if (resolveOnlyCommand?.hasMutations)
|
|
89
93
|
instructions.push(`Run: \`${renderResolveCommand(resolveOnlyCommand)}\``);
|
|
90
94
|
instructions.push(...buildResolveCommandInstruction(resolveCommand, renderResolveCommand(resolveCommand)), buildFixCompletionInstruction());
|
|
@@ -1,17 +1,5 @@
|
|
|
1
1
|
import type { AutoMinimizedItem, AutoResolvedThread, RuleAutoResolveReport, ShepherdReport } from "../types.mts";
|
|
2
2
|
export declare function uniqueReasons(reasons: readonly string[]): string[];
|
|
3
|
-
export declare function reasonClause(reasons: readonly string[]): string | undefined;
|
|
4
|
-
export declare function ruleAutoResolveBody(counts: {
|
|
5
|
-
threads: number;
|
|
6
|
-
comments: number;
|
|
7
|
-
reviewSummaries: number;
|
|
8
|
-
}): string | undefined;
|
|
9
|
-
export declare function formatRuleAutoResolveJournalItem(input: {
|
|
10
|
-
body: string;
|
|
11
|
-
reasons: readonly string[];
|
|
12
|
-
viewerLogin?: string;
|
|
13
|
-
urls: readonly string[];
|
|
14
|
-
}): string;
|
|
15
3
|
export declare function decorateAutoResolveError(error: string, reasons: readonly string[]): string;
|
|
16
4
|
export declare function reasonsForError(error: string, ids: readonly string[], ruleReasons: ReadonlyMap<string, readonly string[]>): string[];
|
|
17
5
|
export declare function ruleAutoResolveFromReport(report: ShepherdReport): RuleAutoResolveReport | undefined;
|
|
@@ -7,7 +7,7 @@ export function uniqueReasons(reasons) {
|
|
|
7
7
|
}
|
|
8
8
|
return unique;
|
|
9
9
|
}
|
|
10
|
-
|
|
10
|
+
function reasonClause(reasons) {
|
|
11
11
|
const unique = uniqueReasons(reasons);
|
|
12
12
|
if (unique.length === 0)
|
|
13
13
|
return undefined;
|
|
@@ -21,7 +21,7 @@ function joinedReason(reasons) {
|
|
|
21
21
|
function plural(count, singular, pluralForm = `${singular}s`) {
|
|
22
22
|
return `${count} ${count === 1 ? singular : pluralForm}`;
|
|
23
23
|
}
|
|
24
|
-
|
|
24
|
+
function ruleAutoResolveBody(counts) {
|
|
25
25
|
const parts = [];
|
|
26
26
|
if (counts.threads > 0)
|
|
27
27
|
parts.push(`auto-resolved ${plural(counts.threads, "thread")}`);
|
|
@@ -39,15 +39,6 @@ function withReasonClause(body, reasons) {
|
|
|
39
39
|
const clause = reasonClause(reasons);
|
|
40
40
|
return clause ? `${body} ${clause}` : body;
|
|
41
41
|
}
|
|
42
|
-
export function formatRuleAutoResolveJournalItem(input) {
|
|
43
|
-
const login = input.viewerLogin?.trim();
|
|
44
|
-
const actor = login ? `as @${login}` : "(token login unavailable)";
|
|
45
|
-
const clause = reasonClause(input.reasons);
|
|
46
|
-
const links = input.urls.map((url) => url.trim()).filter(Boolean);
|
|
47
|
-
const reasonSuffix = clause ? ` ${clause}` : "";
|
|
48
|
-
const linkSuffix = links.length > 0 ? `: ${links.join(", ")}` : "";
|
|
49
|
-
return `- ${input.body} ${actor}${reasonSuffix}${linkSuffix}`;
|
|
50
|
-
}
|
|
51
42
|
export function decorateAutoResolveError(error, reasons) {
|
|
52
43
|
const clause = reasonClause(reasons);
|
|
53
44
|
return clause ? `${error} ${clause}` : error;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { autoMinimizeComments, autoResolveThreads } from "../comments/resolve.mjs";
|
|
2
|
-
import { runJournal } from "./journal/index.mjs";
|
|
3
2
|
import { canGenerateGithubMutation } from "../github/mutation-policy.mjs";
|
|
4
|
-
import { decorateAutoResolveError,
|
|
3
|
+
import { decorateAutoResolveError, minimizedItem, reasonsForError, resolvedThread, uniqueReasons, } from "./rule-auto-resolve-format.mjs";
|
|
5
4
|
const EMPTY = {
|
|
6
5
|
threadIds: [],
|
|
7
6
|
commentIds: [],
|
|
@@ -17,9 +16,6 @@ function idsIn(ids, confirmed) {
|
|
|
17
16
|
function decorateErrors(errors, ids, ruleReasons) {
|
|
18
17
|
return errors.map((error) => decorateAutoResolveError(error, reasonsForError(error, ids, ruleReasons)));
|
|
19
18
|
}
|
|
20
|
-
function successReasons(ids, ruleReasons) {
|
|
21
|
-
return uniqueReasons(ids.flatMap((id) => [...(ruleReasons.get(id) ?? [])]));
|
|
22
|
-
}
|
|
23
19
|
export async function applySuppressedRuleAutoResolve(input) {
|
|
24
20
|
if (!input.enabled) {
|
|
25
21
|
return {
|
|
@@ -70,9 +66,6 @@ export async function applySuppressedRuleAutoResolve(input) {
|
|
|
70
66
|
...minimized.errors.flatMap((error) => reasonsForError(error, minimizeIds, input.partition.ruleReasons)),
|
|
71
67
|
...resolved.errors.flatMap((error) => reasonsForError(error, threadIds, input.partition.ruleReasons)),
|
|
72
68
|
]);
|
|
73
|
-
const journalError = await journalSuccesses(input, autoResolved, autoMinimized);
|
|
74
|
-
if (journalError)
|
|
75
|
-
autoResolveErrors.push(journalError);
|
|
76
69
|
return {
|
|
77
70
|
threadIds: input.partition.ruleAutoResolveThreadIds.filter((id) => !resolvedSet.has(id)),
|
|
78
71
|
commentIds: input.partition.ruleAutoResolveCommentIds.filter((id) => !minimizedSet.has(id)),
|
|
@@ -83,37 +76,3 @@ export async function applySuppressedRuleAutoResolve(input) {
|
|
|
83
76
|
errorReasons,
|
|
84
77
|
};
|
|
85
78
|
}
|
|
86
|
-
async function journalSuccesses(input, autoResolved, autoMinimized) {
|
|
87
|
-
const body = ruleAutoResolveBody({
|
|
88
|
-
threads: autoResolved.length,
|
|
89
|
-
comments: autoMinimized.filter((item) => item.kind === "pr-comment").length,
|
|
90
|
-
reviewSummaries: autoMinimized.filter((item) => item.kind === "review-summary").length,
|
|
91
|
-
});
|
|
92
|
-
if (!body)
|
|
93
|
-
return undefined;
|
|
94
|
-
const ids = [...autoResolved.map((thread) => thread.id), ...autoMinimized.map((item) => item.id)];
|
|
95
|
-
const reasons = successReasons(ids, input.partition.ruleReasons);
|
|
96
|
-
const item = formatRuleAutoResolveJournalItem({
|
|
97
|
-
body,
|
|
98
|
-
reasons,
|
|
99
|
-
...(input.batch.viewerLogin ? { viewerLogin: input.batch.viewerLogin } : {}),
|
|
100
|
-
urls: [
|
|
101
|
-
...autoResolved.map((thread) => thread.url),
|
|
102
|
-
...autoMinimized.map((item) => item.url ?? ""),
|
|
103
|
-
],
|
|
104
|
-
});
|
|
105
|
-
try {
|
|
106
|
-
await runJournal({
|
|
107
|
-
prNumber: input.prNumber,
|
|
108
|
-
targetRepository: { owner: input.repo.owner, name: input.repo.name },
|
|
109
|
-
rawItem: item,
|
|
110
|
-
dryRun: false,
|
|
111
|
-
});
|
|
112
|
-
return undefined;
|
|
113
|
-
}
|
|
114
|
-
catch (err) {
|
|
115
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
116
|
-
const clause = reasonClause(reasons);
|
|
117
|
-
return clause ? `journal: ${message} ${clause}` : `journal: ${message}`;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
@@ -4,12 +4,3 @@ export declare const SHEPHERD_JOURNAL_DETAILS_OPEN = "<details>";
|
|
|
4
4
|
export declare const SHEPHERD_JOURNAL_DETAILS_SUMMARY = "<summary>Shepherd Journal</summary>";
|
|
5
5
|
export declare const SHEPHERD_JOURNAL_DETAILS_CLOSE = "</details>";
|
|
6
6
|
export declare const SHEPHERD_JOURNAL_APPEND_HINT = "If Shepherd Journal details already exist, append entries inside them instead of creating another container.";
|
|
7
|
-
export declare const SHEPHERD_JOURNAL_FIRST_LOOK_GUIDANCE = "Read each body under `## Review summaries (first look)` and journal any warranted note before review mutations.";
|
|
8
|
-
/** The concrete `apply journal` command. The placeholder is single-quoted so a substituted decision stays literal in the shell. */
|
|
9
|
-
export declare function buildShepherdJournalCommand(prReference: string | number): string;
|
|
10
|
-
/**
|
|
11
|
-
* Build the Shepherd Journal instruction step. The reference-citation convention (link
|
|
12
|
-
* threads/comments by heading URL, cite reviews by ID) is a short clause, so it stays inline:
|
|
13
|
-
* a separate playbook would cost the agent an extra read turn that outweighs the clause.
|
|
14
|
-
*/
|
|
15
|
-
export declare function buildShepherdJournalInstruction(prReference: string | number): string;
|
|
@@ -1,21 +1,6 @@
|
|
|
1
|
-
import { buildPrShepherdCommand } from "../cli/runner.mjs";
|
|
2
1
|
export const SHEPHERD_JOURNAL_SECTION = "Shepherd Journal";
|
|
3
2
|
export const SHEPHERD_JOURNAL_SECTION_PATTERN = /^##\s+Shepherd\s+Journal$/;
|
|
4
3
|
export const SHEPHERD_JOURNAL_DETAILS_OPEN = "<details>";
|
|
5
4
|
export const SHEPHERD_JOURNAL_DETAILS_SUMMARY = "<summary>Shepherd Journal</summary>";
|
|
6
5
|
export const SHEPHERD_JOURNAL_DETAILS_CLOSE = "</details>";
|
|
7
6
|
export const SHEPHERD_JOURNAL_APPEND_HINT = "If Shepherd Journal details already exist, append entries inside them instead of creating another container.";
|
|
8
|
-
export const SHEPHERD_JOURNAL_FIRST_LOOK_GUIDANCE = `Read each body under \`## Review summaries (first look)\` and journal any warranted note before review mutations.`;
|
|
9
|
-
/** The concrete `apply journal` command. The placeholder is single-quoted so a substituted decision stays literal in the shell. */
|
|
10
|
-
export function buildShepherdJournalCommand(prReference) {
|
|
11
|
-
return `${buildPrShepherdCommand(["apply", "journal", String(prReference)]).text} '- <decision>'`;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Build the Shepherd Journal instruction step. The reference-citation convention (link
|
|
15
|
-
* threads/comments by heading URL, cite reviews by ID) is a short clause, so it stays inline:
|
|
16
|
-
* a separate playbook would cost the agent an extra read turn that outweighs the clause.
|
|
17
|
-
*/
|
|
18
|
-
export function buildShepherdJournalInstruction(prReference) {
|
|
19
|
-
const command = buildShepherdJournalCommand(prReference);
|
|
20
|
-
return `Journal key decisions or rejections with URLs or IDs: \`${command}\``;
|
|
21
|
-
}
|
package/bin/config/load.d.mts
CHANGED
|
@@ -81,8 +81,8 @@ export interface PrShepherdConfig {
|
|
|
81
81
|
*/
|
|
82
82
|
resolveOtherHumanThreads: ResolveOtherHumanThreads;
|
|
83
83
|
/**
|
|
84
|
-
* `inline` (default) prints the commit/push
|
|
85
|
-
* every tick; `playbook` folds
|
|
84
|
+
* `inline` (default) prints the commit/push step of `fix_code` instructions
|
|
85
|
+
* every tick; `playbook` folds it into one `Playbook: "Fix-code loop"` pointer. The
|
|
86
86
|
* default is inline because the token bench measured playbook as a net cost: it adds a read. The `--instructions` flag and MCP `instructions` input override per call.
|
|
87
87
|
*/
|
|
88
88
|
instructions: InstructionStyle;
|
package/bin/mcp/server.mjs
CHANGED
|
@@ -33,7 +33,7 @@ const iterateInputSchema = z
|
|
|
33
33
|
instructions: z
|
|
34
34
|
.enum(["playbook", "inline"])
|
|
35
35
|
.optional()
|
|
36
|
-
.describe("fix_code instruction style. inline (default) prints every step each tick; playbook folds commit
|
|
36
|
+
.describe("fix_code instruction style. inline (default) prints every step each tick; playbook folds commit and push into the Fix-code loop playbook."),
|
|
37
37
|
pollMode: z
|
|
38
38
|
.enum(["auto", "poll", "event"])
|
|
39
39
|
.optional()
|
package/package.json
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
# Fix-code loop
|
|
2
2
|
|
|
3
|
-
Apply when a step says `Playbook: "Fix-code loop"`. It replaces the commit
|
|
3
|
+
Apply when a step says `Playbook: "Fix-code loop"`. It replaces the commit and push procedure that `--instructions=inline` prints on every `[FIX_CODE]` tick. Run the numbered steps in `## Instructions` in order; this is the one that names it.
|
|
4
4
|
|
|
5
5
|
- Review and fix first. Decide per item whether it needs a code change; the reply and resolve mutations are generated either way.
|
|
6
6
|
- Commit and push. If you changed any files (code, docs, or config), commit them and push to the PR head branch. If you did not, do not commit. The repository's pre-push hooks and `AGENTS.md` own lint, typecheck, and format, and its conventions own rebase style and push flags.
|
|
7
|
-
- Journal. Only when the step that names this playbook prints an `apply journal` command: for any substantial decision or rejection, run that command with a `- <decision>` item. Link threads and comments by heading URL and cite reviews by ID. Journal before the review mutations. When no `apply journal` command is printed, journaling is unavailable on this tick; do not build or run one.
|
|
8
7
|
- The `apply review` step stays inline because it needs `$DISMISS_MESSAGE` substitution. Push first: its `--require-sha HEAD` must read the pushed SHA.
|
|
9
8
|
- `[FIX_CODE]` is non-terminal. After the last step, iterate again immediately with the same options. Only `[ESCALATE]` hands work to a human, and `[CANCEL]` ends polling.
|
|
10
9
|
- Playbooks named by other steps (`Playbook: "<name>"`) ship with the package. Without the skill, print one with `pr-shepherd playbook "<name>"`, or the MCP `playbook` tool.
|
|
@@ -5,7 +5,7 @@ Apply when a step says `Playbook: "Merge queue ejection"`. The `**queue removal*
|
|
|
5
5
|
1. Update the PR head from the latest base following the repository's branch-update convention. On a native stack, use the printed stack update route instead. Reproduce the failing step on the updated head.
|
|
6
6
|
2. If the failure belongs to this PR, fix it. The printed push step commits and pushes the fix.
|
|
7
7
|
3. If the failure does not reproduce and the update changed the head, the printed push step pushes it (`gh stack push` on a native stack). Never run a printed `requeue:` or `acknowledge queue removal:` command after a push. Shepherd prints a fresh queue command once the new head is READY.
|
|
8
|
-
4. If the failure comes from the base itself, do not requeue or acknowledge it.
|
|
8
|
+
4. If the failure comes from the base itself, do not requeue or acknowledge it. Make no change. The unchanged failure escalates through the stall timeout.
|
|
9
9
|
5. If the removal reason suggests a person dequeued the PR, skip the step 1 update unless a conflict step requires it. Fix a failure that belongs to this PR; otherwise make no change. Never enqueue.
|
|
10
10
|
6. If the step says no queue command was printed, never enqueue. A failure that does not reproduce follows step 4. Shepherd prints no queue command once the same head was already removed before (`removals on this head` in the header), so a flaky failure is not retried indefinitely.
|
|
11
11
|
7. Otherwise, run the printed `requeue:` or `acknowledge queue removal:` command exactly as printed, and only if all of these hold:
|
|
@@ -21,6 +21,6 @@ const rule: ClassifyRule = (item) => {
|
|
|
21
21
|
export default rule;
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
`suppress: true` removes a matched item from agent output. Add `autoResolve: true` only when the requested policy also calls for resolving its thread or minimizing its comment or review summary. It is unsupported for `changes-requested` reviews, whose dismissal needs a message. Optional `reason` is
|
|
24
|
+
`suppress: true` removes a matched item from agent output. Add `autoResolve: true` only when the requested policy also calls for resolving its thread or minimizing its comment or review summary. It is unsupported for `changes-requested` reviews, whose dismissal needs a message. Optional `reason` is printed and reported when the rule fires. With both flags, `actions.autoMinimizeSuppressed: true` (the default) lets Shepherd perform the authorized mutation, then print a `## Classification auto-resolve` line and record `threads.autoResolved` / `comments.autoMinimized`. It does not append to the Shepherd Journal. Journal notes are optional agent-authored updates made only through an explicit `apply journal` call. Failed IDs stay on the generated `apply review` command and the failure is listed under that line. Set `actions.autoMinimizeSuppressed: false` to keep the handoff without mutating or printing the line. When GitHub does not confirm capability, the item returns to normal first-look visibility. Matching rules combine their flags and keep every non-empty reason, so inspect existing rules before adding one.
|
|
25
25
|
|
|
26
26
|
Exercise the exported rule against the observed item and negative examples: a different author, kind, and substantive message from the same bot. Confirm only the intended item matches before enabling automatic resolution. After editing a rule, restart a persistent MCP server or long-running poll process; it caches loaded rule modules. A new CLI process loads the change. A classifier does not retroactively remove already displayed output.
|
package/src/classify/types.mts
CHANGED
|
@@ -53,7 +53,7 @@ export interface ClassifyAction {
|
|
|
53
53
|
readonly autoResolve?: boolean;
|
|
54
54
|
/** When true, hides the item from agent output (seen marker is still written). */
|
|
55
55
|
readonly suppress?: boolean;
|
|
56
|
-
/** Note included in the auto-resolve report
|
|
56
|
+
/** Note included in the auto-resolve report and tick line when this rule fires. */
|
|
57
57
|
readonly reason?: string;
|
|
58
58
|
}
|
|
59
59
|
|