@bike4mind/cli 0.2.10-feat-cli-multi-directory-workspace.17245 → 0.2.10-feat-explore-subagent-implementation.17246
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/bin/bike4mind-cli.mjs +0 -12
- package/dist/index.js +449 -664
- package/package.json +6 -7
package/dist/index.js
CHANGED
|
@@ -91,13 +91,8 @@ import { Box as Box10, Text as Text10, Static } from "ink";
|
|
|
91
91
|
// src/components/StatusBar.tsx
|
|
92
92
|
import React from "react";
|
|
93
93
|
import { Box, Text } from "ink";
|
|
94
|
-
var StatusBar = React.memo(function StatusBar2({
|
|
95
|
-
sessionName,
|
|
96
|
-
model,
|
|
97
|
-
tokenUsage,
|
|
98
|
-
workspaceCount
|
|
99
|
-
}) {
|
|
100
|
-
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", justifyContent: "space-between", width: "100%", paddingX: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, sessionName), /* @__PURE__ */ React.createElement(Box, { gap: 2 }, workspaceCount && workspaceCount > 1 && /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "\u{1F4C1} ", workspaceCount, " dirs"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, tokenUsage.toLocaleString(), " tokens"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, model)));
|
|
94
|
+
var StatusBar = React.memo(function StatusBar2({ sessionName, model, tokenUsage }) {
|
|
95
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", justifyContent: "space-between", width: "100%", paddingX: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, sessionName), /* @__PURE__ */ React.createElement(Box, { gap: 2 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, tokenUsage.toLocaleString(), " tokens"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, model)));
|
|
101
96
|
});
|
|
102
97
|
|
|
103
98
|
// src/components/InputPrompt.tsx
|
|
@@ -1430,7 +1425,6 @@ function App({
|
|
|
1430
1425
|
const sessionName = useCliStore((state) => state.session?.name || "New Session");
|
|
1431
1426
|
const currentModel = useCliStore((state) => state.session?.model || "claude-sonnet-4-5-20250929");
|
|
1432
1427
|
const totalTokens = useCliStore((state) => state.session?.metadata.totalTokens || 0);
|
|
1433
|
-
const workspaceCount = useCliStore((state) => state.session?.workingDirectories?.length || 1);
|
|
1434
1428
|
const isThinking = useCliStore((state) => state.isThinking);
|
|
1435
1429
|
const permissionPrompt = useCliStore((state) => state.permissionPrompt);
|
|
1436
1430
|
const showConfigEditor = useCliStore((state) => state.showConfigEditor);
|
|
@@ -1496,15 +1490,7 @@ ${errorBlock}`;
|
|
|
1496
1490
|
onPrefillConsumed,
|
|
1497
1491
|
onBashModeChange: setIsBashMode
|
|
1498
1492
|
}
|
|
1499
|
-
)), /* @__PURE__ */ React11.createElement(
|
|
1500
|
-
StatusBar,
|
|
1501
|
-
{
|
|
1502
|
-
sessionName,
|
|
1503
|
-
model: currentModel,
|
|
1504
|
-
tokenUsage: totalTokens,
|
|
1505
|
-
workspaceCount
|
|
1506
|
-
}
|
|
1507
|
-
)));
|
|
1493
|
+
)), /* @__PURE__ */ React11.createElement(StatusBar, { sessionName, model: currentModel, tokenUsage: totalTokens })));
|
|
1508
1494
|
}
|
|
1509
1495
|
|
|
1510
1496
|
// src/components/MessageList.tsx
|
|
@@ -5165,8 +5151,8 @@ async function processAndStoreImages(images, context) {
|
|
|
5165
5151
|
const buffer = await downloadImage(image);
|
|
5166
5152
|
const fileType = await fileTypeFromBuffer2(buffer);
|
|
5167
5153
|
const filename = `${uuidv45()}.${fileType?.ext}`;
|
|
5168
|
-
const
|
|
5169
|
-
return
|
|
5154
|
+
const path15 = await context.imageGenerateStorage.upload(buffer, filename, {});
|
|
5155
|
+
return path15;
|
|
5170
5156
|
}));
|
|
5171
5157
|
}
|
|
5172
5158
|
async function updateQuestAndReturnMarkdown(storedImageUrls, context) {
|
|
@@ -6378,8 +6364,8 @@ async function processAndStoreImage(imageUrl, context) {
|
|
|
6378
6364
|
const buffer = await downloadImage2(imageUrl);
|
|
6379
6365
|
const fileType = await fileTypeFromBuffer3(buffer);
|
|
6380
6366
|
const filename = `${uuidv46()}.${fileType?.ext}`;
|
|
6381
|
-
const
|
|
6382
|
-
return
|
|
6367
|
+
const path15 = await context.imageGenerateStorage.upload(buffer, filename, {});
|
|
6368
|
+
return path15;
|
|
6383
6369
|
}
|
|
6384
6370
|
async function updateQuestAndReturnMarkdown2(storedImagePath, context) {
|
|
6385
6371
|
await context.onFinish?.("edit_image", storedImagePath);
|
|
@@ -8283,7 +8269,8 @@ var fileReadTool = {
|
|
|
8283
8269
|
return content;
|
|
8284
8270
|
} catch (error) {
|
|
8285
8271
|
context.logger.error("\u274C FileRead: Failed", error);
|
|
8286
|
-
|
|
8272
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
8273
|
+
return `Error reading file: ${errorMessage}`;
|
|
8287
8274
|
}
|
|
8288
8275
|
},
|
|
8289
8276
|
toolSchema: {
|
|
@@ -9470,7 +9457,7 @@ async function generateFileDeletePreview(args) {
|
|
|
9470
9457
|
if (!existsSync7(args.path)) {
|
|
9471
9458
|
return `[File does not exist: ${args.path}]`;
|
|
9472
9459
|
}
|
|
9473
|
-
const stats = await import("fs/promises").then((
|
|
9460
|
+
const stats = await import("fs/promises").then((fs12) => fs12.stat(args.path));
|
|
9474
9461
|
return `[File will be deleted]
|
|
9475
9462
|
|
|
9476
9463
|
Path: ${args.path}
|
|
@@ -9815,330 +9802,26 @@ async function executeTool(toolName, input, apiClient, localToolFn) {
|
|
|
9815
9802
|
}
|
|
9816
9803
|
}
|
|
9817
9804
|
|
|
9818
|
-
// src/tools/workspaceAwareFileTools.ts
|
|
9819
|
-
import path13 from "path";
|
|
9820
|
-
import fs11 from "fs/promises";
|
|
9821
|
-
import { existsSync as existsSync8 } from "fs";
|
|
9822
|
-
function validatePathInWorkspace(filePath, workspaceManager) {
|
|
9823
|
-
try {
|
|
9824
|
-
return workspaceManager.resolvePath(filePath);
|
|
9825
|
-
} catch (error) {
|
|
9826
|
-
throw error;
|
|
9827
|
-
}
|
|
9828
|
-
}
|
|
9829
|
-
function createWorkspaceAwareFileRead(workspaceManager) {
|
|
9830
|
-
return {
|
|
9831
|
-
toolSchema: {
|
|
9832
|
-
name: "file_read",
|
|
9833
|
-
description: "Read contents of a file from the workspace. Returns file contents as a string.",
|
|
9834
|
-
parameters: {
|
|
9835
|
-
type: "object",
|
|
9836
|
-
properties: {
|
|
9837
|
-
path: {
|
|
9838
|
-
type: "string",
|
|
9839
|
-
description: "Path to the file to read (relative to workspace or absolute)"
|
|
9840
|
-
}
|
|
9841
|
-
},
|
|
9842
|
-
required: ["path"]
|
|
9843
|
-
}
|
|
9844
|
-
},
|
|
9845
|
-
toolFn: async (parameters) => {
|
|
9846
|
-
const args = parameters;
|
|
9847
|
-
const { path: filePath } = args;
|
|
9848
|
-
const resolvedPath = validatePathInWorkspace(filePath, workspaceManager);
|
|
9849
|
-
if (!existsSync8(resolvedPath)) {
|
|
9850
|
-
throw new Error(`File not found: ${filePath}`);
|
|
9851
|
-
}
|
|
9852
|
-
const content = await fs11.readFile(resolvedPath, "utf-8");
|
|
9853
|
-
return content;
|
|
9854
|
-
}
|
|
9855
|
-
};
|
|
9856
|
-
}
|
|
9857
|
-
function createWorkspaceAwareCreateFile(workspaceManager) {
|
|
9858
|
-
return {
|
|
9859
|
-
toolSchema: {
|
|
9860
|
-
name: "create_file",
|
|
9861
|
-
description: "Create a new file with the given content in the workspace.",
|
|
9862
|
-
parameters: {
|
|
9863
|
-
type: "object",
|
|
9864
|
-
properties: {
|
|
9865
|
-
path: {
|
|
9866
|
-
type: "string",
|
|
9867
|
-
description: "Path where the file should be created (relative to workspace or absolute)"
|
|
9868
|
-
},
|
|
9869
|
-
content: {
|
|
9870
|
-
type: "string",
|
|
9871
|
-
description: "Content to write to the file"
|
|
9872
|
-
}
|
|
9873
|
-
},
|
|
9874
|
-
required: ["path", "content"]
|
|
9875
|
-
}
|
|
9876
|
-
},
|
|
9877
|
-
toolFn: async (parameters) => {
|
|
9878
|
-
const args = parameters;
|
|
9879
|
-
const { path: filePath, content } = args;
|
|
9880
|
-
const resolvedPath = validatePathInWorkspace(filePath, workspaceManager);
|
|
9881
|
-
if (existsSync8(resolvedPath)) {
|
|
9882
|
-
throw new Error(`File already exists: ${filePath}. Use edit_file to modify existing files.`);
|
|
9883
|
-
}
|
|
9884
|
-
const parentDir = path13.dirname(resolvedPath);
|
|
9885
|
-
await fs11.mkdir(parentDir, { recursive: true });
|
|
9886
|
-
await fs11.writeFile(resolvedPath, content, "utf-8");
|
|
9887
|
-
return `File created successfully: ${filePath}`;
|
|
9888
|
-
}
|
|
9889
|
-
};
|
|
9890
|
-
}
|
|
9891
|
-
function createWorkspaceAwareEditFile(workspaceManager) {
|
|
9892
|
-
return {
|
|
9893
|
-
toolSchema: {
|
|
9894
|
-
name: "edit_file",
|
|
9895
|
-
description: "Edit an existing file by replacing its entire content.",
|
|
9896
|
-
parameters: {
|
|
9897
|
-
type: "object",
|
|
9898
|
-
properties: {
|
|
9899
|
-
path: {
|
|
9900
|
-
type: "string",
|
|
9901
|
-
description: "Path to the file to edit (relative to workspace or absolute)"
|
|
9902
|
-
},
|
|
9903
|
-
content: {
|
|
9904
|
-
type: "string",
|
|
9905
|
-
description: "New content for the file"
|
|
9906
|
-
}
|
|
9907
|
-
},
|
|
9908
|
-
required: ["path", "content"]
|
|
9909
|
-
}
|
|
9910
|
-
},
|
|
9911
|
-
toolFn: async (parameters) => {
|
|
9912
|
-
const args = parameters;
|
|
9913
|
-
const { path: filePath, content } = args;
|
|
9914
|
-
const resolvedPath = validatePathInWorkspace(filePath, workspaceManager);
|
|
9915
|
-
if (!existsSync8(resolvedPath)) {
|
|
9916
|
-
throw new Error(`File not found: ${filePath}. Use create_file to create new files.`);
|
|
9917
|
-
}
|
|
9918
|
-
await fs11.writeFile(resolvedPath, content, "utf-8");
|
|
9919
|
-
return `File edited successfully: ${filePath}`;
|
|
9920
|
-
}
|
|
9921
|
-
};
|
|
9922
|
-
}
|
|
9923
|
-
function createWorkspaceAwareDeleteFile(workspaceManager) {
|
|
9924
|
-
return {
|
|
9925
|
-
toolSchema: {
|
|
9926
|
-
name: "delete_file",
|
|
9927
|
-
description: "Delete a file from the workspace.",
|
|
9928
|
-
parameters: {
|
|
9929
|
-
type: "object",
|
|
9930
|
-
properties: {
|
|
9931
|
-
path: {
|
|
9932
|
-
type: "string",
|
|
9933
|
-
description: "Path to the file to delete (relative to workspace or absolute)"
|
|
9934
|
-
}
|
|
9935
|
-
},
|
|
9936
|
-
required: ["path"]
|
|
9937
|
-
}
|
|
9938
|
-
},
|
|
9939
|
-
toolFn: async (parameters) => {
|
|
9940
|
-
const args = parameters;
|
|
9941
|
-
const { path: filePath } = args;
|
|
9942
|
-
const resolvedPath = validatePathInWorkspace(filePath, workspaceManager);
|
|
9943
|
-
if (!existsSync8(resolvedPath)) {
|
|
9944
|
-
throw new Error(`File not found: ${filePath}`);
|
|
9945
|
-
}
|
|
9946
|
-
await fs11.unlink(resolvedPath);
|
|
9947
|
-
return `File deleted successfully: ${filePath}`;
|
|
9948
|
-
}
|
|
9949
|
-
};
|
|
9950
|
-
}
|
|
9951
|
-
function createWorkspaceAwareGlobFiles(workspaceManager) {
|
|
9952
|
-
return {
|
|
9953
|
-
toolSchema: {
|
|
9954
|
-
name: "glob_files",
|
|
9955
|
-
description: "Search for files matching a glob pattern in the workspace.",
|
|
9956
|
-
parameters: {
|
|
9957
|
-
type: "object",
|
|
9958
|
-
properties: {
|
|
9959
|
-
pattern: {
|
|
9960
|
-
type: "string",
|
|
9961
|
-
description: 'Glob pattern to match files (e.g., "**/*.ts", "src/**/*.js")'
|
|
9962
|
-
},
|
|
9963
|
-
cwd: {
|
|
9964
|
-
type: "string",
|
|
9965
|
-
description: "Directory to search from (relative to primary workspace or absolute)"
|
|
9966
|
-
}
|
|
9967
|
-
},
|
|
9968
|
-
required: ["pattern"]
|
|
9969
|
-
}
|
|
9970
|
-
},
|
|
9971
|
-
toolFn: async (parameters) => {
|
|
9972
|
-
const args = parameters;
|
|
9973
|
-
const { pattern, cwd: relativeCwd } = args;
|
|
9974
|
-
const primaryWorkspace = workspaceManager.getPrimaryWorkspace();
|
|
9975
|
-
let searchDir = primaryWorkspace;
|
|
9976
|
-
if (relativeCwd) {
|
|
9977
|
-
searchDir = validatePathInWorkspace(relativeCwd, workspaceManager);
|
|
9978
|
-
}
|
|
9979
|
-
const glob = (await import("fast-glob")).default;
|
|
9980
|
-
const files = await glob(pattern, {
|
|
9981
|
-
cwd: searchDir,
|
|
9982
|
-
absolute: false,
|
|
9983
|
-
onlyFiles: true
|
|
9984
|
-
});
|
|
9985
|
-
const result = {
|
|
9986
|
-
pattern,
|
|
9987
|
-
cwd: searchDir,
|
|
9988
|
-
files: files.map((f) => path13.join(searchDir, f)),
|
|
9989
|
-
count: files.length
|
|
9990
|
-
};
|
|
9991
|
-
return JSON.stringify(result);
|
|
9992
|
-
}
|
|
9993
|
-
};
|
|
9994
|
-
}
|
|
9995
|
-
function createWorkspaceAwareGrepSearch(workspaceManager) {
|
|
9996
|
-
return {
|
|
9997
|
-
toolSchema: {
|
|
9998
|
-
name: "grep_search",
|
|
9999
|
-
description: "Search for text patterns in files within the workspace using grep.",
|
|
10000
|
-
parameters: {
|
|
10001
|
-
type: "object",
|
|
10002
|
-
properties: {
|
|
10003
|
-
pattern: {
|
|
10004
|
-
type: "string",
|
|
10005
|
-
description: "Regular expression pattern to search for"
|
|
10006
|
-
},
|
|
10007
|
-
path: {
|
|
10008
|
-
type: "string",
|
|
10009
|
-
description: "Directory or file to search (relative to primary workspace or absolute)"
|
|
10010
|
-
},
|
|
10011
|
-
filePattern: {
|
|
10012
|
-
type: "string",
|
|
10013
|
-
description: 'File pattern to filter files (e.g., "*.ts", "*.js")'
|
|
10014
|
-
}
|
|
10015
|
-
},
|
|
10016
|
-
required: ["pattern"]
|
|
10017
|
-
}
|
|
10018
|
-
},
|
|
10019
|
-
toolFn: async (parameters) => {
|
|
10020
|
-
const args = parameters;
|
|
10021
|
-
const { pattern, path: searchPath, filePattern } = args;
|
|
10022
|
-
const primaryWorkspace = workspaceManager.getPrimaryWorkspace();
|
|
10023
|
-
let searchDir = primaryWorkspace;
|
|
10024
|
-
if (searchPath) {
|
|
10025
|
-
searchDir = validatePathInWorkspace(searchPath, workspaceManager);
|
|
10026
|
-
}
|
|
10027
|
-
const glob = (await import("fast-glob")).default;
|
|
10028
|
-
const globPattern = filePattern || "**/*";
|
|
10029
|
-
const files = await glob(globPattern, {
|
|
10030
|
-
cwd: searchDir,
|
|
10031
|
-
absolute: true,
|
|
10032
|
-
onlyFiles: true
|
|
10033
|
-
});
|
|
10034
|
-
const matches = [];
|
|
10035
|
-
const regex = new RegExp(pattern, "gi");
|
|
10036
|
-
for (const file of files) {
|
|
10037
|
-
try {
|
|
10038
|
-
const content = await fs11.readFile(file, "utf-8");
|
|
10039
|
-
const lines = content.split("\n");
|
|
10040
|
-
lines.forEach((line, index) => {
|
|
10041
|
-
if (regex.test(line)) {
|
|
10042
|
-
matches.push({
|
|
10043
|
-
file: path13.relative(primaryWorkspace, file),
|
|
10044
|
-
line: index + 1,
|
|
10045
|
-
content: line.trim()
|
|
10046
|
-
});
|
|
10047
|
-
}
|
|
10048
|
-
});
|
|
10049
|
-
} catch (error) {
|
|
10050
|
-
}
|
|
10051
|
-
}
|
|
10052
|
-
const result = {
|
|
10053
|
-
pattern,
|
|
10054
|
-
searchDir,
|
|
10055
|
-
matches,
|
|
10056
|
-
totalMatches: matches.length
|
|
10057
|
-
};
|
|
10058
|
-
return JSON.stringify(result);
|
|
10059
|
-
}
|
|
10060
|
-
};
|
|
10061
|
-
}
|
|
10062
|
-
function createWorkspaceAwareBashExecute(workspaceManager) {
|
|
10063
|
-
return {
|
|
10064
|
-
toolSchema: {
|
|
10065
|
-
name: "bash_execute",
|
|
10066
|
-
description: "Execute a bash command in the workspace. Use with caution.",
|
|
10067
|
-
parameters: {
|
|
10068
|
-
type: "object",
|
|
10069
|
-
properties: {
|
|
10070
|
-
command: {
|
|
10071
|
-
type: "string",
|
|
10072
|
-
description: "Bash command to execute"
|
|
10073
|
-
},
|
|
10074
|
-
cwd: {
|
|
10075
|
-
type: "string",
|
|
10076
|
-
description: "Working directory for command execution (relative to primary workspace or absolute)"
|
|
10077
|
-
},
|
|
10078
|
-
timeout: {
|
|
10079
|
-
type: "number",
|
|
10080
|
-
description: "Timeout in milliseconds (default: 30000)"
|
|
10081
|
-
}
|
|
10082
|
-
},
|
|
10083
|
-
required: ["command"]
|
|
10084
|
-
}
|
|
10085
|
-
},
|
|
10086
|
-
toolFn: async (parameters) => {
|
|
10087
|
-
const args = parameters;
|
|
10088
|
-
const { command, cwd: relativeCwd, timeout = 3e4 } = args;
|
|
10089
|
-
const primaryWorkspace = workspaceManager.getPrimaryWorkspace();
|
|
10090
|
-
let execDir = primaryWorkspace;
|
|
10091
|
-
if (relativeCwd) {
|
|
10092
|
-
execDir = path13.resolve(primaryWorkspace, relativeCwd);
|
|
10093
|
-
}
|
|
10094
|
-
const { execSync: execSync2 } = await import("child_process");
|
|
10095
|
-
try {
|
|
10096
|
-
const output = execSync2(command, {
|
|
10097
|
-
cwd: execDir,
|
|
10098
|
-
encoding: "utf-8",
|
|
10099
|
-
timeout,
|
|
10100
|
-
maxBuffer: 10 * 1024 * 1024
|
|
10101
|
-
// 10MB
|
|
10102
|
-
});
|
|
10103
|
-
const result = {
|
|
10104
|
-
success: true,
|
|
10105
|
-
output: output.toString(),
|
|
10106
|
-
exitCode: 0
|
|
10107
|
-
};
|
|
10108
|
-
return JSON.stringify(result);
|
|
10109
|
-
} catch (error) {
|
|
10110
|
-
const result = {
|
|
10111
|
-
success: false,
|
|
10112
|
-
output: error.stdout?.toString() || "",
|
|
10113
|
-
error: error.stderr?.toString() || error.message,
|
|
10114
|
-
exitCode: error.status || 1
|
|
10115
|
-
};
|
|
10116
|
-
return JSON.stringify(result);
|
|
10117
|
-
}
|
|
10118
|
-
}
|
|
10119
|
-
};
|
|
10120
|
-
}
|
|
10121
|
-
|
|
10122
9805
|
// src/utils/toolsAdapter.ts
|
|
10123
9806
|
var NoOpStorage = class extends BaseStorage {
|
|
10124
9807
|
async upload(input, destination, options) {
|
|
10125
9808
|
return `/tmp/${destination}`;
|
|
10126
9809
|
}
|
|
10127
|
-
async download(
|
|
9810
|
+
async download(path15) {
|
|
10128
9811
|
throw new Error("Download not supported in CLI");
|
|
10129
9812
|
}
|
|
10130
|
-
async delete(
|
|
9813
|
+
async delete(path15) {
|
|
10131
9814
|
}
|
|
10132
|
-
async getSignedUrl(
|
|
10133
|
-
return `/tmp/${
|
|
9815
|
+
async getSignedUrl(path15) {
|
|
9816
|
+
return `/tmp/${path15}`;
|
|
10134
9817
|
}
|
|
10135
|
-
getPublicUrl(
|
|
10136
|
-
return `/tmp/${
|
|
9818
|
+
getPublicUrl(path15) {
|
|
9819
|
+
return `/tmp/${path15}`;
|
|
10137
9820
|
}
|
|
10138
|
-
async getPreview(
|
|
10139
|
-
return `/tmp/${
|
|
9821
|
+
async getPreview(path15) {
|
|
9822
|
+
return `/tmp/${path15}`;
|
|
10140
9823
|
}
|
|
10141
|
-
async getMetadata(
|
|
9824
|
+
async getMetadata(path15) {
|
|
10142
9825
|
return { size: 0, contentType: "application/octet-stream" };
|
|
10143
9826
|
}
|
|
10144
9827
|
};
|
|
@@ -10232,7 +9915,31 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
|
|
|
10232
9915
|
}
|
|
10233
9916
|
};
|
|
10234
9917
|
}
|
|
10235
|
-
|
|
9918
|
+
var TOOL_NAME_MAPPING = {
|
|
9919
|
+
// Claude Code -> B4M
|
|
9920
|
+
read: "file_read",
|
|
9921
|
+
write: "create_file",
|
|
9922
|
+
edit: "edit_file",
|
|
9923
|
+
delete: "delete_file",
|
|
9924
|
+
glob: "glob_files",
|
|
9925
|
+
grep: "grep_search",
|
|
9926
|
+
bash: "bash_execute",
|
|
9927
|
+
// B4M -> Claude Code (reverse mapping)
|
|
9928
|
+
file_read: "read",
|
|
9929
|
+
create_file: "write",
|
|
9930
|
+
edit_file: "edit",
|
|
9931
|
+
delete_file: "delete",
|
|
9932
|
+
glob_files: "glob",
|
|
9933
|
+
grep_search: "grep",
|
|
9934
|
+
bash_execute: "bash"
|
|
9935
|
+
};
|
|
9936
|
+
function normalizeToolName(toolName) {
|
|
9937
|
+
if (toolName.includes("_")) {
|
|
9938
|
+
return toolName;
|
|
9939
|
+
}
|
|
9940
|
+
return TOOL_NAME_MAPPING[toolName] || toolName;
|
|
9941
|
+
}
|
|
9942
|
+
function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient, toolFilter) {
|
|
10236
9943
|
const logger2 = new CliLogger();
|
|
10237
9944
|
const storage = new NoOpStorage();
|
|
10238
9945
|
const user = {
|
|
@@ -10302,32 +10009,25 @@ function generateCliTools(userId, llm, model, permissionManager, workspaceManage
|
|
|
10302
10009
|
toolConfig,
|
|
10303
10010
|
model
|
|
10304
10011
|
);
|
|
10305
|
-
|
|
10012
|
+
let tools2 = Object.entries(toolsMap).filter(([key]) => toolConfig[key] !== void 0).map(
|
|
10306
10013
|
([_, tool]) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient)
|
|
10307
10014
|
);
|
|
10308
|
-
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
|
|
10314
|
-
|
|
10315
|
-
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
|
|
10319
|
-
|
|
10320
|
-
|
|
10321
|
-
|
|
10322
|
-
|
|
10323
|
-
|
|
10324
|
-
createWorkspaceAwareBashExecute(workspaceManager)
|
|
10325
|
-
];
|
|
10326
|
-
const filteredTools = tools2.filter((tool) => !fileToolNames.includes(tool.toolSchema.name));
|
|
10327
|
-
const wrappedWorkspaceTools = workspaceAwareTools.map(
|
|
10328
|
-
(tool) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient)
|
|
10329
|
-
);
|
|
10330
|
-
return { tools: [...filteredTools, ...wrappedWorkspaceTools], agentContext };
|
|
10015
|
+
if (toolFilter) {
|
|
10016
|
+
const { allowedTools, deniedTools } = toolFilter;
|
|
10017
|
+
const normalizedAllowed = allowedTools?.map(normalizeToolName);
|
|
10018
|
+
const normalizedDenied = deniedTools?.map(normalizeToolName);
|
|
10019
|
+
tools2 = tools2.filter((tool) => {
|
|
10020
|
+
const toolName = tool.toolSchema.name;
|
|
10021
|
+
if (normalizedDenied && normalizedDenied.includes(toolName)) {
|
|
10022
|
+
return false;
|
|
10023
|
+
}
|
|
10024
|
+
if (normalizedAllowed && normalizedAllowed.length > 0) {
|
|
10025
|
+
return normalizedAllowed.includes(toolName);
|
|
10026
|
+
}
|
|
10027
|
+
return true;
|
|
10028
|
+
});
|
|
10029
|
+
}
|
|
10030
|
+
return { tools: tools2, agentContext };
|
|
10331
10031
|
}
|
|
10332
10032
|
|
|
10333
10033
|
// src/config/toolSafety.ts
|
|
@@ -10506,174 +10206,6 @@ var PermissionManager = class {
|
|
|
10506
10206
|
}
|
|
10507
10207
|
};
|
|
10508
10208
|
|
|
10509
|
-
// src/utils/WorkspaceManager.ts
|
|
10510
|
-
import path14 from "path";
|
|
10511
|
-
import fs12 from "fs";
|
|
10512
|
-
var WorkspaceManager = class _WorkspaceManager {
|
|
10513
|
-
constructor(initialWorkspaces = [process.cwd()]) {
|
|
10514
|
-
this.workspaces = [];
|
|
10515
|
-
this.workspaces = initialWorkspaces.length > 0 ? initialWorkspaces.map((dir) => path14.resolve(dir)) : [path14.resolve(process.cwd())];
|
|
10516
|
-
}
|
|
10517
|
-
static {
|
|
10518
|
-
/**
|
|
10519
|
-
* System directories that should never be added as workspaces
|
|
10520
|
-
*/
|
|
10521
|
-
this.FORBIDDEN_PATHS = [
|
|
10522
|
-
"/",
|
|
10523
|
-
"/bin",
|
|
10524
|
-
"/boot",
|
|
10525
|
-
"/dev",
|
|
10526
|
-
"/etc",
|
|
10527
|
-
"/lib",
|
|
10528
|
-
"/lib64",
|
|
10529
|
-
"/proc",
|
|
10530
|
-
"/root",
|
|
10531
|
-
"/sbin",
|
|
10532
|
-
"/sys",
|
|
10533
|
-
"/usr",
|
|
10534
|
-
"/var",
|
|
10535
|
-
"/tmp",
|
|
10536
|
-
"/System",
|
|
10537
|
-
"/Library",
|
|
10538
|
-
"/Applications",
|
|
10539
|
-
"/Windows",
|
|
10540
|
-
"/Program Files",
|
|
10541
|
-
"/Program Files (x86)"
|
|
10542
|
-
];
|
|
10543
|
-
}
|
|
10544
|
-
/**
|
|
10545
|
-
* Get all workspace directories
|
|
10546
|
-
*/
|
|
10547
|
-
getWorkspaces() {
|
|
10548
|
-
return [...this.workspaces];
|
|
10549
|
-
}
|
|
10550
|
-
/**
|
|
10551
|
-
* Get the primary workspace (first in list, used as default)
|
|
10552
|
-
*/
|
|
10553
|
-
getPrimaryWorkspace() {
|
|
10554
|
-
return this.workspaces[0] || process.cwd();
|
|
10555
|
-
}
|
|
10556
|
-
/**
|
|
10557
|
-
* Add a new workspace directory
|
|
10558
|
-
* @throws Error if directory is invalid, doesn't exist, or is forbidden
|
|
10559
|
-
*/
|
|
10560
|
-
addWorkspace(directory) {
|
|
10561
|
-
const resolvedPath = path14.resolve(directory);
|
|
10562
|
-
if (!fs12.existsSync(resolvedPath)) {
|
|
10563
|
-
throw new Error(`Directory does not exist: ${resolvedPath}`);
|
|
10564
|
-
}
|
|
10565
|
-
const stat = fs12.statSync(resolvedPath);
|
|
10566
|
-
if (!stat.isDirectory()) {
|
|
10567
|
-
throw new Error(`Path is not a directory: ${resolvedPath}`);
|
|
10568
|
-
}
|
|
10569
|
-
if (_WorkspaceManager.isForbiddenPath(resolvedPath)) {
|
|
10570
|
-
throw new Error(
|
|
10571
|
-
`Cannot add system directory as workspace: ${resolvedPath}
|
|
10572
|
-
Adding system directories like /, /usr, /etc is not allowed for security reasons.`
|
|
10573
|
-
);
|
|
10574
|
-
}
|
|
10575
|
-
if (this.workspaces.includes(resolvedPath)) {
|
|
10576
|
-
throw new Error(`Directory already in workspace: ${resolvedPath}`);
|
|
10577
|
-
}
|
|
10578
|
-
this.workspaces.push(resolvedPath);
|
|
10579
|
-
}
|
|
10580
|
-
/**
|
|
10581
|
-
* Remove a workspace directory
|
|
10582
|
-
* @throws Error if trying to remove the last/primary workspace
|
|
10583
|
-
*/
|
|
10584
|
-
removeWorkspace(directory) {
|
|
10585
|
-
const resolvedPath = path14.resolve(directory);
|
|
10586
|
-
const index = this.workspaces.indexOf(resolvedPath);
|
|
10587
|
-
if (index === -1) {
|
|
10588
|
-
throw new Error(`Directory not in workspace: ${resolvedPath}`);
|
|
10589
|
-
}
|
|
10590
|
-
if (this.workspaces.length === 1) {
|
|
10591
|
-
throw new Error("Cannot remove the last workspace directory");
|
|
10592
|
-
}
|
|
10593
|
-
if (index === 0) {
|
|
10594
|
-
throw new Error(
|
|
10595
|
-
"Cannot remove the primary workspace. Remove other directories first, or use /clear to start a new session."
|
|
10596
|
-
);
|
|
10597
|
-
}
|
|
10598
|
-
this.workspaces.splice(index, 1);
|
|
10599
|
-
}
|
|
10600
|
-
/**
|
|
10601
|
-
* Check if a path is within any workspace
|
|
10602
|
-
* @param filePath Absolute or relative path to check
|
|
10603
|
-
* @returns true if path is within an allowed workspace
|
|
10604
|
-
*/
|
|
10605
|
-
isPathInWorkspace(filePath) {
|
|
10606
|
-
const absolutePath = path14.isAbsolute(filePath) ? filePath : path14.resolve(this.getPrimaryWorkspace(), filePath);
|
|
10607
|
-
return this.workspaces.some(
|
|
10608
|
-
(workspace) => absolutePath.startsWith(workspace + path14.sep) || absolutePath === workspace
|
|
10609
|
-
);
|
|
10610
|
-
}
|
|
10611
|
-
/**
|
|
10612
|
-
* Resolve a file path across all workspaces
|
|
10613
|
-
* For relative paths: searches all workspaces and returns first match
|
|
10614
|
-
* For absolute paths: validates it's within a workspace
|
|
10615
|
-
* @throws Error if path is outside all workspaces
|
|
10616
|
-
*/
|
|
10617
|
-
resolvePath(filePath) {
|
|
10618
|
-
if (path14.isAbsolute(filePath)) {
|
|
10619
|
-
if (!this.isPathInWorkspace(filePath)) {
|
|
10620
|
-
throw new Error(
|
|
10621
|
-
`Access denied: Path is outside all workspace directories.
|
|
10622
|
-
Path: ${filePath}
|
|
10623
|
-
Workspaces: ${this.workspaces.join(", ")}
|
|
10624
|
-
Use '/directory add <path>' to add the directory to your workspace.`
|
|
10625
|
-
);
|
|
10626
|
-
}
|
|
10627
|
-
return filePath;
|
|
10628
|
-
}
|
|
10629
|
-
const primaryResolved = path14.resolve(this.getPrimaryWorkspace(), filePath);
|
|
10630
|
-
if (fs12.existsSync(primaryResolved)) {
|
|
10631
|
-
return primaryResolved;
|
|
10632
|
-
}
|
|
10633
|
-
for (let i = 1; i < this.workspaces.length; i++) {
|
|
10634
|
-
const candidatePath = path14.resolve(this.workspaces[i], filePath);
|
|
10635
|
-
if (fs12.existsSync(candidatePath)) {
|
|
10636
|
-
return candidatePath;
|
|
10637
|
-
}
|
|
10638
|
-
}
|
|
10639
|
-
return primaryResolved;
|
|
10640
|
-
}
|
|
10641
|
-
/**
|
|
10642
|
-
* Get the workspace that contains a given path
|
|
10643
|
-
* @returns The workspace directory, or null if not in any workspace
|
|
10644
|
-
*/
|
|
10645
|
-
getWorkspaceForPath(filePath) {
|
|
10646
|
-
const absolutePath = path14.isAbsolute(filePath) ? filePath : path14.resolve(this.getPrimaryWorkspace(), filePath);
|
|
10647
|
-
for (const workspace of this.workspaces) {
|
|
10648
|
-
if (absolutePath.startsWith(workspace + path14.sep) || absolutePath === workspace) {
|
|
10649
|
-
return workspace;
|
|
10650
|
-
}
|
|
10651
|
-
}
|
|
10652
|
-
return null;
|
|
10653
|
-
}
|
|
10654
|
-
/**
|
|
10655
|
-
* Check if a path is a forbidden system directory
|
|
10656
|
-
*/
|
|
10657
|
-
static isForbiddenPath(resolvedPath) {
|
|
10658
|
-
if (_WorkspaceManager.FORBIDDEN_PATHS.includes(resolvedPath)) {
|
|
10659
|
-
return true;
|
|
10660
|
-
}
|
|
10661
|
-
return _WorkspaceManager.FORBIDDEN_PATHS.some((forbidden) => resolvedPath.startsWith(forbidden + path14.sep));
|
|
10662
|
-
}
|
|
10663
|
-
/**
|
|
10664
|
-
* Serialize workspace state for storage in session
|
|
10665
|
-
*/
|
|
10666
|
-
toJSON() {
|
|
10667
|
-
return this.getWorkspaces();
|
|
10668
|
-
}
|
|
10669
|
-
/**
|
|
10670
|
-
* Create WorkspaceManager from serialized state
|
|
10671
|
-
*/
|
|
10672
|
-
static fromJSON(workspaces) {
|
|
10673
|
-
return new _WorkspaceManager(workspaces);
|
|
10674
|
-
}
|
|
10675
|
-
};
|
|
10676
|
-
|
|
10677
10209
|
// src/utils/apiUrl.ts
|
|
10678
10210
|
var BIKE4MIND_URL = "https://app.bike4mind.com";
|
|
10679
10211
|
function getApiUrl(configApiConfig) {
|
|
@@ -10690,8 +10222,8 @@ function getEnvironmentName(configApiConfig) {
|
|
|
10690
10222
|
}
|
|
10691
10223
|
|
|
10692
10224
|
// src/utils/contextLoader.ts
|
|
10693
|
-
import * as
|
|
10694
|
-
import * as
|
|
10225
|
+
import * as fs11 from "fs";
|
|
10226
|
+
import * as path13 from "path";
|
|
10695
10227
|
import { homedir as homedir4 } from "os";
|
|
10696
10228
|
var CONTEXT_FILE_SIZE_LIMIT = 100 * 1024;
|
|
10697
10229
|
var PROJECT_CONTEXT_FILES = [
|
|
@@ -10712,9 +10244,9 @@ function formatFileSize2(bytes) {
|
|
|
10712
10244
|
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
10713
10245
|
}
|
|
10714
10246
|
function tryReadContextFile(dir, filename, source) {
|
|
10715
|
-
const filePath =
|
|
10247
|
+
const filePath = path13.join(dir, filename);
|
|
10716
10248
|
try {
|
|
10717
|
-
const stats =
|
|
10249
|
+
const stats = fs11.lstatSync(filePath);
|
|
10718
10250
|
if (stats.isDirectory()) {
|
|
10719
10251
|
return null;
|
|
10720
10252
|
}
|
|
@@ -10728,7 +10260,7 @@ function tryReadContextFile(dir, filename, source) {
|
|
|
10728
10260
|
error: `${source === "global" ? "Global" : "Project"} ${filename} exceeds 100KB limit (${formatFileSize2(stats.size)})`
|
|
10729
10261
|
};
|
|
10730
10262
|
}
|
|
10731
|
-
const content =
|
|
10263
|
+
const content = fs11.readFileSync(filePath, "utf-8");
|
|
10732
10264
|
return {
|
|
10733
10265
|
filename,
|
|
10734
10266
|
content,
|
|
@@ -10780,7 +10312,7 @@ ${project.content}`;
|
|
|
10780
10312
|
}
|
|
10781
10313
|
async function loadContextFiles(projectDir) {
|
|
10782
10314
|
const errors = [];
|
|
10783
|
-
const globalDir =
|
|
10315
|
+
const globalDir = path13.join(homedir4(), ".bike4mind");
|
|
10784
10316
|
const projectDirectory = projectDir || process.cwd();
|
|
10785
10317
|
const [globalResult, projectResult] = await Promise.all([
|
|
10786
10318
|
Promise.resolve(findContextFile(globalDir, GLOBAL_CONTEXT_FILES, "global")),
|
|
@@ -10804,8 +10336,8 @@ async function loadContextFiles(projectDir) {
|
|
|
10804
10336
|
// ../../b4m-core/packages/mcp/dist/src/client.js
|
|
10805
10337
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
10806
10338
|
import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
10807
|
-
import
|
|
10808
|
-
import { existsSync as
|
|
10339
|
+
import path14 from "path";
|
|
10340
|
+
import { existsSync as existsSync8, readdirSync as readdirSync3 } from "fs";
|
|
10809
10341
|
var MCPClient = class {
|
|
10810
10342
|
// Note: This class handles MCP server communication with repository filtering
|
|
10811
10343
|
mcp;
|
|
@@ -10845,18 +10377,18 @@ var MCPClient = class {
|
|
|
10845
10377
|
const root = process.env.INIT_CWD || process.cwd();
|
|
10846
10378
|
const candidatePaths = [
|
|
10847
10379
|
// When running from SST Lambda with node_modules structure (copyFiles)
|
|
10848
|
-
|
|
10380
|
+
path14.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
|
|
10849
10381
|
// When running from SST Lambda deployed environment (/var/task)
|
|
10850
|
-
|
|
10382
|
+
path14.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
10851
10383
|
// When running from SST Lambda (.sst/artifacts/mcpHandler-dev), navigate to monorepo root (3 levels up)
|
|
10852
|
-
|
|
10384
|
+
path14.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
10853
10385
|
// When running from packages/client (Next.js app), navigate to monorepo root (2 levels up)
|
|
10854
|
-
|
|
10386
|
+
path14.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
10855
10387
|
// Original paths (backward compatibility)
|
|
10856
|
-
|
|
10857
|
-
|
|
10388
|
+
path14.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
10389
|
+
path14.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
|
|
10858
10390
|
];
|
|
10859
|
-
const serverScriptPath = candidatePaths.find((p) =>
|
|
10391
|
+
const serverScriptPath = candidatePaths.find((p) => existsSync8(p));
|
|
10860
10392
|
if (!serverScriptPath) {
|
|
10861
10393
|
const getDirectories = (source) => readdirSync3(source, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
10862
10394
|
console.error(`[MCP] Server script not found. Tried paths:`, candidatePaths);
|
|
@@ -11658,7 +11190,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
11658
11190
|
// package.json
|
|
11659
11191
|
var package_default = {
|
|
11660
11192
|
name: "@bike4mind/cli",
|
|
11661
|
-
version: "0.2.10-feat-
|
|
11193
|
+
version: "0.2.10-feat-explore-subagent-implementation.17246+5c3defa5d",
|
|
11662
11194
|
type: "module",
|
|
11663
11195
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
11664
11196
|
license: "UNLICENSED",
|
|
@@ -11726,7 +11258,6 @@ var package_default = {
|
|
|
11726
11258
|
diff: "^8.0.2",
|
|
11727
11259
|
dotenv: "^16.3.1",
|
|
11728
11260
|
"eventsource-parser": "^3.0.6",
|
|
11729
|
-
"fast-glob": "^3.3.3",
|
|
11730
11261
|
"file-type": "^18.7.0",
|
|
11731
11262
|
"fuse.js": "^7.1.0",
|
|
11732
11263
|
ink: "^6.5.1",
|
|
@@ -11762,10 +11293,10 @@ var package_default = {
|
|
|
11762
11293
|
},
|
|
11763
11294
|
devDependencies: {
|
|
11764
11295
|
"@bike4mind/agents": "0.1.0",
|
|
11765
|
-
"@bike4mind/common": "2.39.1-feat-
|
|
11766
|
-
"@bike4mind/mcp": "1.20.4-feat-
|
|
11767
|
-
"@bike4mind/services": "2.34.2-feat-
|
|
11768
|
-
"@bike4mind/utils": "2.1.4-feat-
|
|
11296
|
+
"@bike4mind/common": "2.39.1-feat-explore-subagent-implementation.17246+5c3defa5d",
|
|
11297
|
+
"@bike4mind/mcp": "1.20.4-feat-explore-subagent-implementation.17246+5c3defa5d",
|
|
11298
|
+
"@bike4mind/services": "2.34.2-feat-explore-subagent-implementation.17246+5c3defa5d",
|
|
11299
|
+
"@bike4mind/utils": "2.1.4-feat-explore-subagent-implementation.17246+5c3defa5d",
|
|
11769
11300
|
"@types/better-sqlite3": "^7.6.13",
|
|
11770
11301
|
"@types/diff": "^5.0.9",
|
|
11771
11302
|
"@types/jsonwebtoken": "^9.0.4",
|
|
@@ -11778,7 +11309,7 @@ var package_default = {
|
|
|
11778
11309
|
typescript: "^5.9.3",
|
|
11779
11310
|
vitest: "^3.2.4"
|
|
11780
11311
|
},
|
|
11781
|
-
gitHead: "
|
|
11312
|
+
gitHead: "5c3defa5da2bec9450d7b030dafc850c05863d68"
|
|
11782
11313
|
};
|
|
11783
11314
|
|
|
11784
11315
|
// src/config/constants.ts
|
|
@@ -11786,6 +11317,331 @@ var USAGE_DAYS = 30;
|
|
|
11786
11317
|
var MODEL_NAME_COLUMN_WIDTH = 18;
|
|
11787
11318
|
var USAGE_CACHE_TTL = 5 * 60 * 1e3;
|
|
11788
11319
|
|
|
11320
|
+
// src/agents/SubagentOrchestrator.ts
|
|
11321
|
+
var SubagentOrchestrator = class {
|
|
11322
|
+
constructor(deps) {
|
|
11323
|
+
this.deps = deps;
|
|
11324
|
+
}
|
|
11325
|
+
/**
|
|
11326
|
+
* Delegate a task to a specialized subagent
|
|
11327
|
+
*
|
|
11328
|
+
* @param options - Configuration for subagent execution
|
|
11329
|
+
* @returns Subagent result with summary
|
|
11330
|
+
*/
|
|
11331
|
+
async delegateToSubagent(options) {
|
|
11332
|
+
const { task, type, thoroughness, parentSessionId, config: configOverride } = options;
|
|
11333
|
+
const baseConfig = this.deps.subagentConfigs.get(type);
|
|
11334
|
+
if (!baseConfig) {
|
|
11335
|
+
throw new Error(`No configuration found for subagent type: ${type}`);
|
|
11336
|
+
}
|
|
11337
|
+
const config = {
|
|
11338
|
+
...baseConfig,
|
|
11339
|
+
...configOverride,
|
|
11340
|
+
type
|
|
11341
|
+
};
|
|
11342
|
+
const model = config.model || "claude-3-5-haiku-20241022";
|
|
11343
|
+
const maxIterations = this.getMaxIterations(config, thoroughness);
|
|
11344
|
+
const toolFilter = {
|
|
11345
|
+
allowedTools: config.allowedTools,
|
|
11346
|
+
deniedTools: config.deniedTools
|
|
11347
|
+
};
|
|
11348
|
+
const agentContext = {
|
|
11349
|
+
currentAgent: null,
|
|
11350
|
+
observationQueue: []
|
|
11351
|
+
};
|
|
11352
|
+
const { tools: tools2, agentContext: updatedContext } = generateCliTools(
|
|
11353
|
+
this.deps.userId,
|
|
11354
|
+
this.deps.llm,
|
|
11355
|
+
model,
|
|
11356
|
+
this.deps.permissionManager,
|
|
11357
|
+
this.deps.showPermissionPrompt,
|
|
11358
|
+
agentContext,
|
|
11359
|
+
this.deps.configStore,
|
|
11360
|
+
this.deps.apiClient,
|
|
11361
|
+
toolFilter
|
|
11362
|
+
);
|
|
11363
|
+
this.deps.logger.debug(`Spawning ${type} subagent with ${tools2.length} tools, thoroughness: ${thoroughness}`);
|
|
11364
|
+
const subagent = new ReActAgent({
|
|
11365
|
+
userId: this.deps.userId,
|
|
11366
|
+
logger: this.deps.logger,
|
|
11367
|
+
llm: this.deps.llm,
|
|
11368
|
+
model,
|
|
11369
|
+
tools: tools2,
|
|
11370
|
+
maxIterations,
|
|
11371
|
+
systemPrompt: config.systemPrompt || this.getDefaultSystemPrompt(type)
|
|
11372
|
+
});
|
|
11373
|
+
updatedContext.currentAgent = subagent;
|
|
11374
|
+
const startTime = Date.now();
|
|
11375
|
+
const result = await subagent.run(task, {
|
|
11376
|
+
maxIterations
|
|
11377
|
+
});
|
|
11378
|
+
const duration = Date.now() - startTime;
|
|
11379
|
+
this.deps.logger.debug(
|
|
11380
|
+
`Subagent completed in ${duration}ms, ${result.completionInfo.iterations} iterations, ${result.completionInfo.totalTokens} tokens`
|
|
11381
|
+
);
|
|
11382
|
+
const summary = this.summarizeResult(result, type);
|
|
11383
|
+
const subagentResult = {
|
|
11384
|
+
...result,
|
|
11385
|
+
subagentType: type,
|
|
11386
|
+
thoroughness,
|
|
11387
|
+
summary,
|
|
11388
|
+
parentSessionId
|
|
11389
|
+
};
|
|
11390
|
+
return subagentResult;
|
|
11391
|
+
}
|
|
11392
|
+
/**
|
|
11393
|
+
* Get max iterations based on thoroughness and config
|
|
11394
|
+
*/
|
|
11395
|
+
getMaxIterations(config, thoroughness) {
|
|
11396
|
+
const defaults = {
|
|
11397
|
+
quick: 2,
|
|
11398
|
+
medium: 5,
|
|
11399
|
+
very_thorough: 10
|
|
11400
|
+
};
|
|
11401
|
+
const configIterations = config.maxIterations || defaults;
|
|
11402
|
+
return configIterations[thoroughness];
|
|
11403
|
+
}
|
|
11404
|
+
/**
|
|
11405
|
+
* Get default system prompt for subagent type
|
|
11406
|
+
*/
|
|
11407
|
+
getDefaultSystemPrompt(type) {
|
|
11408
|
+
switch (type) {
|
|
11409
|
+
case "explore":
|
|
11410
|
+
return `You are a code exploration specialist. Your job is to search and analyze codebases efficiently.
|
|
11411
|
+
|
|
11412
|
+
Focus on:
|
|
11413
|
+
- Finding relevant files and functions
|
|
11414
|
+
- Understanding code structure and patterns
|
|
11415
|
+
- Providing clear, concise summaries
|
|
11416
|
+
|
|
11417
|
+
You have read-only access. Use file_read, grep_search, and glob_files to explore.
|
|
11418
|
+
|
|
11419
|
+
When you find what you're looking for, provide a clear summary including:
|
|
11420
|
+
1. What you found (files, functions, patterns)
|
|
11421
|
+
2. Key insights or observations
|
|
11422
|
+
3. Relevant code locations
|
|
11423
|
+
|
|
11424
|
+
Be thorough but concise. Your summary will be used by the main agent.`;
|
|
11425
|
+
case "plan":
|
|
11426
|
+
return `You are a task planning specialist. Your job is to break down complex tasks into clear, actionable steps.
|
|
11427
|
+
|
|
11428
|
+
Focus on:
|
|
11429
|
+
- Identifying dependencies and blockers
|
|
11430
|
+
- Creating logical sequence of steps
|
|
11431
|
+
- Estimating scope and priorities
|
|
11432
|
+
|
|
11433
|
+
Provide a structured plan that the main agent can execute.`;
|
|
11434
|
+
case "review":
|
|
11435
|
+
return `You are a code review specialist. Your job is to analyze code quality and identify issues.
|
|
11436
|
+
|
|
11437
|
+
Focus on:
|
|
11438
|
+
- Code quality and best practices
|
|
11439
|
+
- Potential bugs and edge cases
|
|
11440
|
+
- Performance and security considerations
|
|
11441
|
+
|
|
11442
|
+
Provide actionable feedback with specific file and line references.`;
|
|
11443
|
+
default:
|
|
11444
|
+
return "You are a helpful AI assistant.";
|
|
11445
|
+
}
|
|
11446
|
+
}
|
|
11447
|
+
/**
|
|
11448
|
+
* Summarize subagent result for parent agent
|
|
11449
|
+
*/
|
|
11450
|
+
summarizeResult(result, type) {
|
|
11451
|
+
const { finalAnswer, steps, completionInfo } = result;
|
|
11452
|
+
const toolCalls = steps.filter((s) => s.type === "action");
|
|
11453
|
+
const filesRead = toolCalls.filter((s) => s.metadata?.toolName === "file_read").length;
|
|
11454
|
+
const searches = toolCalls.filter((s) => s.metadata?.toolName === "grep_search").length;
|
|
11455
|
+
const globs = toolCalls.filter((s) => s.metadata?.toolName === "glob_files").length;
|
|
11456
|
+
let summary = `**${type.charAt(0).toUpperCase() + type.slice(1)} Subagent Results**
|
|
11457
|
+
|
|
11458
|
+
`;
|
|
11459
|
+
summary += `*Execution: ${completionInfo.iterations} iterations, ${completionInfo.toolCalls} tool calls*
|
|
11460
|
+
|
|
11461
|
+
`;
|
|
11462
|
+
if (type === "explore") {
|
|
11463
|
+
summary += `*Exploration: ${filesRead} files read, ${searches} searches, ${globs} glob patterns*
|
|
11464
|
+
|
|
11465
|
+
`;
|
|
11466
|
+
}
|
|
11467
|
+
const maxLength = 1e3;
|
|
11468
|
+
if (finalAnswer.length > maxLength) {
|
|
11469
|
+
summary += finalAnswer.slice(0, maxLength) + "\n\n...(truncated)";
|
|
11470
|
+
} else {
|
|
11471
|
+
summary += finalAnswer;
|
|
11472
|
+
}
|
|
11473
|
+
return summary;
|
|
11474
|
+
}
|
|
11475
|
+
};
|
|
11476
|
+
|
|
11477
|
+
// src/agents/configs.ts
|
|
11478
|
+
var EXPLORE_CONFIG = {
|
|
11479
|
+
type: "explore",
|
|
11480
|
+
model: "claude-3-5-haiku-20241022",
|
|
11481
|
+
systemPrompt: `You are a code exploration specialist. Your job is to search and analyze codebases efficiently.
|
|
11482
|
+
|
|
11483
|
+
Focus on:
|
|
11484
|
+
- Finding relevant files and functions
|
|
11485
|
+
- Understanding code structure and patterns
|
|
11486
|
+
- Providing clear, concise summaries
|
|
11487
|
+
|
|
11488
|
+
You have read-only access. Use file_read, grep_search, and glob_files to explore.
|
|
11489
|
+
|
|
11490
|
+
When you find what you're looking for, provide a clear summary including:
|
|
11491
|
+
1. What you found (files, functions, patterns)
|
|
11492
|
+
2. Key insights or observations
|
|
11493
|
+
3. Relevant code locations
|
|
11494
|
+
|
|
11495
|
+
Be thorough but concise. Your summary will be used by the main agent.`,
|
|
11496
|
+
allowedTools: [
|
|
11497
|
+
"file_read",
|
|
11498
|
+
"grep_search",
|
|
11499
|
+
"glob_files",
|
|
11500
|
+
"bash_execute",
|
|
11501
|
+
// Only for read-only commands like ls, cat, find
|
|
11502
|
+
"current_datetime",
|
|
11503
|
+
"math_evaluate"
|
|
11504
|
+
],
|
|
11505
|
+
deniedTools: [
|
|
11506
|
+
"create_file",
|
|
11507
|
+
"edit_file",
|
|
11508
|
+
"delete_file",
|
|
11509
|
+
"web_search",
|
|
11510
|
+
"weather_info",
|
|
11511
|
+
"blog_publish",
|
|
11512
|
+
"blog_edit",
|
|
11513
|
+
"blog_draft"
|
|
11514
|
+
],
|
|
11515
|
+
maxIterations: {
|
|
11516
|
+
quick: 2,
|
|
11517
|
+
medium: 5,
|
|
11518
|
+
very_thorough: 10
|
|
11519
|
+
},
|
|
11520
|
+
defaultThoroughness: "medium"
|
|
11521
|
+
};
|
|
11522
|
+
var PLAN_CONFIG = {
|
|
11523
|
+
type: "plan",
|
|
11524
|
+
model: "claude-3-5-haiku-20241022",
|
|
11525
|
+
systemPrompt: `You are a task planning specialist. Your job is to break down complex tasks into clear, actionable steps.
|
|
11526
|
+
|
|
11527
|
+
Focus on:
|
|
11528
|
+
- Identifying dependencies and blockers
|
|
11529
|
+
- Creating logical sequence of steps
|
|
11530
|
+
- Estimating scope and priorities
|
|
11531
|
+
|
|
11532
|
+
You can explore the codebase to understand the current architecture before planning.
|
|
11533
|
+
|
|
11534
|
+
Provide a structured plan that the main agent can execute.`,
|
|
11535
|
+
allowedTools: ["file_read", "grep_search", "glob_files", "bash_execute", "current_datetime", "math_evaluate"],
|
|
11536
|
+
deniedTools: ["create_file", "edit_file", "delete_file", "web_search", "weather_info"],
|
|
11537
|
+
maxIterations: {
|
|
11538
|
+
quick: 3,
|
|
11539
|
+
medium: 7,
|
|
11540
|
+
very_thorough: 12
|
|
11541
|
+
},
|
|
11542
|
+
defaultThoroughness: "medium"
|
|
11543
|
+
};
|
|
11544
|
+
var REVIEW_CONFIG = {
|
|
11545
|
+
type: "review",
|
|
11546
|
+
model: "claude-3-5-sonnet-20241022",
|
|
11547
|
+
// Use Sonnet for better reasoning
|
|
11548
|
+
systemPrompt: `You are a code review specialist. Your job is to analyze code quality and identify issues.
|
|
11549
|
+
|
|
11550
|
+
Focus on:
|
|
11551
|
+
- Code quality and best practices
|
|
11552
|
+
- Potential bugs and edge cases
|
|
11553
|
+
- Performance and security considerations
|
|
11554
|
+
|
|
11555
|
+
You have read-only access to analyze code.
|
|
11556
|
+
|
|
11557
|
+
Provide actionable feedback with specific file and line references.`,
|
|
11558
|
+
allowedTools: ["file_read", "grep_search", "glob_files", "bash_execute", "current_datetime"],
|
|
11559
|
+
deniedTools: ["create_file", "edit_file", "delete_file", "web_search", "weather_info"],
|
|
11560
|
+
maxIterations: {
|
|
11561
|
+
quick: 3,
|
|
11562
|
+
medium: 8,
|
|
11563
|
+
very_thorough: 15
|
|
11564
|
+
},
|
|
11565
|
+
defaultThoroughness: "medium"
|
|
11566
|
+
};
|
|
11567
|
+
function getDefaultSubagentConfigs() {
|
|
11568
|
+
return /* @__PURE__ */ new Map([
|
|
11569
|
+
["explore", EXPLORE_CONFIG],
|
|
11570
|
+
["plan", PLAN_CONFIG],
|
|
11571
|
+
["review", REVIEW_CONFIG]
|
|
11572
|
+
]);
|
|
11573
|
+
}
|
|
11574
|
+
|
|
11575
|
+
// src/agents/delegateTool.ts
|
|
11576
|
+
function createSubagentDelegateTool(orchestrator, parentSessionId) {
|
|
11577
|
+
return {
|
|
11578
|
+
toolFn: async (args) => {
|
|
11579
|
+
const params = args;
|
|
11580
|
+
if (!params.task) {
|
|
11581
|
+
throw new Error("subagent_delegate: task parameter is required");
|
|
11582
|
+
}
|
|
11583
|
+
if (!params.type) {
|
|
11584
|
+
throw new Error("subagent_delegate: type parameter is required");
|
|
11585
|
+
}
|
|
11586
|
+
const thoroughness = params.thoroughness || "medium";
|
|
11587
|
+
const type = params.type;
|
|
11588
|
+
const result = await orchestrator.delegateToSubagent({
|
|
11589
|
+
task: params.task,
|
|
11590
|
+
type,
|
|
11591
|
+
thoroughness,
|
|
11592
|
+
parentSessionId
|
|
11593
|
+
});
|
|
11594
|
+
return result.summary;
|
|
11595
|
+
},
|
|
11596
|
+
toolSchema: {
|
|
11597
|
+
name: "subagent_delegate",
|
|
11598
|
+
description: `Delegate a task to a specialized subagent for focused work.
|
|
11599
|
+
|
|
11600
|
+
**When to use this tool:**
|
|
11601
|
+
- **Explore**: When you need to search through the codebase, find files, or understand code structure (read-only)
|
|
11602
|
+
- **Plan**: When you need to break down a complex task into actionable steps
|
|
11603
|
+
- **Review**: When you need to analyze code quality and identify potential issues
|
|
11604
|
+
|
|
11605
|
+
**Benefits:**
|
|
11606
|
+
- Keeps main conversation focused and clean
|
|
11607
|
+
- Uses specialized prompts optimized for each task type
|
|
11608
|
+
- Faster execution with appropriate models (Haiku for explore/plan, Sonnet for review)
|
|
11609
|
+
|
|
11610
|
+
**Example uses:**
|
|
11611
|
+
- "Find all files that use the authentication system" \u2192 explore
|
|
11612
|
+
- "Search for components that handle user input" \u2192 explore
|
|
11613
|
+
- "Break down implementing a new feature into steps" \u2192 plan
|
|
11614
|
+
- "Review this module for potential bugs" \u2192 review`,
|
|
11615
|
+
parameters: {
|
|
11616
|
+
type: "object",
|
|
11617
|
+
properties: {
|
|
11618
|
+
task: {
|
|
11619
|
+
type: "string",
|
|
11620
|
+
description: "Clear description of what you want the subagent to do. Be specific about what you're looking for or what needs to be accomplished."
|
|
11621
|
+
},
|
|
11622
|
+
type: {
|
|
11623
|
+
type: "string",
|
|
11624
|
+
enum: ["explore", "plan", "review"],
|
|
11625
|
+
description: `Type of subagent to use:
|
|
11626
|
+
- explore: Read-only codebase exploration and search (fast, uses Haiku)
|
|
11627
|
+
- plan: Task breakdown and planning (uses Haiku)
|
|
11628
|
+
- review: Code quality analysis and review (uses Sonnet for better reasoning)`
|
|
11629
|
+
},
|
|
11630
|
+
thoroughness: {
|
|
11631
|
+
type: "string",
|
|
11632
|
+
enum: ["quick", "medium", "very_thorough"],
|
|
11633
|
+
description: `How thoroughly to execute:
|
|
11634
|
+
- quick: Fast lookup, 1-2 iterations
|
|
11635
|
+
- medium: Balanced exploration, 3-5 iterations (default)
|
|
11636
|
+
- very_thorough: Comprehensive analysis, 8-10 iterations`
|
|
11637
|
+
}
|
|
11638
|
+
},
|
|
11639
|
+
required: ["task", "type"]
|
|
11640
|
+
}
|
|
11641
|
+
}
|
|
11642
|
+
};
|
|
11643
|
+
}
|
|
11644
|
+
|
|
11789
11645
|
// src/index.tsx
|
|
11790
11646
|
process.removeAllListeners("warning");
|
|
11791
11647
|
process.on("warning", (warning) => {
|
|
@@ -11800,19 +11656,6 @@ var usageCache = null;
|
|
|
11800
11656
|
function CliApp() {
|
|
11801
11657
|
const { exit } = useApp();
|
|
11802
11658
|
const imageRenderer = new ImageRenderer();
|
|
11803
|
-
const getInitialWorkspaces = () => {
|
|
11804
|
-
const workspaces = [process.cwd()];
|
|
11805
|
-
const addDirsEnv = process.env.B4M_ADD_DIRS;
|
|
11806
|
-
if (addDirsEnv) {
|
|
11807
|
-
try {
|
|
11808
|
-
const additionalDirs = JSON.parse(addDirsEnv);
|
|
11809
|
-
workspaces.push(...additionalDirs);
|
|
11810
|
-
} catch (error) {
|
|
11811
|
-
logger.warn(`Failed to parse B4M_ADD_DIRS environment variable: ${error}`);
|
|
11812
|
-
}
|
|
11813
|
-
}
|
|
11814
|
-
return workspaces;
|
|
11815
|
-
};
|
|
11816
11659
|
const [state, setState] = useState8({
|
|
11817
11660
|
session: null,
|
|
11818
11661
|
sessionStore: new SessionStore(),
|
|
@@ -11825,7 +11668,6 @@ function CliApp() {
|
|
|
11825
11668
|
agent: null,
|
|
11826
11669
|
mcpManager: null,
|
|
11827
11670
|
permissionManager: null,
|
|
11828
|
-
workspaceManager: new WorkspaceManager(getInitialWorkspaces()),
|
|
11829
11671
|
permissionPrompt: null,
|
|
11830
11672
|
trustLocationSelector: null,
|
|
11831
11673
|
rewindSelector: null,
|
|
@@ -11930,8 +11772,7 @@ function CliApp() {
|
|
|
11930
11772
|
totalTokens: 0,
|
|
11931
11773
|
totalCost: 0,
|
|
11932
11774
|
toolCallCount: 0
|
|
11933
|
-
}
|
|
11934
|
-
workingDirectories: state.workspaceManager.getWorkspaces()
|
|
11775
|
+
}
|
|
11935
11776
|
};
|
|
11936
11777
|
setState((prev) => ({
|
|
11937
11778
|
...prev,
|
|
@@ -11976,8 +11817,7 @@ function CliApp() {
|
|
|
11976
11817
|
totalTokens: 0,
|
|
11977
11818
|
totalCost: 0,
|
|
11978
11819
|
toolCallCount: 0
|
|
11979
|
-
}
|
|
11980
|
-
workingDirectories: state.workspaceManager.getWorkspaces()
|
|
11820
|
+
}
|
|
11981
11821
|
};
|
|
11982
11822
|
await logger.initialize(newSession.id);
|
|
11983
11823
|
logger.setVerbose(process.env.B4M_VERBOSE === "1");
|
|
@@ -12024,7 +11864,6 @@ function CliApp() {
|
|
|
12024
11864
|
llm,
|
|
12025
11865
|
modelInfo.id,
|
|
12026
11866
|
permissionManager,
|
|
12027
|
-
state.workspaceManager,
|
|
12028
11867
|
promptFn,
|
|
12029
11868
|
agentContext,
|
|
12030
11869
|
state.configStore,
|
|
@@ -12040,7 +11879,34 @@ function CliApp() {
|
|
|
12040
11879
|
console.log(` \u{1F4E1} ${serverName}: ${count} tool(s)`);
|
|
12041
11880
|
});
|
|
12042
11881
|
}
|
|
12043
|
-
const
|
|
11882
|
+
const subagentConfigs = getDefaultSubagentConfigs();
|
|
11883
|
+
if (config.subagents) {
|
|
11884
|
+
if (config.subagents.explore) {
|
|
11885
|
+
const currentConfig = subagentConfigs.get("explore");
|
|
11886
|
+
subagentConfigs.set("explore", { ...currentConfig, ...config.subagents.explore });
|
|
11887
|
+
}
|
|
11888
|
+
if (config.subagents.plan) {
|
|
11889
|
+
const currentConfig = subagentConfigs.get("plan");
|
|
11890
|
+
subagentConfigs.set("plan", { ...currentConfig, ...config.subagents.plan });
|
|
11891
|
+
}
|
|
11892
|
+
if (config.subagents.review) {
|
|
11893
|
+
const currentConfig = subagentConfigs.get("review");
|
|
11894
|
+
subagentConfigs.set("review", { ...currentConfig, ...config.subagents.review });
|
|
11895
|
+
}
|
|
11896
|
+
}
|
|
11897
|
+
const orchestrator = new SubagentOrchestrator({
|
|
11898
|
+
userId: config.userId,
|
|
11899
|
+
llm,
|
|
11900
|
+
logger: silentLogger,
|
|
11901
|
+
permissionManager,
|
|
11902
|
+
showPermissionPrompt: promptFn,
|
|
11903
|
+
configStore: state.configStore,
|
|
11904
|
+
apiClient,
|
|
11905
|
+
subagentConfigs
|
|
11906
|
+
});
|
|
11907
|
+
const subagentDelegateTool = createSubagentDelegateTool(orchestrator, newSession.id);
|
|
11908
|
+
const allTools = [...b4mTools, ...mcpTools, subagentDelegateTool];
|
|
11909
|
+
console.log(`\u{1F916} Subagent delegation enabled (explore, plan, review)`);
|
|
12044
11910
|
const projectDir = state.configStore.getProjectConfigDir();
|
|
12045
11911
|
const contextResult = await loadContextFiles(projectDir);
|
|
12046
11912
|
if (contextResult.globalContext) {
|
|
@@ -12077,6 +11943,15 @@ FOR CODING TASKS:
|
|
|
12077
11943
|
- When searching for code: Use grep_search or glob_files to find relevant files
|
|
12078
11944
|
- Permission system will ask for approval before any file writes
|
|
12079
11945
|
|
|
11946
|
+
SUBAGENT DELEGATION:
|
|
11947
|
+
- You have access to specialized subagents via the subagent_delegate tool
|
|
11948
|
+
- Use subagents for focused exploration, planning, or review tasks:
|
|
11949
|
+
* explore: Fast read-only codebase search (e.g., "find all auth files", "locate API endpoints")
|
|
11950
|
+
* plan: Break down complex tasks into actionable steps
|
|
11951
|
+
* review: Analyze code quality and identify issues
|
|
11952
|
+
- Subagents keep the main conversation clean and run faster with optimized models
|
|
11953
|
+
- Delegate when you need to search extensively or analyze code structure
|
|
11954
|
+
|
|
12080
11955
|
FOR GENERAL TASKS:
|
|
12081
11956
|
- Use available tools to get information (weather, web search, calculations, etc.)
|
|
12082
11957
|
- When user asks follow-up questions, use conversation context to understand what they're referring to
|
|
@@ -12087,6 +11962,7 @@ EXAMPLES:
|
|
|
12087
11962
|
- "how about Japan?" \u2192 use weather tool for Japan (applying same question from context)
|
|
12088
11963
|
- "enhance README" \u2192 file_read \u2192 generate \u2192 create_file
|
|
12089
11964
|
- "what packages installed?" \u2192 glob_files "**/package.json" \u2192 file_read
|
|
11965
|
+
- "find all components using React hooks" \u2192 subagent_delegate(task="find all components using React hooks", type="explore")
|
|
12090
11966
|
|
|
12091
11967
|
Remember: Use context from previous messages to understand follow-up questions.${contextSection}`;
|
|
12092
11968
|
const maxIterations = config.preferences.maxIterations === null ? 999999 : config.preferences.maxIterations;
|
|
@@ -12365,11 +12241,6 @@ Tool Permissions:
|
|
|
12365
12241
|
/untrust <tool-name> - Remove tool from trusted list
|
|
12366
12242
|
/trusted - List all trusted tools
|
|
12367
12243
|
|
|
12368
|
-
Workspace Management:
|
|
12369
|
-
/directory show - Show all workspace directories
|
|
12370
|
-
/directory add <path> - Add directory to workspace
|
|
12371
|
-
/directory remove <path> - Remove directory from workspace
|
|
12372
|
-
|
|
12373
12244
|
Project Configuration:
|
|
12374
12245
|
/project-config - Show merged project configuration
|
|
12375
12246
|
`);
|
|
@@ -12411,24 +12282,7 @@ Project Configuration:
|
|
|
12411
12282
|
}
|
|
12412
12283
|
await logger.initialize(loadedSession.id);
|
|
12413
12284
|
logger.debug("=== Session Resumed ===");
|
|
12414
|
-
|
|
12415
|
-
try {
|
|
12416
|
-
const restoredWorkspaceManager = WorkspaceManager.fromJSON(loadedSession.workingDirectories);
|
|
12417
|
-
setState((prev) => ({
|
|
12418
|
-
...prev,
|
|
12419
|
-
session: loadedSession,
|
|
12420
|
-
workspaceManager: restoredWorkspaceManager
|
|
12421
|
-
}));
|
|
12422
|
-
console.log(
|
|
12423
|
-
`\u{1F4C1} Restored ${loadedSession.workingDirectories.length} workspace director${loadedSession.workingDirectories.length !== 1 ? "ies" : "y"}`
|
|
12424
|
-
);
|
|
12425
|
-
} catch (error) {
|
|
12426
|
-
logger.warn(`Failed to restore workspace directories from session: ${error}`);
|
|
12427
|
-
setState((prev) => ({ ...prev, session: loadedSession }));
|
|
12428
|
-
}
|
|
12429
|
-
} else {
|
|
12430
|
-
setState((prev) => ({ ...prev, session: loadedSession }));
|
|
12431
|
-
}
|
|
12285
|
+
setState((prev) => ({ ...prev, session: loadedSession }));
|
|
12432
12286
|
setStoreSession(loadedSession);
|
|
12433
12287
|
useCliStore.getState().clearAgentSteps();
|
|
12434
12288
|
useCliStore.getState().clearPendingMessages();
|
|
@@ -12624,74 +12478,6 @@ Project Configuration:
|
|
|
12624
12478
|
}
|
|
12625
12479
|
console.log("");
|
|
12626
12480
|
break;
|
|
12627
|
-
case "directory": {
|
|
12628
|
-
const subcommand = args[0];
|
|
12629
|
-
if (!subcommand || subcommand === "show") {
|
|
12630
|
-
const workspaces = state.workspaceManager.getWorkspaces();
|
|
12631
|
-
console.log("\n\u{1F4C1} Workspace Directories:\n");
|
|
12632
|
-
workspaces.forEach((dir, index) => {
|
|
12633
|
-
const label = index === 0 ? " (primary)" : "";
|
|
12634
|
-
console.log(` ${index + 1}. ${dir}${label}`);
|
|
12635
|
-
});
|
|
12636
|
-
console.log("");
|
|
12637
|
-
break;
|
|
12638
|
-
}
|
|
12639
|
-
if (subcommand === "add") {
|
|
12640
|
-
const dirPath = args[1];
|
|
12641
|
-
if (!dirPath) {
|
|
12642
|
-
console.log("Usage: /directory add <path>");
|
|
12643
|
-
console.log("Example: /directory add ../other-project");
|
|
12644
|
-
break;
|
|
12645
|
-
}
|
|
12646
|
-
try {
|
|
12647
|
-
state.workspaceManager.addWorkspace(dirPath);
|
|
12648
|
-
if (state.session) {
|
|
12649
|
-
const updatedSession = {
|
|
12650
|
-
...state.session,
|
|
12651
|
-
workingDirectories: state.workspaceManager.getWorkspaces()
|
|
12652
|
-
};
|
|
12653
|
-
setState((prev) => ({ ...prev, session: updatedSession }));
|
|
12654
|
-
await state.sessionStore.save(updatedSession);
|
|
12655
|
-
}
|
|
12656
|
-
console.log(`\u2705 Added directory to workspace: ${dirPath}`);
|
|
12657
|
-
console.log(`\u{1F4C1} Workspace now has ${state.workspaceManager.getWorkspaces().length} directories`);
|
|
12658
|
-
} catch (error) {
|
|
12659
|
-
console.log(`\u274C Failed to add directory: ${error.message}`);
|
|
12660
|
-
}
|
|
12661
|
-
break;
|
|
12662
|
-
}
|
|
12663
|
-
if (subcommand === "remove") {
|
|
12664
|
-
const dirPath = args[1];
|
|
12665
|
-
if (!dirPath) {
|
|
12666
|
-
console.log("Usage: /directory remove <path>");
|
|
12667
|
-
console.log("Example: /directory remove ../other-project");
|
|
12668
|
-
break;
|
|
12669
|
-
}
|
|
12670
|
-
try {
|
|
12671
|
-
state.workspaceManager.removeWorkspace(dirPath);
|
|
12672
|
-
if (state.session) {
|
|
12673
|
-
const updatedSession = {
|
|
12674
|
-
...state.session,
|
|
12675
|
-
workingDirectories: state.workspaceManager.getWorkspaces()
|
|
12676
|
-
};
|
|
12677
|
-
setState((prev) => ({ ...prev, session: updatedSession }));
|
|
12678
|
-
await state.sessionStore.save(updatedSession);
|
|
12679
|
-
}
|
|
12680
|
-
console.log(`\u2705 Removed directory from workspace: ${dirPath}`);
|
|
12681
|
-
console.log(
|
|
12682
|
-
`\u{1F4C1} Workspace now has ${state.workspaceManager.getWorkspaces().length} director${state.workspaceManager.getWorkspaces().length !== 1 ? "ies" : "y"}`
|
|
12683
|
-
);
|
|
12684
|
-
} catch (error) {
|
|
12685
|
-
console.log(`\u274C Failed to remove directory: ${error.message}`);
|
|
12686
|
-
}
|
|
12687
|
-
break;
|
|
12688
|
-
}
|
|
12689
|
-
console.log("Unknown subcommand. Usage:");
|
|
12690
|
-
console.log(" /directory show - Show all workspace directories");
|
|
12691
|
-
console.log(" /directory add <path> - Add directory to workspace");
|
|
12692
|
-
console.log(" /directory remove <path> - Remove directory from workspace");
|
|
12693
|
-
break;
|
|
12694
|
-
}
|
|
12695
12481
|
case "login":
|
|
12696
12482
|
setState((prev) => ({ ...prev, showLoginFlow: true }));
|
|
12697
12483
|
break;
|
|
@@ -12728,8 +12514,7 @@ Project Configuration:
|
|
|
12728
12514
|
totalTokens: 0,
|
|
12729
12515
|
totalCost: 0,
|
|
12730
12516
|
toolCallCount: 0
|
|
12731
|
-
}
|
|
12732
|
-
workingDirectories: state.workspaceManager.getWorkspaces()
|
|
12517
|
+
}
|
|
12733
12518
|
};
|
|
12734
12519
|
await logger.initialize(newSession.id);
|
|
12735
12520
|
logger.debug("=== New Session Started via /clear ===");
|