@ryan_nookpi/pi-extension-ask-user-question 0.3.4 → 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.
@@ -66,6 +66,18 @@ describe("ask-user-question/controller", () => {
66
66
  expect(empty.done).not.toHaveBeenCalled();
67
67
  });
68
68
 
69
+ it("invalidates cached lines when render width changes (terminal resize)", () => {
70
+ const setup = createController([{ id: "text", type: "text", prompt: "Explain" }]);
71
+ const wide = setup.controller.render(70);
72
+ const narrow = setup.controller.render(34);
73
+ expect(narrow).not.toBe(wide);
74
+ for (const line of narrow) {
75
+ expect(line.length).toBeLessThanOrEqual(34);
76
+ }
77
+ const narrowAgain = setup.controller.render(34);
78
+ expect(narrowAgain).toBe(narrow);
79
+ });
80
+
69
81
  it("handles text questions, submission, cancellation, and editor fallback submit", () => {
70
82
  const first = createController([{ id: "text", type: "text", prompt: "Explain", default: "seed" }]);
71
83
  expect(first.editor.getText()).toBe("seed");
package/controller.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Key, matchesKey } from "@mariozechner/pi-tui";
1
+ import { Key, matchesKey } from "@earendil-works/pi-tui";
2
2
 
3
3
  import { allRequiredAnswered, buildAnswers, optionCount, saveOtherAnswer, saveTextAnswer } from "./state.ts";
4
4
  import type { AnswerState, EditorAdapter, FormResult, NormalizedQuestion, RenderTheme } from "./types.ts";
@@ -37,6 +37,7 @@ export function createFormController(input: CreateFormControllerInput): FormCont
37
37
  let otherMode = false;
38
38
  let otherQuestionId: string | null = null;
39
39
  let cachedLines: string[] | undefined;
40
+ let cachedWidth: number | undefined;
40
41
 
41
42
  function getCurrentQuestion(): NormalizedQuestion | undefined {
42
43
  return questions[currentTab];
@@ -44,6 +45,7 @@ export function createFormController(input: CreateFormControllerInput): FormCont
44
45
 
45
46
  function refresh(): void {
46
47
  cachedLines = undefined;
48
+ cachedWidth = undefined;
47
49
  input.requestRender();
48
50
  }
49
51
 
@@ -116,7 +118,8 @@ export function createFormController(input: CreateFormControllerInput): FormCont
116
118
 
117
119
  return {
118
120
  render(width) {
119
- if (cachedLines) return cachedLines;
121
+ if (cachedLines && cachedWidth === width) return cachedLines;
122
+ cachedWidth = width;
120
123
  cachedLines = renderForm({
121
124
  title: input.title,
122
125
  description: input.description,
@@ -134,6 +137,7 @@ export function createFormController(input: CreateFormControllerInput): FormCont
134
137
  },
135
138
  invalidate() {
136
139
  cachedLines = undefined;
140
+ cachedWidth = undefined;
137
141
  },
138
142
  handleInput(data) {
139
143
  if (otherMode) {
package/form-ui.test.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
1
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
2
  import { describe, expect, it, vi } from "vitest";
3
3
 
4
4
  import { createEditorTheme, runAskUserQuestionForm } from "./form-ui.ts";
@@ -66,6 +66,32 @@ describe("ask-user-question/form-ui", () => {
66
66
  expect(result).toEqual({ title: "Title", questions, answers: [], cancelled: true });
67
67
  });
68
68
 
69
+ it("coerces unexpected native bridge answers into safe form results", async () => {
70
+ const questions = normalizeQuestions([
71
+ { id: "choice", type: "radio", prompt: "Pick", options: [{ value: "a", label: "A" }] },
72
+ { id: "single", type: "checkbox", prompt: "Single", options: [{ value: "solo", label: "Solo" }] },
73
+ { id: "missing", type: "checkbox", prompt: "Missing", options: [{ value: "x", label: "X" }] },
74
+ ]);
75
+ const askUserQuestion = vi.fn(async () => ({ choice: 123, single: "solo" }));
76
+ const ctx = {
77
+ hasUI: true,
78
+ ui: { askUserQuestion },
79
+ } as unknown as ExtensionContext;
80
+
81
+ const result = await runAskUserQuestionForm(ctx, {}, questions);
82
+
83
+ expect(result).toEqual({
84
+ title: undefined,
85
+ questions,
86
+ answers: [
87
+ { id: "choice", type: "radio", value: "", wasCustom: false },
88
+ { id: "single", type: "checkbox", value: ["solo"], wasCustom: false },
89
+ { id: "missing", type: "checkbox", value: [], wasCustom: false },
90
+ ],
91
+ cancelled: false,
92
+ });
93
+ });
94
+
69
95
  it("wires the custom UI factory to the controller", async () => {
70
96
  const questions = normalizeQuestions([{ id: "text", type: "text", prompt: "Explain", default: "seed" }]);
71
97
  let captured: FormResult | undefined;
package/form-ui.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
2
- import { Editor, type EditorTheme } from "@mariozechner/pi-tui";
1
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ import { Editor, type EditorTheme } from "@earendil-works/pi-tui";
3
3
 
4
4
  import { createFormController } from "./controller.ts";
5
5
  import { createAnswerState } from "./state.ts";
package/index.test.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
1
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
2
  import { describe, expect, it, vi } from "vitest";
3
3
 
4
4
  import { createExtensionApiMock } from "../../tests/mock-extension-api.ts";
package/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
2
 
3
3
  import { coerceAskUserQuestionParams } from "./coerce.ts";
4
4
  import { runAskUserQuestionForm } from "./form-ui.ts";
package/output.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Text } from "@mariozechner/pi-tui";
1
+ import { Text } from "@earendil-works/pi-tui";
2
2
 
3
3
  import { SYM } from "./constants.ts";
4
4
  import type { FormResult, Question, RenderTheme } from "./types.ts";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryan_nookpi/pi-extension-ask-user-question",
3
- "version": "0.3.4",
3
+ "version": "0.4.0",
4
4
  "description": "AskUserQuestion tool extension for pi.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -26,8 +26,8 @@
26
26
  ]
27
27
  },
28
28
  "peerDependencies": {
29
- "@mariozechner/pi-coding-agent": "*",
30
- "@mariozechner/pi-tui": "*",
29
+ "@earendil-works/pi-coding-agent": "*",
30
+ "@earendil-works/pi-tui": "*",
31
31
  "@sinclair/typebox": "*"
32
32
  },
33
33
  "publishConfig": {
package/schema.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { StringEnum } from "@mariozechner/pi-ai";
2
1
  import { Type } from "@sinclair/typebox";
3
2
 
4
3
  export const OptionSchema = Type.Object({
@@ -14,7 +13,7 @@ export const LooseOptionSchema = Type.Union([
14
13
 
15
14
  export const QuestionSchema = Type.Object({
16
15
  id: Type.Optional(Type.String({ description: "질문 고유 식별자. 생략 시 q1, q2...로 자동 생성" })),
17
- type: StringEnum(["radio", "checkbox", "text"] as const, {
16
+ type: Type.Union([Type.Literal("radio"), Type.Literal("checkbox"), Type.Literal("text")], {
18
17
  description: "질문 유형: radio(단일 선택), checkbox(복수 선택), text(자유 입력)",
19
18
  }),
20
19
  prompt: Type.Optional(Type.String({ description: "사용자에게 표시할 질문 문구" })),
package/view.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { truncateToWidth, wrapTextWithAnsi } from "@mariozechner/pi-tui";
1
+ import { truncateToWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
2
2
 
3
3
  import { SYM } from "./constants.ts";
4
4
  import { allRequiredAnswered, isAnswered } from "./state.ts";