@mgiles/perk 2.2.0 → 3.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 +1 -1
- package/extension/adapters/planAdapterPlannotator.ts +132 -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/draftReviewWaveTools.ts +384 -0
- package/extension/doors/harvestWaveTools.ts +279 -0
- package/extension/doors/land.ts +1 -0
- package/extension/doors/learn.ts +238 -24
- package/extension/doors/learnFactory.ts +33 -7
- package/extension/doors/lifecycleGates.ts +1 -1
- package/extension/doors/objectiveReviewBrowser.ts +514 -0
- package/extension/doors/objectiveStack.ts +1143 -0
- package/extension/doors/planReviewBrowser.ts +453 -0
- package/extension/doors/plannotatorHandoff.ts +120 -49
- package/extension/doors/prReview.ts +203 -22
- package/extension/doors/prReviewBrowser.ts +31 -23
- package/extension/doors/prReviewDynamic.ts +256 -0
- package/extension/doors/prReviewTerminal.ts +6 -11
- package/extension/doors/reviewWaveTools.ts +374 -0
- package/extension/doors/selfcheck.ts +1 -1
- package/extension/doors/submit.ts +86 -10
- 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 +61 -6
- package/extension/factories/objectivePlan.ts +163 -8
- package/extension/factories/objectiveSave.ts +16 -1
- package/extension/factories/planMode.ts +5 -3
- package/extension/factories/planReview.ts +131 -36
- 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 +116 -44
- package/extension/substrate/cache.ts +75 -22
- package/extension/substrate/coldDoor.ts +2 -3
- package/extension/substrate/config.ts +42 -14
- package/extension/substrate/git.ts +10 -0
- package/extension/substrate/providers.ts +4 -33
- package/extension/substrate/resources.ts +11 -0
- package/extension/substrate/result.ts +13 -5
- package/extension/substrate/sessionData.ts +3 -3
- package/extension/substrate/sessionPointers.ts +3 -3
- package/extension/substrate/structuredOutput.ts +78 -19
- package/extension/substrate/terminalLaunch.ts +1 -1
- package/extension/substrate/toolGating.ts +206 -26
- package/extension/substrate/workflowState.ts +6 -0
- package/extension/surfaces/footerProvider.ts +6 -6
- package/extension/surfaces/surfaces.ts +98 -282
- package/extension/vendor/btw/btw.ts +57 -12
- package/extension/waves/adversarialReviewWave.ts +164 -0
- package/extension/waves/auditWave.ts +312 -0
- package/extension/waves/draftReviewWave.ts +189 -0
- package/extension/waves/harvestWave.ts +399 -0
- package/extension/waves/learnWave.ts +155 -0
- package/extension/waves/memoryAdapter.ts +139 -0
- package/extension/waves/objectiveExplorerWave.ts +120 -0
- package/extension/waves/prReviewDynamicWave.ts +696 -0
- package/extension/waves/prReviewWave.ts +274 -0
- package/extension/waves/reportWave.ts +749 -0
- package/extension/waves/reviewClassifierWave.ts +109 -0
- package/extension/waves/rpcAdapter.ts +264 -0
- package/extension/worker/readOnlySession.ts +3 -3
- package/extension/worker/worker.ts +71 -93
- package/extension/workerMain.ts +4 -6
- package/package.json +20 -5
- package/prompts/README.md +19 -0
- package/prompts/_fixtures/live.yaml +71 -72
- package/prompts/common/resume-advisory.md +1 -0
- package/prompts/contexts/adapters/plannotator-gist.md +13 -0
- package/prompts/contexts/adapters/plannotator-objective.md +1 -2
- package/prompts/contexts/adapters/plannotator-plan.md +1 -2
- package/prompts/contexts/gist-authoring.md +6 -20
- package/prompts/contexts/objective-authoring.md +6 -18
- package/prompts/stages/address/action.md +4 -4
- package/prompts/stages/address/preview.md +3 -3
- 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-harvest.md +15 -0
- package/prompts/stages/learn-orchestrate.md +7 -5
- 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 -2
- package/prompts/stages/objective-plan/seed.md +5 -1
- 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 -6
- package/prompts/stages/pr-review-browser/foreign.md +6 -6
- package/prompts/stages/pr-review-dynamic.md +7 -0
- package/prompts/stages/pr-review-terminal/active.md +7 -7
- package/prompts/stages/pr-review-terminal/foreign.md +7 -7
- package/prompts/stages/pr-review.md +7 -6
- 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 +3713 -227
- package/shared/providers.yaml +27 -60
- package/shared/registry.yaml +32 -8
- package/shared/schemas/outputs/objective-doctor.schema.json +449 -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-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/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,16 @@
|
|
|
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
|
-
// objective flavor when the stage is `
|
|
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 the gist flavor when the stage is `gist-author`) and (2) the pure
|
|
13
|
+
// event-bus bridge
|
|
14
|
+
// (`requestPlannotatorPlanReview`; `createPlannotatorBridge` is its thin structural wrapper)
|
|
15
|
+
// that planReview.ts dispatches to when plannotator is the selected plan provider and the
|
|
16
|
+
// plan-review browser open (plannotatorHandoff.ts) launches. The bridge speaks plannotator's
|
|
17
|
+
// published `plannotator:request` event API (in-process `pi.events` bus); the decision wait is
|
|
18
|
+
// a per-review `plannotator:review-result` listener disposed via the unsubscribe pi's
|
|
19
|
+
// `EventBus.on` returns.
|
|
16
20
|
//
|
|
17
21
|
// INERT BY DEFAULT. The shim is ALWAYS registered in index.ts but the injection fires only when
|
|
18
22
|
// the resolved `[providers] plan` selection is `plannotator-plan` (read fresh per-event, same
|
|
@@ -46,6 +50,7 @@
|
|
|
46
50
|
|
|
47
51
|
import { randomUUID } from "node:crypto";
|
|
48
52
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
53
|
+
import { GIST_AUTHOR_STAGE } from "../factories/gistAuthor.ts";
|
|
49
54
|
import { OBJECTIVE_AUTHOR_STAGE } from "../factories/objectiveAuthor.ts";
|
|
50
55
|
import { resolvedPlanProviderId } from "../factories/planMode.ts";
|
|
51
56
|
// Type-only (erased at runtime — no cycle): the outcome vocabulary lives with the review door.
|
|
@@ -58,6 +63,7 @@ import { branchCarries, branchOf, rebuildWorkflowState } from "../substrate/work
|
|
|
58
63
|
export const PLAN_ADAPTER_PLANNOTATOR_CONTEXT_TYPE = "perk:plan-adapter-plannotator";
|
|
59
64
|
const PLAN_ADAPTER_PLANNOTATOR_MARKER = "[PLAN ADAPTER: PLANNOTATOR]";
|
|
60
65
|
const OBJECTIVE_ADAPTER_PLANNOTATOR_MARKER = "[OBJECTIVE ADAPTER: PLANNOTATOR]";
|
|
66
|
+
const GIST_ADAPTER_PLANNOTATOR_MARKER = "[GIST ADAPTER: PLANNOTATOR]";
|
|
61
67
|
|
|
62
68
|
/**
|
|
63
69
|
* The handshake timeout for plannotator's immediate `respond` callback (mirrors plannotator's own
|
|
@@ -90,6 +96,16 @@ export const OBJECTIVE_ADAPTER_PLANNOTATOR_CONTEXT = render(
|
|
|
90
96
|
{ marker: OBJECTIVE_ADAPTER_PLANNOTATOR_MARKER },
|
|
91
97
|
);
|
|
92
98
|
|
|
99
|
+
/**
|
|
100
|
+
* The gist flavor of the bridge prompt, injected in a `gist-author` session instead of the
|
|
101
|
+
* plan/objective flavors. The review surface renders the gist draft (title + scope line +
|
|
102
|
+
* prose); an approval carrying `# Direct Edits` does NOT auto-save — the model folds the diff
|
|
103
|
+
* into the matching `gist_draft` fields and re-reviews (contracts.md §8.23's gist arm).
|
|
104
|
+
*/
|
|
105
|
+
export const GIST_ADAPTER_PLANNOTATOR_CONTEXT = render("contexts/adapters/plannotator-gist.md", {
|
|
106
|
+
marker: GIST_ADAPTER_PLANNOTATOR_MARKER,
|
|
107
|
+
});
|
|
108
|
+
|
|
93
109
|
/** Whether the foreign `plannotator-plan` provider is the selected plan provider for `cwd`. */
|
|
94
110
|
export function isPlannotatorPlanSelected(cwd: string): boolean {
|
|
95
111
|
return resolvedPlanProviderId(cwd) === PLANNOTATOR_PLAN_PROVIDER_ID;
|
|
@@ -97,10 +113,10 @@ export function isPlannotatorPlanSelected(cwd: string): boolean {
|
|
|
97
113
|
|
|
98
114
|
// ------------------------------------------------------------------ the event-bus bridge core
|
|
99
115
|
|
|
100
|
-
/** The minimal `pi.events` surface the bridge needs (mirrors pi's EventBus). */
|
|
116
|
+
/** The minimal `pi.events` surface the bridge needs (mirrors pi's EventBus, whose `on` returns an unsubscribe function). */
|
|
101
117
|
export interface PlannotatorBus {
|
|
102
118
|
emit(channel: string, data: unknown): void;
|
|
103
|
-
on(channel: string, handler: (data: unknown) => void): void;
|
|
119
|
+
on(channel: string, handler: (data: unknown) => void): () => void;
|
|
104
120
|
}
|
|
105
121
|
|
|
106
122
|
/** Plannotator's immediate `respond` handshake payload (pinned envelope, see header). */
|
|
@@ -117,84 +133,96 @@ interface ReviewDecision {
|
|
|
117
133
|
}
|
|
118
134
|
|
|
119
135
|
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
136
|
+
* The pure, offline-testable plan-review bridge (the ergonomic mirror of
|
|
137
|
+
* `requestPlannotatorCodeReview` in plannotatorHandoff.ts): emit ONE `plannotator:request` with
|
|
138
|
+
* `action: "plan-review"`, await the bounded `respond` handshake, then await the human decision
|
|
139
|
+
* on a PER-REVIEW `plannotator:review-result` listener — filtered on the handshake's `reviewId`
|
|
140
|
+
* and disposed via the unsubscribe `bus.on` returns when the decision arrives or the turn
|
|
141
|
+
* aborts. Pure over the bus → unit-testable offline with a fake plannotator listener.
|
|
124
142
|
*/
|
|
125
|
-
export function
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
143
|
+
export async function requestPlannotatorPlanReview(
|
|
144
|
+
bus: PlannotatorBus,
|
|
145
|
+
plan: string,
|
|
146
|
+
signal?: AbortSignal,
|
|
147
|
+
): Promise<ReviewOutcome> {
|
|
148
|
+
if (signal?.aborted) return { status: "aborted" };
|
|
129
149
|
|
|
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
|
-
});
|
|
150
|
+
// 1. Emit the request and await the immediate `respond` handshake (bounded — fail-open).
|
|
151
|
+
const requestId = randomUUID();
|
|
152
|
+
let respondResolve: (response: HandshakeResponse) => void = () => {};
|
|
153
|
+
const handshake = new Promise<HandshakeResponse | "timeout">((resolve) => {
|
|
154
|
+
respondResolve = resolve;
|
|
140
155
|
});
|
|
156
|
+
const timer = setTimeout(() => respondResolve("timeout" as never), handshakeTimeoutMs());
|
|
157
|
+
bus.emit("plannotator:request", {
|
|
158
|
+
requestId,
|
|
159
|
+
action: "plan-review",
|
|
160
|
+
payload: { planContent: plan, origin: "perk" },
|
|
161
|
+
respond: (response: unknown) => respondResolve(response as HandshakeResponse),
|
|
162
|
+
});
|
|
163
|
+
const response = await handshake;
|
|
164
|
+
clearTimeout(timer);
|
|
141
165
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
166
|
+
if (response === "timeout") {
|
|
167
|
+
return {
|
|
168
|
+
status: "unavailable",
|
|
169
|
+
warning: "plannotator did not respond to the review request (handshake timeout)",
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (response?.status !== "handled") {
|
|
173
|
+
const detail = response?.error ? `: ${response.error}` : "";
|
|
174
|
+
return {
|
|
175
|
+
status: "unavailable",
|
|
176
|
+
warning: `plannotator reported ${response?.status ?? "an invalid response"}${detail}`,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const reviewId = response.result?.reviewId;
|
|
180
|
+
if (response.result?.status !== "pending" || typeof reviewId !== "string") {
|
|
181
|
+
return {
|
|
182
|
+
status: "unavailable",
|
|
183
|
+
warning: "plannotator handshake returned no pending reviewId",
|
|
184
|
+
};
|
|
185
|
+
}
|
|
160
186
|
|
|
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
|
-
}
|
|
187
|
+
// A turn aborted DURING the handshake wait must not wedge: `addEventListener("abort", …)` on
|
|
188
|
+
// an already-aborted signal never fires, so re-check before registering the decision wait.
|
|
189
|
+
if (signal?.aborted) return { status: "aborted" };
|
|
181
190
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
191
|
+
// 2. Await the human decision (no timeout — the reviewer takes as long as they take), but
|
|
192
|
+
// honor a turn abort so an interrupted session never leaks a wedged promise. Either exit
|
|
193
|
+
// disposes the result listener via the unsubscribe.
|
|
194
|
+
return await new Promise<ReviewOutcome>((resolve) => {
|
|
195
|
+
let settled = false;
|
|
196
|
+
const finish = (outcome: ReviewOutcome): void => {
|
|
197
|
+
if (settled) return;
|
|
198
|
+
settled = true;
|
|
199
|
+
unsubscribe();
|
|
200
|
+
signal?.removeEventListener("abort", onAbort);
|
|
201
|
+
resolve(outcome);
|
|
202
|
+
};
|
|
203
|
+
const onAbort = (): void => finish({ status: "aborted" });
|
|
204
|
+
const unsubscribe = bus.on("plannotator:review-result", (data) => {
|
|
205
|
+
const d = data as { reviewId?: unknown; approved?: unknown; feedback?: unknown };
|
|
206
|
+
if (d?.reviewId !== reviewId) return;
|
|
207
|
+
const decision: ReviewDecision = {
|
|
208
|
+
approved: d.approved === true,
|
|
209
|
+
feedback: typeof d.feedback === "string" && d.feedback.trim() ? d.feedback : undefined,
|
|
188
210
|
};
|
|
189
|
-
|
|
190
|
-
signal?.removeEventListener("abort", onAbort);
|
|
191
|
-
resolve({ status: "completed", reviewId, ...decision });
|
|
192
|
-
});
|
|
193
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
211
|
+
finish({ status: "completed", reviewId, ...decision });
|
|
194
212
|
});
|
|
195
|
-
|
|
213
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
214
|
+
});
|
|
215
|
+
}
|
|
196
216
|
|
|
197
|
-
|
|
217
|
+
/**
|
|
218
|
+
* Create the plannotator bridge over an event bus — the thin structural slice
|
|
219
|
+
* (`{ review(plan, signal) }`) that `registerPlanReview` injects into the review door; the body
|
|
220
|
+
* lives in `requestPlannotatorPlanReview`.
|
|
221
|
+
*/
|
|
222
|
+
export function createPlannotatorBridge(bus: PlannotatorBus): {
|
|
223
|
+
review(plan: string, signal?: AbortSignal): Promise<ReviewOutcome>;
|
|
224
|
+
} {
|
|
225
|
+
return { review: (plan, signal) => requestPlannotatorPlanReview(bus, plan, signal) };
|
|
198
226
|
}
|
|
199
227
|
|
|
200
228
|
// ------------------------------------------------------------------ Direct Edits extraction
|
|
@@ -260,26 +288,38 @@ export function extractDirectEdits(feedback: string): { diff: string; remainder?
|
|
|
260
288
|
*/
|
|
261
289
|
export function registerPlanAdapterPlannotator(pi: ExtensionAPI): void {
|
|
262
290
|
// Inject the bridge context while the read-only gate is active AND plannotator is selected.
|
|
263
|
-
//
|
|
264
|
-
// objective flavor (the review surface renders the objective draft)
|
|
265
|
-
//
|
|
266
|
-
// `perk:workflow-state.mode` (the gate's
|
|
291
|
+
// Three content flavors, one customType: an objective-author session (also read-only) gets
|
|
292
|
+
// the objective flavor (the review surface renders the objective draft), a gist-author
|
|
293
|
+
// session gets the gist flavor (the rendered gist draft); any other gated stage gets the plan
|
|
294
|
+
// flavor. The gate-active check reads the persisted `perk:workflow-state.mode` (the gate's
|
|
295
|
+
// state twin) — never the gate itself.
|
|
267
296
|
pi.on("before_agent_start", async (_event, ctx) => {
|
|
268
297
|
if (!isPlannotatorPlanSelected(ctx.cwd)) return;
|
|
269
298
|
const branch = branchOf(ctx);
|
|
270
299
|
const state = rebuildWorkflowState(branch);
|
|
271
300
|
if (state.mode !== "read-only") return;
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
301
|
+
const flavor =
|
|
302
|
+
state.stage === OBJECTIVE_AUTHOR_STAGE
|
|
303
|
+
? "objective"
|
|
304
|
+
: state.stage === GIST_AUTHOR_STAGE
|
|
305
|
+
? "gist"
|
|
306
|
+
: "plan";
|
|
307
|
+
const content =
|
|
308
|
+
flavor === "objective"
|
|
309
|
+
? OBJECTIVE_ADAPTER_PLANNOTATOR_CONTEXT
|
|
310
|
+
: flavor === "gist"
|
|
311
|
+
? GIST_ADAPTER_PLANNOTATOR_CONTEXT
|
|
312
|
+
: PLAN_ADAPTER_PLANNOTATOR_CONTEXT;
|
|
276
313
|
// 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
|
|
314
|
+
// a stage change still delivers the missing flavor while a prior copy of another flavor
|
|
278
315
|
// sits on the branch. Injected customs persist, so a live copy suppresses re-injection;
|
|
279
316
|
// compaction dropping it makes the scan come up clean and the next turn re-injects.
|
|
280
|
-
const marker =
|
|
281
|
-
|
|
282
|
-
|
|
317
|
+
const marker =
|
|
318
|
+
flavor === "objective"
|
|
319
|
+
? OBJECTIVE_ADAPTER_PLANNOTATOR_MARKER
|
|
320
|
+
: flavor === "gist"
|
|
321
|
+
? GIST_ADAPTER_PLANNOTATOR_MARKER
|
|
322
|
+
: PLAN_ADAPTER_PLANNOTATOR_MARKER;
|
|
283
323
|
if (branchCarries(branch, marker)) return;
|
|
284
324
|
return {
|
|
285
325
|
message: {
|
|
@@ -290,11 +330,12 @@ export function registerPlanAdapterPlannotator(pi: ExtensionAPI): void {
|
|
|
290
330
|
};
|
|
291
331
|
});
|
|
292
332
|
|
|
293
|
-
// Strip the stale bridge markers (
|
|
333
|
+
// Strip the stale bridge markers (ALL THREE flavors) from context when plannotator-plan is no
|
|
294
334
|
// longer selected (same hygiene as the tombell shim), so they never linger across a deselect.
|
|
295
335
|
const hasMarker = (text: string): boolean =>
|
|
296
336
|
text.includes(PLAN_ADAPTER_PLANNOTATOR_MARKER) ||
|
|
297
|
-
text.includes(OBJECTIVE_ADAPTER_PLANNOTATOR_MARKER)
|
|
337
|
+
text.includes(OBJECTIVE_ADAPTER_PLANNOTATOR_MARKER) ||
|
|
338
|
+
text.includes(GIST_ADAPTER_PLANNOTATOR_MARKER);
|
|
298
339
|
pi.on("context", async (event, ctx) => {
|
|
299
340
|
if (isPlannotatorPlanSelected(ctx.cwd)) return;
|
|
300
341
|
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.
|