@henryqw/pi-ask-question 0.1.7 → 0.1.9

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 CHANGED
@@ -41,7 +41,6 @@ pi remove npm:@henryqw/pi-ask-question
41
41
  ## Development
42
42
 
43
43
  ```bash
44
- npm test --workspace @henryqw/pi-ask-question
45
44
  npm run typecheck --workspace @henryqw/pi-ask-question
46
45
  npm run pack:check --workspace @henryqw/pi-ask-question
47
46
  ```
@@ -3,6 +3,7 @@ import {
3
3
  Editor,
4
4
  type EditorTheme,
5
5
  Key,
6
+ type KeyId,
6
7
  matchesKey,
7
8
  Text,
8
9
  visibleWidth,
@@ -18,7 +19,9 @@ interface QuestionOption {
18
19
  type DisplayOption = QuestionOption & { isOther?: boolean };
19
20
 
20
21
  const CUSTOM_OPTION_LABEL = "Something else.";
21
- const NUMBER_KEYS = ["1", "2", "3", "4"] as const;
22
+ // Models are told to omit "(Recommended)" from labels but don't always comply; normalize instead of duplicating.
23
+ const RECOMMENDED_SUFFIX = /\s*\(recommended\)\s*$/i;
24
+ const withRecommended = (label: string): string => `${label.replace(RECOMMENDED_SUFFIX, "")} (Recommended)`;
22
25
 
23
26
  interface QuestionDetails {
24
27
  question: string;
@@ -72,8 +75,7 @@ export default function askQuestionExtension(pi: ExtensionAPI): void {
72
75
  };
73
76
  }
74
77
  let validationError: string | undefined;
75
- if (suppliedOptions.length < 1 || suppliedOptions.length > 3) validationError = "1 to 3 options required";
76
- else if (!question) validationError = "Question must not be blank";
78
+ if (!question) validationError = "Question must not be blank";
77
79
  else if (suppliedOptions.some((option) => !option.label)) validationError = "Option labels must not be blank";
78
80
  else if (new Set(options.map((option) => option.toLowerCase())).size !== options.length) validationError = "Option labels must be unique";
79
81
  else if (options.some((option) => option.toLowerCase() === CUSTOM_OPTION_LABEL.toLowerCase())) validationError = `Option label "${CUSTOM_OPTION_LABEL}" is reserved`;
@@ -136,7 +138,7 @@ export default function askQuestionExtension(pi: ExtensionAPI): void {
136
138
  return;
137
139
  }
138
140
 
139
- const numberIndex = NUMBER_KEYS.findIndex((key, index) => index < allOptions.length && matchesKey(data, key));
141
+ const numberIndex = allOptions.findIndex((_, index) => matchesKey(data, `${index + 1}` as KeyId));
140
142
  if (numberIndex >= 0) {
141
143
  optionIndex = numberIndex;
142
144
  selectOption();
@@ -154,11 +156,10 @@ export default function askQuestionExtension(pi: ExtensionAPI): void {
154
156
  if (cachedLines) return cachedLines;
155
157
  const lines: string[] = [];
156
158
  const renderWidth = Math.max(1, width);
157
- const addWrapped = (text: string): void => { lines.push(...wrapTextWithAnsi(text, renderWidth)); };
158
159
  const addWrappedWithPrefix = (prefix: string, text: string): void => {
159
160
  const prefixWidth = visibleWidth(prefix);
160
161
  if (prefixWidth >= renderWidth) {
161
- addWrapped(prefix + text);
162
+ lines.push(...wrapTextWithAnsi(prefix + text, renderWidth));
162
163
  return;
163
164
  }
164
165
  const wrapped = wrapTextWithAnsi(text, renderWidth - prefixWidth);
@@ -175,7 +176,7 @@ export default function askQuestionExtension(pi: ExtensionAPI): void {
175
176
  const option = allOptions[index]!;
176
177
  const selected = index === optionIndex;
177
178
  const prefix = selected ? theme.fg("accent", "> ") : " ";
178
- const label = `${index + 1}. ${option.label}${index === 0 ? " (Recommended)" : ""}${option.isOther && editMode ? " ✎" : ""}`;
179
+ const label = `${index + 1}. ${index === 0 ? withRecommended(option.label) : option.label}${option.isOther && editMode ? " ✎" : ""}`;
179
180
  addWrappedWithPrefix(prefix, theme.fg(selected || (option.isOther && editMode) ? "accent" : "text", label));
180
181
  if (option.description) addWrappedWithPrefix(" ", theme.fg("muted", option.description));
181
182
  }
@@ -230,7 +231,7 @@ export default function askQuestionExtension(pi: ExtensionAPI): void {
230
231
  const options = Array.isArray(args.options) ? args.options : [];
231
232
  if (options.length) {
232
233
  const labels = options.map((option: QuestionOption) => option.label);
233
- const numbered = [...labels, CUSTOM_OPTION_LABEL].map((option, index) => `${index + 1}. ${option}${index === 0 ? " (Recommended)" : ""}`);
234
+ const numbered = [...labels, CUSTOM_OPTION_LABEL].map((option, index) => `${index + 1}. ${index === 0 ? withRecommended(option) : option}`);
234
235
  text += `\n${theme.fg("dim", ` Options: ${numbered.join(", ")}`)}`;
235
236
  }
236
237
  return new Text(text, 0, 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henryqw/pi-ask-question",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Ask Pi users one interactive question with choices or a custom answer.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -19,7 +19,6 @@
19
19
  "LICENSE"
20
20
  ],
21
21
  "scripts": {
22
- "test": "node --test test/*.test.ts",
23
22
  "test:manual": "pi --no-extensions -e ./extensions/ask-question.ts --tools ask_question --no-session \"We need storage for a small team app. Before making changes, ask me to choose storage.\"",
24
23
  "typecheck": "tsc --noEmit --allowImportingTsExtensions --target ES2022 --module NodeNext --moduleResolution NodeNext --skipLibCheck extensions/ask-question.ts",
25
24
  "pack:check": "npm pack --dry-run"