@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
@@ -1,198 +0,0 @@
1
- // The universal, first-party `ask_user_question` tool. Lets a model interactively ask the
2
- // human a clarifying question during a turn (free-text, or a multiple-choice selection with an
3
- // always-present "type a custom answer" escape) and continues its turn with the answer. It is
4
- // NON-terminating and headless-fail-safe: with no interactive UI it returns a graceful no-user
5
- // sentinel instead of blocking (the AGENTS.md headless-fail-safe convention).
6
- //
7
- // To be callable *during planning* the tool name is added to `READ_ONLY_TOOLS` in toolGating.ts —
8
- // read-only mode otherwise hides every custom tool (the documented read-only gating trap). The
9
- // stricter SDK_READ_ONLY_TOOLS (headless child sessions) is intentionally NOT touched.
10
- //
11
- // REGISTRATION-TIME VACATING (askuser interface seam). `ask_user_question` is a pluggable provider
12
- // seam: a repo may select the foreign `@juicesharp/rpiv-ask-user-question` extension via
13
- // `[providers] askuser = "juicesharp-ask-user"`. That package registers a tool with the IDENTICAL
14
- // name `ask_user_question`. Tools (unlike commands) do NOT get `:N` suffixes — a same-named tool
15
- // replaces/warns by extension load order, which is non-deterministic. So under a foreign askuser
16
- // selection `registerAskUser` registers NOTHING (resolves the provider id once at factory time and
17
- // early-returns before `pi.registerTool`), leaving exactly one `ask_user_question` standing — the
18
- // same registration-time vacating proven on the plan seam (`registerPlanMode`). This is an
19
- // INTERFACE seam: there is no durable artifact to bridge (no `cache.plan-ref`/`perk:checkpoint`
20
- // analogue), so the adapter is vacate-only (`adapter: null`, no shim, no injected context); the
21
- // foreign tool self-documents via its own `promptGuidelines`. The foreign package is two-
22
- // directionally wired by `_converge_provider_packages` (installed only when selected, removed on
23
- // deselect), so under the default (`perk-ask-user`) the foreign package is never loaded and perk's
24
- // tool is the sole registrant — the default/fail-safe path is the hard guarantee (zero behavior
25
- // change).
26
- //
27
- // Structure: a pure, injectable core (`runAskUserQuestion`) over a minimal `AskUserUI` surface, plus
28
- // a thin `registerAskUser` wrapper — mirrors ciExecutor.ts's pure-core + injected-fakes testability.
29
-
30
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
31
- import { loadPerkConfig } from "../substrate/config.ts";
32
- import {
33
- loadProviders,
34
- PERK_ASK_USER_PROVIDER_ID,
35
- resolveProviders,
36
- } from "../substrate/providers.ts";
37
- import { paramsOf, stringArrayParam, stringParam } from "../substrate/toolParams.ts";
38
-
39
- /** The always-appended escape entry on the select path so preset options never trap the user. */
40
- export const OTHER_CHOICE = "✏️ Other (type a custom answer)…";
41
-
42
- /** The minimal structural UI surface the core needs (a subset of `ctx.ui`). */
43
- export interface AskUserUI {
44
- select(
45
- title: string,
46
- options: string[],
47
- opts?: { signal?: AbortSignal },
48
- ): Promise<string | undefined>;
49
- input(
50
- title: string,
51
- placeholder?: string,
52
- opts?: { signal?: AbortSignal },
53
- ): Promise<string | undefined>;
54
- }
55
-
56
- /** The structured `details` surface for the tool result (failure/dismissal are non-throwing). */
57
- export interface AskDetails {
58
- ok: boolean;
59
- /** Whether a real user answer was captured (false for headless / dismissed / empty). */
60
- answered: boolean;
61
- }
62
-
63
- /** A non-terminating tool result: the model continues its turn with the answer text. */
64
- export type AskResult = { content: { type: "text"; text: string }[]; details: AskDetails };
65
-
66
- const DISMISSED_TEXT = "(no answer — the user dismissed the prompt.)";
67
- const NO_UI_TEXT =
68
- "(no interactive user available — proceed using your best judgment and state the assumption you made.)";
69
- const NO_QUESTION_TEXT = "ask_user_question: no question provided.";
70
-
71
- function answer(text: string, answered: boolean): AskResult {
72
- return { content: [{ type: "text", text }], details: { ok: true, answered } };
73
- }
74
-
75
- /**
76
- * The pure core: ask the user `question` (optionally with preset `options`) and resolve to a
77
- * non-terminating tool result carrying the answer text. Fully offline-testable with a fake UI.
78
- */
79
- export async function runAskUserQuestion(args: {
80
- hasUI: boolean;
81
- ui: AskUserUI;
82
- question: string;
83
- options?: string[];
84
- signal?: AbortSignal;
85
- }): Promise<AskResult> {
86
- const { hasUI, ui, options, signal } = args;
87
- const question = (args.question ?? "").trim();
88
- if (!question) return answer(NO_QUESTION_TEXT, false);
89
- if (!hasUI) return answer(NO_UI_TEXT, false);
90
-
91
- if (options && options.length > 0) {
92
- const choice = await ui.select(question, [...options, OTHER_CHOICE], { signal });
93
- if (choice === undefined) return answer(DISMISSED_TEXT, false);
94
- if (choice === OTHER_CHOICE) {
95
- const typed = await ui.input(question, undefined, { signal });
96
- return typed === undefined ? answer(DISMISSED_TEXT, false) : answer(typed, true);
97
- }
98
- return answer(choice, true);
99
- }
100
-
101
- const typed = await ui.input(question, undefined, { signal });
102
- return typed === undefined ? answer(DISMISSED_TEXT, false) : answer(typed, true);
103
- }
104
-
105
- /**
106
- * Decode unknown `ask_user_question` tool-call params (the tool-boundary seam), in
107
- * this tool's native graceful vocabulary: `question` absent OR mistyped → "" (routed into
108
- * `runAskUserQuestion`'s NO_QUESTION_TEXT arm — answered: false, never throws/blocks); `options`
109
- * mistyped → advisory-dropped to undefined (the free-text path) — a UI affordance, not a durable
110
- * write (the decided exception to strict-fail).
111
- */
112
- export function decodeAskUserParams(params: unknown): { question: string; options?: string[] } {
113
- const p = paramsOf(params);
114
- if (p === null) return { question: "" };
115
- return {
116
- question: stringParam(p, "question") ?? "",
117
- // Advisory drop: a mistyped `options` falls back to the free-text path.
118
- options: stringArrayParam(p, "options") ?? undefined,
119
- };
120
- }
121
-
122
- const TOOL_GUIDELINES = [
123
- "Prefer ask_user_question during planning to resolve genuine ambiguity rather than guessing.",
124
- "Ask ONE focused question per ask_user_question call, and wait for the answer before the next.",
125
- "Provide ask_user_question `options` when the answer is a choice — a free-text escape is always added.",
126
- "Explore the codebase before calling ask_user_question: if a question is answerable from the code, read it instead of asking.",
127
- ];
128
-
129
- /**
130
- * The resolved `[providers] askuser` selection id for `cwd`. Fail-safe to the perk-ask-user
131
- * reference: any load/resolution failure returns the reference id so perk's own tool keeps
132
- * registering — the default path is the hard guarantee. Mirror of `resolvedPlanProviderId`: the
133
- * catch narrows to genuine file-read/parse failures (the resolver is per-seam fail-open) and is
134
- * logged, never silent.
135
- */
136
- export function resolvedAskUserProviderId(cwd: string): string {
137
- try {
138
- return resolveProviders(loadPerkConfig(cwd).providers, loadProviders()).askuser.id;
139
- } catch (error) {
140
- console.error(
141
- `perk: askuser provider resolution failed — falling back to ${PERK_ASK_USER_PROVIDER_ID}: ${error}`,
142
- );
143
- return PERK_ASK_USER_PROVIDER_ID;
144
- }
145
- }
146
-
147
- /**
148
- * Whether perk's own `ask_user_question` reference is the selected askuser provider for `cwd`. When
149
- * a foreign askuser provider is selected via `[providers] askuser`, perk's tool vacates (registers
150
- * nothing) so the foreign same-named tool is the sole registrant.
151
- */
152
- export function isPerkAskUserReferenceSelected(cwd: string): boolean {
153
- return resolvedAskUserProviderId(cwd) === PERK_ASK_USER_PROVIDER_ID;
154
- }
155
-
156
- /** Register the universal warm tool: `ask_user_question`. */
157
- export function registerAskUser(pi: ExtensionAPI): void {
158
- // Registration-time vacating: under a foreign askuser selection register NOTHING (the foreign
159
- // package's same-named tool is the sole registrant). Factory-time `process.cwd()` resolution
160
- // mirrors `registerPlanMode`. The default/fail-safe path registers exactly as today.
161
- if (resolvedAskUserProviderId(process.cwd()) !== PERK_ASK_USER_PROVIDER_ID) return;
162
- pi.registerTool({
163
- name: "ask_user_question",
164
- label: "Ask user",
165
- description:
166
- "Ask the human user a clarifying question and return their answer. Supports a free-text " +
167
- "prompt or a multiple-choice selection (a 'type a custom answer' escape is always added). " +
168
- "NON-terminating: the turn continues with the answer. With no interactive UI it returns a " +
169
- "no-user sentinel instead of blocking.",
170
- promptSnippet: "Ask the user a clarifying question and get their answer",
171
- promptGuidelines: TOOL_GUIDELINES,
172
- executionMode: "sequential",
173
- parameters: {
174
- type: "object",
175
- additionalProperties: false,
176
- required: ["question"],
177
- properties: {
178
- question: { type: "string", description: "The question to ask the user." },
179
- options: {
180
- type: "array",
181
- items: { type: "string" },
182
- description:
183
- "Optional preset choices rendered as a selector; a free-text escape is always appended.",
184
- },
185
- },
186
- },
187
- async execute(_toolCallId, params, signal, _onUpdate, ctx) {
188
- const { question, options } = decodeAskUserParams(params);
189
- return runAskUserQuestion({
190
- hasUI: ctx.hasUI,
191
- ui: ctx.ui,
192
- question,
193
- options,
194
- signal,
195
- });
196
- },
197
- });
198
- }
@@ -1,36 +0,0 @@
1
- {
2
- "type": "object",
3
- "additionalProperties": false,
4
- "required": ["node", "relevant_files", "symbols", "anchors", "patterns", "open_questions"],
5
- "properties": {
6
- "node": {"type": "string"},
7
- "relevant_files": {
8
- "type": "array",
9
- "items": {
10
- "type": "object",
11
- "additionalProperties": false,
12
- "required": ["path", "why"],
13
- "properties": {
14
- "path": {"type": "string"},
15
- "why": {"type": "string"}
16
- }
17
- }
18
- },
19
- "symbols": {
20
- "type": "array",
21
- "items": {
22
- "type": "object",
23
- "additionalProperties": false,
24
- "required": ["name", "path", "why"],
25
- "properties": {
26
- "name": {"type": "string"},
27
- "path": {"type": "string"},
28
- "why": {"type": "string"}
29
- }
30
- }
31
- },
32
- "anchors": {"type": "array", "items": {"type": "string"}},
33
- "patterns": {"type": "array", "items": {"type": "string"}},
34
- "open_questions": {"type": "array", "items": {"type": "string"}}
35
- }
36
- }
@@ -1,47 +0,0 @@
1
- {
2
- "type": "object",
3
- "additionalProperties": false,
4
- "required": ["pr", "review_threads", "discussion_comments", "counts"],
5
- "properties": {
6
- "pr": {"type": "integer"},
7
- "review_threads": {
8
- "type": "array",
9
- "items": {
10
- "type": "object",
11
- "additionalProperties": false,
12
- "required": ["thread_id", "classification", "path", "line", "summary"],
13
- "properties": {
14
- "thread_id": {"type": "string"},
15
- "classification": {"type": "string", "enum": ["actionable", "informational", "praise", "question"]},
16
- "path": {"type": ["string", "null"]},
17
- "line": {"type": ["integer", "null"]},
18
- "summary": {"type": "string"}
19
- }
20
- }
21
- },
22
- "discussion_comments": {
23
- "type": "array",
24
- "items": {
25
- "type": "object",
26
- "additionalProperties": false,
27
- "required": ["comment_id", "classification", "summary"],
28
- "properties": {
29
- "comment_id": {"type": "integer"},
30
- "classification": {"type": "string", "enum": ["actionable", "informational", "praise", "question"]},
31
- "summary": {"type": "string"}
32
- }
33
- }
34
- },
35
- "counts": {
36
- "type": "object",
37
- "additionalProperties": false,
38
- "required": ["actionable", "informational", "praise", "question"],
39
- "properties": {
40
- "actionable": {"type": "integer"},
41
- "informational": {"type": "integer"},
42
- "praise": {"type": "integer"},
43
- "question": {"type": "integer"}
44
- }
45
- }
46
- }
47
- }
@@ -1,7 +0,0 @@
1
- {{ marker }}
2
- This implement session tracks progress through the `@juicesharp/rpiv-todo` checklist overlay —
3
- the selected todo provider (perk's own checkpoint surface has stepped aside). Seed the overlay
4
- from the plan body's `## Steps` numbered list — one checklist item per step, in order — then
5
- mark each item complete as you finish it, using the overlay's own controls; perk's
6
- `[WIP:n]`/`[DONE:n]` markers are not needed here. If the plan has no `## Steps` list there is
7
- nothing to seed — let the overlay behave as its defaults suggest.