@bike4mind/cli 0.2.28 → 0.2.29-cache-mcp-tools-schema-and-refactor.18845

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/index.js CHANGED
@@ -5,16 +5,17 @@ import {
5
5
  getEffectiveApiKey,
6
6
  getOpenWeatherKey,
7
7
  getSerperKey
8
- } from "./chunk-VGYTNJXN.js";
8
+ } from "./chunk-QUV5XB2V.js";
9
9
  import {
10
- ConfigStore
11
- } from "./chunk-23T2XGSZ.js";
10
+ ConfigStore,
11
+ logger
12
+ } from "./chunk-LBTTUQJM.js";
12
13
  import {
13
14
  selectActiveBackgroundAgents,
14
15
  useCliStore
15
16
  } from "./chunk-TVW4ZESU.js";
16
- import "./chunk-JJBDHUGY.js";
17
- import "./chunk-ZEMWV6IR.js";
17
+ import "./chunk-AQWQ7KZX.js";
18
+ import "./chunk-WEUKIPNI.js";
18
19
  import {
19
20
  BFLImageService,
20
21
  BaseStorage,
@@ -26,7 +27,7 @@ import {
26
27
  OpenAIBackend,
27
28
  OpenAIImageService,
28
29
  XAIImageService
29
- } from "./chunk-UNOJBVD2.js";
30
+ } from "./chunk-H4SPUAKA.js";
30
31
  import {
31
32
  AiEvents,
32
33
  ApiKeyEvents,
@@ -82,7 +83,7 @@ import {
82
83
  XAI_IMAGE_MODELS,
83
84
  b4mLLMTools,
84
85
  getMcpProviderMetadata
85
- } from "./chunk-XJRPAAUS.js";
86
+ } from "./chunk-5GSDPBTM.js";
86
87
  import {
87
88
  Logger
88
89
  } from "./chunk-OCYRD7D6.js";
@@ -551,8 +552,9 @@ var COMMANDS = [
551
552
  args: "<name>"
552
553
  },
553
554
  {
554
- name: "sessions",
555
- description: "List saved sessions"
555
+ name: "resume",
556
+ description: "List and resume saved sessions",
557
+ aliases: ["sessions"]
556
558
  },
557
559
  {
558
560
  name: "config",
@@ -1112,11 +1114,11 @@ function InputPrompt({
1112
1114
  }
1113
1115
  };
1114
1116
  const getPlaceholder = () => {
1115
- if (disabled) return "Thinking...";
1117
+ if (disabled) return "";
1116
1118
  if (isBashMode) return "Enter shell command to execute...";
1117
1119
  return "Type your message, /help for commands, @file to reference, or ! for bash";
1118
1120
  };
1119
- return /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { bold: true, color: isBashMode ? "yellow" : "cyan" }, isBashMode ? "$ " : "> "), /* @__PURE__ */ React5.createElement(
1121
+ return /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { bold: true, color: disabled ? "gray" : isBashMode ? "yellow" : "cyan", dimColor: disabled }, isBashMode ? "$ " : "> "), /* @__PURE__ */ React5.createElement(
1120
1122
  CustomTextInput,
1121
1123
  {
1122
1124
  key: inputKey.current,
@@ -1139,7 +1141,7 @@ import React6 from "react";
1139
1141
  import { Box as Box5, Text as Text6 } from "ink";
1140
1142
  import Spinner from "ink-spinner";
1141
1143
  var ThoughtStream = React6.memo(function ThoughtStream2({ isThinking }) {
1142
- return /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column", gap: 1 }, isThinking && /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Thinking...")));
1144
+ return /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column" }, isThinking && /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Thinking...")));
1143
1145
  });
1144
1146
 
1145
1147
  // src/components/AgentThinking.tsx
@@ -1271,9 +1273,30 @@ function PermissionPrompt({
1271
1273
  setResponded(true);
1272
1274
  onResponse(items[selectedIndex].value);
1273
1275
  }, [responded, onResponse, items, selectedIndex]);
1276
+ const handleSelectIndex = useCallback(
1277
+ (index) => {
1278
+ if (responded) return;
1279
+ setResponded(true);
1280
+ onResponse(items[index].value);
1281
+ },
1282
+ [responded, onResponse, items]
1283
+ );
1274
1284
  useInput3(
1275
- (_input, key) => {
1285
+ (input, key) => {
1276
1286
  if (responded) return;
1287
+ const num = parseInt(input, 10);
1288
+ if (num >= 1 && num <= items.length) {
1289
+ handleSelectIndex(num - 1);
1290
+ return;
1291
+ }
1292
+ if (input.toLowerCase() === "y") {
1293
+ handleSelectIndex(0);
1294
+ return;
1295
+ }
1296
+ if (input.toLowerCase() === "n") {
1297
+ handleSelectIndex(items.length - 1);
1298
+ return;
1299
+ }
1277
1300
  if (key.upArrow) {
1278
1301
  setSelectedIndex((i) => i > 0 ? i - 1 : items.length - 1);
1279
1302
  } else if (key.downArrow) {
@@ -1288,7 +1311,7 @@ function PermissionPrompt({
1288
1311
  const rawArgsString = typeof args === "string" ? args : JSON.stringify(args, null, 2);
1289
1312
  const argsString = rawArgsString.length > MAX_ARGS_LENGTH ? rawArgsString.slice(0, MAX_ARGS_LENGTH) + `
1290
1313
  ... (${rawArgsString.length - MAX_ARGS_LENGTH} more chars)` : rawArgsString;
1291
- return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", borderStyle: "bold", borderColor: "yellow", padding: 1, marginY: 1 }, /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: "yellow" }, "\u26A0\uFE0F Permission Required")), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Tool: "), /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: "cyan" }, toolName)), toolDescription && /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Action: "), /* @__PURE__ */ React10.createElement(Text9, null, toolDescription)), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { bold: true }, "Arguments:"), /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, argsString))), preview && /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { bold: true }, "Preview:"), /* @__PURE__ */ React10.createElement(Box9, { borderStyle: "single", borderColor: "gray", paddingX: 1, flexDirection: "column" }, renderDiffPreview(preview))), !canBeTrusted && /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "red", dimColor: true }, "Note: This tool cannot be trusted due to its dangerous nature.")), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1, flexDirection: "column" }, items.map((item, index) => /* @__PURE__ */ React10.createElement(Box9, { key: item.value }, /* @__PURE__ */ React10.createElement(Text9, { color: index === selectedIndex ? "cyan" : void 0, bold: index === selectedIndex }, index === selectedIndex ? "\u276F " : " ", item.label)))));
1314
+ return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", borderStyle: "bold", borderColor: "yellow", padding: 1, marginY: 1 }, /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: "yellow" }, "\u26A0\uFE0F Permission Required")), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Tool: "), /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: "cyan" }, toolName)), toolDescription && /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Action: "), /* @__PURE__ */ React10.createElement(Text9, null, toolDescription)), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { bold: true }, "Arguments:"), /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, argsString))), preview && /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { bold: true }, "Preview:"), /* @__PURE__ */ React10.createElement(Box9, { borderStyle: "single", borderColor: "gray", paddingX: 1, flexDirection: "column" }, renderDiffPreview(preview))), !canBeTrusted && /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "red", dimColor: true }, "Note: This tool cannot be trusted due to its dangerous nature.")), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1, flexDirection: "column" }, items.map((item, index) => /* @__PURE__ */ React10.createElement(Box9, { key: item.value }, /* @__PURE__ */ React10.createElement(Text9, { color: "cyan" }, index + 1, "."), /* @__PURE__ */ React10.createElement(Text9, { color: index === selectedIndex ? "cyan" : void 0, bold: index === selectedIndex }, index === selectedIndex ? " \u276F " : " ", item.label)))), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Press 1-", items.length, ", y/n, or \u2191\u2193 + Enter")));
1292
1315
  }
1293
1316
 
1294
1317
  // src/components/ConfigEditor.tsx
@@ -1707,33 +1730,21 @@ function truncateValue(value, maxLength) {
1707
1730
  }
1708
1731
  return str.slice(0, maxLength) + "...";
1709
1732
  }
1710
- function getRoleDisplay(role) {
1711
- switch (role) {
1712
- case "user":
1713
- return { color: "cyan", label: "\u{1F464} You" };
1714
- case "system":
1715
- return { color: "gray", label: "\u2139\uFE0F System" };
1716
- default:
1717
- return { color: "green", label: "\u{1F916} Assistant" };
1718
- }
1719
- }
1720
1733
  var MessageItem = React14.memo(function MessageItem2({ message }) {
1721
1734
  const isUser = message.role === "user";
1722
- const isContinuation = message.metadata?.isContinuation === true;
1723
- const roleDisplay = getRoleDisplay(message.role);
1724
- return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column", marginBottom: 1 }, !isContinuation && /* @__PURE__ */ React14.createElement(Box13, null, /* @__PURE__ */ React14.createElement(Text13, { bold: true, color: roleDisplay.color }, roleDisplay.label), isUser && /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), isUser && message.content && /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2 }, /* @__PURE__ */ React14.createElement(Text13, { backgroundColor: "whiteBright", color: "black" }, " ", message.content, " ")), !isUser && message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React14.createElement(Text13, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
1735
+ return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column" }, isUser && message.content && /* @__PURE__ */ React14.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React14.createElement(Text13, { backgroundColor: "whiteBright", color: "black" }, "\u276F ", message.content, " ")), !isUser && message.metadata?.steps && message.metadata.steps.filter((s) => ["thought", "action"].includes(s.type)).length > 0 && /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2, flexDirection: "column", marginBottom: 1 }, message.metadata.steps.map((step, idx) => {
1725
1736
  if (step.type === "thought") {
1726
- return /* @__PURE__ */ React14.createElement(Box13, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React14.createElement(Text13, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, ` ${step.content}`));
1737
+ return /* @__PURE__ */ React14.createElement(Box13, { key: idx, flexDirection: "column" }, /* @__PURE__ */ React14.createElement(Text13, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, ` ${step.content}`));
1727
1738
  }
1728
1739
  if (step.type === "action") {
1729
1740
  const toolName = step.metadata?.toolName || "unknown";
1730
1741
  const toolInput = step.metadata?.toolInput;
1731
1742
  const observationStep = message.metadata?.steps?.[idx + 1];
1732
1743
  const result = observationStep?.type === "observation" ? observationStep.content : null;
1733
- return /* @__PURE__ */ React14.createElement(Box13, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React14.createElement(Text13, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, ` Input: ${truncateValue(toolInput, 100)}`), result && /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, ` Result: ${truncateValue(result, 200)}`));
1744
+ return /* @__PURE__ */ React14.createElement(Box13, { key: idx, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React14.createElement(Text13, { color: "yellow" }, "\u{1F527} ", toolName), toolInput && /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, ` Input: ${truncateValue(toolInput, 100)}`), result && /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, ` Result: ${truncateValue(result, 200)}`));
1734
1745
  }
