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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pr-shepherd",
3
3
  "description": "Autonomous PR CI monitor and review-comment resolver for agentic coding tools",
4
- "version": "0.59.2",
4
+ "version": "0.59.3",
5
5
  "author": {
6
6
  "name": "Jonathan Ong",
7
7
  "email": "jonathanrichardong@gmail.com"
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. Commit and push any changes.
37
- 4. Journal key decisions or rejections with URLs or IDs: `pr-shepherd apply journal 123 '- <decision>'`
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 log a Shepherd Journal entry; set it to `false` to keep comments untouched. See [docs/configuration.md](docs/configuration.md#classifystandard--default-).
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:
@@ -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, tick line, and Shepherd Journal when this rule fires. */
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;
@@ -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
- * The generic commit/push step and the journal step. Playbook style folds both into one pointer
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
- * Playbook-style replacement for the invariant commit/push and journal steps. The
8
- * procedure text lives in the "Fix-code loop" playbook; only the trigger and the concrete
9
- * journal command stay in the per-tick output. `inline` style never calls this.
10
- */
11
- function buildFixLoopInstruction(prReference, journal) {
12
- const parts = ["Commit and push any changes."];
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
- parts.push(`${playbookPointer(FIX_CODE_LOOP_PLAYBOOK)} CLI: \`${cli}\`.`);
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, viewerCanUpdate?: boolean, hasExhaustedWorkflowRerun?: boolean, stackRebase?: string, // native stack layers rebase with gh-stack, not branch by branch
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 { buildPushJournalSteps } from "./fix-loop-instruction.mjs";
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, viewerCanUpdate = false, hasExhaustedWorkflowRerun = false, stackRebase, // native stack layers rebase with gh-stack, not branch by branch
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 && viewerCanUpdate)
52
- instructions.push(SHEPHERD_JOURNAL_FIRST_LOOK_GUIDANCE);
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(...buildPushJournalSteps(instructionStyle, prReference, genericPush, wantsJournal));
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
- export function reasonClause(reasons) {
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
- export function ruleAutoResolveBody(counts) {
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;
@@ -13,9 +13,4 @@ export declare function applySuppressedRuleAutoResolve(input: {
13
13
  enabled: boolean;
14
14
  partition: BatchPartition;
15
15
  batch: BatchPrData;
16
- prNumber: number;
17
- repo: {
18
- owner: string;
19
- name: string;
20
- };
21
16
  }): Promise<RuleAutoResolveApplied>;
@@ -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, formatRuleAutoResolveJournalItem, minimizedItem, reasonClause, reasonsForError, resolvedThread, ruleAutoResolveBody, uniqueReasons, } from "./rule-auto-resolve-format.mjs";
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
- }
@@ -81,8 +81,8 @@ export interface PrShepherdConfig {
81
81
  */
82
82
  resolveOtherHumanThreads: ResolveOtherHumanThreads;
83
83
  /**
84
- * `inline` (default) prints the commit/push and journal steps of `fix_code` instructions
85
- * every tick; `playbook` folds them into one `Playbook: "Fix-code loop"` pointer. The
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;
@@ -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, push and journal into the Fix-code loop playbook."),
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,6 +1,6 @@
1
1
  {
2
2
  "name": "pr-shepherd",
3
- "version": "0.59.2",
3
+ "version": "0.59.3",
4
4
  "description": "Autonomous PR CI monitor and review-comment resolver for agentic coding tools",
5
5
  "keywords": [
6
6
  "automation",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pr-shepherd",
3
3
  "description": "Autonomous PR CI monitor and review-comment resolver for agentic coding tools",
4
- "version": "0.59.2",
4
+ "version": "0.59.3",
5
5
  "author": {
6
6
  "name": "Jonathan Ong",
7
7
  "email": "jonathanrichardong@gmail.com"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pr-shepherd",
3
- "version": "0.59.2",
3
+ "version": "0.59.3",
4
4
  "description": "Autonomous PR CI monitor and review-comment resolver for Codex.",
5
5
  "author": {
6
6
  "name": "Jonathan Ong",
@@ -2,7 +2,7 @@
2
2
  "mcpServers": {
3
3
  "pr-shepherd": {
4
4
  "command": "npx",
5
- "args": ["--yes", "--package", "pr-shepherd@0.59.2", "pr-shepherd-mcp"]
5
+ "args": ["--yes", "--package", "pr-shepherd@0.59.3", "pr-shepherd-mcp"]
6
6
  }
7
7
  }
8
8
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "pr-shepherd": {
3
3
  "command": "npx",
4
- "args": ["--yes", "--package", "pr-shepherd@0.59.2", "pr-shepherd-mcp"]
4
+ "args": ["--yes", "--package", "pr-shepherd@0.59.3", "pr-shepherd-mcp"]
5
5
  }
6
6
  }
@@ -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, push, and journal 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.
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. If a Shepherd Journal step is printed, record the finding with it. Make no change. The unchanged failure escalates through the stall timeout.
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 the note printed, reported, and journaled 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, record `threads.autoResolved` / `comments.autoMinimized`, and append one Shepherd Journal item for the token login. 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, journaling, 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.
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.
@@ -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, tick line, and Shepherd Journal when this rule fires. */
56
+ /** Note included in the auto-resolve report and tick line when this rule fires. */
57
57
  readonly reason?: string;
58
58
  }
59
59