@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.
Files changed (145) hide show
  1. package/README.md +1 -1
  2. package/extension/adapters/planAdapterPlannotator.ts +135 -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/commitCompact.ts +98 -10
  9. package/extension/doors/draftReviewWaveTools.ts +412 -0
  10. package/extension/doors/dreamWaveTools.ts +475 -0
  11. package/extension/doors/harvestWaveTools.ts +279 -0
  12. package/extension/doors/land.ts +1 -0
  13. package/extension/doors/learn.ts +24 -6
  14. package/extension/doors/learnFactory.ts +33 -7
  15. package/extension/doors/lifecycleGates.ts +1 -1
  16. package/extension/doors/objectiveReviewBrowser.ts +537 -0
  17. package/extension/doors/objectiveStack.ts +1143 -0
  18. package/extension/doors/planReviewBrowser.ts +475 -0
  19. package/extension/doors/plannotatorHandoff.ts +120 -49
  20. package/extension/doors/prReview.ts +180 -63
  21. package/extension/doors/prReviewBrowser.ts +31 -23
  22. package/extension/doors/prReviewDynamic.ts +57 -30
  23. package/extension/doors/prReviewTerminal.ts +6 -11
  24. package/extension/doors/reviewWaveTools.ts +397 -0
  25. package/extension/doors/selfcheck.ts +1 -1
  26. package/extension/doors/submit.ts +82 -7
  27. package/extension/factories/gistAuthor.ts +4 -4
  28. package/extension/factories/gistDraft.ts +1 -1
  29. package/extension/factories/gistSave.ts +2 -2
  30. package/extension/factories/objective.ts +7 -7
  31. package/extension/factories/objectiveAuthor.ts +5 -4
  32. package/extension/factories/objectiveDraft.ts +153 -30
  33. package/extension/factories/objectiveDreamReport.ts +347 -0
  34. package/extension/factories/objectivePlan.ts +163 -9
  35. package/extension/factories/objectiveSave.ts +90 -2
  36. package/extension/factories/planMode.ts +5 -3
  37. package/extension/factories/planReview.ts +304 -46
  38. package/extension/factories/planTitle.ts +27 -9
  39. package/extension/hunkFeedback/inbox.ts +389 -0
  40. package/extension/hunkFeedback/perkFeedback.ts +364 -0
  41. package/extension/hunkFeedback/receiver.ts +296 -0
  42. package/extension/hunkFeedback/store.ts +533 -0
  43. package/extension/index.ts +169 -57
  44. package/extension/substrate/agentScratch.ts +171 -0
  45. package/extension/substrate/bindingDelivery.ts +9 -11
  46. package/extension/substrate/cache.ts +166 -23
  47. package/extension/substrate/coldDoor.ts +2 -3
  48. package/extension/substrate/command.ts +9 -6
  49. package/extension/substrate/config.ts +41 -12
  50. package/extension/substrate/git.ts +95 -2
  51. package/extension/substrate/providers.ts +4 -33
  52. package/extension/substrate/resources.ts +11 -0
  53. package/extension/substrate/result.ts +16 -7
  54. package/extension/substrate/sessionData.ts +8 -6
  55. package/extension/substrate/sessionPointers.ts +4 -5
  56. package/extension/substrate/structuredOutput.ts +78 -19
  57. package/extension/substrate/toolGating.ts +179 -23
  58. package/extension/substrate/workflowState.ts +50 -2
  59. package/extension/surfaces/footerProvider.ts +6 -6
  60. package/extension/surfaces/report.ts +38 -12
  61. package/extension/surfaces/surfaces.ts +213 -275
  62. package/extension/vendor/btw/btw.ts +94 -17
  63. package/extension/waves/adversarialReviewWave.ts +181 -0
  64. package/extension/waves/auditWave.ts +312 -0
  65. package/extension/waves/draftReviewWave.ts +205 -0
  66. package/extension/waves/dreamReducerWave.ts +700 -0
  67. package/extension/waves/dreamReport.ts +1494 -0
  68. package/extension/waves/dreamWave.ts +927 -0
  69. package/extension/waves/harvestWave.ts +399 -0
  70. package/extension/waves/memoryAdapter.ts +14 -1
  71. package/extension/waves/objectiveExplorerWave.ts +120 -0
  72. package/extension/waves/ponytail.ts +104 -0
  73. package/extension/waves/prReviewDynamicWave.ts +383 -72
  74. package/extension/waves/prReviewWave.ts +175 -25
  75. package/extension/waves/reportWave.ts +503 -107
  76. package/extension/waves/reviewClassifierWave.ts +109 -0
  77. package/extension/waves/rpcAdapter.ts +65 -2
  78. package/extension/worker/readOnlySession.ts +4 -5
  79. package/extension/worker/worker.ts +71 -93
  80. package/extension/workerMain.ts +4 -6
  81. package/package.json +18 -5
  82. package/prompts/README.md +19 -0
  83. package/prompts/_fixtures/live.yaml +115 -65
  84. package/prompts/commit-and-compact-continuation.md +13 -0
  85. package/prompts/common/resume-advisory.md +1 -0
  86. package/prompts/contexts/adapters/plannotator-gist.md +13 -0
  87. package/prompts/contexts/adapters/plannotator-objective.md +8 -3
  88. package/prompts/contexts/adapters/plannotator-plan.md +8 -3
  89. package/prompts/contexts/gist-authoring.md +6 -20
  90. package/prompts/contexts/objective-authoring.md +6 -18
  91. package/prompts/stages/address/action.md +3 -14
  92. package/prompts/stages/address/preview.md +2 -13
  93. package/prompts/stages/audit.md +18 -0
  94. package/prompts/stages/conflict-resolution.md +1 -1
  95. package/prompts/stages/gist-author/seed.md +4 -4
  96. package/prompts/stages/gist-save.md +1 -1
  97. package/prompts/stages/implement.md +3 -1
  98. package/prompts/stages/learn-code.md +1 -1
  99. package/prompts/stages/learn-docs.md +2 -2
  100. package/prompts/stages/learn-dream.md +10 -0
  101. package/prompts/stages/learn-harvest.md +15 -0
  102. package/prompts/stages/learn-orchestrate.md +1 -1
  103. package/prompts/stages/objective-author/adopt.md +5 -3
  104. package/prompts/stages/objective-author/file.md +5 -4
  105. package/prompts/stages/objective-author/seed.md +5 -4
  106. package/prompts/stages/objective-land.md +7 -0
  107. package/prompts/stages/objective-plan/guidance.md +2 -13
  108. package/prompts/stages/objective-plan/seed.md +5 -12
  109. package/prompts/stages/objective-recover.md +8 -0
  110. package/prompts/stages/objective-replan.md +4 -2
  111. package/prompts/stages/objective-review-browser.md +9 -0
  112. package/prompts/stages/objective-sync.md +6 -0
  113. package/prompts/stages/plan-from/adopt.md +2 -2
  114. package/prompts/stages/plan-from/file.md +2 -2
  115. package/prompts/stages/plan-review-browser.md +9 -0
  116. package/prompts/stages/pr-review-browser/active.md +6 -14
  117. package/prompts/stages/pr-review-browser/foreign.md +6 -14
  118. package/prompts/stages/pr-review-dynamic.md +6 -6
  119. package/prompts/stages/pr-review-terminal/active.md +7 -15
  120. package/prompts/stages/pr-review-terminal/foreign.md +7 -15
  121. package/prompts/stages/pr-review-terminal/local.md +1 -1
  122. package/prompts/stages/pr-review.md +5 -5
  123. package/prompts/stages/replan.md +3 -3
  124. package/shared/README.md +2 -2
  125. package/shared/bindings.yaml +15 -0
  126. package/shared/contracts-history.md +11 -0
  127. package/shared/contracts.md +5373 -281
  128. package/shared/providers.yaml +27 -60
  129. package/shared/registry.yaml +42 -18
  130. package/shared/schemas/inputs/review-post-batch.schema.json +14 -1
  131. package/shared/schemas/outputs/objective-doctor.schema.json +487 -0
  132. package/shared/schemas/outputs/objective-stack-land.schema.json +732 -0
  133. package/shared/schemas/outputs/objective-stack-recover.schema.json +397 -0
  134. package/shared/schemas/outputs/objective-stack-status.schema.json +577 -0
  135. package/shared/schemas/outputs/objective-stack-sync.schema.json +184 -0
  136. package/shared/schemas/outputs/plan-save.schema.json +12 -0
  137. package/shared/schemas/outputs/pr-land.schema.json +3 -3
  138. package/shared/schemas/outputs/pr-submit.schema.json +177 -1
  139. package/extension/adapters/todoAdapterJuicesharp.ts +0 -102
  140. package/extension/checkpoints/checkpoints.ts +0 -550
  141. package/extension/checkpoints/planSteps.ts +0 -108
  142. package/extension/doors/askUser.ts +0 -198
  143. package/prompts/common/output-schemas/objective-explorer.md +0 -36
  144. package/prompts/common/output-schemas/review-classifier.md +0 -47
  145. package/prompts/contexts/adapters/juicesharp-todo.md +0 -7
