@bike4mind/cli 0.2.31-feat-dynamic-agent-creation.19590 → 0.2.31-feat-python-playground.19625
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/{artifactExtractor-IKIJZW6C.js → artifactExtractor-HSTGLMPL.js} +1 -1
- package/dist/{chunk-PV6PZFPC.js → chunk-2PXPXXDN.js} +50 -3
- package/dist/{chunk-ZS65NK25.js → chunk-32PKF3N7.js} +4 -8
- package/dist/{chunk-PLA2VBQW.js → chunk-EEGKRFVP.js} +27 -1
- package/dist/{chunk-DQHCE3TN.js → chunk-LAZAO77H.js} +2 -2
- package/dist/{chunk-S6AUIWWB.js → chunk-RTD3GUAP.js} +2 -2
- package/dist/{chunk-HNT6CPNA.js → chunk-S3S2FV2N.js} +6 -6
- package/dist/{chunk-4P27WV34.js → chunk-ZHXVJFCI.js} +2 -2
- package/dist/commands/doctorCommand.js +1 -1
- package/dist/commands/mcpCommand.js +1 -1
- package/dist/commands/updateCommand.js +1 -1
- package/dist/{create-PVJSCD5H.js → create-ICV35GRO.js} +3 -3
- package/dist/index.js +734 -396
- package/dist/{llmMarkdownGenerator-BMYWIXYB.js → llmMarkdownGenerator-6FOXI2T4.js} +1 -1
- package/dist/{markdownGenerator-H7BNB5KO.js → markdownGenerator-6RY7C5B7.js} +1 -1
- package/dist/{mementoService-RDPCVXWF.js → mementoService-OHL67X43.js} +3 -3
- package/dist/{src-IKK3N7TZ.js → src-O2DE2WAD.js} +7 -1
- package/dist/{src-DCF72PTU.js → src-ZR2BFWLN.js} +2 -2
- package/dist/{subtractCredits-K4PWRUF4.js → subtractCredits-OPURYRNV.js} +3 -3
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import "./chunk-GQGOWACU.js";
|
|
3
|
-
import "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
import "./chunk-ZHXVJFCI.js";
|
|
4
|
+
import "./chunk-RTD3GUAP.js";
|
|
5
5
|
import "./chunk-BPFEGDC7.js";
|
|
6
6
|
import "./chunk-BDQBOLYG.js";
|
|
7
7
|
import {
|
|
8
8
|
getEffectiveApiKey,
|
|
9
9
|
getOpenWeatherKey,
|
|
10
10
|
getSerperKey
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-LAZAO77H.js";
|
|
12
12
|
import {
|
|
13
13
|
ConfigStore,
|
|
14
14
|
logger
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-32PKF3N7.js";
|
|
16
16
|
import {
|
|
17
17
|
checkForUpdate,
|
|
18
18
|
package_default
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-S3S2FV2N.js";
|
|
20
20
|
import {
|
|
21
21
|
selectActiveBackgroundAgents,
|
|
22
22
|
useCliStore
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
OpenAIBackend,
|
|
33
33
|
OpenAIImageService,
|
|
34
34
|
XAIImageService
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-2PXPXXDN.js";
|
|
36
36
|
import {
|
|
37
37
|
AiEvents,
|
|
38
38
|
ApiKeyEvents,
|
|
@@ -90,7 +90,7 @@ import {
|
|
|
90
90
|
getMcpProviderMetadata,
|
|
91
91
|
getViewById,
|
|
92
92
|
resolveNavigationIntents
|
|
93
|
-
} from "./chunk-
|
|
93
|
+
} from "./chunk-EEGKRFVP.js";
|
|
94
94
|
import {
|
|
95
95
|
Logger
|
|
96
96
|
} from "./chunk-PFBYGCOW.js";
|
|
@@ -733,8 +733,25 @@ var COMMANDS = [
|
|
|
733
733
|
},
|
|
734
734
|
{
|
|
735
735
|
name: "rewind",
|
|
736
|
-
description: "Rewind conversation to a previous point"
|
|
737
|
-
|
|
736
|
+
description: "Rewind conversation to a previous point"
|
|
737
|
+
},
|
|
738
|
+
{
|
|
739
|
+
name: "undo",
|
|
740
|
+
description: "Undo the last file change"
|
|
741
|
+
},
|
|
742
|
+
{
|
|
743
|
+
name: "checkpoints",
|
|
744
|
+
description: "List available file restore points"
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
name: "restore",
|
|
748
|
+
description: "Restore files to a specific checkpoint",
|
|
749
|
+
args: "<number>"
|
|
750
|
+
},
|
|
751
|
+
{
|
|
752
|
+
name: "diff",
|
|
753
|
+
description: "Show diff between current state and a checkpoint",
|
|
754
|
+
args: "[number]"
|
|
738
755
|
},
|
|
739
756
|
{
|
|
740
757
|
name: "project-config",
|
|
@@ -1647,19 +1664,6 @@ function buildConfigItems(availableModels) {
|
|
|
1647
1664
|
exportFormat: value
|
|
1648
1665
|
}
|
|
1649
1666
|
})
|
|
1650
|
-
},
|
|
1651
|
-
{
|
|
1652
|
-
key: "enableDynamicAgentCreation",
|
|
1653
|
-
label: "Dynamic Agents",
|
|
1654
|
-
type: "boolean",
|
|
1655
|
-
getValue: (config) => config.preferences.enableDynamicAgentCreation ?? false,
|
|
1656
|
-
setValue: (config, value) => ({
|
|
1657
|
-
...config,
|
|
1658
|
-
preferences: {
|
|
1659
|
-
...config.preferences,
|
|
1660
|
-
enableDynamicAgentCreation: value
|
|
1661
|
-
}
|
|
1662
|
-
})
|
|
1663
1667
|
}
|
|
1664
1668
|
);
|
|
1665
1669
|
return items;
|
|
@@ -2851,9 +2855,346 @@ var CommandHistoryStore = class {
|
|
|
2851
2855
|
}
|
|
2852
2856
|
};
|
|
2853
2857
|
|
|
2854
|
-
// src/storage/
|
|
2855
|
-
import fs5 from "fs
|
|
2858
|
+
// src/storage/CheckpointStore.ts
|
|
2859
|
+
import { promises as fs5 } from "fs";
|
|
2860
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync, unlinkSync } from "fs";
|
|
2861
|
+
import { execFileSync } from "child_process";
|
|
2856
2862
|
import path6 from "path";
|
|
2863
|
+
var MAX_FILE_SIZE2 = 10 * 1024 * 1024;
|
|
2864
|
+
var DEFAULT_KEEP_COUNT = 50;
|
|
2865
|
+
var ABSENT_MARKER = ".b4m-absent";
|
|
2866
|
+
var CheckpointStore = class {
|
|
2867
|
+
constructor(projectDir) {
|
|
2868
|
+
this.metadata = null;
|
|
2869
|
+
this.sessionId = null;
|
|
2870
|
+
this.initialized = false;
|
|
2871
|
+
this.projectDir = projectDir;
|
|
2872
|
+
this.shadowRepoDir = path6.join(projectDir, ".b4m", "shadow-repo");
|
|
2873
|
+
this.metadataPath = path6.join(projectDir, ".b4m", "checkpoints.json");
|
|
2874
|
+
}
|
|
2875
|
+
/**
|
|
2876
|
+
* Initialize the shadow git repository and load metadata
|
|
2877
|
+
*/
|
|
2878
|
+
async init(sessionId) {
|
|
2879
|
+
this.sessionId = sessionId;
|
|
2880
|
+
await fs5.mkdir(path6.join(this.projectDir, ".b4m"), { recursive: true });
|
|
2881
|
+
if (!existsSync4(path6.join(this.shadowRepoDir, ".git"))) {
|
|
2882
|
+
await fs5.mkdir(this.shadowRepoDir, { recursive: true });
|
|
2883
|
+
this.git("init");
|
|
2884
|
+
this.git("config", "user.email", "checkpoint@b4m.local");
|
|
2885
|
+
this.git("config", "user.name", "B4M Checkpoint");
|
|
2886
|
+
this.git("commit", "--allow-empty", "-m", "checkpoint-init");
|
|
2887
|
+
}
|
|
2888
|
+
await this.loadMetadata();
|
|
2889
|
+
await this.ensureGitignore();
|
|
2890
|
+
await this.pruneCheckpoints(DEFAULT_KEEP_COUNT);
|
|
2891
|
+
this.initialized = true;
|
|
2892
|
+
}
|
|
2893
|
+
/**
|
|
2894
|
+
* Update session ID (e.g., on /clear or /resume)
|
|
2895
|
+
*/
|
|
2896
|
+
setSessionId(sessionId) {
|
|
2897
|
+
this.sessionId = sessionId;
|
|
2898
|
+
}
|
|
2899
|
+
/**
|
|
2900
|
+
* Create a checkpoint by snapshotting the current state of target files
|
|
2901
|
+
* before they are modified by a tool.
|
|
2902
|
+
*
|
|
2903
|
+
* @param toolName - The tool about to modify files
|
|
2904
|
+
* @param filePaths - Relative paths of files about to be modified
|
|
2905
|
+
* @param name - Optional human-readable checkpoint name
|
|
2906
|
+
*/
|
|
2907
|
+
async createCheckpoint(toolName, filePaths, name) {
|
|
2908
|
+
if (!this.initialized || !this.sessionId) {
|
|
2909
|
+
return null;
|
|
2910
|
+
}
|
|
2911
|
+
const checkpointName = name || `before-${toolName}-${path6.basename(filePaths[0] || "unknown")}`;
|
|
2912
|
+
try {
|
|
2913
|
+
let hasChanges = false;
|
|
2914
|
+
for (const filePath of filePaths) {
|
|
2915
|
+
const absolutePath = this.validatePathWithinProject(filePath);
|
|
2916
|
+
const shadowPath = path6.join(this.shadowRepoDir, filePath);
|
|
2917
|
+
const shadowDir = path6.dirname(shadowPath);
|
|
2918
|
+
const absentMarkerPath = path6.join(shadowDir, `${path6.basename(filePath)}${ABSENT_MARKER}`);
|
|
2919
|
+
await fs5.mkdir(shadowDir, { recursive: true });
|
|
2920
|
+
if (existsSync4(absolutePath)) {
|
|
2921
|
+
const stats = await fs5.lstat(absolutePath);
|
|
2922
|
+
if (stats.isSymbolicLink()) {
|
|
2923
|
+
continue;
|
|
2924
|
+
}
|
|
2925
|
+
if (stats.size > MAX_FILE_SIZE2) {
|
|
2926
|
+
continue;
|
|
2927
|
+
}
|
|
2928
|
+
if (existsSync4(absentMarkerPath)) {
|
|
2929
|
+
await fs5.unlink(absentMarkerPath);
|
|
2930
|
+
}
|
|
2931
|
+
await fs5.copyFile(absolutePath, shadowPath);
|
|
2932
|
+
hasChanges = true;
|
|
2933
|
+
} else {
|
|
2934
|
+
if (existsSync4(shadowPath)) {
|
|
2935
|
+
await fs5.unlink(shadowPath);
|
|
2936
|
+
}
|
|
2937
|
+
await fs5.writeFile(absentMarkerPath, "", "utf-8");
|
|
2938
|
+
hasChanges = true;
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
if (!hasChanges) {
|
|
2942
|
+
return null;
|
|
2943
|
+
}
|
|
2944
|
+
this.git("add", "-A");
|
|
2945
|
+
try {
|
|
2946
|
+
this.git("diff", "--cached", "--quiet");
|
|
2947
|
+
return null;
|
|
2948
|
+
} catch {
|
|
2949
|
+
}
|
|
2950
|
+
this.git("commit", "-m", checkpointName);
|
|
2951
|
+
const sha = this.git("rev-parse", "--short", "HEAD").trim();
|
|
2952
|
+
const checkpoint = {
|
|
2953
|
+
id: sha,
|
|
2954
|
+
name: checkpointName,
|
|
2955
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2956
|
+
toolName,
|
|
2957
|
+
filePaths: [...filePaths],
|
|
2958
|
+
sessionId: this.sessionId
|
|
2959
|
+
};
|
|
2960
|
+
if (!this.metadata) {
|
|
2961
|
+
this.metadata = { checkpoints: [], createdAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2962
|
+
}
|
|
2963
|
+
this.metadata.checkpoints.push(checkpoint);
|
|
2964
|
+
await this.saveMetadata();
|
|
2965
|
+
return checkpoint;
|
|
2966
|
+
} catch {
|
|
2967
|
+
return null;
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
/**
|
|
2971
|
+
* List checkpoints for the current session (most recent first)
|
|
2972
|
+
*/
|
|
2973
|
+
listCheckpoints() {
|
|
2974
|
+
if (!this.metadata || !this.sessionId) {
|
|
2975
|
+
return [];
|
|
2976
|
+
}
|
|
2977
|
+
return this.metadata.checkpoints.filter((cp) => cp.sessionId === this.sessionId).reverse();
|
|
2978
|
+
}
|
|
2979
|
+
/**
|
|
2980
|
+
* Get a specific checkpoint by 1-based index (1 = most recent)
|
|
2981
|
+
*/
|
|
2982
|
+
getCheckpoint(index) {
|
|
2983
|
+
const checkpoints = this.listCheckpoints();
|
|
2984
|
+
if (index < 1 || index > checkpoints.length) {
|
|
2985
|
+
return null;
|
|
2986
|
+
}
|
|
2987
|
+
return checkpoints[index - 1];
|
|
2988
|
+
}
|
|
2989
|
+
/**
|
|
2990
|
+
* Restore files to the state captured in a specific checkpoint
|
|
2991
|
+
*
|
|
2992
|
+
* @param index - 1-based index (1 = most recent)
|
|
2993
|
+
* @returns The checkpoint that was restored to
|
|
2994
|
+
*/
|
|
2995
|
+
async restoreCheckpoint(index) {
|
|
2996
|
+
const checkpoint = this.getCheckpoint(index);
|
|
2997
|
+
if (!checkpoint) {
|
|
2998
|
+
throw new Error(`Checkpoint #${index} not found. Use /checkpoints to see available restore points.`);
|
|
2999
|
+
}
|
|
3000
|
+
for (const filePath of checkpoint.filePaths) {
|
|
3001
|
+
const absolutePath = this.validatePathWithinProject(filePath);
|
|
3002
|
+
try {
|
|
3003
|
+
const content = this.git("show", `${checkpoint.id}:${filePath}`);
|
|
3004
|
+
await fs5.mkdir(path6.dirname(absolutePath), { recursive: true });
|
|
3005
|
+
await fs5.writeFile(absolutePath, content, "utf-8");
|
|
3006
|
+
} catch {
|
|
3007
|
+
try {
|
|
3008
|
+
this.git("show", `${checkpoint.id}:${path6.dirname(filePath)}/${path6.basename(filePath)}${ABSENT_MARKER}`);
|
|
3009
|
+
if (existsSync4(absolutePath)) {
|
|
3010
|
+
await fs5.unlink(absolutePath);
|
|
3011
|
+
}
|
|
3012
|
+
} catch {
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
return checkpoint;
|
|
3017
|
+
}
|
|
3018
|
+
/**
|
|
3019
|
+
* Undo the last file change (restore to most recent checkpoint)
|
|
3020
|
+
*/
|
|
3021
|
+
async undoLast() {
|
|
3022
|
+
return this.restoreCheckpoint(1);
|
|
3023
|
+
}
|
|
3024
|
+
/**
|
|
3025
|
+
* Get diff between current file state and a checkpoint
|
|
3026
|
+
*
|
|
3027
|
+
* @param index - 1-based index (1 = most recent, default)
|
|
3028
|
+
* @returns Unified diff string
|
|
3029
|
+
*/
|
|
3030
|
+
getCheckpointDiff(index = 1) {
|
|
3031
|
+
const checkpoint = this.getCheckpoint(index);
|
|
3032
|
+
if (!checkpoint) {
|
|
3033
|
+
throw new Error(`Checkpoint #${index} not found. Use /checkpoints to see available restore points.`);
|
|
3034
|
+
}
|
|
3035
|
+
const diffParts = [];
|
|
3036
|
+
for (const filePath of checkpoint.filePaths) {
|
|
3037
|
+
const absolutePath = this.validatePathWithinProject(filePath);
|
|
3038
|
+
const tmpCheckpoint = path6.join(this.shadowRepoDir, ".diff-a");
|
|
3039
|
+
const tmpCurrent = path6.join(this.shadowRepoDir, ".diff-b");
|
|
3040
|
+
try {
|
|
3041
|
+
let checkpointContent;
|
|
3042
|
+
try {
|
|
3043
|
+
checkpointContent = this.git("show", `${checkpoint.id}:${filePath}`);
|
|
3044
|
+
} catch {
|
|
3045
|
+
checkpointContent = "";
|
|
3046
|
+
}
|
|
3047
|
+
let currentContent = "";
|
|
3048
|
+
if (existsSync4(absolutePath)) {
|
|
3049
|
+
currentContent = readFileSync4(absolutePath, "utf-8");
|
|
3050
|
+
}
|
|
3051
|
+
if (checkpointContent === currentContent) {
|
|
3052
|
+
continue;
|
|
3053
|
+
}
|
|
3054
|
+
writeFileSync(tmpCheckpoint, checkpointContent, "utf-8");
|
|
3055
|
+
writeFileSync(tmpCurrent, currentContent, "utf-8");
|
|
3056
|
+
try {
|
|
3057
|
+
this.git(
|
|
3058
|
+
"diff",
|
|
3059
|
+
"--no-index",
|
|
3060
|
+
"--color",
|
|
3061
|
+
`--src-prefix=checkpoint:`,
|
|
3062
|
+
`--dst-prefix=current:`,
|
|
3063
|
+
tmpCheckpoint,
|
|
3064
|
+
tmpCurrent
|
|
3065
|
+
);
|
|
3066
|
+
} catch (diffError) {
|
|
3067
|
+
if (diffError && typeof diffError === "object" && "stdout" in diffError) {
|
|
3068
|
+
const output = diffError.stdout?.toString() || "";
|
|
3069
|
+
if (output) {
|
|
3070
|
+
diffParts.push(`--- ${filePath} (checkpoint #${index})
|
|
3071
|
+
+++ ${filePath} (current)
|
|
3072
|
+
${output}`);
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
} catch {
|
|
3077
|
+
} finally {
|
|
3078
|
+
try {
|
|
3079
|
+
unlinkSync(tmpCheckpoint);
|
|
3080
|
+
} catch {
|
|
3081
|
+
}
|
|
3082
|
+
try {
|
|
3083
|
+
unlinkSync(tmpCurrent);
|
|
3084
|
+
} catch {
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
return diffParts.join("\n");
|
|
3089
|
+
}
|
|
3090
|
+
/**
|
|
3091
|
+
* Prune old checkpoints beyond the keep count
|
|
3092
|
+
*/
|
|
3093
|
+
async pruneCheckpoints(keepCount = DEFAULT_KEEP_COUNT) {
|
|
3094
|
+
if (!this.metadata) return;
|
|
3095
|
+
const total = this.metadata.checkpoints.length;
|
|
3096
|
+
if (total <= keepCount) return;
|
|
3097
|
+
this.metadata.checkpoints = this.metadata.checkpoints.slice(-keepCount);
|
|
3098
|
+
await this.saveMetadata();
|
|
3099
|
+
try {
|
|
3100
|
+
this.git("gc", "--auto", "--quiet");
|
|
3101
|
+
} catch {
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
/**
|
|
3105
|
+
* Clean up the shadow repository entirely
|
|
3106
|
+
*/
|
|
3107
|
+
async cleanup() {
|
|
3108
|
+
try {
|
|
3109
|
+
await fs5.rm(this.shadowRepoDir, { recursive: true, force: true });
|
|
3110
|
+
if (existsSync4(this.metadataPath)) {
|
|
3111
|
+
await fs5.unlink(this.metadataPath);
|
|
3112
|
+
}
|
|
3113
|
+
this.metadata = null;
|
|
3114
|
+
this.initialized = false;
|
|
3115
|
+
} catch {
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
// --- Private helpers ---
|
|
3119
|
+
/**
|
|
3120
|
+
* Validate that a file path resolves within the project directory.
|
|
3121
|
+
* Prevents path traversal attacks (e.g., ../../etc/passwd).
|
|
3122
|
+
*/
|
|
3123
|
+
validatePathWithinProject(filePath) {
|
|
3124
|
+
const absolutePath = path6.resolve(this.projectDir, filePath);
|
|
3125
|
+
const normalizedProject = path6.resolve(this.projectDir) + path6.sep;
|
|
3126
|
+
if (!absolutePath.startsWith(normalizedProject) && absolutePath !== path6.resolve(this.projectDir)) {
|
|
3127
|
+
throw new Error(`Path traversal detected: ${filePath}`);
|
|
3128
|
+
}
|
|
3129
|
+
return absolutePath;
|
|
3130
|
+
}
|
|
3131
|
+
/**
|
|
3132
|
+
* Execute a git command in the shadow repo
|
|
3133
|
+
*/
|
|
3134
|
+
git(...args) {
|
|
3135
|
+
return execFileSync("git", args, {
|
|
3136
|
+
cwd: this.shadowRepoDir,
|
|
3137
|
+
encoding: "utf-8",
|
|
3138
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
3139
|
+
timeout: 1e4
|
|
3140
|
+
});
|
|
3141
|
+
}
|
|
3142
|
+
/**
|
|
3143
|
+
* Load checkpoint metadata from disk
|
|
3144
|
+
*/
|
|
3145
|
+
async loadMetadata() {
|
|
3146
|
+
try {
|
|
3147
|
+
if (existsSync4(this.metadataPath)) {
|
|
3148
|
+
const data = await fs5.readFile(this.metadataPath, "utf-8");
|
|
3149
|
+
this.metadata = JSON.parse(data);
|
|
3150
|
+
} else {
|
|
3151
|
+
this.metadata = {
|
|
3152
|
+
checkpoints: [],
|
|
3153
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3154
|
+
};
|
|
3155
|
+
}
|
|
3156
|
+
} catch {
|
|
3157
|
+
this.metadata = {
|
|
3158
|
+
checkpoints: [],
|
|
3159
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3160
|
+
};
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
/**
|
|
3164
|
+
* Save checkpoint metadata to disk
|
|
3165
|
+
*/
|
|
3166
|
+
async saveMetadata() {
|
|
3167
|
+
if (!this.metadata) return;
|
|
3168
|
+
await fs5.writeFile(this.metadataPath, JSON.stringify(this.metadata, null, 2), "utf-8");
|
|
3169
|
+
}
|
|
3170
|
+
/**
|
|
3171
|
+
* Ensure .b4m/ is in .gitignore
|
|
3172
|
+
*/
|
|
3173
|
+
async ensureGitignore() {
|
|
3174
|
+
const gitignorePath = path6.join(this.projectDir, ".gitignore");
|
|
3175
|
+
const entryToAdd = ".b4m/";
|
|
3176
|
+
try {
|
|
3177
|
+
let content = "";
|
|
3178
|
+
try {
|
|
3179
|
+
content = await fs5.readFile(gitignorePath, "utf-8");
|
|
3180
|
+
} catch {
|
|
3181
|
+
}
|
|
3182
|
+
if (content.includes(entryToAdd) || content.includes(".b4m")) {
|
|
3183
|
+
return;
|
|
3184
|
+
}
|
|
3185
|
+
const newContent = content.trim() + (content ? "\n" : "") + `
|
|
3186
|
+
# B4M checkpoint data
|
|
3187
|
+
${entryToAdd}
|
|
3188
|
+
`;
|
|
3189
|
+
await fs5.writeFile(gitignorePath, newContent, "utf-8");
|
|
3190
|
+
} catch {
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
};
|
|
3194
|
+
|
|
3195
|
+
// src/storage/CustomCommandStore.ts
|
|
3196
|
+
import fs6 from "fs/promises";
|
|
3197
|
+
import path7 from "path";
|
|
2857
3198
|
import os from "os";
|
|
2858
3199
|
|
|
2859
3200
|
// src/utils/commandParser.ts
|
|
@@ -3017,14 +3358,14 @@ var CustomCommandStore = class {
|
|
|
3017
3358
|
const home = os.homedir();
|
|
3018
3359
|
const root = projectRoot || process.cwd();
|
|
3019
3360
|
this.globalCommandsDirs = [
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3361
|
+
path7.join(home, ".bike4mind", "commands"),
|
|
3362
|
+
path7.join(home, ".claude", "commands"),
|
|
3363
|
+
path7.join(home, ".claude", "skills")
|
|
3023
3364
|
];
|
|
3024
3365
|
this.projectCommandsDirs = [
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3366
|
+
path7.join(root, ".bike4mind", "commands"),
|
|
3367
|
+
path7.join(root, ".claude", "commands"),
|
|
3368
|
+
path7.join(root, ".claude", "skills")
|
|
3028
3369
|
];
|
|
3029
3370
|
}
|
|
3030
3371
|
/**
|
|
@@ -3049,7 +3390,7 @@ var CustomCommandStore = class {
|
|
|
3049
3390
|
*/
|
|
3050
3391
|
async loadCommandsFromDirectory(directory, source) {
|
|
3051
3392
|
try {
|
|
3052
|
-
const stats = await
|
|
3393
|
+
const stats = await fs6.stat(directory);
|
|
3053
3394
|
if (!stats.isDirectory()) {
|
|
3054
3395
|
return;
|
|
3055
3396
|
}
|
|
@@ -3082,9 +3423,9 @@ var CustomCommandStore = class {
|
|
|
3082
3423
|
async findCommandFiles(directory) {
|
|
3083
3424
|
const files = [];
|
|
3084
3425
|
try {
|
|
3085
|
-
const entries = await
|
|
3426
|
+
const entries = await fs6.readdir(directory, { withFileTypes: true });
|
|
3086
3427
|
for (const entry of entries) {
|
|
3087
|
-
const fullPath =
|
|
3428
|
+
const fullPath = path7.join(directory, entry.name);
|
|
3088
3429
|
if (entry.isDirectory()) {
|
|
3089
3430
|
const subFiles = await this.findCommandFiles(fullPath);
|
|
3090
3431
|
files.push(...subFiles);
|
|
@@ -3104,14 +3445,14 @@ var CustomCommandStore = class {
|
|
|
3104
3445
|
* @param source - Source identifier ('global' or 'project')
|
|
3105
3446
|
*/
|
|
3106
3447
|
async loadCommandFile(filePath, source) {
|
|
3107
|
-
const filename =
|
|
3448
|
+
const filename = path7.basename(filePath);
|
|
3108
3449
|
const isSkillFile = filename.toLowerCase() === "skill.md";
|
|
3109
3450
|
const commandName = isSkillFile ? this.extractSkillName(filePath) : extractCommandName(filename);
|
|
3110
3451
|
if (!commandName) {
|
|
3111
3452
|
console.warn(`Invalid command filename: ${filename} (must end with .md and have valid name)`);
|
|
3112
3453
|
return;
|
|
3113
3454
|
}
|
|
3114
|
-
const fileContent = await
|
|
3455
|
+
const fileContent = await fs6.readFile(filePath, "utf-8");
|
|
3115
3456
|
const command = parseCommandFile(fileContent, filePath, commandName, source);
|
|
3116
3457
|
this.commands.set(commandName, command);
|
|
3117
3458
|
}
|
|
@@ -3123,7 +3464,7 @@ var CustomCommandStore = class {
|
|
|
3123
3464
|
* @returns Skill name or null if invalid
|
|
3124
3465
|
*/
|
|
3125
3466
|
extractSkillName(filePath) {
|
|
3126
|
-
const parentDir =
|
|
3467
|
+
const parentDir = path7.basename(path7.dirname(filePath));
|
|
3127
3468
|
return parentDir && parentDir !== "skills" ? parentDir : null;
|
|
3128
3469
|
}
|
|
3129
3470
|
/**
|
|
@@ -3185,15 +3526,15 @@ var CustomCommandStore = class {
|
|
|
3185
3526
|
*/
|
|
3186
3527
|
async createCommandFile(name, isGlobal = false) {
|
|
3187
3528
|
const targetDir = isGlobal ? this.globalCommandsDirs[0] : this.projectCommandsDirs[0];
|
|
3188
|
-
const filePath =
|
|
3189
|
-
const fileExists = await
|
|
3529
|
+
const filePath = path7.join(targetDir, `${name}.md`);
|
|
3530
|
+
const fileExists = await fs6.access(filePath).then(
|
|
3190
3531
|
() => true,
|
|
3191
3532
|
() => false
|
|
3192
3533
|
);
|
|
3193
3534
|
if (fileExists) {
|
|
3194
3535
|
throw new Error(`Command file already exists: ${filePath}`);
|
|
3195
3536
|
}
|
|
3196
|
-
await
|
|
3537
|
+
await fs6.mkdir(targetDir, { recursive: true });
|
|
3197
3538
|
const template = `---
|
|
3198
3539
|
description: ${name} command
|
|
3199
3540
|
argument-hint: [args]
|
|
@@ -3208,7 +3549,7 @@ You can use:
|
|
|
3208
3549
|
- $1, $2, etc. for positional arguments
|
|
3209
3550
|
- @filename for file references
|
|
3210
3551
|
`;
|
|
3211
|
-
await
|
|
3552
|
+
await fs6.writeFile(filePath, template, "utf-8");
|
|
3212
3553
|
return filePath;
|
|
3213
3554
|
}
|
|
3214
3555
|
};
|
|
@@ -3868,13 +4209,11 @@ var TOOL_CREATE_FILE = "create_file";
|
|
|
3868
4209
|
var TOOL_BASH_EXECUTE = "bash_execute";
|
|
3869
4210
|
var TOOL_SUBAGENT_DELEGATE = "subagent_delegate";
|
|
3870
4211
|
var TOOL_WRITE_TODOS = "write_todos";
|
|
3871
|
-
var TOOL_CREATE_DYNAMIC_AGENT = "create_dynamic_agent";
|
|
3872
4212
|
var EXPLORE_SUBAGENT_TYPE = "explore";
|
|
3873
4213
|
function buildCoreSystemPrompt(contextSection, config) {
|
|
3874
4214
|
let projectContextSection = "";
|
|
3875
4215
|
let agentDirectoryContext = "";
|
|
3876
4216
|
let skillsSection = "";
|
|
3877
|
-
let dynamicAgentSection = "";
|
|
3878
4217
|
if (typeof contextSection === "string") {
|
|
3879
4218
|
projectContextSection = contextSection;
|
|
3880
4219
|
if (config) {
|
|
@@ -3884,9 +4223,6 @@ function buildCoreSystemPrompt(contextSection, config) {
|
|
|
3884
4223
|
if (config.agentStore) {
|
|
3885
4224
|
agentDirectoryContext = config.agentStore.getDirectoryContext();
|
|
3886
4225
|
}
|
|
3887
|
-
if (config.enableDynamicAgentCreation) {
|
|
3888
|
-
dynamicAgentSection = buildDynamicAgentPromptSection();
|
|
3889
|
-
}
|
|
3890
4226
|
}
|
|
3891
4227
|
} else if (contextSection && typeof contextSection === "object") {
|
|
3892
4228
|
config = contextSection;
|
|
@@ -3905,9 +4241,6 @@ ${config.contextContent}`;
|
|
|
3905
4241
|
if (config.agentStore) {
|
|
3906
4242
|
agentDirectoryContext = config.agentStore.getDirectoryContext();
|
|
3907
4243
|
}
|
|
3908
|
-
if (config.enableDynamicAgentCreation) {
|
|
3909
|
-
dynamicAgentSection = buildDynamicAgentPromptSection();
|
|
3910
|
-
}
|
|
3911
4244
|
}
|
|
3912
4245
|
return `You are an autonomous AI assistant with access to tools. Your job is to help users by taking action and solving problems proactively.
|
|
3913
4246
|
|
|
@@ -3940,7 +4273,6 @@ SUBAGENT DELEGATION:
|
|
|
3940
4273
|
- You have access to specialized subagents via the ${TOOL_SUBAGENT_DELEGATE} tool
|
|
3941
4274
|
- ${agentDirectoryContext ? `${agentDirectoryContext}` : ""}
|
|
3942
4275
|
- Subagents keep the main conversation clean and run faster with optimized models
|
|
3943
|
-
${dynamicAgentSection}
|
|
3944
4276
|
|
|
3945
4277
|
CODE SEARCH BEST PRACTICES:
|
|
3946
4278
|
When searching code, follow this hierarchy for speed and efficiency:
|
|
@@ -3999,52 +4331,6 @@ EXAMPLES:
|
|
|
3999
4331
|
|
|
4000
4332
|
Remember: Use context from previous messages to understand follow-up questions.${projectContextSection}${skillsSection}`;
|
|
4001
4333
|
}
|
|
4002
|
-
function buildDynamicAgentPromptSection() {
|
|
4003
|
-
return `
|
|
4004
|
-
DYNAMIC AGENT CREATION:
|
|
4005
|
-
You have access to the '${TOOL_CREATE_DYNAMIC_AGENT}' tool, which lets you create and spawn one-off agents at runtime with custom system prompts.
|
|
4006
|
-
|
|
4007
|
-
**When to use '${TOOL_CREATE_DYNAMIC_AGENT}' instead of '${TOOL_SUBAGENT_DELEGATE}':**
|
|
4008
|
-
- When no pre-defined agent fits the task \u2014 you need custom instructions or a specialized role
|
|
4009
|
-
- When you want to give a sub-task a tailored system prompt (e.g., "You are a security auditor...")
|
|
4010
|
-
- When you need fine-grained control over which tools the spawned agent can access
|
|
4011
|
-
|
|
4012
|
-
**How to use it:**
|
|
4013
|
-
1. Provide a descriptive 'name' (e.g., "security-auditor", "migration-checker")
|
|
4014
|
-
2. Write a focused 'systemPrompt' that defines the agent's role and constraints
|
|
4015
|
-
3. Specify the 'task' \u2014 what the agent should accomplish
|
|
4016
|
-
4. Optionally restrict tools with 'allowedTools' or 'deniedTools'
|
|
4017
|
-
5. Use 'run_in_background: true' for long-running or parallel work
|
|
4018
|
-
|
|
4019
|
-
**Constraints:** Dynamic agents cannot spawn other agents (no recursion). They are ephemeral and not saved.
|
|
4020
|
-
|
|
4021
|
-
**Examples (simple \u2192 complex):**
|
|
4022
|
-
|
|
4023
|
-
1. **Security Auditor** \u2014 Review a file for vulnerabilities:
|
|
4024
|
-
name: "security-auditor"
|
|
4025
|
-
systemPrompt: "You are a security auditor. Review code for common vulnerabilities: injection attacks, hardcoded secrets, unsafe user input handling, and OWASP Top 10 issues. Report findings with severity (critical/high/medium/low), the affected line or pattern, and a recommended fix."
|
|
4026
|
-
task: "Audit src/auth/login.ts for security vulnerabilities"
|
|
4027
|
-
allowedTools: ["file_read", "grep_search"]
|
|
4028
|
-
|
|
4029
|
-
2. **Test Gap Analyzer** \u2014 Find untested modules:
|
|
4030
|
-
name: "test-gap-analyzer"
|
|
4031
|
-
systemPrompt: "You are a test coverage analyst. Compare source files against test files to identify modules that lack tests. For each gap, note the source file, its key exports/functions, and why it should be tested. Prioritize files with complex logic or external integrations."
|
|
4032
|
-
task: "Analyze apps/cli/src/agents/ and identify source files without corresponding test coverage"
|
|
4033
|
-
thoroughness: "very_thorough"
|
|
4034
|
-
|
|
4035
|
-
3. **Refactoring Planner** \u2014 Analyze and propose refactoring:
|
|
4036
|
-
name: "refactor-planner"
|
|
4037
|
-
systemPrompt: "You are a senior software architect specializing in code quality. Analyze the target module for code smells: long functions (>50 lines), deep nesting (>3 levels), god objects, duplicated logic, unclear naming, and tight coupling. Propose a concrete refactoring strategy with specific steps, expected benefits, and risk assessment for each change."
|
|
4038
|
-
task: "Analyze src/core/prompts.ts and propose a refactoring plan"
|
|
4039
|
-
thoroughness: "very_thorough"
|
|
4040
|
-
|
|
4041
|
-
4. **PR Description Writer** \u2014 Generate PR descriptions from diffs:
|
|
4042
|
-
name: "pr-writer"
|
|
4043
|
-
systemPrompt: "You are a technical writer who creates clear, structured pull request descriptions. Given a git diff or list of changes, produce: a concise summary (1-3 sentences), a bulleted list of specific changes grouped by category, and a testing checklist. Use imperative mood. Focus on the 'why' not just the 'what'."
|
|
4044
|
-
task: "Generate a PR description for the current branch changes"
|
|
4045
|
-
allowedTools: ["bash_execute", "file_read"]
|
|
4046
|
-
thoroughness: "quick"`;
|
|
4047
|
-
}
|
|
4048
4334
|
|
|
4049
4335
|
// ../../b4m-core/packages/services/dist/src/referService/generateCodes.js
|
|
4050
4336
|
import { randomBytes } from "crypto";
|
|
@@ -4495,7 +4781,7 @@ import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
|
4495
4781
|
import OpenAI2 from "openai";
|
|
4496
4782
|
|
|
4497
4783
|
// ../../b4m-core/packages/services/dist/src/importHistoryService/index.js
|
|
4498
|
-
import
|
|
4784
|
+
import fs7, { unlinkSync as unlinkSync2 } from "fs";
|
|
4499
4785
|
import yauzl from "yauzl";
|
|
4500
4786
|
import axios3 from "axios";
|
|
4501
4787
|
|
|
@@ -6186,8 +6472,8 @@ async function processAndStoreImages(images, context) {
|
|
|
6186
6472
|
const buffer = await downloadImage(image);
|
|
6187
6473
|
const fileType = await fileTypeFromBuffer2(buffer);
|
|
6188
6474
|
const filename = `${uuidv45()}.${fileType?.ext}`;
|
|
6189
|
-
const
|
|
6190
|
-
return
|
|
6475
|
+
const path21 = await context.imageGenerateStorage.upload(buffer, filename, {});
|
|
6476
|
+
return path21;
|
|
6191
6477
|
}));
|
|
6192
6478
|
}
|
|
6193
6479
|
async function updateQuestAndReturnMarkdown(storedImageUrls, context) {
|
|
@@ -7513,8 +7799,8 @@ async function processAndStoreImage(imageUrl, context) {
|
|
|
7513
7799
|
const buffer = await downloadImage2(imageUrl);
|
|
7514
7800
|
const fileType = await fileTypeFromBuffer3(buffer);
|
|
7515
7801
|
const filename = `${uuidv46()}.${fileType?.ext}`;
|
|
7516
|
-
const
|
|
7517
|
-
return
|
|
7802
|
+
const path21 = await context.imageGenerateStorage.upload(buffer, filename, {});
|
|
7803
|
+
return path21;
|
|
7518
7804
|
}
|
|
7519
7805
|
async function generateFullMask(imageBuffer) {
|
|
7520
7806
|
const sharp = (await import("sharp")).default;
|
|
@@ -9455,33 +9741,33 @@ var planetVisibilityTool = {
|
|
|
9455
9741
|
};
|
|
9456
9742
|
|
|
9457
9743
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/fileRead/index.js
|
|
9458
|
-
import { promises as
|
|
9459
|
-
import { existsSync as
|
|
9460
|
-
import
|
|
9461
|
-
var
|
|
9744
|
+
import { promises as fs8 } from "fs";
|
|
9745
|
+
import { existsSync as existsSync5, statSync as statSync4 } from "fs";
|
|
9746
|
+
import path8 from "path";
|
|
9747
|
+
var MAX_FILE_SIZE3 = 10 * 1024 * 1024;
|
|
9462
9748
|
async function readFileContent(params) {
|
|
9463
9749
|
const { path: filePath, encoding = "utf-8", offset = 0, limit } = params;
|
|
9464
|
-
const normalizedPath =
|
|
9465
|
-
const resolvedPath =
|
|
9466
|
-
const cwd =
|
|
9750
|
+
const normalizedPath = path8.normalize(filePath);
|
|
9751
|
+
const resolvedPath = path8.resolve(process.cwd(), normalizedPath);
|
|
9752
|
+
const cwd = path8.resolve(process.cwd());
|
|
9467
9753
|
if (!resolvedPath.startsWith(cwd)) {
|
|
9468
9754
|
throw new Error(`Access denied: Cannot read files outside of current working directory`);
|
|
9469
9755
|
}
|
|
9470
|
-
if (!
|
|
9756
|
+
if (!existsSync5(resolvedPath)) {
|
|
9471
9757
|
throw new Error(`File not found: ${filePath}`);
|
|
9472
9758
|
}
|
|
9473
9759
|
const stats = statSync4(resolvedPath);
|
|
9474
9760
|
if (stats.isDirectory()) {
|
|
9475
9761
|
throw new Error(`Path is a directory, not a file: ${filePath}`);
|
|
9476
9762
|
}
|
|
9477
|
-
if (stats.size >
|
|
9478
|
-
throw new Error(`File too large: ${(stats.size / 1024 / 1024).toFixed(2)}MB (max ${
|
|
9763
|
+
if (stats.size > MAX_FILE_SIZE3) {
|
|
9764
|
+
throw new Error(`File too large: ${(stats.size / 1024 / 1024).toFixed(2)}MB (max ${MAX_FILE_SIZE3 / 1024 / 1024}MB)`);
|
|
9479
9765
|
}
|
|
9480
9766
|
const isBinary = await checkIfBinary(resolvedPath);
|
|
9481
9767
|
if (isBinary && encoding === "utf-8") {
|
|
9482
9768
|
throw new Error(`File appears to be binary. Use encoding 'base64' to read binary files, or specify a different encoding.`);
|
|
9483
9769
|
}
|
|
9484
|
-
const content = await
|
|
9770
|
+
const content = await fs8.readFile(resolvedPath, encoding);
|
|
9485
9771
|
if (typeof content === "string") {
|
|
9486
9772
|
const lines = content.split("\n");
|
|
9487
9773
|
const totalLines = lines.length;
|
|
@@ -9519,7 +9805,7 @@ ${content}`;
|
|
|
9519
9805
|
}
|
|
9520
9806
|
async function checkIfBinary(filePath) {
|
|
9521
9807
|
const buffer = Buffer.alloc(8192);
|
|
9522
|
-
const fd = await
|
|
9808
|
+
const fd = await fs8.open(filePath, "r");
|
|
9523
9809
|
try {
|
|
9524
9810
|
const { bytesRead } = await fd.read(buffer, 0, 8192, 0);
|
|
9525
9811
|
const chunk = buffer.slice(0, bytesRead);
|
|
@@ -9536,7 +9822,7 @@ var fileReadTool = {
|
|
|
9536
9822
|
context.logger.info("\u{1F4C4} FileRead: Reading file", { path: params.path });
|
|
9537
9823
|
try {
|
|
9538
9824
|
const content = await readFileContent(params);
|
|
9539
|
-
const stats = statSync4(
|
|
9825
|
+
const stats = statSync4(path8.resolve(process.cwd(), path8.normalize(params.path)));
|
|
9540
9826
|
context.logger.info("\u2705 FileRead: Success", {
|
|
9541
9827
|
path: params.path,
|
|
9542
9828
|
size: stats.size,
|
|
@@ -9580,25 +9866,25 @@ var fileReadTool = {
|
|
|
9580
9866
|
};
|
|
9581
9867
|
|
|
9582
9868
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/createFile/index.js
|
|
9583
|
-
import { promises as
|
|
9584
|
-
import { existsSync as
|
|
9585
|
-
import
|
|
9869
|
+
import { promises as fs9 } from "fs";
|
|
9870
|
+
import { existsSync as existsSync6 } from "fs";
|
|
9871
|
+
import path9 from "path";
|
|
9586
9872
|
async function createFile(params) {
|
|
9587
9873
|
const { path: filePath, content, createDirectories = true } = params;
|
|
9588
|
-
const normalizedPath =
|
|
9589
|
-
const resolvedPath =
|
|
9590
|
-
const cwd =
|
|
9874
|
+
const normalizedPath = path9.normalize(filePath);
|
|
9875
|
+
const resolvedPath = path9.resolve(process.cwd(), normalizedPath);
|
|
9876
|
+
const cwd = path9.resolve(process.cwd());
|
|
9591
9877
|
if (!resolvedPath.startsWith(cwd)) {
|
|
9592
9878
|
throw new Error(`Access denied: Cannot create files outside of current working directory`);
|
|
9593
9879
|
}
|
|
9594
|
-
const fileExists =
|
|
9880
|
+
const fileExists = existsSync6(resolvedPath);
|
|
9595
9881
|
const action = fileExists ? "overwritten" : "created";
|
|
9596
9882
|
if (createDirectories) {
|
|
9597
|
-
const dir =
|
|
9598
|
-
await
|
|
9883
|
+
const dir = path9.dirname(resolvedPath);
|
|
9884
|
+
await fs9.mkdir(dir, { recursive: true });
|
|
9599
9885
|
}
|
|
9600
|
-
await
|
|
9601
|
-
const stats = await
|
|
9886
|
+
await fs9.writeFile(resolvedPath, content, "utf-8");
|
|
9887
|
+
const stats = await fs9.stat(resolvedPath);
|
|
9602
9888
|
const lines = content.split("\n").length;
|
|
9603
9889
|
return `File ${action} successfully: ${filePath}
|
|
9604
9890
|
Size: ${stats.size} bytes
|
|
@@ -9609,7 +9895,7 @@ var createFileTool = {
|
|
|
9609
9895
|
implementation: (context) => ({
|
|
9610
9896
|
toolFn: async (value) => {
|
|
9611
9897
|
const params = value;
|
|
9612
|
-
const fileExists =
|
|
9898
|
+
const fileExists = existsSync6(path9.resolve(process.cwd(), path9.normalize(params.path)));
|
|
9613
9899
|
context.logger.info(`\u{1F4DD} CreateFile: ${fileExists ? "Overwriting" : "Creating"} file`, {
|
|
9614
9900
|
path: params.path,
|
|
9615
9901
|
size: params.content.length
|
|
@@ -9651,7 +9937,7 @@ var createFileTool = {
|
|
|
9651
9937
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/globFiles/index.js
|
|
9652
9938
|
import { glob } from "glob";
|
|
9653
9939
|
import { stat } from "fs/promises";
|
|
9654
|
-
import
|
|
9940
|
+
import path10 from "path";
|
|
9655
9941
|
var DEFAULT_IGNORE_PATTERNS = [
|
|
9656
9942
|
"**/node_modules/**",
|
|
9657
9943
|
"**/.git/**",
|
|
@@ -9667,7 +9953,7 @@ var DEFAULT_IGNORE_PATTERNS = [
|
|
|
9667
9953
|
async function findFiles(params) {
|
|
9668
9954
|
const { pattern, dir_path, case_sensitive = true, respect_git_ignore = true } = params;
|
|
9669
9955
|
const baseCwd = process.cwd();
|
|
9670
|
-
const targetDir = dir_path ?
|
|
9956
|
+
const targetDir = dir_path ? path10.resolve(baseCwd, path10.normalize(dir_path)) : baseCwd;
|
|
9671
9957
|
if (!targetDir.startsWith(baseCwd)) {
|
|
9672
9958
|
throw new Error(`Access denied: Cannot search outside of current working directory`);
|
|
9673
9959
|
}
|
|
@@ -9707,7 +9993,7 @@ async function findFiles(params) {
|
|
|
9707
9993
|
const summary = `Found ${filesWithStats.length} file(s)${truncated ? ` (showing first ${MAX_RESULTS})` : ""} matching: ${pattern}`;
|
|
9708
9994
|
const dirInfo = dir_path ? `
|
|
9709
9995
|
Directory: ${dir_path}` : "";
|
|
9710
|
-
const filesList = results.map((file) =>
|
|
9996
|
+
const filesList = results.map((file) => path10.relative(baseCwd, file.path)).join("\n");
|
|
9711
9997
|
return `${summary}${dirInfo}
|
|
9712
9998
|
|
|
9713
9999
|
${filesList}`;
|
|
@@ -9761,27 +10047,48 @@ var globFilesTool = {
|
|
|
9761
10047
|
|
|
9762
10048
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/grepSearch/index.js
|
|
9763
10049
|
import { stat as stat2 } from "fs/promises";
|
|
9764
|
-
import
|
|
9765
|
-
import
|
|
10050
|
+
import { existsSync as existsSync7 } from "fs";
|
|
10051
|
+
import path11 from "path";
|
|
10052
|
+
import { execFile, execFileSync as execFileSync2 } from "child_process";
|
|
9766
10053
|
import { promisify } from "util";
|
|
9767
10054
|
import { createRequire } from "module";
|
|
9768
10055
|
var execFileAsync = promisify(execFile);
|
|
9769
10056
|
var require2 = createRequire(import.meta.url);
|
|
10057
|
+
var cachedRgPath = null;
|
|
9770
10058
|
function getRipgrepPath() {
|
|
10059
|
+
if (cachedRgPath)
|
|
10060
|
+
return cachedRgPath;
|
|
9771
10061
|
try {
|
|
9772
10062
|
const ripgrepPath = require2.resolve("@vscode/ripgrep");
|
|
9773
|
-
const ripgrepDir =
|
|
9774
|
-
const rgBinary =
|
|
10063
|
+
const ripgrepDir = path11.dirname(ripgrepPath);
|
|
10064
|
+
const rgBinary = path11.join(ripgrepDir, "..", "bin", "rg" + (process.platform === "win32" ? ".exe" : ""));
|
|
10065
|
+
if (!existsSync7(rgBinary)) {
|
|
10066
|
+
const postinstallScript = path11.join(ripgrepDir, "..", "lib", "postinstall.js");
|
|
10067
|
+
if (existsSync7(postinstallScript)) {
|
|
10068
|
+
execFileSync2(process.execPath, [postinstallScript], {
|
|
10069
|
+
cwd: path11.join(ripgrepDir, ".."),
|
|
10070
|
+
stdio: "pipe",
|
|
10071
|
+
timeout: 3e4
|
|
10072
|
+
});
|
|
10073
|
+
}
|
|
10074
|
+
if (!existsSync7(rgBinary)) {
|
|
10075
|
+
throw new Error(`ripgrep binary not found at ${rgBinary}. The postinstall download may have failed. Try running: cd ${path11.join(ripgrepDir, "..")} && node lib/postinstall.js`);
|
|
10076
|
+
}
|
|
10077
|
+
}
|
|
10078
|
+
cachedRgPath = rgBinary;
|
|
9775
10079
|
return rgBinary;
|
|
9776
10080
|
} catch (error) {
|
|
10081
|
+
if (error instanceof Error && error.message.includes("ripgrep binary not found")) {
|
|
10082
|
+
throw error;
|
|
10083
|
+
}
|
|
9777
10084
|
throw new Error("ripgrep is not available. Please install @vscode/ripgrep: pnpm add @vscode/ripgrep --filter @bike4mind/services");
|
|
9778
10085
|
}
|
|
9779
10086
|
}
|
|
9780
10087
|
function isPathWithinWorkspace(targetPath, baseCwd) {
|
|
9781
|
-
const resolvedTarget =
|
|
9782
|
-
const resolvedBase =
|
|
9783
|
-
const relativePath =
|
|
9784
|
-
return !relativePath.startsWith("..") && !
|
|
10088
|
+
const resolvedTarget = path11.resolve(targetPath);
|
|
10089
|
+
const resolvedBase = path11.resolve(baseCwd);
|
|
10090
|
+
const relativePath = path11.relative(resolvedBase, resolvedTarget);
|
|
10091
|
+
return !relativePath.startsWith("..") && !path11.isAbsolute(relativePath);
|
|
9785
10092
|
}
|
|
9786
10093
|
function convertGlobToRipgrepGlobs(globPattern) {
|
|
9787
10094
|
if (!globPattern || globPattern === "**/*") {
|
|
@@ -9797,7 +10104,7 @@ function convertGlobToRipgrepGlobs(globPattern) {
|
|
|
9797
10104
|
async function searchFiles2(params) {
|
|
9798
10105
|
const { pattern, dir_path, include } = params;
|
|
9799
10106
|
const baseCwd = process.cwd();
|
|
9800
|
-
const targetDir = dir_path ?
|
|
10107
|
+
const targetDir = dir_path ? path11.resolve(baseCwd, dir_path) : baseCwd;
|
|
9801
10108
|
if (!isPathWithinWorkspace(targetDir, baseCwd)) {
|
|
9802
10109
|
throw new Error(`Path validation failed: "${dir_path}" resolves outside the allowed workspace directory`);
|
|
9803
10110
|
}
|
|
@@ -9859,7 +10166,7 @@ async function searchFiles2(params) {
|
|
|
9859
10166
|
if (item.type === "match") {
|
|
9860
10167
|
const match = item;
|
|
9861
10168
|
allMatches.push({
|
|
9862
|
-
filePath:
|
|
10169
|
+
filePath: path11.relative(targetDir, match.data.path.text) || path11.basename(match.data.path.text),
|
|
9863
10170
|
lineNumber: match.data.line_number,
|
|
9864
10171
|
line: match.data.lines.text.trimEnd()
|
|
9865
10172
|
// Remove trailing newline
|
|
@@ -9952,18 +10259,18 @@ var grepSearchTool = {
|
|
|
9952
10259
|
};
|
|
9953
10260
|
|
|
9954
10261
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/deleteFile/index.js
|
|
9955
|
-
import { promises as
|
|
9956
|
-
import { existsSync as
|
|
9957
|
-
import
|
|
10262
|
+
import { promises as fs10 } from "fs";
|
|
10263
|
+
import { existsSync as existsSync8, statSync as statSync5 } from "fs";
|
|
10264
|
+
import path12 from "path";
|
|
9958
10265
|
async function deleteFile(params) {
|
|
9959
10266
|
const { path: filePath, recursive = false } = params;
|
|
9960
|
-
const normalizedPath =
|
|
9961
|
-
const resolvedPath =
|
|
9962
|
-
const cwd =
|
|
10267
|
+
const normalizedPath = path12.normalize(filePath);
|
|
10268
|
+
const resolvedPath = path12.resolve(process.cwd(), normalizedPath);
|
|
10269
|
+
const cwd = path12.resolve(process.cwd());
|
|
9963
10270
|
if (!resolvedPath.startsWith(cwd)) {
|
|
9964
10271
|
throw new Error(`Access denied: Cannot delete files outside of current working directory`);
|
|
9965
10272
|
}
|
|
9966
|
-
if (!
|
|
10273
|
+
if (!existsSync8(resolvedPath)) {
|
|
9967
10274
|
throw new Error(`File or directory not found: ${filePath}`);
|
|
9968
10275
|
}
|
|
9969
10276
|
const stats = statSync5(resolvedPath);
|
|
@@ -9973,10 +10280,10 @@ async function deleteFile(params) {
|
|
|
9973
10280
|
throw new Error(`Path is a directory: ${filePath}. Use recursive=true to delete directories and their contents.`);
|
|
9974
10281
|
}
|
|
9975
10282
|
if (isDirectory) {
|
|
9976
|
-
await
|
|
10283
|
+
await fs10.rm(resolvedPath, { recursive: true, force: true });
|
|
9977
10284
|
return `Directory deleted successfully: ${filePath}`;
|
|
9978
10285
|
} else {
|
|
9979
|
-
await
|
|
10286
|
+
await fs10.unlink(resolvedPath);
|
|
9980
10287
|
return `File deleted successfully: ${filePath}
|
|
9981
10288
|
Size: ${size} bytes`;
|
|
9982
10289
|
}
|
|
@@ -9986,8 +10293,8 @@ var deleteFileTool = {
|
|
|
9986
10293
|
implementation: (context) => ({
|
|
9987
10294
|
toolFn: async (value) => {
|
|
9988
10295
|
const params = value;
|
|
9989
|
-
const resolvedPath =
|
|
9990
|
-
const isDirectory =
|
|
10296
|
+
const resolvedPath = path12.resolve(process.cwd(), path12.normalize(params.path));
|
|
10297
|
+
const isDirectory = existsSync8(resolvedPath) && statSync5(resolvedPath).isDirectory();
|
|
9991
10298
|
context.logger.info(`\u{1F5D1}\uFE0F DeleteFile: Deleting ${isDirectory ? "directory" : "file"}`, {
|
|
9992
10299
|
path: params.path,
|
|
9993
10300
|
recursive: params.recursive
|
|
@@ -11564,6 +11871,7 @@ var solverMetadata = {
|
|
|
11564
11871
|
requires: "Backend compute",
|
|
11565
11872
|
available: false,
|
|
11566
11873
|
tagline: "Quantum-inspired classical simulation",
|
|
11874
|
+
externalUrl: "https://q.bike4mind.com/jobs/e499f34a-d40e-4cfb-98e5-47e0a2091f3d",
|
|
11567
11875
|
fullDescription: "A classical simulator of the Quantum Approximate Optimization Algorithm (QAOA). Encodes the job-shop scheduling problem as a QUBO (Quadratic Unconstrained Binary Optimization), then simulates the quantum variational circuit classically. Provides a preview of quantum advantage without requiring real quantum hardware.",
|
|
11568
11876
|
howItWorks: [
|
|
11569
11877
|
"Encode scheduling problem as QUBO matrix",
|
|
@@ -11609,6 +11917,7 @@ var solverMetadata = {
|
|
|
11609
11917
|
requires: "IonQ API key + credits",
|
|
11610
11918
|
available: false,
|
|
11611
11919
|
tagline: "Real quantum hardware optimization",
|
|
11920
|
+
externalUrl: "https://cloud.ionq.com/jobs/019c07ff-205a-76d0-9f83-24098ea09589",
|
|
11612
11921
|
fullDescription: "Runs the Quantum Approximate Optimization Algorithm on IonQ\u2019s trapped-ion quantum computers. This is real quantum computation \u2014 the problem is encoded as a QUBO, compiled to native quantum gates, and executed on physical qubits. Results include genuine quantum effects like superposition and entanglement.",
|
|
11613
11922
|
howItWorks: [
|
|
11614
11923
|
"Encode scheduling problem as QUBO matrix",
|
|
@@ -12666,7 +12975,7 @@ var navigateViewTool = {
|
|
|
12666
12975
|
|
|
12667
12976
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/bashExecute/index.js
|
|
12668
12977
|
import { spawn } from "child_process";
|
|
12669
|
-
import
|
|
12978
|
+
import path13 from "path";
|
|
12670
12979
|
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
12671
12980
|
var MAX_OUTPUT_SIZE = 100 * 1024;
|
|
12672
12981
|
var DANGEROUS_PATTERNS = [
|
|
@@ -12850,7 +13159,7 @@ async function executeBashCommand(params) {
|
|
|
12850
13159
|
};
|
|
12851
13160
|
}
|
|
12852
13161
|
const baseCwd = process.cwd();
|
|
12853
|
-
const targetCwd = relativeCwd ?
|
|
13162
|
+
const targetCwd = relativeCwd ? path13.resolve(baseCwd, relativeCwd) : baseCwd;
|
|
12854
13163
|
const effectiveTimeout = Math.min(timeout, 5 * 60 * 1e3);
|
|
12855
13164
|
return new Promise((resolve3) => {
|
|
12856
13165
|
let stdout = "";
|
|
@@ -13853,9 +14162,9 @@ function parseQuery(query) {
|
|
|
13853
14162
|
}
|
|
13854
14163
|
|
|
13855
14164
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/editLocalFile/index.js
|
|
13856
|
-
import { promises as
|
|
13857
|
-
import { existsSync as
|
|
13858
|
-
import
|
|
14165
|
+
import { promises as fs11 } from "fs";
|
|
14166
|
+
import { existsSync as existsSync9 } from "fs";
|
|
14167
|
+
import path14 from "path";
|
|
13859
14168
|
import { diffLines as diffLines3 } from "diff";
|
|
13860
14169
|
function generateDiff(original, modified) {
|
|
13861
14170
|
const differences = diffLines3(original, modified);
|
|
@@ -13879,16 +14188,16 @@ function generateDiff(original, modified) {
|
|
|
13879
14188
|
}
|
|
13880
14189
|
async function editLocalFile(params) {
|
|
13881
14190
|
const { path: filePath, old_string, new_string } = params;
|
|
13882
|
-
const normalizedPath =
|
|
13883
|
-
const resolvedPath =
|
|
13884
|
-
const cwd =
|
|
14191
|
+
const normalizedPath = path14.normalize(filePath);
|
|
14192
|
+
const resolvedPath = path14.resolve(process.cwd(), normalizedPath);
|
|
14193
|
+
const cwd = path14.resolve(process.cwd());
|
|
13885
14194
|
if (!resolvedPath.startsWith(cwd)) {
|
|
13886
14195
|
throw new Error(`Access denied: Cannot edit files outside of current working directory`);
|
|
13887
14196
|
}
|
|
13888
|
-
if (!
|
|
14197
|
+
if (!existsSync9(resolvedPath)) {
|
|
13889
14198
|
throw new Error(`File not found: ${filePath}`);
|
|
13890
14199
|
}
|
|
13891
|
-
const currentContent = await
|
|
14200
|
+
const currentContent = await fs11.readFile(resolvedPath, "utf-8");
|
|
13892
14201
|
if (!currentContent.includes(old_string)) {
|
|
13893
14202
|
const preview = old_string.length > 100 ? old_string.substring(0, 100) + "..." : old_string;
|
|
13894
14203
|
throw new Error(`String to replace not found in file. Make sure the old_string matches exactly (including whitespace and line endings). Searched for: "${preview}"`);
|
|
@@ -13898,7 +14207,7 @@ async function editLocalFile(params) {
|
|
|
13898
14207
|
throw new Error(`Found ${occurrences} occurrences of the string to replace. Please provide a more specific old_string that matches exactly one location.`);
|
|
13899
14208
|
}
|
|
13900
14209
|
const newContent = currentContent.replace(old_string, new_string);
|
|
13901
|
-
await
|
|
14210
|
+
await fs11.writeFile(resolvedPath, newContent, "utf-8");
|
|
13902
14211
|
const diffResult = generateDiff(old_string, new_string);
|
|
13903
14212
|
return `File edited successfully: ${filePath}
|
|
13904
14213
|
Changes: +${diffResult.additions} lines, -${diffResult.deletions} lines
|
|
@@ -14547,12 +14856,27 @@ var GithubManagerAgent = (config) => ({
|
|
|
14547
14856
|
exclusiveMcpServers: ["github"],
|
|
14548
14857
|
systemPrompt: `You are a GitHub specialist with access to GitHub's API. Your job is to help manage repositories, issues, pull requests, code search, branches, and CI/CD workflows.
|
|
14549
14858
|
|
|
14550
|
-
## First Step: Resolve Repository Context
|
|
14551
|
-
|
|
14859
|
+
## First Step: ALWAYS Resolve Repository Context Automatically
|
|
14860
|
+
Before doing ANYTHING else, call the \`github__current_user\` tool. The response includes:
|
|
14552
14861
|
- **selected_repositories**: The list of repositories the user has enabled for AI access (in \`owner/repo\` format)
|
|
14553
14862
|
- **user**: The authenticated user's profile (login, name, etc.)
|
|
14554
14863
|
|
|
14555
|
-
|
|
14864
|
+
### Repository Resolution Rules (CRITICAL)
|
|
14865
|
+
You MUST use the selected_repositories list to automatically resolve the owner and repo. NEVER ask the user for the org, owner, or full repository path. Instead:
|
|
14866
|
+
|
|
14867
|
+
1. **Exact match**: If the user says a repo name that exactly matches a repo name in selected_repositories, use it immediately.
|
|
14868
|
+
2. **Partial/fuzzy match**: If the user provides a partial name, match it against any repo in selected_repositories whose name contains that term (e.g., if the user says "lumina" and selected_repositories contains \`SomeOrg/lumina5\`, use that).
|
|
14869
|
+
3. **Single repo shortcut**: If only one repository is selected, ALWAYS default to it \u2014 no questions asked.
|
|
14870
|
+
4. **Multiple matches**: Only if multiple selected repos match the user's term AND you truly cannot disambiguate, list the matching repos and ask which one. Do NOT list repos that don't match.
|
|
14871
|
+
5. **No match**: If nothing in selected_repositories matches, tell the user which repos are available and ask them to clarify.
|
|
14872
|
+
|
|
14873
|
+
**NEVER ask the user to "paste the GitHub URL" or provide the org/owner name.** You already have this information from \`github__current_user\`. The owner and repo are embedded in the \`owner/repo\` format of each selected repository entry.
|
|
14874
|
+
|
|
14875
|
+
### Defaults for Ambiguous Requests
|
|
14876
|
+
- **Timezone**: Default to UTC unless the user specifies otherwise.
|
|
14877
|
+
- **PR/Issue state**: Default to \`open\` unless the user specifies otherwise.
|
|
14878
|
+
- **Scope**: All matching repos unless the user narrows it down.
|
|
14879
|
+
- **When in doubt, act**: Prefer making reasonable assumptions and proceeding over asking clarifying questions. You can always note your assumptions in the response.
|
|
14556
14880
|
|
|
14557
14881
|
## Capabilities
|
|
14558
14882
|
|
|
@@ -14593,7 +14917,7 @@ Use this to resolve the owner and repo name when the user does not specify them.
|
|
|
14593
14917
|
## Output Format
|
|
14594
14918
|
Provide a clear summary of actions taken:
|
|
14595
14919
|
1. What was done (created, searched, merged, etc.)
|
|
14596
|
-
2.
|
|
14920
|
+
2. Always provide links to relevant items (e.g., owner/repo#123, PR URLs)
|
|
14597
14921
|
3. Any issues or warnings encountered
|
|
14598
14922
|
|
|
14599
14923
|
Be precise with repository names, issue numbers, and PR numbers. Your results will be used by the main agent.`
|
|
@@ -15288,10 +15612,10 @@ var ToolErrorType;
|
|
|
15288
15612
|
// src/utils/diffPreview.ts
|
|
15289
15613
|
import * as Diff from "diff";
|
|
15290
15614
|
import { readFile } from "fs/promises";
|
|
15291
|
-
import { existsSync as
|
|
15615
|
+
import { existsSync as existsSync10 } from "fs";
|
|
15292
15616
|
async function generateFileDiffPreview(args) {
|
|
15293
15617
|
try {
|
|
15294
|
-
if (!
|
|
15618
|
+
if (!existsSync10(args.path)) {
|
|
15295
15619
|
const lines2 = args.content.split("\n");
|
|
15296
15620
|
const preview = lines2.slice(0, 20).join("\n");
|
|
15297
15621
|
const hasMore = lines2.length > 20;
|
|
@@ -15329,10 +15653,10 @@ ${diffLines4.join("\n")}`;
|
|
|
15329
15653
|
}
|
|
15330
15654
|
async function generateFileDeletePreview(args) {
|
|
15331
15655
|
try {
|
|
15332
|
-
if (!
|
|
15656
|
+
if (!existsSync10(args.path)) {
|
|
15333
15657
|
return `[File does not exist: ${args.path}]`;
|
|
15334
15658
|
}
|
|
15335
|
-
const stats = await import("fs/promises").then((
|
|
15659
|
+
const stats = await import("fs/promises").then((fs15) => fs15.stat(args.path));
|
|
15336
15660
|
return `[File will be deleted]
|
|
15337
15661
|
|
|
15338
15662
|
Path: ${args.path}
|
|
@@ -15631,25 +15955,26 @@ var DEFAULT_AGENT_MODEL = "claude-3-5-haiku-20241022";
|
|
|
15631
15955
|
var DEFAULT_THOROUGHNESS = "medium";
|
|
15632
15956
|
|
|
15633
15957
|
// src/utils/toolsAdapter.ts
|
|
15958
|
+
import path15 from "path";
|
|
15634
15959
|
var NoOpStorage = class extends BaseStorage {
|
|
15635
15960
|
async upload(input, destination, options) {
|
|
15636
15961
|
return `/tmp/${destination}`;
|
|
15637
15962
|
}
|
|
15638
|
-
async download(
|
|
15963
|
+
async download(path21) {
|
|
15639
15964
|
throw new Error("Download not supported in CLI");
|
|
15640
15965
|
}
|
|
15641
|
-
async delete(
|
|
15966
|
+
async delete(path21) {
|
|
15642
15967
|
}
|
|
15643
|
-
async getSignedUrl(
|
|
15644
|
-
return `/tmp/${
|
|
15968
|
+
async getSignedUrl(path21) {
|
|
15969
|
+
return `/tmp/${path21}`;
|
|
15645
15970
|
}
|
|
15646
|
-
getPublicUrl(
|
|
15647
|
-
return `/tmp/${
|
|
15971
|
+
getPublicUrl(path21) {
|
|
15972
|
+
return `/tmp/${path21}`;
|
|
15648
15973
|
}
|
|
15649
|
-
async getPreview(
|
|
15650
|
-
return `/tmp/${
|
|
15974
|
+
async getPreview(path21) {
|
|
15975
|
+
return `/tmp/${path21}`;
|
|
15651
15976
|
}
|
|
15652
|
-
async getMetadata(
|
|
15977
|
+
async getMetadata(path21) {
|
|
15653
15978
|
return { size: 0, contentType: "application/octet-stream" };
|
|
15654
15979
|
}
|
|
15655
15980
|
};
|
|
@@ -15822,6 +16147,27 @@ function wrapToolWithHooks(tool, hooks, hookContext) {
|
|
|
15822
16147
|
}
|
|
15823
16148
|
};
|
|
15824
16149
|
}
|
|
16150
|
+
var CHECKPOINT_TOOLS = /* @__PURE__ */ new Set(["create_file", "edit_local_file", "delete_file"]);
|
|
16151
|
+
function wrapToolWithCheckpointing(tool, checkpointStore) {
|
|
16152
|
+
if (!checkpointStore || !CHECKPOINT_TOOLS.has(tool.toolSchema.name)) {
|
|
16153
|
+
return tool;
|
|
16154
|
+
}
|
|
16155
|
+
const originalFn = tool.toolFn;
|
|
16156
|
+
const toolName = tool.toolSchema.name;
|
|
16157
|
+
return {
|
|
16158
|
+
...tool,
|
|
16159
|
+
toolFn: async (args) => {
|
|
16160
|
+
const filePath = args?.path;
|
|
16161
|
+
if (filePath) {
|
|
16162
|
+
try {
|
|
16163
|
+
await checkpointStore.createCheckpoint(toolName, [filePath], `before-${toolName}-${path15.basename(filePath)}`);
|
|
16164
|
+
} catch {
|
|
16165
|
+
}
|
|
16166
|
+
}
|
|
16167
|
+
return originalFn(args);
|
|
16168
|
+
}
|
|
16169
|
+
};
|
|
16170
|
+
}
|
|
15825
16171
|
var TOOL_NAME_MAPPING = {
|
|
15826
16172
|
// Claude Code -> B4M
|
|
15827
16173
|
read: "file_read",
|
|
@@ -15846,7 +16192,7 @@ function normalizeToolName(toolName) {
|
|
|
15846
16192
|
}
|
|
15847
16193
|
return TOOL_NAME_MAPPING[toolName] || toolName;
|
|
15848
16194
|
}
|
|
15849
|
-
function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient, toolFilter) {
|
|
16195
|
+
function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient, toolFilter, checkpointStore) {
|
|
15850
16196
|
const logger2 = new CliLogger();
|
|
15851
16197
|
const storage = new NoOpStorage();
|
|
15852
16198
|
const user = {
|
|
@@ -15905,9 +16251,17 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
15905
16251
|
// imageProcessorLambdaName (not needed for CLI)
|
|
15906
16252
|
tools_to_generate
|
|
15907
16253
|
);
|
|
15908
|
-
let tools = Object.entries(toolsMap).map(
|
|
15909
|
-
|
|
15910
|
-
|
|
16254
|
+
let tools = Object.entries(toolsMap).map(([_, tool]) => {
|
|
16255
|
+
const permissionWrapped = wrapToolWithPermission(
|
|
16256
|
+
tool,
|
|
16257
|
+
permissionManager,
|
|
16258
|
+
showPermissionPrompt,
|
|
16259
|
+
agentContext,
|
|
16260
|
+
configStore,
|
|
16261
|
+
apiClient
|
|
16262
|
+
);
|
|
16263
|
+
return wrapToolWithCheckpointing(permissionWrapped, checkpointStore ?? null);
|
|
16264
|
+
});
|
|
15911
16265
|
if (toolFilter) {
|
|
15912
16266
|
const { allowedTools, deniedTools } = toolFilter;
|
|
15913
16267
|
const normalizedAllowed = allowedTools?.map(normalizeToolName);
|
|
@@ -16085,8 +16439,8 @@ function getEnvironmentName(configApiConfig) {
|
|
|
16085
16439
|
}
|
|
16086
16440
|
|
|
16087
16441
|
// src/utils/contextLoader.ts
|
|
16088
|
-
import * as
|
|
16089
|
-
import * as
|
|
16442
|
+
import * as fs12 from "fs";
|
|
16443
|
+
import * as path16 from "path";
|
|
16090
16444
|
import { homedir as homedir3 } from "os";
|
|
16091
16445
|
var CONTEXT_FILE_SIZE_LIMIT = 100 * 1024;
|
|
16092
16446
|
var PROJECT_CONTEXT_FILES = [
|
|
@@ -16107,9 +16461,9 @@ function formatFileSize2(bytes) {
|
|
|
16107
16461
|
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
16108
16462
|
}
|
|
16109
16463
|
function tryReadContextFile(dir, filename, source) {
|
|
16110
|
-
const filePath =
|
|
16464
|
+
const filePath = path16.join(dir, filename);
|
|
16111
16465
|
try {
|
|
16112
|
-
const stats =
|
|
16466
|
+
const stats = fs12.lstatSync(filePath);
|
|
16113
16467
|
if (stats.isDirectory()) {
|
|
16114
16468
|
return null;
|
|
16115
16469
|
}
|
|
@@ -16123,7 +16477,7 @@ function tryReadContextFile(dir, filename, source) {
|
|
|
16123
16477
|
error: `${source === "global" ? "Global" : "Project"} ${filename} exceeds 100KB limit (${formatFileSize2(stats.size)})`
|
|
16124
16478
|
};
|
|
16125
16479
|
}
|
|
16126
|
-
const content =
|
|
16480
|
+
const content = fs12.readFileSync(filePath, "utf-8");
|
|
16127
16481
|
return {
|
|
16128
16482
|
filename,
|
|
16129
16483
|
content,
|
|
@@ -16175,7 +16529,7 @@ ${project.content}`;
|
|
|
16175
16529
|
}
|
|
16176
16530
|
async function loadContextFiles(projectDir) {
|
|
16177
16531
|
const errors = [];
|
|
16178
|
-
const globalDir =
|
|
16532
|
+
const globalDir = path16.join(homedir3(), ".bike4mind");
|
|
16179
16533
|
const projectDirectory = projectDir || process.cwd();
|
|
16180
16534
|
const [globalResult, projectResult] = await Promise.all([
|
|
16181
16535
|
Promise.resolve(findContextFile(globalDir, GLOBAL_CONTEXT_FILES, "global")),
|
|
@@ -16446,8 +16800,8 @@ function substituteArguments(template, args) {
|
|
|
16446
16800
|
// ../../b4m-core/packages/mcp/dist/src/client.js
|
|
16447
16801
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
16448
16802
|
import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
16449
|
-
import
|
|
16450
|
-
import { existsSync as
|
|
16803
|
+
import path17 from "path";
|
|
16804
|
+
import { existsSync as existsSync11, readdirSync as readdirSync3 } from "fs";
|
|
16451
16805
|
var MCPClient = class {
|
|
16452
16806
|
// Note: This class handles MCP server communication with repository filtering
|
|
16453
16807
|
mcp;
|
|
@@ -16488,18 +16842,18 @@ var MCPClient = class {
|
|
|
16488
16842
|
const root = process.env.INIT_CWD || process.cwd();
|
|
16489
16843
|
const candidatePaths = [
|
|
16490
16844
|
// When running from SST Lambda with node_modules structure (copyFiles)
|
|
16491
|
-
|
|
16845
|
+
path17.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
|
|
16492
16846
|
// When running from SST Lambda deployed environment (/var/task)
|
|
16493
|
-
|
|
16847
|
+
path17.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
16494
16848
|
// When running from SST Lambda (.sst/artifacts/mcpHandler-dev), navigate to monorepo root (3 levels up)
|
|
16495
|
-
|
|
16849
|
+
path17.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
16496
16850
|
// When running from packages/client (Next.js app), navigate to monorepo root (2 levels up)
|
|
16497
|
-
|
|
16851
|
+
path17.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
16498
16852
|
// Original paths (backward compatibility)
|
|
16499
|
-
|
|
16500
|
-
|
|
16853
|
+
path17.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
16854
|
+
path17.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
|
|
16501
16855
|
];
|
|
16502
|
-
const serverScriptPath = candidatePaths.find((p) =>
|
|
16856
|
+
const serverScriptPath = candidatePaths.find((p) => existsSync11(p));
|
|
16503
16857
|
if (!serverScriptPath) {
|
|
16504
16858
|
const getDirectories = (source) => readdirSync3(source, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
16505
16859
|
console.error(`[MCP] Server script not found. Tried paths:`, candidatePaths);
|
|
@@ -17429,6 +17783,7 @@ var WebSocketLlmBackend = class {
|
|
|
17429
17783
|
settled = true;
|
|
17430
17784
|
this.wsManager.offRequest(requestId);
|
|
17431
17785
|
this.wsManager.offDisconnect(onDisconnect);
|
|
17786
|
+
options.abortSignal?.removeEventListener("abort", abortHandler);
|
|
17432
17787
|
action();
|
|
17433
17788
|
};
|
|
17434
17789
|
const settleResolve = () => settle(() => resolve3());
|
|
@@ -17438,19 +17793,16 @@ var WebSocketLlmBackend = class {
|
|
|
17438
17793
|
settleReject(new Error("WebSocket connection lost during completion"));
|
|
17439
17794
|
};
|
|
17440
17795
|
this.wsManager.onDisconnect(onDisconnect);
|
|
17796
|
+
const abortHandler = () => {
|
|
17797
|
+
logger.debug("[WebSocketLlmBackend] Abort signal received");
|
|
17798
|
+
settleResolve();
|
|
17799
|
+
};
|
|
17441
17800
|
if (options.abortSignal) {
|
|
17442
17801
|
if (options.abortSignal.aborted) {
|
|
17443
17802
|
settleResolve();
|
|
17444
17803
|
return;
|
|
17445
17804
|
}
|
|
17446
|
-
options.abortSignal.addEventListener(
|
|
17447
|
-
"abort",
|
|
17448
|
-
() => {
|
|
17449
|
-
logger.debug("[WebSocketLlmBackend] Abort signal received");
|
|
17450
|
-
settleResolve();
|
|
17451
|
-
},
|
|
17452
|
-
{ once: true }
|
|
17453
|
-
);
|
|
17805
|
+
options.abortSignal.addEventListener("abort", abortHandler, { once: true });
|
|
17454
17806
|
}
|
|
17455
17807
|
const updateUsage = (usage) => {
|
|
17456
17808
|
if (usage) {
|
|
@@ -17788,27 +18140,35 @@ var WebSocketToolExecutor = class {
|
|
|
17788
18140
|
const requestId = uuidv412();
|
|
17789
18141
|
return new Promise((resolve3, reject) => {
|
|
17790
18142
|
let settled = false;
|
|
18143
|
+
let timeoutTimer;
|
|
17791
18144
|
const settle = (action) => {
|
|
17792
18145
|
if (settled) return;
|
|
17793
18146
|
settled = true;
|
|
18147
|
+
clearTimeout(timeoutTimer);
|
|
17794
18148
|
this.wsManager.offRequest(requestId);
|
|
17795
18149
|
this.wsManager.offDisconnect(onDisconnect);
|
|
18150
|
+
abortSignal?.removeEventListener("abort", abortHandler);
|
|
17796
18151
|
action();
|
|
17797
18152
|
};
|
|
17798
18153
|
const settleResolve = (result) => settle(() => resolve3(result));
|
|
17799
18154
|
const settleReject = (err) => settle(() => reject(err));
|
|
18155
|
+
const TOOL_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
18156
|
+
timeoutTimer = setTimeout(() => {
|
|
18157
|
+
settleReject(new Error(`Tool execution timed out after ${TOOL_TIMEOUT_MS / 1e3}s`));
|
|
18158
|
+
}, TOOL_TIMEOUT_MS);
|
|
17800
18159
|
const onDisconnect = () => {
|
|
17801
18160
|
settleReject(new Error("WebSocket connection lost during tool execution"));
|
|
17802
18161
|
};
|
|
17803
18162
|
this.wsManager.onDisconnect(onDisconnect);
|
|
18163
|
+
const abortHandler = () => {
|
|
18164
|
+
settleReject(new Error("Tool execution aborted"));
|
|
18165
|
+
};
|
|
17804
18166
|
if (abortSignal) {
|
|
17805
18167
|
if (abortSignal.aborted) {
|
|
17806
18168
|
settleReject(new Error("Tool execution aborted"));
|
|
17807
18169
|
return;
|
|
17808
18170
|
}
|
|
17809
|
-
abortSignal.addEventListener("abort",
|
|
17810
|
-
once: true
|
|
17811
|
-
});
|
|
18171
|
+
abortSignal.addEventListener("abort", abortHandler, { once: true });
|
|
17812
18172
|
}
|
|
17813
18173
|
this.wsManager.onRequest(requestId, (message) => {
|
|
17814
18174
|
if (message.action === "cli_tool_response") {
|
|
@@ -18226,16 +18586,10 @@ var SubagentOrchestrator = class {
|
|
|
18226
18586
|
*/
|
|
18227
18587
|
async delegateToAgent(options) {
|
|
18228
18588
|
const { task, agentName, thoroughness, variables, parentSessionId, model, allowedTools } = options;
|
|
18229
|
-
|
|
18230
|
-
if (
|
|
18231
|
-
|
|
18232
|
-
|
|
18233
|
-
const storedDef = this.deps.agentStore.getAgent(agentName);
|
|
18234
|
-
if (!storedDef) {
|
|
18235
|
-
const available = this.deps.agentStore.getAgentNames().join(", ");
|
|
18236
|
-
throw new Error(`Unknown agent: "${agentName}". Available agents: ${available}`);
|
|
18237
|
-
}
|
|
18238
|
-
agentDef = storedDef;
|
|
18589
|
+
const agentDef = this.deps.agentStore.getAgent(agentName);
|
|
18590
|
+
if (!agentDef) {
|
|
18591
|
+
const available = this.deps.agentStore.getAgentNames().join(", ");
|
|
18592
|
+
throw new Error(`Unknown agent: "${agentName}". Available agents: ${available}`);
|
|
18239
18593
|
}
|
|
18240
18594
|
const effectiveModel = model || agentDef.model;
|
|
18241
18595
|
const effectiveThoroughness = thoroughness || agentDef.defaultThoroughness;
|
|
@@ -18262,7 +18616,10 @@ var SubagentOrchestrator = class {
|
|
|
18262
18616
|
this.deps.showPermissionPrompt,
|
|
18263
18617
|
agentContext,
|
|
18264
18618
|
this.deps.configStore,
|
|
18265
|
-
this.deps.apiClient
|
|
18619
|
+
this.deps.apiClient,
|
|
18620
|
+
void 0,
|
|
18621
|
+
// toolFilter (applied separately below)
|
|
18622
|
+
this.deps.checkpointStore
|
|
18266
18623
|
);
|
|
18267
18624
|
const filteredTools = filterToolsByPatterns2(allTools, toolFilter.allowedTools, toolFilter.deniedTools);
|
|
18268
18625
|
if (this.deps.customCommandStore) {
|
|
@@ -18415,8 +18772,8 @@ var SubagentOrchestrator = class {
|
|
|
18415
18772
|
};
|
|
18416
18773
|
|
|
18417
18774
|
// src/agents/AgentStore.ts
|
|
18418
|
-
import
|
|
18419
|
-
import
|
|
18775
|
+
import fs13 from "fs/promises";
|
|
18776
|
+
import path18 from "path";
|
|
18420
18777
|
import os2 from "os";
|
|
18421
18778
|
import matter2 from "gray-matter";
|
|
18422
18779
|
var FULL_MODEL_ID_PREFIXES = [
|
|
@@ -18566,10 +18923,10 @@ var AgentStore = class {
|
|
|
18566
18923
|
const root = projectRoot || process.cwd();
|
|
18567
18924
|
const home = os2.homedir();
|
|
18568
18925
|
this.builtinAgentsDir = builtinDir;
|
|
18569
|
-
this.globalB4MAgentsDir =
|
|
18570
|
-
this.globalClaudeAgentsDir =
|
|
18571
|
-
this.projectB4MAgentsDir =
|
|
18572
|
-
this.projectClaudeAgentsDir =
|
|
18926
|
+
this.globalB4MAgentsDir = path18.join(home, ".bike4mind", "agents");
|
|
18927
|
+
this.globalClaudeAgentsDir = path18.join(home, ".claude", "agents");
|
|
18928
|
+
this.projectB4MAgentsDir = path18.join(root, ".bike4mind", "agents");
|
|
18929
|
+
this.projectClaudeAgentsDir = path18.join(root, ".claude", "agents");
|
|
18573
18930
|
}
|
|
18574
18931
|
/**
|
|
18575
18932
|
* Load all agents from all directories
|
|
@@ -18593,7 +18950,7 @@ var AgentStore = class {
|
|
|
18593
18950
|
*/
|
|
18594
18951
|
async loadAgentsFromDirectory(directory, source) {
|
|
18595
18952
|
try {
|
|
18596
|
-
const stats = await
|
|
18953
|
+
const stats = await fs13.stat(directory);
|
|
18597
18954
|
if (!stats.isDirectory()) {
|
|
18598
18955
|
return;
|
|
18599
18956
|
}
|
|
@@ -18621,9 +18978,9 @@ var AgentStore = class {
|
|
|
18621
18978
|
async findAgentFiles(directory) {
|
|
18622
18979
|
const files = [];
|
|
18623
18980
|
try {
|
|
18624
|
-
const entries = await
|
|
18981
|
+
const entries = await fs13.readdir(directory, { withFileTypes: true });
|
|
18625
18982
|
for (const entry of entries) {
|
|
18626
|
-
const fullPath =
|
|
18983
|
+
const fullPath = path18.join(directory, entry.name);
|
|
18627
18984
|
if (entry.isDirectory()) {
|
|
18628
18985
|
const subFiles = await this.findAgentFiles(fullPath);
|
|
18629
18986
|
files.push(...subFiles);
|
|
@@ -18640,10 +18997,10 @@ var AgentStore = class {
|
|
|
18640
18997
|
* Parse a single agent markdown file
|
|
18641
18998
|
*/
|
|
18642
18999
|
async parseAgentFile(filePath, source) {
|
|
18643
|
-
const content = await
|
|
19000
|
+
const content = await fs13.readFile(filePath, "utf-8");
|
|
18644
19001
|
const { data: frontmatter, content: body } = matter2(content);
|
|
18645
19002
|
const parsed = AgentFrontmatterSchema.parse(frontmatter);
|
|
18646
|
-
const name =
|
|
19003
|
+
const name = path18.basename(filePath, ".md");
|
|
18647
19004
|
const modelInput = parsed.model || DEFAULT_AGENT_MODEL;
|
|
18648
19005
|
const resolution = resolveModelAlias(modelInput, name, filePath);
|
|
18649
19006
|
if (!resolution.resolved && resolution.warning) {
|
|
@@ -18723,16 +19080,16 @@ var AgentStore = class {
|
|
|
18723
19080
|
*/
|
|
18724
19081
|
async createAgentFile(name, isGlobal = false, useClaude = true) {
|
|
18725
19082
|
const targetDir = isGlobal ? useClaude ? this.globalClaudeAgentsDir : this.globalB4MAgentsDir : useClaude ? this.projectClaudeAgentsDir : this.projectB4MAgentsDir;
|
|
18726
|
-
const filePath =
|
|
19083
|
+
const filePath = path18.join(targetDir, `${name}.md`);
|
|
18727
19084
|
try {
|
|
18728
|
-
await
|
|
19085
|
+
await fs13.access(filePath);
|
|
18729
19086
|
throw new Error(`Agent file already exists: ${filePath}`);
|
|
18730
19087
|
} catch (error) {
|
|
18731
19088
|
if (error.code !== "ENOENT") {
|
|
18732
19089
|
throw error;
|
|
18733
19090
|
}
|
|
18734
19091
|
}
|
|
18735
|
-
await
|
|
19092
|
+
await fs13.mkdir(targetDir, { recursive: true });
|
|
18736
19093
|
const template = `---
|
|
18737
19094
|
description: ${name} agent description
|
|
18738
19095
|
model: claude-3-5-haiku-20241022
|
|
@@ -18767,7 +19124,7 @@ You are a ${name} specialist. Your job is to [describe primary task].
|
|
|
18767
19124
|
## Output Format
|
|
18768
19125
|
Describe the expected output format here.
|
|
18769
19126
|
`;
|
|
18770
|
-
await
|
|
19127
|
+
await fs13.writeFile(filePath, template, "utf-8");
|
|
18771
19128
|
return filePath;
|
|
18772
19129
|
}
|
|
18773
19130
|
/**
|
|
@@ -18900,136 +19257,6 @@ ${agentDescriptions}
|
|
|
18900
19257
|
};
|
|
18901
19258
|
}
|
|
18902
19259
|
|
|
18903
|
-
// src/agents/dynamicAgentTool.ts
|
|
18904
|
-
function createDynamicAgentTool(orchestrator, parentSessionId, backgroundManager) {
|
|
18905
|
-
return {
|
|
18906
|
-
toolFn: async (args) => {
|
|
18907
|
-
const params = args;
|
|
18908
|
-
if (!params.task) {
|
|
18909
|
-
throw new Error("create_dynamic_agent: task parameter is required");
|
|
18910
|
-
}
|
|
18911
|
-
if (!params.name) {
|
|
18912
|
-
throw new Error("create_dynamic_agent: name parameter is required");
|
|
18913
|
-
}
|
|
18914
|
-
if (!/^[a-zA-Z0-9_-]+$/.test(params.name)) {
|
|
18915
|
-
throw new Error(
|
|
18916
|
-
"create_dynamic_agent: name must contain only alphanumeric characters, hyphens, and underscores"
|
|
18917
|
-
);
|
|
18918
|
-
}
|
|
18919
|
-
if (!params.systemPrompt) {
|
|
18920
|
-
throw new Error("create_dynamic_agent: systemPrompt parameter is required");
|
|
18921
|
-
}
|
|
18922
|
-
const deniedTools = [...params.deniedTools || [], ...ALWAYS_DENIED_FOR_AGENTS, "create_dynamic_agent"];
|
|
18923
|
-
const agentDefinition = {
|
|
18924
|
-
description: params.description || `Dynamic agent: ${params.name}`,
|
|
18925
|
-
model: params.model || DEFAULT_AGENT_MODEL,
|
|
18926
|
-
systemPrompt: params.systemPrompt,
|
|
18927
|
-
allowedTools: params.allowedTools,
|
|
18928
|
-
deniedTools,
|
|
18929
|
-
maxIterations: { ...DEFAULT_MAX_ITERATIONS },
|
|
18930
|
-
defaultThoroughness: DEFAULT_THOROUGHNESS,
|
|
18931
|
-
defaultVariables: params.variables
|
|
18932
|
-
};
|
|
18933
|
-
const spawnOptions = {
|
|
18934
|
-
task: params.task,
|
|
18935
|
-
agentName: params.name,
|
|
18936
|
-
thoroughness: params.thoroughness,
|
|
18937
|
-
variables: params.variables,
|
|
18938
|
-
parentSessionId,
|
|
18939
|
-
model: params.model,
|
|
18940
|
-
allowedTools: params.allowedTools,
|
|
18941
|
-
agentDefinition
|
|
18942
|
-
};
|
|
18943
|
-
if (params.run_in_background && backgroundManager) {
|
|
18944
|
-
const jobId = backgroundManager.spawn({
|
|
18945
|
-
...spawnOptions,
|
|
18946
|
-
groupDescription: params.group_description
|
|
18947
|
-
});
|
|
18948
|
-
return `Dynamic background agent "${params.name}" started. Job ID: ${jobId}. Use check_agent_status tool with this job ID to retrieve results when ready.`;
|
|
18949
|
-
}
|
|
18950
|
-
const result = await orchestrator.delegateToAgent(spawnOptions);
|
|
18951
|
-
return result.summary;
|
|
18952
|
-
},
|
|
18953
|
-
toolSchema: {
|
|
18954
|
-
name: "create_dynamic_agent",
|
|
18955
|
-
description: `Create and spawn a one-off agent at runtime with a custom system prompt.
|
|
18956
|
-
|
|
18957
|
-
Unlike agent_delegate (which uses pre-defined agents), this tool lets you compose a new agent on the fly with custom instructions, model, and tool restrictions.
|
|
18958
|
-
|
|
18959
|
-
**When to use this tool:**
|
|
18960
|
-
- When no existing agent fits the task at hand
|
|
18961
|
-
- When you need a specialized agent with custom instructions for a one-off task
|
|
18962
|
-
- When you need fine-grained control over what tools the agent can access
|
|
18963
|
-
|
|
18964
|
-
**Constraints:**
|
|
18965
|
-
- Dynamic agents CANNOT call agent_delegate or create_dynamic_agent (no recursive spawning)
|
|
18966
|
-
- Dynamic agents are ephemeral \u2014 they are not saved or reusable across sessions
|
|
18967
|
-
|
|
18968
|
-
**Example uses:**
|
|
18969
|
-
- Create a security auditor agent with specific review criteria
|
|
18970
|
-
- Create a migration agent that follows a custom checklist
|
|
18971
|
-
- Create a specialized code generator with domain-specific instructions`,
|
|
18972
|
-
parameters: {
|
|
18973
|
-
type: "object",
|
|
18974
|
-
properties: {
|
|
18975
|
-
task: {
|
|
18976
|
-
type: "string",
|
|
18977
|
-
description: "Clear description of what you want the dynamic agent to accomplish."
|
|
18978
|
-
},
|
|
18979
|
-
name: {
|
|
18980
|
-
type: "string",
|
|
18981
|
-
description: 'Unique name for this dynamic agent (e.g., "security-auditor", "migration-helper"). Used for logging and identification.'
|
|
18982
|
-
},
|
|
18983
|
-
systemPrompt: {
|
|
18984
|
-
type: "string",
|
|
18985
|
-
description: "Custom system prompt for the agent. This defines the agent's role, capabilities, and constraints. Use $TASK to reference the task parameter."
|
|
18986
|
-
},
|
|
18987
|
-
description: {
|
|
18988
|
-
type: "string",
|
|
18989
|
-
description: "Short description of the agent's purpose (for logging)."
|
|
18990
|
-
},
|
|
18991
|
-
model: {
|
|
18992
|
-
type: "string",
|
|
18993
|
-
description: 'Model to use for this agent (e.g., "claude-sonnet-4-5-20250929"). Defaults to the agent system default.'
|
|
18994
|
-
},
|
|
18995
|
-
allowedTools: {
|
|
18996
|
-
type: "array",
|
|
18997
|
-
items: { type: "string" },
|
|
18998
|
-
description: 'Whitelist of tool name patterns the agent can use. Supports wildcards (e.g., "file_*", "mcp__github__*"). If omitted, all non-denied tools are available.'
|
|
18999
|
-
},
|
|
19000
|
-
deniedTools: {
|
|
19001
|
-
type: "array",
|
|
19002
|
-
items: { type: "string" },
|
|
19003
|
-
description: "Blacklist of tool name patterns the agent cannot use. agent_delegate and create_dynamic_agent are always denied."
|
|
19004
|
-
},
|
|
19005
|
-
thoroughness: {
|
|
19006
|
-
type: "string",
|
|
19007
|
-
enum: ["quick", "medium", "very_thorough"],
|
|
19008
|
-
description: `How thoroughly to execute:
|
|
19009
|
-
- quick: Fast, 1-2 iterations
|
|
19010
|
-
- medium: Balanced, 3-5 iterations (default)
|
|
19011
|
-
- very_thorough: Comprehensive, 8-10+ iterations`
|
|
19012
|
-
},
|
|
19013
|
-
variables: {
|
|
19014
|
-
type: "object",
|
|
19015
|
-
additionalProperties: { type: "string" },
|
|
19016
|
-
description: 'Variables to substitute in the system prompt. For example: { "DOMAIN": "auth" } replaces $DOMAIN in the prompt.'
|
|
19017
|
-
},
|
|
19018
|
-
run_in_background: {
|
|
19019
|
-
type: "boolean",
|
|
19020
|
-
description: "Run the agent in the background (non-blocking). Returns a job ID immediately. Use check_agent_status to poll for results."
|
|
19021
|
-
},
|
|
19022
|
-
group_description: {
|
|
19023
|
-
type: "string",
|
|
19024
|
-
description: "Short description of what this group of background agents is working on. Only needed for the first background agent in a group."
|
|
19025
|
-
}
|
|
19026
|
-
},
|
|
19027
|
-
required: ["task", "name", "systemPrompt"]
|
|
19028
|
-
}
|
|
19029
|
-
}
|
|
19030
|
-
};
|
|
19031
|
-
}
|
|
19032
|
-
|
|
19033
19260
|
// src/agents/BackgroundAgentManager.ts
|
|
19034
19261
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
19035
19262
|
var DEFAULT_MAX_CONCURRENT = 4;
|
|
@@ -19550,7 +19777,7 @@ function createTodoStore(onUpdate) {
|
|
|
19550
19777
|
|
|
19551
19778
|
// src/tools/findDefinitionTool.ts
|
|
19552
19779
|
import { stat as stat3 } from "fs/promises";
|
|
19553
|
-
import
|
|
19780
|
+
import path19 from "path";
|
|
19554
19781
|
import { execFile as execFile2 } from "child_process";
|
|
19555
19782
|
import { promisify as promisify2 } from "util";
|
|
19556
19783
|
import { createRequire as createRequire2 } from "module";
|
|
@@ -19571,8 +19798,8 @@ var ALL_KEYWORDS = Object.values(KIND_KEYWORDS).flat();
|
|
|
19571
19798
|
function getRipgrepPath2() {
|
|
19572
19799
|
try {
|
|
19573
19800
|
const ripgrepPath = require3.resolve("@vscode/ripgrep");
|
|
19574
|
-
const ripgrepDir =
|
|
19575
|
-
return
|
|
19801
|
+
const ripgrepDir = path19.dirname(ripgrepPath);
|
|
19802
|
+
return path19.join(ripgrepDir, "..", "bin", "rg" + (process.platform === "win32" ? ".exe" : ""));
|
|
19576
19803
|
} catch {
|
|
19577
19804
|
throw new Error(
|
|
19578
19805
|
"ripgrep is not available. Please install @vscode/ripgrep: pnpm add @vscode/ripgrep --filter @bike4mind/services"
|
|
@@ -19580,10 +19807,10 @@ function getRipgrepPath2() {
|
|
|
19580
19807
|
}
|
|
19581
19808
|
}
|
|
19582
19809
|
function isPathWithinWorkspace2(targetPath, baseCwd) {
|
|
19583
|
-
const resolvedTarget =
|
|
19584
|
-
const resolvedBase =
|
|
19585
|
-
const relativePath =
|
|
19586
|
-
return !relativePath.startsWith("..") && !
|
|
19810
|
+
const resolvedTarget = path19.resolve(targetPath);
|
|
19811
|
+
const resolvedBase = path19.resolve(baseCwd);
|
|
19812
|
+
const relativePath = path19.relative(resolvedBase, resolvedTarget);
|
|
19813
|
+
return !relativePath.startsWith("..") && !path19.isAbsolute(relativePath);
|
|
19587
19814
|
}
|
|
19588
19815
|
function escapeRegex(str) {
|
|
19589
19816
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -19616,7 +19843,7 @@ async function findDefinitions(params) {
|
|
|
19616
19843
|
throw new Error("symbol_name is required");
|
|
19617
19844
|
}
|
|
19618
19845
|
const baseCwd = process.cwd();
|
|
19619
|
-
const targetDir = search_path ?
|
|
19846
|
+
const targetDir = search_path ? path19.resolve(baseCwd, search_path) : baseCwd;
|
|
19620
19847
|
if (!isPathWithinWorkspace2(targetDir, baseCwd)) {
|
|
19621
19848
|
throw new Error(`Path validation failed: "${search_path}" resolves outside the allowed workspace directory`);
|
|
19622
19849
|
}
|
|
@@ -19671,7 +19898,7 @@ async function findDefinitions(params) {
|
|
|
19671
19898
|
const lineText = match.data.lines.text.trimEnd();
|
|
19672
19899
|
if (isLikelyDefinition(lineText)) {
|
|
19673
19900
|
allMatches.push({
|
|
19674
|
-
filePath:
|
|
19901
|
+
filePath: path19.relative(targetDir, match.data.path.text) || path19.basename(match.data.path.text),
|
|
19675
19902
|
lineNumber: match.data.line_number,
|
|
19676
19903
|
line: lineText
|
|
19677
19904
|
});
|
|
@@ -19749,8 +19976,8 @@ function createFindDefinitionTool() {
|
|
|
19749
19976
|
}
|
|
19750
19977
|
|
|
19751
19978
|
// src/tools/getFileStructure/index.ts
|
|
19752
|
-
import { existsSync as
|
|
19753
|
-
import
|
|
19979
|
+
import { existsSync as existsSync12, promises as fs14, statSync as statSync6 } from "fs";
|
|
19980
|
+
import path20 from "path";
|
|
19754
19981
|
|
|
19755
19982
|
// src/tools/getFileStructure/formatter.ts
|
|
19756
19983
|
var SECTIONS = [
|
|
@@ -19787,35 +20014,35 @@ function formatSection(lines, title, items, format) {
|
|
|
19787
20014
|
}
|
|
19788
20015
|
|
|
19789
20016
|
// src/tools/getFileStructure/index.ts
|
|
19790
|
-
var
|
|
20017
|
+
var MAX_FILE_SIZE4 = 10 * 1024 * 1024;
|
|
19791
20018
|
function createGetFileStructureTool() {
|
|
19792
20019
|
return {
|
|
19793
20020
|
toolFn: async (value) => {
|
|
19794
20021
|
const params = value;
|
|
19795
20022
|
try {
|
|
19796
20023
|
const cwd = process.cwd();
|
|
19797
|
-
const resolvedPath =
|
|
20024
|
+
const resolvedPath = path20.resolve(cwd, params.path);
|
|
19798
20025
|
if (!resolvedPath.startsWith(cwd)) {
|
|
19799
20026
|
return "Error: Access denied - cannot read files outside of current working directory";
|
|
19800
20027
|
}
|
|
19801
|
-
if (!
|
|
20028
|
+
if (!existsSync12(resolvedPath)) {
|
|
19802
20029
|
return `Error: File not found: ${params.path}`;
|
|
19803
20030
|
}
|
|
19804
20031
|
const stats = statSync6(resolvedPath);
|
|
19805
20032
|
if (stats.isDirectory()) {
|
|
19806
20033
|
return `Error: Path is a directory, not a file: ${params.path}`;
|
|
19807
20034
|
}
|
|
19808
|
-
if (stats.size >
|
|
19809
|
-
return `Error: File too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Max: ${
|
|
20035
|
+
if (stats.size > MAX_FILE_SIZE4) {
|
|
20036
|
+
return `Error: File too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Max: ${MAX_FILE_SIZE4 / 1024 / 1024}MB`;
|
|
19810
20037
|
}
|
|
19811
|
-
const ext =
|
|
20038
|
+
const ext = path20.extname(resolvedPath).toLowerCase();
|
|
19812
20039
|
const { getLanguageForExtension, parseFileStructure, getSupportedLanguages } = await import("./treeSitterEngine-4SGFQDY3.js");
|
|
19813
20040
|
const languageId = getLanguageForExtension(ext);
|
|
19814
20041
|
if (!languageId) {
|
|
19815
20042
|
const supported = getSupportedLanguages();
|
|
19816
20043
|
return `Error: Unsupported file type "${ext}". Supported languages: ${supported.join(", ")}`;
|
|
19817
20044
|
}
|
|
19818
|
-
const sourceCode = await
|
|
20045
|
+
const sourceCode = await fs14.readFile(resolvedPath, "utf-8");
|
|
19819
20046
|
const lineCount = sourceCode.split("\n").length;
|
|
19820
20047
|
const items = await parseFileStructure(sourceCode, languageId);
|
|
19821
20048
|
return formatStructureOutput(params.path, items, stats.size, lineCount);
|
|
@@ -19877,7 +20104,8 @@ function CliApp() {
|
|
|
19877
20104
|
abortController: null,
|
|
19878
20105
|
contextContent: "",
|
|
19879
20106
|
backgroundManager: null,
|
|
19880
|
-
wsManager: null
|
|
20107
|
+
wsManager: null,
|
|
20108
|
+
checkpointStore: null
|
|
19881
20109
|
});
|
|
19882
20110
|
const [isInitialized, setIsInitialized] = useState10(false);
|
|
19883
20111
|
const [initError, setInitError] = useState10(null);
|
|
@@ -20131,6 +20359,12 @@ function CliApp() {
|
|
|
20131
20359
|
enqueuePermissionPrompt(prompt);
|
|
20132
20360
|
});
|
|
20133
20361
|
};
|
|
20362
|
+
const checkpointProjectDir = state.configStore.getProjectConfigDir() || process.cwd();
|
|
20363
|
+
const checkpointStore = new CheckpointStore(checkpointProjectDir);
|
|
20364
|
+
try {
|
|
20365
|
+
await checkpointStore.init(newSession.id);
|
|
20366
|
+
} catch {
|
|
20367
|
+
}
|
|
20134
20368
|
const agentContext = {
|
|
20135
20369
|
currentAgent: null,
|
|
20136
20370
|
observationQueue: []
|
|
@@ -20143,7 +20377,10 @@ function CliApp() {
|
|
|
20143
20377
|
promptFn,
|
|
20144
20378
|
agentContext,
|
|
20145
20379
|
state.configStore,
|
|
20146
|
-
apiClient
|
|
20380
|
+
apiClient,
|
|
20381
|
+
void 0,
|
|
20382
|
+
// toolFilter
|
|
20383
|
+
checkpointStore
|
|
20147
20384
|
);
|
|
20148
20385
|
startupLog.push(`\u{1F6E0}\uFE0F Loaded ${b4mTools2.length} B4M tool(s)`);
|
|
20149
20386
|
const mcpManager = new McpManager(config);
|
|
@@ -20174,7 +20411,8 @@ function CliApp() {
|
|
|
20174
20411
|
apiClient,
|
|
20175
20412
|
agentStore,
|
|
20176
20413
|
customCommandStore: state.customCommandStore,
|
|
20177
|
-
enableParallelToolExecution: config.preferences.enableParallelToolExecution === true
|
|
20414
|
+
enableParallelToolExecution: config.preferences.enableParallelToolExecution === true,
|
|
20415
|
+
checkpointStore
|
|
20178
20416
|
});
|
|
20179
20417
|
const backgroundManager = new BackgroundAgentManager(orchestrator);
|
|
20180
20418
|
backgroundManager.setOnStatusChange((job) => {
|
|
@@ -20185,7 +20423,6 @@ function CliApp() {
|
|
|
20185
20423
|
useCliStore.getState().setPendingBackgroundTrigger(true);
|
|
20186
20424
|
});
|
|
20187
20425
|
const agentDelegateTool = createAgentDelegateTool(orchestrator, agentStore, newSession.id, backgroundManager);
|
|
20188
|
-
const dynamicAgentTool = config.preferences.enableDynamicAgentCreation === true ? createDynamicAgentTool(orchestrator, newSession.id, backgroundManager) : null;
|
|
20189
20426
|
const backgroundTools = createBackgroundAgentTools(backgroundManager);
|
|
20190
20427
|
const notifyingLlm = new NotifyingLlmBackend(llm, backgroundManager);
|
|
20191
20428
|
const todoStore = createTodoStore();
|
|
@@ -20208,9 +20445,6 @@ function CliApp() {
|
|
|
20208
20445
|
if (skillTool) {
|
|
20209
20446
|
cliTools.push(skillTool);
|
|
20210
20447
|
}
|
|
20211
|
-
if (dynamicAgentTool) {
|
|
20212
|
-
cliTools.push(dynamicAgentTool);
|
|
20213
|
-
}
|
|
20214
20448
|
const allTools = [...b4mTools2, ...mcpTools, ...cliTools];
|
|
20215
20449
|
startupLog.push(`\u{1F4C2} Working directory: ${process.cwd()}`);
|
|
20216
20450
|
const agentNamesList = agentStore.getAgentNames().join(", ");
|
|
@@ -20221,9 +20455,6 @@ function CliApp() {
|
|
|
20221
20455
|
startupLog.push(`\u{1F6E0}\uFE0F Skill tool enabled (${skillCount} skills available)`);
|
|
20222
20456
|
}
|
|
20223
20457
|
}
|
|
20224
|
-
if (dynamicAgentTool) {
|
|
20225
|
-
startupLog.push(`\u{1F9EA} Dynamic agent creation enabled (experimental)`);
|
|
20226
|
-
}
|
|
20227
20458
|
logger.debug(
|
|
20228
20459
|
`Total tools available to agent: ${allTools.length} (${b4mTools2.length} B4M + ${mcpTools.length} MCP + ${cliTools.length} CLI)`
|
|
20229
20460
|
);
|
|
@@ -20242,8 +20473,7 @@ function CliApp() {
|
|
|
20242
20473
|
contextContent: contextResult.mergedContent,
|
|
20243
20474
|
agentStore,
|
|
20244
20475
|
customCommands: state.customCommandStore.getAllCommands(),
|
|
20245
|
-
enableSkillTool
|
|
20246
|
-
enableDynamicAgentCreation: config.preferences.enableDynamicAgentCreation === true
|
|
20476
|
+
enableSkillTool
|
|
20247
20477
|
});
|
|
20248
20478
|
const maxIterations = config.preferences.maxIterations === null ? 999999 : config.preferences.maxIterations;
|
|
20249
20479
|
const agent = new ReActAgent({
|
|
@@ -20305,8 +20535,10 @@ function CliApp() {
|
|
|
20305
20535
|
// Store raw context for compact instructions
|
|
20306
20536
|
backgroundManager,
|
|
20307
20537
|
// Store for grouped notification turn tracking
|
|
20308
|
-
wsManager
|
|
20538
|
+
wsManager,
|
|
20309
20539
|
// WebSocket connection manager (null if using SSE fallback)
|
|
20540
|
+
checkpointStore
|
|
20541
|
+
// File change checkpointing for undo/restore
|
|
20310
20542
|
}));
|
|
20311
20543
|
setStoreSession(newSession);
|
|
20312
20544
|
const bannerLines = [
|
|
@@ -20999,6 +21231,10 @@ Available commands:
|
|
|
20999
21231
|
/exit - Exit the CLI
|
|
21000
21232
|
/clear - Start a new session
|
|
21001
21233
|
/rewind - Rewind conversation to a previous point
|
|
21234
|
+
/undo - Undo the last file change
|
|
21235
|
+
/checkpoints - List available file restore points
|
|
21236
|
+
/restore <n> - Restore files to a specific checkpoint
|
|
21237
|
+
/diff [n] - Show diff between current state and a checkpoint
|
|
21002
21238
|
/login - Authenticate with your B4M account
|
|
21003
21239
|
/logout - Clear authentication and sign out
|
|
21004
21240
|
/whoami - Show current authenticated user
|
|
@@ -21091,6 +21327,9 @@ Keyboard Shortcuts:
|
|
|
21091
21327
|
}
|
|
21092
21328
|
await logger.initialize(loadedSession.id);
|
|
21093
21329
|
logger.debug("=== Session Resumed ===");
|
|
21330
|
+
if (state.checkpointStore) {
|
|
21331
|
+
state.checkpointStore.setSessionId(loadedSession.id);
|
|
21332
|
+
}
|
|
21094
21333
|
setState((prev) => ({ ...prev, session: loadedSession }));
|
|
21095
21334
|
setStoreSession(loadedSession);
|
|
21096
21335
|
useCliStore.getState().clearPendingMessages();
|
|
@@ -21329,6 +21568,9 @@ Keyboard Shortcuts:
|
|
|
21329
21568
|
};
|
|
21330
21569
|
await logger.initialize(newSession.id);
|
|
21331
21570
|
logger.debug("=== New Session Started via /clear ===");
|
|
21571
|
+
if (state.checkpointStore) {
|
|
21572
|
+
state.checkpointStore.setSessionId(newSession.id);
|
|
21573
|
+
}
|
|
21332
21574
|
setState((prev) => ({ ...prev, session: newSession }));
|
|
21333
21575
|
setStoreSession(newSession);
|
|
21334
21576
|
useCliStore.getState().clearPendingMessages();
|
|
@@ -21339,8 +21581,7 @@ Keyboard Shortcuts:
|
|
|
21339
21581
|
`);
|
|
21340
21582
|
break;
|
|
21341
21583
|
}
|
|
21342
|
-
case "rewind":
|
|
21343
|
-
case "undo": {
|
|
21584
|
+
case "rewind": {
|
|
21344
21585
|
if (!state.session) {
|
|
21345
21586
|
console.log("No active session to rewind");
|
|
21346
21587
|
return;
|
|
@@ -21399,6 +21640,103 @@ Keyboard Shortcuts:
|
|
|
21399
21640
|
}));
|
|
21400
21641
|
break;
|
|
21401
21642
|
}
|
|
21643
|
+
case "undo": {
|
|
21644
|
+
if (!state.checkpointStore) {
|
|
21645
|
+
console.log("Checkpointing not available.");
|
|
21646
|
+
return;
|
|
21647
|
+
}
|
|
21648
|
+
const undoCheckpoints = state.checkpointStore.listCheckpoints();
|
|
21649
|
+
if (undoCheckpoints.length === 0) {
|
|
21650
|
+
console.log("No checkpoints available. No file changes have been made yet.");
|
|
21651
|
+
return;
|
|
21652
|
+
}
|
|
21653
|
+
try {
|
|
21654
|
+
const restored = await state.checkpointStore.undoLast();
|
|
21655
|
+
console.log(`
|
|
21656
|
+
\u2705 Restored ${restored.filePaths.length} file(s) to state before: ${restored.name}`);
|
|
21657
|
+
for (const f of restored.filePaths) {
|
|
21658
|
+
console.log(` - ${f}`);
|
|
21659
|
+
}
|
|
21660
|
+
console.log("");
|
|
21661
|
+
} catch (err) {
|
|
21662
|
+
console.log(`Failed to undo: ${err instanceof Error ? err.message : String(err)}`);
|
|
21663
|
+
}
|
|
21664
|
+
break;
|
|
21665
|
+
}
|
|
21666
|
+
case "checkpoints": {
|
|
21667
|
+
if (!state.checkpointStore) {
|
|
21668
|
+
console.log("Checkpointing not available.");
|
|
21669
|
+
return;
|
|
21670
|
+
}
|
|
21671
|
+
const cpList = state.checkpointStore.listCheckpoints();
|
|
21672
|
+
if (cpList.length === 0) {
|
|
21673
|
+
console.log("No checkpoints yet. Checkpoints are created automatically before file changes.");
|
|
21674
|
+
return;
|
|
21675
|
+
}
|
|
21676
|
+
console.log("\nCheckpoints (most recent first):\n");
|
|
21677
|
+
cpList.forEach((cp, idx) => {
|
|
21678
|
+
const ageMs = Date.now() - new Date(cp.timestamp).getTime();
|
|
21679
|
+
const ageSec = Math.floor(ageMs / 1e3);
|
|
21680
|
+
let age;
|
|
21681
|
+
if (ageSec < 60) age = `${ageSec}s ago`;
|
|
21682
|
+
else if (ageSec < 3600) age = `${Math.floor(ageSec / 60)}m ago`;
|
|
21683
|
+
else age = `${Math.floor(ageSec / 3600)}h ago`;
|
|
21684
|
+
console.log(` ${idx + 1}. ${cp.name} (${age}) - ${cp.filePaths.length} file(s)`);
|
|
21685
|
+
});
|
|
21686
|
+
console.log("\nUse /restore <number> to restore, /diff <number> to see changes.\n");
|
|
21687
|
+
break;
|
|
21688
|
+
}
|
|
21689
|
+
case "restore": {
|
|
21690
|
+
if (!state.checkpointStore) {
|
|
21691
|
+
console.log("Checkpointing not available.");
|
|
21692
|
+
return;
|
|
21693
|
+
}
|
|
21694
|
+
const restoreTarget = args[0];
|
|
21695
|
+
if (!restoreTarget) {
|
|
21696
|
+
console.log("Usage: /restore <checkpoint-number>");
|
|
21697
|
+
console.log("Use /checkpoints to list available restore points.");
|
|
21698
|
+
return;
|
|
21699
|
+
}
|
|
21700
|
+
const restoreNum = parseInt(restoreTarget, 10);
|
|
21701
|
+
if (isNaN(restoreNum) || restoreNum < 1) {
|
|
21702
|
+
console.log("Please provide a valid checkpoint number. Use /checkpoints to list.");
|
|
21703
|
+
return;
|
|
21704
|
+
}
|
|
21705
|
+
try {
|
|
21706
|
+
const restoredCp = await state.checkpointStore.restoreCheckpoint(restoreNum);
|
|
21707
|
+
console.log(`
|
|
21708
|
+
\u2705 Restored to checkpoint: ${restoredCp.name}`);
|
|
21709
|
+
for (const f of restoredCp.filePaths) {
|
|
21710
|
+
console.log(` - ${f}`);
|
|
21711
|
+
}
|
|
21712
|
+
console.log("");
|
|
21713
|
+
} catch (err) {
|
|
21714
|
+
console.log(`Failed to restore: ${err instanceof Error ? err.message : String(err)}`);
|
|
21715
|
+
}
|
|
21716
|
+
break;
|
|
21717
|
+
}
|
|
21718
|
+
case "diff": {
|
|
21719
|
+
if (!state.checkpointStore) {
|
|
21720
|
+
console.log("Checkpointing not available.");
|
|
21721
|
+
return;
|
|
21722
|
+
}
|
|
21723
|
+
const diffTarget = args[0] ? parseInt(args[0], 10) : 1;
|
|
21724
|
+
if (isNaN(diffTarget) || diffTarget < 1) {
|
|
21725
|
+
console.log("Usage: /diff [checkpoint-number] (defaults to 1, most recent)");
|
|
21726
|
+
return;
|
|
21727
|
+
}
|
|
21728
|
+
try {
|
|
21729
|
+
const diffOutput = state.checkpointStore.getCheckpointDiff(diffTarget);
|
|
21730
|
+
if (!diffOutput.trim()) {
|
|
21731
|
+
console.log("No differences found between checkpoint and current state.");
|
|
21732
|
+
} else {
|
|
21733
|
+
console.log(diffOutput);
|
|
21734
|
+
}
|
|
21735
|
+
} catch (err) {
|
|
21736
|
+
console.log(`Failed to generate diff: ${err instanceof Error ? err.message : String(err)}`);
|
|
21737
|
+
}
|
|
21738
|
+
break;
|
|
21739
|
+
}
|
|
21402
21740
|
case "usage": {
|
|
21403
21741
|
const usageAuthTokens = await state.configStore.getAuthTokens();
|
|
21404
21742
|
if (!usageAuthTokens || new Date(usageAuthTokens.expiresAt) <= /* @__PURE__ */ new Date()) {
|