@mgiles/perk 2.3.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.
Files changed (128) hide show
  1. package/README.md +1 -1
  2. package/extension/adapters/planAdapterPlannotator.ts +132 -91
  3. package/extension/adapters/planAdapterTombell.ts +5 -3
  4. package/extension/doors/address.ts +235 -37
  5. package/extension/doors/annotationPush.ts +924 -0
  6. package/extension/doors/auditWaveTools.ts +352 -0
  7. package/extension/doors/ciExecutor.ts +220 -55
  8. package/extension/doors/draftReviewWaveTools.ts +384 -0
  9. package/extension/doors/harvestWaveTools.ts +279 -0
  10. package/extension/doors/land.ts +1 -0
  11. package/extension/doors/learn.ts +24 -6
  12. package/extension/doors/learnFactory.ts +33 -7
  13. package/extension/doors/lifecycleGates.ts +1 -1
  14. package/extension/doors/objectiveReviewBrowser.ts +514 -0
  15. package/extension/doors/objectiveStack.ts +1143 -0
  16. package/extension/doors/planReviewBrowser.ts +453 -0
  17. package/extension/doors/plannotatorHandoff.ts +120 -49
  18. package/extension/doors/prReview.ts +26 -16
  19. package/extension/doors/prReviewBrowser.ts +31 -23
  20. package/extension/doors/prReviewDynamic.ts +25 -18
  21. package/extension/doors/prReviewTerminal.ts +6 -11
  22. package/extension/doors/reviewWaveTools.ts +374 -0
  23. package/extension/doors/selfcheck.ts +1 -1
  24. package/extension/doors/submit.ts +82 -7
  25. package/extension/factories/gistAuthor.ts +4 -4
  26. package/extension/factories/gistDraft.ts +1 -1
  27. package/extension/factories/gistSave.ts +2 -2
  28. package/extension/factories/objective.ts +7 -7
  29. package/extension/factories/objectiveAuthor.ts +5 -4
  30. package/extension/factories/objectiveDraft.ts +61 -6
  31. package/extension/factories/objectivePlan.ts +163 -9
  32. package/extension/factories/objectiveSave.ts +16 -1
  33. package/extension/factories/planMode.ts +5 -3
  34. package/extension/factories/planReview.ts +131 -36
  35. package/extension/factories/planTitle.ts +27 -9
  36. package/extension/hunkFeedback/inbox.ts +389 -0
  37. package/extension/hunkFeedback/perkFeedback.ts +364 -0
  38. package/extension/hunkFeedback/receiver.ts +296 -0
  39. package/extension/hunkFeedback/store.ts +533 -0
  40. package/extension/index.ts +109 -44
  41. package/extension/substrate/cache.ts +75 -22
  42. package/extension/substrate/coldDoor.ts +2 -3
  43. package/extension/substrate/config.ts +36 -12
  44. package/extension/substrate/git.ts +10 -0
  45. package/extension/substrate/providers.ts +4 -33
  46. package/extension/substrate/resources.ts +11 -0
  47. package/extension/substrate/result.ts +13 -5
  48. package/extension/substrate/sessionData.ts +3 -3
  49. package/extension/substrate/sessionPointers.ts +3 -3
  50. package/extension/substrate/structuredOutput.ts +78 -19
  51. package/extension/substrate/toolGating.ts +170 -23
  52. package/extension/substrate/workflowState.ts +6 -0
  53. package/extension/surfaces/footerProvider.ts +6 -6
  54. package/extension/surfaces/surfaces.ts +98 -282
  55. package/extension/vendor/btw/btw.ts +57 -12
  56. package/extension/waves/adversarialReviewWave.ts +164 -0
  57. package/extension/waves/auditWave.ts +312 -0
  58. package/extension/waves/draftReviewWave.ts +189 -0
  59. package/extension/waves/harvestWave.ts +399 -0
  60. package/extension/waves/memoryAdapter.ts +14 -1
  61. package/extension/waves/objectiveExplorerWave.ts +120 -0
  62. package/extension/waves/prReviewDynamicWave.ts +291 -61
  63. package/extension/waves/prReviewWave.ts +58 -13
  64. package/extension/waves/reportWave.ts +406 -106
  65. package/extension/waves/reviewClassifierWave.ts +109 -0
  66. package/extension/waves/rpcAdapter.ts +65 -2
  67. package/extension/worker/readOnlySession.ts +3 -3
  68. package/extension/worker/worker.ts +71 -93
  69. package/extension/workerMain.ts +4 -6
  70. package/package.json +15 -5
  71. package/prompts/README.md +19 -0
  72. package/prompts/_fixtures/live.yaml +66 -65
  73. package/prompts/common/resume-advisory.md +1 -0
  74. package/prompts/contexts/adapters/plannotator-gist.md +13 -0
  75. package/prompts/contexts/adapters/plannotator-objective.md +1 -2
  76. package/prompts/contexts/adapters/plannotator-plan.md +1 -2
  77. package/prompts/contexts/gist-authoring.md +6 -20
  78. package/prompts/contexts/objective-authoring.md +6 -18
  79. package/prompts/stages/address/action.md +3 -14
  80. package/prompts/stages/address/preview.md +2 -13
  81. package/prompts/stages/audit.md +18 -0
  82. package/prompts/stages/gist-author/seed.md +4 -4
  83. package/prompts/stages/gist-save.md +1 -1
  84. package/prompts/stages/implement.md +3 -1
  85. package/prompts/stages/learn-code.md +1 -1
  86. package/prompts/stages/learn-docs.md +2 -2
  87. package/prompts/stages/learn-harvest.md +15 -0
  88. package/prompts/stages/learn-orchestrate.md +1 -1
  89. package/prompts/stages/objective-author/adopt.md +5 -3
  90. package/prompts/stages/objective-author/file.md +5 -4
  91. package/prompts/stages/objective-author/seed.md +5 -4
  92. package/prompts/stages/objective-land.md +7 -0
  93. package/prompts/stages/objective-plan/guidance.md +2 -13
  94. package/prompts/stages/objective-plan/seed.md +5 -12
  95. package/prompts/stages/objective-recover.md +8 -0
  96. package/prompts/stages/objective-replan.md +4 -2
  97. package/prompts/stages/objective-review-browser.md +9 -0
  98. package/prompts/stages/objective-sync.md +6 -0
  99. package/prompts/stages/plan-from/adopt.md +2 -2
  100. package/prompts/stages/plan-from/file.md +2 -2
  101. package/prompts/stages/plan-review-browser.md +9 -0
  102. package/prompts/stages/pr-review-browser/active.md +6 -14
  103. package/prompts/stages/pr-review-browser/foreign.md +6 -14
  104. package/prompts/stages/pr-review-dynamic.md +3 -3
  105. package/prompts/stages/pr-review-terminal/active.md +7 -15
  106. package/prompts/stages/pr-review-terminal/foreign.md +7 -15
  107. package/prompts/stages/pr-review.md +2 -2
  108. package/prompts/stages/replan.md +3 -3
  109. package/shared/README.md +2 -2
  110. package/shared/bindings.yaml +12 -0
  111. package/shared/contracts-history.md +11 -0
  112. package/shared/contracts.md +3652 -236
  113. package/shared/providers.yaml +27 -60
  114. package/shared/registry.yaml +32 -8
  115. package/shared/schemas/outputs/objective-doctor.schema.json +449 -0
  116. package/shared/schemas/outputs/objective-stack-land.schema.json +732 -0
  117. package/shared/schemas/outputs/objective-stack-recover.schema.json +397 -0
  118. package/shared/schemas/outputs/objective-stack-status.schema.json +577 -0
  119. package/shared/schemas/outputs/objective-stack-sync.schema.json +184 -0
  120. package/shared/schemas/outputs/plan-save.schema.json +12 -0
  121. package/shared/schemas/outputs/pr-submit.schema.json +177 -1
  122. package/extension/adapters/todoAdapterJuicesharp.ts +0 -102
  123. package/extension/checkpoints/checkpoints.ts +0 -550
  124. package/extension/checkpoints/planSteps.ts +0 -108
  125. package/extension/doors/askUser.ts +0 -198
  126. package/prompts/common/output-schemas/objective-explorer.md +0 -36
  127. package/prompts/common/output-schemas/review-classifier.md +0 -47
  128. package/prompts/contexts/adapters/juicesharp-todo.md +0 -7
