fix question option schema (#846)

This commit is contained in:
Ted 2026-01-19 14:57:23 +01:00 committed by GitHub
parent 4cc1c96495
commit f711340136
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -22,28 +22,17 @@ interface QuestionDetails {
wasCustom?: boolean; wasCustom?: boolean;
} }
// Support both simple strings and objects with descriptions // Options with labels and optional descriptions
const OptionSchema = Type.Union([ const OptionSchema = Type.Object({
Type.String(),
Type.Object({
label: Type.String({ description: "Display label for the option" }), label: Type.String({ description: "Display label for the option" }),
description: Type.Optional(Type.String({ description: "Optional description shown below label" })), description: Type.Optional(Type.String({ description: "Optional description shown below label" })),
}), });
]);
const QuestionParams = Type.Object({ const QuestionParams = Type.Object({
question: Type.String({ description: "The question to ask the user" }), question: Type.String({ description: "The question to ask the user" }),
options: Type.Array(OptionSchema, { description: "Options for the user to choose from" }), options: Type.Array(OptionSchema, { description: "Options for the user to choose from" }),
}); });
// Normalize option to { label, description? }
function normalizeOption(opt: string | { label: string; description?: string }): OptionWithDesc {
if (typeof opt === "string") {
return { label: opt };
}
return opt;
}
export default function question(pi: ExtensionAPI) { export default function question(pi: ExtensionAPI) {
pi.registerTool({ pi.registerTool({
name: "question", name: "question",
@ -57,7 +46,7 @@ export default function question(pi: ExtensionAPI) {
content: [{ type: "text", text: "Error: UI not available (running in non-interactive mode)" }], content: [{ type: "text", text: "Error: UI not available (running in non-interactive mode)" }],
details: { details: {
question: params.question, question: params.question,
options: params.options.map((o) => (typeof o === "string" ? o : o.label)), options: params.options.map((o) => o.label),
answer: null, answer: null,
} as QuestionDetails, } as QuestionDetails,
}; };
@ -70,9 +59,7 @@ export default function question(pi: ExtensionAPI) {
}; };
} }
// Normalize options const allOptions: DisplayOption[] = [...params.options, { label: "Type something.", isOther: true }];
const normalizedOptions = params.options.map(normalizeOption);
const allOptions: DisplayOption[] = [...normalizedOptions, { label: "Type something.", isOther: true }];
const result = await ctx.ui.custom<{ answer: string; wasCustom: boolean; index?: number } | null>( const result = await ctx.ui.custom<{ answer: string; wasCustom: boolean; index?: number } | null>(
(tui, theme, _kb, done) => { (tui, theme, _kb, done) => {
@ -209,7 +196,7 @@ export default function question(pi: ExtensionAPI) {
); );
// Build simple options list for details // Build simple options list for details
const simpleOptions = normalizedOptions.map((o) => o.label); const simpleOptions = params.options.map((o) => o.label);
if (!result) { if (!result) {
return { return {
@ -244,7 +231,7 @@ export default function question(pi: ExtensionAPI) {
let text = theme.fg("toolTitle", theme.bold("question ")) + theme.fg("muted", args.question); let text = theme.fg("toolTitle", theme.bold("question ")) + theme.fg("muted", args.question);
const opts = Array.isArray(args.options) ? args.options : []; const opts = Array.isArray(args.options) ? args.options : [];
if (opts.length) { if (opts.length) {
const labels = opts.map((o: string | { label: string }) => (typeof o === "string" ? o : o.label)); const labels = opts.map((o: OptionWithDesc) => o.label);
const numbered = [...labels, "Type something."].map((o, i) => `${i + 1}. ${o}`); const numbered = [...labels, "Type something."].map((o, i) => `${i + 1}. ${o}`);
text += `\n${theme.fg("dim", ` Options: ${numbered.join(", ")}`)}`; text += `\n${theme.fg("dim", ` Options: ${numbered.join(", ")}`)}`;
} }