pi-interview 0.8.0 → 0.8.1

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/form/script.js CHANGED
@@ -738,6 +738,12 @@
738
738
  return askModels[0]?.provider || "";
739
739
  }
740
740
 
741
+ const ASK_DEPTH_OPTIONS = [
742
+ { key: "quick", label: "Quick" },
743
+ { key: "standard", label: "Standard" },
744
+ { key: "deep", label: "Deep" },
745
+ ];
746
+
741
747
  function createDefaultActiveInsight(questionId, optionKey) {
742
748
  const selectedModel = askModels.some((model) => model.value === defaultAskModel)
743
749
  ? defaultAskModel
@@ -754,6 +760,7 @@
754
760
  advancedOpen: false,
755
761
  selectedProvider: parsed.provider || getFirstProvider(),
756
762
  selectedModel,
763
+ selectedDepth: "standard",
757
764
  abortController: null,
758
765
  };
759
766
  }
@@ -1021,6 +1028,7 @@
1021
1028
  optionKey,
1022
1029
  prompt,
1023
1030
  model: modelOverride && modelOverride !== defaultAskModel ? modelOverride : null,
1031
+ depth: active.selectedDepth || "standard",
1024
1032
  }),
1025
1033
  signal: active.abortController.signal,
1026
1034
  });
@@ -1180,22 +1188,27 @@
1180
1188
  const metaRow = document.createElement("div");
1181
1189
  metaRow.className = "option-insight-meta-row";
1182
1190
 
