@bike4mind/cli 0.2.12-refactor-github-mcp.17405 → 0.2.14-feat-auto-context-customization.17477

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
@@ -4,9 +4,9 @@ import {
4
4
  getEffectiveApiKey,
5
5
  getOpenWeatherKey,
6
6
  getSerperKey
7
- } from "./chunk-ZHCX7BLB.js";
8
- import "./chunk-ID4NS6KF.js";
9
- import "./chunk-RJFNIMVH.js";
7
+ } from "./chunk-NR4J636Y.js";
8
+ import "./chunk-7CKPTTMR.js";
9
+ import "./chunk-QPYHK4UF.js";
10
10
  import {
11
11
  BFLImageService,
12
12
  BaseStorage,
@@ -15,10 +15,10 @@ import {
15
15
  OpenAIBackend,
16
16
  OpenAIImageService,
17
17
  XAIImageService
18
- } from "./chunk-BU5TP3BH.js";
18
+ } from "./chunk-TJAFO6U3.js";
19
19
  import {
20
20
  Logger
21
- } from "./chunk-AMDXHL6S.js";
21
+ } from "./chunk-OCYRD7D6.js";
22
22
  import "./chunk-BPFEGDC7.js";
23
23
  import {
24
24
  AiEvents,
@@ -73,10 +73,7 @@ import {
73
73
  XAI_IMAGE_MODELS,
74
74
  b4mLLMTools,
75
75
  getMcpProviderMetadata
76
- } from "./chunk-OHR7UCTC.js";
77
- import {
78
- __require
79
- } from "./chunk-PDX44BCA.js";
76
+ } from "./chunk-PGQJWOFR.js";
80
77
 
81
78
  // src/index.tsx
82
79
  import React17, { useState as useState8, useEffect as useEffect3, useCallback, useRef as useRef2 } from "react";
@@ -869,10 +866,11 @@ function InputPrompt({
869
866
  if (shouldShowCommandAutocomplete && filteredCommands.length > 0) {
870
867
  if (key.upArrow) {
871
868
  setSelectedIndex((prev) => prev > 0 ? prev - 1 : filteredCommands.length - 1);
869
+ return;
872
870
  } else if (key.downArrow) {
873
871
  setSelectedIndex((prev) => prev < filteredCommands.length - 1 ? prev + 1 : 0);
872
+ return;
874
873
  }
875
- return;
876
874
  }
877
875
  if (!shouldShowCommandAutocomplete && !fileAutocomplete?.active && history.length > 0) {
878
876
  if (key.upArrow) {
@@ -1351,7 +1349,7 @@ function MessageItem({ message }) {
1351
1349
  const isUser = message.role === "user";
1352
1350
  return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: isUser ? "cyan" : "green" }, isUser ? "\u{1F464} You" : "\u{1F916} Assistant"), isUser && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), isUser && message.content && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { backgroundColor: "whiteBright", color: "black" }, " ", message.content, " ")), !isUser && message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { 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__ */ React10.createElement(Text9, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
1353
1351
  if (step.type === "thought") {
1354
- return /* @__PURE__ */ React10.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` ${step.content.slice(0, 200)}${step.content.length > 200 ? "..." : ""}`));
1352
+ return /* @__PURE__ */ React10.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` ${step.content}`));
1355
1353
  }
1356
1354
  if (step.type === "action") {
1357
1355
  const toolName = step.metadata?.toolName || "unknown";
@@ -3140,7 +3138,8 @@ ${options.context}` : this.getSystemPrompt()
3140
3138
  tools: this.context.tools,
3141
3139
  maxTokens,
3142
3140
  temperature,
3143
- abortSignal: options.signal
3141
+ abortSignal: options.signal,
3142
+ tool_choice: this.context.toolChoice
3144
3143
  },
3145
3144
  async (texts, completionInfo) => {
3146
3145
  for (const text of texts) {
@@ -3394,6 +3393,15 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
3394
3393
  };
3395
3394
 
3396
3395
  // src/core/prompts.ts
3396
+ var TOOL_GREP_SEARCH = "grep_search";
3397
+ var TOOL_GLOB_FILES = "glob_files";
3398
+ var TOOL_FILE_READ = "file_read";
3399
+ var TOOL_EDIT_LOCAL_FILE = "edit_local_file";
3400
+ var TOOL_CREATE_FILE = "create_file";
3401
+ var TOOL_BASH_EXECUTE = "bash_execute";
3402
+ var TOOL_SUBAGENT_DELEGATE = "subagent_delegate";
3403
+ var TOOL_WRITE_TODOS = "write_todos";
3404
+ var EXPLORE_SUBAGENT_TYPE = "explore";
3397
3405
  function buildCoreSystemPrompt(contextSection = "") {
3398
3406
  return `You are an autonomous AI assistant with access to tools. Your job is to help users by taking action and solving problems proactively.
3399
3407
 
@@ -3405,15 +3413,17 @@ CORE BEHAVIOR:
3405
3413
 
3406
3414
  FOR SOFTWARE ENGINEERING TASKS:
3407
3415
  When requested to perform tasks like fixing bugs, adding features, refactoring, or explaining code, follow this sequence:
3408
- 1. **Understand:** Think about the user's request and the relevant codebase context. Use 'grep_search' and 'glob_files' search tools extensively (in parallel if independent) to understand file structures, existing code patterns, and conventions. Use 'file_read' to understand context and validate any assumptions you may have. If you need to read multiple files, you should make multiple parallel calls to 'file_read'.
3416
+ 1. **Understand:** Think about the user's request and the relevant codebase context. Use '${TOOL_GREP_SEARCH}' and '${TOOL_GLOB_FILES}' search tools extensively (in parallel if independent) to understand file structures, existing code patterns, and conventions. Use '${TOOL_FILE_READ}' to understand context and validate any assumptions you may have. If you need to read multiple files, you should make multiple parallel calls to '${TOOL_FILE_READ}'.
3417
+ When the task involves **complex refactoring, codebase exploration or system-wide analysis**, your **first and primary action** must be to delegate to the '${EXPLORE_SUBAGENT_TYPE}' agent using the '${TOOL_SUBAGENT_DELEGATE}' tool. Use it to build a comprehensive understanding of the code, its structure, and dependencies. For **simple, targeted searches** (like finding a specific function name, file path, or variable declaration), you should use '${TOOL_GREP_SEARCH}' or '${TOOL_GLOB_FILES}' directly.
3409
3418
  2. **Plan:** Build a coherent and grounded (based on the understanding in step 1) plan for how you intend to resolve the user's task. Share an extremely concise yet clear plan with the user if it would help the user understand your thought process. As part of the plan, you should use an iterative development process that includes writing unit tests to verify your changes. Use output logs or debug statements as part of this process to arrive at a solution.
3410
- 3. **Implement:** Use the available tools (e.g., 'edit_local_file', 'create_file', 'bash_execute' ...) to act on the plan, strictly adhering to the project's established conventions.
3419
+ If '${EXPLORE_SUBAGENT_TYPE}' subagent was used, do not ignore the output of the agent, you must use it as the foundation of your plan. For complex tasks, break them down into smaller, manageable subtasks and use the \`${TOOL_WRITE_TODOS}\` tool to track your progress. Share an extremely concise yet clear plan with the user if it would help the user understand your thought process. As part of the plan, you should use an iterative development process that includes writing unit tests to verify your changes. Use output logs or debug statements as part of this process to arrive at a solution.
3420
+ 3. **Implement:** Use the available tools (e.g., '${TOOL_EDIT_LOCAL_FILE}', '${TOOL_CREATE_FILE}', '${TOOL_BASH_EXECUTE}' ...) to act on the plan, strictly adhering to the project's established conventions.
3411
3421
  4. **Verify (Tests):** If applicable and feasible, verify the changes using the project's testing procedures. Identify the correct test commands and frameworks by examining 'README' files, build/package configuration (e.g., 'package.json'), or existing test execution patterns. NEVER assume standard test commands. When executing test commands, prefer "run once" or "CI" modes to ensure the command terminates after completion.
3412
3422
  5. **Verify (Standards):** VERY IMPORTANT: After making code changes, execute the project-specific build, linting and type-checking commands (e.g., 'tsc', 'npm run lint', 'ruff check .') that you have identified for this project (or obtained from the user). This ensures code quality and adherence to standards.
3413
3423
  6. **Finalize:** After all verification passes, consider the task complete. Do not remove or revert any changes or created files (like tests). Await the user's next instruction.
3414
3424
 
3415
3425
  SUBAGENT DELEGATION:
3416
- - You have access to specialized subagents via the subagent_delegate tool
3426
+ - You have access to specialized subagents via the ${TOOL_SUBAGENT_DELEGATE} tool
3417
3427
  - Use subagents for focused exploration, planning, or review tasks:
3418
3428
  * explore: Fast read-only codebase search (e.g., "find all auth files", "locate API endpoints")
3419
3429
  * plan: Break down complex tasks into actionable steps
@@ -3429,7 +3439,7 @@ FOR GENERAL TASKS:
3429
3439
  ## Shell tool output token efficiency:
3430
3440
  IT IS CRITICAL TO FOLLOW THESE GUIDELINES TO AVOID EXCESSIVE TOKEN CONSUMPTION.
3431
3441
 
3432
- - Always prefer command flags that reduce output verbosity when using 'bash_execute'.
3442
+ - Always prefer command flags that reduce output verbosity when using '${TOOL_BASH_EXECUTE}'.
3433
3443
  - Aim to minimize tool output tokens while still capturing necessary information.
3434
3444
  - If a command is expected to produce a lot of output, use quiet or silent flags where available and appropriate.
3435
3445
  - Always consider the trade-off between output verbosity and the need for information. If a command's full output is essential for understanding the result, avoid overly aggressive quieting that might obscure important details.
@@ -3439,9 +3449,9 @@ IT IS CRITICAL TO FOLLOW THESE GUIDELINES TO AVOID EXCESSIVE TOKEN CONSUMPTION.
3439
3449
  EXAMPLES:
3440
3450
  - "what should I wear in Texas?" \u2192 use weather tool for Texas
3441
3451
  - "how about Japan?" \u2192 use weather tool for Japan (applying same question from context)
3442
- - "enhance README" \u2192 file_read \u2192 generate \u2192 create_file
3443
- - "what packages installed?" \u2192 glob_files "**/package.json" \u2192 file_read
3444
- - "find all components using React hooks" \u2192 subagent_delegate(task="find all components using React hooks", type="explore")
3452
+ - "enhance README" \u2192 ${TOOL_FILE_READ} \u2192 generate \u2192 ${TOOL_EDIT_LOCAL_FILE}
3453
+ - "what packages installed?" \u2192 ${TOOL_GLOB_FILES} "**/package.json" \u2192 ${TOOL_FILE_READ}
3454
+ - "find all components using React hooks" \u2192 ${TOOL_SUBAGENT_DELEGATE}(task="find all components using React hooks", type="explore")
3445
3455
 
3446
3456
  Remember: Use context from previous messages to understand follow-up questions.${contextSection}`;
3447
3457
  }
