@pikaa-ai/pikaa 0.2.3 → 0.2.5

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/bin/pikaa.js CHANGED
@@ -105,14 +105,15 @@ async function downloadBinary(url, dest, redirects = 0) {
105
105
  }
106
106
 
107
107
  async function main() {
108
- const existing = findExistingBinary();
109
- if (existing) {
110
- launchBinary(existing);
108
+ // 1. If local bun and dist/cli.js are available in the package, run immediately
109
+ if (tryLaunchWithBun()) {
111
110
  return;
112
111
  }
113
112
 
114
- // If local bun and dist/cli.js are available, run immediately with 0 download
115
- if (tryLaunchWithBun()) {
113
+ // 2. Otherwise use existing native binary if present
114
+ const existing = findExistingBinary();
115
+ if (existing) {
116
+ launchBinary(existing);
116
117
  return;
117
118
  }
118
119
 
package/dist/cli.js CHANGED
@@ -468,12 +468,17 @@ function buildSystemPrompt(params) {
468
468
  const sections = [];
469
469
  sections.push(params.basePrompt || "You are Groupy, an expert autonomous AI coding assistant. You think step-by-step, act surgically, and write clean, correct code.");
470
470
  sections.push([
471
- "## Operational Guidelines",
472
- "1. Think before acting. Understand the task and the code it touches before making edits.",
473
- "2. Simplicity first: write the minimum code that solves the problem. No unnecessary abstractions.",
474
- "3. Use `apply_patch` for surgical code replacements rather than overwriting whole files.",
475
- "4. Execute shell commands with `shell` to inspect environment, run tests, or build targets.",
476
- "5. Always verify your changes with tests or execution checks."
471
+ "## Editing Constraints & Guidelines",
472
+ "- Use `apply_patch` for surgical single-file edits. TargetContent must match existing file content exactly.",
473
+ "- Use `write_file` for creating new files or when completely replacing the full content of a file.",
474
+ "- Use `read_file` to inspect files and `grep_search` / `find_files` to discover symbols and locate files across the project.",
475
+ "- NEVER create temporary scripts, scratch files, or chunk files (e.g. `_tmp_*.ps1`, `_tmp_*.txt`, `split_*.py`) in the workspace to manipulate, split, or read files.",
476
+ "- NEVER execute shell or PowerShell scripts as a workaround for reading, writing, or editing text files.",
477
+ "- Use the `shell` tool ONLY for running tests, build targets, package installations, or checking environment/git status.",
478
+ "- When user requirements are ambiguous or require architectural decisions, use `request_user_input` or `ask_question` to present 2-4 clear options. Prefix your recommended choice with `(Recommended)` (e.g. `['(Recommended) Option A', 'Option B']`).",
479
+ "- You may be in a dirty git worktree. NEVER revert existing changes made by the user.",
480
+ "- NEVER use destructive commands like `git reset --hard` or `git checkout --`.",
481
+ "- Be concise, direct, and act surgically. Write clean, correct code with minimal necessary modifications."
477
482
  ].join(`
478
483
  `));
479
484
  if (params.memoriesPrompt) {
@@ -640,6 +645,15 @@ async function runTurn(session, turnContext, input) {
640
645
  description,
641
646
  command
642
647
  });
648
+ },
649
+ requestInput: async (question, options) => {
650
+ const questionId = `quest_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
651
+ return session.requestUserQuestion({
652
+ questionId,
653
+ turnId,
654
+ question,
655
+ options
656
+ });
643
657
  }
644
658
  });
645
659
  const functionOutputItem = {
@@ -769,7 +783,7 @@ class ExecPolicy {
769
783
  }
770
784
  initDefaultRules() {
771
785
  this.addRule(/^(git\s+(status|log|diff|branch|show|rev-parse))/i, "allow", "Safe git query");
772
- this.addRule(/^(ls|dir|cat|grep|rg|find|pwd|echo|head|tail|wc|which|where)\b/i, "allow", "Safe read-only shell command");
786
+ this.addRule(/^(ls|dir|cat|type|grep|rg|find|pwd|echo|head|tail|wc|which|where)\b/i, "allow", "Safe read-only shell command");
773
787
  this.addRule(/^(bun\s+(test|--version|-v)|npm\s+(test|--version|-v)|node\s+-v)\b/i, "allow", "Testing & runtime check");
774
788
  this.addRule(/^(rm|del|rmdir|format|mkfs)\b/i, "prompt", "Destructive file removal");
775
789
  this.addRule(/^(git\s+(push|reset\s+--hard|clean\s+-fd|rebase))\b/i, "prompt", "Destructive git operation");
@@ -811,6 +825,7 @@ class Session {
811
825
  status = "idle";
812
826
  eventListeners = [];
813
827
  pendingApprovals = new Map;
828
+ pendingUserQuestions = new Map;
814
829
  submissionResolvers = [];
815
830
  submissionQueue = [];
816
831
  isTerminated = false;
@@ -926,6 +941,35 @@ class Session {
926
941
  resolver(approved);
927
942
  }
928
943
  }
944
+ requestUserQuestion(params) {
945
+ this.emitEvent({
946
+ type: "UserQuestionRequired",
947
+ questionId: params.questionId,
948
+ turnId: params.turnId,
949
+ question: params.question,
950
+ options: params.options
951
+ });
952
+ this.emitEvent({
953
+ type: "StatusChanged",
954
+ status: "waiting_user_input"
955
+ });
956
+ return new Promise((resolve2) => {
957
+ this.pendingUserQuestions.set(params.questionId, (answer) => {
958
+ this.emitEvent({
959
+ type: "StatusChanged",
960
+ status: "running"
961
+ });
962
+ resolve2(answer);
963
+ });
964
+ });
965
+ }
966
+ resolveUserQuestion(questionId, answer) {
967
+ const resolver = this.pendingUserQuestions.get(questionId);
968
+ if (resolver) {
969
+ this.pendingUserQuestions.delete(questionId);
970
+ resolver(answer);
971
+ }
972
+ }
929
973
  async submit(op) {
930
974
  const subId = `sub_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
931
975
  const submission = {
@@ -1252,14 +1296,14 @@ var writeFileTool = {
1252
1296
  // src/tools/handlers/request-user-input.ts
1253
1297
  var requestUserInputTool = {
1254
1298
  name: "request_user_input",
1255
- description: "Ask the user a clarifying question or present options when instructions are ambiguous.",
1299
+ description: "Ask the user a clarifying question or present multiple-choice options when instructions are ambiguous. You can include recommendations prefixing choices with '(Recommended)'.",
1256
1300
  parameters: {
1257
1301
  type: "object",
1258
1302
  properties: {
1259
1303
  question: { type: "string", description: "The specific question to ask the user." },
1260
1304
  options: {
1261
1305
  type: "array",
1262
- description: "Optional list of choices for the user to select from.",
1306
+ description: "Optional list of choices for the user to select from (e.g. ['(Recommended) Option A', 'Option B']).",
1263
1307
  items: { type: "string", description: "Choice text" }
1264
1308
  }
1265
1309
  },
@@ -1268,19 +1312,31 @@ var requestUserInputTool = {
1268
1312
  async execute(args, ctx) {
1269
1313
  const question = String(args.question || "");
1270
1314
  const options = Array.isArray(args.options) ? args.options : [];
1315
+ if (ctx.requestInput) {
1316
+ const answer = await ctx.requestInput(question, options);
1317
+ return {
1318
+ output: `User responded: "${answer}"`
1319
+ };
1320
+ }
1271
1321
  if (ctx.requestApproval) {
1272
1322
  const description = options.length > 0 ? `${question}
1273
- Options: ${options.map((o, idx) => `${idx + 1}. ${o}`).join(", ")}` : question;
1323
+ Options:
1324
+ ${options.map((o, idx) => ` [${idx + 1}] ${o}`).join(`
1325
+ `)}` : question;
1274
1326
  const answered = await ctx.requestApproval(description);
1275
1327
  return {
1276
1328
  output: answered ? "User confirmed to proceed." : "User declined or requested alternative."
1277
1329
  };
1278
1330
  }
1279
1331
  return {
1280
- output: `Question sent to user: '${question}'. Waiting for input.`
1332
+ output: `Question presented to user: '${question}' with options [${options.join(", ")}].`
1281
1333
  };
1282
1334
  }
1283
1335
  };
1336
+ var askQuestionTool = {
1337
+ ...requestUserInputTool,
1338
+ name: "ask_question"
1339
+ };
1284
1340
  // src/search/engine.ts
1285
1341
  import { readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync2, existsSync as existsSync4 } from "fs";
1286
1342
  import { resolve as resolve4, relative, join, extname } from "path";
@@ -1939,6 +1995,7 @@ function createDefaultTools(options = {}) {
1939
1995
  router2.register(writeFileTool);
1940
1996
  router2.register(listDirTool);
1941
1997
  router2.register(requestUserInputTool);
1998
+ router2.register(askQuestionTool);
1942
1999
  for (const tool of createFileSearchTools()) {
1943
2000
  router2.register(tool);
1944
2001
  }
@@ -5395,6 +5452,72 @@ async function promptToolApproval(params) {
5395
5452
  });
5396
5453
  return decision;
5397
5454
  }
5455
+ async function promptUserQuestion(params) {
5456
+ const { question, options = [] } = params;
5457
+ const boxWidth = Math.min(process.stdout.columns ?? 80, 75);
5458
+ const border = "\u2500".repeat(Math.max(10, boxWidth - 20));
5459
+ console.log(`
5460
+ ${style.cyan("\u250C\u2500\u2500")} ${style.bold("AI Question")} ${style.cyan(border)}`);
5461
+ const qLines = question.split(`
5462
+ `);
5463
+ for (const line of qLines) {
5464
+ console.log(` ${style.cyan("\u2502")} ${style.bold(line)}`);
5465
+ }
5466
+ if (options.length > 0) {
5467
+ console.log(` ${style.cyan("\u2502")}`);
5468
+ options.forEach((opt, idx) => {
5469
+ const numTag = style.cyan(`[${idx + 1}]`);
5470
+ const isRec = opt.includes("(Recommended)") || opt.includes("(recommended)");
5471
+ const optText = isRec ? opt.replace(/\(Recommended\)/i, style.green("(Recommended)")) : opt;
5472
+ console.log(` ${style.cyan("\u2502")} ${numTag} ${optText}`);
5473
+ });
5474
+ }
5475
+ console.log(` ${style.cyan("\u2514" + "\u2500".repeat(Math.max(10, boxWidth - 4)))}
5476
+ `);
5477
+ if (!process.stdin.isTTY || false || !process.stdin.readable) {
5478
+ return options[0] || "yes";
5479
+ }
5480
+ return new Promise((resolve13) => {
5481
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
5482
+ const promptLabel = options.length > 0 ? `Select [1-${options.length}] or type custom response: ` : `Your response: `;
5483
+ rl.question(` ${style.bold(promptLabel)}`, (answer) => {
5484
+ rl.close();
5485
+ const trimmed = answer.trim();
5486
+ if (!trimmed) {
5487
+ const fallback = options[0] || "";
5488
+ console.log(style.dim(` \u21B3 Default: ${fallback || "(empty)"}
5489
+ `));
5490
+ resolve13(fallback);
5491
+ return;
5492
+ }
5493
+ const num = parseInt(trimmed, 10);
5494
+ if (!isNaN(num) && num >= 1 && num <= options.length) {
5495
+ const picked = options[num - 1];
5496
+ console.log(style.green(` \u2714 Selected: ${picked}
5497
+ `));
5498
+ resolve13(picked);
5499
+ return;
5500
+ }
5501
+ if (options.length === 2) {
5502
+ if (/^(y|yes)$/i.test(trimmed)) {
5503
+ console.log(style.green(` \u2714 Selected: ${options[0]}
5504
+ `));
5505
+ resolve13(options[0]);
5506
+ return;
5507
+ }
5508
+ if (/^(n|no)$/i.test(trimmed)) {
5509
+ console.log(style.green(` \u2714 Selected: ${options[1]}
5510
+ `));
5511
+ resolve13(options[1]);
5512
+ return;
5513
+ }
5514
+ }
5515
+ console.log(style.green(` \u2714 Answer: ${trimmed}
5516
+ `));
5517
+ resolve13(trimmed);
5518
+ });
5519
+ });
5520
+ }
5398
5521
 
5399
5522
  // src/cli/ui/markdown.ts
5400
5523
  class MarkdownHighlighter {
@@ -5746,6 +5869,10 @@ class CliRepl {
5746
5869
  this.spinner.stop();
5747
5870
  await this.handleInteractiveApproval(msg);
5748
5871
  break;
5872
+ case "UserQuestionRequired":
5873
+ this.spinner.stop();
5874
+ await this.handleInteractiveUserQuestion(msg);
5875
+ break;
5749
5876
  case "TurnCompleted":
5750
5877
  if (this.reasoningStarted) {
5751
5878
  console.log(style.dim(`
@@ -5824,6 +5951,18 @@ class CliRepl {
5824
5951
  this.session.resolveApproval(msg.approvalId, false);
5825
5952
  }
5826
5953
  }
5954
+ async handleInteractiveUserQuestion(msg) {
5955
+ try {
5956
+ const answer = await promptUserQuestion({
5957
+ question: msg.question,
5958
+ options: msg.options
5959
+ });
5960
+ this.session.resolveUserQuestion(msg.questionId, answer);
5961
+ this.spinner.start(`Processing response and continuing...`);
5962
+ } catch {
5963
+ this.session.resolveUserQuestion(msg.questionId, "");
5964
+ }
5965
+ }
5827
5966
  async start() {
5828
5967
  const creds = new CredentialsStore().load();
5829
5968
  const accountUser = creds?.user?.username || creds?.user?.email || (creds?.accessToken ? "Authenticated" : undefined);
@@ -5906,6 +6045,8 @@ async function main() {
5906
6045
  let model = process.env.GROUPY_MODEL || process.env.OPENAI_MODEL || "groupy";
5907
6046
  let baseUrl = process.env.GROUPY_BASE_URL || process.env.OPENAI_BASE_URL || savedCreds?.baseUrl;
5908
6047
  let apiKey = process.env.GROUPY_API_KEY || process.env.OPENAI_API_KEY || savedCreds?.accessToken;
6048
+ let explicitBaseUrl;
6049
+ let explicitApiKey;
5909
6050
  let cwd = process.cwd();
5910
6051
  let role = "default";
5911
6052
  let mcpConfigFile;
@@ -5930,7 +6071,7 @@ async function main() {
5930
6071
  printWhoami2(credStore);
5931
6072
  process.exit(0);
5932
6073
  } else if (arg === "models" || arg === "--models") {
5933
- await printAvailableModels(baseUrl || "http://localhost:8090/v1", apiKey);
6074
+ await printAvailableModels(baseUrl || "https://api.groupy-hub.store/v1", apiKey);
5934
6075
  process.exit(0);
5935
6076
  } else if (arg === "list" || arg === "sessions" || arg === "--list") {
5936
6077
  printSessionsList(storageManager);
@@ -5949,15 +6090,20 @@ async function main() {
5949
6090
  } else if (arg === "--model" || arg === "-m") {
5950
6091
  model = args[++i] || model;
5951
6092
  } else if (arg === "--base-url" || arg === "-u") {
5952
- baseUrl = args[++i] || baseUrl;
6093
+ explicitBaseUrl = args[++i];
6094
+ baseUrl = explicitBaseUrl || baseUrl;
5953
6095
  } else if (arg === "--api-key" || arg === "-k") {
5954
- apiKey = args[++i] || apiKey;
6096
+ explicitApiKey = args[++i];
6097
+ apiKey = explicitApiKey || apiKey;
5955
6098
  } else if (arg === "--cwd" || arg === "-C") {
5956
6099
  cwd = resolve13(args[++i] || cwd);
5957
6100
  } else if (arg === "--role" || arg === "-r") {
5958
6101
  role = args[++i] || role;
5959
6102
  } else if (arg === "--mcp") {
5960
6103
  mcpConfigFile = args[++i];
6104
+ } else if (arg === "--version" || arg === "-v" || arg === "version") {
6105
+ console.log(`pikaa v0.2.5`);
6106
+ process.exit(0);
5961
6107
  } else if (arg === "--help" || arg === "-h") {
5962
6108
  printCliHelp();
5963
6109
  process.exit(0);
@@ -5966,8 +6112,8 @@ async function main() {
5966
6112
  }
5967
6113
  }
5968
6114
  const modelClient = new ModelClient({
5969
- baseUrl,
5970
- apiKey,
6115
+ baseUrl: explicitBaseUrl,
6116
+ apiKey: explicitApiKey,
5971
6117
  defaultModel: model
5972
6118
  });
5973
6119
  const tools = createDefaultTools({ skillsLoader, memoryStore, worktreeManager });
@@ -6223,6 +6369,7 @@ ${style.bold("OPTIONS:")}
6223
6369
  -C, --cwd <path> Working directory for agent operations (default: current dir)
6224
6370
  -r, --role <role> Initial agent role (default, reviewer, researcher, tester, planner)
6225
6371
  --mcp <path> Path to MCP server configuration JSON file
6372
+ -v, --version Show CLI version
6226
6373
  -h, --help Show this help message
6227
6374
 
6228
6375
  ${style.bold("EXAMPLES:")}
package/dist/index.js CHANGED
@@ -453,7 +453,7 @@ class ExecPolicy {
453
453
  }
454
454
  initDefaultRules() {
455
455
  this.addRule(/^(git\s+(status|log|diff|branch|show|rev-parse))/i, "allow", "Safe git query");
456
- this.addRule(/^(ls|dir|cat|grep|rg|find|pwd|echo|head|tail|wc|which|where)\b/i, "allow", "Safe read-only shell command");
456
+ this.addRule(/^(ls|dir|cat|type|grep|rg|find|pwd|echo|head|tail|wc|which|where)\b/i, "allow", "Safe read-only shell command");
457
457
  this.addRule(/^(bun\s+(test|--version|-v)|npm\s+(test|--version|-v)|node\s+-v)\b/i, "allow", "Testing & runtime check");
458
458
  this.addRule(/^(rm|del|rmdir|format|mkfs)\b/i, "prompt", "Destructive file removal");
459
459
  this.addRule(/^(git\s+(push|reset\s+--hard|clean\s+-fd|rebase))\b/i, "prompt", "Destructive git operation");
@@ -658,14 +658,14 @@ var writeFileTool = {
658
658
  // src/tools/handlers/request-user-input.ts
659
659
  var requestUserInputTool = {
660
660
  name: "request_user_input",
661
- description: "Ask the user a clarifying question or present options when instructions are ambiguous.",
661
+ description: "Ask the user a clarifying question or present multiple-choice options when instructions are ambiguous. You can include recommendations prefixing choices with '(Recommended)'.",
662
662
  parameters: {
663
663
  type: "object",
664
664
  properties: {
665
665
  question: { type: "string", description: "The specific question to ask the user." },
666
666
  options: {
667
667
  type: "array",
668
- description: "Optional list of choices for the user to select from.",
668
+ description: "Optional list of choices for the user to select from (e.g. ['(Recommended) Option A', 'Option B']).",
669
669
  items: { type: "string", description: "Choice text" }
670
670
  }
671
671
  },
@@ -674,19 +674,31 @@ var requestUserInputTool = {
674
674
  async execute(args, ctx) {
675
675
  const question = String(args.question || "");
676
676
  const options = Array.isArray(args.options) ? args.options : [];
677
+ if (ctx.requestInput) {
678
+ const answer = await ctx.requestInput(question, options);
679
+ return {
680
+ output: `User responded: "${answer}"`
681
+ };
682
+ }
677
683
  if (ctx.requestApproval) {
678
684
  const description = options.length > 0 ? `${question}
679
- Options: ${options.map((o, idx) => `${idx + 1}. ${o}`).join(", ")}` : question;
685
+ Options:
686
+ ${options.map((o, idx) => ` [${idx + 1}] ${o}`).join(`
687
+ `)}` : question;
680
688
  const answered = await ctx.requestApproval(description);
681
689
  return {
682
690
  output: answered ? "User confirmed to proceed." : "User declined or requested alternative."
683
691
  };
684
692
  }
685
693
  return {
686
- output: `Question sent to user: '${question}'. Waiting for input.`
694
+ output: `Question presented to user: '${question}' with options [${options.join(", ")}].`
687
695
  };
688
696
  }
689
697
  };
698
+ var askQuestionTool = {
699
+ ...requestUserInputTool,
700
+ name: "ask_question"
701
+ };
690
702
  // src/search/engine.ts
691
703
  import { readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync2, existsSync as existsSync4 } from "fs";
692
704
  import { resolve as resolve4, relative, join, extname } from "path";
@@ -1345,6 +1357,7 @@ function createDefaultTools(options = {}) {
1345
1357
  router2.register(writeFileTool);
1346
1358
  router2.register(listDirTool);
1347
1359
  router2.register(requestUserInputTool);
1360
+ router2.register(askQuestionTool);
1348
1361
  for (const tool of createFileSearchTools()) {
1349
1362
  router2.register(tool);
1350
1363
  }
@@ -1438,12 +1451,17 @@ function buildSystemPrompt(params) {
1438
1451
  const sections = [];
1439
1452
  sections.push(params.basePrompt || "You are Groupy, an expert autonomous AI coding assistant. You think step-by-step, act surgically, and write clean, correct code.");
1440
1453
  sections.push([
1441
- "## Operational Guidelines",
1442
- "1. Think before acting. Understand the task and the code it touches before making edits.",
1443
- "2. Simplicity first: write the minimum code that solves the problem. No unnecessary abstractions.",
1444
- "3. Use `apply_patch` for surgical code replacements rather than overwriting whole files.",
1445
- "4. Execute shell commands with `shell` to inspect environment, run tests, or build targets.",
1446
- "5. Always verify your changes with tests or execution checks."
1454
+ "## Editing Constraints & Guidelines",
1455
+ "- Use `apply_patch` for surgical single-file edits. TargetContent must match existing file content exactly.",
1456
+ "- Use `write_file` for creating new files or when completely replacing the full content of a file.",
1457
+ "- Use `read_file` to inspect files and `grep_search` / `find_files` to discover symbols and locate files across the project.",
1458
+ "- NEVER create temporary scripts, scratch files, or chunk files (e.g. `_tmp_*.ps1`, `_tmp_*.txt`, `split_*.py`) in the workspace to manipulate, split, or read files.",
1459
+ "- NEVER execute shell or PowerShell scripts as a workaround for reading, writing, or editing text files.",
1460
+ "- Use the `shell` tool ONLY for running tests, build targets, package installations, or checking environment/git status.",
1461
+ "- When user requirements are ambiguous or require architectural decisions, use `request_user_input` or `ask_question` to present 2-4 clear options. Prefix your recommended choice with `(Recommended)` (e.g. `['(Recommended) Option A', 'Option B']`).",
1462
+ "- You may be in a dirty git worktree. NEVER revert existing changes made by the user.",
1463
+ "- NEVER use destructive commands like `git reset --hard` or `git checkout --`.",
1464
+ "- Be concise, direct, and act surgically. Write clean, correct code with minimal necessary modifications."
1447
1465
  ].join(`
1448
1466
  `));
1449
1467
  if (params.memoriesPrompt) {
@@ -1668,6 +1686,15 @@ async function runTurn(session, turnContext, input) {
1668
1686
  description,
1669
1687
  command
1670
1688
  });
1689
+ },
1690
+ requestInput: async (question, options) => {
1691
+ const questionId = `quest_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
1692
+ return session.requestUserQuestion({
1693
+ questionId,
1694
+ turnId,
1695
+ question,
1696
+ options
1697
+ });
1671
1698
  }
1672
1699
  });
1673
1700
  const functionOutputItem = {
@@ -1805,6 +1832,7 @@ class Session {
1805
1832
  status = "idle";
1806
1833
  eventListeners = [];
1807
1834
  pendingApprovals = new Map;
1835
+ pendingUserQuestions = new Map;
1808
1836
  submissionResolvers = [];
1809
1837
  submissionQueue = [];
1810
1838
  isTerminated = false;
@@ -1920,6 +1948,35 @@ class Session {
1920
1948
  resolver(approved);
1921
1949
  }
1922
1950
  }
1951
+ requestUserQuestion(params) {
1952
+ this.emitEvent({
1953
+ type: "UserQuestionRequired",
1954
+ questionId: params.questionId,
1955
+ turnId: params.turnId,
1956
+ question: params.question,
1957
+ options: params.options
1958
+ });
1959
+ this.emitEvent({
1960
+ type: "StatusChanged",
1961
+ status: "waiting_user_input"
1962
+ });
1963
+ return new Promise((resolve6) => {
1964
+ this.pendingUserQuestions.set(params.questionId, (answer) => {
1965
+ this.emitEvent({
1966
+ type: "StatusChanged",
1967
+ status: "running"
1968
+ });
1969
+ resolve6(answer);
1970
+ });
1971
+ });
1972
+ }
1973
+ resolveUserQuestion(questionId, answer) {
1974
+ const resolver = this.pendingUserQuestions.get(questionId);
1975
+ if (resolver) {
1976
+ this.pendingUserQuestions.delete(questionId);
1977
+ resolver(answer);
1978
+ }
1979
+ }
1923
1980
  async submit(op) {
1924
1981
  const subId = `sub_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
1925
1982
  const submission = {
@@ -4123,6 +4180,7 @@ export {
4123
4180
  TurnContext,
4124
4181
  WorktreeManager,
4125
4182
  applyPatchTool,
4183
+ askQuestionTool,
4126
4184
  buildSystemPrompt,
4127
4185
  captureWorldState,
4128
4186
  compactHistory,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikaa-ai/pikaa",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "PIKAA CLI - AI coding agent that runs locally in your terminal.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",