Files
oh-my-pi/packages/coding-agent/examples/custom-tools/question/index.ts
T
can1357 2be354322d chore: renamed package scope to @oh-my-pi for consistent branding
- Renamed npm package scope from @mariozechner to @oh-my-pi across all packages for consistent branding.
- Removed packages/pods directory and all related pod management functionality.
- Updated import paths and module declarations throughout codebase to reflect new package names.
- Reformatted code with consistent trailing comma removal and ternary operator alignment.
2026-01-02 18:08:23 +01:00

85 lines
2.4 KiB
TypeScript

/**
* Question Tool - Let the LLM ask the user a question with options
*/
import type { CustomTool, CustomToolFactory } from "@oh-my-pi/pi-coding-agent";
interface QuestionDetails {
question: string;
options: string[];
answer: string | null;
}
const factory: CustomToolFactory = (pi) => {
const { Type } = pi.typebox;
const { Text } = pi.pi;
const QuestionParams = Type.Object({
question: Type.String({ description: "The question to ask the user" }),
options: Type.Array(Type.String(), { description: "Options for the user to choose from" }),
});
const tool: CustomTool<typeof QuestionParams, QuestionDetails> = {
name: "question",
label: "Question",
description: "Ask the user a question and let them pick from options. Use when you need user input to proceed.",
parameters: QuestionParams,
async execute(_toolCallId, params, _onUpdate, _ctx, _signal) {
if (!pi.hasUI) {
return {
content: [{ type: "text", text: "Error: UI not available (running in non-interactive mode)" }],
details: { question: params.question, options: params.options, answer: null },
};
}
if (params.options.length === 0) {
return {
content: [{ type: "text", text: "Error: No options provided" }],
details: { question: params.question, options: [], answer: null },
};
}
const answer = await pi.ui.select(params.question, params.options);
if (answer === undefined) {
return {
content: [{ type: "text", text: "User cancelled the selection" }],
details: { question: params.question, options: params.options, answer: null },
};
}
return {
content: [{ type: "text", text: `User selected: ${answer}` }],
details: { question: params.question, options: params.options, answer },
};
},
renderCall(args, theme) {
let text = theme.fg("toolTitle", theme.bold("question ")) + theme.fg("muted", args.question);
if (args.options?.length) {
text += `\n${theme.fg("dim", ` Options: ${args.options.join(", ")}`)}`;
}
return new Text(text, 0, 0);
},
renderResult(result, _options, theme) {
const { details } = result;
if (!details) {
const text = result.content[0];
return new Text(text?.type === "text" ? text.text : "", 0, 0);
}
if (details.answer === null) {
return new Text(theme.fg("warning", "Cancelled"), 0, 0);
}
return new Text(theme.fg("success", "✓ ") + theme.fg("accent", details.answer), 0, 0);
},
};
return tool;
};
export default factory;