@@ -4385,7 +4395,9 @@ var updateSchema = z77.object({
4385
4395
  name: z77.string().optional(),
4386
4396
  description: z77.string().optional(),
4387
4397
  billingContact: z77.string().optional(),
4388
- currentCredits: z77.coerce.number().optional()
4398
+ currentCredits: z77.coerce.number().optional(),
4399
+ systemPrompt: z77.string().max(1e4).optional()
4400
+ // ~2500 tokens
4389
4401
  });
4390
4402
 
4391
4403
  // ../../b4m-core/packages/services/dist/src/organizationService/delete.js
@@ -8797,98 +8809,80 @@ var createFileTool = {
8797
8809
  };
8798
8810
 
8799
8811
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/globFiles/index.js
8800
- import { readdir } from "fs/promises";
8801
- import { statSync as statSync5 } from "fs";
8812
+ import { glob } from "glob";
8813
+ import { stat } from "fs/promises";
8802
8814
  import path9 from "path";
8803
- function matchGlob(filePath, pattern) {
8804
- let regexPattern = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/\{([^}]+)\}/g, (_, options) => `(${options.split(",").join("|")})`).replace(/\[([^\]]+)\]/g, "[$1]");
8805
- regexPattern = regexPattern.replace(/\{\{GLOBSTAR\}\}\//g, "(.*/)?").replace(/\/\{\{GLOBSTAR\}\}/g, "(/.*)?").replace(/\{\{GLOBSTAR\}\}/g, ".*");
8806
- const regex = new RegExp(`^${regexPattern}$`);
8807
- return regex.test(filePath);
8808
- }
8809
- async function walkDir(dir, pattern, includeHidden, maxResults, basePath = "") {
8810
- const matches = [];
8811
- try {
8812
- const entries = await readdir(dir, { withFileTypes: true });
8813
- for (const entry of entries) {
8814
- if (matches.length >= maxResults) {
8815
- break;
8816
- }
8817
- if (!includeHidden && entry.name.startsWith(".")) {
8818
- continue;
8819
- }
8820
- const relativePath = basePath ? path9.join(basePath, entry.name) : entry.name;
8821
- const fullPath = path9.join(dir, entry.name);
8822
- if (matchGlob(relativePath, pattern)) {
8823
- matches.push(relativePath);
8824
- }
8825
- if (entry.isDirectory() && (pattern.includes("**") || pattern.includes("/"))) {
8826
- const subMatches = await walkDir(fullPath, pattern, includeHidden, maxResults - matches.length, relativePath);
8827
- matches.push(...subMatches);
8828
- }
8829
- }
8830
- } catch (error) {
8831
- }
8832
- return matches;
8833
- }
8834
- async function listFiles(params) {
8835
- const { pattern, cwd: relativeCwd, includeHidden = false, maxResults = 1e3 } = params;
8815
+ var DEFAULT_IGNORE_PATTERNS2 = [
8816
+ "**/node_modules/**",
8817
+ "**/.git/**",
8818
+ "**/dist/**",
8819
+ "**/build/**",
8820
+ "**/.next/**",
8821
+ "**/coverage/**",
8822
+ "**/.turbo/**",
8823
+ "**/.sst/**",
8824
+ "**/*.min.js",
8825
+ "**/*.min.css"
8826
+ ];
8827
+ async function findFiles(params) {
8828
+ const { pattern, dir_path, case_sensitive = true, respect_git_ignore = true } = params;
8836
8829
  const baseCwd = process.cwd();
8837
- const targetCwd = relativeCwd ? path9.resolve(baseCwd, path9.normalize(relativeCwd)) : baseCwd;
8838
- if (!targetCwd.startsWith(baseCwd)) {
8839
- throw new Error(`Access denied: Cannot list files outside of current working directory`);
8840
- }
8841
- const matches = await walkDir(targetCwd, pattern, includeHidden, maxResults);
8830
+ const targetDir = dir_path ? path9.resolve(baseCwd, path9.normalize(dir_path)) : baseCwd;
8831
+ if (!targetDir.startsWith(baseCwd)) {
8832
+ throw new Error(`Access denied: Cannot search outside of current working directory`);
8833
+ }
8834
+ const ignorePatterns = respect_git_ignore ? DEFAULT_IGNORE_PATTERNS2 : [];
8835
+ const matches = await glob(pattern, {
8836
+ cwd: targetDir,
8837
+ dot: false,
8838
+ // Don't match hidden files by default
8839
+ ignore: ignorePatterns,
8840
+ absolute: true,
8841
+ nodir: true,
8842
+ // Only return files, not directories
8843
+ nocase: !case_sensitive,
8844
+ // Case-insensitive if requested
8845
+ maxDepth: 20
8846
+ // Reasonable depth limit
8847
+ });
8842
8848
  if (matches.length === 0) {
8843
- return `No files found matching pattern: ${pattern}`;
8849
+ return `No files found matching pattern: ${pattern}${dir_path ? ` in ${dir_path}` : ""}`;
8844
8850
  }
8845
- const filesInfo = [];
8846
- for (const match of matches) {
8851
+ const filesWithStats = [];
8852
+ for (const filePath of matches) {
8847
8853
  try {
8848
- const fullPath = path9.join(targetCwd, match);
8849
- const stats = statSync5(fullPath);
8850
- filesInfo.push({
8851
- path: match,
8852
- size: stats.size,
8853
- isDirectory: stats.isDirectory(),
8854
- modified: stats.mtime.toISOString()
8854
+ const stats = await stat(filePath);
8855
+ filesWithStats.push({
8856
+ path: filePath,
8857
+ mtime: stats.mtimeMs
8855
8858
  });
8856
- } catch (error) {
8859
+ } catch {
8857
8860
  continue;
8858
8861
  }
8859
8862
  }
8860
- const truncated = matches.length >= maxResults;
8861
- const summary = `Found ${filesInfo.length} file(s)${truncated ? ` (truncated to ${maxResults})` : ""} matching: ${pattern}
8862
- `;
8863
- const cwdInfo = relativeCwd ? `Directory: ${relativeCwd}
8864
- ` : "";
8865
- const filesList = filesInfo.map((file) => {
8866
- const typeIcon = file.isDirectory ? "\u{1F4C1}" : "\u{1F4C4}";
8867
- const sizeStr = file.isDirectory ? "" : ` (${formatBytes(file.size)})`;
8868
- return `${typeIcon} ${file.path}${sizeStr}`;
8869
- }).join("\n");
8870
- return `${summary}${cwdInfo}
8863
+ filesWithStats.sort((a, b) => b.mtime - a.mtime);
8864
+ const MAX_RESULTS = 500;
8865
+ const truncated = filesWithStats.length > MAX_RESULTS;
8866
+ const results = truncated ? filesWithStats.slice(0, MAX_RESULTS) : filesWithStats;
8867
+ const summary = `Found ${filesWithStats.length} file(s)${truncated ? ` (showing first ${MAX_RESULTS})` : ""} matching: ${pattern}`;
8868
+ const dirInfo = dir_path ? `
8869
+ Directory: ${dir_path}` : "";
8870
+ const filesList = results.map((file) => path9.relative(baseCwd, file.path)).join("\n");
8871
+ return `${summary}${dirInfo}
8872
+
8871
8873
  ${filesList}`;
8872
8874
  }
8873
- function formatBytes(bytes) {
8874
- if (bytes === 0)
8875
- return "0 B";
8876
- const k = 1024;
8877
- const sizes = ["B", "KB", "MB", "GB"];
8878
- const i = Math.floor(Math.log(bytes) / Math.log(k));
8879
- return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
8880
- }
8881
8875
  var globFilesTool = {
8882
8876
  name: "glob_files",
8883
8877
  implementation: (context) => ({
8884
8878
  toolFn: async (value) => {
8885
8879
  const params = value;
8886
- context.logger.info("\u{1F50D} GlobFiles: Listing files", {
8880
+ context.logger.info("\u{1F50D} GlobFiles: Finding files", {
8887
8881
  pattern: params.pattern,
8888
- cwd: params.cwd || "."
8882
+ dir_path: params.dir_path || "."
8889
8883
  });
8890
8884
  try {
8891
- const result = await listFiles(params);
8885
+ const result = await findFiles(params);
8892
8886
  context.logger.info("\u2705 GlobFiles: Success");
8893
8887
  return result;
8894
8888
  } catch (error) {
@@ -8898,25 +8892,25 @@ var globFilesTool = {
8898
8892
  },
8899
8893
  toolSchema: {
8900
8894
  name: "glob_files",
8901
- description: "List files and directories matching a glob pattern. Supports wildcards like *, **, ?, and character ranges. Restricted to current working directory for security.",
8895
+ description: "Efficiently finds files matching specific glob patterns (e.g., `src/**/*.ts`, `**/*.md`), returning absolute paths sorted by modification time (newest first). Use this to locate files by name or extension when you need to discover or explore files in a project.",
8902
8896
  parameters: {
8903
8897
  type: "object",
8904
8898
  properties: {
8905
8899
  pattern: {
8906
8900
  type: "string",
8907
- description: 'Glob pattern to match files (e.g., "*.ts", "src/**/*.tsx", "test/*.{js,ts}"). Use ** for recursive directory search.'
8901
+ description: 'The glob pattern to match against file paths. Supports wildcards (*, **), character ranges ([abc]), and brace expansion ({ts,tsx}). Examples: "*.ts" for TypeScript files in current dir, "src/**/*.tsx" for all TSX files in src tree, "**/*.{js,ts}" for all JS/TS files.'
8908
8902
  },
8909
- cwd: {
8903
+ dir_path: {
8910
8904
  type: "string",
8911
- description: "Directory to search in, relative to current working directory (optional). Defaults to current directory."
8905
+ description: "Optional: The absolute path to the directory to search in. If not specified, uses the current working directory. Must be within the current working directory."
8912
8906
  },
8913
- includeHidden: {
8907
+ case_sensitive: {
8914
8908
  type: "boolean",
8915
- description: "Include hidden files and directories (starting with .). Defaults to false."
8909
+ description: "Optional: Whether the pattern matching should be case-sensitive. Defaults to true. Set to false for case-insensitive matching on case-sensitive file systems."
8916
8910
  },
8917
- maxResults: {
8918
- type: "number",
8919
- description: "Maximum number of results to return (default: 1000). Prevents overwhelming output."
8911
+ respect_git_ignore: {
8912
+ type: "boolean",
8913
+ description: "Optional: Whether to respect common ignore patterns (node_modules, .git, dist, build, etc.). Defaults to true. Set to false to include all files."
8920
8914
  }
8921
8915
  },
8922
8916
  required: ["pattern"]
@@ -8926,158 +8920,144 @@ var globFilesTool = {
8926
8920
  };
8927
8921
 
8928
8922
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/grepSearch/index.js
8929
- import { readdir as readdir2 } from "fs/promises";
8930
- import { readFileSync as readFileSync3, statSync as statSync6 } from "fs";
8923
+ import { globStream } from "glob";
8924
+ import { readFile, stat as stat2 } from "fs/promises";
8931
8925
  import path10 from "path";
8932
8926
  var MAX_FILE_SIZE3 = 10 * 1024 * 1024;
8933
- function matchGlob2(filePath, pattern) {
8934
- const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{GLOBSTAR\}\}/g, ".*").replace(/\?/g, ".").replace(/\{([^}]+)\}/g, (_, options) => `(${options.split(",").join("|")})`).replace(/\[([^\]]+)\]/g, "[$1]");
8935
- const regex = new RegExp(`^${regexPattern}$`);
8936
- return regex.test(filePath);
8937
- }
8938
- async function walkDir2(dir, pattern, maxResults, basePath = "") {
8939
- const matches = [];
8927
+ var DEFAULT_IGNORE_PATTERNS3 = [
8928
+ "**/node_modules/**",
8929
+ "**/.git/**",
8930
+ "**/dist/**",
8931
+ "**/build/**",
8932
+ "**/.next/**",
8933
+ "**/coverage/**",
8934
+ "**/*.min.js",
8935
+ "**/*.min.css",
8936
+ "**/package-lock.json",
8937
+ "**/pnpm-lock.yaml",
8938
+ "**/yarn.lock"
8939
+ ];
8940
+ async function isBinaryFile2(filePath) {
8940
8941
  try {
8941
- const entries = await readdir2(dir, { withFileTypes: true });
8942
- for (const entry of entries) {
8943
- if (matches.length >= maxResults) {
8944
- break;
8945
- }
8946
- if (entry.name.startsWith(".")) {
8947
- continue;
8948
- }
8949
- const relativePath = basePath ? path10.join(basePath, entry.name) : entry.name;
8950
- const fullPath = path10.join(dir, entry.name);
8951
- if (matchGlob2(relativePath, pattern)) {
8952
- matches.push(relativePath);
8953
- }
8954
- if (entry.isDirectory() && (pattern.includes("**") || pattern.includes("/"))) {
8955
- const subMatches = await walkDir2(fullPath, pattern, maxResults - matches.length, relativePath);
8956
- matches.push(...subMatches);
8957
- }
8958
- }
8959
- } catch (error) {
8942
+ const { openSync, readSync, closeSync } = await import("fs");
8943
+ const buffer = Buffer.alloc(8192);
8944
+ const fd = openSync(filePath, "r");
8945
+ const bytesRead = readSync(fd, buffer, 0, 8192, 0);
8946
+ closeSync(fd);
8947
+ const chunk = buffer.slice(0, bytesRead);
8948
+ return chunk.includes(0);
8949
+ } catch {
8950
+ return true;
8960
8951
  }
8961
- return matches;
8952
+ }
8953
+ function isPathWithinWorkspace(targetPath, baseCwd) {
8954
+ const resolvedTarget = path10.resolve(targetPath);
8955
+ const resolvedBase = path10.resolve(baseCwd);
8956
+ return resolvedTarget.startsWith(resolvedBase);
8962
8957
  }
8963
8958
  async function searchFiles2(params) {
8964
- const { pattern, filePattern = "**/*", cwd: relativeCwd, caseSensitive = false, maxResults = 100, contextLines = 0 } = params;
8959
+ const { pattern, dir_path, include } = params;
8965
8960
  const baseCwd = process.cwd();
8966
- const targetCwd = relativeCwd ? path10.resolve(baseCwd, path10.normalize(relativeCwd)) : baseCwd;
8967
- if (!targetCwd.startsWith(baseCwd)) {
8968
- throw new Error(`Access denied: Cannot search files outside of current working directory`);
8961
+ const targetDir = dir_path ? path10.resolve(baseCwd, dir_path) : baseCwd;
8962
+ if (!isPathWithinWorkspace(targetDir, baseCwd)) {
8963
+ throw new Error(`Path validation failed: "${dir_path}" resolves outside the allowed workspace directory`);
8964
+ }
8965
+ try {
8966
+ const stats = await stat2(targetDir);
8967
+ if (!stats.isDirectory()) {
8968
+ throw new Error(`Path is not a directory: ${dir_path}`);
8969
+ }
8970
+ } catch (error) {
8971
+ if (error.code === "ENOENT") {
8972
+ throw new Error(`Path does not exist: ${dir_path}`);
8973
+ }
8974
+ throw error;
8969
8975
  }
8970
8976
  let searchRegex;
8971
8977
  try {
8972
- const flags = caseSensitive ? "g" : "gi";
8973
- searchRegex = new RegExp(pattern, flags);
8978
+ searchRegex = new RegExp(pattern, "i");
8974
8979
  } catch (error) {
8975
8980
  throw new Error(`Invalid regex pattern: ${pattern}`);
8976
8981
  }
8977
- const filesToSearch = await walkDir2(targetCwd, filePattern, 1e4);
8978
- const filteredFiles = [];
8979
- for (const match of filesToSearch) {
8980
- const fullPath = path10.join(targetCwd, match);
8982
+ const globPattern = include || "**/*";
8983
+ const filesStream = globStream(globPattern, {
8984
+ cwd: targetDir,
8985
+ dot: false,
8986
+ // Skip hidden files
8987
+ ignore: DEFAULT_IGNORE_PATTERNS3,
8988
+ absolute: true,
8989
+ nodir: true
8990
+ // Only match files, not directories
8991
+ });
8992
+ const allMatches = [];
8993
+ let filesSearched = 0;
8994
+ const maxMatches = 500;
8995
+ for await (const filePath of filesStream) {
8996
+ if (allMatches.length >= maxMatches) {
8997
+ break;
8998
+ }
8999
+ const fileAbsPath = filePath;
8981
9000
  try {
8982
- const stats = statSync6(fullPath);
8983
- if (stats.isDirectory() || stats.size > MAX_FILE_SIZE3) {
9001
+ const stats = await stat2(fileAbsPath);
9002
+ if (stats.size > MAX_FILE_SIZE3) {
8984
9003
  continue;
8985
9004
  }
8986
- if (isBinaryFile2(fullPath)) {
9005
+ if (await isBinaryFile2(fileAbsPath)) {
8987
9006
  continue;
8988
9007
  }
8989
- filteredFiles.push(match);
8990
- } catch {
8991
- continue;
8992
- }
8993
- }
8994
- if (filteredFiles.length === 0) {
8995
- return `No files found matching pattern: ${filePattern}`;
8996
- }
8997
- const matches = [];
8998
- let filesSearched = 0;
8999
- for (const file of filteredFiles) {
9000
- if (matches.length >= maxResults) {
9001
- break;
9002
- }
9003
- filesSearched++;
9004
- const fullPath = path10.join(targetCwd, file);
9005
- try {
9006
- const content = readFileSync3(fullPath, "utf-8");
9007
- const lines = content.split("\n");
9008
- for (let i = 0; i < lines.length; i++) {
9009
- if (matches.length >= maxResults) {
9010
- break;
9011
- }
9012
- const line = lines[i];
9013
- if (searchRegex.test(line)) {
9014
- const match = {
9015
- file,
9016
- line: i + 1,
9017
- // 1-indexed line numbers
9018
- content: line.trim()
9019
- };
9020
- if (contextLines > 0) {
9021
- const before = [];
9022
- const after = [];
9023
- for (let j = Math.max(0, i - contextLines); j < i; j++) {
9024
- before.push(lines[j]);
9025
- }
9026
- for (let j = i + 1; j <= Math.min(lines.length - 1, i + contextLines); j++) {
9027
- after.push(lines[j]);
9028
- }
9029
- match.context = { before, after };
9030
- }
9031
- matches.push(match);
9008
+ filesSearched++;
9009
+ const content = await readFile(fileAbsPath, "utf8");
9010
+ const lines = content.split(/\r?\n/);
9011
+ lines.forEach((line, index) => {
9012
+ if (allMatches.length < maxMatches && searchRegex.test(line)) {
9013
+ allMatches.push({
9014
+ filePath: path10.relative(targetDir, fileAbsPath) || path10.basename(fileAbsPath),
9015
+ lineNumber: index + 1,
9016
+ line
9017
+ });
9032
9018
  }
9033
- searchRegex.lastIndex = 0;
9019
+ });
9020
+ } catch (readError) {
9021
+ const errCode = readError.code;
9022
+ if (errCode !== "ENOENT" && errCode !== "EACCES") {
9023
+ console.debug(`Could not read ${fileAbsPath}: ${readError.message}`);
9034
9024
  }
9035
- } catch {
9036
- continue;
9037
9025
  }
9038
9026
  }
9039
- if (matches.length === 0) {
9040
- return `No matches found for pattern: ${pattern}
9041
- Searched ${filesSearched} file(s)`;
9027
+ const searchDirDisplay = dir_path || ".";
9028
+ const filterInfo = include ? ` (filter: "${include}")` : "";
9029
+ if (allMatches.length === 0) {
9030
+ return `No matches found for pattern "${pattern}" in "${searchDirDisplay}"${filterInfo}.
9031
+ Searched ${filesSearched} file(s).`;
9042
9032
  }
9043
- const truncated = matches.length >= maxResults;
9044
- const summary = `Found ${matches.length} match(es)${truncated ? ` (truncated to ${maxResults})` : ""} in ${filesSearched} file(s)
9033
+ const matchesByFile = allMatches.reduce((acc, match) => {
9034
+ if (!acc[match.filePath]) {
9035
+ acc[match.filePath] = [];
9036
+ }
9037
+ acc[match.filePath].push(match);
9038
+ acc[match.filePath].sort((a, b) => a.lineNumber - b.lineNumber);
9039
+ return acc;
9040
+ }, {});
9041
+ const matchCount = allMatches.length;
9042
+ const matchTerm = matchCount === 1 ? "match" : "matches";
9043
+ const truncated = matchCount >= maxMatches;
9044
+ let result = `Found ${matchCount} ${matchTerm}${truncated ? " (truncated)" : ""} for pattern "${pattern}" in "${searchDirDisplay}"${filterInfo}:
9045
9045
  `;
9046
- const patternInfo = `Pattern: ${pattern}${caseSensitive ? "" : " (case-insensitive)"}
9046
+ result += `Searched ${filesSearched} file(s)
9047
+ ---
9047
9048
  `;
9048
- const cwdInfo = relativeCwd ? `Directory: ${relativeCwd}
9049
- ` : "";
9050
- const matchesList = matches.map((match) => {
9051
- let result = `
9052
- \u{1F4C4} ${match.file}:${match.line}
9053
- ${match.content}`;
9054
- if (match.context) {
9055
- if (match.context.before.length > 0) {
9056
- result = `${result}
9057
- Context before:
9058
- ${match.context.before.map((l) => ` ${l}`).join("\n")}`;
9059
- }
9060
- if (match.context.after.length > 0) {
9061
- result = `${result}
9062
- Context after:
9063
- ${match.context.after.map((l) => ` ${l}`).join("\n")}`;
9064
- }
9065
- }
9066
- return result;
9067
- }).join("\n");
9068
- return `${summary}${patternInfo}${cwdInfo}${matchesList}`;
9069
- }
9070
- function isBinaryFile2(filePath) {
9071
- try {
9072
- const buffer = Buffer.alloc(8192);
9073
- const fd = __require("fs").openSync(filePath, "r");
9074
- const bytesRead = __require("fs").readSync(fd, buffer, 0, 8192, 0);
9075
- __require("fs").closeSync(fd);
9076
- const chunk = buffer.slice(0, bytesRead);
9077
- return chunk.includes(0);
9078
- } catch {
9079
- return true;
9049
+ for (const filePath in matchesByFile) {
9050
+ result += `File: ${filePath}
9051
+ `;
9052
+ matchesByFile[filePath].forEach((match) => {
9053
+ const trimmedLine = match.line.trim();
9054
+ const displayLine = trimmedLine.length > 200 ? trimmedLine.slice(0, 200) + "..." : trimmedLine;
9055
+ result += `L${match.lineNumber}: ${displayLine}
9056
+ `;
9057
+ });
9058
+ result += "---\n";
9080
9059
  }
9060
+ return result.trim();
9081
9061
  }
9082
9062
  var grepSearchTool = {
9083
9063
  name: "grep_search",
@@ -9086,8 +9066,8 @@ var grepSearchTool = {
9086
9066
  const params = value;
9087
9067
  context.logger.info("\u{1F50D} GrepSearch: Searching files", {
9088
9068
  pattern: params.pattern,
9089
- filePattern: params.filePattern || "**/*",
9090
- cwd: params.cwd || "."
9069
+ dir_path: params.dir_path || ".",
9070
+ include: params.include || "**/*"
9091
9071
  });
9092
9072
  try {
9093
9073
  const result = await searchFiles2(params);
@@ -9100,33 +9080,21 @@ var grepSearchTool = {
9100
9080
  },
9101
9081
  toolSchema: {
9102
9082
  name: "grep_search",
9103
- description: "Search for a regex pattern in file contents. Similar to grep command. Searches text files only, excluding binary files. Restricted to current working directory for security.",
9083
+ description: "Searches for a regular expression pattern within the content of files in a specified directory (or current working directory). Can filter files by a glob pattern. Returns the lines containing matches, along with their file paths and line numbers.",
9104
9084
  parameters: {
9105
9085
  type: "object",
9106
9086
  properties: {
9107
9087
  pattern: {
9108
9088
  type: "string",
9109
- description: "Regular expression pattern to search for in file contents"
9089
+ description: "The regular expression (regex) pattern to search for within file contents (e.g., 'function\\s+myFunction', 'import\\s+\\{.*\\}\\s+from\\s+.*')."
9110
9090
  },
9111
- filePattern: {
9091
+ dir_path: {
9112
9092
  type: "string",
9113
- description: 'Glob pattern to filter which files to search (optional, default: "**/*"). Example: "src/**/*.ts" to search only TypeScript files in src directory.'
9093
+ description: "Optional: The absolute path to the directory to search within. If omitted, searches the current working directory."
9114
9094
  },
9115
- cwd: {
9095
+ include: {
9116
9096
  type: "string",
9117
- description: "Directory to search in, relative to current working directory (optional). Defaults to current directory."
9118
- },
9119
- caseSensitive: {
9120
- type: "boolean",
9121
- description: "Whether the search should be case-sensitive (default: false)"
9122
- },
9123
- maxResults: {
9124
- type: "number",
9125
- description: "Maximum number of matches to return (default: 100). Prevents overwhelming output."
9126
- },
9127
- contextLines: {
9128
- type: "number",
9129
- description: "Number of context lines to show before and after each match (default: 0). Similar to grep -C option."
9097
+ description: "Optional: A glob pattern to filter which files are searched (e.g., '*.js', '*.{ts,tsx}', 'src/**'). If omitted, searches all files (respecting potential global ignores)."
9130
9098
  }
9131
9099
  },
9132
9100
  required: ["pattern"]
@@ -9137,7 +9105,7 @@ var grepSearchTool = {
9137
9105
 
9138
9106
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/deleteFile/index.js
9139
9107
  import { promises as fs10 } from "fs";
9140
- import { existsSync as existsSync6, statSync as statSync7 } from "fs";
9108
+ import { existsSync as existsSync6, statSync as statSync5 } from "fs";
9141
9109
  import path11 from "path";
9142
9110
  async function deleteFile(params) {
9143
9111
  const { path: filePath, recursive = false } = params;
@@ -9150,7 +9118,7 @@ async function deleteFile(params) {
9150
9118
  if (!existsSync6(resolvedPath)) {
9151
9119
  throw new Error(`File or directory not found: ${filePath}`);
9152
9120
  }
9153
- const stats = statSync7(resolvedPath);
9121
+ const stats = statSync5(resolvedPath);
9154
9122
  const isDirectory = stats.isDirectory();
9155
9123
  const size = stats.size;
9156
9124
  if (isDirectory && !recursive) {
@@ -9171,7 +9139,7 @@ var deleteFileTool = {
9171
9139
  toolFn: async (value) => {
9172
9140
  const params = value;
9173
9141
  const resolvedPath = path11.resolve(process.cwd(), path11.normalize(params.path));
9174
- const isDirectory = existsSync6(resolvedPath) && statSync7(resolvedPath).isDirectory();
9142
+ const isDirectory = existsSync6(resolvedPath) && statSync5(resolvedPath).isDirectory();
9175
9143
  context.logger.info(`\u{1F5D1}\uFE0F DeleteFile: Deleting ${isDirectory ? "directory" : "file"}`, {
9176
9144
  path: params.path,
9177
9145
  recursive: params.recursive
@@ -9206,6 +9174,92 @@ var deleteFileTool = {
9206
9174
  })
9207
9175
  };
9208
9176
 
9177
+ // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/knowledgeBaseSearch/index.js
9178
+ function formatSearchResults(files) {
9179
+ if (files.length === 0) {
9180
+ return "No documents found matching your search query in your knowledge base.";
9181
+ }
9182
+ const formattedFiles = files.map((file, index) => {
9183
+ const tags = file.tags?.map((t) => t.name).join(", ") || "none";
9184
+ const notes = file.notes ? `
9185
+ Notes: ${file.notes}` : "";
9186
+ const fileType = file.type || "FILE";
9187
+ return `${index + 1}. **${file.fileName}**
9188
+ Type: ${fileType} | MIME: ${file.mimeType}
9189
+ Tags: ${tags}${notes}`;
9190
+ });
9191
+ return `Found ${files.length} document(s) in your knowledge base:
9192
+
9193
+ ` + formattedFiles.join("\n\n") + "\n\n*To use these documents in your conversation, you can ask me to reference specific files by name.*";
9194
+ }
9195
+ var knowledgeBaseSearchTool = {
9196
+ name: "search_knowledge_base",
9197
+ implementation: (context) => ({
9198
+ toolFn: async (value) => {
9199
+ const params = value;
9200
+ const { query, tags, file_type, max_results = 5 } = params;
9201
+ console.log("\u{1F4DA} Knowledge Base Search: Starting search for query:", query);
9202
+ if (!context.db.fabfiles) {
9203
+ console.error("\u274C Knowledge Base Search: fabfiles repository not available");
9204
+ return "Knowledge base search is not available at this time.";
9205
+ }
9206
+ try {
9207
+ const searchResults = await context.db.fabfiles.search(context.userId, query, {
9208
+ tags: tags || [],
9209
+ type: file_type,
9210
+ shared: false
9211
+ // Search all accessible files, not just shared
9212
+ }, {
9213
+ page: 1,
9214
+ limit: Math.min(max_results, 10)
9215
+ // Cap at 10 results
9216
+ }, {
9217
+ by: "fileName",
9218
+ direction: "asc"
9219
+ }, {
9220
+ includeShared: true
9221
+ // Include organization and explicitly shared files
9222
+ });
9223
+ console.log("\u{1F4DA} Knowledge Base Search: Found", searchResults.data.length, "results");
9224
+ return formatSearchResults(searchResults.data);
9225
+ } catch (error) {
9226
+ console.error("\u274C Knowledge Base Search: Error during search:", error);
9227
+ return "An error occurred while searching your knowledge base. Please try again.";
9228
+ }
9229
+ },
9230
+ toolSchema: {
9231
+ name: "search_knowledge_base",
9232
+ description: "Search the user's uploaded knowledge base (fab files). Returns relevant documents from the user's own files, organization-shared files, and files explicitly shared with them. Use this tool when the user asks about their own documents, uploaded files, or organization knowledge.",
9233
+ parameters: {
9234
+ type: "object",
9235
+ properties: {
9236
+ query: {
9237
+ type: "string",
9238
+ description: "The search query to find relevant documents by filename or content"
9239
+ },
9240
+ tags: {
9241
+ type: "array",
9242
+ items: { type: "string" },
9243
+ description: "Optional: filter results to only include files with these tags"
9244
+ },
9245
+ file_type: {
9246
+ type: "string",
9247
+ enum: ["pdf", "text", "image", "excel", "word", "json", "csv", "markdown", "code", "url"],
9248
+ description: "Optional: filter results by file type"
9249
+ },
9250
+ max_results: {
9251
+ type: "number",
9252
+ description: "Maximum number of results to return (default: 5, max: 10)",
9253
+ minimum: 1,
9254
+ maximum: 10
9255
+ }
9256
+ },
9257
+ required: ["query"]
9258
+ }
9259
+ }
9260
+ })
9261
+ };
9262
+
9209
9263
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/bashExecute/index.js
9210
9264
  import { spawn } from "child_process";
9211
9265
  import path12 from "path";
@@ -9708,6 +9762,8 @@ var tools = {
9708
9762
  glob_files: globFilesTool,
9709
9763
  grep_search: grepSearchTool,
9710
9764
  delete_file: deleteFileTool,
9765
+ // Knowledge base search
9766
+ search_knowledge_base: knowledgeBaseSearchTool,
9711
9767
  // Shell execution
9712
9768
  bash_execute: bashExecuteTool
9713
9769
  };
@@ -9947,7 +10003,7 @@ var ToolErrorType;
9947
10003
 
9948
10004
  // src/utils/diffPreview.ts
9949
10005
  import * as Diff from "diff";
9950
- import { readFile } from "fs/promises";
10006
+ import { readFile as readFile2 } from "fs/promises";
9951
10007
  import { existsSync as existsSync8 } from "fs";
9952
10008
  async function generateFileDiffPreview(args) {
9953
10009
  try {
@@ -9962,7 +10018,7 @@ ${preview}${hasMore ? `
9962
10018
 
9963
10019
  ... (${lines2.length - 20} more lines)` : ""}`;
9964
10020
  }
9965
- const currentContent = await readFile(args.path, "utf-8");
10021
+ const currentContent = await readFile2(args.path, "utf-8");
9966
10022
  const patch = Diff.createPatch(
9967
10023
  args.path,
9968
10024
  currentContent,
@@ -10871,6 +10927,82 @@ async function loadContextFiles(projectDir) {
10871
10927
  };
10872
10928
  }
10873
10929
 
10930
+ // src/utils/formatStep.ts
10931
+ var MAX_INPUT_LENGTH = 500;
10932
+ var formatStep = (step) => {
10933
+ if (step.type === "action" && step.metadata?.toolInput) {
10934
+ let parsedInput = step.metadata.toolInput;
10935
+ if (typeof parsedInput === "string") {
10936
+ try {
10937
+ parsedInput = JSON.parse(parsedInput);
10938
+ } catch {
10939
+ }
10940
+ }
10941
+ if (step.metadata.toolName === "edit_local_file") {
10942
+ const pathOnly = typeof parsedInput === "object" && parsedInput !== null && "path" in parsedInput ? { path: parsedInput.path } : parsedInput;
10943
+ return {
10944
+ ...step,
10945
+ metadata: {
10946
+ ...step.metadata,
10947
+ toolInput: pathOnly
10948
+ }
10949
+ };
10950
+ }
10951
+ if (step.metadata.toolName === "write_todos") {
10952
+ const todos = typeof parsedInput === "object" && parsedInput !== null && "todos" in parsedInput ? parsedInput.todos : null;
10953
+ const count = Array.isArray(todos) ? todos.length : 0;
10954
+ return {
10955
+ ...step,
10956
+ metadata: {
10957
+ ...step.metadata,
10958
+ toolInput: `${count} todo${count !== 1 ? "s" : ""}`
10959
+ }
10960
+ };
10961
+ }
10962
+ const inputStr = typeof parsedInput === "string" ? parsedInput : JSON.stringify(parsedInput);
10963
+ if (inputStr.length > MAX_INPUT_LENGTH) {
10964
+ const truncatedInput = inputStr.slice(0, MAX_INPUT_LENGTH) + `... (${inputStr.length - MAX_INPUT_LENGTH} more chars)`;
10965
+ return {
10966
+ ...step,
10967
+ metadata: {
10968
+ ...step.metadata,
10969
+ toolInput: truncatedInput
10970
+ }
10971
+ };
10972
+ }
10973
+ }
10974
+ if (step.type === "observation" && step.content) {
10975
+ if (step.metadata?.toolName === "file_read") {
10976
+ const lineCount = step.content.split("\n").length;
10977
+ return {
10978
+ ...step,
10979
+ content: `Read ${lineCount} line${lineCount !== 1 ? "s" : ""}`
10980
+ };
10981
+ }
10982
+ if (step.metadata?.toolName === "grep_search") {
10983
+ const match = step.content.match(/^Found (\d+) file\(s\)/);
10984
+ if (match) {
10985
+ const count = parseInt(match[1], 10);
10986
+ return {
10987
+ ...step,
10988
+ content: `Found ${count} file${count !== 1 ? "s" : ""}`
10989
+ };
10990
+ }
10991
+ }
10992
+ if (step.metadata?.toolName === "glob_files") {
10993
+ const match = step.content.match(/^Found (\d+) file\(s\)/);
10994
+ if (match) {
10995
+ const count = parseInt(match[1], 10);
10996
+ return {
10997
+ ...step,
10998
+ content: `Found ${count} file${count !== 1 ? "s" : ""}`
10999
+ };
11000
+ }
11001
+ }
11002
+ }
11003
+ return step;
11004
+ };
11005
+
10874
11006
  // src/utils/argumentSubstitution.ts
10875
11007
  function substituteArguments(template, args) {
10876
11008
  let result = template;
@@ -11786,7 +11918,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
11786
11918
  // package.json
11787
11919
  var package_default = {
11788
11920
  name: "@bike4mind/cli",
11789
- version: "0.2.12-refactor-github-mcp.17405+ae8801bbe",
11921
+ version: "0.2.14-feat-auto-context-customization.17477+d37aab404",
11790
11922
  type: "module",
11791
11923
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
11792
11924
  license: "UNLICENSED",
@@ -11857,6 +11989,7 @@ var package_default = {
11857
11989
  "eventsource-parser": "^3.0.6",
11858
11990
  "file-type": "^18.7.0",
11859
11991
  "fuse.js": "^7.1.0",
11992
+ glob: "^13.0.0",
11860
11993
  "gray-matter": "^4.0.3",
11861
11994
  ink: "^6.5.1",
11862
11995
  "ink-select-input": "^6.2.0",
@@ -11891,10 +12024,10 @@ var package_default = {
11891
12024
  },
11892
12025
  devDependencies: {
11893
12026
  "@bike4mind/agents": "0.1.0",
11894
- "@bike4mind/common": "2.41.1-refactor-github-mcp.17405+ae8801bbe",
11895
- "@bike4mind/mcp": "1.21.1-refactor-github-mcp.17405+ae8801bbe",
11896
- "@bike4mind/services": "2.36.1-refactor-github-mcp.17405+ae8801bbe",
11897
- "@bike4mind/utils": "2.1.6-refactor-github-mcp.17405+ae8801bbe",
12027
+ "@bike4mind/common": "2.42.1-feat-auto-context-customization.17477+d37aab404",
12028
+ "@bike4mind/mcp": "1.21.3-feat-auto-context-customization.17477+d37aab404",
12029
+ "@bike4mind/services": "2.37.1-feat-auto-context-customization.17477+d37aab404",
12030
+ "@bike4mind/utils": "2.1.8-feat-auto-context-customization.17477+d37aab404",
11898
12031
  "@types/better-sqlite3": "^7.6.13",
11899
12032
  "@types/diff": "^5.0.9",
11900
12033
  "@types/jsonwebtoken": "^9.0.4",
@@ -11902,12 +12035,13 @@ var package_default = {
11902
12035
  "@types/react": "^19.2.7",
11903
12036
  "@types/uuid": "^9.0.7",
11904
12037
  "@types/yargs": "^17.0.32",
12038
+ "ink-testing-library": "^4.0.0",
11905
12039
  tsup: "^8.5.1",
11906
12040
  tsx: "^4.21.0",
11907
12041
  typescript: "^5.9.3",
11908
12042
  vitest: "^3.2.4"
11909
12043
  },
11910
- gitHead: "ae8801bbe418b5047a42104f8aa2bb56c198e4b5"
12044
+ gitHead: "d37aab40411dfc92c084c715054d5ea3aaaff579"
11911
12045
  };
11912
12046
 
11913
12047
  // src/config/constants.ts
@@ -12263,6 +12397,131 @@ function createSubagentDelegateTool(orchestrator, parentSessionId) {
12263
12397
  };
12264
12398
  }
12265
12399
 
12400
+ // src/tools/writeTodosTool.ts
12401
+ var VALID_STATUSES = ["pending", "in_progress", "completed", "cancelled"];
12402
+ function validateTodos(todos) {
12403
+ if (!Array.isArray(todos)) {
12404
+ throw new Error("write_todos: todos must be an array");
12405
+ }
12406
+ if (todos.length === 0) {
12407
+ throw new Error("write_todos: todos array cannot be empty");
12408
+ }
12409
+ let inProgressCount = 0;
12410
+ const validatedTodos = todos.map((item, index) => {
12411
+ if (!item || typeof item !== "object") {
12412
+ throw new Error(`write_todos: item at index ${index} must be an object`);
12413
+ }
12414
+ const { description, status } = item;
12415
+ if (typeof description !== "string" || description.trim() === "") {
12416
+ throw new Error(`write_todos: item at index ${index} must have a non-empty description`);
12417
+ }
12418
+ if (typeof status !== "string" || !VALID_STATUSES.includes(status)) {
12419
+ throw new Error(
12420
+ `write_todos: item at index ${index} has invalid status "${status}". Must be one of: ${VALID_STATUSES.join(", ")}`
12421
+ );
12422
+ }
12423
+ if (status === "in_progress") {
12424
+ inProgressCount++;
12425
+ }
12426
+ return {
12427
+ description: description.trim(),
12428
+ status
12429
+ };
12430
+ });
12431
+ if (inProgressCount > 1) {
12432
+ throw new Error(`write_todos: only one task can be 'in_progress' at a time, but found ${inProgressCount}`);
12433
+ }
12434
+ return validatedTodos;
12435
+ }
12436
+ function formatTodosOutput(todos) {
12437
+ const statusEmoji = {
12438
+ pending: "\u2B1C",
12439
+ in_progress: "\u{1F504}",
12440
+ completed: "\u2705",
12441
+ cancelled: "\u274C"
12442
+ };
12443
+ const lines = todos.map((todo, index) => {
12444
+ const emoji = statusEmoji[todo.status];
12445
+ return `${index + 1}. ${emoji} [${todo.status}] ${todo.description}`;
12446
+ });
12447
+ return lines.join("\n");
12448
+ }
12449
+ function createWriteTodosTool(todoStore) {
12450
+ return {
12451
+ toolFn: async (args) => {
12452
+ const params = args;
12453
+ const validatedTodos = validateTodos(params.todos);
12454
+ todoStore.todos = validatedTodos;
12455
+ if (todoStore.onUpdate) {
12456
+ todoStore.onUpdate(validatedTodos);
12457
+ }
12458
+ const output = formatTodosOutput(validatedTodos);
12459
+ return `Todo list updated successfully:
12460
+
12461
+ ${output}`;
12462
+ },
12463
+ toolSchema: {
12464
+ name: "write_todos",
12465
+ description: `Create or update a list of subtasks for complex multi-step requests.
12466
+
12467
+ **When to use this tool:**
12468
+ - When handling complex requests that require multiple steps
12469
+ - To break down a large task into smaller, manageable subtasks
12470
+ - To track progress through a multi-step workflow
12471
+
12472
+ **Important guidelines:**
12473
+ - Call this tool early when receiving complex requests
12474
+ - Update the list immediately when starting, completing, or cancelling tasks
12475
+ - Only ONE task should be 'in_progress' at a time
12476
+ - Never batch updates - update as soon as state changes
12477
+
12478
+ **Status values:**
12479
+ - pending: Task not yet started
12480
+ - in_progress: Currently working on this task (only ONE allowed)
12481
+ - completed: Task finished successfully
12482
+ - cancelled: Task will not be completed
12483
+
12484
+ **Example:**
12485
+ If asked to "create a new React component with tests", break it down:
12486
+ 1. Create component file \u2192 in_progress
12487
+ 2. Add component logic \u2192 pending
12488
+ 3. Create test file \u2192 pending
12489
+ 4. Write unit tests \u2192 pending`,
12490
+ parameters: {
12491
+ type: "object",
12492
+ properties: {
12493
+ todos: {
12494
+ type: "array",
12495
+ description: "Complete list of todo items. This replaces any existing list. Each item needs a description and status.",
12496
+ items: {
12497
+ type: "object",
12498
+ properties: {
12499
+ description: {
12500
+ type: "string",
12501
+ description: "Clear, concise description of the task"
12502
+ },
12503
+ status: {
12504
+ type: "string",
12505
+ enum: ["pending", "in_progress", "completed", "cancelled"],
12506
+ description: "Current status of the task. Only ONE task can be 'in_progress' at a time."
12507
+ }
12508
+ },
12509
+ required: ["description", "status"]
12510
+ }
12511
+ }
12512
+ },
12513
+ required: ["todos"]
12514
+ }
12515
+ }
12516
+ };
12517
+ }
12518
+ function createTodoStore(onUpdate) {
12519
+ return {
12520
+ todos: [],
12521
+ onUpdate
12522
+ };
12523
+ }
12524
+
12266
12525
  // src/index.tsx
12267
12526
  process.removeAllListeners("warning");
12268
12527
  process.on("warning", (warning) => {
@@ -12547,7 +12806,9 @@ function CliApp() {
12547
12806
  subagentConfigs
12548
12807
  });
12549
12808
  const subagentDelegateTool = createSubagentDelegateTool(orchestrator, newSession.id);
12550
- const allTools = [...b4mTools, ...mcpTools, subagentDelegateTool];
12809
+ const todoStore = createTodoStore();
12810
+ const writeTodosTool = createWriteTodosTool(todoStore);
12811
+ const allTools = [...b4mTools, ...mcpTools, subagentDelegateTool, writeTodosTool];
12551
12812
  console.log(`\u{1F916} Subagent delegation enabled (explore, plan, review)`);
12552
12813
  const projectDir = state.configStore.getProjectConfigDir();
12553
12814
  const contextResult = await loadContextFiles(projectDir);
@@ -12587,26 +12848,12 @@ ${contextResult.mergedContent}` : "";
12587
12848
  const lastIdx = pendingMessages.length - 1;
12588
12849
  if (lastIdx >= 0 && pendingMessages[lastIdx].role === "assistant") {
12589
12850
  const existingSteps = pendingMessages[lastIdx].metadata?.steps || [];
12590
- const MAX_INPUT_LENGTH = 500;
12591
- let truncatedStep = step;
12592
- if (step.type === "action" && step.metadata?.toolInput) {
12593
- const inputStr = typeof step.metadata.toolInput === "string" ? step.metadata.toolInput : JSON.stringify(step.metadata.toolInput);
12594
- if (inputStr.length > MAX_INPUT_LENGTH) {
12595
- const truncatedInput = inputStr.slice(0, MAX_INPUT_LENGTH) + `... (${inputStr.length - MAX_INPUT_LENGTH} more chars)`;
12596
- truncatedStep = {
12597
- ...step,
12598
- metadata: {
12599
- ...step.metadata,
12600
- toolInput: truncatedInput
12601
- }
12602
- };
12603
- }
12604
- }
12851
+ const formattedStep = formatStep(step);
12605
12852
  updatePendingMessage(lastIdx, {
12606
12853
  ...pendingMessages[lastIdx],
12607
12854
  metadata: {
12608
12855
  ...pendingMessages[lastIdx].metadata,
12609
- steps: [...existingSteps, truncatedStep]
12856
+ steps: [...existingSteps, formattedStep]
12610
12857
  }
12611
12858
  });
12612
12859
  }
@@ -12748,7 +12995,7 @@ ${contextResult.mergedContent}` : "";
12748
12995
  content: result.finalAnswer,
12749
12996
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
12750
12997
  metadata: {
12751
- steps: result.steps,
12998
+ steps: result.steps.map(formatStep),
12752
12999
  tokenUsage: {
12753
13000
  prompt: 0,
12754
13001
  completion: 0,
@@ -12921,7 +13168,7 @@ ${contextResult.mergedContent}` : "";
12921
13168
  completion: 0,
12922
13169
  total: result.completionInfo.totalTokens
12923
13170
  },
12924
- steps: result.steps,
13171
+ steps: result.steps.map(formatStep),
12925
13172
  // Complete history: thoughts, actions, observations
12926
13173
  permissionDenied
12927
13174
  }
@@ -13027,7 +13274,7 @@ ${output}` : output.trim() || "(no output)",
13027
13274
  if (!imageStore) {
13028
13275
  if (!imageStoreInitPromise.current) {
13029
13276
  imageStoreInitPromise.current = (async () => {
13030
- const { ImageStore: ImageStoreClass } = await import("./ImageStore-RQZ7OF7P.js");
13277
+ const { ImageStore: ImageStoreClass } = await import("./ImageStore-MMUOUPI2.js");
13031
13278
  const newImageStore = new ImageStoreClass();
13032
13279
  setState((prev) => ({
13033
13280
  ...prev,