@henryqw/pi-pr 3.1.9 → 4.0.2
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/README.md +28 -11
- package/docs/pr-routing.svg +1 -1
- package/extensions/pr-ci.ts +908 -0
- package/extensions/pr-command.ts +107 -21
- package/extensions/pr-comment-sweep.ts +1102 -0
- package/extensions/pr-create.ts +684 -0
- package/extensions/pr-execution.ts +558 -0
- package/extensions/pr-feedback.ts +519 -0
- package/extensions/pr-github.ts +363 -145
- package/extensions/pr-merge.ts +4 -44
- package/extensions/pr-routing.ts +2 -1
- package/extensions/pr-ui.ts +1 -1
- package/extensions/pr-update-branch.ts +327 -0
- package/extensions/pr.ts +480 -16
- package/package.json +4 -2
- package/skills/pi-pr-comment-sweep/SKILL.md +45 -89
- package/skills/pi-pr-comment-sweep/references/recovery.md +24 -46
- package/skills/pi-pr-comment-sweep/references/thread-triage.md +2 -2
- package/skills/pi-pr-comment-sweep/scripts/pr-feedback.mjs +223 -1799
- package/skills/pi-pr-create/SKILL.md +6 -77
- package/skills/pi-pr-fix-ci/SKILL.md +39 -39
- package/skills/pi-pr-update-branch/SKILL.md +5 -108
package/extensions/pr-command.ts
CHANGED
|
@@ -15,42 +15,113 @@ import {
|
|
|
15
15
|
import {
|
|
16
16
|
deriveNextStep,
|
|
17
17
|
type NextStep,
|
|
18
|
+
type PullRequestTarget,
|
|
18
19
|
} from "./pr-routing.ts";
|
|
19
20
|
|
|
20
|
-
type WorkflowNextStep = Extract<NextStep, "create" | "update-branch" | "sweep" | "fix-ci">;
|
|
21
|
+
export type WorkflowNextStep = Extract<NextStep, "create" | "update-branch" | "sweep" | "fix-ci">;
|
|
21
22
|
|
|
22
|
-
const WORKFLOWS: Record<WorkflowNextStep, string> = {
|
|
23
|
-
create: "skill:pi-pr-create",
|
|
24
|
-
"update-branch": "skill:pi-pr-update-branch",
|
|
25
|
-
sweep: "skill:pi-pr-comment-sweep",
|
|
26
|
-
"fix-ci": "skill:pi-pr-fix-ci",
|
|
23
|
+
const WORKFLOWS: Record<WorkflowNextStep, { command: string; action: string }> = {
|
|
24
|
+
create: { command: "skill:pi-pr-create", action: "prepare" },
|
|
25
|
+
"update-branch": { command: "skill:pi-pr-update-branch", action: "merge" },
|
|
26
|
+
sweep: { command: "skill:pi-pr-comment-sweep", action: "start" },
|
|
27
|
+
"fix-ci": { command: "skill:pi-pr-fix-ci", action: "collect" },
|
|
27
28
|
};
|
|
29
|
+
const CREATE_BASE = /^--base=([a-z0-9](?:[a-z0-9.-]*[a-z0-9])?\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+:[^\s\0]+)$/;
|
|
30
|
+
|
|
31
|
+
export type WorkflowReservation =
|
|
32
|
+
| { route: "create"; target: PullRequestTarget; base?: string }
|
|
33
|
+
| { route: Exclude<WorkflowNextStep, "create">; pullRequest: CurrentPullRequest };
|
|
28
34
|
|
|
29
35
|
type PrCommandPi = Pick<ExtensionAPI, "exec" | "getCommands" | "sendUserMessage">;
|
|
30
|
-
export type
|
|
36
|
+
export type PrCommandInvocation = ((nextStep: NextStep) => void) & {
|
|
37
|
+
sessionGeneration: number;
|
|
38
|
+
assertCurrent(): void;
|
|
39
|
+
};
|
|
40
|
+
export type PrCommandHandler = (
|
|
41
|
+
args: string,
|
|
42
|
+
ctx: ExtensionCommandContext,
|
|
43
|
+
onRouteResolved?: PrCommandInvocation | ((nextStep: NextStep) => void),
|
|
44
|
+
) => Promise<NextStep>;
|
|
31
45
|
|
|
32
|
-
type
|
|
46
|
+
export type WorkflowPromptIdentity = Readonly<{
|
|
47
|
+
route: WorkflowNextStep;
|
|
48
|
+
skill: string;
|
|
49
|
+
runId: string;
|
|
50
|
+
action: string;
|
|
51
|
+
}>;
|
|
52
|
+
|
|
53
|
+
export type PrCommandDependencies = {
|
|
33
54
|
loadCurrentPullRequest?: typeof loadCurrentPullRequest;
|
|
34
55
|
linkInferredPullRequest?: typeof linkInferredPullRequest;
|
|
56
|
+
reserveWorkflow?: (
|
|
57
|
+
reservation: WorkflowReservation,
|
|
58
|
+
ctx: ExtensionCommandContext,
|
|
59
|
+
invocation?: PrCommandInvocation,
|
|
60
|
+
) => Promise<string>;
|
|
61
|
+
markWorkflowPromptQueued?: (identity: WorkflowPromptIdentity, queued: boolean) => void;
|
|
62
|
+
releaseWorkflow?: (runId: string, invocation?: PrCommandInvocation) => void;
|
|
35
63
|
};
|
|
36
64
|
|
|
37
|
-
function
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
65
|
+
function createBase(instructions: string): string | undefined {
|
|
66
|
+
if (!instructions) return undefined;
|
|
67
|
+
const match = CREATE_BASE.exec(instructions);
|
|
68
|
+
if (!match) throw new Error("PR creation accepts only --base=<host>/<owner>/<repository>:<ref>");
|
|
69
|
+
return match[1]!;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function workflowReservation(
|
|
73
|
+
nextStep: WorkflowNextStep,
|
|
74
|
+
discovery: Awaited<ReturnType<typeof loadCurrentPullRequest>>,
|
|
41
75
|
instructions: string,
|
|
42
|
-
):
|
|
76
|
+
): WorkflowReservation {
|
|
77
|
+
if (nextStep === "create") {
|
|
78
|
+
if (discovery.kind !== "none") throw new Error("/pr create failed: creation target is unavailable");
|
|
79
|
+
const base = createBase(instructions);
|
|
80
|
+
return { route: "create", target: discovery.creationTarget, ...(base === undefined ? {} : { base }) };
|
|
81
|
+
}
|
|
82
|
+
if (instructions) throw new Error("The current /pr helper route does not accept instructions");
|
|
83
|
+
if (discovery.kind !== "current") throw new Error(`/pr ${nextStep} failed: pull request is unavailable`);
|
|
84
|
+
return { route: nextStep, pullRequest: discovery.pullRequest };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function packageWorkflowCommand(pi: PrCommandPi, route: WorkflowNextStep) {
|
|
88
|
+
const workflow = WORKFLOWS[route];
|
|
43
89
|
const command = pi.getCommands().find((candidate) =>
|
|
44
|
-
candidate.name ===
|
|
90
|
+
candidate.name === workflow.command &&
|
|
45
91
|
candidate.source === "skill" &&
|
|
46
92
|
candidate.sourceInfo.origin === "package"
|
|
47
93
|
);
|
|
48
|
-
if (!command) throw new Error(`${
|
|
94
|
+
if (!command) throw new Error(`${workflow.command} failed: bundled workflow is unavailable`);
|
|
95
|
+
return { command, action: workflow.action };
|
|
96
|
+
}
|
|
49
97
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
98
|
+
async function dispatchWorkflow(
|
|
99
|
+
pi: PrCommandPi,
|
|
100
|
+
ctx: ExtensionCommandContext,
|
|
101
|
+
route: WorkflowNextStep,
|
|
102
|
+
reservation: WorkflowReservation,
|
|
103
|
+
invocation: PrCommandInvocation | undefined,
|
|
104
|
+
reserve: NonNullable<PrCommandDependencies["reserveWorkflow"]>,
|
|
105
|
+
markPromptQueued: NonNullable<PrCommandDependencies["markWorkflowPromptQueued"]>,
|
|
106
|
+
release: NonNullable<PrCommandDependencies["releaseWorkflow"]>,
|
|
107
|
+
): Promise<void> {
|
|
108
|
+
const workflow = packageWorkflowCommand(pi, route);
|
|
109
|
+
let runId: string | undefined;
|
|
110
|
+
try {
|
|
111
|
+
runId = await reserve(reservation, ctx, invocation);
|
|
112
|
+
invocation?.assertCurrent();
|
|
113
|
+
const queued = !ctx.isIdle();
|
|
114
|
+
const identity = { route, skill: workflow.command.name, runId, action: workflow.action };
|
|
115
|
+
markPromptQueued(identity, queued);
|
|
116
|
+
const options = queued
|
|
117
|
+
? { deliverAs: "followUp" as const, expandPromptTemplates: true }
|
|
118
|
+
: { expandPromptTemplates: true };
|
|
119
|
+
invocation?.assertCurrent();
|
|
120
|
+
pi.sendUserMessage(`/${identity.skill} runId=${identity.runId} action=${identity.action}`, options);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (runId !== undefined) release(runId, invocation);
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
54
125
|
}
|
|
55
126
|
|
|
56
127
|
function noActionNotification(pullRequest: CurrentPullRequest): { message: string; type: "info" | "warning" } {
|
|
@@ -60,6 +131,9 @@ function noActionNotification(pullRequest: CurrentPullRequest): { message: strin
|
|
|
60
131
|
if (pullRequest.conditions.draft) {
|
|
61
132
|
return { message: `PR #${pullRequest.number} is draft; no action available`, type: "warning" };
|
|
62
133
|
}
|
|
134
|
+
if (pullRequest.conditions.ci === "failure-blocked") {
|
|
135
|
+
return { message: `PR #${pullRequest.number} has a failed CI check that cannot run the CI fix workflow`, type: "warning" };
|
|
136
|
+
}
|
|
63
137
|
const mutatingWorkflowSelected = pullRequest.conditions.baseUpdateRequired || pullRequest.conditions.conflict ||
|
|
64
138
|
pullRequest.conditions.changesRequested || pullRequest.conditions.unresolvedThreads > 0 ||
|
|
65
139
|
pullRequest.conditions.ci === "failure";
|
|
@@ -175,10 +249,20 @@ export function createPrCommandHandler(
|
|
|
175
249
|
): PrCommandHandler {
|
|
176
250
|
const load = dependencies.loadCurrentPullRequest ?? loadCurrentPullRequest;
|
|
177
251
|
const link = dependencies.linkInferredPullRequest ?? linkInferredPullRequest;
|
|
178
|
-
|
|
252
|
+
const reserve = dependencies.reserveWorkflow ?? (async () => {
|
|
253
|
+
throw new Error("/pr workflow tools are unavailable");
|
|
254
|
+
});
|
|
255
|
+
const markPromptQueued = dependencies.markWorkflowPromptQueued ?? (() => {});
|
|
256
|
+
const release = dependencies.releaseWorkflow ?? (() => {});
|
|
257
|
+
return async (args, ctx, onRouteResolved) => {
|
|
258
|
+
const commandInvocation = onRouteResolved && "assertCurrent" in onRouteResolved
|
|
259
|
+
? onRouteResolved as PrCommandInvocation
|
|
260
|
+
: undefined;
|
|
179
261
|
const instructions = args.trim();
|
|
180
262
|
const discovery = await load(pi, ctx);
|
|
263
|
+
commandInvocation?.assertCurrent();
|
|
181
264
|
const nextStep = deriveNextStep(discovery);
|
|
265
|
+
onRouteResolved?.(nextStep);
|
|
182
266
|
if (instructions && !(nextStep in WORKFLOWS)) {
|
|
183
267
|
throw new Error("The current /pr route does not accept instructions");
|
|
184
268
|
}
|
|
@@ -204,7 +288,9 @@ export function createPrCommandHandler(
|
|
|
204
288
|
}
|
|
205
289
|
|
|
206
290
|
if (!(nextStep in WORKFLOWS)) throw new Error(`/pr cannot dispatch route ${nextStep}`);
|
|
207
|
-
|
|
291
|
+
const route = nextStep as WorkflowNextStep;
|
|
292
|
+
const reservation = workflowReservation(route, discovery, instructions);
|
|
293
|
+
await dispatchWorkflow(pi, ctx, route, reservation, commandInvocation, reserve, markPromptQueued, release);
|
|
208
294
|
return nextStep;
|
|
209
295
|
};
|
|
210
296
|
}
|