1183
- const model = document.createElement("div");
1184
- model.className = "option-insight-model";
1185
- model.textContent = getInsightModelLabel(active);
1186
- metaRow.appendChild(model);
1187
-
1188
- const advancedToggle = document.createElement("button");
1189
- advancedToggle.type = "button";
1190
- advancedToggle.className = "option-insight-advanced-toggle";
1191
- advancedToggle.textContent = active.advancedOpen ? "Advanced model settings ▾" : "Advanced model settings ▸";
1192
- advancedToggle.addEventListener("click", (event) => {
1191
+ const modelBadge = document.createElement("button");
1192
+ modelBadge.type = "button";
1193
+ modelBadge.className = "option-insight-model-badge";
1194
+ const badgeLabel = document.createElement("span");
1195
+ badgeLabel.className = "badge-label";
1196
+ badgeLabel.textContent = "Model";
1197
+ const badgeValue = document.createElement("span");
1198
+ badgeValue.textContent = getInsightModelLabel(active);
1199
+ const badgeCaret = document.createElement("span");
1200
+ badgeCaret.className = "badge-caret";
1201
+ badgeCaret.textContent = active.advancedOpen ? "▾" : "▸";
1202
+ modelBadge.appendChild(badgeLabel);
1203
+ modelBadge.appendChild(badgeValue);
1204
+ modelBadge.appendChild(badgeCaret);
1205
+ modelBadge.addEventListener("click", (event) => {
1193
1206
  event.preventDefault();
1194
1207
  event.stopPropagation();
1195
1208
  active.advancedOpen = !active.advancedOpen;
1196
1209
  replaceQuestionOptionList(question, getQuestionValue(question), optionKey);
1197
1210
  });
1198
- metaRow.appendChild(advancedToggle);
1211
+ metaRow.appendChild(modelBadge);
1199
1212
 
1200
1213
  panel.appendChild(metaRow);
1201
1214
 
@@ -1203,38 +1216,63 @@
1203
1216
  const advanced = document.createElement("div");
1204
1217
  advanced.className = "option-insight-advanced";
1205
1218
 
1206
- const providerSelect = document.createElement("select");
1207
- providerSelect.className = "option-insight-select";
1208
1219
  const providers = [...new Set(askModels.map((model) => model.provider))];
1220
+ const providerRow = document.createElement("div");
1221
+ providerRow.className = "option-insight-provider-row";
1209
1222
  providers.forEach((provider) => {
1210
- const option = document.createElement("option");
1211
- option.value = provider;
1212
- option.textContent = providerLabel(provider);
1213
- providerSelect.appendChild(option);
1214
- });
1215
- providerSelect.value = active.selectedProvider || providers[0] || "";
1216
- providerSelect.addEventListener("change", () => {
1217
- active.selectedProvider = providerSelect.value;
1218
- const providerModels = getModelsForProvider(active.selectedProvider);
1219
- active.selectedModel = providerModels[0]?.value || null;
1220
- replaceQuestionOptionList(question, getQuestionValue(question), optionKey);
1223
+ const btn = document.createElement("button");
1224
+ btn.type = "button";
1225
+ const isSelected = (active.selectedProvider || providers[0] || "") === provider;
1226
+ btn.className = "option-insight-provider-btn" + (isSelected ? " is-selected" : "");
1227
+ btn.textContent = providerLabel(provider);
1228
+ btn.addEventListener("click", (event) => {
1229
+ event.preventDefault();
1230
+ event.stopPropagation();
1231
+ active.selectedProvider = provider;
1232
+ const providerModels = getModelsForProvider(provider);
1233
+ active.selectedModel = providerModels[0]?.value || null;
1234
+ replaceQuestionOptionList(question, getQuestionValue(question), optionKey);
1235
+ });
1236
+ providerRow.appendChild(btn);
1221
1237
  });
1222
- advanced.appendChild(providerSelect);
1238
+ advanced.appendChild(providerRow);
1223
1239
 
1224
- const modelSelect = document.createElement("select");
1225
- modelSelect.className = "option-insight-select";
1226
1240
  const providerModels = getModelsForProvider(active.selectedProvider);
1241
+ const modelRow = document.createElement("div");
1242
+ modelRow.className = "option-insight-model-row";
1227
1243
  providerModels.forEach((modelOption) => {
1228
- const option = document.createElement("option");
1229
- option.value = modelOption.value;
1230
- option.textContent = modelOption.label;
1231
- modelSelect.appendChild(option);
1244
+ const btn = document.createElement("button");
1245
+ btn.type = "button";
1246
+ const isSelected = active.selectedModel === modelOption.value;
1247
+ btn.className = "option-insight-model-btn" + (isSelected ? " is-selected" : "");
1248
+ btn.textContent = modelOption.label;
1249
+ btn.addEventListener("click", (event) => {
1250
+ event.preventDefault();
1251
+ event.stopPropagation();
1252
+ active.selectedModel = modelOption.value;
1253
+ replaceQuestionOptionList(question, getQuestionValue(question), optionKey);
1254
+ });
1255
+ modelRow.appendChild(btn);
1232
1256
  });
1233
- modelSelect.value = active.selectedModel || providerModels[0]?.value || "";
1234
- modelSelect.addEventListener("change", () => {
1235
- active.selectedModel = modelSelect.value;
1257
+ advanced.appendChild(modelRow);
1258
+
1259
+ const depthRow = document.createElement("div");
1260
+ depthRow.className = "option-insight-depth-row";
1261
+ ASK_DEPTH_OPTIONS.forEach((depth) => {
1262
+ const btn = document.createElement("button");
1263
+ btn.type = "button";
1264
+ const isSelected = active.selectedDepth === depth.key;
1265
+ btn.className = "option-insight-depth-btn" + (isSelected ? " is-selected" : "");
1266
+ btn.textContent = depth.label;
1267
+ btn.addEventListener("click", (event) => {
1268
+ event.preventDefault();
1269
+ event.stopPropagation();
1270
+ active.selectedDepth = depth.key;
1271
+ replaceQuestionOptionList(question, getQuestionValue(question), optionKey);
1272
+ });
1273
+ depthRow.appendChild(btn);
1236
1274
  });
1237
- advanced.appendChild(modelSelect);
1275
+ advanced.appendChild(depthRow);
1238
1276
 
1239
1277
  panel.appendChild(advanced);
1240
1278
  }
@@ -1255,6 +1293,20 @@
1255
1293
 
1256
1294
  panel.appendChild(actions);
1257
1295
 
1296
+ if (active.loading && !active.result) {
1297
+ const loading = document.createElement("div");
1298
+ loading.className = "option-insight-loading";
1299
+ const spinner = document.createElement("span");
1300
+ spinner.className = "option-insight-spinner";
1301
+ spinner.textContent = "Thinking";
1302
+ loading.appendChild(spinner);
1303
+ const dots = document.createElement("span");
1304
+ dots.className = "option-insight-dots";
1305
+ dots.textContent = "...";
1306
+ loading.appendChild(dots);
1307
+ panel.appendChild(loading);
1308
+ }
1309
+
1258
1310
  if (active.error) {
1259
1311
  const error = document.createElement("div");
1260
1312
  error.className = "option-insight-error";
package/form/styles.css CHANGED
@@ -2018,7 +2018,6 @@ button {
2018
2018
 
2019
2019
  .option-insight-meta-row {
2020
2020
  display: flex;
2021
- justify-content: space-between;
2022
2021
  align-items: center;
2023
2022
  gap: 10px;
2024
2023
  }
@@ -2029,6 +2028,39 @@ button {
2029
2028
  color: var(--fg-muted);
2030
2029
  }
2031
2030
 
2031
+ .option-insight-model-badge {
2032
+ font-family: var(--font-body);
2033
+ font-size: 11px;
2034
+ font-weight: 600;
2035
+ padding: 5px 12px;
2036
+ border-radius: 999px;
2037
+ border: 1px solid var(--border-muted);
2038
+ background: var(--bg-elevated);
2039
+ color: var(--fg-muted);
2040
+ cursor: pointer;
2041
+ transition: border-color 0.12s, background 0.12s, color 0.12s;
2042
+ display: inline-flex;
2043
+ align-items: center;
2044
+ gap: 6px;
2045
+ }
2046
+
2047
+ .option-insight-model-badge:hover {
2048
+ color: var(--fg);
2049
+ border-color: var(--card-accent, var(--accent));
2050
+ background: color-mix(in srgb, var(--card-accent, var(--accent)) 8%, var(--bg-elevated));
2051
+ }
2052
+
2053
+ .option-insight-model-badge .badge-caret {
2054
+ font-size: 14px;
2055
+ opacity: 0.85;
2056
+ line-height: 1;
2057
+ }
2058
+
2059
+ .option-insight-model-badge .badge-label {
2060
+ font-weight: 500;
2061
+ opacity: 0.55;
2062
+ }
2063
+
2032
2064
  .option-insight-chips {
2033
2065
  display: flex;
2034
2066
  flex-wrap: wrap;
@@ -2066,8 +2098,7 @@ button {
2066
2098
  background: color-mix(in srgb, var(--card-accent, var(--accent)) 10%, transparent);
2067
2099
  }
2068
2100
 
2069
- .option-insight-input,
2070
- .option-insight-select {
2101
+ .option-insight-input {
2071
2102
  width: 100%;
2072
2103
  border: 1px solid var(--border-muted);
2073
2104
  border-radius: 12px;
@@ -2076,23 +2107,129 @@ button {
2076
2107
  color: var(--fg);
2077
2108
  font-family: var(--font-body);
2078
2109
  font-size: 13px;
2079
- }
2080
-
2081
- .option-insight-input {
2082
2110
  min-height: 72px;
2083
2111
  resize: vertical;
2084
2112
  }
2085
2113
 
2086
- .option-insight-advanced-toggle {
2087
- padding: 0;
2088
- border: none;
2114
+ .option-insight-advanced {
2115
+ display: flex;
2116
+ flex-direction: column;
2117
+ gap: 10px;
2118
+ }
2119
+
2120
+ .option-insight-provider-row {
2121
+ display: flex;
2122
+ flex-wrap: wrap;
2123
+ gap: 6px;
2124
+ }
2125
+
2126
+ .option-insight-provider-btn {
2127
+ font-family: var(--font-body);
2128
+ font-size: 11px;
2129
+ font-weight: 600;
2130
+ padding: 4px 10px;
2131
+ border-radius: 999px;
2132
+ border: 1px solid var(--border-muted);
2133
+ background: transparent;
2089
2134
  color: var(--fg-muted);
2135
+ cursor: pointer;
2136
+ transition: border-color 0.12s, background 0.12s, color 0.12s;
2090
2137
  }
2091
2138
 
2092
- .option-insight-advanced {
2093
- display: grid;
2094
- grid-template-columns: 1fr 1fr;
2095
- gap: 8px;
2139
+ .option-insight-provider-btn:hover {
2140
+ color: var(--fg);
2141
+ border-color: var(--accent);
2142
+ }
2143
+
2144
+ .option-insight-provider-btn.is-selected {
2145
+ box-shadow: inset 0 -2px 0 0 var(--card-accent, var(--accent));
2146
+ border-color: var(--card-accent, var(--accent));
2147
+ color: var(--card-accent, var(--accent));
2148
+ }
2149
+
2150
+ .option-insight-model-row {
2151
+ display: flex;
2152
+ flex-wrap: wrap;
2153
+ gap: 6px;
2154
+ }
2155
+
2156
+ .option-insight-model-btn {
2157
+ font-family: var(--font-body);
2158
+ font-size: 11px;
2159
+ font-weight: 600;
2160
+ padding: 4px 10px;
2161
+ border-radius: 999px;
2162
+ border: 1px solid var(--border-muted);
2163
+ background: transparent;
2164
+ color: var(--fg-muted);
2165
+ cursor: pointer;
2166
+ transition: border-color 0.12s, background 0.12s, color 0.12s;
2167
+ }
2168
+
2169
+ .option-insight-model-btn:hover {
2170
+ color: var(--fg);
2171
+ border-color: var(--accent);
2172
+ }
2173
+
2174
+ .option-insight-model-btn.is-selected {
2175
+ border-color: color-mix(in srgb, var(--card-accent, var(--accent)) 55%, transparent);
2176
+ color: var(--card-accent, var(--accent));
2177
+ background: color-mix(in srgb, var(--card-accent, var(--accent)) 10%, transparent);
2178
+ }
2179
+
2180
+ .option-insight-depth-row {
2181
+ display: flex;
2182
+ flex-wrap: wrap;
2183
+ gap: 6px;
2184
+ padding-top: 6px;
2185
+ border-top: 1px solid var(--border-muted);
2186
+ }
2187
+
2188
+ .option-insight-depth-btn {
2189
+ font-family: var(--font-body);
2190
+ font-size: 11px;
2191
+ font-weight: 600;
2192
+ padding: 4px 10px;
2193
+ border-radius: 999px;
2194
+ border: 1px solid var(--border-muted);
2195
+ background: transparent;
2196
+ color: var(--fg-muted);
2197
+ cursor: pointer;
2198
+ transition: border-color 0.12s, background 0.12s, color 0.12s;
2199
+ }
2200
+
2201
+ .option-insight-depth-btn:hover {
2202
+ color: var(--fg);
2203
+ border-color: var(--accent);
2204
+ }
2205
+
2206
+ .option-insight-depth-btn.is-selected {
2207
+ box-shadow: inset 0 -2px 0 0 var(--card-accent, var(--accent));
2208
+ border-color: var(--card-accent, var(--accent));
2209
+ color: var(--card-accent, var(--accent));
2210
+ }
2211
+
2212
+ .option-insight-loading {
2213
+ display: flex;
2214
+ align-items: center;
2215
+ gap: 4px;
2216
+ padding: 12px 0;
2217
+ font-size: 13px;
2218
+ color: var(--fg-muted);
2219
+ }
2220
+
2221
+ .option-insight-spinner {
2222
+ font-weight: 600;
2223
+ color: var(--card-accent, var(--accent));
2224
+ }
2225
+
2226
+ .option-insight-dots {
2227
+ animation: option-insight-pulse 1.2s ease-in-out infinite;
2228
+ }
2229
+
2230
+ @keyframes option-insight-pulse {
2231
+ 0%, 100% { opacity: 0.3; }
2232
+ 50% { opacity: 1; }
2096
2233
  }
2097
2234
 
2098
2235
  .option-insight-actions,
@@ -2210,10 +2347,6 @@ button {
2210
2347
  align-items: flex-start;
2211
2348
  flex-direction: column;
2212
2349
  }
2213
-
2214
- .option-insight-advanced {
2215
- grid-template-columns: 1fr;
2216
- }
2217
2350
  }
2218
2351
 
2219
2352
  /* Side-by-side layout */
package/index.ts CHANGED
@@ -302,8 +302,9 @@ export function selectGenerateModels<T extends GenerateModelCandidate>(
302
302
  return { primary: availableModels[0] ?? null, fallback: null };
303
303
  }
304
304
 
305
- function buildAskModelsData(
305
+ export function buildAskModelsData(
306
306
  availableModels: Model<Api>[],
307
+ currentModel: Model<Api> | null,
307
308
  primaryModel: Model<Api> | null,
308
309
  fallbackModel: Model<Api> | null,
309
310
  ): AskModelOption[] {
@@ -321,16 +322,15 @@ function buildAskModelsData(
321
322
  });
322
323
  };
323
324
 
325
+ addModel(currentModel);
324
326
  addModel(primaryModel);
325
327
  addModel(fallbackModel);
326
- for (const model of availableModels) {
327
- addModel(model);
328
+ for (const modelRef of PREFERRED_GENERATE_MODELS) {
329
+ const preferredModel = findModelByRef(availableModels, modelRef);
330
+ addModel(preferredModel);
328
331
  }
329
332
 
330
- return models.sort((a, b) => {
331
- if (a.provider !== b.provider) return a.provider.localeCompare(b.provider);
332
- return a.label.localeCompare(b.label);
333
- });
333
+ return models;
334
334
  }
335
335
 
336
336
  export function extractGenerateResponseText(
@@ -815,7 +815,7 @@ export default function (pi: ExtensionAPI) {
815
815
  ctx.model ?? null,
816
816
  availableGenerateModels,
817
817
  );
818
- const askModels = buildAskModelsData(availableGenerateModels, generateModel, fallbackGenerateModel);
818
+ const askModels = buildAskModelsData(availableGenerateModels, ctx.model ?? null, generateModel, fallbackGenerateModel);
819
819
  const defaultAskModel = generateModel ? formatModelRef(generateModel) : null;
820
820
 
821
821
  // Expand ~ in snapshotDir if present
@@ -1112,15 +1112,21 @@ export default function (pi: ExtensionAPI) {
1112
1112
  return selectedModel;
1113
1113
  };
1114
1114
 
1115
- onOptionInsight = async (questionId, option, prompt, modelOverride, generateSignal) => {
1115
+ onOptionInsight = async (questionId, option, prompt, modelOverride, depth, generateSignal) => {
1116
1116
  const question = questionsData.questions.find((q) => q.id === questionId);
1117
1117
  if (!question) throw new Error(`Unknown question: ${questionId}`);
1118
1118
  const optionText = getOptionLabel(option);
1119
1119
  const optionContent = typeof option === "string" ? null : option.content;
1120
1120
 
1121
+ const depthInstructions = {
1122
+ quick: "Keep the analysis very brief: a one-sentence summary and at most one bullet point.",
1123
+ standard: "Be concrete and concise. A short summary and a few bullet points.",
1124
+ deep: "Provide a thorough analysis: detailed summary, multiple bullet points covering tradeoffs, risks, and edge cases.",
1125
+ };
1126
+
1121
1127
  const questionPrompt = [
1122
1128
  "Analyze this single interview answer option.",
1123
- "Be concrete and concise.",
1129
+ depthInstructions[depth as keyof typeof depthInstructions] || depthInstructions.standard,
1124
1130
  "Explain what is good or risky about the option, and suggest a rewrite only if it would materially improve clarity.",
1125
1131
  "Return ONLY JSON with summary, bullets, and optional suggestedText.",
1126
1132
  "",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-interview",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Interactive interview form extension for pi coding agent",
5
5
  "author": "Nico Bailon",
6
6
  "license": "MIT",
package/server.ts CHANGED
@@ -250,6 +250,7 @@ export interface InterviewServerCallbacks {
250
250
  option: OptionValue,
251
251
  prompt: string,
252
252
  modelOverride: string | null,
253
+ depth: string,
253
254
  signal: AbortSignal,
254
255
  ) => Promise<OptionInsightResult>;
255
256
  }
@@ -1932,6 +1933,7 @@ export async function startInterviewServer(
1932
1933
  optionKey?: string;
1933
1934
  prompt?: string;
1934
1935
  model?: string | null;
1936
+ depth?: string;
1935
1937
  };
1936
1938
 
1937
1939
  if (typeof payload.questionId !== "string") {
@@ -1973,6 +1975,7 @@ export async function startInterviewServer(
1973
1975
  option,
1974
1976
  payload.prompt.trim(),
1975
1977
  typeof payload.model === "string" ? payload.model : null,
1978
+ typeof payload.depth === "string" ? payload.depth : "standard",
1976
1979
  controller.signal,
1977
1980
  );
1978
1981
  sendJson(res, 200, { ok: true, optionText, ...result });