@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.
- package/README.md +33 -0
- package/dist/adapters/model-client.d.ts +20 -0
- package/dist/adapters/model-client.d.ts.map +1 -0
- package/dist/adapters/model-client.js +129 -0
- package/dist/adapters/model-client.js.map +1 -0
- package/dist/core/demo.d.ts +9 -0
- package/dist/core/demo.d.ts.map +1 -0
- package/dist/core/demo.js +118 -0
- package/dist/core/demo.js.map +1 -0
- package/dist/core/project-context.d.ts +31 -0
- package/dist/core/project-context.d.ts.map +1 -0
- package/dist/core/project-context.js +82 -0
- package/dist/core/project-context.js.map +1 -0
- package/dist/core/signals.d.ts +43 -0
- package/dist/core/signals.d.ts.map +1 -0
- package/dist/core/signals.js +160 -0
- package/dist/core/signals.js.map +1 -0
- package/dist/core/state.d.ts +33 -0
- package/dist/core/state.d.ts.map +1 -0
- package/dist/core/state.js +62 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/types.d.ts +71 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +18 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +301 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/compose-template.d.ts +8 -0
- package/dist/prompts/compose-template.d.ts.map +1 -0
- package/dist/prompts/compose-template.js +40 -0
- package/dist/prompts/compose-template.js.map +1 -0
- package/dist/prompts/interview-template.d.ts +24 -0
- package/dist/prompts/interview-template.d.ts.map +1 -0
- package/dist/prompts/interview-template.js +101 -0
- package/dist/prompts/interview-template.js.map +1 -0
- package/dist/ui/interview-ui.d.ts +14 -0
- package/dist/ui/interview-ui.d.ts.map +1 -0
- package/dist/ui/interview-ui.js +285 -0
- package/dist/ui/interview-ui.js.map +1 -0
- 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"}
|