@lnittman/pi-interview 0.4.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 (42) hide show
  1. package/README.md +33 -0
  2. package/dist/adapters/model-client.d.ts +20 -0
  3. package/dist/adapters/model-client.d.ts.map +1 -0
  4. package/dist/adapters/model-client.js +129 -0
  5. package/dist/adapters/model-client.js.map +1 -0
  6. package/dist/core/demo.d.ts +9 -0
  7. package/dist/core/demo.d.ts.map +1 -0
  8. package/dist/core/demo.js +118 -0
  9. package/dist/core/demo.js.map +1 -0
  10. package/dist/core/project-context.d.ts +31 -0
  11. package/dist/core/project-context.d.ts.map +1 -0
  12. package/dist/core/project-context.js +82 -0
  13. package/dist/core/project-context.js.map +1 -0
  14. package/dist/core/signals.d.ts +43 -0
  15. package/dist/core/signals.d.ts.map +1 -0
  16. package/dist/core/signals.js +160 -0
  17. package/dist/core/signals.js.map +1 -0
  18. package/dist/core/state.d.ts +33 -0
  19. package/dist/core/state.d.ts.map +1 -0
  20. package/dist/core/state.js +62 -0
  21. package/dist/core/state.js.map +1 -0
  22. package/dist/core/types.d.ts +71 -0
  23. package/dist/core/types.d.ts.map +1 -0
  24. package/dist/core/types.js +18 -0
  25. package/dist/core/types.js.map +1 -0
  26. package/dist/index.d.ts +9 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +301 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/prompts/compose-template.d.ts +8 -0
  31. package/dist/prompts/compose-template.d.ts.map +1 -0
  32. package/dist/prompts/compose-template.js +40 -0
  33. package/dist/prompts/compose-template.js.map +1 -0
  34. package/dist/prompts/interview-template.d.ts +24 -0
  35. package/dist/prompts/interview-template.d.ts.map +1 -0
  36. package/dist/prompts/interview-template.js +101 -0
  37. package/dist/prompts/interview-template.js.map +1 -0
  38. package/dist/ui/interview-ui.d.ts +14 -0
  39. package/dist/ui/interview-ui.d.ts.map +1 -0
  40. package/dist/ui/interview-ui.js +285 -0
  41. package/dist/ui/interview-ui.js.map +1 -0
  42. package/package.json +51 -0
