Commit 4768d4b8d34e
Changed files (1)
dots
pi
agent
extensions
dots/pi/agent/extensions/ask-user.ts
@@ -0,0 +1,93 @@
+/**
+ * Ask User Tool
+ *
+ * Allows the AI to ask the user a question and wait for their response.
+ * Similar to Claude Code's "ask" tool. Supports both free-form text input
+ * and optional multiple-choice suggestions.
+ *
+ * Usage by the AI:
+ * ask_user({ question: "Which database should I use?" })
+ * ask_user({ question: "How should I handle errors?", suggestions: ["retry", "fail fast", "log and continue"] })
+ */
+
+import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
+import { Type } from "@sinclair/typebox";
+
+export default function (pi: ExtensionAPI) {
+ pi.registerTool({
+ name: "ask_user",
+ label: "Ask User",
+ description:
+ "Ask the user a question and wait for their response. Use when you need clarification, a decision, or user input to proceed. Supports optional suggestions the user can pick from or ignore.",
+ promptGuidelines: [
+ "Use ask_user when you need user input, clarification, or a decision before proceeding.",
+ "Prefer ask_user over guessing when the choice significantly affects the outcome.",
+ "Keep questions concise and specific. Provide suggestions when there are obvious options.",
+ ],
+ parameters: Type.Object({
+ question: Type.String({ description: "The question to ask the user" }),
+ suggestions: Type.Optional(
+ Type.Array(Type.String(), {
+ description:
+ "Optional list of suggested answers. User can pick one or type their own response.",
+ }),
+ ),
+ }),
+
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
+ if (!ctx.hasUI) {
+ return {
+ content: [
+ {
+ type: "text",
+ text: "Error: Cannot ask user — running in non-interactive mode",
+ },
+ ],
+ details: { question: params.question, answer: null },
+ };
+ }
+
+ const { question, suggestions } = params;
+ const hasSuggestions = suggestions && suggestions.length > 0;
+
+ let answer: string | undefined;
+
+ if (hasSuggestions) {
+ // Show suggestions as selectable options + free-form input
+ const freeFormOption = "✎ Type a different answer…";
+ const options = [...suggestions, freeFormOption];
+ const choice = await ctx.ui.select(question, options);
+
+ if (choice === undefined) {
+ // User cancelled
+ return {
+ content: [{ type: "text", text: "User cancelled — did not answer the question." }],
+ details: { question, suggestions, answer: null },
+ };
+ }
+
+ if (choice === freeFormOption) {
+ // User chose to type their own answer
+ answer = await ctx.ui.input(question);
+ } else {
+ answer = choice;
+ }
+ } else {
+ // Free-form text input
+ answer = await ctx.ui.input(question);
+ }
+
+ if (answer === undefined || answer.trim() === "") {
+ return {
+ content: [{ type: "text", text: "User cancelled — did not answer the question." }],
+ details: { question, suggestions, answer: null },
+ };
+ }
+
+ return {
+ content: [{ type: "text", text: `User answered: ${answer}` }],
+ details: { question, suggestions, answer },
+ };
+ },
+ });
+}