1735
1746
  return null;
1736
- }).filter(Boolean)), !isUser && message.content !== "..." && /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2, marginTop: message.metadata?.steps?.length ? 1 : 0 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React14.createElement(Text13, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React14.createElement(MarkdownRenderer, { content: message.content })), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2 }, /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
1747
+ }).filter(Boolean)), !isUser && message.content !== "..." && /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2, marginBottom: 1 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React14.createElement(Text13, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React14.createElement(MarkdownRenderer, { content: message.content })), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2 }, /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
1737
1748
  });
1738
1749
 
1739
1750
  // src/utils/processFileReferences.ts
@@ -2163,7 +2174,7 @@ function SessionSelector({ sessions, currentSession, onSelect, onCancel }) {
2163
2174
  {
2164
2175
  items,
2165
2176
  onSelect: handleSelectionSelect,
2166
- itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React19.createElement(Box18, null, /* @__PURE__ */ React19.createElement(Text18, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
2177
+ itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React19.createElement(Box18, null, /* @__PURE__ */ React19.createElement(Text18, { color: isSelected ? "cyan" : void 0 }, label))
2167
2178
  }
2168
2179
  ), /* @__PURE__ */ React19.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to cancel")));
2169
2180
  }
@@ -2176,7 +2187,7 @@ function SessionSelector({ sessions, currentSession, onSelect, onCancel }) {
2176
2187
  {
2177
2188
  items: confirmationItems,
2178
2189
  onSelect: handleConfirmationSelect,
2179
- itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React19.createElement(Box18, null, /* @__PURE__ */ React19.createElement(Text18, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
2190
+ itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React19.createElement(Box18, null, /* @__PURE__ */ React19.createElement(Text18, { color: isSelected ? "cyan" : void 0 }, label))
2180
2191
  }
2181
2192
  ), /* @__PURE__ */ React19.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to go back")));
2182
2193
  }
@@ -2623,271 +2634,13 @@ var CommandHistoryStore = class {
2623
2634
  };
2624
2635
 
2625
2636
  // src/storage/CustomCommandStore.ts
2626
- import fs6 from "fs/promises";
2627
- import path6 from "path";
2628
- import os2 from "os";
2629
-
2630
- // src/utils/commandParser.ts
2631
- import matter from "gray-matter";
2632
- import { z } from "zod";
2633
-
2634
- // src/utils/Logger.ts
2635
2637
  import fs5 from "fs/promises";
2636
2638
  import path5 from "path";
2637
2639
  import os from "os";
2638
- var Logger2 = class _Logger {
2639
- constructor() {
2640
- this.logFilePath = null;
2641
- this.sessionId = null;
2642
- this.fileLoggingEnabled = true;
2643
- this.consoleVerbose = false;
2644
- }
2645
- static {
2646
- this.instance = null;
2647
- }
2648
- static getInstance() {
2649
- if (!_Logger.instance) {
2650
- _Logger.instance = new _Logger();
2651
- }
2652
- return _Logger.instance;
2653
- }
2654
- /**
2655
- * Initialize the logger with a session ID
2656
- */
2657
- async initialize(sessionId) {
2658
- this.sessionId = sessionId;
2659
- const debugDir = path5.join(os.homedir(), ".bike4mind", "debug");
2660
- await fs5.mkdir(debugDir, { recursive: true });
2661
- this.logFilePath = path5.join(debugDir, `${sessionId}.txt`);
2662
- await this.writeToFile("INFO", "=== CLI SESSION START ===");
2663
- }
2664
- /**
2665
- * Set whether verbose console logging is enabled
2666
- */
2667
- setVerbose(enabled) {
2668
- this.consoleVerbose = enabled;
2669
- }
2670
- /**
2671
- * Set whether file logging is enabled
2672
- */
2673
- setFileLoggingEnabled(enabled) {
2674
- this.fileLoggingEnabled = enabled;
2675
- }
2676
- /**
2677
- * DEBUG level - verbose-only console, always file
2678
- */
2679
- debug(message) {
2680
- this.writeToFile("DEBUG", message).catch(() => {
2681
- });
2682
- if (this.consoleVerbose) {
2683
- console.log(message);
2684
- }
2685
- }
2686
- /**
2687
- * INFO level - always shown to user
2688
- */
2689
- info(message) {
2690
- this.writeToFile("INFO", message).catch(() => {
2691
- });
2692
- console.log(message);
2693
- }
2694
- /**
2695
- * WARN level - always shown to user
2696
- */
2697
- warn(message) {
2698
- this.writeToFile("WARN", message).catch(() => {
2699
- });
2700
- console.warn(message);
2701
- }
2702
- /**
2703
- * ERROR level - always shown to user
2704
- */
2705
- error(message, err) {
2706
- this.writeToFile("ERROR", message).catch(() => {
2707
- });
2708
- if (err) {
2709
- this.logErrorDetailsToFile(err).catch(() => {
2710
- });
2711
- }
2712
- console.error(message);
2713
- }
2714
- /**
2715
- * Write log entry to file
2716
- */
2717
- async writeToFile(level, message) {
2718
- if (!this.fileLoggingEnabled || !this.logFilePath) {
2719
- return;
2720
- }
2721
- try {
2722
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
2723
- const logEntry = `[${timestamp}] [${level}] ${message}
2724
- `;
2725
- await fs5.appendFile(this.logFilePath, logEntry, "utf-8");
2726
- } catch (error) {
2727
- console.error("File logging failed:", error);
2728
- }
2729
- }
2730
- /**
2731
- * Log error details to file
2732
- */
2733
- async logErrorDetailsToFile(err) {
2734
- if (!this.fileLoggingEnabled || !this.logFilePath) {
2735
- return;
2736
- }
2737
- try {
2738
- if (err && typeof err === "object" && "response" in err && err.response) {
2739
- const response = err.response;
2740
- const config = err && typeof err === "object" && "config" in err ? err.config : void 0;
2741
- await this.writeToFile("ERROR", ` Status: ${response.status} ${response.statusText || ""}`);
2742
- await this.writeToFile("ERROR", ` URL: ${config?.url || "unknown"}`);
2743
- await this.writeToFile("ERROR", ` Headers: ${this.safeStringify(response.headers)}`);
2744
- if (response.data) {
2745
- const errorText = this.extractErrorMessage(response.data);
2746
- if (errorText.trim().startsWith("<!DOCTYPE") || errorText.trim().startsWith("<html")) {
2747
- await this.writeToFile("ERROR", ` Response Type: HTML Error Page`);
2748
- const parsedError = this.parseHtmlError(errorText);
2749
- if (parsedError) {
2750
- await this.writeToFile("ERROR", ` Error Message: ${parsedError}`);
2751
- }
2752
- await this.writeToFile("ERROR", ` Raw HTML: ${this.truncate(errorText, 1e3)}`);
2753
- } else {
2754
- const preview = this.truncate(errorText, 500);
2755
- await this.writeToFile("ERROR", ` Response: ${preview}`);
2756
- }
2757
- } else {
2758
- await this.writeToFile("ERROR", ` Response: (no data)`);
2759
- }
2760
- }
2761
- if (err && typeof err === "object" && "stack" in err && typeof err.stack === "string") {
2762
- const stackLines = err.stack.split("\n").slice(0, 5).join("\n ");
2763
- await this.writeToFile("ERROR", ` Stack: ${stackLines}`);
2764
- } else if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
2765
- await this.writeToFile("ERROR", ` Message: ${err.message}`);
2766
- }
2767
- } catch (error) {
2768
- }
2769
- }
2770
- /**
2771
- * Safely stringify object, handling circular references
2772
- */
2773
- safeStringify(obj) {
2774
- try {
2775
- return JSON.stringify(obj);
2776
- } catch (error) {
2777
- if (error instanceof Error && error.message.includes("circular")) {
2778
- try {
2779
- const seen = /* @__PURE__ */ new WeakSet();
2780
- return JSON.stringify(obj, (key, value) => {
2781
- if (typeof value === "object" && value !== null) {
2782
- if (seen.has(value)) {
2783
- return "[Circular]";
2784
- }
2785
- seen.add(value);
2786
- }
2787
- return value;
2788
- });
2789
- } catch {
2790
- return "[Unable to stringify]";
2791
- }
2792
- }
2793
- return "[Stringify error]";
2794
- }
2795
- }
2796
- /**
2797
- * Extract readable text from error response data
2798
- */
2799
- extractErrorMessage(data) {
2800
- if (Buffer.isBuffer(data)) {
2801
- return data.toString("utf-8");
2802
- }
2803
- if (typeof data === "string") {
2804
- return data;
2805
- }
2806
- if (data && typeof data === "object" && "_readableState" in data && data._readableState && typeof data._readableState === "object" && "buffer" in data._readableState && Array.isArray(data._readableState.buffer) && data._readableState.buffer.length > 0) {
2807
- const chunks = [];
2808
- for (const chunk of data._readableState.buffer) {
2809
- if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "Buffer" && "data" in chunk && Array.isArray(chunk.data)) {
2810
- chunks.push(Buffer.from(chunk.data));
2811
- } else if (Buffer.isBuffer(chunk)) {
2812
- chunks.push(chunk);
2813
- } else if (chunk && typeof chunk === "object" && "data" in chunk) {
2814
- if (Buffer.isBuffer(chunk.data)) {
2815
- chunks.push(chunk.data);
2816
- } else if (Array.isArray(chunk.data)) {
2817
- chunks.push(Buffer.from(chunk.data));
2818
- }
2819
- }
2820
- }
2821
- if (chunks.length > 0) {
2822
- return Buffer.concat(chunks).toString("utf-8");
2823
- }
2824
- }
2825
- return this.safeStringify(data);
2826
- }
2827
- /**
2828
- * Parse HTML error page to extract error message
2829
- */
2830
- parseHtmlError(html) {
2831
- const titleMatch = html.match(/<title>(.*?)<\/title>/i);
2832
- const h1Match = html.match(/<h1>(.*?)<\/h1>/i);
2833
- const bodyMatch = html.match(/<body[^>]*>(.*?)<\/body>/is);
2834
- if (titleMatch && titleMatch[1] !== "Error") {
2835
- return titleMatch[1].trim();
2836
- }
2837
- if (h1Match) {
2838
- return h1Match[1].trim();
2839
- }
2840
- if (bodyMatch) {
2841
- const text = bodyMatch[1].replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
2842
- return text.substring(0, 200);
2843
- }
2844
- return null;
2845
- }
2846
- /**
2847
- * Truncate string to max length with ellipsis
2848
- */
2849
- truncate(str, maxLength) {
2850
- if (str.length <= maxLength) {
2851
- return str;
2852
- }
2853
- return str.substring(0, maxLength) + "... [truncated]";
2854
- }
2855
- /**
2856
- * Format bytes to human-readable size
2857
- */
2858
- formatBytes(bytes) {
2859
- if (bytes === 0) return "0 B";
2860
- const k = 1024;
2861
- const sizes = ["B", "KB", "MB", "GB"];
2862
- const i = Math.floor(Math.log(bytes) / Math.log(k));
2863
- return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
2864
- }
2865
- /**
2866
- * Clean up old debug logs (older than 30 days)
2867
- */
2868
- async cleanupOldLogs() {
2869
- if (!this.fileLoggingEnabled) return;
2870
- try {
2871
- const debugDir = path5.join(os.homedir(), ".bike4mind", "debug");
2872
- const files = await fs5.readdir(debugDir);
2873
- const now = Date.now();
2874
- const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
2875
- for (const file of files) {
2876
- const filePath = path5.join(debugDir, file);
2877
- const stats = await fs5.stat(filePath);
2878
- if (stats.mtime.getTime() < thirtyDaysAgo) {
2879
- await fs5.unlink(filePath);
2880
- }
2881
- }
2882
- } catch (error) {
2883
- console.error("Failed to cleanup old logs:", error);
2884
- }
2885
- }
2886
- };
2887
- var logger = Logger2.getInstance();
2888
- logger.setVerbose(process.env.B4M_VERBOSE === "1");
2889
2640
 
