@pikaa-ai/pikaa 0.2.4 → 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/dist/cli.js +140 -5
- package/dist/index.js +58 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -475,6 +475,7 @@ function buildSystemPrompt(params) {
|
|
|
475
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
476
|
"- NEVER execute shell or PowerShell scripts as a workaround for reading, writing, or editing text files.",
|
|
477
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']`).",
|
|
478
479
|
"- You may be in a dirty git worktree. NEVER revert existing changes made by the user.",
|
|
479
480
|
"- NEVER use destructive commands like `git reset --hard` or `git checkout --`.",
|
|
480
481
|
"- Be concise, direct, and act surgically. Write clean, correct code with minimal necessary modifications."
|
|
@@ -644,6 +645,15 @@ async function runTurn(session, turnContext, input) {
|
|
|
644
645
|
description,
|
|
645
646
|
command
|
|
646
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
|
+
});
|
|
647
657
|
}
|
|
648
658
|
});
|
|
649
659
|
const functionOutputItem = {
|
|
@@ -815,6 +825,7 @@ class Session {
|
|
|
815
825
|
status = "idle";
|
|
816
826
|
eventListeners = [];
|
|
817
827
|
pendingApprovals = new Map;
|
|
828
|
+
pendingUserQuestions = new Map;
|
|
818
829
|
submissionResolvers = [];
|
|
819
830
|
submissionQueue = [];
|
|
820
831
|
isTerminated = false;
|
|
@@ -930,6 +941,35 @@ class Session {
|
|
|
930
941
|
resolver(approved);
|
|
931
942
|
}
|
|
932
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
|
+
}
|
|
933
973
|
async submit(op) {
|
|
934
974
|
const subId = `sub_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
|
|
935
975
|
const submission = {
|
|
@@ -1256,14 +1296,14 @@ var writeFileTool = {
|
|
|
1256
1296
|
// src/tools/handlers/request-user-input.ts
|
|
1257
1297
|
var requestUserInputTool = {
|
|
1258
1298
|
name: "request_user_input",
|
|
1259
|
-
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)'.",
|
|
1260
1300
|
parameters: {
|
|
1261
1301
|
type: "object",
|
|
1262
1302
|
properties: {
|
|
1263
1303
|
question: { type: "string", description: "The specific question to ask the user." },
|
|
1264
1304
|
options: {
|
|
1265
1305
|
type: "array",
|
|
1266
|
-
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']).",
|
|
1267
1307
|
items: { type: "string", description: "Choice text" }
|
|
1268
1308
|
}
|
|
1269
1309
|
},
|
|
@@ -1272,19 +1312,31 @@ var requestUserInputTool = {
|
|
|
1272
1312
|
async execute(args, ctx) {
|
|
1273
1313
|
const question = String(args.question || "");
|
|
1274
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
|
+
}
|
|
1275
1321
|
if (ctx.requestApproval) {
|
|
1276
1322
|
const description = options.length > 0 ? `${question}
|
|
1277
|
-
Options:
|
|
1323
|
+
Options:
|
|
1324
|
+
${options.map((o, idx) => ` [${idx + 1}] ${o}`).join(`
|
|
1325
|
+
`)}` : question;
|
|
1278
1326
|
const answered = await ctx.requestApproval(description);
|
|
1279
1327
|
return {
|
|
1280
1328
|
output: answered ? "User confirmed to proceed." : "User declined or requested alternative."
|
|
1281
1329
|
};
|
|
1282
1330
|
}
|
|
1283
1331
|
return {
|
|
1284
|
-
output: `Question
|
|
1332
|
+
output: `Question presented to user: '${question}' with options [${options.join(", ")}].`
|
|
1285
1333
|
};
|
|
1286
1334
|
}
|
|
1287
1335
|
};
|
|
1336
|
+
var askQuestionTool = {
|
|
1337
|
+
...requestUserInputTool,
|
|
1338
|
+
name: "ask_question"
|
|
1339
|
+
};
|
|
1288
1340
|
// src/search/engine.ts
|
|
1289
1341
|
import { readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync2, existsSync as existsSync4 } from "fs";
|
|
1290
1342
|
import { resolve as resolve4, relative, join, extname } from "path";
|
|
@@ -1943,6 +1995,7 @@ function createDefaultTools(options = {}) {
|
|
|
1943
1995
|
router2.register(writeFileTool);
|
|
1944
1996
|
router2.register(listDirTool);
|
|
1945
1997
|
router2.register(requestUserInputTool);
|
|
1998
|
+
router2.register(askQuestionTool);
|
|
1946
1999
|
for (const tool of createFileSearchTools()) {
|
|
1947
2000
|
router2.register(tool);
|
|
1948
2001
|
}
|
|
@@ -5399,6 +5452,72 @@ async function promptToolApproval(params) {
|
|
|
5399
5452
|
});
|
|
5400
5453
|
return decision;
|
|
5401
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
|
+
}
|
|
5402
5521
|
|
|
5403
5522
|
// src/cli/ui/markdown.ts
|
|
5404
5523
|
class MarkdownHighlighter {
|
|
@@ -5750,6 +5869,10 @@ class CliRepl {
|
|
|
5750
5869
|
this.spinner.stop();
|
|
5751
5870
|
await this.handleInteractiveApproval(msg);
|
|
5752
5871
|
break;
|
|
5872
|
+
case "UserQuestionRequired":
|
|
5873
|
+
this.spinner.stop();
|
|
5874
|
+
await this.handleInteractiveUserQuestion(msg);
|
|
5875
|
+
break;
|
|
5753
5876
|
case "TurnCompleted":
|
|
5754
5877
|
if (this.reasoningStarted) {
|
|
5755
5878
|
console.log(style.dim(`
|
|
@@ -5828,6 +5951,18 @@ class CliRepl {
|
|
|
5828
5951
|
this.session.resolveApproval(msg.approvalId, false);
|
|
5829
5952
|
}
|
|
5830
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
|
+
}
|
|
5831
5966
|
async start() {
|
|
5832
5967
|
const creds = new CredentialsStore().load();
|
|
5833
5968
|
const accountUser = creds?.user?.username || creds?.user?.email || (creds?.accessToken ? "Authenticated" : undefined);
|
|
@@ -5967,7 +6102,7 @@ async function main() {
|
|
|
5967
6102
|
} else if (arg === "--mcp") {
|
|
5968
6103
|
mcpConfigFile = args[++i];
|
|
5969
6104
|
} else if (arg === "--version" || arg === "-v" || arg === "version") {
|
|
5970
|
-
console.log(`pikaa v0.2.
|
|
6105
|
+
console.log(`pikaa v0.2.5`);
|
|
5971
6106
|
process.exit(0);
|
|
5972
6107
|
} else if (arg === "--help" || arg === "-h") {
|
|
5973
6108
|
printCliHelp();
|
package/dist/index.js
CHANGED
|
@@ -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:
|
|
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
|
|
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
|
}
|
|
@@ -1445,6 +1458,7 @@ function buildSystemPrompt(params) {
|
|
|
1445
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.",
|
|
1446
1459
|
"- NEVER execute shell or PowerShell scripts as a workaround for reading, writing, or editing text files.",
|
|
1447
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']`).",
|
|
1448
1462
|
"- You may be in a dirty git worktree. NEVER revert existing changes made by the user.",
|
|
1449
1463
|
"- NEVER use destructive commands like `git reset --hard` or `git checkout --`.",
|
|
1450
1464
|
"- Be concise, direct, and act surgically. Write clean, correct code with minimal necessary modifications."
|
|
@@ -1672,6 +1686,15 @@ async function runTurn(session, turnContext, input) {
|
|
|
1672
1686
|
description,
|
|
1673
1687
|
command
|
|
1674
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
|
+
});
|
|
1675
1698
|
}
|
|
1676
1699
|
});
|
|
1677
1700
|
const functionOutputItem = {
|
|
@@ -1809,6 +1832,7 @@ class Session {
|
|
|
1809
1832
|
status = "idle";
|
|
1810
1833
|
eventListeners = [];
|
|
1811
1834
|
pendingApprovals = new Map;
|
|
1835
|
+
pendingUserQuestions = new Map;
|
|
1812
1836
|
submissionResolvers = [];
|
|
1813
1837
|
submissionQueue = [];
|
|
1814
1838
|
isTerminated = false;
|
|
@@ -1924,6 +1948,35 @@ class Session {
|
|
|
1924
1948
|
resolver(approved);
|
|
1925
1949
|
}
|
|
1926
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
|
+
}
|
|
1927
1980
|
async submit(op) {
|
|
1928
1981
|
const subId = `sub_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
|
|
1929
1982
|
const submission = {
|
|
@@ -4127,6 +4180,7 @@ export {
|
|
|
4127
4180
|
TurnContext,
|
|
4128
4181
|
WorktreeManager,
|
|
4129
4182
|
applyPatchTool,
|
|
4183
|
+
askQuestionTool,
|
|
4130
4184
|
buildSystemPrompt,
|
|
4131
4185
|
captureWorldState,
|
|
4132
4186
|
compactHistory,
|