@mgiles/perk 1.1.0 → 2.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/README.md +68 -44
- package/extension/adapters/planAdapterPlannotator.ts +27 -41
- package/extension/adapters/planAdapterTombell.ts +15 -28
- package/extension/adapters/todoAdapterJuicesharp.ts +10 -13
- package/extension/checkpoints/checkpoints.ts +19 -12
- package/extension/doors/address.ts +4 -4
- package/extension/doors/askUser.ts +12 -8
- package/extension/doors/ciExecutor.ts +21 -14
- package/extension/doors/hunkHandoff.ts +202 -0
- package/extension/doors/land.ts +31 -9
- package/extension/doors/learn.ts +2 -2
- package/extension/doors/learnFactory.ts +144 -0
- package/extension/doors/plannotatorHandoff.ts +509 -0
- package/extension/doors/prReview.ts +4 -4
- package/extension/doors/prReviewBrowser.ts +341 -0
- package/extension/doors/prReviewTerminal.ts +267 -0
- package/extension/doors/selfcheck.ts +238 -5
- package/extension/doors/submit.ts +20 -0
- package/extension/doors/submitPrReview.ts +408 -0
- package/extension/factories/objective.ts +15 -5
- package/extension/factories/objectiveAuthor.ts +15 -32
- package/extension/factories/objectiveDraft.ts +1 -1
- package/extension/factories/objectivePlan.ts +12 -10
- package/extension/factories/objectiveSave.ts +2 -2
- package/extension/factories/planMode.ts +22 -40
- package/extension/factories/planReview.ts +213 -191
- package/extension/factories/planSave.ts +7 -7
- package/extension/index.ts +83 -25
- package/extension/substrate/bindingDelivery.ts +32 -10
- package/extension/substrate/bindings.ts +4 -2
- package/extension/substrate/cache.ts +34 -7
- package/extension/substrate/clipboard.ts +81 -0
- package/extension/substrate/config.ts +88 -65
- package/extension/substrate/git.ts +43 -0
- package/extension/substrate/paths.ts +1 -1
- package/extension/substrate/prompts.ts +2 -2
- package/extension/substrate/providers.ts +62 -8
- package/extension/substrate/sessionPointers.ts +35 -6
- package/extension/substrate/structuredOutput.ts +3 -1
- package/extension/substrate/terminalLaunch.ts +178 -0
- package/extension/substrate/toolGating.ts +330 -79
- package/extension/substrate/toolParams.ts +7 -0
- package/extension/substrate/workflowState.ts +54 -2
- package/extension/surfaces/footerProvider.ts +8 -4
- package/extension/surfaces/surfaces.ts +330 -12
- package/extension/vendor/btw/btw.ts +10 -0
- package/extension/worker/readOnlySession.ts +19 -6
- package/extension/worker/worker.ts +77 -7
- package/extension/workerMain.ts +12 -13
- package/package.json +3 -3
- package/prompts/_fixtures/live.yaml +117 -2
- package/prompts/contexts/adapters/juicesharp-todo.md +7 -0
- package/prompts/contexts/adapters/plannotator-objective.md +7 -0
- package/prompts/contexts/adapters/plannotator-plan.md +6 -0
- package/prompts/contexts/adapters/tombell-plan.md +17 -0
- package/prompts/contexts/objective-authoring.md +20 -0
- package/prompts/contexts/plan-authoring.md +24 -0
- package/prompts/contexts/read-only.md +10 -0
- package/prompts/stages/conflict-resolution.md +1 -1
- package/prompts/stages/learn-code.md +1 -1
- package/prompts/stages/learn-docs.md +2 -2
- package/prompts/stages/learn-orchestrate.md +1 -1
- package/prompts/stages/objective-author/adopt.md +1 -1
- package/prompts/stages/objective-author/file.md +1 -1
- package/prompts/stages/objective-plan/guidance.md +1 -1
- package/prompts/stages/objective-plan/seed.md +1 -1
- package/prompts/stages/objective-reconcile.md +1 -1
- package/prompts/stages/objective-replan.md +1 -1
- package/prompts/stages/plan-from/adopt.md +2 -2
- package/prompts/stages/plan-from/file.md +2 -2
- package/prompts/stages/pr-review-browser/active.md +11 -0
- package/prompts/stages/pr-review-browser/foreign.md +11 -0
- package/prompts/stages/pr-review-terminal/active.md +12 -0
- package/prompts/stages/pr-review-terminal/foreign.md +13 -0
- package/prompts/stages/pr-review-terminal/local.md +4 -0
- package/prompts/stages/pr-review.md +1 -1
- package/prompts/stages/replan.md +2 -2
- package/prompts/stages/skills/create-from.md +1 -1
- package/prompts/stages/skills/create.md +1 -1
- package/prompts/stages/skills/refine.md +1 -1
- package/shared/README.md +22 -18
- package/shared/bindings.yaml +10 -2
- package/shared/contracts-history.md +24 -0
- package/shared/contracts.md +1442 -1787
- package/shared/providers.yaml +8 -1
- package/shared/registry.yaml +7 -8
- package/shared/schemas/inputs/review-submit-batch.schema.json +66 -0
- package/shared/schemas/outputs/pr-review-checkout.schema.json +69 -0
- package/shared/schemas/outputs/pr-review-cleanup.schema.json +54 -0
- package/shared/schemas/outputs/pr-review-submit.schema.json +64 -0
- package/extension/doors/learnCode.ts +0 -100
- package/extension/doors/learnDocs.ts +0 -100
- package/extension/doors/prReviewLocal.ts +0 -229
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
// The warm `/pr-review-local` command: open the plannotator browser code-review UI on the active
|
|
2
|
-
// worktree's PR, with the GitHub PR URL filled in IMPLICITLY (no copy-paste). The end result is
|
|
3
|
-
// identical to plannotator's own `/plannotator-review <pr-url>`.
|
|
4
|
-
//
|
|
5
|
-
// pi exposes NO API for one extension to invoke another's slash command (`sendUserMessage` sends
|
|
6
|
-
// text to the model; `steer`/`followUp` ERROR on slash commands). So perk cannot literally call
|
|
7
|
-
// `/plannotator-review`. Instead it speaks plannotator's published `pi.events` API — a
|
|
8
|
-
// `plannotator:request` with `action: "code-review"` and a `prUrl` payload — which opens the EXACT
|
|
9
|
-
// same browser UI. Same in-process bus perk already uses for plan review (createPlannotatorBridge).
|
|
10
|
-
//
|
|
11
|
-
// A tiny read-only `perk pr url --json` cold door resolves the active PR's URL from the worktree's
|
|
12
|
-
// plan-ref branch (GitHub resolution stays canonical in Python).
|
|
13
|
-
//
|
|
14
|
-
// EVENT ENVELOPE (pinned against `@plannotator/pi-extension@0.21.2`, `plannotator-events.ts`):
|
|
15
|
-
// request — pi.events.emit("plannotator:request", { requestId, action: "code-review",
|
|
16
|
-
// payload: { prUrl, cwd }, respond }) // respond = in-payload callback
|
|
17
|
-
// reply — respond({ status: "handled", result: { approved, feedback?, annotations? } })
|
|
18
|
-
// | respond({ status: "unavailable" | "error", error? })
|
|
19
|
-
// Unlike plan-review there is NO handshake / no `reviewId` channel and no timeout: for code-review
|
|
20
|
-
// plannotator `await openCodeReview(...)` then responds ONCE with the final result.
|
|
21
|
-
|
|
22
|
-
import { randomUUID } from "node:crypto";
|
|
23
|
-
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
24
|
-
import type { PlannotatorBus } from "../adapters/planAdapterPlannotator.ts";
|
|
25
|
-
import {
|
|
26
|
-
type ColdJson,
|
|
27
|
-
numberField,
|
|
28
|
-
objectField,
|
|
29
|
-
runColdDoor,
|
|
30
|
-
stringField,
|
|
31
|
-
} from "../substrate/coldDoor.ts";
|
|
32
|
-
import { registerPerkCommand } from "../substrate/command.ts";
|
|
33
|
-
import { interceptConsoleError } from "../substrate/consoleCapture.ts";
|
|
34
|
-
import { failFor } from "../substrate/result.ts";
|
|
35
|
-
import { report } from "../surfaces/report.ts";
|
|
36
|
-
|
|
37
|
-
/** Plannotator's code-review slash command — its presence detects the extension is loaded. */
|
|
38
|
-
export const PLANNOTATOR_REVIEW_COMMAND = "plannotator-review";
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* The short, perk-authored triage suffix appended to feedback ONLY when the reviewer left
|
|
42
|
-
* annotations — mirrors plannotator's own "address these notes" routing, but perk-worded.
|
|
43
|
-
*/
|
|
44
|
-
const TRIAGE_SUFFIX =
|
|
45
|
-
"\n\nTriage these review notes first: decide which are actionable, then address the actionable ones.";
|
|
46
|
-
|
|
47
|
-
/** The outcome of a plannotator code-review request — a small local discriminated union. */
|
|
48
|
-
export type CodeReviewOutcome =
|
|
49
|
-
| {
|
|
50
|
-
status: "handled";
|
|
51
|
-
approved: boolean;
|
|
52
|
-
feedback: string | undefined;
|
|
53
|
-
annotationCount: number;
|
|
54
|
-
}
|
|
55
|
-
| { status: "unavailable" | "error"; warning: string }
|
|
56
|
-
| { status: "aborted" };
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Whether plannotator is loaded — detected by its `plannotator-review` command being registered
|
|
60
|
-
* (independent of the selected plan provider; code review is orthogonal to plan-review selection).
|
|
61
|
-
* `getCommands()` returns `SlashCommandInfo[]` whose `name` is the bare command name.
|
|
62
|
-
*/
|
|
63
|
-
export function plannotatorPresent(pi: ExtensionAPI): boolean {
|
|
64
|
-
return pi.getCommands().some((c) => c.name === PLANNOTATOR_REVIEW_COMMAND);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** Plannotator's `respond(...)` reply for a code-review request (pinned envelope, see header). */
|
|
68
|
-
interface CodeReviewResponse {
|
|
69
|
-
status?: string;
|
|
70
|
-
error?: string;
|
|
71
|
-
result?: { approved?: unknown; feedback?: unknown; annotations?: unknown };
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* The pure, offline-testable bridge: emit ONE `plannotator:request` with `action: "code-review"`
|
|
76
|
-
* and resolve when plannotator calls `respond(...)`. No handshake / no timeout (plannotator awaits
|
|
77
|
-
* `openCodeReview(...)` then responds once). Honors a turn abort. Pure over the bus → unit-testable
|
|
78
|
-
* with a fake plannotator listener.
|
|
79
|
-
*/
|
|
80
|
-
export async function requestPlannotatorCodeReview(
|
|
81
|
-
bus: PlannotatorBus,
|
|
82
|
-
opts: { prUrl: string; cwd: string; signal?: AbortSignal },
|
|
83
|
-
): Promise<CodeReviewOutcome> {
|
|
84
|
-
if (opts.signal?.aborted) return { status: "aborted" };
|
|
85
|
-
|
|
86
|
-
return await new Promise<CodeReviewOutcome>((resolve) => {
|
|
87
|
-
let settled = false;
|
|
88
|
-
const finish = (outcome: CodeReviewOutcome): void => {
|
|
89
|
-
if (settled) return;
|
|
90
|
-
settled = true;
|
|
91
|
-
opts.signal?.removeEventListener("abort", onAbort);
|
|
92
|
-
resolve(outcome);
|
|
93
|
-
};
|
|
94
|
-
const onAbort = (): void => finish({ status: "aborted" });
|
|
95
|
-
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
96
|
-
|
|
97
|
-
bus.emit("plannotator:request", {
|
|
98
|
-
requestId: randomUUID(),
|
|
99
|
-
action: "code-review",
|
|
100
|
-
payload: { prUrl: opts.prUrl, cwd: opts.cwd },
|
|
101
|
-
respond: (raw: unknown) => {
|
|
102
|
-
const response = raw as CodeReviewResponse;
|
|
103
|
-
if (response?.status === "handled") {
|
|
104
|
-
const result = response.result ?? {};
|
|
105
|
-
const feedback =
|
|
106
|
-
typeof result.feedback === "string" && result.feedback.trim()
|
|
107
|
-
? result.feedback
|
|
108
|
-
: undefined;
|
|
109
|
-
finish({
|
|
110
|
-
status: "handled",
|
|
111
|
-
approved: result.approved === true,
|
|
112
|
-
feedback,
|
|
113
|
-
annotationCount: Array.isArray(result.annotations) ? result.annotations.length : 0,
|
|
114
|
-
});
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
const status = response?.status === "error" ? "error" : "unavailable";
|
|
118
|
-
const detail = response?.error ? `: ${response.error}` : "";
|
|
119
|
-
finish({ status, warning: `plannotator reported ${response?.status ?? status}${detail}` });
|
|
120
|
-
},
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/** Narrow the `perk pr url --json` success payload; strict on `pr.{number,url}`. */
|
|
126
|
-
function decodePrUrl(payload: ColdJson): { number: number; url: string } | null {
|
|
127
|
-
const pr = objectField(payload, "pr");
|
|
128
|
-
if (pr === undefined) return null;
|
|
129
|
-
const number = numberField(pr, "number");
|
|
130
|
-
const url = stringField(pr, "url");
|
|
131
|
-
if (number === undefined || url === undefined) return null;
|
|
132
|
-
return { number, url };
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/** Route the code-review outcome back into the session — mirrors plannotator's own routing. */
|
|
136
|
-
function routePrReviewOutcome(
|
|
137
|
-
pi: ExtensionAPI,
|
|
138
|
-
ctx: ExtensionContext,
|
|
139
|
-
out: CodeReviewOutcome,
|
|
140
|
-
): void {
|
|
141
|
-
if (out.status === "unavailable" || out.status === "error") {
|
|
142
|
-
report(ctx, "pr-review-local", "error", out.warning, { alsoLog: true });
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
if (out.status !== "handled") return; // aborted: the turn was interrupted — no-op
|
|
146
|
-
|
|
147
|
-
if (out.feedback === undefined) {
|
|
148
|
-
report(ctx, "pr-review-local", "info", "Code review approved — no changes requested.");
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
const message = out.feedback + (out.annotationCount > 0 ? TRIAGE_SUFFIX : "");
|
|
152
|
-
// Inject the feedback as a real turn (the submit.ts driveConflictResolution pattern): an
|
|
153
|
-
// immediate turn when idle, else delivered after the current streaming batch.
|
|
154
|
-
if (ctx.isIdle()) {
|
|
155
|
-
pi.sendUserMessage(message);
|
|
156
|
-
} else {
|
|
157
|
-
pi.sendUserMessage(message, { deliverAs: "followUp" });
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/** Register the warm `/pr-review-local` command. */
|
|
162
|
-
export function registerPrReviewLocal(pi: ExtensionAPI): void {
|
|
163
|
-
registerPerkCommand(pi, "pr-review-local", {
|
|
164
|
-
description:
|
|
165
|
-
"Open the plannotator browser code review on the active PR (URL filled in automatically).",
|
|
166
|
-
handler: async (_args, ctx) => {
|
|
167
|
-
if (!ctx.hasUI) {
|
|
168
|
-
report(
|
|
169
|
-
ctx,
|
|
170
|
-
"pr-review-local",
|
|
171
|
-
"info",
|
|
172
|
-
"/pr-review-local requires an interactive session (the plannotator browser review needs UI).",
|
|
173
|
-
);
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
if (!plannotatorPresent(pi)) {
|
|
177
|
-
report(
|
|
178
|
-
ctx,
|
|
179
|
-
"pr-review-local",
|
|
180
|
-
"info",
|
|
181
|
-
"/pr-review-local requires the @plannotator/pi-extension package (its /plannotator-review command was not found).",
|
|
182
|
-
);
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const r = await runColdDoor<{ number: number; url: string }>(
|
|
187
|
-
pi,
|
|
188
|
-
ctx,
|
|
189
|
-
["pr", "url", "--json"],
|
|
190
|
-
{ label: "perk pr url", decode: decodePrUrl },
|
|
191
|
-
);
|
|
192
|
-
if (!r.ok) {
|
|
193
|
-
failFor(ctx, "pr-review-local")(r.message, r.errorType);
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
report(
|
|
198
|
-
ctx,
|
|
199
|
-
"pr-review-local",
|
|
200
|
-
"info",
|
|
201
|
-
`Opening plannotator code review for PR #${r.data.number} …`,
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
// Kick the long-running review in the BACKGROUND — do not block the session for the whole
|
|
205
|
-
// review (plannotator responds once on completion). Mirrors plannotator's own `.then` route.
|
|
206
|
-
// While setup runs, re-route plannotator's in-process `console.error` chatter through the
|
|
207
|
-
// TUI-safe report() seam so it never clobbers the input box; the debounce restores once setup
|
|
208
|
-
// goes quiet, with the `finally` as a backstop.
|
|
209
|
-
void (async () => {
|
|
210
|
-
const interceptor = interceptConsoleError(
|
|
211
|
-
(line) => report(ctx, "pr-review-local", "info", line),
|
|
212
|
-
// plannotator can pause up to ~4s between setup lines — keep the quiet window comfortably
|
|
213
|
-
// above that so the debounce doesn't restore mid-setup and let the next line clobber.
|
|
214
|
-
{ quietMs: 6000 },
|
|
215
|
-
);
|
|
216
|
-
try {
|
|
217
|
-
const out = await requestPlannotatorCodeReview(pi.events, {
|
|
218
|
-
prUrl: r.data.url,
|
|
219
|
-
cwd: ctx.cwd,
|
|
220
|
-
signal: ctx.signal,
|
|
221
|
-
});
|
|
222
|
-
routePrReviewOutcome(pi, ctx, out);
|
|
223
|
-
} finally {
|
|
224
|
-
interceptor.restore();
|
|
225
|
-
}
|
|
226
|
-
})();
|
|
227
|
-
},
|
|
228
|
-
});
|
|
229
|
-
}
|