@@ -1,6 +1,8 @@
1
- // The plannotator browser-review substrate serving `/pr-review-browser`: the presence probe,
2
- // the pinned `code-review` event envelope + annotation decode, the active-PR resolution ladder,
3
- // the respond routing, and the composable browser-open core (port preset + readiness poll).
1
+ // The plannotator browser-review substrate serving `/pr-review-browser` and
2
+ // `/plan-review-browser`'s plan-review open: the presence probe, the pinned `code-review` event envelope + annotation
3
+ // decode, the active-PR resolution ladder, the respond routing, and the composable browser-open
4
+ // core (port preset + readiness poll) in BOTH flavors — code review
5
+ // (`startPlannotatorBrowser`) and plan review (`startPlannotatorPlanReview`).
4
6
  //
5
7
  // pi exposes NO API for one extension to invoke another's slash command (`sendUserMessage` sends
6
8
  // text to the model; `steer`/`followUp` ERROR on slash commands). So perk cannot literally call
@@ -30,23 +32,36 @@
30
32
  // back to the reviewer's configured default diff — graceful degradation, no version detection.
31
33
  // The requested diffType only sets the INITIAL view (the reviewer can switch from the header menu).
32
34
  //
33
- // SERVER ADDRESSING (why `startPlannotatorBrowser`'s env preset works): the pi extension runs
34
- // plannotator's review server IN-PROCESS (`node:http`, not the standalone Bun binary), and its
35
- // port resolution (`server/network.ts getServerPort()`) reads `PLANNOTATOR_PORT` at bind time
36
- // perk's extension and plannotator's server share one Node process, so an env var set here is
37
- // read there. The core picks a free ephemeral port, presets the env var, emits the bridge
38
- // request, polls `GET /api/diff` (a review-server-only route) for readiness, and ALWAYS restores
39
- // the prior env value in a `finally` when the poll ends. Because the port is read at bind time,
40
- // the server URL is KNOWN the moment the port is picked — before the server is up — which is
41
- // what lets `/pr-review-browser` open the browser in the background and inject its guidance
42
- // immediately. Concurrency caveat: a second plannotator server starting in the same process
43
- // during the window would collide on the fixed port rare and loud (EADDRINUSE plannotator
44
- // throws the bridge settles error), never silent.
35
+ // SERVER ADDRESSING (why the browser-open core's env preset works): the pi extension runs
36
+ // plannotator's servers IN-PROCESS (`node:http`, not the standalone Bun binary), and their
37
+ // shared port resolution (`server/network.ts getServerPort()`, used by both entry points
38
+ // `startPlanReviewServer` in `server/serverPlan.ts` and the code-review server in
39
+ // `server/serverReview.ts`) reads `PLANNOTATOR_PORT` at bind time perk's extension and
40
+ // plannotator's server share one Node process, so an env var set here is read there. The core
41
+ // picks a free ephemeral port, presets the env var, emits the bridge request, polls a
42
+ // server-flavor-unique readiness route (`GET /api/diff` for code review, `GET /api/plan` for
43
+ // plan review each present only in its own server flavor, so a probe can never false-positive
44
+ // against the wrong one), and ALWAYS restores the prior env value in a `finally` when the poll
45
+ // ends. Because the port is read at bind time, the server URL is KNOWN the moment the port is
46
+ // picked before the server is up which is what lets a door open the browser in the
47
+ // background and inject its guidance immediately. Lifecycle difference between the flavors: the
48
+ // plan server is already BOUND when the handshake respond arrives (code-review responds only
49
+ // ONCE, at the end), but the readiness poll still earns its keep for plan review — it confirms
50
+ // the server answers, bounds the env-restore window uniformly, and an early handshake failure
51
+ // settles the bridge → the poll stops early (`bridge_settled`), exactly like code review.
52
+ // Concurrency caveat: a second plannotator server starting in the same process during the
53
+ // window would collide on the fixed port — rare and loud (EADDRINUSE → plannotator throws →
54
+ // the bridge settles error), never silent.
45
55
 