@@ -53,7 +53,7 @@ import {
53
53
  stringField,
54
54
  } from "../substrate/coldDoor.ts";
55
55
  import { registerPerkCommand } from "../substrate/command.ts";
56
- import { loadPerkConfig } from "../substrate/config.ts";
56
+ import { subagentModel } from "../substrate/config.ts";
57
57
  import { render } from "../substrate/prompts.ts";
58
58
  import { failFor, ok, type Result } from "../substrate/result.ts";
59
59
  import { arrayParam, paramsOf, stringParam } from "../substrate/toolParams.ts";
@@ -65,7 +65,11 @@ import {
65
65
  type LearnAngleSelection,
66
66
  runLearnWave,
67
67
  } from "../waves/learnWave.ts";
68
- import type { WaveAdapter } from "../waves/reportWave.ts";
68
+ import {
69
+ toAttemptReceipt,
70
+ type WaveAdapter,
71
+ type WaveAttemptReceipt,
72
+ } from "../waves/reportWave.ts";
69
73
  import { createRpcWaveAdapter } from "../waves/rpcAdapter.ts";
70
74
  import { planReadInstruction } from "./lifecycleGates.ts";
71
75
 
@@ -301,9 +305,12 @@ export function learnOrchestrateGuidance(opts: {
301
305
  export interface LearnWaveOk {
302
306
  reports: { angle: string; report: unknown }[];
303
307
  skipped: { angle: string; reason: string; detail: string }[];
308
+ /** The single launch's output-free attempt receipt (observability only — details, not prose). */
309
+ attempts: WaveAttemptReceipt[];
304
310
  }
305
311
 
306
- export type LearnWaveResult = Result<LearnWaveOk>;
312
+ /** The fail arm retains any receipt known before the failure (the `failFor` extras hook). */
313
+ export type LearnWaveResult = Result<LearnWaveOk, { attempts: WaveAttemptReceipt[] }>;
307
314
 
308
315
  /**
309
316
  * The `run_learn_wave` execute core, extracted for testability with the adapter as the injected
@@ -326,7 +333,7 @@ export async function executeLearnWave(
326
333
  signal?: AbortSignal;
327
334
  },
328
335
  ): Promise<LearnWaveResult> {
329
- const fail = failFor(target, "run_learn_wave");
336
+ const fail = failFor<{ attempts: WaveAttemptReceipt[] }>(target, "run_learn_wave");
330
337
  const manifestPath = join(opts.bundleDir, "manifest.json");
331
338
  const result = await runLearnWave(
332
339
  adapter,
@@ -338,12 +345,23 @@ export async function executeLearnWave(
338
345
  },
339
346
  opts.signal,
340
347
  );
348
+ // The learn flow has no retry — ONE attempt over the validated selection.
349
+ const attempts = [
350
+ toAttemptReceipt(
351
+ "learn",
352
+ 1,
353
+ opts.selections.map((s) => s.angle),
354
+ result.receipt,
355
+ ),
356
+ ];
341
357
 
342
358
  if (!result.complete) {
343
359
  const waveFailure = result.failures.find((f) => f.key === null);
360
+ // The receipt known before the failure rides the fail details (never the prose).
344
361
  return fail(
345
362
  waveFailure?.detail ?? "the analyst wave failed without detail",
346
363
  waveFailure?.reason ?? "run-failed",
364
+ { attempts },
347
365
  );
348
366
  }
349
367
 
@@ -368,7 +386,7 @@ export async function executeLearnWave(
368
386
  .join("\n")}`,
369
387
  );
370
388
  }
371
- return ok(parts.join("\n\n"), { reports, skipped });
389
+ return ok(parts.join("\n\n"), { reports, skipped, attempts });
372
390
  }
373
391
 
374
392
  const WAVE_TOOL_GUIDELINES = [
@@ -533,7 +551,7 @@ export function registerLearn(pi: ExtensionAPI): void {
533
551
  }
534
552
  // Model resolution lives here (not in the guidance): `[models.subagents] learn-analyst`
535
553
  // rides the wave as the workflow-level `model` default.
536
- const model = loadPerkConfig(ctx.cwd).subagents["learn-analyst"];
554
+ const model = subagentModel(ctx.cwd, "learn-analyst");
537
555
  return executeLearnWave(createRpcWaveAdapter(pi.events), ctx, {
538
556
  bundleDir,
539
557
  selections,
@@ -6,21 +6,27 @@
6
6
  // Each door DELEGATES the gather to the Python plane (`perk learn <kind> --gather --json` via the
7
7
  // shared cold-door client `runColdDoor` — gate-safe, not subject to the read-only bash allowlist),
8
8
  // decodes `{ inbox_path, learn_numbers }`, then injects the factory guidance via
9
- // `pi.sendUserMessage` so the model reads the inbox, authors the plan, and saves it. The save is
10
- // surface-dependent (the seed spells this out): where `plan_save` is active (a read-write session,
11
- // the warm doors' usual host) the model passes `consumed_learn` explicitly load-bearing here,
12
- // because the warm gather is side-effect-free and writes NO handoff carrier; in a gated read-only
13
- // session `plan_save` is hidden (toolGating.ts), so the save lands review-first via `plan_review`
14
- // and the COLD doors' handoff carrier supplies `consumed_learn`. No model tool is registered here.
9
+ // `pi.sendUserMessage` so the model reads the inbox, authors the plan, and saves it. The warm
10
+ // gather is side-effect-free and writes NO handoff carrier, so `plan_save` passing
11
+ // `consumed_learn` explicitly is the ONLY surface that can carry the consumed numbers herethe
12
+ // interactive host guard (contracts.md §8.2) enforces it: an interactive session where the
13
+ // `plan_save` tool is not currently active (`pi.getActiveTools()` the authority, reflecting the
14
+ // read-only gate, worktree stage scoping, AND foreign providers' `setActiveTools` restrictions
15
+ // that write no perk workflow-state) is refused BEFORE the gather, pointing at the cold door
16
+ // (`perk learn docs` / `perk learn code`), whose handoff carrier supplies `consumed_learn` on the
17
+ // review-first save path. Workflow-state only flavors the refusal message, never decides it. No
18
+ // model tool is registered here.
15
19
  //
16
20
  // Headless-safe: rich UI is guarded by `ctx.hasUI`; without a UI it logs to stderr and returns
17
- // (the gather still runs so the inbox is materialized, but no turn is driven).
21
+ // (the gather still runs so the inbox is materialized, but no turn is driven — the save hazard
22
+ // cannot occur, so the guard is interactive-only).
18
23
 
19
24
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
20
25
  import { bindingSuffix } from "../substrate/bindingDelivery.ts";
21
26
  import { type ColdJson, runColdDoor, stringField } from "../substrate/coldDoor.ts";
22
27
  import { registerPerkCommand } from "../substrate/command.ts";
23
28
  import { render } from "../substrate/prompts.ts";
29
+ import { branchOf, rebuildWorkflowState } from "../substrate/workflowState.ts";
24
30
  import { report } from "../surfaces/report.ts";
25
31
 
26
32
  /**
@@ -106,6 +112,26 @@ export function registerLearnFactoryDoor(pi: ExtensionAPI, kind: LearnFactoryDoo
106
112
  registerPerkCommand(pi, kind.name, {
107
113
  description: kind.description,
108
114
  handler: async (_args, ctx: ExtensionContext) => {
115
+ // The interactive host guard (see the header): refuse BEFORE the gather when `plan_save`
116
+ // is not currently active — `pi.getActiveTools()` is the authoritative predicate;
117
+ // workflow-state only flavors the message (it cannot see foreign restrictions).
118
+ if (ctx.hasUI && !pi.getActiveTools().includes("plan_save")) {
119
+ const state = rebuildWorkflowState(branchOf(ctx));
120
+ const why =
121
+ state.mode === "read-only"
122
+ ? "this session is read-only"
123
+ : state.stage !== undefined
124
+ ? `this session is scoped to the ${state.stage} stage`
125
+ : "a provider restriction hides it";
126
+ report(
127
+ ctx,
128
+ kind.name,
129
+ "error",
130
+ `the plan_save tool is not active here (${why}), so this session cannot save a plan ` +
131
+ `carrying consumed_learn — use the cold door instead: perk learn ${kind.subcommand}.`,
132
+ );
133
+ return;
134
+ }
109
135
  // Report-only door (no Result type): branch on `errorType` directly (the coldDoor header
110
136
  // convention). A clean empty inbox exits non-zero with error_type=no_learn_issues — the
111
137
  // client's envelope-aware arm surfaces it gently.
@@ -81,7 +81,7 @@ export function planReadInstruction(provider: string, prId: string, url: string)
81
81
  * The wording lives in the canonical template `prompts/stages/implement.md`, rendered by the shared
82
82
  * seam (contracts.md §8.31); branching stays in code — only the `read_cmd` var differs. This warm
83
83
  * handoff is now byte-identical to the cold/worker primer, so it carries the same "Progress
84
- * markers:" tail (the prior shorter near-copy omission is removed).
84
+ * tracking:" tail (the prior shorter near-copy omission is removed).
85
85
  */
86
86
  export function implementHandoffPrompt(ref: PlanRef): string {
87
87
  const readCmd = planReadInstruction(ref.provider, String(ref.pr_id), ref.url);