pr-shepherd 0.60.1 → 1.0.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 +7 -5
- package/bin/api.d.mts +35 -24
- package/bin/api.mjs +68 -48
- package/bin/checks/classify.d.mts +1 -1
- package/bin/checks/classify.mjs +4 -4
- package/bin/checks/log-excerpt.mjs +1 -1
- package/bin/cli/arg-flags.mjs +0 -3
- package/bin/cli/clean-handler.d.mts +1 -1
- package/bin/cli/clean-handler.mjs +5 -15
- package/bin/cli/default-poll.d.mts +2 -0
- package/bin/cli/default-poll.mjs +9 -6
- package/bin/cli/formatters.d.mts +2 -3
- package/bin/cli/formatters.mjs +0 -26
- package/bin/cli/handlers.d.mts +3 -3
- package/bin/cli/handlers.mjs +45 -35
- package/bin/cli/help-command-pages.d.mts +13 -158
- package/bin/cli/help-command-pages.mjs +12 -157
- package/bin/cli/help-iterate-poll-pages.d.mts +4 -3
- package/bin/cli/help-iterate-poll-pages.mjs +76 -36
- package/bin/cli/help-log-file-page.d.mts +1 -1
- package/bin/cli/help-log-file-page.mjs +3 -5
- package/bin/cli/help-top-page.d.mts +1 -1
- package/bin/cli/help-top-page.mjs +19 -20
- package/bin/cli/help.d.mts +14 -159
- package/bin/cli/iterate-checks-formatter.mjs +1 -1
- package/bin/cli/iterate-flags.d.mts +2 -4
- package/bin/cli/iterate-flags.mjs +4 -20
- package/bin/cli/iterate-formatter.d.mts +1 -1
- package/bin/cli/iterate-formatter.mjs +6 -6
- package/bin/cli/iterate-instructions.d.mts +1 -1
- package/bin/cli/iterate-instructions.mjs +5 -6
- package/bin/cli/iterate-lean.mjs +32 -40
- package/bin/cli/iterate-verbose.mjs +1 -1
- package/bin/cli/journal-formatter.mjs +0 -3
- package/bin/cli/journal-handler.d.mts +1 -1
- package/bin/cli/journal-handler.mjs +2 -1
- package/bin/cli/mutate-formatter.mjs +0 -9
- package/bin/cli/next-check-format.d.mts +0 -4
- package/bin/cli/next-check-format.mjs +0 -4
- package/bin/cli/poll-handler.d.mts +3 -0
- package/bin/cli/poll-handler.mjs +88 -47
- package/bin/cli/poll-summary-emitter.mjs +2 -2
- package/bin/cli/poll-summary-formatter.mjs +2 -2
- package/bin/cli/poll-targets.d.mts +2 -2
- package/bin/cli/poll-targets.mjs +15 -35
- package/bin/cli/removed-flags.d.mts +3 -0
- package/bin/cli/removed-flags.mjs +24 -0
- package/bin/cli/runner.d.mts +1 -1
- package/bin/cli/runner.mjs +2 -2
- package/bin/cli/stack-overview.d.mts +0 -1
- package/bin/cli/stack-overview.mjs +1 -2
- package/bin/cli/{fix-formatter-extra.d.mts → triage-formatter-extra.d.mts} +1 -2
- package/bin/cli/{fix-formatter-extra.mjs → triage-formatter-extra.mjs} +0 -6
- package/bin/cli/triage-formatter.d.mts +4 -0
- package/bin/cli/{fix-formatter.mjs → triage-formatter.mjs} +31 -42
- package/bin/cli/unknown-flags.d.mts +6 -0
- package/bin/cli/unknown-flags.mjs +29 -0
- package/bin/cli-parser.mjs +41 -62
- package/bin/commands/check-annotations.d.mts +1 -1
- package/bin/commands/check-annotations.mjs +1 -1
- package/bin/commands/check-review-budget.d.mts +1 -1
- package/bin/commands/check-review-budget.mjs +1 -1
- package/bin/commands/check-wait-detectors.d.mts +1 -1
- package/bin/commands/check-wait-detectors.mjs +1 -1
- package/bin/commands/check.d.mts +1 -1
- package/bin/commands/check.mjs +7 -7
- package/bin/commands/commit-suggestion-instruction.d.mts +2 -2
- package/bin/commands/commit-suggestion-instruction.mjs +3 -3
- package/bin/commands/iterate/api-usage.mjs +8 -8
- package/bin/commands/iterate/check-instructions.d.mts +4 -4
- package/bin/commands/iterate/check-instructions.mjs +8 -8
- package/bin/commands/iterate/classify.mjs +2 -2
- package/bin/commands/iterate/escalate.mjs +6 -6
- package/bin/commands/iterate/index.mjs +20 -23
- package/bin/commands/iterate/merge-plan.mjs +3 -3
- package/bin/commands/iterate/merge-state.mjs +4 -7
- package/bin/commands/iterate/native-stack-rebase.mjs +1 -1
- package/bin/commands/iterate/render.d.mts +1 -1
- package/bin/commands/iterate/render.mjs +2 -2
- package/bin/commands/iterate/stall.mjs +11 -9
- package/bin/commands/iterate/{fix-loop-instruction.mjs → triage-loop-instruction.mjs} +7 -3
- package/bin/commands/iterate/{fix-code.d.mts → triage.d.mts} +2 -2
- package/bin/commands/iterate/{fix-code.mjs → triage.mjs} +14 -23
- package/bin/commands/iterate/unreported-required.mjs +2 -5
- package/bin/commands/journal/index.d.mts +1 -3
- package/bin/commands/journal/index.mjs +1 -1
- package/bin/commands/mark-files-as-viewed.d.mts +1 -3
- package/bin/commands/mark-files-as-viewed.mjs +1 -1
- package/bin/commands/next-check.d.mts +2 -2
- package/bin/commands/next-check.mjs +4 -2
- package/bin/commands/poll-progress.mjs +1 -1
- package/bin/commands/poll-summary-explicit-instructions.mjs +1 -1
- package/bin/commands/poll-summary-instructions.mjs +7 -7
- package/bin/commands/poll-summary.mjs +4 -4
- package/bin/commands/poll.d.mts +1 -1
- package/bin/commands/poll.mjs +4 -4
- package/bin/commands/resolve-mutate.d.mts +1 -1
- package/bin/commands/resolve-mutate.mjs +1 -1
- package/bin/commands/rest-stack-merge-readiness.mjs +1 -1
- package/bin/commands/single-tick.d.mts +5 -0
- package/bin/commands/single-tick.mjs +7 -0
- package/bin/commands/stack-drain.mjs +1 -1
- package/bin/commands/stack-merge-flag.mjs +1 -1
- package/bin/commands/stack-stall.d.mts +2 -2
- package/bin/commands/stack-stall.mjs +2 -2
- package/bin/commands/stack-work.d.mts +2 -2
- package/bin/commands/stack-work.mjs +2 -2
- package/bin/commands/suggestion-patch-item.d.mts +0 -1
- package/bin/commands/suggestion-patch-item.mjs +0 -12
- package/bin/commands/suggestion-patches.d.mts +2 -2
- package/bin/commands/suggestion-patches.mjs +1 -1
- package/bin/commands/tick-instructions.d.mts +9 -0
- package/bin/commands/tick-instructions.mjs +23 -0
- package/bin/commands/tick.d.mts +15 -0
- package/bin/commands/{event-mode.mjs → tick.mjs} +39 -55
- package/bin/comments/minimize-policy.mjs +1 -3
- package/bin/comments/resolve.d.mts +0 -23
- package/bin/comments/resolve.mjs +0 -4
- package/bin/comments/review-output-budget.d.mts +1 -1
- package/bin/comments/review-output-budget.mjs +1 -1
- package/bin/comments/sha-poll.mjs +3 -1
- package/bin/config/default-bot-usernames.d.mts +2 -0
- package/bin/config/default-bot-usernames.mjs +15 -0
- package/bin/config/load.d.mts +4 -138
- package/bin/config/load.mjs +88 -443
- package/bin/config/merge-command-args.mjs +2 -2
- package/bin/config/merge-method.d.mts +1 -2
- package/bin/config/merge-method.mjs +1 -8
- package/bin/config/renames.d.mts +8 -0
- package/bin/config/renames.mjs +87 -0
- package/bin/config/resolve.d.mts +31 -0
- package/bin/config/resolve.mjs +89 -0
- package/bin/config/schema.d.mts +161 -0
- package/bin/config/schema.mjs +168 -0
- package/bin/exit-codes.d.mts +1 -1
- package/bin/exit-codes.mjs +3 -3
- package/bin/github/poll-summary-check-blockers.mjs +1 -1
- package/bin/github/poll-summary-command.mjs +8 -6
- package/bin/github/poll-summary-projector.mjs +2 -2
- package/bin/github/poll-summary-review.mjs +1 -1
- package/bin/github/poll-summary-route.mjs +9 -9
- package/bin/github/poll-summary-unreported.mjs +1 -1
- package/bin/github/poll-summary.mjs +1 -1
- package/bin/github/queries.d.mts +1 -1
- package/bin/github/queries.mjs +1 -1
- package/bin/mcp/server.mjs +48 -54
- package/bin/merge-status/derive.mjs +1 -1
- package/bin/state/durable-state.d.mts +1 -3
- package/bin/state/durable-state.mjs +2 -8
- package/bin/state/fix-attempts.d.mts +2 -2
- package/bin/state/fix-attempts.mjs +1 -1
- package/bin/state/merge-request.mjs +1 -1
- package/bin/state/pr-fingerprint.mjs +4 -11
- package/bin/state/seen-comments.d.mts +4 -5
- package/bin/state/seen-comments.mjs +8 -14
- package/bin/state/stack-stall.d.mts +1 -1
- package/bin/state/stack-stall.mjs +1 -1
- package/bin/state/stall-state-store.d.mts +1 -1
- package/bin/state/stall-state-store.mjs +1 -1
- package/bin/types/escalate.d.mts +2 -2
- package/bin/types/iterate.d.mts +12 -26
- package/bin/types/merge-queue.d.mts +2 -2
- package/bin/types/next-check.d.mts +1 -1
- package/bin/types/poll-summary.d.mts +3 -6
- package/bin/types/report.d.mts +2 -2
- package/bin/types/suggestion-patch.d.mts +1 -8
- package/package.json +2 -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/SKILL.md +6 -6
- package/plugins/pr-shepherd/skills/pr-shepherd/references/ci-failure-triage.md +1 -1
- package/plugins/pr-shepherd/skills/pr-shepherd/references/{cloud-event-loop.md → event-loop.md} +5 -5
- package/plugins/pr-shepherd/skills/pr-shepherd/references/mcp-fallback.md +3 -3
- package/plugins/pr-shepherd/skills/pr-shepherd/references/merge-queue-ejection.md +1 -1
- package/plugins/pr-shepherd/skills/pr-shepherd/references/suggestion-patches.md +1 -1
- package/plugins/pr-shepherd/skills/pr-shepherd/references/{fix-code-loop.md → triage-loop.md} +3 -3
- package/plugins/pr-shepherd/skills/reduce-pr-noise/SKILL.md +1 -1
- package/plugins/pr-shepherd/skills/reduce-pr-noise/references/classifiers.md +2 -2
- package/plugins/pr-shepherd/skills/reduce-pr-noise/references/settings.md +11 -11
- package/pr-shepherd.schema.json +361 -0
- package/bin/cli/event-state-scope.d.mts +0 -19
- package/bin/cli/event-state-scope.mjs +0 -60
- package/bin/cli/fix-formatter.d.mts +0 -4
- package/bin/commands/commit-suggestion.d.mts +0 -8
- package/bin/commands/commit-suggestion.mjs +0 -26
- package/bin/commands/event-instructions.d.mts +0 -11
- package/bin/commands/event-instructions.mjs +0 -27
- package/bin/commands/event-mode.d.mts +0 -14
- package/bin/commands/poll-mode.d.mts +0 -8
- package/bin/commands/poll-mode.mjs +0 -14
- package/bin/config.json +0 -68
- package/bin/types/protected-run.d.mts +0 -6
- package/bin/types/protected-run.mjs +0 -1
- /package/bin/commands/iterate/{fix-loop-instruction.d.mts → triage-loop-instruction.d.mts} +0 -0
|
@@ -23,7 +23,7 @@ export interface ReviewOutputBuckets {
|
|
|
23
23
|
editedComments: EditedComment[];
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
|
-
* Applies `iterate.
|
|
26
|
+
* Applies `iterate.review-output-max-chars` to one tick's review buckets. Priority: changes-requested
|
|
27
27
|
* reviews, active threads and comments, first-look items, review summaries, edited comments.
|
|
28
28
|
* Approved reviews are opt-in minimize targets, not review work, and are never held.
|
|
29
29
|
* Held items leave every list — the report, the generated mutation IDs, and the seen-marker
|
|
@@ -10,7 +10,7 @@ const threadItem = (thread) => ({
|
|
|
10
10
|
cost: threadTranscriptBody(thread).length,
|
|
11
11
|
});
|
|
12
12
|
/**
|
|
13
|
-
* Applies `iterate.
|
|
13
|
+
* Applies `iterate.review-output-max-chars` to one tick's review buckets. Priority: changes-requested
|
|
14
14
|
* reviews, active threads and comments, first-look items, review summaries, edited comments.
|
|
15
15
|
* Approved reviews are opt-in minimize targets, not review work, and are never held.
|
|
16
16
|
* Held items leave every list — the report, the generated mutation IDs, and the seen-marker
|
|
@@ -14,7 +14,7 @@ type StateKey = {
|
|
|
14
14
|
* when this read differs from the digest stored beside the report, or no digest is stored yet
|
|
15
15
|
* (the seeding tick): the stored report may predate what the detectors saw, so the tick must
|
|
16
16
|
* not reuse it through the GraphQL fingerprint either. A same-digest read past
|
|
17
|
-
* `poll.
|
|
17
|
+
* `poll.reconcile-seconds` leaves `changed` unset.
|
|
18
18
|
*/
|
|
19
19
|
export type WaitDetectorResult = {
|
|
20
20
|
reused: ShepherdReport;
|
|
@@ -51,7 +51,7 @@ async function readDetectors(prNumber, repo, report) {
|
|
|
51
51
|
* is rethrown so the tick aborts and polling backs off.
|
|
52
52
|
*/
|
|
53
53
|
export async function readWaitDetectors(prNumber, repo, stateKey, config, now = Date.now()) {
|
|
54
|
-
const reconcileMs = config.poll
|
|
54
|
+
const reconcileMs = config.poll["reconcile-seconds"] * 1000;
|
|
55
55
|
if (reconcileMs === 0 || getGithubTransport() === "rest")
|
|
56
56
|
return undefined;
|
|
57
57
|
const stored = await loadPrFingerprint(stateKey);
|
package/bin/commands/check.d.mts
CHANGED
|
@@ -7,7 +7,7 @@ type CheckOptions = GlobalOptions & {
|
|
|
7
7
|
persistSeen?: boolean;
|
|
8
8
|
fingerprintCache?: boolean;
|
|
9
9
|
merge?: boolean;
|
|
10
|
-
/** Per-tick review-item body budget (`iterate.
|
|
10
|
+
/** Per-tick review-item body budget (`iterate.review-output-max-chars`). Omitted or 0: no cap. */
|
|
11
11
|
reviewOutputMaxChars?: number;
|
|
12
12
|
};
|
|
13
13
|
export declare function runCheck(opts: CheckOptions, context?: CheckExecutionContext): Promise<ShepherdReport>;
|
package/bin/commands/check.mjs
CHANGED
|
@@ -60,7 +60,7 @@ export async function runCheck(opts, context) {
|
|
|
60
60
|
async function runScopedCheck(opts, context, repo, prNumber, stateKey) {
|
|
61
61
|
const config = loadConfig();
|
|
62
62
|
const reuseFingerprint = opts.fingerprintCache === true;
|
|
63
|
-
const paginateApprovedReviews = config.iterate
|
|
63
|
+
const paginateApprovedReviews = config.iterate["minimize-approvals"];
|
|
64
64
|
const includeReceiptSummary = (await context?.wantsReceiptSummary(prNumber, repo)) ?? false;
|
|
65
65
|
// GraphQL transport: conditional REST change detectors can replay an idle report for free.
|
|
66
66
|
// Changed trunk rules invalidate a cached report; check again without reuse.
|
|
@@ -125,7 +125,7 @@ async function runScopedCheck(opts, context, repo, prNumber, stateKey) {
|
|
|
125
125
|
owner: repo.owner,
|
|
126
126
|
name: repo.name,
|
|
127
127
|
pr: prNumber,
|
|
128
|
-
relevantEvents: config.checks
|
|
128
|
+
relevantEvents: config.checks["ci-trigger-events"],
|
|
129
129
|
}, context);
|
|
130
130
|
const classifiedPrChecks = classifyChecks(allChecks, {
|
|
131
131
|
requiredContexts: unreported.requiredContexts,
|
|
@@ -194,7 +194,7 @@ async function runScopedCheck(opts, context, repo, prNumber, stateKey) {
|
|
|
194
194
|
const seenMap = await loadSeenMap(stateKey);
|
|
195
195
|
await aliasThreadSeenMarkers(repo, prNumber, batchData.reviewThreads.map((thread) => thread.id), seenMap);
|
|
196
196
|
const canGenerateItemMutation = (id, body, capability, operation) => !mutationWasDenied(id, body, seenMap) && canGenerateGithubMutation(capability, operation);
|
|
197
|
-
const botUsernames = normalizeBotUsernames(config
|
|
197
|
+
const botUsernames = normalizeBotUsernames(config["bot-usernames"]);
|
|
198
198
|
const ruleSet = await loadClassifyRules(getEffectiveCwd(), config.standardRules);
|
|
199
199
|
const classifyIndex = buildClassifyIndex(ruleSet, batchData);
|
|
200
200
|
const partition = partitionBatch(classifyIndex, batchData);
|
|
@@ -202,7 +202,7 @@ async function runScopedCheck(opts, context, repo, prNumber, stateKey) {
|
|
|
202
202
|
const ignoredAnnotated = merged.ignored.filter((c) => (c.annotations?.length ?? 0) > 0);
|
|
203
203
|
const minimizedCommentCandidates = batchData.comments.filter((c) => c.isMinimized && !partition.suppressedCommentIds.has(c.id));
|
|
204
204
|
const deniedRuleAutoResolveCommentIds = new Set(partition.ruleAutoResolveCommentIds.filter((id) => !canGenerateItemMutation(id, batchData.comments.find((comment) => comment.id === id)?.body ?? "", batchData.comments.find((comment) => comment.id === id)?.viewerCanMinimize, "minimize")));
|
|
205
|
-
const visibleCommentClassification = classifyVisibleComments(batchData.comments.filter((c) => !partition.suppressedCommentIds.has(c.id) || deniedRuleAutoResolveCommentIds.has(c.id)), seenMap, config.iterate
|
|
205
|
+
const visibleCommentClassification = classifyVisibleComments(batchData.comments.filter((c) => !partition.suppressedCommentIds.has(c.id) || deniedRuleAutoResolveCommentIds.has(c.id)), seenMap, config.iterate["minimize-comments"], botUsernames, new Set(batchData.comments
|
|
206
206
|
.filter((comment) => mutationWasDenied(comment.id, comment.body, seenMap))
|
|
207
207
|
.map((comment) => comment.id)));
|
|
208
208
|
const deniedRuleAutoResolveThreadIds = new Set(partition.ruleAutoResolveThreadIds.filter((id) => {
|
|
@@ -211,7 +211,7 @@ async function runScopedCheck(opts, context, repo, prNumber, stateKey) {
|
|
|
211
211
|
!canGenerateItemMutation(thread.id, threadTranscriptBody(thread), thread.viewerCanResolve, "resolve"));
|
|
212
212
|
}));
|
|
213
213
|
const visibleThreadCandidates = batchData.reviewThreads.filter((t) => !partition.suppressedThreadIds.has(t.id) || deniedRuleAutoResolveThreadIds.has(t.id));
|
|
214
|
-
const resolveOtherHumanThreads = config.iterate
|
|
214
|
+
const resolveOtherHumanThreads = config.iterate["resolve-other-human-threads"] ?? "none";
|
|
215
215
|
const threadMutationRouting = buildThreadMutationRouting(visibleThreadCandidates, botUsernames, partition.ruleAutoResolveThreadIds, resolveOtherHumanThreads);
|
|
216
216
|
const replyThreadIds = new Set(threadMutationRouting.replyThreadIds);
|
|
217
217
|
const resolveThreadIds = new Set(threadMutationRouting.resolveThreadIds);
|
|
@@ -288,11 +288,11 @@ async function runScopedCheck(opts, context, repo, prNumber, stateKey) {
|
|
|
288
288
|
// persisting seen markers for it now would make it silently vanish from every later tick
|
|
289
289
|
// once it's no longer "new" or "edited", even after the PR leaves the queue. A queued PR
|
|
290
290
|
// that also has check-driven work (failing checks, actionable annotations, conflicts) is
|
|
291
|
-
// NOT deferred — index.mts still renders these items via
|
|
291
|
+
// NOT deferred — index.mts still renders these items via triage — so this must stay false
|
|
292
292
|
// in that case too, or their seen markers would be suppressed while actually being shown.
|
|
293
293
|
const deferWhileQueued = opts.merge === true &&
|
|
294
294
|
batchData.isInMergeQueue === true &&
|
|
295
|
-
config.actions
|
|
295
|
+
config.actions["work-while-queued"] !== true &&
|
|
296
296
|
!hasCheckDrivenActionableWork({
|
|
297
297
|
failing: merged.failing,
|
|
298
298
|
passing: merged.passing,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Build the `
|
|
3
|
-
* Currently emitted by iterate `
|
|
2
|
+
* Build the `suggest` instruction step for agent consumers.
|
|
3
|
+
* Currently emitted by iterate `triage` for suggestion review threads. The CLI keeps
|
|
4
4
|
* only the trigger and the concrete command; refusal/drift handling is invariant across
|
|
5
5
|
* every invocation, so it lives in the pr-shepherd skill's "Suggestion patches" playbook
|
|
6
6
|
* instead of being re-emitted every tick (see AGENTS.md "Keep skills and loop prompts
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { buildPrShepherdCommand } from "../cli/runner.mjs";
|
|
2
2
|
import { playbookPointer } from "./playbook-pointer.mjs";
|
|
3
3
|
/**
|
|
4
|
-
* Build the `
|
|
5
|
-
* Currently emitted by iterate `
|
|
4
|
+
* Build the `suggest` instruction step for agent consumers.
|
|
5
|
+
* Currently emitted by iterate `triage` for suggestion review threads. The CLI keeps
|
|
6
6
|
* only the trigger and the concrete command; refusal/drift handling is invariant across
|
|
7
7
|
* every invocation, so it lives in the pr-shepherd skill's "Suggestion patches" playbook
|
|
8
8
|
* instead of being re-emitted every tick (see AGENTS.md "Keep skills and loop prompts
|
|
@@ -12,7 +12,7 @@ import { playbookPointer } from "./playbook-pointer.mjs";
|
|
|
12
12
|
*/
|
|
13
13
|
export function buildCommitSuggestionInstruction(prReference, sectionName) {
|
|
14
14
|
const command = buildPrShepherdCommand([
|
|
15
|
-
"
|
|
15
|
+
"suggest",
|
|
16
16
|
String(prReference),
|
|
17
17
|
"--thread-id",
|
|
18
18
|
"<id>",
|
|
@@ -3,9 +3,9 @@ import { summarizeApiTelemetry } from "../../github/api-telemetry.mjs";
|
|
|
3
3
|
import { selectQuotaWarning } from "../quota-selection.mjs";
|
|
4
4
|
import { buildQuotaAwareContinuation } from "../../quota-warning.mjs";
|
|
5
5
|
import { getGithubTransport } from "../../github/transport.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { isTriageContinuation } from "./check-instructions.mjs";
|
|
7
7
|
function shouldWarn(result) {
|
|
8
|
-
return ["wait", "mark_ready", "merge", "
|
|
8
|
+
return ["wait", "mark_ready", "merge", "triage"].includes(result.action);
|
|
9
9
|
}
|
|
10
10
|
export async function attachApiUsage(result, persistWarning, preservePersistedWarning = false, minimumPollIntervalMinutes = 0) {
|
|
11
11
|
const apiUsage = summarizeApiTelemetry();
|
|
@@ -16,7 +16,7 @@ export async function attachApiUsage(result, persistWarning, preservePersistedWa
|
|
|
16
16
|
if (quotaWarning === undefined && shouldWarn(result) && (apiUsage.graphql || core)) {
|
|
17
17
|
const [owner, repo] = result.repo.split("/");
|
|
18
18
|
if (owner && repo) {
|
|
19
|
-
const bands = loadConfig().
|
|
19
|
+
const bands = loadConfig().quotaWarningBands.map((band) => ({
|
|
20
20
|
...band,
|
|
21
21
|
pollIntervalMinutes: Math.max(band.pollIntervalMinutes, minimumPollIntervalMinutes),
|
|
22
22
|
}));
|
|
@@ -29,14 +29,14 @@ export async function attachApiUsage(result, persistWarning, preservePersistedWa
|
|
|
29
29
|
apiUsage,
|
|
30
30
|
...(quotaWarning !== undefined && { quotaWarning }),
|
|
31
31
|
};
|
|
32
|
-
if (quotaWarning === undefined || withWarning.action !== "
|
|
32
|
+
if (quotaWarning === undefined || withWarning.action !== "triage")
|
|
33
33
|
return withWarning;
|
|
34
|
-
const instructions = [...withWarning.
|
|
34
|
+
const instructions = [...withWarning.triage.instructions];
|
|
35
35
|
const completion = instructions.at(-1);
|
|
36
36
|
if (completion !== undefined) {
|
|
37
|
-
if (
|
|
38
|
-
instructions[instructions.length - 1] = buildQuotaAwareContinuation(quotaWarning, "`[
|
|
37
|
+
if (isTriageContinuation(completion)) {
|
|
38
|
+
instructions[instructions.length - 1] = buildQuotaAwareContinuation(quotaWarning, "`[TRIAGE]` is non-terminal. After completing these steps,");
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
-
return { ...withWarning,
|
|
41
|
+
return { ...withWarning, triage: { ...withWarning.triage, instructions } };
|
|
42
42
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { AgentCheck, ResolveCommand, Review } from "../../types.mts";
|
|
2
|
-
/** The `[
|
|
3
|
-
export declare function
|
|
2
|
+
/** The `[TRIAGE]` recurrence step, whether plain or rewritten by the quota warning. */
|
|
3
|
+
export declare function isTriageContinuation(step: string): boolean;
|
|
4
4
|
/** Build the stale-CR step for `## Changes-requested reviews`. Empty when no human CR is stale. */
|
|
5
5
|
export declare function buildCrStaleClause(reviews: Review[]): string;
|
|
6
6
|
/**
|
|
7
7
|
* Build the optional branch-update hint. Empty unless the branch is behind or conflicts with its
|
|
8
|
-
* base and the user configured a non-blank `iterate.
|
|
8
|
+
* base and the user configured a non-blank `iterate.behind-base-hint` — the CLI never prescribes
|
|
9
9
|
* rebase/merge mechanics itself (see "Keep skills and loop prompts minimal" in AGENTS.md); this
|
|
10
10
|
* only echoes back the caller's own configured pointer. `hint` is trimmed and type-checked at the
|
|
11
11
|
* point of use (rather than at config load) so a malformed rc file value (non-string, or
|
|
@@ -37,7 +37,7 @@ export declare function buildResolveCommandInstruction(resolveCommand: ResolveCo
|
|
|
37
37
|
export declare function buildFailingCheckInstructions(checks: AgentCheck[]): string[];
|
|
38
38
|
/** Update the PR branch after an external blocker merges or closes. Never rerun that job. */
|
|
39
39
|
export declare function buildReleasedBlockerInstruction(prNumber: number): string;
|
|
40
|
-
/** `iterate.
|
|
40
|
+
/** `iterate.review-output-max-chars` held review items back; the rerun step that follows shows them. */
|
|
41
41
|
export declare function buildReviewSubsetInstruction(reviewItems: {
|
|
42
42
|
shown: number;
|
|
43
43
|
total: number;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { playbookPointer } from "../playbook-pointer.mjs";
|
|
2
|
-
// The `[
|
|
3
|
-
const
|
|
4
|
-
/** The `[
|
|
5
|
-
export function
|
|
6
|
-
return step ===
|
|
2
|
+
// The `[TRIAGE]` heading and the skill already say the action is non-terminal.
|
|
3
|
+
const TRIAGE_CONTINUATION = "Rerun Shepherd now.";
|
|
4
|
+
/** The `[TRIAGE]` recurrence step, whether plain or rewritten by the quota warning. */
|
|
5
|
+
export function isTriageContinuation(step) {
|
|
6
|
+
return step === TRIAGE_CONTINUATION || /\[TRIAGE\].*non-terminal/i.test(step);
|
|
7
7
|
}
|
|
8
8
|
/** Build the stale-CR step for `## Changes-requested reviews`. Empty when no human CR is stale. */
|
|
9
9
|
export function buildCrStaleClause(reviews) {
|
|
@@ -13,7 +13,7 @@ export function buildCrStaleClause(reviews) {
|
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
15
|
* Build the optional branch-update hint. Empty unless the branch is behind or conflicts with its
|
|
16
|
-
* base and the user configured a non-blank `iterate.
|
|
16
|
+
* base and the user configured a non-blank `iterate.behind-base-hint` — the CLI never prescribes
|
|
17
17
|
* rebase/merge mechanics itself (see "Keep skills and loop prompts minimal" in AGENTS.md); this
|
|
18
18
|
* only echoes back the caller's own configured pointer. `hint` is trimmed and type-checked at the
|
|
19
19
|
* point of use (rather than at config load) so a malformed rc file value (non-string, or
|
|
@@ -74,12 +74,12 @@ export function buildReleasedBlockerInstruction(prNumber) {
|
|
|
74
74
|
? "Update this PR branch from its base using the repository's branch-update procedure. Do not rerun the job; a rerun retests the old merge ref."
|
|
75
75
|
: `Update this PR branch from its base with \`gh pr update-branch ${prNumber} --rebase\`. Do not rerun the job; a rerun retests the old merge ref.`;
|
|
76
76
|
}
|
|
77
|
-
/** `iterate.
|
|
77
|
+
/** `iterate.review-output-max-chars` held review items back; the rerun step that follows shows them. */
|
|
78
78
|
export function buildReviewSubsetInstruction(reviewItems) {
|
|
79
79
|
return `This is a subset: ${reviewItems.shown} of ${reviewItems.total} review items are shown, and only their IDs are in the commands above. After handling these items, rerun Shepherd for the next batch; do not fetch the held items yourself.`;
|
|
80
80
|
}
|
|
81
81
|
/** Recurrence only. Commit, push, rerun, and SHA steps are earlier instructions. */
|
|
82
82
|
export function buildFixCompletionInstruction() {
|
|
83
|
-
return
|
|
83
|
+
return TRIAGE_CONTINUATION;
|
|
84
84
|
}
|
|
85
85
|
import { getGithubTransport } from "../../github/transport.mjs";
|
|
@@ -24,7 +24,7 @@ export function classifyReviewSummaries(summaries, approvals, minimizeApprovals,
|
|
|
24
24
|
// so their minimize IDs ride in the agent-facing apply command. Seen summaries
|
|
25
25
|
// (already surfaced in a prior tick) have no new content to show — the CLI
|
|
26
26
|
// self-minimizes them in-process (selfMinimizeIds) instead of routing a
|
|
27
|
-
// cosmetic-only mutation through
|
|
27
|
+
// cosmetic-only mutation through triage (issue #313). Edited summaries are
|
|
28
28
|
// excluded entirely: they are already minimized server-side (body changed after
|
|
29
29
|
// minimize was applied).
|
|
30
30
|
const minimizeIds = summaries.firstLook.filter(eligible).map((r) => r.id);
|
|
@@ -61,7 +61,7 @@ export function classifyReviewSummaries(summaries, approvals, minimizeApprovals,
|
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
|
-
* A durable-state
|
|
64
|
+
* A durable-state (cloud) session can lose its uncertain-reply records
|
|
65
65
|
* between ticks. Its generated reply command opts in to adopting a reply GitHub already shows,
|
|
66
66
|
* so rerunning the same printed command cannot double-post. Direct apply requests never do.
|
|
67
67
|
*/
|
|
@@ -64,11 +64,11 @@ function renderEscalateCheck(check) {
|
|
|
64
64
|
}
|
|
65
65
|
export function checkEscalateTriggers(actionableThreads, threadAttempts) {
|
|
66
66
|
const triggers = [];
|
|
67
|
-
const maxAttempts = loadConfig().iterate
|
|
68
|
-
// Trigger 1:
|
|
67
|
+
const maxAttempts = loadConfig().iterate["triage-attempts-per-thread"];
|
|
68
|
+
// Trigger 1: triage thrash — same thread dispatched too many times without resolving.
|
|
69
69
|
const thrashThreads = actionableThreads.filter((t) => (threadAttempts[t.id] ?? 0) >= maxAttempts);
|
|
70
70
|
if (thrashThreads.length > 0) {
|
|
71
|
-
triggers.push("
|
|
71
|
+
triggers.push("triage-thrash");
|
|
72
72
|
}
|
|
73
73
|
return {
|
|
74
74
|
triggers,
|
|
@@ -265,9 +265,9 @@ export function buildEscalateSuggestion(triggers, detail) {
|
|
|
265
265
|
const reason = detail ? ` (${detail})` : "";
|
|
266
266
|
return `Could not determine the PR's base branch${reason} — automated rebases are paused because branch safety is unclear. Run the rebase manually against the PR's real target branch.`;
|
|
267
267
|
}
|
|
268
|
-
if (triggers.includes("
|
|
269
|
-
const attempts = loadConfig().iterate
|
|
270
|
-
return `The same thread(s) remain unresolved after their pending review commands were returned for ${attempts}
|
|
268
|
+
if (triggers.includes("triage-thrash")) {
|
|
269
|
+
const attempts = loadConfig().iterate["triage-attempts-per-thread"];
|
|
270
|
+
return `The same thread(s) remain unresolved after their pending review commands were returned for ${attempts} TRIAGE ticks. Automated iteration is paused for a manual decision.`;
|
|
271
271
|
}
|
|
272
272
|
return "Ambiguous state — automated handling cannot proceed safely. Inspect the PR and act manually.";
|
|
273
273
|
}
|
|
@@ -8,7 +8,7 @@ import { buildWaitLog, buildTerminalCancelResult, blockedCancelNote } from "./he
|
|
|
8
8
|
import { classifyReviewSummaries } from "./classify.mjs";
|
|
9
9
|
import { applyStallGuard } from "./stall.mjs";
|
|
10
10
|
import { clearStallState } from "../../state/iterate-stall.mjs";
|
|
11
|
-
import {
|
|
11
|
+
import { handleTriage } from "./triage.mjs";
|
|
12
12
|
import { normalizeBotUsernames } from "../../comments/authors.mjs";
|
|
13
13
|
import { autoMinimizeComments } from "../../comments/resolve.mjs";
|
|
14
14
|
import { hasCheckDrivenActionableWork } from "../check-annotations.mjs";
|
|
@@ -60,24 +60,21 @@ async function attachEditedComments(result, tick, opts) {
|
|
|
60
60
|
}
|
|
61
61
|
async function runIterateCore(opts, tick) {
|
|
62
62
|
const config = loadConfig();
|
|
63
|
-
const botUsernames = normalizeBotUsernames(config
|
|
64
|
-
const readyDelaySeconds = opts.readyDelaySeconds ?? config.
|
|
65
|
-
const stallTimeoutSeconds = opts.stallTimeoutSeconds ?? config.iterate
|
|
63
|
+
const botUsernames = normalizeBotUsernames(config["bot-usernames"]);
|
|
64
|
+
const readyDelaySeconds = opts.readyDelaySeconds ?? config.iterate["ready-delay-minutes"] * 60;
|
|
65
|
+
const stallTimeoutSeconds = opts.stallTimeoutSeconds ?? config.iterate["stall-timeout-minutes"] * 60;
|
|
66
66
|
const prNumber = opts.prNumber ?? (await getCurrentPrNumber());
|
|
67
67
|
if (prNumber === null) {
|
|
68
68
|
throw new ShepherdError("No open PR found for current branch. Pass a PR number explicitly.", EXIT.UNAVAILABLE);
|
|
69
69
|
}
|
|
70
|
-
const neverCancelRuns = opts.neverCancelRuns ?? config.actions.neverCancelRuns;
|
|
71
70
|
const checkContext = createCheckExecutionContext(readyDelaySeconds);
|
|
72
71
|
const report = await runCheck({
|
|
73
72
|
...opts,
|
|
74
73
|
prNumber,
|
|
75
|
-
// Outdated Shepherd-visible threads are always resolved by iterate.
|
|
76
|
-
// old actions.autoResolveOutdated switch is retained only as an ignored
|
|
77
|
-
// compatibility key in the config loader.
|
|
74
|
+
// Outdated Shepherd-visible threads are always resolved by iterate.
|
|
78
75
|
autoResolve: true,
|
|
79
|
-
autoMinimizeSuppressed: config.actions
|
|
80
|
-
reviewOutputMaxChars: config.iterate
|
|
76
|
+
autoMinimizeSuppressed: config.actions["auto-minimize-suppressed"],
|
|
77
|
+
reviewOutputMaxChars: config.iterate["review-output-max-chars"],
|
|
81
78
|
}, checkContext);
|
|
82
79
|
tick.report = report;
|
|
83
80
|
const [repoOwner, repoName] = report.repo.split("/");
|
|
@@ -107,7 +104,7 @@ async function runIterateCore(opts, tick) {
|
|
|
107
104
|
firstLook: report.firstLookSummaries,
|
|
108
105
|
seen: report.reviewSummaries,
|
|
109
106
|
edited: report.editedSummaries,
|
|
110
|
-
}, report.approvedReviews, config.iterate
|
|
107
|
+
}, report.approvedReviews, config.iterate["minimize-approvals"], config.iterate["minimize-comments"], botUsernames, [
|
|
111
108
|
...report.threads.actionable,
|
|
112
109
|
...report.threads.resolutionOnly,
|
|
113
110
|
...(report.heldThreadReviewIds ?? []).map((reviewId) => ({ reviewId })),
|
|
@@ -128,7 +125,7 @@ async function runIterateCore(opts, tick) {
|
|
|
128
125
|
}
|
|
129
126
|
// A first look at an already-minimized comment is only an acknowledgment. Edited PR comments
|
|
130
127
|
// (report.comments.edited) are neither: they never block readiness or restart the ready delay.
|
|
131
|
-
// Review items held back by iterate.
|
|
128
|
+
// Review items held back by iterate.review-output-max-chars keep the PR out of ready/merge: a
|
|
132
129
|
// capped tick that shows only first-look comments still counts, since held work follows.
|
|
133
130
|
const hasReadinessWork = (report.reviewItems !== undefined && report.comments.firstLook.length > 0) ||
|
|
134
131
|
report.threads.actionable.length > 0 ||
|
|
@@ -142,7 +139,7 @@ async function runIterateCore(opts, tick) {
|
|
|
142
139
|
reviewSummaryIds.length > 0 ||
|
|
143
140
|
firstLookSummaries.length > 0 ||
|
|
144
141
|
editedSummaries.length > 0 ||
|
|
145
|
-
(config.iterate
|
|
142
|
+
(config.iterate["minimize-approvals"] && surfacedApprovals.length > 0);
|
|
146
143
|
const hasActionableWork = hasReadinessWork || report.comments.firstLook.length > 0;
|
|
147
144
|
const activeMerge = Boolean(opts.merge &&
|
|
148
145
|
(report.mergeQueue?.inQueue ||
|
|
@@ -183,12 +180,12 @@ async function runIterateCore(opts, tick) {
|
|
|
183
180
|
const checkDrivenActionableWork = hasCheckDrivenActionableWork(reportForWork.checks, report.mergeStatus.status);
|
|
184
181
|
const deferWhileQueued = opts.merge === true &&
|
|
185
182
|
report.mergeQueue?.inQueue === true &&
|
|
186
|
-
config.actions
|
|
183
|
+
config.actions["work-while-queued"] !== true;
|
|
187
184
|
if (hasActionableWork && !(deferWhileQueued && !checkDrivenActionableWork)) {
|
|
188
|
-
return
|
|
185
|
+
return handleTriage({
|
|
189
186
|
base,
|
|
190
187
|
report: reportForWork,
|
|
191
|
-
opts: { ...opts, prNumber
|
|
188
|
+
opts: { ...opts, prNumber },
|
|
192
189
|
headSha,
|
|
193
190
|
stallKey,
|
|
194
191
|
prNumber,
|
|
@@ -208,10 +205,10 @@ async function runIterateCore(opts, tick) {
|
|
|
208
205
|
});
|
|
209
206
|
}
|
|
210
207
|
if (unreportedRepair.length > 0 && staleAncestry) {
|
|
211
|
-
return
|
|
208
|
+
return handleTriage({
|
|
212
209
|
base,
|
|
213
210
|
report: reportForWork,
|
|
214
|
-
opts: { ...opts, prNumber
|
|
211
|
+
opts: { ...opts, prNumber },
|
|
215
212
|
headSha,
|
|
216
213
|
stallKey,
|
|
217
214
|
prNumber,
|
|
@@ -241,15 +238,15 @@ async function runIterateCore(opts, tick) {
|
|
|
241
238
|
firstLookSummaries,
|
|
242
239
|
editedSummaries,
|
|
243
240
|
surfacedApprovals,
|
|
244
|
-
minimizeApprovals: config.iterate
|
|
241
|
+
minimizeApprovals: config.iterate["minimize-approvals"],
|
|
245
242
|
});
|
|
246
243
|
if (mergeStateResult)
|
|
247
244
|
return annotateBlockedWait(mergeStateResult, blockerGate);
|
|
248
245
|
if (staleAncestry) {
|
|
249
|
-
return
|
|
246
|
+
return handleTriage({
|
|
250
247
|
base,
|
|
251
248
|
report: reportForWork,
|
|
252
|
-
opts: { ...opts, prNumber
|
|
249
|
+
opts: { ...opts, prNumber },
|
|
253
250
|
headSha,
|
|
254
251
|
stallKey,
|
|
255
252
|
prNumber,
|
|
@@ -287,7 +284,7 @@ async function runIterateCore(opts, tick) {
|
|
|
287
284
|
escalate: { ...escalation, humanMessage: buildEscalateHumanMessage(escalation, report.pr) },
|
|
288
285
|
};
|
|
289
286
|
}
|
|
290
|
-
const autoMarkReady = !opts.noAutoMarkReady && config.
|
|
287
|
+
const autoMarkReady = !opts.noAutoMarkReady && config["mark-ready"].auto;
|
|
291
288
|
if (canMarkReady && !autoMarkReady) {
|
|
292
289
|
await clearReadyDelay(report.pr, repoOwner, repoName);
|
|
293
290
|
await clearStallState(stallKey);
|
|
@@ -307,7 +304,7 @@ async function runIterateCore(opts, tick) {
|
|
|
307
304
|
const receipt = receiptCurrent
|
|
308
305
|
? "written"
|
|
309
306
|
: await recordReadyReceipt(receiptKey, report, checkContext);
|
|
310
|
-
// Aggregate
|
|
307
|
+
// Aggregate `stack` routing trusts only a layer's receipt, so an unwritten
|
|
311
308
|
// one keeps the elapsed marker and retries next tick. A one-PR receipt
|
|
312
309
|
// only lets a rerun skip the wait, so its failure never changes the action.
|
|
313
310
|
// A layer GitHub reports BLOCKED gets no receipt, so aggregate routing
|
|
@@ -7,7 +7,7 @@ import { getGithubTransport } from "../../github/transport.mjs";
|
|
|
7
7
|
const ENQUEUE_MUTATION = "mutation EnqueuePullRequest($pullRequestId: ID!, $expectedHeadOid: GitObjectID!) { enqueuePullRequest(input: { pullRequestId: $pullRequestId, expectedHeadOid: $expectedHeadOid }) { mergeQueueEntry { id } } }";
|
|
8
8
|
export function buildMergeCommandPlan(input) {
|
|
9
9
|
if (input.transport === "rest" || getGithubTransport() === "rest") {
|
|
10
|
-
const configured = configuredMergeMethod(loadConfig().merge
|
|
10
|
+
const configured = configuredMergeMethod(loadConfig().merge);
|
|
11
11
|
if (!input.queue && input.queueKnown !== true && configured)
|
|
12
12
|
return {
|
|
13
13
|
unavailable: "REST cannot verify merge-queue policy and cannot apply a configured direct merge method to an automatic merge request. Refresh branch policy before merging.",
|
|
@@ -70,10 +70,10 @@ export function buildMergeCommandPlan(input) {
|
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
const mergeConfig = loadConfig().merge;
|
|
73
|
-
const configuredArgs = mergeConfig
|
|
73
|
+
const configuredArgs = mergeConfig["command-args"];
|
|
74
74
|
const decision = chooseMergeMethod({
|
|
75
75
|
allowed: input.allowedMergeMethods,
|
|
76
|
-
configured: configuredMergeMethod(mergeConfig
|
|
76
|
+
configured: configuredMergeMethod(mergeConfig),
|
|
77
77
|
fallback: "merge",
|
|
78
78
|
});
|
|
79
79
|
if ("unavailable" in decision)
|
|
@@ -6,14 +6,14 @@ import { buildPrShepherdCommand } from "../../cli/runner.mjs";
|
|
|
6
6
|
import { inlineCode } from "../../util/markdown.mjs";
|
|
7
7
|
/**
|
|
8
8
|
* A one-PR poll cannot prove the layers below it merged or that its PR number is a safe merge
|
|
9
|
-
* selector. Route it through the aggregate stack
|
|
9
|
+
* selector. Route it through the aggregate stack command, which merges one bottom layer at a time.
|
|
10
10
|
*/
|
|
11
11
|
function buildStackedRouteResult(base, report, stack) {
|
|
12
12
|
const prUrl = formatPrUrl(report.repo, report.pr);
|
|
13
13
|
return {
|
|
14
14
|
...base,
|
|
15
|
-
action: "
|
|
16
|
-
|
|
15
|
+
action: "triage",
|
|
16
|
+
triage: {
|
|
17
17
|
threads: [],
|
|
18
18
|
resolutionOnlyThreads: [],
|
|
19
19
|
actionableComments: [],
|
|
@@ -31,14 +31,11 @@ function buildStackedRouteResult(base, report, stack) {
|
|
|
31
31
|
},
|
|
32
32
|
instructions: [
|
|
33
33
|
`PR #${report.pr} is layer ${stack.position} of ${stack.size} in native stack #${stack.number}; do not run \`gh pr merge\` for this layer.`,
|
|
34
|
-
`Run ${inlineCode(buildPrShepherdCommand(["
|
|
34
|
+
`Run ${inlineCode(buildPrShepherdCommand(["stack", prUrl, "--until-terminal", "--merge"]).text)} to reconcile the stack and run each bottom-layer merge command it emits.`,
|
|
35
35
|
],
|
|
36
|
-
inProgressRunIds: [],
|
|
37
|
-
protectedRuns: [],
|
|
38
36
|
firstLookThreads: [],
|
|
39
37
|
firstLookComments: [],
|
|
40
38
|
},
|
|
41
|
-
cancelled: [],
|
|
42
39
|
};
|
|
43
40
|
}
|
|
44
41
|
export function buildReadyMergeOutcome(enabled, readyElapsed, base, report) {
|
|
@@ -15,7 +15,7 @@ export function buildNativeStackRebaseInstruction(repo, stackNumber, start) {
|
|
|
15
15
|
: "trunk" in start
|
|
16
16
|
? `trunk \`${start.trunk}\``
|
|
17
17
|
: `bottom open layer PR #${start.bottomPr}`;
|
|
18
|
-
return `Use the repository's stack-update procedure for native stack #${stackNumber} in \`${repo}\`, starting at ${boundary} and preserving the ordered parent boundaries of every affected upper layer. Read membership with REST-backed Shepherd
|
|
18
|
+
return `Use the repository's stack-update procedure for native stack #${stackNumber} in \`${repo}\`, starting at ${boundary} and preserving the ordered parent boundaries of every affected upper layer. Read membership with REST-backed Shepherd \`stack\` output and update and push affected branches using the caller's git workflow. ${playbookPointer("Branch update")}`;
|
|
19
19
|
}
|
|
20
20
|
const [checkout, command] = "parentBranch" in start
|
|
21
21
|
? [
|
|
@@ -7,7 +7,7 @@ import type { InstructionStyle } from "../../config/load.mts";
|
|
|
7
7
|
* if it is not. `apply review` reads that HEAD itself with a read-only `git rev-parse HEAD`.
|
|
8
8
|
*/
|
|
9
9
|
export declare function renderResolveCommand(rc: ResolveCommand): string;
|
|
10
|
-
export declare function buildFixInstructions(threads: AgentThread[], actionableComments: AgentComment[], checks: AgentCheck[], changesRequestedReviews: Review[], baseBranch: string, resolveCommand: ResolveCommand, hasConflicts: boolean, prReference: string | number,
|
|
10
|
+
export declare function buildFixInstructions(threads: AgentThread[], actionableComments: AgentComment[], checks: AgentCheck[], changesRequestedReviews: Review[], baseBranch: string, resolveCommand: ResolveCommand, hasConflicts: boolean, prReference: string | number, firstLookThreads?: FirstLookThread[], firstLookComments?: FirstLookComment[], firstLookSummaries?: Review[], editedSummaries?: Review[], resolutionOnlyThreads?: ReviewThread[], resolveOnlyCommand?: ResolveCommand, behindBaseHint?: string, // iterate.behind-base-hint — see buildBehindBaseHintInstruction
|
|
11
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[];
|
|
@@ -5,7 +5,7 @@ import { buildCommitSuggestionInstruction } from "../commit-suggestion-instructi
|
|
|
5
5
|
import { partitionFixThreads } from "./fix-instruction-threads.mjs";
|
|
6
6
|
import { buildBranchPushInstruction, buildConflictInstruction } from "./native-stack-rebase.mjs";
|
|
7
7
|
import { queueEjectionSteps } from "./queue-recovery-instructions.mjs";
|
|
8
|
-
import { buildPushSteps } from "./
|
|
8
|
+
import { buildPushSteps } from "./triage-loop-instruction.mjs";
|
|
9
9
|
/**
|
|
10
10
|
* Render a resolve command as a shell snippet. Appends `--require-sha HEAD` when set: run after
|
|
11
11
|
* the push step, the local HEAD is the pushed PR head, and `apply review` rejects the mutations
|
|
@@ -16,7 +16,7 @@ export function renderResolveCommand(rc) {
|
|
|
16
16
|
return rc.requiresHeadSha ? `${command} ${REQUIRE_HEAD_SHA}` : command;
|
|
17
17
|
}
|
|
18
18
|
const REQUIRE_HEAD_SHA = "--require-sha HEAD";
|
|
19
|
-
export function buildFixInstructions(threads, actionableComments, checks, changesRequestedReviews, baseBranch, resolveCommand, hasConflicts, prReference,
|
|
19
|
+
export function buildFixInstructions(threads, actionableComments, checks, changesRequestedReviews, baseBranch, resolveCommand, hasConflicts, prReference, firstLookThreads = [], firstLookComments = [], firstLookSummaries = [], editedSummaries = [], resolutionOnlyThreads = [], resolveOnlyCommand, behindBaseHint = "", // iterate.behind-base-hint — see buildBehindBaseHintInstruction
|
|
20
20
|
isBehind = false, _viewerCanUpdate = false, hasExhaustedWorkflowRerun = false, stackRebase, // native stack layers rebase with gh-stack, not branch by branch
|
|
21
21
|
queueEjection, // current merge-queue removal with failing queue checks
|
|
22
22
|
instructionStyle = "inline") {
|
|
@@ -5,25 +5,27 @@ import { buildEscalateSuggestion, buildEscalateHumanMessage, formatDurationAppro
|
|
|
5
5
|
import { checksWithActionableAnnotations } from "../check-annotations.mjs";
|
|
6
6
|
import { formatPrUrl } from "../../pr-reference.mjs";
|
|
7
7
|
function pendingReviewCommandsFromResult(result) {
|
|
8
|
-
if (result.action !== "
|
|
8
|
+
if (result.action !== "triage")
|
|
9
9
|
return undefined;
|
|
10
10
|
const pending = {
|
|
11
|
-
...(result.
|
|
12
|
-
resolveOnlyCommand: result.
|
|
11
|
+
...(result.triage.resolveOnlyCommand?.hasMutations && {
|
|
12
|
+
resolveOnlyCommand: result.triage.resolveOnlyCommand,
|
|
13
|
+
}),
|
|
14
|
+
...(result.triage.resolveCommand.hasMutations && {
|
|
15
|
+
resolveCommand: result.triage.resolveCommand,
|
|
13
16
|
}),
|
|
14
|
-
...(result.fix.resolveCommand.hasMutations && { resolveCommand: result.fix.resolveCommand }),
|
|
15
17
|
};
|
|
16
18
|
return Object.keys(pending).length > 0 ? pending : undefined;
|
|
17
19
|
}
|
|
18
20
|
function surfacedSummariesFromResult(result) {
|
|
19
|
-
if (result.action !== "
|
|
21
|
+
if (result.action !== "triage")
|
|
20
22
|
return {};
|
|
21
23
|
return {
|
|
22
|
-
...(result.
|
|
23
|
-
firstLookSummaries: result.
|
|
24
|
+
...(result.triage.firstLookSummaries.length > 0 && {
|
|
25
|
+
firstLookSummaries: result.triage.firstLookSummaries,
|
|
24
26
|
}),
|
|
25
|
-
...(result.
|
|
26
|
-
editedSummaries: result.
|
|
27
|
+
...(result.triage.editedSummaries.length > 0 && {
|
|
28
|
+
editedSummaries: result.triage.editedSummaries,
|
|
27
29
|
}),
|
|
28
30
|
};
|
|
29
31
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { renderShellCommand } from "../../cli/runner.mjs";
|
|
2
2
|
import { loadConfig } from "../../config/load.mjs";
|
|
3
3
|
import { playbookPointer } from "../playbook-pointer.mjs";
|
|
4
|
-
const
|
|
4
|
+
const TRIAGE_LOOP_PLAYBOOK = "Triage loop";
|
|
5
5
|
/** The generic push stays separate from conflict and queue-recovery push instructions. */
|
|
6
6
|
export function buildPushSteps(style, genericPush) {
|
|
7
7
|
if (!genericPush)
|
|
@@ -9,6 +9,10 @@ export function buildPushSteps(style, genericPush) {
|
|
|
9
9
|
const push = "Commit and push any changes.";
|
|
10
10
|
if (style === "inline")
|
|
11
11
|
return [push];
|
|
12
|
-
const cli = renderShellCommand([
|
|
13
|
-
|
|
12
|
+
const cli = renderShellCommand([
|
|
13
|
+
...loadConfig()["cli-command"],
|
|
14
|
+
"playbook",
|
|
15
|
+
TRIAGE_LOOP_PLAYBOOK,
|
|
16
|
+
]);
|
|
17
|
+
return [`${push} ${playbookPointer(TRIAGE_LOOP_PLAYBOOK)} CLI: \`${cli}\`.`];
|
|
14
18
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { IterateCommandOptions, IterateResult, IterateResultBase, Review, ShepherdReport } from "../../types.mts";
|
|
2
2
|
import type { NormalizedBotUsernames } from "../../comments/authors.mts";
|
|
3
|
-
interface
|
|
3
|
+
interface HandleTriageContext {
|
|
4
4
|
base: IterateResultBase;
|
|
5
5
|
report: ShepherdReport;
|
|
6
6
|
opts: IterateCommandOptions;
|
|
@@ -25,5 +25,5 @@ interface HandleFixCodeContext {
|
|
|
25
25
|
/** Verified stack-repair guidance, when ancestry is stale. */
|
|
26
26
|
repairInstructions?: string[];
|
|
27
27
|
}
|
|
28
|
-
export declare function
|
|
28
|
+
export declare function handleTriage(ctx: HandleTriageContext): Promise<IterateResult>;
|
|
29
29
|
export {};
|