@bike4mind/cli 0.2.11-cli-cancel-pending-message.17326 → 0.2.11-cli-todo-list.17329

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.
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-PDX44BCA.js";
3
2
 
4
3
  // src/storage/ImageStore.ts
5
4
  import { createHash } from "crypto";
@@ -2,7 +2,6 @@
2
2
  import {
3
3
  CurationArtifactType
4
4
  } from "./chunk-WCYNJOOX.js";
5
- import "./chunk-PDX44BCA.js";
6
5
 
7
6
  // ../../b4m-core/packages/services/dist/src/notebookCurationService/artifactExtractor.js
8
7
  var ARTIFACT_TAG_REGEX = /<artifact\s+(.*?)>([\s\S]*?)<\/artifact>/gi;
@@ -6,7 +6,7 @@ import {
6
6
  getSettingsByNames,
7
7
  obfuscateApiKey,
8
8
  secureParameters
9
- } from "./chunk-SQBLLN7K.js";
9
+ } from "./chunk-M4EAFD3H.js";
10
10
  import {
11
11
  ApiKeyType,
12
12
  MementoTier,
@@ -7,7 +7,7 @@ import {
7
7
  getSettingsMap,
8
8
  getSettingsValue,
9
9
  secureParameters
10
- } from "./chunk-SQBLLN7K.js";
10
+ } from "./chunk-M4EAFD3H.js";
11
11
  import {
12
12
  KnowledgeType,
13
13
  SupportedFabFileMimeTypes
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  Logger
4
- } from "./chunk-AMDXHL6S.js";
4
+ } from "./chunk-OCYRD7D6.js";
5
5
  import {
6
6
  BedrockEmbeddingModel,
7
7
  ChatModels,
@@ -160,7 +160,7 @@ var notifyEventLogsToSlack = async ({ event, stage, slackUrl, throttlingSlackUrl
160
160
  if (!allowedStages.includes(stage))
161
161
  return;
162
162
  const logEvents = logData.logEvents;
163
- const { notificationDeduplicator: notificationDeduplicator2 } = await import("./notificationDeduplicator-UW7D75QS.js");
163
+ const { notificationDeduplicator: notificationDeduplicator2 } = await import("./notificationDeduplicator-UTHJHMSZ.js");
164
164
  for (const logEvent of logEvents) {
165
165
  try {
166
166
  let message;
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  BadRequestError,
4
4
  secureParameters
5
- } from "./chunk-SQBLLN7K.js";
5
+ } from "./chunk-M4EAFD3H.js";
6
6
  import {
7
7
  GenericCreditDeductTransaction,
8
8
  ImageEditUsageTransaction,
@@ -2,11 +2,10 @@
2
2
  import {
3
3
  createFabFile,
4
4
  createFabFileSchema
5
- } from "./chunk-RA3CZOUX.js";
6
- import "./chunk-SQBLLN7K.js";
7
- import "./chunk-AMDXHL6S.js";
5
+ } from "./chunk-GLQV2QBP.js";
6
+ import "./chunk-M4EAFD3H.js";
7
+ import "./chunk-OCYRD7D6.js";
8
8
  import "./chunk-WCYNJOOX.js";
9
- import "./chunk-PDX44BCA.js";
10
9
  export {
11
10
  createFabFile,
12
11
  createFabFileSchema
@@ -2,7 +2,6 @@
2
2
  import {
3
3
  FormatConverter
4
4
  } from "./chunk-BPFEGDC7.js";
5
- import "./chunk-PDX44BCA.js";
6
5
  export {
7
6
  FormatConverter
8
7
  };
package/dist/index.js CHANGED
@@ -4,9 +4,9 @@ import {
4
4
  getEffectiveApiKey,
5
5
  getOpenWeatherKey,
6
6
  getSerperKey
7
- } from "./chunk-DGAIF2QC.js";
8
- import "./chunk-RBZRTCAY.js";
9
- import "./chunk-RA3CZOUX.js";
7
+ } from "./chunk-6AYWWNXH.js";
8
+ import "./chunk-WENSCUXW.js";
9
+ import "./chunk-GLQV2QBP.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-SQBLLN7K.js";
18
+ } from "./chunk-M4EAFD3H.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,
@@ -74,9 +74,6 @@ import {
74
74
  b4mLLMTools,
75
75
  getMcpProviderMetadata
76
76
  } from "./chunk-WCYNJOOX.js";
77
- import {
78
- __require
79
- } from "./chunk-PDX44BCA.js";
80
77
 
81
78
  // src/index.tsx
82
79
  import React17, { useState as useState8, useEffect as useEffect3, useCallback, useRef as useRef2 } from "react";
@@ -3093,6 +3090,21 @@ ${options.context}` : this.getSystemPrompt()
3093
3090
  let finalAnswer = "";
3094
3091
  let reachedMaxIterations = false;
3095
3092
  while (iterations < maxIterations) {
3093
+ if (options.signal?.aborted) {
3094
+ this.context.logger.info("[ReActAgent] Operation aborted by user");
3095
+ const result2 = {
3096
+ finalAnswer: "Interrupted",
3097
+ steps: this.steps,
3098
+ completionInfo: {
3099
+ totalTokens: this.totalTokens,
3100
+ iterations,
3101
+ toolCalls: this.toolCallCount,
3102
+ reachedMaxIterations: false
3103
+ }
3104
+ };
3105
+ this.emit("complete", result2);
3106
+ return result2;
3107
+ }
3096
3108
  iterations++;
3097
3109
  this.context.logger.debug(`[ReActAgent] Starting iteration ${iterations}/${maxIterations}`);
3098
3110
  let iterationComplete = false;
@@ -3106,7 +3118,8 @@ ${options.context}` : this.getSystemPrompt()
3106
3118
  stream: false,
3107
3119
  tools: this.context.tools,
3108
3120
  maxTokens,
3109
- temperature
3121
+ temperature,
3122
+ abortSignal: options.signal
3110
3123
  },
3111
3124
  async (texts, completionInfo) => {
3112
3125
  for (const text of texts) {
@@ -3249,6 +3262,9 @@ ${options.context}` : this.getSystemPrompt()
3249
3262
  this.emit("final_answer", finalStep);
3250
3263
  }
3251
3264
  }
3265
+ if (options.signal?.aborted && !finalAnswer) {
3266
+ finalAnswer = "Interrupted";
3267
+ }
3252
3268
  const result = {
3253
3269
  finalAnswer,
3254
3270
  steps: this.steps,
@@ -3357,6 +3373,15 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
3357
3373
  };
3358
3374
 
3359
3375
  // src/core/prompts.ts
3376
+ var TOOL_GREP_SEARCH = "grep_search";
3377
+ var TOOL_GLOB_FILES = "glob_files";
3378
+ var TOOL_FILE_READ = "file_read";
3379
+ var TOOL_EDIT_LOCAL_FILE = "edit_local_file";
3380
+ var TOOL_CREATE_FILE = "create_file";
3381
+ var TOOL_BASH_EXECUTE = "bash_execute";
3382
+ var TOOL_SUBAGENT_DELEGATE = "subagent_delegate";
3383
+ var TOOL_WRITE_TODOS = "write_todos";
3384
+ var EXPLORE_SUBAGENT_TYPE = "explore";
3360
3385
  function buildCoreSystemPrompt(contextSection = "") {
3361
3386
  return `You are an autonomous AI assistant with access to tools. Your job is to help users by taking action and solving problems proactively.
3362
3387
 
@@ -3368,15 +3393,17 @@ CORE BEHAVIOR:
3368
3393
 
3369
3394
  FOR SOFTWARE ENGINEERING TASKS:
3370
3395
  When requested to perform tasks like fixing bugs, adding features, refactoring, or explaining code, follow this sequence:
3371
- 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'.
3396
+ 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}'.
3397
+ 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.
3372
3398
  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.
3373
- 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.
3399
+ 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.
3400
+ 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.
3374
3401
  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.
3375
3402
  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.
3376
3403
  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.
3377
3404
 
3378
3405
  SUBAGENT DELEGATION:
3379
- - You have access to specialized subagents via the subagent_delegate tool
3406
+ - You have access to specialized subagents via the ${TOOL_SUBAGENT_DELEGATE} tool
3380
3407
  - Use subagents for focused exploration, planning, or review tasks:
3381
3408
  * explore: Fast read-only codebase search (e.g., "find all auth files", "locate API endpoints")
3382
3409
  * plan: Break down complex tasks into actionable steps
@@ -3392,7 +3419,7 @@ FOR GENERAL TASKS:
3392
3419
  ## Shell tool output token efficiency:
3393
3420
  IT IS CRITICAL TO FOLLOW THESE GUIDELINES TO AVOID EXCESSIVE TOKEN CONSUMPTION.
3394
3421
 
3395
- - Always prefer command flags that reduce output verbosity when using 'bash_execute'.
3422
+ - Always prefer command flags that reduce output verbosity when using '${TOOL_BASH_EXECUTE}'.
3396
3423
  - Aim to minimize tool output tokens while still capturing necessary information.
3397
3424
  - If a command is expected to produce a lot of output, use quiet or silent flags where available and appropriate.
3398
3425
  - 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.
@@ -3402,9 +3429,9 @@ IT IS CRITICAL TO FOLLOW THESE GUIDELINES TO AVOID EXCESSIVE TOKEN CONSUMPTION.
3402
3429
  EXAMPLES:
3403
3430
  - "what should I wear in Texas?" \u2192 use weather tool for Texas
3404
3431
  - "how about Japan?" \u2192 use weather tool for Japan (applying same question from context)
3405
- - "enhance README" \u2192 file_read \u2192 generate \u2192 create_file
3406
- - "what packages installed?" \u2192 glob_files "**/package.json" \u2192 file_read
3407
- - "find all components using React hooks" \u2192 subagent_delegate(task="find all components using React hooks", type="explore")
3432
+ - "enhance README" \u2192 ${TOOL_FILE_READ} \u2192 generate \u2192 ${TOOL_EDIT_LOCAL_FILE}
3433
+ - "what packages installed?" \u2192 ${TOOL_GLOB_FILES} "**/package.json" \u2192 ${TOOL_FILE_READ}
3434
+ - "find all components using React hooks" \u2192 ${TOOL_SUBAGENT_DELEGATE}(task="find all components using React hooks", type="explore")
3408
3435
 
3409
3436
  Remember: Use context from previous messages to understand follow-up questions.${contextSection}`;
3410
3437
  }
@@ -8760,98 +8787,80 @@ var createFileTool = {
8760
8787
  };
8761
8788
 
8762
8789
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/globFiles/index.js
8763
- import { readdir } from "fs/promises";
8764
- import { statSync as statSync5 } from "fs";
8790
+ import { glob } from "glob";
8791
+ import { stat } from "fs/promises";
8765
8792
  import path9 from "path";
8766
- function matchGlob(filePath, pattern) {
8767
- let regexPattern = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/\{([^}]+)\}/g, (_, options) => `(${options.split(",").join("|")})`).replace(/\[([^\]]+)\]/g, "[$1]");
8768
- regexPattern = regexPattern.replace(/\{\{GLOBSTAR\}\}\//g, "(.*/)?").replace(/\/\{\{GLOBSTAR\}\}/g, "(/.*)?").replace(/\{\{GLOBSTAR\}\}/g, ".*");
8769
- const regex = new RegExp(`^${regexPattern}$`);
8770
- return regex.test(filePath);
8771
- }
8772
- async function walkDir(dir, pattern, includeHidden, maxResults, basePath = "") {
8773
- const matches = [];
8774
- try {
8775
- const entries = await readdir(dir, { withFileTypes: true });
8776
- for (const entry of entries) {
8777
- if (matches.length >= maxResults) {
8778
- break;
8779
- }
8780
- if (!includeHidden && entry.name.startsWith(".")) {
8781
- continue;
8782
- }
8783
- const relativePath = basePath ? path9.join(basePath, entry.name) : entry.name;
8784
- const fullPath = path9.join(dir, entry.name);
8785
- if (matchGlob(relativePath, pattern)) {
8786
- matches.push(relativePath);
8787
- }
8788
- if (entry.isDirectory() && (pattern.includes("**") || pattern.includes("/"))) {
8789
- const subMatches = await walkDir(fullPath, pattern, includeHidden, maxResults - matches.length, relativePath);
8790
- matches.push(...subMatches);
8791
- }
8792
- }
8793
- } catch (error) {
8794
- }
8795
- return matches;
8796
- }
8797
- async function listFiles(params) {
8798
- const { pattern, cwd: relativeCwd, includeHidden = false, maxResults = 1e3 } = params;
8793
+ var DEFAULT_IGNORE_PATTERNS2 = [
8794
+ "**/node_modules/**",
8795
+ "**/.git/**",
8796
+ "**/dist/**",
8797
+ "**/build/**",
8798
+ "**/.next/**",
8799
+ "**/coverage/**",
8800
+ "**/.turbo/**",
8801
+ "**/.sst/**",
8802
+ "**/*.min.js",
8803
+ "**/*.min.css"
8804
+ ];
8805
+ async function findFiles(params) {
8806
+ const { pattern, dir_path, case_sensitive = true, respect_git_ignore = true } = params;
8799
8807
  const baseCwd = process.cwd();
8800
- const targetCwd = relativeCwd ? path9.resolve(baseCwd, path9.normalize(relativeCwd)) : baseCwd;
8801
- if (!targetCwd.startsWith(baseCwd)) {
8802
- throw new Error(`Access denied: Cannot list files outside of current working directory`);
8803
- }
8804
- const matches = await walkDir(targetCwd, pattern, includeHidden, maxResults);
8808
+ const targetDir = dir_path ? path9.resolve(baseCwd, path9.normalize(dir_path)) : baseCwd;
8809
+ if (!targetDir.startsWith(baseCwd)) {
8810
+ throw new Error(`Access denied: Cannot search outside of current working directory`);
8811
+ }
8812
+ const ignorePatterns = respect_git_ignore ? DEFAULT_IGNORE_PATTERNS2 : [];
8813
+ const matches = await glob(pattern, {
8814
+ cwd: targetDir,
8815
+ dot: false,
8816
+ // Don't match hidden files by default
8817
+ ignore: ignorePatterns,
8818
+ absolute: true,
8819
+ nodir: true,
8820
+ // Only return files, not directories
8821
+ nocase: !case_sensitive,
8822
+ // Case-insensitive if requested
8823
+ maxDepth: 20
8824
+ // Reasonable depth limit
8825
+ });
8805
8826
  if (matches.length === 0) {
8806
- return `No files found matching pattern: ${pattern}`;
8827
+ return `No files found matching pattern: ${pattern}${dir_path ? ` in ${dir_path}` : ""}`;
8807
8828
  }
8808
- const filesInfo = [];
8809
- for (const match of matches) {
8829
+ const filesWithStats = [];
8830
+ for (const filePath of matches) {
8810
8831
  try {
8811
- const fullPath = path9.join(targetCwd, match);
8812
- const stats = statSync5(fullPath);
8813
- filesInfo.push({
8814
- path: match,
8815
- size: stats.size,
8816
- isDirectory: stats.isDirectory(),
8817
- modified: stats.mtime.toISOString()
8832
+ const stats = await stat(filePath);
8833
+ filesWithStats.push({
8834
+ path: filePath,
8835
+ mtime: stats.mtimeMs
8818
8836
  });
8819
- } catch (error) {
8837
+ } catch {
8820
8838
  continue;
8821
8839
  }
8822
8840
  }
8823
- const truncated = matches.length >= maxResults;
8824
- const summary = `Found ${filesInfo.length} file(s)${truncated ? ` (truncated to ${maxResults})` : ""} matching: ${pattern}
8825
- `;
8826
- const cwdInfo = relativeCwd ? `Directory: ${relativeCwd}
8827
- ` : "";
8828
- const filesList = filesInfo.map((file) => {
8829
- const typeIcon = file.isDirectory ? "\u{1F4C1}" : "\u{1F4C4}";
8830
- const sizeStr = file.isDirectory ? "" : ` (${formatBytes(file.size)})`;
8831
- return `${typeIcon} ${file.path}${sizeStr}`;
8832
- }).join("\n");
8833
- return `${summary}${cwdInfo}
8841
+ filesWithStats.sort((a, b) => b.mtime - a.mtime);
8842
+ const MAX_RESULTS = 500;
8843
+ const truncated = filesWithStats.length > MAX_RESULTS;
8844
+ const results = truncated ? filesWithStats.slice(0, MAX_RESULTS) : filesWithStats;
8845
+ const summary = `Found ${filesWithStats.length} file(s)${truncated ? ` (showing first ${MAX_RESULTS})` : ""} matching: ${pattern}`;
8846
+ const dirInfo = dir_path ? `
8847
+ Directory: ${dir_path}` : "";
8848
+ const filesList = results.map((file) => path9.relative(baseCwd, file.path)).join("\n");
8849
+ return `${summary}${dirInfo}
8850
+
8834
8851
  ${filesList}`;
8835
8852
  }
8836
- function formatBytes(bytes) {
8837
- if (bytes === 0)
8838
- return "0 B";
8839
- const k = 1024;
8840
- const sizes = ["B", "KB", "MB", "GB"];
8841
- const i = Math.floor(Math.log(bytes) / Math.log(k));
8842
- return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
8843
- }
8844
8853
  var globFilesTool = {
8845
8854
  name: "glob_files",
8846
8855
  implementation: (context) => ({
8847
8856
  toolFn: async (value) => {
8848
8857
  const params = value;
8849
- context.logger.info("\u{1F50D} GlobFiles: Listing files", {
8858
+ context.logger.info("\u{1F50D} GlobFiles: Finding files", {
8850
8859
  pattern: params.pattern,
8851
- cwd: params.cwd || "."
8860
+ dir_path: params.dir_path || "."
8852
8861
  });
8853
8862
  try {
8854
- const result = await listFiles(params);
8863
+ const result = await findFiles(params);
8855
8864
  context.logger.info("\u2705 GlobFiles: Success");
8856
8865
  return result;
8857
8866
  } catch (error) {
@@ -8861,25 +8870,25 @@ var globFilesTool = {
8861
8870
  },
8862
8871
  toolSchema: {
8863
8872
  name: "glob_files",
8864
- description: "List files and directories matching a glob pattern. Supports wildcards like *, **, ?, and character ranges. Restricted to current working directory for security.",
8873
+ 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.",
8865
8874
  parameters: {
8866
8875
  type: "object",
8867
8876
  properties: {
8868
8877
  pattern: {
8869
8878
  type: "string",
8870
- description: 'Glob pattern to match files (e.g., "*.ts", "src/**/*.tsx", "test/*.{js,ts}"). Use ** for recursive directory search.'
8879
+ 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.'
8871
8880
  },
8872
- cwd: {
8881
+ dir_path: {
8873
8882
  type: "string",
8874
- description: "Directory to search in, relative to current working directory (optional). Defaults to current directory."
8883
+ 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."
8875
8884
  },
8876
- includeHidden: {
8885
+ case_sensitive: {
8877
8886
  type: "boolean",
8878
- description: "Include hidden files and directories (starting with .). Defaults to false."
8887
+ 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."
8879
8888
  },
8880
- maxResults: {
8881
- type: "number",
8882
- description: "Maximum number of results to return (default: 1000). Prevents overwhelming output."
8889
+ respect_git_ignore: {
8890
+ type: "boolean",
8891
+ description: "Optional: Whether to respect common ignore patterns (node_modules, .git, dist, build, etc.). Defaults to true. Set to false to include all files."
8883
8892
  }
8884
8893
  },
8885
8894
  required: ["pattern"]
@@ -8889,158 +8898,144 @@ var globFilesTool = {
8889
8898
  };
8890
8899
 
8891
8900
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/grepSearch/index.js
8892
- import { readdir as readdir2 } from "fs/promises";
8893
- import { readFileSync as readFileSync3, statSync as statSync6 } from "fs";
8901
+ import { globStream } from "glob";
8902
+ import { readFile, stat as stat2 } from "fs/promises";
8894
8903
  import path10 from "path";
8895
8904
  var MAX_FILE_SIZE3 = 10 * 1024 * 1024;
8896
- function matchGlob2(filePath, pattern) {
8897
- const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{GLOBSTAR\}\}/g, ".*").replace(/\?/g, ".").replace(/\{([^}]+)\}/g, (_, options) => `(${options.split(",").join("|")})`).replace(/\[([^\]]+)\]/g, "[$1]");
8898
- const regex = new RegExp(`^${regexPattern}$`);
8899
- return regex.test(filePath);
8900
- }
8901
- async function walkDir2(dir, pattern, maxResults, basePath = "") {
8902
- const matches = [];
8905
+ var DEFAULT_IGNORE_PATTERNS3 = [
8906
+ "**/node_modules/**",
8907
+ "**/.git/**",
8908
+ "**/dist/**",
8909
+ "**/build/**",
8910
+ "**/.next/**",
8911
+ "**/coverage/**",
8912
+ "**/*.min.js",
8913
+ "**/*.min.css",
8914
+ "**/package-lock.json",
8915
+ "**/pnpm-lock.yaml",
8916
+ "**/yarn.lock"
8917
+ ];
8918
+ async function isBinaryFile2(filePath) {
8903
8919
  try {
8904
- const entries = await readdir2(dir, { withFileTypes: true });
8905
- for (const entry of entries) {
8906
- if (matches.length >= maxResults) {
8907
- break;
8908
- }
8909
- if (entry.name.startsWith(".")) {
8910
- continue;
8911
- }
8912
- const relativePath = basePath ? path10.join(basePath, entry.name) : entry.name;
8913
- const fullPath = path10.join(dir, entry.name);
8914
- if (matchGlob2(relativePath, pattern)) {
8915
- matches.push(relativePath);
8916
- }
8917
- if (entry.isDirectory() && (pattern.includes("**") || pattern.includes("/"))) {
8918
- const subMatches = await walkDir2(fullPath, pattern, maxResults - matches.length, relativePath);
8919
- matches.push(...subMatches);
8920
- }
8921
- }
8922
- } catch (error) {
8920
+ const { openSync, readSync, closeSync } = await import("fs");
8921
+ const buffer = Buffer.alloc(8192);
8922
+ const fd = openSync(filePath, "r");
8923
+ const bytesRead = readSync(fd, buffer, 0, 8192, 0);
8924
+ closeSync(fd);
8925
+ const chunk = buffer.slice(0, bytesRead);
8926
+ return chunk.includes(0);
8927
+ } catch {
8928
+ return true;
8923
8929
  }
8924
- return matches;
8930
+ }
8931
+ function isPathWithinWorkspace(targetPath, baseCwd) {
8932
+ const resolvedTarget = path10.resolve(targetPath);
8933
+ const resolvedBase = path10.resolve(baseCwd);
8934
+ return resolvedTarget.startsWith(resolvedBase);
8925
8935
  }
8926
8936
  async function searchFiles2(params) {
8927
- const { pattern, filePattern = "**/*", cwd: relativeCwd, caseSensitive = false, maxResults = 100, contextLines = 0 } = params;
8937
+ const { pattern, dir_path, include } = params;
8928
8938
  const baseCwd = process.cwd();
8929
- const targetCwd = relativeCwd ? path10.resolve(baseCwd, path10.normalize(relativeCwd)) : baseCwd;
8930
- if (!targetCwd.startsWith(baseCwd)) {
8931
- throw new Error(`Access denied: Cannot search files outside of current working directory`);
8939
+ const targetDir = dir_path ? path10.resolve(baseCwd, dir_path) : baseCwd;
8940
+ if (!isPathWithinWorkspace(targetDir, baseCwd)) {
8941
+ throw new Error(`Path validation failed: "${dir_path}" resolves outside the allowed workspace directory`);
8942
+ }
8943
+ try {
8944
+ const stats = await stat2(targetDir);
8945
+ if (!stats.isDirectory()) {
8946
+ throw new Error(`Path is not a directory: ${dir_path}`);
8947
+ }
8948
+ } catch (error) {
8949
+ if (error.code === "ENOENT") {
8950
+ throw new Error(`Path does not exist: ${dir_path}`);
8951
+ }
8952
+ throw error;
8932
8953
  }
8933
8954
  let searchRegex;
8934
8955
  try {
8935
- const flags = caseSensitive ? "g" : "gi";
8936
- searchRegex = new RegExp(pattern, flags);
8956
+ searchRegex = new RegExp(pattern, "i");
8937
8957
  } catch (error) {
8938
8958
  throw new Error(`Invalid regex pattern: ${pattern}`);
8939
8959
  }
8940
- const filesToSearch = await walkDir2(targetCwd, filePattern, 1e4);
8941
- const filteredFiles = [];
8942
- for (const match of filesToSearch) {
8943
- const fullPath = path10.join(targetCwd, match);
8960
+ const globPattern = include || "**/*";
8961
+ const filesStream = globStream(globPattern, {
8962
+ cwd: targetDir,
8963
+ dot: false,
8964
+ // Skip hidden files
8965
+ ignore: DEFAULT_IGNORE_PATTERNS3,
8966
+ absolute: true,
8967
+ nodir: true
8968
+ // Only match files, not directories
8969
+ });
8970
+ const allMatches = [];
8971
+ let filesSearched = 0;
8972
+ const maxMatches = 500;
8973
+ for await (const filePath of filesStream) {
8974
+ if (allMatches.length >= maxMatches) {
8975
+ break;
8976
+ }
8977
+ const fileAbsPath = filePath;
8944
8978
  try {
8945
- const stats = statSync6(fullPath);
8946
- if (stats.isDirectory() || stats.size > MAX_FILE_SIZE3) {
8979
+ const stats = await stat2(fileAbsPath);
8980
+ if (stats.size > MAX_FILE_SIZE3) {
8947
8981
  continue;
8948
8982
  }
8949
- if (isBinaryFile2(fullPath)) {
8983
+ if (await isBinaryFile2(fileAbsPath)) {
8950
8984
  continue;
8951
8985
  }
8952
- filteredFiles.push(match);
8953
- } catch {
8954
- continue;
8955
- }
8956
- }
8957
- if (filteredFiles.length === 0) {
8958
- return `No files found matching pattern: ${filePattern}`;
8959
- }
8960
- const matches = [];
8961
- let filesSearched = 0;
8962
- for (const file of filteredFiles) {
8963
- if (matches.length >= maxResults) {
8964
- break;
8965
- }
8966
- filesSearched++;
8967
- const fullPath = path10.join(targetCwd, file);
8968
- try {
8969
- const content = readFileSync3(fullPath, "utf-8");
8970
- const lines = content.split("\n");
8971
- for (let i = 0; i < lines.length; i++) {
8972
- if (matches.length >= maxResults) {
8973
- break;
8974
- }
8975
- const line = lines[i];
8976
- if (searchRegex.test(line)) {
8977
- const match = {
8978
- file,
8979
- line: i + 1,
8980
- // 1-indexed line numbers
8981
- content: line.trim()
8982
- };
8983
- if (contextLines > 0) {
8984
- const before = [];
8985
- const after = [];
8986
- for (let j = Math.max(0, i - contextLines); j < i; j++) {
8987
- before.push(lines[j]);
8988
- }
8989
- for (let j = i + 1; j <= Math.min(lines.length - 1, i + contextLines); j++) {
8990
- after.push(lines[j]);
8991
- }
8992
- match.context = { before, after };
8993
- }
8994
- matches.push(match);
8986
+ filesSearched++;
8987
+ const content = await readFile(fileAbsPath, "utf8");
8988
+ const lines = content.split(/\r?\n/);
8989
+ lines.forEach((line, index) => {
8990
+ if (allMatches.length < maxMatches && searchRegex.test(line)) {
8991
+ allMatches.push({
8992
+ filePath: path10.relative(targetDir, fileAbsPath) || path10.basename(fileAbsPath),
8993
+ lineNumber: index + 1,
8994
+ line
8995
+ });
8995
8996
  }
8996
- searchRegex.lastIndex = 0;
8997
+ });
8998
+ } catch (readError) {
8999
+ const errCode = readError.code;
9000
+ if (errCode !== "ENOENT" && errCode !== "EACCES") {
9001
+ console.debug(`Could not read ${fileAbsPath}: ${readError.message}`);
8997
9002
  }
8998
- } catch {
8999
- continue;
9000
9003
  }
9001
9004
  }
9002
- if (matches.length === 0) {
9003
- return `No matches found for pattern: ${pattern}
9004
- Searched ${filesSearched} file(s)`;
9005
+ const searchDirDisplay = dir_path || ".";
9006
+ const filterInfo = include ? ` (filter: "${include}")` : "";
9007
+ if (allMatches.length === 0) {
9008
+ return `No matches found for pattern "${pattern}" in "${searchDirDisplay}"${filterInfo}.
9009
+ Searched ${filesSearched} file(s).`;
9005
9010
  }
9006
- const truncated = matches.length >= maxResults;
9007
- const summary = `Found ${matches.length} match(es)${truncated ? ` (truncated to ${maxResults})` : ""} in ${filesSearched} file(s)
9011
+ const matchesByFile = allMatches.reduce((acc, match) => {
9012
+ if (!acc[match.filePath]) {
9013
+ acc[match.filePath] = [];
9014
+ }
9015
+ acc[match.filePath].push(match);
9016
+ acc[match.filePath].sort((a, b) => a.lineNumber - b.lineNumber);
9017
+ return acc;
9018
+ }, {});
9019
+ const matchCount = allMatches.length;
9020
+ const matchTerm = matchCount === 1 ? "match" : "matches";
9021
+ const truncated = matchCount >= maxMatches;
9022
+ let result = `Found ${matchCount} ${matchTerm}${truncated ? " (truncated)" : ""} for pattern "${pattern}" in "${searchDirDisplay}"${filterInfo}:
9008
9023
  `;
9009
- const patternInfo = `Pattern: ${pattern}${caseSensitive ? "" : " (case-insensitive)"}
9024
+ result += `Searched ${filesSearched} file(s)
9025
+ ---
9010
9026
  `;
9011
- const cwdInfo = relativeCwd ? `Directory: ${relativeCwd}
9012
- ` : "";
9013
- const matchesList = matches.map((match) => {
9014
- let result = `
9015
- \u{1F4C4} ${match.file}:${match.line}
9016
- ${match.content}`;
9017
- if (match.context) {
9018
- if (match.context.before.length > 0) {
9019
- result = `${result}
9020
- Context before:
9021
- ${match.context.before.map((l) => ` ${l}`).join("\n")}`;
9022
- }
9023
- if (match.context.after.length > 0) {
9024
- result = `${result}
9025
- Context after:
9026
- ${match.context.after.map((l) => ` ${l}`).join("\n")}`;
9027
- }
9028
- }
9029
- return result;
9030
- }).join("\n");
9031
- return `${summary}${patternInfo}${cwdInfo}${matchesList}`;
9032
- }
9033
- function isBinaryFile2(filePath) {
9034
- try {
9035
- const buffer = Buffer.alloc(8192);
9036
- const fd = __require("fs").openSync(filePath, "r");
9037
- const bytesRead = __require("fs").readSync(fd, buffer, 0, 8192, 0);
9038
- __require("fs").closeSync(fd);
9039
- const chunk = buffer.slice(0, bytesRead);
9040
- return chunk.includes(0);
9041
- } catch {
9042
- return true;
9027
+ for (const filePath in matchesByFile) {
9028
+ result += `File: ${filePath}
9029
+ `;
9030
+ matchesByFile[filePath].forEach((match) => {
9031
+ const trimmedLine = match.line.trim();
9032
+ const displayLine = trimmedLine.length > 200 ? trimmedLine.slice(0, 200) + "..." : trimmedLine;
9033
+ result += `L${match.lineNumber}: ${displayLine}
9034
+ `;
9035
+ });
9036
+ result += "---\n";
9043
9037
  }
9038
+ return result.trim();
9044
9039
  }
9045
9040
  var grepSearchTool = {
9046
9041
  name: "grep_search",
@@ -9049,8 +9044,8 @@ var grepSearchTool = {
9049
9044
  const params = value;
9050
9045
  context.logger.info("\u{1F50D} GrepSearch: Searching files", {
9051
9046
  pattern: params.pattern,
9052
- filePattern: params.filePattern || "**/*",
9053
- cwd: params.cwd || "."
9047
+ dir_path: params.dir_path || ".",
9048
+ include: params.include || "**/*"
9054
9049
  });
9055
9050
  try {
9056
9051
  const result = await searchFiles2(params);
@@ -9063,33 +9058,21 @@ var grepSearchTool = {
9063
9058
  },
9064
9059
  toolSchema: {
9065
9060
  name: "grep_search",
9066
- 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.",
9061
+ 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.",
9067
9062
  parameters: {
9068
9063
  type: "object",
9069
9064
  properties: {
9070
9065
  pattern: {
9071
9066
  type: "string",
9072
- description: "Regular expression pattern to search for in file contents"
9067
+ description: "The regular expression (regex) pattern to search for within file contents (e.g., 'function\\s+myFunction', 'import\\s+\\{.*\\}\\s+from\\s+.*')."
9073
9068
  },
9074
- filePattern: {
9069
+ dir_path: {
9075
9070
  type: "string",
9076
- description: 'Glob pattern to filter which files to search (optional, default: "**/*"). Example: "src/**/*.ts" to search only TypeScript files in src directory.'
9071
+ description: "Optional: The absolute path to the directory to search within. If omitted, searches the current working directory."
9077
9072
  },
9078
- cwd: {
9073
+ include: {
9079
9074
  type: "string",
9080
- description: "Directory to search in, relative to current working directory (optional). Defaults to current directory."
9081
- },
9082
- caseSensitive: {
9083
- type: "boolean",
9084
- description: "Whether the search should be case-sensitive (default: false)"
9085
- },
9086
- maxResults: {
9087
- type: "number",
9088
- description: "Maximum number of matches to return (default: 100). Prevents overwhelming output."
9089
- },
9090
- contextLines: {
9091
- type: "number",
9092
- description: "Number of context lines to show before and after each match (default: 0). Similar to grep -C option."
9075
+ 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)."
9093
9076
  }
9094
9077
  },
9095
9078
  required: ["pattern"]
@@ -9100,7 +9083,7 @@ var grepSearchTool = {
9100
9083
 
9101
9084
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/deleteFile/index.js
9102
9085
  import { promises as fs10 } from "fs";
9103
- import { existsSync as existsSync6, statSync as statSync7 } from "fs";
9086
+ import { existsSync as existsSync6, statSync as statSync5 } from "fs";
9104
9087
  import path11 from "path";
9105
9088
  async function deleteFile(params) {
9106
9089
  const { path: filePath, recursive = false } = params;
@@ -9113,7 +9096,7 @@ async function deleteFile(params) {
9113
9096
  if (!existsSync6(resolvedPath)) {
9114
9097
  throw new Error(`File or directory not found: ${filePath}`);
9115
9098
  }
9116
- const stats = statSync7(resolvedPath);
9099
+ const stats = statSync5(resolvedPath);
9117
9100
  const isDirectory = stats.isDirectory();
9118
9101
  const size = stats.size;
9119
9102
  if (isDirectory && !recursive) {
@@ -9134,7 +9117,7 @@ var deleteFileTool = {
9134
9117
  toolFn: async (value) => {
9135
9118
  const params = value;
9136
9119
  const resolvedPath = path11.resolve(process.cwd(), path11.normalize(params.path));
9137
- const isDirectory = existsSync6(resolvedPath) && statSync7(resolvedPath).isDirectory();
9120
+ const isDirectory = existsSync6(resolvedPath) && statSync5(resolvedPath).isDirectory();
9138
9121
  context.logger.info(`\u{1F5D1}\uFE0F DeleteFile: Deleting ${isDirectory ? "directory" : "file"}`, {
9139
9122
  path: params.path,
9140
9123
  recursive: params.recursive
@@ -9910,7 +9893,7 @@ var ToolErrorType;
9910
9893
 
9911
9894
  // src/utils/diffPreview.ts
9912
9895
  import * as Diff from "diff";
9913
- import { readFile } from "fs/promises";
9896
+ import { readFile as readFile2 } from "fs/promises";
9914
9897
  import { existsSync as existsSync8 } from "fs";
9915
9898
  async function generateFileDiffPreview(args) {
9916
9899
  try {
@@ -9925,7 +9908,7 @@ ${preview}${hasMore ? `
9925
9908
 
9926
9909
  ... (${lines2.length - 20} more lines)` : ""}`;
9927
9910
  }
9928
- const currentContent = await readFile(args.path, "utf-8");
9911
+ const currentContent = await readFile2(args.path, "utf-8");
9929
9912
  const patch = Diff.createPatch(
9930
9913
  args.path,
9931
9914
  currentContent,
@@ -11301,6 +11284,10 @@ var ServerLlmBackend = class {
11301
11284
  */
11302
11285
  async complete(model, messages, options, callback) {
11303
11286
  logger.debug(`[ServerLlmBackend] Starting complete() with model: ${model}`);
11287
+ if (options.abortSignal?.aborted) {
11288
+ logger.debug("[ServerLlmBackend] Request aborted before start");
11289
+ return;
11290
+ }
11304
11291
  return new Promise(async (resolve3, reject) => {
11305
11292
  try {
11306
11293
  logger.debug("[ServerLlmBackend] Making streaming request...");
@@ -11401,7 +11388,25 @@ var ServerLlmBackend = class {
11401
11388
  }
11402
11389
  }
11403
11390
  });
11391
+ if (options.abortSignal) {
11392
+ const abortHandler = () => {
11393
+ logger.debug("[ServerLlmBackend] Abort signal received, destroying stream");
11394
+ response.data.destroy();
11395
+ resolve3();
11396
+ };
11397
+ if (options.abortSignal.aborted) {
11398
+ abortHandler();
11399
+ return;
11400
+ }
11401
+ options.abortSignal.addEventListener("abort", abortHandler, { once: true });
11402
+ response.data.on("close", () => {
11403
+ options.abortSignal?.removeEventListener("abort", abortHandler);
11404
+ });
11405
+ }
11404
11406
  response.data.on("data", (chunk) => {
11407
+ if (options.abortSignal?.aborted) {
11408
+ return;
11409
+ }
11405
11410
  parser.feed(chunk.toString());
11406
11411
  });
11407
11412
  response.data.on("end", () => {
@@ -11413,9 +11418,23 @@ var ServerLlmBackend = class {
11413
11418
  }
11414
11419
  });
11415
11420
  response.data.on("error", (error) => {
11421
+ if (options.abortSignal?.aborted) {
11422
+ resolve3();
11423
+ return;
11424
+ }
11416
11425
  reject(error);
11417
11426
  });
11418
11427
  } catch (error) {
11428
+ if (options.abortSignal?.aborted) {
11429
+ logger.debug("[ServerLlmBackend] Request was aborted, resolving gracefully");
11430
+ resolve3();
11431
+ return;
11432
+ }
11433
+ if (isAxiosError(error) && error.code === "ERR_CANCELED") {
11434
+ logger.debug("[ServerLlmBackend] Request was canceled, resolving gracefully");
11435
+ resolve3();
11436
+ return;
11437
+ }
11419
11438
  logger.error("LLM completion failed", error);
11420
11439
  if (isAxiosError(error)) {
11421
11440
  logger.debug(
@@ -11566,8 +11585,10 @@ var ServerLlmBackend = class {
11566
11585
  logger.debug(` Body: ${logger.formatBytes(bodySize)}`);
11567
11586
  logger.debug(` Preview: ${bodyStr.substring(0, 200)}`);
11568
11587
  const response = await axiosInstance.post(this.completionsEndpoint, requestBody, {
11569
- responseType: "stream"
11588
+ responseType: "stream",
11570
11589
  // Auth header is automatically injected by ApiClient interceptor
11590
+ // Pass abort signal to cancel request if user presses ESC
11591
+ signal: options.abortSignal
11571
11592
  });
11572
11593
  logger.debug(`\u2190 ${response.status} ${response.statusText}`);
11573
11594
  return response;
@@ -11711,7 +11732,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
11711
11732
  // package.json
11712
11733
  var package_default = {
11713
11734
  name: "@bike4mind/cli",
11714
- version: "0.2.11-cli-cancel-pending-message.17326+24dfb0510",
11735
+ version: "0.2.11-cli-todo-list.17329+bc504ca29",
11715
11736
  type: "module",
11716
11737
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
11717
11738
  license: "UNLICENSED",
@@ -11781,6 +11802,7 @@ var package_default = {
11781
11802
  "eventsource-parser": "^3.0.6",
11782
11803
  "file-type": "^18.7.0",
11783
11804
  "fuse.js": "^7.1.0",
11805
+ glob: "^13.0.0",
11784
11806
  "gray-matter": "^4.0.3",
11785
11807
  ink: "^6.5.1",
11786
11808
  "ink-select-input": "^6.2.0",
@@ -11815,10 +11837,10 @@ var package_default = {
11815
11837
  },
11816
11838
  devDependencies: {
11817
11839
  "@bike4mind/agents": "0.1.0",
11818
- "@bike4mind/common": "2.40.1-cli-cancel-pending-message.17326+24dfb0510",
11819
- "@bike4mind/mcp": "1.20.5-cli-cancel-pending-message.17326+24dfb0510",
11820
- "@bike4mind/services": "2.35.1-cli-cancel-pending-message.17326+24dfb0510",
11821
- "@bike4mind/utils": "2.1.5-cli-cancel-pending-message.17326+24dfb0510",
11840
+ "@bike4mind/common": "2.40.1-cli-todo-list.17329+bc504ca29",
11841
+ "@bike4mind/mcp": "1.20.5-cli-todo-list.17329+bc504ca29",
11842
+ "@bike4mind/services": "2.35.1-cli-todo-list.17329+bc504ca29",
11843
+ "@bike4mind/utils": "2.1.5-cli-todo-list.17329+bc504ca29",
11822
11844
  "@types/better-sqlite3": "^7.6.13",
11823
11845
  "@types/diff": "^5.0.9",
11824
11846
  "@types/jsonwebtoken": "^9.0.4",
@@ -11831,7 +11853,7 @@ var package_default = {
11831
11853
  typescript: "^5.9.3",
11832
11854
  vitest: "^3.2.4"
11833
11855
  },
11834
- gitHead: "24dfb0510b2c3e1385417da0a3fe6c3604cdf8f8"
11856
+ gitHead: "bc504ca299975846c55f1c16f05f4e7535ce2fda"
11835
11857
  };
11836
11858
 
11837
11859
  // src/config/constants.ts
@@ -12187,6 +12209,131 @@ function createSubagentDelegateTool(orchestrator, parentSessionId) {
12187
12209
  };
12188
12210
  }
12189
12211
 
12212
+ // src/tools/writeTodosTool.ts
12213
+ var VALID_STATUSES = ["pending", "in_progress", "completed", "cancelled"];
12214
+ function validateTodos(todos) {
12215
+ if (!Array.isArray(todos)) {
12216
+ throw new Error("write_todos: todos must be an array");
12217
+ }
12218
+ if (todos.length === 0) {
12219
+ throw new Error("write_todos: todos array cannot be empty");
12220
+ }
12221
+ let inProgressCount = 0;
12222
+ const validatedTodos = todos.map((item, index) => {
12223
+ if (!item || typeof item !== "object") {
12224
+ throw new Error(`write_todos: item at index ${index} must be an object`);
12225
+ }
12226
+ const { description, status } = item;
12227
+ if (typeof description !== "string" || description.trim() === "") {
12228
+ throw new Error(`write_todos: item at index ${index} must have a non-empty description`);
12229
+ }
12230
+ if (typeof status !== "string" || !VALID_STATUSES.includes(status)) {
12231
+ throw new Error(
12232
+ `write_todos: item at index ${index} has invalid status "${status}". Must be one of: ${VALID_STATUSES.join(", ")}`
12233
+ );
12234
+ }
12235
+ if (status === "in_progress") {
12236
+ inProgressCount++;
12237
+ }
12238
+ return {
12239
+ description: description.trim(),
12240
+ status
12241
+ };
12242
+ });
12243
+ if (inProgressCount > 1) {
12244
+ throw new Error(`write_todos: only one task can be 'in_progress' at a time, but found ${inProgressCount}`);
12245
+ }
12246
+ return validatedTodos;
12247
+ }
12248
+ function formatTodosOutput(todos) {
12249
+ const statusEmoji = {
12250
+ pending: "\u2B1C",
12251
+ in_progress: "\u{1F504}",
12252
+ completed: "\u2705",
12253
+ cancelled: "\u274C"
12254
+ };
12255
+ const lines = todos.map((todo, index) => {
12256
+ const emoji = statusEmoji[todo.status];
12257
+ return `${index + 1}. ${emoji} [${todo.status}] ${todo.description}`;
12258
+ });
12259
+ return lines.join("\n");
12260
+ }
12261
+ function createWriteTodosTool(todoStore) {
12262
+ return {
12263
+ toolFn: async (args) => {
12264
+ const params = args;
12265
+ const validatedTodos = validateTodos(params.todos);
12266
+ todoStore.todos = validatedTodos;
12267
+ if (todoStore.onUpdate) {
12268
+ todoStore.onUpdate(validatedTodos);
12269
+ }
12270
+ const output = formatTodosOutput(validatedTodos);
12271
+ return `Todo list updated successfully:
12272
+
12273
+ ${output}`;
12274
+ },
12275
+ toolSchema: {
12276
+ name: "write_todos",
12277
+ description: `Create or update a list of subtasks for complex multi-step requests.
12278
+
12279
+ **When to use this tool:**
12280
+ - When handling complex requests that require multiple steps
12281
+ - To break down a large task into smaller, manageable subtasks
12282
+ - To track progress through a multi-step workflow
12283
+
12284
+ **Important guidelines:**
12285
+ - Call this tool early when receiving complex requests
12286
+ - Update the list immediately when starting, completing, or cancelling tasks
12287
+ - Only ONE task should be 'in_progress' at a time
12288
+ - Never batch updates - update as soon as state changes
12289
+
12290
+ **Status values:**
12291
+ - pending: Task not yet started
12292
+ - in_progress: Currently working on this task (only ONE allowed)
12293
+ - completed: Task finished successfully
12294
+ - cancelled: Task will not be completed
12295
+
12296
+ **Example:**
12297
+ If asked to "create a new React component with tests", break it down:
12298
+ 1. Create component file \u2192 in_progress
12299
+ 2. Add component logic \u2192 pending
12300
+ 3. Create test file \u2192 pending
12301
+ 4. Write unit tests \u2192 pending`,
12302
+ parameters: {
12303
+ type: "object",
12304
+ properties: {
12305
+ todos: {
12306
+ type: "array",
12307
+ description: "Complete list of todo items. This replaces any existing list. Each item needs a description and status.",
12308
+ items: {
12309
+ type: "object",
12310
+ properties: {
12311
+ description: {
12312
+ type: "string",
12313
+ description: "Clear, concise description of the task"
12314
+ },
12315
+ status: {
12316
+ type: "string",
12317
+ enum: ["pending", "in_progress", "completed", "cancelled"],
12318
+ description: "Current status of the task. Only ONE task can be 'in_progress' at a time."
12319
+ }
12320
+ },
12321
+ required: ["description", "status"]
12322
+ }
12323
+ }
12324
+ },
12325
+ required: ["todos"]
12326
+ }
12327
+ }
12328
+ };
12329
+ }
12330
+ function createTodoStore(onUpdate) {
12331
+ return {
12332
+ todos: [],
12333
+ onUpdate
12334
+ };
12335
+ }
12336
+
12190
12337
  // src/index.tsx
12191
12338
  process.removeAllListeners("warning");
12192
12339
  process.on("warning", (warning) => {
@@ -12270,7 +12417,7 @@ function CliApp() {
12270
12417
  logger.debug("[ABORT] ESC pressed - aborting current operation...");
12271
12418
  state.abortController.abort();
12272
12419
  setState((prev) => ({ ...prev, abortController: null }));
12273
- console.log("\n\u26A0\uFE0F Operation cancelled by user (ESC)\n");
12420
+ useCliStore.getState().setIsThinking(false);
12274
12421
  }
12275
12422
  return;
12276
12423
  }
@@ -12471,7 +12618,9 @@ function CliApp() {
12471
12618
  subagentConfigs
12472
12619
  });
12473
12620
  const subagentDelegateTool = createSubagentDelegateTool(orchestrator, newSession.id);
12474
- const allTools = [...b4mTools, ...mcpTools, subagentDelegateTool];
12621
+ const todoStore = createTodoStore();
12622
+ const writeTodosTool = createWriteTodosTool(todoStore);
12623
+ const allTools = [...b4mTools, ...mcpTools, subagentDelegateTool, writeTodosTool];
12475
12624
  console.log(`\u{1F916} Subagent delegation enabled (explore, plan, review)`);
12476
12625
  const projectDir = state.configStore.getProjectConfigDir();
12477
12626
  const contextResult = await loadContextFiles(projectDir);
@@ -12536,13 +12685,16 @@ ${contextResult.mergedContent}` : "";
12536
12685
  }
12537
12686
  };
12538
12687
  agent.on("thought", stepHandler);
12688
+ agent.on("observation", stepHandler);
12539
12689
  agent.on("action", stepHandler);
12540
12690
  orchestrator.setBeforeRunCallback((subagent, _subagentType) => {
12541
12691
  subagent.on("thought", stepHandler);
12692
+ subagent.on("observation", stepHandler);
12542
12693
  subagent.on("action", stepHandler);
12543
12694
  });
12544
12695
  orchestrator.setAfterRunCallback((subagent, _subagentType) => {
12545
12696
  subagent.off("thought", stepHandler);
12697
+ subagent.off("observation", stepHandler);
12546
12698
  subagent.off("action", stepHandler);
12547
12699
  });
12548
12700
  setState((prev) => ({
@@ -12653,10 +12805,6 @@ ${contextResult.mergedContent}` : "";
12653
12805
  previousMessages: previousMessages.length > 0 ? previousMessages : void 0,
12654
12806
  signal: abortController.signal
12655
12807
  });
12656
- if (abortController.signal.aborted) {
12657
- logger.debug("[ABORT] Custom command was cancelled, discarding result");
12658
- return;
12659
- }
12660
12808
  const permissionDenied = result.finalAnswer.startsWith("Permission denied for tool");
12661
12809
  if (permissionDenied) {
12662
12810
  console.log("\n\u26A0\uFE0F Action denied by user\n");
@@ -12829,10 +12977,6 @@ ${contextResult.mergedContent}` : "";
12829
12977
  previousMessages: previousMessages.length > 0 ? previousMessages : void 0,
12830
12978
  signal: abortController.signal
12831
12979
  });
12832
- if (abortController.signal.aborted) {
12833
- logger.debug("[ABORT] Operation was cancelled, discarding result");
12834
- return;
12835
- }
12836
12980
  const permissionDenied = result.finalAnswer.startsWith("Permission denied for tool");
12837
12981
  if (permissionDenied) {
12838
12982
  console.log("\n\u26A0\uFE0F Action denied by user\n");
@@ -12956,7 +13100,7 @@ ${output}` : output.trim() || "(no output)",
12956
13100
  if (!imageStore) {
12957
13101
  if (!imageStoreInitPromise.current) {
12958
13102
  imageStoreInitPromise.current = (async () => {
12959
- const { ImageStore: ImageStoreClass } = await import("./ImageStore-RQZ7OF7P.js");
13103
+ const { ImageStore: ImageStoreClass } = await import("./ImageStore-MMUOUPI2.js");
12960
13104
  const newImageStore = new ImageStoreClass();
12961
13105
  setState((prev) => ({
12962
13106
  ...prev,
@@ -2,7 +2,6 @@
2
2
  import {
3
3
  CurationArtifactType
4
4
  } from "./chunk-WCYNJOOX.js";
5
- import "./chunk-PDX44BCA.js";
6
5
 
7
6
  // ../../b4m-core/packages/services/dist/src/notebookCurationService/llmMarkdownGenerator.js
8
7
  var DEFAULT_OPTIONS = {
@@ -2,7 +2,6 @@
2
2
  import {
3
3
  CurationArtifactType
4
4
  } from "./chunk-WCYNJOOX.js";
5
- import "./chunk-PDX44BCA.js";
6
5
 
7
6
  // ../../b4m-core/packages/services/dist/src/notebookCurationService/markdownGenerator.js
8
7
  var DEFAULT_OPTIONS = {
@@ -2,11 +2,10 @@
2
2
  import {
3
3
  findMostSimilarMemento,
4
4
  getRelevantMementos
5
- } from "./chunk-DGAIF2QC.js";
6
- import "./chunk-SQBLLN7K.js";
7
- import "./chunk-AMDXHL6S.js";
5
+ } from "./chunk-6AYWWNXH.js";
6
+ import "./chunk-M4EAFD3H.js";
7
+ import "./chunk-OCYRD7D6.js";
8
8
  import "./chunk-WCYNJOOX.js";
9
- import "./chunk-PDX44BCA.js";
10
9
  export {
11
10
  findMostSimilarMemento,
12
11
  getRelevantMementos
@@ -2,8 +2,7 @@
2
2
  import {
3
3
  NotificationDeduplicator,
4
4
  notificationDeduplicator
5
- } from "./chunk-AMDXHL6S.js";
6
- import "./chunk-PDX44BCA.js";
5
+ } from "./chunk-OCYRD7D6.js";
7
6
  export {
8
7
  NotificationDeduplicator,
9
8
  notificationDeduplicator
@@ -120,7 +120,7 @@ import {
120
120
  validateMermaidSyntax,
121
121
  warmUpSettingsCache,
122
122
  withRetry
123
- } from "./chunk-SQBLLN7K.js";
123
+ } from "./chunk-M4EAFD3H.js";
124
124
  import {
125
125
  Logger,
126
126
  NotificationDeduplicator,
@@ -128,9 +128,8 @@ import {
128
128
  notifyEventLogsToSlack,
129
129
  postLowCreditsNotificationToSlack,
130
130
  postMessageToSlack
131
- } from "./chunk-AMDXHL6S.js";
131
+ } from "./chunk-OCYRD7D6.js";
132
132
  import "./chunk-WCYNJOOX.js";
133
- import "./chunk-PDX44BCA.js";
134
133
  export {
135
134
  AWSBackend,
136
135
  AdminSettingsCache,
@@ -294,7 +294,6 @@ import {
294
294
  validateReactArtifactV2,
295
295
  validateSvgArtifactV2
296
296
  } from "./chunk-WCYNJOOX.js";
297
- import "./chunk-PDX44BCA.js";
298
297
  export {
299
298
  ALL_IMAGE_MODELS,
300
299
  ALL_IMAGE_SIZES,
@@ -2,11 +2,10 @@
2
2
  import {
3
3
  SubtractCreditsSchema,
4
4
  subtractCredits
5
- } from "./chunk-RBZRTCAY.js";
6
- import "./chunk-SQBLLN7K.js";
7
- import "./chunk-AMDXHL6S.js";
5
+ } from "./chunk-WENSCUXW.js";
6
+ import "./chunk-M4EAFD3H.js";
7
+ import "./chunk-OCYRD7D6.js";
8
8
  import "./chunk-WCYNJOOX.js";
9
- import "./chunk-PDX44BCA.js";
10
9
  export {
11
10
  SubtractCreditsSchema,
12
11
  subtractCredits
@@ -14,7 +14,6 @@ import {
14
14
  verifyBackupCode,
15
15
  verifyTOTPToken
16
16
  } from "./chunk-BDQBOLYG.js";
17
- import "./chunk-PDX44BCA.js";
18
17
  export {
19
18
  clearFailedAttempts,
20
19
  generateBackupCodes,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bike4mind/cli",
3
- "version": "0.2.11-cli-cancel-pending-message.17326+24dfb0510",
3
+ "version": "0.2.11-cli-todo-list.17329+bc504ca29",
4
4
  "type": "module",
5
5
  "description": "Interactive CLI tool for Bike4Mind with ReAct agents",
6
6
  "license": "UNLICENSED",
@@ -70,6 +70,7 @@
70
70
  "eventsource-parser": "^3.0.6",
71
71
  "file-type": "^18.7.0",
72
72
  "fuse.js": "^7.1.0",
73
+ "glob": "^13.0.0",
73
74
  "gray-matter": "^4.0.3",
74
75
  "ink": "^6.5.1",
75
76
  "ink-select-input": "^6.2.0",
@@ -104,10 +105,10 @@
104
105
  },
105
106
  "devDependencies": {
106
107
  "@bike4mind/agents": "0.1.0",
107
- "@bike4mind/common": "2.40.1-cli-cancel-pending-message.17326+24dfb0510",
108
- "@bike4mind/mcp": "1.20.5-cli-cancel-pending-message.17326+24dfb0510",
109
- "@bike4mind/services": "2.35.1-cli-cancel-pending-message.17326+24dfb0510",
110
- "@bike4mind/utils": "2.1.5-cli-cancel-pending-message.17326+24dfb0510",
108
+ "@bike4mind/common": "2.40.1-cli-todo-list.17329+bc504ca29",
109
+ "@bike4mind/mcp": "1.20.5-cli-todo-list.17329+bc504ca29",
110
+ "@bike4mind/services": "2.35.1-cli-todo-list.17329+bc504ca29",
111
+ "@bike4mind/utils": "2.1.5-cli-todo-list.17329+bc504ca29",
111
112
  "@types/better-sqlite3": "^7.6.13",
112
113
  "@types/diff": "^5.0.9",
113
114
  "@types/jsonwebtoken": "^9.0.4",
@@ -120,5 +121,5 @@
120
121
  "typescript": "^5.9.3",
121
122
  "vitest": "^3.2.4"
122
123
  },
123
- "gitHead": "24dfb0510b2c3e1385417da0a3fe6c3604cdf8f8"
124
+ "gitHead": "bc504ca299975846c55f1c16f05f4e7535ce2fda"
124
125
  }
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env node
2
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
- }) : x)(function(x) {
5
- if (typeof require !== "undefined") return require.apply(this, arguments);
6
- throw Error('Dynamic require of "' + x + '" is not supported');
7
- });
8
-
9
- export {
10
- __require
11
- };