2890
2641
  // src/utils/commandParser.ts
2642
+ import matter from "gray-matter";
2643
+ import { z } from "zod";
2891
2644
  var flexibleString = z.union([z.string(), z.array(z.string())]).transform((val) => Array.isArray(val) ? val.join(" ") : val).optional();
2892
2645
  var flexibleStringArray = z.union([z.string(), z.array(z.string())]).transform((val) => Array.isArray(val) ? val : [val]).optional();
2893
2646
  function needsQuoting(value) {
@@ -3042,17 +2795,17 @@ function extractCommandName(filename) {
3042
2795
  var CustomCommandStore = class {
3043
2796
  constructor(projectRoot) {
3044
2797
  this.commands = /* @__PURE__ */ new Map();
3045
- const home = os2.homedir();
2798
+ const home = os.homedir();
3046
2799
  const root = projectRoot || process.cwd();
3047
2800
  this.globalCommandsDirs = [
3048
- path6.join(home, ".bike4mind", "commands"),
3049
- path6.join(home, ".claude", "commands"),
3050
- path6.join(home, ".claude", "skills")
2801
+ path5.join(home, ".bike4mind", "commands"),
2802
+ path5.join(home, ".claude", "commands"),
2803
+ path5.join(home, ".claude", "skills")
3051
2804
  ];
3052
2805
  this.projectCommandsDirs = [
3053
- path6.join(root, ".bike4mind", "commands"),
3054
- path6.join(root, ".claude", "commands"),
3055
- path6.join(root, ".claude", "skills")
2806
+ path5.join(root, ".bike4mind", "commands"),
2807
+ path5.join(root, ".claude", "commands"),
2808
+ path5.join(root, ".claude", "skills")
3056
2809
  ];
3057
2810
  }
3058
2811
  /**
@@ -3077,7 +2830,7 @@ var CustomCommandStore = class {
3077
2830
  */
3078
2831
  async loadCommandsFromDirectory(directory, source) {
3079
2832
  try {
3080
- const stats = await fs6.stat(directory);
2833
+ const stats = await fs5.stat(directory);
3081
2834
  if (!stats.isDirectory()) {
3082
2835
  return;
3083
2836
  }
@@ -3110,9 +2863,9 @@ var CustomCommandStore = class {
3110
2863
  async findCommandFiles(directory) {
3111
2864
  const files = [];
3112
2865
  try {
3113
- const entries = await fs6.readdir(directory, { withFileTypes: true });
2866
+ const entries = await fs5.readdir(directory, { withFileTypes: true });
3114
2867
  for (const entry of entries) {
3115
- const fullPath = path6.join(directory, entry.name);
2868
+ const fullPath = path5.join(directory, entry.name);
3116
2869
  if (entry.isDirectory()) {
3117
2870
  const subFiles = await this.findCommandFiles(fullPath);
3118
2871
  files.push(...subFiles);
@@ -3132,14 +2885,14 @@ var CustomCommandStore = class {
3132
2885
  * @param source - Source identifier ('global' or 'project')
3133
2886
  */
3134
2887
  async loadCommandFile(filePath, source) {
3135
- const filename = path6.basename(filePath);
2888
+ const filename = path5.basename(filePath);
3136
2889
  const isSkillFile = filename.toLowerCase() === "skill.md";
3137
2890
  const commandName = isSkillFile ? this.extractSkillName(filePath) : extractCommandName(filename);
3138
2891
  if (!commandName) {
3139
2892
  console.warn(`Invalid command filename: ${filename} (must end with .md and have valid name)`);
3140
2893
  return;
3141
2894
  }
3142
- const fileContent = await fs6.readFile(filePath, "utf-8");
2895
+ const fileContent = await fs5.readFile(filePath, "utf-8");
3143
2896
  const command = parseCommandFile(fileContent, filePath, commandName, source);
3144
2897
  this.commands.set(commandName, command);
3145
2898
  }
@@ -3151,7 +2904,7 @@ var CustomCommandStore = class {
3151
2904
  * @returns Skill name or null if invalid
3152
2905
  */
3153
2906
  extractSkillName(filePath) {
3154
- const parentDir = path6.basename(path6.dirname(filePath));
2907
+ const parentDir = path5.basename(path5.dirname(filePath));
3155
2908
  return parentDir && parentDir !== "skills" ? parentDir : null;
3156
2909
  }
3157
2910
  /**
@@ -3213,15 +2966,15 @@ var CustomCommandStore = class {
3213
2966
  */
3214
2967
  async createCommandFile(name, isGlobal = false) {
3215
2968
  const targetDir = isGlobal ? this.globalCommandsDirs[0] : this.projectCommandsDirs[0];
3216
- const filePath = path6.join(targetDir, `${name}.md`);
3217
- const fileExists = await fs6.access(filePath).then(
2969
+ const filePath = path5.join(targetDir, `${name}.md`);
2970
+ const fileExists = await fs5.access(filePath).then(
3218
2971
  () => true,
3219
2972
  () => false
3220
2973
  );
3221
2974
  if (fileExists) {
3222
2975
  throw new Error(`Command file already exists: ${filePath}`);
3223
2976
  }
3224
- await fs6.mkdir(targetDir, { recursive: true });
2977
+ await fs5.mkdir(targetDir, { recursive: true });
3225
2978
  const template = `---
3226
2979
  description: ${name} command
3227
2980
  argument-hint: [args]
@@ -3236,7 +2989,7 @@ You can use:
3236
2989
  - $1, $2, etc. for positional arguments
3237
2990
  - @filename for file references
3238
2991
  `;
3239
- await fs6.writeFile(filePath, template, "utf-8");
2992
+ await fs5.writeFile(filePath, template, "utf-8");
3240
2993
  return filePath;
3241
2994
  }
3242
2995
  };
@@ -3717,18 +3470,23 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
3717
3470
  async executeToolWithQueueFallback(toolUse) {
3718
3471
  const queuedObs = this.observationQueue.find((obs) => obs.toolId === getToolId(toolUse));
3719
3472
  if (queuedObs) {
3720
- const result2 = queuedObs.result;
3473
+ const result = queuedObs.result;
3721
3474
  const index = this.observationQueue.indexOf(queuedObs);
3722
3475
  this.observationQueue.splice(index, 1);
3723
- return typeof result2 === "string" ? result2 : JSON.stringify(result2);
3476
+ return typeof result === "string" ? result : JSON.stringify(result);
3724
3477
  }
3725
3478
  const tool = this.context.tools.find((t) => t.toolSchema.name === toolUse.name);
3726
3479
  if (!tool) {
3727
- throw new Error(`Tool ${toolUse.name} not found in agent context`);
3480
+ return `Error: Tool ${toolUse.name} not found in agent context`;
3481
+ }
3482
+ try {
3483
+ const params = this.parseToolArguments(toolUse.arguments);
3484
+ const result = await tool.toolFn(params);
3485
+ return typeof result === "string" ? result : JSON.stringify(result);
3486
+ } catch (error) {
3487
+ const message = error instanceof Error ? error.message : String(error);
3488
+ return `Error: ${message}`;
3728
3489
  }
3729
- const params = this.parseToolArguments(toolUse.arguments);
3730
- const result = await tool.toolFn(params);
3731
- return typeof result === "string" ? result : JSON.stringify(result);
3732
3490
  }
3733
3491
  /**
3734
3492
  * Build and append tool call/result messages for the conversation history
@@ -3806,6 +3564,7 @@ var DEFAULT_TOOL_CATEGORIES = {
3806
3564
  grep_search: "prompt_default",
3807
3565
  glob_files: "prompt_default",
3808
3566
  get_file_tree: "prompt_default",
3567
+ get_file_structure: "prompt_default",
3809
3568
  git_status: "prompt_default",
3810
3569
  git_diff: "prompt_default",
3811
3570
  git_log: "prompt_default",
@@ -4380,7 +4139,7 @@ import { GoogleGenerativeAI } from "@google/generative-ai";
4380
4139
  import OpenAI2 from "openai";
4381
4140
 
4382
4141
  // ../../b4m-core/packages/services/dist/src/importHistoryService/index.js
4383
- import fs7, { unlinkSync } from "fs";
4142
+ import fs6, { unlinkSync } from "fs";
4384
4143
  import yauzl from "yauzl";
4385
4144
  import axios3 from "axios";
4386
4145
 
@@ -9328,15 +9087,15 @@ var planetVisibilityTool = {
9328
9087
  };
9329
9088
 
9330
9089
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/fileRead/index.js
9331
- import { promises as fs8 } from "fs";
9090
+ import { promises as fs7 } from "fs";
9332
9091
  import { existsSync as existsSync3, statSync as statSync4 } from "fs";
9333
- import path7 from "path";
9092
+ import path6 from "path";
9334
9093
  var MAX_FILE_SIZE2 = 10 * 1024 * 1024;
9335
9094
  async function readFileContent(params) {
9336
9095
  const { path: filePath, encoding = "utf-8", offset = 0, limit } = params;
9337
- const normalizedPath = path7.normalize(filePath);
9338
- const resolvedPath = path7.resolve(process.cwd(), normalizedPath);
9339
- const cwd = path7.resolve(process.cwd());
9096
+ const normalizedPath = path6.normalize(filePath);
9097
+ const resolvedPath = path6.resolve(process.cwd(), normalizedPath);
9098
+ const cwd = path6.resolve(process.cwd());
9340
9099
  if (!resolvedPath.startsWith(cwd)) {
9341
9100
  throw new Error(`Access denied: Cannot read files outside of current working directory`);
9342
9101
  }
@@ -9354,7 +9113,7 @@ async function readFileContent(params) {
9354
9113
  if (isBinary && encoding === "utf-8") {
9355
9114
  throw new Error(`File appears to be binary. Use encoding 'base64' to read binary files, or specify a different encoding.`);
9356
9115
  }
9357
- const content = await fs8.readFile(resolvedPath, encoding);
9116
+ const content = await fs7.readFile(resolvedPath, encoding);
9358
9117
  if (typeof content === "string") {
9359
9118
  const lines = content.split("\n");
9360
9119
  const totalLines = lines.length;
@@ -9392,7 +9151,7 @@ ${content}`;
9392
9151
  }
9393
9152
  async function checkIfBinary(filePath) {
9394
9153
  const buffer = Buffer.alloc(8192);
9395
- const fd = await fs8.open(filePath, "r");
9154
+ const fd = await fs7.open(filePath, "r");
9396
9155
  try {
9397
9156
  const { bytesRead } = await fd.read(buffer, 0, 8192, 0);
9398
9157
  const chunk = buffer.slice(0, bytesRead);
@@ -9409,7 +9168,7 @@ var fileReadTool = {
9409
9168
  context.logger.info("\u{1F4C4} FileRead: Reading file", { path: params.path });
9410
9169
  try {
9411
9170
  const content = await readFileContent(params);
9412
- const stats = statSync4(path7.resolve(process.cwd(), path7.normalize(params.path)));
9171
+ const stats = statSync4(path6.resolve(process.cwd(), path6.normalize(params.path)));
9413
9172
  context.logger.info("\u2705 FileRead: Success", {
9414
9173
  path: params.path,
9415
9174
  size: stats.size,
@@ -9453,25 +9212,25 @@ var fileReadTool = {
9453
9212
  };
9454
9213
 
9455
9214
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/createFile/index.js
9456
- import { promises as fs9 } from "fs";
9215
+ import { promises as fs8 } from "fs";
9457
9216
  import { existsSync as existsSync4 } from "fs";
9458
- import path8 from "path";
9217
+ import path7 from "path";
9459
9218
  async function createFile(params) {
9460
9219
  const { path: filePath, content, createDirectories = true } = params;
9461
- const normalizedPath = path8.normalize(filePath);
9462
- const resolvedPath = path8.resolve(process.cwd(), normalizedPath);
9463
- const cwd = path8.resolve(process.cwd());
9220
+ const normalizedPath = path7.normalize(filePath);
9221
+ const resolvedPath = path7.resolve(process.cwd(), normalizedPath);
9222
+ const cwd = path7.resolve(process.cwd());
9464
9223
  if (!resolvedPath.startsWith(cwd)) {
9465
9224
  throw new Error(`Access denied: Cannot create files outside of current working directory`);
9466
9225
  }
9467
9226
  const fileExists = existsSync4(resolvedPath);
9468
9227
  const action = fileExists ? "overwritten" : "created";
9469
9228
  if (createDirectories) {
9470
- const dir = path8.dirname(resolvedPath);
9471
- await fs9.mkdir(dir, { recursive: true });
9229
+ const dir = path7.dirname(resolvedPath);
9230
+ await fs8.mkdir(dir, { recursive: true });
9472
9231
  }
9473
- await fs9.writeFile(resolvedPath, content, "utf-8");
9474
- const stats = await fs9.stat(resolvedPath);
9232
+ await fs8.writeFile(resolvedPath, content, "utf-8");
9233
+ const stats = await fs8.stat(resolvedPath);
9475
9234
  const lines = content.split("\n").length;
9476
9235
  return `File ${action} successfully: ${filePath}
9477
9236
  Size: ${stats.size} bytes
@@ -9482,7 +9241,7 @@ var createFileTool = {
9482
9241
  implementation: (context) => ({
9483
9242
  toolFn: async (value) => {
9484
9243
  const params = value;
9485
- const fileExists = existsSync4(path8.resolve(process.cwd(), path8.normalize(params.path)));
9244
+ const fileExists = existsSync4(path7.resolve(process.cwd(), path7.normalize(params.path)));
9486
9245
  context.logger.info(`\u{1F4DD} CreateFile: ${fileExists ? "Overwriting" : "Creating"} file`, {
9487
9246
  path: params.path,
9488
9247
  size: params.content.length
@@ -9524,7 +9283,7 @@ var createFileTool = {
9524
9283
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/globFiles/index.js
9525
9284
  import { glob } from "glob";
9526
9285
  import { stat } from "fs/promises";
9527
- import path9 from "path";
9286
+ import path8 from "path";
9528
9287
  var DEFAULT_IGNORE_PATTERNS2 = [
9529
9288
  "**/node_modules/**",
9530
9289
  "**/.git/**",
@@ -9540,7 +9299,7 @@ var DEFAULT_IGNORE_PATTERNS2 = [
9540
9299
  async function findFiles(params) {
9541
9300
  const { pattern, dir_path, case_sensitive = true, respect_git_ignore = true } = params;
9542
9301
  const baseCwd = process.cwd();
9543
- const targetDir = dir_path ? path9.resolve(baseCwd, path9.normalize(dir_path)) : baseCwd;
9302
+ const targetDir = dir_path ? path8.resolve(baseCwd, path8.normalize(dir_path)) : baseCwd;
9544
9303
  if (!targetDir.startsWith(baseCwd)) {
9545
9304
  throw new Error(`Access denied: Cannot search outside of current working directory`);
9546
9305
  }
@@ -9580,7 +9339,7 @@ async function findFiles(params) {
9580
9339
  const summary = `Found ${filesWithStats.length} file(s)${truncated ? ` (showing first ${MAX_RESULTS})` : ""} matching: ${pattern}`;
9581
9340
  const dirInfo = dir_path ? `
9582
9341
  Directory: ${dir_path}` : "";
9583
- const filesList = results.map((file) => path9.relative(baseCwd, file.path)).join("\n");
9342
+ const filesList = results.map((file) => path8.relative(baseCwd, file.path)).join("\n");
9584
9343
  return `${summary}${dirInfo}
9585
9344
 
9586
9345
  ${filesList}`;
@@ -9634,7 +9393,7 @@ var globFilesTool = {
9634
9393
 
9635
9394
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/grepSearch/index.js
9636
9395
  import { stat as stat2 } from "fs/promises";
9637
- import path10 from "path";
9396
+ import path9 from "path";
9638
9397
  import { execFile } from "child_process";
9639
9398
  import { promisify } from "util";
9640
9399
  import { createRequire } from "module";
@@ -9643,18 +9402,18 @@ var require2 = createRequire(import.meta.url);
9643
9402
  function getRipgrepPath() {
9644
9403
  try {
9645
9404
  const ripgrepPath = require2.resolve("@vscode/ripgrep");
9646
- const ripgrepDir = path10.dirname(ripgrepPath);
9647
- const rgBinary = path10.join(ripgrepDir, "..", "bin", "rg" + (process.platform === "win32" ? ".exe" : ""));
9405
+ const ripgrepDir = path9.dirname(ripgrepPath);
9406
+ const rgBinary = path9.join(ripgrepDir, "..", "bin", "rg" + (process.platform === "win32" ? ".exe" : ""));
9648
9407
  return rgBinary;
9649
9408
  } catch (error) {
9650
9409
  throw new Error("ripgrep is not available. Please install @vscode/ripgrep: pnpm add @vscode/ripgrep --filter @bike4mind/services");
9651
9410
  }
9652
9411
  }
9653
9412
  function isPathWithinWorkspace(targetPath, baseCwd) {
9654
- const resolvedTarget = path10.resolve(targetPath);
9655
- const resolvedBase = path10.resolve(baseCwd);
9656
- const relativePath = path10.relative(resolvedBase, resolvedTarget);
9657
- return !relativePath.startsWith("..") && !path10.isAbsolute(relativePath);
9413
+ const resolvedTarget = path9.resolve(targetPath);
9414
+ const resolvedBase = path9.resolve(baseCwd);
9415
+ const relativePath = path9.relative(resolvedBase, resolvedTarget);
9416
+ return !relativePath.startsWith("..") && !path9.isAbsolute(relativePath);
9658
9417
  }
9659
9418
  function convertGlobToRipgrepGlobs(globPattern) {
9660
9419
  if (!globPattern || globPattern === "**/*") {
@@ -9670,7 +9429,7 @@ function convertGlobToRipgrepGlobs(globPattern) {
9670
9429
  async function searchFiles2(params) {
9671
9430
  const { pattern, dir_path, include } = params;
9672
9431
  const baseCwd = process.cwd();
9673
- const targetDir = dir_path ? path10.resolve(baseCwd, dir_path) : baseCwd;
9432
+ const targetDir = dir_path ? path9.resolve(baseCwd, dir_path) : baseCwd;
9674
9433
  if (!isPathWithinWorkspace(targetDir, baseCwd)) {
9675
9434
  throw new Error(`Path validation failed: "${dir_path}" resolves outside the allowed workspace directory`);
9676
9435
  }
@@ -9732,7 +9491,7 @@ async function searchFiles2(params) {
9732
9491
  if (item.type === "match") {
9733
9492
  const match = item;
9734
9493
  allMatches.push({
9735
- filePath: path10.relative(targetDir, match.data.path.text) || path10.basename(match.data.path.text),
9494
+ filePath: path9.relative(targetDir, match.data.path.text) || path9.basename(match.data.path.text),
9736
9495
  lineNumber: match.data.line_number,
9737
9496
  line: match.data.lines.text.trimEnd()
9738
9497
  // Remove trailing newline
@@ -9825,14 +9584,14 @@ var grepSearchTool = {
9825
9584
  };
9826
9585
 
9827
9586
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/deleteFile/index.js
9828
- import { promises as fs10 } from "fs";
9587
+ import { promises as fs9 } from "fs";
9829
9588
  import { existsSync as existsSync5, statSync as statSync5 } from "fs";
9830
- import path11 from "path";
9589
+ import path10 from "path";
9831
9590
  async function deleteFile(params) {
9832
9591
  const { path: filePath, recursive = false } = params;
9833
- const normalizedPath = path11.normalize(filePath);
9834
- const resolvedPath = path11.resolve(process.cwd(), normalizedPath);
9835
- const cwd = path11.resolve(process.cwd());
9592
+ const normalizedPath = path10.normalize(filePath);
9593
+ const resolvedPath = path10.resolve(process.cwd(), normalizedPath);
9594
+ const cwd = path10.resolve(process.cwd());
9836
9595
  if (!resolvedPath.startsWith(cwd)) {
9837
9596
  throw new Error(`Access denied: Cannot delete files outside of current working directory`);
9838
9597
  }
@@ -9846,10 +9605,10 @@ async function deleteFile(params) {
9846
9605
  throw new Error(`Path is a directory: ${filePath}. Use recursive=true to delete directories and their contents.`);
9847
9606
  }
9848
9607
  if (isDirectory) {
9849
- await fs10.rm(resolvedPath, { recursive: true, force: true });
9608
+ await fs9.rm(resolvedPath, { recursive: true, force: true });
9850
9609
  return `Directory deleted successfully: ${filePath}`;
9851
9610
  } else {
9852
- await fs10.unlink(resolvedPath);
9611
+ await fs9.unlink(resolvedPath);
9853
9612
  return `File deleted successfully: ${filePath}
9854
9613
  Size: ${size} bytes`;
9855
9614
  }
@@ -9859,7 +9618,7 @@ var deleteFileTool = {
9859
9618
  implementation: (context) => ({
9860
9619
  toolFn: async (value) => {
9861
9620
  const params = value;
9862
- const resolvedPath = path11.resolve(process.cwd(), path11.normalize(params.path));
9621
+ const resolvedPath = path10.resolve(process.cwd(), path10.normalize(params.path));
9863
9622
  const isDirectory = existsSync5(resolvedPath) && statSync5(resolvedPath).isDirectory();
9864
9623
  context.logger.info(`\u{1F5D1}\uFE0F DeleteFile: Deleting ${isDirectory ? "directory" : "file"}`, {
9865
9624
  path: params.path,
@@ -9985,7 +9744,7 @@ var knowledgeBaseSearchTool = {
9985
9744
 
9986
9745
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/bashExecute/index.js
9987
9746
  import { spawn } from "child_process";
9988
- import path12 from "path";
9747
+ import path11 from "path";
9989
9748
  var DEFAULT_TIMEOUT_MS = 6e4;
9990
9749
  var MAX_OUTPUT_SIZE = 100 * 1024;
9991
9750
  var DANGEROUS_PATTERNS = [
@@ -10169,7 +9928,7 @@ async function executeBashCommand(params) {
10169
9928
  };
10170
9929
  }
10171
9930
  const baseCwd = process.cwd();
10172
- const targetCwd = relativeCwd ? path12.resolve(baseCwd, relativeCwd) : baseCwd;
9931
+ const targetCwd = relativeCwd ? path11.resolve(baseCwd, relativeCwd) : baseCwd;
10173
9932
  const effectiveTimeout = Math.min(timeout, 5 * 60 * 1e3);
10174
9933
  return new Promise((resolve3) => {
10175
9934
  let stdout = "";
@@ -10359,9 +10118,9 @@ BLOCKED OPERATIONS:
10359
10118
  };
10360
10119
 
10361
10120
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/editLocalFile/index.js
10362
- import { promises as fs11 } from "fs";
10121
+ import { promises as fs10 } from "fs";
10363
10122
  import { existsSync as existsSync6 } from "fs";
10364
- import path13 from "path";
10123
+ import path12 from "path";
10365
10124
  import { diffLines as diffLines3 } from "diff";
10366
10125
  function generateDiff(original, modified) {
10367
10126
  const differences = diffLines3(original, modified);
@@ -10385,16 +10144,16 @@ function generateDiff(original, modified) {
10385
10144
  }
10386
10145
  async function editLocalFile(params) {
10387
10146
  const { path: filePath, old_string, new_string } = params;
10388
- const normalizedPath = path13.normalize(filePath);
10389
- const resolvedPath = path13.resolve(process.cwd(), normalizedPath);
10390
- const cwd = path13.resolve(process.cwd());
10147
+ const normalizedPath = path12.normalize(filePath);
10148
+ const resolvedPath = path12.resolve(process.cwd(), normalizedPath);
10149
+ const cwd = path12.resolve(process.cwd());
10391
10150
  if (!resolvedPath.startsWith(cwd)) {
10392
10151
  throw new Error(`Access denied: Cannot edit files outside of current working directory`);
10393
10152
  }
10394
10153
  if (!existsSync6(resolvedPath)) {
10395
10154
  throw new Error(`File not found: ${filePath}`);
10396
10155
  }
10397
- const currentContent = await fs11.readFile(resolvedPath, "utf-8");
10156
+ const currentContent = await fs10.readFile(resolvedPath, "utf-8");
10398
10157
  if (!currentContent.includes(old_string)) {
10399
10158
  const preview = old_string.length > 100 ? old_string.substring(0, 100) + "..." : old_string;
10400
10159
  throw new Error(`String to replace not found in file. Make sure the old_string matches exactly (including whitespace and line endings). Searched for: "${preview}"`);
@@ -10404,7 +10163,7 @@ async function editLocalFile(params) {
10404
10163
  throw new Error(`Found ${occurrences} occurrences of the string to replace. Please provide a more specific old_string that matches exactly one location.`);
10405
10164
  }
10406
10165
  const newContent = currentContent.replace(old_string, new_string);
10407
- await fs11.writeFile(resolvedPath, newContent, "utf-8");
10166
+ await fs10.writeFile(resolvedPath, newContent, "utf-8");
10408
10167
  const diffResult = generateDiff(old_string, new_string);
10409
10168
  return `File edited successfully: ${filePath}
10410
10169
  Changes: +${diffResult.additions} lines, -${diffResult.deletions} lines
@@ -10979,7 +10738,9 @@ var QuestStartBodySchema = z139.object({
10979
10738
  fabFileIds: z139.array(z139.string()).optional()
10980
10739
  })).optional(),
10981
10740
  /** User's timezone (IANA format, e.g., "America/New_York") */
10982
- timezone: z139.string().optional()
10741
+ timezone: z139.string().optional(),
10742
+ /** Pre-fetched API key table from invoke phase — avoids redundant DB call in process (#6616 P1-a) */
10743
+ apiKeyTable: z139.record(z139.string(), z139.string().nullable()).optional()
10983
10744
  });
10984
10745
 
10985
10746
  // ../../b4m-core/packages/services/dist/src/llm/StatusManager.js
@@ -12335,8 +12096,8 @@ function getEnvironmentName(configApiConfig) {
12335
12096
  }
12336
12097
 
12337
12098
  // src/utils/contextLoader.ts
12338
- import * as fs12 from "fs";
12339
- import * as path14 from "path";
12099
+ import * as fs11 from "fs";
12100
+ import * as path13 from "path";
12340
12101
  import { homedir as homedir3 } from "os";
12341
12102
  var CONTEXT_FILE_SIZE_LIMIT = 100 * 1024;
12342
12103
  var PROJECT_CONTEXT_FILES = [
@@ -12357,9 +12118,9 @@ function formatFileSize2(bytes) {
12357
12118
  return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
12358
12119
  }
12359
12120
  function tryReadContextFile(dir, filename, source) {
12360
- const filePath = path14.join(dir, filename);
12121
+ const filePath = path13.join(dir, filename);
12361
12122
  try {
12362
- const stats = fs12.lstatSync(filePath);
12123
+ const stats = fs11.lstatSync(filePath);
12363
12124
  if (stats.isDirectory()) {
12364
12125
  return null;
12365
12126
  }
@@ -12373,7 +12134,7 @@ function tryReadContextFile(dir, filename, source) {
12373
12134
  error: `${source === "global" ? "Global" : "Project"} ${filename} exceeds 100KB limit (${formatFileSize2(stats.size)})`
12374
12135
  };
12375
12136
  }
12376
- const content = fs12.readFileSync(filePath, "utf-8");
12137
+ const content = fs11.readFileSync(filePath, "utf-8");
12377
12138
  return {
12378
12139
  filename,
12379
12140
  content,
@@ -12425,7 +12186,7 @@ ${project.content}`;
12425
12186
  }
12426
12187
  async function loadContextFiles(projectDir) {
12427
12188
  const errors = [];
12428
- const globalDir = path14.join(homedir3(), ".bike4mind");
12189
+ const globalDir = path13.join(homedir3(), ".bike4mind");
12429
12190
  const projectDirectory = projectDir || process.cwd();
12430
12191
  const [globalResult, projectResult] = await Promise.all([
12431
12192
  Promise.resolve(findContextFile(globalDir, GLOBAL_CONTEXT_FILES, "global")),
@@ -12696,7 +12457,7 @@ function substituteArguments(template, args) {
12696
12457
  // ../../b4m-core/packages/mcp/dist/src/client.js
12697
12458
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
12698
12459
  import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
12699
- import path15 from "path";
12460
+ import path14 from "path";
12700
12461
  import { existsSync as existsSync8, readdirSync as readdirSync3 } from "fs";
12701
12462
  var MCPClient = class {
12702
12463
  // Note: This class handles MCP server communication with repository filtering
@@ -12734,21 +12495,20 @@ var MCPClient = class {
12734
12495
  if (this.customCommand && this.customCommand.trim() !== "") {
12735
12496
  command = this.customCommand;
12736
12497
  args = this.customArgs ?? [];
12737
- console.log(`[MCP] Using external command for ${this.serverName}: ${command} ${args.join(" ")}`);
12738
12498
  } else {
12739
12499
  const root = process.env.INIT_CWD || process.cwd();
12740
12500
  const candidatePaths = [
12741
12501
  // When running from SST Lambda with node_modules structure (copyFiles)
12742
- path15.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
12502
+ path14.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
12743
12503
  // When running from SST Lambda deployed environment (/var/task)
12744
- path15.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
12504
+ path14.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
12745
12505
  // When running from SST Lambda (.sst/artifacts/mcpHandler-dev), navigate to monorepo root (3 levels up)
12746
- path15.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
12506
+ path14.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
12747
12507
  // When running from packages/client (Next.js app), navigate to monorepo root (2 levels up)
12748
- path15.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
12508
+ path14.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
12749
12509
  // Original paths (backward compatibility)
12750
- path15.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
12751
- path15.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
12510
+ path14.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
12511
+ path14.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
12752
12512
  ];
12753
12513
  const serverScriptPath = candidatePaths.find((p) => existsSync8(p));
12754
12514
  if (!serverScriptPath) {
@@ -13563,7 +13323,7 @@ var ServerLlmBackend = class {
13563
13323
  logger.info("\u26A0\uFE0F Using fallback model list (no CLI-compatible models available)");
13564
13324
  return this.getFallbackModels();
13565
13325
  }
13566
- logger.info(`\u{1F4CB} Loaded ${filteredModels.length} models from server`);
13326
+ logger.debug(`\u{1F4CB} Loaded ${filteredModels.length} models from server`);
13567
13327
  return filteredModels;
13568
13328
  } catch (error) {
13569
13329
  logger.warn(
@@ -13801,7 +13561,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
13801
13561
  // package.json
13802
13562
  var package_default = {
13803
13563
  name: "@bike4mind/cli",
13804
- version: "0.2.28",
13564
+ version: "0.2.29-cache-mcp-tools-schema-and-refactor.18845+131a0f301",
13805
13565
  type: "module",
13806
13566
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
13807
13567
  license: "UNLICENSED",
@@ -13896,10 +13656,12 @@ var package_default = {
13896
13656
  sharp: "^0.34.5",
13897
13657
  speakeasy: "^2.0.0",
13898
13658
  tiktoken: "^1.0.16",
13659
+ "tree-sitter-wasms": "^0.1.13",
13899
13660
  turndown: "^7.2.0",
13900
13661
  unpdf: "^0.10.0",
13901
13662
  uuid: "^9.0.1",
13902
13663
  voyageai: "^0.0.4",
13664
+ "web-tree-sitter": "0.25.10",
13903
13665
  xlsx: "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
13904
13666
  yargs: "^17.7.2",
13905
13667
  yauzl: "^3.1.3",
@@ -13909,10 +13671,10 @@ var package_default = {
13909
13671
  },
13910
13672
  devDependencies: {
13911
13673
  "@bike4mind/agents": "0.1.0",
13912
- "@bike4mind/common": "2.50.0",
13913
- "@bike4mind/mcp": "1.29.0",
13914
- "@bike4mind/services": "2.48.0",
13915
- "@bike4mind/utils": "2.5.0",
13674
+ "@bike4mind/common": "2.50.1-cache-mcp-tools-schema-and-refactor.18845+131a0f301",
13675
+ "@bike4mind/mcp": "1.29.1-cache-mcp-tools-schema-and-refactor.18845+131a0f301",
13676
+ "@bike4mind/services": "2.48.1-cache-mcp-tools-schema-and-refactor.18845+131a0f301",
13677
+ "@bike4mind/utils": "2.5.1-cache-mcp-tools-schema-and-refactor.18845+131a0f301",
13916
13678
  "@types/better-sqlite3": "^7.6.13",
13917
13679
  "@types/diff": "^5.0.9",
13918
13680
  "@types/jsonwebtoken": "^9.0.4",
@@ -13929,7 +13691,7 @@ var package_default = {
13929
13691
  optionalDependencies: {
13930
13692
  "@vscode/ripgrep": "^1.17.0"
13931
13693
  },
13932
- gitHead: "28d4afd2bd097efcd751a14cbda1eee68cae2ad2"
13694
+ gitHead: "131a0f301e2a9170b2d207809ee117065b7bf54d"
13933
13695
  };
13934
13696
 
13935
13697
  // src/config/constants.ts
@@ -14422,9 +14184,9 @@ var SubagentOrchestrator = class {
14422
14184
  };
14423
14185
 
14424
14186
  // src/agents/AgentStore.ts
14425
- import fs13 from "fs/promises";
14426
- import path16 from "path";
14427
- import os3 from "os";
14187
+ import fs12 from "fs/promises";
14188
+ import path15 from "path";
14189
+ import os2 from "os";
14428
14190
  import matter2 from "gray-matter";
14429
14191
  var FULL_MODEL_ID_PREFIXES = [
14430
14192
  "claude-",
@@ -14570,12 +14332,12 @@ var AgentStore = class {
14570
14332
  constructor(builtinDir, projectRoot) {
14571
14333
  this.agents = /* @__PURE__ */ new Map();
14572
14334
  const root = projectRoot || process.cwd();
14573
- const home = os3.homedir();
14335
+ const home = os2.homedir();
14574
14336
  this.builtinAgentsDir = builtinDir;
14575
- this.globalB4MAgentsDir = path16.join(home, ".bike4mind", "agents");
14576
- this.globalClaudeAgentsDir = path16.join(home, ".claude", "agents");
14577
- this.projectB4MAgentsDir = path16.join(root, ".bike4mind", "agents");
14578
- this.projectClaudeAgentsDir = path16.join(root, ".claude", "agents");
14337
+ this.globalB4MAgentsDir = path15.join(home, ".bike4mind", "agents");
14338
+ this.globalClaudeAgentsDir = path15.join(home, ".claude", "agents");
14339
+ this.projectB4MAgentsDir = path15.join(root, ".bike4mind", "agents");
14340
+ this.projectClaudeAgentsDir = path15.join(root, ".claude", "agents");
14579
14341
  }
14580
14342
  /**
14581
14343
  * Load all agents from all directories
@@ -14599,7 +14361,7 @@ var AgentStore = class {
14599
14361
  */
14600
14362
  async loadAgentsFromDirectory(directory, source) {
14601
14363
  try {
14602
- const stats = await fs13.stat(directory);
14364
+ const stats = await fs12.stat(directory);
14603
14365
  if (!stats.isDirectory()) {
14604
14366
  return;
14605
14367
  }
@@ -14627,9 +14389,9 @@ var AgentStore = class {
14627
14389
  async findAgentFiles(directory) {
14628
14390
  const files = [];
14629
14391
  try {
14630
- const entries = await fs13.readdir(directory, { withFileTypes: true });
14392
+ const entries = await fs12.readdir(directory, { withFileTypes: true });
14631
14393
  for (const entry of entries) {
14632
- const fullPath = path16.join(directory, entry.name);
14394
+ const fullPath = path15.join(directory, entry.name);
14633
14395
  if (entry.isDirectory()) {
14634
14396
  const subFiles = await this.findAgentFiles(fullPath);
14635
14397
  files.push(...subFiles);
@@ -14646,10 +14408,10 @@ var AgentStore = class {
14646
14408
  * Parse a single agent markdown file
14647
14409
  */
14648
14410
  async parseAgentFile(filePath, source) {
14649
- const content = await fs13.readFile(filePath, "utf-8");
14411
+ const content = await fs12.readFile(filePath, "utf-8");
14650
14412
  const { data: frontmatter, content: body } = matter2(content);
14651
14413
  const parsed = AgentFrontmatterSchema.parse(frontmatter);
14652
- const name = path16.basename(filePath, ".md");
14414
+ const name = path15.basename(filePath, ".md");
14653
14415
  const modelInput = parsed.model || DEFAULT_AGENT_MODEL;
14654
14416
  const resolution = resolveModelAlias(modelInput, name, filePath);
14655
14417
  if (!resolution.resolved && resolution.warning) {
@@ -14729,16 +14491,16 @@ var AgentStore = class {
14729
14491
  */
14730
14492
  async createAgentFile(name, isGlobal = false, useClaude = true) {
14731
14493
  const targetDir = isGlobal ? useClaude ? this.globalClaudeAgentsDir : this.globalB4MAgentsDir : useClaude ? this.projectClaudeAgentsDir : this.projectB4MAgentsDir;
14732
- const filePath = path16.join(targetDir, `${name}.md`);
14494
+ const filePath = path15.join(targetDir, `${name}.md`);
14733
14495
  try {
14734
- await fs13.access(filePath);
14496
+ await fs12.access(filePath);
14735
14497
  throw new Error(`Agent file already exists: ${filePath}`);
14736
14498
  } catch (error) {
14737
14499
  if (error.code !== "ENOENT") {
14738
14500
  throw error;
14739
14501
  }
14740
14502
  }
14741
- await fs13.mkdir(targetDir, { recursive: true });
14503
+ await fs12.mkdir(targetDir, { recursive: true });
14742
14504
  const template = `---
14743
14505
  description: ${name} agent description
14744
14506
  model: claude-3-5-haiku-20241022
@@ -14773,7 +14535,7 @@ You are a ${name} specialist. Your job is to [describe primary task].
14773
14535
  ## Output Format
14774
14536
  Describe the expected output format here.
14775
14537
  `;
14776
- await fs13.writeFile(filePath, template, "utf-8");
14538
+ await fs12.writeFile(filePath, template, "utf-8");
14777
14539
  return filePath;
14778
14540
  }
14779
14541
  /**
@@ -15411,7 +15173,7 @@ function createTodoStore(onUpdate) {
15411
15173
 
15412
15174
  // src/tools/findDefinitionTool.ts
15413
15175
  import { stat as stat3 } from "fs/promises";
15414
- import path17 from "path";
15176
+ import path16 from "path";
15415
15177
  import { execFile as execFile2 } from "child_process";
15416
15178
  import { promisify as promisify2 } from "util";
15417
15179
  import { createRequire as createRequire2 } from "module";
@@ -15432,8 +15194,8 @@ var ALL_KEYWORDS = Object.values(KIND_KEYWORDS).flat();
15432
15194
  function getRipgrepPath2() {
15433
15195
  try {
15434
15196
  const ripgrepPath = require3.resolve("@vscode/ripgrep");
15435
- const ripgrepDir = path17.dirname(ripgrepPath);
15436
- return path17.join(ripgrepDir, "..", "bin", "rg" + (process.platform === "win32" ? ".exe" : ""));
15197
+ const ripgrepDir = path16.dirname(ripgrepPath);
15198
+ return path16.join(ripgrepDir, "..", "bin", "rg" + (process.platform === "win32" ? ".exe" : ""));
15437
15199
  } catch {
15438
15200
  throw new Error(
15439
15201
  "ripgrep is not available. Please install @vscode/ripgrep: pnpm add @vscode/ripgrep --filter @bike4mind/services"
@@ -15441,10 +15203,10 @@ function getRipgrepPath2() {
15441
15203
  }
15442
15204
  }
15443
15205
  function isPathWithinWorkspace2(targetPath, baseCwd) {
15444
- const resolvedTarget = path17.resolve(targetPath);
15445
- const resolvedBase = path17.resolve(baseCwd);
15446
- const relativePath = path17.relative(resolvedBase, resolvedTarget);
15447
- return !relativePath.startsWith("..") && !path17.isAbsolute(relativePath);
15206
+ const resolvedTarget = path16.resolve(targetPath);
15207
+ const resolvedBase = path16.resolve(baseCwd);
15208
+ const relativePath = path16.relative(resolvedBase, resolvedTarget);
15209
+ return !relativePath.startsWith("..") && !path16.isAbsolute(relativePath);
15448
15210
  }
15449
15211
  function escapeRegex(str) {
15450
15212
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -15477,7 +15239,7 @@ async function findDefinitions(params) {
15477
15239
  throw new Error("symbol_name is required");
15478
15240
  }
15479
15241
  const baseCwd = process.cwd();
15480
- const targetDir = search_path ? path17.resolve(baseCwd, search_path) : baseCwd;
15242
+ const targetDir = search_path ? path16.resolve(baseCwd, search_path) : baseCwd;
15481
15243
  if (!isPathWithinWorkspace2(targetDir, baseCwd)) {
15482
15244
  throw new Error(`Path validation failed: "${search_path}" resolves outside the allowed workspace directory`);
15483
15245
  }
@@ -15532,7 +15294,7 @@ async function findDefinitions(params) {
15532
15294
  const lineText = match.data.lines.text.trimEnd();
15533
15295
  if (isLikelyDefinition(lineText)) {
15534
15296
  allMatches.push({
15535
- filePath: path17.relative(targetDir, match.data.path.text) || path17.basename(match.data.path.text),
15297
+ filePath: path16.relative(targetDir, match.data.path.text) || path16.basename(match.data.path.text),
15536
15298
  lineNumber: match.data.line_number,
15537
15299
  line: lineText
15538
15300
  });
@@ -15609,6 +15371,99 @@ function createFindDefinitionTool() {
15609
15371
  };
15610
15372
  }
15611
15373
 
15374
+ // src/tools/getFileStructure/index.ts
15375
+ import { existsSync as existsSync9, promises as fs13, statSync as statSync6 } from "fs";
15376
+ import path17 from "path";
15377
+
15378
+ // src/tools/getFileStructure/formatter.ts
15379
+ var SECTIONS = [
15380
+ { title: "IMPORTS", kind: "import", exportable: false },
15381
+ { title: "EXPORTS", kind: "export", exportable: false },
15382
+ { title: "FUNCTIONS", kind: "function", exportable: true },
15383
+ { title: "CLASSES", kind: "class", exportable: true },
15384
+ { title: "INTERFACES", kind: "interface", exportable: true },
15385
+ { title: "TYPES", kind: "type", exportable: true },
15386
+ { title: "ENUMS", kind: "enum", exportable: true }
15387
+ ];
15388
+ function formatStructureOutput(filePath, items, fileSize, lineCount) {
15389
+ const sizeStr = fileSize < 1024 ? `${fileSize} B` : `${(fileSize / 1024).toFixed(1)} KB`;
15390
+ const lines = [`File: ${filePath} (${sizeStr}, ${lineCount} lines)`, ""];
15391
+ for (const section of SECTIONS) {
15392
+ const sectionItems = items.filter((i) => i.kind === section.kind);
15393
+ formatSection(lines, section.title, sectionItems, (item) => {
15394
+ const prefix = section.exportable && item.exported ? "export " : "";
15395
+ return `L${item.line}: ${prefix}${item.name}`;
15396
+ });
15397
+ }
15398
+ return lines.join("\n").trimEnd();
15399
+ }
15400
+ function formatSection(lines, title, items, format) {
15401
+ lines.push(`${title} (${items.length}):`);
15402
+ if (items.length === 0) {
15403
+ lines.push(" (none)");
15404
+ } else {
15405
+ for (const item of items) {
15406
+ lines.push(` ${format(item)}`);
15407
+ }
15408
+ }
15409
+ lines.push("");
15410
+ }
15411
+
15412
+ // src/tools/getFileStructure/index.ts
15413
+ var MAX_FILE_SIZE3 = 10 * 1024 * 1024;
15414
+ function createGetFileStructureTool() {
15415
+ return {
15416
+ toolFn: async (value) => {
15417
+ const params = value;
15418
+ try {
15419
+ const cwd = process.cwd();
15420
+ const resolvedPath = path17.resolve(cwd, params.path);
15421
+ if (!resolvedPath.startsWith(cwd)) {
15422
+ return "Error: Access denied - cannot read files outside of current working directory";
15423
+ }
15424
+ if (!existsSync9(resolvedPath)) {
15425
+ return `Error: File not found: ${params.path}`;
15426
+ }
15427
+ const stats = statSync6(resolvedPath);
15428
+ if (stats.isDirectory()) {
15429
+ return `Error: Path is a directory, not a file: ${params.path}`;
15430
+ }
15431
+ if (stats.size > MAX_FILE_SIZE3) {
15432
+ return `Error: File too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Max: ${MAX_FILE_SIZE3 / 1024 / 1024}MB`;
15433
+ }
15434
+ const ext = path17.extname(resolvedPath).toLowerCase();
15435
+ const { getLanguageForExtension, parseFileStructure, getSupportedLanguages } = await import("./treeSitterEngine-4SGFQDY3.js");
15436
+ const languageId = getLanguageForExtension(ext);
15437
+ if (!languageId) {
15438
+ const supported = getSupportedLanguages();
15439
+ return `Error: Unsupported file type "${ext}". Supported languages: ${supported.join(", ")}`;
15440
+ }
15441
+ const sourceCode = await fs13.readFile(resolvedPath, "utf-8");
15442
+ const lineCount = sourceCode.split("\n").length;
15443
+ const items = await parseFileStructure(sourceCode, languageId);
15444
+ return formatStructureOutput(params.path, items, stats.size, lineCount);
15445
+ } catch (error) {
15446
+ const errorMessage = error instanceof Error ? error.message : String(error);
15447
+ return `Error analyzing file structure: ${errorMessage}`;
15448
+ }
15449
+ },
15450
+ toolSchema: {
15451
+ name: "get_file_structure",
15452
+ description: "Extract the high-level structure of a source file (imports, exports, functions, classes, interfaces, types) using AST parsing. Returns a concise outline with line numbers. Much more efficient than file_read when you only need to understand what a file contains or exports. Use this before file_read to decide which parts of a large file to read.",
15453
+ parameters: {
15454
+ type: "object",
15455
+ properties: {
15456
+ path: {
15457
+ type: "string",
15458
+ description: "Path to the source file (relative to cwd or absolute within cwd). Supported: .ts, .tsx, .js, .jsx, .mjs, .cjs, .py"
15459
+ }
15460
+ },
15461
+ required: ["path"]
15462
+ }
15463
+ }
15464
+ };
15465
+ }
15466
+
15612
15467
  // src/index.tsx
15613
15468
  process.removeAllListeners("warning");
15614
15469
  process.on("warning", (warning) => {
@@ -15725,6 +15580,7 @@ function CliApp() {
15725
15580
  });
15726
15581
  const init = useCallback2(async () => {
15727
15582
  try {
15583
+ const startupLog = [];
15728
15584
  const config = await state.configStore.load();
15729
15585
  const history = await state.commandHistoryStore.load();
15730
15586
  setCommandHistory(history);
@@ -15732,7 +15588,7 @@ function CliApp() {
15732
15588
  await state.customCommandStore.loadCommands();
15733
15589
  const customCommandCount = state.customCommandStore.getCommandCount();
15734
15590
  if (customCommandCount > 0) {
15735
- console.log(`\u{1F4DD} Loaded ${customCommandCount} custom command${customCommandCount !== 1 ? "s" : ""}`);
15591
+ startupLog.push(`\u{1F4DD} Loaded ${customCommandCount} custom command${customCommandCount !== 1 ? "s" : ""}`);
15736
15592
  }
15737
15593
  } catch (error) {
15738
15594
  console.warn("Failed to load custom commands:", error instanceof Error ? error.message : String(error));
@@ -15753,7 +15609,7 @@ function CliApp() {
15753
15609
  } else {
15754
15610
  isAuthenticated = true;
15755
15611
  const daysUntilExpiry = Math.floor((expiresAt.getTime() - Date.now()) / (1e3 * 60 * 60 * 24));
15756
- console.log(`\u2705 Authenticated (expires in ${daysUntilExpiry} day${daysUntilExpiry !== 1 ? "s" : ""})`);
15612
+ startupLog.push(`\u2705 Authenticated (expires in ${daysUntilExpiry} day${daysUntilExpiry !== 1 ? "s" : ""})`);
15757
15613
  }
15758
15614
  }
15759
15615
  if (!isAuthenticated) {
@@ -15867,22 +15723,23 @@ function CliApp() {
15867
15723
  state.configStore,
15868
15724
  apiClient
15869
15725
  );
15870
- console.log(`\u{1F6E0}\uFE0F Loaded ${b4mTools2.length} B4M tool(s)`);
15726
+ startupLog.push(`\u{1F6E0}\uFE0F Loaded ${b4mTools2.length} B4M tool(s)`);
15871
15727
  const mcpManager = new McpManager(config);
15872
15728
  await mcpManager.initialize();
15873
15729
  const mcpTools = mcpManager.getTools();
15874
15730
  if (mcpTools.length > 0) {
15875
- const toolNames = mcpTools.map((t) => t.toolSchema.name).join(", ");
15876
- console.log(`\u{1F4E1} Loaded ${mcpTools.length} MCP tool(s): ${toolNames}`);
15731
+ const toolCountByServer = mcpManager.getToolCount();
15732
+ const serverSummaries = toolCountByServer.map((s) => `${s.serverName} (${s.count})`).join(", ");
15733
+ startupLog.push(`\u{1F4E1} Loaded ${mcpTools.length} MCP tool(s): ${serverSummaries}`);
15877
15734
  } else {
15878
- console.log(`\u{1F4E1} No MCP tools loaded`);
15735
+ startupLog.push(`\u{1F4E1} No MCP tools loaded`);
15879
15736
  }
15880
15737
  const builtinAgentsDir = new URL("./agents/defaults/", import.meta.url).pathname;
15881
15738
  const agentProjectDir = state.configStore.getProjectConfigDir();
15882
15739
  const agentStore = new AgentStore(builtinAgentsDir, agentProjectDir || process.cwd());
15883
15740
  await agentStore.loadAgents();
15884
15741
  const agentSummary = agentStore.getSummary();
15885
- console.log(
15742
+ startupLog.push(
15886
15743
  `\u{1F916} Loaded ${agentSummary.total} agent(s): ${agentSummary.builtin} built-in, ${agentSummary.global} global, ${agentSummary.project} project`
15887
15744
  );
15888
15745
  const orchestrator = new SubagentOrchestrator({
@@ -15917,18 +15774,25 @@ function CliApp() {
15917
15774
  sessionId: newSession.id
15918
15775
  }) : null;
15919
15776
  const findDefinitionTool = createFindDefinitionTool();
15920
- const cliTools = [agentDelegateTool, ...backgroundTools, writeTodosTool, findDefinitionTool];
15777
+ const getFileStructureTool = createGetFileStructureTool();
15778
+ const cliTools = [
15779
+ agentDelegateTool,
15780
+ ...backgroundTools,
15781
+ writeTodosTool,
15782
+ findDefinitionTool,
15783
+ getFileStructureTool
15784
+ ];
15921
15785
  if (skillTool) {
15922
15786
  cliTools.push(skillTool);
15923
15787
  }
15924
15788
  const allTools = [...b4mTools2, ...mcpTools, ...cliTools];
15925
- console.log(`\u{1F4C2} Working directory: ${process.cwd()}`);
15789
+ startupLog.push(`\u{1F4C2} Working directory: ${process.cwd()}`);
15926
15790
  const agentNamesList = agentStore.getAgentNames().join(", ");
15927
- console.log(`\u{1F916} Subagent delegation enabled (${agentNamesList}) + background execution`);
15791
+ startupLog.push(`\u{1F916} Subagent delegation enabled (${agentNamesList}) + background execution`);
15928
15792
  if (skillTool) {
15929
15793
  const skillCount = state.customCommandStore.getCommandCount();
15930
15794
  if (skillCount > 0) {
15931
- console.log(`\u{1F6E0}\uFE0F Skill tool enabled (${skillCount} skills available)`);
15795
+ startupLog.push(`\u{1F6E0}\uFE0F Skill tool enabled (${skillCount} skills available)`);
15932
15796
  }
15933
15797
  }
15934
15798
  logger.debug(
@@ -15937,13 +15801,13 @@ function CliApp() {
15937
15801
  const projectDir = state.configStore.getProjectConfigDir();
15938
15802
  const contextResult = await loadContextFiles(projectDir);
15939
15803
  if (contextResult.globalContext) {
15940
- console.log(`\u{1F4C4} Global context: ${contextResult.globalContext.filename}`);
15804
+ startupLog.push(`\u{1F4C4} Global context: ${contextResult.globalContext.filename}`);
15941
15805
  }
15942
15806
  if (contextResult.projectContext) {
15943
- console.log(`\u{1F4C4} Project context: ${contextResult.projectContext.filename}`);
15807
+ startupLog.push(`\u{1F4C4} Project context: ${contextResult.projectContext.filename}`);
15944
15808
  }
15945
15809
  for (const error of contextResult.errors) {
15946
- console.log(`\u26A0\uFE0F Context file error: ${error}`);
15810
+ startupLog.push(`\u26A0\uFE0F Context file error: ${error}`);
15947
15811
  }
15948
15812
  let contextSection = contextResult.mergedContent ? `
15949
15813
 
@@ -16019,8 +15883,36 @@ ${contextResult.mergedContent}` : "";
16019
15883
  // Store for grouped notification turn tracking
16020
15884
  }));
16021
15885
  setStoreSession(newSession);
15886
+ const bannerLines = [
15887
+ { text: "\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557", ansi: "\x1B[36m\x1B[1m" },
15888
+ { text: "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551", ansi: "\x1B[36m\x1B[1m" },
15889
+ { text: "\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551", ansi: "\x1B[36m\x1B[1m" },
15890
+ { text: "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551", ansi: "\x1B[36m\x1B[1m" },
15891
+ { text: "\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551", ansi: "\x1B[36m\x1B[1m" },
15892
+ { text: "\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D", ansi: "\x1B[36m\x1B[1m" },
15893
+ { text: "", ansi: "" },
15894
+ { text: `v${package_default.version} - AI-Powered CLI`, ansi: "\x1B[2m" },
15895
+ { text: "/help for more information", ansi: "\x1B[2m" }
15896
+ ];
15897
+ const bannerWidth = 30;
15898
+ const termWidth = process.stdout.columns || 80;
15899
+ const rightColWidth = termWidth - bannerWidth - 2;
15900
+ const truncate = (str, max) => {
15901
+ if (str.length > max) return str.slice(0, max - 1) + "\u2026";
15902
+ return str;
15903
+ };
15904
+ const totalLines = Math.max(bannerLines.length, startupLog.length);
15905
+ for (let i = 0; i < totalLines; i++) {
15906
+ const banner = bannerLines[i];
15907
+ const leftText = banner?.text || "";
15908
+ const leftAnsi = banner?.ansi || "";
15909
+ const right = startupLog[i] || "";
15910
+ const coloredLeft = leftText ? `${leftAnsi}${leftText}\x1B[0m` : "";
15911
+ const gap = " ".repeat(bannerWidth - leftText.length + 2);
15912
+ console.log(coloredLeft + gap + truncate(right, rightColWidth));
15913
+ }
16022
15914
  setIsInitialized(true);
16023
- console.log("\n");
15915
+ console.log("");
16024
15916
  } catch (error) {
16025
15917
  console.error("Initialization error:", error);
16026
15918
  setInitError(error instanceof Error ? error.message : "Unknown error");
@@ -16675,7 +16567,7 @@ Available commands:
16675
16567
  /whoami - Show current authenticated user
16676
16568
  /usage - Show credit usage and balance
16677
16569
  /save <name> - Save current session
16678
- /sessions - List saved sessions
16570
+ /resume - List and resume saved sessions
16679
16571
  /config - Show configuration
16680
16572
 
16681
16573
  API Configuration:
@@ -16694,7 +16586,24 @@ Project Configuration:
16694
16586
  Custom Commands:
16695
16587
  /commands - List all custom commands
16696
16588
  /commands:new <name> - Create a new custom command
16697
- /commands:reload - Reload custom commands from disk${hasCustomCommands ? "\n\n\u{1F4DD} Custom Commands Available:" : ""}${hasCustomCommands ? customCommands.map((cmd) => {
16589
+ /commands:reload - Reload custom commands from disk
16590
+
16591
+ Keyboard Shortcuts:
16592
+ Ctrl+C - Press twice to exit
16593
+ Esc - Abort current operation
16594
+ Shift+Tab - Toggle auto-accept edits
16595
+ Ctrl+U - Clear current line
16596
+ Ctrl+K - Clear from cursor to end of line
16597
+ Ctrl+W - Delete word before cursor
16598
+ Ctrl+A - Move cursor to beginning
16599
+ Ctrl+E - Move cursor to end
16600
+ Ctrl+B / \u2190 - Move cursor left
16601
+ Ctrl+F / \u2192 - Move cursor right
16602
+ Ctrl+D - Delete character at cursor
16603
+ Ctrl+L - Clear input
16604
+ \u2191 / \u2193 - Navigate history / autocomplete
16605
+ Tab - Accept autocomplete suggestion
16606
+ Shift+Cmd+Click - Open links in browser${hasCustomCommands ? "\n\n\u{1F4DD} Custom Commands Available:" : ""}${hasCustomCommands ? customCommands.map((cmd) => {
16698
16607
  const source = cmd.source === "global" ? "\u{1F3E0}" : "\u{1F4C1}";
16699
16608
  const argHint = cmd.argumentHint ? ` ${cmd.argumentHint}` : "";
16700
16609
  return `
@@ -16724,6 +16633,7 @@ Custom Commands:
16724
16633
  console.log(`\u2705 Session saved as "${sessionName}"`);
16725
16634
  break;
16726
16635
  }
16636
+ case "resume":
16727
16637
  case "sessions": {
16728
16638
  const sessions = await state.sessionStore.list(20);
16729
16639
  if (sessions.length === 0) {
@@ -16734,7 +16644,6 @@ Custom Commands:
16734
16644
  const handleSessionSelect = async (selectedSession) => {
16735
16645
  setState((prev) => ({ ...prev, sessionSelector: null }));
16736
16646
  if (!selectedSession) {
16737
- console.log("\u274C Session selection cancelled");
16738
16647
  return;
16739
16648
  }
16740
16649
  const loadedSession = await state.sessionStore.load(selectedSession.id);
@@ -17586,17 +17495,6 @@ No usage data available for the last ${USAGE_DAYS} days.`);
17586
17495
  }
17587
17496
  );
17588
17497
  }
17589
- var BANNER = `
17590
- \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557
17591
- \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551
17592
- \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551
17593
- \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551
17594
- \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551
17595
- \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
17596
- `;
17597
- console.log("\x1B[36m\x1B[1m%s\x1B[0m", BANNER);
17598
- console.log(`\x1B[2m v${package_default.version} - AI-Powered CLI\x1B[0m`);
17599
- console.log(" /help for more information\n");
17600
17498
  var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "development";
17601
17499
  if (isDevMode) {
17602
17500
  logger.debug("\u{1F527} Running in development mode (using TypeScript source)\n");