@mgiles/perk 3.2.0 → 3.3.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.
Files changed (202) hide show
  1. package/README.md +5 -0
  2. package/extension/authoring/gist/draft.ts +198 -0
  3. package/extension/authoring/gist/prose.ts +46 -0
  4. package/extension/authoring/gist/review.ts +133 -0
  5. package/extension/authoring/gist/save.ts +118 -0
  6. package/extension/authoring/objective/draft.ts +345 -0
  7. package/extension/{factories/objectiveDreamReport.ts → authoring/objective/dreamReportGate.ts} +74 -131
  8. package/extension/authoring/objective/planning.ts +124 -0
  9. package/extension/authoring/objective/prose.ts +103 -0
  10. package/extension/authoring/objective/review.ts +128 -0
  11. package/extension/authoring/objective/save.ts +224 -0
  12. package/extension/authoring/plan/draft.ts +84 -0
  13. package/extension/authoring/plan/prose.ts +41 -0
  14. package/extension/authoring/plan/review.ts +269 -0
  15. package/extension/authoring/plan/save.ts +256 -0
  16. package/extension/authoring/plan/source.ts +82 -0
  17. package/extension/authoring/refinement/context.ts +468 -0
  18. package/extension/authoring/refinement/draft.ts +261 -0
  19. package/extension/authoring/refinement/prose.ts +79 -0
  20. package/extension/authoring/refinement/review.ts +111 -0
  21. package/extension/authoring/refinement/save.ts +119 -0
  22. package/extension/authoring/review/approvalGate.ts +34 -0
  23. package/extension/authoring/review/draftContext.ts +68 -0
  24. package/extension/codeReview/automated.ts +352 -0
  25. package/extension/codeReview/submission.ts +229 -0
  26. package/extension/delivery/address.ts +295 -0
  27. package/extension/delivery/ci.ts +355 -0
  28. package/extension/delivery/commitCompact.ts +93 -0
  29. package/extension/delivery/conflictResolution.ts +247 -0
  30. package/extension/delivery/ready.ts +193 -0
  31. package/extension/delivery/stackConflict.ts +361 -0
  32. package/extension/delivery/stackObjective.ts +16 -0
  33. package/extension/delivery/stackReconcile.ts +165 -0
  34. package/extension/delivery/submit.ts +171 -0
  35. package/extension/index.ts +365 -380
  36. package/extension/learning/analystWave.ts +324 -0
  37. package/extension/learning/audit.ts +667 -0
  38. package/extension/learning/capture.ts +92 -0
  39. package/extension/learning/containment.ts +104 -0
  40. package/extension/{waves/dreamWave.ts → learning/dream.ts} +112 -94
  41. package/extension/learning/dreamAnalysis.ts +435 -0
  42. package/extension/{waves/dreamReducerWave.ts → learning/dreamReducer.ts} +46 -41
  43. package/extension/{waves → learning}/dreamReport.ts +35 -31
  44. package/extension/learning/harvest.ts +491 -0
  45. package/extension/learning/prose.ts +66 -0
  46. package/extension/learning/routing.ts +79 -0
  47. package/extension/pi/v1/bashScanTimeout.ts +64 -0
  48. package/extension/{doors/prReview.ts → pi/v1/codeReview/automated.ts} +215 -311
  49. package/extension/{doors/prReviewBrowser.ts → pi/v1/codeReview/browser.ts} +53 -33
  50. package/extension/{doors/hunkHandoff.ts → pi/v1/codeReview/checkout.ts} +12 -8
  51. package/extension/{doors/reviewWaveTools.ts → pi/v1/codeReview/reviewWave.ts} +146 -114
  52. package/extension/{doors/stackReviewBrowser.ts → pi/v1/codeReview/stack.ts} +62 -29
  53. package/extension/pi/v1/codeReview/submit.ts +354 -0
  54. package/extension/{doors/prReviewTerminal.ts → pi/v1/codeReview/terminal.ts} +32 -27
  55. package/extension/pi/v1/contextEvidence.ts +80 -0
  56. package/extension/pi/v1/contextInjection.ts +207 -0
  57. package/extension/{doors → pi/v1/delivery}/address.ts +154 -267
  58. package/extension/pi/v1/delivery/ci.ts +570 -0
  59. package/extension/pi/v1/delivery/commitCompact.ts +201 -0
  60. package/extension/pi/v1/delivery/conflictResolverEngine.ts +425 -0
  61. package/extension/{doors → pi/v1/delivery}/land.ts +123 -61
  62. package/extension/pi/v1/delivery/ready.ts +322 -0
  63. package/extension/pi/v1/delivery/stackConflictResolver.ts +172 -0
  64. package/extension/pi/v1/delivery/stackDrive.ts +120 -0
  65. package/extension/pi/v1/delivery/stackLand.ts +223 -0
  66. package/extension/pi/v1/delivery/stackRecover.ts +265 -0
  67. package/extension/pi/v1/delivery/stackStatus.ts +237 -0
  68. package/extension/pi/v1/delivery/stackSync.ts +658 -0
  69. package/extension/pi/v1/delivery/submit.ts +389 -0
  70. package/extension/pi/v1/delivery/submitConflict.ts +186 -0
  71. package/extension/pi/v1/draftReview.ts +431 -0
  72. package/extension/{doors → pi/v1}/draftReviewWaveTools.ts +141 -151
  73. package/extension/pi/v1/gist.ts +794 -0
  74. package/extension/pi/v1/learning/audit.ts +186 -0
  75. package/extension/pi/v1/learning/dream.ts +207 -0
  76. package/extension/{doors/learnFactory.ts → pi/v1/learning/factory.ts} +18 -65
  77. package/extension/{doors/harvestWaveTools.ts → pi/v1/learning/harvest.ts} +46 -100
  78. package/extension/pi/v1/learning/learn.ts +585 -0
  79. package/extension/pi/v1/lifecycleGates.ts +127 -0
  80. package/extension/{factories → pi/v1}/objective.ts +53 -33
  81. package/extension/pi/v1/objectiveAuthoring.ts +672 -0
  82. package/extension/pi/v1/objectiveDreamGate.ts +160 -0
  83. package/extension/{factories/objectivePlan.ts → pi/v1/objectivePlanning.ts} +328 -533
  84. package/extension/pi/v1/objectiveRefinement.ts +1320 -0
  85. package/extension/pi/v1/objectiveReview.ts +451 -0
  86. package/extension/{doors → pi/v1}/objectiveReviewBrowser.ts +259 -172
  87. package/extension/pi/v1/plan.ts +812 -0
  88. package/extension/pi/v1/planReview.ts +820 -0
  89. package/extension/{doors → pi/v1}/planReviewBrowser.ts +228 -152
  90. package/extension/{doors/annotationPush.ts → pi/v1/providers/annotations.ts} +158 -89
  91. package/extension/pi/v1/providers/plannotator.ts +487 -0
  92. package/extension/{doors → pi/v1/providers}/plannotatorHandoff.ts +73 -27
  93. package/extension/pi/v1/providers/selection.ts +43 -0
  94. package/extension/{adapters/planAdapterTombell.ts → pi/v1/providers/tombell.ts} +43 -72
  95. package/extension/pi/v1/review.ts +538 -0
  96. package/extension/pi/v1/reviewOutcome.ts +9 -0
  97. package/extension/pi/v1/scoutWave.ts +318 -0
  98. package/extension/{doors → pi/v1}/selfcheck.ts +4 -4
  99. package/extension/session/branchWorkflowSession.ts +60 -0
  100. package/extension/session/lifecycle.ts +644 -0
  101. package/extension/session/lifecycleGates.ts +64 -0
  102. package/extension/session/saveDestination.ts +87 -0
  103. package/extension/session/workflowSession.ts +971 -0
  104. package/extension/substrate/agentScratch.ts +27 -54
  105. package/extension/substrate/bashScanTimeout.ts +181 -0
  106. package/extension/substrate/bindingDelivery.ts +38 -30
  107. package/extension/substrate/bindings.ts +4 -5
  108. package/extension/substrate/cache.ts +64 -12
  109. package/extension/substrate/childRestrictions.ts +39 -0
  110. package/extension/substrate/coldDoor.ts +17 -1
  111. package/extension/substrate/config.ts +157 -21
  112. package/extension/substrate/git.ts +88 -6
  113. package/extension/substrate/modelVisible.ts +53 -0
  114. package/extension/substrate/prompts.ts +22 -0
  115. package/extension/substrate/registry.ts +2 -0
  116. package/extension/substrate/resolverLease.ts +5 -4
  117. package/extension/substrate/sessionData.ts +85 -152
  118. package/extension/substrate/toolGating.ts +263 -84
  119. package/extension/substrate/unifiedDiff.ts +1 -1
  120. package/extension/substrate/workflowState.ts +178 -163
  121. package/extension/substrate/worktreeResolverLock.ts +261 -0
  122. package/extension/surfaces/surfaces.ts +79 -27
  123. package/extension/waves/adversarialReviewWave.ts +87 -46
  124. package/extension/waves/blockedReports.ts +59 -0
  125. package/extension/waves/draftReviewWave.ts +42 -42
  126. package/extension/waves/laneIdentity.ts +77 -0
  127. package/extension/waves/objectiveExplorerWave.ts +24 -24
  128. package/extension/waves/prReviewWave.ts +89 -77
  129. package/extension/waves/reportWave.ts +438 -578
  130. package/extension/waves/reviewClassifierWave.ts +22 -22
  131. package/extension/waves/rpcAdapter.ts +100 -15
  132. package/extension/waves/scoutWave.ts +192 -0
  133. package/extension/waves/transport.ts +480 -0
  134. package/extension/worker/sdkAdapter.ts +494 -0
  135. package/extension/worker/stageExecution.ts +679 -0
  136. package/extension/workerMain.ts +18 -19
  137. package/package.json +6 -4
  138. package/prompts/_fixtures/live.yaml +43 -18
  139. package/prompts/contexts/adapters/plannotator-gist.md +6 -0
  140. package/prompts/contexts/adapters/plannotator-objective.md +6 -0
  141. package/prompts/contexts/adapters/plannotator-plan.md +8 -1
  142. package/prompts/contexts/adapters/plannotator-refinement.md +22 -0
  143. package/prompts/contexts/objective-refinement.md +17 -0
  144. package/prompts/contexts/read-only.md +1 -1
  145. package/prompts/stages/conflict-resolution-continuation.md +9 -6
  146. package/prompts/stages/conflict-resolution.md +4 -4
  147. package/prompts/stages/objective-plan/guidance.md +2 -2
  148. package/prompts/stages/objective-plan/seed.md +9 -1
  149. package/prompts/stages/objective-reconcile-ready.md +1 -1
  150. package/prompts/stages/objective-reconcile.md +1 -1
  151. package/prompts/stages/objective-refine/seed.md +18 -0
  152. package/prompts/stages/objective-review-browser.md +4 -4
  153. package/prompts/stages/objective-sync.md +1 -1
  154. package/prompts/stages/plan-review-browser.md +4 -4
  155. package/prompts/stages/pr-review-browser/active.md +3 -4
  156. package/prompts/stages/pr-review-browser/foreign.md +3 -4
  157. package/prompts/stages/pr-review-terminal/active.md +3 -3
  158. package/prompts/stages/pr-review-terminal/foreign.md +3 -3
  159. package/prompts/stages/pr-review.md +3 -3
  160. package/prompts/stages/stack-review-browser/stack.md +5 -6
  161. package/shared/README.md +8 -0
  162. package/shared/bindings.yaml +3 -3
  163. package/shared/contracts.md +2601 -506
  164. package/shared/fixtures/issues-table.json +130 -0
  165. package/shared/registry.yaml +13 -0
  166. package/shared/schemas/outputs/objective-node-engagement.schema.json +318 -0
  167. package/shared/schemas/outputs/objective-stack-status.schema.json +6 -1
  168. package/shared/schemas/outputs/pr-review-context.schema.json +54 -9
  169. package/shared/schemas/outputs/pr-review-stack-context.schema.json +196 -0
  170. package/extension/adapters/planAdapterPlannotator.ts +0 -362
  171. package/extension/doors/auditWaveTools.ts +0 -352
  172. package/extension/doors/ciExecutor.ts +0 -756
  173. package/extension/doors/commitCompact.ts +0 -251
  174. package/extension/doors/dreamWaveTools.ts +0 -489
  175. package/extension/doors/learn.ts +0 -668
  176. package/extension/doors/lifecycleGates.ts +0 -207
  177. package/extension/doors/objectiveStack.ts +0 -1543
  178. package/extension/doors/prReviewDynamic.ts +0 -276
  179. package/extension/doors/ready.ts +0 -279
  180. package/extension/doors/submit.ts +0 -373
  181. package/extension/doors/submitPrReview.ts +0 -505
  182. package/extension/factories/gistAuthor.ts +0 -94
  183. package/extension/factories/gistDraft.ts +0 -265
  184. package/extension/factories/gistSave.ts +0 -251
  185. package/extension/factories/implementHere.ts +0 -116
  186. package/extension/factories/objectiveAuthor.ts +0 -98
  187. package/extension/factories/objectiveDraft.ts +0 -466
  188. package/extension/factories/objectiveSave.ts +0 -366
  189. package/extension/factories/planDraft.ts +0 -140
  190. package/extension/factories/planMode.ts +0 -205
  191. package/extension/factories/planReview.ts +0 -1237
  192. package/extension/factories/planSave.ts +0 -604
  193. package/extension/factories/planTitle.ts +0 -141
  194. package/extension/substrate/structuredOutput.ts +0 -202
  195. package/extension/waves/auditWave.ts +0 -312
  196. package/extension/waves/harvestWave.ts +0 -399
  197. package/extension/waves/learnWave.ts +0 -155
  198. package/extension/waves/memoryAdapter.ts +0 -139
  199. package/extension/waves/prReviewDynamicWave.ts +0 -777
  200. package/extension/worker/readOnlySession.ts +0 -294
  201. package/extension/worker/worker.ts +0 -899
  202. package/prompts/stages/pr-review-dynamic.md +0 -7
