@bike4mind/cli 0.2.12-refactor-github-mcp.17405 → 0.2.13
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/{ImageStore-RQZ7OF7P.js → ImageStore-MMUOUPI2.js} +0 -1
- package/dist/{artifactExtractor-F2VLARCK.js → artifactExtractor-BRHUIR7I.js} +1 -2
- package/dist/{chunk-ZHCX7BLB.js → chunk-3MT5HI3G.js} +2 -2
- package/dist/{chunk-OHR7UCTC.js → chunk-A7POWM75.js} +13 -0
- package/dist/chunk-BDQBOLYG.js +0 -0
- package/dist/chunk-BPFEGDC7.js +0 -0
- package/dist/{chunk-RJFNIMVH.js → chunk-K44CDTQN.js} +2 -2
- package/dist/{chunk-AMDXHL6S.js → chunk-OCYRD7D6.js} +1 -1
- package/dist/{chunk-ID4NS6KF.js → chunk-PMY7IPOC.js} +18 -2
- package/dist/{chunk-BU5TP3BH.js → chunk-SZXO7HOF.js} +19 -2
- package/dist/create-X2GOA457.js +12 -0
- package/dist/{formatConverter-I7EIUVDY.js → formatConverter-5QEJDW24.js} +0 -1
- package/dist/index.js +436 -280
- package/dist/{llmMarkdownGenerator-AVPOXVE5.js → llmMarkdownGenerator-AHTZFV2T.js} +1 -2
- package/dist/{markdownGenerator-FBXMTP3B.js → markdownGenerator-BLXDOZWQ.js} +1 -2
- package/dist/mementoService-ZAFUWL4U.js +12 -0
- package/dist/{notificationDeduplicator-UW7D75QS.js → notificationDeduplicator-UTHJHMSZ.js} +1 -2
- package/dist/{src-SYE7WXBJ.js → src-SIU2KHTA.js} +3 -2
- package/dist/{src-YU4VN5YG.js → src-STUEUNUB.js} +3 -4
- package/dist/subtractCredits-KRFWXMZH.js +12 -0
- package/dist/{utils-JPMDGUBL.js → utils-JCHWDM4Z.js} +0 -1
- package/package.json +19 -19
- package/dist/chunk-PDX44BCA.js +0 -11
- package/dist/create-MXBWYABO.js +0 -13
- package/dist/mementoService-4WLVEWPZ.js +0 -13
- package/dist/subtractCredits-FGISXF2D.js +0 -13
package/dist/index.js
CHANGED
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
getEffectiveApiKey,
|
|
5
5
|
getOpenWeatherKey,
|
|
6
6
|
getSerperKey
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-3MT5HI3G.js";
|
|
8
|
+
import "./chunk-PMY7IPOC.js";
|
|
9
|
+
import "./chunk-K44CDTQN.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-
|
|
18
|
+
} from "./chunk-SZXO7HOF.js";
|
|
19
19
|
import {
|
|
20
20
|
Logger
|
|
21
|
-
} from "./chunk-
|
|
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-
|
|
77
|
-
import {
|
|
78
|
-
__require
|
|
79
|
-
} from "./chunk-PDX44BCA.js";
|
|
76
|
+
} from "./chunk-A7POWM75.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
|
|
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 '
|
|
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
|
-
|
|
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
|
|
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 '
|
|
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
|
|
3443
|
-
- "what packages installed?" \u2192
|
|
3444
|
-
- "find all components using React hooks" \u2192
|
|
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
|
}
|
|
@@ -8797,98 +8807,80 @@ var createFileTool = {
|
|
|
8797
8807
|
};
|
|
8798
8808
|
|
|
8799
8809
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/globFiles/index.js
|
|
8800
|
-
import {
|
|
8801
|
-
import {
|
|
8810
|
+
import { glob } from "glob";
|
|
8811
|
+
import { stat } from "fs/promises";
|
|
8802
8812
|
import path9 from "path";
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
8811
|
-
|
|
8812
|
-
|
|
8813
|
-
|
|
8814
|
-
|
|
8815
|
-
|
|
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;
|
|
8813
|
+
var DEFAULT_IGNORE_PATTERNS2 = [
|
|
8814
|
+
"**/node_modules/**",
|
|
8815
|
+
"**/.git/**",
|
|
8816
|
+
"**/dist/**",
|
|
8817
|
+
"**/build/**",
|
|
8818
|
+
"**/.next/**",
|
|
8819
|
+
"**/coverage/**",
|
|
8820
|
+
"**/.turbo/**",
|
|
8821
|
+
"**/.sst/**",
|
|
8822
|
+
"**/*.min.js",
|
|
8823
|
+
"**/*.min.css"
|
|
8824
|
+
];
|
|
8825
|
+
async function findFiles(params) {
|
|
8826
|
+
const { pattern, dir_path, case_sensitive = true, respect_git_ignore = true } = params;
|
|
8836
8827
|
const baseCwd = process.cwd();
|
|
8837
|
-
const
|
|
8838
|
-
if (!
|
|
8839
|
-
throw new Error(`Access denied: Cannot
|
|
8840
|
-
}
|
|
8841
|
-
const
|
|
8828
|
+
const targetDir = dir_path ? path9.resolve(baseCwd, path9.normalize(dir_path)) : baseCwd;
|
|
8829
|
+
if (!targetDir.startsWith(baseCwd)) {
|
|
8830
|
+
throw new Error(`Access denied: Cannot search outside of current working directory`);
|
|
8831
|
+
}
|
|
8832
|
+
const ignorePatterns = respect_git_ignore ? DEFAULT_IGNORE_PATTERNS2 : [];
|
|
8833
|
+
const matches = await glob(pattern, {
|
|
8834
|
+
cwd: targetDir,
|
|
8835
|
+
dot: false,
|
|
8836
|
+
// Don't match hidden files by default
|
|
8837
|
+
ignore: ignorePatterns,
|
|
8838
|
+
absolute: true,
|
|
8839
|
+
nodir: true,
|
|
8840
|
+
// Only return files, not directories
|
|
8841
|
+
nocase: !case_sensitive,
|
|
8842
|
+
// Case-insensitive if requested
|
|
8843
|
+
maxDepth: 20
|
|
8844
|
+
// Reasonable depth limit
|
|
8845
|
+
});
|
|
8842
8846
|
if (matches.length === 0) {
|
|
8843
|
-
return `No files found matching pattern: ${pattern}`;
|
|
8847
|
+
return `No files found matching pattern: ${pattern}${dir_path ? ` in ${dir_path}` : ""}`;
|
|
8844
8848
|
}
|
|
8845
|
-
const
|
|
8846
|
-
for (const
|
|
8849
|
+
const filesWithStats = [];
|
|
8850
|
+
for (const filePath of matches) {
|
|
8847
8851
|
try {
|
|
8848
|
-
const
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
size: stats.size,
|
|
8853
|
-
isDirectory: stats.isDirectory(),
|
|
8854
|
-
modified: stats.mtime.toISOString()
|
|
8852
|
+
const stats = await stat(filePath);
|
|
8853
|
+
filesWithStats.push({
|
|
8854
|
+
path: filePath,
|
|
8855
|
+
mtime: stats.mtimeMs
|
|
8855
8856
|
});
|
|
8856
|
-
} catch
|
|
8857
|
+
} catch {
|
|
8857
8858
|
continue;
|
|
8858
8859
|
}
|
|
8859
8860
|
}
|
|
8860
|
-
|
|
8861
|
-
const
|
|
8862
|
-
|
|
8863
|
-
const
|
|
8864
|
-
` : ""
|
|
8865
|
-
const
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
return `${summary}${cwdInfo}
|
|
8861
|
+
filesWithStats.sort((a, b) => b.mtime - a.mtime);
|
|
8862
|
+
const MAX_RESULTS = 500;
|
|
8863
|
+
const truncated = filesWithStats.length > MAX_RESULTS;
|
|
8864
|
+
const results = truncated ? filesWithStats.slice(0, MAX_RESULTS) : filesWithStats;
|
|
8865
|
+
const summary = `Found ${filesWithStats.length} file(s)${truncated ? ` (showing first ${MAX_RESULTS})` : ""} matching: ${pattern}`;
|
|
8866
|
+
const dirInfo = dir_path ? `
|
|
8867
|
+
Directory: ${dir_path}` : "";
|
|
8868
|
+
const filesList = results.map((file) => path9.relative(baseCwd, file.path)).join("\n");
|
|
8869
|
+
return `${summary}${dirInfo}
|
|
8870
|
+
|
|
8871
8871
|
${filesList}`;
|
|
8872
8872
|
}
|
|
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
8873
|
var globFilesTool = {
|
|
8882
8874
|
name: "glob_files",
|
|
8883
8875
|
implementation: (context) => ({
|
|
8884
8876
|
toolFn: async (value) => {
|
|
8885
8877
|
const params = value;
|
|
8886
|
-
context.logger.info("\u{1F50D} GlobFiles:
|
|
8878
|
+
context.logger.info("\u{1F50D} GlobFiles: Finding files", {
|
|
8887
8879
|
pattern: params.pattern,
|
|
8888
|
-
|
|
8880
|
+
dir_path: params.dir_path || "."
|
|
8889
8881
|
});
|
|
8890
8882
|
try {
|
|
8891
|
-
const result = await
|
|
8883
|
+
const result = await findFiles(params);
|
|
8892
8884
|
context.logger.info("\u2705 GlobFiles: Success");
|
|
8893
8885
|
return result;
|
|
8894
8886
|
} catch (error) {
|
|
@@ -8898,25 +8890,25 @@ var globFilesTool = {
|
|
|
8898
8890
|
},
|
|
8899
8891
|
toolSchema: {
|
|
8900
8892
|
name: "glob_files",
|
|
8901
|
-
description: "
|
|
8893
|
+
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
8894
|
parameters: {
|
|
8903
8895
|
type: "object",
|
|
8904
8896
|
properties: {
|
|
8905
8897
|
pattern: {
|
|
8906
8898
|
type: "string",
|
|
8907
|
-
description: '
|
|
8899
|
+
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
8900
|
},
|
|
8909
|
-
|
|
8901
|
+
dir_path: {
|
|
8910
8902
|
type: "string",
|
|
8911
|
-
description: "
|
|
8903
|
+
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
8904
|
},
|
|
8913
|
-
|
|
8905
|
+
case_sensitive: {
|
|
8914
8906
|
type: "boolean",
|
|
8915
|
-
description: "
|
|
8907
|
+
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
8908
|
},
|
|
8917
|
-
|
|
8918
|
-
type: "
|
|
8919
|
-
description: "
|
|
8909
|
+
respect_git_ignore: {
|
|
8910
|
+
type: "boolean",
|
|
8911
|
+
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
8912
|
}
|
|
8921
8913
|
},
|
|
8922
8914
|
required: ["pattern"]
|
|
@@ -8926,158 +8918,144 @@ var globFilesTool = {
|
|
|
8926
8918
|
};
|
|
8927
8919
|
|
|
8928
8920
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/grepSearch/index.js
|
|
8929
|
-
import {
|
|
8930
|
-
import {
|
|
8921
|
+
import { globStream } from "glob";
|
|
8922
|
+
import { readFile, stat as stat2 } from "fs/promises";
|
|
8931
8923
|
import path10 from "path";
|
|
8932
8924
|
var MAX_FILE_SIZE3 = 10 * 1024 * 1024;
|
|
8933
|
-
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
|
|
8925
|
+
var DEFAULT_IGNORE_PATTERNS3 = [
|
|
8926
|
+
"**/node_modules/**",
|
|
8927
|
+
"**/.git/**",
|
|
8928
|
+
"**/dist/**",
|
|
8929
|
+
"**/build/**",
|
|
8930
|
+
"**/.next/**",
|
|
8931
|
+
"**/coverage/**",
|
|
8932
|
+
"**/*.min.js",
|
|
8933
|
+
"**/*.min.css",
|
|
8934
|
+
"**/package-lock.json",
|
|
8935
|
+
"**/pnpm-lock.yaml",
|
|
8936
|
+
"**/yarn.lock"
|
|
8937
|
+
];
|
|
8938
|
+
async function isBinaryFile2(filePath) {
|
|
8940
8939
|
try {
|
|
8941
|
-
const
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
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) {
|
|
8940
|
+
const { openSync, readSync, closeSync } = await import("fs");
|
|
8941
|
+
const buffer = Buffer.alloc(8192);
|
|
8942
|
+
const fd = openSync(filePath, "r");
|
|
8943
|
+
const bytesRead = readSync(fd, buffer, 0, 8192, 0);
|
|
8944
|
+
closeSync(fd);
|
|
8945
|
+
const chunk = buffer.slice(0, bytesRead);
|
|
8946
|
+
return chunk.includes(0);
|
|
8947
|
+
} catch {
|
|
8948
|
+
return true;
|
|
8960
8949
|
}
|
|
8961
|
-
|
|
8950
|
+
}
|
|
8951
|
+
function isPathWithinWorkspace(targetPath, baseCwd) {
|
|
8952
|
+
const resolvedTarget = path10.resolve(targetPath);
|
|
8953
|
+
const resolvedBase = path10.resolve(baseCwd);
|
|
8954
|
+
return resolvedTarget.startsWith(resolvedBase);
|
|
8962
8955
|
}
|
|
8963
8956
|
async function searchFiles2(params) {
|
|
8964
|
-
const { pattern,
|
|
8957
|
+
const { pattern, dir_path, include } = params;
|
|
8965
8958
|
const baseCwd = process.cwd();
|
|
8966
|
-
const
|
|
8967
|
-
if (!
|
|
8968
|
-
throw new Error(`
|
|
8959
|
+
const targetDir = dir_path ? path10.resolve(baseCwd, dir_path) : baseCwd;
|
|
8960
|
+
if (!isPathWithinWorkspace(targetDir, baseCwd)) {
|
|
8961
|
+
throw new Error(`Path validation failed: "${dir_path}" resolves outside the allowed workspace directory`);
|
|
8962
|
+
}
|
|
8963
|
+
try {
|
|
8964
|
+
const stats = await stat2(targetDir);
|
|
8965
|
+
if (!stats.isDirectory()) {
|
|
8966
|
+
throw new Error(`Path is not a directory: ${dir_path}`);
|
|
8967
|
+
}
|
|
8968
|
+
} catch (error) {
|
|
8969
|
+
if (error.code === "ENOENT") {
|
|
8970
|
+
throw new Error(`Path does not exist: ${dir_path}`);
|
|
8971
|
+
}
|
|
8972
|
+
throw error;
|
|
8969
8973
|
}
|
|
8970
8974
|
let searchRegex;
|
|
8971
8975
|
try {
|
|
8972
|
-
|
|
8973
|
-
searchRegex = new RegExp(pattern, flags);
|
|
8976
|
+
searchRegex = new RegExp(pattern, "i");
|
|
8974
8977
|
} catch (error) {
|
|
8975
8978
|
throw new Error(`Invalid regex pattern: ${pattern}`);
|
|
8976
8979
|
}
|
|
8977
|
-
const
|
|
8978
|
-
const
|
|
8979
|
-
|
|
8980
|
-
|
|
8980
|
+
const globPattern = include || "**/*";
|
|
8981
|
+
const filesStream = globStream(globPattern, {
|
|
8982
|
+
cwd: targetDir,
|
|
8983
|
+
dot: false,
|
|
8984
|
+
// Skip hidden files
|
|
8985
|
+
ignore: DEFAULT_IGNORE_PATTERNS3,
|
|
8986
|
+
absolute: true,
|
|
8987
|
+
nodir: true
|
|
8988
|
+
// Only match files, not directories
|
|
8989
|
+
});
|
|
8990
|
+
const allMatches = [];
|
|
8991
|
+
let filesSearched = 0;
|
|
8992
|
+
const maxMatches = 500;
|
|
8993
|
+
for await (const filePath of filesStream) {
|
|
8994
|
+
if (allMatches.length >= maxMatches) {
|
|
8995
|
+
break;
|
|
8996
|
+
}
|
|
8997
|
+
const fileAbsPath = filePath;
|
|
8981
8998
|
try {
|
|
8982
|
-
const stats =
|
|
8983
|
-
if (stats.
|
|
8999
|
+
const stats = await stat2(fileAbsPath);
|
|
9000
|
+
if (stats.size > MAX_FILE_SIZE3) {
|
|
8984
9001
|
continue;
|
|
8985
9002
|
}
|
|
8986
|
-
if (isBinaryFile2(
|
|
9003
|
+
if (await isBinaryFile2(fileAbsPath)) {
|
|
8987
9004
|
continue;
|
|
8988
9005
|
}
|
|
8989
|
-
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
|
|
8993
|
-
|
|
8994
|
-
|
|
8995
|
-
|
|
8996
|
-
|
|
8997
|
-
|
|
8998
|
-
|
|
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);
|
|
9006
|
+
filesSearched++;
|
|
9007
|
+
const content = await readFile(fileAbsPath, "utf8");
|
|
9008
|
+
const lines = content.split(/\r?\n/);
|
|
9009
|
+
lines.forEach((line, index) => {
|
|
9010
|
+
if (allMatches.length < maxMatches && searchRegex.test(line)) {
|
|
9011
|
+
allMatches.push({
|
|
9012
|
+
filePath: path10.relative(targetDir, fileAbsPath) || path10.basename(fileAbsPath),
|
|
9013
|
+
lineNumber: index + 1,
|
|
9014
|
+
line
|
|
9015
|
+
});
|
|
9032
9016
|
}
|
|
9033
|
-
|
|
9017
|
+
});
|
|
9018
|
+
} catch (readError) {
|
|
9019
|
+
const errCode = readError.code;
|
|
9020
|
+
if (errCode !== "ENOENT" && errCode !== "EACCES") {
|
|
9021
|
+
console.debug(`Could not read ${fileAbsPath}: ${readError.message}`);
|
|
9034
9022
|
}
|
|
9035
|
-
} catch {
|
|
9036
|
-
continue;
|
|
9037
9023
|
}
|
|
9038
9024
|
}
|
|
9039
|
-
|
|
9040
|
-
|
|
9041
|
-
|
|
9025
|
+
const searchDirDisplay = dir_path || ".";
|
|
9026
|
+
const filterInfo = include ? ` (filter: "${include}")` : "";
|
|
9027
|
+
if (allMatches.length === 0) {
|
|
9028
|
+
return `No matches found for pattern "${pattern}" in "${searchDirDisplay}"${filterInfo}.
|
|
9029
|
+
Searched ${filesSearched} file(s).`;
|
|
9042
9030
|
}
|
|
9043
|
-
const
|
|
9044
|
-
|
|
9031
|
+
const matchesByFile = allMatches.reduce((acc, match) => {
|
|
9032
|
+
if (!acc[match.filePath]) {
|
|
9033
|
+
acc[match.filePath] = [];
|
|
9034
|
+
}
|
|
9035
|
+
acc[match.filePath].push(match);
|
|
9036
|
+
acc[match.filePath].sort((a, b) => a.lineNumber - b.lineNumber);
|
|
9037
|
+
return acc;
|
|
9038
|
+
}, {});
|
|
9039
|
+
const matchCount = allMatches.length;
|
|
9040
|
+
const matchTerm = matchCount === 1 ? "match" : "matches";
|
|
9041
|
+
const truncated = matchCount >= maxMatches;
|
|
9042
|
+
let result = `Found ${matchCount} ${matchTerm}${truncated ? " (truncated)" : ""} for pattern "${pattern}" in "${searchDirDisplay}"${filterInfo}:
|
|
9045
9043
|
`;
|
|
9046
|
-
|
|
9044
|
+
result += `Searched ${filesSearched} file(s)
|
|
9045
|
+
---
|
|
9047
9046
|
`;
|
|
9048
|
-
const
|
|
9049
|
-
`
|
|
9050
|
-
|
|
9051
|
-
|
|
9052
|
-
|
|
9053
|
-
|
|
9054
|
-
|
|
9055
|
-
|
|
9056
|
-
|
|
9057
|
-
|
|
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;
|
|
9047
|
+
for (const filePath in matchesByFile) {
|
|
9048
|
+
result += `File: ${filePath}
|
|
9049
|
+
`;
|
|
9050
|
+
matchesByFile[filePath].forEach((match) => {
|
|
9051
|
+
const trimmedLine = match.line.trim();
|
|
9052
|
+
const displayLine = trimmedLine.length > 200 ? trimmedLine.slice(0, 200) + "..." : trimmedLine;
|
|
9053
|
+
result += `L${match.lineNumber}: ${displayLine}
|
|
9054
|
+
`;
|
|
9055
|
+
});
|
|
9056
|
+
result += "---\n";
|
|
9080
9057
|
}
|
|
9058
|
+
return result.trim();
|
|
9081
9059
|
}
|
|
9082
9060
|
var grepSearchTool = {
|
|
9083
9061
|
name: "grep_search",
|
|
@@ -9086,8 +9064,8 @@ var grepSearchTool = {
|
|
|
9086
9064
|
const params = value;
|
|
9087
9065
|
context.logger.info("\u{1F50D} GrepSearch: Searching files", {
|
|
9088
9066
|
pattern: params.pattern,
|
|
9089
|
-
|
|
9090
|
-
|
|
9067
|
+
dir_path: params.dir_path || ".",
|
|
9068
|
+
include: params.include || "**/*"
|
|
9091
9069
|
});
|
|
9092
9070
|
try {
|
|
9093
9071
|
const result = await searchFiles2(params);
|
|
@@ -9100,33 +9078,21 @@ var grepSearchTool = {
|
|
|
9100
9078
|
},
|
|
9101
9079
|
toolSchema: {
|
|
9102
9080
|
name: "grep_search",
|
|
9103
|
-
description: "
|
|
9081
|
+
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
9082
|
parameters: {
|
|
9105
9083
|
type: "object",
|
|
9106
9084
|
properties: {
|
|
9107
9085
|
pattern: {
|
|
9108
9086
|
type: "string",
|
|
9109
|
-
description: "
|
|
9087
|
+
description: "The regular expression (regex) pattern to search for within file contents (e.g., 'function\\s+myFunction', 'import\\s+\\{.*\\}\\s+from\\s+.*')."
|
|
9110
9088
|
},
|
|
9111
|
-
|
|
9089
|
+
dir_path: {
|
|
9112
9090
|
type: "string",
|
|
9113
|
-
description:
|
|
9091
|
+
description: "Optional: The absolute path to the directory to search within. If omitted, searches the current working directory."
|
|
9114
9092
|
},
|
|
9115
|
-
|
|
9093
|
+
include: {
|
|
9116
9094
|
type: "string",
|
|
9117
|
-
description: "
|
|
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."
|
|
9095
|
+
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
9096
|
}
|
|
9131
9097
|
},
|
|
9132
9098
|
required: ["pattern"]
|
|
@@ -9137,7 +9103,7 @@ var grepSearchTool = {
|
|
|
9137
9103
|
|
|
9138
9104
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/deleteFile/index.js
|
|
9139
9105
|
import { promises as fs10 } from "fs";
|
|
9140
|
-
import { existsSync as existsSync6, statSync as
|
|
9106
|
+
import { existsSync as existsSync6, statSync as statSync5 } from "fs";
|
|
9141
9107
|
import path11 from "path";
|
|
9142
9108
|
async function deleteFile(params) {
|
|
9143
9109
|
const { path: filePath, recursive = false } = params;
|
|
@@ -9150,7 +9116,7 @@ async function deleteFile(params) {
|
|
|
9150
9116
|
if (!existsSync6(resolvedPath)) {
|
|
9151
9117
|
throw new Error(`File or directory not found: ${filePath}`);
|
|
9152
9118
|
}
|
|
9153
|
-
const stats =
|
|
9119
|
+
const stats = statSync5(resolvedPath);
|
|
9154
9120
|
const isDirectory = stats.isDirectory();
|
|
9155
9121
|
const size = stats.size;
|
|
9156
9122
|
if (isDirectory && !recursive) {
|
|
@@ -9171,7 +9137,7 @@ var deleteFileTool = {
|
|
|
9171
9137
|
toolFn: async (value) => {
|
|
9172
9138
|
const params = value;
|
|
9173
9139
|
const resolvedPath = path11.resolve(process.cwd(), path11.normalize(params.path));
|
|
9174
|
-
const isDirectory = existsSync6(resolvedPath) &&
|
|
9140
|
+
const isDirectory = existsSync6(resolvedPath) && statSync5(resolvedPath).isDirectory();
|
|
9175
9141
|
context.logger.info(`\u{1F5D1}\uFE0F DeleteFile: Deleting ${isDirectory ? "directory" : "file"}`, {
|
|
9176
9142
|
path: params.path,
|
|
9177
9143
|
recursive: params.recursive
|
|
@@ -9947,7 +9913,7 @@ var ToolErrorType;
|
|
|
9947
9913
|
|
|
9948
9914
|
// src/utils/diffPreview.ts
|
|
9949
9915
|
import * as Diff from "diff";
|
|
9950
|
-
import { readFile } from "fs/promises";
|
|
9916
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
9951
9917
|
import { existsSync as existsSync8 } from "fs";
|
|
9952
9918
|
async function generateFileDiffPreview(args) {
|
|
9953
9919
|
try {
|
|
@@ -9962,7 +9928,7 @@ ${preview}${hasMore ? `
|
|
|
9962
9928
|
|
|
9963
9929
|
... (${lines2.length - 20} more lines)` : ""}`;
|
|
9964
9930
|
}
|
|
9965
|
-
const currentContent = await
|
|
9931
|
+
const currentContent = await readFile2(args.path, "utf-8");
|
|
9966
9932
|
const patch = Diff.createPatch(
|
|
9967
9933
|
args.path,
|
|
9968
9934
|
currentContent,
|
|
@@ -10871,6 +10837,82 @@ async function loadContextFiles(projectDir) {
|
|
|
10871
10837
|
};
|
|
10872
10838
|
}
|
|
10873
10839
|
|
|
10840
|
+
// src/utils/formatStep.ts
|
|
10841
|
+
var MAX_INPUT_LENGTH = 500;
|
|
10842
|
+
var formatStep = (step) => {
|
|
10843
|
+
if (step.type === "action" && step.metadata?.toolInput) {
|
|
10844
|
+
let parsedInput = step.metadata.toolInput;
|
|
10845
|
+
if (typeof parsedInput === "string") {
|
|
10846
|
+
try {
|
|
10847
|
+
parsedInput = JSON.parse(parsedInput);
|
|
10848
|
+
} catch {
|
|
10849
|
+
}
|
|
10850
|
+
}
|
|
10851
|
+
if (step.metadata.toolName === "edit_local_file") {
|
|
10852
|
+
const pathOnly = typeof parsedInput === "object" && parsedInput !== null && "path" in parsedInput ? { path: parsedInput.path } : parsedInput;
|
|
10853
|
+
return {
|
|
10854
|
+
...step,
|
|
10855
|
+
metadata: {
|
|
10856
|
+
...step.metadata,
|
|
10857
|
+
toolInput: pathOnly
|
|
10858
|
+
}
|
|
10859
|
+
};
|
|
10860
|
+
}
|
|
10861
|
+
if (step.metadata.toolName === "write_todos") {
|
|
10862
|
+
const todos = typeof parsedInput === "object" && parsedInput !== null && "todos" in parsedInput ? parsedInput.todos : null;
|
|
10863
|
+
const count = Array.isArray(todos) ? todos.length : 0;
|
|
10864
|
+
return {
|
|
10865
|
+
...step,
|
|
10866
|
+
metadata: {
|
|
10867
|
+
...step.metadata,
|
|
10868
|
+
toolInput: `${count} todo${count !== 1 ? "s" : ""}`
|
|
10869
|
+
}
|
|
10870
|
+
};
|
|
10871
|
+
}
|
|
10872
|
+
const inputStr = typeof parsedInput === "string" ? parsedInput : JSON.stringify(parsedInput);
|
|
10873
|
+
if (inputStr.length > MAX_INPUT_LENGTH) {
|
|
10874
|
+
const truncatedInput = inputStr.slice(0, MAX_INPUT_LENGTH) + `... (${inputStr.length - MAX_INPUT_LENGTH} more chars)`;
|
|
10875
|
+
return {
|
|
10876
|
+
...step,
|
|
10877
|
+
metadata: {
|
|
10878
|
+
...step.metadata,
|
|
10879
|
+
toolInput: truncatedInput
|
|
10880
|
+
}
|
|
10881
|
+
};
|
|
10882
|
+
}
|
|
10883
|
+
}
|
|
10884
|
+
if (step.type === "observation" && step.content) {
|
|
10885
|
+
if (step.metadata?.toolName === "file_read") {
|
|
10886
|
+
const lineCount = step.content.split("\n").length;
|
|
10887
|
+
return {
|
|
10888
|
+
...step,
|
|
10889
|
+
content: `Read ${lineCount} line${lineCount !== 1 ? "s" : ""}`
|
|
10890
|
+
};
|
|
10891
|
+
}
|
|
10892
|
+
if (step.metadata?.toolName === "grep_search") {
|
|
10893
|
+
const match = step.content.match(/^Found (\d+) file\(s\)/);
|
|
10894
|
+
if (match) {
|
|
10895
|
+
const count = parseInt(match[1], 10);
|
|
10896
|
+
return {
|
|
10897
|
+
...step,
|
|
10898
|
+
content: `Found ${count} file${count !== 1 ? "s" : ""}`
|
|
10899
|
+
};
|
|
10900
|
+
}
|
|
10901
|
+
}
|
|
10902
|
+
if (step.metadata?.toolName === "glob_files") {
|
|
10903
|
+
const match = step.content.match(/^Found (\d+) file\(s\)/);
|
|
10904
|
+
if (match) {
|
|
10905
|
+
const count = parseInt(match[1], 10);
|
|
10906
|
+
return {
|
|
10907
|
+
...step,
|
|
10908
|
+
content: `Found ${count} file${count !== 1 ? "s" : ""}`
|
|
10909
|
+
};
|
|
10910
|
+
}
|
|
10911
|
+
}
|
|
10912
|
+
}
|
|
10913
|
+
return step;
|
|
10914
|
+
};
|
|
10915
|
+
|
|
10874
10916
|
// src/utils/argumentSubstitution.ts
|
|
10875
10917
|
function substituteArguments(template, args) {
|
|
10876
10918
|
let result = template;
|
|
@@ -11786,7 +11828,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
11786
11828
|
// package.json
|
|
11787
11829
|
var package_default = {
|
|
11788
11830
|
name: "@bike4mind/cli",
|
|
11789
|
-
version: "0.2.
|
|
11831
|
+
version: "0.2.13",
|
|
11790
11832
|
type: "module",
|
|
11791
11833
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
11792
11834
|
license: "UNLICENSED",
|
|
@@ -11857,6 +11899,7 @@ var package_default = {
|
|
|
11857
11899
|
"eventsource-parser": "^3.0.6",
|
|
11858
11900
|
"file-type": "^18.7.0",
|
|
11859
11901
|
"fuse.js": "^7.1.0",
|
|
11902
|
+
glob: "^13.0.0",
|
|
11860
11903
|
"gray-matter": "^4.0.3",
|
|
11861
11904
|
ink: "^6.5.1",
|
|
11862
11905
|
"ink-select-input": "^6.2.0",
|
|
@@ -11890,11 +11933,11 @@ var package_default = {
|
|
|
11890
11933
|
zustand: "^4.5.4"
|
|
11891
11934
|
},
|
|
11892
11935
|
devDependencies: {
|
|
11893
|
-
"@bike4mind/agents": "
|
|
11894
|
-
"@bike4mind/common": "
|
|
11895
|
-
"@bike4mind/mcp": "
|
|
11896
|
-
"@bike4mind/services": "
|
|
11897
|
-
"@bike4mind/utils": "
|
|
11936
|
+
"@bike4mind/agents": "workspace:*",
|
|
11937
|
+
"@bike4mind/common": "workspace:*",
|
|
11938
|
+
"@bike4mind/mcp": "workspace:*",
|
|
11939
|
+
"@bike4mind/services": "workspace:*",
|
|
11940
|
+
"@bike4mind/utils": "workspace:*",
|
|
11898
11941
|
"@types/better-sqlite3": "^7.6.13",
|
|
11899
11942
|
"@types/diff": "^5.0.9",
|
|
11900
11943
|
"@types/jsonwebtoken": "^9.0.4",
|
|
@@ -11902,12 +11945,12 @@ var package_default = {
|
|
|
11902
11945
|
"@types/react": "^19.2.7",
|
|
11903
11946
|
"@types/uuid": "^9.0.7",
|
|
11904
11947
|
"@types/yargs": "^17.0.32",
|
|
11948
|
+
"ink-testing-library": "^4.0.0",
|
|
11905
11949
|
tsup: "^8.5.1",
|
|
11906
11950
|
tsx: "^4.21.0",
|
|
11907
11951
|
typescript: "^5.9.3",
|
|
11908
11952
|
vitest: "^3.2.4"
|
|
11909
|
-
}
|
|
11910
|
-
gitHead: "ae8801bbe418b5047a42104f8aa2bb56c198e4b5"
|
|
11953
|
+
}
|
|
11911
11954
|
};
|
|
11912
11955
|
|
|
11913
11956
|
// src/config/constants.ts
|
|
@@ -12263,6 +12306,131 @@ function createSubagentDelegateTool(orchestrator, parentSessionId) {
|
|
|
12263
12306
|
};
|
|
12264
12307
|
}
|
|
12265
12308
|
|
|
12309
|
+
// src/tools/writeTodosTool.ts
|
|
12310
|
+
var VALID_STATUSES = ["pending", "in_progress", "completed", "cancelled"];
|
|
12311
|
+
function validateTodos(todos) {
|
|
12312
|
+
if (!Array.isArray(todos)) {
|
|
12313
|
+
throw new Error("write_todos: todos must be an array");
|
|
12314
|
+
}
|
|
12315
|
+
if (todos.length === 0) {
|
|
12316
|
+
throw new Error("write_todos: todos array cannot be empty");
|
|
12317
|
+
}
|
|
12318
|
+
let inProgressCount = 0;
|
|
12319
|
+
const validatedTodos = todos.map((item, index) => {
|
|
12320
|
+
if (!item || typeof item !== "object") {
|
|
12321
|
+
throw new Error(`write_todos: item at index ${index} must be an object`);
|
|
12322
|
+
}
|
|
12323
|
+
const { description, status } = item;
|
|
12324
|
+
if (typeof description !== "string" || description.trim() === "") {
|
|
12325
|
+
throw new Error(`write_todos: item at index ${index} must have a non-empty description`);
|
|
12326
|
+
}
|
|
12327
|
+
if (typeof status !== "string" || !VALID_STATUSES.includes(status)) {
|
|
12328
|
+
throw new Error(
|
|
12329
|
+
`write_todos: item at index ${index} has invalid status "${status}". Must be one of: ${VALID_STATUSES.join(", ")}`
|
|
12330
|
+
);
|
|
12331
|
+
}
|
|
12332
|
+
if (status === "in_progress") {
|
|
12333
|
+
inProgressCount++;
|
|
12334
|
+
}
|
|
12335
|
+
return {
|
|
12336
|
+
description: description.trim(),
|
|
12337
|
+
status
|
|
12338
|
+
};
|
|
12339
|
+
});
|
|
12340
|
+
if (inProgressCount > 1) {
|
|
12341
|
+
throw new Error(`write_todos: only one task can be 'in_progress' at a time, but found ${inProgressCount}`);
|
|
12342
|
+
}
|
|
12343
|
+
return validatedTodos;
|
|
12344
|
+
}
|
|
12345
|
+
function formatTodosOutput(todos) {
|
|
12346
|
+
const statusEmoji = {
|
|
12347
|
+
pending: "\u2B1C",
|
|
12348
|
+
in_progress: "\u{1F504}",
|
|
12349
|
+
completed: "\u2705",
|
|
12350
|
+
cancelled: "\u274C"
|
|
12351
|
+
};
|
|
12352
|
+
const lines = todos.map((todo, index) => {
|
|
12353
|
+
const emoji = statusEmoji[todo.status];
|
|
12354
|
+
return `${index + 1}. ${emoji} [${todo.status}] ${todo.description}`;
|
|
12355
|
+
});
|
|
12356
|
+
return lines.join("\n");
|
|
12357
|
+
}
|
|
12358
|
+
function createWriteTodosTool(todoStore) {
|
|
12359
|
+
return {
|
|
12360
|
+
toolFn: async (args) => {
|
|
12361
|
+
const params = args;
|
|
12362
|
+
const validatedTodos = validateTodos(params.todos);
|
|
12363
|
+
todoStore.todos = validatedTodos;
|
|
12364
|
+
if (todoStore.onUpdate) {
|
|
12365
|
+
todoStore.onUpdate(validatedTodos);
|
|
12366
|
+
}
|
|
12367
|
+
const output = formatTodosOutput(validatedTodos);
|
|
12368
|
+
return `Todo list updated successfully:
|
|
12369
|
+
|
|
12370
|
+
${output}`;
|
|
12371
|
+
},
|
|
12372
|
+
toolSchema: {
|
|
12373
|
+
name: "write_todos",
|
|
12374
|
+
description: `Create or update a list of subtasks for complex multi-step requests.
|
|
12375
|
+
|
|
12376
|
+
**When to use this tool:**
|
|
12377
|
+
- When handling complex requests that require multiple steps
|
|
12378
|
+
- To break down a large task into smaller, manageable subtasks
|
|
12379
|
+
- To track progress through a multi-step workflow
|
|
12380
|
+
|
|
12381
|
+
**Important guidelines:**
|
|
12382
|
+
- Call this tool early when receiving complex requests
|
|
12383
|
+
- Update the list immediately when starting, completing, or cancelling tasks
|
|
12384
|
+
- Only ONE task should be 'in_progress' at a time
|
|
12385
|
+
- Never batch updates - update as soon as state changes
|
|
12386
|
+
|
|
12387
|
+
**Status values:**
|
|
12388
|
+
- pending: Task not yet started
|
|
12389
|
+
- in_progress: Currently working on this task (only ONE allowed)
|
|
12390
|
+
- completed: Task finished successfully
|
|
12391
|
+
- cancelled: Task will not be completed
|
|
12392
|
+
|
|
12393
|
+
**Example:**
|
|
12394
|
+
If asked to "create a new React component with tests", break it down:
|
|
12395
|
+
1. Create component file \u2192 in_progress
|
|
12396
|
+
2. Add component logic \u2192 pending
|
|
12397
|
+
3. Create test file \u2192 pending
|
|
12398
|
+
4. Write unit tests \u2192 pending`,
|
|
12399
|
+
parameters: {
|
|
12400
|
+
type: "object",
|
|
12401
|
+
properties: {
|
|
12402
|
+
todos: {
|
|
12403
|
+
type: "array",
|
|
12404
|
+
description: "Complete list of todo items. This replaces any existing list. Each item needs a description and status.",
|
|
12405
|
+
items: {
|
|
12406
|
+
type: "object",
|
|
12407
|
+
properties: {
|
|
12408
|
+
description: {
|
|
12409
|
+
type: "string",
|
|
12410
|
+
description: "Clear, concise description of the task"
|
|
12411
|
+
},
|
|
12412
|
+
status: {
|
|
12413
|
+
type: "string",
|
|
12414
|
+
enum: ["pending", "in_progress", "completed", "cancelled"],
|
|
12415
|
+
description: "Current status of the task. Only ONE task can be 'in_progress' at a time."
|
|
12416
|
+
}
|
|
12417
|
+
},
|
|
12418
|
+
required: ["description", "status"]
|
|
12419
|
+
}
|
|
12420
|
+
}
|
|
12421
|
+
},
|
|
12422
|
+
required: ["todos"]
|
|
12423
|
+
}
|
|
12424
|
+
}
|
|
12425
|
+
};
|
|
12426
|
+
}
|
|
12427
|
+
function createTodoStore(onUpdate) {
|
|
12428
|
+
return {
|
|
12429
|
+
todos: [],
|
|
12430
|
+
onUpdate
|
|
12431
|
+
};
|
|
12432
|
+
}
|
|
12433
|
+
|
|
12266
12434
|
// src/index.tsx
|
|
12267
12435
|
process.removeAllListeners("warning");
|
|
12268
12436
|
process.on("warning", (warning) => {
|
|
@@ -12547,7 +12715,9 @@ function CliApp() {
|
|
|
12547
12715
|
subagentConfigs
|
|
12548
12716
|
});
|
|
12549
12717
|
const subagentDelegateTool = createSubagentDelegateTool(orchestrator, newSession.id);
|
|
12550
|
-
const
|
|
12718
|
+
const todoStore = createTodoStore();
|
|
12719
|
+
const writeTodosTool = createWriteTodosTool(todoStore);
|
|
12720
|
+
const allTools = [...b4mTools, ...mcpTools, subagentDelegateTool, writeTodosTool];
|
|
12551
12721
|
console.log(`\u{1F916} Subagent delegation enabled (explore, plan, review)`);
|
|
12552
12722
|
const projectDir = state.configStore.getProjectConfigDir();
|
|
12553
12723
|
const contextResult = await loadContextFiles(projectDir);
|
|
@@ -12587,26 +12757,12 @@ ${contextResult.mergedContent}` : "";
|
|
|
12587
12757
|
const lastIdx = pendingMessages.length - 1;
|
|
12588
12758
|
if (lastIdx >= 0 && pendingMessages[lastIdx].role === "assistant") {
|
|
12589
12759
|
const existingSteps = pendingMessages[lastIdx].metadata?.steps || [];
|
|
12590
|
-
const
|
|
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
|
-
}
|
|
12760
|
+
const formattedStep = formatStep(step);
|
|
12605
12761
|
updatePendingMessage(lastIdx, {
|
|
12606
12762
|
...pendingMessages[lastIdx],
|
|
12607
12763
|
metadata: {
|
|
12608
12764
|
...pendingMessages[lastIdx].metadata,
|
|
12609
|
-
steps: [...existingSteps,
|
|
12765
|
+
steps: [...existingSteps, formattedStep]
|
|
12610
12766
|
}
|
|
12611
12767
|
});
|
|
12612
12768
|
}
|
|
@@ -12748,7 +12904,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
12748
12904
|
content: result.finalAnswer,
|
|
12749
12905
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12750
12906
|
metadata: {
|
|
12751
|
-
steps: result.steps,
|
|
12907
|
+
steps: result.steps.map(formatStep),
|
|
12752
12908
|
tokenUsage: {
|
|
12753
12909
|
prompt: 0,
|
|
12754
12910
|
completion: 0,
|
|
@@ -12921,7 +13077,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
12921
13077
|
completion: 0,
|
|
12922
13078
|
total: result.completionInfo.totalTokens
|
|
12923
13079
|
},
|
|
12924
|
-
steps: result.steps,
|
|
13080
|
+
steps: result.steps.map(formatStep),
|
|
12925
13081
|
// Complete history: thoughts, actions, observations
|
|
12926
13082
|
permissionDenied
|
|
12927
13083
|
}
|
|
@@ -13027,7 +13183,7 @@ ${output}` : output.trim() || "(no output)",
|
|
|
13027
13183
|
if (!imageStore) {
|
|
13028
13184
|
if (!imageStoreInitPromise.current) {
|
|
13029
13185
|
imageStoreInitPromise.current = (async () => {
|
|
13030
|
-
const { ImageStore: ImageStoreClass } = await import("./ImageStore-
|
|
13186
|
+
const { ImageStore: ImageStoreClass } = await import("./ImageStore-MMUOUPI2.js");
|
|
13031
13187
|
const newImageStore = new ImageStoreClass();
|
|
13032
13188
|
setState((prev) => ({
|
|
13033
13189
|
...prev,
|