@bike4mind/cli 0.2.12-ja-fix-slack-github-repo-matching.17428 → 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-SUX7CM3M.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-TSL6XWBP.js → chunk-K44CDTQN.js} +2 -2
- package/dist/{chunk-AMDXHL6S.js → chunk-OCYRD7D6.js} +1 -1
- package/dist/{chunk-KRVKVP5N.js → chunk-PMY7IPOC.js} +18 -2
- package/dist/{chunk-KKYUZIAR.js → chunk-SZXO7HOF.js} +2 -2
- package/dist/create-X2GOA457.js +12 -0
- package/dist/{formatConverter-I7EIUVDY.js → formatConverter-5QEJDW24.js} +0 -1
- package/dist/index.js +434 -279
- 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-X53U3ZW5.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-IQITRVUM.js +0 -13
- package/dist/mementoService-3C2I4CHP.js +0 -13
- package/dist/subtractCredits-6LARUXMI.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";
|
|
@@ -3395,6 +3393,15 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
|
|
|
3395
3393
|
};
|
|
3396
3394
|
|
|
3397
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";
|
|
3398
3405
|
function buildCoreSystemPrompt(contextSection = "") {
|
|
3399
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.
|
|
3400
3407
|
|
|
@@ -3406,15 +3413,17 @@ CORE BEHAVIOR:
|
|
|
3406
3413
|
|
|
3407
3414
|
FOR SOFTWARE ENGINEERING TASKS:
|
|
3408
3415
|
When requested to perform tasks like fixing bugs, adding features, refactoring, or explaining code, follow this sequence:
|
|
3409
|
-
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.
|
|
3410
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.
|
|
3411
|
-
|
|
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.
|
|
3412
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.
|
|
3413
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.
|
|
3414
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.
|
|
3415
3424
|
|
|
3416
3425
|
SUBAGENT DELEGATION:
|
|
3417
|
-
- You have access to specialized subagents via the
|
|
3426
|
+
- You have access to specialized subagents via the ${TOOL_SUBAGENT_DELEGATE} tool
|
|
3418
3427
|
- Use subagents for focused exploration, planning, or review tasks:
|
|
3419
3428
|
* explore: Fast read-only codebase search (e.g., "find all auth files", "locate API endpoints")
|
|
3420
3429
|
* plan: Break down complex tasks into actionable steps
|
|
@@ -3430,7 +3439,7 @@ FOR GENERAL TASKS:
|
|
|
3430
3439
|
## Shell tool output token efficiency:
|
|
3431
3440
|
IT IS CRITICAL TO FOLLOW THESE GUIDELINES TO AVOID EXCESSIVE TOKEN CONSUMPTION.
|
|
3432
3441
|
|
|
3433
|
-
- Always prefer command flags that reduce output verbosity when using '
|
|
3442
|
+
- Always prefer command flags that reduce output verbosity when using '${TOOL_BASH_EXECUTE}'.
|
|
3434
3443
|
- Aim to minimize tool output tokens while still capturing necessary information.
|
|
3435
3444
|
- If a command is expected to produce a lot of output, use quiet or silent flags where available and appropriate.
|
|
3436
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.
|
|
@@ -3440,9 +3449,9 @@ IT IS CRITICAL TO FOLLOW THESE GUIDELINES TO AVOID EXCESSIVE TOKEN CONSUMPTION.
|
|
|
3440
3449
|
EXAMPLES:
|
|
3441
3450
|
- "what should I wear in Texas?" \u2192 use weather tool for Texas
|
|
3442
3451
|
- "how about Japan?" \u2192 use weather tool for Japan (applying same question from context)
|
|
3443
|
-
- "enhance README" \u2192
|
|
3444
|
-
- "what packages installed?" \u2192
|
|
3445
|
-
- "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")
|
|
3446
3455
|
|
|
3447
3456
|
Remember: Use context from previous messages to understand follow-up questions.${contextSection}`;
|
|
3448
3457
|
}
|
|
@@ -8798,98 +8807,80 @@ var createFileTool = {
|
|
|
8798
8807
|
};
|
|
8799
8808
|
|
|
8800
8809
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/globFiles/index.js
|
|
8801
|
-
import {
|
|
8802
|
-
import {
|
|
8810
|
+
import { glob } from "glob";
|
|
8811
|
+
import { stat } from "fs/promises";
|
|
8803
8812
|
import path9 from "path";
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
8811
|
-
|
|
8812
|
-
|
|
8813
|
-
|
|
8814
|
-
|
|
8815
|
-
|
|
8816
|
-
|
|
8817
|
-
|
|
8818
|
-
if (!includeHidden && entry.name.startsWith(".")) {
|
|
8819
|
-
continue;
|
|
8820
|
-
}
|
|
8821
|
-
const relativePath = basePath ? path9.join(basePath, entry.name) : entry.name;
|
|
8822
|
-
const fullPath = path9.join(dir, entry.name);
|
|
8823
|
-
if (matchGlob(relativePath, pattern)) {
|
|
8824
|
-
matches.push(relativePath);
|
|
8825
|
-
}
|
|
8826
|
-
if (entry.isDirectory() && (pattern.includes("**") || pattern.includes("/"))) {
|
|
8827
|
-
const subMatches = await walkDir(fullPath, pattern, includeHidden, maxResults - matches.length, relativePath);
|
|
8828
|
-
matches.push(...subMatches);
|
|
8829
|
-
}
|
|
8830
|
-
}
|
|
8831
|
-
} catch (error) {
|
|
8832
|
-
}
|
|
8833
|
-
return matches;
|
|
8834
|
-
}
|
|
8835
|
-
async function listFiles(params) {
|
|
8836
|
-
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;
|
|
8837
8827
|
const baseCwd = process.cwd();
|
|
8838
|
-
const
|
|
8839
|
-
if (!
|
|
8840
|
-
throw new Error(`Access denied: Cannot
|
|
8841
|
-
}
|
|
8842
|
-
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
|
+
});
|
|
8843
8846
|
if (matches.length === 0) {
|
|
8844
|
-
return `No files found matching pattern: ${pattern}`;
|
|
8847
|
+
return `No files found matching pattern: ${pattern}${dir_path ? ` in ${dir_path}` : ""}`;
|
|
8845
8848
|
}
|
|
8846
|
-
const
|
|
8847
|
-
for (const
|
|
8849
|
+
const filesWithStats = [];
|
|
8850
|
+
for (const filePath of matches) {
|
|
8848
8851
|
try {
|
|
8849
|
-
const
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
size: stats.size,
|
|
8854
|
-
isDirectory: stats.isDirectory(),
|
|
8855
|
-
modified: stats.mtime.toISOString()
|
|
8852
|
+
const stats = await stat(filePath);
|
|
8853
|
+
filesWithStats.push({
|
|
8854
|
+
path: filePath,
|
|
8855
|
+
mtime: stats.mtimeMs
|
|
8856
8856
|
});
|
|
8857
|
-
} catch
|
|
8857
|
+
} catch {
|
|
8858
8858
|
continue;
|
|
8859
8859
|
}
|
|
8860
8860
|
}
|
|
8861
|
-
|
|
8862
|
-
const
|
|
8863
|
-
|
|
8864
|
-
const
|
|
8865
|
-
` : ""
|
|
8866
|
-
const
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
|
|
8871
|
-
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
|
+
|
|
8872
8871
|
${filesList}`;
|
|
8873
8872
|
}
|
|
8874
|
-
function formatBytes(bytes) {
|
|
8875
|
-
if (bytes === 0)
|
|
8876
|
-
return "0 B";
|
|
8877
|
-
const k = 1024;
|
|
8878
|
-
const sizes = ["B", "KB", "MB", "GB"];
|
|
8879
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
8880
|
-
return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
|
|
8881
|
-
}
|
|
8882
8873
|
var globFilesTool = {
|
|
8883
8874
|
name: "glob_files",
|
|
8884
8875
|
implementation: (context) => ({
|
|
8885
8876
|
toolFn: async (value) => {
|
|
8886
8877
|
const params = value;
|
|
8887
|
-
context.logger.info("\u{1F50D} GlobFiles:
|
|
8878
|
+
context.logger.info("\u{1F50D} GlobFiles: Finding files", {
|
|
8888
8879
|
pattern: params.pattern,
|
|
8889
|
-
|
|
8880
|
+
dir_path: params.dir_path || "."
|
|
8890
8881
|
});
|
|
8891
8882
|
try {
|
|
8892
|
-
const result = await
|
|
8883
|
+
const result = await findFiles(params);
|
|
8893
8884
|
context.logger.info("\u2705 GlobFiles: Success");
|
|
8894
8885
|
return result;
|
|
8895
8886
|
} catch (error) {
|
|
@@ -8899,25 +8890,25 @@ var globFilesTool = {
|
|
|
8899
8890
|
},
|
|
8900
8891
|
toolSchema: {
|
|
8901
8892
|
name: "glob_files",
|
|
8902
|
-
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.",
|
|
8903
8894
|
parameters: {
|
|
8904
8895
|
type: "object",
|
|
8905
8896
|
properties: {
|
|
8906
8897
|
pattern: {
|
|
8907
8898
|
type: "string",
|
|
8908
|
-
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.'
|
|
8909
8900
|
},
|
|
8910
|
-
|
|
8901
|
+
dir_path: {
|
|
8911
8902
|
type: "string",
|
|
8912
|
-
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."
|
|
8913
8904
|
},
|
|
8914
|
-
|
|
8905
|
+
case_sensitive: {
|
|
8915
8906
|
type: "boolean",
|
|
8916
|
-
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."
|
|
8917
8908
|
},
|
|
8918
|
-
|
|
8919
|
-
type: "
|
|
8920
|
-
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."
|
|
8921
8912
|
}
|
|
8922
8913
|
},
|
|
8923
8914
|
required: ["pattern"]
|
|
@@ -8927,158 +8918,144 @@ var globFilesTool = {
|
|
|
8927
8918
|
};
|
|
8928
8919
|
|
|
8929
8920
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/grepSearch/index.js
|
|
8930
|
-
import {
|
|
8931
|
-
import {
|
|
8921
|
+
import { globStream } from "glob";
|
|
8922
|
+
import { readFile, stat as stat2 } from "fs/promises";
|
|
8932
8923
|
import path10 from "path";
|
|
8933
8924
|
var MAX_FILE_SIZE3 = 10 * 1024 * 1024;
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
|
|
8940
|
-
|
|
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) {
|
|
8941
8939
|
try {
|
|
8942
|
-
const
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
const fullPath = path10.join(dir, entry.name);
|
|
8952
|
-
if (matchGlob2(relativePath, pattern)) {
|
|
8953
|
-
matches.push(relativePath);
|
|
8954
|
-
}
|
|
8955
|
-
if (entry.isDirectory() && (pattern.includes("**") || pattern.includes("/"))) {
|
|
8956
|
-
const subMatches = await walkDir2(fullPath, pattern, maxResults - matches.length, relativePath);
|
|
8957
|
-
matches.push(...subMatches);
|
|
8958
|
-
}
|
|
8959
|
-
}
|
|
8960
|
-
} 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;
|
|
8961
8949
|
}
|
|
8962
|
-
|
|
8950
|
+
}
|
|
8951
|
+
function isPathWithinWorkspace(targetPath, baseCwd) {
|
|
8952
|
+
const resolvedTarget = path10.resolve(targetPath);
|
|
8953
|
+
const resolvedBase = path10.resolve(baseCwd);
|
|
8954
|
+
return resolvedTarget.startsWith(resolvedBase);
|
|
8963
8955
|
}
|
|
8964
8956
|
async function searchFiles2(params) {
|
|
8965
|
-
const { pattern,
|
|
8957
|
+
const { pattern, dir_path, include } = params;
|
|
8966
8958
|
const baseCwd = process.cwd();
|
|
8967
|
-
const
|
|
8968
|
-
if (!
|
|
8969
|
-
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;
|
|
8970
8973
|
}
|
|
8971
8974
|
let searchRegex;
|
|
8972
8975
|
try {
|
|
8973
|
-
|
|
8974
|
-
searchRegex = new RegExp(pattern, flags);
|
|
8976
|
+
searchRegex = new RegExp(pattern, "i");
|
|
8975
8977
|
} catch (error) {
|
|
8976
8978
|
throw new Error(`Invalid regex pattern: ${pattern}`);
|
|
8977
8979
|
}
|
|
8978
|
-
const
|
|
8979
|
-
const
|
|
8980
|
-
|
|
8981
|
-
|
|
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;
|
|
8982
8998
|
try {
|
|
8983
|
-
const stats =
|
|
8984
|
-
if (stats.
|
|
8999
|
+
const stats = await stat2(fileAbsPath);
|
|
9000
|
+
if (stats.size > MAX_FILE_SIZE3) {
|
|
8985
9001
|
continue;
|
|
8986
9002
|
}
|
|
8987
|
-
if (isBinaryFile2(
|
|
9003
|
+
if (await isBinaryFile2(fileAbsPath)) {
|
|
8988
9004
|
continue;
|
|
8989
9005
|
}
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
|
|
8993
|
-
|
|
8994
|
-
|
|
8995
|
-
|
|
8996
|
-
|
|
8997
|
-
|
|
8998
|
-
|
|
8999
|
-
|
|
9000
|
-
for (const file of filteredFiles) {
|
|
9001
|
-
if (matches.length >= maxResults) {
|
|
9002
|
-
break;
|
|
9003
|
-
}
|
|
9004
|
-
filesSearched++;
|
|
9005
|
-
const fullPath = path10.join(targetCwd, file);
|
|
9006
|
-
try {
|
|
9007
|
-
const content = readFileSync3(fullPath, "utf-8");
|
|
9008
|
-
const lines = content.split("\n");
|
|
9009
|
-
for (let i = 0; i < lines.length; i++) {
|
|
9010
|
-
if (matches.length >= maxResults) {
|
|
9011
|
-
break;
|
|
9012
|
-
}
|
|
9013
|
-
const line = lines[i];
|
|
9014
|
-
if (searchRegex.test(line)) {
|
|
9015
|
-
const match = {
|
|
9016
|
-
file,
|
|
9017
|
-
line: i + 1,
|
|
9018
|
-
// 1-indexed line numbers
|
|
9019
|
-
content: line.trim()
|
|
9020
|
-
};
|
|
9021
|
-
if (contextLines > 0) {
|
|
9022
|
-
const before = [];
|
|
9023
|
-
const after = [];
|
|
9024
|
-
for (let j = Math.max(0, i - contextLines); j < i; j++) {
|
|
9025
|
-
before.push(lines[j]);
|
|
9026
|
-
}
|
|
9027
|
-
for (let j = i + 1; j <= Math.min(lines.length - 1, i + contextLines); j++) {
|
|
9028
|
-
after.push(lines[j]);
|
|
9029
|
-
}
|
|
9030
|
-
match.context = { before, after };
|
|
9031
|
-
}
|
|
9032
|
-
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
|
+
});
|
|
9033
9016
|
}
|
|
9034
|
-
|
|
9017
|
+
});
|
|
9018
|
+
} catch (readError) {
|
|
9019
|
+
const errCode = readError.code;
|
|
9020
|
+
if (errCode !== "ENOENT" && errCode !== "EACCES") {
|
|
9021
|
+
console.debug(`Could not read ${fileAbsPath}: ${readError.message}`);
|
|
9035
9022
|
}
|
|
9036
|
-
} catch {
|
|
9037
|
-
continue;
|
|
9038
9023
|
}
|
|
9039
9024
|
}
|
|
9040
|
-
|
|
9041
|
-
|
|
9042
|
-
|
|
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).`;
|
|
9043
9030
|
}
|
|
9044
|
-
const
|
|
9045
|
-
|
|
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}:
|
|
9046
9043
|
`;
|
|
9047
|
-
|
|
9044
|
+
result += `Searched ${filesSearched} file(s)
|
|
9045
|
+
---
|
|
9048
9046
|
`;
|
|
9049
|
-
const
|
|
9050
|
-
`
|
|
9051
|
-
|
|
9052
|
-
|
|
9053
|
-
|
|
9054
|
-
|
|
9055
|
-
|
|
9056
|
-
|
|
9057
|
-
|
|
9058
|
-
|
|
9059
|
-
${match.context.before.map((l) => ` ${l}`).join("\n")}`;
|
|
9060
|
-
}
|
|
9061
|
-
if (match.context.after.length > 0) {
|
|
9062
|
-
result = `${result}
|
|
9063
|
-
Context after:
|
|
9064
|
-
${match.context.after.map((l) => ` ${l}`).join("\n")}`;
|
|
9065
|
-
}
|
|
9066
|
-
}
|
|
9067
|
-
return result;
|
|
9068
|
-
}).join("\n");
|
|
9069
|
-
return `${summary}${patternInfo}${cwdInfo}${matchesList}`;
|
|
9070
|
-
}
|
|
9071
|
-
function isBinaryFile2(filePath) {
|
|
9072
|
-
try {
|
|
9073
|
-
const buffer = Buffer.alloc(8192);
|
|
9074
|
-
const fd = __require("fs").openSync(filePath, "r");
|
|
9075
|
-
const bytesRead = __require("fs").readSync(fd, buffer, 0, 8192, 0);
|
|
9076
|
-
__require("fs").closeSync(fd);
|
|
9077
|
-
const chunk = buffer.slice(0, bytesRead);
|
|
9078
|
-
return chunk.includes(0);
|
|
9079
|
-
} catch {
|
|
9080
|
-
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";
|
|
9081
9057
|
}
|
|
9058
|
+
return result.trim();
|
|
9082
9059
|
}
|
|
9083
9060
|
var grepSearchTool = {
|
|
9084
9061
|
name: "grep_search",
|
|
@@ -9087,8 +9064,8 @@ var grepSearchTool = {
|
|
|
9087
9064
|
const params = value;
|
|
9088
9065
|
context.logger.info("\u{1F50D} GrepSearch: Searching files", {
|
|
9089
9066
|
pattern: params.pattern,
|
|
9090
|
-
|
|
9091
|
-
|
|
9067
|
+
dir_path: params.dir_path || ".",
|
|
9068
|
+
include: params.include || "**/*"
|
|
9092
9069
|
});
|
|
9093
9070
|
try {
|
|
9094
9071
|
const result = await searchFiles2(params);
|
|
@@ -9101,33 +9078,21 @@ var grepSearchTool = {
|
|
|
9101
9078
|
},
|
|
9102
9079
|
toolSchema: {
|
|
9103
9080
|
name: "grep_search",
|
|
9104
|
-
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.",
|
|
9105
9082
|
parameters: {
|
|
9106
9083
|
type: "object",
|
|
9107
9084
|
properties: {
|
|
9108
9085
|
pattern: {
|
|
9109
9086
|
type: "string",
|
|
9110
|
-
description: "
|
|
9087
|
+
description: "The regular expression (regex) pattern to search for within file contents (e.g., 'function\\s+myFunction', 'import\\s+\\{.*\\}\\s+from\\s+.*')."
|
|
9111
9088
|
},
|
|
9112
|
-
|
|
9089
|
+
dir_path: {
|
|
9113
9090
|
type: "string",
|
|
9114
|
-
description:
|
|
9091
|
+
description: "Optional: The absolute path to the directory to search within. If omitted, searches the current working directory."
|
|
9115
9092
|
},
|
|
9116
|
-
|
|
9093
|
+
include: {
|
|
9117
9094
|
type: "string",
|
|
9118
|
-
description: "
|
|
9119
|
-
},
|
|
9120
|
-
caseSensitive: {
|
|
9121
|
-
type: "boolean",
|
|
9122
|
-
description: "Whether the search should be case-sensitive (default: false)"
|
|
9123
|
-
},
|
|
9124
|
-
maxResults: {
|
|
9125
|
-
type: "number",
|
|
9126
|
-
description: "Maximum number of matches to return (default: 100). Prevents overwhelming output."
|
|
9127
|
-
},
|
|
9128
|
-
contextLines: {
|
|
9129
|
-
type: "number",
|
|
9130
|
-
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)."
|
|
9131
9096
|
}
|
|
9132
9097
|
},
|
|
9133
9098
|
required: ["pattern"]
|
|
@@ -9138,7 +9103,7 @@ var grepSearchTool = {
|
|
|
9138
9103
|
|
|
9139
9104
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/deleteFile/index.js
|
|
9140
9105
|
import { promises as fs10 } from "fs";
|
|
9141
|
-
import { existsSync as existsSync6, statSync as
|
|
9106
|
+
import { existsSync as existsSync6, statSync as statSync5 } from "fs";
|
|
9142
9107
|
import path11 from "path";
|
|
9143
9108
|
async function deleteFile(params) {
|
|
9144
9109
|
const { path: filePath, recursive = false } = params;
|
|
@@ -9151,7 +9116,7 @@ async function deleteFile(params) {
|
|
|
9151
9116
|
if (!existsSync6(resolvedPath)) {
|
|
9152
9117
|
throw new Error(`File or directory not found: ${filePath}`);
|
|
9153
9118
|
}
|
|
9154
|
-
const stats =
|
|
9119
|
+
const stats = statSync5(resolvedPath);
|
|
9155
9120
|
const isDirectory = stats.isDirectory();
|
|
9156
9121
|
const size = stats.size;
|
|
9157
9122
|
if (isDirectory && !recursive) {
|
|
@@ -9172,7 +9137,7 @@ var deleteFileTool = {
|
|
|
9172
9137
|
toolFn: async (value) => {
|
|
9173
9138
|
const params = value;
|
|
9174
9139
|
const resolvedPath = path11.resolve(process.cwd(), path11.normalize(params.path));
|
|
9175
|
-
const isDirectory = existsSync6(resolvedPath) &&
|
|
9140
|
+
const isDirectory = existsSync6(resolvedPath) && statSync5(resolvedPath).isDirectory();
|
|
9176
9141
|
context.logger.info(`\u{1F5D1}\uFE0F DeleteFile: Deleting ${isDirectory ? "directory" : "file"}`, {
|
|
9177
9142
|
path: params.path,
|
|
9178
9143
|
recursive: params.recursive
|
|
@@ -9948,7 +9913,7 @@ var ToolErrorType;
|
|
|
9948
9913
|
|
|
9949
9914
|
// src/utils/diffPreview.ts
|
|
9950
9915
|
import * as Diff from "diff";
|
|
9951
|
-
import { readFile } from "fs/promises";
|
|
9916
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
9952
9917
|
import { existsSync as existsSync8 } from "fs";
|
|
9953
9918
|
async function generateFileDiffPreview(args) {
|
|
9954
9919
|
try {
|
|
@@ -9963,7 +9928,7 @@ ${preview}${hasMore ? `
|
|
|
9963
9928
|
|
|
9964
9929
|
... (${lines2.length - 20} more lines)` : ""}`;
|
|
9965
9930
|
}
|
|
9966
|
-
const currentContent = await
|
|
9931
|
+
const currentContent = await readFile2(args.path, "utf-8");
|
|
9967
9932
|
const patch = Diff.createPatch(
|
|
9968
9933
|
args.path,
|
|
9969
9934
|
currentContent,
|
|
@@ -10872,6 +10837,82 @@ async function loadContextFiles(projectDir) {
|
|
|
10872
10837
|
};
|
|
10873
10838
|
}
|
|
10874
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
|
+
|
|
10875
10916
|
// src/utils/argumentSubstitution.ts
|
|
10876
10917
|
function substituteArguments(template, args) {
|
|
10877
10918
|
let result = template;
|
|
@@ -11787,7 +11828,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
11787
11828
|
// package.json
|
|
11788
11829
|
var package_default = {
|
|
11789
11830
|
name: "@bike4mind/cli",
|
|
11790
|
-
version: "0.2.
|
|
11831
|
+
version: "0.2.13",
|
|
11791
11832
|
type: "module",
|
|
11792
11833
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
11793
11834
|
license: "UNLICENSED",
|
|
@@ -11858,6 +11899,7 @@ var package_default = {
|
|
|
11858
11899
|
"eventsource-parser": "^3.0.6",
|
|
11859
11900
|
"file-type": "^18.7.0",
|
|
11860
11901
|
"fuse.js": "^7.1.0",
|
|
11902
|
+
glob: "^13.0.0",
|
|
11861
11903
|
"gray-matter": "^4.0.3",
|
|
11862
11904
|
ink: "^6.5.1",
|
|
11863
11905
|
"ink-select-input": "^6.2.0",
|
|
@@ -11891,11 +11933,11 @@ var package_default = {
|
|
|
11891
11933
|
zustand: "^4.5.4"
|
|
11892
11934
|
},
|
|
11893
11935
|
devDependencies: {
|
|
11894
|
-
"@bike4mind/agents": "
|
|
11895
|
-
"@bike4mind/common": "
|
|
11896
|
-
"@bike4mind/mcp": "
|
|
11897
|
-
"@bike4mind/services": "
|
|
11898
|
-
"@bike4mind/utils": "
|
|
11936
|
+
"@bike4mind/agents": "workspace:*",
|
|
11937
|
+
"@bike4mind/common": "workspace:*",
|
|
11938
|
+
"@bike4mind/mcp": "workspace:*",
|
|
11939
|
+
"@bike4mind/services": "workspace:*",
|
|
11940
|
+
"@bike4mind/utils": "workspace:*",
|
|
11899
11941
|
"@types/better-sqlite3": "^7.6.13",
|
|
11900
11942
|
"@types/diff": "^5.0.9",
|
|
11901
11943
|
"@types/jsonwebtoken": "^9.0.4",
|
|
@@ -11903,12 +11945,12 @@ var package_default = {
|
|
|
11903
11945
|
"@types/react": "^19.2.7",
|
|
11904
11946
|
"@types/uuid": "^9.0.7",
|
|
11905
11947
|
"@types/yargs": "^17.0.32",
|
|
11948
|
+
"ink-testing-library": "^4.0.0",
|
|
11906
11949
|
tsup: "^8.5.1",
|
|
11907
11950
|
tsx: "^4.21.0",
|
|
11908
11951
|
typescript: "^5.9.3",
|
|
11909
11952
|
vitest: "^3.2.4"
|
|
11910
|
-
}
|
|
11911
|
-
gitHead: "e58dba2ec406a51c01acee154913c40c02c8ebf2"
|
|
11953
|
+
}
|
|
11912
11954
|
};
|
|
11913
11955
|
|
|
11914
11956
|
// src/config/constants.ts
|
|
@@ -12264,6 +12306,131 @@ function createSubagentDelegateTool(orchestrator, parentSessionId) {
|
|
|
12264
12306
|
};
|
|
12265
12307
|
}
|
|
12266
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
|
+
|
|
12267
12434
|
// src/index.tsx
|
|
12268
12435
|
process.removeAllListeners("warning");
|
|
12269
12436
|
process.on("warning", (warning) => {
|
|
@@ -12548,7 +12715,9 @@ function CliApp() {
|
|
|
12548
12715
|
subagentConfigs
|
|
12549
12716
|
});
|
|
12550
12717
|
const subagentDelegateTool = createSubagentDelegateTool(orchestrator, newSession.id);
|
|
12551
|
-
const
|
|
12718
|
+
const todoStore = createTodoStore();
|
|
12719
|
+
const writeTodosTool = createWriteTodosTool(todoStore);
|
|
12720
|
+
const allTools = [...b4mTools, ...mcpTools, subagentDelegateTool, writeTodosTool];
|
|
12552
12721
|
console.log(`\u{1F916} Subagent delegation enabled (explore, plan, review)`);
|
|
12553
12722
|
const projectDir = state.configStore.getProjectConfigDir();
|
|
12554
12723
|
const contextResult = await loadContextFiles(projectDir);
|
|
@@ -12588,26 +12757,12 @@ ${contextResult.mergedContent}` : "";
|
|
|
12588
12757
|
const lastIdx = pendingMessages.length - 1;
|
|
12589
12758
|
if (lastIdx >= 0 && pendingMessages[lastIdx].role === "assistant") {
|
|
12590
12759
|
const existingSteps = pendingMessages[lastIdx].metadata?.steps || [];
|
|
12591
|
-
const
|
|
12592
|
-
let truncatedStep = step;
|
|
12593
|
-
if (step.type === "action" && step.metadata?.toolInput) {
|
|
12594
|
-
const inputStr = typeof step.metadata.toolInput === "string" ? step.metadata.toolInput : JSON.stringify(step.metadata.toolInput);
|
|
12595
|
-
if (inputStr.length > MAX_INPUT_LENGTH) {
|
|
12596
|
-
const truncatedInput = inputStr.slice(0, MAX_INPUT_LENGTH) + `... (${inputStr.length - MAX_INPUT_LENGTH} more chars)`;
|
|
12597
|
-
truncatedStep = {
|
|
12598
|
-
...step,
|
|
12599
|
-
metadata: {
|
|
12600
|
-
...step.metadata,
|
|
12601
|
-
toolInput: truncatedInput
|
|
12602
|
-
}
|
|
12603
|
-
};
|
|
12604
|
-
}
|
|
12605
|
-
}
|
|
12760
|
+
const formattedStep = formatStep(step);
|
|
12606
12761
|
updatePendingMessage(lastIdx, {
|
|
12607
12762
|
...pendingMessages[lastIdx],
|
|
12608
12763
|
metadata: {
|
|
12609
12764
|
...pendingMessages[lastIdx].metadata,
|
|
12610
|
-
steps: [...existingSteps,
|
|
12765
|
+
steps: [...existingSteps, formattedStep]
|
|
12611
12766
|
}
|
|
12612
12767
|
});
|
|
12613
12768
|
}
|
|
@@ -12749,7 +12904,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
12749
12904
|
content: result.finalAnswer,
|
|
12750
12905
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12751
12906
|
metadata: {
|
|
12752
|
-
steps: result.steps,
|
|
12907
|
+
steps: result.steps.map(formatStep),
|
|
12753
12908
|
tokenUsage: {
|
|
12754
12909
|
prompt: 0,
|
|
12755
12910
|
completion: 0,
|
|
@@ -12922,7 +13077,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
12922
13077
|
completion: 0,
|
|
12923
13078
|
total: result.completionInfo.totalTokens
|
|
12924
13079
|
},
|
|
12925
|
-
steps: result.steps,
|
|
13080
|
+
steps: result.steps.map(formatStep),
|
|
12926
13081
|
// Complete history: thoughts, actions, observations
|
|
12927
13082
|
permissionDenied
|
|
12928
13083
|
}
|
|
@@ -13028,7 +13183,7 @@ ${output}` : output.trim() || "(no output)",
|
|
|
13028
13183
|
if (!imageStore) {
|
|
13029
13184
|
if (!imageStoreInitPromise.current) {
|
|
13030
13185
|
imageStoreInitPromise.current = (async () => {
|
|
13031
|
-
const { ImageStore: ImageStoreClass } = await import("./ImageStore-
|
|
13186
|
+
const { ImageStore: ImageStoreClass } = await import("./ImageStore-MMUOUPI2.js");
|
|
13032
13187
|
const newImageStore = new ImageStoreClass();
|
|
13033
13188
|
setState((prev) => ({
|
|
13034
13189
|
...prev,
|