package/dist/index.js ADDED
@@ -0,0 +1,301 @@
1
+ /**
2
+ * pi-quiz — Multiple-choice next-prompt quiz for pi.
3
+ *
4
+ * Every question is multiple choice. Grounded in specific files, errors, signals.
5
+ * Ctrl+Q to trigger. /quiz for commands. Haiku by default.
6
+ */
7
+ import { completeSimple } from "@mariozechner/pi-ai";
8
+ import { buildTurnContext, buildTurnContextFromBranch } from "./core/signals.js";
9
+ import { buildQuizPromptContext } from "./prompts/interview-template.js";
10
+ import { QuizModelClient } from "./adapters/model-client.js";
11
+ import { showInterviewUI } from "./ui/interview-ui.js";
12
+ import { emptyState, recordQuizCall, shouldBackOff, formatUsageStatus, } from "./core/state.js";
13
+ import { buildProjectSnapshot, } from "./core/project-context.js";
14
+ import { DEFAULT_CONFIG } from "./core/types.js";
15
+ import { getDemoTurn, listDemoScenarios } from "./core/demo.js";
16
+ const CUSTOM_TYPE = "pi-quiz-state";
17
+ export default function interview(pi) {
18
+ let config = { ...DEFAULT_CONFIG };
19
+ let ctx;
20
+ let lastTurn;
21
+ let quizActive = false;
22
+ let epoch = 0;
23
+ let state = emptyState();
24
+ let projectSnapshot;
25
+ const modelClient = new QuizModelClient({ getContext: () => ctx }, completeSimple);
26
+ // ─── State Persistence ────────────────────────────────────────────────
27
+ function persistState() {
28
+ pi.appendEntry(CUSTOM_TYPE, { ...state });
29
+ }
30
+ function restoreState(entries) {
31
+ state = emptyState();
32
+ for (const entry of entries) {
33
+ if (entry.type === "custom" && entry.customType === CUSTOM_TYPE && entry.data) {
34
+ if (entry.data.version === 1) {
35
+ state = entry.data;
36
+ }
37
+ }
38
+ }
39
+ }
40
+ function refreshUsageWidget() {
41
+ if (!ctx?.hasUI)
42
+ return;
43
+ const status = formatUsageStatus(state);
44
+ ctx.ui.setStatus("quiz-usage", status);
45
+ }
46
+ // ─── Project Context ──────────────────────────────────────────────────
47
+ async function ensureProjectSnapshot() {
48
+ if (!projectSnapshot && ctx) {
49
+ try {
50
+ projectSnapshot = await buildProjectSnapshot(ctx.cwd);
51
+ }
52
+ catch {
53
+ // Non-critical — quiz works without it
54
+ }
55
+ }
56
+ }
57
+ // ─── Core Flow ────────────────────────────────────────────────────────
58
+ async function runQuiz(turn, context, currentEpoch, manual = false) {
59
+ if (!context.hasUI || quizActive)
60
+ return;
61
+ if (currentEpoch !== epoch)
62
+ return;
63
+ // Don't re-quiz the same turn
64
+ if (!manual && state.lastQuizTurnId === turn.turnId)
65
+ return;
66
+ // Back-off after repeated skips/cancels (unless manual)
67
+ if (!manual && shouldBackOff(state))
68
+ return;
69
+ // Skip trivially short responses (unless manual)
70
+ if (!manual &&
71
+ config.skipOnSimpleResponse &&
72
+ turn.assistantText.length < 100 &&
73
+ turn.unresolvedQuestions.length === 0 &&
74
+ turn.status === "success") {
75
+ return;
76
+ }
77
+ quizActive = true;
78
+ try {
79
+ await ensureProjectSnapshot();
80
+ const promptContext = buildQuizPromptContext(turn, config, projectSnapshot);
81
+ context.ui.setWidget("quiz-loading", [
82
+ ` ${context.ui.theme.fg("dim", "✦ quiz...")}`,
83
+ ], { placement: "belowEditor" });
84
+ const result = await modelClient.generateQuiz(promptContext, config);
85
+ if (currentEpoch !== epoch) {
86
+ context.ui.setWidget("quiz-loading", undefined);
87
+ return;
88
+ }
89
+ context.ui.setWidget("quiz-loading", undefined);
90
+ if (result.skipped || result.questions.length === 0) {
91
+ state = recordQuizCall(state, result.usage, "skipped", turn.turnId);
92
+ persistState();
93
+ refreshUsageWidget();
94
+ // Brief flash only on first few skips
95
+ if (state.consecutiveSkips <= 2) {
96
+ context.ui.setWidget("quiz-loading", [
97
+ ` ${context.ui.theme.fg("dim", `✦ —${result.skipReason ? ` ${result.skipReason}` : ""}`)}`,
98
+ ], { placement: "belowEditor" });
99
+ setTimeout(() => context.ui.setWidget("quiz-loading", undefined), 1500);
100
+ }
101
+ return;
102
+ }
103
+ // Show usage inline
104
+ if (result.usage) {
105
+ const cost = result.usage.costTotal ? ` $${result.usage.costTotal.toFixed(4)}` : "";
106
+ context.ui.setStatus("quiz", `✦ ${result.usage.totalTokens} tok${cost}`);
107
+ }
108
+ const submission = await showInterviewUI(context, result.questions, config);
109
+ context.ui.setStatus("quiz", undefined);
110
+ if (submission.cancelled) {
111
+ state = recordQuizCall(state, result.usage, "cancelled", turn.turnId);
112
+ }
113
+ else {
114
+ state = recordQuizCall(state, result.usage, "completed", turn.turnId);
115
+ if (submission.composedPrompt) {
116
+ // Send directly as a user message — don't pollute the editor
117
+ pi.sendUserMessage(submission.composedPrompt);
118
+ }
119
+ }
120
+ persistState();
121
+ refreshUsageWidget();
122
+ }
123
+ catch (error) {
124
+ context.ui.setWidget("quiz-loading", undefined);
125
+ const msg = error instanceof Error ? error.message : String(error);
126
+ context.ui.notify(`quiz: ${msg.slice(0, 80)}`, "error");
127
+ }
128
+ finally {
129
+ quizActive = false;
130
+ }
131
+ }
132
+ // ─── Helpers ──────────────────────────────────────────────────────────
133
+ function getTurnFromBranch(context) {
134
+ const branchEntries = context.sessionManager
135
+ .getBranch()
136
+ .filter((e) => e.type === "message");
137
+ return buildTurnContextFromBranch(branchEntries) ?? undefined;
138
+ }
139
+ // ─── Events ───────────────────────────────────────────────────────────
140
+ pi.on("session_start", async (_ev, c) => {
141
+ ctx = c;
142
+ epoch++;
143
+ projectSnapshot = undefined;
144
+ restoreState(c.sessionManager.getEntries());
145
+ refreshUsageWidget();
146
+ });
147
+ pi.on("session_switch", async (_ev, c) => {
148
+ ctx = c;
149
+ epoch++;
150
+ quizActive = false;
151
+ projectSnapshot = undefined;
152
+ restoreState(c.sessionManager.getEntries());
153
+ refreshUsageWidget();
154
+ });
155
+ pi.on("session_fork", async (_ev, c) => {
156
+ ctx = c;
157
+ epoch++;
158
+ quizActive = false;
159
+ restoreState(c.sessionManager.getEntries());
160
+ refreshUsageWidget();
161
+ });
162
+ pi.on("agent_end", async (event, c) => {
163
+ ctx = c;
164
+ const e = ++epoch;
165
+ if (config.mode !== "auto")
166
+ return;
167
+ const branchEntries = c.sessionManager.getBranch();
168
+ const branchMessages = branchEntries
169
+ .filter((entry) => entry.type === "message")
170
+ .map((entry) => entry.message);
171
+ const leafId = c.sessionManager.getLeafId() ?? `turn-${Date.now()}`;
172
+ const turn = buildTurnContext({
173
+ turnId: leafId,
174
+ sourceLeafId: leafId,
175
+ messagesFromPrompt: event.messages,
176
+ branchMessages: branchMessages,
177
+ occurredAt: new Date().toISOString(),
178
+ });
179
+ if (!turn)
180
+ return;
181
+ lastTurn = turn;
182
+ await runQuiz(turn, c, e);
183
+ });
184
+ pi.on("input", async (_ev, c) => {
185
+ ctx = c;
186
+ epoch++;
187
+ c.ui.setWidget("quiz-loading", undefined);
188
+ return { action: "continue" };
189
+ });
190
+ // ─── Commands ─────────────────────────────────────────────────────────
191
+ pi.registerCommand("interview", {
192
+ description: "interview: ask | demo [scenario] | status | reset | config <key> <value>",
193
+ handler: async (args, c) => {
194
+ ctx = c;
195
+ const [sub, ...rest] = args.trim().split(/\s+/);
196
+ if (!sub || sub === "ask") {
197
+ const turn = lastTurn ?? getTurnFromBranch(c);
198
+ if (!turn) {
199
+ c.ui.notify("No conversation context", "warning");
200
+ return;
201
+ }
202
+ const e = ++epoch;
203
+ await runQuiz(turn, c, e, true);
204
+ return;
205
+ }
206
+ if (sub === "demo") {
207
+ const scenario = rest[0];
208
+ if (scenario === "list" || scenario === "help") {
209
+ c.ui.notify(`scenarios: ${listDemoScenarios().join(", ")}`, "info");
210
+ return;
211
+ }
212
+ const turn = getDemoTurn(scenario);
213
+ const e = ++epoch;
214
+ await runQuiz(turn, c, e, true);
215
+ return;
216
+ }
217
+ if (sub === "status") {
218
+ const usage = state.usage;
219
+ const lines = [
220
+ `✦ pi-quiz`,
221
+ `mode: ${config.mode} · model: ${config.model}`,
222
+ `maxQ: ${config.maxQuestions} · maxOpts: ${config.maxOptions}`,
223
+ `calls: ${usage.calls} (${usage.completions} used, ${usage.skips} skipped, ${usage.cancels} cancelled)`,
224
+ `tokens: ↑${usage.totalInputTokens} ↓${usage.totalOutputTokens} $${usage.totalCost.toFixed(4)}`,
225
+ `backoff: ${shouldBackOff(state) ? "active (3+ skips)" : "off"}`,
226
+ ];
227
+ if (projectSnapshot) {
228
+ lines.push(`project: ${projectSnapshot.name}${projectSnapshot.branch ? ` (${projectSnapshot.branch})` : ""}`);
229
+ }
230
+ pi.sendMessage({
231
+ customType: "quiz-info",
232
+ content: lines.join("\n"),
233
+ display: true,
234
+ }, { triggerTurn: false });
235
+ return;
236
+ }
237
+ if (sub === "reset") {
238
+ state = emptyState();
239
+ persistState();
240
+ refreshUsageWidget();
241
+ c.ui.notify("quiz state reset", "info");
242
+ return;
243
+ }
244
+ if (sub === "config") {
245
+ const key = rest[0];
246
+ const val = rest.slice(1).join(" ");
247
+ if (!key) {
248
+ c.ui.notify("/quiz config <key> <value>", "info");
249
+ return;
250
+ }
251
+ switch (key) {
252
+ case "mode":
253
+ if (val === "auto" || val === "manual") {
254
+ config.mode = val;
255
+ c.ui.notify(`mode: ${val}`, "info");
256
+ }
257
+ break;
258
+ case "model":
259
+ config.model = val || DEFAULT_CONFIG.model;
260
+ c.ui.notify(`model: ${config.model}`, "info");
261
+ break;
262
+ case "maxQuestions":
263
+ config.maxQuestions = Math.max(1, Math.min(5, parseInt(val) || 3));
264
+ c.ui.notify(`maxQuestions: ${config.maxQuestions}`, "info");
265
+ break;
266
+ case "maxOptions":
267
+ config.maxOptions = Math.max(2, Math.min(8, parseInt(val) || 5));
268
+ c.ui.notify(`maxOptions: ${config.maxOptions}`, "info");
269
+ break;
270
+ case "skip":
271
+ config.skipOnSimpleResponse = val !== "false";
272
+ c.ui.notify(`skip: ${config.skipOnSimpleResponse}`, "info");
273
+ break;
274
+ case "instruction":
275
+ config.customInstruction = val;
276
+ c.ui.notify(val ? `instruction: "${val}"` : "instruction cleared", "info");
277
+ break;
278
+ default:
279
+ c.ui.notify(`Unknown: ${key}`, "warning");
280
+ }
281
+ return;
282
+ }
283
+ c.ui.notify("/quiz [ask | status | reset | config <key> <value>]", "info");
284
+ },
285
+ });
286
+ // ─── Shortcut ─────────────────────────────────────────────────────────
287
+ pi.registerShortcut("ctrl+i", {
288
+ description: "Trigger interview",
289
+ handler: async (c) => {
290
+ ctx = c;
291
+ const turn = lastTurn ?? getTurnFromBranch(c);
292
+ if (!turn) {
293
+ c.ui.notify("No conversation context", "warning");
294
+ return;
295
+ }
296
+ const e = ++epoch;
297
+ await runQuiz(turn, c, e, true);
298
+ },
299
+ });
300
+ }
301
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Compose quiz answers into a natural language prompt.
3
+ * Deterministic — no model call needed.
4
+ */
5
+ import type { QuizQuestion, QuizAnswer, QuizSubmission } from "../core/types.js";
6
+ export declare function composePrompt(questions: QuizQuestion[], answers: QuizAnswer[], maxChars: number): string;
7
+ export declare function buildSubmission(questions: QuizQuestion[], answers: QuizAnswer[], maxChars: number, startTime: number, cancelled: boolean): QuizSubmission;
8
+ //# sourceMappingURL=compose-template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose-template.d.ts","sourceRoot":"","sources":["../../src/prompts/compose-template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEjF,wBAAgB,aAAa,CAC3B,SAAS,EAAE,YAAY,EAAE,EACzB,OAAO,EAAE,UAAU,EAAE,EACrB,QAAQ,EAAE,MAAM,GACf,MAAM,CA8BR;AAED,wBAAgB,eAAe,CAC7B,SAAS,EAAE,YAAY,EAAE,EACzB,OAAO,EAAE,UAAU,EAAE,EACrB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,OAAO,GACjB,cAAc,CAOhB"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Compose quiz answers into a natural language prompt.
3
+ * Deterministic — no model call needed.
4
+ */
5
+ export function composePrompt(questions, answers, maxChars) {
6
+ const parts = [];
7
+ for (const answer of answers) {
8
+ if (answer.skipped)
9
+ continue;
10
+ if (answer.selectedOptions && answer.selectedOptions.length > 0) {
11
+ if (answer.selectedOptions.length === 1) {
12
+ parts.push(answer.selectedOptions[0]);
13
+ }
14
+ else {
15
+ parts.push(answer.selectedOptions.join(", then "));
16
+ }
17
+ }
18
+ if (answer.text && answer.text.trim()) {
19
+ parts.push(answer.text.trim());
20
+ }
21
+ }
22
+ if (parts.length === 0)
23
+ return "";
24
+ let composed = parts.length === 1
25
+ ? parts[0]
26
+ : parts.join(". ");
27
+ if (composed.length > maxChars) {
28
+ composed = composed.slice(0, maxChars - 1) + "…";
29
+ }
30
+ return composed;
31
+ }
32
+ export function buildSubmission(questions, answers, maxChars, startTime, cancelled) {
33
+ return {
34
+ answers,
35
+ composedPrompt: cancelled ? "" : composePrompt(questions, answers, maxChars),
36
+ cancelled,
37
+ durationMs: Date.now() - startTime,
38
+ };
39
+ }
40
+ //# sourceMappingURL=compose-template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose-template.js","sourceRoot":"","sources":["../../src/prompts/compose-template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,UAAU,aAAa,CAC3B,SAAyB,EACzB,OAAqB,EACrB,QAAgB;IAEhB,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,OAAO;YAAE,SAAS;QAE7B,IAAI,MAAM,CAAC,eAAe,IAAI,MAAM,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChE,IAAI,MAAM,CAAC,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;YACxC,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;YACrD,CAAC;QACH,CAAC;QAED,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAElC,IAAI,QAAQ,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC;QAC/B,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QACV,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAErB,IAAI,QAAQ,CAAC,MAAM,GAAG,QAAQ,EAAE,CAAC;QAC/B,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;IACnD,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,SAAyB,EACzB,OAAqB,EACrB,QAAgB,EAChB,SAAiB,EACjB,SAAkB;IAElB,OAAO;QACL,OAAO;QACP,cAAc,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC;QAC5E,SAAS;QACT,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;KACnC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Quiz prompt template.
3
+ *
4
+ * Every question MUST be multiple choice.
5
+ * No "text" type questions — the UI handles freeform via "Type something else..." option.
6
+ */
7
+ import type { TurnContext, QuizConfig } from "../core/types.js";
8
+ import type { ProjectSnapshot } from "../core/project-context.js";
9
+ export interface QuizPromptContext {
10
+ assistantText: string;
11
+ turnStatus: TurnContext["status"];
12
+ recentUserPrompts: string[];
13
+ toolSignals: string[];
14
+ touchedFiles: string[];
15
+ unresolvedQuestions: string[];
16
+ abortContextNote?: string;
17
+ projectContext?: string;
18
+ maxQuestions: number;
19
+ maxOptions: number;
20
+ customInstruction: string;
21
+ }
22
+ export declare function buildQuizPromptContext(turn: TurnContext, config: QuizConfig, project?: ProjectSnapshot): QuizPromptContext;
23
+ export declare function renderQuizPrompt(ctx: QuizPromptContext): string;
24
+ //# sourceMappingURL=interview-template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interview-template.d.ts","sourceRoot":"","sources":["../../src/prompts/interview-template.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAQlE,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;IAClC,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,UAAU,EAClB,OAAO,CAAC,EAAE,eAAe,GACxB,iBAAiB,CAkBnB;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,CAoE/D"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Quiz prompt template.
3
+ *
4
+ * Every question MUST be multiple choice.
5
+ * No "text" type questions — the UI handles freeform via "Type something else..." option.
6
+ */
7
+ import { formatProjectContext } from "../core/project-context.js";
8
+ function truncate(value, maxChars) {
9
+ if (value.length <= maxChars)
10
+ return value;
11
+ return value.slice(0, maxChars) + "…";
12
+ }
13
+ export function buildQuizPromptContext(turn, config, project) {
14
+ return {
15
+ assistantText: truncate(turn.assistantText, 50_000),
16
+ turnStatus: turn.status,
17
+ recentUserPrompts: turn.recentUserPrompts
18
+ .slice(0, 10)
19
+ .map((p) => truncate(p, 500)),
20
+ toolSignals: turn.toolSignals.slice(0, 12),
21
+ touchedFiles: turn.touchedFiles.slice(0, 10),
22
+ unresolvedQuestions: turn.unresolvedQuestions.slice(0, 8),
23
+ abortContextNote: turn.abortContextNote
24
+ ? truncate(turn.abortContextNote, 300)
25
+ : undefined,
26
+ projectContext: project ? formatProjectContext(project) : undefined,
27
+ maxQuestions: config.maxQuestions,
28
+ maxOptions: config.maxOptions,
29
+ customInstruction: config.customInstruction,
30
+ };
31
+ }
32
+ export function renderQuizPrompt(ctx) {
33
+ return `You generate multiple-choice questions to help a developer decide what to instruct their coding agent next.
34
+
35
+ EVERY question MUST have concrete options. No free-text questions.
36
+
37
+ Return ONLY valid JSON:
38
+ {
39
+ "questions": [
40
+ {
41
+ "id": "string",
42
+ "text": "Short question (under 80 chars)",
43
+ "type": "multi",
44
+ "options": [
45
+ { "label": "Concrete action (under 60 chars)", "description": "optional context" }
46
+ ]
47
+ }
48
+ ],
49
+ "skipped": false
50
+ }
51
+
52
+ If the next step is obvious (e.g. agent proposed something clear), return:
53
+ { "questions": [], "skipped": true, "skipReason": "brief reason" }
54
+
55
+ ── Context ──
56
+ ${ctx.projectContext ? `\nProject:\n${ctx.projectContext}\n` : ""}
57
+ TurnStatus: ${ctx.turnStatus}
58
+ ${ctx.abortContextNote ? `\nAbortContext:\n${ctx.abortContextNote}` : ""}
59
+
60
+ RecentUserMessages:
61
+ ${ctx.recentUserPrompts.length > 0 ? ctx.recentUserPrompts.map((p) => `- ${p}`).join("\n") : "(none)"}
62
+
63
+ ToolSignals:
64
+ ${ctx.toolSignals.length > 0 ? ctx.toolSignals.map((s) => `- ${s}`).join("\n") : "(none)"}
65
+
66
+ TouchedFiles:
67
+ ${ctx.touchedFiles.length > 0 ? ctx.touchedFiles.map((f) => `- ${f}`).join("\n") : "(none)"}
68
+
69
+ UnresolvedQuestions:
70
+ ${ctx.unresolvedQuestions.length > 0 ? ctx.unresolvedQuestions.map((q) => `- ${q}`).join("\n") : "(none)"}
71
+
72
+ LatestAssistantMessage:
73
+ \`\`\`
74
+ ${ctx.assistantText || "(empty)"}
75
+ \`\`\`
76
+ ${ctx.customInstruction.trim() ? `\nPreference:\n${ctx.customInstruction.trim()}` : ""}
77
+
78
+ ── Rules ──
79
+
80
+ GROUNDING (critical):
81
+ - Every option label MUST reference specific artifacts from the context above
82
+ - If TouchedFiles has "src/auth/login.ts", options should name that file, not say "the auth module"
83
+ - If ToolSignals has "bash(npm test):error", options should reference the test failure specifically
84
+ - If UnresolvedQuestions exist, turn them into structured options verbatim
85
+ - NEVER generate generic options like "Continue working", "Fix issues", "Improve code"
86
+ - Bad: "Add error handling" — Good: "Add try/catch to parseConfig in src/config/loader.ts"
87
+ - Bad: "Run tests" — Good: "Re-run the 3 failing vitest specs in packages/backend"
88
+
89
+ STRUCTURE:
90
+ - Generate 1-${ctx.maxQuestions} questions, each with 2-${ctx.maxOptions} options
91
+ - type is ALWAYS "multi" — user can check one or several options
92
+ - First option = most natural/likely next step
93
+ - description field: use for file paths, error counts, or other concrete context
94
+ - User can also add freeform notes via the UI — don't generate text-input questions
95
+
96
+ SKIP when:
97
+ - Agent proposed a clear next step and user just needs to affirm
98
+ - Conversation is wrapping up naturally
99
+ - The last exchange was a simple Q&A with no follow-up implied`;
100
+ }
101
+ //# sourceMappingURL=interview-template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interview-template.js","sourceRoot":"","sources":["../../src/prompts/interview-template.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Interview UI — based on the proven ask-user extension patterns.
3
+ *
4
+ * - ALL questions rendered as multi-select (checkboxes, not radio)
5
+ * - Space toggles selection with visual ☑/☐ feedback
6
+ * - Enter confirms and advances
7
+ * - 'n' key opens notes input for any question
8
+ * - Tab navigates between questions
9
+ * - matchesKey/Key for cross-terminal compat
10
+ */
11
+ import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
12
+ import type { QuizQuestion, QuizSubmission, QuizConfig } from "../core/types.js";
13
+ export declare function showInterviewUI(ctx: ExtensionContext, questions: QuizQuestion[], config: QuizConfig): Promise<QuizSubmission>;
14
+ //# sourceMappingURL=interview-ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interview-ui.d.ts","sourceRoot":"","sources":["../../src/ui/interview-ui.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAOtE,OAAO,KAAK,EACV,YAAY,EAEZ,cAAc,EACd,UAAU,EACX,MAAM,kBAAkB,CAAC;AAG1B,wBAAsB,eAAe,CACnC,GAAG,EAAE,gBAAgB,EACrB,SAAS,EAAE,YAAY,EAAE,EACzB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,cAAc,CAAC,CAiSzB"}