46
56
  import { randomUUID } from "node:crypto";
47
57
  import { createServer } from "node:net";
48
58
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
49
- import type { PlannotatorBus } from "../adapters/planAdapterPlannotator.ts";
59
+ import {
60
+ type PlannotatorBus,
61
+ requestPlannotatorPlanReview,
62
+ } from "../adapters/planAdapterPlannotator.ts";
63
+ // Type-only (erased at runtime — no cycle): the outcome vocabulary lives with the review door.
64
+ import type { ReviewOutcome } from "../factories/planReview.ts";
50
65
  import { readPlanRef } from "../substrate/cache.ts";
51
66
  import {
52
67
  type ColdDoorResult,
@@ -388,7 +403,7 @@ export function routeBrowserRespond(
388
403
 
389
404
  // ------------------------------------------------------------------------ the browser-open core
390
405
 
391
- /** The readiness-probe cadence: one `GET /api/diff` per second. */
406
+ /** The readiness-probe cadence: one probe per second. */
392
407
  export const READINESS_PROBE_INTERVAL_MS = 1_000;
393
408
 
394
409
  /**
@@ -410,10 +425,24 @@ export async function pickFreePort(): Promise<number> {
410
425
  });
411
426
  }
412
427
 
413
- /** The default readiness probe: `GET <url>/api/diff` — a review-server-only route. */
414
- async function probeReviewServer(url: string, signal?: AbortSignal): Promise<boolean> {
428
+ /**
429
+ * The code-review readiness route review-server-only (`server/serverReview.ts`; absent from
430
+ * the plan server), so the probe can never false-positive against a plan server. Pinned at
431
+ * `@plannotator/pi-extension@0.26.4`.
432
+ */
433
+ export const CODE_REVIEW_READINESS_PROBE_PATH = "/api/diff";
434
+
435
+ /**
436
+ * The plan-review readiness route — plan-server-only (`server/serverPlan.ts`; absent from the
437
+ * review server), the mirror of the code-review pin. Pinned at
438
+ * `@plannotator/pi-extension@0.26.4`.
439
+ */
440
+ export const PLAN_REVIEW_READINESS_PROBE_PATH = "/api/plan";
441
+
442
+ /** The default readiness probe: `GET <url><path>` — `path` a server-flavor-unique route. */
443
+ async function probeServer(url: string, path: string, signal?: AbortSignal): Promise<boolean> {
415
444
  try {
416
- const response = await fetch(`${url}/api/diff`, { signal });
445
+ const response = await fetch(`${url}${path}`, { signal });
417
446
  return response.ok;
418
447
  } catch {
419
448
  return false;
@@ -432,35 +461,39 @@ export interface StartBrowserDeps {
432
461
  sleep?: (ms: number) => Promise<void>;
433
462
  }
434
463
 
435
- /** A started browser open: the deterministic address + the two observable promises. */
436
- export interface StartedBrowser {
464
+ /** A started surface open: the deterministic address + the two observable promises. */
465
+ export interface StartedSurface<T> {
437
466
  url: string;
438
467
  port: number;
439
- bridgePromise: Promise<CodeReviewOutcome>;
468
+ bridgePromise: Promise<T>;
440
469
  readiness: Promise<BrowserReadiness>;
441
470
  }
442
471
 
472
+ /** The code-review flavor of a started open (the original name — `/pr-review-browser` imports it). */
473
+ export type StartedBrowser = StartedSurface<CodeReviewOutcome>;
474
+
443
475
  /**
444
- * The composable browser-open core: pick a free port → save + preset `PLANNOTATOR_PORT` → emit
445
- * the `code-review` bridge request (the PR-mode payload `{prUrl, cwd}` byte-for-byte
446
- * plannotator's defaults, including its own local checkout for Ask AI / Full-stack: deliberately
447
- * NOT `useLocal: false`, the human chose the full surface) return immediately with the
448
- * deterministic `{url, port}` plus the two promises the caller observes: `bridgePromise` (the
449
- * single respond) and `readiness` (the `GET /api/diff` poll1s cadence, 120s budget,
450
- * attempt-counted so injected test clocks stay deterministic; stops early when the bridge
451
- * settles first an early error/unavailable respond means the server never comes or the turn
452
- * aborts). The prior env value is ALWAYS restored (delete if previously unset) in a `finally`
453
- * when the poll ends: after the window the fixed port is released back to plannotator's own
454
- * resolution (random port) for any later server. A port-pick failure throws — the caller owns
455
- * its failure surface.
476
+ * The generic engine behind both browser-open flavors: pick a free port → save + preset
477
+ * `PLANNOTATOR_PORT` invoke the launch closure WHILE the env var is preset (plannotator's
478
+ * `listenOnPort` reads it at bind time) return immediately with the deterministic `{url, port}`
479
+ * plus the two promises the caller observes: `bridgePromise` (the launch's settled outcome) and
480
+ * `readiness` (a `GET <url><probePath>` poll 1s cadence, 120s budget, attempt-counted so
481
+ * injected test clocks stay deterministic; stops early when the bridge settles firstan early
482
+ * error/unavailable respond means the server never comes or the turn aborts). The prior env
483
+ * value is ALWAYS restored (delete if previously unset) in a `finally` when the poll ends: after
484
+ * the window the fixed port is released back to plannotator's own resolution (random port) for
485
+ * any later server. A port-pick failure throws the caller owns its failure surface.
456
486
  */
457
- export async function startPlannotatorBrowser(
458
- bus: PlannotatorBus,
459
- opts: { prUrl: string; cwd: string; signal?: AbortSignal },
460
- deps: StartBrowserDeps = {},
461
- ): Promise<StartedBrowser> {
487
+ async function startPlannotatorSurface<T>(
488
+ launch: (signal?: AbortSignal) => Promise<T>,
489
+ probePath: string,
490
+ signal: AbortSignal | undefined,
491
+ deps: StartBrowserDeps,
492
+ ): Promise<StartedSurface<T>> {
462
493
  const pickPort = deps.pickFreePort ?? pickFreePort;
463
- const probe = deps.probe ?? probeReviewServer;
494
+ const probe =
495
+ deps.probe ??
496
+ ((url: string, probeSignal?: AbortSignal) => probeServer(url, probePath, probeSignal));
464
497
  const intervalMs = deps.intervalMs ?? READINESS_PROBE_INTERVAL_MS;
465
498
  const budgetMs = deps.budgetMs ?? READINESS_PROBE_BUDGET_MS;
466
499
  const sleep =
@@ -472,14 +505,10 @@ export async function startPlannotatorBrowser(
472
505
  const priorPort = process.env.PLANNOTATOR_PORT;
473
506
  process.env.PLANNOTATOR_PORT = String(port);
474
507
 
475
- // Emit the bridge request while PLANNOTATOR_PORT is preset — plannotator's `listenOnPort`
508
+ // Launch the bridge request while PLANNOTATOR_PORT is preset — plannotator's `listenOnPort`
476
509
  // reads it at bind time.
477
510
  let bridgeSettled = false;
478
- const bridgePromise = requestPlannotatorCodeReview(bus, {
479
- prUrl: opts.prUrl,
480
- cwd: opts.cwd,
481
- signal: opts.signal,
482
- });
511
+ const bridgePromise = launch(signal);
483
512
  void bridgePromise.then(() => {
484
513
  bridgeSettled = true;
485
514
  });
@@ -490,9 +519,9 @@ export async function startPlannotatorBrowser(
490
519
  for (let i = 0; i < attempts; i++) {
491
520
  // Abort first: an aborted turn also settles the bridge (as `aborted`), and the abort
492
521
  // arm must win so the observer stays silent instead of degrading.
493
- if (opts.signal?.aborted === true) return "aborted";
522
+ if (signal?.aborted === true) return "aborted";
494
523
  if (bridgeSettled) return "bridge_settled";
495
- if (await probe(url, opts.signal)) return "ready";
524
+ if (await probe(url, signal)) return "ready";
496
525
  await sleep(intervalMs);
497
526
  }
498
527
  return "timeout";
@@ -507,3 +536,45 @@ export async function startPlannotatorBrowser(
507
536
 
508
537
  return { url, port, bridgePromise, readiness };
509
538
  }
539
+
540
+ /**
541
+ * The composable code-review browser open: the engine with launch = the `code-review` bridge
542
+ * request (the PR-mode payload `{prUrl, cwd}` byte-for-byte — plannotator's defaults, including
543
+ * its own local checkout for Ask AI / Full-stack: deliberately NOT `useLocal: false`, the human
544
+ * chose the full surface) and the `/api/diff` readiness route (`bridgePromise` is the single
545
+ * respond — code-review has no handshake).
546
+ */
547
+ export async function startPlannotatorBrowser(
548
+ bus: PlannotatorBus,
549
+ opts: { prUrl: string; cwd: string; signal?: AbortSignal },
550
+ deps: StartBrowserDeps = {},
551
+ ): Promise<StartedBrowser> {
552
+ return await startPlannotatorSurface(
553
+ (signal) => requestPlannotatorCodeReview(bus, { prUrl: opts.prUrl, cwd: opts.cwd, signal }),
554
+ CODE_REVIEW_READINESS_PROBE_PATH,
555
+ opts.signal,
556
+ deps,
557
+ );
558
+ }
559
+
560
+ /**
561
+ * The composable plan-review browser open: the engine with launch = the `plan-review` bridge
562
+ * request (handshake + per-review decision listener — see `requestPlannotatorPlanReview`) and
563
+ * the `/api/plan` readiness route. Unlike code-review, the plan server is already bound when the
564
+ * handshake respond arrives, so `readiness` usually settles `ready` on an early attempt — the
565
+ * poll still bounds the env-restore window and stops early (`bridge_settled`) on a handshake
566
+ * failure. The deterministic `{url, port}` is what lets a door prime the plan server (e.g.
567
+ * `push_annotations`) without waiting for the human's decision.
568
+ */
569
+ export async function startPlannotatorPlanReview(
570
+ bus: PlannotatorBus,
571
+ opts: { plan: string; signal?: AbortSignal },
572
+ deps: StartBrowserDeps = {},
573
+ ): Promise<StartedSurface<ReviewOutcome>> {
574
+ return await startPlannotatorSurface(
575
+ (signal) => requestPlannotatorPlanReview(bus, opts.plan, signal),
576
+ PLAN_REVIEW_READINESS_PROBE_PATH,
577
+ opts.signal,
578
+ deps,
579
+ );
580
+ }