@polka-codes/cli 0.8.24 → 0.8.25

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.
Files changed (2) hide show
  1. package/dist/index.js +174 -49
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -38447,20 +38447,25 @@ var {
38447
38447
  Help
38448
38448
  } = import__.default;
38449
38449
  // package.json
38450
- var version = "0.8.24";
38450
+ var version = "0.8.25";
38451
38451
 
38452
38452
  // ../core/src/AiService/AiServiceBase.ts
38453
38453
  class AiServiceBase {
38454
38454
  usageMeter;
38455
38455
  options;
38456
+ #abortController;
38456
38457
  constructor(options) {
38457
38458
  this.options = options;
38458
38459
  this.usageMeter = options.usageMeter;
38459
38460
  }
38461
+ abort() {
38462
+ this.#abortController?.abort();
38463
+ }
38460
38464
  async* send(systemPrompt, messages) {
38461
38465
  this.usageMeter.checkLimit();
38462
38466
  this.usageMeter.incrementMessageCount();
38463
- const stream = this.sendImpl(systemPrompt, messages);
38467
+ this.#abortController = new AbortController;
38468
+ const stream = this.sendImpl(systemPrompt, messages, this.#abortController.signal);
38464
38469
  for await (const chunk of stream) {
38465
38470
  switch (chunk.type) {
38466
38471
  case "usage":
@@ -38473,7 +38478,8 @@ class AiServiceBase {
38473
38478
  async request(systemPrompt, messages) {
38474
38479
  this.usageMeter.checkLimit();
38475
38480
  this.usageMeter.incrementMessageCount();
38476
- const stream = this.sendImpl(systemPrompt, messages);
38481
+ this.#abortController = new AbortController;
38482
+ const stream = this.sendImpl(systemPrompt, messages, this.#abortController.signal);
38477
38483
  const usage = {
38478
38484
  inputTokens: 0,
38479
38485
  outputTokens: 0,
@@ -41978,7 +41984,7 @@ class AnthropicService extends AiServiceBase {
41978
41984
  info: anthropicModels[id] ?? anthropicModels[anthropicDefaultModelId]
41979
41985
  };
41980
41986
  }
41981
- async* sendImpl(systemPrompt, messages) {
41987
+ async* sendImpl(systemPrompt, messages, signal) {
41982
41988
  let stream;
41983
41989
  const modelId = this.model.id;
41984
41990
  const cacheControl = this.#options.enableCache ? { type: "ephemeral" } : undefined;
@@ -42036,7 +42042,7 @@ class AnthropicService extends AiServiceBase {
42036
42042
  return message;
42037
42043
  }),
42038
42044
  stream: true
42039
- });
42045
+ }, { signal });
42040
42046
  break;
42041
42047
  }
42042
42048
  default: {
@@ -42047,7 +42053,7 @@ class AnthropicService extends AiServiceBase {
42047
42053
  system: [{ text: systemPrompt, type: "text" }],
42048
42054
  messages,
42049
42055
  stream: true
42050
- });
42056
+ }, { signal });
42051
42057
  break;
42052
42058
  }
42053
42059
  }
@@ -47405,7 +47411,7 @@ class DeepSeekService extends AiServiceBase {
47405
47411
  info: deepSeekModels[id] ?? deepSeekModels[deepSeekDefaultModelId]
47406
47412
  };
47407
47413
  }
