@mgiles/perk 2.3.0 → 3.1.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 +1 -1
- package/extension/adapters/planAdapterPlannotator.ts +135 -91
- package/extension/adapters/planAdapterTombell.ts +5 -3
- package/extension/doors/address.ts +235 -37
- package/extension/doors/annotationPush.ts +924 -0
- package/extension/doors/auditWaveTools.ts +352 -0
- package/extension/doors/ciExecutor.ts +220 -55
- package/extension/doors/commitCompact.ts +98 -10
- package/extension/doors/draftReviewWaveTools.ts +412 -0
- package/extension/doors/dreamWaveTools.ts +475 -0
- package/extension/doors/harvestWaveTools.ts +279 -0
- package/extension/doors/land.ts +1 -0
- package/extension/doors/learn.ts +24 -6
- package/extension/doors/learnFactory.ts +33 -7
- package/extension/doors/lifecycleGates.ts +1 -1
- package/extension/doors/objectiveReviewBrowser.ts +537 -0
- package/extension/doors/objectiveStack.ts +1143 -0
- package/extension/doors/planReviewBrowser.ts +475 -0
- package/extension/doors/plannotatorHandoff.ts +120 -49
- package/extension/doors/prReview.ts +180 -63
- package/extension/doors/prReviewBrowser.ts +31 -23
- package/extension/doors/prReviewDynamic.ts +57 -30
- package/extension/doors/prReviewTerminal.ts +6 -11
- package/extension/doors/reviewWaveTools.ts +397 -0
- package/extension/doors/selfcheck.ts +1 -1
- package/extension/doors/submit.ts +82 -7
- package/extension/factories/gistAuthor.ts +4 -4
- package/extension/factories/gistDraft.ts +1 -1
- package/extension/factories/gistSave.ts +2 -2
- package/extension/factories/objective.ts +7 -7
- package/extension/factories/objectiveAuthor.ts +5 -4
- package/extension/factories/objectiveDraft.ts +153 -30
- package/extension/factories/objectiveDreamReport.ts +347 -0
- package/extension/factories/objectivePlan.ts +163 -9
- package/extension/factories/objectiveSave.ts +90 -2
- package/extension/factories/planMode.ts +5 -3
- package/extension/factories/planReview.ts +304 -46
- package/extension/factories/planTitle.ts +27 -9
- package/extension/hunkFeedback/inbox.ts +389 -0
- package/extension/hunkFeedback/perkFeedback.ts +364 -0
- package/extension/hunkFeedback/receiver.ts +296 -0
- package/extension/hunkFeedback/store.ts +533 -0
- package/extension/index.ts +169 -57
- package/extension/substrate/agentScratch.ts +171 -0
- package/extension/substrate/bindingDelivery.ts +9 -11
- package/extension/substrate/cache.ts +166 -23
- package/extension/substrate/coldDoor.ts +2 -3
- package/extension/substrate/command.ts +9 -6
- package/extension/substrate/config.ts +41 -12
- package/extension/substrate/git.ts +95 -2
- package/extension/substrate/providers.ts +4 -33
- package/extension/substrate/resources.ts +11 -0
- package/extension/substrate/result.ts +16 -7
- package/extension/substrate/sessionData.ts +8 -6
- package/extension/substrate/sessionPointers.ts +4 -5
- package/extension/substrate/structuredOutput.ts +78 -19
- package/extension/substrate/toolGating.ts +179 -23
- package/extension/substrate/workflowState.ts +50 -2
- package/extension/surfaces/footerProvider.ts +6 -6
- package/extension/surfaces/report.ts +38 -12
- package/extension/surfaces/surfaces.ts +213 -275
- package/extension/vendor/btw/btw.ts +94 -17
- package/extension/waves/adversarialReviewWave.ts +181 -0
- package/extension/waves/auditWave.ts +312 -0
- package/extension/waves/draftReviewWave.ts +205 -0
- package/extension/waves/dreamReducerWave.ts +700 -0
- package/extension/waves/dreamReport.ts +1494 -0
- package/extension/waves/dreamWave.ts +927 -0
- package/extension/waves/harvestWave.ts +399 -0
- package/extension/waves/memoryAdapter.ts +14 -1
- package/extension/waves/objectiveExplorerWave.ts +120 -0
- package/extension/waves/ponytail.ts +104 -0
- package/extension/waves/prReviewDynamicWave.ts +383 -72
- package/extension/waves/prReviewWave.ts +175 -25
- package/extension/waves/reportWave.ts +503 -107
- package/extension/waves/reviewClassifierWave.ts +109 -0
- package/extension/waves/rpcAdapter.ts +65 -2
- package/extension/worker/readOnlySession.ts +4 -5
- package/extension/worker/worker.ts +71 -93
- package/extension/workerMain.ts +4 -6
- package/package.json +18 -5
- package/prompts/README.md +19 -0
- package/prompts/_fixtures/live.yaml +115 -65
- package/prompts/commit-and-compact-continuation.md +13 -0
- package/prompts/common/resume-advisory.md +1 -0
- package/prompts/contexts/adapters/plannotator-gist.md +13 -0
- package/prompts/contexts/adapters/plannotator-objective.md +8 -3
- package/prompts/contexts/adapters/plannotator-plan.md +8 -3
- package/prompts/contexts/gist-authoring.md +6 -20
- package/prompts/contexts/objective-authoring.md +6 -18
- package/prompts/stages/address/action.md +3 -14
- package/prompts/stages/address/preview.md +2 -13
- package/prompts/stages/audit.md +18 -0
- package/prompts/stages/conflict-resolution.md +1 -1
- package/prompts/stages/gist-author/seed.md +4 -4
- package/prompts/stages/gist-save.md +1 -1
- package/prompts/stages/implement.md +3 -1
- package/prompts/stages/learn-code.md +1 -1
- package/prompts/stages/learn-docs.md +2 -2
- package/prompts/stages/learn-dream.md +10 -0
- package/prompts/stages/learn-harvest.md +15 -0
- package/prompts/stages/learn-orchestrate.md +1 -1
- package/prompts/stages/objective-author/adopt.md +5 -3
- package/prompts/stages/objective-author/file.md +5 -4
- package/prompts/stages/objective-author/seed.md +5 -4
- package/prompts/stages/objective-land.md +7 -0
- package/prompts/stages/objective-plan/guidance.md +2 -13
- package/prompts/stages/objective-plan/seed.md +5 -12
- package/prompts/stages/objective-recover.md +8 -0
- package/prompts/stages/objective-replan.md +4 -2
- package/prompts/stages/objective-review-browser.md +9 -0
- package/prompts/stages/objective-sync.md +6 -0
- package/prompts/stages/plan-from/adopt.md +2 -2
- package/prompts/stages/plan-from/file.md +2 -2
- package/prompts/stages/plan-review-browser.md +9 -0
- package/prompts/stages/pr-review-browser/active.md +6 -14
- package/prompts/stages/pr-review-browser/foreign.md +6 -14
- package/prompts/stages/pr-review-dynamic.md +6 -6
- package/prompts/stages/pr-review-terminal/active.md +7 -15
- package/prompts/stages/pr-review-terminal/foreign.md +7 -15
- package/prompts/stages/pr-review-terminal/local.md +1 -1
- package/prompts/stages/pr-review.md +5 -5
- package/prompts/stages/replan.md +3 -3
- package/shared/README.md +2 -2
- package/shared/bindings.yaml +15 -0
- package/shared/contracts-history.md +11 -0
- package/shared/contracts.md +5373 -281
- package/shared/providers.yaml +27 -60
- package/shared/registry.yaml +42 -18
- package/shared/schemas/inputs/review-post-batch.schema.json +14 -1
- package/shared/schemas/outputs/objective-doctor.schema.json +487 -0
- package/shared/schemas/outputs/objective-stack-land.schema.json +732 -0
- package/shared/schemas/outputs/objective-stack-recover.schema.json +397 -0
- package/shared/schemas/outputs/objective-stack-status.schema.json +577 -0
- package/shared/schemas/outputs/objective-stack-sync.schema.json +184 -0
- package/shared/schemas/outputs/plan-save.schema.json +12 -0
- package/shared/schemas/outputs/pr-land.schema.json +3 -3
- package/shared/schemas/outputs/pr-submit.schema.json +177 -1
- package/extension/adapters/todoAdapterJuicesharp.ts +0 -102
- package/extension/checkpoints/checkpoints.ts +0 -550
- package/extension/checkpoints/planSteps.ts +0 -108
- package/extension/doors/askUser.ts +0 -198
- package/prompts/common/output-schemas/objective-explorer.md +0 -36
- package/prompts/common/output-schemas/review-classifier.md +0 -47
- package/prompts/contexts/adapters/juicesharp-todo.md +0 -7
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ For the guided first run, follow
|
|
|
65
65
|
|
|
66
66
|
## Documentation
|
|
67
67
|
|
|
68
|
-
Operator docs live under [`docs/user-docs/`](docs/user-docs/index.
|
|
68
|
+
Operator docs live under [`docs/user-docs/`](docs/user-docs/index.mdx):
|
|
69
69
|
|
|
70
70
|
- **[Tutorials](docs/user-docs/tutorials/index.md)** — learn by doing; start with
|
|
71
71
|
[Get started with perk](docs/user-docs/tutorials/get-started.md).
|
|
@@ -7,12 +7,17 @@
|
|
|
7
7
|
// INJECTION + BRIDGE ONLY: the `plan_review` TOOL lives in `extension/factories/planReview.ts`
|
|
8
8
|
// (perk's backend-neutral review door); this module is the injection-only adapter shape. It owns
|
|
9
9
|
// (1) the plannotator review-step authoring context (injected while the gate is active AND
|
|
10
|
-
// plannotator is selected —
|
|
11
|
-
// dedup'd on the flavor's marker: the plan bridge context,
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
10
|
+
// plannotator is selected — THREE content flavors, one customType, each once-only: branch-scan
|
|
11
|
+
// dedup'd on the flavor's marker: the plan bridge context, the objective flavor when the stage
|
|
12
|
+
// is `objective-author` or `objective-save` (both objective stages route to the objective
|
|
13
|
+
// review arm), or the gist flavor when the stage is `gist-author`) and (2) the pure
|
|
14
|
+
// event-bus bridge
|
|
15
|
+
// (`requestPlannotatorPlanReview`; `createPlannotatorBridge` is its thin structural wrapper)
|
|
16
|
+
// that planReview.ts dispatches to when plannotator is the selected plan provider and the
|
|
17
|
+
// plan-review browser open (plannotatorHandoff.ts) launches. The bridge speaks plannotator's
|
|
18
|
+
// published `plannotator:request` event API (in-process `pi.events` bus); the decision wait is
|
|
19
|
+
// a per-review `plannotator:review-result` listener disposed via the unsubscribe pi's
|
|
20
|
+
// `EventBus.on` returns.
|
|
16
21
|
//
|
|
17
22
|
// INERT BY DEFAULT. The shim is ALWAYS registered in index.ts but the injection fires only when
|
|
18
23
|
// the resolved `[providers] plan` selection is `plannotator-plan` (read fresh per-event, same
|
|
@@ -46,7 +51,9 @@
|
|
|
46
51
|
|
|
47
52
|
import { randomUUID } from "node:crypto";
|
|
48
53
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
54
|
+
import { GIST_AUTHOR_STAGE } from "../factories/gistAuthor.ts";
|
|
49
55
|
import { OBJECTIVE_AUTHOR_STAGE } from "../factories/objectiveAuthor.ts";
|
|
56
|
+
import { OBJECTIVE_SAVE_STAGE } from "../factories/objectiveSave.ts";
|
|
50
57
|
import { resolvedPlanProviderId } from "../factories/planMode.ts";
|
|
51
58
|
// Type-only (erased at runtime — no cycle): the outcome vocabulary lives with the review door.
|
|
52
59
|
import type { ReviewOutcome } from "../factories/planReview.ts";
|
|
@@ -58,6 +65,7 @@ import { branchCarries, branchOf, rebuildWorkflowState } from "../substrate/work
|
|
|
58
65
|
export const PLAN_ADAPTER_PLANNOTATOR_CONTEXT_TYPE = "perk:plan-adapter-plannotator";
|
|
59
66
|
const PLAN_ADAPTER_PLANNOTATOR_MARKER = "[PLAN ADAPTER: PLANNOTATOR]";
|
|
60
67
|
const OBJECTIVE_ADAPTER_PLANNOTATOR_MARKER = "[OBJECTIVE ADAPTER: PLANNOTATOR]";
|
|
68
|
+
const GIST_ADAPTER_PLANNOTATOR_MARKER = "[GIST ADAPTER: PLANNOTATOR]";
|
|
61
69
|
|
|
62
70
|
/**
|
|
63
71
|
* The handshake timeout for plannotator's immediate `respond` callback (mirrors plannotator's own
|
|
@@ -80,8 +88,8 @@ export const PLAN_ADAPTER_PLANNOTATOR_CONTEXT = render("contexts/adapters/planno
|
|
|
80
88
|
});
|
|
81
89
|
|
|
82
90
|
/**
|
|
83
|
-
* The objective flavor of the bridge prompt, injected in an
|
|
84
|
-
* `objective-author`
|
|
91
|
+
* The objective flavor of the bridge prompt, injected in an objective-authoring session
|
|
92
|
+
* (stage `objective-author` or `objective-save`) instead of the plan flavor. An APPROVED review auto-saves the
|
|
85
93
|
* objective via the `objectiveApprovalSave` seam; `/objective-save` is the manual failsafe on
|
|
86
94
|
* the skipped/unavailable arms.
|
|
87
95
|
*/
|
|
@@ -90,6 +98,16 @@ export const OBJECTIVE_ADAPTER_PLANNOTATOR_CONTEXT = render(
|
|
|
90
98
|
{ marker: OBJECTIVE_ADAPTER_PLANNOTATOR_MARKER },
|
|
91
99
|
);
|
|
92
100
|
|
|
101
|
+
/**
|
|
102
|
+
* The gist flavor of the bridge prompt, injected in a `gist-author` session instead of the
|
|
103
|
+
* plan/objective flavors. The review surface renders the gist draft (title + scope line +
|
|
104
|
+
* prose); an approval carrying `# Direct Edits` does NOT auto-save — the model folds the diff
|
|
105
|
+
* into the matching `gist_draft` fields and re-reviews (contracts.md §8.23's gist arm).
|
|
106
|
+
*/
|
|
107
|
+
export const GIST_ADAPTER_PLANNOTATOR_CONTEXT = render("contexts/adapters/plannotator-gist.md", {
|
|
108
|
+
marker: GIST_ADAPTER_PLANNOTATOR_MARKER,
|
|
109
|
+
});
|
|
110
|
+
|
|
93
111
|
/** Whether the foreign `plannotator-plan` provider is the selected plan provider for `cwd`. */
|
|
94
112
|
export function isPlannotatorPlanSelected(cwd: string): boolean {
|
|
95
113
|
return resolvedPlanProviderId(cwd) === PLANNOTATOR_PLAN_PROVIDER_ID;
|
|
@@ -97,10 +115,10 @@ export function isPlannotatorPlanSelected(cwd: string): boolean {
|
|
|
97
115
|
|
|
98
116
|
// ------------------------------------------------------------------ the event-bus bridge core
|
|
99
117
|
|
|
100
|
-
/** The minimal `pi.events` surface the bridge needs (mirrors pi's EventBus). */
|
|
118
|
+
/** The minimal `pi.events` surface the bridge needs (mirrors pi's EventBus, whose `on` returns an unsubscribe function). */
|
|
101
119
|
export interface PlannotatorBus {
|
|
102
120
|
emit(channel: string, data: unknown): void;
|
|
103
|
-
on(channel: string, handler: (data: unknown) => void): void;
|
|
121
|
+
on(channel: string, handler: (data: unknown) => void): () => void;
|
|
104
122
|
}
|
|
105
123
|
|
|
106
124
|
/** Plannotator's immediate `respond` handshake payload (pinned envelope, see header). */
|
|
@@ -117,84 +135,96 @@ interface ReviewDecision {
|
|
|
117
135
|
}
|
|
118
136
|
|
|
119
137
|
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
138
|
+
* The pure, offline-testable plan-review bridge (the ergonomic mirror of
|
|
139
|
+
* `requestPlannotatorCodeReview` in plannotatorHandoff.ts): emit ONE `plannotator:request` with
|
|
140
|
+
* `action: "plan-review"`, await the bounded `respond` handshake, then await the human decision
|
|
141
|
+
* on a PER-REVIEW `plannotator:review-result` listener — filtered on the handshake's `reviewId`
|
|
142
|
+
* and disposed via the unsubscribe `bus.on` returns when the decision arrives or the turn
|
|
143
|
+
* aborts. Pure over the bus → unit-testable offline with a fake plannotator listener.
|
|
124
144
|
*/
|
|
125
|
-
export function
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
145
|
+
export async function requestPlannotatorPlanReview(
|
|
146
|
+
bus: PlannotatorBus,
|
|
147
|
+
plan: string,
|
|
148
|
+
signal?: AbortSignal,
|
|
149
|
+
): Promise<ReviewOutcome> {
|
|
150
|
+
if (signal?.aborted) return { status: "aborted" };
|
|
129
151
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
pending.delete(d.reviewId);
|
|
136
|
-
resolve({
|
|
137
|
-
approved: d.approved === true,
|
|
138
|
-
feedback: typeof d.feedback === "string" && d.feedback.trim() ? d.feedback : undefined,
|
|
139
|
-
});
|
|
152
|
+
// 1. Emit the request and await the immediate `respond` handshake (bounded — fail-open).
|
|
153
|
+
const requestId = randomUUID();
|
|
154
|
+
let respondResolve: (response: HandshakeResponse) => void = () => {};
|
|
155
|
+
const handshake = new Promise<HandshakeResponse | "timeout">((resolve) => {
|
|
156
|
+
respondResolve = resolve;
|
|
140
157
|
});
|
|
158
|
+
const timer = setTimeout(() => respondResolve("timeout" as never), handshakeTimeoutMs());
|
|
159
|
+
bus.emit("plannotator:request", {
|
|
160
|
+
requestId,
|
|
161
|
+
action: "plan-review",
|
|
162
|
+
payload: { planContent: plan, origin: "perk" },
|
|
163
|
+
respond: (response: unknown) => respondResolve(response as HandshakeResponse),
|
|
164
|
+
});
|
|
165
|
+
const response = await handshake;
|
|
166
|
+
clearTimeout(timer);
|
|
141
167
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
168
|
+
if (response === "timeout") {
|
|
169
|
+
return {
|
|
170
|
+
status: "unavailable",
|
|
171
|
+
warning: "plannotator did not respond to the review request (handshake timeout)",
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
if (response?.status !== "handled") {
|
|
175
|
+
const detail = response?.error ? `: ${response.error}` : "";
|
|
176
|
+
return {
|
|
177
|
+
status: "unavailable",
|
|
178
|
+
warning: `plannotator reported ${response?.status ?? "an invalid response"}${detail}`,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const reviewId = response.result?.reviewId;
|
|
182
|
+
if (response.result?.status !== "pending" || typeof reviewId !== "string") {
|
|
183
|
+
return {
|
|
184
|
+
status: "unavailable",
|
|
185
|
+
warning: "plannotator handshake returned no pending reviewId",
|
|
186
|
+
};
|
|
187
|
+
}
|
|
160
188
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
warning: "plannotator did not respond to the review request (handshake timeout)",
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
if (response?.status !== "handled") {
|
|
168
|
-
const detail = response?.error ? `: ${response.error}` : "";
|
|
169
|
-
return {
|
|
170
|
-
status: "unavailable",
|
|
171
|
-
warning: `plannotator reported ${response?.status ?? "an invalid response"}${detail}`,
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
const reviewId = response.result?.reviewId;
|
|
175
|
-
if (response.result?.status !== "pending" || typeof reviewId !== "string") {
|
|
176
|
-
return {
|
|
177
|
-
status: "unavailable",
|
|
178
|
-
warning: "plannotator handshake returned no pending reviewId",
|
|
179
|
-
};
|
|
180
|
-
}
|
|
189
|
+
// A turn aborted DURING the handshake wait must not wedge: `addEventListener("abort", …)` on
|
|
190
|
+
// an already-aborted signal never fires, so re-check before registering the decision wait.
|
|
191
|
+
if (signal?.aborted) return { status: "aborted" };
|
|
181
192
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
193
|
+
// 2. Await the human decision (no timeout — the reviewer takes as long as they take), but
|
|
194
|
+
// honor a turn abort so an interrupted session never leaks a wedged promise. Either exit
|
|
195
|
+
// disposes the result listener via the unsubscribe.
|
|
196
|
+
return await new Promise<ReviewOutcome>((resolve) => {
|
|
197
|
+
let settled = false;
|
|
198
|
+
const finish = (outcome: ReviewOutcome): void => {
|
|
199
|
+
if (settled) return;
|
|
200
|
+
settled = true;
|
|
201
|
+
unsubscribe();
|
|
202
|
+
signal?.removeEventListener("abort", onAbort);
|
|
203
|
+
resolve(outcome);
|
|
204
|
+
};
|
|
205
|
+
const onAbort = (): void => finish({ status: "aborted" });
|
|
206
|
+
const unsubscribe = bus.on("plannotator:review-result", (data) => {
|
|
207
|
+
const d = data as { reviewId?: unknown; approved?: unknown; feedback?: unknown };
|
|
208
|
+
if (d?.reviewId !== reviewId) return;
|
|
209
|
+
const decision: ReviewDecision = {
|
|
210
|
+
approved: d.approved === true,
|
|
211
|
+
feedback: typeof d.feedback === "string" && d.feedback.trim() ? d.feedback : undefined,
|
|
188
212
|
};
|
|
189
|
-
|
|
190
|
-
signal?.removeEventListener("abort", onAbort);
|
|
191
|
-
resolve({ status: "completed", reviewId, ...decision });
|
|
192
|
-
});
|
|
193
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
213
|
+
finish({ status: "completed", reviewId, ...decision });
|
|
194
214
|
});
|
|
195
|
-
|
|
215
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
216
|
+
});
|
|
217
|
+
}
|
|
196
218
|
|
|
197
|
-
|
|
219
|
+
/**
|
|
220
|
+
* Create the plannotator bridge over an event bus — the thin structural slice
|
|
221
|
+
* (`{ review(plan, signal) }`) that `registerPlanReview` injects into the review door; the body
|
|
222
|
+
* lives in `requestPlannotatorPlanReview`.
|
|
223
|
+
*/
|
|
224
|
+
export function createPlannotatorBridge(bus: PlannotatorBus): {
|
|
225
|
+
review(plan: string, signal?: AbortSignal): Promise<ReviewOutcome>;
|
|
226
|
+
} {
|
|
227
|
+
return { review: (plan, signal) => requestPlannotatorPlanReview(bus, plan, signal) };
|
|
198
228
|
}
|
|
199
229
|
|
|
200
230
|
// ------------------------------------------------------------------ Direct Edits extraction
|
|
@@ -260,8 +290,10 @@ export function extractDirectEdits(feedback: string): { diff: string; remainder?
|
|
|
260
290
|
*/
|
|
261
291
|
export function registerPlanAdapterPlannotator(pi: ExtensionAPI): void {
|
|
262
292
|
// Inject the bridge context while the read-only gate is active AND plannotator is selected.
|
|
263
|
-
//
|
|
264
|
-
// objective
|
|
293
|
+
// Three content flavors, one customType: an objective-authoring session (also read-only —
|
|
294
|
+
// BOTH objective stages: `plan_review` routes objective-author AND objective-save to the
|
|
295
|
+
// objective review arm) gets the objective flavor (the review surface renders the objective
|
|
296
|
+
// draft), a gist-author session gets the gist flavor (the rendered gist draft); any other
|
|
265
297
|
// gated stage gets the plan flavor. The gate-active check reads the persisted
|
|
266
298
|
// `perk:workflow-state.mode` (the gate's state twin) — never the gate itself.
|
|
267
299
|
pi.on("before_agent_start", async (_event, ctx) => {
|
|
@@ -269,17 +301,28 @@ export function registerPlanAdapterPlannotator(pi: ExtensionAPI): void {
|
|
|
269
301
|
const branch = branchOf(ctx);
|
|
270
302
|
const state = rebuildWorkflowState(branch);
|
|
271
303
|
if (state.mode !== "read-only") return;
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
304
|
+
const flavor =
|
|
305
|
+
state.stage === OBJECTIVE_AUTHOR_STAGE || state.stage === OBJECTIVE_SAVE_STAGE
|
|
306
|
+
? "objective"
|
|
307
|
+
: state.stage === GIST_AUTHOR_STAGE
|
|
308
|
+
? "gist"
|
|
309
|
+
: "plan";
|
|
310
|
+
const content =
|
|
311
|
+
flavor === "objective"
|
|
312
|
+
? OBJECTIVE_ADAPTER_PLANNOTATOR_CONTEXT
|
|
313
|
+
: flavor === "gist"
|
|
314
|
+
? GIST_ADAPTER_PLANNOTATOR_CONTEXT
|
|
315
|
+
: PLAN_ADAPTER_PLANNOTATOR_CONTEXT;
|
|
276
316
|
// Once-only PER FLAVOR: the dedup key is the flavor's marker (not the shared customType), so
|
|
277
|
-
// a stage change still delivers the missing flavor while a prior copy of
|
|
317
|
+
// a stage change still delivers the missing flavor while a prior copy of another flavor
|
|
278
318
|
// sits on the branch. Injected customs persist, so a live copy suppresses re-injection;
|
|
279
319
|
// compaction dropping it makes the scan come up clean and the next turn re-injects.
|
|
280
|
-
const marker =
|
|
281
|
-
|
|
282
|
-
|
|
320
|
+
const marker =
|
|
321
|
+
flavor === "objective"
|
|
322
|
+
? OBJECTIVE_ADAPTER_PLANNOTATOR_MARKER
|
|
323
|
+
: flavor === "gist"
|
|
324
|
+
? GIST_ADAPTER_PLANNOTATOR_MARKER
|
|
325
|
+
: PLAN_ADAPTER_PLANNOTATOR_MARKER;
|
|
283
326
|
if (branchCarries(branch, marker)) return;
|
|
284
327
|
return {
|
|
285
328
|
message: {
|
|
@@ -290,11 +333,12 @@ export function registerPlanAdapterPlannotator(pi: ExtensionAPI): void {
|
|
|
290
333
|
};
|
|
291
334
|
});
|
|
292
335
|
|
|
293
|
-
// Strip the stale bridge markers (
|
|
336
|
+
// Strip the stale bridge markers (ALL THREE flavors) from context when plannotator-plan is no
|
|
294
337
|
// longer selected (same hygiene as the tombell shim), so they never linger across a deselect.
|
|
295
338
|
const hasMarker = (text: string): boolean =>
|
|
296
339
|
text.includes(PLAN_ADAPTER_PLANNOTATOR_MARKER) ||
|
|
297
|
-
text.includes(OBJECTIVE_ADAPTER_PLANNOTATOR_MARKER)
|
|
340
|
+
text.includes(OBJECTIVE_ADAPTER_PLANNOTATOR_MARKER) ||
|
|
341
|
+
text.includes(GIST_ADAPTER_PLANNOTATOR_MARKER);
|
|
298
342
|
pi.on("context", async (event, ctx) => {
|
|
299
343
|
if (isPlannotatorPlanSelected(ctx.cwd)) return;
|
|
300
344
|
return {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
// stages bind only to the provider-agnostic plan-ref and are unchanged.
|
|
36
36
|
|
|
37
37
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
38
|
+
import { GIST_AUTHOR_STAGE } from "../factories/gistAuthor.ts";
|
|
38
39
|
import { OBJECTIVE_AUTHOR_STAGE } from "../factories/objectiveAuthor.ts";
|
|
39
40
|
import { resolvedPlanProviderId } from "../factories/planMode.ts";
|
|
40
41
|
import { render } from "../substrate/prompts.ts";
|
|
@@ -89,13 +90,14 @@ export function registerPlanAdapterTombell(pi: ExtensionAPI): void {
|
|
|
89
90
|
// Inject the bridge context while the foreign tombell-plan provider is selected AND a plan
|
|
90
91
|
// authoring mode is on — perk's read-only gate (per the persisted `perk:workflow-state.mode`,
|
|
91
92
|
// the gate's state twin — never the gate object) OR tombell's own persisted `plan-mode-state`
|
|
92
|
-
// entry (the ad-hoc interactive `/plan` arm). Objective-author sessions are
|
|
93
|
-
// (objectiveAuthor
|
|
93
|
+
// entry (the ad-hoc interactive `/plan` arm). Objective-author and gist-author sessions are
|
|
94
|
+
// excepted (objectiveAuthor/gistAuthor own those sessions; mirrors the plannotator adapter's
|
|
95
|
+
// recipe — the tombell REPLACE posture covers the plan surface only).
|
|
94
96
|
pi.on("before_agent_start", async (_event, ctx) => {
|
|
95
97
|
if (!isTombellPlanSelected(ctx.cwd)) return;
|
|
96
98
|
const branch = branchOf(ctx);
|
|
97
99
|
const state = rebuildWorkflowState(branch);
|
|
98
|
-
if (state.stage === OBJECTIVE_AUTHOR_STAGE) return;
|
|
100
|
+
if (state.stage === OBJECTIVE_AUTHOR_STAGE || state.stage === GIST_AUTHOR_STAGE) return;
|
|
99
101
|
if (state.mode !== "read-only" && !isTombellPlanModeEnabled(branch)) return;
|
|
100
102
|
// Once-only: injected customs persist to the branch, so a live copy suppresses re-injection;
|
|
101
103
|
// compaction dropping it makes the scan come up clean and the next turn re-injects.
|