@@ -1,207 +0,0 @@
1
- // Session-lifecycle gates (the interior safety primitive). One reusable dirty-repo guard on
2
- // session_before_switch / session_before_fork that returns { cancel: true } when an *active perk
3
- // workflow* has uncommitted changes — so a stage transition never silently orphans work. Plus the
4
- // guard-only `/implement` command that *enforces* the implement stage's `warm: false` legality (the
5
- // plan→implement jump requires fresh context; that is the cold door `perk implement`).
6
- //
7
- // pi.on("session_before_fork"/"...switch") handlers are fired by extensionRunner.emit({type,...})
8
- // and their { cancel } result round-trips; the handler's
9
- // pi.exec("git",["status","--porcelain"],{cwd}) resolves the session cwd.
10
-
11
- import type {
12
- ExtensionAPI,
13
- ExtensionCommandContext,
14
- ExtensionContext,
15
- } from "@earendil-works/pi-coding-agent";
16
- import type { PlanRef } from "../substrate/cache.ts";
17
- import { registerPerkCommand } from "../substrate/command.ts";
18
- import { render } from "../substrate/prompts.ts";
19
- import { type BranchSource, branchOf, rebuildWorkflowState } from "../substrate/workflowState.ts";
20
- import { report } from "../surfaces/report.ts";
21
-
22
- // The planning stages whose sessions never legitimately run lifecycle doors. After an approved
23
- // save, a still-live planning session holds TWO plan identities — the cwd binding (a positioned
24
- // stacked session's predecessor checkout, read via readPlanRef(ctx.cwd)) and the just-saved plan
25
- // on active_plan_ref — so a door invocation there could act on the predecessor. At the repo root
26
- // the same invocation fails confusingly today; the refusal is honest in both shapes.
27
- const PLANNING_STAGES = new Set(["plan", "objective-plan"]);
28
-
29
- /**
30
- * The planning-stage lifecycle-door refusal (the shared first check of the warm /submit,
31
- * /address, /land, and /learn doors): when this session's workflow-state `stage` is a planning
32
- * stage, return the refusal message directing the human at the fresh-session implement door;
33
- * `null` otherwise (non-planning stages — and stage-less sessions — are unaffected). Fail-CLOSED
34
- * on an unreadable branch: without the state this guard cannot prove the session is not a
35
- * positioned planning session (whose cwd binding is the PREDECESSOR — the exact target it
36
- * protects), so an unreadable read refuses rather than letting the door act.
37
- */
38
- export function planningStageRefusal(ctx: BranchSource, door: string): string | null {
39
- let stage: string | undefined;
40
- try {
41
- stage = rebuildWorkflowState(branchOf(ctx)).stage;
42
- } catch (error) {
43
- return (
44
- `${door} is unavailable: the session's workflow state could not be read ` +
45
- `(${String(error)}), so this cannot be proven not to be a planning session — ` +
46
- "retry, or implement the saved plan with `perk impl <N>` in a fresh session."
47
- );
48
- }
49
- if (stage === undefined || !PLANNING_STAGES.has(stage)) return null;
50
- return (
51
- `${door} is unavailable in a planning session (stage ${stage}): a planning session can ` +
52
- "hold two plan identities (its checkout's own binding and the just-saved plan) — " +
53
- "implement the saved plan with `perk impl <N>` in a fresh session instead."
54
- );
55
- }
56
-
57
- const DIRTY_MESSAGE = "uncommitted changes — commit or stash before switching/forking this stage.";
58
- const HANDOFF_DIRTY_MESSAGE =
59
- "uncommitted changes — commit before a fresh-context /implement handoff (the plan is the " +
60
- "only artifact that crosses the boundary).";
61
-
62
- /**
63
- * Pure gate policy: cancel a transition only inside an active perk workflow whose tree is
64
- * dirty. Kept separate from the `pi.exec` effect so the matrix is unit-testable offline.
65
- */
66
- export function gateDecision(inputs: { active: boolean; dirty: boolean }): { cancel: boolean } {
67
- return { cancel: inputs.active && inputs.dirty };
68
- }
69
-
70
- /** True when this session is linked to a plan (a perk workflow is in progress). */
71
- function workflowActive(ctx: ExtensionContext): boolean {
72
- return rebuildWorkflowState(branchOf(ctx)).active_plan_ref != null;
73
- }
74
-
75
- /**
76
- * The shared dirty-repo guard for switch/fork. Allows (returns undefined) outside a workflow or on
77
- * a clean tree; cancels with a loud, fail-safe-headless message on a dirty tree in a workflow. If
78
- * `git status` itself fails (e.g. not a repo) we allow — this is a hygiene guard, not a validator.
79
- */
80
- async function guardTransition(
81
- pi: ExtensionAPI,
82
- ctx: ExtensionContext,
83
- ): Promise<{ cancel: true } | undefined> {
84
- const active = workflowActive(ctx);
85
- if (!active) return undefined; // perk does not interfere with non-perk transitions
86
- const res = await pi.exec("git", ["status", "--porcelain"], { cwd: ctx.cwd });
87
- const dirty = res.code === 0 && res.stdout.trim().length > 0;
88
- if (!gateDecision({ active, dirty }).cancel) return undefined;
89
- report(ctx, "lifecycle", "warning", DIRTY_MESSAGE); // fail-safe-headless: loud, still cancels
90
- return { cancel: true };
91
- }
92
-
93
- /**
94
- * The per-backend plan-read instruction — the prompt SSOT for "how do I read the saved
95
- * plan". Byte-identical to `perk/run/launch.py::_plan_read_instruction` (the Python twin); drift in
96
- * either plane fails the paired parity suites. `github` reads via `gh`; `linear` points at the
97
- * pi-mono-linear tools with an `open <url>` fallback; any other provider falls back to opening
98
- * the url.
99
- *
100
- * The wording now lives in the canonical templates `prompts/common/plan-read/*.md`, rendered
101
- * identically by both planes via the shared render seam (contracts.md §8.31); branching stays in
102
- * code — only the arm chosen and the vars passed differ. Golden-fixture parity (the three
103
- * `plan-read-*` cases) plus a thin per-arm selection test replace the dedicated substring parity.
104
- */
105
- export function planReadInstruction(provider: string, prId: string, url: string): string {
106
- if (provider === "github") return render("common/plan-read/github.md", { pr_id: prId, url });
107
- if (provider === "linear") return render("common/plan-read/linear.md", { pr_id: prId, url });
108
- return render("common/plan-read/other.md", { pr_id: prId, url });
109
- }
110
-
111
- /**
112
- * The plan-read priming seed for a fresh implement session. The in-session twin of
113
- * `perk/run/launch.py`'s `_implement_prompt`: carry the plan FORWARD (read it from its canonical
114
- * source), never summarize it — the plan is the only artifact that crosses the boundary.
115
- *
116
- * The wording lives in the canonical template `prompts/stages/implement.md`, rendered by the shared
117
- * seam (contracts.md §8.31); branching stays in code — only the `read_cmd` var differs. This warm
118
- * handoff is now byte-identical to the cold/worker primer, so it carries the same "Progress
119
- * tracking:" tail (the prior shorter near-copy omission is removed).
120
- */
121
- export function implementHandoffPrompt(ref: PlanRef): string {
122
- const readCmd = planReadInstruction(ref.provider, String(ref.pr_id), ref.url);
123
- return render("stages/implement.md", {
124
- provider: ref.provider,
125
- pr_id: String(ref.pr_id),
126
- url: ref.url,
127
- read_cmd: readCmd,
128
- });
129
- }
130
-
131
- /**
132
- * The warm `/implement`. It still NEVER performs the cross-worktree plan→implement
133
- * transition (that is structurally the Python cold door's job — no extension session API can change
134
- * cwd). Outside an impl context it refuses and points at `perk implement`. INSIDE an active
135
- * impl worktree (read-write + a linked plan-ref) it offers the in-process twin of the cold door: a
136
- * lossless `ctx.newSession` fresh-context handoff seeded with the plan-read priming (the plan, and
137
- * the worktree's materialized plan-ref, are the durable state — model-visible output stays capped).
138
- * Dirty-tree hygiene is gated manually here (belt-and-suspenders: `newSession` is a session-replace,
139
- * not a fork/switch, so it may bypass the `session_before_*` gate — we refuse on a dirty tree
140
- * either way), fail-safe-headless.
141
- */
142
- function registerImplementGuard(pi: ExtensionAPI): void {
143
- registerPerkCommand(pi, "implement", {
144
- description:
145
- "Refresh implement context (in-worktree handoff); cross-worktree is `perk implement`.",
146
- handler: async (_args, ctx) => {
147
- const state = rebuildWorkflowState(branchOf(ctx));
148
- const ref = state.active_plan_ref;
149
- const inImpl = state.mode === "read-write" && ref != null;
150
- if (!inImpl) {
151
- report(
152
- ctx,
153
- "implement",
154
- "warning",
155
- "/implement is cold-only here — run `perk implement` from a shell for fresh context.",
156
- );
157
- return;
158
- }
159
-
160
- // Dirty-tree gate (manual; see the doc comment). Refuse the handoff with uncommitted work.
161
- const status = await pi.exec("git", ["status", "--porcelain"], { cwd: ctx.cwd });
162
- if (status.code === 0 && status.stdout.trim().length > 0) {
163
- report(ctx, "implement", "warning", HANDOFF_DIRTY_MESSAGE);
164
- return;
165
- }
166
-
167
- const commandCtx = ctx as ExtensionCommandContext;
168
- if (typeof commandCtx.newSession !== "function") {
169
- report(
170
- ctx,
171
- "implement",
172
- "warning",
173
- "a fresh-context /implement handoff needs an interactive session.",
174
- );
175
- return;
176
- }
177
-
178
- const prompt = implementHandoffPrompt(ref as PlanRef);
179
- const parentSession = ctx.sessionManager.getSessionFile() ?? undefined;
180
- const result = await commandCtx.newSession({
181
- parentSession,
182
- withSession: async (fresh) => {
183
- await fresh.sendUserMessage(prompt);
184
- },
185
- });
186
-
187
- // Verify + cap model-visible output (the full state lives in the worktree + the plan issue).
188
- if (result.cancelled) {
189
- report(ctx, "implement", "info", "/implement handoff cancelled — staying in this session.");
190
- return;
191
- }
192
- report(
193
- ctx,
194
- "implement",
195
- "info",
196
- `fresh implement session started for plan #${(ref as PlanRef).pr_id} — the plan is carried forward, not summarized.`,
197
- );
198
- },
199
- });
200
- }
201
-
202
- /** Register the dirty-repo lifecycle gate (switch + fork) and the guard-only `/implement`. */
203
- export function registerLifecycleGates(pi: ExtensionAPI): void {
204
- pi.on("session_before_fork", async (_event, ctx) => guardTransition(pi, ctx));
205
- pi.on("session_before_switch", async (_event, ctx) => guardTransition(pi, ctx));
206
- registerImplementGuard(pi);
207
- }