47408
- async* sendImpl(systemPrompt, messages) {
47414
+ async* sendImpl(systemPrompt, messages, signal) {
47409
47415
  const openAiMessages = [
47410
47416
  { role: "system", content: systemPrompt },
47411
47417
  ...convertToOpenAiMessages(messages)
@@ -47417,7 +47423,7 @@ class DeepSeekService extends AiServiceBase {
47417
47423
  temperature: 0,
47418
47424
  stream: true,
47419
47425
  stream_options: { include_usage: true }
47420
- });
47426
+ }, { signal });
47421
47427
  for await (const chunk of stream) {
47422
47428
  const delta = chunk.choices[0]?.delta;
47423
47429
  if (delta?.reasoning_content) {
@@ -47461,7 +47467,7 @@ class OllamaService extends AiServiceBase {
47461
47467
  info: openAiModelInfoSaneDefaults
47462
47468
  };
47463
47469
  }
47464
- async* sendImpl(systemPrompt, messages) {
47470
+ async* sendImpl(systemPrompt, messages, signal) {
47465
47471
  const openAiMessages = [
47466
47472
  { role: "system", content: systemPrompt },
47467
47473
  ...convertToOpenAiMessages(messages)
@@ -47471,7 +47477,7 @@ class OllamaService extends AiServiceBase {
47471
47477
  messages: openAiMessages,
47472
47478
  temperature: 0,
47473
47479
  stream: true
47474
- });
47480
+ }, { signal });
47475
47481
  for await (const chunk of stream) {
47476
47482
  const delta = chunk.choices[0]?.delta;
47477
47483
  if (delta?.content) {
@@ -47518,7 +47524,7 @@ class OpenRouterService extends AiServiceBase {
47518
47524
  this.#modelProviderInfo = data.data;
47519
47525
  });
47520
47526
  }
47521
- async* sendImpl(systemPrompt, messages) {
47527
+ async* sendImpl(systemPrompt, messages, signal) {
47522
47528
  const openAiMessages = [
47523
47529
  { role: "system", content: systemPrompt },
47524
47530
  ...convertToOpenAiMessages(messages)
@@ -47578,7 +47584,7 @@ class OpenRouterService extends AiServiceBase {
47578
47584
  transforms: shouldApplyMiddleOutTransform ? ["middle-out"] : undefined,
47579
47585
  include_reasoning: true,
47580
47586
  ...reasoning
47581
- });
47587
+ }, { signal });
47582
47588
  let genId;
47583
47589
  for await (const chunk of stream) {
47584
47590
  if ("error" in chunk) {
@@ -47851,7 +47857,7 @@ var getArray = (args, name, defaultValue) => {
47851
47857
  return [ret];
47852
47858
  };
47853
47859
  // ../core/src/tools/utils/replaceInFile.ts
47854
- var replaceInFile = async (fileContent, diff) => {
47860
+ var replaceInFile = (fileContent, diff) => {
47855
47861
  const blockPattern = /<<<<<+ SEARCH>?\s*\r?\n([\s\S]*?)\r?\n=======[ \t]*\r?\n([\s\S]*?)\r?\n?>>>>>+ REPLACE/g;
47856
47862
  const blocks = [];
47857
47863
  for (let match = blockPattern.exec(diff);match !== null; match = blockPattern.exec(diff)) {
@@ -47896,14 +47902,32 @@ var replaceInFile = async (fileContent, diff) => {
47896
47902
  const startPos = endPos - strippedSearch.length;
47897
47903
  return content.slice(0, startPos) + replace + content.slice(endPos);
47898
47904
  }
47899
- throw new Error(`Could not find the following text in file:
47900
- ${search}`);
47905
+ return null;
47901
47906
  };
47902
47907
  let updatedFile = fileContent;
47908
+ let appliedCount = 0;
47909
+ const totalCount = blocks.length;
47903
47910
  for (const { search, replace } of blocks) {
47904
- updatedFile = findAndReplace(updatedFile, search, replace);
47911
+ const result = findAndReplace(updatedFile, search, replace);
47912
+ if (result !== null) {
47913
+ updatedFile = result;
47914
+ appliedCount++;
47915
+ }
47916
+ }
47917
+ let status;
47918
+ if (appliedCount === 0) {
47919
+ status = "no_diff_applied";
47920
+ } else if (appliedCount < totalCount) {
47921
+ status = "some_diff_applied";
47922
+ } else {
47923
+ status = "all_diff_applied";
47905
47924
  }
47906
- return updatedFile;
47925
+ return {
47926
+ content: updatedFile,
47927
+ status,
47928
+ appliedCount,
47929
+ totalCount
47930
+ };
47907
47931
  };
47908
47932
  // ../core/src/tools/askFollowupQuestion.ts
47909
47933
  var toolInfo = {
@@ -48598,14 +48622,26 @@ var handler8 = async (provider, args) => {
48598
48622
  if (fileContent == null) {
48599
48623
  return {
48600
48624
  type: "Error" /* Error */,
48601
- message: `<error><replace_in_file_path>${path}</replace_in_file_path><error_message>File not found</error_message></error>`
48625
+ message: `<replace_in_file_result path="${path}" status="failed" message="File not found" />`
48626
+ };
48627
+ }
48628
+ const result = replaceInFile(fileContent, diff);
48629
+ if (result.status === "no_diff_applied") {
48630
+ return {
48631
+ type: "Error" /* Error */,
48632
+ message: `<replace_in_file_result path="${path}" status="failed" message="Unable to apply changes" />`
48633
+ };
48634
+ }
48635
+ await provider.writeFile(path, result.content);
48636
+ if (result.status === "some_diff_applied") {
48637
+ return {
48638
+ type: "Reply" /* Reply */,
48639
+ message: `<replace_in_file_result path="${path}" status="some_diff_applied" applied_count="${result.appliedCount}" total_count="${result.totalCount}" />`
48602
48640
  };
48603
48641
  }
48604
- const result = await replaceInFile(fileContent, diff);
48605
- await provider.writeFile(path, result);
48606
48642
  return {
48607
48643
  type: "Reply" /* Reply */,
48608
- message: `<replace_in_file_path>${path}</replace_in_file_path>`
48644
+ message: `<replace_in_file_result path="${path}" status="all_diff_applied" />`
48609
48645
  };
48610
48646
  };
48611
48647
  var isAvailable8 = (provider) => {
@@ -49663,6 +49699,7 @@ class AgentBase {
49663
49699
  handlers;
49664
49700
  #messages = [];
49665
49701
  #policies;
49702
+ #aborted = false;
49666
49703
  constructor(name, ai, config) {
49667
49704
  this.ai = ai;
49668
49705
  if (config.agents && config.agents.length > 0) {
@@ -49694,6 +49731,10 @@ ${instance.prompt}`;
49694
49731
  this.config = config;
49695
49732
  this.#policies = policies;
49696
49733
  }
49734
+ abort() {
49735
+ this.#aborted = true;
49736
+ this.ai.abort();
49737
+ }
49697
49738
  get parameters() {
49698
49739
  return this.ai.options.parameters;
49699
49740
  }
@@ -49722,11 +49763,17 @@ ${instance.prompt}`;
49722
49763
  async#processLoop(userMessage) {
49723
49764
  let nextRequest = userMessage;
49724
49765
  while (true) {
49766
+ if (this.#aborted) {
49767
+ return { type: "Aborted" };
49768
+ }
49725
49769
  if (this.ai.usageMeter.isLimitExceeded().result) {
49726
49770
  this.#callback({ kind: "UsageExceeded" /* UsageExceeded */, agent: this });
49727
49771
  return { type: "UsageExceeded" };
49728
49772
  }
49729
49773
  const response = await this.#request(nextRequest);
49774
+ if (this.#aborted) {
49775
+ return { type: "Aborted" };
49776
+ }
49730
49777
  const resp = await this.#handleResponse(response);
49731
49778
  if (resp.type === "exit") {
49732
49779
  this.#callback({ kind: "EndTask" /* EndTask */, agent: this, exitReason: resp.reason });
@@ -49773,14 +49820,23 @@ ${instance.prompt}`;
49773
49820
  }
49774
49821
  }
49775
49822
  } catch (error) {
49823
+ if (error instanceof Error && error.name === "AbortError") {
49824
+ break;
49825
+ }
49776
49826
  console.error("Error in stream:", error);
49777
49827
  }
49778
49828
  if (currentAssistantMessage) {
49779
49829
  break;
49780
49830
  }
49831
+ if (this.#aborted) {
49832
+ break;
49833
+ }
49781
49834
  console.debug(`Retrying request ${i2 + 1} of ${retryCount}`);
49782
49835
  }
49783
49836
  if (!currentAssistantMessage) {
49837
+ if (this.#aborted) {
49838
+ return [];
49839
+ }
49784
49840
  throw new Error("No assistant message received");
49785
49841
  }
49786
49842
  this.#messages.push({
@@ -50457,6 +50513,7 @@ class MultiAgent {
50457
50513
  case "Delegate" /* Delegate */:
50458
50514
  console.warn("Unexpected exit reason", delegateResult);
50459
50515
  break;
50516
+ case "Aborted":
50460
50517
  case "Interrupted" /* Interrupted */:
50461
50518
  return delegateResult;
50462
50519
  case "Exit" /* Exit */:
@@ -50464,6 +50521,7 @@ class MultiAgent {
50464
50521
  }
50465
50522
  return delegateResult;
50466
50523
  }
50524
+ case "Aborted":
50467
50525
  case "Interrupted" /* Interrupted */:
50468
50526
  case "Exit" /* Exit */:
50469
50527
  this.#agents.pop();
@@ -50496,6 +50554,11 @@ class MultiAgent {
50496
50554
  get hasActiveAgent() {
50497
50555
  return this.#agents.length > 0;
50498
50556
  }
50557
+ abort() {
50558
+ if (this.hasActiveAgent) {
50559
+ this.#agents[this.#agents.length - 1].abort();
50560
+ }
50561
+ }
50499
50562
  }
50500
50563
  // ../core/node_modules/zod/lib/index.mjs
50501
50564
  var util;
@@ -54554,27 +54617,26 @@ var prompt = `
54554
54617
  You are equipped with **Knowledge Management** capabilities:
54555
54618
 
54556
54619
  1. **What to capture**
54557
- Public API of each file (public classes, functions, methods, parameters, return types).
54558
- High-level description of each file's purpose.
54559
- Invariants and assumptions that must always hold.
54560
- Project or directory-specific coding patterns, styles, and architectural conventions.
54561
- Rules (commenting, testing, documentation, security, etc.).
54562
- • Any other insight that a future contributor would find crucial.
54620
+ - Public API of each file (public classes, functions, methods, parameters, return types).
54621
+ - High-level description of each file's purpose.
54622
+ - Invariants and assumptions that must always hold.
54623
+ - Project or directory-specific coding patterns, styles, and architectural conventions.
54624
+ - Any other insight that a future contributor would find crucial.
54563
54625
 
54564
54626
  2. **Where to store it**
54565
- Save knowledge in a YAML file named \`knowledge.ai.yml\`.
54566
- **Create the file in the repository root if it does not yet exist.**
54567
- One file per directory.
54627
+ - Save knowledge in a YAML file named \`knowledge.ai.yml\`.
54628
+ - **Create the file in the repository root if it does not yet exist.**
54629
+ - One file per directory.
54568
54630
  - The repository root file records knowledge that applies project-wide (e.g., service responsibilities, global patterns).
54569
54631
  - Each sub-directory keeps only the knowledge relevant to that directory or package.
54570
- Use clear top-level keys such as \`description\`, \`files\`, \`rules\`.
54632
+ - Use clear top-level keys such as \`description\`, \`files\`, \`rules\`.
54571
54633
 
54572
54634
  3. **When to update**
54573
- **Default behaviour:** only create / update knowledge for the files you actively read, create, or modify during the current task.
54635
+ - **Default behaviour:** only create / update knowledge for the files you actively read, create, or modify during the current task.
54574
54636
  - Operate on other files **only if the user explicitly requests it**.
54575
- **While working**: after reading, analysing, creating, or modifying code, immediately record any new or changed knowledge.
54576
- **On refactor / deletion**: locate and delete or amend obsolete entries so that knowledge never drifts from the codebase.
54577
- **Granularity**: update only the affected directory's \`knowledge.ai.yml\`, except when the change has global impact.
54637
+ - **While working**: after reading, analysing, creating, or modifying code, immediately record any new or changed knowledge.
54638
+ - **On refactor / deletion**: locate and delete or amend obsolete entries so that knowledge never drifts from the codebase.
54639
+ - **Granularity**: update only the affected directory's \`knowledge.ai.yml\`, except when the change has global impact.
54578
54640
 
54579
54641
  4. **How to format (illustrative)**
54580
54642
  \`\`\`yaml
@@ -54584,19 +54646,14 @@ files:
54584
54646
  description: "Numeric helpers for currency calculations"
54585
54647
  api:
54586
54648
  functions:
54587
- 1:
54588
- name: "add"
54589
- params:
54590
- 1: { name: "a", type: "number" }
54591
- 2: { name: "b", type: "number" }
54592
- returns: "number"
54649
+ 1: add(a: number, b: number): number
54593
54650
  rules:
54594
54651
  1: "rules that apply to all files in this directory"
54595
54652
  \`\`\`
54596
54653
 
54597
54654
  5. **Source of truth**
54598
- **Never invent knowledge.** Everything you record must be *directly derived* from existing code, comments, commit messages, or explicit user instructions.
54599
- If a section has no confirmed content, omit it rather than guessing.
54655
+ - **Never invent knowledge.** Everything you record must be *directly derived* from existing code, comments, commit messages, or explicit user instructions.
54656
+ - If a section has no confirmed content, omit it rather than guessing.
54600
54657
 
54601
54658
  6. **Automatic context**
54602
54659
  When you are asked to read or modify a file, the orchestration layer will supply any existing knowledge for that path automatically. Use it, refine it, and keep it accurate.
@@ -54605,11 +54662,11 @@ rules:
54605
54662
  You can use the \`updateKnowledge\` tool to efficiently update knowledge files with smart merging capabilities.
54606
54663
 
54607
54664
  8. **Dictionary-First Format**
54608
- **Always prefer dictionaries** for structured data.
54609
- The **\`files\` section must be a dictionary keyed by file path** (e.g., \`"math.ts": {...}\`).
54610
- For other lists (rules, functions, etc.), use numbered dictionaries.
54611
- Arrays are allowed only when strict ordering is essential and dictionaries cannot express it.
54612
- When removing items, refer to them by their numeric key or index; gaps are fine.
54665
+ - **Always prefer dictionaries** for structured data.
54666
+ - The **\`files\` section must be a dictionary keyed by file path** (e.g., \`"math.ts": {...}\`).
54667
+ - For other lists (rules, functions, etc.), use numbered dictionaries.
54668
+ - Arrays are allowed only when strict ordering is essential and dictionaries cannot express it.
54669
+ - When removing items, refer to them by their numeric key or index; gaps are fine.
54613
54670
 
54614
54671
  Your workflow **must**:
54615
54672
  1. Detect knowledge deltas.
@@ -55223,6 +55280,12 @@ Available commands:
55223
55280
  }
55224
55281
  });
55225
55282
  this.#rl.on("SIGINT", () => {
55283
+ if (this.#busy) {
55284
+ console.log(`
55285
+ Aborting request...`);
55286
+ this.#options.onInterrupt?.();
55287
+ return;
55288
+ }
55226
55289
  if (this.#isMultilineMode) {
55227
55290
  this.#isMultilineMode = false;
55228
55291
  this.#multilineBuffer = [];
@@ -61370,11 +61433,13 @@ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
61370
61433
  var source_default = chalk;
61371
61434
 
61372
61435
  // ../cli-shared/src/utils/eventHandler.ts
61436
+ var toolCallStats = new Map;
61373
61437
  var printEvent = (verbose, usageMeter) => {
61374
61438
  let hadReasoning = false;
61375
61439
  return (event) => {
61376
61440
  switch (event.kind) {
61377
61441
  case "StartTask" /* StartTask */:
61442
+ toolCallStats.clear();
61378
61443
  if (verbose > 1) {
61379
61444
  console.log(`
61380
61445
  ====== System Prompt ======
@@ -61391,7 +61456,28 @@ ${event.systemPrompt}`);
61391
61456
  ======== New Request ========
61392
61457
  `);
61393
61458
  if (verbose) {
61394
- console.log(event.userMessage);
61459
+ const { userMessage } = event;
61460
+ if (typeof userMessage === "string") {
61461
+ console.log(userMessage);
61462
+ } else {
61463
+ for (const content of userMessage) {
61464
+ if (content.type === "text") {
61465
+ console.log(content.text);
61466
+ } else if (content.type === "image") {
61467
+ if (content.source.type === "base64") {
61468
+ console.log(source_default.yellow(`[Image content: ${content.source.media_type}]`));
61469
+ } else if (content.source.type === "url") {
61470
+ console.log(source_default.yellow(`[Image content from URL: ${content.source.url}]`));
61471
+ } else {
61472
+ console.log(source_default.red("[Unknown image source type]"));
61473
+ console.log(content.source);
61474
+ }
61475
+ } else {
61476
+ console.log(source_default.red("[Unknown content type]"));
61477
+ console.log(content);
61478
+ }
61479
+ }
61480
+ }
61395
61481
  console.log(`
61396
61482
 
61397
61483
  ======== Request Message Ended ========
@@ -61423,12 +61509,27 @@ ${event.systemPrompt}`);
61423
61509
  hadReasoning = true;
61424
61510
  break;
61425
61511
  case "ToolUse" /* ToolUse */:
61512
+ {
61513
+ const stats = toolCallStats.get(event.tool) ?? { calls: 0, success: 0, errors: 0 };
61514
+ stats.calls++;
61515
+ toolCallStats.set(event.tool, stats);
61516
+ }
61426
61517
  break;
61427
61518
  case "ToolReply" /* ToolReply */:
61519
+ {
61520
+ const stats = toolCallStats.get(event.tool) ?? { calls: 0, success: 0, errors: 0 };
61521
+ stats.success++;
61522
+ toolCallStats.set(event.tool, stats);
61523
+ }
61428
61524
  break;
61429
61525
  case "ToolInvalid" /* ToolInvalid */:
61430
61526
  break;
61431
61527
  case "ToolError" /* ToolError */:
61528
+ {
61529
+ const stats = toolCallStats.get(event.tool) ?? { calls: 0, success: 0, errors: 0 };
61530
+ stats.errors++;
61531
+ toolCallStats.set(event.tool, stats);
61532
+ }
61432
61533
  break;
61433
61534
  case "ToolInterrupted" /* ToolInterrupted */:
61434
61535
  break;
@@ -61474,6 +61575,24 @@ ${event.systemPrompt}`);
61474
61575
  console.log("Interrupted Message:", event.exitReason.message);
61475
61576
  break;
61476
61577
  }
61578
+ console.log(`
61579
+
61580
+ ======== Tool Call Stats ========`);
61581
+ if (toolCallStats.size > 0) {
61582
+ const tableData = [...toolCallStats.entries()].map(([tool2, stats]) => {
61583
+ const successRate = stats.calls > 0 ? stats.success / stats.calls * 100 : 0;
61584
+ return {
61585
+ "Tool Name": tool2,
61586
+ Calls: stats.calls,
61587
+ Success: stats.success,
61588
+ Errors: stats.errors,
61589
+ "Success Rate": `${successRate.toFixed(2)}%`
61590
+ };
61591
+ });
61592
+ console.table(tableData);
61593
+ } else {
61594
+ console.log("No tools were called.");
61595
+ }
61477
61596
  break;
61478
61597
  }
61479
61598
  };
@@ -61678,6 +61797,9 @@ ${fileList.join(`
61678
61797
  async continueTask(message) {
61679
61798
  return await this.multiAgent.continueTask(message);
61680
61799
  }
61800
+ abort() {
61801
+ this.multiAgent.abort();
61802
+ }
61681
61803
  get hasActiveAgent() {
61682
61804
  return this.multiAgent.hasActiveAgent;
61683
61805
  }
@@ -61920,6 +62042,9 @@ var runChat = async (opts, command) => {
61920
62042
  console.log();
61921
62043
  runner.printUsage();
61922
62044
  process.exit(0);
62045
+ },
62046
+ onInterrupt: () => {
62047
+ runner.abort();
61923
62048
  }
61924
62049
  });
61925
62050
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polka-codes/cli",
3
- "version": "0.8.24",
3
+ "version": "0.8.25",
4
4
  "license": "AGPL-3.0",
5
5
  "author": "github@polka.codes",
6
6
  "type": "module",